3 * Copyright (C) 2016 Cumulus Networks, Inc.
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.
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.
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
24 #include "pim_instance.h"
26 #include "pim_igmpv2.h"
27 #include "pim_igmpv3.h"
34 static void on_trace(const char *label
, struct interface
*ifp
,
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
);
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
)
50 uint8_t max_resp_code
;
52 struct sockaddr_in to
;
56 /* max_resp_code must be non-zero else this will look like an IGMP v1
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);
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
));
68 checksum
= in_cksum(query_buf
, msg_size
);
69 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
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
,
77 zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
78 dst_str
, ifname
, group_str
);
81 memset(&to
, 0, sizeof(to
));
82 to
.sin_family
= AF_INET
;
83 to
.sin_addr
= dst_addr
;
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
,
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
));
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
);
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
)
111 struct interface
*ifp
= igmp
->interface
;
112 struct in_addr group_addr
;
113 struct pim_interface
*pim_ifp
;
114 char group_str
[INET_ADDRSTRLEN
];
116 on_trace(__func__
, igmp
->interface
, from
);
120 if (igmp
->mtrace_only
)
123 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
124 if (PIM_DEBUG_GM_PACKETS
)
126 "Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
127 from_str
, ifp
->name
, igmp_msg_len
,
131 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
133 "Recv IGMPv2 REPORT from %s on %s: size=%d with invalid checksum",
134 from_str
, ifp
->name
, igmp_msg_len
);
138 /* Collecting IGMP Rx stats */
139 igmp
->igmp_stats
.report_v2
++;
141 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
143 if (PIM_DEBUG_GM_PACKETS
) {
144 pim_inet4_dump("<dst?>", group_addr
, group_str
,
146 zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s", from_str
,
147 ifp
->name
, group_str
);
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
157 if (pim_is_grp_ssm(pim_ifp
->pim
, group_addr
)) {
158 if (PIM_DEBUG_GM_PACKETS
) {
160 "Ignoring IGMPv2 group record %pI4 from %s on %s exclude mode in SSM range",
161 &group_addr
.s_addr
, from_str
, ifp
->name
);
169 * 7.3.2. In the Presence of Older Version Group Members
171 * When Group Compatibility Mode is IGMPv2, a router internally
172 * translates the following IGMPv2 messages for that group to their
173 * IGMPv3 equivalents:
175 * IGMPv2 Message IGMPv3 Equivalent
176 * -------------- -----------------
180 igmpv3_report_isex(igmp
, from
, group_addr
, 0, NULL
, 1);
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
)
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
;
193 on_trace(__func__
, igmp
->interface
, from
);
195 if (igmp
->mtrace_only
)
198 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
199 if (PIM_DEBUG_GM_PACKETS
)
201 "Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
202 from_str
, ifp
->name
, igmp_msg_len
,
206 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
208 "Recv IGMPv2 LEAVE from %s on %s with invalid checksum",
209 from_str
, ifp
->name
);
214 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
216 if (PIM_DEBUG_GM_PACKETS
) {
217 pim_inet4_dump("<dst?>", group_addr
, group_str
,
219 zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s", from_str
,
220 ifp
->name
, group_str
);
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)
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.
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
)
242 "IGMPv2 Leave message is ignored since received on address other than ALL-ROUTERS or Group-address");
246 /* Collecting IGMP Rx stats */
247 igmp
->igmp_stats
.leave_v2
++;
251 * 7.3.2. In the Presence of Older Version Group Members
253 * When Group Compatibility Mode is IGMPv2, a router internally
254 * translates the following IGMPv2 messages for that group to their
255 * IGMPv3 equivalents:
257 * IGMPv2 Message IGMPv3 Equivalent
258 * -------------- -----------------
262 igmpv3_report_toin(igmp
, from
, group_addr
, 0, NULL
);