]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv2.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / pimd / pim_igmpv2.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * PIM for Quagga
4 * Copyright (C) 2016 Cumulus Networks, Inc.
5 * Daniel Walton
6 */
7
8 #include "zebra.h"
9
10 #include "pimd.h"
11 #include "pim_instance.h"
12 #include "pim_igmp.h"
13 #include "pim_igmpv2.h"
14 #include "pim_igmpv3.h"
15 #include "pim_ssm.h"
16 #include "pim_str.h"
17 #include "pim_time.h"
18 #include "pim_util.h"
19
20
21 static void on_trace(const char *label, struct interface *ifp,
22 struct in_addr from)
23 {
24 if (PIM_DEBUG_GM_TRACE) {
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 }
29 }
30
31 void igmp_v2_send_query(struct gm_group *group, int fd, const char *ifname,
32 char *query_buf, struct in_addr dst_addr,
33 struct in_addr group_addr,
34 int query_max_response_time_dsec)
35 {
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 */
45 /* RFC 2236: 2.2. , v2's is equal to it */
46 max_resp_code = query_max_response_time_dsec;
47 assert(max_resp_code > 0);
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
58 if (PIM_DEBUG_GM_PACKETS) {
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 }
93 }
94
95 int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
96 const char *from_str, char *igmp_msg, int igmp_msg_len)
97 {
98 struct interface *ifp = igmp->interface;
99 struct in_addr group_addr;
100 struct pim_interface *pim_ifp;
101 char group_str[INET_ADDRSTRLEN];
102
103 on_trace(__func__, igmp->interface, from);
104
105 pim_ifp = ifp->info;
106
107 if (igmp->mtrace_only)
108 return 0;
109
110 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
111 if (PIM_DEBUG_GM_PACKETS)
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);
116 }
117
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
125 /* Collecting IGMP Rx stats */
126 igmp->igmp_stats.report_v2++;
127
128 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
129
130 if (PIM_DEBUG_GM_PACKETS) {
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
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)) {
145 if (PIM_DEBUG_GM_PACKETS) {
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
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;
170 }
171
172 int igmp_v2_recv_leave(struct gm_sock *igmp, struct ip *ip_hdr,
173 const char *from_str, char *igmp_msg, int igmp_msg_len)
174 {
175 struct interface *ifp = igmp->interface;
176 struct in_addr group_addr;
177 char group_str[INET_ADDRSTRLEN];
178 struct in_addr from = ip_hdr->ip_src;
179
180 on_trace(__func__, igmp->interface, from);
181
182 if (igmp->mtrace_only)
183 return 0;
184
185 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
186 if (PIM_DEBUG_GM_PACKETS)
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);
191 }
192
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
200
201 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
202
203 if (PIM_DEBUG_GM_PACKETS) {
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 }
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)) {
227 if (PIM_DEBUG_GM_EVENTS)
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 */
234 igmp->igmp_stats.leave_v2++;
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;
252 }