]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: implement draft-ietf-grow-bgp-gshut-10
authorDaniel Walton <dwalton@cumulusnetworks.com>
Fri, 25 Aug 2017 18:27:49 +0000 (18:27 +0000)
committerDaniel Walton <dwalton@cumulusnetworks.com>
Fri, 25 Aug 2017 18:27:49 +0000 (18:27 +0000)
Signed-off-by: Daniel Walton <dwalton@cumulusnetworks.com>
bgpd/bgp_attr.c
bgpd/bgp_clist.c
bgpd/bgp_community.c
bgpd/bgp_community.h
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/bgp_routemap.c
bgpd/bgp_updgrp_adv.c
bgpd/bgp_vty.c
bgpd/bgpd.c
bgpd/bgpd.h

index b03b408f7d63bc89cf2a16fc4a4e639af6d3ba1a..0487cb4e3301cbee64d578cf2e69eaebb59da084 100644 (file)
@@ -848,10 +848,24 @@ struct attr *bgp_attr_aggregate_intern(struct bgp *bgp, u_char origin,
        attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
 
        if (community) {
+               u_int32_t gshut = COMMUNITY_GSHUT;
+
+               /* If we are not shutting down ourselves and we are
+                * aggregating a route that contains the GSHUT community we
+                * need to remove that community when creating the aggregate */
+               if (!bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN) &&
+                   community_include(community, gshut)) {
+                       community_del_val(community, &gshut);
+               }
+
                attr.community = community;
                attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES);
        }
 
+       if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+               bgp_attr_add_gshut_community(&attr);
+       }
+
        attr.label_index = BGP_INVALID_LABEL_INDEX;
        attr.label = MPLS_INVALID_LABEL;
        attr.weight = BGP_ATTR_DEFAULT_WEIGHT;
@@ -1400,7 +1414,7 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args)
 
        attr->local_pref = stream_getl(peer->ibuf);
 
-       /* Set atomic aggregate flag. */
+       /* Set the local-pref flag. */
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
 
        return BGP_ATTR_PARSE_PROCEED;
index 13587b35993ad2cc00370243a057ba6b2509bb8c..94ea35fa0ac2a41c02131f93b33880547fc86bb5 100644 (file)
@@ -355,6 +355,9 @@ static char *community_str_get(struct community *com, int i)
        case COMMUNITY_LOCAL_AS:
                len = strlen(" local-AS");
                break;
+       case COMMUNITY_GSHUT:
+               len = strlen(" graceful-shutdown");
+               break;
        default:
                len = strlen(" 65536:65535");
                break;
@@ -380,6 +383,10 @@ static char *community_str_get(struct community *com, int i)
                strcpy(pnt, "local-AS");
                pnt += strlen("local-AS");
                break;
+       case COMMUNITY_GSHUT:
+               strcpy(pnt, "graceful-shutdown");
+               pnt += strlen("graceful-shutdown");
+               break;
        default:
                as = (comval >> 16) & 0xFFFF;
                val = comval & 0xFFFF;
index 389d723e0429d2b7e4f457c41a31596106a31a83..7e8411b6a02c7fff1264209753aa53c4d88a8513 100644 (file)
@@ -191,6 +191,7 @@ struct community *community_uniq_sort(struct community *com)
    0xFFFFFF01      "no-export"
    0xFFFFFF02      "no-advertise"
    0xFFFFFF03      "local-AS"
+   0xFFFF0000      "graceful-shutdown"
 
    For other values, "AS:VAL" format is used.  */
 static void set_community_string(struct community *com)
@@ -244,6 +245,9 @@ static void set_community_string(struct community *com)
                case COMMUNITY_LOCAL_AS:
                        len += strlen(" local-AS");
                        break;
+               case COMMUNITY_GSHUT:
+                       len += strlen(" graceful-shutdown");
+                       break;
                default:
                        len += strlen(" 65536:65535");
                        break;
@@ -289,6 +293,12 @@ static void set_community_string(struct community *com)
                        json_string = json_object_new_string("localAs");
                        json_object_array_add(json_community_list, json_string);
                        break;
