]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bfdd: implement session interface observer
authorRafael Zalamena <rzalamena@opensourcerouting.org>
Sat, 2 Feb 2019 11:57:08 +0000 (09:57 -0200)
committerRafael Zalamena <rzalamena@opensourcerouting.org>
Thu, 14 Feb 2019 16:18:23 +0000 (14:18 -0200)
Allow `bfdd` to configure inexisting interfaces / VRF and only activate
them once the interface/VRF start existing. This implementation doesn't
handle dynamic VRFs yet.

Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
bfdd/bfd.c
bfdd/bfd.h
bfdd/bfd_packet.c
bfdd/bfdd.c
bfdd/bfdd_vty.c
bfdd/config.c
bfdd/event.c
bfdd/ptm_adapter.c

index aa09d0be4ac26a8be9fb5cb1fbe56cdd2a9a0e39..f2f63480f7c6e70643e3955c206541d1d8f54160 100644 (file)
@@ -36,10 +36,12 @@ DEFINE_QOBJ_TYPE(bfd_session);
 /*
  * Prototypes
  */
+struct bfd_session *bs_peer_waiting_find(struct bfd_peer_cfg *);
+
 static uint32_t ptm_bfd_gen_ID(void);
 static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd);
 static void bfd_session_free(struct bfd_session *bs);
-static struct bfd_session *bfd_session_new(int sd);
+static struct bfd_session *bfd_session_new(void);
 static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
                                         uint32_t ldisc);
 static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
@@ -49,6 +51,54 @@ static const char *get_diag_str(int diag);
 /*
  * Functions
  */
+struct bfd_session *bs_peer_waiting_find(struct bfd_peer_cfg *bpc)
+{
+       struct bfd_session_observer *bso;
+       struct bfd_session *bs = NULL;
+       bool is_shop, is_ipv4;
+
+       TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
+               bs = bso->bso_bs;
+
+               is_shop = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
+               is_ipv4 = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6);
+               /* Quick checks first. */
+               if (is_shop != (!bpc->bpc_mhop))
+                       continue;
+               if (is_ipv4 != bpc->bpc_ipv4)
+                       continue;
+
+               /*
+                * Slow lookup without hash because we don't have all
+                * information yet.
+                */
+               if (is_shop) {
+                       if (strcmp(bs->ifname, bpc->bpc_localif))
+                               continue;
+                       if (memcmp(&bs->shop.peer, &bpc->bpc_peer,
+                                  sizeof(bs->shop.peer)))
+                               continue;
+
+                       break;
+               }
+
+               if (strcmp(bs->vrfname, bpc->bpc_vrfname))
+                       continue;
+               if (memcmp(&bs->mhop.peer, &bpc->bpc_peer,
+                          sizeof(bs->mhop.peer)))
+                       continue;
+               if (memcmp(&bs->mhop.local, &bpc->bpc_local,
+                          sizeof(bs->mhop.local)))
+                       continue;
+
+               break;
+       }
+       if (bso == NULL)
+               bs = NULL;
+
+       return bs;
+}
+
 struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
 {
        struct bfd_session *bs;
@@ -95,7 +145,151 @@ struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
                bs = bfd_shop_lookup(shop);
        }
 
