]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: Add Long-lived Graceful Restart capability (restarter) 9884/head
authorDonatas Abraitis <donatas.abraitis@gmail.com>
Mon, 25 Oct 2021 14:23:02 +0000 (17:23 +0300)
committerDonatas Abraitis <donatas.abraitis@gmail.com>
Sun, 31 Oct 2021 18:25:42 +0000 (20:25 +0200)
Restart Router mode.

FRRouting (Restarter):
```
 bgp long-lived-graceful-restart stale-time 10
 bgp graceful-restart restart-time 1
```

Tested with GoBGP (Helper):
```
    long-lived-graceful-restart: advertised and received
        Local:
    ipv4-unicast, restart time 100000 sec
        Remote:
    ipv4-unicast, restart time 10 sec, forward flag set
```

Logs:

```
{"Key":"192.168.10.123","Reason":"graceful-restart","State":"BGP_FSM_ESTABLISHED","Topic":"Peer","level":"info","msg":"Peer Down","time":"2021-10-25T17:48:36+03:00"}
{"Key":"192.168.10.123","State":"BGP_FSM_IDLE","Topic":"Peer","level":"warning","msg":"graceful restart timer expired","time":"2021-10-25T17:48:37+03:00"}
{"Family":65537,"Key":"192.168.10.123","Topic":"Peer","level":"info","msg":"start LLGR restart timer (10 sec) for ipv4-unicast","time":"2021-10-25T17:48:37+03:00"}
{"Family":65537,"Key":"192.168.10.123","Topic":"Peer","level":"info","msg":"LLGR restart timer (10 sec) for ipv4-unicast expired","time":"2021-10-25T17:48:47+03:00"}

% ./gobgp global rib
   Network              Next Hop             AS_PATH              Age        Attrs
S*>10.0.2.0/24          192.168.10.123       174                  00:12:08   [{Origin: ?} {Med: 0} {Communities: llgr-stale} {Extcomms: [174:1282304808]}]
```

Helper mode will be added with upcoming PRs.

Signed-off-by: Donatas Abraitis <donatas.abraitis@gmail.com>
bgpd/bgp_open.c
bgpd/bgp_open.h
bgpd/bgp_vty.c
bgpd/bgpd.c
bgpd/bgpd.h
doc/user/bgp.rst

index ca8b1e398bdd022960e89622949d99becde8f7a6..e15690835aafed5c956262359ff0db5cf939ab17 100644 (file)
@@ -572,6 +572,46 @@ static int bgp_capability_restart(struct peer *peer,
        return 0;
 }
 
+static int bgp_capability_llgr(struct peer *peer,
+                              struct capability_header *caphdr)
+{
+       struct stream *s = BGP_INPUT(peer);
+       size_t end = stream_get_getp(s) + caphdr->length;
+
+       SET_FLAG(peer->cap, PEER_CAP_LLGR_RCV);
+
+       while (stream_get_getp(s) + 4 <= end) {
+               afi_t afi;
+               safi_t safi;
+               iana_afi_t pkt_afi = stream_getw(s);
+               iana_safi_t pkt_safi = stream_getc(s);
+               uint8_t flags = stream_getc(s);
+               uint32_t stale_time = stream_get3(s);
+
+               if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+                       if (bgp_debug_neighbor_events(peer))
+                               zlog_debug(
+                                       "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Long-lived Graceful Restart capability for this AFI/SAFI",
+                                       peer->host, iana_afi2str(pkt_afi),
+                                       iana_safi2str(pkt_safi));
+               } else if (!peer->afc[afi][safi]
+                          || !CHECK_FLAG(peer->af_cap[afi][safi],
+                                         PEER_CAP_RESTART_AF_RCV)) {
+                       if (bgp_debug_neighbor_events(peer))
+                               zlog_debug(
+                                       "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Long-lived Graceful Restart capability",
+                                       peer->host, iana_afi2str(pkt_afi),
+                                       iana_safi2str(pkt_safi));
+               } else {
+                       peer->llgr[afi][safi].flags = flags;
+                       peer->llgr[afi][safi].stale_time = stale_time;
+                       SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_LLGR_AF_RCV);
+               }
+       }
+
+       return 0;
+}
+
 /* Unlike other capability parsing routines, this one returns 0 on error */
 static as_t bgp_capability_as4(struct peer *peer, struct capability_header *hdr)
 {
@@ -954,6 +994,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
                case CAPABILITY_CODE_RESTART:
                        ret = bgp_capability_restart(peer, &caphdr);
                        break;
+               case CAPABILITY_CODE_LLGR:
+                       ret = bgp_capability_llgr(peer, &caphdr);
+                       break;
                case CAPABILITY_CODE_DYNAMIC:
                case CAPABILITY_CODE_DYNAMIC_OLD:
                        SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV);
