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