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_igmp.h"
29 #include "nx_ipv4.h"
30 #include "nx_packet.h"
31
32
33 #ifndef NX_DISABLE_IPV4
34 /**************************************************************************/
35 /* */
36 /* FUNCTION RELEASE */
37 /* */
38 /* _nx_igmp_interface_report_send PORTABLE C */
39 /* 6.1 */
40 /* AUTHOR */
41 /* */
42 /* Yuxin Zhou, Microsoft Corporation */
43 /* */
44 /* DESCRIPTION */
45 /* */
46 /* This function builds and sends an IGMP report. If it is a JOIN */
47 /* report, the IP nx_igmp_reports_sent statistic is incremented. */
48 /* */
49 /* Note: An IGMPv1 host does not send a LEAVE message. The caller in */
50 /* that case, _nx_igmp_multicast_interface_leave_internal, checks the */
51 /* IGMP host version and only calls this function for IGMPv2 hosts. */
52 /* */
53 /* INPUT */
54 /* */
55 /* ip_ptr IP instance pointer */
56 /* group_address Multicast group to join */
57 /* interface_index Index to the interface */
58 /* is_joining Indicate if joining or leaving*/
59 /* NX_TRUE = send join report */
60 /* NX_FALSE = send leave report*/
61 /* */
62 /* OUTPUT */
63 /* */
64 /* status Completion status */
65 /* */
66 /* CALLS */
67 /* */
68 /* _nx_ip_packet_send Send packet from the IP layer */
69 /* _nx_packet_allocate Allocate a packet for report */
70 /* tx_mutex_get Obtain protection mutex */
71 /* tx_mutex_put Release protection mutex */
72 /* */
73 /* CALLED BY */
74 /* */
75 /* nx_igmp_periodic_processing Performs periodic IGMP tasks */
76 /* nx_igmp_multicast_interface_leave_internal */
77 /* Processes a LEAVE report for */
78 /* transmission to all routers */
79 /* */
80 /* RELEASE HISTORY */
81 /* */
82 /* DATE NAME DESCRIPTION */
83 /* */
84 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
85 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
86 /* resulting in version 6.1 */
87 /* */
88 /**************************************************************************/
_nx_igmp_interface_report_send(NX_IP * ip_ptr,ULONG group_address,UINT interface_index,UINT is_joining)89 UINT _nx_igmp_interface_report_send(NX_IP *ip_ptr, ULONG group_address, UINT interface_index, UINT is_joining)
90 {
91
92 NX_INTERFACE *nx_interface;
93 UINT router_alert = 0;
94 UINT status;
95 ULONG checksum;
96 ULONG temp;
97 NX_PACKET *packet_ptr;
98 NX_IGMP_HEADER *header_ptr;
99
100
101 #ifndef NX_DISABLE_IGMPV2
102 if (ip_ptr -> nx_ip_igmp_router_version == NX_IGMP_HOST_VERSION_2)
103 {
104 router_alert = 4;
105 }
106 #endif
107
108 /* Obtain the IP mutex so we can search the multicast join list. */
109 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
110
111 nx_interface = &ip_ptr -> nx_ip_interface[interface_index];
112
113 /* Build an IGMP host response packet and send it! */
114
115 /* Allocate a packet to place the IGMP host response message in. */
116 #ifdef NX_ENABLE_DUAL_PACKET_POOL
117 /* Allocate from auxiliary packet pool first. */
118 status = _nx_packet_allocate(ip_ptr -> nx_ip_auxiliary_packet_pool, &packet_ptr, (ULONG)(NX_IGMP_PACKET + router_alert + NX_IGMP_HEADER_SIZE), TX_NO_WAIT);
119 if ((status != NX_SUCCESS) && (ip_ptr -> nx_ip_auxiliary_packet_pool != ip_ptr -> nx_ip_default_packet_pool))
120 #endif /* NX_ENABLE_DUAL_PACKET_POOL */
121 {
122 status = _nx_packet_allocate(ip_ptr -> nx_ip_default_packet_pool, &packet_ptr, (ULONG)(NX_IGMP_PACKET + router_alert + NX_IGMP_HEADER_SIZE), TX_NO_WAIT);
123 }
124
125 if (status)
126 {
127
128 /* Packet allocation failed. Release the mutex and return error status. */
129 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
130
131 return(status);
132 }
133
134 /* Add debug information. */
135 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
136
137 /* Prepare an IGMP response and send on the "all hosts" multicast
138 address. */
139
140 #ifndef NX_DISABLE_IGMP_INFO
141 /* Increase the IGMP reports sent count. */
142 if (is_joining == NX_TRUE)
143 {
144 ip_ptr -> nx_ip_igmp_reports_sent++;
145 }
146
147 #endif
148
149 /* Calculate the IGMP response message size and store it in the
150 packet header. */
151 /*lint -e{644} suppress variable might not be initialized, since "packet_ptr" was initialized as long as status is NX_SUCCESS. */
152 packet_ptr -> nx_packet_length = NX_IGMP_HEADER_SIZE;
153
154 /* Setup the prepend pointer. */
155 packet_ptr -> nx_packet_prepend_ptr -= NX_IGMP_HEADER_SIZE;
156
157 /* Stamp the outgoing interface. */
158 packet_ptr -> nx_packet_address.nx_packet_interface_ptr = nx_interface;
159
160 /* Build the IGMP host response packet. */
161
162 /* Setup the pointer to the message area. */
163 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
164 header_ptr = (NX_IGMP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
165
166 #ifndef NX_DISABLE_IGMPV2
167
168 /* Build the IGMPv2 response message. */
169
170 /* Is the router using IGMPv1? */
171 if (ip_ptr -> nx_ip_igmp_router_version == NX_IGMP_HOST_VERSION_1)
172 {
173 #endif /* NX_DISABLE_IGMPV2 */
174
175 /* Yes; Set the header fields with the max response time
176 zero and the version/type 0x12. */
177 header_ptr -> nx_igmp_header_word_0 = (ULONG)(NX_IGMP_VERSION | NX_IGMP_HOST_RESPONSE_TYPE);
178 header_ptr -> nx_igmp_header_word_1 = group_address;
179 #ifndef NX_DISABLE_IGMPV2
180 }
181 /* The router is running the IGMPv2 (or higher) protocol. */
182 else
183 {
184
185 /* Indicate if the report is a join or leave report. */
186 if (is_joining)
187 {
188
189 header_ptr -> nx_igmp_header_word_0 = (ULONG)(NX_IGMP_HOST_V2_JOIN_TYPE);
190 }
191 else
192 {
193 header_ptr -> nx_igmp_header_word_0 = (ULONG)(NX_IGMP_HOST_V2_LEAVE_TYPE);
194 }
195
196 header_ptr -> nx_igmp_header_word_1 = group_address;
197 }
198 #endif /* NX_DISABLE_IGMPV2 */
199
200
201 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
202 if (!(packet_ptr -> nx_packet_address.nx_packet_interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_IGMP_TX_CHECKSUM))
203 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
204 {
205
206 /* Calculate the checksum. */
207 temp = header_ptr -> nx_igmp_header_word_0;
208 checksum = (temp >> NX_SHIFT_BY_16);
209 checksum += (temp & NX_LOWER_16_MASK);
210 temp = header_ptr -> nx_igmp_header_word_1;
211 checksum += (temp >> NX_SHIFT_BY_16);
212 checksum += (temp & NX_LOWER_16_MASK);
213
214 /* Add in the carry bits into the checksum. */
215 checksum = (checksum >> NX_SHIFT_BY_16) + (checksum & NX_LOWER_16_MASK);
216
217 /* Do it again in case previous operation generates an overflow. */
218 checksum = (checksum >> NX_SHIFT_BY_16) + (checksum & NX_LOWER_16_MASK);
219
220 /* Place the checksum into the first header word. */
221 header_ptr -> nx_igmp_header_word_0 = header_ptr -> nx_igmp_header_word_0 | (~checksum & NX_LOWER_16_MASK);
222 }
223 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
224 else
225 {
226 packet_ptr -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_IGMP_TX_CHECKSUM;
227 }
228 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
229
230 /* IGMPv2 packets must be IPv4 packets. */
231 packet_ptr -> nx_packet_ip_version = NX_IP_VERSION_V4;
232
233 /* If NX_LITTLE_ENDIAN is defined, the headers need to be swapped. */
234 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_igmp_header_word_0);
235 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_igmp_header_word_1);
236
237 /* Send the IGMP response packet out! */
238 if (is_joining == NX_TRUE)
239 {
240
241 /* For JOIN reports, set the packet destination to the group address. */
242 _nx_ip_packet_send(ip_ptr, packet_ptr,
243 group_address,
244 NX_IP_NORMAL, NX_IGMP_TTL, NX_IP_IGMP, NX_FRAGMENT_OKAY,
245 group_address);
246 }
247 else
248 {
249
250 /* For LEAVE reports, set the destination to ALL ROUTERS as per RFC 2236 Section 3 page 4.*/
251 _nx_ip_packet_send(ip_ptr, packet_ptr,
252 NX_ALL_ROUTERS_ADDRESS,
253 NX_IP_NORMAL, NX_IGMP_TTL, NX_IP_IGMP, NX_FRAGMENT_OKAY,
254 NX_ALL_ROUTERS_ADDRESS);
255 }
256
257 /* Release the protection over the IP instance. */
258 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
259
260 return NX_SUCCESS;
261 }
262 #endif /* NX_DISABLE_IPV4 */
263
264