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