back to topotato report
topotato coverage report
Current view: top level - bfdd - bfd_packet.c (source / functions) Hit Total Coverage
Test: test_pim_bfd.py::PIMBFDTest Lines: 312 813 38.4 %
Date: 2023-02-24 18:39:40 Functions: 23 34 67.6 %

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

Generated by: LCOV version v1.16-topotato