From 4685db418e3a861205a28f975afeb9869f674337 Mon Sep 17 00:00:00 2001 From: Francois Dumontet Date: Wed, 12 Jul 2023 19:44:02 +0200 Subject: [PATCH] bgpd: add set as-path exclude acl-list command A route-map applied on incoming BGP updates is not able to exclude the unwanted as segments, based on an AS path access-list. The below configuration illustrates the case: router bgp 65001 address-family ipv4 unicast neighbor 192.168.1.2 route-map rule_2 in exit-address-family bgp as-path access-list RULE permit ^65 route-map rule_2 permit 10 set as-path exclude as-path-access-list RULE ``` BGP routing table entry for 10.10.10.10/32, version 13 Paths: (1 available, best #1, table default) Advertised to non peer-group peers: 192.168.10.65 65000 1 2 3 123 192.168.10.65 from 192.168.10.65 (10.10.10.11) Origin IGP, metric 0, valid, external, best (First path received) ``` After: ``` do show ip bgp 10.10.10.10/32 BGP routing table entry for 10.10.10.10/32, version 15 Paths: (1 available, best #1, table default) Advertised to non peer-group peers: 192.168.10.65 2 3 123 192.168.10.65 from 192.168.10.65 (10.10.10.11) Origin IGP, metric 0, valid, external, best (First path received) ``` Signed-off-by: Francois Dumontet --- bgpd/bgp_aspath.c | 87 +++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_aspath.h | 3 ++ bgpd/bgp_filter.c | 26 -------------- bgpd/bgp_filter.h | 27 ++++++++++++++ bgpd/bgp_routemap.c | 71 +++++++++++++++++++++++++++++++++--- 5 files changed, 184 insertions(+), 30 deletions(-) diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 22c9fe0e6686..4ea81216bf58 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -21,6 +21,7 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_errors.h" +#include "bgpd/bgp_filter.h" /* Attr. Flags and Attr. Type Code. */ #define AS_HEADER_SIZE 2 @@ -1616,6 +1617,92 @@ struct aspath *aspath_filter_exclude_all(struct aspath *source) return newpath; } +struct aspath *aspath_filter_exclude_acl(struct aspath *source, + struct as_list *acl_list) +{ + struct assegment *cur_seg, *new_seg, *prev_seg, *next_seg; + struct as_list *cur_as_list; + struct as_filter *cur_as_filter; + char str_buf[ASPATH_STR_DEFAULT_LEN]; + uint32_t nb_as_del; + uint32_t i, j; + + cur_seg = source->segments; + prev_seg = NULL; + /* segments from source aspath */ + while (cur_seg) { + next_seg = cur_seg->next; + cur_as_list = acl_list; + nb_as_del = 0; + /* aspath filter list from acl_list */ + while (cur_as_list) { + cur_as_filter = cur_as_list->head; + while (cur_as_filter) { + for (i = 0; i < cur_seg->length; i++) { + if (cur_seg->as[i] == 0) + continue; + + snprintfrr(str_buf, + ASPATH_STR_DEFAULT_LEN, + ASN_FORMAT(source->asnotation), + &cur_seg->as[i]); + if (!regexec(cur_as_filter->reg, + str_buf, 0, NULL, 0)) { + cur_seg->as[i] = 0; + nb_as_del++; + } + } + + cur_as_filter = cur_as_filter->next; + } + + cur_as_list = cur_as_list->next; + } + /* full segment is excluded remove it */ + if (nb_as_del == cur_seg->length) { + if (cur_seg == source->segments) + /* first segment */ + source->segments = cur_seg->next; + else + prev_seg->next = cur_seg->next; + assegment_free(cur_seg); + } + /* change in segment size -> new allocation and replace segment*/ + else if (nb_as_del) { + new_seg = assegment_new(cur_seg->type, + cur_seg->length - nb_as_del); + j = 0; + for (i = 0; i < cur_seg->length; i++) { + if (cur_seg->as[i] == 0) + continue; + new_seg->as[j] = cur_seg->as[i]; + j++; + } + new_seg->next = next_seg; + if (cur_seg == source->segments) + /* first segment */ + source->segments = new_seg; + else if (prev_seg) + prev_seg->next = new_seg; + assegment_free(cur_seg); + } + prev_seg = cur_seg; + cur_seg = next_seg; + } + + + aspath_str_update(source, false); + /* We are happy returning even an empty AS_PATH, because the + * administrator + * might expect this very behaviour. There's a mean to avoid this, if + * necessary, + * by having a match rule against certain AS_PATH regexps in the + * route-map index. + */ + return source; +} + + /* Add specified AS to the leftmost of aspath. */ static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno, uint8_t type, unsigned num) diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index a3aae14f8f7e..b1a61d5b99ae 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -8,6 +8,7 @@ #include "lib/json.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_filter.h" /* AS path segment type. */ #define AS_SET 1 @@ -77,6 +78,8 @@ extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2); extern struct aspath *aspath_filter_exclude(struct aspath *source, struct aspath *exclude_list); extern struct aspath *aspath_filter_exclude_all(struct aspath *source); +extern struct aspath *aspath_filter_exclude_acl(struct aspath *source, + struct as_list *acl_list); extern struct aspath *aspath_add_seq_n(struct aspath *aspath, as_t asno, unsigned num); extern struct aspath *aspath_add_seq(struct aspath *aspath, as_t asno); diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index fbea6862b85c..ad541b67ad44 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -15,7 +15,6 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_regex.h" -#include "bgpd/bgp_filter.h" /* List of AS filter list. */ struct as_list_list { @@ -35,30 +34,6 @@ struct as_list_master { void (*delete_hook)(const char *); }; -/* Element of AS path filter. */ -struct as_filter { - struct as_filter *next; - struct as_filter *prev; - - enum as_filter_type type; - - regex_t *reg; - char *reg_str; - - /* Sequence number. */ - int64_t seq; -}; - -/* AS path filter list. */ -struct as_list { - char *name; - - struct as_list *next; - struct as_list *prev; - - struct as_filter *head; - struct as_filter *tail; -}; /* Calculate new sequential number. */ @@ -220,7 +195,6 @@ struct as_list *as_list_lookup(const char *name) for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) if (strcmp(aslist->name, name) == 0) return aslist; - return NULL; } diff --git a/bgpd/bgp_filter.h b/bgpd/bgp_filter.h index a294ebc69eae..1890fd3d9664 100644 --- a/bgpd/bgp_filter.h +++ b/bgpd/bgp_filter.h @@ -10,6 +10,33 @@ enum as_filter_type { AS_FILTER_DENY, AS_FILTER_PERMIT }; + +/* Element of AS path filter. */ +struct as_filter { + struct as_filter *next; + struct as_filter *prev; + + enum as_filter_type type; + + regex_t *reg; + char *reg_str; + + /* Sequence number. */ + int64_t seq; +}; + +/* AS path filter list. */ +struct as_list { + char *name; + + struct as_list *next; + struct as_list *prev; + + struct as_filter *head; + struct as_filter *tail; +}; + + extern void bgp_filter_init(void); extern void bgp_filter_reset(void); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index c0365d8e3b83..099056777693 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -2303,18 +2303,27 @@ static const struct route_map_rule_cmd route_set_aspath_prepend_cmd = { struct aspath_exclude { struct aspath *aspath; bool exclude_all; + char *exclude_aspath_acl_name; + struct as_list *exclude_aspath_acl; }; static void *route_aspath_exclude_compile(const char *arg) { struct aspath_exclude *ase; const char *str = arg; + static const char asp_acl[] = "as-path-access-list"; ase = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct aspath_exclude)); - if (!strmatch(str, "all")) - ase->aspath = aspath_str2aspath(str, bgp_get_asnotation(NULL)); - else + if (strmatch(str, "all")) ase->exclude_all = true; + else if (!strncmp(str, asp_acl, strlen(asp_acl))) { + str += strlen(asp_acl); + while (*str == ' ') + str++; + ase->exclude_aspath_acl_name = XSTRDUP(MTYPE_TMP, str); + ase->exclude_aspath_acl = as_list_lookup(str); + } else + ase->aspath = aspath_str2aspath(str, bgp_get_asnotation(NULL)); return ase; } @@ -2323,6 +2332,8 @@ static void route_aspath_exclude_free(void *rule) struct aspath_exclude *ase = rule; aspath_free(ase->aspath); + if (ase->exclude_aspath_acl_name) + XFREE(MTYPE_TMP, ase->exclude_aspath_acl_name); XFREE(MTYPE_ROUTE_MAP_COMPILED, ase); } @@ -2357,10 +2368,20 @@ route_set_aspath_exclude(void *rule, const struct prefix *dummy, void *object) else if (ase->exclude_all) path->attr->aspath = aspath_filter_exclude_all(new_path); + else if (ase->exclude_aspath_acl_name) { + if (!ase->exclude_aspath_acl) + ase->exclude_aspath_acl = + as_list_lookup(ase->exclude_aspath_acl_name); + if (ase->exclude_aspath_acl) + path->attr->aspath = + aspath_filter_exclude_acl(new_path, + ase->exclude_aspath_acl); + } + return RMAP_OKAY; } -/* Set ASn exlude rule structure. */ +/* Set ASn exclude rule structure. */ static const struct route_map_rule_cmd route_set_aspath_exclude_cmd = { "as-path exclude", route_set_aspath_exclude, @@ -6053,6 +6074,46 @@ DEFUN_YANG (no_set_aspath_exclude, return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG(set_aspath_exclude_access_list, set_aspath_exclude_access_list_cmd, + "set as-path exclude as-path-access-list AS_PATH_FILTER_NAME", + SET_STR + "Transform BGP AS-path attribute\n" + "Exclude from the as-path\n" + "Specify an as path access list name\n" + "AS path access list name\n") +{ + char *str; + const char *xpath = + "./set-action[action='frr-bgp-route-map:as-path-exclude']"; + char xpath_value[XPATH_MAXLEN]; + + str = argv_concat(argv, argc, 3); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:exclude-as-path", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(no_set_aspath_exclude_access_list, no_set_aspath_exclude_access_list_cmd, + "no set as-path exclude as-path-access-list [AS_PATH_FILTER_NAME]", + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Exclude from the as-path\n" + "Specify an as path access list name\n" + "AS path access list name\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:as-path-exclude']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + ALIAS_YANG (no_set_aspath_exclude, no_set_aspath_exclude_all_cmd, "no set as-path exclude", NO_STR SET_STR @@ -7616,11 +7677,13 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &set_aspath_prepend_lastas_cmd); install_element(RMAP_NODE, &set_aspath_exclude_cmd); install_element(RMAP_NODE, &set_aspath_exclude_all_cmd); + install_element(RMAP_NODE, &set_aspath_exclude_access_list_cmd); install_element(RMAP_NODE, &set_aspath_replace_asn_cmd); install_element(RMAP_NODE, &no_set_aspath_prepend_cmd); install_element(RMAP_NODE, &no_set_aspath_prepend_lastas_cmd); install_element(RMAP_NODE, &no_set_aspath_exclude_cmd); install_element(RMAP_NODE, &no_set_aspath_exclude_all_cmd); + install_element(RMAP_NODE, &no_set_aspath_exclude_access_list_cmd); install_element(RMAP_NODE, &no_set_aspath_replace_asn_cmd); install_element(RMAP_NODE, &set_origin_cmd); install_element(RMAP_NODE, &no_set_origin_cmd);