@@ -1404,6 +1447,53 @@ static void bgp_peer_send_gr_capability(struct stream *s, struct peer *peer,
        stream_putc_at(s, capp, len);
 }
 
+static void bgp_peer_send_llgr_capability(struct stream *s, struct peer *peer,
+                                         unsigned long cp)
+{
+       int len;
+       iana_afi_t pkt_afi;
+       afi_t afi;
+       safi_t safi;
+       iana_safi_t pkt_safi;
+       unsigned long capp = 0;
+       unsigned long rcapp = 0;
+
+       if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV))
+               return;
+
+       SET_FLAG(peer->cap, PEER_CAP_LLGR_ADV);
+
+       stream_putc(s, BGP_OPEN_OPT_CAP);
+       capp = stream_get_endp(s); /* Set Capability Len Pointer */
+       stream_putc(s, 0);       /* Capability Length */
+       stream_putc(s, CAPABILITY_CODE_LLGR);
+
+       rcapp = stream_get_endp(s);
+       stream_putc(s, 0);
+
+       FOREACH_AFI_SAFI (afi, safi) {
+               if (!peer->afc[afi][safi])
+                       continue;
+
+               bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+               stream_putw(s, pkt_afi);
+               stream_putc(s, pkt_safi);
+               stream_putc(s, LLGR_F_BIT);
+               stream_put3(s, peer->bgp->llgr_stale_time);
+
+               SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_LLGR_AF_ADV);
+       }
+
+       /* Total Long-lived Graceful Restart capability Len. */
+       len = stream_get_endp(s) - rcapp - 1;
+       stream_putc_at(s, rcapp, len);
+
+       /* Total Capability Len. */
+       len = stream_get_endp(s) - capp - 1;
+       stream_putc_at(s, capp, len);
+}
+
 /* Fill in capability open option to the packet. */
 void bgp_open_capability(struct stream *s, struct peer *peer)
 {
@@ -1632,6 +1722,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer)
        }
 
        bgp_peer_send_gr_capability(s, peer, cp);
+       bgp_peer_send_llgr_capability(s, peer, cp);
 
        /* Total Opt Parm Len. */
        len = stream_get_endp(s) - cp - 1;
