]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: add set as-path exclude acl-list command
authorFrancois Dumontet <francois.dumontet@6wind.com>
Tue, 11 Jul 2023 08:03:04 +0000 (10:03 +0200)
committerFrancois Dumontet <francois.dumontet@6wind.com>
Mon, 7 Aug 2023 10:30:34 +0000 (12:30 +0200)
A route-map applied on incoming BGP updates is not able
to replace an unwanted as segments by another one.
unwanted as segment are 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 replace as-path-access-list RULE 6000

```
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
      6000 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)
```

Signed-off-by: Francois Dumontet <francois.dumontet@6wind.com>
bgpd/bgp_aspath.c
bgpd/bgp_aspath.h
bgpd/bgp_routemap.c

index 4ea81216bf586172ae8c4f5756e23af8dec8e6eb..2e2248cd72c0fdca23cbe29e95ae878b01f67f50 100644 (file)
@@ -1231,6 +1231,46 @@ bool aspath_private_as_check(struct aspath *aspath)
        return true;
 }
 
+/* Replace all ASN instances of the regex rule with our own ASN  */
+struct aspath *aspath_replace_regex_asn(struct aspath *aspath,
+                                       struct as_list *acl_list, as_t our_asn)
+{
+       struct aspath *new;
+       struct assegment *cur_seg;
+       struct as_list *cur_as_list;
+       struct as_filter *cur_as_filter;
+       char str_buf[ASPATH_STR_DEFAULT_LEN];
+       uint32_t i;
+
+       new = aspath_dup(aspath);
+       cur_seg = new->segments;
+
+       while (cur_seg) {
+               cur_as_list = 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++) {
+                                       snprintfrr(str_buf,
+                                                  ASPATH_STR_DEFAULT_LEN,
+                                                  ASN_FORMAT(new->asnotation),
+                                                  &cur_seg->as[i]);
+                                       if (!regexec(cur_as_filter->reg,
+                                                    str_buf, 0, NULL, 0))
+                                               cur_seg->as[i] = our_asn;
+                               }
+                               cur_as_filter = cur_as_filter->next;
+                       }
+                       cur_as_list = cur_as_list->next;
+               }
+               cur_seg = cur_seg->next;
+       }
+
+       aspath_str_update(new, false);
+       return new;
+}
+
+
 /* Replace all instances of the target ASN with our own ASN */
 struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
                                           as_t target_asn, as_t our_asn)
index b1a61d5b99aecd260c81479d71bdda780cf05b21..ebfc7d087d666bf008d3ed7e76de6cd68470a5bf 100644 (file)
@@ -107,6 +107,9 @@ extern unsigned int aspath_get_last_as(struct aspath *aspath);
 extern int aspath_loop_check(struct aspath *aspath, as_t asno);
 extern int aspath_loop_check_confed(struct aspath *aspath, as_t asno);
 extern bool aspath_private_as_check(struct aspath *aspath);
+extern struct aspath *aspath_replace_regex_asn(struct aspath *aspath,
+                                              struct as_list *acl_list,
+                                              as_t our_asn);
 extern struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
                                                  as_t target_asn,
                                                  as_t our_asn);
index b7ac976e23bf156cc9a05eb96cf752bf8b75df11..a8e63101876cccb7a493f67a79599af101d474b3 100644 (file)
@@ -2410,11 +2410,16 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
        as_t configured_asn;
        char *buf;
        char src_asn[ASN_STRING_MAX_SIZE];
+       char *acl_list_name = NULL;
+       uint32_t acl_list_name_len = 0;
+       char *buf_acl_name = NULL;
+       static const char asp_acl[] = "as-path-access-list";
+       struct as_list *aspath_acl = NULL;
 
        if (path->peer->sort != BGP_PEER_EBGP) {
                zlog_warn(
                        "`set as-path replace` is supported only for EBGP peers");
-               return RMAP_NOOP;
+               goto end_ko;
        }
 
        buf = strchr(replace, ' ');
@@ -2422,6 +2427,46 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
                configured_asn = path->peer->change_local_as
                                         ? path->peer->change_local_as
                                         : path->peer->local_as;
+       } else if (!strncmp(replace, asp_acl, strlen(asp_acl))) {
+               /* its as-path-acl-list command get the access list name */
+               while (*buf == ' ')
+                       buf++;
+               buf_acl_name = buf;
+               buf = strchr(buf_acl_name, ' ');
+               if (buf)
+                       acl_list_name_len = buf - buf_acl_name;
+               else
+                       acl_list_name_len = strlen(buf_acl_name);
+
+               buf_acl_name[acl_list_name_len] = 0;
+               /* get the acl-list */
+               aspath_acl = as_list_lookup(buf_acl_name);
+               if (!aspath_acl) {
+                       zlog_warn("`set as-path replace`, invalid as-path-access-list name: %s",
+                                 buf_acl_name);
+                       goto end_ko;
+               }
+               acl_list_name = XSTRDUP(MTYPE_TMP, buf_acl_name);
+               buf_acl_name[acl_list_name_len] = ' ';
+
+               if (!buf) {
+                       configured_asn = path->peer->change_local_as
+                                                ? path->peer->change_local_as
+                                                : path->peer->local_as;
+               } else {
+                       while (*buf == ' ')
+                               buf++;
+                       /* get the configured asn */
+                       if (!asn_str2asn(buf, &configured_asn)) {
+                               zlog_warn(
+                                       "`set as-path replace`, invalid configured AS %s",
+                                       buf);
+                               goto end_ko;
+                       }
+               }
+
+               replace = buf;
+
        } else {
                memcpy(src_asn, replace, (size_t)(buf - replace));
                src_asn[(size_t)(buf - replace)] = '\0';
@@ -2431,13 +2476,14 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
                        zlog_warn(
                                "`set as-path replace`, invalid configured AS %s",
                                buf);
-                       return RMAP_NOOP;
+                       goto end_ko;
                }
        }
 
