ipsec_inbound.c
Go to the documentation of this file.
1 /**
2  * @file ipsec_inbound.c
3  * @brief IPsec processing of inbound IP traffic
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2022-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneIPSEC Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Dependencies
32 #include "ipsec/ipsec.h"
33 #include "ipsec/ipsec_inbound.h"
34 #include "ipsec/ipsec_misc.h"
35 #include "debug.h"
36 
37 //Check IPsec library configuration
38 #if (IPSEC_SUPPORT == ENABLED)
39 
40 
41 /**
42  * @brief Inbound IPv4 traffic processing
43  * @param[in] interface Underlying network interface
44  * @param[in] ipv4Header Pointer to the IPv4 header
45  * @param[in] buffer Multi-part buffer containing the IP payload
46  * @param[in] offset Offset from the beginning of the buffer
47  * @return Error code
48  **/
49 
51  const Ipv4Header *ipv4Header, const NetBuffer *buffer, size_t offset)
52 {
53  error_t error;
54  IpsecSpdEntry *spdEntry;
55  IpsecSelector selector;
56 
57  //The packet is examined and demuxed into one of two categories (refer to
58  //RFC 7296, section 5.2)
59  if(ipv4Header->protocol == IPV4_PROTOCOL_AH ||
60  ipv4Header->protocol == IPV4_PROTOCOL_ESP)
61  {
62  //If the packet appears to be IPsec protected and it is addressed to
63  //this device, then parse the AH or the ESP header
64  error = NO_ERROR;
65  }
66  else
67  {
68  //If the packet is not addressed to the device or is addressed to this
69  //device and is not AH or ESP, look up the packet header in the SPD-I
70  //cache (refer to RFC 4301, section 5.2)
71  error = ipsecGetInboundIpv4PacketSelector(ipv4Header,
72  ipv4Header->protocol, buffer, offset, &selector);
73 
74  //Check status code
75  if(!error)
76  {
77  //Search the SPD for a matching entry
79  IPSEC_POLICY_ACTION_INVALID, &selector);
80 
81  //Any SPD entry found?
82  if(spdEntry != NULL)
83  {
84  //Check applicable SPD policies
86  {
87  //The packet allowed to bypass IPsec protection
88  error = NO_ERROR;
89  }
90  else
91  {
92  //Discard the packet
93  error = ERROR_POLICY_FAILURE;
94  }
95  }
96  else
97  {
98  //If there is no match, discard the traffic
99  error = ERROR_POLICY_FAILURE;
100  }
101  }
102  }
103 
104  //Return status code
105  return error;
106 }
107 
108 
109 /**
110  * @brief Extract packet's selector from inbound IPv4 packet
111  * @param[in] ipv4Header Pointer to the IPv4 header
112  * @param[in] nextHeader Value of the next header field
113  * @param[in] buffer Multi-part buffer containing the IP payload
114  * @param[in] offset Offset from the beginning of the buffer
115  * @param[out] selector Pointer to the IPsec selector
116  * @return Error code
117  **/
118 
120  uint8_t nextHeader, const NetBuffer *buffer, size_t offset,
121  IpsecSelector *selector)
122 {
123  error_t error;
124  size_t length;
125  const uint8_t *data;
126 
127  //Initialize status code
128  error = NO_ERROR;
129 
130  //Local IP address range
131  selector->localIpAddr.start.length = sizeof(Ipv4Addr);
132  selector->localIpAddr.start.ipv4Addr = ipv4Header->destAddr;
133  selector->localIpAddr.end.length = sizeof(Ipv4Addr);
134  selector->localIpAddr.end.ipv4Addr = ipv4Header->destAddr;
135 
136  //Remote IP address range
137  selector->remoteIpAddr.start.length = sizeof(Ipv4Addr);
138  selector->remoteIpAddr.start.ipv4Addr = ipv4Header->srcAddr;
139  selector->remoteIpAddr.end.length = sizeof(Ipv4Addr);
140  selector->remoteIpAddr.end.ipv4Addr = ipv4Header->srcAddr;
141 
142  //Next Layer Protocol value
143  selector->nextProtocol = nextHeader;
144 
145  //Retrieve the length of the data
146  length = netBufferGetLength(buffer) - offset;
147  //Point to the data
148  data = netBufferAt(buffer, offset);
149 
150  //Sanity check
151  if(data != NULL)
152  {
153  //Several additional selectors depend on the Next Layer Protocol value
154  //(refer to RFC 4301, section 4.4.1.1)
155  if(nextHeader == IPV4_PROTOCOL_UDP && length >= sizeof(UdpHeader))
156  {
157  //Point to the UDP header
158  UdpHeader *udpHeader = (UdpHeader *) data;
159 
160  //If the Next Layer Protocol value is UDP, then there are selectors
161  //for local and remote ports
162  selector->localPort.start = ntohs(udpHeader->destPort);
163  selector->localPort.end = ntohs(udpHeader->destPort);
164  selector->remotePort.start = ntohs(udpHeader->srcPort);
165  selector->remotePort.end = ntohs(udpHeader->srcPort);
166  }
167  else if(nextHeader == IPV4_PROTOCOL_TCP && length >= sizeof(TcpHeader))
168  {
169  //Point to the TCP header
170  TcpHeader *tcpHeader = (TcpHeader *) data;
171 
172  //If the Next Layer Protocol value is TCP, then there are selectors
173  //for local and remote ports
174  selector->localPort.start = ntohs(tcpHeader->destPort);
175  selector->localPort.end = ntohs(tcpHeader->destPort);
176  selector->remotePort.start = ntohs(tcpHeader->srcPort);
177  selector->remotePort.end = ntohs(tcpHeader->srcPort);
178  }
179  else if(nextHeader == IPV4_PROTOCOL_ICMP && length >= sizeof(IcmpHeader))
180  {
181  //Point to the ICMP header
182  IcmpHeader *icmpHeader = (IcmpHeader *) data;
183 
184  //If the Next Layer Protocol value is ICMP, then there is a 16-bit
185  //selector for the ICMP message type and code
187  selector->localPort.end = IPSEC_PORT_END_OPAQUE;
188  selector->remotePort.start = IPSEC_ICMP_PORT(icmpHeader->type, icmpHeader->code);
189  selector->remotePort.end = IPSEC_ICMP_PORT(icmpHeader->type, icmpHeader->code);
190  }
191  else
192  {
193  //The local and remote port selectors may be labeled as OPAQUE to
194  //accommodate situations where these fields are inaccessible
196  selector->localPort.end = IPSEC_PORT_END_OPAQUE;
199  }
200  }
201  else
202  {
203  //Report an error
204  error = ERROR_INVALID_HEADER;
205  }
206 
207  //Return status code
208  return error;
209 }
210 
211 
212 /**
213  * @brief Determine the higher-order bits of the sequence number
214  * @param[in] sa Pointer to the security association
215  * @param[in] seql Lower 32 bits of the sequence number extracted from the
216  * received packet
217  * @return 64-bit sequence number
218  **/
219 
220 uint64_t ipsecGetSeqNum(IpsecSadEntry *sa, uint32_t seql)
221 {
222  uint32_t bl;
223  uint32_t tl;
224  uint32_t th;
225  uint32_t seqh;
226 
227  //Extended sequence numbers?
228  if(sa->esn)
229  {
230  //The upper bound of window represents the highest sequence number
231  //authenticated so far
232  tl = sa->seqNum & 0xFFFFFFFF;
233  th = (sa->seqNum >> 32) & 0xFFFFFFFF;
234 
235  //Lower bound of window
236  bl = tl - IPSEC_ANTI_REPLAY_WINDOW_SIZE + 1;
237 
238  //When performing the anti-replay check, or when determining which
239  //high-order bits to use to authenticate an incoming packet, there are
240  //two cases
241  if(tl >= (IPSEC_ANTI_REPLAY_WINDOW_SIZE - 1))
242  {
243  //In this case, the window is within one sequence number subspace
244  if(seql >= bl)
245  {
246  seqh = th;
247  }
248  else
249  {
250  seqh = th + 1;
251  }
252  }
253  else
254  {
255  //In this case, the window spans two sequence number subspaces
256  if(seql >= bl)
257  {
258  seqh = th - 1;
259  }
260  else
261  {
262  seqh = th;
263  }
264  }
265  }
266  else
267  {
268  //The sequence number is a 32-bit field
269  seqh = 0;
270  }
271 
272  //Reconstruct the 64-bit sequence number
273  return ((uint64_t) seqh << 32) | seql;
274 }
275 
276 #endif
#define ntohs(value)
Definition: cpu_endian.h:421
Debugging facilities.
error_t
Error codes.
Definition: error.h:43
@ ERROR_POLICY_FAILURE
Definition: error.h:298
@ ERROR_INVALID_HEADER
Definition: error.h:87
@ NO_ERROR
Success.
Definition: error.h:44
uint8_t data[]
Definition: ethernet.h:222
IcmpHeader
Definition: icmp.h:113
IPsec (IP security)
#define IPSEC_PORT_START_OPAQUE
Definition: ipsec.h:148
#define IpsecSadEntry
Definition: ipsec.h:36
#define IPSEC_ANTI_REPLAY_WINDOW_SIZE
Definition: ipsec.h:104
@ IPSEC_POLICY_ACTION_INVALID
Definition: ipsec.h:230
@ IPSEC_POLICY_ACTION_BYPASS
Definition: ipsec.h:232
#define IPSEC_ICMP_PORT(type, code)
Definition: ipsec.h:152
#define IPSEC_PORT_END_OPAQUE
Definition: ipsec.h:149
error_t ipsecGetInboundIpv4PacketSelector(const Ipv4Header *ipv4Header, uint8_t nextHeader, const NetBuffer *buffer, size_t offset, IpsecSelector *selector)
Extract packet's selector from inbound IPv4 packet.
uint64_t ipsecGetSeqNum(IpsecSadEntry *sa, uint32_t seql)
Determine the higher-order bits of the sequence number.
error_t ipsecProcessInboundIpv4Packet(NetInterface *interface, const Ipv4Header *ipv4Header, const NetBuffer *buffer, size_t offset)
Inbound IPv4 traffic processing.
Definition: ipsec_inbound.c:50
IPsec processing of inbound IP traffic.
IpsecSpdEntry * ipsecFindSpdEntry(IpsecContext *context, IpsecPolicyAction policyAction, const IpsecSelector *selector)
Search the SPD database for a matching entry.
Definition: ipsec_misc.c:51
Helper routines for IPsec.
@ IPV4_PROTOCOL_AH
Definition: ipv4.h:225
@ IPV4_PROTOCOL_ESP
Definition: ipv4.h:224
@ IPV4_PROTOCOL_UDP
Definition: ipv4.h:223
@ IPV4_PROTOCOL_ICMP
Definition: ipv4.h:220
@ IPV4_PROTOCOL_TCP
Definition: ipv4.h:222
#define Ipv4Header
Definition: ipv4.h:36
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:267
uint8_t nextHeader
Definition: ipv6.h:273
NetContext netContext
Definition: net.c:75
#define NetInterface
Definition: net.h:36
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:415
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
Ipv4Addr ipv4Addr
Definition: ip.h:84
size_t length
Definition: ip.h:80
IpAddr start
Definition: ipsec.h:281
IpAddr end
Definition: ipsec.h:282
uint16_t start
Definition: ipsec.h:292
uint16_t end
Definition: ipsec.h:293
IPsec selector.
Definition: ipsec.h:302
IpsecPortRange localPort
Local port range.
Definition: ipsec.h:306
IpsecAddrRange localIpAddr
Local IP address range.
Definition: ipsec.h:303
IpsecAddrRange remoteIpAddr
Remote IP address range.
Definition: ipsec.h:304
uint8_t nextProtocol
Next layer protocol.
Definition: ipsec.h:305
IpsecPortRange remotePort
Remote port range.
Definition: ipsec.h:307
Security Policy Database (SPD) entry.
Definition: ipsec.h:344
IpsecPolicyAction policyAction
Processing choice (DISCARD, BYPASS or PROTECT)
Definition: ipsec.h:345
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
void * ipsecContext
IPsec context.
Definition: net.h:329
uint8_t length
Definition: tcp.h:368
TcpHeader
Definition: tcp.h:358
UdpHeader
Definition: udp.h:85