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