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