]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv2.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[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_instance.h"
25 #include "pim_igmp.h"
26 #include "pim_igmpv2.h"
27 #include "pim_igmpv3.h"
28 #include "pim_ssm.h"
29 #include "pim_str.h"
30 #include "pim_time.h"
31 #include "pim_util.h"
32
33
34 static void on_trace(const char *label, struct interface *ifp,
35 struct in_addr from)
36 {
37 if (PIM_DEBUG_GM_TRACE) {
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 }
42 }
43
44 void igmp_v2_send_query(struct gm_group *group, int fd, const char *ifname,
45 char *query_buf, struct in_addr dst_addr,
46 struct in_addr group_addr,
47 int query_max_response_time_dsec)
48 {
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 */
58 /* RFC 2236: 2.2. , v2's is equal to it */
59 max_resp_code = query_max_response_time_dsec;
60 assert(max_resp_code > 0);
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
71 if (PIM_DEBUG_GM_PACKETS) {
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 }
106 }
107
108 int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
109 const char *from_str, char *igmp_msg, int igmp_msg_len)
110 {
111 struct interface *ifp = igmp->interface;
112 struct in_addr group_addr;
113 struct pim_interface *pim_ifp;
114 char group_str[INET_ADDRSTRLEN];
115
116 on_trace(__func__, igmp->interface, from);
117
118 pim_ifp = ifp->info;
119
120 if (igmp->mtrace_only)
121 return 0;
122
123 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
124 if (PIM_DEBUG_GM_PACKETS)
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);
129 }
130
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
138 /* Collecting IGMP Rx stats */
139 igmp->igmp_stats.report_v2++;
140
141 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
142
143 if (PIM_DEBUG_GM_PACKETS) {
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
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)) {
158 if (PIM_DEBUG_GM_PACKETS) {
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
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;
183 }
184
185 int igmp_v2_recv_leave(struct gm_sock *igmp, struct ip *ip_hdr,
186 const char *from_str, char *igmp_msg, int igmp_msg_len)
187 {
188 struct interface *ifp = igmp->interface;
189 struct in_addr group_addr;
190 char group_str[INET_ADDRSTRLEN];
191 struct in_addr from = ip_hdr->ip_src;
192
193 on_trace(__func__, igmp->interface, from);
194
195 if (igmp->mtrace_only)
196 return 0;
197
198 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
199 if (PIM_DEBUG_GM_PACKETS)
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);
204 }
205
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
213
214 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
215
216 if (PIM_DEBUG_GM_PACKETS) {
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 }
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)) {
240 if (PIM_DEBUG_GM_EVENTS)
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 */
247 igmp->igmp_stats.leave_v2++;
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;
265 }