]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmpv2.c
Merge pull request #12665 from opensourcerouting/cs-misc
[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 23#include "pimd.h"
993e3d8e 24#include "pim_instance.h"
b05b72e8
DW
25#include "pim_igmp.h"
26#include "pim_igmpv2.h"
27#include "pim_igmpv3.h"
d6f31d2a 28#include "pim_ssm.h"
b05b72e8
DW
29#include "pim_str.h"
30#include "pim_time.h"
31#include "pim_util.h"
32
33
d62a17ae 34static void on_trace(const char *label, struct interface *ifp,
35 struct in_addr from)
b05b72e8 36{
a96d64b0 37 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 38 char from_str[INET_ADDRSTRLEN];
39 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
40 zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
41 }
b05b72e8
DW
42}
43
a16db099 44void igmp_v2_send_query(struct gm_group *group, int fd, const char *ifname,
d62a17ae 45 char *query_buf, struct in_addr dst_addr,
46 struct in_addr group_addr,
47 int query_max_response_time_dsec)
b05b72e8 48{
d62a17ae 49 ssize_t msg_size = 8;
50 uint8_t max_resp_code;
51 ssize_t sent;
52 struct sockaddr_in to;
53 socklen_t tolen;
54 uint16_t checksum;
55
56 /* max_resp_code must be non-zero else this will look like an IGMP v1
57 * query */
1c475815 58 /* RFC 2236: 2.2. , v2's is equal to it */
59 max_resp_code = query_max_response_time_dsec;
df5dfb77 60 assert(max_resp_code > 0);
d62a17ae 61
62 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
63 query_buf[1] = max_resp_code;
64 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
65 0; /* for computing checksum */
66 memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
67
68 checksum = in_cksum(query_buf, msg_size);
69 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
70
55eb347d 71 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 72 char dst_str[INET_ADDRSTRLEN];
73 char group_str[INET_ADDRSTRLEN];
74 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
75 pim_inet4_dump("<group?>", group_addr, group_str,
76 sizeof(group_str));
77 zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
78 dst_str, ifname, group_str);
79 }
80
81 memset(&to, 0, sizeof(to));
82 to.sin_family = AF_INET;
83 to.sin_addr = dst_addr;
84 tolen = sizeof(to);
85
86 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
87 (struct sockaddr *)&to, tolen);
88 if (sent != (ssize_t)msg_size) {
89 char dst_str[INET_ADDRSTRLEN];
90 char group_str[INET_ADDRSTRLEN];
91 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
92 pim_inet4_dump("<group?>", group_addr, group_str,
93 sizeof(group_str));
94 if (sent < 0) {
95 zlog_warn(
96 "Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
97 dst_str, ifname, group_str, msg_size, errno,
98 safe_strerror(errno));
99 } else {
100 zlog_warn(
101 "Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
102 dst_str, ifname, group_str, msg_size, sent);
103 }
104 return;
105 }
b05b72e8
DW
106}
107
c5f76fad 108int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
d62a17ae 109 const char *from_str, char *igmp_msg, int igmp_msg_len)
b05b72e8 110{
d62a17ae 111 struct interface *ifp = igmp->interface;
112 struct in_addr group_addr;
d6f31d2a 113 struct pim_interface *pim_ifp;
d62a17ae 114 char group_str[INET_ADDRSTRLEN];
115
15569c58 116 on_trace(__func__, igmp->interface, from);
d62a17ae 117
d6f31d2a
A
118 pim_ifp = ifp->info;
119
f83f3966
MS
120 if (igmp->mtrace_only)
121 return 0;
122
d62a17ae 123 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
55eb347d 124 if (PIM_DEBUG_GM_PACKETS)
3b93886a
MR
125 zlog_debug(
126 "Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
127 from_str, ifp->name, igmp_msg_len,
128 IGMP_V12_MSG_SIZE);
d62a17ae 129 }
130
9041c30a
MR
131 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
132 zlog_warn(
133 "Recv IGMPv2 REPORT from %s on %s: size=%d with invalid checksum",
134 from_str, ifp->name, igmp_msg_len);
135 return -1;
136 }
137
21313cbf 138 /* Collecting IGMP Rx stats */
f2058cb4 139 igmp->igmp_stats.report_v2++;
21313cbf 140
d62a17ae 141 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
142
55eb347d 143 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 144 pim_inet4_dump("<dst?>", group_addr, group_str,
145 sizeof(group_str));
146 zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s", from_str,
147 ifp->name, group_str);
148 }
149
d6f31d2a
A
150 /*
151 * RFC 4604
152 * section 2.2.1
153 * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
154 * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
155 * the SSM range.
156 */
157 if (pim_is_grp_ssm(pim_ifp->pim, group_addr)) {
55eb347d 158 if (PIM_DEBUG_GM_PACKETS) {
d6f31d2a
A
159 zlog_debug(
160 "Ignoring IGMPv2 group record %pI4 from %s on %s exclude mode in SSM range",
161 &group_addr.s_addr, from_str, ifp->name);
162 }
163 return -1;
164 }
165
166
d62a17ae 167 /*
168 * RFC 3376
169 * 7.3.2. In the Presence of Older Version Group Members
170 *
171 * When Group Compatibility Mode is IGMPv2, a router internally
172 * translates the following IGMPv2 messages for that group to their
173 * IGMPv3 equivalents:
174 *
175 * IGMPv2 Message IGMPv3 Equivalent
176 * -------------- -----------------
177 * Report IS_EX( {} )
178 * Leave TO_IN( {} )
179 */
180 igmpv3_report_isex(igmp, from, group_addr, 0, NULL, 1);
181
182 return 0;
b05b72e8
DW
183}
184
c5f76fad 185int igmp_v2_recv_leave(struct gm_sock *igmp, struct ip *ip_hdr,
d62a17ae 186 const char *from_str, char *igmp_msg, int igmp_msg_len)
b05b72e8 187{
d62a17ae 188 struct interface *ifp = igmp->interface;
189 struct in_addr group_addr;
190 char group_str[INET_ADDRSTRLEN];
d1b61cb9 191 struct in_addr from = ip_hdr->ip_src;
d62a17ae 192
15569c58 193 on_trace(__func__, igmp->interface, from);
d62a17ae 194
f83f3966
MS
195 if (igmp->mtrace_only)
196 return 0;
197
d62a17ae 198 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
55eb347d 199 if (PIM_DEBUG_GM_PACKETS)
69b9ea0b
MR
200 zlog_debug(
201 "Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
202 from_str, ifp->name, igmp_msg_len,
203 IGMP_V12_MSG_SIZE);
d62a17ae 204 }
205
9041c30a
MR
206 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
207 zlog_warn(
208 "Recv IGMPv2 LEAVE from %s on %s with invalid checksum",
209 from_str, ifp->name);
210 return -1;
211 }
212
21313cbf 213
d62a17ae 214 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
215
55eb347d 216 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 217 pim_inet4_dump("<dst?>", group_addr, group_str,
218 sizeof(group_str));
219 zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s", from_str,
220 ifp->name, group_str);
221 }
d1b61cb9
MR
222 /*
223 * As per RFC 2236, section 9:
224 Message Type Destination Group
225 ------------ -----------------
226 General Query ALL-SYSTEMS (224.0.0.1)
227 Group-Specific Query The group being queried
228 Membership Report The group being reported
229 Leave Message ALL-ROUTERS (224.0.0.2)
230
231 Note: in older (i.e., non-standard and now obsolete) versions of
232 IGMPv2, hosts send Leave Messages to the group being left. A
233 router SHOULD accept Leave Messages addressed to the group being
234 left in the interests of backwards compatibility with such hosts.
235 In all cases, however, hosts MUST send to the ALL-ROUTERS address
236 to be compliant with this specification.
237 */
238 if ((ntohl(ip_hdr->ip_dst.s_addr) != INADDR_ALLRTRS_GROUP)
239 && (ip_hdr->ip_dst.s_addr != group_addr.s_addr)) {
95b13dc5 240 if (PIM_DEBUG_GM_EVENTS)
d1b61cb9
MR
241 zlog_debug(
242 "IGMPv2 Leave message is ignored since received on address other than ALL-ROUTERS or Group-address");
243 return -1;
244 }
245
246 /* Collecting IGMP Rx stats */
f2058cb4 247 igmp->igmp_stats.leave_v2++;
d62a17ae 248
249 /*
250 * RFC 3376
251 * 7.3.2. In the Presence of Older Version Group Members
252 *
253 * When Group Compatibility Mode is IGMPv2, a router internally
254 * translates the following IGMPv2 messages for that group to their
255 * IGMPv3 equivalents:
256 *
257 * IGMPv2 Message IGMPv3 Equivalent
258 * -------------- -----------------
259 * Report IS_EX( {} )
260 * Leave TO_IN( {} )
261 */
262 igmpv3_report_toin(igmp, from, group_addr, 0, NULL);
263
264 return 0;
b05b72e8 265}