Line data Source code
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 0 : static void on_trace(const char *label, struct interface *ifp,
35 : struct in_addr from)
36 : {
37 0 : if (PIM_DEBUG_GM_TRACE) {
38 0 : char from_str[INET_ADDRSTRLEN];
39 0 : pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
40 0 : zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
41 : }
42 0 : }
43 :
44 0 : 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 0 : ssize_t msg_size = 8;
50 0 : uint8_t max_resp_code;
51 0 : ssize_t sent;
52 0 : struct sockaddr_in to;
53 0 : socklen_t tolen;
54 0 : 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 0 : max_resp_code = query_max_response_time_dsec;
60 0 : assert(max_resp_code > 0);
61 :
62 0 : query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
63 0 : query_buf[1] = max_resp_code;
64 0 : *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
65 : 0; /* for computing checksum */
66 0 : memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
67 :
68 0 : checksum = in_cksum(query_buf, msg_size);
69 0 : *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
70 :
71 0 : if (PIM_DEBUG_GM_PACKETS) {
72 0 : char dst_str[INET_ADDRSTRLEN];
73 0 : char group_str[INET_ADDRSTRLEN];
74 0 : pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
75 0 : pim_inet4_dump("<group?>", group_addr, group_str,
76 : sizeof(group_str));
77 0 : zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
78 : dst_str, ifname, group_str);
79 : }
80 :
81 0 : memset(&to, 0, sizeof(to));
82 0 : to.sin_family = AF_INET;
83 0 : to.sin_addr = dst_addr;
84 0 : tolen = sizeof(to);
85 :
86 0 : sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
87 : (struct sockaddr *)&to, tolen);
88 0 : if (sent != (ssize_t)msg_size) {
89 0 : char dst_str[INET_ADDRSTRLEN];
90 0 : char group_str[INET_ADDRSTRLEN];
91 0 : pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
92 0 : pim_inet4_dump("<group?>", group_addr, group_str,
93 : sizeof(group_str));
94 0 : if (sent < 0) {
95 0 : 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 0 : 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 0 : return;
105 : }
106 : }
107 :
108 0 : 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 0 : struct interface *ifp = igmp->interface;
112 0 : struct in_addr group_addr;
113 0 : struct pim_interface *pim_ifp;
114 0 : char group_str[INET_ADDRSTRLEN];
115 :
116 0 : on_trace(__func__, igmp->interface, from);
117 :
118 0 : pim_ifp = ifp->info;
119 :
120 0 : if (igmp->mtrace_only)
121 : return 0;
122 :
123 0 : if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
124 0 : if (PIM_DEBUG_GM_PACKETS)
125 0 : 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 0 : if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
132 0 : zlog_warn(
133 : "Recv IGMPv2 REPORT from %s on %s: size=%d with invalid checksum",
134 : from_str, ifp->name, igmp_msg_len);
135 0 : return -1;
136 : }
137 :
138 : /* Collecting IGMP Rx stats */
139 0 : igmp->igmp_stats.report_v2++;
140 :
141 0 : memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
142 :
143 0 : if (PIM_DEBUG_GM_PACKETS) {
144 0 : pim_inet4_dump("<dst?>", group_addr, group_str,
145 : sizeof(group_str));
146 0 : 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 0 : if (pim_is_grp_ssm(pim_ifp->pim, group_addr)) {
158 0 : if (PIM_DEBUG_GM_PACKETS) {
159 0 : 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 0 : 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 0 : igmpv3_report_isex(igmp, from, group_addr, 0, NULL, 1);
181 :
182 0 : return 0;
183 : }
184 :
185 0 : 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 0 : struct interface *ifp = igmp->interface;
189 0 : struct in_addr group_addr;
190 0 : char group_str[INET_ADDRSTRLEN];
191 0 : struct in_addr from = ip_hdr->ip_src;
192 :
193 0 : on_trace(__func__, igmp->interface, from);
194 :
195 0 : if (igmp->mtrace_only)
196 : return 0;
197 :
198 0 : if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
199 0 : if (PIM_DEBUG_GM_PACKETS)
200 0 : 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 0 : if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
207 0 : zlog_warn(
208 : "Recv IGMPv2 LEAVE from %s on %s with invalid checksum",
209 : from_str, ifp->name);
210 0 : return -1;
211 : }
212 :
213 :
214 0 : memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
215 :
216 0 : if (PIM_DEBUG_GM_PACKETS) {
217 0 : pim_inet4_dump("<dst?>", group_addr, group_str,
218 : sizeof(group_str));
219 0 : 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 0 : if ((ntohl(ip_hdr->ip_dst.s_addr) != INADDR_ALLRTRS_GROUP)
239 0 : && (ip_hdr->ip_dst.s_addr != group_addr.s_addr)) {
240 0 : if (PIM_DEBUG_GM_EVENTS)
241 0 : zlog_debug(
242 : "IGMPv2 Leave message is ignored since received on address other than ALL-ROUTERS or Group-address");
243 0 : return -1;
244 : }
245 :
246 : /* Collecting IGMP Rx stats */
247 0 : 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 0 : igmpv3_report_toin(igmp, from, group_addr, 0, NULL);
263 :
264 0 : return 0;
265 : }
|