esp.c
Go to the documentation of this file.
1 /**
2  * @file esp.c
3  * @brief ESP (IP Encapsulating Security Payload)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2022-2025 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.5.2
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL ESP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ipsec/ipsec.h"
36 #include "ipsec/ipsec_inbound.h"
38 #include "ipsec/ipsec_misc.h"
39 #include "esp/esp.h"
40 #include "esp/esp_packet_decrypt.h"
41 #include "core/tcp_fsm.h"
42 #include "core/raw_socket.h"
43 #include "ipv4/icmp.h"
44 #include "debug.h"
45 
46 //Check IPsec library configuration
47 #if (ESP_SUPPORT == ENABLED)
48 
49 
50 /**
51  * @brief Process ESP protected packet
52  * @param[in] interface Underlying network interface
53  * @param[in] ipv4Header Pointer to the IPv4 header
54  * @param[in] buffer Multi-part buffer containing the ESP protected packet
55  * @param[in] offset Offset to the first byte of the ESP header
56  * @param[in] ancillary Additional options passed to the stack along with
57  * the packet
58  * @return Error code
59  **/
60 
62  const Ipv4Header *ipv4Header, const NetBuffer *buffer, size_t offset,
63  NetRxAncillary *ancillary)
64 {
65  error_t error;
66  size_t length;
67  uint64_t seq;
68  uint8_t nextHeader;
69  size_t offset2;
70  NetBuffer *buffer2;
71  IpsecContext *context;
72  IpsecSadEntry *sa;
73  EspHeader *espHeader;
74  IpsecSelector selector;
75  IpPseudoHeader pseudoHeader;
76 
77  //Point to the IPsec context
78  context = netContext.ipsecContext;
79  //Sanity check
80  if(context == NULL)
81  return ERROR_FAILURE;
82 
83  //Retrieve the length of the payload
84  length = netBufferGetLength(buffer) - offset;
85 
86  //Malformed packet?
87  if(length < sizeof(EspHeader))
88  return ERROR_INVALID_HEADER;
89 
90  //Point to the ESP header
91  espHeader = netBufferAt(buffer, offset, 0);
92  //Sanity check
93  if(espHeader == NULL)
94  return ERROR_FAILURE;
95 
96  //Debug message
97  TRACE_INFO("Parsing ESP header...\r\n");
98  //Dump AH header contents for debugging purpose
99  espDumpHeader(espHeader);
100 
101  //Upon receipt of a packet containing an ESP Header, the receiver determines
102  //the appropriate (unidirectional) SA via lookup in the SAD (refer to
103  //RFC 4303, section 3.4.2)
105  ntohl(espHeader->spi));
106 
107  //If no valid Security Association exists for this packet the receiver
108  //must discard the packet. This is an auditable event
109  if(sa == NULL)
110  {
111  //Debug message
112  TRACE_WARNING("ESP: No matching SA found!\r\n");
113  //Report an error
114  return ERROR_POLICY_FAILURE;
115  }
116 
117  //Check IPsec mode
118  if(sa->mode == IPSEC_MODE_TRANSPORT)
119  {
120  //Transport mode ESP is applied only to whole IP datagrams (not to IP
121  //fragments)
122  if((ntohs(ipv4Header->fragmentOffset) & IPV4_OFFSET_MASK) != 0 ||
123  (ntohs(ipv4Header->fragmentOffset) & IPV4_FLAG_MF) != 0)
124  {
125  return ERROR_INVALID_HEADER;
126  }
127  }
128  else
129  {
130  //In tunnel mode, ESP is applied to an IP packet, which may be a fragment
131  //of an IP datagram
132  }
133 
134  //Because only the low-order 32 bits are transmitted with the packet, the
135  //receiver must deduce and track the sequence number subspace into which
136  //each packet falls
137  seq = ipsecGetSeqNum(sa, ntohl(espHeader->seqNum));
138 
139  //For each received packet, the receiver must verify that the packet
140  //contains a Sequence Number that does not duplicate the Sequence Number of
141  //any other packets received during the life of this SA. This should be the
142  //first ESP check applied to a packet after it has been matched to an SA, to
143  //speed rejection of duplicate packets (refer to RFC 4303, section 3.4.3)
144  error = ipsecCheckReplayWindow(sa, seq);
145 
146  //Duplicate packets are rejected
147  if(error)
148  {
149  //Debug message
150  TRACE_WARNING("ESP: Invalid sequence number!\r\n");
151  //Report an error
153  }
154 
155  //Point to the payload data
156  offset += sizeof(EspHeader);
157  length -= sizeof(EspHeader);
158 
159  //Check the length of the payload data
160  if(length > ESP_BUFFER_SIZE)
161  return ERROR_INVALID_LENGTH;
162 
163  //Copy the payload data to be decrypted
164  netBufferRead(context->buffer, buffer, offset, length);
165 
166  //if a separate integrity algorithm is employed, then the receiver proceeds
167  //to integrity verification, then decryption. If a combined mode algorithm
168  //is employed, the integrity check is performed along with decryption
169  error = espDecryptPacket(context, sa, espHeader, context->buffer, &length,
170  &nextHeader);
171 
172  //If the integrity check fails, the receiver must discard the received IP
173  //datagram as invalid. This is an auditable event
174  if(error)
175  {
176  //Debug message
177  TRACE_WARNING("ESP: ICV validation failed!\r\n");
178  //Report an error
180  }
181 
182  //The receive window is updated only if the ICV verification succeeds
183  ipsecUpdateReplayWindow(sa, seq);
184 
185  //Allocate a buffer to hold the decrypted payload
186  buffer2 = ipAllocBuffer(length, &offset2);
187  //Failed to allocate memory?
188  if(buffer2 == NULL)
189  return ERROR_OUT_OF_MEMORY;
190 
191  //Copy the resulting data
192  netBufferWrite(buffer2, offset2, context->buffer, length);
193 
194  //Retrieve packet's selector
195  error = ipsecGetInboundIpv4PacketSelector(ipv4Header, nextHeader, buffer2,
196  offset2, &selector);
197 
198  //Check status code
199  if(!error)
200  {
201  //Match the packet against the inbound selectors identified by the SAD
202  //entry to verify that the received packet is appropriate for the SA via
203  //which it was received (refer to RFC 4301, section 5.2)
204  if(ipsecIsSubsetSelector(&selector, &sa->selector))
205  {
206  //Form the IPv4 pseudo header
207  pseudoHeader.length = sizeof(Ipv4PseudoHeader);
208  pseudoHeader.ipv4Data.srcAddr = ipv4Header->srcAddr;
209  pseudoHeader.ipv4Data.destAddr = ipv4Header->destAddr;
210  pseudoHeader.ipv4Data.reserved = 0;
211  pseudoHeader.ipv4Data.protocol = nextHeader;
212  pseudoHeader.ipv4Data.length = htons(length);
213 
214  //If the computed and received ICVs match, then the datagram is valid,
215  //and it is accepted (refer to RFC 4303, section 3.4.4.1)
216  switch(nextHeader)
217  {
218  //ICMP protocol?
219  case IPV4_PROTOCOL_ICMP:
220  //Process incoming ICMP message
221  icmpProcessMessage(interface, &pseudoHeader.ipv4Data, buffer2,
222  offset2);
223 
224 #if (RAW_SOCKET_SUPPORT == ENABLED)
225  //Allow raw sockets to process ICMP messages
226  rawSocketProcessIpPacket(interface, &pseudoHeader, buffer2,
227  offset2, ancillary);
228 #endif
229  //Continue processing
230  break;
231 
232 #if (IGMP_HOST_SUPPORT == ENABLED || IGMP_ROUTER_SUPPORT == ENABLED || \
233  IGMP_SNOOPING_SUPPORT == ENABLED)
234  //IGMP protocol?
235  case IPV4_PROTOCOL_IGMP:
236  //Process incoming IGMP message
237  igmpProcessMessage(interface, &pseudoHeader.ipv4Data, buffer2,
238  offset2, ancillary);
239 
240 #if (RAW_SOCKET_SUPPORT == ENABLED)
241  //Allow raw sockets to process IGMP messages
242  rawSocketProcessIpPacket(interface, &pseudoHeader, buffer2,
243  offset2, ancillary);
244 #endif
245  //Continue processing
246  break;
247 #endif
248 
249 #if (TCP_SUPPORT == ENABLED)
250  //TCP protocol?
251  case IPV4_PROTOCOL_TCP:
252  //Process incoming TCP segment
253  tcpProcessSegment(interface, &pseudoHeader, buffer2, offset2,
254  ancillary);
255  //Continue processing
256  break;
257 #endif
258 
259 #if (UDP_SUPPORT == ENABLED)
260  //UDP protocol?
261  case IPV4_PROTOCOL_UDP:
262  //Process incoming UDP datagram
263  error = udpProcessDatagram(interface, &pseudoHeader, buffer2, offset2,
264  ancillary);
265  //Continue processing
266  break;
267 #endif
268 
269  //Unknown protocol?
270  default:
271 #if (RAW_SOCKET_SUPPORT == ENABLED)
272  //Allow raw sockets to process IPv4 packets
273  error = rawSocketProcessIpPacket(interface, &pseudoHeader, buffer2,
274  offset2, ancillary);
275 #else
276  //Report an error
278 #endif
279  //Continue processing
280  break;
281  }
282  }
283  else
284  {
285  //If an IPsec system receives an inbound packet on an SA and the
286  //packet's header fields are not consistent with the selectors for
287  //the SA, it must discard the packet. This is an auditable event
288  error = ERROR_POLICY_FAILURE;
289  }
290  }
291 
292  //Free previously allocated memory
293  netBufferFree(buffer2);
294 
295  //Return status code
296  return error;
297 }
298 
299 
300 /**
301  * @brief Dump ESP header for debugging purpose
302  * @param[in] espHeader Pointer to the ESP header
303  **/
304 
305 void espDumpHeader(const EspHeader *espHeader)
306 {
307  //Dump ESP header contents
308  TRACE_DEBUG(" SPI = 0x%08" PRIX32 "\r\n", ntohl(espHeader->spi));
309  TRACE_DEBUG(" Sequence Number = 0x%08" PRIX32 "\r\n", ntohl(espHeader->seqNum));
310 }
311 
312 #endif
#define htons(value)
Definition: cpu_endian.h:413
NetBuffer * ipAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold an IP packet.
Definition: ip.c:710
#define Ipv4Header
Definition: ipv4.h:36
@ IPV4_PROTOCOL_ICMP
Definition: ipv4.h:251
@ IPV4_OFFSET_MASK
Definition: ipv4.h:240
@ ERROR_WRONG_SEQUENCE_NUMBER
Definition: error.h:183
error_t rawSocketProcessIpPacket(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary)
Process incoming IP packet.
Definition: raw_socket.c:68
IPsec selector.
Definition: ipsec.h:302
size_t netBufferRead(void *dest, const NetBuffer *src, size_t srcOffset, size_t length)
Read data from a multi-part buffer.
Definition: net_mem.c:690
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
@ ERROR_INVALID_HEADER
Definition: error.h:87
ESP packet decryption.
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
NetContext netContext
Definition: net.c:75
error_t udpProcessDatagram(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary)
Incoming UDP datagram processing.
Definition: udp.c:124
#define ESP_BUFFER_SIZE
Definition: esp.h:277
size_t length
Definition: ip.h:111
error_t ipsecCheckReplayWindow(const IpsecSadEntry *sa, uint64_t seqNum)
Perform replay detection.
@ IPSEC_PROTOCOL_ESP
Definition: ipsec.h:193
uint64_t ipsecGetSeqNum(IpsecSadEntry *sa, uint32_t seql)
Determine the higher-order bits of the sequence number.
IP pseudo header.
Definition: ip.h:110
void icmpProcessMessage(NetInterface *interface, const Ipv4PseudoHeader *requestPseudoHeader, const NetBuffer *buffer, size_t offset)
Incoming ICMP message processing.
Definition: icmp.c:111
@ IPV4_PROTOCOL_TCP
Definition: ipv4.h:253
ESP (IP Encapsulating Security Payload)
ICMP (Internet Control Message Protocol)
@ IPSEC_MODE_TRANSPORT
Definition: ipsec.h:205
error_t
Error codes.
Definition: error.h:43
@ ERROR_PROTOCOL_UNREACHABLE
Definition: error.h:84
TCP finite state machine.
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
@ IPV4_FLAG_MF
Definition: ipv4.h:239
Helper routines for IPsec.
bool_t ipsecIsSubsetSelector(const IpsecSelector *selector1, const IpsecSelector *selector2)
Test if a selector is a subset of another selector.
Definition: ipsec_misc.c:362
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:36
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
@ ERROR_INVALID_LENGTH
Definition: error.h:111
uint8_t nextHeader
Definition: ipv6.h:282
IPsec context.
Definition: ipsec.h:434
#define Ipv4PseudoHeader
Definition: ipv4.h:39
#define TRACE_INFO(...)
Definition: debug.h:105
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
IPsec processing of inbound IP traffic.
IpsecSadEntry * ipsecFindInboundSadEntry(IpsecContext *context, IpsecProtocol protocol, uint32_t spi)
Search the SAD database for a matching inbound entry.
Definition: ipsec_misc.c:134
Anti-replay mechanism.
void * ipsecContext
IPsec context.
Definition: net.h:339
#define IpsecSadEntry
Definition: ipsec.h:36
TCP/IP raw sockets.
@ IPV4_PROTOCOL_IGMP
Definition: ipv4.h:252
EspHeader
Definition: esp.h:326
#define ntohs(value)
Definition: cpu_endian.h:421
#define TRACE_WARNING(...)
Definition: debug.h:93
#define TRACE_DEBUG(...)
Definition: debug.h:119
IPsec (IP security)
@ IPV4_PROTOCOL_UDP
Definition: ipv4.h:254
@ ERROR_AUTHENTICATION_FAILED
Definition: error.h:69
error_t ipv4ProcessEspHeader(NetInterface *interface, const Ipv4Header *ipv4Header, const NetBuffer *buffer, size_t offset, NetRxAncillary *ancillary)
Process ESP protected packet.
Definition: esp.c:61
error_t espDecryptPacket(IpsecContext *context, IpsecSadEntry *sa, const EspHeader *espHeader, uint8_t *payload, size_t *payloadLen, uint8_t *nextHeader)
Decrypt an incoming ESP packet.
size_t netBufferWrite(NetBuffer *dest, size_t destOffset, const void *src, size_t length)
Write data to a multi-part buffer.
Definition: net_mem.c:637
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 ipsecGetInboundIpv4PacketSelector(const Ipv4Header *ipv4Header, uint8_t nextHeader, const NetBuffer *buffer, size_t offset, IpsecSelector *selector)
Extract packet's selector from inbound IPv4 packet.
void tcpProcessSegment(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary)
Incoming TCP segment processing.
Definition: tcp_fsm.c:73
void ipsecUpdateReplayWindow(IpsecSadEntry *sa, uint64_t seqNum)
Update sliding window.
void espDumpHeader(const EspHeader *espHeader)
Dump ESP header for debugging purpose.
Definition: esp.c:305
@ ERROR_POLICY_FAILURE
Definition: error.h:300
#define ntohl(value)
Definition: cpu_endian.h:422
Ipv4PseudoHeader ipv4Data
Definition: ip.h:115
uint8_t buffer[ESP_BUFFER_SIZE]
Memory buffer for input/output operations.
Definition: ipsec.h:450
void igmpProcessMessage(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary)
Process incoming IGMP message.
Definition: igmp_common.c:293
Debugging facilities.