-       return bs;
+       if (bs != NULL)
+               return bs;
+
+       /* Search for entries that are incomplete. */
+       return bs_peer_waiting_find(bpc);
+}
+
+/*
+ * Starts a disabled BFD session.
+ *
+ * A session is disabled when the specified interface/VRF doesn't exist
+ * yet. It might happen on FRR boot or with virtual interfaces.
+ */
+int bfd_session_enable(struct bfd_session *bs)
+{
+       struct sockaddr_in6 *sin6;
+       struct interface *ifp = NULL;
+       struct vrf *vrf = NULL;
+       int psock;
+
+       /*
+        * If the interface or VRF doesn't exist, then we must register
+        * the session but delay its start.
+        */
+       if (bs->ifname[0] != 0) {
+               ifp = if_lookup_by_name_all_vrf(bs->ifname);
+               if (ifp == NULL) {
+                       log_error(
+                               "session-enable: specified interface doesn't exists.");
+                       return 0;
+               }
+
+               vrf = vrf_lookup_by_id(ifp->vrf_id);
+               if (vrf == NULL) {
+                       log_error("session-enable: specified VRF doesn't exists.");
+                       return 0;
+               }
+       }
+
+       if (bs->vrfname[0] != 0) {
+               vrf = vrf_lookup_by_name(bs->vrfname);
+               if (vrf == NULL) {
+                       log_error("session-enable: specified VRF doesn't exists.");
+                       return 0;
+               }
+       }
+
+       /* Assign interface/VRF pointers. */
+       bs->vrf = vrf;
+       if (bs->vrf == NULL)
+               bs->vrf = vrf_lookup_by_id(VRF_DEFAULT);
+
+       if (bs->ifname[0] != 0 &&
+           BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
+               bs->ifp = ifp;
+
+       /* Set the IPv6 scope id for link-local addresses. */
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
+               sin6 = &bs->mhop.peer.sa_sin6;
+               if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+                       sin6->sin6_scope_id = bs->ifp != NULL
+                                                     ? bs->ifp->ifindex
+                                                     : IFINDEX_INTERNAL;
+
+               sin6 = &bs->mhop.local.sa_sin6;
+               if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+                       sin6->sin6_scope_id = bs->ifp != NULL
+                                                     ? bs->ifp->ifindex
+                                                     : IFINDEX_INTERNAL;
+
+               bs->local_ip.sa_sin6 = *sin6;
+               bs->local_address.sa_sin6 = *sin6;
+       }
+
+       /*
+        * Get socket for transmitting control packets.  Note that if we
+        * could use the destination port (3784) for the source
+        * port we wouldn't need a socket per session.
+        */
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) {
+               psock = bp_peer_socket(bs);
+               if (psock == -1)
+                       return -1;
+       } else {
+               psock = bp_peer_socketv6(bs);
+               if (psock == -1)
+                       return -1;
+       }
+
+       /*
+        * We've got a valid socket, lets start the timers and the
+        * protocol.
+        */
+       bs->sock = psock;
+       bfd_recvtimer_update(bs);
+       ptm_bfd_start_xmt_timer(bs, false);
+
+       /* Registrate session into data structures. */
+       bs->discrs.my_discr = ptm_bfd_gen_ID();
+       bfd_id_insert(bs);
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
+               if (vrf != NULL)
+                       bs->mhop.vrfid = vrf->vrf_id;
+               else
+                       bs->mhop.vrfid = VRF_DEFAULT;
+
+               bfd_mhop_insert(bs);
+       } else {
+               if (ifp != NULL)
+                       bs->shop.ifindex = ifp->ifindex;
+               else
+                       bs->shop.ifindex = IFINDEX_INTERNAL;
+
+               bfd_shop_insert(bs);
+       }
+
+       return 0;
+}
+
+/*
+ * Disabled a running BFD session.
+ *
+ * A session is disabled when the specified interface/VRF gets removed
+ * (e.g. virtual interfaces).
+ */
+void bfd_session_disable(struct bfd_session *bs)
+{
+       /* Free up socket resources. */
+       if (bs->sock != -1) {
+               close(bs->sock);
+               bs->sock = -1;
+       }
+
+       /* Disable all timers. */
+       bfd_recvtimer_delete(bs);
+       bfd_echo_recvtimer_delete(bs);
+       bfd_xmttimer_delete(bs);
+       bfd_echo_xmttimer_delete(bs);
+
+       /* Unregister session from hashes to avoid unwanted activation. */
+       bfd_id_delete(bs->discrs.my_discr);
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
+               bfd_mhop_delete(bs->mhop);
+       else
+               bfd_shop_delete(bs->shop);
 }
 
 static uint32_t ptm_bfd_gen_ID(void)
@@ -347,7 +541,7 @@ int bfd_echo_recvtimer_cb(struct thread *t)
        return 0;
 }
 