+               case COMMUNITY_GSHUT:
+                       strcpy(pnt, "graceful-shutdown");
+                       pnt += strlen("graceful-shutdown");
+                       json_string = json_object_new_string("gracefulShutdown");
+                       json_object_array_add(json_community_list, json_string);
+                       break;
                default:
                        as = (comval >> 16) & 0xFFFF;
                        val = comval & 0xFFFF;
@@ -480,6 +490,7 @@ enum community_token {
        community_token_no_export,
        community_token_no_advertise,
        community_token_local_as,
+       community_token_gshut,
        community_token_unknown
 };
 
@@ -523,6 +534,12 @@ community_gettoken(const char *buf, enum community_token *token, u_int32_t *val)
                        p += strlen("local-AS");
                        return p;
                }
+               if (strncmp(p, "graceful-shutdown", strlen("graceful-shutdown")) == 0) {
+                       *val = COMMUNITY_GSHUT;
+                       *token = community_token_gshut;
+                       p += strlen("graceful-shutdown");
+                       return p;
+               }
 
                /* Unknown string. */
                *token = community_token_unknown;
@@ -595,6 +612,7 @@ struct community *community_str2com(const char *str)
                case community_token_no_export:
                case community_token_no_advertise:
                case community_token_local_as:
+               case community_token_gshut:
                        if (com == NULL) {
                                com = community_new();
                                com->json = NULL;
index c59eebf2e9adf7807d4ec2e2fcff7cc2d42507c0..f728debdb5e9527be0573ee0e577af29c7d984ad 100644 (file)
@@ -48,6 +48,7 @@ struct community {
 #define COMMUNITY_NO_ADVERTISE          0xFFFFFF02
 #define COMMUNITY_NO_EXPORT_SUBCONFED   0xFFFFFF03
 #define COMMUNITY_LOCAL_AS              0xFFFFFF03
+#define COMMUNITY_GSHUT                 0xFFFF0000
 
 /* Macros of community attribute.  */
 #define com_length(X)    ((X)->size * 4)
index bb204b01f1265784e4d3ecee1a46ad98a10a7b98..e12c6fb7d8c61a9e355fa560f52da0c3ff4a9ffc 100644 (file)
@@ -1267,6 +1267,39 @@ static void bgp_peer_as_override(struct bgp *bgp, afi_t afi, safi_t safi,
        }
 }
 
+void bgp_attr_add_gshut_community(struct attr *attr)
+{
+       struct community *old;
+       struct community *new;
+       struct community *merge;
+       struct community *gshut;
+
+       old = attr->community;
+       gshut = community_str2com("graceful-shutdown");
+
+       if (old) {
+               merge = community_merge(community_dup(old), gshut);
+
+               if (old->refcnt== 0)
+                       community_free(old);
+
+               new = community_uniq_sort(merge);
+               community_free(merge);
+       } else {
+               new = community_dup(gshut);
+       }
+
+       community_free(gshut);
+       attr->community = new;
+       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES);
+
+       /* When we add the graceful-shutdown community we must also
+        * lower the local-preference */
+       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+       attr->local_pref = BGP_GSHUT_LOCAL_PREF;
+}
+
+
 static void subgroup_announce_reset_nhop(u_char family, struct attr *attr)
 {
        if (family == AF_INET)
@@ -1623,6 +1656,15 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_info *ri,
                }
        }
 
+       if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+               if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) {
+                       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+                       attr->local_pref = BGP_GSHUT_LOCAL_PREF;
+               } else {
+                       bgp_attr_add_gshut_community(attr);
+               }
+       }
+
        /* After route-map has been applied, we check to see if the nexthop to
         * be carried in the attribute (that is used for the announcement) can
         * be cleared off or not. We do this in all cases where we would be
@@ -2725,6 +2767,22 @@ int bgp_update(struct peer *peer, struct prefix *p, u_int32_t addpath_id,
                goto filtered;
        }
 
+       if (peer->sort == BGP_PEER_EBGP) {
+
+               /* If we receive the graceful-shutdown community from an eBGP peer we
+                * must lower local-preference */
+               if (new_attr.community &&
+                   community_include(new_attr.community, COMMUNITY_GSHUT)) {
+                       new_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+                       new_attr.local_pref = BGP_GSHUT_LOCAL_PREF;
+
+               /* If graceful-shutdown is configured then add the GSHUT community to
+                * all paths received from eBGP peers */
+               } else if (bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+                       bgp_attr_add_gshut_community(&new_attr);
+               }
+       }
+
        /* next hop check.  */
        if (bgp_update_martian_nexthop(bgp, afi, safi, &new_attr)) {
                reason = "martian or self next-hop;";
@@ -4044,9 +4102,18 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p,
                        bgp_static_withdraw(bgp, p, afi, safi);
                        return;
                }
