]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: bmp loc-rib peer up/down for vrfs
authorMaxence Younsi <mx.yns@outlook.fr>
Thu, 13 Apr 2023 13:28:32 +0000 (15:28 +0200)
committerLouis Scalbert <louis.scalbert@6wind.com>
Fri, 11 Oct 2024 13:14:12 +0000 (15:14 +0200)
added bmp bgp peer for vrfs
added peer up vrf in bmp peer up state
added vrf state in bmpbgp
added safe bmp_peer_sendall : bmp_peer_sendall_safe
changed bgp_open_send to call new bgp_open_make
bgp_open_make creates a bgp open packet, now used in bmp for peer up vrf
added hook and call to bgp instance state
vrf peer state is recomputed when interfaces (including vrf itf) go up / down
and when it gets created or removed

Link: https://github.com/mxyns/frr/commit/e48ba380700d53124131f4e4419f646c05b40c86
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
Signed-off-by: Maxence Younsi <mx.yns@outlook.fr>
bgpd/bgp_bmp.c
bgpd/bgp_bmp.h
bgpd/bgp_packet.c
bgpd/bgp_packet.h
bgpd/bgpd.c
bgpd/bgpd.h

index 9d99c2c7fda80882b5b81df13fa6786a6801e236..08f8b8b734ffeb16a2b3fe039d83f25f9604d7fc 100644 (file)
@@ -39,6 +39,8 @@
 #include "bgpd/bgp_trace.h"
 #include "bgpd/bgp_network.h"
 #include "bgpd/bgp_label.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_aspath.h"
 
 static void bmp_close(struct bmp *bmp);
 static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp);
@@ -246,6 +248,33 @@ static void bmp_free(struct bmp *bmp)
 #define BMP_PEER_TYPE_LOCAL_INSTANCE 2
 #define BMP_PEER_TYPE_LOC_RIB_INSTANCE 3
 
+/* determine the peer type for per-peer headers from a vrf_id
+ * for non loc-rib peer type only
+ */
+static inline int bmp_get_peer_type_vrf(vrf_id_t vrf_id)
+{
+       switch (vrf_id) {
+       case VRF_DEFAULT:
+               return BMP_PEER_TYPE_GLOBAL_INSTANCE;
+       case VRF_UNKNOWN:
+               /* when the VRF is unknown consider it a local instance */
+               return BMP_PEER_TYPE_LOCAL_INSTANCE;
+       default:
+               return BMP_PEER_TYPE_RD_INSTANCE;
+       }
+}
+
+/* determine the peer type for per-peer headers from a struct peer
+ * provide a bgp->peer_self for loc-rib
+ */
+static inline int bmp_get_peer_type(struct peer *peer)
+{
+       if (peer->bgp->peer_self == peer)
+               return BMP_PEER_TYPE_LOC_RIB_INSTANCE;
+
+       return bmp_get_peer_type_vrf(peer->bgp->vrf_id);
+}
+
 static inline int bmp_get_peer_distinguisher(struct bmp *bmp, afi_t afi,
                                             uint8_t peer_type,
                                             uint64_t *result_ref)
@@ -370,17 +399,18 @@ static void bmp_put_info_tlv(struct stream *s, uint16_t type,
        stream_put(s, string, len);
 }
 
-static void __attribute__((unused))
-bmp_put_vrftablename_info_tlv(struct stream *s, struct bmp *bmp)
+/* put the vrf table name of the bgp instance bmp is bound to in a tlv on the
+ * stream
+ */
+static void bmp_put_vrftablename_info_tlv(struct stream *s, struct bgp *bgp)
 {
+       const char *vrftablename = "global";
 
 #define BMP_INFO_TYPE_VRFTABLENAME 3
-       const char *vrftablename = "global";
-       if (bmp->targets->bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
-               struct vrf *vrf = vrf_lookup_by_id(bmp->targets->bgp->vrf_id);
 
-               vrftablename = vrf ? vrf->name : NULL;
-       }
+       if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT)
+               vrftablename = bgp->name;
+
        if (vrftablename != NULL)
                bmp_put_info_tlv(s, BMP_INFO_TYPE_VRFTABLENAME, vrftablename);
 }