-static struct bfd_session *bfd_session_new(int sd)
+static struct bfd_session *bfd_session_new(void)
 {
        struct bfd_session *bs;
 
@@ -362,6 +556,7 @@ static struct bfd_session *bfd_session_new(int sd)
        bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO;
        bs->detect_mult = BFD_DEFDETECTMULT;
        bs->mh_ttl = BFD_DEF_MHOP_TTL;
+       bs->ses_state = PTM_BFD_DOWN;
 
        /* Initiate connection with slow timers. */
        bs_set_slow_timers(bs);
@@ -370,7 +565,7 @@ static struct bfd_session *bfd_session_new(int sd)
        bs->remote_timers = bs->cur_timers;
        bs->remote_detect_mult = BFD_DEFDETECTMULT;
 
-       bs->sock = sd;
+       bs->sock = -1;
        monotime(&bs->uptime);
        bs->downtime = bs->uptime;
 
@@ -464,7 +659,9 @@ skip_echo:
                bs->ses_state = PTM_BFD_ADM_DOWN;
                control_notify(bs);
 
-               ptm_bfd_snd(bs, 0);
+               /* Don't try to send packets with a disabled session. */
+               if (bs->sock != -1)
+                       ptm_bfd_snd(bs, 0);
        } else {
                /* Check if already working. */
                if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
@@ -497,19 +694,19 @@ static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
 
 static void bfd_session_free(struct bfd_session *bs)
 {
-       if (bs->sock != -1)
-               close(bs->sock);
+       struct bfd_session_observer *bso;
 
-       bfd_recvtimer_delete(bs);
-       bfd_echo_recvtimer_delete(bs);
-       bfd_xmttimer_delete(bs);
-       bfd_echo_xmttimer_delete(bs);
+       bfd_session_disable(bs);
 
-       bfd_id_delete(bs->discrs.my_discr);
-       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
-               bfd_mhop_delete(bs->mhop);
-       else
-               bfd_shop_delete(bs->shop);
+       /* Remove observer if any. */
+       TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
+               if (bso->bso_bs != bs)
+                       continue;
+
+               break;
+       }
+       if (bso != NULL)
+               bs_observer_del(bso);
 
        pl_free(bs->pl);
 
@@ -520,9 +717,6 @@ static void bfd_session_free(struct bfd_session *bs)
 struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
 {
        struct bfd_session *bfd, *l_bfd;
-       struct interface *ifp = NULL;
-       struct vrf *vrf = NULL;
-       int psock;
 
        /* check to see if this needs a new session */
        l_bfd = bs_peer_find(bpc);
@@ -534,115 +728,50 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
                        return NULL;
        }
 
-       /*
-        * No session found, we have to allocate a new one.
-        *
-        * First a few critical checks:
-        *
-        *   * Check that the specified interface exists.
-        *   * Check that the specified VRF exists.
-        *   * Attempt to create the UDP socket (might fail if we exceed
-        *     our limits).
-        */
-       if (bpc->bpc_has_localif) {
-               ifp = if_lookup_by_name_all_vrf(bpc->bpc_localif);
-               if (ifp == NULL) {
-                       log_error(
-                               "session-new: specified interface doesn't exists.");
-                       return NULL;
-               }
-
-               vrf = vrf_lookup_by_id(ifp->vrf_id);
-               if (vrf == NULL) {
-                       log_error("session-new: specified VRF doesn't exists.");
-                       return NULL;
-               }
-       }
-
-       if (bpc->bpc_has_vrfname) {
-               vrf = vrf_lookup_by_name(bpc->bpc_vrfname);
-               if (vrf == NULL) {
-                       log_error("session-new: specified VRF doesn't exists.");
-                       return NULL;
-               }
-       }
-
-       /*
-        * Get socket for transmitting control packets.  Note that if we
-        * could use the destination port (3784) for the source
-        * port we wouldn't need a socket per session.
-        */
-       if (bpc->bpc_ipv4) {
-               psock = bp_peer_socket(bpc);
-               if (psock == -1)
-                       return NULL;
-       } else {
-               psock = bp_peer_socketv6(bpc);
-               if (psock == -1)
-                       return NULL;
-       }
-
-       /* Get memory */
-       bfd = bfd_session_new(psock);
+       /* Get BFD session storage with its defaults. */
+       bfd = bfd_session_new();
        if (bfd == NULL) {
                log_error("session-new: allocation failed");
                return NULL;
        }
 
-       /* Assign VRF pointer. */
-       bfd->vrf = vrf;
-       if (bfd->vrf == NULL)
-               bfd->vrf = vrf_lookup_by_id(VRF_DEFAULT);
-
-       if (bpc->bpc_has_localif && !bpc->bpc_mhop)
-               bfd->ifp = ifp;
+       /*
+        * Store interface/VRF name in case we need to delay session
+        * start. See `bfd_session_enable` for more information.
+        */
+       if (bpc->bpc_has_localif)
+               strlcpy(bfd->ifname, bpc->bpc_localif, sizeof(bfd->ifname));
 
-       if (bpc->bpc_ipv4 == false) {
-               BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
+       if (bpc->bpc_has_vrfname)
+               strlcpy(bfd->vrfname, bpc->bpc_vrfname, sizeof(bfd->vrfname));
 
-               /* Set the IPv6 scope id for link-local addresses. */
-               if (IN6_IS_ADDR_LINKLOCAL(&bpc->bpc_local.sa_sin6.sin6_addr))
-                       bpc->bpc_local.sa_sin6.sin6_scope_id =
-                               bfd->ifp != NULL ? bfd->ifp->ifindex
-                                                : IFINDEX_INTERNAL;
-               if (IN6_IS_ADDR_LINKLOCAL(&bpc->bpc_peer.sa_sin6.sin6_addr))
-                       bpc->bpc_peer.sa_sin6.sin6_scope_id =
-                               bfd->ifp != NULL ? bfd->ifp->ifindex
-                                                : IFINDEX_INTERNAL;
-       }
+       /* Add observer if we have moving parts. */
+       if (bfd->ifname[0] || bfd->vrfname[0])
+               bs_observer_add(bfd);
 
-       /* Initialize the session */
-       bfd->ses_state = PTM_BFD_DOWN;
-       bfd->discrs.my_discr = ptm_bfd_gen_ID();
-       bfd->discrs.remote_discr = 0;
-       bfd->local_ip = bpc->bpc_local;
-       bfd->local_address = bpc->bpc_local;
-       bfd_recvtimer_update(bfd);
-       ptm_bfd_start_xmt_timer(bfd, false);
-
-       /* Registrate session into data structures. */
-       bfd_id_insert(bfd);
+       /* Copy remaining data. */
+       if (bpc->bpc_ipv4 == false)
+               BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
 
        if (bpc->bpc_mhop) {
                BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
                bfd->mhop.peer = bpc->bpc_peer;
                bfd->mhop.local = bpc->bpc_local;
-               if (vrf != NULL)
-                       bfd->mhop.vrfid = vrf->vrf_id;
-               else
-                       bfd->mhop.vrfid = VRF_DEFAULT;
-
-               bfd_mhop_insert(bfd);
        } else {
                bfd->shop.peer = bpc->bpc_peer;
-               if (ifp != NULL)
-                       bfd->shop.ifindex = ifp->ifindex;
-               else
-                       bfd->shop.ifindex = IFINDEX_INTERNAL;
+       }
 
-               bfd_shop_insert(bfd);
+       bfd->local_ip = bpc->bpc_local;
+       bfd->local_address = bpc->bpc_local;
+
+       /* Try to enable session and schedule for packet receive/send. */
+       if (bfd_session_enable(bfd) == -1) {
+               /* Unrecoverable failure, remove the session/peer. */
+               bfd_session_free(bfd);
+               return NULL;
        }
 
+       /* Apply other configurations. */
        _bfd_session_update(bfd, bpc);
 
        log_info("session-new: %s", bs_to_string(bfd));
@@ -1123,6 +1252,31 @@ const char *bs_to_string(struct bfd_session *bs)
        return buf;
 }
 
