1 /**************************************************************************/
2 /* */
3 /* Copyright (c) Microsoft Corporation. All rights reserved. */
4 /* */
5 /* This software is licensed under the Microsoft Software License */
6 /* Terms for Microsoft Azure RTOS. Full text of the license can be */
7 /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
8 /* and in the root directory of this software. */
9 /* */
10 /**************************************************************************/
11
12
13 /**************************************************************************/
14 /**************************************************************************/
15 /** */
16 /** NetX Component */
17 /** */
18 /** Internet Group Management Protocol (IGMP) */
19 /** */
20 /**************************************************************************/
21 /**************************************************************************/
22
23 #define NX_SOURCE_CODE
24
25
26 /* Include necessary system files. */
27
28 #include "nx_api.h"
29 #include "nx_packet.h"
30 #include "nx_igmp.h"
31
32
33 #ifndef NX_DISABLE_IPV4
34 /**************************************************************************/
35 /* */
36 /* FUNCTION RELEASE */
37 /* */
38 /* _nx_igmp_packet_process PORTABLE C */
39 /* 6.1 */
40 /* AUTHOR */
41 /* */
42 /* Yuxin Zhou, Microsoft Corporation */
43 /* */
44 /* DESCRIPTION */
45 /* */
46 /* This function handles reception of IGMP packets. There are two types*/
47 /* of IGMP packets. Routers will send IGMP query messages and other */
48 /* send responses intended for the router but will be seen by all other */
49 /* hosts belonging to the same multicast group. In the latter case the */
50 /* other hosts will cancel their own response for the same multicast */
51 /* group. */
52 /* */
53 /* INPUT */
54 /* */
55 /* ip_ptr IP instance pointer */
56 /* packet_ptr IGMP packet pointer */
57 /* */
58 /* OUTPUT */
59 /* */
60 /* None */
61 /* */
62 /* CALLS */
63 /* */
64 /* _nx_packet_release Release IGMP packet */
65 /* tx_time_get Get current time */
66 /* */
67 /* CALLED BY */
68 /* */
69 /* _nx_igmp_packet_receive IGMP packet receive */
70 /* _nx_igmp_queue_process IGMP queue processing */
71 /* */
72 /* RELEASE HISTORY */
73 /* */
74 /* DATE NAME DESCRIPTION */
75 /* */
76 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
77 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
78 /* resulting in version 6.1 */
79 /* */
80 /**************************************************************************/
_nx_igmp_packet_process(NX_IP * ip_ptr,NX_PACKET * packet_ptr)81 VOID _nx_igmp_packet_process(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
82 {
83
84 UINT i;
85 ULONG update_time;
86 NX_IGMP_HEADER *header_ptr;
87 USHORT max_update_time;
88 ULONG checksum;
89 ULONG length;
90 UCHAR *word_ptr;
91 NX_PACKET *current_packet;
92 ULONG long_temp;
93 USHORT short_temp;
94
95
96 /* Add debug information. */
97 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
98
99 /* Setup a pointer to the IGMP packet header. */
100 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
101 header_ptr = (NX_IGMP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
102
103 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
104 if (!(packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_IGMP_RX_CHECKSUM))
105 #endif /* FEATURE_LINK_CAPABILITY */
106 {
107
108
109 /* First verify the checksum is correct. */
110
111 /* Setup the length of the packet checksum. */
112 length = packet_ptr -> nx_packet_length;
113
114 /* Determine if we need to add a padding byte. */
115 if (((length / sizeof(USHORT)) * sizeof(USHORT)) != length)
116 {
117
118 /* We have single byte alignment and we need two byte alignment. */
119 length++;
120
121 #ifndef NX_DISABLE_PACKET_CHAIN
122 /* Determine if there is a last packet pointer. */
123 if (packet_ptr -> nx_packet_last)
124 {
125
126 /* Multi-packet message, add a zero byte at the end. */
127 *((packet_ptr -> nx_packet_last) -> nx_packet_append_ptr) = 0;
128 }
129 else
130 {
131 #endif
132
133 /* Write a zero byte at the end of the first and only packet. */
134 *(packet_ptr -> nx_packet_append_ptr) = 0;
135 #ifndef NX_DISABLE_PACKET_CHAIN
136 }
137 #endif
138 }
139
140 /* Setup the pointer to the start of the packet. */
141 word_ptr = (UCHAR *)packet_ptr -> nx_packet_prepend_ptr;
142
143 /* Initialize the current packet to the input packet pointer. */
144 current_packet = packet_ptr;
145
146 checksum = 0;
147
148
149 /* Loop to calculate the checksum over the entire packet. */
150 while (length)
151 {
152 /* Determine if there is at least one ULONG left. */
153 if ((UINT)(current_packet -> nx_packet_append_ptr - word_ptr) >= sizeof(ULONG))
154 {
155
156 /* Pickup a whole ULONG. */
157 long_temp = *((ULONG *)word_ptr);
158
159 /* Add upper 16-bits into checksum. */
160 checksum = checksum + (long_temp >> NX_SHIFT_BY_16);
161
162 /* Check for carry bits. */
163 if (checksum & NX_CARRY_BIT)
164 {
165 checksum = (checksum & NX_LOWER_16_MASK) + 1;
166 }
167
168 /* Add lower 16-bits into checksum. */
169 checksum = checksum + (long_temp & NX_LOWER_16_MASK);
170
171 /* Check for carry bits. */
172
173 if (checksum & NX_CARRY_BIT)
174 {
175 checksum = (checksum & NX_LOWER_16_MASK) + 1;
176 }
177
178 /* Move the word pointer and decrease the length. */
179 word_ptr = word_ptr + sizeof(ULONG);
180 length = length - (ULONG)sizeof(ULONG);
181 }
182 else
183 {
184
185 /* Pickup the 16-bit word. */
186 short_temp = *((USHORT *)word_ptr);
187
188 /* Add next 16-bit word into checksum. */
189 checksum = checksum + short_temp;
190
191 /* Check for carry bits. */
192 if (checksum & NX_CARRY_BIT)
193 {
194 checksum = (checksum & NX_LOWER_16_MASK) + 1;
195 }
196
197 /* Move the word pointer and decrease the length. */
198 word_ptr = word_ptr + sizeof(USHORT);
199 length = length - (ULONG)sizeof(USHORT);
200 }
201
202 #ifndef NX_DISABLE_PACKET_CHAIN
203 /* Determine if we are at the end of the current packet. */
204 if ((word_ptr >= (UCHAR *)current_packet -> nx_packet_append_ptr) &&
205 (current_packet -> nx_packet_next))
206 {
207
208 /* We have crossed the packet boundary. Move to the next packet
209 structure. */
210 current_packet = current_packet -> nx_packet_next;
211
212 /* Setup the new word pointer. */
213 word_ptr = (UCHAR *)current_packet -> nx_packet_prepend_ptr;
214 }
215 #endif
216 }
217
218 checksum = ~checksum & NX_LOWER_16_MASK;
219
220 /* Determine if the checksum is valid. */
221 if (checksum)
222 {
223
224 /* It is not. By RFC requirements we should not accept this packet. */
225
226 #ifndef NX_DISABLE_IGMP_INFO
227 /* Increment the IGMP invalid packet error. */
228 ip_ptr -> nx_ip_igmp_invalid_packets++;
229
230 /* Increment the IGMP checksum error count. */
231 ip_ptr -> nx_ip_igmp_checksum_errors++;
232 #endif /* NX_DISABLE_IGMP_INFO */
233
234 /* Toss this IGMP packet out. */
235 _nx_packet_release(packet_ptr);
236 return;
237 }
238 }
239
240 /* Swap the IGMP headers to host byte order. */
241 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_igmp_header_word_0);
242 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_igmp_header_word_1);
243
244 /* If trace is enabled, insert this event into the trace buffer. */
245 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_IGMP_RECEIVE, ip_ptr, *(((ULONG *)packet_ptr -> nx_packet_prepend_ptr) - 2), packet_ptr, header_ptr -> nx_igmp_header_word_0, NX_TRACE_INTERNAL_EVENTS, 0, 0);
246
247 /* Determine the type of IGMP message received. Note that an IGMPv1 host will respond
248 to an IGMPv2 general query but not process the maximum response time field. */
249 if ((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_TYPE_MASK) == NX_IGMP_ROUTER_QUERY_TYPE)
250 {
251
252
253 #ifndef NX_DISABLE_IGMP_INFO
254 /* Increment the IGMP queries received count. */
255 ip_ptr -> nx_ip_igmp_queries_received++;
256 #endif
257
258 /* Set the max response time recommended by RFC 1112 set by host in seconds. In a
259 IGMPv2 network, the router may set a different max time in its IGMP membership queries. */
260 max_update_time = NX_IGMP_MAX_UPDATE_TIME;
261
262 #ifndef NX_DISABLE_IGMPV2
263
264 /* Determine the IGMP version the sender (router) is using. */
265
266 /* Is the max response time non zero? */
267
268
269 if ((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_MAX_RESP_TIME_MASK) != NX_NULL)
270 {
271 /* Yes, this must be an IGMPv2 router. */
272 ip_ptr -> nx_ip_igmp_router_version = NX_IGMP_HOST_VERSION_2;
273
274 /* Parse the max response time from the IGMP header. */
275 max_update_time = (USHORT)((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_MAX_RESP_TIME_MASK) >> 16) & 0x0000FF;
276
277 /* Convert from tenths of a second to seconds. */
278 max_update_time /= 10;
279 }
280 else
281 {
282 /* No; IGMPv1 requires setting this field to zero. */
283 ip_ptr -> nx_ip_igmp_router_version = NX_IGMP_HOST_VERSION_1;
284 }
285
286 #endif
287
288 /* Then generate a random update time initially in timer ticks for the delay. */
289 update_time = tx_time_get() & 0xF;
290
291 /* Check we have a valid non zero update time that does not exceed the
292 maximum response time. */
293 if ((update_time > max_update_time) || (update_time == 0))
294 {
295
296 /* If not, wrap the update time back to one second. */
297 update_time = 1;
298 }
299
300 /* Loop through the multicast join list and assign an arbitrary timeout to
301 respond between 1 and maximum response time for each group. */
302 for (i = 0; i < NX_MAX_MULTICAST_GROUPS; i++)
303 {
304
305 /* Is there a group address in this slot? */
306 if (ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_join_list == NX_NULL)
307 {
308
309 /* No, skip doing further processing. */
310 continue;
311 }
312
313 /* Does the group address in the header match our join list? */
314 if ((ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_join_list != header_ptr -> nx_igmp_header_word_1) &&
315 /* or is this a general membership query? */
316 (header_ptr -> nx_igmp_header_word_1 != NX_NULL))
317 {
318
319 /* No; so no need to update the timer, skip to the next group in the host list. */
320 continue;
321 }
322
323 /* Is the current host group running timer less than the max delay? */
324 if (((ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time < max_update_time) &&
325 (ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time != NX_NULL)) ||
326 (ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time == NX_WAIT_FOREVER))
327 {
328
329 /* Yes; Let the current timer timeout. Skip to the next group. */
330 continue;
331 }
332
333 /* Set the timeout for this multicast group. */
334 ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time = update_time;
335
336 /* Then increment the update time for the next host group so the update/expiration times
337 are separated by one second. This avoids bursts of IGMP reports to the server. */
338 update_time++;
339
340 /* Check after each multicast group that we have not exceeded the maximum response time. */
341 if (update_time > max_update_time)
342 {
343
344 /* We have, so wrap the update time back to one. */
345 update_time = 1;
346 }
347 }
348 }
349 #ifndef NX_DISABLE_IGMPV2
350
351 /* Is this another IGMPv1 host's join request? */
352 else if (((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_TYPE_MASK) == NX_IGMP_HOST_RESPONSE_TYPE) ||
353 /* ...Or an IGMPv2 host's join request? */
354 ((header_ptr -> nx_igmp_header_word_0 & NX_IGMPV2_TYPE_MASK) == NX_IGMP_HOST_V2_JOIN_TYPE))
355 #else
356
357 /* Is this another IGMPv1 host's join request? */
358 else if ((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_TYPE_MASK) == NX_IGMP_HOST_RESPONSE_TYPE)
359 #endif
360 {
361
362 /* Yes; Loop through the host multicast join list to find a matching group. */
363 for (i = 0; i < NX_MAX_MULTICAST_GROUPS; i++)
364 {
365
366 /* Compare the group address in the header with the host list. Is this a match? */
367 if ((ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_join_list == header_ptr -> nx_igmp_header_word_1) &&
368 (ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time != NX_WAIT_FOREVER))
369 {
370
371 /* Yes; Clear the update time. This will cancel sending a join
372 request for the same multicast group. */
373 ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time = 0;
374 break;
375 }
376 }
377 }
378
379 /* Release the IGMP packet. */
380 _nx_packet_release(packet_ptr);
381 }
382 #endif /* !NX_DISABLE_IPV4 */
383
384