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