]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd_packet.c
Merge pull request #3611 from opensourcerouting/nb-fixes
[mirror_frr.git] / bfdd / bfd_packet.c
1 /*********************************************************************
2 * Copyright 2017 Cumulus Networks, Inc. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; see the file COPYING; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * bfd_packet.c: implements the BFD protocol packet handling.
19 *
20 * Authors
21 * -------
22 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
23 * Kanna Rajagopal [kanna@cumulusnetworks.com]
24 * Radhika Mahankali [Radhika@cumulusnetworks.com]
25 */
26
27 #include <zebra.h>
28
29 #ifdef BFD_LINUX
30 #include <linux/if_packet.h>
31 #endif /* BFD_LINUX */
32
33 #include <netinet/if_ether.h>
34 #include <netinet/udp.h>
35
36 #include "lib/sockopt.h"
37
38 #include "bfd.h"
39
40
41 /*
42 * Prototypes
43 */
44 static int ptm_bfd_process_echo_pkt(int s);
45 int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
46 size_t datalen);
47
48 static void bfd_sd_reschedule(int sd);
49 ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
50 char *port, size_t portlen, char *vrfname,
51 size_t vrfnamelen, struct sockaddr_any *local,
52 struct sockaddr_any *peer);
53 ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
54 char *port, size_t portlen, char *vrfname,
55 size_t vrfnamelen, struct sockaddr_any *local,
56 struct sockaddr_any *peer);
57 int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
58 struct sockaddr *to, socklen_t tolen);
59 int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr);
60
61 /* socket related prototypes */
62 static void bp_set_ipopts(int sd);
63 static void bp_bind_ip(int sd, uint16_t port);
64 static void bp_set_ipv6opts(int sd);
65 static void bp_bind_ipv6(int sd, uint16_t port);
66
67
68 /*
69 * Functions
70 */
71 int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
72 size_t datalen)
73 {
74 struct sockaddr *sa;
75 struct sockaddr_in sin;
76 struct sockaddr_in6 sin6;
77 socklen_t slen;
78 ssize_t rv;
79 int sd = -1;
80
81 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
82 memset(&sin6, 0, sizeof(sin6));
83 sin6.sin6_family = AF_INET6;
84 sin6.sin6_addr = bs->shop.peer.sa_sin6.sin6_addr;
85 sin6.sin6_port =
86 (port) ? *port
87 : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
88 ? htons(BFD_DEF_MHOP_DEST_PORT)
89 : htons(BFD_DEFDESTPORT);
90
91 sd = bs->sock;
92 sa = (struct sockaddr *)&sin6;
93 slen = sizeof(sin6);
94 } else {
95 memset(&sin, 0, sizeof(sin));
96 sin.sin_family = AF_INET;
97 sin.sin_addr = bs->shop.peer.sa_sin.sin_addr;
98 sin.sin_port =
99 (port) ? *port
100 : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
101 ? htons(BFD_DEF_MHOP_DEST_PORT)
102 : htons(BFD_DEFDESTPORT);
103
104 sd = bs->sock;
105 sa = (struct sockaddr *)&sin;
106 slen = sizeof(sin);
107 }
108
109 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
110 sa->sa_len = slen;
111 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
112 rv = sendto(sd, data, datalen, 0, sa, slen);
113 if (rv <= 0) {
114 log_debug("packet-send: send failure: %s", strerror(errno));
115 return -1;
116 }
117 if (rv < (ssize_t)datalen)
118 log_debug("packet-send: send partial", strerror(errno));
119
120 return 0;
121 }
122
123 void ptm_bfd_echo_snd(struct bfd_session *bfd)
124 {
125 struct sockaddr_any *sa;
126 socklen_t salen;
127 int sd;
128 struct bfd_echo_pkt bep;
129 struct sockaddr_in sin;
130 struct sockaddr_in6 sin6;
131
132 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
133 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
134
135 memset(&bep, 0, sizeof(bep));
136 bep.ver = BFD_ECHO_VERSION;
137 bep.len = BFD_ECHO_PKT_LEN;
138 bep.my_discr = htonl(bfd->discrs.my_discr);
139
140 sa = BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MH) ? &bfd->mhop.peer
141 : &bfd->shop.peer;
142 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
143 sd = bglobal.bg_echov6;
144 sin6 = sa->sa_sin6;
145 sin6.sin6_port = htons(BFD_DEF_ECHO_PORT);
146 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
147 sin6.sin6_len = sizeof(sin6);
148 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
149
150 sa = (struct sockaddr_any *)&sin6;
151 salen = sizeof(sin6);
152 } else {
153 sd = bglobal.bg_echo;
154 sin = sa->sa_sin;
155 sin.sin_port = htons(BFD_DEF_ECHO_PORT);
156 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
157 sin.sin_len = sizeof(sin);
158 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
159
160 sa = (struct sockaddr_any *)&sin;
161 salen = sizeof(sin);
162 }
163 if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep),
164 (struct sockaddr *)sa, salen)
165 == -1)
166 return;
167
168 bfd->stats.tx_echo_pkt++;
169 }
170
171 static int ptm_bfd_process_echo_pkt(int s)
172 {
173 struct bfd_session *bfd;
174 uint32_t my_discr = 0;
175 uint8_t ttl = 0;
176
177 /* Receive and parse echo packet. */
178 if (bp_bfd_echo_in(s, &ttl, &my_discr) == -1)
179 return 0;
180
181 /* Your discriminator not zero - use it to find session */
182 bfd = bfd_id_lookup(my_discr);
183 if (bfd == NULL) {
184 log_debug("echo-packet: no matching session (id:%u)", my_discr);
185 return -1;
186 }
187
188 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
189 log_debug("echo-packet: echo disabled [%s] (id:%u)",
190 bs_to_string(bfd), my_discr);
191 return -1;
192 }
193
194 bfd->stats.rx_echo_pkt++;
195
196 /* Compute detect time */
197 bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO;
198
199 /* Update echo receive timeout. */
200 if (bfd->echo_detect_TO > 0)
201 bfd_echo_recvtimer_update(bfd);
202
203 return 0;
204 }
205
206 void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
207 {
208 struct bfd_pkt cp;
209
210 /* Set fields according to section 6.5.7 */
211 cp.diag = bfd->local_diag;
212 BFD_SETVER(cp.diag, BFD_VERSION);
213 cp.flags = 0;
214 BFD_SETSTATE(cp.flags, bfd->ses_state);
215 BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND);
216 BFD_SETPBIT(cp.flags, bfd->polling);
217 BFD_SETFBIT(cp.flags, fbit);
218 cp.detect_mult = bfd->detect_mult;
219 cp.len = BFD_PKT_LEN;
220 cp.discrs.my_discr = htonl(bfd->discrs.my_discr);
221 cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
222 if (bfd->polling) {
223 cp.timers.desired_min_tx =
224 htonl(bfd->new_timers.desired_min_tx);
225 cp.timers.required_min_rx =
226 htonl(bfd->new_timers.required_min_rx);
227 } else {
228 cp.timers.desired_min_tx = htonl(bfd->timers.desired_min_tx);
229 cp.timers.required_min_rx = htonl(bfd->timers.required_min_rx);
230 }
231 cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo);
232
233 if (_ptm_bfd_send(bfd, NULL, &cp, BFD_PKT_LEN) != 0)
234 return;
235
236 bfd->stats.tx_ctrl_pkt++;
237 }
238
239 ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
240 char *port, size_t portlen, char *vrfname,
241 size_t vrfnamelen, struct sockaddr_any *local,
242 struct sockaddr_any *peer)
243 {
244 struct interface *ifp;
245 struct cmsghdr *cm;
246 int ifindex;
247 ssize_t mlen;
248 struct sockaddr_in msgaddr;
249 struct msghdr msghdr;
250 struct iovec iov[1];
251 uint8_t cmsgbuf[255];
252
253 port[0] = '\0';
254
255 /* Prepare the recvmsg params. */
256 iov[0].iov_base = msgbuf;
257 iov[0].iov_len = msgbuflen;
258
259 memset(&msghdr, 0, sizeof(msghdr));
260 msghdr.msg_name = &msgaddr;
261 msghdr.msg_namelen = sizeof(msgaddr);
262 msghdr.msg_iov = iov;
263 msghdr.msg_iovlen = 1;
264 msghdr.msg_control = cmsgbuf;
265 msghdr.msg_controllen = sizeof(cmsgbuf);
266
267 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
268 if (mlen == -1) {
269 if (errno != EAGAIN)
270 log_error("ipv4-recv: recv failed: %s",
271 strerror(errno));
272
273 return -1;
274 }
275
276 /* Get source address */
277 peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
278
279 /* Get and check TTL */
280 for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
281 cm = CMSG_NXTHDR(&msghdr, cm)) {
282 if (cm->cmsg_level != IPPROTO_IP)
283 continue;
284
285 switch (cm->cmsg_type) {
286 #ifdef BFD_LINUX
287 case IP_TTL: {
288 uint32_t ttlval;
289
290 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
291 if (ttlval > 255) {
292 log_debug("ipv4-recv: invalid TTL: %u", ttlval);
293 return -1;
294 }
295 *ttl = ttlval;
296 break;
297 }
298
299 case IP_PKTINFO: {
300 struct in_pktinfo *pi =
301 (struct in_pktinfo *)CMSG_DATA(cm);
302
303 if (pi == NULL)
304 break;
305
306 local->sa_sin.sin_family = AF_INET;
307 local->sa_sin.sin_addr = pi->ipi_addr;
308 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
309 local->sa_sin.sin_len = sizeof(local->sa_sin);
310 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
311
312 ifp = if_lookup_by_index(pi->ipi_ifindex, VRF_DEFAULT);
313 if (ifp == NULL)
314 break;
315
316 if (strlcpy(port, ifp->name, portlen) >= portlen)
317 log_debug(
318 "ipv4-recv: interface name truncated");
319 break;
320 }
321 #endif /* BFD_LINUX */
322 #ifdef BFD_BSD
323 case IP_RECVTTL: {
324 memcpy(ttl, CMSG_DATA(cm), sizeof(*ttl));
325 break;
326 }
327
328 case IP_RECVDSTADDR: {
329 struct in_addr ia;
330
331 memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
332 local->sa_sin.sin_family = AF_INET;
333 local->sa_sin.sin_addr = ia;
334 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
335 local->sa_sin.sin_len = sizeof(local->sa_sin);
336 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
337 break;
338 }
339 #endif /* BFD_BSD */
340
341 default:
342 /*
343 * On *BSDs we expect to land here when skipping
344 * the IP_RECVIF header. It will be handled by
345 * getsockopt_ifindex() below.
346 */
347 /* NOTHING */
348 break;
349 }
350 }
351
352 /* OS agnostic way of getting interface name. */
353 if (port[0] == 0) {
354 ifindex = getsockopt_ifindex(AF_INET, &msghdr);
355 if (ifindex <= 0)
356 return mlen;
357
358 ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
359 if (ifp == NULL)
360 return mlen;
361
362 if (strlcpy(port, ifp->name, portlen) >= portlen)
363 log_debug("ipv4-recv: interface name truncated");
364 }
365
366 return mlen;
367 }
368
369 ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
370 char *port, size_t portlen, char *vrfname,
371 size_t vrfnamelen, struct sockaddr_any *local,
372 struct sockaddr_any *peer)
373 {
374 struct interface *ifp;
375 struct cmsghdr *cm;
376 struct in6_pktinfo *pi6 = NULL;
377 int ifindex = 0;
378 ssize_t mlen;
379 uint32_t ttlval;
380 struct sockaddr_in6 msgaddr6;
381 struct msghdr msghdr6;
382 struct iovec iov[1];
383 uint8_t cmsgbuf6[255];
384
385 /* Prepare the recvmsg params. */
386 iov[0].iov_base = msgbuf;
387 iov[0].iov_len = msgbuflen;
388
389 memset(&msghdr6, 0, sizeof(msghdr6));
390 msghdr6.msg_name = &msgaddr6;
391 msghdr6.msg_namelen = sizeof(msgaddr6);
392 msghdr6.msg_iov = iov;
393 msghdr6.msg_iovlen = 1;
394 msghdr6.msg_control = cmsgbuf6;
395 msghdr6.msg_controllen = sizeof(cmsgbuf6);
396
397 mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
398 if (mlen == -1) {
399 if (errno != EAGAIN)
400 log_error("ipv6-recv: recv failed: %s",
401 strerror(errno));
402
403 return -1;
404 }
405
406 /* Get source address */
407 peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
408
409 /* Get and check TTL */
410 for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
411 cm = CMSG_NXTHDR(&msghdr6, cm)) {
412 if (cm->cmsg_level != IPPROTO_IPV6)
413 continue;
414
415 if (cm->cmsg_type == IPV6_HOPLIMIT) {
416 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
417 if (ttlval > 255) {
418 log_debug("ipv6-recv: invalid TTL: %u", ttlval);
419 return -1;
420 }
421
422 *ttl = ttlval;
423 } else if (cm->cmsg_type == IPV6_PKTINFO) {
424 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
425 if (pi6) {
426 local->sa_sin6.sin6_family = AF_INET6;
427 local->sa_sin6.sin6_addr = pi6->ipi6_addr;
428 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
429 local->sa_sin6.sin6_len = sizeof(local->sa_sin6);
430 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
431
432 ifindex = pi6->ipi6_ifindex;
433 ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
434 if (ifp == NULL)
435 break;
436
437 if (strlcpy(port, ifp->name, portlen)
438 >= portlen)
439 log_debug(
440 "ipv6-recv: interface name truncated");
441 }
442 }
443 }
444
445 /* Set scope ID for link local addresses. */
446 if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr))
447 peer->sa_sin6.sin6_scope_id = ifindex;
448 if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr))
449 local->sa_sin6.sin6_scope_id = ifindex;
450
451 return mlen;
452 }
453
454 static void bfd_sd_reschedule(int sd)
455 {
456 if (sd == bglobal.bg_shop) {
457 THREAD_OFF(bglobal.bg_ev[0]);
458 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
459 &bglobal.bg_ev[0]);
460 } else if (sd == bglobal.bg_mhop) {
461 THREAD_OFF(bglobal.bg_ev[1]);
462 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop,
463 &bglobal.bg_ev[1]);
464 } else if (sd == bglobal.bg_shop6) {
465 THREAD_OFF(bglobal.bg_ev[2]);
466 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6,
467 &bglobal.bg_ev[2]);
468 } else if (sd == bglobal.bg_mhop6) {
469 THREAD_OFF(bglobal.bg_ev[3]);
470 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6,
471 &bglobal.bg_ev[3]);
472 } else if (sd == bglobal.bg_echo) {
473 THREAD_OFF(bglobal.bg_ev[4]);
474 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo,
475 &bglobal.bg_ev[4]);
476 } else if (sd == bglobal.bg_echov6) {
477 THREAD_OFF(bglobal.bg_ev[5]);
478 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6,
479 &bglobal.bg_ev[5]);
480 }
481 }
482
483 static void cp_debug(bool mhop, struct sockaddr_any *peer,
484 struct sockaddr_any *local, const char *port,
485 const char *vrf, const char *fmt, ...)
486 {
487 char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
488 va_list vl;
489
490 if (peer->sa_sin.sin_family)
491 snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
492 else
493 peerstr[0] = 0;
494
495 if (local->sa_sin.sin_family)
496 snprintf(localstr, sizeof(localstr), " local:%s",
497 satostr(local));
498 else
499 localstr[0] = 0;
500
501 if (port[0])
502 snprintf(portstr, sizeof(portstr), " port:%s", port);
503 else
504 portstr[0] = 0;
505
506 if (vrf[0])
507 snprintf(vrfstr, sizeof(vrfstr), " vrf:%s", port);
508 else
509 vrfstr[0] = 0;
510
511 va_start(vl, fmt);
512 vsnprintf(buf, sizeof(buf), fmt, vl);
513 va_end(vl);
514
515 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
516 mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
517 }
518
519 int bfd_recv_cb(struct thread *t)
520 {
521 int sd = THREAD_FD(t);
522 struct bfd_session *bfd;
523 struct bfd_pkt *cp;
524 bool is_mhop;
525 ssize_t mlen = 0;
526 uint32_t oldEchoXmt_TO, oldXmtTime;
527 uint8_t ttl;
528 struct sockaddr_any local, peer;
529 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
530 uint8_t msgbuf[1516];
531
532 /* Schedule next read. */
533 bfd_sd_reschedule(sd);
534
535 /* Handle echo packets. */
536 if (sd == bglobal.bg_echo || sd == bglobal.bg_echov6) {
537 ptm_bfd_process_echo_pkt(sd);
538 return 0;
539 }
540
541 /* Sanitize input/output. */
542 memset(port, 0, sizeof(port));
543 memset(vrfname, 0, sizeof(vrfname));
544 memset(&local, 0, sizeof(local));
545 memset(&peer, 0, sizeof(peer));
546
547 /* Handle control packets. */
548 is_mhop = false;
549 if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) {
550 is_mhop = sd == bglobal.bg_mhop;
551 mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, port,
552 sizeof(port), vrfname, sizeof(vrfname),
553 &local, &peer);
554 } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) {
555 is_mhop = sd == bglobal.bg_mhop6;
556 mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, port,
557 sizeof(port), vrfname, sizeof(vrfname),
558 &local, &peer);
559 }
560
561 /* Implement RFC 5880 6.8.6 */
562 if (mlen < BFD_PKT_LEN) {
563 cp_debug(is_mhop, &peer, &local, port, vrfname,
564 "too small (%ld bytes)", mlen);
565 return 0;
566 }
567
568 /* Validate packet TTL. */
569 if ((is_mhop == false) && (ttl != BFD_TTL_VAL)) {
570 cp_debug(is_mhop, &peer, &local, port, vrfname,
571 "invalid TTL: %d expected %d", ttl, BFD_TTL_VAL);
572 return 0;
573 }
574
575 /*
576 * Parse the control header for inconsistencies:
577 * - Invalid version;
578 * - Bad multiplier configuration;
579 * - Short packets;
580 * - Invalid discriminator;
581 */
582 cp = (struct bfd_pkt *)(msgbuf);
583 if (BFD_GETVER(cp->diag) != BFD_VERSION) {
584 cp_debug(is_mhop, &peer, &local, port, vrfname,
585 "bad version %d", BFD_GETVER(cp->diag));
586 return 0;
587 }
588
589 if (cp->detect_mult == 0) {
590 cp_debug(is_mhop, &peer, &local, port, vrfname,
591 "detect multiplier set to zero");
592 return 0;
593 }
594
595 if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
596 cp_debug(is_mhop, &peer, &local, port, vrfname, "too small");
597 return 0;
598 }
599
600 if (cp->discrs.my_discr == 0) {
601 cp_debug(is_mhop, &peer, &local, port, vrfname,
602 "'my discriminator' is zero");
603 return 0;
604 }
605
606 /* Find the session that this packet belongs. */
607 bfd = ptm_bfd_sess_find(cp, port, &peer, &local, vrfname, is_mhop);
608 if (bfd == NULL) {
609 cp_debug(is_mhop, &peer, &local, port, vrfname,
610 "no session found");
611 return 0;
612 }
613
614 bfd->stats.rx_ctrl_pkt++;
615
616 /*
617 * Multi hop: validate packet TTL.
618 * Single hop: set local address that received the packet.
619 */
620 if (is_mhop) {
621 if ((BFD_TTL_VAL - bfd->mh_ttl) > BFD_TTL_VAL) {
622 cp_debug(is_mhop, &peer, &local, port, vrfname,
623 "exceeded max hop count (expected %d, got %d)",
624 bfd->mh_ttl, BFD_TTL_VAL);
625 return 0;
626 }
627 } else if (bfd->local_ip.sa_sin.sin_family == AF_UNSPEC) {
628 bfd->local_ip = local;
629 }
630
631 /*
632 * If no interface was detected, save the interface where the
633 * packet came in.
634 */
635 if (bfd->ifp == NULL)
636 bfd->ifp = if_lookup_by_name(port, VRF_DEFAULT);
637
638 /* Log remote discriminator changes. */
639 if ((bfd->discrs.remote_discr != 0)
640 && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
641 cp_debug(is_mhop, &peer, &local, port, vrfname,
642 "remote discriminator mismatch (expected %d, got %d)",
643 bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
644
645 bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
646
647 /* If received the Final bit, the new values should take effect */
648 if (bfd->polling && BFD_GETFBIT(cp->flags)) {
649 bfd->timers.desired_min_tx = bfd->new_timers.desired_min_tx;
650 bfd->timers.required_min_rx = bfd->new_timers.required_min_rx;
651 bfd->new_timers.desired_min_tx = 0;
652 bfd->new_timers.required_min_rx = 0;
653 bfd->polling = 0;
654 }
655
656 if (!bfd->demand_mode) {
657 /* Compute detect time */
658 bfd->detect_TO = cp->detect_mult
659 * ((bfd->timers.required_min_rx
660 > ntohl(cp->timers.desired_min_tx))
661 ? bfd->timers.required_min_rx
662 : ntohl(cp->timers.desired_min_tx));
663 bfd->remote_detect_mult = cp->detect_mult;
664 } else
665 cp_debug(is_mhop, &peer, &local, port, vrfname,
666 "unsupported demand mode");
667
668 /* Save remote diagnostics before state switch. */
669 bfd->remote_diag = cp->diag & BFD_DIAGMASK;
670
671 /* State switch from section 6.8.6 */
672 if (BFD_GETSTATE(cp->flags) == PTM_BFD_ADM_DOWN) {
673 if (bfd->ses_state != PTM_BFD_DOWN)
674 ptm_bfd_ses_dn(bfd, BD_NEIGHBOR_DOWN);
675 } else {
676 switch (bfd->ses_state) {
677 case (PTM_BFD_DOWN):
678 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT)
679 ptm_bfd_ses_up(bfd);
680 else if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
681 bfd->ses_state = PTM_BFD_INIT;
682 break;
683 case (PTM_BFD_INIT):
684 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT
685 || BFD_GETSTATE(cp->flags) == PTM_BFD_UP)
686 ptm_bfd_ses_up(bfd);
687 break;
688 case (PTM_BFD_UP):
689 if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
690 ptm_bfd_ses_dn(bfd, BD_NEIGHBOR_DOWN);
691 break;
692 }
693 }
694
695 /*
696 * Handle echo packet status:
697 * - Start echo packets if configured and permitted
698 * (required_min_echo > 0);
699 * - Stop echo packets if not allowed (required_min_echo == 0);
700 * - Recalculate echo packet interval;
701 */
702 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO)) {
703 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
704 if (!ntohl(cp->timers.required_min_echo)) {
705 ptm_bfd_echo_stop(bfd, 1);
706 } else {
707 oldEchoXmt_TO = bfd->echo_xmt_TO;
708 bfd->echo_xmt_TO =
709 bfd->timers.required_min_echo;
710 if (ntohl(cp->timers.required_min_echo)
711 > bfd->echo_xmt_TO)
712 bfd->echo_xmt_TO = ntohl(
713 cp->timers.required_min_echo);
714 if (oldEchoXmt_TO != bfd->echo_xmt_TO)
715 ptm_bfd_echo_start(bfd);
716 }
717 } else if (ntohl(cp->timers.required_min_echo)) {
718 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
719 if (ntohl(cp->timers.required_min_echo)
720 > bfd->echo_xmt_TO)
721 bfd->echo_xmt_TO =
722 ntohl(cp->timers.required_min_echo);
723 ptm_bfd_echo_start(bfd);
724 }
725 }
726
727 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
728 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
729 if (ntohl(cp->timers.required_min_echo) > bfd->echo_xmt_TO)
730 bfd->echo_xmt_TO = ntohl(cp->timers.required_min_echo);
731 }
732
733 /* Calculate new transmit time */
734 oldXmtTime = bfd->xmt_TO;
735 bfd->xmt_TO =
736 (bfd->timers.desired_min_tx > ntohl(cp->timers.required_min_rx))
737 ? bfd->timers.desired_min_tx
738 : ntohl(cp->timers.required_min_rx);
739
740 /* If transmit time has changed, and too much time until next xmt,
741 * restart
742 */
743 if (BFD_GETPBIT(cp->flags)) {
744 ptm_bfd_xmt_TO(bfd, 1);
745 } else if (oldXmtTime != bfd->xmt_TO) {
746 /* XXX add some skid to this as well */
747 ptm_bfd_start_xmt_timer(bfd, false);
748 }
749
750 /* Restart detection timer (packet received) */
751 if (!bfd->demand_mode)
752 bfd_recvtimer_update(bfd);
753
754 /*
755 * Save the timers and state sent by the remote end
756 * for debugging and statistics.
757 */
758 if (BFD_GETFBIT(cp->flags)) {
759 bfd->remote_timers.desired_min_tx =
760 ntohl(cp->timers.desired_min_tx);
761 bfd->remote_timers.required_min_rx =
762 ntohl(cp->timers.required_min_rx);
763 bfd->remote_timers.required_min_echo =
764 ntohl(cp->timers.required_min_echo);
765
766 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bfd);
767 }
768
769 return 0;
770 }
771
772 /*
773 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
774 * the packet is looped back or returns the my discriminator ID along
775 * with the TTL.
776 *
777 * Returns -1 on error or loopback or 0 on success.
778 */
779 int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr)
780 {
781 struct bfd_echo_pkt *bep;
782 ssize_t rlen;
783 struct sockaddr_any local, peer;
784 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
785 uint8_t msgbuf[1516];
786
787 if (sd == bglobal.bg_echo)
788 rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, port,
789 sizeof(port), vrfname, sizeof(vrfname),
790 &local, &peer);
791 else
792 rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), ttl, port,
793 sizeof(port), vrfname, sizeof(vrfname),
794 &local, &peer);
795
796 /* Short packet, better not risk reading it. */
797 if (rlen < (ssize_t)sizeof(*bep)) {
798 cp_debug(false, &peer, &local, port, vrfname,
799 "small echo packet");
800 return -1;
801 }
802
803 /* Test for loopback. */
804 if (*ttl == BFD_TTL_VAL) {
805 bp_udp_send(sd, *ttl - 1, msgbuf, rlen,
806 (struct sockaddr *)&peer,
807 (sd == bglobal.bg_echo) ? sizeof(peer.sa_sin)
808 : sizeof(peer.sa_sin6));
809 return -1;
810 }
811
812 /* Read my discriminator from BFD Echo packet. */
813 bep = (struct bfd_echo_pkt *)msgbuf;
814 *my_discr = ntohl(bep->my_discr);
815 if (*my_discr == 0) {
816 cp_debug(false, &peer, &local, port, vrfname,
817 "invalid echo packet discriminator (zero)");
818 return -1;
819 }
820
821 return 0;
822 }
823
824 int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
825 struct sockaddr *to, socklen_t tolen)
826 {
827 struct cmsghdr *cmsg;
828 ssize_t wlen;
829 int ttlval = ttl;
830 bool is_ipv6 = to->sa_family == AF_INET6;
831 struct msghdr msg;
832 struct iovec iov[1];
833 uint8_t msgctl[255];
834
835 /* Prepare message data. */
836 iov[0].iov_base = data;
837 iov[0].iov_len = datalen;
838
839 memset(&msg, 0, sizeof(msg));
840 memset(msgctl, 0, sizeof(msgctl));
841 msg.msg_name = to;
842 msg.msg_namelen = tolen;
843 msg.msg_iov = iov;
844 msg.msg_iovlen = 1;
845
846 /* Prepare the packet TTL information. */
847 if (ttl > 0) {
848 /* Use ancillary data. */
849 msg.msg_control = msgctl;
850 msg.msg_controllen = CMSG_LEN(sizeof(ttlval));
851
852 /* Configure the ancillary data. */
853 cmsg = CMSG_FIRSTHDR(&msg);
854 cmsg->cmsg_len = CMSG_LEN(sizeof(ttlval));
855 if (is_ipv6) {
856 cmsg->cmsg_level = IPPROTO_IPV6;
857 cmsg->cmsg_type = IPV6_HOPLIMIT;
858 } else {
859 #if BFD_LINUX
860 cmsg->cmsg_level = IPPROTO_IP;
861 cmsg->cmsg_type = IP_TTL;
862 #else
863 /* FreeBSD does not support TTL in ancillary data. */
864 msg.msg_control = NULL;
865 msg.msg_controllen = 0;
866
867 bp_set_ttl(sd, ttl);
868 #endif /* BFD_BSD */
869 }
870 memcpy(CMSG_DATA(cmsg), &ttlval, sizeof(ttlval));
871 }
872
873 /* Send echo back. */
874 wlen = sendmsg(sd, &msg, 0);
875 if (wlen <= 0) {
876 log_debug("udp-send: loopback failure: (%d) %s", errno, strerror(errno));
877 return -1;
878 } else if (wlen < (ssize_t)datalen) {
879 log_debug("udp-send: partial send: %ld expected %ld", wlen,
880 datalen);
881 return -1;
882 }
883
884 return 0;
885 }
886
887
888 /*
889 * Sockets creation.
890 */
891
892
893 /*
894 * IPv4 sockets
895 */
896 int bp_set_ttl(int sd, uint8_t value)
897 {
898 int ttl = value;
899
900 if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
901 log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value,
902 strerror(errno));
903 return -1;
904 }
905
906 return 0;
907 }
908
909 int bp_set_tos(int sd, uint8_t value)
910 {
911 int tos = value;
912
913 if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
914 log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value,
915 strerror(errno));
916 return -1;
917 }
918
919 return 0;
920 }
921
922 static void bp_set_ipopts(int sd)
923 {
924 int rcvttl = BFD_RCV_TTL_VAL;
925
926 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
927 log_fatal("set-ipopts: TTL configuration failed");
928
929 if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
930 == -1)
931 log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl,
932 strerror(errno));
933
934 #ifdef BFD_LINUX
935 int pktinfo = BFD_PKT_INFO_VAL;
936
937 /* Figure out address and interface to do the peer matching. */
938 if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
939 == -1)
940 log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo,
941 strerror(errno));
942 #endif /* BFD_LINUX */
943 #ifdef BFD_BSD
944 int yes = 1;
945
946 /* Find out our address for peer matching. */
947 if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
948 log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes,
949 strerror(errno));
950
951 /* Find out interface where the packet came in. */
952 if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
953 log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes,
954 strerror(errno));
955 #endif /* BFD_BSD */
956 }
957
958 static void bp_bind_ip(int sd, uint16_t port)
959 {
960 struct sockaddr_in sin;
961
962 memset(&sin, 0, sizeof(sin));
963 sin.sin_family = AF_INET;
964 sin.sin_addr.s_addr = htonl(INADDR_ANY);
965 sin.sin_port = htons(port);
966 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
967 log_fatal("bind-ip: bind: %s", strerror(errno));
968 }
969
970 int bp_udp_shop(void)
971 {
972 int sd;
973
974 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
975 if (sd == -1)
976 log_fatal("udp-shop: socket: %s", strerror(errno));
977
978 bp_set_ipopts(sd);
979 bp_bind_ip(sd, BFD_DEFDESTPORT);
980
981 return sd;
982 }
983
984 int bp_udp_mhop(void)
985 {
986 int sd;
987
988 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
989 if (sd == -1)
990 log_fatal("udp-mhop: socket: %s", strerror(errno));
991
992 bp_set_ipopts(sd);
993 bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
994
995 return sd;
996 }
997
998 int bp_peer_socket(struct bfd_peer_cfg *bpc)
999 {
1000 int sd, pcount;
1001 struct sockaddr_in sin;
1002 static int srcPort = BFD_SRCPORTINIT;
1003
1004 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1005 if (sd == -1) {
1006 log_error("ipv4-new: failed to create socket: %s",
1007 strerror(errno));
1008 return -1;
1009 }
1010
1011 /* Set TTL to 255 for all transmitted packets */
1012 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) {
1013 close(sd);
1014 return -1;
1015 }
1016
1017 /* Set TOS to CS6 for all transmitted packets */
1018 if (bp_set_tos(sd, BFD_TOS_VAL) != 0) {
1019 close(sd);
1020 return -1;
1021 }
1022
1023 if (bpc->bpc_has_localif) {
1024 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1025 close(sd);
1026 return -1;
1027 }
1028 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1029 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1030 close(sd);
1031 return -1;
1032 }
1033 }
1034
1035 /* Find an available source port in the proper range */
1036 memset(&sin, 0, sizeof(sin));
1037 sin = bpc->bpc_local.sa_sin;
1038 sin.sin_family = AF_INET;
1039 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1040 sin.sin_len = sizeof(sin);
1041 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1042 if (bpc->bpc_mhop)
1043 sin.sin_addr = bpc->bpc_local.sa_sin.sin_addr;
1044 else
1045 sin.sin_addr.s_addr = INADDR_ANY;
1046
1047 pcount = 0;
1048 do {
1049 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1050 /* Searched all ports, none available */
1051 log_error("ipv4-new: failed to bind port: %s",
1052 strerror(errno));
1053 close(sd);
1054 return -1;
1055 }
1056 if (srcPort >= BFD_SRCPORTMAX)
1057 srcPort = BFD_SRCPORTINIT;
1058 sin.sin_port = htons(srcPort++);
1059 } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
1060
1061 return sd;
1062 }
1063
1064
1065 /*
1066 * IPv6 sockets
1067 */
1068
1069 int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
1070 {
1071 struct interface *ifp;
1072 int sd, pcount;
1073 struct sockaddr_in6 sin6;
1074 static int srcPort = BFD_SRCPORTINIT;
1075
1076 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1077 if (sd == -1) {
1078 log_error("ipv6-new: failed to create socket: %s",
1079 strerror(errno));
1080 return -1;
1081 }
1082
1083 /* Set TTL to 255 for all transmitted packets */
1084 if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) {
1085 close(sd);
1086 return -1;
1087 }
1088
1089 /* Set TOS to CS6 for all transmitted packets */
1090 if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) {
1091 close(sd);
1092 return -1;
1093 }
1094
1095 /* Find an available source port in the proper range */
1096 memset(&sin6, 0, sizeof(sin6));
1097 sin6.sin6_family = AF_INET6;
1098 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1099 sin6.sin6_len = sizeof(sin6);
1100 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1101 sin6 = bpc->bpc_local.sa_sin6;
1102 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
1103 ifp = if_lookup_by_name(bpc->bpc_localif, VRF_DEFAULT);
1104 sin6.sin6_scope_id =
1105 (ifp != NULL) ? ifp->ifindex : IFINDEX_INTERNAL;
1106 }
1107
1108 if (bpc->bpc_has_localif) {
1109 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1110 close(sd);
1111 return -1;
1112 }
1113 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1114 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1115 close(sd);
1116 return -1;
1117 }
1118 }
1119
1120 pcount = 0;
1121 do {
1122 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1123 /* Searched all ports, none available */
1124 log_error("ipv6-new: failed to bind port: %s",
1125 strerror(errno));
1126 close(sd);
1127 return -1;
1128 }
1129 if (srcPort >= BFD_SRCPORTMAX)
1130 srcPort = BFD_SRCPORTINIT;
1131 sin6.sin6_port = htons(srcPort++);
1132 } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1133
1134 return sd;
1135 }
1136
1137 int bp_set_ttlv6(int sd, uint8_t value)
1138 {
1139 int ttl = value;
1140
1141 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))
1142 == -1) {
1143 log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1144 value, strerror(errno));
1145 return -1;
1146 }
1147
1148 return 0;
1149 }
1150
1151 int bp_set_tosv6(int sd, uint8_t value)
1152 {
1153 int tos = value;
1154
1155 if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))
1156 == -1) {
1157 log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value,
1158 strerror(errno));
1159 return -1;
1160 }
1161
1162 return 0;
1163 }
1164
1165 static void bp_set_ipv6opts(int sd)
1166 {
1167 int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1168 int ipv6_only = BFD_IPV6_ONLY_VAL;
1169
1170 if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
1171 log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1172 BFD_TTL_VAL, strerror(errno));
1173
1174 if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1)
1175 log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1176 BFD_RCV_TTL_VAL, strerror(errno));
1177
1178 if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
1179 log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1180 ipv6_pktinfo, strerror(errno));
1181
1182 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1183 sizeof(ipv6_only))
1184 == -1)
1185 log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1186 ipv6_only, strerror(errno));
1187 }
1188
1189 static void bp_bind_ipv6(int sd, uint16_t port)
1190 {
1191 struct sockaddr_in6 sin6;
1192
1193 memset(&sin6, 0, sizeof(sin6));
1194 sin6.sin6_family = AF_INET6;
1195 sin6.sin6_addr = in6addr_any;
1196 sin6.sin6_port = htons(port);
1197 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1198 sin6.sin6_len = sizeof(sin6);
1199 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1200 if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1201 log_fatal("bind-ipv6: bind: %s", strerror(errno));
1202 }
1203
1204 int bp_udp6_shop(void)
1205 {
1206 int sd;
1207
1208 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1209 if (sd == -1)
1210 log_fatal("udp6-shop: socket: %s", strerror(errno));
1211
1212 bp_set_ipv6opts(sd);
1213 bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1214
1215 return sd;
1216 }
1217
1218 int bp_udp6_mhop(void)
1219 {
1220 int sd;
1221
1222 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1223 if (sd == -1)
1224 log_fatal("udp6-mhop: socket: %s", strerror(errno));
1225
1226 bp_set_ipv6opts(sd);
1227 bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1228
1229 return sd;
1230 }
1231
1232 int bp_echo_socket(void)
1233 {
1234 int s;
1235
1236 s = socket(AF_INET, SOCK_DGRAM, 0);
1237 if (s == -1)
1238 log_fatal("echo-socket: socket: %s", strerror(errno));
1239
1240 bp_set_ipopts(s);
1241 bp_bind_ip(s, BFD_DEF_ECHO_PORT);
1242
1243 return s;
1244 }
1245
1246 int bp_echov6_socket(void)
1247 {
1248 int s;
1249
1250 s = socket(AF_INET6, SOCK_DGRAM, 0);
1251 if (s == -1)
1252 log_fatal("echov6-socket: socket: %s", strerror(errno));
1253
1254 bp_set_ipv6opts(s);
1255 bp_bind_ipv6(s, BFD_DEF_ECHO_PORT);
1256
1257 return s;
1258 }