@@ -428,6 +458,10 @@ static void bmp_notify_put(struct stream *s, struct bgp_notify *nfy)
                        + sizeof(marker));
 }
 
+/* send peer up/down for peer based on down boolean value
+ * returns the message to send or NULL if the peer_distinguisher is not
+ * available
+ */
 static struct stream *bmp_peerstate(struct peer *peer, bool down)
 {
        struct stream *s;
@@ -438,11 +472,14 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
        uptime.tv_usec = 0;
        monotime_to_realtime(&uptime, &uptime_real);
 
+       uint8_t peer_type = bmp_get_peer_type(peer);
+       bool is_locrib = peer_type == BMP_PEER_TYPE_LOC_RIB_INSTANCE;
+
 #define BGP_BMP_MAX_PACKET_SIZE        1024
 #define BMP_PEERUP_INFO_TYPE_STRING 0
        s = stream_new(BGP_MAX_PACKET_SIZE);
 
-       if (peer_established(peer->connection) && !down) {
+       if ((peer_established(peer->connection) || is_locrib) && !down) {
                struct bmp_bgp_peer *bbpeer;
 
                bmp_common_hdr(s, BMP_VERSION_3,
@@ -452,7 +489,9 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
                                 &uptime_real);
 
                /* Local Address (16 bytes) */
-               if (peer->su_local->sa.sa_family == AF_INET6)
+               if (!peer->su_local || is_locrib)
+                       stream_put(s, 0, 16);
+               else if (peer->su_local->sa.sa_family == AF_INET6)
                        stream_put(s, &peer->su_local->sin6.sin6_addr, 16);
                else if (peer->su_local->sa.sa_family == AF_INET) {
                        stream_putl(s, 0);
@@ -462,15 +501,21 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
                }
 
                /* Local Port, Remote Port */
-               if (peer->su_local->sa.sa_family == AF_INET6)
+               if (is_locrib)
+                       stream_putw(s, 0);
+               else if (peer->su_local->sa.sa_family == AF_INET6)
                        stream_putw(s, htons(peer->su_local->sin6.sin6_port));
                else if (peer->su_local->sa.sa_family == AF_INET)
                        stream_putw(s, htons(peer->su_local->sin.sin_port));
-               if (peer->su_remote->sa.sa_family == AF_INET6)
+
+               if (is_locrib)
+                       stream_putw(s, 0);
+               else if (peer->su_remote->sa.sa_family == AF_INET6)
                        stream_putw(s, htons(peer->su_remote->sin6.sin6_port));
                else if (peer->su_remote->sa.sa_family == AF_INET)
                        stream_putw(s, htons(peer->su_remote->sin.sin_port));
 
+               /* TODO craft message with fields & capabilities for loc-rib */
                static const uint8_t dummy_open[] = {
                        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -510,32 +555,40 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
                type_pos = stream_get_endp(s);
                stream_putc(s, 0);      /* placeholder for down reason */
 
-               switch (peer->last_reset) {
-               case PEER_DOWN_NOTIFY_RECEIVED:
-                       type = BMP_PEERDOWN_REMOTE_NOTIFY;
-                       bmp_notify_put(s, &peer->notify);
-                       break;
-               case PEER_DOWN_CLOSE_SESSION:
-                       type = BMP_PEERDOWN_REMOTE_CLOSE;
-                       break;
-               case PEER_DOWN_WAITING_NHT:
-                       type = BMP_PEERDOWN_LOCAL_FSM;
-                       stream_putw(s, BGP_FSM_TcpConnectionFails);
-                       break;
-               /*
-                * TODO: Map remaining PEER_DOWN_* reasons to RFC event codes.
-                * TODO: Implement BMP_PEERDOWN_LOCAL_NOTIFY.
-                *
-                * See RFC7854 ss. 4.9
-                */
-               default:
-                       type = BMP_PEERDOWN_LOCAL_FSM;
-                       stream_putw(s, BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE);
-                       break;
+               if (is_locrib) {
+                       type = BMP_PEERDOWN_LOCAL_TLV;
+               } else {
+                       switch (peer->last_reset) {
+                       case PEER_DOWN_NOTIFY_RECEIVED:
+                               type = BMP_PEERDOWN_REMOTE_NOTIFY;
+                               bmp_notify_put(s, &peer->notify);
+                               break;
+                       case PEER_DOWN_CLOSE_SESSION:
+                               type = BMP_PEERDOWN_REMOTE_CLOSE;
+                               break;
+                       case PEER_DOWN_WAITING_NHT:
+                               type = BMP_PEERDOWN_LOCAL_FSM;
+                               stream_putw(s, BGP_FSM_TcpConnectionFails);
+                               break;
+                       /*
+                        * TODO: Map remaining PEER_DOWN_* reasons to RFC event
+                        * codes.
+                        * TODO: Implement BMP_PEERDOWN_LOCAL_NOTIFY.
+                        *
+                        * See RFC7854 ss. 4.9
+                        */
+                       default:
+                               type = BMP_PEERDOWN_LOCAL_FSM;
+                               stream_putw(s, BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE);
+                               break;
+                       }
                }
                stream_putc_at(s, type_pos, type);
        }
 
+       if (is_locrib)
+               bmp_put_vrftablename_info_tlv(s, peer->bgp);
+
        len = stream_get_endp(s);
        stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */
        return s;
@@ -558,6 +611,25 @@ static int bmp_send_peerup(struct bmp *bmp)
        return 0;
 }
 
+static int bmp_send_peerup_vrf(struct bmp *bmp)
+{
+       struct bmp_bgp *bmpbgp = bmp->targets->bmpbgp;
+       struct stream *s;
+
+       /* send unconditionally because state may has been set before the
+        * session was up. and in this case the peer up has not been sent.
+        */
+       bmp_bgp_update_vrf_status(bmpbgp, vrf_state_unknown);
+
+       s = bmp_peerstate(bmpbgp->bgp->peer_self, bmpbgp->vrf_state == vrf_state_down);
+
+       pullwr_write_stream(bmp->pullwr, s);
+       stream_free(s);
+
+       return 0;
+}
+
+/* send a stream to all bmp sessions configured in a bgp instance */
 /* XXX: kludge - filling the pullwr's buffer */
 static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s)
 {
@@ -570,6 +642,16 @@ static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s)
        stream_free(s);
 }
 
+static void bmp_send_all_safe(struct bmp_bgp *bmpbgp, struct stream *s)
+{
+       if (!bmpbgp) {
+               stream_free(s);
+               return;
+       }
+
+       bmp_send_all(bmpbgp, s);
+}
+
 /*
  * Route Mirroring
  */
@@ -816,7 +898,7 @@ static int bmp_peer_status_changed(struct peer *peer)
                }
        }
 
