]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv2.c
Merge remote-tracking branch 'origin/stable/3.0'
[mirror_frr.git] / pimd / pim_igmpv2.c
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_igmp.h"
25 #include "pim_igmpv2.h"
26 #include "pim_igmpv3.h"
27 #include "pim_str.h"
28 #include "pim_time.h"
29 #include "pim_util.h"
30
31
32 static void
33 on_trace (const char *label,
34 struct interface *ifp, struct in_addr from)
35 {
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",
40 label, from_str, ifp->name);
41 }
42 }
43
44 void
45 igmp_v2_send_query (struct igmp_group *group,
46 int fd,
47 const char *ifname,
48 char *query_buf,
49 struct in_addr dst_addr,
50 struct in_addr group_addr,
51 int query_max_response_time_dsec)
52 {
53 ssize_t msg_size = 8;
54 uint8_t max_resp_code;
55 ssize_t sent;
56 struct sockaddr_in to;
57 socklen_t tolen;
58 uint16_t checksum;
59
60 /* max_resp_code must be non-zero else this will look like an IGMP v1 query */
61 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
62 zassert(max_resp_code > 0);
63
64 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
65 query_buf[1] = max_resp_code;
66 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = 0; /* for computing checksum */
67 memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
68
69 checksum = in_cksum(query_buf, msg_size);
70 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
71
72 if (PIM_DEBUG_IGMP_PACKETS) {
73 char dst_str[INET_ADDRSTRLEN];
74 char group_str[INET_ADDRSTRLEN];
75 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
76 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
77 zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
78 dst_str, ifname, group_str);
79 }
80
81 memset(&to, 0, sizeof(to));
82 to.sin_family = AF_INET;
83 to.sin_addr = dst_addr;
84 tolen = sizeof(to);
85
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, sizeof(group_str));
93 if (sent < 0) {
94 zlog_warn("Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
95 dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno));
96 }
97 else {
98 zlog_warn("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);
100 }
101 return;
102 }
103 }
104
105 int
106 igmp_v2_recv_report (struct igmp_sock *igmp,
107 struct in_addr from, const char *from_str,
108 char *igmp_msg, int igmp_msg_len)
109 {
110 struct interface *ifp = igmp->interface;
111 struct in_addr group_addr;
112 char group_str[INET_ADDRSTRLEN];
113
114 on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
115
116 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
117 zlog_warn("Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
118 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
119 return -1;
120 }
121
122 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
123
124 if (PIM_DEBUG_IGMP_PACKETS) {
125 pim_inet4_dump("<dst?>", group_addr, group_str, sizeof(group_str));
126 zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s",
127 from_str, ifp->name, group_str);
128 }
129
130 /*
131 * RFC 3376
132 * 7.3.2. In the Presence of Older Version Group Members
133 *
134 * When Group Compatibility Mode is IGMPv2, a router internally
135 * translates the following IGMPv2 messages for that group to their
136 * IGMPv3 equivalents:
137 *
138 * IGMPv2 Message IGMPv3 Equivalent
139 * -------------- -----------------
140 * Report IS_EX( {} )
141 * Leave TO_IN( {} )
142 */
143 igmpv3_report_isex (igmp, from, group_addr, 0, NULL, 1);
144
145 return 0;
146 }
147
148 int
149 igmp_v2_recv_leave (struct igmp_sock *igmp,
150 struct in_addr from, const char *from_str,
151 char *igmp_msg, int igmp_msg_len)
152 {
153 struct interface *ifp = igmp->interface;
154 struct in_addr group_addr;
155 char group_str[INET_ADDRSTRLEN];
156
157 on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
158
159 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
160 zlog_warn("Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
161 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
162 return -1;
163 }
164
165 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
166
167 if (PIM_DEBUG_IGMP_PACKETS) {
168 pim_inet4_dump("<dst?>", group_addr, group_str, sizeof(group_str));
169 zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s",
170 from_str, ifp->name, group_str);
171 }
172
173 /*
174 * RFC 3376
175 * 7.3.2. In the Presence of Older Version Group Members
176 *
177 * When Group Compatibility Mode is IGMPv2, a router internally
178 * translates the following IGMPv2 messages for that group to their
179 * IGMPv3 equivalents:
180 *
181 * IGMPv2 Message IGMPv3 Equivalent
182 * -------------- -----------------
183 * Report IS_EX( {} )
184 * Leave TO_IN( {} )
185 */
186 igmpv3_report_toin (igmp, from, group_addr, 0, NULL);
187
188 return 0;
189 }