]> git.proxmox.com Git - mirror_frr.git/blobdiff - bfdd/bfd_packet.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / bfdd / bfd_packet.c
index 6b0afef65fbe5bee43d6fd65a8ec3d6f4da96e21..7be235326c5e49202ed94c563e2d4bae7855d63b 100644 (file)
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*********************************************************************
  * Copyright 2017 Cumulus Networks, Inc.  All rights reserved.
  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
  * bfd_packet.c: implements the BFD protocol packet handling.
  *
  * Authors
@@ -55,8 +42,8 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
                      struct sockaddr_any *peer);
 int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
                struct sockaddr *to, socklen_t tolen);
-int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd,
-                  uint8_t *ttl, uint32_t *my_discr);
+int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
+                  uint32_t *my_discr, uint64_t *my_rtt);
 #ifdef BFD_LINUX
 ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen,
                         uint8_t *ttl, ifindex_t *ifindex,
@@ -207,6 +194,7 @@ void ptm_bfd_echo_fp_snd(struct bfd_session *bfd)
        struct iphdr *iph;
        struct bfd_echo_pkt *beph;
        static char sendbuff[100];
+       struct timeval time_sent;
 
        if (!bvrf)
                return;
@@ -219,8 +207,8 @@ void ptm_bfd_echo_fp_snd(struct bfd_session *bfd)
 
        /* add eth hdr */
        eth = (struct ethhdr *)(sendbuff);
-       memcpy(eth->h_source, bfd->ifp->hw_addr, sizeof(bfd->ifp->hw_addr));
-       memcpy(eth->h_dest, bfd->peer_hw_addr, sizeof(bfd->peer_hw_addr));
+       memcpy(eth->h_source, bfd->ifp->hw_addr, sizeof(eth->h_source));
+       memcpy(eth->h_dest, bfd->peer_hw_addr, sizeof(eth->h_dest));
 
        total_len += sizeof(struct ethhdr);
 
@@ -259,6 +247,11 @@ void ptm_bfd_echo_fp_snd(struct bfd_session *bfd)
        beph->len = BFD_ECHO_PKT_LEN;
        beph->my_discr = htonl(bfd->discrs.my_discr);
 
+       /* RTT calculation: add starting time in packet */
+       monotime(&time_sent);
+       beph->time_sent_sec = htobe64(time_sent.tv_sec);
+       beph->time_sent_usec = htobe64(time_sent.tv_usec);
+
        total_len += sizeof(struct bfd_echo_pkt);
        uh->len =
                htons(total_len - sizeof(struct iphdr) - sizeof(struct ethhdr));
@@ -338,10 +331,11 @@ static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s)
 {
        struct bfd_session *bfd;
        uint32_t my_discr = 0;
+       uint64_t my_rtt = 0;
        uint8_t ttl = 0;
 
        /* Receive and parse echo packet. */
-       if (bp_bfd_echo_in(bvrf, s, &ttl, &my_discr) == -1)
+       if (bp_bfd_echo_in(bvrf, s, &ttl, &my_discr, &my_rtt) == -1)
                return 0;
 
        /* Your discriminator not zero - use it to find session */
@@ -360,6 +354,16 @@ static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s)
                return -1;
        }
 
+       /* RTT Calculation: add current RTT to samples */
+       if (my_rtt != 0) {
+               bfd->rtt[bfd->rtt_index] = my_rtt;
+               bfd->rtt_index++;
+               if (bfd->rtt_index >= BFD_RTT_SAMPLE)
+                       bfd->rtt_index = 0;
+               if (bfd->rtt_valid < BFD_RTT_SAMPLE)
+                       bfd->rtt_valid++;
+       }
+
        bfd->stats.rx_echo_pkt++;
 
        /* Compute detect time */
@@ -561,8 +565,8 @@ ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
                        memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
                        if (ttlval > 255) {
                                if (bglobal.debug_network)
-                                       zlog_debug("ipv4-recv: invalid TTL: %u",
-                                                  ttlval);
+                                       zlog_debug("%s: invalid TTL: %u",
+                                                  __func__, ttlval);
                                return -1;
                        }
                        *ttl = ttlval;
@@ -669,8 +673,8 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
                        memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
                        if (ttlval > 255) {
                                if (bglobal.debug_network)
-                                       zlog_debug("ipv6-recv: invalid TTL: %u",
-                                                  ttlval);
+                                       zlog_debug("%s: invalid TTL: %u",
+                                                  __func__, ttlval);
                                return -1;
                        }
 