-       bmp_send_all(bmpbgp, bmp_peerstate(peer, false));
+       bmp_send_all_safe(bmpbgp, bmp_peerstate(peer, false));
        return 0;
 }
 
@@ -838,7 +920,7 @@ static int bmp_peer_backward(struct peer *peer)
                bbpeer->open_rx_len = 0;
        }
 
-       bmp_send_all(bmpbgp, bmp_peerstate(peer, true));
+       bmp_send_all_safe(bmpbgp, bmp_peerstate(peer, true));
        return 0;
 }
 
@@ -1476,6 +1558,7 @@ static void bmp_wrfill(struct bmp *bmp, struct pullwr *pullwr)
 {
        switch(bmp->state) {
        case BMP_PeerUp:
+               bmp_send_peerup_vrf(bmp);
                bmp_send_peerup(bmp);
                bmp->state = BMP_Run;
                break;
@@ -1839,6 +1922,7 @@ static struct bmp_bgp *bmp_bgp_get(struct bgp *bgp)
 
        bmpbgp = XCALLOC(MTYPE_BMP, sizeof(*bmpbgp));
        bmpbgp->bgp = bgp;
+       bmpbgp->vrf_state = vrf_state_unknown;
        bmpbgp->mirror_qsizelimit = ~0UL;
        bmp_mirrorq_init(&bmpbgp->mirrorq);
        bmp_bgph_add(&bmp_bgph, bmpbgp);
@@ -1873,6 +1957,79 @@ static int bmp_bgp_del(struct bgp *bgp)
        return 0;
 }
 
+static void bmp_bgp_peer_vrf(struct bmp_bgp_peer *bbpeer, struct bgp *bgp)
+{
+       struct peer *peer = bgp->peer_self;
+       uint16_t send_holdtime;
+       as_t local_as;
+
+       if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+               send_holdtime = peer->holdtime;
+       else
+               send_holdtime = peer->bgp->default_holdtime;
+
+       /* local-as Change */
+       if (peer->change_local_as)
+               local_as = peer->change_local_as;
+       else
+               local_as = peer->local_as;
+
+       struct stream *s = bgp_open_make(peer, send_holdtime, local_as);
+       size_t open_len = stream_get_endp(s);
+
+       bbpeer->open_rx_len = open_len;
+       bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, open_len);
+       memcpy(bbpeer->open_rx, s->data, open_len);
+
+       bbpeer->open_tx_len = open_len;
+       bbpeer->open_tx = bbpeer->open_rx;
+}
+
+/* update the vrf status of the bmpbgp struct for vrf peer up/down
+ *
+ * if force is unknown, use zebra vrf state
+ *
+ * returns true if state has changed
+ */
+bool bmp_bgp_update_vrf_status(struct bmp_bgp *bmpbgp, enum bmp_vrf_state force)
+{
+       enum bmp_vrf_state old_state;
+       struct bmp_bgp_peer *bbpeer;
+       struct peer *peer;
+       struct vrf *vrf;
+       struct bgp *bgp;
+       bool changed;
+
+       if (!bmpbgp || !bmpbgp->bgp)
+               return false;
+
+       bgp = bmpbgp->bgp;
+       old_state = bmpbgp->vrf_state;
+
+       vrf = bgp_vrf_lookup_by_instance_type(bgp);
+       bmpbgp->vrf_state = force != vrf_state_unknown ? force
+                           : vrf_is_enabled(vrf)      ? vrf_state_up
+                                                      : vrf_state_down;
+
+       changed = old_state != bmpbgp->vrf_state;
+       if (changed) {
+               peer = bmpbgp->bgp->peer_self;
+               if (bmpbgp->vrf_state == vrf_state_up) {
+                       bbpeer = bmp_bgp_peer_get(peer);
+                       bmp_bgp_peer_vrf(bbpeer, bgp);
+               } else {
+                       bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
+                       if (bbpeer) {
+                               XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
+                               bmp_peerh_del(&bmp_peerh, bbpeer);
+                               XFREE(MTYPE_BMP_PEER, bbpeer);
+                       }
+               }
+       }
+
+       return changed;
+}
+
 static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid)
 {
        struct bmp_bgp_peer dummy = { .peerid = peerid };
@@ -2953,6 +3110,44 @@ static int bgp_bmp_early_fini(void)
        return 0;
 }
 
