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