+int bs_observer_add(struct bfd_session *bs)
+{
+       struct bfd_session_observer *bso;
+
+       bso = XMALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
+       bso->bso_bs = bs;
+       bso->bso_isinterface = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
+       if (bso->bso_isinterface)
+               strlcpy(bso->bso_entryname, bs->ifname,
+                       sizeof(bso->bso_entryname));
+       else
+               strlcpy(bso->bso_entryname, bs->vrfname,
+                       sizeof(bso->bso_entryname));
+
+       TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
+
+       return 0;
+}
+
+void bs_observer_del(struct bfd_session_observer *bso)
+{
+       TAILQ_REMOVE(&bglobal.bg_obslist, bso, bso_entry);
+       XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
+}
+
 
 /*
  * BFD hash data structures to find sessions.
index 64121ae97938e7027c136d7b8808e3d287552818..b4d123647a5c91fb02b1c9f9269c034add49c211 100644 (file)
@@ -48,6 +48,7 @@ DECLARE_MTYPE(BFDD_TMP);
 DECLARE_MTYPE(BFDD_CONFIG);
 DECLARE_MTYPE(BFDD_LABEL);
 DECLARE_MTYPE(BFDD_CONTROL);
+DECLARE_MTYPE(BFDD_SESSION_OBSERVER);
 DECLARE_MTYPE(BFDD_NOTIFICATION);
 
 struct bfd_timers {
@@ -239,6 +240,8 @@ struct bfd_session {
        struct sockaddr_any local_ip;
        struct interface *ifp;
        struct vrf *vrf;
+       char ifname[MAXNAMELEN];
+       char vrfname[MAXNAMELEN];
        uint8_t local_mac[ETHERNET_ADDRESS_LENGTH];
        uint8_t peer_mac[ETHERNET_ADDRESS_LENGTH];
 
@@ -279,6 +282,15 @@ struct bfd_state_str_list {
        int type;
 };
 
+struct bfd_session_observer {
+       struct bfd_session *bso_bs;
+       bool bso_isinterface;
+       char bso_entryname[MAXNAMELEN];
+
+       TAILQ_ENTRY(bfd_session_observer) bso_entry;
+};
+TAILQ_HEAD(obslist, bfd_session_observer);
+
 
 /* States defined per 4.1 */
 #define PTM_BFD_ADM_DOWN 0