+
+               if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN))
+                       bgp_attr_add_gshut_community(&attr_tmp);
+
                attr_new = bgp_attr_intern(&attr_tmp);
-       } else
+       } else {
+
+               if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN))
+                       bgp_attr_add_gshut_community(&attr);
+
                attr_new = bgp_attr_intern(&attr);
+       }
 
        for (ri = rn->info; ri; ri = ri->next)
                if (ri->peer == bgp->peer_self && ri->type == ZEBRA_ROUTE_BGP
@@ -6152,6 +6219,9 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
                        }
                }
 
+               if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN))
+                       bgp_attr_add_gshut_community(&attr_new);
+
                bn = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi,
                                      SAFI_UNICAST, p, NULL);
 
@@ -8019,8 +8089,8 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
                                  enum bgp_show_type type);
 static int bgp_show_regexp(struct vty *vty, const char *regstr, afi_t afi,
                           safi_t safi, enum bgp_show_type type);
-static int bgp_show_community(struct vty *vty, struct bgp *bgp, int argc,
-                             struct cmd_token **argv, int exact, afi_t afi,
+static int bgp_show_community(struct vty *vty, struct bgp *bgp,
+                             const char *comstr, int exact, afi_t afi,
                              safi_t safi);
 
 static int bgp_show_table(struct vty *vty, struct bgp *bgp,
@@ -8846,7 +8916,7 @@ DEFUN (show_ip_bgp,
              |prefix-list WORD\
              |filter-list WORD\
              |statistics\
-             |community [<AA:NN|local-AS|no-advertise|no-export> [exact-match]]\
+             |community [<AA:NN|local-AS|no-advertise|no-export|graceful-shutdown> [exact-match]]\
              |community-list <(1-500)|WORD> [exact-match]\
              |A.B.C.D/M longer-prefixes\
              |X:X::X:X/M longer-prefixes>\
@@ -8874,6 +8944,7 @@ DEFUN (show_ip_bgp,
        "Do not send outside local AS (well-known community)\n"
        "Do not advertise to any peer (well-known community)\n"
        "Do not export to next AS (well-known community)\n"
+       "Graceful shutdown (well-known community)\n"
        "Exact match of the communities\n"
        "Display routes matching the community-list\n"
        "community-list number\n"
@@ -8891,6 +8962,7 @@ DEFUN (show_ip_bgp,
        enum bgp_show_type sh_type = bgp_show_type_normal;
        struct bgp *bgp = NULL;
        int idx = 0;
+       int idx_community_type = 0;
 
        bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
                                            &bgp);
@@ -8934,12 +9006,15 @@ DEFUN (show_ip_bgp,
 
        if (argv_find(argv, argc, "community", &idx)) {
                /* show a specific community */
-               if (argv_find(argv, argc, "local-AS", &idx)
-                   || argv_find(argv, argc, "no-advertise", &idx)
-                   || argv_find(argv, argc, "no-export", &idx)) {
-                       if (argv_find(argv, argc, "exact_match", &idx))
+               if (argv_find(argv, argc, "local-AS", &idx_community_type)
+                   || argv_find(argv, argc, "no-advertise", &idx_community_type)
+                   || argv_find(argv, argc, "no-export", &idx_community_type)
+                   || argv_find(argv, argc, "graceful-shutdown", &idx_community_type)
+                   || argv_find(argv, argc, "AA:NN", &idx_community_type)) {
+
+                       if (argv_find(argv, argc, "exact-match", &idx))
                                exact_match = 1;
-                       return bgp_show_community(vty, bgp, argc, argv,
+                       return bgp_show_community(vty, bgp, argv[idx_community_type]->arg,
                                                  exact_match, afi, safi);
                }
                /* show all communities */
@@ -9170,39 +9245,16 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp,
        return bgp_show(vty, bgp, afi, safi, type, rmap, 0);
 }
 
-static int bgp_show_community(struct vty *vty, struct bgp *bgp, int argc,
-                             struct cmd_token **argv, int exact, afi_t afi,
+static int bgp_show_community(struct vty *vty, struct bgp *bgp,
+                             const char *comstr, int exact, afi_t afi,
                              safi_t safi)
 {
        struct community *com;
-       struct buffer *b;
-       int i;
-       char *str;
-       int first = 0;
        int ret = 0;
 
-       b = buffer_new(1024);
-       for (i = 0; i < argc; i++) {
-               if (first)
-                       buffer_putc(b, ' ');
-               else {
-                       if (strmatch(argv[i]->text, "unicast")
-                           || strmatch(argv[i]->text, "multicast"))
-                               continue;
-                       first = 1;
-               }
-
-               buffer_putstr(b, argv[i]->arg);
-       }
-       buffer_putc(b, '\0');
-
-       str = buffer_getstr(b);
-       buffer_free(b);
-
-       com = community_str2com(str);
-       XFREE(MTYPE_TMP, str);
+       com = community_str2com(comstr);
        if (!com) {
-               vty_out(vty, "%% Community malformed: \n");
+               vty_out(vty, "%% Community malformed: %s\n", comstr);
                return CMD_WARNING;
        }
 
index 1a1817bad35642410d83b8b13e4049be4a57b175..b9d7957c6857d6e59de428f0c1b6845f044eeb22 100644 (file)
@@ -423,6 +423,7 @@ extern void bgp_info_restore(struct bgp_node *, struct bgp_info *);
 extern int bgp_info_cmp_compatible(struct bgp *, struct bgp_info *,
                                   struct bgp_info *, char *pfx_buf, afi_t afi,
                                   safi_t safi);
+extern void bgp_attr_add_gshut_community(struct attr *attr);
 
 extern void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,
                               struct bgp_maxpaths_cfg *mpath_cfg,
index 5a5d2a5d5d4f6fe88ec0522327f5a8ed40ed274a..dce18a73d76b8dfbd9db02c830907eabd83ec0d2 100644 (file)
@@ -3768,6 +3768,11 @@ DEFUN (set_community,
                        buffer_putstr(b, "no-export");
                        continue;
                }
+               if (strncmp(argv[i]->arg, "graceful-shutdown", strlen(argv[i]->arg))
+                   == 0) {
+                       buffer_putstr(b, "graceful-shutdown");
+                       continue;
+               }
                buffer_putstr(b, argv[i]->arg);
        }
        buffer_putc(b, '\0');
index 0a33fa5ed4237379c7851d44da1c8a5ca1289815..1bd6fb8b3d57421101c2d816795b1513435c3b9d 100644 (file)
@@ -691,6 +691,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
 
        bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
        aspath = attr.aspath;
+
        attr.local_pref = bgp->default_local_pref;
 
        if (afi == AFI_IP)
@@ -749,6 +750,11 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
        } else {
                if (!CHECK_FLAG(subgrp->sflags,
                                SUBGRP_STATUS_DEFAULT_ORIGINATE)) {
+
+                       if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+                               bgp_attr_add_gshut_community(&attr);
+                       }
+
                        SET_FLAG(subgrp->sflags,
                                 SUBGRP_STATUS_DEFAULT_ORIGINATE);
                        subgroup_default_update_packet(subgrp, &attr, from);
index 2e18a6d44fb5605d1baf51ab376a3698e6ac233a..d9117deaf587f89987106093ba9ff91db4787094 100644 (file)
@@ -1804,6 +1804,69 @@ DEFUN (no_bgp_graceful_restart_preserve_fw,
        return CMD_SUCCESS;
 }
 
+static void bgp_redistribute_redo(struct bgp *bgp)
+{
+       afi_t afi;
+       int i;
+       struct list *red_list;
+       struct listnode *node;
+       struct bgp_redist *red;
+
+        for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+                for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+
+                        red_list = bgp->redist[afi][i];
+                        if (!red_list)
+                                continue;
+
+                        for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) {
+                               bgp_redistribute_resend(bgp, afi, i,
+                                                       red->instance);
+                       }
+               }
+       }
+}
+
+/* "bgp graceful-shutdown" configuration */
+DEFUN (bgp_graceful_shutdown,
+       bgp_graceful_shutdown_cmd,
+       "bgp graceful-shutdown",
+       BGP_STR
+       "Graceful shutdown parameters\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+       if (!bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+               bgp_flag_set(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN);
+               bgp_static_redo_import_check(bgp);
+               bgp_redistribute_redo(bgp);
+               bgp_clear_star_soft_out(vty, bgp->name);
+               bgp_clear_star_soft_in(vty, bgp->name);
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_shutdown,
+       no_bgp_graceful_shutdown_cmd,
+       "no bgp graceful-shutdown",
+       NO_STR
+       BGP_STR
+       "Graceful shutdown parameters\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+       if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+               bgp_flag_unset(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN);
+               bgp_static_redo_import_check(bgp);
+               bgp_redistribute_redo(bgp);
+               bgp_clear_star_soft_out(vty, bgp->name);
+               bgp_clear_star_soft_in(vty, bgp->name);
+       }
+
+       return CMD_SUCCESS;
+}
+
 /* "bgp fast-external-failover" configuration. */
 DEFUN (bgp_fast_external_failover,
        bgp_fast_external_failover_cmd,
@@ -10459,6 +10522,7 @@ DEFUN (bgp_redistribute_ipv4,
                vty_out(vty, "%% Invalid route type\n");
                return CMD_WARNING_CONFIG_FAILED;
        }
+
        bgp_redist_add(bgp, AFI_IP, type, 0);
        return bgp_redistribute_set(bgp, AFI_IP, type, 0);
 }
@@ -11335,6 +11399,10 @@ void bgp_vty_init(void)
        install_element(BGP_NODE, &bgp_graceful_restart_preserve_fw_cmd);
        install_element(BGP_NODE, &no_bgp_graceful_restart_preserve_fw_cmd);
 
+       /* "bgp graceful-shutdown" commands */
+       install_element(BGP_NODE, &bgp_graceful_shutdown_cmd);
+       install_element(BGP_NODE, &no_bgp_graceful_shutdown_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 9d7c38c8716d498b65b1645a90189a1d626698f9..258d650d207c4530b97a7c2eb4b8073b32a27e44 100644 (file)
@@ -7190,6 +7190,10 @@ int bgp_config_write(struct vty *vty)
                if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART))
                        vty_out(vty, " bgp graceful-restart\n");
 
+               /* BGP graceful-shutdown */
+               if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN))
+                       vty_out(vty, " bgp graceful-shutdown\n");
+
                /* BGP graceful-restart Preserve State F bit. */
                if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD))
                        vty_out(vty,
index f6e7b2277f9501636a9ef31f198e87ab30c030b8..da30ecd963a44ae000fd2f35076a29e9cbd579e2 100644 (file)
@@ -303,6 +303,7 @@ struct bgp {
 #define BGP_FLAG_FORCE_STATIC_PROCESS     (1 << 18)
 #define BGP_FLAG_SHOW_HOSTNAME            (1 << 19)
 #define BGP_FLAG_GR_PRESERVE_FWD          (1 << 20)
+#define BGP_FLAG_GRACEFUL_SHUTDOWN        (1 << 21)
 
        /* BGP Per AF flags */
        u_int16_t af_flags[AFI_MAX][SAFI_MAX];
@@ -1091,6 +1092,10 @@ struct bgp_nlri {
 /* BGP default local preference.  */
 #define BGP_DEFAULT_LOCAL_PREF                 100
 
+/* BGP local-preference to send when 'bgp graceful-shutdown'
+ * is configured */
+#define BGP_GSHUT_LOCAL_PREF                     0
+
 /* BGP default subgroup packet queue max .  */
 #define BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX      40