+/* called when a bgp instance goes up/down, implying that the underlying VRF
+ * has been created or deleted in zebra
+ */
+static int bmp_vrf_state_changed(struct bgp *bgp)
+{
+       struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
+
+       if (!bmp_bgp_update_vrf_status(bmpbgp, vrf_state_unknown))
+               return 1;
+
+       bmp_send_all_safe(bmpbgp,
+                         bmp_peerstate(bgp->peer_self, bmpbgp->vrf_state == vrf_state_down));
+
+       return 0;
+}
+
+/* called when an interface goes up/down in a vrf, this may signal that the
+ * VRF changed state and is how bgp_snmp detects vrf state changes
+ */
+static int bmp_vrf_itf_state_changed(struct bgp *bgp, struct interface *itf)
+{
+       struct bmp_bgp *bmpbgp;
+       enum bmp_vrf_state new_state;
+
+       /* if the update is not about the vrf device double-check
+        * the zebra status of the vrf
+        */
+       if (!itf || !if_is_vrf(itf))
+               return bmp_vrf_state_changed(bgp);
+
+       bmpbgp = bmp_bgp_find(bgp);
+       new_state = if_is_up(itf) ? vrf_state_up : vrf_state_down;
+       if (bmp_bgp_update_vrf_status(bmpbgp, new_state))
+               bmp_send_all(bmpbgp, bmp_peerstate(bgp->peer_self, new_state == vrf_state_down));
+
+       return 0;
+}
+
 static int bgp_bmp_module_init(void)
 {
        hook_register(bgp_packet_dump, bmp_mirror_packet);
@@ -2965,6 +3160,8 @@ static int bgp_bmp_module_init(void)
        hook_register(frr_late_init, bgp_bmp_init);
        hook_register(bgp_route_update, bmp_route_update);
        hook_register(frr_early_fini, bgp_bmp_early_fini);
+       hook_register(bgp_instance_state, bmp_vrf_state_changed);
+       hook_register(bgp_vrf_status_changed, bmp_vrf_itf_state_changed);
        return 0;
 }
 