index bc6eedac850a9e34d5198989cb36096fcb5f2960..0d616926a2e0702e530e4ad5f0ef4f962827cd56 100644 (file)
@@ -50,6 +50,7 @@ struct graceful_restart_af {
 #define CAPABILITY_CODE_DYNAMIC        67 /* Dynamic Capability */
 #define CAPABILITY_CODE_ADDPATH        69 /* Addpath Capability */
 #define CAPABILITY_CODE_ENHANCED_RR    70 /* Enhanced Route Refresh capability */
+#define CAPABILITY_CODE_LLGR           71 /* Long-lived Graceful Restart */
 #define CAPABILITY_CODE_FQDN           73 /* Advertise hostname capability */
 #define CAPABILITY_CODE_ENHE            5 /* Extended Next Hop Encoding */
 #define CAPABILITY_CODE_REFRESH_OLD   128 /* Route Refresh Capability(cisco) */
@@ -66,6 +67,7 @@ struct graceful_restart_af {
 #define CAPABILITY_CODE_ENHE_LEN        6 /* NRLI AFI = 2, SAFI = 2, Nexthop AFI = 2 */
 #define CAPABILITY_CODE_MIN_FQDN_LEN    2
 #define CAPABILITY_CODE_ENHANCED_LEN    0
+#define CAPABILITY_CODE_LLGR_LEN        0
 #define CAPABILITY_CODE_ORF_LEN         5
 #define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
 
@@ -88,6 +90,9 @@ struct graceful_restart_af {
 #define RESTART_R_BIT              0x8000
 #define RESTART_F_BIT              0x80
 
+/* Long-lived Graceful Restart */
+#define LLGR_F_BIT 0x80
+
 extern int bgp_open_option_parse(struct peer *, uint8_t, int *);
 extern void bgp_open_capability(struct stream *, struct peer *);
 extern void bgp_capability_vty_out(struct vty *vty, struct peer *peer,
index 772e20dc852a6e81faa59284375d29edf8b808a2..65806bb5f46f9417bb91e8d73f9f243735dde9d2 100644 (file)
@@ -3168,6 +3168,36 @@ DEFUN (no_bgp_graceful_restart_rib_stale_time,
        return CMD_SUCCESS;
 }
 
+DEFUN(bgp_llgr_stalepath_time, bgp_llgr_stalepath_time_cmd,
+      "bgp long-lived-graceful-restart stale-time (0-4294967295)", BGP_STR
+      "Enable Long-lived Graceful Restart\n"
+      "Specifies maximum time to wait before purging long-lived stale routes\n"
+      "Stale time value (seconds)\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+       uint32_t llgr_stale_time;
+
+       llgr_stale_time = strtoul(argv[3]->arg, NULL, 10);
+       bgp->llgr_stale_time = llgr_stale_time;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_llgr_stalepath_time, no_bgp_llgr_stalepath_time_cmd,
+      "no bgp long-lived-graceful-restart stale-time [(0-4294967295)]",
+      NO_STR BGP_STR
+      "Enable Long-lived Graceful Restart\n"
+      "Specifies maximum time to wait before purging long-lived stale routes\n"
+      "Stale time value (seconds)\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+       bgp->llgr_stale_time = BGP_DEFAULT_LLGR_STALE_TIME;
+
+       return CMD_SUCCESS;
+}
+
 static inline void bgp_initiate_graceful_shut_unshut(struct vty *vty,
                                                     struct bgp *bgp)
 {
@@ -12838,6 +12868,61 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
                                        }
                                }
 
+                               /* Long-lived Graceful Restart */
+                               if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_RCV)
+                                   || CHECK_FLAG(p->cap, PEER_CAP_LLGR_ADV)) {
+                                       json_object *json_llgr = NULL;
+                                       const char *afi_safi_str;
+
+                                       if (CHECK_FLAG(p->cap,
+                                                      PEER_CAP_LLGR_ADV)
+                                           && CHECK_FLAG(p->cap,
+                                                         PEER_CAP_LLGR_RCV))
+                                               json_object_string_add(
+                                                       json_cap,
+                                                       "longLivedGracefulRestart",
+                                                       "advertisedAndReceived");
+                                       else if (CHECK_FLAG(p->cap,
+                                                           PEER_CAP_LLGR_ADV))
+                                               json_object_string_add(
+                                                       json_cap,
+                                                       "longLivedGracefulRestart",
+                                                       "advertised");
+                                       else if (CHECK_FLAG(p->cap,
+                                                           PEER_CAP_LLGR_RCV))
+                                               json_object_string_add(
+                                                       json_cap,
+                                                       "longLivedGracefulRestart",
+                                                       "received");
+
+                                       if (CHECK_FLAG(p->cap,
+                                                      PEER_CAP_LLGR_RCV)) {
+                                               json_llgr =
+                                                       json_object_new_object();
+
+                                               FOREACH_AFI_SAFI (afi, safi) {
+                                                       if (CHECK_FLAG(
+                                                                   p->af_cap
+                                                                           [afi]
+                                                                           [safi],
+                                                                   PEER_CAP_ENHE_AF_RCV)) {
+                                                               afi_safi_str = get_afi_safi_str(
+                                                                       afi,
+                                                                       safi,
+                                                                       true);
+                                                               json_object_string_add(
+                                                                       json_llgr,
+                                                                       afi_safi_str,
+                                                                       "received");
+                                                       }
+                                               }
+                                               json_object_object_add(
+                                                       json_cap,
+                                                       "longLivedGracefulRestartByPeer",
+                                                       json_llgr);
+                                       }
+                               }
+
                                /* Route Refresh */
                                if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV)
                                    || CHECK_FLAG(p->cap,
@@ -13278,6 +13363,43 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
                                        }
                                }
 
