ipsec_outbound.c
Go to the documentation of this file.
1 /**
2  * @file ipsec_outbound.c
3  * @brief IPsec processing of outbound IP traffic
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2022-2026 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.6.6
29  **/
30 
31 //Dependencies
32 #include "ipsec/ipsec.h"
33 #include "ipsec/ipsec_outbound.h"
34 #include "ipsec/ipsec_misc.h"
35 #include "ike/ike.h"
36 #include "ah/ah.h"
37 #include "esp/esp.h"
38 #include "debug.h"
39 
40 //Check IPsec library configuration
41 #if (IPSEC_SUPPORT == ENABLED)
42 
43 
44 /**
45  * @brief Outbound IPv4 traffic processing
46  * @param[in] interface Underlying network interface
47  * @param[in] pseudoHeader IPv4 pseudo header
48  * @param[in] fragId Fragment identification field
49  * @param[in] buffer Multi-part buffer containing the payload
50  * @param[in] offset Offset to the first byte of the payload
51  * @param[in] ancillary Additional options passed to the stack along with
52  * the packet
53  * @return Error code
54  **/
55 
57  const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, NetBuffer *buffer,
58  size_t offset, NetTxAncillary *ancillary)
59 {
60  error_t error;
61  IpsecContext *context;
62  IpsecSadEntry *sadEntry;
63  IpsecSpdEntry *spdEntry;
64  IpsecSelector selector;
65 
66  //Point to the IPsec context
67  context = interface->netContext->ipsecContext;
68 
69  //Extract packet's selector from the packet headers
70  error = ipsecGetOutboundIpv4PacketSelector(pseudoHeader, buffer, offset,
71  &selector);
72 
73  //Check status code
74  if(!error)
75  {
76  //Search the SPD for a matching entry
78  &selector, TRUE);
79 
80  //Any SPD entry found?
81  if(spdEntry != NULL)
82  {
83  //Check applicable SPD policies
85  {
86  //If the SPD entry calls for PROTECT, then search the SAD for an
87  //existing security association
88  sadEntry = ipsecFindOutboundSadEntry(context, &selector);
89 
90  //Any SAD entry found?
91  if(sadEntry != NULL)
92  {
93  //Protect the outbound packet using AH or ESP
94  error = ipsecProtectOutboundIpv4Packet(context, sadEntry,
95  interface, pseudoHeader, fragId, buffer, offset, ancillary);
96  }
97  else
98  {
99  IpsecPacketInfo packetInfo;
100 
101  //The key management mechanism is invoked to create the SA
102  packetInfo.localIpAddr.length = sizeof(Ipv4Addr);
103  packetInfo.localIpAddr.ipv4Addr = pseudoHeader->srcAddr;
104  packetInfo.remoteIpAddr.length = sizeof(Ipv4Addr);
105  packetInfo.remoteIpAddr.ipv4Addr = pseudoHeader->destAddr;
106  packetInfo.nextProtocol = pseudoHeader->protocol;
107  packetInfo.localPort = selector.localPort.start;
108  packetInfo.remotePort = selector.remotePort.start;
109 
110  //Create a new SA
111  ikeCreateChildSa(interface->netContext->ikeContext, &packetInfo);
112 
113  //There is no requirement that an implementation buffer the packet
114  //if there is a cache miss (refer to RFC 4301, section 5.2)
115  error = ERROR_IN_PROGRESS;
116  }
117  }
118  else if(spdEntry->policyAction == IPSEC_POLICY_ACTION_BYPASS)
119  {
120  //If the SPD entry calls for BYPASS, then the packet is not protected
121  error = ipsecSendIpv4Packet(interface, pseudoHeader, fragId,
122  buffer, offset, ancillary);
123  }
124  else
125  {
126  //If the SPD entry calls for DISCARD, then drop the packet
127  error = ERROR_POLICY_FAILURE;
128  }
129  }
130  else
131  {
132  //If there is no match, discard the traffic
133  error = ERROR_POLICY_FAILURE;
134  }
135  }
136 
137  //Return status code
138  return error;
139 }
140 
141 
142 /**
143  * @brief Extract packet's selector from outbound IPv4 packet
144  * @param[in] pseudoHeader IPv4 pseudo header
145  * @param[in] buffer Multi-part buffer containing the IP payload
146  * @param[in] offset Offset from the beginning of the buffer
147  * @param[out] selector Pointer to the IPsec selector
148  * @return Error code
149  **/
150 
152  const NetBuffer *buffer, size_t offset, IpsecSelector *selector)
153 {
154  error_t error;
155  size_t length;
156  const uint8_t *data;
157 
158  //Initialize status code
159  error = NO_ERROR;
160 
161  //Local IP address range
162  selector->localIpAddr.start.length = sizeof(Ipv4Addr);
163  selector->localIpAddr.start.ipv4Addr = pseudoHeader->srcAddr;
164  selector->localIpAddr.end.length = sizeof(Ipv4Addr);
165  selector->localIpAddr.end.ipv4Addr = pseudoHeader->srcAddr;
166 
167  //Remote IP address range
168  selector->remoteIpAddr.start.length = sizeof(Ipv4Addr);
169  selector->remoteIpAddr.start.ipv4Addr = pseudoHeader->destAddr;
170  selector->remoteIpAddr.end.length = sizeof(Ipv4Addr);
171  selector->remoteIpAddr.end.ipv4Addr = pseudoHeader->destAddr;
172 
173  //Next Layer Protocol value
174  selector->nextProtocol = pseudoHeader->protocol;
175 
176  //Retrieve the length of the data
177  length = netBufferGetLength(buffer) - offset;
178  //Point to the data
179  data = netBufferAt(buffer, offset, 0);
180 
181  //Sanity check
182  if(data != NULL)
183  {
184  //Several additional selectors depend on the Next Layer Protocol value
185  //(refer to RFC 4301, section 4.4.1.1)
186  if(pseudoHeader->protocol == IPV4_PROTOCOL_UDP &&
187  length >= sizeof(UdpHeader))
188  {
189  //Point to the UDP header
190  UdpHeader *udpHeader = (UdpHeader *) data;
191 
192  //If the Next Layer Protocol value is UDP, then there are selectors
193  //for local and remote ports
194  selector->localPort.start = ntohs(udpHeader->srcPort);
195  selector->localPort.end = ntohs(udpHeader->srcPort);
196  selector->remotePort.start = ntohs(udpHeader->destPort);
197  selector->remotePort.end = ntohs(udpHeader->destPort);
198  }
199  else if(pseudoHeader->protocol == IPV4_PROTOCOL_TCP &&
200  length >= sizeof(TcpHeader))
201  {
202  //Point to the TCP header
203  TcpHeader *tcpHeader = (TcpHeader *) data;
204 
205  //If the Next Layer Protocol value is TCP, then there are selectors
206  //for local and remote ports
207  selector->localPort.start = ntohs(tcpHeader->srcPort);
208  selector->localPort.end = ntohs(tcpHeader->srcPort);
209  selector->remotePort.start = ntohs(tcpHeader->destPort);
210  selector->remotePort.end = ntohs(tcpHeader->destPort);
211  }
212  else if(pseudoHeader->protocol == IPV4_PROTOCOL_ICMP &&
213  length >= sizeof(IcmpHeader))
214  {
215  //Point to the ICMP header
216  IcmpHeader *icmpHeader = (IcmpHeader *) data;
217 
218  //If the Next Layer Protocol value is ICMP, then there is a 16-bit
219  //selector for the ICMP message type and code
220  selector->localPort.start = IPSEC_ICMP_PORT(icmpHeader->type, icmpHeader->code);
221  selector->localPort.end = IPSEC_ICMP_PORT(icmpHeader->type, icmpHeader->code);
224  }
225  else
226  {
227  //The local and remote port selectors may be labeled as OPAQUE to
228  //accommodate situations where these fields are inaccessible
230  selector->localPort.end = IPSEC_PORT_END_OPAQUE;
233  }
234  }
235  else
236  {
237  //Report an error
238  error = ERROR_INVALID_HEADER;
239  }
240 
241  //Return status code
242  return error;
243 }
244 
245 
246 /**
247  * @brief Protect an outbound IPv4 packet using AH or ESP
248  * @param[in] context Pointer to the IPsec context
249  * @param[in] sa Pointer to the security association
250  * @param[in] interface Underlying network interface
251  * @param[in] pseudoHeader IPv4 pseudo header
252  * @param[in] fragId Fragment identification field
253  * @param[in] buffer Multi-part buffer containing the payload
254  * @param[in] offset Offset to the first byte of the payload
255  * @param[in] ancillary Additional options passed to the stack along with
256  * the packet
257  * @return Error code
258  **/
259 
261  NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader,
262  uint16_t fragId, NetBuffer *buffer, size_t offset,
263  NetTxAncillary *ancillary)
264 {
265  error_t error;
266 
267  //Check the state of the SAD entry
268  if(sa->state == IPSEC_SA_STATE_OPEN)
269  {
270 #if (AH_SUPPORT == ENABLED)
271  //AH protocol?
272  if(sa->protocol == IPSEC_PROTOCOL_AH)
273  {
274  //Protect the IPv4 packet using AH
275  error = ahProtectOutboundIpv4Packet(context, sa, interface,
276  pseudoHeader, fragId, buffer, offset, ancillary);
277  }
278  else
279 #endif
280 #if (ESP_SUPPORT == ENABLED)
281  //ESP protocol?
282  if(sa->protocol == IPSEC_PROTOCOL_ESP)
283  {
284  //Protect the IPv4 packet using ESP
285  error = espProtectOutboundIpv4Packet(context, sa, interface,
286  pseudoHeader, fragId, buffer, offset, ancillary);
287  }
288  else
289 #endif
290  //Invalid IPsec protocol?
291  {
292  //Report an error
293  error = ERROR_INVALID_PROTOCOL;
294  }
295  }
296  else
297  {
298  //The establishment of the SA pair is in progress
299  error = ERROR_IN_PROGRESS;
300  }
301 
302  //Return status code
303  return error;
304 }
305 
306 
307 /**
308  * @brief Send an IPv4 packet
309  * @param[in] interface Underlying network interface
310  * @param[in] pseudoHeader IPv4 pseudo header
311  * @param[in] fragId Fragment identification field
312  * @param[in] buffer Multi-part buffer containing the payload
313  * @param[in] offset Offset to the first byte of the payload
314  * @param[in] ancillary Additional options passed to the stack along with
315  * the packet
316  * @return Error code
317  **/
318 
320  const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, NetBuffer *buffer,
321  size_t offset, NetTxAncillary *ancillary)
322 {
323  error_t error;
324  size_t length;
325 
326  //Retrieve the length of payload
327  length = netBufferGetLength(buffer) - offset;
328 
329  //Check the length of the payload
330  if((length + sizeof(Ipv4Header)) <= interface->ipv4Context.linkMtu)
331  {
332  //If the payload length is smaller than the network interface MTU
333  //then no fragmentation is needed
334  error = ipv4SendPacket(interface, pseudoHeader, fragId, 0, buffer,
335  offset, ancillary);
336  }
337  else
338  {
339 #if (IPV4_FRAG_SUPPORT == ENABLED)
340  //An IP datagram can be marked "don't fragment". Any IP datagram so
341  //marked is not to be fragmented under any circumstances (refer to
342  //RFC791, section 2.3)
343  if(!ancillary->dontFrag)
344  {
345  //If the payload length exceeds the network interface MTU then the
346  //device must fragment the data
347  error = ipv4FragmentDatagram(interface, pseudoHeader, fragId, buffer,
348  offset, ancillary);
349  }
350  else
351 #endif
352  {
353  //If IP datagram cannot be delivered to its destination without
354  //fragmenting it, it is to be discarded instead
355  error = ERROR_MESSAGE_TOO_LONG;
356  }
357  }
358 
359  //Return status code
360  return error;
361 }
362 
363 #endif
#define Ipv4Header
Definition: ipv4.h:36
@ IPV4_PROTOCOL_ICMP
Definition: ipv4.h:276
uint16_t end
Definition: ipsec.h:300
error_t ikeCreateChildSa(IkeContext *context, const IpsecPacketInfo *packet)
Create a new Child SA.
Definition: ike.c:776
@ IPSEC_PROTOCOL_AH
Definition: ipsec.h:199
IPsec selector.
Definition: ipsec.h:309
error_t espProtectOutboundIpv4Packet(IpsecContext *context, IpsecSadEntry *sa, NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Protect an outbound IPv4 packet using ESP.
Definition: esp.c:66
IPsec processing of outbound IP traffic.
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
#define IPSEC_PORT_START_OPAQUE
Definition: ipsec.h:155
#define TRUE
Definition: os_port.h:50
IpAddr remoteIpAddr
Remote IP address.
Definition: ipsec.h:325
uint8_t data[]
Definition: ethernet.h:224
@ ERROR_INVALID_HEADER
Definition: error.h:87
IpAddr end
Definition: ipsec.h:289
#define IPSEC_ICMP_PORT(type, code)
Definition: ipsec.h:159
error_t ipv4FragmentDatagram(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, uint16_t id, const NetBuffer *payload, size_t payloadOffset, NetTxAncillary *ancillary)
Fragment an IPv4 datagram into smaller packets.
Definition: ipv4_frag.c:67
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:324
@ IPSEC_POLICY_ACTION_PROTECT
Definition: ipsec.h:240
@ IPSEC_PROTOCOL_ESP
Definition: ipsec.h:200
IcmpHeader
Definition: icmp.h:149
uint16_t remotePort
Remote port.
Definition: ipsec.h:328
@ IPV4_PROTOCOL_TCP
Definition: ipv4.h:278
ESP (IP Encapsulating Security Payload)
@ ERROR_IN_PROGRESS
Definition: error.h:214
error_t ipsecProtectOutboundIpv4Packet(IpsecContext *context, IpsecSadEntry *sa, NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Protect an outbound IPv4 packet using AH or ESP.
uint8_t nextProtocol
Next layer protocol.
Definition: ipsec.h:326
error_t ipsecGetOutboundIpv4PacketSelector(const Ipv4PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, IpsecSelector *selector)
Extract packet's selector from outbound IPv4 packet.
uint8_t nextProtocol
Next layer protocol.
Definition: ipsec.h:312
uint16_t localPort
Local port.
Definition: ipsec.h:327
error_t
Error codes.
Definition: error.h:43
IpsecPolicyAction policyAction
Processing choice (DISCARD, BYPASS or PROTECT)
Definition: ipsec.h:352
error_t ipsecSendIpv4Packet(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send an IPv4 packet.
IpsecPortRange remotePort
Remote port range.
Definition: ipsec.h:314
IpsecSadEntry * ipsecFindOutboundSadEntry(IpsecContext *context, const IpsecSelector *selector)
Search the SAD database for a matching outbound entry.
Definition: ipsec_misc.c:185
error_t ipv4SendPacket(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, size_t fragOffset, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send an IPv4 packet.
Definition: ipv4.c:1152
Helper routines for IPsec.
#define NetInterface
Definition: net.h:40
#define NetTxAncillary
Definition: net_misc.h:36
#define Ipv4PseudoHeader
Definition: ipv4.h:40
uint8_t length
Definition: tcp.h:375
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
@ ERROR_MESSAGE_TOO_LONG
Definition: error.h:137
@ ERROR_INVALID_PROTOCOL
Definition: error.h:101
size_t length
Definition: ip.h:95
AH (IP Authentication Header)
IpsecSpdEntry * ipsecFindSpdEntry(IpsecContext *context, IpsecPolicyAction policyAction, const IpsecSelector *selector, bool_t subset)
Search the SPD database for a matching entry.
Definition: ipsec_misc.c:52
IpAddr start
Definition: ipsec.h:288
#define IpsecSadEntry
Definition: ipsec.h:36
uint16_t start
Definition: ipsec.h:299
UdpHeader
Definition: udp.h:85
IKEv2 (Internet Key Exchange Protocol)
#define ntohs(value)
Definition: cpu_endian.h:421
#define IPSEC_PORT_END_OPAQUE
Definition: ipsec.h:156
Ipv4Addr ipv4Addr
Definition: ip.h:99
IPsec (IP security)
@ IPV4_PROTOCOL_UDP
Definition: ipv4.h:279
error_t ipsecProcessOutboundIpv4Packet(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Outbound IPv4 traffic processing.
IP packet information.
Definition: ipsec.h:323
IpsecAddrRange localIpAddr
Local IP address range.
Definition: ipsec.h:310
@ IPSEC_POLICY_ACTION_INVALID
Definition: ipsec.h:237
IpAddr localIpAddr
Local IP address.
Definition: ipsec.h:324
#define IpsecContext
Definition: ipsec.h:40
@ IPSEC_POLICY_ACTION_BYPASS
Definition: ipsec.h:239
void * netBufferAt(const NetBuffer *buffer, size_t offset, size_t length)
Returns a pointer to a data segment.
Definition: net_mem.c:418
error_t ahProtectOutboundIpv4Packet(IpsecContext *context, IpsecSadEntry *sa, NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, uint16_t fragId, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Protect an outbound IPv4 packet using AH.
Definition: ah.c:64
Security Policy Database (SPD) entry.
Definition: ipsec.h:351
TcpHeader
Definition: tcp.h:365
@ IPSEC_SA_STATE_OPEN
Definition: ipsec.h:278
IpsecPortRange localPort
Local port range.
Definition: ipsec.h:313
@ ERROR_POLICY_FAILURE
Definition: error.h:300
IpsecAddrRange remoteIpAddr
Remote IP address range.
Definition: ipsec.h:311
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.