]> git.proxmox.com Git - mirror_frr.git/blobdiff - bfdd/bfd.c
bfdd: Adding new fields to display show bfd peer
[mirror_frr.git] / bfdd / bfd.c
index 08a70abc1ea29435d94e0b1b4516175382280764..cc171f2ebfd405d6cb4711258565ef78debf6960 100644 (file)
 DEFINE_MTYPE_STATIC(BFDD, BFDD_CONFIG, "long-lived configuration memory")
 DEFINE_MTYPE_STATIC(BFDD, BFDD_SESSION_OBSERVER, "Session observer")
 DEFINE_MTYPE_STATIC(BFDD, BFDD_VRF, "BFD VRF")
-DEFINE_QOBJ_TYPE(bfd_session)
 
 /*
  * Prototypes
  */
-void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
-                struct sockaddr_any *local, bool mhop, const char *ifname,
-                const char *vrfname);
-
 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(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);
@@ -56,6 +49,8 @@ static void bs_admin_down_handler(struct bfd_session *bs, int nstate);
 static void bs_down_handler(struct bfd_session *bs, int nstate);
 static void bs_init_handler(struct bfd_session *bs, int nstate);
 static void bs_up_handler(struct bfd_session *bs, int nstate);
+static void bs_neighbour_admin_down_handler(struct bfd_session *bfd,
+                                           uint8_t diag);
 
 /* Zeroed array with the size of an IPv6 address. */
 struct in6_addr zero_addr;
@@ -91,6 +86,8 @@ void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
                strlcpy(key->ifname, ifname, sizeof(key->ifname));
        if (vrfname && vrfname[0])
                strlcpy(key->vrfname, vrfname, sizeof(key->vrfname));
+       else
+               strlcpy(key->vrfname, VRF_DEFAULT_NAME, sizeof(key->vrfname));
 }
 
 struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
@@ -164,6 +161,7 @@ int bfd_session_enable(struct bfd_session *bs)
        bs->vrf = vrf;
        if (bs->vrf == NULL)
                bs->vrf = vrf_lookup_by_id(VRF_DEFAULT);
+       assert(bs->vrf);
 
        if (bs->key.ifname[0]
            && BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0)
@@ -218,9 +216,13 @@ void bfd_session_disable(struct bfd_session *bs)
 
        /* Disable all timers. */
        bfd_recvtimer_delete(bs);
-       bfd_echo_recvtimer_delete(bs);
        bfd_xmttimer_delete(bs);
-       bfd_echo_xmttimer_delete(bs);
+       ptm_bfd_echo_stop(bs);
+       bs->vrf = NULL;
+       bs->ifp = NULL;
+
+       /* Set session down so it doesn't report UP and disabled. */
+       ptm_bfd_sess_dn(bs, BD_PATH_DOWN);
 }
 
 static uint32_t ptm_bfd_gen_ID(void)
@@ -312,7 +314,7 @@ void ptm_bfd_sess_up(struct bfd_session *bfd)
        /* Start sending control packets with poll bit immediately. */
        ptm_bfd_snd(bfd, 0);
 
-       control_notify(bfd);
+       control_notify(bfd, bfd->ses_state);
 
        if (old_state != bfd->ses_state) {
                bfd->stats.session_up++;
@@ -333,14 +335,21 @@ void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag)
        bfd->demand_mode = 0;
        monotime(&bfd->downtime);
 
-       ptm_bfd_snd(bfd, 0);
+       /*
+        * Only attempt to send if we have a valid socket:
+        * this function might be called by session disablers and in
+        * this case we won't have a valid socket (i.e. interface was
+        * removed or VRF doesn't exist anymore).
+        */
+       if (bfd->sock != -1)
+               ptm_bfd_snd(bfd, 0);
 
        /* Slow down the control packets, the connection is down. */
        bs_set_slow_timers(bfd);
 
        /* only signal clients when going from up->down state */
        if (old_state == PTM_BFD_UP)
-               control_notify(bfd);
+               control_notify(bfd, PTM_BFD_DOWN);
 
        /* Stop echo packet transmission if they are active */
        if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