index 33247c402504d5c1c96976607c213145e9b2212b..d45a4278f69ed2657763eee588d09e88d4f3ba01 100644 (file)
@@ -268,10 +268,19 @@ PREDECL_HASH(bmp_bgph);
 
 #define BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE 0x00
 
+enum bmp_vrf_state {
+       vrf_state_down = -1,
+       vrf_state_unknown = 0,
+       vrf_state_up = 1,
+};
+
 struct bmp_bgp {
        struct bmp_bgph_item bbi;
 
        struct bgp *bgp;
+
+       enum bmp_vrf_state vrf_state;
+
        struct bmp_targets_head targets;
 
        struct bmp_mirrorq_head mirrorq;
@@ -280,12 +289,17 @@ struct bmp_bgp {
        size_t mirror_qsizelimit;
 };
 
+extern bool bmp_bgp_update_vrf_status(struct bmp_bgp *bmpbgp, enum bmp_vrf_state force);
+
 enum {
-       BMP_PEERDOWN_LOCAL_NOTIFY       = 1,
-       BMP_PEERDOWN_LOCAL_FSM          = 2,
-       BMP_PEERDOWN_REMOTE_NOTIFY      = 3,
-       BMP_PEERDOWN_REMOTE_CLOSE       = 4,
-       BMP_PEERDOWN_ENDMONITOR         = 5,
+       /* RFC7854 - 10.8 */
+       BMP_PEERDOWN_LOCAL_NOTIFY = 1,
+       BMP_PEERDOWN_LOCAL_FSM = 2,
+       BMP_PEERDOWN_REMOTE_NOTIFY = 3,
+       BMP_PEERDOWN_REMOTE_CLOSE = 4,
+       BMP_PEERDOWN_ENDMONITOR = 5,
+       /* RFC9069 - 8.4 */
+       BMP_PEERDOWN_LOCAL_TLV = 6,
 };
 
 enum {
index 646ab1d95f62fb49a2e7304478d356d0ed873186..6b116db1075da2cc4355d58ac2ef1de096dca93b 100644 (file)
@@ -641,31 +641,11 @@ void bgp_keepalive_send(struct peer *peer)
        bgp_writes_on(peer->connection);
 }
 
-/*
- * Creates a BGP Open packet and appends it to the peer's output queue.
- * Sets capabilities as necessary.
- */
-void bgp_open_send(struct peer_connection *connection)
+struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as)
 {
-       struct stream *s;
-       uint16_t send_holdtime;
-       as_t local_as;
-       struct peer *peer = connection->peer;
+       struct stream *s = stream_new(BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE);
        bool ext_opt_params = false;
 
-       if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
-               send_holdtime = peer->holdtime;
-       else
-               send_holdtime = peer->bgp->default_holdtime;
-
-       /* local-as Change */
-       if (peer->change_local_as)
-               local_as = peer->change_local_as;
-       else
-               local_as = peer->local_as;
-
-       s = stream_new(BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE);
-
        /* Make open packet. */
        bgp_packet_set_marker(s, BGP_MSG_OPEN);
 
@@ -704,6 +684,33 @@ void bgp_open_send(struct peer_connection *connection)
                           ext_opt_params ? " (Extended)" : "", BGP_VERSION_4,
                           local_as, send_holdtime, &peer->local_id);
 
