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