@@ -396,17 +405,11 @@ struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
 
        /* Search for session without using discriminator. */
        ifp = if_lookup_by_index(ifindex, vrfid);
-       if (vrfid == VRF_DEFAULT) {
-               /*
-                * Don't use the default vrf, otherwise we won't find
-                * sessions that doesn't specify it.
-                */
-               vrf = NULL;
-       } else
-               vrf = vrf_lookup_by_id(vrfid);
+
+       vrf = vrf_lookup_by_id(vrfid);
 
        gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL,
-                   vrf ? vrf->name : NULL);
+                   vrf ? vrf->name : VRF_DEFAULT_NAME);
 
        /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
        return bfd_key_lookup(key);
@@ -469,14 +472,12 @@ int bfd_echo_recvtimer_cb(struct thread *t)
        return 0;
 }
 
-static struct bfd_session *bfd_session_new(void)
+struct bfd_session *bfd_session_new(void)
 {
        struct bfd_session *bs;
 
        bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs));
 
-       QOBJ_REG(bs, bfd_session);
-
        bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX;
        bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
        bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO;
@@ -583,7 +584,7 @@ skip_echo:
 
                /* Change and notify state change. */
                bs->ses_state = PTM_BFD_ADM_DOWN;
-               control_notify(bs);
+               control_notify(bs, bs->ses_state);
 
                /* Don't try to send packets with a disabled session. */
                if (bs->sock != -1)
@@ -597,7 +598,7 @@ skip_echo:
 
                /* Change and notify state change. */
                bs->ses_state = PTM_BFD_DOWN;
-               control_notify(bs);
+               control_notify(bs, bs->ses_state);
 
                /* Enable all timers. */
                bfd_recvtimer_update(bs);
@@ -629,7 +630,7 @@ static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
        return 0;
 }
 
-static void bfd_session_free(struct bfd_session *bs)
+void bfd_session_free(struct bfd_session *bs)
 {
        struct bfd_session_observer *bso;
 
@@ -650,7 +651,6 @@ static void bfd_session_free(struct bfd_session *bs)
 
        pl_free(bs->pl);
 
-       QOBJ_UNREG(bs);
        XFREE(MTYPE_BFDD_CONFIG, bs);
 }
 
@@ -686,6 +686,9 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
        if (bpc->bpc_has_vrfname)
                strlcpy(bfd->key.vrfname, bpc->bpc_vrfname,
                        sizeof(bfd->key.vrfname));
+       else
+               strlcpy(bfd->key.vrfname, VRF_DEFAULT_NAME,
+                       sizeof(bfd->key.vrfname));
 
        /* Copy remaining data. */
        if (bpc->bpc_ipv4 == false)
@@ -717,6 +720,17 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
 
        bfd->key.mhop = bpc->bpc_mhop;
 
+       if (bs_registrate(bfd) == NULL)
+               return NULL;
+
+       /* Apply other configurations. */
+       _bfd_session_update(bfd, bpc);
+
+       return bfd;
+}
+
+struct bfd_session *bs_registrate(struct bfd_session *bfd)
+{
        /* Registrate session into data structures. */
        bfd_key_insert(bfd);
        bfd->discrs.my_discr = ptm_bfd_gen_ID();
@@ -733,9 +747,6 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
        if (bfd->key.ifname[0] || bfd->key.vrfname[0] || bfd->sock == -1)
                bs_observer_add(bfd);
 
-       /* Apply other configurations. */
-       _bfd_session_update(bfd, bpc);
-
        log_info("session-new: %s", bs_to_string(bfd));
 
        control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
@@ -859,10 +870,46 @@ static void bs_init_handler(struct bfd_session *bs, int nstate)
        }
 }
 
