]> git.proxmox.com Git - mirror_frr.git/blame - bfdd/bfd_packet.c
Merge pull request #13506 from anlancs/fix/bfdd-vrf-check
[mirror_frr.git] / bfdd / bfd_packet.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
e9e2c950
RZ
2/*********************************************************************
3 * Copyright 2017 Cumulus Networks, Inc. All rights reserved.
4 *
e9e2c950
RZ
5 * bfd_packet.c: implements the BFD protocol packet handling.
6 *
7 * Authors
8 * -------
9 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
10 * Kanna Rajagopal [kanna@cumulusnetworks.com]
11 * Radhika Mahankali [Radhika@cumulusnetworks.com]
12 */
13
14#include <zebra.h>
15
16#ifdef BFD_LINUX
17#include <linux/if_packet.h>
18#endif /* BFD_LINUX */
19
e9e2c950 20#include <netinet/if_ether.h>
e9e2c950 21#include <netinet/udp.h>
e9e2c950
RZ
22
23#include "lib/sockopt.h"
57485b0b 24#include "lib/checksum.h"
25#include "lib/network.h"
e9e2c950
RZ
26
27#include "bfd.h"
28
e9e2c950
RZ
29/*
30 * Prototypes
31 */
7bcadbae 32static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s);
2f11c53f
RZ
33int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
34 size_t datalen);
e9e2c950 35
7bcadbae 36static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd);
50571b2e 37ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
b333abc2 38 ifindex_t *ifindex, struct sockaddr_any *local,
50571b2e
RZ
39 struct sockaddr_any *peer);
40ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
b333abc2 41 ifindex_t *ifindex, struct sockaddr_any *local,
50571b2e 42 struct sockaddr_any *peer);
2f11c53f
RZ
43int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
44 struct sockaddr *to, socklen_t tolen);
618a06fe 45int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
46 uint32_t *my_discr, uint64_t *my_rtt);
57485b0b 47#ifdef BFD_LINUX
48ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen,
49 uint8_t *ttl, ifindex_t *ifindex,
50 struct sockaddr_any *local, struct sockaddr_any *peer);
51void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
52 struct sockaddr_any *peer, struct interface *ifp);
53int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
54 struct bfd_session *bfd);
55ssize_t bfd_recv_fp_echo(int sd, uint8_t *msgbuf, size_t msgbuflen,
56 uint8_t *ttl, ifindex_t *ifindex,
57 struct sockaddr_any *local, struct sockaddr_any *peer);
58#endif
e9e2c950
RZ
59
60/* socket related prototypes */
61static void bp_set_ipopts(int sd);
62static void bp_bind_ip(int sd, uint16_t port);
63static void bp_set_ipv6opts(int sd);
64static void bp_bind_ipv6(int sd, uint16_t port);
65
66
67/*
68 * Functions
69 */
2f11c53f
RZ
70int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
71 size_t datalen)
e9e2c950
RZ
72{
73 struct sockaddr *sa;
74 struct sockaddr_in sin;
75 struct sockaddr_in6 sin6;
e9e2c950
RZ
76 socklen_t slen;
77 ssize_t rv;
78 int sd = -1;
79
b88113ef 80 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
e9e2c950
RZ
81 memset(&sin6, 0, sizeof(sin6));
82 sin6.sin6_family = AF_INET6;
79b4a6fc 83 memcpy(&sin6.sin6_addr, &bs->key.peer, sizeof(sin6.sin6_addr));
1db60b57 84 if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
79b4a6fc
RZ
85 sin6.sin6_scope_id = bs->ifp->ifindex;
86
e9e2c950
RZ
87 sin6.sin6_port =
88 (port) ? *port
b88113ef 89 : (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
e9e2c950
RZ
90 ? htons(BFD_DEF_MHOP_DEST_PORT)
91 : htons(BFD_DEFDESTPORT);
92
93 sd = bs->sock;
94 sa = (struct sockaddr *)&sin6;
95 slen = sizeof(sin6);
96 } else {
97 memset(&sin, 0, sizeof(sin));
98 sin.sin_family = AF_INET;
79b4a6fc 99 memcpy(&sin.sin_addr, &bs->key.peer, sizeof(sin.sin_addr));
e9e2c950
RZ
100 sin.sin_port =
101 (port) ? *port
b88113ef 102 : (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
e9e2c950
RZ
103 ? htons(BFD_DEF_MHOP_DEST_PORT)
104 : htons(BFD_DEFDESTPORT);
105
106 sd = bs->sock;
107 sa = (struct sockaddr *)&sin;
108 slen = sizeof(sin);
109 }
110
111#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
112 sa->sa_len = slen;
113#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
114 rv = sendto(sd, data, datalen, 0, sa, slen);
115 if (rv <= 0) {
48da2c31
RZ
116 if (bglobal.debug_network)
117 zlog_debug("packet-send: send failure: %s",
118 strerror(errno));
e9e2c950
RZ
119 return -1;
120 }
48da2c31
RZ
121 if (rv < (ssize_t)datalen) {
122 if (bglobal.debug_network)
123 zlog_debug("packet-send: send partial: %s",
124 strerror(errno));
125 }
e9e2c950
RZ
126
127 return 0;
128}
129
57485b0b 130#ifdef BFD_LINUX
131/*
132 * Compute the UDP checksum.
133 *
134 * Checksum is not set in the packet, just computed.
135 *
136 * pkt
137 * Packet, fully filled out except for checksum field.
138 *
139 * pktsize
140 * sizeof(*pkt)
141 *
142 * ip
c9aac2c3 143 * IP address that pkt will be transmitted from and to.
57485b0b 144 *
145 * Returns:
146 * Checksum in network byte order.
147 */
148static uint16_t bfd_pkt_checksum(struct udphdr *pkt, size_t pktsize,
149 struct in6_addr *ip, sa_family_t family)
150{
151 uint16_t chksum;
152
153 pkt->check = 0;
154
155 if (family == AF_INET6) {
156 struct ipv6_ph ph = {};
157
158 memcpy(&ph.src, ip, sizeof(ph.src));
159 memcpy(&ph.dst, ip, sizeof(ph.dst));
160 ph.ulpl = htons(pktsize);
161 ph.next_hdr = IPPROTO_UDP;
162 chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
163 } else {
164 struct ipv4_ph ph = {};
165
166 memcpy(&ph.src, ip, sizeof(ph.src));
167 memcpy(&ph.dst, ip, sizeof(ph.dst));
168 ph.proto = IPPROTO_UDP;
169 ph.len = htons(pktsize);
170 chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
171 }
172
173 return chksum;
174}
175
176/*
177 * This routine creates the entire ECHO packet so that it will be looped
178 * in the forwarding plane of the peer router instead of going up the
179 * stack in BFD to be looped. If we haven't learned the peers MAC yet
180 * no echo is sent.
181 *
182 * echo packet with src/dst IP equal to local IP
183 * dest MAC as peer's MAC
184 *
185 * currently support ipv4
186 */
187void ptm_bfd_echo_fp_snd(struct bfd_session *bfd)
188{
189 int sd;
190 struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd);
191 int total_len = 0;
192 struct ethhdr *eth;
193 struct udphdr *uh;
194 struct iphdr *iph;
195 struct bfd_echo_pkt *beph;
196 static char sendbuff[100];
618a06fe 197 struct timeval time_sent;
57485b0b 198
199 if (!bvrf)
200 return;
201 if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET))
202 return;
203 if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
204 SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
205
206 memset(sendbuff, 0, sizeof(sendbuff));
207
208 /* add eth hdr */
209 eth = (struct ethhdr *)(sendbuff);
f14233ed
RZ
210 memcpy(eth->h_source, bfd->ifp->hw_addr, sizeof(eth->h_source));
211 memcpy(eth->h_dest, bfd->peer_hw_addr, sizeof(eth->h_dest));
57485b0b 212
213 total_len += sizeof(struct ethhdr);
214
215 sd = bvrf->bg_echo;
216 eth->h_proto = htons(ETH_P_IP);
217
218 /* add ip hdr */
219 iph = (struct iphdr *)(sendbuff + sizeof(struct ethhdr));
220
221 iph->ihl = sizeof(struct ip) >> 2;
222 iph->version = IPVERSION;
223 iph->tos = IPTOS_PREC_INTERNETCONTROL;
224 iph->id = (uint16_t)frr_weak_random();
225 iph->ttl = BFD_TTL_VAL;
226 iph->protocol = IPPROTO_UDP;
227 memcpy(&iph->saddr, &bfd->local_address.sa_sin.sin_addr,
228 sizeof(bfd->local_address.sa_sin.sin_addr));
229 memcpy(&iph->daddr, &bfd->local_address.sa_sin.sin_addr,
230 sizeof(bfd->local_address.sa_sin.sin_addr));
231 total_len += sizeof(struct iphdr);
232
233 /* add udp hdr */
234 uh = (struct udphdr *)(sendbuff + sizeof(struct iphdr) +
235 sizeof(struct ethhdr));
236 uh->source = htons(BFD_DEF_ECHO_PORT);
237 uh->dest = htons(BFD_DEF_ECHO_PORT);
238
239 total_len += sizeof(struct udphdr);
240
241 /* add bfd echo */
242 beph = (struct bfd_echo_pkt *)(sendbuff + sizeof(struct udphdr) +
243 sizeof(struct iphdr) +
244 sizeof(struct ethhdr));
245
246 beph->ver = BFD_ECHO_VERSION;
247 beph->len = BFD_ECHO_PKT_LEN;
248 beph->my_discr = htonl(bfd->discrs.my_discr);
249
618a06fe 250 /* RTT calculation: add starting time in packet */
251 monotime(&time_sent);
252 beph->time_sent_sec = htobe64(time_sent.tv_sec);
253 beph->time_sent_usec = htobe64(time_sent.tv_usec);
254
57485b0b 255 total_len += sizeof(struct bfd_echo_pkt);
256 uh->len =
257 htons(total_len - sizeof(struct iphdr) - sizeof(struct ethhdr));
258 uh->check = bfd_pkt_checksum(
259 uh, (total_len - sizeof(struct iphdr) - sizeof(struct ethhdr)),
260 (struct in6_addr *)&iph->saddr, AF_INET);
261
262 iph->tot_len = htons(total_len - sizeof(struct ethhdr));
263 iph->check = in_cksum((const void *)iph, sizeof(struct iphdr));
264
265 if (bp_udp_send_fp(sd, (uint8_t *)&sendbuff, total_len, bfd) == -1)
266 return;
267
268 bfd->stats.tx_echo_pkt++;
269}
270#endif
271
e9e2c950
RZ
272void ptm_bfd_echo_snd(struct bfd_session *bfd)
273{
79b4a6fc 274 struct sockaddr *sa;
2f11c53f
RZ
275 socklen_t salen;
276 int sd;
277 struct bfd_echo_pkt bep;
278 struct sockaddr_in sin;
279 struct sockaddr_in6 sin6;
7bcadbae 280 struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd);
e9e2c950 281
7bcadbae
PG
282 if (!bvrf)
283 return;
b88113ef
RZ
284 if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
285 SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
e9e2c950 286
2f11c53f
RZ
287 memset(&bep, 0, sizeof(bep));
288 bep.ver = BFD_ECHO_VERSION;
289 bep.len = BFD_ECHO_PKT_LEN;
290 bep.my_discr = htonl(bfd->discrs.my_discr);
291
b88113ef 292 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
f1446f98
DS
293 if (bvrf->bg_echov6 == -1)
294 return;
7bcadbae 295 sd = bvrf->bg_echov6;
79b4a6fc 296 memset(&sin6, 0, sizeof(sin6));
4cf4e832 297 sin6.sin6_family = AF_INET6;
79b4a6fc
RZ
298 memcpy(&sin6.sin6_addr, &bfd->key.peer, sizeof(sin6.sin6_addr));
299 if (bfd->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
300 sin6.sin6_scope_id = bfd->ifp->ifindex;
301
2f11c53f
RZ
302 sin6.sin6_port = htons(BFD_DEF_ECHO_PORT);
303#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
304 sin6.sin6_len = sizeof(sin6);
305#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
306
79b4a6fc 307 sa = (struct sockaddr *)&sin6;
2f11c53f 308 salen = sizeof(sin6);
e9e2c950 309 } else {
7bcadbae 310 sd = bvrf->bg_echo;
f91d3ae3 311 memset(&sin, 0, sizeof(sin));
4cf4e832 312 sin.sin_family = AF_INET;
79b4a6fc 313 memcpy(&sin.sin_addr, &bfd->key.peer, sizeof(sin.sin_addr));
2f11c53f
RZ
314 sin.sin_port = htons(BFD_DEF_ECHO_PORT);
315#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
316 sin.sin_len = sizeof(sin);
317#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
e9e2c950 318
79b4a6fc 319 sa = (struct sockaddr *)&sin;
2f11c53f 320 salen = sizeof(sin);
e9e2c950 321 }
79b4a6fc
RZ
322 if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep), sa,
323 salen)
2f11c53f
RZ
324 == -1)
325 return;
e9e2c950
RZ
326
327 bfd->stats.tx_echo_pkt++;
328}
329
7bcadbae 330static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s)
e9e2c950 331{
e9e2c950 332 struct bfd_session *bfd;
2f11c53f 333 uint32_t my_discr = 0;
618a06fe 334 uint64_t my_rtt = 0;
2f11c53f 335 uint8_t ttl = 0;
e9e2c950 336
2f11c53f 337 /* Receive and parse echo packet. */
618a06fe 338 if (bp_bfd_echo_in(bvrf, s, &ttl, &my_discr, &my_rtt) == -1)
2f11c53f 339 return 0;
e9e2c950
RZ
340
341 /* Your discriminator not zero - use it to find session */
342 bfd = bfd_id_lookup(my_discr);
343 if (bfd == NULL) {
48da2c31
RZ
344 if (bglobal.debug_network)
345 zlog_debug("echo-packet: no matching session (id:%u)",
346 my_discr);
e9e2c950
RZ
347 return -1;
348 }
349
b88113ef 350 if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
48da2c31
RZ
351 if (bglobal.debug_network)
352 zlog_debug("echo-packet: echo disabled [%s] (id:%u)",
353 bs_to_string(bfd), my_discr);
e9e2c950
RZ
354 return -1;
355 }
356
618a06fe 357 /* RTT Calculation: add current RTT to samples */
358 if (my_rtt != 0) {
359 bfd->rtt[bfd->rtt_index] = my_rtt;
360 bfd->rtt_index++;
361 if (bfd->rtt_index >= BFD_RTT_SAMPLE)
362 bfd->rtt_index = 0;
363 if (bfd->rtt_valid < BFD_RTT_SAMPLE)
364 bfd->rtt_valid++;
365 }
366
e9e2c950
RZ
367 bfd->stats.rx_echo_pkt++;
368
369 /* Compute detect time */
370 bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO;
371
372 /* Update echo receive timeout. */
451eb5a2
RZ
373 if (bfd->echo_detect_TO > 0)
374 bfd_echo_recvtimer_update(bfd);
e9e2c950
RZ
375
376 return 0;
377}
378
379void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
380{
bfea1011 381 struct bfd_pkt cp = {};
e9e2c950 382
e9e2c950
RZ
383 /* Set fields according to section 6.5.7 */
384 cp.diag = bfd->local_diag;
385 BFD_SETVER(cp.diag, BFD_VERSION);
386 cp.flags = 0;
387 BFD_SETSTATE(cp.flags, bfd->ses_state);
9beff0bd 388
b88113ef 389 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_CBIT))
9beff0bd
PG
390 BFD_SETCBIT(cp.flags, BFD_CBIT);
391
e9e2c950 392 BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND);
0592db96
RZ
393
394 /*
395 * Polling and Final can't be set at the same time.
396 *
397 * RFC 5880, Section 6.5.
398 */
e9e2c950 399 BFD_SETFBIT(cp.flags, fbit);
0592db96
RZ
400 if (fbit == 0)
401 BFD_SETPBIT(cp.flags, bfd->polling);
402
e9e2c950
RZ
403 cp.detect_mult = bfd->detect_mult;
404 cp.len = BFD_PKT_LEN;
405 cp.discrs.my_discr = htonl(bfd->discrs.my_discr);
406 cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
407 if (bfd->polling) {
408 cp.timers.desired_min_tx =
f43b9368 409 htonl(bfd->timers.desired_min_tx);
e9e2c950 410 cp.timers.required_min_rx =
f43b9368 411 htonl(bfd->timers.required_min_rx);
e9e2c950 412 } else {
f43b9368
RZ
413 /*
414 * We can only announce current setting on poll, this
415 * avoids timing mismatch with our peer and give it
416 * the oportunity to learn. See `bs_final_handler` for
417 * more information.
418 */
419 cp.timers.desired_min_tx =
420 htonl(bfd->cur_timers.desired_min_tx);
421 cp.timers.required_min_rx =
422 htonl(bfd->cur_timers.required_min_rx);
e9e2c950 423 }
4df3e31c 424 cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo_rx);
e9e2c950 425
2f11c53f 426 if (_ptm_bfd_send(bfd, NULL, &cp, BFD_PKT_LEN) != 0)
e9e2c950 427 return;
e9e2c950
RZ
428
429 bfd->stats.tx_ctrl_pkt++;
430}
431
57485b0b 432#ifdef BFD_LINUX
433/*
434 * receive the ipv4 echo packet that was loopback in the peers forwarding plane
435 */
436ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen,
437 uint8_t *ttl, ifindex_t *ifindex,
438 struct sockaddr_any *local, struct sockaddr_any *peer)
439{
440 ssize_t mlen;
441 struct sockaddr_ll msgaddr;
442 struct msghdr msghdr;
443 struct iovec iov[1];
444 uint16_t recv_checksum;
445 uint16_t checksum;
446 struct iphdr *ip;
447 struct udphdr *uh;
448
449 /* Prepare the recvmsg params. */
450 iov[0].iov_base = msgbuf;
451 iov[0].iov_len = msgbuflen;
452
453 memset(&msghdr, 0, sizeof(msghdr));
454 msghdr.msg_name = &msgaddr;
455 msghdr.msg_namelen = sizeof(msgaddr);
456 msghdr.msg_iov = iov;
457 msghdr.msg_iovlen = 1;
458
459 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
460 if (mlen == -1) {
461 if (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINTR)
462 zlog_err("%s: recv failed: %s", __func__,
463 strerror(errno));
464
465 return -1;
466 }
467
468 ip = (struct iphdr *)(msgbuf + sizeof(struct ethhdr));
469
470 /* verify ip checksum */
471 recv_checksum = ip->check;
472 ip->check = 0;
473 checksum = in_cksum((const void *)ip, sizeof(struct iphdr));
474 if (recv_checksum != checksum) {
475 if (bglobal.debug_network)
476 zlog_debug(
477 "%s: invalid iphdr checksum expected 0x%x rcvd 0x%x",
478 __func__, checksum, recv_checksum);
479 return -1;
480 }
481
482 *ttl = ip->ttl;
483 if (*ttl != 254) {
57485b0b 484 if (bglobal.debug_network)
485 zlog_debug("%s: invalid TTL: %u", __func__, *ttl);
486 return -1;
487 }
488
489 local->sa_sin.sin_family = AF_INET;
490 memcpy(&local->sa_sin.sin_addr, &ip->saddr, sizeof(ip->saddr));
491 peer->sa_sin.sin_family = AF_INET;
492 memcpy(&peer->sa_sin.sin_addr, &ip->daddr, sizeof(ip->daddr));
493
494 *ifindex = msgaddr.sll_ifindex;
495
496 /* verify udp checksum */
497 uh = (struct udphdr *)(msgbuf + sizeof(struct iphdr) +
498 sizeof(struct ethhdr));
499 recv_checksum = uh->check;
500 uh->check = 0;
501 checksum = bfd_pkt_checksum(uh, ntohs(uh->len),
502 (struct in6_addr *)&ip->saddr, AF_INET);
503 if (recv_checksum != checksum) {
504 if (bglobal.debug_network)
505 zlog_debug(
506 "%s: invalid udphdr checksum expected 0x%x rcvd 0x%x",
507 __func__, checksum, recv_checksum);
508 return -1;
509 }
510 return mlen;
511}
512#endif
513
50571b2e 514ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
b333abc2 515 ifindex_t *ifindex, struct sockaddr_any *local,
50571b2e 516 struct sockaddr_any *peer)
e9e2c950
RZ
517{
518 struct cmsghdr *cm;
e9e2c950 519 ssize_t mlen;
16084964
RZ
520 struct sockaddr_in msgaddr;
521 struct msghdr msghdr;
522 struct iovec iov[1];
523 uint8_t cmsgbuf[255];
524
525 /* Prepare the recvmsg params. */
526 iov[0].iov_base = msgbuf;
50571b2e 527 iov[0].iov_len = msgbuflen;
16084964
RZ
528
529 memset(&msghdr, 0, sizeof(msghdr));
530 msghdr.msg_name = &msgaddr;
531 msghdr.msg_namelen = sizeof(msgaddr);
532 msghdr.msg_iov = iov;
533 msghdr.msg_iovlen = 1;
534 msghdr.msg_control = cmsgbuf;
535 msghdr.msg_controllen = sizeof(cmsgbuf);
536
e9e2c950
RZ
537 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
538 if (mlen == -1) {
03e7f088 539 if (errno != EAGAIN)
259b64eb 540 zlog_err("ipv4-recv: recv failed: %s", strerror(errno));
03e7f088 541
e9e2c950
RZ
542 return -1;
543 }
544
545 /* Get source address */
546 peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
547
548 /* Get and check TTL */
549 for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
550 cm = CMSG_NXTHDR(&msghdr, cm)) {
551 if (cm->cmsg_level != IPPROTO_IP)
552 continue;
553
554 switch (cm->cmsg_type) {
555#ifdef BFD_LINUX
556 case IP_TTL: {
50571b2e
RZ
557 uint32_t ttlval;
558
559 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
560 if (ttlval > 255) {
48da2c31 561 if (bglobal.debug_network)
9e5fc01f 562 zlog_debug("%s: invalid TTL: %u",
563 __func__, ttlval);
e9e2c950
RZ
564 return -1;
565 }
50571b2e 566 *ttl = ttlval;
e9e2c950
RZ
567 break;
568 }
569
570 case IP_PKTINFO: {
571 struct in_pktinfo *pi =
572 (struct in_pktinfo *)CMSG_DATA(cm);
573
574 if (pi == NULL)
575 break;
576
577 local->sa_sin.sin_family = AF_INET;
578 local->sa_sin.sin_addr = pi->ipi_addr;
f43a14d2
RZ
579#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
580 local->sa_sin.sin_len = sizeof(local->sa_sin);
581#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
80edb675 582
b333abc2 583 *ifindex = pi->ipi_ifindex;
e9e2c950
RZ
584 break;
585 }
586#endif /* BFD_LINUX */
587#ifdef BFD_BSD
588 case IP_RECVTTL: {
50571b2e 589 memcpy(ttl, CMSG_DATA(cm), sizeof(*ttl));
e9e2c950
RZ
590 break;
591 }
592
593 case IP_RECVDSTADDR: {
594 struct in_addr ia;
595
596 memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
597 local->sa_sin.sin_family = AF_INET;
598 local->sa_sin.sin_addr = ia;
f43a14d2
RZ
599#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
600 local->sa_sin.sin_len = sizeof(local->sa_sin);
601#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
e9e2c950
RZ
602 break;
603 }
604#endif /* BFD_BSD */
605
606 default:
607 /*
608 * On *BSDs we expect to land here when skipping
609 * the IP_RECVIF header. It will be handled by
610 * getsockopt_ifindex() below.
611 */
612 /* NOTHING */
613 break;
614 }
615 }
616
617 /* OS agnostic way of getting interface name. */
b333abc2
RZ
618 if (*ifindex == IFINDEX_INTERNAL)
619 *ifindex = getsockopt_ifindex(AF_INET, &msghdr);
e9e2c950
RZ
620
621 return mlen;
622}
623
50571b2e 624ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
b333abc2 625 ifindex_t *ifindex, struct sockaddr_any *local,
50571b2e 626 struct sockaddr_any *peer)
e9e2c950
RZ
627{
628 struct cmsghdr *cm;
629 struct in6_pktinfo *pi6 = NULL;
630 ssize_t mlen;
6e01e275 631 uint32_t ttlval;
16084964
RZ
632 struct sockaddr_in6 msgaddr6;
633 struct msghdr msghdr6;
634 struct iovec iov[1];
635 uint8_t cmsgbuf6[255];
636
637 /* Prepare the recvmsg params. */
638 iov[0].iov_base = msgbuf;
50571b2e 639 iov[0].iov_len = msgbuflen;
16084964
RZ
640
641 memset(&msghdr6, 0, sizeof(msghdr6));
642 msghdr6.msg_name = &msgaddr6;
643 msghdr6.msg_namelen = sizeof(msgaddr6);
644 msghdr6.msg_iov = iov;
645 msghdr6.msg_iovlen = 1;
646 msghdr6.msg_control = cmsgbuf6;
647 msghdr6.msg_controllen = sizeof(cmsgbuf6);
648
e9e2c950
RZ
649 mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
650 if (mlen == -1) {
03e7f088 651 if (errno != EAGAIN)
259b64eb 652 zlog_err("ipv6-recv: recv failed: %s", strerror(errno));
03e7f088 653
e9e2c950
RZ
654 return -1;
655 }
656
657 /* Get source address */
658 peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
659
660 /* Get and check TTL */
661 for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
662 cm = CMSG_NXTHDR(&msghdr6, cm)) {
663 if (cm->cmsg_level != IPPROTO_IPV6)
664 continue;
665
666 if (cm->cmsg_type == IPV6_HOPLIMIT) {
6e01e275 667 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
50571b2e 668 if (ttlval > 255) {
48da2c31 669 if (bglobal.debug_network)
9e5fc01f 670 zlog_debug("%s: invalid TTL: %u",
671 __func__, ttlval);
e9e2c950
RZ
672 return -1;
673 }
50571b2e
RZ
674
675 *ttl = ttlval;
e9e2c950
RZ
676 } else if (cm->cmsg_type == IPV6_PKTINFO) {
677 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
678 if (pi6) {
f43a14d2 679 local->sa_sin6.sin6_family = AF_INET6;
e9e2c950 680 local->sa_sin6.sin6_addr = pi6->ipi6_addr;
f43a14d2
RZ
681#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
682 local->sa_sin6.sin6_len = sizeof(local->sa_sin6);
683#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
80edb675 684
b333abc2 685 *ifindex = pi6->ipi6_ifindex;
fa3bf3a2
MS
686
687 /* Set scope ID for link local addresses. */
688 if (IN6_IS_ADDR_LINKLOCAL(
689 &peer->sa_sin6.sin6_addr))
690 peer->sa_sin6.sin6_scope_id = *ifindex;
691 if (IN6_IS_ADDR_LINKLOCAL(
692 &local->sa_sin6.sin6_addr))
693 local->sa_sin6.sin6_scope_id = *ifindex;
e9e2c950
RZ
694 }
695 }
696 }
697
698 return mlen;
699}
700
7bcadbae 701static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd)
e9e2c950 702{
7bcadbae 703 if (sd == bvrf->bg_shop) {
e16d030c 704 EVENT_OFF(bvrf->bg_ev[0]);
907a2395
DS
705 event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop,
706 &bvrf->bg_ev[0]);
7bcadbae 707 } else if (sd == bvrf->bg_mhop) {
e16d030c 708 EVENT_OFF(bvrf->bg_ev[1]);
907a2395
DS
709 event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
710 &bvrf->bg_ev[1]);
7bcadbae 711 } else if (sd == bvrf->bg_shop6) {
e16d030c 712 EVENT_OFF(bvrf->bg_ev[2]);
907a2395
DS
713 event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
714 &bvrf->bg_ev[2]);
7bcadbae 715 } else if (sd == bvrf->bg_mhop6) {
e16d030c 716 EVENT_OFF(bvrf->bg_ev[3]);
907a2395
DS
717 event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
718 &bvrf->bg_ev[3]);
7bcadbae 719 } else if (sd == bvrf->bg_echo) {
e16d030c 720 EVENT_OFF(bvrf->bg_ev[4]);
907a2395
DS
721 event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
722 &bvrf->bg_ev[4]);
7bcadbae 723 } else if (sd == bvrf->bg_echov6) {
e16d030c 724 EVENT_OFF(bvrf->bg_ev[5]);
907a2395
DS
725 event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
726 &bvrf->bg_ev[5]);
e9e2c950
RZ
727 }
728}
729
0f9de11a 730PRINTFRR(6, 7)
03e7f088 731static void cp_debug(bool mhop, struct sockaddr_any *peer,
b333abc2
RZ
732 struct sockaddr_any *local, ifindex_t ifindex,
733 vrf_id_t vrfid, const char *fmt, ...)
03e7f088
RZ
734{
735 char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
736 va_list vl;
737
48da2c31
RZ
738 /* Don't to any processing if debug is disabled. */
739 if (bglobal.debug_network == false)
740 return;
741
03e7f088
RZ
742 if (peer->sa_sin.sin_family)
743 snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
744 else
745 peerstr[0] = 0;
746
747 if (local->sa_sin.sin_family)
748 snprintf(localstr, sizeof(localstr), " local:%s",
749 satostr(local));
750 else
751 localstr[0] = 0;
752
b333abc2
RZ
753 if (ifindex != IFINDEX_INTERNAL)
754 snprintf(portstr, sizeof(portstr), " port:%u", ifindex);
03e7f088
RZ
755 else
756 portstr[0] = 0;
757
b333abc2
RZ
758 if (vrfid != VRF_DEFAULT)
759 snprintf(vrfstr, sizeof(vrfstr), " vrf:%u", vrfid);
03e7f088
RZ
760 else
761 vrfstr[0] = 0;
762
763 va_start(vl, fmt);
764 vsnprintf(buf, sizeof(buf), fmt, vl);
765 va_end(vl);
766
259b64eb
RZ
767 zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
768 mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
03e7f088
RZ
769}
770
e6685141 771void bfd_recv_cb(struct event *t)
e9e2c950 772{
e16d030c 773 int sd = EVENT_FD(t);
e9e2c950
RZ
774 struct bfd_session *bfd;
775 struct bfd_pkt *cp;
8a9f760e 776 bool is_mhop;
e9e2c950 777 ssize_t mlen = 0;
9f37770f 778 uint8_t ttl = 0;
f7b3ca19 779 vrf_id_t vrfid;
b333abc2 780 ifindex_t ifindex = IFINDEX_INTERNAL;
e9e2c950 781 struct sockaddr_any local, peer;
50571b2e 782 uint8_t msgbuf[1516];
134a53ec 783 struct interface *ifp = NULL;
e16d030c 784 struct bfd_vrf_global *bvrf = EVENT_ARG(t);
e9e2c950
RZ
785
786 /* Schedule next read. */
7bcadbae 787 bfd_sd_reschedule(bvrf, sd);
e9e2c950 788
03e7f088 789 /* Handle echo packets. */
7bcadbae
PG
790 if (sd == bvrf->bg_echo || sd == bvrf->bg_echov6) {
791 ptm_bfd_process_echo_pkt(bvrf, sd);
cc9f21da 792 return;
e9e2c950
RZ
793 }
794
ff98a589 795 /* Sanitize input/output. */
ff98a589
RZ
796 memset(&local, 0, sizeof(local));
797 memset(&peer, 0, sizeof(peer));
798
03e7f088 799 /* Handle control packets. */
8a9f760e 800 is_mhop = false;
7bcadbae
PG
801 if (sd == bvrf->bg_shop || sd == bvrf->bg_mhop) {
802 is_mhop = sd == bvrf->bg_mhop;
b333abc2 803 mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
50571b2e 804 &local, &peer);
7bcadbae
PG
805 } else if (sd == bvrf->bg_shop6 || sd == bvrf->bg_mhop6) {
806 is_mhop = sd == bvrf->bg_mhop6;
b333abc2 807 mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
50571b2e 808 &local, &peer);
e9e2c950 809 }
e9e2c950 810
eb4135ba
IR
811 /*
812 * With netns backend, we have a separate socket in each VRF. It means
813 * that bvrf here is correct and we believe the bvrf->vrf->vrf_id.
814 * With VRF-lite backend, we have a single socket in the default VRF.
815 * It means that we can't believe the bvrf->vrf->vrf_id. But in
816 * VRF-lite, the ifindex is globally unique, so we can retrieve the
817 * correct vrf_id from the interface.
134a53ec 818 */
eb4135ba 819 vrfid = bvrf->vrf->vrf_id;
134a53ec
PG
820 if (ifindex) {
821 ifp = if_lookup_by_index(ifindex, vrfid);
822 if (ifp)
096f7609 823 vrfid = ifp->vrf->vrf_id;
134a53ec
PG
824 }
825
e9e2c950
RZ
826 /* Implement RFC 5880 6.8.6 */
827 if (mlen < BFD_PKT_LEN) {
b333abc2 828 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
d131cca9 829 "too small (%zd bytes)", mlen);
cc9f21da 830 return;
e9e2c950
RZ
831 }
832
262e1d25 833 /* Validate single hop packet TTL. */
d8729f8c 834 if ((!is_mhop) && (ttl != BFD_TTL_VAL)) {
b333abc2 835 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
50571b2e 836 "invalid TTL: %d expected %d", ttl, BFD_TTL_VAL);
cc9f21da 837 return;
50571b2e
RZ
838 }
839
03e7f088
RZ
840 /*
841 * Parse the control header for inconsistencies:
842 * - Invalid version;
843 * - Bad multiplier configuration;
844 * - Short packets;
845 * - Invalid discriminator;
846 */
16084964 847 cp = (struct bfd_pkt *)(msgbuf);
e9e2c950 848 if (BFD_GETVER(cp->diag) != BFD_VERSION) {
b333abc2 849 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
03e7f088 850 "bad version %d", BFD_GETVER(cp->diag));
cc9f21da 851 return;
e9e2c950
RZ
852 }
853
854 if (cp->detect_mult == 0) {
b333abc2 855 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
03e7f088 856 "detect multiplier set to zero");
cc9f21da 857 return;
e9e2c950
RZ
858 }
859
860 if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
b333abc2 861 cp_debug(is_mhop, &peer, &local, ifindex, vrfid, "too small");
cc9f21da 862 return;
e9e2c950
RZ
863 }
864
865 if (cp->discrs.my_discr == 0) {
b333abc2 866 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
03e7f088 867 "'my discriminator' is zero");
cc9f21da 868 return;
e9e2c950
RZ
869 }
870
03e7f088 871 /* Find the session that this packet belongs. */
eb4135ba 872 bfd = ptm_bfd_sess_find(cp, &peer, &local, ifp, vrfid, is_mhop);
e9e2c950 873 if (bfd == NULL) {
b333abc2 874 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
03e7f088 875 "no session found");
cc9f21da 876 return;
e9e2c950 877 }
edc3f631
PG
878 /*
879 * We may have a situation where received packet is on wrong vrf
880 */
b17c1796 881 if (bfd && bfd->vrf && bfd->vrf->vrf_id != vrfid) {
edc3f631
PG
882 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
883 "wrong vrfid.");
884 return;
885 }
e9e2c950 886
7ffd0b80 887 /* Ensure that existing good sessions are not overridden. */
888 if (!cp->discrs.remote_discr && bfd->ses_state != PTM_BFD_DOWN &&
889 bfd->ses_state != PTM_BFD_ADM_DOWN) {
890 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
891 "'remote discriminator' is zero, not overridden");
892 return;
893 }
894
03e7f088
RZ
895 /*
896 * Multi hop: validate packet TTL.
57485b0b 897 * Single hop: set local address that received the packet.
898 * set peers mac address for echo packets
03e7f088 899 */
e9e2c950 900 if (is_mhop) {
262e1d25 901 if (ttl < bfd->mh_ttl) {
b333abc2 902 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
03e7f088 903 "exceeded max hop count (expected %d, got %d)",
262e1d25 904 bfd->mh_ttl, ttl);
cc9f21da 905 return;
e9e2c950 906 }
57485b0b 907 } else {
908
909 if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC)
910 bfd->local_address = local;
911#ifdef BFD_LINUX
912 if (ifp)
913 bfd_peer_mac_set(sd, bfd, &peer, ifp);
914#endif
e9e2c950
RZ
915 }
916
95fd223c 917 bfd->stats.rx_ctrl_pkt++;
918
e9e2c950
RZ
919 /*
920 * If no interface was detected, save the interface where the
921 * packet came in.
922 */
edc3622d 923 if (!is_mhop && bfd->ifp == NULL)
eb4135ba 924 bfd->ifp = ifp;
e9e2c950 925
03e7f088 926 /* Log remote discriminator changes. */
e9e2c950 927 if ((bfd->discrs.remote_discr != 0)
03e7f088 928 && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
b333abc2 929 cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
e945606e 930 "remote discriminator mismatch (expected %u, got %u)",
03e7f088 931 bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
e9e2c950
RZ
932
933 bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
934
e9e2c950
RZ
935 /* Save remote diagnostics before state switch. */
936 bfd->remote_diag = cp->diag & BFD_DIAGMASK;
937
c0ef9a8a
RZ
938 /* Update remote timers settings. */
939 bfd->remote_timers.desired_min_tx = ntohl(cp->timers.desired_min_tx);
940 bfd->remote_timers.required_min_rx = ntohl(cp->timers.required_min_rx);
941 bfd->remote_timers.required_min_echo =
942 ntohl(cp->timers.required_min_echo);
943 bfd->remote_detect_mult = cp->detect_mult;
944
9beff0bd
PG
945 if (BFD_GETCBIT(cp->flags))
946 bfd->remote_cbit = 1;
947 else
948 bfd->remote_cbit = 0;
949
aef131af
RZ
950 /* State switch from section 6.2. */
951 bs_state_handler(bfd, BFD_GETSTATE(cp->flags));
e9e2c950 952
c0ef9a8a
RZ
953 /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
954 if (bfd->polling && BFD_GETFBIT(cp->flags)) {
09e949a6 955 /* Disable polling. */
c0ef9a8a 956 bfd->polling = 0;
e9e2c950 957
c0ef9a8a
RZ
958 /* Handle poll finalization. */
959 bs_final_handler(bfd);
e9e2c950
RZ
960 }
961
074f7681
IR
962 /*
963 * Detection timeout calculation:
964 * The minimum detection timeout is the remote detection
965 * multipler (number of packets to be missed) times the agreed
966 * transmission interval.
967 *
968 * RFC 5880, Section 6.8.4.
969 */
970 if (bfd->cur_timers.required_min_rx > bfd->remote_timers.desired_min_tx)
971 bfd->detect_TO = bfd->remote_detect_mult
972 * bfd->cur_timers.required_min_rx;
973 else
974 bfd->detect_TO = bfd->remote_detect_mult
975 * bfd->remote_timers.desired_min_tx;
976
977 /* Apply new receive timer immediately. */
978 bfd_recvtimer_update(bfd);
979
c0ef9a8a
RZ
980 /* Handle echo timers changes. */
981 bs_echo_timer_handler(bfd);
e9e2c950
RZ
982
983 /*
c0ef9a8a
RZ
984 * We've received a packet with the POLL bit set, we must send
985 * a control packet back with the FINAL bit set.
986 *
987 * RFC 5880, Section 6.5.
e9e2c950 988 */
0c1af3e4
RZ
989 if (BFD_GETPBIT(cp->flags)) {
990 /* We are finalizing a poll negotiation. */
991 bs_final_handler(bfd);
992
993 /* Send the control packet with the final bit immediately. */
c0ef9a8a 994 ptm_bfd_snd(bfd, 1);
0c1af3e4 995 }
e9e2c950
RZ
996}
997
2f11c53f
RZ
998/*
999 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
1000 * the packet is looped back or returns the my discriminator ID along
1001 * with the TTL.
1002 *
1003 * Returns -1 on error or loopback or 0 on success.
1004 */
618a06fe 1005int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
1006 uint32_t *my_discr, uint64_t *my_rtt)
2f11c53f
RZ
1007{
1008 struct bfd_echo_pkt *bep;
1009 ssize_t rlen;
1010 struct sockaddr_any local, peer;
b333abc2
RZ
1011 ifindex_t ifindex = IFINDEX_INTERNAL;
1012 vrf_id_t vrfid = VRF_DEFAULT;
2f11c53f 1013 uint8_t msgbuf[1516];
57485b0b 1014 size_t bfd_offset = 0;
1015
1016 if (sd == bvrf->bg_echo) {
1017#ifdef BFD_LINUX
1018 rlen = bfd_recv_ipv4_fp(sd, msgbuf, sizeof(msgbuf), ttl,
1019 &ifindex, &local, &peer);
2f11c53f 1020
57485b0b 1021 /* silently drop echo packet that is looped in fastpath but
1022 * still comes up to BFD
1023 */
1024 if (rlen == -1)
1025 return -1;
1026 bfd_offset = sizeof(struct udphdr) + sizeof(struct iphdr) +
1027 sizeof(struct ethhdr);
1028#else
b333abc2 1029 rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex,
2f11c53f 1030 &local, &peer);
57485b0b 1031 bfd_offset = 0;
1032#endif
1033 } else {
b333abc2 1034 rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex,
2f11c53f 1035 &local, &peer);
57485b0b 1036 bfd_offset = 0;
1037 }
2f11c53f
RZ
1038
1039 /* Short packet, better not risk reading it. */
1040 if (rlen < (ssize_t)sizeof(*bep)) {
b333abc2 1041 cp_debug(false, &peer, &local, ifindex, vrfid,
2f11c53f
RZ
1042 "small echo packet");
1043 return -1;
1044 }
1045
57485b0b 1046 /* Test for loopback for ipv6, ipv4 is looped in forwarding plane */
1047 if ((*ttl == BFD_TTL_VAL) && (sd == bvrf->bg_echov6)) {
2f11c53f
RZ
1048 bp_udp_send(sd, *ttl - 1, msgbuf, rlen,
1049 (struct sockaddr *)&peer,
7bcadbae 1050 (sd == bvrf->bg_echo) ? sizeof(peer.sa_sin)
2f11c53f
RZ
1051 : sizeof(peer.sa_sin6));
1052 return -1;
1053 }
1054
1055 /* Read my discriminator from BFD Echo packet. */
57485b0b 1056 bep = (struct bfd_echo_pkt *)(msgbuf + bfd_offset);
2f11c53f
RZ
1057 *my_discr = ntohl(bep->my_discr);
1058 if (*my_discr == 0) {
b333abc2 1059 cp_debug(false, &peer, &local, ifindex, vrfid,
2f11c53f
RZ
1060 "invalid echo packet discriminator (zero)");
1061 return -1;
1062 }
1063
618a06fe 1064#ifdef BFD_LINUX
1065 /* RTT Calculation: determine RTT time of IPv4 echo pkt */
1066 if (sd == bvrf->bg_echo) {
1067 struct timeval time_sent = {0, 0};
1068
1069 time_sent.tv_sec = be64toh(bep->time_sent_sec);
1070 time_sent.tv_usec = be64toh(bep->time_sent_usec);
1071 *my_rtt = monotime_since(&time_sent, NULL);
1072 }
1073#endif
1074
2f11c53f
RZ
1075 return 0;
1076}
1077
57485b0b 1078#ifdef BFD_LINUX
1079/*
1080 * send a bfd packet with src/dst same IP so that the peer will receive
1081 * the packet and forward it back to sender in the forwarding plane
1082 */
1083int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
1084 struct bfd_session *bfd)
1085{
1086 ssize_t wlen;
618a06fe 1087 struct msghdr msg = {0};
57485b0b 1088 struct iovec iov[1];
1089 uint8_t msgctl[255];
618a06fe 1090 struct sockaddr_ll sadr_ll = {0};
57485b0b 1091
1092 sadr_ll.sll_ifindex = bfd->ifp->ifindex;
1093 sadr_ll.sll_halen = ETH_ALEN;
1094 memcpy(sadr_ll.sll_addr, bfd->peer_hw_addr, sizeof(bfd->peer_hw_addr));
1095 sadr_ll.sll_protocol = htons(ETH_P_IP);
1096
1097 /* Prepare message data. */
1098 iov[0].iov_base = data;
1099 iov[0].iov_len = datalen;
1100
57485b0b 1101 memset(msgctl, 0, sizeof(msgctl));
1102 msg.msg_name = &sadr_ll;
1103 msg.msg_namelen = sizeof(sadr_ll);
1104 msg.msg_iov = iov;
1105 msg.msg_iovlen = 1;
1106
1107 /* Send echo to peer */
1108 wlen = sendmsg(sd, &msg, 0);
1109
1110 if (wlen <= 0) {
1111 if (bglobal.debug_network)
9e5fc01f 1112 zlog_debug("%s: loopback failure: (%d) %s", __func__,
1113 errno, strerror(errno));
57485b0b 1114 return -1;
1115 } else if (wlen < (ssize_t)datalen) {
1116 if (bglobal.debug_network)
9e5fc01f 1117 zlog_debug("%s: partial send: %zd expected %zu",
1118 __func__, wlen, datalen);
57485b0b 1119 return -1;
1120 }
1121
1122 return 0;
1123}
1124#endif
1125
2f11c53f
RZ
1126int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
1127 struct sockaddr *to, socklen_t tolen)
1128{
1129 struct cmsghdr *cmsg;
1130 ssize_t wlen;
1131 int ttlval = ttl;
1132 bool is_ipv6 = to->sa_family == AF_INET6;
1133 struct msghdr msg;
1134 struct iovec iov[1];
1135 uint8_t msgctl[255];
1136
1137 /* Prepare message data. */
1138 iov[0].iov_base = data;
1139 iov[0].iov_len = datalen;
1140
1141 memset(&msg, 0, sizeof(msg));
1142 memset(msgctl, 0, sizeof(msgctl));
1143 msg.msg_name = to;
1144 msg.msg_namelen = tolen;
1145 msg.msg_iov = iov;
1146 msg.msg_iovlen = 1;
1147
1148 /* Prepare the packet TTL information. */
1149 if (ttl > 0) {
1150 /* Use ancillary data. */
1151 msg.msg_control = msgctl;
1152 msg.msg_controllen = CMSG_LEN(sizeof(ttlval));
1153
1154 /* Configure the ancillary data. */
1155 cmsg = CMSG_FIRSTHDR(&msg);
1156 cmsg->cmsg_len = CMSG_LEN(sizeof(ttlval));
1157 if (is_ipv6) {
1158 cmsg->cmsg_level = IPPROTO_IPV6;
1159 cmsg->cmsg_type = IPV6_HOPLIMIT;
1160 } else {
0cd61f8d 1161#ifdef BFD_LINUX
2f11c53f
RZ
1162 cmsg->cmsg_level = IPPROTO_IP;
1163 cmsg->cmsg_type = IP_TTL;
1164#else
1165 /* FreeBSD does not support TTL in ancillary data. */
1166 msg.msg_control = NULL;
1167 msg.msg_controllen = 0;
1168
1169 bp_set_ttl(sd, ttl);
1170#endif /* BFD_BSD */
1171 }
1172 memcpy(CMSG_DATA(cmsg), &ttlval, sizeof(ttlval));
1173 }
1174
1175 /* Send echo back. */
1176 wlen = sendmsg(sd, &msg, 0);
1177 if (wlen <= 0) {
48da2c31 1178 if (bglobal.debug_network)
9e5fc01f 1179 zlog_debug("%s: loopback failure: (%d) %s", __func__,
1180 errno, strerror(errno));
2f11c53f
RZ
1181 return -1;
1182 } else if (wlen < (ssize_t)datalen) {
48da2c31 1183 if (bglobal.debug_network)
9e5fc01f 1184 zlog_debug("%s: partial send: %zd expected %zu",
1185 __func__, wlen, datalen);
2f11c53f
RZ
1186 return -1;
1187 }
1188
1189 return 0;
1190}
1191
e9e2c950
RZ
1192
1193/*
1194 * Sockets creation.
1195 */
1196
1197
1198/*
1199 * IPv4 sockets
1200 */
6e01e275 1201int bp_set_ttl(int sd, uint8_t value)
e9e2c950 1202{
6e01e275
RZ
1203 int ttl = value;
1204
1205 if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
9e5fc01f 1206 zlog_warn("%s: setsockopt(IP_TTL, %d): %s", __func__, value,
259b64eb 1207 strerror(errno));
e9e2c950
RZ
1208 return -1;
1209 }
1210
1211 return 0;
1212}
1213
6e01e275 1214int bp_set_tos(int sd, uint8_t value)
e9e2c950 1215{
6e01e275
RZ
1216 int tos = value;
1217
1218 if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
9e5fc01f 1219 zlog_warn("%s: setsockopt(IP_TOS, %d): %s", __func__, value,
259b64eb 1220 strerror(errno));
e9e2c950
RZ
1221 return -1;
1222 }
1223
1224 return 0;
1225}
1226
edc3f631
PG
1227static bool bp_set_reuse_addr(int sd)
1228{
1229 int one = 1;
1230
1231 if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
9e5fc01f 1232 zlog_warn("%s: setsockopt(SO_REUSEADDR, %d): %s", __func__, one,
1233 strerror(errno));
edc3f631
PG
1234 return false;
1235 }
1236 return true;
1237}
1238
1239static bool bp_set_reuse_port(int sd)
1240{
1241 int one = 1;
1242
1243 if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) == -1) {
9e5fc01f 1244 zlog_warn("%s: setsockopt(SO_REUSEPORT, %d): %s", __func__, one,
1245 strerror(errno));
edc3f631
PG
1246 return false;
1247 }
1248 return true;
1249}
1250
1251
e9e2c950
RZ
1252static void bp_set_ipopts(int sd)
1253{
6e01e275
RZ
1254 int rcvttl = BFD_RCV_TTL_VAL;
1255
edc3f631
PG
1256 if (!bp_set_reuse_addr(sd))
1257 zlog_fatal("set-reuse-addr: failed");
1258
1259 if (!bp_set_reuse_port(sd))
1260 zlog_fatal("set-reuse-port: failed");
1261
6e01e275 1262 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
259b64eb 1263 zlog_fatal("set-ipopts: TTL configuration failed");
e9e2c950
RZ
1264
1265 if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
1266 == -1)
259b64eb
RZ
1267 zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl,
1268 strerror(errno));
e9e2c950
RZ
1269
1270#ifdef BFD_LINUX
1271 int pktinfo = BFD_PKT_INFO_VAL;
6e01e275 1272
e9e2c950
RZ
1273 /* Figure out address and interface to do the peer matching. */
1274 if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
1275 == -1)
259b64eb
RZ
1276 zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s",
1277 pktinfo, strerror(errno));
e9e2c950
RZ
1278#endif /* BFD_LINUX */
1279#ifdef BFD_BSD
1280 int yes = 1;
1281
1282 /* Find out our address for peer matching. */
1283 if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
259b64eb
RZ
1284 zlog_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s",
1285 yes, strerror(errno));
e9e2c950
RZ
1286
1287 /* Find out interface where the packet came in. */
1288 if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
259b64eb
RZ
1289 zlog_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes,
1290 strerror(errno));
e9e2c950
RZ
1291#endif /* BFD_BSD */
1292}
1293
1294static void bp_bind_ip(int sd, uint16_t port)
1295{
1296 struct sockaddr_in sin;
1297
1298 memset(&sin, 0, sizeof(sin));
1299 sin.sin_family = AF_INET;
1300 sin.sin_addr.s_addr = htonl(INADDR_ANY);
1301 sin.sin_port = htons(port);
1302 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
259b64eb 1303 zlog_fatal("bind-ip: bind: %s", strerror(errno));
e9e2c950
RZ
1304}
1305
4a9feb66 1306int bp_udp_shop(const struct vrf *vrf)
e9e2c950
RZ
1307{
1308 int sd;
1309
0cf6db21 1310 frr_with_privs(&bglobal.bfdd_privs) {
4a9feb66
RZ
1311 sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1312 vrf->name);
7bcadbae 1313 }
e9e2c950 1314 if (sd == -1)
259b64eb 1315 zlog_fatal("udp-shop: socket: %s", strerror(errno));
e9e2c950
RZ
1316
1317 bp_set_ipopts(sd);
1318 bp_bind_ip(sd, BFD_DEFDESTPORT);
e9e2c950
RZ
1319 return sd;
1320}
1321
4a9feb66 1322int bp_udp_mhop(const struct vrf *vrf)
e9e2c950
RZ
1323{
1324 int sd;
1325
0cf6db21 1326 frr_with_privs(&bglobal.bfdd_privs) {
4a9feb66
RZ
1327 sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1328 vrf->name);
7bcadbae 1329 }
e9e2c950 1330 if (sd == -1)
259b64eb 1331 zlog_fatal("udp-mhop: socket: %s", strerror(errno));
e9e2c950
RZ
1332
1333 bp_set_ipopts(sd);
1334 bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
1335
1336 return sd;
1337}
1338
d245e522 1339int bp_peer_socket(const struct bfd_session *bs)
e9e2c950
RZ
1340{
1341 int sd, pcount;
1342 struct sockaddr_in sin;
1343 static int srcPort = BFD_SRCPORTINIT;
1f4b73e5
PG
1344 const char *device_to_bind = NULL;
1345
1346 if (bs->key.ifname[0])
1347 device_to_bind = (const char *)bs->key.ifname;
031705c9
PG
1348 else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
1349 || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
1350 && bs->key.vrfname[0])))
1f4b73e5 1351 device_to_bind = (const char *)bs->key.vrfname;
e9e2c950 1352
0cf6db21 1353 frr_with_privs(&bglobal.bfdd_privs) {
e52a6383 1354 sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC,
1f4b73e5 1355 bs->vrf->vrf_id, device_to_bind);
e52a6383 1356 }
03e7f088 1357 if (sd == -1) {
259b64eb
RZ
1358 zlog_err("ipv4-new: failed to create socket: %s",
1359 strerror(errno));
e9e2c950 1360 return -1;
03e7f088 1361 }
e9e2c950 1362
8a9f760e
RZ
1363 /* Set TTL to 255 for all transmitted packets */
1364 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) {
1365 close(sd);
1366 return -1;
e9e2c950
RZ
1367 }
1368
1369 /* Set TOS to CS6 for all transmitted packets */
6e01e275 1370 if (bp_set_tos(sd, BFD_TOS_VAL) != 0) {
e9e2c950
RZ
1371 close(sd);
1372 return -1;
1373 }
1374
e9e2c950 1375 /* Find an available source port in the proper range */
79b4a6fc 1376 memset(&sin, 0, sizeof(sin));
e9e2c950
RZ
1377 sin.sin_family = AF_INET;
1378#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1379 sin.sin_len = sizeof(sin);
1380#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
79b4a6fc 1381 memcpy(&sin.sin_addr, &bs->key.local, sizeof(sin.sin_addr));
e9e2c950
RZ
1382
1383 pcount = 0;
1384 do {
1385 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1386 /* Searched all ports, none available */
259b64eb
RZ
1387 zlog_err("ipv4-new: failed to bind port: %s",
1388 strerror(errno));
e9e2c950
RZ
1389 close(sd);
1390 return -1;
1391 }
1392 if (srcPort >= BFD_SRCPORTMAX)
1393 srcPort = BFD_SRCPORTINIT;
1394 sin.sin_port = htons(srcPort++);
1395 } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
1396
1397 return sd;
1398}
1399
1400
1401/*
1402 * IPv6 sockets
1403 */
1404
d245e522 1405int bp_peer_socketv6(const struct bfd_session *bs)
e9e2c950 1406{
80edb675 1407 int sd, pcount;
e9e2c950
RZ
1408 struct sockaddr_in6 sin6;
1409 static int srcPort = BFD_SRCPORTINIT;
1f4b73e5
PG
1410 const char *device_to_bind = NULL;
1411
1412 if (bs->key.ifname[0])
1413 device_to_bind = (const char *)bs->key.ifname;
031705c9
PG
1414 else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
1415 || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
1416 && bs->key.vrfname[0])))
1f4b73e5 1417 device_to_bind = (const char *)bs->key.vrfname;
e9e2c950 1418
0cf6db21 1419 frr_with_privs(&bglobal.bfdd_privs) {
e52a6383 1420 sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC,
1f4b73e5 1421 bs->vrf->vrf_id, device_to_bind);
e52a6383 1422 }
03e7f088 1423 if (sd == -1) {
259b64eb
RZ
1424 zlog_err("ipv6-new: failed to create socket: %s",
1425 strerror(errno));
e9e2c950 1426 return -1;
03e7f088 1427 }
e9e2c950 1428
8a9f760e
RZ
1429 /* Set TTL to 255 for all transmitted packets */
1430 if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) {
1431 close(sd);
1432 return -1;
e9e2c950
RZ
1433 }
1434
1435 /* Set TOS to CS6 for all transmitted packets */
6e01e275 1436 if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) {
e9e2c950
RZ
1437 close(sd);
1438 return -1;
1439 }
1440
1441 /* Find an available source port in the proper range */
79b4a6fc 1442 memset(&sin6, 0, sizeof(sin6));
e9e2c950
RZ
1443 sin6.sin6_family = AF_INET6;
1444#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1445 sin6.sin6_len = sizeof(sin6);
1446#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
79b4a6fc 1447 memcpy(&sin6.sin6_addr, &bs->key.local, sizeof(sin6.sin6_addr));
1db60b57 1448 if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
79b4a6fc 1449 sin6.sin6_scope_id = bs->ifp->ifindex;
e9e2c950 1450
e9e2c950
RZ
1451 pcount = 0;
1452 do {
1453 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1454 /* Searched all ports, none available */
259b64eb
RZ
1455 zlog_err("ipv6-new: failed to bind port: %s",
1456 strerror(errno));
e9e2c950
RZ
1457 close(sd);
1458 return -1;
1459 }
1460 if (srcPort >= BFD_SRCPORTMAX)
1461 srcPort = BFD_SRCPORTINIT;
1462 sin6.sin6_port = htons(srcPort++);
1463 } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1464
1465 return sd;
1466}
1467
6e01e275 1468int bp_set_ttlv6(int sd, uint8_t value)
e9e2c950 1469{
6e01e275
RZ
1470 int ttl = value;
1471
1472 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))
e9e2c950 1473 == -1) {
259b64eb
RZ
1474 zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1475 value, strerror(errno));
e9e2c950
RZ
1476 return -1;
1477 }
1478
1479 return 0;
1480}
1481
6e01e275 1482int bp_set_tosv6(int sd, uint8_t value)
e9e2c950 1483{
6e01e275
RZ
1484 int tos = value;
1485
1486 if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))
e9e2c950 1487 == -1) {
259b64eb
RZ
1488 zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value,
1489 strerror(errno));
e9e2c950
RZ
1490 return -1;
1491 }
1492
1493 return 0;
1494}
1495
1496static void bp_set_ipv6opts(int sd)
1497{
6e01e275
RZ
1498 int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1499 int ipv6_only = BFD_IPV6_ONLY_VAL;
e9e2c950 1500
edc3f631
PG
1501 if (!bp_set_reuse_addr(sd))
1502 zlog_fatal("set-reuse-addr: failed");
1503
1504 if (!bp_set_reuse_port(sd))
1505 zlog_fatal("set-reuse-port: failed");
1506
6e01e275 1507 if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
259b64eb
RZ
1508 zlog_fatal(
1509 "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1510 BFD_TTL_VAL, strerror(errno));
e9e2c950 1511
6e01e275 1512 if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1)
259b64eb
RZ
1513 zlog_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1514 BFD_RCV_TTL_VAL, strerror(errno));
e9e2c950
RZ
1515
1516 if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
259b64eb
RZ
1517 zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1518 ipv6_pktinfo, strerror(errno));
e9e2c950
RZ
1519
1520 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1521 sizeof(ipv6_only))
1522 == -1)
259b64eb
RZ
1523 zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1524 ipv6_only, strerror(errno));
e9e2c950
RZ
1525}
1526
1527static void bp_bind_ipv6(int sd, uint16_t port)
1528{
1529 struct sockaddr_in6 sin6;
1530
1531 memset(&sin6, 0, sizeof(sin6));
1532 sin6.sin6_family = AF_INET6;
1533 sin6.sin6_addr = in6addr_any;
1534 sin6.sin6_port = htons(port);
1535#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1536 sin6.sin6_len = sizeof(sin6);
1537#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1538 if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
259b64eb 1539 zlog_fatal("bind-ipv6: bind: %s", strerror(errno));
e9e2c950
RZ
1540}
1541
4a9feb66 1542int bp_udp6_shop(const struct vrf *vrf)
e9e2c950
RZ
1543{
1544 int sd;
1545
0cf6db21 1546 frr_with_privs(&bglobal.bfdd_privs) {
4a9feb66
RZ
1547 sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1548 vrf->name);
7bcadbae 1549 }
f1446f98
DS
1550 if (sd == -1) {
1551 if (errno != EAFNOSUPPORT)
1552 zlog_fatal("udp6-shop: socket: %s", strerror(errno));
1553 else
1554 zlog_warn("udp6-shop: V6 is not supported, continuing");
1555
1556 return -1;
1557 }
e9e2c950
RZ
1558
1559 bp_set_ipv6opts(sd);
1560 bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1561
1562 return sd;
1563}
1564
4a9feb66 1565int bp_udp6_mhop(const struct vrf *vrf)
e9e2c950
RZ
1566{
1567 int sd;
1568
0cf6db21 1569 frr_with_privs(&bglobal.bfdd_privs) {
4a9feb66
RZ
1570 sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1571 vrf->name);
7bcadbae 1572 }
f1446f98
DS
1573 if (sd == -1) {
1574 if (errno != EAFNOSUPPORT)
1575 zlog_fatal("udp6-mhop: socket: %s", strerror(errno));
1576 else
1577 zlog_warn("udp6-mhop: V6 is not supported, continuing");
1578
1579 return -1;
1580 }
e9e2c950
RZ
1581
1582 bp_set_ipv6opts(sd);
1583 bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1584
1585 return sd;
1586}
2f11c53f 1587
57485b0b 1588#ifdef BFD_LINUX
1589/* tcpdump -dd udp dst port 3785 */
1590struct sock_filter my_filterudp[] = {
1591 {0x28, 0, 0, 0x0000000c}, {0x15, 0, 8, 0x00000800},
1592 {0x30, 0, 0, 0x00000017}, {0x15, 0, 6, 0x00000011},
1593 {0x28, 0, 0, 0x00000014}, {0x45, 4, 0, 0x00001fff},
1594 {0xb1, 0, 0, 0x0000000e}, {0x48, 0, 0, 0x00000010},
1595 {0x15, 0, 1, 0x00000ec9}, {0x6, 0, 0, 0x00040000},
1596 {0x6, 0, 0, 0x00000000},
1597};
1598
1599#define MY_FILTER_LENGTH 11
1600
1601int bp_echo_socket(const struct vrf *vrf)
1602{
1603 int s;
1604
1605 frr_with_privs (&bglobal.bfdd_privs) {
1606 s = vrf_socket(AF_PACKET, SOCK_RAW, ETH_P_IP, vrf->vrf_id,
1607 vrf->name);
1608 }
1609
1610 if (s == -1)
1611 zlog_fatal("echo-socket: socket: %s", strerror(errno));
1612
1613 struct sock_fprog pf;
618a06fe 1614 struct sockaddr_ll sll = {0};
57485b0b 1615
1616 /* adjust filter for socket to only receive ECHO packets */
1617 pf.filter = my_filterudp;
1618 pf.len = MY_FILTER_LENGTH;
1619 if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) ==
1620 -1) {
1621 zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__,
1622 strerror(errno));
500fe387 1623 close(s);
57485b0b 1624 return -1;
1625 }
1626
5609e9d7 1627 memset(&sll, 0, sizeof(sll));
57485b0b 1628 sll.sll_family = AF_PACKET;
1629 sll.sll_protocol = htons(ETH_P_IP);
1630 sll.sll_ifindex = 0;
1631 if (bind(s, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
1632 zlog_warn("Failed to bind echo socket: %s",
1633 safe_strerror(errno));
500fe387 1634 close(s);
57485b0b 1635 return -1;
1636 }
1637
1638 return s;
1639}
1640#else
4a9feb66 1641int bp_echo_socket(const struct vrf *vrf)
2f11c53f
RZ
1642{
1643 int s;
1644
0cf6db21 1645 frr_with_privs(&bglobal.bfdd_privs) {
4a9feb66 1646 s = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name);
7bcadbae 1647 }
2f11c53f 1648 if (s == -1)
259b64eb 1649 zlog_fatal("echo-socket: socket: %s", strerror(errno));
2f11c53f
RZ
1650
1651 bp_set_ipopts(s);
1652 bp_bind_ip(s, BFD_DEF_ECHO_PORT);
1653
1654 return s;
1655}
57485b0b 1656#endif
2f11c53f 1657
4a9feb66 1658int bp_echov6_socket(const struct vrf *vrf)
2f11c53f
RZ
1659{
1660 int s;
1661
0cf6db21 1662 frr_with_privs(&bglobal.bfdd_privs) {
4a9feb66 1663 s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name);
7bcadbae 1664 }
f1446f98
DS
1665 if (s == -1) {
1666 if (errno != EAFNOSUPPORT)
1667 zlog_fatal("echov6-socket: socket: %s",
1668 strerror(errno));
1669 else
1670 zlog_warn("echov6-socket: V6 is not supported, continuing");
1671
1672 return -1;
1673 }
2f11c53f
RZ
1674
1675 bp_set_ipv6opts(s);
1676 bp_bind_ipv6(s, BFD_DEF_ECHO_PORT);
1677
1678 return s;
1679}
57485b0b 1680
1681#ifdef BFD_LINUX
1682/* get peer's mac address to be used with Echo packets when they are looped in
1683 * peers forwarding plane
1684 */
1685void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
1686 struct sockaddr_any *peer, struct interface *ifp)
1687{
1688 struct arpreq arpreq_;
1689
1690 if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET))
1691 return;
97739c28
DS
1692 if (ifp->flags & IFF_NOARP)
1693 return;
57485b0b 1694
1695 if (peer->sa_sin.sin_family == AF_INET) {
1696 /* IPV4 */
1697 struct sockaddr_in *addr =
1698 (struct sockaddr_in *)&arpreq_.arp_pa;
1699
1700 memset(&arpreq_, 0, sizeof(struct arpreq));
1701 addr->sin_family = AF_INET;
1702 memcpy(&addr->sin_addr.s_addr, &peer->sa_sin.sin_addr,
1703 sizeof(addr->sin_addr));
1704 strlcpy(arpreq_.arp_dev, ifp->name, sizeof(arpreq_.arp_dev));
1705
1706 if (ioctl(sd, SIOCGARP, &arpreq_) < 0) {
97739c28
DS
1707 zlog_warn(
1708 "BFD: getting peer's mac on %s failed error %s",
1709 ifp->name, strerror(errno));
57485b0b 1710 UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
1711 memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr));
1712
1713 } else {
1714 memcpy(bfd->peer_hw_addr, arpreq_.arp_ha.sa_data,
1715 sizeof(bfd->peer_hw_addr));
1716 SET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
1717 }
1718 }
1719}
1720#endif