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