+static void bs_neighbour_admin_down_handler(struct bfd_session *bfd,
+                                           uint8_t diag)
+{
+       int old_state = bfd->ses_state;
+
+       bfd->local_diag = diag;
+       bfd->discrs.remote_discr = 0;
+       bfd->ses_state = PTM_BFD_DOWN;
+       bfd->polling = 0;
+       bfd->demand_mode = 0;
+       monotime(&bfd->downtime);
+
+       /* Slow down the control packets, the connection is down. */
+       bs_set_slow_timers(bfd);
+
+       /* only signal clients when going from up->down state */
+       if (old_state == PTM_BFD_UP)
+               control_notify(bfd, PTM_BFD_ADM_DOWN);
+
+       /* Stop echo packet transmission if they are active */
+       if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
+               ptm_bfd_echo_stop(bfd);
+
+       if (old_state != bfd->ses_state) {
+               bfd->stats.session_down++;
+
+               log_info("state-change: [%s] %s -> %s reason:%s",
+                       bs_to_string(bfd), state_list[old_state].str,
+                       state_list[bfd->ses_state].str,
+                       get_diag_str(bfd->local_diag));
+       }
+}
+
 static void bs_up_handler(struct bfd_session *bs, int nstate)
 {
        switch (nstate) {
        case PTM_BFD_ADM_DOWN:
+               bs_neighbour_admin_down_handler(bs, BD_ADMIN_DOWN);
+               break;
+
        case PTM_BFD_DOWN:
                /* Peer lost or asked to shutdown connection. */
                ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
@@ -1209,19 +1256,10 @@ int bs_observer_add(struct bfd_session *bs)
        struct bfd_session_observer *bso;
 
        bso = XCALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
-       bso->bso_isaddress = false;
        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->key.ifname,
-                       sizeof(bso->bso_entryname));
-       /* Handle socket binding failures caused by missing local addresses. */
-       if (bs->sock == -1) {
-               bso->bso_isaddress = true;
-               bso->bso_addr.family = bs->key.family;
-               memcpy(&bso->bso_addr.u.prefix, &bs->key.local,
-                      sizeof(bs->key.local));
-       }
+       bso->bso_addr.family = bs->key.family;
+       memcpy(&bso->bso_addr.u.prefix, &bs->key.local,
+              sizeof(bs->key.local));
 
        TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
 
@@ -1342,9 +1380,10 @@ struct bfd_key_walk_partial_lookup {
 };
 
 /* ignore some parameters */
-static int bfd_key_lookup_ignore_partial_walker(struct hash_bucket *b, void *data)
+static int bfd_key_lookup_ignore_partial_walker(struct hash_bucket *b,
+                                               void *data)
 {
-       struct bfd_key_walk_partial_lookup  *ctx =
+       struct bfd_key_walk_partial_lookup *ctx =
                (struct bfd_key_walk_partial_lookup *)data;
        struct bfd_session *given = ctx->given;
        struct bfd_session *parsed = b->data;
@@ -1353,7 +1392,8 @@ static int bfd_key_lookup_ignore_partial_walker(struct hash_bucket *b, void *dat
                return HASHWALK_CONTINUE;
        if (given->key.mhop != parsed->key.mhop)
                return HASHWALK_CONTINUE;
-       if (memcmp(&given->key.peer, &parsed->key.peer, sizeof(struct in6_addr)))
+       if (memcmp(&given->key.peer, &parsed->key.peer,
+                  sizeof(struct in6_addr)))
                return HASHWALK_CONTINUE;
        if (memcmp(given->key.vrfname, parsed->key.vrfname, MAXNAMELEN))
                return HASHWALK_CONTINUE;
@@ -1433,7 +1473,7 @@ struct bfd_session *bfd_key_lookup(struct bfd_key key)
        if (ctx.result) {
                bsp = ctx.result;
                log_debug(" peer %s found, but ifp"
-                         " and/or loc-addr params ignored");
+                         " and/or loc-addr params ignored", peer_buf);
        }
        return bsp;
 }
@@ -1531,6 +1571,94 @@ void bfd_shutdown(void)
        hash_free(bfd_key_hash);
 }
 