+                               /* Long-lived Graceful Restart */
+                               if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_RCV)
+                                   || CHECK_FLAG(p->cap, PEER_CAP_LLGR_ADV)) {
+                                       vty_out(vty,
+                                               "    Long-lived Graceful Restart:");
+                                       if (CHECK_FLAG(p->cap,
+                                                      PEER_CAP_LLGR_ADV))
+                                               vty_out(vty, " advertised");
+                                       if (CHECK_FLAG(p->cap,
+                                                      PEER_CAP_LLGR_RCV))
+                                               vty_out(vty, " %sreceived",
+                                                       CHECK_FLAG(
+                                                               p->cap,
+                                                               PEER_CAP_LLGR_ADV)
+                                                               ? "and "
+                                                               : "");
+                                       vty_out(vty, "\n");
+
+                                       if (CHECK_FLAG(p->cap,
+                                                      PEER_CAP_LLGR_RCV)) {
+                                               vty_out(vty,
+                                                       "      Address families by peer:\n");
+                                               FOREACH_AFI_SAFI (afi, safi)
+                                                       if (CHECK_FLAG(
+                                                                   p->af_cap
+                                                                           [afi]
+                                                                           [safi],
+                                                                   PEER_CAP_LLGR_AF_RCV))
+                                                               vty_out(vty,
+                                                                       "           %s\n",
+                                                                       get_afi_safi_str(
+                                                                               afi,
+                                                                               safi,
+                                                                               false));
+                                       }
+                               }
+
                                /* Route Refresh */
                                if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV)
                                    || CHECK_FLAG(p->cap,
@@ -17253,6 +17375,12 @@ int bgp_config_write(struct vty *vty)
                        if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN))
                                vty_out(vty, " bgp graceful-shutdown\n");
 
