]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmpv2.c
Merge pull request #10366 from AbhishekNR/mld_cli
[mirror_frr.git] / pimd / pim_igmpv2.c
CommitLineData
b05b72e8
DW
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 *
896014f4
DL
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
b05b72e8
DW
19 */
20
fcd6282c
DS
21#include "zebra.h"
22
b05b72e8
DW
23#include "pimd.h"
24#include "pim_igmp.h"
25#include "pim_igmpv2.h"
26#include "pim_igmpv3.h"
d6f31d2a 27#include "pim_ssm.h"
b05b72e8
DW
28#include "pim_str.h"
29#include "pim_time.h"
30#include "pim_util.h"
31
32
d62a17ae 33static void on_trace(const char *label, struct interface *ifp,
34 struct in_addr from)
b05b72e8 35{
d62a17ae 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 }
b05b72e8
DW
41}
42
a16db099 43void igmp_v2_send_query(struct gm_group *group, int fd, const char *ifname,
d62a17ae 44 char *query_buf, struct in_addr dst_addr,
45 struct in_addr group_addr,
46 int query_max_response_time_dsec)
b05b72e8 47{
d62a17ae 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 */
1c475815 57 /* RFC 2236: 2.2. , v2's is equal to it */
58 max_resp_code = query_max_response_time_dsec;
df5dfb77 59 assert(max_resp_code > 0);
d62a17ae 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 }
b05b72e8
DW
105}
106
c5f76fad 107int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
d62a17ae 108 const char *from_str, char *igmp_msg, int igmp_msg_len)
b05b72e8 109{
d62a17ae 110 struct interface *ifp = igmp->interface;
111 struct in_addr group_addr;
d6f31d2a 112 struct pim_interface *pim_ifp;
d62a17ae 113 char group_str[INET_ADDRSTRLEN];
114
15569c58 115 on_trace(__func__, igmp->interface, from);
d62a17ae 116
d6f31d2a
A
117 pim_ifp = ifp->info;
118
f83f3966
MS
119 if (igmp->mtrace_only)
120 return 0;
121
d62a17ae 122 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
3b93886a
MR
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);
d62a17ae 128 }
129
9041c30a
MR
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
21313cbf 137 /* Collecting IGMP Rx stats */
f2058cb4 138 igmp->igmp_stats.report_v2++;
21313cbf 139
d62a17ae 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
d6f31d2a
A
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
d62a17ae 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;
b05b72e8
DW
182}
183
c5f76fad 184int igmp_v2_recv_leave(struct gm_sock *igmp, struct ip *ip_hdr,
d62a17ae 185 const char *from_str, char *igmp_msg, int igmp_msg_len)
b05b72e8 186{
d62a17ae 187 struct interface *ifp = igmp->interface;
188 struct in_addr group_addr;
189 char group_str[INET_ADDRSTRLEN];
d1b61cb9 190 struct in_addr from = ip_hdr->ip_src;
d62a17ae 191
15569c58 192 on_trace(__func__, igmp->interface, from);
d62a17ae 193
f83f3966
MS
194 if (igmp->mtrace_only)
195 return 0;
196
d62a17ae 197 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
69b9ea0b
MR
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);
d62a17ae 203 }
204
9041c30a
MR
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
21313cbf 212
d62a17ae 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 }
d1b61cb9
MR
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 */
f2058cb4 246 igmp->igmp_stats.leave_v2++;
d62a17ae 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;
b05b72e8 264}