+struct bfd_session_iterator {
+       int bsi_stop;
+       bool bsi_mhop;
+       const struct bfd_session *bsi_bs;
+};
+
+static int _bfd_session_next(struct hash_bucket *hb, void *arg)
+{
+       struct bfd_session_iterator *bsi = arg;
+       struct bfd_session *bs = hb->data;
+
+       /* Previous entry signaled stop. */
+       if (bsi->bsi_stop == 1) {
+               /* Match the single/multi hop sessions. */
+               if (bs->key.mhop != bsi->bsi_mhop)
+                       return HASHWALK_CONTINUE;
+
+               bsi->bsi_bs = bs;
+               return HASHWALK_ABORT;
+       }
+
+       /* We found the current item, stop in the next one. */
+       if (bsi->bsi_bs == hb->data) {
+               bsi->bsi_stop = 1;
+               /* Set entry to NULL to signal end of list. */
+               bsi->bsi_bs = NULL;
+       } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop) {
+               /* We want the first list item. */
+               bsi->bsi_stop = 1;
+               bsi->bsi_bs = hb->data;
+               return HASHWALK_ABORT;
+       }
+
+       return HASHWALK_CONTINUE;
+}
+
+/*
+ * bfd_session_next: uses the current session to find the next.
+ *
+ * `bs` might point to NULL to get the first item of the data structure.
+ */
+const struct bfd_session *bfd_session_next(const struct bfd_session *bs,
+                                          bool mhop)
+{
+       struct bfd_session_iterator bsi;
+
+       bsi.bsi_stop = 0;
+       bsi.bsi_bs = bs;
+       bsi.bsi_mhop = mhop;
+       hash_walk(bfd_key_hash, _bfd_session_next, &bsi);
+       if (bsi.bsi_stop == 0)
+               return NULL;
+
+       return bsi.bsi_bs;
+}
+
+static void _bfd_session_remove_manual(struct hash_bucket *hb,
+                                      void *arg __attribute__((__unused__)))
+{
+       struct bfd_session *bs = hb->data;
+
+       /* Delete only manually configured sessions. */
+       if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0)
+               return;
+
+       bs->refcount--;
+       BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
+
+       /* Don't delete sessions still in use. */
+       if (bs->refcount != 0)
+               return;
+
+       bfd_session_free(bs);
+}
+
+/*
+ * bfd_sessions_remove_manual: remove all manually configured sessions.
+ *
+ * NOTE: this function doesn't remove automatically created sessions.
+ */
+void bfd_sessions_remove_manual(void)
+{
+       hash_iterate(bfd_key_hash, _bfd_session_remove_manual, NULL);
+}
+
+/*
+ * VRF related functions.
+ */
 static int bfd_vrf_new(struct vrf *vrf)
 {
        log_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
@@ -1543,6 +1671,16 @@ static int bfd_vrf_delete(struct vrf *vrf)
        return 0;
 }
 
+static int bfd_vrf_update(struct vrf *vrf)
+{
+       if (!vrf_is_enabled(vrf))
+               return 0;
+       log_debug("VRF update: %s(%u)", vrf->name, vrf->vrf_id);
+       /* a different name is given; update bfd list */
+       bfdd_sessions_enable_vrf(vrf);
+       return 0;
+}
+
 static int bfd_vrf_enable(struct vrf *vrf)
 {
        struct bfd_vrf_global *bvrf;
@@ -1558,17 +1696,17 @@ static int bfd_vrf_enable(struct vrf *vrf)
        if (vrf->vrf_id == VRF_DEFAULT ||
            vrf_get_backend() == VRF_BACKEND_NETNS) {
                if (!bvrf->bg_shop)
-                       bvrf->bg_shop = bp_udp_shop(vrf->vrf_id);
+                       bvrf->bg_shop = bp_udp_shop(vrf);
                if (!bvrf->bg_mhop)
-                       bvrf->bg_mhop = bp_udp_mhop(vrf->vrf_id);
+                       bvrf->bg_mhop = bp_udp_mhop(vrf);
                if (!bvrf->bg_shop6)
-                       bvrf->bg_shop6 = bp_udp6_shop(vrf->vrf_id);
+                       bvrf->bg_shop6 = bp_udp6_shop(vrf);
                if (!bvrf->bg_mhop6)
-                       bvrf->bg_mhop6 = bp_udp6_mhop(vrf->vrf_id);
+                       bvrf->bg_mhop6 = bp_udp6_mhop(vrf);
                if (!bvrf->bg_echo)
-                       bvrf->bg_echo = bp_echo_socket(vrf->vrf_id);
+                       bvrf->bg_echo = bp_echo_socket(vrf);
                if (!bvrf->bg_echov6)
-                       bvrf->bg_echov6 = bp_echov6_socket(vrf->vrf_id);
+                       bvrf->bg_echov6 = bp_echov6_socket(vrf);
 
                /* Add descriptors to the event loop. */
                if (!bvrf->bg_ev[0])
@@ -1611,12 +1749,23 @@ static int bfd_vrf_disable(struct vrf *vrf)
        }
 
        log_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id);
