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
25 #include "pim_igmpv2.h"
26 #include "pim_igmpv3.h"
33 static void on_trace(const char *label
, struct interface
*ifp
,
36 if (PIM_DEBUG_IGMP_TRACE
) {
37 char from_str
[INET_ADDRSTRLEN
];
38 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
39 zlog_debug("%s: from %s on %s", label
, from_str
, ifp
->name
);
43 void igmp_v2_send_query(struct gm_group
*group
, int fd
, const char *ifname
,
44 char *query_buf
, struct in_addr dst_addr
,
45 struct in_addr group_addr
,
46 int query_max_response_time_dsec
)
49 uint8_t max_resp_code
;
51 struct sockaddr_in to
;
55 /* max_resp_code must be non-zero else this will look like an IGMP v1
57 /* RFC 2236: 2.2. , v2's is equal to it */
58 max_resp_code
= query_max_response_time_dsec
;
59 assert(max_resp_code
> 0);
61 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
62 query_buf
[1] = max_resp_code
;
63 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
64 0; /* for computing checksum */
65 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
67 checksum
= in_cksum(query_buf
, msg_size
);
68 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
70 if (PIM_DEBUG_IGMP_PACKETS
) {
71 char dst_str
[INET_ADDRSTRLEN
];
72 char group_str
[INET_ADDRSTRLEN
];
73 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
74 pim_inet4_dump("<group?>", group_addr
, group_str
,
76 zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
77 dst_str
, ifname
, group_str
);
80 memset(&to
, 0, sizeof(to
));
81 to
.sin_family
= AF_INET
;
82 to
.sin_addr
= dst_addr
;
85 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
86 (struct sockaddr
*)&to
, tolen
);
87 if (sent
!= (ssize_t
)msg_size
) {
88 char dst_str
[INET_ADDRSTRLEN
];
89 char group_str
[INET_ADDRSTRLEN
];
90 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
91 pim_inet4_dump("<group?>", group_addr
, group_str
,
95 "Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
96 dst_str
, ifname
, group_str
, msg_size
, errno
,
97 safe_strerror(errno
));
100 "Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
101 dst_str
, ifname
, group_str
, msg_size
, sent
);
107 int igmp_v2_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
108 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
110 struct interface
*ifp
= igmp
->interface
;
111 struct in_addr group_addr
;
112 struct pim_interface
*pim_ifp
;
113 char group_str
[INET_ADDRSTRLEN
];
115 on_trace(__func__
, igmp
->interface
, from
);
119 if (igmp
->mtrace_only
)
122 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
123 if (PIM_DEBUG_IGMP_PACKETS
)
125 "Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
126 from_str
, ifp
->name
, igmp_msg_len
,
130 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
132 "Recv IGMPv2 REPORT from %s on %s: size=%d with invalid checksum",
133 from_str
, ifp
->name
, igmp_msg_len
);
137 /* Collecting IGMP Rx stats */
138 igmp
->igmp_stats
.report_v2
++;
140 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
142 if (PIM_DEBUG_IGMP_PACKETS
) {
143 pim_inet4_dump("<dst?>", group_addr
, group_str
,
145 zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s", from_str
,
146 ifp
->name
, group_str
);
152 * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
153 * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
156 if (pim_is_grp_ssm(pim_ifp
->pim
, group_addr
)) {
157 if (PIM_DEBUG_IGMP_PACKETS
) {
159 "Ignoring IGMPv2 group record %pI4 from %s on %s exclude mode in SSM range",
160 &group_addr
.s_addr
, from_str
, ifp
->name
);
168 * 7.3.2. In the Presence of Older Version Group Members
170 * When Group Compatibility Mode is IGMPv2, a router internally
171 * translates the following IGMPv2 messages for that group to their
172 * IGMPv3 equivalents:
174 * IGMPv2 Message IGMPv3 Equivalent
175 * -------------- -----------------
179 igmpv3_report_isex(igmp
, from
, group_addr
, 0, NULL
, 1);
184 int igmp_v2_recv_leave(struct gm_sock
*igmp
, struct ip
*ip_hdr
,
185 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
187 struct interface
*ifp
= igmp
->interface
;
188 struct in_addr group_addr
;
189 char group_str
[INET_ADDRSTRLEN
];
190 struct in_addr from
= ip_hdr
->ip_src
;
192 on_trace(__func__
, igmp
->interface
, from
);
194 if (igmp
->mtrace_only
)
197 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
198 if (PIM_DEBUG_IGMP_PACKETS
)
200 "Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
201 from_str
, ifp
->name
, igmp_msg_len
,
205 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
207 "Recv IGMPv2 LEAVE from %s on %s with invalid checksum",
208 from_str
, ifp
->name
);
213 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
215 if (PIM_DEBUG_IGMP_PACKETS
) {
216 pim_inet4_dump("<dst?>", group_addr
, group_str
,
218 zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s", from_str
,
219 ifp
->name
, group_str
);
222 * As per RFC 2236, section 9:
223 Message Type Destination Group
224 ------------ -----------------
225 General Query ALL-SYSTEMS (224.0.0.1)
226 Group-Specific Query The group being queried
227 Membership Report The group being reported
228 Leave Message ALL-ROUTERS (224.0.0.2)
230 Note: in older (i.e., non-standard and now obsolete) versions of
231 IGMPv2, hosts send Leave Messages to the group being left. A
232 router SHOULD accept Leave Messages addressed to the group being
233 left in the interests of backwards compatibility with such hosts.
234 In all cases, however, hosts MUST send to the ALL-ROUTERS address
235 to be compliant with this specification.
237 if ((ntohl(ip_hdr
->ip_dst
.s_addr
) != INADDR_ALLRTRS_GROUP
)
238 && (ip_hdr
->ip_dst
.s_addr
!= group_addr
.s_addr
)) {
239 if (PIM_DEBUG_IGMP_EVENTS
)
241 "IGMPv2 Leave message is ignored since received on address other than ALL-ROUTERS or Group-address");
245 /* Collecting IGMP Rx stats */
246 igmp
->igmp_stats
.leave_v2
++;
250 * 7.3.2. In the Presence of Older Version Group Members
252 * When Group Compatibility Mode is IGMPv2, a router internally
253 * translates the following IGMPv2 messages for that group to their
254 * IGMPv3 equivalents:
256 * IGMPv2 Message IGMPv3 Equivalent
257 * -------------- -----------------
261 igmpv3_report_toin(igmp
, from
, group_addr
, 0, NULL
);