@@ -392,6 +404,8 @@ struct bfd_global {
        struct bcslist bg_bcslist;
 
        struct pllist bg_pllist;
+
+       struct obslist bg_obslist;
 };
 extern struct bfd_global bglobal;
 extern struct bfd_diag_str_list diag_list[];
@@ -460,8 +474,8 @@ int bp_udp_shop(void);
 int bp_udp_mhop(void);
 int bp_udp6_shop(void);
 int bp_udp6_mhop(void);
-int bp_peer_socket(struct bfd_peer_cfg *bpc);
-int bp_peer_socketv6(struct bfd_peer_cfg *bpc);
+int bp_peer_socket(const struct bfd_session *);
+int bp_peer_socketv6(const struct bfd_session *);
 int bp_echo_socket(void);
 int bp_echov6_socket(void);
 
@@ -499,6 +513,8 @@ void bfd_echo_xmttimer_assign(struct bfd_session *bs, bfd_ev_cb cb);
  *
  * BFD protocol specific code.
  */
+int bfd_session_enable(struct bfd_session *);
+void bfd_session_disable(struct bfd_session *);
 struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc);
 int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc);
 void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag);
@@ -524,6 +540,9 @@ int strtosa(const char *addr, struct sockaddr_any *sa);
 void integer2timestr(uint64_t time, char *buf, size_t buflen);
 const char *bs_to_string(struct bfd_session *bs);
 
+int bs_observer_add(struct bfd_session *);
+void bs_observer_del(struct bfd_session_observer *);
+
 /* BFD hash data structures interface */
 void bfd_initialize(void);
 void bfd_shutdown(void);
index 18d6ad25025d50aef1f2c3cae5bb8390fccb0aad..992ad09d32d2da38389c001511940e5e1675e372 100644 (file)
@@ -887,7 +887,7 @@ int bp_udp_mhop(void)
        return sd;
 }
 
-int bp_peer_socket(struct bfd_peer_cfg *bpc)
+int bp_peer_socket(const struct bfd_session *bs)
 {
        int sd, pcount;
        struct sockaddr_in sin;
@@ -912,28 +912,26 @@ int bp_peer_socket(struct bfd_peer_cfg *bpc)
                return -1;
        }
 
-       if (bpc->bpc_has_localif) {
-               if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
+       if (bs->shop.ifindex != IFINDEX_INTERNAL) {
+               if (bp_bind_dev(sd, bs->ifp->name) != 0) {
                        close(sd);
                        return -1;
                }
-       } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
-               if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
+       } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) &&
+                  bs->mhop.vrfid != VRF_DEFAULT) {
+               if (bp_bind_dev(sd, bs->vrf->name) != 0) {
                        close(sd);
                        return -1;
                }
        }
 
        /* Find an available source port in the proper range */
-       memset(&sin, 0, sizeof(sin));
-       sin = bpc->bpc_local.sa_sin;
+       sin = bs->local_ip.sa_sin;
        sin.sin_family = AF_INET;
 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
        sin.sin_len = sizeof(sin);
 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
-       if (bpc->bpc_mhop)
-               sin.sin_addr = bpc->bpc_local.sa_sin.sin_addr;
-       else
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
                sin.sin_addr.s_addr = INADDR_ANY;
 
        pcount = 0;
@@ -958,9 +956,8 @@ int bp_peer_socket(struct bfd_peer_cfg *bpc)
  * IPv6 sockets
  */
 
-int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
+int bp_peer_socketv6(const struct bfd_session *bs)
 {
-       struct interface *ifp;
        int sd, pcount;
        struct sockaddr_in6 sin6;
        static int srcPort = BFD_SRCPORTINIT;
@@ -985,25 +982,20 @@ int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
        }
 
        /* Find an available source port in the proper range */