@@ -729,6 +733,7 @@ static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd)
        }
 }
 
+PRINTFRR(6, 7)
 static void cp_debug(bool mhop, struct sockaddr_any *peer,
                     struct sockaddr_any *local, ifindex_t ifindex,
                     vrf_id_t vrfid, const char *fmt, ...)
@@ -827,7 +832,7 @@ void bfd_recv_cb(struct thread *t)
        /* Implement RFC 5880 6.8.6 */
        if (mlen < BFD_PKT_LEN) {
                cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
-                        "too small (%ld bytes)", mlen);
+                        "too small (%zd bytes)", mlen);
                return;
        }
 
@@ -876,6 +881,14 @@ void bfd_recv_cb(struct thread *t)
                         "no session found");
                return;
        }
+       /*
+        * We may have a situation where received packet is on wrong vrf
+        */
+       if (bfd && bfd->vrf && bfd->vrf != bvrf->vrf) {
+               cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
+                        "wrong vrfid.");
+               return;
+       }
 
        /* Ensure that existing good sessions are not overridden. */
        if (!cp->discrs.remote_discr && bfd->ses_state != PTM_BFD_DOWN &&
@@ -995,8 +1008,8 @@ void bfd_recv_cb(struct thread *t)
  *
  * Returns -1 on error or loopback or 0 on success.
  */
-int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd,
-                  uint8_t *ttl, uint32_t *my_discr)
+int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
+                  uint32_t *my_discr, uint64_t *my_rtt)
 {
        struct bfd_echo_pkt *bep;
        ssize_t rlen;
@@ -1054,6 +1067,17 @@ int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd,
                return -1;
        }
 
+#ifdef BFD_LINUX
+       /* RTT Calculation: determine RTT time of IPv4 echo pkt */
+       if (sd == bvrf->bg_echo) {
+               struct timeval time_sent = {0, 0};
+
+               time_sent.tv_sec = be64toh(bep->time_sent_sec);
+               time_sent.tv_usec = be64toh(bep->time_sent_usec);
+               *my_rtt = monotime_since(&time_sent, NULL);
+       }
+#endif
+
        return 0;
 }
 
@@ -1066,11 +1090,10 @@ int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
                   struct bfd_session *bfd)
 {
        ssize_t wlen;
-       struct msghdr msg;
+       struct msghdr msg = {0};
        struct iovec iov[1];
        uint8_t msgctl[255];
-       struct sockaddr_ll sadr_ll;
-
+       struct sockaddr_ll sadr_ll = {0};
 
        sadr_ll.sll_ifindex = bfd->ifp->ifindex;
        sadr_ll.sll_halen = ETH_ALEN;
@@ -1081,7 +1104,6 @@ int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
        iov[0].iov_base = data;
        iov[0].iov_len = datalen;
 
-       memset(&msg, 0, sizeof(msg));
        memset(msgctl, 0, sizeof(msgctl));
        msg.msg_name = &sadr_ll;
        msg.msg_namelen = sizeof(sadr_ll);
@@ -1093,13 +1115,13 @@ int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
 
        if (wlen <= 0) {
                if (bglobal.debug_network)
-                       zlog_debug("udp-send: loopback failure: (%d) %s", errno,
-                                  strerror(errno));
+                       zlog_debug("%s: loopback failure: (%d) %s", __func__,
+                                  errno, strerror(errno));
                return -1;
        } else if (wlen < (ssize_t)datalen) {
                if (bglobal.debug_network)
-                       zlog_debug("udp-send: partial send: %zd expected %zu",
-                                  wlen, datalen);
+                       zlog_debug("%s: partial send: %zd expected %zu",
+                                  __func__, wlen, datalen);
                return -1;
        }
 
@@ -1160,13 +1182,13 @@ int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
        wlen = sendmsg(sd, &msg, 0);
        if (wlen <= 0) {
                if (bglobal.debug_network)
-                       zlog_debug("udp-send: loopback failure: (%d) %s", errno,
-                                  strerror(errno));
+                       zlog_debug("%s: loopback failure: (%d) %s", __func__,
+                                  errno, strerror(errno));
                return -1;
        } else if (wlen < (ssize_t)datalen) {
                if (bglobal.debug_network)
-                       zlog_debug("udp-send: partial send: %zd expected %zu",
-                                  wlen, datalen);
+                       zlog_debug("%s: partial send: %zd expected %zu",
+                                  __func__, wlen, datalen);
                return -1;
        }
 
@@ -1187,7 +1209,7 @@ int bp_set_ttl(int sd, uint8_t value)
        int ttl = value;
 
        if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
-               zlog_warn("set-ttl: setsockopt(IP_TTL, %d): %s", value,
+               zlog_warn("%s: setsockopt(IP_TTL, %d): %s", __func__, value,
                          strerror(errno));
                return -1;
        }
@@ -1200,7 +1222,7 @@ int bp_set_tos(int sd, uint8_t value)
        int tos = value;
 
        if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
-               zlog_warn("set-tos: setsockopt(IP_TOS, %d): %s", value,
+               zlog_warn("%s: setsockopt(IP_TOS, %d): %s", __func__, value,
                          strerror(errno));
                return -1;
        }
@@ -1208,10 +1230,41 @@ int bp_set_tos(int sd, uint8_t value)
        return 0;
 }
 