+               /* Long-lived Graceful Restart */
+               if (bgp->llgr_stale_time != BGP_DEFAULT_LLGR_STALE_TIME)
+                       vty_out(vty,
+                               " bgp long-lived-graceful-restart stale-time %u\n",
+                               bgp->llgr_stale_time);
+
                /* BGP graceful-restart. */
                if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME)
                        vty_out(vty,
@@ -17830,6 +17958,10 @@ void bgp_vty_init(void)
        install_element(BGP_NODE, &bgp_graceful_shutdown_cmd);
        install_element(BGP_NODE, &no_bgp_graceful_shutdown_cmd);
 
+       /* "bgp long-lived-graceful-restart" commands */
+       install_element(BGP_NODE, &bgp_llgr_stalepath_time_cmd);
+       install_element(BGP_NODE, &no_bgp_llgr_stalepath_time_cmd);
+
        /* "bgp fast-external-failover" commands */
        install_element(BGP_NODE, &bgp_fast_external_failover_cmd);
        install_element(BGP_NODE, &no_bgp_fast_external_failover_cmd);
index c5a5e49a482b3a6ec0609e22a021036a53b42d9e..ff2c0c8bdaf42011662e4ed1bed8bb6eb9438342 100644 (file)
@@ -3169,6 +3169,7 @@ static struct bgp *bgp_create(as_t *as, const char *name,
        bgp_addpath_init_bgp_data(&bgp->tx_addpath);
        bgp->fast_convergence = false;
        bgp->as = *as;
+       bgp->llgr_stale_time = BGP_DEFAULT_LLGR_STALE_TIME;
 
 #ifdef ENABLE_BGP_VNC
        if (inst_type != BGP_INSTANCE_TYPE_VRF) {
index 5e1eacbb9e627ab13ba6581aa09d419919d13ae1..e60acecfaee5570100f0655a3a464655576c7287 100644 (file)
@@ -613,6 +613,9 @@ struct bgp {
        struct graceful_restart_info gr_info[AFI_MAX][SAFI_MAX];
        uint32_t rib_stale_time;
 
+       /* BGP Long-lived Graceful Restart */
+       uint32_t llgr_stale_time;
+
 #define BGP_ROUTE_SELECT_DELAY 1
 #define BGP_MAX_BEST_ROUTE_SELECT 10000
        /* Maximum-paths configuration */
@@ -1052,6 +1055,11 @@ enum bgp_fsm_status {
 
 #define PEER_HOSTNAME(peer) ((peer)->host ? (peer)->host : "(unknown peer)")
 
+struct llgr_info {
+       uint32_t stale_time;
+       uint8_t flags;
+};
+
 /* BGP neighbor structure. */
 struct peer {
        /* BGP structure.  */
@@ -1182,6 +1190,8 @@ struct peer {
 #define PEER_CAP_ENHANCED_RR_RCV (1U << 18) /* enhanced rr received */
 #define PEER_CAP_EXTENDED_MESSAGE_ADV (1U << 19)
 #define PEER_CAP_EXTENDED_MESSAGE_RCV (1U << 20)
+#define PEER_CAP_LLGR_ADV (1U << 21)
+#define PEER_CAP_LLGR_RCV (1U << 22)
 
        /* Capability flags (reset in bgp_stop) */
        uint32_t af_cap[AFI_MAX][SAFI_MAX];
@@ -1200,6 +1210,8 @@ struct peer {
 #define PEER_CAP_ENHE_AF_ADV                (1U << 12) /* Extended nexthopi afi/safi advertised */
 #define PEER_CAP_ENHE_AF_RCV                (1U << 13) /* Extended nexthop afi/safi received */
 #define PEER_CAP_ENHE_AF_NEGO               (1U << 14) /* Extended nexthop afi/safi negotiated */
+#define PEER_CAP_LLGR_AF_ADV                (1U << 15)
+#define PEER_CAP_LLGR_AF_RCV                (1U << 16)
 
        /* Global configuration flags. */
        /*
@@ -1657,6 +1669,9 @@ struct peer {
        /* set TCP max segment size */
        uint32_t tcp_mss;
 
+       /* Long-lived Graceful Restart */
+       struct llgr_info llgr[AFI_MAX][SAFI_MAX];
+
        QOBJ_FIELDS;
 };
 DECLARE_QOBJ_TYPE(peer);
@@ -1869,6 +1884,9 @@ struct bgp_nlri {
 #define BGP_DEFAULT_RIB_STALE_TIME             500
 #define BGP_DEFAULT_UPDATE_ADVERTISEMENT_TIME  1
 
+/* BGP Long-lived Graceful Restart */
+#define BGP_DEFAULT_LLGR_STALE_TIME 360
+
 /* BGP uptime string length.  */
 #define BGP_UPTIME_LEN 25
 
index 1793ae3d27c2f00fb33da6f2b48f4757abbfa7d2..38d215e7654881038552ae37ba3c5fe7592a5de5 100644 (file)
@@ -989,6 +989,18 @@ BGP GR Peer Mode Commands
    at the peer level.
 
 
+Long-lived Graceful Restart
+---------------------------
+
+Currently, only restarter mode is supported. This capability is advertised only
+if graceful restart capability is negotiated.
+
+.. clicmd:: bgp long-lived-graceful-restart stale-time (0-4294967295)
+
+   Specifies the maximum time to wait before purging long-lived stale routes for
+   helper routers.
+
+
 .. _bgp-shutdown:
 
 Administrative Shutdown