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