]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmpv2.c
pimd: zassert => assert
[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
d62a17ae 42void igmp_v2_send_query(struct igmp_group *group, int fd, const char *ifname,
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
d62a17ae 105int igmp_v2_recv_report(struct igmp_sock *igmp, struct in_addr from,
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) {
118 zlog_warn(
119 "Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
120 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
121 return -1;
122 }
123
9041c30a
MR
124 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
125 zlog_warn(
126 "Recv IGMPv2 REPORT from %s on %s: size=%d with invalid checksum",
127 from_str, ifp->name, igmp_msg_len);
128 return -1;
129 }
130
21313cbf
MS
131 /* Collecting IGMP Rx stats */
132 igmp->rx_stats.report_v2++;
133
d62a17ae 134 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
135
136 if (PIM_DEBUG_IGMP_PACKETS) {
137 pim_inet4_dump("<dst?>", group_addr, group_str,
138 sizeof(group_str));
139 zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s", from_str,
140 ifp->name, group_str);
141 }
142
143 /*
144 * RFC 3376
145 * 7.3.2. In the Presence of Older Version Group Members
146 *
147 * When Group Compatibility Mode is IGMPv2, a router internally
148 * translates the following IGMPv2 messages for that group to their
149 * IGMPv3 equivalents:
150 *
151 * IGMPv2 Message IGMPv3 Equivalent
152 * -------------- -----------------
153 * Report IS_EX( {} )
154 * Leave TO_IN( {} )
155 */
156 igmpv3_report_isex(igmp, from, group_addr, 0, NULL, 1);
157
158 return 0;
b05b72e8
DW
159}
160
d1b61cb9 161int igmp_v2_recv_leave(struct igmp_sock *igmp, struct ip *ip_hdr,
d62a17ae 162 const char *from_str, char *igmp_msg, int igmp_msg_len)
b05b72e8 163{
d62a17ae 164 struct interface *ifp = igmp->interface;
165 struct in_addr group_addr;
166 char group_str[INET_ADDRSTRLEN];
d1b61cb9 167 struct in_addr from = ip_hdr->ip_src;
d62a17ae 168
15569c58 169 on_trace(__func__, igmp->interface, from);
d62a17ae 170
f83f3966
MS
171 if (igmp->mtrace_only)
172 return 0;
173
d62a17ae 174 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
175 zlog_warn(
176 "Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
177 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
178 return -1;
179 }
180
9041c30a
MR
181 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
182 zlog_warn(
183 "Recv IGMPv2 LEAVE from %s on %s with invalid checksum",
184 from_str, ifp->name);
185 return -1;
186 }
187
21313cbf 188
d62a17ae 189 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
190
191 if (PIM_DEBUG_IGMP_PACKETS) {
192 pim_inet4_dump("<dst?>", group_addr, group_str,
193 sizeof(group_str));
194 zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s", from_str,
195 ifp->name, group_str);
196 }
d1b61cb9
MR
197 /*
198 * As per RFC 2236, section 9:
199 Message Type Destination Group
200 ------------ -----------------
201 General Query ALL-SYSTEMS (224.0.0.1)
202 Group-Specific Query The group being queried
203 Membership Report The group being reported
204 Leave Message ALL-ROUTERS (224.0.0.2)
205
206 Note: in older (i.e., non-standard and now obsolete) versions of
207 IGMPv2, hosts send Leave Messages to the group being left. A
208 router SHOULD accept Leave Messages addressed to the group being
209 left in the interests of backwards compatibility with such hosts.
210 In all cases, however, hosts MUST send to the ALL-ROUTERS address
211 to be compliant with this specification.
212 */
213 if ((ntohl(ip_hdr->ip_dst.s_addr) != INADDR_ALLRTRS_GROUP)
214 && (ip_hdr->ip_dst.s_addr != group_addr.s_addr)) {
215 if (PIM_DEBUG_IGMP_EVENTS)
216 zlog_debug(
217 "IGMPv2 Leave message is ignored since received on address other than ALL-ROUTERS or Group-address");
218 return -1;
219 }
220
221 /* Collecting IGMP Rx stats */
222 igmp->rx_stats.leave_v2++;
d62a17ae 223
224 /*
225 * RFC 3376
226 * 7.3.2. In the Presence of Older Version Group Members
227 *
228 * When Group Compatibility Mode is IGMPv2, a router internally
229 * translates the following IGMPv2 messages for that group to their
230 * IGMPv3 equivalents:
231 *
232 * IGMPv2 Message IGMPv3 Equivalent
233 * -------------- -----------------
234 * Report IS_EX( {} )
235 * Leave TO_IN( {} )
236 */
237 igmpv3_report_toin(igmp, from, group_addr, 0, NULL);
238
239 return 0;
b05b72e8 240}