-       if (!strmatch(replace, "any") && !asn_str2asn(replace, &replace_asn)) {
+       if (replace && !strmatch(replace, "any") &&
+           !asn_str2asn(replace, &replace_asn)) {
                zlog_warn("`set as-path replace`, invalid AS %s", replace);
-               return RMAP_NOOP;
+               goto end_ko;
        }
 
        if (path->attr->aspath->refcnt)
@@ -2445,16 +2491,29 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
        else
                aspath_new = path->attr->aspath;
 
-       if (strmatch(replace, "any")) {
+       if (aspath_acl) {
+               path->attr->aspath = aspath_replace_regex_asn(aspath_new,
+                                                             aspath_acl,
+                                                             configured_asn);
+       } else if (strmatch(replace, "any")) {
                path->attr->aspath =
                        aspath_replace_all_asn(aspath_new, configured_asn);
-       } else
+       } else {
                path->attr->aspath = aspath_replace_specific_asn(
                        aspath_new, replace_asn, configured_asn);
-
+       }
        aspath_free(aspath_new);
 
+
+       if (acl_list_name)
+               XFREE(MTYPE_TMP, acl_list_name);
        return RMAP_OKAY;
+
+end_ko:
+       if (acl_list_name)
+               XFREE(MTYPE_TMP, acl_list_name);
+       return RMAP_NOOP;
+
 }
 
 static const struct route_map_rule_cmd route_set_aspath_replace_cmd = {
@@ -6087,6 +6146,61 @@ DEFPY_YANG(no_set_aspath_replace_asn, no_set_aspath_replace_asn_cmd,
        return nb_cli_apply_changes(vty, NULL);
 }
 
+DEFPY_YANG(
+       set_aspath_replace_access_list, set_aspath_replace_access_list_cmd,
+       "set as-path replace as-path-access-list AS_PATH_FILTER_NAME$aspath_filter_name [<ASNUM>$configured_asn]",
+       SET_STR
+       "Transform BGP AS-path attribute\n"
+       "Replace AS number to local or configured AS number\n"
+       "Specify an as path access list name\n"
+       "AS path access list name\n"
+       "Define the configured AS number\n")
+{
+       char *str;
+       const char *xpath =
+               "./set-action[action='frr-bgp-route-map:as-path-replace']";
+       char xpath_value[XPATH_MAXLEN];
+       as_t as_configured_value;
+       char replace_value[ASN_STRING_MAX_SIZE * 2];
+
+       if (configured_asn_str &&
+           !asn_str2asn(configured_asn_str, &as_configured_value)) {
+               vty_out(vty, "%% Invalid AS configured value %s\n",
+                       configured_asn_str);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       str = argv_concat(argv, argc, 3);
+
+       nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+       snprintf(replace_value, sizeof(replace_value), "%s %s", aspath_filter_name, str);
+
+       snprintf(xpath_value, sizeof(xpath_value),
+                "%s/rmap-set-action/frr-bgp-route-map:replace-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_replace_access_list, no_set_aspath_replace_access_list_cmd,
+       "no set as-path replace as-path-access-list [AS_PATH_FILTER_NAME] [<ASNUM>$configured_asn]",
+       NO_STR
+       SET_STR
+       "Transform BGP AS_PATH attribute\n"
+       "Replace AS number to local or configured AS number\n"
+       "Specify an as path access list name\n"
+       "AS path access list name\n"
+       "Define the configured AS number\n")
+{
+       const char *xpath =
+               "./set-action[action='frr-bgp-route-map:as-path-replace']";
+
+       nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+       return nb_cli_apply_changes(vty, NULL);
+}
+
 DEFUN_YANG (no_set_aspath_prepend,
            no_set_aspath_prepend_cmd,
            "no set as-path prepend [ASNUM]",
@@ -7792,12 +7906,14 @@ void bgp_route_map_init(void)
        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, &set_aspath_replace_access_list_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, &no_set_aspath_replace_access_list_cmd);
        install_element(RMAP_NODE, &set_origin_cmd);
        install_element(RMAP_NODE, &no_set_origin_cmd);
        install_element(RMAP_NODE, &set_atomic_aggregate_cmd);