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