-       memset(&sin6, 0, sizeof(sin6));
+       sin6 = bs->local_ip.sa_sin6;
        sin6.sin6_family = AF_INET6;
 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
        sin6.sin6_len = sizeof(sin6);
 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
-       sin6 = bpc->bpc_local.sa_sin6;
-       if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
-               ifp = if_lookup_by_name(bpc->bpc_localif, VRF_DEFAULT);
-               sin6.sin6_scope_id =
-                       (ifp != NULL) ? ifp->ifindex : IFINDEX_INTERNAL;
-       }
 
-       if (bpc->bpc_has_localif) {
-               if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
+       if (bs->shop.ifindex != IFINDEX_INTERNAL) {
+               if (bp_bind_dev(sd, bs->ifp->name) != 0) {
                        close(sd);
                        return -1;
                }
-       } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
-               if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
+       } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) &&
+                  bs->mhop.vrfid != VRF_DEFAULT) {
+               if (bp_bind_dev(sd, bs->vrf->name) != 0) {
                        close(sd);
                        return -1;
                }
index 250f8d21c0d30aaf5ed77b3d0871fa627d746b3b..6023b5e4f0a533194727e3ef67f9a2bc9844107d 100644 (file)
@@ -32,6 +32,7 @@ DEFINE_MTYPE(BFDD, BFDD_TMP, "short-lived temporary memory");
 DEFINE_MTYPE(BFDD, BFDD_CONFIG, "long-lived configuration memory");
 DEFINE_MTYPE(BFDD, BFDD_LABEL, "long-lived label memory");
 DEFINE_MTYPE(BFDD, BFDD_CONTROL, "long-lived control socket memory");
+DEFINE_MTYPE(BFDD, BFDD_SESSION_OBSERVER, "Session observer");
 DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "short-lived control notification data");
 
 /* Master of threads. */
@@ -153,6 +154,7 @@ struct bfd_state_str_list state_list[] = {
 static void bg_init(void)
 {
        TAILQ_INIT(&bglobal.bg_bcslist);
+       TAILQ_INIT(&bglobal.bg_obslist);
 
        bglobal.bg_shop = bp_udp_shop();
        bglobal.bg_mhop = bp_udp_mhop();
index 71429ffbbf9b17d9b98139c018980c9e57ced32d..8d99ccc693b6654bee45a4f5aa2bb4c6085366ce 100644 (file)
@@ -51,7 +51,8 @@
  */
 static int bfdd_write_config(struct vty *vty);
 static int bfdd_peer_write_config(struct vty *vty);
-static void _bfdd_peer_write_config(struct hash_backet *hb, void *arg);
+static void _bfdd_peer_write_config(struct vty *vty, struct bfd_session *bs);
+static void _bfdd_peer_write_config_iter(struct hash_backet *hb, void *arg);
 static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop,
                              const struct sockaddr_any *peer,
                              const struct sockaddr_any *local,
@@ -369,16 +370,16 @@ static void _display_peer_header(struct vty *vty, struct bfd_session *bs)
                vty_out(vty, "\tpeer %s", satostr(&bs->mhop.peer));
                vty_out(vty, " multihop");
                vty_out(vty, " local-address %s", satostr(&bs->mhop.local));
-               if (bs->mhop.vrfid != VRF_DEFAULT)
-                       vty_out(vty, " vrf %s", bs->vrf->name);
+               if (bs->vrfname[0])
+                       vty_out(vty, " vrf %s", bs->vrfname);
                vty_out(vty, "\n");
        } else {
                vty_out(vty, "\tpeer %s", satostr(&bs->shop.peer));
                if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
                        vty_out(vty, " local-address %s",
                                satostr(&bs->local_address));
-               if (bs->shop.ifindex != IFINDEX_INTERNAL)
-                       vty_out(vty, " interface %s", bs->ifp->name);
+               if (bs->ifname[0])
+                       vty_out(vty, " interface %s", bs->ifname);
                vty_out(vty, "\n");
        }
 
@@ -454,16 +455,16 @@ static struct json_object *_peer_json_header(struct bfd_session *bs)
                json_object_boolean_true_add(jo, "multihop");
                json_object_string_add(jo, "peer", satostr(&bs->mhop.peer));
                json_object_string_add(jo, "local", satostr(&bs->mhop.local));
-               if (bs->mhop.vrfid != VRF_DEFAULT)
-                       json_object_string_add(jo, "vrf", bs->vrf->name);
+               if (bs->vrfname[0])
+                       json_object_string_add(jo, "vrf", bs->vrfname);
        } else {
                json_object_boolean_false_add(jo, "multihop");
                json_object_string_add(jo, "peer", satostr(&bs->shop.peer));
                if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
                        json_object_string_add(jo, "local",
                                               satostr(&bs->local_address));
-               if (bs->shop.ifindex != IFINDEX_INTERNAL)
-                       json_object_string_add(jo, "interface", bs->ifp->name);
+               if (bs->ifname[0])
+                       json_object_string_add(jo, "interface", bs->ifname);
        }
 
        if (bs->pl)