+
+       /* Disable read/write poll triggering. */
+       THREAD_OFF(bvrf->bg_ev[0]);
+       THREAD_OFF(bvrf->bg_ev[1]);
+       THREAD_OFF(bvrf->bg_ev[2]);
+       THREAD_OFF(bvrf->bg_ev[3]);
+       THREAD_OFF(bvrf->bg_ev[4]);
+       THREAD_OFF(bvrf->bg_ev[5]);
+
        /* Close all descriptors. */
        socket_close(&bvrf->bg_echo);
        socket_close(&bvrf->bg_shop);
        socket_close(&bvrf->bg_mhop);
        socket_close(&bvrf->bg_shop6);
        socket_close(&bvrf->bg_mhop6);
+       socket_close(&bvrf->bg_echo);
+       socket_close(&bvrf->bg_echov6);
 
        /* free context */
        XFREE(MTYPE_BFDD_VRF, bvrf);
@@ -1628,7 +1777,7 @@ static int bfd_vrf_disable(struct vrf *vrf)
 void bfd_vrf_init(void)
 {
        vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable,
-                bfd_vrf_delete, NULL);
+                bfd_vrf_delete, bfd_vrf_update);
 }
 
 void bfd_vrf_terminate(void)
@@ -1652,3 +1801,54 @@ struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd)
                return NULL;
        return bfd->vrf->info;
 }
+
+void bfd_session_update_vrf_name(struct bfd_session *bs, struct vrf *vrf)
+{
+       if (!vrf || !bs)
+               return;
+       /* update key */
+       hash_release(bfd_key_hash, bs);
+       /*
+        * HACK: Change the BFD VRF in the running configuration directly,
+        * bypassing the northbound layer. This is necessary to avoid deleting
+        * the BFD and readding it in the new VRF, which would have
+        * several implications.
+        */
+       if (yang_module_find("frr-bfdd") && bs->key.vrfname[0]) {
+               struct lyd_node *bfd_dnode;
+               char xpath[XPATH_MAXLEN], xpath_srcaddr[XPATH_MAXLEN + 32];
+               char addr_buf[INET6_ADDRSTRLEN];
+               int slen;
+
+               /* build xpath */
+               if (bs->key.mhop) {
+                       inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf));
+                       snprintf(xpath_srcaddr, sizeof(xpath_srcaddr), "[source-addr='%s']",
+                                addr_buf);
+               } else
+                       xpath_srcaddr[0] = 0;
+               inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf));
+               slen = snprintf(xpath, sizeof(xpath),
+                               "/frr-bfdd:bfdd/bfd/sessions/%s%s[dest-addr='%s']",
+                               bs->key.mhop ? "multi-hop" : "single-hop", xpath_srcaddr,
+                               addr_buf);
+               if (bs->key.ifname[0])
+                       slen += snprintf(xpath + slen, sizeof(xpath) - slen,
+                                        "[interface='%s']", bs->key.ifname);
+               else
+                       slen += snprintf(xpath + slen, sizeof(xpath) - slen,
+                                        "[interface='']");
+               snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']/vrf",
+                        bs->key.vrfname);
+
+               bfd_dnode = yang_dnode_get(running_config->dnode, xpath,
+                                          bs->key.vrfname);
+               if (bfd_dnode) {
+                       yang_dnode_change_leaf(bfd_dnode, vrf->name);
+                       running_config->version++;
+               }
+       }
+       memset(bs->key.vrfname, 0, sizeof(bs->key.vrfname));
+       strlcpy(bs->key.vrfname, vrf->name, sizeof(bs->key.vrfname));
+       hash_get(bfd_key_hash, bs, hash_alloc_intern);
+}