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