@@ -910,28 +911,28 @@ static int bfdd_write_config(struct vty *vty)
        return 0;
 }
 
-static void _bfdd_peer_write_config(struct hash_backet *hb, void *arg)
+static void _bfdd_peer_write_config(struct vty *vty, struct bfd_session *bs)
 {
-       struct vty *vty = arg;
-       struct bfd_session *bs = hb->data;
-
        if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
                vty_out(vty, " peer %s", satostr(&bs->mhop.peer));
                vty_out(vty, " multihop");
                vty_out(vty, " local-address %s", satostr(&bs->mhop.local));
-               if (bs->mhop.vrfid != VRF_DEFAULT)
-                       vty_out(vty, " vrf %s", bs->vrf->name);
+               if (bs->vrfname[0])
+                       vty_out(vty, " vrf %s", bs->vrfname);
                vty_out(vty, "\n");
        } else {
                vty_out(vty, " peer %s", satostr(&bs->shop.peer));
                if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
                        vty_out(vty, " local-address %s",
                                satostr(&bs->local_address));
-               if (bs->shop.ifindex != IFINDEX_INTERNAL)
-                       vty_out(vty, " interface %s", bs->ifp->name);
+               if (bs->ifname[0])
+                       vty_out(vty, " interface %s", bs->ifname);
                vty_out(vty, "\n");
        }
 
+       if (bs->sock == -1)
+               vty_out(vty, "  ! vrf or interface doesn't exist\n");
+
        if (bs->detect_mult != BPC_DEF_DETECTMULTIPLIER)
                vty_out(vty, "  detect-multiplier %d\n", bs->detect_mult);
        if (bs->timers.required_min_rx != (BPC_DEF_RECEIVEINTERVAL * 1000))
@@ -966,9 +967,27 @@ DEFUN_NOSH(show_debugging_bfd,
        return CMD_SUCCESS;
 }
 
+static void _bfdd_peer_write_config_iter(struct hash_backet *hb, void *arg)
+{
+       struct vty *vty = arg;
+       struct bfd_session *bs = hb->data;
+
+       _bfdd_peer_write_config(vty, bs);
+}
+
 static int bfdd_peer_write_config(struct vty *vty)
 {
-       bfd_id_iterate(_bfdd_peer_write_config, vty);
+       struct bfd_session_observer *bso;
+
+       bfd_id_iterate(_bfdd_peer_write_config_iter, vty);
+       TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
+               /* Only print disabled sessions here. */
+               if (bso->bso_bs->sock != -1)
+                       continue;
+
+               _bfdd_peer_write_config(vty, bso->bso_bs);
+       }
+
        return 1;
 }
 
index 9e3abcb1b8aaa9cece49a269b298632cc293aece..d1342eff1ec22fb58efa982b226c7731128da976 100644 (file)
@@ -321,9 +321,9 @@ static int parse_peer_label_config(struct json_object *jo,
                }
        } else {
                bpc->bpc_peer = pl->pl_bs->shop.peer;
-               if (pl->pl_bs->shop.ifindex != IFINDEX_INTERNAL) {
+               if (pl->pl_bs->ifname[0]) {
                        bpc->bpc_has_localif = true;
-                       strlcpy(bpc->bpc_localif, pl->pl_bs->ifp->name,
+                       strlcpy(bpc->bpc_localif, pl->pl_bs->ifname,
                                sizeof(bpc->bpc_localif));
                }
        }
@@ -531,8 +531,8 @@ static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
                                       satostr(&bs->mhop.peer));
                json_object_string_add(jo, "local-address",
                                       satostr(&bs->mhop.local));
-               if (bs->mhop.vrfid != VRF_DEFAULT)
-                       json_object_string_add(jo, "vrf-name", bs->vrf->name);
+               if (bs->vrfname[0])
+                       json_object_string_add(jo, "vrf-name", bs->vrfname);
        } else {
                json_object_boolean_false_add(jo, "multihop");
                json_object_string_add(jo, "peer-address",
@@ -540,9 +540,9 @@ static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
                if (bs->local_address.sa_sin.sin_family != AF_UNSPEC)
                        json_object_string_add(jo, "local-address",
                                               satostr(&bs->local_address));
-               if (bs->shop.ifindex != IFINDEX_INTERNAL)
+               if (bs->ifname[0])
                        json_object_string_add(jo, "local-interface",
-                                              bs->ifp->name);
+                                              bs->ifname);
        }
 
        if (bs->pl)