+       return s;
+}
+
+/*
+ * Creates a BGP Open packet and appends it to the peer's output queue.
+ * Sets capabilities as necessary.
+ */
+void bgp_open_send(struct peer_connection *connection)
+{
+       struct stream *s;
+       uint16_t send_holdtime;
+       as_t local_as;
+       struct peer *peer = connection->peer;
+
+       if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+               send_holdtime = peer->holdtime;
+       else
+               send_holdtime = peer->bgp->default_holdtime;
+
+       /* local-as Change */
+       if (peer->change_local_as)
+               local_as = peer->change_local_as;
+       else
+               local_as = peer->local_as;
+
+       s = bgp_open_make(peer, send_holdtime, local_as);
+
        /* Dump packet if debug option is set. */
        /* bgp_packet_dump (s); */
        hook_call(bgp_packet_send, peer, BGP_MSG_OPEN, stream_get_endp(s), s);
index b67acf205593032ab40f469de7bba67a8e658752..c266b17266ec9c89625d550612d9bb9dd52ea488 100644 (file)
@@ -43,6 +43,7 @@ DECLARE_HOOK(bgp_packet_send,
 
 /* Packet send and receive function prototypes. */
 extern void bgp_keepalive_send(struct peer *peer);
+extern struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as);
 extern void bgp_open_send(struct peer_connection *connection);
 extern void bgp_notify_send(struct peer_connection *connection, uint8_t code,
                            uint8_t sub_code);
index 80b1ae39d4d1a3fdf96877477bbea735a3e4a1cf..a186243ffcfc8e12f0a33cf2307f026f4b5203b2 100644 (file)
@@ -85,6 +85,7 @@ DEFINE_QOBJ_TYPE(bgp_master);
 DEFINE_QOBJ_TYPE(bgp);
 DEFINE_QOBJ_TYPE(peer);
 DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp));
+DEFINE_HOOK(bgp_instance_state, (struct bgp *bgp), (bgp));
 
 /* BGP process wide configuration.  */
 static struct bgp_master bgp_master;
@@ -3929,6 +3930,9 @@ void bgp_instance_up(struct bgp *bgp)
        struct peer *peer;
        struct listnode *node, *next;
 
+       /* notify BMP of instance state changed */
+       hook_call(bgp_instance_state, bgp);
+
        bgp_set_redist_vrf_bitmaps(bgp, true);
 
        /* Register with zebra. */
@@ -3957,6 +3961,9 @@ void bgp_instance_down(struct bgp *bgp)
        /* Cleanup evpn instance state */
        bgp_evpn_instance_down(bgp);
 
+       /* notify BMP of instance state changed */
+       hook_call(bgp_instance_state, bgp);
+
        /* Stop timers. */
        if (bgp->t_rmap_def_originate_eval)
                EVENT_OFF(bgp->t_rmap_def_originate_eval);
index 3c3655f0a5adca250d6390999e7ce3a7d30ee1e4..852efdf19d3164480f6b0088d3afb280d68d4413 100644 (file)
@@ -880,6 +880,7 @@ DECLARE_HOOK(bgp_snmp_traps_config_write, (struct vty *vty), (vty));
 DECLARE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp));
 DECLARE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled),
             (vrf, enabled));
+DECLARE_HOOK(bgp_instance_state, (struct bgp *bgp), (bgp));
 
 /* Thread callback information */
 struct afi_safi_info {