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 Group Management Protocol (IGMP)                           */
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_packet.h"
29 #include "nx_igmp.h"
30 #include "nx_ip.h"
31 
32 
33 #ifndef NX_DISABLE_IPV4
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _nx_igmp_packet_process                             PORTABLE C      */
39 /*                                                           6.3.0        */
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 /*  10-31-2023     Tiejun Zhou              Modified comment(s),          */
80 /*                                            unified checksum calculate, */
81 /*                                            resulting in version 6.3.0  */
82 /*                                                                        */
83 /**************************************************************************/
_nx_igmp_packet_process(NX_IP * ip_ptr,NX_PACKET * packet_ptr)84 VOID  _nx_igmp_packet_process(NX_IP *ip_ptr, NX_PACKET *packet_ptr)
85 {
86 
87 UINT            i;
88 ULONG           update_time;
89 NX_IGMP_HEADER *header_ptr;
90 USHORT          max_update_time;
91 ULONG           checksum;
92 
93 
94     /* Add debug information. */
95     NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
96 
97     /* Setup a pointer to the IGMP packet header.  */
98     /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary  */
99     header_ptr =  (NX_IGMP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
100 
101 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
102     if (!(packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_IGMP_RX_CHECKSUM))
103 #endif /* FEATURE_LINK_CAPABILITY */
104     {
105 
106 
107         /* First verify the checksum is correct. */
108         checksum =  _nx_ip_checksum_compute(packet_ptr, NX_IP_IGMP,
109                                             (UINT)packet_ptr -> nx_packet_length,
110                                             NX_NULL, NX_NULL);
111 
112         checksum = ~checksum & NX_LOWER_16_MASK;
113 
114         /* Determine if the checksum is valid.  */
115         if (checksum)
116         {
117 
118             /* It is not. By RFC requirements we should not accept this packet. */
119 
120 #ifndef NX_DISABLE_IGMP_INFO
121             /* Increment the IGMP invalid packet error.  */
122             ip_ptr -> nx_ip_igmp_invalid_packets++;
123 
124             /* Increment the IGMP checksum error count.  */
125             ip_ptr -> nx_ip_igmp_checksum_errors++;
126 #endif /* NX_DISABLE_IGMP_INFO */
127 
128             /* Toss this IGMP packet out.  */
129             _nx_packet_release(packet_ptr);
130             return;
131         }
132     }
133 
134     /* Swap the IGMP headers to host byte order. */
135     NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_igmp_header_word_0);
136     NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_igmp_header_word_1);
137 
138     /* If trace is enabled, insert this event into the trace buffer.  */
139     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);
140 
141     /* Determine the type of IGMP message received.  Note that an IGMPv1 host will respond
142        to an IGMPv2 general query but not process the maximum response time field. */
143     if ((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_TYPE_MASK) == NX_IGMP_ROUTER_QUERY_TYPE)
144     {
145 
146 
147 #ifndef NX_DISABLE_IGMP_INFO
148         /* Increment the IGMP queries received count.  */
149         ip_ptr -> nx_ip_igmp_queries_received++;
150 #endif
151 
152         /* Set the max response time recommended by RFC 1112 set by host in seconds. In a
153            IGMPv2 network, the router may set a different max time in its IGMP membership queries. */
154         max_update_time  = NX_IGMP_MAX_UPDATE_TIME;
155 
156 #ifndef NX_DISABLE_IGMPV2
157 
158         /* Determine the IGMP version the sender (router) is using.  */
159 
160         /* Is the max response time non zero? */
161 
162 
163         if ((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_MAX_RESP_TIME_MASK) != NX_NULL)
164         {
165             /* Yes, this must be an IGMPv2 router. */
166             ip_ptr -> nx_ip_igmp_router_version = NX_IGMP_HOST_VERSION_2;
167 
168             /* Parse the max response time from the IGMP header. */
169             max_update_time  = (USHORT)((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_MAX_RESP_TIME_MASK) >> 16) & 0x0000FF;
170 
171             /* Convert from tenths of a second to seconds. */
172             max_update_time /= 10;
173         }
174         else
175         {
176             /* No; IGMPv1 requires setting this field to zero. */
177             ip_ptr -> nx_ip_igmp_router_version = NX_IGMP_HOST_VERSION_1;
178         }
179 
180 #endif
181 
182         /* Then generate a random update time initially in timer ticks for the delay. */
183         update_time =  tx_time_get() & 0xF;
184 
185         /* Check we have a valid non zero update time that does not exceed the
186            maximum response time. */
187         if ((update_time > max_update_time) || (update_time == 0))
188         {
189 
190             /* If not, wrap the update time back to one second. */
191             update_time =  1;
192         }
193 
194         /* Loop through the multicast join list and assign an arbitrary timeout to
195            respond between 1 and maximum response time for each group.  */
196         for (i = 0; i < NX_MAX_MULTICAST_GROUPS; i++)
197         {
198 
199             /* Is there a group address in this slot?  */
200             if (ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_join_list == NX_NULL)
201             {
202 
203                 /* No, skip doing further processing. */
204                 continue;
205             }
206 
207             /* Does the group address in the header match our join list? */
208             if ((ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_join_list != header_ptr -> nx_igmp_header_word_1) &&
209                 /* or is this a general membership query? */
210                 (header_ptr -> nx_igmp_header_word_1 != NX_NULL))
211             {
212 
213                 /* No; so no need to update the timer, skip to the next group in the host list. */
214                 continue;
215             }
216 
217             /* Is the current host group running timer less than the max delay? */
218             if (((ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time < max_update_time) &&
219                  (ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time != NX_NULL)) ||
220                 (ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time == NX_WAIT_FOREVER))
221             {
222 
223                 /* Yes; Let the current timer timeout. Skip to the next group. */
224                 continue;
225             }
226 
227             /* Set the timeout for this multicast group. */
228             ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time = update_time;
229 
230             /* Then increment the update time for the next host group so the update/expiration times
231                are separated by one second. This avoids bursts of IGMP reports to the server. */
232             update_time++;
233 
234             /* Check after each multicast group that we have not exceeded the maximum response time. */
235             if (update_time > max_update_time)
236             {
237 
238                 /* We have, so wrap the update time back to one. */
239                 update_time =  1;
240             }
241         }
242     }
243 #ifndef NX_DISABLE_IGMPV2
244 
245     /* Is this another IGMPv1 host's join request? */
246     else if (((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_TYPE_MASK) == NX_IGMP_HOST_RESPONSE_TYPE) ||
247              /* ...Or an IGMPv2 host's join request? */
248              ((header_ptr -> nx_igmp_header_word_0 & NX_IGMPV2_TYPE_MASK) == NX_IGMP_HOST_V2_JOIN_TYPE))
249 #else
250 
251     /* Is this another IGMPv1 host's join request? */
252     else if ((header_ptr -> nx_igmp_header_word_0 & NX_IGMP_TYPE_MASK) == NX_IGMP_HOST_RESPONSE_TYPE)
253 #endif
254     {
255 
256         /* Yes;  Loop through the host multicast join list to find a matching group.  */
257         for (i = 0; i < NX_MAX_MULTICAST_GROUPS; i++)
258         {
259 
260             /* Compare the group address in the header with the host list. Is this a match? */
261             if ((ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_join_list == header_ptr -> nx_igmp_header_word_1) &&
262                 (ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time != NX_WAIT_FOREVER))
263             {
264 
265                 /* Yes; Clear the update time. This will cancel sending a join
266                    request for the same multicast group.  */
267                 ip_ptr -> nx_ipv4_multicast_entry[i].nx_ipv4_multicast_update_time =  0;
268                 break;
269             }
270         }
271     }
272 
273     /* Release the IGMP packet.  */
274     _nx_packet_release(packet_ptr);
275 }
276 #endif /* !NX_DISABLE_IPV4  */
277 
278