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