1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2016 Cumulus Networks, Inc.
11 #include "pim_instance.h"
13 #include "pim_igmpv2.h"
14 #include "pim_igmpv3.h"
21 static void on_trace(const char *label
, struct interface
*ifp
,
24 if (PIM_DEBUG_GM_TRACE
) {
25 char from_str
[INET_ADDRSTRLEN
];
26 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
27 zlog_debug("%s: from %s on %s", label
, from_str
, ifp
->name
);
31 void igmp_v2_send_query(struct gm_group
*group
, int fd
, const char *ifname
,
32 char *query_buf
, struct in_addr dst_addr
,
33 struct in_addr group_addr
,
34 int query_max_response_time_dsec
)
37 uint8_t max_resp_code
;
39 struct sockaddr_in to
;
43 /* max_resp_code must be non-zero else this will look like an IGMP v1
45 /* RFC 2236: 2.2. , v2's is equal to it */
46 max_resp_code
= query_max_response_time_dsec
;
47 assert(max_resp_code
> 0);
49 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
50 query_buf
[1] = max_resp_code
;
51 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
52 0; /* for computing checksum */
53 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
55 checksum
= in_cksum(query_buf
, msg_size
);
56 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
58 if (PIM_DEBUG_GM_PACKETS
) {
59 char dst_str
[INET_ADDRSTRLEN
];
60 char group_str
[INET_ADDRSTRLEN
];
61 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
62 pim_inet4_dump("<group?>", group_addr
, group_str
,
64 zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
65 dst_str
, ifname
, group_str
);
68 memset(&to
, 0, sizeof(to
));
69 to
.sin_family
= AF_INET
;
70 to
.sin_addr
= dst_addr
;
73 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
74 (struct sockaddr
*)&to
, tolen
);
75 if (sent
!= (ssize_t
)msg_size
) {
76 char dst_str
[INET_ADDRSTRLEN
];
77 char group_str
[INET_ADDRSTRLEN
];
78 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
79 pim_inet4_dump("<group?>", group_addr
, group_str
,
83 "Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
84 dst_str
, ifname
, group_str
, msg_size
, errno
,
85 safe_strerror(errno
));
88 "Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
89 dst_str
, ifname
, group_str
, msg_size
, sent
);
95 int igmp_v2_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
96 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
98 struct interface
*ifp
= igmp
->interface
;
99 struct in_addr group_addr
;
100 struct pim_interface
*pim_ifp
;
101 char group_str
[INET_ADDRSTRLEN
];
103 on_trace(__func__
, igmp
->interface
, from
);
107 if (igmp
->mtrace_only
)
110 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
111 if (PIM_DEBUG_GM_PACKETS
)
113 "Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
114 from_str
, ifp
->name
, igmp_msg_len
,
118 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
120 "Recv IGMPv2 REPORT from %s on %s: size=%d with invalid checksum",
121 from_str
, ifp
->name
, igmp_msg_len
);
125 /* Collecting IGMP Rx stats */
126 igmp
->igmp_stats
.report_v2
++;
128 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
130 if (PIM_DEBUG_GM_PACKETS
) {
131 pim_inet4_dump("<dst?>", group_addr
, group_str
,
133 zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s", from_str
,
134 ifp
->name
, group_str
);
140 * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
141 * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
144 if (pim_is_grp_ssm(pim_ifp
->pim
, group_addr
)) {
145 if (PIM_DEBUG_GM_PACKETS
) {
147 "Ignoring IGMPv2 group record %pI4 from %s on %s exclude mode in SSM range",
148 &group_addr
.s_addr
, from_str
, ifp
->name
);
156 * 7.3.2. In the Presence of Older Version Group Members
158 * When Group Compatibility Mode is IGMPv2, a router internally
159 * translates the following IGMPv2 messages for that group to their
160 * IGMPv3 equivalents:
162 * IGMPv2 Message IGMPv3 Equivalent
163 * -------------- -----------------
167 igmpv3_report_isex(igmp
, from
, group_addr
, 0, NULL
, 1);
172 int igmp_v2_recv_leave(struct gm_sock
*igmp
, struct ip
*ip_hdr
,
173 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
175 struct interface
*ifp
= igmp
->interface
;
176 struct in_addr group_addr
;
177 char group_str
[INET_ADDRSTRLEN
];
178 struct in_addr from
= ip_hdr
->ip_src
;
180 on_trace(__func__
, igmp
->interface
, from
);
182 if (igmp
->mtrace_only
)
185 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
186 if (PIM_DEBUG_GM_PACKETS
)
188 "Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
189 from_str
, ifp
->name
, igmp_msg_len
,
193 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
195 "Recv IGMPv2 LEAVE from %s on %s with invalid checksum",
196 from_str
, ifp
->name
);
201 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
203 if (PIM_DEBUG_GM_PACKETS
) {
204 pim_inet4_dump("<dst?>", group_addr
, group_str
,
206 zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s", from_str
,
207 ifp
->name
, group_str
);
210 * As per RFC 2236, section 9:
211 Message Type Destination Group
212 ------------ -----------------
213 General Query ALL-SYSTEMS (224.0.0.1)
214 Group-Specific Query The group being queried
215 Membership Report The group being reported
216 Leave Message ALL-ROUTERS (224.0.0.2)
218 Note: in older (i.e., non-standard and now obsolete) versions of
219 IGMPv2, hosts send Leave Messages to the group being left. A
220 router SHOULD accept Leave Messages addressed to the group being
221 left in the interests of backwards compatibility with such hosts.
222 In all cases, however, hosts MUST send to the ALL-ROUTERS address
223 to be compliant with this specification.
225 if ((ntohl(ip_hdr
->ip_dst
.s_addr
) != INADDR_ALLRTRS_GROUP
)
226 && (ip_hdr
->ip_dst
.s_addr
!= group_addr
.s_addr
)) {
227 if (PIM_DEBUG_GM_EVENTS
)
229 "IGMPv2 Leave message is ignored since received on address other than ALL-ROUTERS or Group-address");
233 /* Collecting IGMP Rx stats */
234 igmp
->igmp_stats
.leave_v2
++;
238 * 7.3.2. In the Presence of Older Version Group Members
240 * When Group Compatibility Mode is IGMPv2, a router internally
241 * translates the following IGMPv2 messages for that group to their
242 * IGMPv3 equivalents:
244 * IGMPv2 Message IGMPv3 Equivalent
245 * -------------- -----------------
249 igmpv3_report_toin(igmp
, from
, group_addr
, 0, NULL
);