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