index 3f48921af9ac76ee3783a88cfaf36b64e5581476..5ba54c2b0b2a1422a4b7eadc5e9d9d5bc4b4fc1d 100644 (file)
@@ -43,7 +43,8 @@ void bfd_recvtimer_update(struct bfd_session *bs)
        bfd_recvtimer_delete(bs);
 
        /* Don't add event if peer is deactivated. */
-       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
+           bs->sock == -1)
                return;
 
        tv_normalize(&tv);
@@ -63,7 +64,8 @@ void bfd_echo_recvtimer_update(struct bfd_session *bs)
        bfd_echo_recvtimer_delete(bs);
 
        /* Don't add event if peer is deactivated. */
-       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
+           bs->sock == -1)
                return;
 
        tv_normalize(&tv);
@@ -83,7 +85,8 @@ void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
        bfd_xmttimer_delete(bs);
 
        /* Don't add event if peer is deactivated. */
-       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
+           bs->sock == -1)
                return;
 
        tv_normalize(&tv);
@@ -102,7 +105,8 @@ void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
        bfd_echo_xmttimer_delete(bs);
 
        /* Don't add event if peer is deactivated. */
-       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ||
+           bs->sock == -1)
                return;
 
        tv_normalize(&tv);
index 8ce33e5270ccc72607c708335b560e545e78a4ea..3f1512d8e715de38b86304c3b90a2e3e2cf20682 100644 (file)
@@ -597,23 +597,78 @@ static void bfdd_zebra_connected(struct zclient *zc)
        zclient_send_message(zclient);
 }
 
-static int bfdd_interface_update(int cmd, struct zclient *zc, uint16_t len,
+static void bfdd_sessions_enable_interface(struct interface *ifp)
+{
+       struct bfd_session_observer *bso;
+       struct bfd_session *bs;
+
+       TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
+               if (bso->bso_isinterface == false)
+                       continue;
+
+               /* Interface name mismatch. */
+               bs = bso->bso_bs;
+               if (strcmp(ifp->name, bs->ifname))
+                       continue;
+               /* Skip enabled sessions. */
+               if (bs->sock != -1)
+                       continue;
+
+               /* Try to enable it. */
+               bfd_session_enable(bs);
+       }
+}
+
+static void bfdd_sessions_disable_interface(struct interface *ifp)
+{
+       struct bfd_session_observer *bso;
+       struct bfd_session *bs;
+
+       TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
+               if (bso->bso_isinterface == false)
+                       continue;
+
+               /* Interface name mismatch. */
+               bs = bso->bso_bs;
+               if (strcmp(ifp->name, bs->ifname))
+                       continue;
+               /* Skip disabled sessions. */
+               if (bs->sock == -1)
+                       continue;
+
+               /* Try to enable it. */
+               bfd_session_disable(bs);
+
+               TAILQ_INSERT_HEAD(&bglobal.bg_obslist, bso, bso_entry);
+       }
+}
+
+static int bfdd_interface_update(int cmd, struct zclient *zc,
+                                uint16_t len __attribute__((__unused__)),
                                 vrf_id_t vrfid)
 {
+       struct interface *ifp;
+
        /*
         * `zebra_interface_add_read` will handle the interface creation
         * on `lib/if.c`. We'll use that data structure instead of
         * rolling our own.
         */
        if (cmd == ZEBRA_INTERFACE_ADD) {
-               zebra_interface_add_read(zc->ibuf, vrfid);
+               ifp = zebra_interface_add_read(zc->ibuf, vrfid);
+               if (ifp == NULL)
+                       return 0;
+
+               bfdd_sessions_enable_interface(ifp);
                return 0;
        }
 
        /* Update interface information. */
-       zebra_interface_state_read(zc->ibuf, vrfid);
+       ifp = zebra_interface_state_read(zc->ibuf, vrfid);
+       if (ifp == NULL)
+               return 0;
 
-       /* TODO: stop all sessions using this interface. */
+       bfdd_sessions_disable_interface(ifp);
 
        return 0;
 }