]> git.proxmox.com Git - mirror_frr.git/blame - bfdd/bfd_packet.c
bfdd: slow down on peer connection loss
[mirror_frr.git] / bfdd / bfd_packet.c
CommitLineData
e9e2c950
RZ
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
e9e2c950 33#include <netinet/if_ether.h>
e9e2c950 34#include <netinet/udp.h>
e9e2c950
RZ
35
36#include "lib/sockopt.h"
37
38#include "bfd.h"
39
8a9f760e 40
e9e2c950
RZ
41/*
42 * Prototypes
43 */
e9e2c950 44static int ptm_bfd_process_echo_pkt(int s);
2f11c53f
RZ
45int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
46 size_t datalen);
e9e2c950
RZ
47
48static void bfd_sd_reschedule(int sd);
50571b2e
RZ
49ssize_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);
53ssize_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);
2f11c53f
RZ
57int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
58 struct sockaddr *to, socklen_t tolen);
59int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr);
e9e2c950
RZ
60
61/* socket related prototypes */
62static void bp_set_ipopts(int sd);
63static void bp_bind_ip(int sd, uint16_t port);
64static void bp_set_ipv6opts(int sd);
65static void bp_bind_ipv6(int sd, uint16_t port);
66
67
68/*
69 * Functions
70 */
2f11c53f
RZ
71int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
72 size_t datalen)
e9e2c950
RZ
73{
74 struct sockaddr *sa;
75 struct sockaddr_in sin;
76 struct sockaddr_in6 sin6;
e9e2c950
RZ
77 socklen_t slen;
78 ssize_t rv;
79 int sd = -1;
80
2f11c53f 81 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
e9e2c950
RZ
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) {
03e7f088 114 log_debug("packet-send: send failure: %s", strerror(errno));
e9e2c950
RZ
115 return -1;
116 }
03e7f088
RZ
117 if (rv < (ssize_t)datalen)
118 log_debug("packet-send: send partial", strerror(errno));
e9e2c950
RZ
119
120 return 0;
121}
122
e9e2c950
RZ
123void ptm_bfd_echo_snd(struct bfd_session *bfd)
124{
2f11c53f
RZ
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;
e9e2c950 131
2f11c53f 132 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
e9e2c950 133 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
e9e2c950 134
2f11c53f
RZ
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);
e9e2c950 152 } else {
2f11c53f
RZ
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 */
e9e2c950 159
2f11c53f
RZ
160 sa = (struct sockaddr_any *)&sin;
161 salen = sizeof(sin);
e9e2c950 162 }
2f11c53f
RZ
163 if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep),
164 (struct sockaddr *)sa, salen)
165 == -1)
166 return;
e9e2c950
RZ
167
168 bfd->stats.tx_echo_pkt++;
169}
170
e9e2c950
RZ
171static int ptm_bfd_process_echo_pkt(int s)
172{
e9e2c950 173 struct bfd_session *bfd;
2f11c53f
RZ
174 uint32_t my_discr = 0;
175 uint8_t ttl = 0;
e9e2c950 176
2f11c53f
RZ
177 /* Receive and parse echo packet. */
178 if (bp_bfd_echo_in(s, &ttl, &my_discr) == -1)
179 return 0;
e9e2c950
RZ
180
181 /* Your discriminator not zero - use it to find session */
182 bfd = bfd_id_lookup(my_discr);
183 if (bfd == NULL) {
03e7f088 184 log_debug("echo-packet: no matching session (id:%u)", my_discr);
e9e2c950
RZ
185 return -1;
186 }
187
188 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
9835f17d
RZ
189 log_debug("echo-packet: echo disabled [%s] (id:%u)",
190 bs_to_string(bfd), my_discr);
e9e2c950
RZ
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. */
451eb5a2
RZ
200 if (bfd->echo_detect_TO > 0)
201 bfd_echo_recvtimer_update(bfd);
e9e2c950
RZ
202
203 return 0;
204}
205
206void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
207{
208 struct bfd_pkt cp;
209
e9e2c950
RZ
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);
0592db96
RZ
216
217 /*
218 * Polling and Final can't be set at the same time.
219 *
220 * RFC 5880, Section 6.5.
221 */
e9e2c950 222 BFD_SETFBIT(cp.flags, fbit);
0592db96
RZ
223 if (fbit == 0)
224 BFD_SETPBIT(cp.flags, bfd->polling);
225
e9e2c950
RZ
226 cp.detect_mult = bfd->detect_mult;
227 cp.len = BFD_PKT_LEN;
228 cp.discrs.my_discr = htonl(bfd->discrs.my_discr);
229 cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
230 if (bfd->polling) {
231 cp.timers.desired_min_tx =
f43b9368 232 htonl(bfd->timers.desired_min_tx);
e9e2c950 233 cp.timers.required_min_rx =
f43b9368 234 htonl(bfd->timers.required_min_rx);
e9e2c950 235 } else {
f43b9368
RZ
236 /*
237 * We can only announce current setting on poll, this
238 * avoids timing mismatch with our peer and give it
239 * the oportunity to learn. See `bs_final_handler` for
240 * more information.
241 */
242 cp.timers.desired_min_tx =
243 htonl(bfd->cur_timers.desired_min_tx);
244 cp.timers.required_min_rx =
245 htonl(bfd->cur_timers.required_min_rx);
e9e2c950
RZ
246 }
247 cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo);
248
2f11c53f 249 if (_ptm_bfd_send(bfd, NULL, &cp, BFD_PKT_LEN) != 0)
e9e2c950 250 return;
e9e2c950
RZ
251
252 bfd->stats.tx_ctrl_pkt++;
253}
254
50571b2e
RZ
255ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
256 char *port, size_t portlen, char *vrfname,
257 size_t vrfnamelen, struct sockaddr_any *local,
258 struct sockaddr_any *peer)
e9e2c950 259{
80edb675 260 struct interface *ifp;
e9e2c950
RZ
261 struct cmsghdr *cm;
262 int ifindex;
263 ssize_t mlen;
16084964
RZ
264 struct sockaddr_in msgaddr;
265 struct msghdr msghdr;
266 struct iovec iov[1];
267 uint8_t cmsgbuf[255];
268
e10cfdaf
DL
269 port[0] = '\0';
270
16084964
RZ
271 /* Prepare the recvmsg params. */
272 iov[0].iov_base = msgbuf;
50571b2e 273 iov[0].iov_len = msgbuflen;
16084964
RZ
274
275 memset(&msghdr, 0, sizeof(msghdr));
276 msghdr.msg_name = &msgaddr;
277 msghdr.msg_namelen = sizeof(msgaddr);
278 msghdr.msg_iov = iov;
279 msghdr.msg_iovlen = 1;
280 msghdr.msg_control = cmsgbuf;
281 msghdr.msg_controllen = sizeof(cmsgbuf);
282
e9e2c950
RZ
283 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
284 if (mlen == -1) {
03e7f088
RZ
285 if (errno != EAGAIN)
286 log_error("ipv4-recv: recv failed: %s",
287 strerror(errno));
288
e9e2c950
RZ
289 return -1;
290 }
291
292 /* Get source address */
293 peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
294
295 /* Get and check TTL */
296 for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
297 cm = CMSG_NXTHDR(&msghdr, cm)) {
298 if (cm->cmsg_level != IPPROTO_IP)
299 continue;
300
301 switch (cm->cmsg_type) {
302#ifdef BFD_LINUX
303 case IP_TTL: {
50571b2e
RZ
304 uint32_t ttlval;
305
306 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
307 if (ttlval > 255) {
308 log_debug("ipv4-recv: invalid TTL: %u", ttlval);
e9e2c950
RZ
309 return -1;
310 }
50571b2e 311 *ttl = ttlval;
e9e2c950
RZ
312 break;
313 }
314
315 case IP_PKTINFO: {
316 struct in_pktinfo *pi =
317 (struct in_pktinfo *)CMSG_DATA(cm);
318
319 if (pi == NULL)
320 break;
321
322 local->sa_sin.sin_family = AF_INET;
323 local->sa_sin.sin_addr = pi->ipi_addr;
f43a14d2
RZ
324#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
325 local->sa_sin.sin_len = sizeof(local->sa_sin);
326#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
80edb675
RZ
327
328 ifp = if_lookup_by_index(pi->ipi_ifindex, VRF_DEFAULT);
329 if (ifp == NULL)
330 break;
331
332 if (strlcpy(port, ifp->name, portlen) >= portlen)
333 log_debug(
334 "ipv4-recv: interface name truncated");
e9e2c950
RZ
335 break;
336 }
337#endif /* BFD_LINUX */
338#ifdef BFD_BSD
339 case IP_RECVTTL: {
50571b2e 340 memcpy(ttl, CMSG_DATA(cm), sizeof(*ttl));
e9e2c950
RZ
341 break;
342 }
343
344 case IP_RECVDSTADDR: {
345 struct in_addr ia;
346
347 memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
348 local->sa_sin.sin_family = AF_INET;
349 local->sa_sin.sin_addr = ia;
f43a14d2
RZ
350#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
351 local->sa_sin.sin_len = sizeof(local->sa_sin);
352#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
e9e2c950
RZ
353 break;
354 }
355#endif /* BFD_BSD */
356
357 default:
358 /*
359 * On *BSDs we expect to land here when skipping
360 * the IP_RECVIF header. It will be handled by
361 * getsockopt_ifindex() below.
362 */
363 /* NOTHING */
364 break;
365 }
366 }
367
368 /* OS agnostic way of getting interface name. */
369 if (port[0] == 0) {
370 ifindex = getsockopt_ifindex(AF_INET, &msghdr);
80edb675
RZ
371 if (ifindex <= 0)
372 return mlen;
373
374 ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
375 if (ifp == NULL)
376 return mlen;
377
378 if (strlcpy(port, ifp->name, portlen) >= portlen)
379 log_debug("ipv4-recv: interface name truncated");
e9e2c950
RZ
380 }
381
382 return mlen;
383}
384
50571b2e
RZ
385ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
386 char *port, size_t portlen, char *vrfname,
387 size_t vrfnamelen, struct sockaddr_any *local,
388 struct sockaddr_any *peer)
e9e2c950 389{
80edb675 390 struct interface *ifp;
e9e2c950
RZ
391 struct cmsghdr *cm;
392 struct in6_pktinfo *pi6 = NULL;
43adc702 393 int ifindex = 0;
e9e2c950 394 ssize_t mlen;
6e01e275 395 uint32_t ttlval;
16084964
RZ
396 struct sockaddr_in6 msgaddr6;
397 struct msghdr msghdr6;
398 struct iovec iov[1];
399 uint8_t cmsgbuf6[255];
400
401 /* Prepare the recvmsg params. */
402 iov[0].iov_base = msgbuf;
50571b2e 403 iov[0].iov_len = msgbuflen;
16084964
RZ
404
405 memset(&msghdr6, 0, sizeof(msghdr6));
406 msghdr6.msg_name = &msgaddr6;
407 msghdr6.msg_namelen = sizeof(msgaddr6);
408 msghdr6.msg_iov = iov;
409 msghdr6.msg_iovlen = 1;
410 msghdr6.msg_control = cmsgbuf6;
411 msghdr6.msg_controllen = sizeof(cmsgbuf6);
412
e9e2c950
RZ
413 mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
414 if (mlen == -1) {
03e7f088 415 if (errno != EAGAIN)
6e01e275 416 log_error("ipv6-recv: recv failed: %s",
03e7f088
RZ
417 strerror(errno));
418
e9e2c950
RZ
419 return -1;
420 }
421
422 /* Get source address */
423 peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
424
425 /* Get and check TTL */
426 for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
427 cm = CMSG_NXTHDR(&msghdr6, cm)) {
428 if (cm->cmsg_level != IPPROTO_IPV6)
429 continue;
430
431 if (cm->cmsg_type == IPV6_HOPLIMIT) {
6e01e275 432 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
50571b2e
RZ
433 if (ttlval > 255) {
434 log_debug("ipv6-recv: invalid TTL: %u", ttlval);
e9e2c950
RZ
435 return -1;
436 }
50571b2e
RZ
437
438 *ttl = ttlval;
e9e2c950
RZ
439 } else if (cm->cmsg_type == IPV6_PKTINFO) {
440 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
441 if (pi6) {
f43a14d2 442 local->sa_sin6.sin6_family = AF_INET6;
e9e2c950 443 local->sa_sin6.sin6_addr = pi6->ipi6_addr;
f43a14d2
RZ
444#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
445 local->sa_sin6.sin6_len = sizeof(local->sa_sin6);
446#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
80edb675 447
43adc702 448 ifindex = pi6->ipi6_ifindex;
80edb675
RZ
449 ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
450 if (ifp == NULL)
451 break;
452
453 if (strlcpy(port, ifp->name, portlen)
454 >= portlen)
455 log_debug(
456 "ipv6-recv: interface name truncated");
e9e2c950
RZ
457 }
458 }
459 }
460
43adc702
RZ
461 /* Set scope ID for link local addresses. */
462 if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr))
463 peer->sa_sin6.sin6_scope_id = ifindex;
464 if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr))
465 local->sa_sin6.sin6_scope_id = ifindex;
466
e9e2c950
RZ
467 return mlen;
468}
469
470static void bfd_sd_reschedule(int sd)
471{
472 if (sd == bglobal.bg_shop) {
8529e180 473 THREAD_OFF(bglobal.bg_ev[0]);
e9e2c950
RZ
474 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
475 &bglobal.bg_ev[0]);
476 } else if (sd == bglobal.bg_mhop) {
8529e180 477 THREAD_OFF(bglobal.bg_ev[1]);
e9e2c950
RZ
478 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop,
479 &bglobal.bg_ev[1]);
480 } else if (sd == bglobal.bg_shop6) {
8529e180 481 THREAD_OFF(bglobal.bg_ev[2]);
e9e2c950
RZ
482 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6,
483 &bglobal.bg_ev[2]);
484 } else if (sd == bglobal.bg_mhop6) {
8529e180 485 THREAD_OFF(bglobal.bg_ev[3]);
e9e2c950
RZ
486 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6,
487 &bglobal.bg_ev[3]);
488 } else if (sd == bglobal.bg_echo) {
8529e180 489 THREAD_OFF(bglobal.bg_ev[4]);
e9e2c950
RZ
490 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo,
491 &bglobal.bg_ev[4]);
2f11c53f 492 } else if (sd == bglobal.bg_echov6) {
8529e180 493 THREAD_OFF(bglobal.bg_ev[5]);
2f11c53f
RZ
494 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6,
495 &bglobal.bg_ev[5]);
e9e2c950
RZ
496 }
497}
498
03e7f088
RZ
499static void cp_debug(bool mhop, struct sockaddr_any *peer,
500 struct sockaddr_any *local, const char *port,
501 const char *vrf, const char *fmt, ...)
502{
503 char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
504 va_list vl;
505
506 if (peer->sa_sin.sin_family)
507 snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
508 else
509 peerstr[0] = 0;
510
511 if (local->sa_sin.sin_family)
512 snprintf(localstr, sizeof(localstr), " local:%s",
513 satostr(local));
514 else
515 localstr[0] = 0;
516
517 if (port[0])
518 snprintf(portstr, sizeof(portstr), " port:%s", port);
519 else
520 portstr[0] = 0;
521
522 if (vrf[0])
523 snprintf(vrfstr, sizeof(vrfstr), " vrf:%s", port);
524 else
525 vrfstr[0] = 0;
526
527 va_start(vl, fmt);
528 vsnprintf(buf, sizeof(buf), fmt, vl);
529 va_end(vl);
530
531 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
532 mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
533}
534
e9e2c950
RZ
535int bfd_recv_cb(struct thread *t)
536{
537 int sd = THREAD_FD(t);
538 struct bfd_session *bfd;
539 struct bfd_pkt *cp;
8a9f760e 540 bool is_mhop;
e9e2c950 541 ssize_t mlen = 0;
50571b2e 542 uint8_t ttl;
e9e2c950
RZ
543 struct sockaddr_any local, peer;
544 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
50571b2e 545 uint8_t msgbuf[1516];
e9e2c950
RZ
546
547 /* Schedule next read. */
548 bfd_sd_reschedule(sd);
549
03e7f088 550 /* Handle echo packets. */
2f11c53f 551 if (sd == bglobal.bg_echo || sd == bglobal.bg_echov6) {
e9e2c950
RZ
552 ptm_bfd_process_echo_pkt(sd);
553 return 0;
554 }
555
ff98a589
RZ
556 /* Sanitize input/output. */
557 memset(port, 0, sizeof(port));
558 memset(vrfname, 0, sizeof(vrfname));
559 memset(&local, 0, sizeof(local));
560 memset(&peer, 0, sizeof(peer));
561
03e7f088 562 /* Handle control packets. */
8a9f760e 563 is_mhop = false;
e9e2c950
RZ
564 if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) {
565 is_mhop = sd == bglobal.bg_mhop;
50571b2e
RZ
566 mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, port,
567 sizeof(port), vrfname, sizeof(vrfname),
568 &local, &peer);
e9e2c950
RZ
569 } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) {
570 is_mhop = sd == bglobal.bg_mhop6;
50571b2e
RZ
571 mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, port,
572 sizeof(port), vrfname, sizeof(vrfname),
573 &local, &peer);
e9e2c950 574 }
e9e2c950
RZ
575
576 /* Implement RFC 5880 6.8.6 */
577 if (mlen < BFD_PKT_LEN) {
03e7f088
RZ
578 cp_debug(is_mhop, &peer, &local, port, vrfname,
579 "too small (%ld bytes)", mlen);
e9e2c950
RZ
580 return 0;
581 }
582
50571b2e
RZ
583 /* Validate packet TTL. */
584 if ((is_mhop == false) && (ttl != BFD_TTL_VAL)) {
585 cp_debug(is_mhop, &peer, &local, port, vrfname,
586 "invalid TTL: %d expected %d", ttl, BFD_TTL_VAL);
587 return 0;
588 }
589
03e7f088
RZ
590 /*
591 * Parse the control header for inconsistencies:
592 * - Invalid version;
593 * - Bad multiplier configuration;
594 * - Short packets;
595 * - Invalid discriminator;
596 */
16084964 597 cp = (struct bfd_pkt *)(msgbuf);
e9e2c950 598 if (BFD_GETVER(cp->diag) != BFD_VERSION) {
03e7f088
RZ
599 cp_debug(is_mhop, &peer, &local, port, vrfname,
600 "bad version %d", BFD_GETVER(cp->diag));
e9e2c950
RZ
601 return 0;
602 }
603
604 if (cp->detect_mult == 0) {
03e7f088
RZ
605 cp_debug(is_mhop, &peer, &local, port, vrfname,
606 "detect multiplier set to zero");
e9e2c950
RZ
607 return 0;
608 }
609
610 if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
03e7f088 611 cp_debug(is_mhop, &peer, &local, port, vrfname, "too small");
e9e2c950
RZ
612 return 0;
613 }
614
615 if (cp->discrs.my_discr == 0) {
03e7f088
RZ
616 cp_debug(is_mhop, &peer, &local, port, vrfname,
617 "'my discriminator' is zero");
e9e2c950
RZ
618 return 0;
619 }
620
03e7f088 621 /* Find the session that this packet belongs. */
e9e2c950
RZ
622 bfd = ptm_bfd_sess_find(cp, port, &peer, &local, vrfname, is_mhop);
623 if (bfd == NULL) {
03e7f088
RZ
624 cp_debug(is_mhop, &peer, &local, port, vrfname,
625 "no session found");
e9e2c950
RZ
626 return 0;
627 }
628
e9e2c950 629 bfd->stats.rx_ctrl_pkt++;
03e7f088
RZ
630
631 /*
632 * Multi hop: validate packet TTL.
633 * Single hop: set local address that received the packet.
634 */
e9e2c950 635 if (is_mhop) {
6e01e275 636 if ((BFD_TTL_VAL - bfd->mh_ttl) > BFD_TTL_VAL) {
03e7f088
RZ
637 cp_debug(is_mhop, &peer, &local, port, vrfname,
638 "exceeded max hop count (expected %d, got %d)",
6e01e275 639 bfd->mh_ttl, BFD_TTL_VAL);
e9e2c950
RZ
640 return 0;
641 }
642 } else if (bfd->local_ip.sa_sin.sin_family == AF_UNSPEC) {
643 bfd->local_ip = local;
644 }
645
646 /*
647 * If no interface was detected, save the interface where the
648 * packet came in.
649 */
80edb675
RZ
650 if (bfd->ifp == NULL)
651 bfd->ifp = if_lookup_by_name(port, VRF_DEFAULT);
e9e2c950 652
03e7f088 653 /* Log remote discriminator changes. */
e9e2c950 654 if ((bfd->discrs.remote_discr != 0)
03e7f088
RZ
655 && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
656 cp_debug(is_mhop, &peer, &local, port, vrfname,
657 "remote discriminator mismatch (expected %d, got %d)",
658 bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
e9e2c950
RZ
659
660 bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
661
e9e2c950
RZ
662 /* Save remote diagnostics before state switch. */
663 bfd->remote_diag = cp->diag & BFD_DIAGMASK;
664
c0ef9a8a
RZ
665 /* Update remote timers settings. */
666 bfd->remote_timers.desired_min_tx = ntohl(cp->timers.desired_min_tx);
667 bfd->remote_timers.required_min_rx = ntohl(cp->timers.required_min_rx);
668 bfd->remote_timers.required_min_echo =
669 ntohl(cp->timers.required_min_echo);
670 bfd->remote_detect_mult = cp->detect_mult;
671
aef131af
RZ
672 /* State switch from section 6.2. */
673 bs_state_handler(bfd, BFD_GETSTATE(cp->flags));
e9e2c950 674
c0ef9a8a
RZ
675 /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
676 if (bfd->polling && BFD_GETFBIT(cp->flags)) {
677 /* Disable pooling. */
678 bfd->polling = 0;
e9e2c950 679
c0ef9a8a
RZ
680 /* Handle poll finalization. */
681 bs_final_handler(bfd);
682 } else {
683 /* Received a packet, lets update the receive timer. */
684 bfd_recvtimer_update(bfd);
e9e2c950
RZ
685 }
686
c0ef9a8a
RZ
687 /* Handle echo timers changes. */
688 bs_echo_timer_handler(bfd);
e9e2c950
RZ
689
690 /*
c0ef9a8a
RZ
691 * We've received a packet with the POLL bit set, we must send
692 * a control packet back with the FINAL bit set.
693 *
694 * RFC 5880, Section 6.5.
e9e2c950 695 */
c0ef9a8a
RZ
696 if (BFD_GETPBIT(cp->flags))
697 ptm_bfd_snd(bfd, 1);
e9e2c950
RZ
698
699 return 0;
700}
701
2f11c53f
RZ
702/*
703 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
704 * the packet is looped back or returns the my discriminator ID along
705 * with the TTL.
706 *
707 * Returns -1 on error or loopback or 0 on success.
708 */
709int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr)
710{
711 struct bfd_echo_pkt *bep;
712 ssize_t rlen;
713 struct sockaddr_any local, peer;
714 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
715 uint8_t msgbuf[1516];
716
717 if (sd == bglobal.bg_echo)
718 rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, port,
719 sizeof(port), vrfname, sizeof(vrfname),
720 &local, &peer);
721 else
722 rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), ttl, port,
723 sizeof(port), vrfname, sizeof(vrfname),
724 &local, &peer);
725
726 /* Short packet, better not risk reading it. */
727 if (rlen < (ssize_t)sizeof(*bep)) {
728 cp_debug(false, &peer, &local, port, vrfname,
729 "small echo packet");
730 return -1;
731 }
732
733 /* Test for loopback. */
734 if (*ttl == BFD_TTL_VAL) {
735 bp_udp_send(sd, *ttl - 1, msgbuf, rlen,
736 (struct sockaddr *)&peer,
737 (sd == bglobal.bg_echo) ? sizeof(peer.sa_sin)
738 : sizeof(peer.sa_sin6));
739 return -1;
740 }
741
742 /* Read my discriminator from BFD Echo packet. */
743 bep = (struct bfd_echo_pkt *)msgbuf;
744 *my_discr = ntohl(bep->my_discr);
745 if (*my_discr == 0) {
746 cp_debug(false, &peer, &local, port, vrfname,
747 "invalid echo packet discriminator (zero)");
748 return -1;
749 }
750
751 return 0;
752}
753
754int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
755 struct sockaddr *to, socklen_t tolen)
756{
757 struct cmsghdr *cmsg;
758 ssize_t wlen;
759 int ttlval = ttl;
760 bool is_ipv6 = to->sa_family == AF_INET6;
761 struct msghdr msg;
762 struct iovec iov[1];
763 uint8_t msgctl[255];
764
765 /* Prepare message data. */
766 iov[0].iov_base = data;
767 iov[0].iov_len = datalen;
768
769 memset(&msg, 0, sizeof(msg));
770 memset(msgctl, 0, sizeof(msgctl));
771 msg.msg_name = to;
772 msg.msg_namelen = tolen;
773 msg.msg_iov = iov;
774 msg.msg_iovlen = 1;
775
776 /* Prepare the packet TTL information. */
777 if (ttl > 0) {
778 /* Use ancillary data. */
779 msg.msg_control = msgctl;
780 msg.msg_controllen = CMSG_LEN(sizeof(ttlval));
781
782 /* Configure the ancillary data. */
783 cmsg = CMSG_FIRSTHDR(&msg);
784 cmsg->cmsg_len = CMSG_LEN(sizeof(ttlval));
785 if (is_ipv6) {
786 cmsg->cmsg_level = IPPROTO_IPV6;
787 cmsg->cmsg_type = IPV6_HOPLIMIT;
788 } else {
789#if BFD_LINUX
790 cmsg->cmsg_level = IPPROTO_IP;
791 cmsg->cmsg_type = IP_TTL;
792#else
793 /* FreeBSD does not support TTL in ancillary data. */
794 msg.msg_control = NULL;
795 msg.msg_controllen = 0;
796
797 bp_set_ttl(sd, ttl);
798#endif /* BFD_BSD */
799 }
800 memcpy(CMSG_DATA(cmsg), &ttlval, sizeof(ttlval));
801 }
802
803 /* Send echo back. */
804 wlen = sendmsg(sd, &msg, 0);
805 if (wlen <= 0) {
806 log_debug("udp-send: loopback failure: (%d) %s", errno, strerror(errno));
807 return -1;
808 } else if (wlen < (ssize_t)datalen) {
809 log_debug("udp-send: partial send: %ld expected %ld", wlen,
810 datalen);
811 return -1;
812 }
813
814 return 0;
815}
816
e9e2c950
RZ
817
818/*
819 * Sockets creation.
820 */
821
822
823/*
824 * IPv4 sockets
825 */
6e01e275 826int bp_set_ttl(int sd, uint8_t value)
e9e2c950 827{
6e01e275
RZ
828 int ttl = value;
829
830 if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
831 log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value,
e9e2c950
RZ
832 strerror(errno));
833 return -1;
834 }
835
836 return 0;
837}
838
6e01e275 839int bp_set_tos(int sd, uint8_t value)
e9e2c950 840{
6e01e275
RZ
841 int tos = value;
842
843 if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
844 log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value,
e9e2c950
RZ
845 strerror(errno));
846 return -1;
847 }
848
849 return 0;
850}
851
852static void bp_set_ipopts(int sd)
853{
6e01e275
RZ
854 int rcvttl = BFD_RCV_TTL_VAL;
855
856 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
857 log_fatal("set-ipopts: TTL configuration failed");
e9e2c950
RZ
858
859 if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
860 == -1)
6e01e275 861 log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl,
e9e2c950
RZ
862 strerror(errno));
863
864#ifdef BFD_LINUX
865 int pktinfo = BFD_PKT_INFO_VAL;
6e01e275 866
e9e2c950
RZ
867 /* Figure out address and interface to do the peer matching. */
868 if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
869 == -1)
6e01e275 870 log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo,
e9e2c950
RZ
871 strerror(errno));
872#endif /* BFD_LINUX */
873#ifdef BFD_BSD
874 int yes = 1;
875
876 /* Find out our address for peer matching. */
877 if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
6e01e275 878 log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes,
e9e2c950
RZ
879 strerror(errno));
880
881 /* Find out interface where the packet came in. */
882 if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
6e01e275 883 log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes,
e9e2c950
RZ
884 strerror(errno));
885#endif /* BFD_BSD */
886}
887
888static void bp_bind_ip(int sd, uint16_t port)
889{
890 struct sockaddr_in sin;
891
892 memset(&sin, 0, sizeof(sin));
893 sin.sin_family = AF_INET;
894 sin.sin_addr.s_addr = htonl(INADDR_ANY);
895 sin.sin_port = htons(port);
896 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
6e01e275 897 log_fatal("bind-ip: bind: %s", strerror(errno));
e9e2c950
RZ
898}
899
900int bp_udp_shop(void)
901{
902 int sd;
903
904 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
905 if (sd == -1)
6e01e275 906 log_fatal("udp-shop: socket: %s", strerror(errno));
e9e2c950
RZ
907
908 bp_set_ipopts(sd);
909 bp_bind_ip(sd, BFD_DEFDESTPORT);
910
911 return sd;
912}
913
914int bp_udp_mhop(void)
915{
916 int sd;
917
918 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
919 if (sd == -1)
6e01e275 920 log_fatal("udp-mhop: socket: %s", strerror(errno));
e9e2c950
RZ
921
922 bp_set_ipopts(sd);
923 bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
924
925 return sd;
926}
927
928int bp_peer_socket(struct bfd_peer_cfg *bpc)
929{
930 int sd, pcount;
931 struct sockaddr_in sin;
932 static int srcPort = BFD_SRCPORTINIT;
933
934 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
03e7f088
RZ
935 if (sd == -1) {
936 log_error("ipv4-new: failed to create socket: %s",
937 strerror(errno));
e9e2c950 938 return -1;
03e7f088 939 }
e9e2c950 940
8a9f760e
RZ
941 /* Set TTL to 255 for all transmitted packets */
942 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) {
943 close(sd);
944 return -1;
e9e2c950
RZ
945 }
946
947 /* Set TOS to CS6 for all transmitted packets */
6e01e275 948 if (bp_set_tos(sd, BFD_TOS_VAL) != 0) {
e9e2c950
RZ
949 close(sd);
950 return -1;
951 }
952
8a9f760e 953 if (bpc->bpc_has_localif) {
e9e2c950
RZ
954 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
955 close(sd);
956 return -1;
957 }
958 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
959 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
960 close(sd);
961 return -1;
962 }
963 }
964
965 /* Find an available source port in the proper range */
966 memset(&sin, 0, sizeof(sin));
967 sin = bpc->bpc_local.sa_sin;
968 sin.sin_family = AF_INET;
969#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
970 sin.sin_len = sizeof(sin);
971#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
8a9f760e 972 if (bpc->bpc_mhop)
e9e2c950
RZ
973 sin.sin_addr = bpc->bpc_local.sa_sin.sin_addr;
974 else
975 sin.sin_addr.s_addr = INADDR_ANY;
976
977 pcount = 0;
978 do {
979 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
980 /* Searched all ports, none available */
03e7f088
RZ
981 log_error("ipv4-new: failed to bind port: %s",
982 strerror(errno));
e9e2c950
RZ
983 close(sd);
984 return -1;
985 }
986 if (srcPort >= BFD_SRCPORTMAX)
987 srcPort = BFD_SRCPORTINIT;
988 sin.sin_port = htons(srcPort++);
989 } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
990
991 return sd;
992}
993
994
995/*
996 * IPv6 sockets
997 */
998
999int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
1000{
80edb675
RZ
1001 struct interface *ifp;
1002 int sd, pcount;
e9e2c950
RZ
1003 struct sockaddr_in6 sin6;
1004 static int srcPort = BFD_SRCPORTINIT;
1005
1006 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
03e7f088
RZ
1007 if (sd == -1) {
1008 log_error("ipv6-new: failed to create socket: %s",
1009 strerror(errno));
e9e2c950 1010 return -1;
03e7f088 1011 }
e9e2c950 1012
8a9f760e
RZ
1013 /* Set TTL to 255 for all transmitted packets */
1014 if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) {
1015 close(sd);
1016 return -1;
e9e2c950
RZ
1017 }
1018
1019 /* Set TOS to CS6 for all transmitted packets */
6e01e275 1020 if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) {
e9e2c950
RZ
1021 close(sd);
1022 return -1;
1023 }
1024
1025 /* Find an available source port in the proper range */
1026 memset(&sin6, 0, sizeof(sin6));
1027 sin6.sin6_family = AF_INET6;
1028#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1029 sin6.sin6_len = sizeof(sin6);
1030#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1031 sin6 = bpc->bpc_local.sa_sin6;
80edb675
RZ
1032 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
1033 ifp = if_lookup_by_name(bpc->bpc_localif, VRF_DEFAULT);
1034 sin6.sin6_scope_id =
1035 (ifp != NULL) ? ifp->ifindex : IFINDEX_INTERNAL;
1036 }
e9e2c950
RZ
1037
1038 if (bpc->bpc_has_localif) {
1039 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1040 close(sd);
1041 return -1;
1042 }
1043 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1044 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1045 close(sd);
1046 return -1;
1047 }
1048 }
1049
1050 pcount = 0;
1051 do {
1052 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1053 /* Searched all ports, none available */
03e7f088
RZ
1054 log_error("ipv6-new: failed to bind port: %s",
1055 strerror(errno));
e9e2c950
RZ
1056 close(sd);
1057 return -1;
1058 }
1059 if (srcPort >= BFD_SRCPORTMAX)
1060 srcPort = BFD_SRCPORTINIT;
1061 sin6.sin6_port = htons(srcPort++);
1062 } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1063
1064 return sd;
1065}
1066
6e01e275 1067int bp_set_ttlv6(int sd, uint8_t value)
e9e2c950 1068{
6e01e275
RZ
1069 int ttl = value;
1070
1071 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))
e9e2c950 1072 == -1) {
6e01e275
RZ
1073 log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1074 value, strerror(errno));
e9e2c950
RZ
1075 return -1;
1076 }
1077
1078 return 0;
1079}
1080
6e01e275 1081int bp_set_tosv6(int sd, uint8_t value)
e9e2c950 1082{
6e01e275
RZ
1083 int tos = value;
1084
1085 if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))
e9e2c950 1086 == -1) {
6e01e275 1087 log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value,
e9e2c950
RZ
1088 strerror(errno));
1089 return -1;
1090 }
1091
1092 return 0;
1093}
1094
1095static void bp_set_ipv6opts(int sd)
1096{
6e01e275
RZ
1097 int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1098 int ipv6_only = BFD_IPV6_ONLY_VAL;
e9e2c950 1099
6e01e275
RZ
1100 if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
1101 log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1102 BFD_TTL_VAL, strerror(errno));
e9e2c950 1103
6e01e275
RZ
1104 if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1)
1105 log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1106 BFD_RCV_TTL_VAL, strerror(errno));
e9e2c950
RZ
1107
1108 if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
6e01e275
RZ
1109 log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1110 ipv6_pktinfo, strerror(errno));
e9e2c950
RZ
1111
1112 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1113 sizeof(ipv6_only))
1114 == -1)
6e01e275
RZ
1115 log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1116 ipv6_only, strerror(errno));
e9e2c950
RZ
1117}
1118
1119static void bp_bind_ipv6(int sd, uint16_t port)
1120{
1121 struct sockaddr_in6 sin6;
1122
1123 memset(&sin6, 0, sizeof(sin6));
1124 sin6.sin6_family = AF_INET6;
1125 sin6.sin6_addr = in6addr_any;
1126 sin6.sin6_port = htons(port);
1127#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1128 sin6.sin6_len = sizeof(sin6);
1129#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1130 if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
6e01e275 1131 log_fatal("bind-ipv6: bind: %s", strerror(errno));
e9e2c950
RZ
1132}
1133
1134int bp_udp6_shop(void)
1135{
1136 int sd;
1137
1138 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1139 if (sd == -1)
6e01e275 1140 log_fatal("udp6-shop: socket: %s", strerror(errno));
e9e2c950
RZ
1141
1142 bp_set_ipv6opts(sd);
1143 bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1144
1145 return sd;
1146}
1147
1148int bp_udp6_mhop(void)
1149{
1150 int sd;
1151
1152 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1153 if (sd == -1)
6e01e275 1154 log_fatal("udp6-mhop: socket: %s", strerror(errno));
e9e2c950
RZ
1155
1156 bp_set_ipv6opts(sd);
1157 bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1158
1159 return sd;
1160}
2f11c53f
RZ
1161
1162int bp_echo_socket(void)
1163{
1164 int s;
1165
1166 s = socket(AF_INET, SOCK_DGRAM, 0);
1167 if (s == -1)
1168 log_fatal("echo-socket: socket: %s", strerror(errno));
1169
1170 bp_set_ipopts(s);
1171 bp_bind_ip(s, BFD_DEF_ECHO_PORT);
1172
1173 return s;
1174}
1175
1176int bp_echov6_socket(void)
1177{
1178 int s;
1179
1180 s = socket(AF_INET6, SOCK_DGRAM, 0);
1181 if (s == -1)
1182 log_fatal("echov6-socket: socket: %s", strerror(errno));
1183
1184 bp_set_ipv6opts(s);
1185 bp_bind_ipv6(s, BFD_DEF_ECHO_PORT);
1186
1187 return s;
1188}