]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_ssmpingd.c
pim6d: fix "show ipv6 mld interface" command
[mirror_frr.git] / pimd / pim_ssmpingd.c
1 /*
2 * PIM for Quagga
3 * Copyright (C) 2008 Everton da Silva Marques
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <zebra.h>
21
22 #include "if.h"
23 #include "log.h"
24 #include "memory.h"
25 #include "sockopt.h"
26 #include "vrf.h"
27 #include "lib_errors.h"
28
29 #include "pimd.h"
30 #include "pim_instance.h"
31 #include "pim_ssmpingd.h"
32 #include "pim_time.h"
33 #include "pim_sock.h"
34 #include "network.h"
35
36 #if PIM_IPV == 4
37 static const char *const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
38 #else
39 static const char *const PIM_SSMPINGD_REPLY_GROUP = "ff3e::4321:1234";
40 #endif
41
42 enum { PIM_SSMPINGD_REQUEST = 'Q', PIM_SSMPINGD_REPLY = 'A' };
43
44 static void ssmpingd_read_on(struct ssmpingd_sock *ss);
45
46 void pim_ssmpingd_init(struct pim_instance *pim)
47 {
48 int result;
49
50 assert(!pim->ssmpingd_list);
51
52 result = inet_pton(PIM_AF, PIM_SSMPINGD_REPLY_GROUP,
53 &pim->ssmpingd_group_addr);
54
55 assert(result > 0);
56 }
57
58 void pim_ssmpingd_destroy(struct pim_instance *pim)
59 {
60 if (pim->ssmpingd_list)
61 list_delete(&pim->ssmpingd_list);
62 }
63
64 static struct ssmpingd_sock *ssmpingd_find(struct pim_instance *pim,
65 pim_addr source_addr)
66 {
67 struct listnode *node;
68 struct ssmpingd_sock *ss;
69
70 if (!pim->ssmpingd_list)
71 return 0;
72
73 for (ALL_LIST_ELEMENTS_RO(pim->ssmpingd_list, node, ss))
74 if (!pim_addr_cmp(source_addr, ss->source_addr))
75 return ss;
76
77 return 0;
78 }
79
80 static void ssmpingd_free(struct ssmpingd_sock *ss)
81 {
82 XFREE(MTYPE_PIM_SSMPINGD, ss);
83 }
84
85 #if PIM_IPV == 4
86 static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
87 {
88 /* Needed to obtain destination address from recvmsg() */
89 #if defined(HAVE_IP_PKTINFO)
90 /* Linux and Solaris IP_PKTINFO */
91 int opt = 1;
92 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
93 zlog_warn(
94 "%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
95 __func__, fd, errno, safe_strerror(errno));
96 }
97 #elif defined(HAVE_IP_RECVDSTADDR)
98 /* BSD IP_RECVDSTADDR */
99 int opt = 1;
100 if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
101 zlog_warn(
102 "%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
103 __func__, fd, errno, safe_strerror(errno));
104 }
105 #else
106 flog_err(
107 EC_LIB_DEVELOPMENT,
108 "%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
109 __FILE__, __func__);
110 close(fd);
111 return -1;
112 #endif
113
114 if (setsockopt_ipv4_multicast_loop(fd, 0)) {
115 zlog_warn(
116 "%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
117 __func__, fd, errno, safe_strerror(errno));
118 close(fd);
119 return PIM_SOCK_ERR_LOOP;
120 }
121
122 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&addr,
123 sizeof(addr))) {
124 zlog_warn(
125 "%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
126 __func__, fd, errno, safe_strerror(errno));
127 close(fd);
128 return -1;
129 }
130
131 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&mttl,
132 sizeof(mttl))) {
133 zlog_warn(
134 "%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
135 __func__, mttl, fd, errno, safe_strerror(errno));
136 close(fd);
137 return -1;
138 }
139
140 return 0;
141 }
142 #else
143 static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
144 {
145 setsockopt_ipv6_pktinfo(fd, 1);
146 setsockopt_ipv6_multicast_hops(fd, mttl);
147
148 if (setsockopt_ipv6_multicast_loop(fd, 0)) {
149 zlog_warn(
150 "%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
151 __func__, fd, errno, safe_strerror(errno));
152 close(fd);
153 return PIM_SOCK_ERR_LOOP;
154 }
155
156 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&addr,
157 sizeof(addr))) {
158 zlog_warn(
159 "%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
160 __func__, fd, errno, safe_strerror(errno));
161 close(fd);
162 return -1;
163 }
164 return 0;
165 }
166 #endif
167
168
169 static int ssmpingd_socket(pim_addr addr, int port, int mttl)
170 {
171 struct sockaddr_storage sockaddr;
172 int fd;
173 int ret;
174 socklen_t len = sizeof(sockaddr);
175
176 fd = socket(PIM_AF, SOCK_DGRAM, IPPROTO_UDP);
177 if (fd < 0) {
178 flog_err_sys(EC_LIB_SOCKET,
179 "%s: could not create socket: errno=%d: %s",
180 __func__, errno, safe_strerror(errno));
181 return -1;
182 }
183
184 pim_socket_getsockname(fd, (struct sockaddr *)&sockaddr, &len);
185
186 if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
187 zlog_warn(
188 "%s: bind(fd=%d,addr=%pSUp,port=%d,len=%zu) failure: errno=%d: %s",
189 __func__, fd, &sockaddr, port, sizeof(sockaddr), errno,
190 safe_strerror(errno));
191 close(fd);
192 return -1;
193 }
194
195 set_nonblocking(fd);
196 sockopt_reuseaddr(fd);
197
198 ret = ssmpingd_setsockopt(fd, addr, mttl);
199 if (ret) {
200 zlog_warn("ssmpingd_setsockopt failed");
201 close(fd);
202 return -1;
203 }
204
205 return fd;
206 }
207
208 static void ssmpingd_delete(struct ssmpingd_sock *ss)
209 {
210 assert(ss);
211
212 THREAD_OFF(ss->t_sock_read);
213
214 if (close(ss->sock_fd)) {
215 zlog_warn(
216 "%s: failure closing ssmpingd sock_fd=%d for source %pPA: errno=%d: %s",
217 __func__, ss->sock_fd, &ss->source_addr, errno,
218 safe_strerror(errno));
219 /* warning only */
220 }
221
222 listnode_delete(ss->pim->ssmpingd_list, ss);
223 ssmpingd_free(ss);
224 }
225
226 static void ssmpingd_sendto(struct ssmpingd_sock *ss, const uint8_t *buf,
227 int len, struct sockaddr_storage to)
228 {
229 socklen_t tolen = sizeof(to);
230 int sent;
231
232 sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
233 (struct sockaddr *)&to, tolen);
234 if (sent != len) {
235 if (sent < 0) {
236 zlog_warn(
237 "%s: sendto() failure to %pSUp,fd=%d len=%d: errno=%d: %s",
238 __func__, &to, ss->sock_fd, len, errno,
239 safe_strerror(errno));
240 } else {
241 zlog_warn(
242 "%s: sendto() partial to %pSUp, fd=%d len=%d: sent=%d",
243 __func__, &to, ss->sock_fd, len, sent);
244 }
245 }
246 }
247
248 static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
249 {
250 struct interface *ifp;
251 struct sockaddr_storage from;
252 struct sockaddr_storage to;
253 socklen_t fromlen = sizeof(from);
254 socklen_t tolen = sizeof(to);
255 ifindex_t ifindex = -1;
256 uint8_t buf[1000];
257 int len;
258
259 ++ss->requests;
260
261 len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf), &from,
262 &fromlen, &to, &tolen, &ifindex);
263
264 if (len < 0) {
265 zlog_warn(
266 "%s: failure receiving ssmping for source %pPA on fd=%d: errno=%d: %s",
267 __func__, &ss->source_addr, ss->sock_fd, errno,
268 safe_strerror(errno));
269 return -1;
270 }
271
272 ifp = if_lookup_by_index(ifindex, ss->pim->vrf->vrf_id);
273
274 if (buf[0] != PIM_SSMPINGD_REQUEST) {
275 zlog_warn(
276 "%s: bad ssmping type=%d from %pSUp to %pSUp on interface %s ifindex=%d fd=%d src=%pPA",
277 __func__, buf[0], &from, &to,
278 ifp ? ifp->name : "<iface?>", ifindex, ss->sock_fd,
279 &ss->source_addr);
280 return 0;
281 }
282
283 if (PIM_DEBUG_SSMPINGD) {
284 zlog_debug(
285 "%s: recv ssmping from %pSUp, to %pSUp, on interface %s ifindex=%d fd=%d src=%pPA",
286 __func__, &from, &to, ifp ? ifp->name : "<iface?>",
287 ifindex, ss->sock_fd, &ss->source_addr);
288 }
289
290 buf[0] = PIM_SSMPINGD_REPLY;
291
292 /* unicast reply */
293 ssmpingd_sendto(ss, buf, len, from);
294
295 /* multicast reply */
296 memcpy(&from, &ss->pim->ssmpingd_group_addr, sizeof(pim_addr));
297 ssmpingd_sendto(ss, buf, len, from);
298
299 return 0;
300 }
301
302 static void ssmpingd_sock_read(struct thread *t)
303 {
304 struct ssmpingd_sock *ss;
305
306 ss = THREAD_ARG(t);
307
308 ssmpingd_read_msg(ss);
309
310 /* Keep reading */
311 ssmpingd_read_on(ss);
312 }
313
314 static void ssmpingd_read_on(struct ssmpingd_sock *ss)
315 {
316 thread_add_read(router->master, ssmpingd_sock_read, ss, ss->sock_fd,
317 &ss->t_sock_read);
318 }
319
320 static struct ssmpingd_sock *ssmpingd_new(struct pim_instance *pim,
321 pim_addr source_addr)
322 {
323 struct ssmpingd_sock *ss;
324 int sock_fd;
325
326 if (!pim->ssmpingd_list) {
327 pim->ssmpingd_list = list_new();
328 pim->ssmpingd_list->del = (void (*)(void *))ssmpingd_free;
329 }
330
331 sock_fd =
332 ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
333 if (sock_fd < 0) {
334 zlog_warn("%s: ssmpingd_socket() failure for source %pPA",
335 __func__, &source_addr);
336 return 0;
337 }
338
339 ss = XCALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
340
341 ss->pim = pim;
342 ss->sock_fd = sock_fd;
343 ss->t_sock_read = NULL;
344 ss->source_addr = source_addr;
345 ss->creation = pim_time_monotonic_sec();
346 ss->requests = 0;
347
348 listnode_add(pim->ssmpingd_list, ss);
349
350 ssmpingd_read_on(ss);
351
352 return ss;
353 }
354
355 int pim_ssmpingd_start(struct pim_instance *pim, pim_addr source_addr)
356 {
357 struct ssmpingd_sock *ss;
358
359 ss = ssmpingd_find(pim, source_addr);
360 if (ss) {
361 /* silently ignore request to recreate entry */
362 return 0;
363 }
364
365 zlog_info("%s: starting ssmpingd for source %pPAs", __func__,
366 &source_addr);
367
368 ss = ssmpingd_new(pim, source_addr);
369 if (!ss) {
370 zlog_warn("%s: ssmpingd_new() failure for source %pPAs",
371 __func__, &source_addr);
372 return -1;
373 }
374
375 return 0;
376 }
377
378 int pim_ssmpingd_stop(struct pim_instance *pim, pim_addr source_addr)
379 {
380 struct ssmpingd_sock *ss;
381
382 ss = ssmpingd_find(pim, source_addr);
383 if (!ss) {
384 zlog_warn("%s: could not find ssmpingd for source %pPAs",
385 __func__, &source_addr);
386 return -1;
387 }
388
389 zlog_info("%s: stopping ssmpingd for source %pPAs", __func__,
390 &source_addr);
391
392 ssmpingd_delete(ss);
393
394 return 0;
395 }