]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv2.c
Merge pull request #10366 from AbhishekNR/mld_cli
[mirror_frr.git] / pimd / pim_igmpv2.c
1 /*
2 * PIM for Quagga
3 * Copyright (C) 2016 Cumulus Networks, Inc.
4 * Daniel Walton
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "zebra.h"
22
23 #include "pimd.h"
24 #include "pim_igmp.h"
25 #include "pim_igmpv2.h"
26 #include "pim_igmpv3.h"
27 #include "pim_ssm.h"
28 #include "pim_str.h"
29 #include "pim_time.h"
30 #include "pim_util.h"
31
32
33 static void on_trace(const char *label, struct interface *ifp,
34 struct in_addr from)
35 {
36 if (PIM_DEBUG_IGMP_TRACE) {
37 char from_str[INET_ADDRSTRLEN];
38 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
39 zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
40 }
41 }
42
43 void igmp_v2_send_query(struct gm_group *group, int fd, const char *ifname,
44 char *query_buf, struct in_addr dst_addr,
45 struct in_addr group_addr,
46 int query_max_response_time_dsec)
47 {
48 ssize_t msg_size = 8;
49 uint8_t max_resp_code;
50 ssize_t sent;
51 struct sockaddr_in to;
52 socklen_t tolen;
53 uint16_t checksum;
54
55 /* max_resp_code must be non-zero else this will look like an IGMP v1
56 * query */
57 /* RFC 2236: 2.2. , v2's is equal to it */
58 max_resp_code = query_max_response_time_dsec;
59 assert(max_resp_code > 0);
60
61 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
62 query_buf[1] = max_resp_code;
63 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
64 0; /* for computing checksum */
65 memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
66
67 checksum = in_cksum(query_buf, msg_size);
68 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
69
70 if (PIM_DEBUG_IGMP_PACKETS) {
71 char dst_str[INET_ADDRSTRLEN];
72 char group_str[INET_ADDRSTRLEN];
73 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
74 pim_inet4_dump("<group?>", group_addr, group_str,
75 sizeof(group_str));
76 zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
77 dst_str, ifname, group_str);
78 }
79
80 memset(&to, 0, sizeof(to));
81 to.sin_family = AF_INET;
82 to.sin_addr = dst_addr;
83 tolen = sizeof(to);
84
85 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
86 (struct sockaddr *)&to, tolen);
87 if (sent != (ssize_t)msg_size) {
88 char dst_str[INET_ADDRSTRLEN];
89 char group_str[INET_ADDRSTRLEN];
90 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
91 pim_inet4_dump("<group?>", group_addr, group_str,
92 sizeof(group_str));
93 if (sent < 0) {
94 zlog_warn(
95 "Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
96 dst_str, ifname, group_str, msg_size, errno,
97 safe_strerror(errno));
98 } else {
99 zlog_warn(
100 "Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
101 dst_str, ifname, group_str, msg_size, sent);
102 }
103 return;
104 }
105 }
106
107 int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
108 const char *from_str, char *igmp_msg, int igmp_msg_len)
109 {
110 struct interface *ifp = igmp->interface;
111 struct in_addr group_addr;
112 struct pim_interface *pim_ifp;
113 char group_str[INET_ADDRSTRLEN];
114
115 on_trace(__func__, igmp->interface, from);
116
117 pim_ifp = ifp->info;
118
119 if (igmp->mtrace_only)
120 return 0;
121
122 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
123 if (PIM_DEBUG_IGMP_PACKETS)
124 zlog_debug(
125 "Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
126 from_str, ifp->name, igmp_msg_len,
127 IGMP_V12_MSG_SIZE);
128 }
129
130 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
131 zlog_warn(
132 "Recv IGMPv2 REPORT from %s on %s: size=%d with invalid checksum",
133 from_str, ifp->name, igmp_msg_len);
134 return -1;
135 }
136
137 /* Collecting IGMP Rx stats */
138 igmp->igmp_stats.report_v2++;
139
140 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
141
142 if (PIM_DEBUG_IGMP_PACKETS) {
143 pim_inet4_dump("<dst?>", group_addr, group_str,
144 sizeof(group_str));
145 zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s", from_str,
146 ifp->name, group_str);
147 }
148
149 /*
150 * RFC 4604
151 * section 2.2.1
152 * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
153 * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
154 * the SSM range.
155 */
156 if (pim_is_grp_ssm(pim_ifp->pim, group_addr)) {
157 if (PIM_DEBUG_IGMP_PACKETS) {
158 zlog_debug(
159 "Ignoring IGMPv2 group record %pI4 from %s on %s exclude mode in SSM range",
160 &group_addr.s_addr, from_str, ifp->name);
161 }
162 return -1;
163 }
164
165
166 /*
167 * RFC 3376
168 * 7.3.2. In the Presence of Older Version Group Members
169 *
170 * When Group Compatibility Mode is IGMPv2, a router internally
171 * translates the following IGMPv2 messages for that group to their
172 * IGMPv3 equivalents:
173 *
174 * IGMPv2 Message IGMPv3 Equivalent
175 * -------------- -----------------
176 * Report IS_EX( {} )
177 * Leave TO_IN( {} )
178 */
179 igmpv3_report_isex(igmp, from, group_addr, 0, NULL, 1);
180
181 return 0;
182 }
183
184 int igmp_v2_recv_leave(struct gm_sock *igmp, struct ip *ip_hdr,
185 const char *from_str, char *igmp_msg, int igmp_msg_len)
186 {
187 struct interface *ifp = igmp->interface;
188 struct in_addr group_addr;
189 char group_str[INET_ADDRSTRLEN];
190 struct in_addr from = ip_hdr->ip_src;
191
192 on_trace(__func__, igmp->interface, from);
193
194 if (igmp->mtrace_only)
195 return 0;
196
197 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
198 if (PIM_DEBUG_IGMP_PACKETS)
199 zlog_debug(
200 "Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
201 from_str, ifp->name, igmp_msg_len,
202 IGMP_V12_MSG_SIZE);
203 }
204
205 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
206 zlog_warn(
207 "Recv IGMPv2 LEAVE from %s on %s with invalid checksum",
208 from_str, ifp->name);
209 return -1;
210 }
211
212
213 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
214
215 if (PIM_DEBUG_IGMP_PACKETS) {
216 pim_inet4_dump("<dst?>", group_addr, group_str,
217 sizeof(group_str));
218 zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s", from_str,
219 ifp->name, group_str);
220 }
221 /*
222 * As per RFC 2236, section 9:
223 Message Type Destination Group
224 ------------ -----------------
225 General Query ALL-SYSTEMS (224.0.0.1)
226 Group-Specific Query The group being queried
227 Membership Report The group being reported
228 Leave Message ALL-ROUTERS (224.0.0.2)
229
230 Note: in older (i.e., non-standard and now obsolete) versions of
231 IGMPv2, hosts send Leave Messages to the group being left. A
232 router SHOULD accept Leave Messages addressed to the group being
233 left in the interests of backwards compatibility with such hosts.
234 In all cases, however, hosts MUST send to the ALL-ROUTERS address
235 to be compliant with this specification.
236 */
237 if ((ntohl(ip_hdr->ip_dst.s_addr) != INADDR_ALLRTRS_GROUP)
238 && (ip_hdr->ip_dst.s_addr != group_addr.s_addr)) {
239 if (PIM_DEBUG_IGMP_EVENTS)
240 zlog_debug(
241 "IGMPv2 Leave message is ignored since received on address other than ALL-ROUTERS or Group-address");
242 return -1;
243 }
244
245 /* Collecting IGMP Rx stats */
246 igmp->igmp_stats.leave_v2++;
247
248 /*
249 * RFC 3376
250 * 7.3.2. In the Presence of Older Version Group Members
251 *
252 * When Group Compatibility Mode is IGMPv2, a router internally
253 * translates the following IGMPv2 messages for that group to their
254 * IGMPv3 equivalents:
255 *
256 * IGMPv2 Message IGMPv3 Equivalent
257 * -------------- -----------------
258 * Report IS_EX( {} )
259 * Leave TO_IN( {} )
260 */
261 igmpv3_report_toin(igmp, from, group_addr, 0, NULL);
262
263 return 0;
264 }