+static bool bp_set_reuse_addr(int sd)
+{
+       int one = 1;
+
+       if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
+               zlog_warn("%s: setsockopt(SO_REUSEADDR, %d): %s", __func__, one,
+                         strerror(errno));
+               return false;
+       }
+       return true;
+}
+
+static bool bp_set_reuse_port(int sd)
+{
+       int one = 1;
+
+       if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) == -1) {
+               zlog_warn("%s: setsockopt(SO_REUSEPORT, %d): %s", __func__, one,
+                         strerror(errno));
+               return false;
+       }
+       return true;
+}
+
+
 static void bp_set_ipopts(int sd)
 {
        int rcvttl = BFD_RCV_TTL_VAL;
 
+       if (!bp_set_reuse_addr(sd))
+               zlog_fatal("set-reuse-addr: failed");
+
+       if (!bp_set_reuse_port(sd))
+               zlog_fatal("set-reuse-port: failed");
+
        if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
                zlog_fatal("set-ipopts: TTL configuration failed");
 
@@ -1332,8 +1385,6 @@ int bp_peer_socket(const struct bfd_session *bs)
        sin.sin_len = sizeof(sin);
 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
        memcpy(&sin.sin_addr, &bs->key.local, sizeof(sin.sin_addr));
-       if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
-               sin.sin_addr.s_addr = INADDR_ANY;
 
        pcount = 0;
        do {
@@ -1453,6 +1504,12 @@ static void bp_set_ipv6opts(int sd)
        int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
        int ipv6_only = BFD_IPV6_ONLY_VAL;
 
+       if (!bp_set_reuse_addr(sd))
+               zlog_fatal("set-reuse-addr: failed");
+
+       if (!bp_set_reuse_port(sd))
+               zlog_fatal("set-reuse-port: failed");
+
        if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
                zlog_fatal(
                        "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
@@ -1560,7 +1617,7 @@ int bp_echo_socket(const struct vrf *vrf)
                zlog_fatal("echo-socket: socket: %s", strerror(errno));
 
        struct sock_fprog pf;
-       struct sockaddr_ll sll;
+       struct sockaddr_ll sll = {0};
 
        /* adjust filter for socket to only receive ECHO packets */
        pf.filter = my_filterudp;
@@ -1569,16 +1626,18 @@ int bp_echo_socket(const struct vrf *vrf)
            -1) {
                zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__,
                          strerror(errno));
+               close(s);
                return -1;
        }
 
-
+       memset(&sll, 0, sizeof(sll));
        sll.sll_family = AF_PACKET;
        sll.sll_protocol = htons(ETH_P_IP);
        sll.sll_ifindex = 0;
        if (bind(s, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
                zlog_warn("Failed to bind echo socket: %s",
                          safe_strerror(errno));
+               close(s);
                return -1;
        }
 
@@ -1636,6 +1695,8 @@ void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
 
        if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET))
                return;
+       if (ifp->flags & IFF_NOARP)
+               return;
 
        if (peer->sa_sin.sin_family == AF_INET) {
                /* IPV4 */
@@ -1649,8 +1710,9 @@ void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
                strlcpy(arpreq_.arp_dev, ifp->name, sizeof(arpreq_.arp_dev));
 
                if (ioctl(sd, SIOCGARP, &arpreq_) < 0) {
-                       zlog_warn("BFD: getting peer's mac failed error %s",
-                                 strerror(errno));
+                       zlog_warn(
+                               "BFD: getting peer's mac on %s failed error %s",
+                               ifp->name, strerror(errno));
                        UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
                        memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr));