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