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"
32 static void on_trace(const char *label
, struct interface
*ifp
,
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
);
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
)
48 uint8_t max_resp_code
;
50 struct sockaddr_in to
;
54 /* max_resp_code must be non-zero else this will look like an IGMP v1
56 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
57 assert(max_resp_code
> 0);
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
));
65 checksum
= in_cksum(query_buf
, msg_size
);
66 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
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
,
74 zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
75 dst_str
, ifname
, group_str
);
78 memset(&to
, 0, sizeof(to
));
79 to
.sin_family
= AF_INET
;
80 to
.sin_addr
= dst_addr
;
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
,
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
));
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
);
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
)
108 struct interface
*ifp
= igmp
->interface
;
109 struct in_addr group_addr
;
110 char group_str
[INET_ADDRSTRLEN
];
112 on_trace(__func__
, igmp
->interface
, from
);
114 if (igmp
->mtrace_only
)
117 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
118 if (PIM_DEBUG_IGMP_PACKETS
)
120 "Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
121 from_str
, ifp
->name
, igmp_msg_len
,
125 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
127 "Recv IGMPv2 REPORT from %s on %s: size=%d with invalid checksum",
128 from_str
, ifp
->name
, igmp_msg_len
);
132 /* Collecting IGMP Rx stats */
133 igmp
->rx_stats
.report_v2
++;
135 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
137 if (PIM_DEBUG_IGMP_PACKETS
) {
138 pim_inet4_dump("<dst?>", group_addr
, group_str
,
140 zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s", from_str
,
141 ifp
->name
, group_str
);
146 * 7.3.2. In the Presence of Older Version Group Members
148 * When Group Compatibility Mode is IGMPv2, a router internally
149 * translates the following IGMPv2 messages for that group to their
150 * IGMPv3 equivalents:
152 * IGMPv2 Message IGMPv3 Equivalent
153 * -------------- -----------------
157 igmpv3_report_isex(igmp
, from
, group_addr
, 0, NULL
, 1);
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
)
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
;
170 on_trace(__func__
, igmp
->interface
, from
);
172 if (igmp
->mtrace_only
)
175 if (igmp_msg_len
!= IGMP_V12_MSG_SIZE
) {
176 if (PIM_DEBUG_IGMP_PACKETS
)
178 "Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
179 from_str
, ifp
->name
, igmp_msg_len
,
183 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
185 "Recv IGMPv2 LEAVE from %s on %s with invalid checksum",
186 from_str
, ifp
->name
);
191 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
193 if (PIM_DEBUG_IGMP_PACKETS
) {
194 pim_inet4_dump("<dst?>", group_addr
, group_str
,
196 zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s", from_str
,
197 ifp
->name
, group_str
);
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)
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.
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
)
219 "IGMPv2 Leave message is ignored since received on address other than ALL-ROUTERS or Group-address");
223 /* Collecting IGMP Rx stats */
224 igmp
->rx_stats
.leave_v2
++;
228 * 7.3.2. In the Presence of Older Version Group Members
230 * When Group Compatibility Mode is IGMPv2, a router internally
231 * translates the following IGMPv2 messages for that group to their
232 * IGMPv3 equivalents:
234 * IGMPv2 Message IGMPv3 Equivalent
235 * -------------- -----------------
239 igmpv3_report_toin(igmp
, from
, group_addr
, 0, NULL
);