From 7f32323620077157dda1127c86ea792e4f5fcd89 Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Fri, 25 Aug 2017 18:27:49 +0000 Subject: [PATCH] bgpd: implement draft-ietf-grow-bgp-gshut-10 Signed-off-by: Daniel Walton --- bgpd/bgp_attr.c | 16 +++++- bgpd/bgp_clist.c | 7 +++ bgpd/bgp_community.c | 18 ++++++ bgpd/bgp_community.h | 1 + bgpd/bgp_route.c | 124 ++++++++++++++++++++++++++++++------------ bgpd/bgp_route.h | 1 + bgpd/bgp_routemap.c | 5 ++ bgpd/bgp_updgrp_adv.c | 6 ++ bgpd/bgp_vty.c | 68 +++++++++++++++++++++++ bgpd/bgpd.c | 4 ++ bgpd/bgpd.h | 5 ++ 11 files changed, 218 insertions(+), 37 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index b03b408f7..0487cb4e3 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -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; diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 13587b359..94ea35fa0 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -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; diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index 389d723e0..7e8411b6a 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -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; diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index c59eebf2e..f728debdb 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -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) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index bb204b01f..e12c6fb7d 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -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 [ [exact-match]]\ + |community [ [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; } diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 1a1817bad..b9d7957c6 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -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, diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 5a5d2a5d5..dce18a73d 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -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'); diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 0a33fa5ed..1bd6fb8b3 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -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); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 2e18a6d44..d9117deaf 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -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); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 9d7c38c87..258d650d2 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -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, diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index f6e7b2277..da30ecd96 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -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 -- 2.39.2