1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** NetX Component */
16 /** */
17 /** Internet Protocol (IP) */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_SOURCE_CODE
23
24
25 /* Include necessary system files. */
26
27 #include "nx_api.h"
28 #include "nx_ip.h"
29 #include "nx_packet.h"
30
31 #ifdef NX_IPSEC_ENABLE
32 #include "nx_ipsec.h"
33 #endif /* NX_IPSEC_ENABLE */
34
35 #ifndef NX_DISABLE_IPV4
36 /**************************************************************************/
37 /* */
38 /* FUNCTION RELEASE */
39 /* */
40 /* _nx_ip_packet_send PORTABLE C */
41 /* 6.1.8 */
42 /* AUTHOR */
43 /* */
44 /* Yuxin Zhou, Microsoft Corporation */
45 /* */
46 /* DESCRIPTION */
47 /* */
48 /* This function prepends an IP header and sends an IP packet to the */
49 /* appropriate link driver. */
50 /* */
51 /* INPUT */
52 /* */
53 /* ip_ptr Pointer to IP control block */
54 /* packet_ptr Pointer to packet to send */
55 /* destination_ip Destination IP address */
56 /* type_of_service Type of service for packet */
57 /* time_to_live Time to live value for packet */
58 /* protocol Protocol being encapsulated */
59 /* fragment Don't fragment bit */
60 /* next_hop_address Next Hop address */
61 /* */
62 /* OUTPUT */
63 /* */
64 /* None */
65 /* */
66 /* CALLS */
67 /* */
68 /* _nx_ip_checksum_compute Compute IP checksum */
69 /* _nx_ip_header_add Add the IP header */
70 /* _nx_ip_route_find Find suitable outgoing */
71 /* _nx_ip_driver_packet_send Send the IP packet */
72 /* _nx_packet_transmit_release Release transmit packet */
73 /* */
74 /* CALLED BY */
75 /* */
76 /* NetX Source Code */
77 /* */
78 /* RELEASE HISTORY */
79 /* */
80 /* DATE NAME DESCRIPTION */
81 /* */
82 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
83 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
84 /* resulting in version 6.1 */
85 /* 08-02-2021 Yuxin Zhou Modified comment(s), and */
86 /* supported TCP/IP offload, */
87 /* added new ip filter, */
88 /* resulting in version 6.1.8 */
89 /* */
90 /**************************************************************************/
_nx_ip_packet_send(NX_IP * ip_ptr,NX_PACKET * packet_ptr,ULONG destination_ip,ULONG type_of_service,ULONG time_to_live,ULONG protocol,ULONG fragment,ULONG next_hop_address)91 VOID _nx_ip_packet_send(NX_IP *ip_ptr, NX_PACKET *packet_ptr,
92 ULONG destination_ip, ULONG type_of_service, ULONG time_to_live,
93 ULONG protocol, ULONG fragment, ULONG next_hop_address)
94 {
95
96 #ifdef NX_IPSEC_ENABLE
97 UINT status = 0;
98 ULONG payload_size;
99 USHORT value;
100 UCHAR is_hw_processed = NX_FALSE;
101 NX_IPV4_HEADER *ip_header_ptr;
102 ULONG checksum;
103 ULONG val;
104 #endif /* NX_IPSEC_ENABLE */
105
106
107 /* Add debug information. */
108 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
109
110 #ifndef NX_DISABLE_IP_INFO
111
112 /* Increment the total send requests counter. */
113 ip_ptr -> nx_ip_total_packet_send_requests++;
114 #endif
115
116 /* Make sure the packet interface is set. */
117 if (packet_ptr -> nx_packet_address.nx_packet_interface_ptr == NX_NULL)
118 {
119
120 #ifndef NX_DISABLE_IP_INFO
121
122 /* Increment the IP invalid packet error. */
123 ip_ptr -> nx_ip_invalid_transmit_packets++;
124 #endif /* !NX_DISABLE_IP_INFO */
125
126 /* Prepend the IP header to the packet. First, make room for the IP header. */
127 packet_ptr -> nx_packet_prepend_ptr = packet_ptr -> nx_packet_prepend_ptr - sizeof(NX_IPV4_HEADER);
128
129 /* Increase the packet length. */
130 packet_ptr -> nx_packet_length = packet_ptr -> nx_packet_length + (ULONG)sizeof(NX_IPV4_HEADER);
131
132 /* Release the packet. */
133 _nx_packet_transmit_release(packet_ptr);
134
135 /* Return... nothing more can be done! */
136 return;
137 }
138
139 #ifdef NX_ENABLE_TCPIP_OFFLOAD
140 if (packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_capability_flag &
141 NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD)
142 {
143 #ifndef NX_DISABLE_IP_INFO
144
145 /* Increment the IP invalid packet error. */
146 ip_ptr -> nx_ip_invalid_transmit_packets++;
147 #endif
148
149 /* Ignore sending all packets for TCP/IP offload. Release the packet. */
150 _nx_packet_transmit_release(packet_ptr);
151
152 /* Return... nothing more can be done! */
153 return;
154 }
155 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
156
157 #ifdef NX_IPSEC_ENABLE
158 /* Check if this packet is continued after HW crypto engine. */
159 if (packet_ptr -> nx_packet_ipsec_sa_ptr &&
160 ((NX_IPSEC_SA *)(packet_ptr -> nx_packet_ipsec_sa_ptr)) -> nx_ipsec_sa_mode == NX_IPSEC_TRANSPORT_MODE &&
161 (packet_ptr -> nx_packet_ipsec_state == NX_IPSEC_AH_PACKET ||
162 packet_ptr -> nx_packet_ipsec_state == NX_IPSEC_ESP_PACKET))
163 {
164 is_hw_processed = NX_TRUE;
165 }
166
167 /* Process this packet in IPsec transport mode? */
168 if (packet_ptr -> nx_packet_ipsec_sa_ptr &&
169 packet_ptr -> nx_packet_ipsec_state != NX_IPSEC_ESP_PACKET &&
170 packet_ptr -> nx_packet_ipsec_state != NX_IPSEC_AH_PACKET &&
171 ((NX_IPSEC_SA *)(packet_ptr -> nx_packet_ipsec_sa_ptr)) -> nx_ipsec_sa_mode == NX_IPSEC_TRANSPORT_MODE)
172 {
173
174 /* Perform IPSec processing and insert IPsec headers before the IP header. */
175 /* Notice that for TCP transmission, a new packet is used to store the encrypted data, while the
176 original TCP packet is put back on to the transmitted queue. */
177 payload_size = packet_ptr -> nx_packet_length;
178 status = _nx_ipsec_ip_output_packet_process(ip_ptr, &packet_ptr, (protocol >> 16), payload_size, &payload_size);
179
180 if ((status != NX_SUCCESS) &&
181 (status != NX_IPSEC_HW_PENDING))
182 {
183
184 /* Install an area for the IP header. This is required by the nx_packet_transmit_release. */
185 packet_ptr -> nx_packet_prepend_ptr = packet_ptr -> nx_packet_prepend_ptr - sizeof(NX_IPV4_HEADER);
186
187 /* Increase the packet length for the IP header. */
188 packet_ptr -> nx_packet_length = packet_ptr -> nx_packet_length + sizeof(NX_IPV4_HEADER);
189
190 /* IPsec output packet process failed. */
191
192 /* Release the packet. */
193 _nx_packet_transmit_release(packet_ptr);
194
195 return;
196 }
197
198 /* Now set the IPSec protocol. */
199 protocol = (ULONG)((((NX_IPSEC_SA *)packet_ptr -> nx_packet_ipsec_sa_ptr) -> nx_ipsec_sa_protocol) << 16);
200
201 /* Set the DF bit.
202 Transport mode SAs have been defined to not carry fragments (IPv4 or IPv6),RFC 4301 page 66&88..*/
203 fragment = NX_DONT_FRAGMENT;
204 }
205 #endif /* NX_IPSEC_ENABLE */
206
207
208 /* If the packet is processed by HW crypto engine, do not add IP header. */
209 #ifdef NX_IPSEC_ENABLE
210 if (!is_hw_processed)
211 #endif /* NX_IPSEC_ENABLE */
212 {
213
214 /* Add the IP Header to the packet. */
215 _nx_ip_header_add(ip_ptr, packet_ptr, packet_ptr -> nx_packet_ip_interface -> nx_interface_ip_address,
216 destination_ip, type_of_service, time_to_live, protocol, fragment);
217
218 #ifdef NX_ENABLE_IP_PACKET_FILTER
219 /* Check if the IP packet filter is set. */
220 if (ip_ptr -> nx_ip_packet_filter)
221 {
222
223 /* Yes, call the IP packet filter routine. */
224 if (ip_ptr -> nx_ip_packet_filter((VOID *)(packet_ptr -> nx_packet_prepend_ptr),
225 NX_IP_PACKET_OUT) != NX_SUCCESS)
226 {
227
228 /* Drop the packet. */
229 _nx_packet_transmit_release(packet_ptr);
230 return;
231 }
232 }
233
234 /* Check if the IP packet filter extended is set. */
235 if (ip_ptr -> nx_ip_packet_filter_extended)
236 {
237
238 /* Yes, call the IP packet filter extended routine. */
239 if (ip_ptr -> nx_ip_packet_filter_extended(ip_ptr, packet_ptr, NX_IP_PACKET_OUT) != NX_SUCCESS)
240 {
241
242 /* Drop the packet. */
243 _nx_packet_transmit_release(packet_ptr);
244 return;
245 }
246 }
247 #endif /* NX_ENABLE_IP_PACKET_FILTER */
248 }
249
250 /* If trace is enabled, insert this event into the trace buffer. */
251 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_IP_SEND, ip_ptr, destination_ip, packet_ptr, packet_ptr -> nx_packet_length, NX_TRACE_INTERNAL_EVENTS, 0, 0);
252
253
254 #ifdef NX_IPSEC_ENABLE
255
256 if (is_hw_processed)
257 {
258
259 /* Destination IP is unknow after HW crypto engine process. */
260 /* Get destination IP from IP header. */
261 /* Setup the IP header pointer. */
262 ip_header_ptr = (NX_IPV4_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
263
264 /* Fix payload size. */
265 value = (USHORT)(packet_ptr -> nx_packet_length);
266 NX_CHANGE_USHORT_ENDIAN(value);
267
268 /* First clear payload_size field. */
269 ip_header_ptr -> nx_ip_header_word_0 &= 0xFFFF;
270
271 /* Fill payload_size field. */
272 ip_header_ptr -> nx_ip_header_word_0 |= (ULONG)(value << NX_SHIFT_BY_16) & 0xFFFF0000;
273
274 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
275 if (!(packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_IPV4_TX_CHECKSUM))
276 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
277 {
278
279 /* Clear checksum in the IP header. */
280 ip_header_ptr -> nx_ip_header_word_2 = ip_header_ptr -> nx_ip_header_word_2 & 0xFFFF;
281
282 checksum = _nx_ip_checksum_compute(packet_ptr, NX_IP_VERSION_V4,
283 /* Length is the size of IP header, including options */
284 20,
285 /* IPv4 header checksum does not use src/dest addresses */
286 NULL, NULL);
287
288 val = (ULONG)(~checksum);
289 val = val & NX_LOWER_16_MASK;
290
291 /* Convert to network byte order. */
292 NX_CHANGE_ULONG_ENDIAN(val);
293
294 /* Now store the checksum in the IP header. */
295 ip_header_ptr -> nx_ip_header_word_2 = ip_header_ptr -> nx_ip_header_word_2 | val;
296 }
297 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
298 else
299 {
300 packet_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_IPV4_TX_CHECKSUM;
301 }
302 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
303 }
304
305 /* Process this packet in IPsec tunnel mode? */
306 if (packet_ptr -> nx_packet_ipsec_sa_ptr &&
307 packet_ptr -> nx_packet_ipsec_state != NX_IPSEC_ESP_PACKET &&
308 packet_ptr -> nx_packet_ipsec_state != NX_IPSEC_AH_PACKET &&
309 ((NX_IPSEC_SA *)(packet_ptr -> nx_packet_ipsec_sa_ptr)) -> nx_ipsec_sa_mode == NX_IPSEC_TUNNEL_MODE)
310 {
311
312
313 /* Perform IPSec processing for tunneling. Insert IPsec headers and encapsulating the IP header. */
314 payload_size = packet_ptr -> nx_packet_length;
315 status = _nx_ipsec_ip_output_packet_process(ip_ptr, &packet_ptr, NX_PROTOCOL_IPV4, payload_size, &payload_size);
316
317 if ((status != NX_SUCCESS) &&
318 (status != NX_IPSEC_HW_PENDING))
319 {
320 /* IPsec output packet process failed. */
321
322 /* Release the packet. */
323 _nx_packet_transmit_release(packet_ptr);
324 }
325
326 /* Tunnel consume the packet. */
327 return;
328 }
329
330 /* Process IPSec on packet requiring AH processing. */
331 if (packet_ptr -> nx_packet_ipsec_sa_ptr &&
332 packet_ptr -> nx_packet_ipsec_state == NX_IPSEC_AH_PACKET)
333 {
334
335 status = ip_ptr -> nx_ip_ipsec_authentication_header_transmit(ip_ptr, &packet_ptr, protocol, 1);
336
337 if ((status != NX_SUCCESS) &&
338 (status != NX_IPSEC_HW_PENDING))
339 {
340 /* Release the packet. */
341 _nx_packet_transmit_release(packet_ptr);
342
343 return;
344 }
345 }
346
347 /* HW crypto driver is processing packet. */
348 if (status == NX_IPSEC_HW_PENDING)
349 {
350
351 #ifndef NX_DISABLE_IP_INFO
352
353 /* Decrement the total send requests counter. */
354 ip_ptr -> nx_ip_total_packet_send_requests--;
355 #endif
356 return;
357 }
358
359 #endif
360
361 /* If the next hop address is null, indicates the specified interface is unreached. */
362 if (next_hop_address == 0)
363 {
364
365 /* Check whether the forward feature is enabled. */
366 if (ip_ptr -> nx_ip_forward_packet_process)
367 {
368
369 /* Initialize the interface. */
370 packet_ptr -> nx_packet_address.nx_packet_interface_ptr = NX_NULL;
371
372 /* Figure out the best interface to send the packet on. */
373 _nx_ip_route_find(ip_ptr, destination_ip, &packet_ptr -> nx_packet_address.nx_packet_interface_ptr, &next_hop_address);
374 }
375
376 /* Make sure the packet interface and next hop address are set. */
377 /*lint -e{644} suppress variable might not be initialized, since "next_hop_address" was initialized in _nx_ip_route_find. */
378 if ((packet_ptr -> nx_packet_address.nx_packet_interface_ptr == NX_NULL) || (next_hop_address == 0))
379 {
380
381 #ifndef NX_DISABLE_IP_INFO
382
383 /* Increment the IP invalid packet error. */
384 ip_ptr -> nx_ip_invalid_transmit_packets++;
385 #endif /* !NX_DISABLE_IP_INFO */
386
387 /* Release the packet. */
388 _nx_packet_transmit_release(packet_ptr);
389
390 /* Return... nothing more can be done! */
391 return;
392 }
393 }
394
395 /* Directly send the packet. */
396 _nx_ip_driver_packet_send(ip_ptr, packet_ptr, destination_ip, fragment, next_hop_address);
397 }
398
399 #endif /* NX_DISABLE_IPV4 */
400
401