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 #include "nx_ipv6.h"
31 #include "nx_icmpv6.h"
32
33 #ifdef FEATURE_NX_IPV6
34
35
36
37 /**************************************************************************/
38 /* */
39 /* FUNCTION RELEASE */
40 /* */
41 /* _nx_ipv6_packet_receive PORTABLE C */
42 /* 6.1 */
43 /* AUTHOR */
44 /* */
45 /* Yuxin Zhou, Microsoft Corporation */
46 /* */
47 /* DESCRIPTION */
48 /* */
49 /* This function receives IPv6 packets from the nx_ip_packet_receive. */
50 /* The packet is either processed here or placed it in a deferred */
51 /* processing queue, depending on the complexity of the packet. */
52 /* */
53 /* INPUT */
54 /* */
55 /* ip_ptr Pointer to IP control block */
56 /* packet_ptr Pointer to packet received */
57 /* */
58 /* OUTPUT */
59 /* */
60 /* None */
61 /* */
62 /* CALLS */
63 /* */
64 /* _nx_packet_release Packet release function */
65 /* _nx_ip_dispatch_process The routine that examines */
66 /* other optional headers and */
67 /* upper layer protocols. */
68 /* */
69 /* CALLED BY */
70 /* */
71 /* Application I/O Driver */
72 /* */
73 /* RELEASE HISTORY */
74 /* */
75 /* DATE NAME DESCRIPTION */
76 /* */
77 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
78 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
79 /* resulting in version 6.1 */
80 /* */
81 /**************************************************************************/
_nx_ipv6_packet_receive(NX_IP * ip_ptr,NX_PACKET * packet_ptr)82 VOID _nx_ipv6_packet_receive(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
83 {
84
85 UINT error;
86 ULONG delta;
87 UINT pkt_length;
88 UCHAR next_header_type;
89 NX_IPV6_HEADER *ip_header_ptr;
90 NXD_IPV6_ADDRESS *interface_ipv6_address_next;
91 NXD_IPV6_ADDRESS *incoming_address = NX_NULL;
92 #ifndef NX_DISABLE_PACKET_CHAIN
93 NX_PACKET *before_last_packet;
94 NX_PACKET *last_packet;
95 #endif /* NX_DISABLE_PACKET_CHAIN */
96
97 #ifdef NX_ENABLE_IPV6_MULTICAST
98 INT i = 0;
99 #endif /* NX_ENABLE_IPV6_MULTICAST */
100
101
102 /* Add debug information. */
103 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
104
105 /* Points to the base of IPv6 header. */
106 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
107 ip_header_ptr = (NX_IPV6_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
108
109 /* Byte swap WORD 1 to obtain IPv6 payload length. */
110 NX_CHANGE_ULONG_ENDIAN(ip_header_ptr -> nx_ip_header_word_1);
111
112 pkt_length = (UINT)((ip_header_ptr -> nx_ip_header_word_1 >> 16) + sizeof(NX_IPV6_HEADER));
113
114 /* Make sure the packet length field matches the payload length field in the IPv6 header. */
115 if (packet_ptr -> nx_packet_length != (ULONG)pkt_length)
116 {
117
118 /* Determine if the packet length is less than the size reported in the IP header. */
119 if (packet_ptr -> nx_packet_length < (ULONG)pkt_length)
120 {
121
122 /* The incoming packet has a wrong payload size. */
123 #ifndef NX_DISABLE_IP_INFO
124
125 /* Increment the IP invalid packet error. */
126 ip_ptr -> nx_ip_invalid_packets++;
127
128 /* Increment the IP receive packets dropped count. */
129 ip_ptr -> nx_ip_receive_packets_dropped++;
130 #endif
131
132 /* Release the packet! */
133 _nx_packet_release(packet_ptr);
134
135 /* In all cases, receive processing is finished. Return to caller. */
136 return;
137 }
138
139 /* Calculate the difference in the length. */
140 delta = packet_ptr -> nx_packet_length - pkt_length;
141
142 /* Adjust the packet length. */
143 packet_ptr -> nx_packet_length = packet_ptr -> nx_packet_length - delta;
144
145 /* Adjust the append pointer. */
146
147 #ifndef NX_DISABLE_PACKET_CHAIN
148 /* Loop to process adjustment that spans multiple packets. */
149 while (delta)
150 {
151
152 /* Determine if the packet is chained (or still chained after the adjustment). */
153 if (packet_ptr -> nx_packet_last == NX_NULL)
154 {
155
156 /* No, packet is not chained, simply adjust the append pointer in the packet. */
157 packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_append_ptr - delta;
158
159 /* Break out of the loop, since the adjustment is complete. */
160 break;
161 }
162
163 /* Pickup the pointer to the last packet. */
164 last_packet = packet_ptr -> nx_packet_last;
165
166 /* Determine if the amount to adjust is less than the payload in the last packet. */
167 /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
168 if (((ULONG)(last_packet -> nx_packet_append_ptr - last_packet -> nx_packet_prepend_ptr)) > delta)
169 {
170
171 /* Yes, simply adjust the append pointer of the last packet in the chain. */
172 /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
173 last_packet -> nx_packet_append_ptr = last_packet -> nx_packet_append_ptr - delta;
174
175 /* Get out of the loop, since the adjustment is complete. */
176 break;
177 }
178 else
179 {
180
181 /* Adjust the delta by the amount in the last packet. */
182 delta = delta - ((ULONG)(last_packet -> nx_packet_append_ptr - last_packet -> nx_packet_prepend_ptr));
183
184 /* Find the packet before the last packet. */
185 before_last_packet = packet_ptr;
186 while (before_last_packet -> nx_packet_next != last_packet)
187 {
188
189 /* Move to the next packet in the chain. */
190 before_last_packet = before_last_packet -> nx_packet_next;
191 }
192
193 /* At this point, we need to release the last packet and adjust the other packet
194 pointers. */
195
196 /* Ensure the next packet pointer is NULL in what is now the last packet. */
197 before_last_packet -> nx_packet_next = NX_NULL;
198
199 /* Determine if the packet is still chained. */
200 if (packet_ptr != before_last_packet)
201 {
202
203 /* Yes, the packet is still chained, setup the last packet pointer. */
204 packet_ptr -> nx_packet_last = before_last_packet;
205 }
206 else
207 {
208
209 /* The packet is no longer chained, set the last packet pointer to NULL. */
210 packet_ptr -> nx_packet_last = NX_NULL;
211 }
212
213 /* Release the last packet. */
214 _nx_packet_release(last_packet);
215 }
216 }
217 #else
218
219 /* Simply adjust the append pointer in the packet. */
220 packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_append_ptr - delta;
221 #endif /* NX_DISABLE_PACKET_CHAIN */
222 }
223
224 /* Byte swap the rest of the IPv6 header fields. */
225 NX_CHANGE_ULONG_ENDIAN(ip_header_ptr -> nx_ip_header_word_0);
226 NX_IPV6_ADDRESS_CHANGE_ENDIAN(ip_header_ptr -> nx_ip_header_destination_ip);
227 NX_IPV6_ADDRESS_CHANGE_ENDIAN(ip_header_ptr -> nx_ip_header_source_ip);
228
229 /* Get a pointer to the first address in the address list for this interface (e.g.
230 the interface the packet was received on). */
231 interface_ipv6_address_next = packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nxd_interface_ipv6_address_list_head;
232
233 /* Check if this packet is intended for this host by looping through all the addresses in the IP interface table for a match. */
234 while (interface_ipv6_address_next)
235 {
236
237 /* Ignore invalid addresses. */
238 if (interface_ipv6_address_next -> nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_UNKNOWN)
239 {
240
241 /* Does the incoming packet match one of the IP interfaces? */
242 if (CHECK_IPV6_ADDRESSES_SAME(ip_header_ptr -> nx_ip_header_destination_ip,
243 interface_ipv6_address_next -> nxd_ipv6_address))
244 {
245
246 /* Yes, we found a match! */
247 incoming_address = interface_ipv6_address_next;
248
249 break;
250 }
251 /* Check for multicast address. */
252 else if (CHECK_IPV6_SOLICITED_NODE_MCAST_ADDRESS(ip_header_ptr -> nx_ip_header_destination_ip,
253 interface_ipv6_address_next -> nxd_ipv6_address))
254 {
255
256 /* Yes, this is a multicast address. */
257 incoming_address = interface_ipv6_address_next;
258
259 break;
260 }
261 }
262
263 /* No match yet, get the next address. */
264 interface_ipv6_address_next = interface_ipv6_address_next -> nxd_ipv6_address_next;
265 }
266
267 #ifdef NX_ENABLE_IPV6_MULTICAST
268
269 if ((incoming_address == NX_NULL) && ((ip_header_ptr -> nx_ip_header_destination_ip[0] & 0xFF000000) == 0xFF000000))
270 {
271
272 /* Search the address whether match our multicast join list. */
273 for (i = 0; i < NX_MAX_MULTICAST_GROUPS; i++)
274 {
275
276 /* Match the destination address with the multicast list */
277 if ((ip_ptr -> nx_ipv6_multicast_entry[i].nx_ip_mld_join_interface_list == packet_ptr -> nx_packet_address.nx_packet_interface_ptr) &&
278 (CHECK_IPV6_ADDRESSES_SAME(ip_header_ptr -> nx_ip_header_destination_ip, (ip_ptr -> nx_ipv6_multicast_entry[i].nx_ip_mld_join_list))))
279 {
280 incoming_address = ip_ptr -> nx_ipv6_multicast_entry[i].nx_ip_mld_join_interface_list -> nxd_interface_ipv6_address_list_head;
281 break;
282 }
283 }
284 }
285 #endif /* NX_ENABLE_IPV6_MULTICAST */
286
287 /* Check for valid interface. */
288 if (incoming_address == NX_NULL)
289 {
290
291 /* The incoming packet has a destination address that does not match any of
292 the local interface addresses so its not for me. */
293
294 #ifndef NX_DISABLE_IP_INFO
295 /* Increment the IP receive packets dropped count. */
296 ip_ptr -> nx_ip_receive_packets_dropped++;
297 #endif
298
299 /* Release the packet. */
300 _nx_packet_release(packet_ptr);
301
302 /* In all cases, receive processing is finished. Return to caller. */
303 return;
304 }
305
306 /* Set the matching address to the packet address. */
307 packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr = incoming_address;
308
309 /*
310 Update the IP header pointer, packet length and packet prepend pointer
311 to point to the next header (either IP option header or
312 upper layer protocol header.
313 */
314
315 packet_ptr -> nx_packet_prepend_ptr += sizeof(NX_IPV6_HEADER);
316 packet_ptr -> nx_packet_length -= (ULONG)sizeof(NX_IPV6_HEADER);
317
318 packet_ptr -> nx_packet_option_offset = 6;
319 next_header_type = (UCHAR)((ip_header_ptr -> nx_ip_header_word_1 >> 8) & 0xFF);
320
321 /*
322 Search all the extension headers, terminate after upper layer protocols
323 (such as UDP, TCP, ICMP).
324
325 Also check the order of the optional headers. Once an out-of-order option
326 field is detected, the search is terminated and an ICMPv6 error message is
327 generated.
328 */
329
330 /* Initialize start of search for just passed the IP header. */
331 packet_ptr -> nx_packet_option_state = (UCHAR)IPV6_BASE_HEADER;
332 packet_ptr -> nx_packet_destination_header = 0;
333
334 #ifndef NX_DISABLE_IP_INFO
335
336 /* Increment the number of packets delivered. */
337 ip_ptr -> nx_ip_total_packets_delivered++;
338
339 /* Increment the IP packet bytes received (not including the header). */
340 ip_ptr -> nx_ip_total_bytes_received += packet_ptr -> nx_packet_length;
341 #endif
342
343 error = _nx_ip_dispatch_process(ip_ptr, packet_ptr, next_header_type);
344
345 if (error)
346 {
347 _nx_packet_release(packet_ptr);
348 }
349
350 return;
351 }
352
353
354 #endif /* FEATURE_NX_IPV6 */
355
356