]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv2.c
*: Add camelCase JSON keys in addition to PascalCase
[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_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
32 static void on_trace(const char *label, struct interface *ifp,
33 struct in_addr from)
34 {
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 }
40 }
41
42 void igmp_v2_send_query(struct gm_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)
46 {
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);
57 assert(max_resp_code > 0);
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 }
103 }
104
105 int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
106 const char *from_str, char *igmp_msg, int igmp_msg_len)
107 {
108 struct interface *ifp = igmp->interface;
109 struct in_addr group_addr;
110 char group_str[INET_ADDRSTRLEN];
111
112 on_trace(__func__, igmp->interface, from);
113
114 if (igmp->mtrace_only)
115 return 0;
116
117 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
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);
123 }
124
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
132 /* Collecting IGMP Rx stats */
133 igmp->rx_stats.report_v2++;
134
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;
160 }
161
162 int igmp_v2_recv_leave(struct gm_sock *igmp, struct ip *ip_hdr,
163 const char *from_str, char *igmp_msg, int igmp_msg_len)
164 {
165 struct interface *ifp = igmp->interface;
166 struct in_addr group_addr;
167 char group_str[INET_ADDRSTRLEN];
168 struct in_addr from = ip_hdr->ip_src;
169
170 on_trace(__func__, igmp->interface, from);
171
172 if (igmp->mtrace_only)
173 return 0;
174
175 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
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);
181 }
182
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
190
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 }
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++;
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;
242 }