diff options
Diffstat (limited to 'lib')
45 files changed, 1096 insertions, 436 deletions
diff --git a/lib/command.h b/lib/command.h index a7a2eaf868..e20bfe3318 100644 --- a/lib/command.h +++ b/lib/command.h @@ -367,6 +367,8 @@ struct cmd_node { #define SHOW_STR "Show running system information\n" #define IP_STR "IP information\n" #define IPV6_STR "IPv6 information\n" +#define SRTE_STR "SR-TE information\n" +#define SRTE_COLOR_STR "SR-TE Color information\n" #define NO_STR "Negate a command or set its defaults\n" #define REDIST_STR "Redistribute information from another routing protocol\n" #define CLEAR_STR "Reset functions\n" diff --git a/lib/filter.h b/lib/filter.h index d41f3b65cd..623fb94527 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -32,6 +32,16 @@ extern "C" { /* Maximum ACL name length */ #define ACL_NAMSIZ 128 +/** Cisco host wildcard mask. */ +#define CISCO_HOST_WILDCARD_MASK "0.0.0.0" +/** Cisco host wildcard binary mask. */ +#define CISCO_BIN_HOST_WILDCARD_MASK INADDR_ANY + +/** Cisco any wildcard mask. */ +#define CISCO_ANY_WILDCARD_MASK "255.255.255.255" +/** Cisco binary any wildcard mask. */ +#define CISCO_BIN_ANY_WILDCARD_MASK INADDR_NONE + /* Filter direction. */ #define FILTER_IN 0 #define FILTER_OUT 1 diff --git a/lib/filter_cli.c b/lib/filter_cli.c index 8c7a515dc5..09fc3289ce 100644 --- a/lib/filter_cli.c +++ b/lib/filter_cli.c @@ -37,14 +37,8 @@ #define ACCESS_LIST_STR "Access list entry\n" #define ACCESS_LIST_LEG_STR "IP standard access list\n" -#define ACCESS_LIST_LEG_EXT_STR "IP standard access list (expanded range)\n" #define ACCESS_LIST_ELEG_STR "IP extended access list\n" #define ACCESS_LIST_ELEG_EXT_STR "IP extended access list (expanded range)\n" -#define ACCESS_LIST_XLEG_STR \ - ACCESS_LIST_LEG_STR \ - ACCESS_LIST_LEG_EXT_STR \ - ACCESS_LIST_ELEG_STR \ - ACCESS_LIST_ELEG_EXT_STR #define ACCESS_LIST_ZEBRA_STR "Access list entry\n" #define ACCESS_LIST_SEQ_STR \ "Sequence number of an entry\n" \ @@ -68,7 +62,6 @@ static int64_t acl_cisco_get_seq(struct access_list *acl, const char *action, struct filter f, *fn; memset(&f, 0, sizeof(f)); - memset(&fc, 0, sizeof(fc)); f.cisco = 1; if (strcmp(action, "permit") == 0) f.type = FILTER_PERMIT; @@ -110,7 +103,8 @@ static int64_t acl_zebra_get_seq(struct access_list *acl, const char *action, f.type = FILTER_DENY; fz = &f.u.zfilter; - fz->prefix = *p; + if (p->family) + prefix_copy(&fz->prefix, p); fz->exact = exact; fn = filter_lookup_zebra(acl, &f); @@ -130,6 +124,7 @@ static void concat_addr_mask_v4(const char *addr, const char *mask, char *dst, int plen; assert(inet_pton(AF_INET, mask, &ia) == 1); + ia.s_addr = ~ia.s_addr; plen = ip_masklen(ia); snprintf(dst, dstlen, "%s/%d", addr, plen); } @@ -171,17 +166,15 @@ static long acl_get_seq(struct vty *vty, const char *xpath) */ DEFPY_YANG( access_list_std, access_list_std_cmd, - "access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>", + "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", ACCESS_LIST_STR ACCESS_LIST_LEG_STR - ACCESS_LIST_LEG_EXT_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "A single host address\n" "Address to match\n" "Address to match\n" - "Wildcard bits\n" - "Any source host\n") + "Wildcard bits\n") { int64_t sseq; char ipmask[64]; @@ -193,8 +186,7 @@ DEFPY_YANG( * none given (backward compatibility). */ snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", - number_str); + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); if (seq_str == NULL) { /* Use XPath to find the next sequence number. */ @@ -222,18 +214,16 @@ DEFPY_YANG( DEFPY_YANG( no_access_list_std, no_access_list_std_cmd, - "no access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>", + "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", NO_STR ACCESS_LIST_STR ACCESS_LIST_LEG_STR - ACCESS_LIST_LEG_EXT_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "A single host address\n" "Address to match\n" "Address to match\n" - "Wildcard bits\n" - "Any source host\n") + "Wildcard bits\n") { struct access_list *acl; struct lyd_node *dnode; @@ -246,15 +236,14 @@ DEFPY_YANG( snprintf( xpath, sizeof(xpath), "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", - number_str, seq_str); + name, seq_str); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } /* Otherwise, to keep compatibility, we need to figure it out. */ snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", - number_str); + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); /* Access-list must exist before entries. */ if (yang_dnode_exists(running_config->dnode, xpath) == false) @@ -263,13 +252,9 @@ DEFPY_YANG( /* Use access-list data structure to fetch sequence. */ dnode = yang_dnode_get(running_config->dnode, xpath); acl = nb_running_get_entry(dnode, NULL, true); - if (host_str != NULL) - sseq = acl_cisco_get_seq(acl, action, host_str, - mask_str ? mask_str : "0.0.0.0", NULL, - NULL); - else - sseq = acl_cisco_get_seq(acl, action, "0.0.0.0", - "255.255.255.255", NULL, NULL); + sseq = acl_cisco_get_seq(acl, action, host_str, + mask_str ? mask_str : CISCO_HOST_WILDCARD_MASK, + NULL, NULL); if (sseq == -1) return CMD_WARNING; @@ -282,10 +267,9 @@ DEFPY_YANG( DEFPY_YANG( access_list_ext, access_list_ext_cmd, - "access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", + "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", ACCESS_LIST_STR ACCESS_LIST_ELEG_STR - ACCESS_LIST_ELEG_EXT_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "IPv4 address\n" @@ -301,7 +285,7 @@ DEFPY_YANG( "Any destination host\n") { int64_t sseq; - char ipmask[64]; + char ipmask[64], ipmask_dst[64]; char xpath[XPATH_MAXLEN]; char xpath_entry[XPATH_MAXLEN + 128]; @@ -310,8 +294,7 @@ DEFPY_YANG( * none given (backward compatibility). */ snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", - number_str); + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); if (seq_str == NULL) { /* Use XPath to find the next sequence number. */ @@ -337,12 +320,12 @@ DEFPY_YANG( if (dst_str != NULL && dst_mask_str == NULL) { nb_cli_enqueue_change(vty, "./destination-host", NB_OP_MODIFY, - src_str); + dst_str); } else if (dst_str != NULL && dst_mask_str != NULL) { - concat_addr_mask_v4(dst_str, dst_mask_str, ipmask, - sizeof(ipmask)); + concat_addr_mask_v4(dst_str, dst_mask_str, ipmask_dst, + sizeof(ipmask_dst)); nb_cli_enqueue_change(vty, "./destination-network", - NB_OP_MODIFY, ipmask); + NB_OP_MODIFY, ipmask_dst); } else { nb_cli_enqueue_change(vty, "./destination-any", NB_OP_CREATE, NULL); @@ -353,11 +336,10 @@ DEFPY_YANG( DEFPY_YANG( no_access_list_ext, no_access_list_ext_cmd, - "no access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", + "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", NO_STR ACCESS_LIST_STR ACCESS_LIST_ELEG_STR - ACCESS_LIST_ELEG_EXT_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "Any Internet Protocol\n" @@ -383,15 +365,14 @@ DEFPY_YANG( snprintfrr( xpath, sizeof(xpath), "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", - number_str, seq_str); + name, seq_str); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } /* Otherwise, to keep compatibility, we need to figure it out. */ snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", - number_str); + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); /* Access-list must exist before entries. */ if (yang_dnode_exists(running_config->dnode, xpath) == false) @@ -404,24 +385,28 @@ DEFPY_YANG( if (dst_str != NULL) sseq = acl_cisco_get_seq( acl, action, src_str, - src_mask_str ? src_mask_str : "0.0.0.0", + src_mask_str ? src_mask_str + : CISCO_HOST_WILDCARD_MASK, dst_str, - dst_mask_str ? dst_mask_str : "0.0.0.0"); + dst_mask_str ? dst_mask_str + : CISCO_HOST_WILDCARD_MASK); else - sseq = acl_cisco_get_seq(acl, action, src_str, - src_mask_str ? src_mask_str - : "0.0.0.0", - "0.0.0.0", "255.255.255.255"); + sseq = acl_cisco_get_seq( + acl, action, src_str, + src_mask_str ? src_mask_str + : CISCO_HOST_WILDCARD_MASK, + "0.0.0.0", CISCO_ANY_WILDCARD_MASK); } else { if (dst_str != NULL) - sseq = acl_cisco_get_seq(acl, action, "0.0.0.0", - "255.255.255.255", dst_str, - dst_mask_str ? dst_mask_str - : "0.0.0.0"); + sseq = acl_cisco_get_seq( + acl, action, "0.0.0.0", CISCO_ANY_WILDCARD_MASK, + dst_str, + dst_mask_str ? dst_mask_str + : CISCO_HOST_WILDCARD_MASK); else - sseq = acl_cisco_get_seq(acl, action, "0.0.0.0", - "255.255.255.255", "0.0.0.0", - "255.255.255.255"); + sseq = acl_cisco_get_seq( + acl, action, "0.0.0.0", CISCO_ANY_WILDCARD_MASK, + "0.0.0.0", CISCO_ANY_WILDCARD_MASK); } if (sseq == -1) return CMD_WARNING; @@ -522,7 +507,7 @@ DEFPY_YANG( /* Use access-list data structure to fetch sequence. */ dnode = yang_dnode_get(running_config->dnode, xpath); acl = nb_running_get_entry(dnode, NULL, true); - if (prefix == NULL) { + if (prefix_str == NULL) { memset(&pany, 0, sizeof(pany)); pany.family = AF_INET; sseq = acl_zebra_get_seq(acl, action, &pany, exact); diff --git a/lib/filter_nb.c b/lib/filter_nb.c index 41bf3cf7f4..8838a48abd 100644 --- a/lib/filter_nb.c +++ b/lib/filter_nb.c @@ -24,10 +24,12 @@ #include "lib/northbound.h" #include "lib/prefix.h" +#include "lib/printfrr.h" #include "lib/filter.h" #include "lib/plist.h" #include "lib/plist_int.h" +#include "lib/routemap.h" /* Helper function. */ static in_addr_t @@ -39,20 +41,35 @@ ipv4_network_addr(in_addr_t hostaddr, int masklen) return hostaddr & mask.s_addr; } -static enum nb_error -prefix_list_length_validate(const struct lyd_node *dnode) +static void acl_notify_route_map(struct access_list *acl, int route_map_event) { - int type = yang_dnode_get_enum(dnode, "../../type"); + switch (route_map_event) { + case RMAP_EVENT_FILTER_ADDED: + if (acl->master->add_hook) + (*acl->master->add_hook)(acl); + break; + case RMAP_EVENT_FILTER_DELETED: + if (acl->master->delete_hook) + (*acl->master->delete_hook)(acl); + break; + } + + route_map_notify_dependencies(acl->name, route_map_event); +} + +static enum nb_error prefix_list_length_validate(struct nb_cb_modify_args *args) +{ + int type = yang_dnode_get_enum(args->dnode, "../../type"); const char *xpath_le = NULL, *xpath_ge = NULL; struct prefix p; uint8_t le, ge; if (type == YPLT_IPV4) { - yang_dnode_get_prefix(&p, dnode, "../ipv4-prefix"); + yang_dnode_get_prefix(&p, args->dnode, "../ipv4-prefix"); xpath_le = "../ipv4-prefix-length-lesser-or-equal"; xpath_ge = "../ipv4-prefix-length-greater-or-equal"; } else { - yang_dnode_get_prefix(&p, dnode, "../ipv6-prefix"); + yang_dnode_get_prefix(&p, args->dnode, "../ipv6-prefix"); xpath_le = "../ipv6-prefix-length-lesser-or-equal"; xpath_ge = "../ipv6-prefix-length-greater-or-equal"; } @@ -61,19 +78,18 @@ prefix_list_length_validate(const struct lyd_node *dnode) * Check rule: * prefix length <= le. */ - if (yang_dnode_exists(dnode, xpath_le)) { - le = yang_dnode_get_uint8(dnode, xpath_le); + if (yang_dnode_exists(args->dnode, xpath_le)) { + le = yang_dnode_get_uint8(args->dnode, xpath_le); if (p.prefixlen > le) goto log_and_fail; - } /* * Check rule: * prefix length < ge. */ - if (yang_dnode_exists(dnode, xpath_ge)) { - ge = yang_dnode_get_uint8(dnode, xpath_ge); + if (yang_dnode_exists(args->dnode, xpath_ge)) { + ge = yang_dnode_get_uint8(args->dnode, xpath_ge); if (p.prefixlen >= ge) goto log_and_fail; } @@ -82,18 +98,21 @@ prefix_list_length_validate(const struct lyd_node *dnode) * Check rule: * ge <= le. */ - if (yang_dnode_exists(dnode, xpath_le) && - yang_dnode_exists(dnode, xpath_ge)) { - le = yang_dnode_get_uint8(dnode, xpath_le); - ge = yang_dnode_get_uint8(dnode, xpath_ge); + if (yang_dnode_exists(args->dnode, xpath_le) + && yang_dnode_exists(args->dnode, xpath_ge)) { + le = yang_dnode_get_uint8(args->dnode, xpath_le); + ge = yang_dnode_get_uint8(args->dnode, xpath_ge); if (ge > le) goto log_and_fail; } return NB_OK; - log_and_fail: - zlog_info("prefix-list: invalid prefix range for %pFX: Make sure that mask length < ge <= le", &p); +log_and_fail: + snprintfrr( + args->errmsg, args->errmsg_len, + "Invalid prefix range for %pFX: Make sure that mask length < ge <= le", + &p); return NB_ERR_VALIDATION; } @@ -110,6 +129,19 @@ static void prefix_list_entry_set_empty(struct prefix_list_entry *ple) ple->le = 0; } +/** + * Unsets the cisco style rule for addresses so it becomes disabled (the + * equivalent of setting: `0.0.0.0/32`). + * + * \param addr address part. + * \param mask mask part. + */ +static void cisco_unset_addr_mask(struct in_addr *addr, struct in_addr *mask) +{ + addr->s_addr = INADDR_ANY; + mask->s_addr = CISCO_BIN_HOST_WILDCARD_MASK; +} + /* * XPath: /frr-filter:lib/access-list */ @@ -253,6 +285,8 @@ lib_access_list_entry_action_modify(struct nb_cb_modify_args *args) else f->type = FILTER_DENY; + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + return NB_OK; } @@ -273,6 +307,8 @@ lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) fz = &f->u.zfilter; yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL); + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + return NB_OK; } @@ -289,6 +325,8 @@ lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) fz = &f->u.zfilter; memset(&fz->prefix, 0, sizeof(fz->prefix)); + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + return NB_OK; } @@ -308,6 +346,8 @@ lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args) fz = &f->u.zfilter; fz->exact = yang_dnode_get_bool(args->dnode, NULL); + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + return NB_OK; } @@ -324,6 +364,8 @@ lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args) fz = &f->u.zfilter; fz->exact = 0; + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + return NB_OK; } @@ -343,7 +385,9 @@ lib_access_list_entry_host_modify(struct nb_cb_modify_args *args) f->cisco = 1; fc = &f->u.cfilter; yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL); - fc->addr_mask.s_addr = INADDR_ANY; + fc->addr_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -359,8 +403,9 @@ lib_access_list_entry_host_destroy(struct nb_cb_destroy_args *args) f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; - fc->addr.s_addr = INADDR_ANY; - fc->addr_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -384,6 +429,9 @@ lib_access_list_entry_network_modify(struct nb_cb_modify_args *args) yang_dnode_get_prefix(&p, args->dnode, NULL); fc->addr.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen); masklen2ip(p.prefixlen, &fc->addr_mask); + fc->addr_mask.s_addr = ~fc->addr_mask.s_addr; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -399,8 +447,9 @@ lib_access_list_entry_network_destroy(struct nb_cb_destroy_args *args) f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; - fc->addr.s_addr = INADDR_ANY; - fc->addr_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -421,7 +470,9 @@ lib_access_list_entry_source_any_create(struct nb_cb_create_args *args) f->cisco = 1; fc = &f->u.cfilter; fc->addr.s_addr = INADDR_ANY; - fc->addr_mask.s_addr = INADDR_NONE; + fc->addr_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -437,8 +488,9 @@ lib_access_list_entry_source_any_destroy(struct nb_cb_destroy_args *args) f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; - fc->addr.s_addr = INADDR_ANY; - fc->addr_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -459,7 +511,9 @@ static int lib_access_list_entry_destination_host_modify( fc = &f->u.cfilter; fc->extended = 1; yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL); - fc->mask_mask.s_addr = INADDR_ANY; + fc->mask_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -476,8 +530,9 @@ static int lib_access_list_entry_destination_host_destroy( f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; fc->extended = 0; - fc->mask.s_addr = INADDR_ANY; - fc->mask_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -501,6 +556,9 @@ static int lib_access_list_entry_destination_network_modify( yang_dnode_get_prefix(&p, args->dnode, NULL); fc->mask.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen); masklen2ip(p.prefixlen, &fc->mask_mask); + fc->mask_mask.s_addr = ~fc->mask_mask.s_addr; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -517,8 +575,9 @@ static int lib_access_list_entry_destination_network_destroy( f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; fc->extended = 0; - fc->mask.s_addr = INADDR_ANY; - fc->mask_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -539,7 +598,9 @@ static int lib_access_list_entry_destination_any_create( fc = &f->u.cfilter; fc->extended = 1; fc->mask.s_addr = INADDR_ANY; - fc->mask_mask.s_addr = INADDR_NONE; + fc->mask_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -556,8 +617,9 @@ static int lib_access_list_entry_destination_any_destroy( f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; fc->extended = 0; - fc->mask.s_addr = INADDR_ANY; - fc->mask_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -592,6 +654,8 @@ static int lib_access_list_entry_any_create(struct nb_cb_create_args *args) break; } + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + return NB_OK; } @@ -607,6 +671,8 @@ static int lib_access_list_entry_any_destroy(struct nb_cb_destroy_args *args) fz = &f->u.zfilter; fz->prefix.family = 0; + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + return NB_OK; } @@ -829,7 +895,7 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( struct prefix_list_entry *ple; if (args->event == NB_EV_VALIDATE && - prefix_list_length_validate(args->dnode) != NB_OK) + prefix_list_length_validate(args) != NB_OK) return NB_ERR_VALIDATION; if (args->event != NB_EV_APPLY) @@ -878,7 +944,7 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( struct prefix_list_entry *ple; if (args->event == NB_EV_VALIDATE && - prefix_list_length_validate(args->dnode) != NB_OK) + prefix_list_length_validate(args) != NB_OK) return NB_ERR_VALIDATION; if (args->event != NB_EV_APPLY) diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index fbb97d2dd5..2066e4c96d 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -55,7 +55,7 @@ int main(int argc, char **argv) vty_init(master, true); lib_cmd_init(); yang_init(true); - nb_init(master, NULL, 0); + nb_init(master, NULL, 0, false); vty_stdio(vty_do_exit); diff --git a/lib/hash.c b/lib/hash.c index 7f8a237047..85982774ac 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -77,9 +77,20 @@ void *hash_alloc_intern(void *arg) return arg; } +/* + * ssq = ssq + (new^2 - old^2) + * = ssq + ((new + old) * (new - old)) + */ #define hash_update_ssq(hz, old, new) \ - atomic_fetch_add_explicit(&hz->stats.ssq, (new + old) * (new - old), \ - memory_order_relaxed); + do { \ + int _adjust = (new + old) * (new - old); \ + if (_adjust < 0) \ + atomic_fetch_sub_explicit(&hz->stats.ssq, -_adjust, \ + memory_order_relaxed); \ + else \ + atomic_fetch_add_explicit(&hz->stats.ssq, _adjust, \ + memory_order_relaxed); \ + } while (0) /* Expand hash if the chain length exceeds the threshold. */ static void hash_expand(struct hash *hash) @@ -188,7 +188,9 @@ void if_destroy_via_zapi(struct interface *ifp) if (ifp_master.destroy_hook) (*ifp_master.destroy_hook)(ifp); + ifp->oldifindex = ifp->ifindex; if_set_index(ifp, IFINDEX_INTERNAL); + if (!ifp->configured) if_delete(&ifp); } @@ -224,6 +224,8 @@ struct interface { not work as expected. */ ifindex_t ifindex; + ifindex_t oldifindex; + /* * ifindex of parent interface, if any */ diff --git a/lib/ipaddr.h b/lib/ipaddr.h index f2b75c1306..730c7ce130 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -25,6 +25,8 @@ #include <zebra.h> +#include "lib/log.h" + #ifdef __cplusplus extern "C" { #endif @@ -59,6 +61,18 @@ struct ipaddr { #define IPADDRSZ(p) \ (IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr)) +static inline int ipaddr_family(const struct ipaddr *ip) +{ + switch (ip->ipa_type) { + case IPADDR_V4: + return AF_INET; + case IPADDR_V6: + return AF_INET6; + default: + return AF_UNSPEC; + } +} + static inline int str2ipaddr(const char *str, struct ipaddr *ip) { int ret; @@ -131,6 +145,31 @@ static inline bool ipaddr_isset(struct ipaddr *ip) return (0 != memcmp(&a, ip, sizeof(struct ipaddr))); } +/* + * generic ordering comparison between IP addresses + */ +static inline int ipaddr_cmp(const struct ipaddr *a, const struct ipaddr *b) +{ + uint32_t va, vb; + va = a->ipa_type; + vb = b->ipa_type; + if (va != vb) + return (va < vb) ? -1 : 1; + switch (a->ipa_type) { + case IPADDR_V4: + va = ntohl(a->ipaddr_v4.s_addr); + vb = ntohl(b->ipaddr_v4.s_addr); + if (va != vb) + return (va < vb) ? -1 : 1; + return 0; + case IPADDR_V6: + return memcmp((void *)&a->ipaddr_v6, (void *)&b->ipaddr_v6, + sizeof(a->ipaddr_v6)); + default: + return 0; + } +} + #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pIA" (struct ipaddr *) #endif diff --git a/lib/keychain.c b/lib/keychain.c index 251211734b..82fd6a65f2 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -1041,6 +1041,8 @@ static int keychain_config_write(struct vty *vty) } vty_out(vty, "\n"); } + + vty_out(vty, " exit\n"); } vty_out(vty, "!\n"); } diff --git a/lib/libfrr.c b/lib/libfrr.c index 2597eb61e2..500a02aacd 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -719,7 +719,7 @@ struct thread_master *frr_init(void) debug_init_cli(); - nb_init(master, di->yang_modules, di->n_yang_modules); + nb_init(master, di->yang_modules, di->n_yang_modules, true); if (nb_db_init() != NB_OK) flog_warn(EC_LIB_NB_DATABASE, "%s: failed to initialize northbound database", diff --git a/lib/linklist.c b/lib/linklist.c index 2936c5b502..84dc6e1419 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -150,6 +150,23 @@ bool listnode_add_sort_nodup(struct list *list, void *val) return true; } +struct list *list_dup(struct list *list) +{ + struct list *dup; + struct listnode *node; + void *data; + + assert(list); + + dup = list_new(); + dup->cmp = list->cmp; + dup->del = list->del; + for (ALL_LIST_ELEMENTS_RO(list, node, data)) + listnode_add(dup, data); + + return dup; +} + void listnode_add_sort(struct list *list, void *val) { struct listnode *n; diff --git a/lib/linklist.h b/lib/linklist.h index 94a1a1604a..d8820c924d 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -327,6 +327,19 @@ extern void list_filter_out_nodes(struct list *list, bool (*cond)(void *data)); */ extern bool listnode_add_sort_nodup(struct list *list, void *val); + +/* + * Duplicate the specified list, creating a shallow copy of each of its + * elements. + * + * list + * list to duplicate + * + * Returns: + * the duplicated list + */ +extern struct list *list_dup(struct list *list); + /* List iteration macro. * Usage: for (ALL_LIST_ELEMENTS (...) { ... } * It is safe to delete the listnode using this macro. @@ -384,6 +384,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_MPLS_LABELS_ADD), DESC_ENTRY(ZEBRA_MPLS_LABELS_DELETE), DESC_ENTRY(ZEBRA_MPLS_LABELS_REPLACE), + DESC_ENTRY(ZEBRA_SR_POLICY_SET), + DESC_ENTRY(ZEBRA_SR_POLICY_DELETE), + DESC_ENTRY(ZEBRA_SR_POLICY_NOTIFY_STATUS), DESC_ENTRY(ZEBRA_IPMR_ROUTE_STATS), DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT), DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT_ASYNC), @@ -448,7 +451,8 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_CLIENT_CAPABILITIES), DESC_ENTRY(ZEBRA_OPAQUE_MESSAGE), DESC_ENTRY(ZEBRA_OPAQUE_REGISTER), - DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER)}; + DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER), + DESC_ENTRY(ZEBRA_NEIGH_DISCOVER)}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; diff --git a/lib/mpls.h b/lib/mpls.h index 126dbf753d..74bd7aae3e 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -72,8 +72,7 @@ extern "C" { /* Maximum # labels that can be pushed. */ #define MPLS_MAX_LABELS 16 -#define IS_MPLS_RESERVED_LABEL(label) \ - (label >= MPLS_LABEL_RESERVED_MIN && label <= MPLS_LABEL_RESERVED_MAX) +#define IS_MPLS_RESERVED_LABEL(label) (label <= MPLS_LABEL_RESERVED_MAX) #define IS_MPLS_UNRESERVED_LABEL(label) \ (label >= MPLS_LABEL_UNRESERVED_MIN \ @@ -129,6 +128,7 @@ enum lsp_types_t { ZEBRA_LSP_OSPF_SR = 4,/* OSPF Segment Routing LSP. */ ZEBRA_LSP_ISIS_SR = 5,/* IS-IS Segment Routing LSP. */ ZEBRA_LSP_SHARP = 6, /* Identifier for test protocol */ + ZEBRA_LSP_SRTE = 7, /* SR-TE LSP */ }; /* Functions for basic label operations. */ diff --git a/lib/nexthop.c b/lib/nexthop.c index 28d96a539c..0ea72d03e1 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -152,6 +152,11 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1, break; } + if (next1->srte_color < next2->srte_color) + return -1; + if (next1->srte_color > next2->srte_color) + return 1; + ret = _nexthop_source_cmp(next1, next2); if (ret != 0) goto done; @@ -643,6 +648,7 @@ void nexthop_copy_no_recurse(struct nexthop *copy, if (copy->backup_num > 0) memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num); + copy->srte_color = nexthop->srte_color; memcpy(©->gate, &nexthop->gate, sizeof(nexthop->gate)); memcpy(©->src, &nexthop->src, sizeof(nexthop->src)); memcpy(©->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src)); diff --git a/lib/nexthop.h b/lib/nexthop.h index ed40cc7eed..cadcea1f41 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -98,6 +98,7 @@ struct nexthop { */ #define NEXTHOP_FLAG_RNH_FILTERED (1 << 5) /* rmap filtered, used by rnh */ #define NEXTHOP_FLAG_HAS_BACKUP (1 << 6) /* Backup nexthop index is set */ +#define NEXTHOP_FLAG_SRTE (1 << 7) /* SR-TE color used for BGP traffic */ #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ @@ -141,6 +142,9 @@ struct nexthop { union { vni_t vni; } nh_encap; + + /* SR-TE color used for matching SR-TE policies */ + uint32_t srte_color; }; /* Utility to append one nexthop to another. */ diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 97815ceeb9..687cac4062 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -940,6 +940,12 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, nhg_hooks.add_nexthop(nhgc, nh); } + if (intf) { + struct interface *ifp = if_lookup_by_name_all_vrf(intf); + + if (ifp) + ifp->configured = true; + } return CMD_SUCCESS; } @@ -952,24 +958,29 @@ static struct cmd_node nexthop_group_node = { .config_write = nexthop_group_write, }; -void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh) +void nexthop_group_write_nexthop_simple(struct vty *vty, + const struct nexthop *nh, + char *altifname) { char buf[100]; - struct vrf *vrf; - int i; + char *ifname; vty_out(vty, "nexthop "); + if (altifname) + ifname = altifname; + else + ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id); + switch (nh->type) { case NEXTHOP_TYPE_IFINDEX: - vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id)); + vty_out(vty, "%s", ifname); break; case NEXTHOP_TYPE_IPV4: vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4)); break; case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), - ifindex2ifname(nh->ifindex, nh->vrf_id)); + vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), ifname); break; case NEXTHOP_TYPE_IPV6: vty_out(vty, "%s", @@ -978,15 +989,23 @@ void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh) case NEXTHOP_TYPE_IPV6_IFINDEX: vty_out(vty, "%s %s", inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)), - ifindex2ifname(nh->ifindex, nh->vrf_id)); + ifname); break; case NEXTHOP_TYPE_BLACKHOLE: break; } +} + +void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh) +{ + struct vrf *vrf; + int i; + + nexthop_group_write_nexthop_simple(vty, nh, NULL); if (nh->vrf_id != VRF_DEFAULT) { vrf = vrf_lookup_by_id(nh->vrf_id); - vty_out(vty, " nexthop-vrf %s", vrf->name); + vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf)); } if (nh->nh_label && nh->nh_label->num_labels > 0) { @@ -1229,6 +1248,7 @@ void nexthop_group_interface_state_change(struct interface *ifp, if (ifp->ifindex != nhop.ifindex) continue; + ifp->configured = true; nh = nexthop_new(); memcpy(nh, &nhop, sizeof(nhop)); diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 0b5ac91bb2..5f7bde0def 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -135,6 +135,9 @@ extern bool nexthop_group_equal(const struct nexthop_group *nhg1, extern struct nexthop_group_cmd *nhgc_find(const char *name); +extern void nexthop_group_write_nexthop_simple(struct vty *vty, + const struct nexthop *nh, + char *altifname); extern void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh); diff --git a/lib/northbound.c b/lib/northbound.c index 48b8499bfc..18500a8bd2 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -57,6 +57,8 @@ static struct { const void *owner_user; } running_config_mgmt_lock; +/* Knob to record config transaction */ +static bool nb_db_enabled; /* * Global lock used to prevent multiple configuration transactions from * happening concurrently. @@ -680,8 +682,12 @@ int nb_candidate_commit_prepare(struct nb_context *context, RB_INIT(nb_config_cbs, &changes); nb_config_diff(running_config, candidate, &changes); - if (RB_EMPTY(nb_config_cbs, &changes)) + if (RB_EMPTY(nb_config_cbs, &changes)) { + snprintf( + errmsg, errmsg_len, + "No changes to apply were found during preparation phase"); return NB_ERR_NO_CHANGES; + } if (nb_candidate_validate_code(context, candidate, &changes, errmsg, errmsg_len) @@ -707,30 +713,28 @@ int nb_candidate_commit_prepare(struct nb_context *context, errmsg_len); } -void nb_candidate_commit_abort(struct nb_transaction *transaction) +void nb_candidate_commit_abort(struct nb_transaction *transaction, char *errmsg, + size_t errmsg_len) { - char errmsg[BUFSIZ] = {0}; - (void)nb_transaction_process(NB_EV_ABORT, transaction, errmsg, - sizeof(errmsg)); + errmsg_len); nb_transaction_free(transaction); } void nb_candidate_commit_apply(struct nb_transaction *transaction, - bool save_transaction, uint32_t *transaction_id) + bool save_transaction, uint32_t *transaction_id, + char *errmsg, size_t errmsg_len) { - char errmsg[BUFSIZ] = {0}; - (void)nb_transaction_process(NB_EV_APPLY, transaction, errmsg, - sizeof(errmsg)); - nb_transaction_apply_finish(transaction, errmsg, sizeof(errmsg)); + errmsg_len); + nb_transaction_apply_finish(transaction, errmsg, errmsg_len); /* Replace running by candidate. */ transaction->config->version++; nb_config_replace(running_config, transaction->config, true); /* Record transaction. */ - if (save_transaction + if (save_transaction && nb_db_enabled && nb_db_transaction_save(transaction, transaction_id) != NB_OK) flog_warn(EC_LIB_NB_TRANSACTION_RECORD_FAILED, "%s: failed to record transaction", __func__); @@ -754,9 +758,9 @@ int nb_candidate_commit(struct nb_context *context, struct nb_config *candidate, */ if (ret == NB_OK) nb_candidate_commit_apply(transaction, save_transaction, - transaction_id); + transaction_id, errmsg, errmsg_len); else if (transaction != NULL) - nb_candidate_commit_abort(transaction); + nb_candidate_commit_abort(transaction, errmsg, errmsg_len); return ret; } @@ -2046,18 +2050,21 @@ void *nb_running_unset_entry(const struct lyd_node *dnode) return entry; } -void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, - bool abort_if_not_found) +static void *nb_running_get_entry_worker(const struct lyd_node *dnode, + const char *xpath, + bool abort_if_not_found, + bool rec_search) { const struct lyd_node *orig_dnode = dnode; char xpath_buf[XPATH_MAXLEN]; + bool rec_flag = true; assert(dnode || xpath); if (!dnode) dnode = yang_dnode_get(running_config->dnode, xpath); - while (dnode) { + while (rec_flag && dnode) { struct nb_config_entry *config, s; yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath)); @@ -2065,6 +2072,8 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, if (config) return config->entry; + rec_flag = rec_search; + dnode = dnode->parent; } @@ -2078,6 +2087,20 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, abort(); } +void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, + bool abort_if_not_found) +{ + return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found, + true); +} + +void *nb_running_get_entry_non_rec(const struct lyd_node *dnode, + const char *xpath, bool abort_if_not_found) +{ + return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found, + false); +} + /* Logging functions. */ const char *nb_event_name(enum nb_event event) { @@ -2195,7 +2218,7 @@ static void nb_load_callbacks(const struct frr_yang_module_info *module) void nb_init(struct thread_master *tm, const struct frr_yang_module_info *const modules[], - size_t nmodules) + size_t nmodules, bool db_enabled) { unsigned int errors = 0; @@ -2220,6 +2243,8 @@ void nb_init(struct thread_master *tm, exit(1); } + nb_db_enabled = db_enabled; + /* Create an empty running configuration. */ running_config = nb_config_new(NULL); running_config_entries = hash_create(running_config_entry_key_make, diff --git a/lib/northbound.h b/lib/northbound.h index bd57013f59..7b481273cb 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -910,8 +910,15 @@ extern int nb_candidate_commit_prepare(struct nb_context *context, * * transaction * Candidate configuration to abort. It's consumed by this function. + * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. */ -extern void nb_candidate_commit_abort(struct nb_transaction *transaction); +extern void nb_candidate_commit_abort(struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len); /* * Commit a previously created configuration transaction. @@ -925,10 +932,17 @@ extern void nb_candidate_commit_abort(struct nb_transaction *transaction); * * transaction_id * Optional output parameter providing the ID of the committed transaction. + * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. */ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, bool save_transaction, - uint32_t *transaction_id); + uint32_t *transaction_id, char *errmsg, + size_t errmsg_len); /* * Create a new transaction to commit a candidate configuration. This is a @@ -1165,6 +1179,14 @@ extern void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, bool abort_if_not_found); /* + * Same as 'nb_running_get_entry', but doesn't search within parent nodes + * recursively if an user point is not found. + */ +extern void *nb_running_get_entry_non_rec(const struct lyd_node *dnode, + const char *xpath, + bool abort_if_not_found); + +/* * Return a human-readable string representing a northbound event. * * event @@ -1217,10 +1239,13 @@ extern const char *nb_client_name(enum nb_client client); * * nmodules * Size of the modules array. + * + * db_enabled + * Set this to record the transactions in the transaction log. */ extern void nb_init(struct thread_master *tm, const struct frr_yang_module_info *const modules[], - size_t nmodules); + size_t nmodules, bool db_enabled); /* * Finish the northbound layer gracefully. Should be called only when the daemon diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 534b5128ee..ee080bca3f 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -63,7 +63,15 @@ static int nb_cli_classic_commit(struct vty *vty) context.user = vty; ret = nb_candidate_commit(&context, vty->candidate_config, true, NULL, NULL, errmsg, sizeof(errmsg)); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { + switch (ret) { + case NB_OK: + /* Successful commit. Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); + break; + case NB_ERR_NO_CHANGES: + break; + default: vty_out(vty, "%% Configuration failed.\n\n"); vty_show_nb_errors(vty, ret, errmsg); if (vty->t_pending_commit) @@ -318,11 +326,14 @@ int nb_cli_confirmed_commit_rollback(struct vty *vty) &context, vty->confirmed_commit_rollback, true, "Rollback to previous configuration - confirmed commit has timed out", &transaction_id, errmsg, sizeof(errmsg)); - if (ret == NB_OK) + if (ret == NB_OK) { vty_out(vty, "Rollback performed successfully (Transaction ID #%u).\n", transaction_id); - else { + /* Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); + } else { vty_out(vty, "Failed to rollback to previous configuration.\n\n"); vty_show_nb_errors(vty, ret, errmsg); @@ -407,6 +418,9 @@ static int nb_cli_commit(struct vty *vty, bool force, vty_out(vty, "%% Configuration committed successfully (Transaction ID #%u).\n\n", transaction_id); + /* Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); return CMD_SUCCESS; case NB_ERR_NO_CHANGES: vty_out(vty, "%% No configuration changes to commit.\n\n"); @@ -1666,6 +1680,9 @@ static int nb_cli_rollback_configuration(struct vty *vty, case NB_OK: vty_out(vty, "%% Configuration was successfully rolled back.\n\n"); + /* Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); return CMD_SUCCESS; case NB_ERR_NO_CHANGES: vty_out(vty, diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index a3aaf02f08..1f480f3d02 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -375,8 +375,10 @@ static int frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen) /* Apply the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_apply(transaction, true, NULL); + nb_candidate_commit_apply(transaction, true, NULL, errmsg, + sizeof(errmsg)); nb_config_free(candidate); } @@ -400,8 +402,9 @@ static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen) /* Abort the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_abort(transaction); + nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg)); nb_config_free(candidate); } diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 83d7e0ce95..f35b4bb31b 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -677,11 +677,13 @@ class NorthboundImpl switch (phase) { case frr::CommitRequest::VALIDATE: + zlog_debug("`-> Performing VALIDATE"); ret = nb_candidate_validate( &context, candidate->config, errmsg, sizeof(errmsg)); break; case frr::CommitRequest::PREPARE: + zlog_debug("`-> Performing PREPARE"); ret = nb_candidate_commit_prepare( &context, candidate->config, comment.c_str(), @@ -689,15 +691,20 @@ class NorthboundImpl sizeof(errmsg)); break; case frr::CommitRequest::ABORT: + zlog_debug("`-> Performing ABORT"); nb_candidate_commit_abort( - candidate->transaction); + candidate->transaction, errmsg, + sizeof(errmsg)); break; case frr::CommitRequest::APPLY: + zlog_debug("`-> Performing ABORT"); nb_candidate_commit_apply( candidate->transaction, true, - &transaction_id); + &transaction_id, errmsg, + sizeof(errmsg)); break; case frr::CommitRequest::ALL: + zlog_debug("`-> Performing ALL"); ret = nb_candidate_commit( &context, candidate->config, true, comment.c_str(), &transaction_id, @@ -735,12 +742,20 @@ class NorthboundImpl grpc::StatusCode::INTERNAL, errmsg); break; } + + if (nb_dbg_client_grpc) + zlog_debug("`-> Result: %s (message: '%s')", + nb_err_name((enum nb_error)ret), + errmsg); + if (ret == NB_OK) { // Response: uint32 transaction_id = 1; if (transaction_id) tag->response.set_transaction_id( transaction_id); } + if (strlen(errmsg) > 0) + tag->response.set_error_message(errmsg); tag->responder.Finish(tag->response, status, tag); tag->state = FINISH; @@ -1281,10 +1296,13 @@ class NorthboundImpl void delete_candidate(struct candidate *candidate) { + char errmsg[BUFSIZ] = {0}; + _candidates.erase(candidate->id); nb_config_free(candidate->config); if (candidate->transaction) - nb_candidate_commit_abort(candidate->transaction); + nb_candidate_commit_abort(candidate->transaction, + errmsg, sizeof(errmsg)); } struct candidate *get_candidate(uint32_t candidate_id) diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 500203173c..3dec685927 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -37,13 +37,11 @@ DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module") static struct debug nb_dbg_client_sysrepo = {0, "Northbound client: Sysrepo"}; static struct thread_master *master; -static struct list *sysrepo_threads; static sr_session_ctx_t *session; static sr_conn_ctx_t *connection; static struct nb_transaction *transaction; static int frr_sr_read_cb(struct thread *thread); -static int frr_sr_write_cb(struct thread *thread); static int frr_sr_finish(void); /* Convert FRR YANG data value to sysrepo YANG data value. */ @@ -236,25 +234,23 @@ static int frr_sr_process_change(struct nb_config *candidate, return NB_OK; } -static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, - const char *module_name, - bool startup_config) +static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session, + const char *module_name, + bool startup_config) { sr_change_iter_t *it; int ret; sr_change_oper_t sr_op; sr_val_t *sr_old_val, *sr_new_val; - char xpath[XPATH_MAXLEN]; struct nb_context context = {}; struct nb_config *candidate; char errmsg[BUFSIZ] = {0}; - snprintf(xpath, sizeof(xpath), "/%s:*", module_name); - ret = sr_get_changes_iter(session, xpath, &it); + ret = sr_get_changes_iter(session, "//*", &it); if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, - "%s: sr_get_changes_iter() failed for xpath %s", - __func__, xpath); + "%s: sr_get_changes_iter() failed for \"%s\"", + __func__, module_name); return ret; } @@ -307,12 +303,14 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, __func__, nb_err_name(ret), errmsg); } + if (!transaction) + nb_config_free(candidate); + /* Map northbound return code to sysrepo return code. */ switch (ret) { case NB_OK: return SR_ERR_OK; case NB_ERR_NO_CHANGES: - nb_config_free(candidate); return SR_ERR_OK; case NB_ERR_LOCKED: return SR_ERR_LOCKED; @@ -329,8 +327,10 @@ static int frr_sr_config_change_cb_apply(sr_session_ctx_t *session, /* Apply the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_apply(transaction, true, NULL); + nb_candidate_commit_apply(transaction, true, NULL, errmsg, + sizeof(errmsg)); nb_config_free(candidate); } @@ -343,8 +343,9 @@ static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session, /* Abort the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_abort(transaction); + nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg)); nb_config_free(candidate); } @@ -353,22 +354,23 @@ static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session, /* Callback for changes in the running configuration. */ static int frr_sr_config_change_cb(sr_session_ctx_t *session, - const char *module_name, - sr_notif_event_t sr_ev, void *private_ctx) + const char *module_name, const char *xpath, + sr_event_t sr_ev, uint32_t request_id, + void *private_data) { switch (sr_ev) { case SR_EV_ENABLED: - return frr_sr_config_change_cb_verify(session, module_name, - true); - case SR_EV_VERIFY: - return frr_sr_config_change_cb_verify(session, module_name, - false); - case SR_EV_APPLY: + return frr_sr_config_change_cb_prepare(session, module_name, + true); + case SR_EV_CHANGE: + return frr_sr_config_change_cb_prepare(session, module_name, + false); + case SR_EV_DONE: return frr_sr_config_change_cb_apply(session, module_name); case SR_EV_ABORT: return frr_sr_config_change_cb_abort(session, module_name); default: - flog_err(EC_LIB_LIBSYSREPO, "%s: unknown sysrepo event: %u", + flog_err(EC_LIB_LIBSYSREPO, "%s: unexpected sysrepo event: %u", __func__, sr_ev); return SR_ERR_INTERNAL; } @@ -378,70 +380,49 @@ static int frr_sr_state_data_iter_cb(const struct lys_node *snode, struct yang_translator *translator, struct yang_data *data, void *arg) { - struct list *elements = arg; - - listnode_add(elements, data); + struct lyd_node *dnode = arg; + + ly_errno = 0; + dnode = lyd_new_path(dnode, ly_native_ctx, data->xpath, data->value, 0, + LYD_PATH_OPT_UPDATE); + if (!dnode && ly_errno) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", + __func__); + yang_data_free(data); + return NB_ERR; + } + yang_data_free(data); return NB_OK; } /* Callback for state retrieval. */ -static int frr_sr_state_cb(const char *xpath, sr_val_t **values, - size_t *values_cnt, uint64_t request_id, - const char *original_xpath, void *private_ctx) +static int frr_sr_state_cb(sr_session_ctx_t *session, const char *module_name, + const char *xpath, const char *request_xpath, + uint32_t request_id, struct lyd_node **parent, + void *private_ctx) { - struct list *elements; - struct yang_data *data; - struct listnode *node; - sr_val_t *v; - int ret, count, i = 0; + struct lyd_node *dnode; - elements = yang_data_list_new(); - if (nb_oper_data_iterate(xpath, NULL, NB_OPER_DATA_ITER_NORECURSE, - frr_sr_state_data_iter_cb, elements) + dnode = *parent; + if (nb_oper_data_iterate(request_xpath, NULL, 0, + frr_sr_state_data_iter_cb, dnode) != NB_OK) { flog_warn(EC_LIB_NB_OPERATIONAL_DATA, "%s: failed to obtain operational data [xpath %s]", __func__, xpath); - goto exit; - } - - if (list_isempty(elements)) - goto exit; - - count = listcount(elements); - ret = sr_new_values(count, &v); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s", __func__, - sr_strerror(ret)); - goto exit; - } - - for (ALL_LIST_ELEMENTS_RO(elements, node, data)) { - if (yang_data_frr2sr(data, &v[i++]) != 0) { - flog_err(EC_LIB_SYSREPO_DATA_CONVERT, - "%s: failed to convert data to sysrepo format", - __func__); - } + return SR_ERR_INTERNAL; } - *values = v; - *values_cnt = count; - - list_delete(&elements); - - return SR_ERR_OK; - -exit: - list_delete(&elements); - *values = NULL; - *values_cnt = 0; + *parent = dnode; return SR_ERR_OK; } -static int frr_sr_config_rpc_cb(const char *xpath, const sr_val_t *sr_input, - const size_t input_cnt, sr_val_t **sr_output, +static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, const char *xpath, + const sr_val_t *sr_input, + const size_t input_cnt, sr_event_t sr_ev, + uint32_t request_id, sr_val_t **sr_output, size_t *sr_output_cnt, void *private_ctx) { struct nb_node *nb_node; @@ -548,8 +529,7 @@ static int frr_sr_notification_send(const char *xpath, struct list *arguments) } } - ret = sr_event_notif_send(session, xpath, values, values_cnt, - SR_EV_NOTIF_DEFAULT); + ret = sr_event_notif_send(session, xpath, values, values_cnt); if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, "%s: sr_event_notif_send() failed for xpath %s", @@ -560,102 +540,13 @@ static int frr_sr_notification_send(const char *xpath, struct list *arguments) return NB_OK; } -/* Code to integrate the sysrepo client into FRR main event loop. */ -struct sysrepo_thread { - struct thread *thread; - sr_fd_event_t event; - int fd; -}; - -static struct sysrepo_thread *frr_sr_fd_lookup(sr_fd_event_t event, int fd) -{ - struct sysrepo_thread *sr_thread; - struct listnode *node; - - for (ALL_LIST_ELEMENTS_RO(sysrepo_threads, node, sr_thread)) { - if (sr_thread->event == event && sr_thread->fd == fd) - return sr_thread; - } - - return NULL; -} - -static void frr_sr_fd_add(int event, int fd) -{ - struct sysrepo_thread *sr_thread; - - if (frr_sr_fd_lookup(event, fd) != NULL) - return; - - sr_thread = XCALLOC(MTYPE_SYSREPO, sizeof(*sr_thread)); - sr_thread->event = event; - sr_thread->fd = fd; - listnode_add(sysrepo_threads, sr_thread); - - switch (event) { - case SR_FD_INPUT_READY: - thread_add_read(master, frr_sr_read_cb, NULL, fd, - &sr_thread->thread); - break; - case SR_FD_OUTPUT_READY: - thread_add_write(master, frr_sr_write_cb, NULL, fd, - &sr_thread->thread); - break; - default: - return; - } -} - -static void frr_sr_fd_free(struct sysrepo_thread *sr_thread) -{ - THREAD_OFF(sr_thread->thread); - XFREE(MTYPE_SYSREPO, sr_thread); -} - -static void frr_sr_fd_del(int event, int fd) -{ - struct sysrepo_thread *sr_thread; - - sr_thread = frr_sr_fd_lookup(event, fd); - if (!sr_thread) - return; - - listnode_delete(sysrepo_threads, sr_thread); - frr_sr_fd_free(sr_thread); -} - -static void frr_sr_fd_update(sr_fd_change_t *fd_change_set, - size_t fd_change_set_cnt) -{ - for (size_t i = 0; i < fd_change_set_cnt; i++) { - int fd = fd_change_set[i].fd; - int event = fd_change_set[i].events; - - if (event != SR_FD_INPUT_READY && event != SR_FD_OUTPUT_READY) - continue; - - switch (fd_change_set[i].action) { - case SR_FD_START_WATCHING: - frr_sr_fd_add(event, fd); - break; - case SR_FD_STOP_WATCHING: - frr_sr_fd_del(event, fd); - break; - default: - break; - } - } -} - static int frr_sr_read_cb(struct thread *thread) { + sr_subscription_ctx_t *sr_subscription = THREAD_ARG(thread); int fd = THREAD_FD(thread); - sr_fd_change_t *fd_change_set = NULL; - size_t fd_change_set_cnt = 0; int ret; - ret = sr_fd_event_process(fd, SR_FD_INPUT_READY, &fd_change_set, - &fd_change_set_cnt); + ret = sr_process_events(sr_subscription, session, NULL); if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s", __func__, sr_strerror(ret)); @@ -663,31 +554,7 @@ static int frr_sr_read_cb(struct thread *thread) } thread = NULL; - thread_add_read(master, frr_sr_read_cb, NULL, fd, &thread); - - frr_sr_fd_update(fd_change_set, fd_change_set_cnt); - free(fd_change_set); - - return 0; -} - -static int frr_sr_write_cb(struct thread *thread) -{ - int fd = THREAD_FD(thread); - sr_fd_change_t *fd_change_set = NULL; - size_t fd_change_set_cnt = 0; - int ret; - - ret = sr_fd_event_process(fd, SR_FD_OUTPUT_READY, &fd_change_set, - &fd_change_set_cnt); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s", - __func__, sr_strerror(ret)); - return -1; - } - - frr_sr_fd_update(fd_change_set, fd_change_set_cnt); - free(fd_change_set); + thread_add_read(master, frr_sr_read_cb, sr_subscription, fd, &thread); return 0; } @@ -697,8 +564,8 @@ static void frr_sr_subscribe_config(struct yang_module *module) int ret; ret = sr_module_change_subscribe( - session, module->name, frr_sr_config_change_cb, NULL, 0, - SR_SUBSCR_DEFAULT | SR_SUBSCR_EV_ENABLED, + session, module->name, NULL, frr_sr_config_change_cb, NULL, 0, + SR_SUBSCR_DEFAULT | SR_SUBSCR_ENABLED | SR_SUBSCR_NO_THREAD, &module->sr_subscription); if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_module_change_subscribe(): %s", @@ -722,11 +589,11 @@ static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg) DEBUGD(&nb_dbg_client_sysrepo, "%s: providing data to '%s'", __func__, nb_node->xpath); - ret = sr_dp_get_items_subscribe( - session, nb_node->xpath, frr_sr_state_cb, NULL, - SR_SUBSCR_CTX_REUSE, &module->sr_subscription); + ret = sr_oper_get_items_subscribe( + session, snode->module->name, nb_node->xpath, frr_sr_state_cb, + NULL, SR_SUBSCR_CTX_REUSE, &module->sr_subscription); if (ret != SR_ERR_OK) - flog_err(EC_LIB_LIBSYSREPO, "sr_dp_get_items_subscribe(): %s", + flog_err(EC_LIB_LIBSYSREPO, "sr_oper_get_items_subscribe(): %s", sr_strerror(ret)); return YANG_ITER_CONTINUE; @@ -747,7 +614,7 @@ static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg) nb_node->xpath); ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, - NULL, SR_SUBSCR_CTX_REUSE, + NULL, 0, SR_SUBSCR_CTX_REUSE, &module->sr_subscription); if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s", @@ -756,30 +623,6 @@ static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg) return YANG_ITER_CONTINUE; } -static int frr_sr_subscribe_action(const struct lys_node *snode, void *arg) -{ - struct yang_module *module = arg; - struct nb_node *nb_node; - int ret; - - if (snode->nodetype != LYS_ACTION) - return YANG_ITER_CONTINUE; - - nb_node = snode->priv; - - DEBUGD(&nb_dbg_client_sysrepo, "%s: providing action to '%s'", __func__, - nb_node->xpath); - - ret = sr_action_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, - NULL, SR_SUBSCR_CTX_REUSE, - &module->sr_subscription); - if (ret != SR_ERR_OK) - flog_err(EC_LIB_LIBSYSREPO, "sr_action_subscribe(): %s", - sr_strerror(ret)); - - return YANG_ITER_CONTINUE; -} - /* CLI commands. */ DEFUN (debug_nb_sr, debug_nb_sr_cmd, @@ -827,22 +670,13 @@ static void frr_sr_cli_init(void) } /* FRR's Sysrepo initialization. */ -static int frr_sr_init(const char *program_name) +static int frr_sr_init(void) { struct yang_module *module; - int sysrepo_fd, ret; - - sysrepo_threads = list_new(); - - ret = sr_fd_watcher_init(&sysrepo_fd, NULL); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_fd_watcher_init(): %s", - __func__, sr_strerror(ret)); - goto cleanup; - } + int ret; /* Connect to Sysrepo. */ - ret = sr_connect(program_name, SR_CONN_DEFAULT, &connection); + ret = sr_connect(SR_CONN_DEFAULT, &connection); if (ret != SR_ERR_OK) { flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_connect(): %s", __func__, sr_strerror(ret)); @@ -850,8 +684,7 @@ static int frr_sr_init(const char *program_name) } /* Start session. */ - ret = sr_session_start(connection, SR_DS_RUNNING, SR_SESS_DEFAULT, - &session); + ret = sr_session_start(connection, SR_DS_RUNNING, &session); if (ret != SR_ERR_OK) { flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_session_start(): %s", __func__, sr_strerror(ret)); @@ -860,19 +693,28 @@ static int frr_sr_init(const char *program_name) /* Perform subscriptions. */ RB_FOREACH (module, yang_modules, &yang_modules) { + int event_pipe; + frr_sr_subscribe_config(module); yang_snodes_iterate_module(module->info, frr_sr_subscribe_state, 0, module); yang_snodes_iterate_module(module->info, frr_sr_subscribe_rpc, 0, module); - yang_snodes_iterate_module(module->info, - frr_sr_subscribe_action, 0, module); + + /* Watch subscriptions. */ + ret = sr_get_event_pipe(module->sr_subscription, &event_pipe); + if (ret != SR_ERR_OK) { + flog_err(EC_LIB_SYSREPO_INIT, + "%s: sr_get_event_pipe(): %s", __func__, + sr_strerror(ret)); + goto cleanup; + } + thread_add_read(master, frr_sr_read_cb, module->sr_subscription, + event_pipe, &module->sr_thread); } hook_register(nb_notification_send, frr_sr_notification_send); - frr_sr_fd_add(SR_FD_INPUT_READY, sysrepo_fd); - return 0; cleanup: @@ -888,7 +730,8 @@ static int frr_sr_finish(void) RB_FOREACH (module, yang_modules, &yang_modules) { if (!module->sr_subscription) continue; - sr_unsubscribe(session, module->sr_subscription); + sr_unsubscribe(module->sr_subscription); + THREAD_OFF(module->sr_thread); } if (session) @@ -896,10 +739,6 @@ static int frr_sr_finish(void) if (connection) sr_disconnect(connection); - sysrepo_threads->del = (void (*)(void *))frr_sr_fd_free; - list_delete(&sysrepo_threads); - sr_fd_watcher_cleanup(); - return 0; } @@ -907,7 +746,7 @@ static int frr_sr_module_late_init(struct thread_master *tm) { master = tm; - if (frr_sr_init(frr_get_progname()) < 0) { + if (frr_sr_init() < 0) { flog_err(EC_LIB_SYSREPO_INIT, "failed to initialize the Sysrepo module"); return -1; @@ -129,6 +129,8 @@ struct pbr_rule { #define MATCH_FRAGMENT_INVERSE_SET (1 << 9) #define MATCH_ICMP_SET (1 << 10) #define MATCH_PROTOCOL_SET (1 << 11) +#define MATCH_FLOW_LABEL_SET (1 << 12) +#define MATCH_FLOW_LABEL_INVERSE_SET (1 << 13) extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); diff --git a/lib/prefix.c b/lib/prefix.c index 697e1a6239..0a88f9eca6 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -188,6 +188,10 @@ int prefix_match(const struct prefix *n, const struct prefix *p) if (n->family == AF_FLOWSPEC) { /* prefixlen is unused. look at fs prefix len */ + if (n->u.prefix_flowspec.family != + p->u.prefix_flowspec.family) + return 0; + if (n->u.prefix_flowspec.prefixlen > p->u.prefix_flowspec.prefixlen) return 0; @@ -325,6 +329,8 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc) len = src->u.prefix_flowspec.prefixlen; dest->u.prefix_flowspec.prefixlen = src->u.prefix_flowspec.prefixlen; + dest->u.prefix_flowspec.family = + src->u.prefix_flowspec.family; dest->family = src->family; temp = XCALLOC(MTYPE_PREFIX_FLOWSPEC, len); dest->u.prefix_flowspec.ptr = (uintptr_t)temp; @@ -374,6 +380,9 @@ int prefix_same(union prefixconstptr up1, union prefixconstptr up2) sizeof(struct evpn_addr))) return 1; if (p1->family == AF_FLOWSPEC) { + if (p1->u.prefix_flowspec.family != + p2->u.prefix_flowspec.family) + return 0; if (p1->u.prefix_flowspec.prefixlen != p2->u.prefix_flowspec.prefixlen) return 0; @@ -415,6 +424,10 @@ int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2) pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr; pp2 = (const uint8_t *)p2->u.prefix_flowspec.ptr; + if (p1->u.prefix_flowspec.family != + p2->u.prefix_flowspec.family) + return 1; + if (p1->u.prefix_flowspec.prefixlen != p2->u.prefix_flowspec.prefixlen) return numcmp(p1->u.prefix_flowspec.prefixlen, diff --git a/lib/prefix.h b/lib/prefix.h index 400f07386f..2a33d532c8 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -176,6 +176,7 @@ struct evpn_addr { #endif struct flowspec_prefix { + uint8_t family; uint16_t prefixlen; /* length in bytes */ uintptr_t ptr; }; diff --git a/lib/privs.c b/lib/privs.c index 5c7e1240e2..dc43b7279d 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -1020,11 +1020,11 @@ void zprivs_get_ids(struct zprivs_ids_t *ids) ids->uid_priv = getuid(); (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid) - : (ids->uid_normal = -1); + : (ids->uid_normal = (uid_t)-1); (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid) - : (ids->gid_normal = -1); + : (ids->gid_normal = (uid_t)-1); (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp) - : (ids->gid_vty = -1); + : (ids->gid_vty = (uid_t)-1); return; } diff --git a/lib/privs.h b/lib/privs.h index db5707d675..18ba8e8888 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -24,6 +24,7 @@ #define _ZEBRA_PRIVS_H #include <pthread.h> +#include <stdint.h> #include "lib/queue.h" #ifdef __cplusplus diff --git a/lib/route_types.txt b/lib/route_types.txt index 71d0a46449..b549c11cfc 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -85,6 +85,7 @@ ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD" ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric" ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP" ZEBRA_ROUTE_NHG, nhg, none, '-', 0, 0, 0, "Nexthop Group" +ZEBRA_ROUTE_SRTE, srte, none, '-', 0, 0, 0, "SR-TE" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-" diff --git a/lib/routemap.c b/lib/routemap.c index df9a6a33ea..fb70860024 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -263,6 +263,24 @@ void route_map_no_match_tag_hook(int (*func)( rmap_match_set_hook.no_match_tag = func; } +/* set sr-te color */ +void route_map_set_srte_color_hook(int (*func)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.set_srte_color = func; +} + +/* no set sr-te color */ +void route_map_no_set_srte_color_hook(int (*func)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.no_set_srte_color = func; +} + /* set ip nexthop */ void route_map_set_ip_nexthop_hook(int (*func)(struct vty *vty, struct route_map_index *index, @@ -2613,6 +2631,47 @@ static unsigned int route_map_dep_data_hash_make_key(const void *p) return string_hash_make(dep_data->rname); } +DEFUN (set_srte_color, + set_srte_color_cmd, + "set sr-te color [(1-4294967295)]", + SET_STR + SRTE_STR + SRTE_COLOR_STR + "Color of the SR-TE Policies to match with\n") +{ + VTY_DECLVAR_CONTEXT(route_map_index, index); + int idx = 0; + char *arg = argv_find(argv, argc, "(1-4294967295)", &idx) + ? argv[idx]->arg + : NULL; + + if (rmap_match_set_hook.set_srte_color) + return rmap_match_set_hook.set_srte_color(vty, index, + "sr-te color", arg); + return CMD_SUCCESS; +} + +DEFUN (no_set_srte_color, + no_set_srte_color_cmd, + "no set sr-te color [(1-4294967295)]", + NO_STR + SET_STR + SRTE_STR + SRTE_COLOR_STR + "Color of the SR-TE Policies to match with\n") +{ + VTY_DECLVAR_CONTEXT(route_map_index, index); + int idx = 0; + char *arg = argv_find(argv, argc, "(1-4294967295)", &idx) + ? argv[idx]->arg + : NULL; + + if (rmap_match_set_hook.no_set_srte_color) + return rmap_match_set_hook.no_set_srte_color( + vty, index, "sr-te color", arg); + return CMD_SUCCESS; +} + static void *route_map_dep_hash_alloc(void *p) { char *dep_name = (char *)p; @@ -3237,5 +3296,8 @@ void route_map_init(void) install_element(RMAP_NODE, &routemap_optimization_cmd); install_element(RMAP_NODE, &no_routemap_optimization_cmd); + install_element(RMAP_NODE, &set_srte_color_cmd); + install_element(RMAP_NODE, &no_set_srte_color_cmd); + install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd); } diff --git a/lib/routemap.h b/lib/routemap.h index 62195b8349..64da4b87ef 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -424,6 +424,14 @@ extern void route_map_match_tag_hook(int (*func)( extern void route_map_no_match_tag_hook(int (*func)( struct vty *vty, struct route_map_index *index, const char *command, const char *arg, route_map_event_t type)); +/* set sr-te color */ +extern void route_map_set_srte_color_hook( + int (*func)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg)); +/* no set sr-te color */ +extern void route_map_no_set_srte_color_hook( + int (*func)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg)); /* set ip nexthop */ extern void route_map_set_ip_nexthop_hook( int (*func)(struct vty *vty, struct route_map_index *index, @@ -606,6 +614,14 @@ struct route_map_match_set_hooks { const char *command, const char *arg, route_map_event_t type); + /* set sr-te color */ + int (*set_srte_color)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* no set sr-te color */ + int (*no_set_srte_color)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + /* set ip nexthop */ int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index, const char *command, const char *arg); diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 7fa759252b..836be38113 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -951,7 +951,8 @@ DEFPY_YANG( no_rmap_call, no_rmap_call_cmd, "no call [NAME]", NO_STR - "Jump to another Route-Map after match+set\n") + "Jump to another Route-Map after match+set\n" + "Target route-map name\n") { nb_cli_enqueue_change(vty, "./call", NB_OP_DESTROY, NULL); diff --git a/lib/srte.h b/lib/srte.h new file mode 100644 index 0000000000..d468c1cac9 --- /dev/null +++ b/lib/srte.h @@ -0,0 +1,56 @@ +/* + * SR-TE definitions + * Copyright 2020 NetDef Inc. + * Sascha Kattelmann + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_SRTE_H +#define _FRR_SRTE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define SRTE_POLICY_NAME_MAX_LENGTH 64 + +enum zebra_sr_policy_status { + ZEBRA_SR_POLICY_UP = 0, + ZEBRA_SR_POLICY_DOWN, +}; + +static inline int sr_policy_compare(const struct ipaddr *a_endpoint, + const struct ipaddr *b_endpoint, + uint32_t a_color, uint32_t b_color) +{ + int ret; + + ret = ipaddr_cmp(a_endpoint, b_endpoint); + if (ret < 0) + return -1; + if (ret > 0) + return 1; + + return a_color - b_color; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_SRTE_H */ diff --git a/lib/stream.c b/lib/stream.c index d3afebbf13..dc207c16a4 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -256,6 +256,42 @@ void stream_forward_getp(struct stream *s, size_t size) s->getp += size; } +bool stream_forward_getp2(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (!GETP_VALID(s, s->getp + size)) + return false; + + s->getp += size; + + return true; +} + +void stream_rewind_getp(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (size > s->getp || !GETP_VALID(s, s->getp - size)) { + STREAM_BOUND_WARN(s, "rewind getp"); + return; + } + + s->getp -= size; +} + +bool stream_rewind_getp2(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (size > s->getp || !GETP_VALID(s, s->getp - size)) + return false; + + s->getp -= size; + + return true; +} + void stream_forward_endp(struct stream *s, size_t size) { STREAM_VERIFY_SANE(s); @@ -268,6 +304,18 @@ void stream_forward_endp(struct stream *s, size_t size) s->endp += size; } +bool stream_forward_endp2(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (!ENDP_VALID(s, s->endp + size)) + return false; + + s->endp += size; + + return true; +} + /* Copy from stream to destination. */ bool stream_get2(void *dst, struct stream *s, size_t size) { @@ -586,6 +634,43 @@ uint32_t stream_get_ipv4(struct stream *s) return l; } +bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip) +{ + uint16_t ipa_len; + + STREAM_VERIFY_SANE(s); + + /* Get address type. */ + if (STREAM_READABLE(s) < sizeof(uint16_t)) { + STREAM_BOUND_WARN2(s, "get ipaddr"); + return false; + } + ip->ipa_type = stream_getw(s); + + /* Get address value. */ + switch (ip->ipa_type) { + case IPADDR_V4: + ipa_len = IPV4_MAX_BYTELEN; + break; + case IPADDR_V6: + ipa_len = IPV6_MAX_BYTELEN; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown ip address-family: %u", __func__, + ip->ipa_type); + return false; + } + if (STREAM_READABLE(s) < ipa_len) { + STREAM_BOUND_WARN2(s, "get ipaddr"); + return false; + } + memcpy(&ip->ip, s->data + s->getp, ipa_len); + s->getp += ipa_len; + + return true; +} + float stream_getf(struct stream *s) { union { @@ -852,6 +937,27 @@ int stream_put_in_addr(struct stream *s, const struct in_addr *addr) return sizeof(uint32_t); } +bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip) +{ + stream_putw(s, ip->ipa_type); + + switch (ip->ipa_type) { + case IPADDR_V4: + stream_put_in_addr(s, &ip->ipaddr_v4); + break; + case IPADDR_V6: + stream_write(s, (uint8_t *)&ip->ipaddr_v6, 16); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown ip address-family: %u", __func__, + ip->ipa_type); + return false; + } + + return true; +} + /* Put in_addr at location in the stream. */ int stream_put_in_addr_at(struct stream *s, size_t putp, const struct in_addr *addr) diff --git a/lib/stream.h b/lib/stream.h index 245f35db51..23f85d809b 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -160,7 +160,6 @@ extern size_t stream_resize_inplace(struct stream **sptr, size_t newsize); extern size_t stream_get_getp(const struct stream *s); extern size_t stream_get_endp(const struct stream *s); extern size_t stream_get_size(const struct stream *s); -extern uint8_t *stream_get_data(struct stream *s); /** * Create a new stream structure; copy offset bytes from s1 to the new @@ -173,7 +172,11 @@ extern struct stream *stream_dupcat(const struct stream *s1, extern void stream_set_getp(struct stream *, size_t); extern void stream_set_endp(struct stream *, size_t); extern void stream_forward_getp(struct stream *, size_t); +extern bool stream_forward_getp2(struct stream *, size_t); +extern void stream_rewind_getp(struct stream *s, size_t size); +extern bool stream_rewind_getp2(struct stream *s, size_t size); extern void stream_forward_endp(struct stream *, size_t); +extern bool stream_forward_endp2(struct stream *, size_t); /* steam_put: NULL source zeroes out size_t bytes of stream */ extern void stream_put(struct stream *, const void *, size_t); @@ -189,6 +192,7 @@ extern int stream_putq(struct stream *, uint64_t); extern int stream_putq_at(struct stream *, size_t, uint64_t); extern int stream_put_ipv4(struct stream *, uint32_t); extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr); +extern bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip); extern int stream_put_in_addr_at(struct stream *s, size_t putp, const struct in_addr *addr); extern int stream_put_in6_addr_at(struct stream *s, size_t putp, @@ -219,6 +223,7 @@ extern uint64_t stream_getq(struct stream *); extern uint64_t stream_getq_from(struct stream *, size_t); bool stream_getq2(struct stream *s, uint64_t *q); extern uint32_t stream_get_ipv4(struct stream *); +extern bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip); /* IEEE-754 floats */ extern float stream_getf(struct stream *); @@ -439,12 +444,36 @@ static inline const uint8_t *ptr_get_be32(const uint8_t *ptr, uint32_t *out) (P) = _pval; \ } while (0) +#define STREAM_GET_IPADDR(S, P) \ + do { \ + if (!stream_get_ipaddr((S), (P))) \ + goto stream_failure; \ + } while (0) + #define STREAM_GET(P, STR, SIZE) \ do { \ if (!stream_get2((P), (STR), (SIZE))) \ goto stream_failure; \ } while (0) +#define STREAM_FORWARD_GETP(STR, SIZE) \ + do { \ + if (!stream_forward_getp2((STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + +#define STREAM_REWIND_GETP(STR, SIZE) \ + do { \ + if (!stream_rewind_getp2((STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + +#define STREAM_FORWARD_ENDP(STR, SIZE) \ + do { \ + if (!stream_forward_endp2((STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + #ifdef __cplusplus } #endif diff --git a/lib/subdir.am b/lib/subdir.am index 34ad30f968..1feaa56d13 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -242,6 +242,7 @@ pkginclude_HEADERS += \ lib/sockunion.h \ lib/spf_backoff.h \ lib/srcdest_table.h \ + lib/srte.h \ lib/stream.h \ lib/systemd.h \ lib/table.h \ diff --git a/lib/thread.c b/lib/thread.c index 1df4eee25c..19e4827283 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -726,17 +726,16 @@ static void thread_free(struct thread_master *master, struct thread *thread) static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize, nfds_t count, const struct timeval *timer_wait) { - /* If timer_wait is null here, that means poll() should block - * indefinitely, - * unless the thread_master has overridden it by setting + /* + * If timer_wait is null here, that means poll() should block + * indefinitely, unless the thread_master has overridden it by setting * ->selectpoll_timeout. + * * If the value is positive, it specifies the maximum number of - * milliseconds - * to wait. If the timeout is -1, it specifies that we should never wait - * and - * always return immediately even if no event is detected. If the value - * is - * zero, the behavior is default. */ + * milliseconds to wait. If the timeout is -1, it specifies that + * we should never wait and always return immediately even if no + * event is detected. If the value is zero, the behavior is default. + */ int timeout = -1; /* number of file descriptors with events */ @@ -860,7 +859,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m, frr_with_mutex(&m->mtx) { if (t_ptr && *t_ptr) - // thread is already scheduled; don't reschedule + /* thread is already scheduled; don't reschedule */ return NULL; thread = thread_get(m, type, func, arg, debugargpass); @@ -940,7 +939,7 @@ struct thread *funcname_thread_add_event(struct thread_master *m, frr_with_mutex(&m->mtx) { if (t_ptr && *t_ptr) - // thread is already scheduled; don't reschedule + /* thread is already scheduled; don't reschedule */ break; thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass); @@ -1047,11 +1046,12 @@ static void do_thread_cancel(struct thread_master *master) struct cancel_req *cr; struct listnode *ln; for (ALL_LIST_ELEMENTS_RO(master->cancel_req, ln, cr)) { - /* If this is an event object cancellation, linear search - * through event - * list deleting any events which have the specified argument. - * We also - * need to check every thread in the ready queue. */ + /* + * If this is an event object cancellation, linear search + * through event list deleting any events which have the + * specified argument. We also need to check every thread + * in the ready queue. + */ if (cr->eventobj) { struct thread *t; @@ -1075,11 +1075,12 @@ static void do_thread_cancel(struct thread_master *master) continue; } - /* The pointer varies depending on whether the cancellation - * request was - * made asynchronously or not. If it was, we need to check - * whether the - * thread even exists anymore before cancelling it. */ + /* + * The pointer varies depending on whether the cancellation + * request was made asynchronously or not. If it was, we + * need to check whether the thread even exists anymore + * before cancelling it. + */ thread = (cr->thread) ? cr->thread : *cr->threadref; if (!thread) @@ -1304,18 +1305,21 @@ static void thread_process_io(struct thread_master *m, unsigned int num) ready++; - /* Unless someone has called thread_cancel from another pthread, - * the only - * thing that could have changed in m->handler.pfds while we - * were - * asleep is the .events field in a given pollfd. Barring - * thread_cancel() - * that value should be a superset of the values we have in our - * copy, so - * there's no need to update it. Similarily, barring deletion, - * the fd - * should still be a valid index into the master's pfds. */ - if (pfds[i].revents & (POLLIN | POLLHUP)) { + /* + * Unless someone has called thread_cancel from another + * pthread, the only thing that could have changed in + * m->handler.pfds while we were asleep is the .events + * field in a given pollfd. Barring thread_cancel() that + * value should be a superset of the values we have in our + * copy, so there's no need to update it. Similarily, + * barring deletion, the fd should still be a valid index + * into the master's pfds. + * + * We are including POLLERR here to do a READ event + * this is because the read should fail and the + * read function should handle it appropriately + */ + if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) { thread_process_io_helper(m, m->read[pfds[i].fd], POLLIN, pfds[i].revents, i); } @@ -1427,11 +1431,10 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * * - If there are events pending, set the poll() timeout to zero * - If there are no events pending, but there are timers - * pending, set the - * timeout to the smallest remaining time on any timer + * pending, set the timeout to the smallest remaining time on + * any timer. * - If there are neither timers nor events pending, but there - * are file - * descriptors pending, block indefinitely in poll() + * are file descriptors pending, block indefinitely in poll() * - If nothing is pending, it's time for the application to die * * In every case except the last, we need to hit poll() at least diff --git a/lib/yang.c b/lib/yang.c index 6ab9492d52..9bfdcb858c 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -84,6 +84,9 @@ static const char *yang_module_imp_clb(const char *mod_name, static const char *const frr_native_modules[] = { "frr-interface", "frr-vrf", + "frr-routing", + "frr-route-map", + "frr-nexthop", "frr-ripd", "frr-ripngd", "frr-isisd", diff --git a/lib/yang.h b/lib/yang.h index cc048c44e8..94bbed233d 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -63,6 +63,7 @@ struct yang_module { #endif #ifdef HAVE_SYSREPO sr_subscription_ctx_t *sr_subscription; + struct thread *sr_thread; #endif }; RB_HEAD(yang_modules, yang_module); diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index 4f743096ee..4c658c1bfb 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -792,6 +792,29 @@ struct yang_data *yang_data_new_empty(const char *xpath) return yang_data_new(xpath, NULL); } +bool yang_dnode_get_empty(const struct lyd_node *dnode, const char *xpath_fmt, + ...) +{ + va_list ap; + char xpath[XPATH_MAXLEN]; + const struct lyd_node_leaf_list *dleaf; + + assert(dnode); + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + + dnode = yang_dnode_get(dnode, xpath); + if (dnode) { + dleaf = (const struct lyd_node_leaf_list *)dnode; + if (dleaf->value_type == LY_TYPE_EMPTY) + return true; + } + + return false; +} + /* * Derived type: IP prefix. */ diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h index 335ff319d5..d781dfb1e4 100644 --- a/lib/yang_wrappers.h +++ b/lib/yang_wrappers.h @@ -120,6 +120,8 @@ extern void yang_get_default_string_buf(char *buf, size_t size, /* empty */ extern struct yang_data *yang_data_new_empty(const char *xpath); +extern bool yang_dnode_get_empty(const struct lyd_node *dnode, + const char *xpath_fmt, ...); /* ip prefix */ extern void yang_str2prefix(const char *value, union prefixptr prefix); diff --git a/lib/zclient.c b/lib/zclient.c index eb62350f4f..b842e7c31b 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -39,6 +39,7 @@ #include "pbr.h" #include "nexthop_group.h" #include "lib_errors.h" +#include "srte.h" DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient") DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs") @@ -436,7 +437,8 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) vrf_id); /* We need router-id information. */ - zebra_message_send(zclient, ZEBRA_ROUTER_ID_ADD, vrf_id); + zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_ADD, AFI_IP, + vrf_id); /* We need interface information. */ zebra_message_send(zclient, ZEBRA_INTERFACE_ADD, vrf_id); @@ -503,7 +505,8 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) vrf_id); /* We need router-id information. */ - zebra_message_send(zclient, ZEBRA_ROUTER_ID_DELETE, vrf_id); + zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_DELETE, AFI_IP, + vrf_id); zebra_message_send(zclient, ZEBRA_INTERFACE_DELETE, vrf_id); @@ -554,6 +557,18 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) } } +int zclient_send_router_id_update(struct zclient *zclient, + zebra_message_types_t type, afi_t afi, + vrf_id_t vrf_id) +{ + struct stream *s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, type, vrf_id); + stream_putw(s, afi); + stream_putw_at(s, 0, stream_get_endp(s)); + return zclient_send_message(zclient); +} + /* Send request to zebra daemon to start or stop RA. */ void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, struct interface *ifp, int enable, @@ -856,6 +871,37 @@ static int zapi_nexthop_cmp_no_labels(const struct zapi_nexthop *next1, break; } + if (next1->srte_color < next2->srte_color) + return -1; + if (next1->srte_color > next2->srte_color) + return 1; + + if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) || + CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + + if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return -1; + + if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return 1; + + if (next1->backup_num > 0 || next2->backup_num > 0) { + + if (next1->backup_num < next2->backup_num) + return -1; + + if (next1->backup_num > next2->backup_num) + return 1; + + ret = memcmp(next1->backup_idx, + next2->backup_idx, next1->backup_num); + if (ret != 0) + return ret; + } + } + return 0; } @@ -886,7 +932,7 @@ static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp, * Encode a single zapi nexthop */ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, - uint32_t api_flags) + uint32_t api_flags, uint32_t api_message) { int i, ret = 0; int nh_flags = api_nh->flags; @@ -950,6 +996,10 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, stream_put(s, &(api_nh->rmac), sizeof(struct ethaddr)); + /* Color for Segment Routing TE. */ + if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE)) + stream_putl(s, api_nh->srte_color); + /* Index of backup nexthop */ if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { /* Validate backup count */ @@ -986,7 +1036,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) stream_putw(s, api->instance); stream_putl(s, api->flags); - stream_putc(s, api->message); + stream_putl(s, api->message); if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, @@ -1047,7 +1097,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } - if (zapi_nexthop_encode(s, api_nh, api->flags) != 0) + if (zapi_nexthop_encode(s, api_nh, api->flags, + api->message) + != 0) return -1; } } @@ -1091,7 +1143,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } - if (zapi_nexthop_encode(s, api_nh, api->flags) != 0) + if (zapi_nexthop_encode(s, api_nh, api->flags, + api->message) + != 0) return -1; } } @@ -1118,7 +1172,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) * Decode a single zapi nexthop object */ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, - uint32_t api_flags) + uint32_t api_flags, uint32_t api_message) { int i, ret = -1; @@ -1171,6 +1225,10 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, STREAM_GET(&(api_nh->rmac), s, sizeof(struct ethaddr)); + /* Color for Segment Routing TE. */ + if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE)) + STREAM_GETL(s, api_nh->srte_color); + /* Backup nexthop index */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { STREAM_GETC(s, api_nh->backup_num); @@ -1208,7 +1266,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) STREAM_GETW(s, api->instance); STREAM_GETL(s, api->flags); - STREAM_GETC(s, api->message); + STREAM_GETL(s, api->message); STREAM_GETC(s, api->safi); if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, @@ -1283,7 +1341,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; - if (zapi_nexthop_decode(s, api_nh, api->flags) != 0) + if (zapi_nexthop_decode(s, api_nh, api->flags, + api->message) + != 0) return -1; } } @@ -1301,7 +1361,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) for (i = 0; i < api->backup_nexthop_num; i++) { api_nh = &api->backup_nexthops[i]; - if (zapi_nexthop_decode(s, api_nh, api->flags) != 0) + if (zapi_nexthop_decode(s, api_nh, api->flags, + api->message) + != 0) return -1; } } @@ -1488,6 +1550,7 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) n->vrf_id = znh->vrf_id; n->ifindex = znh->ifindex; n->gate = znh->gate; + n->srte_color = znh->srte_color; /* * This function currently handles labels @@ -1605,6 +1668,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) memset(nhr, 0, sizeof(*nhr)); + STREAM_GETL(s, nhr->message); STREAM_GETW(s, nhr->prefix.family); STREAM_GETC(s, nhr->prefix.prefixlen); switch (nhr->prefix.family) { @@ -1617,6 +1681,8 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) default: break; } + if (CHECK_FLAG(nhr->message, ZAPI_MESSAGE_SRTE)) + STREAM_GETL(s, nhr->srte_color); STREAM_GETC(s, nhr->type); STREAM_GETW(s, nhr->instance); @@ -1625,7 +1691,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) STREAM_GETC(s, nhr->nexthop_num); for (i = 0; i < nhr->nexthop_num; i++) { - if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0) != 0) + if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0, 0) != 0) return -1; } @@ -2002,7 +2068,7 @@ static void zebra_interface_if_set_value(struct stream *s, uint8_t link_params_status = 0; ifindex_t old_ifindex, new_ifindex; - old_ifindex = ifp->ifindex; + old_ifindex = ifp->oldifindex; /* Read interface's index. */ STREAM_GETL(s, new_ifindex); if_set_index(ifp, new_ifindex); @@ -2821,6 +2887,92 @@ int tm_release_table_chunk(struct zclient *zclient, uint32_t start, return zclient_send_message(zclient); } +int zebra_send_sr_policy(struct zclient *zclient, int cmd, + struct zapi_sr_policy *zp) +{ + if (zapi_sr_policy_encode(zclient->obuf, cmd, zp) < 0) + return -1; + return zclient_send_message(zclient); +} + +int zapi_sr_policy_encode(struct stream *s, int cmd, struct zapi_sr_policy *zp) +{ + struct zapi_srte_tunnel *zt = &zp->segment_list; + + stream_reset(s); + + zclient_create_header(s, cmd, VRF_DEFAULT); + stream_putl(s, zp->color); + stream_put_ipaddr(s, &zp->endpoint); + stream_write(s, &zp->name, SRTE_POLICY_NAME_MAX_LENGTH); + + stream_putc(s, zt->type); + stream_putl(s, zt->local_label); + + if (zt->label_num > MPLS_MAX_LABELS) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: label %u: can't encode %u labels (maximum is %u)", + __func__, zt->local_label, zt->label_num, + MPLS_MAX_LABELS); + return -1; + } + stream_putw(s, zt->label_num); + + for (int i = 0; i < zt->label_num; i++) + stream_putl(s, zt->labels[i]); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return 0; +} + +int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp) +{ + memset(zp, 0, sizeof(*zp)); + + struct zapi_srte_tunnel *zt = &zp->segment_list; + + STREAM_GETL(s, zp->color); + STREAM_GET_IPADDR(s, &zp->endpoint); + STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH); + + /* segment list of active candidate path */ + STREAM_GETC(s, zt->type); + STREAM_GETL(s, zt->local_label); + STREAM_GETW(s, zt->label_num); + if (zt->label_num > MPLS_MAX_LABELS) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: label %u: can't decode %u labels (maximum is %u)", + __func__, zt->local_label, zt->label_num, + MPLS_MAX_LABELS); + return -1; + } + for (int i = 0; i < zt->label_num; i++) + STREAM_GETL(s, zt->labels[i]); + + return 0; + +stream_failure: + return -1; +} + +int zapi_sr_policy_notify_status_decode(struct stream *s, + struct zapi_sr_policy *zp) +{ + memset(zp, 0, sizeof(*zp)); + + STREAM_GETL(s, zp->color); + STREAM_GET_IPADDR(s, &zp->endpoint); + STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH); + STREAM_GETL(s, zp->status); + + return 0; + +stream_failure: + return -1; +} + int zebra_send_mpls_labels(struct zclient *zclient, int cmd, struct zapi_labels *zl) { @@ -2860,7 +3012,7 @@ int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl) for (int i = 0; i < zl->nexthop_num; i++) { znh = &zl->nexthops[i]; - if (zapi_nexthop_encode(s, znh, 0) < 0) + if (zapi_nexthop_encode(s, znh, 0, 0) < 0) return -1; } @@ -2879,7 +3031,7 @@ int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl) for (int i = 0; i < zl->backup_nexthop_num; i++) { znh = &zl->backup_nexthops[i]; - if (zapi_nexthop_encode(s, znh, 0) < 0) + if (zapi_nexthop_encode(s, znh, 0, 0) < 0) return -1; } @@ -2955,7 +3107,7 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl) for (int i = 0; i < zl->nexthop_num; i++) { znh = &zl->nexthops[i]; - if (zapi_nexthop_decode(s, znh, 0) < 0) + if (zapi_nexthop_decode(s, znh, 0, 0) < 0) return -1; } @@ -2976,7 +3128,7 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl) for (int i = 0; i < zl->backup_nexthop_num; i++) { znh = &zl->backup_nexthops[i]; - if (zapi_nexthop_decode(s, znh, 0) < 0) + if (zapi_nexthop_decode(s, znh, 0, 0) < 0) return -1; } } @@ -3643,6 +3795,10 @@ static int zclient_read(struct thread *thread) (*zclient->opaque_unregister_handler)(command, zclient, length, vrf_id); break; + case ZEBRA_SR_POLICY_NOTIFY_STATUS: + if (zclient->sr_policy_notify_status) + (*zclient->sr_policy_notify_status)(command, zclient, + length, vrf_id); default: break; } @@ -3828,3 +3984,23 @@ int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api) stream_failure: return 0; } + +int zclient_send_neigh_discovery_req(struct zclient *zclient, + const struct interface *ifp, + const struct prefix *p) +{ + struct stream *s; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_NEIGH_DISCOVER, ifp->vrf_id); + stream_putl(s, ifp->ifindex); + + stream_putc(s, p->family); + stream_putc(s, p->prefixlen); + stream_put(s, &p->u.prefix, prefix_blen(p)); + + stream_putw_at(s, 0, stream_get_endp(s)); + return zclient_send_message(zclient); +} diff --git a/lib/zclient.h b/lib/zclient.h index da06239d01..c6a67790a1 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -37,6 +37,7 @@ #include "pw.h" #include "mlag.h" +#include "srte.h" #ifdef __cplusplus extern "C" { @@ -89,6 +90,8 @@ enum zserv_client_capabilities { /* Macro to check if there GR enabled. */ #define ZEBRA_CLIENT_GR_ENABLED(X) (X == ZEBRA_CLIENT_GR_CAPABILITIES) +#define ZEBRA_SR_POLICY_NAME_MAX_LENGTH 100 + extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; @@ -143,6 +146,9 @@ typedef enum { ZEBRA_MPLS_LABELS_ADD, ZEBRA_MPLS_LABELS_DELETE, ZEBRA_MPLS_LABELS_REPLACE, + ZEBRA_SR_POLICY_SET, + ZEBRA_SR_POLICY_DELETE, + ZEBRA_SR_POLICY_NOTIFY_STATUS, ZEBRA_IPMR_ROUTE_STATS, ZEBRA_LABEL_MANAGER_CONNECT, ZEBRA_LABEL_MANAGER_CONNECT_ASYNC, @@ -208,6 +214,7 @@ typedef enum { ZEBRA_OPAQUE_MESSAGE, ZEBRA_OPAQUE_REGISTER, ZEBRA_OPAQUE_UNREGISTER, + ZEBRA_NEIGH_DISCOVER, } zebra_message_types_t; enum zebra_error_types { @@ -351,6 +358,7 @@ struct zclient { int (*opaque_msg_handler)(ZAPI_CALLBACK_ARGS); int (*opaque_register_handler)(ZAPI_CALLBACK_ARGS); int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS); + int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS); }; /* Zebra API message flag. */ @@ -368,7 +376,8 @@ struct zclient { * the table being used is not in the VRF. You must pass the * default vrf, else this will be ignored. */ -#define ZAPI_MESSAGE_TABLEID 0x80 +#define ZAPI_MESSAGE_TABLEID 0x0080 +#define ZAPI_MESSAGE_SRTE 0x0100 #define ZSERV_VERSION 6 /* Zserv protocol message header */ @@ -382,6 +391,11 @@ struct zmsghdr { } __attribute__((packed)); #define ZAPI_HEADER_CMD_LOCATION offsetof(struct zmsghdr, command) +/* + * ZAPI nexthop. Note that these are sorted when associated with ZAPI routes, + * and that sorting must be aligned with the sorting of nexthops in + * lib/nexthop.c. Any new fields must be accounted for in zapi_nexthop_cmp(). + */ struct zapi_nexthop { enum nexthop_types_t type; vrf_id_t vrf_id; @@ -403,6 +417,9 @@ struct zapi_nexthop { /* Backup nexthops, for IP-FRR, TI-LFA, etc */ uint8_t backup_num; uint8_t backup_idx[NEXTHOP_MAX_BACKUPS]; + + /* SR-TE color. */ + uint32_t srte_color; }; /* @@ -465,7 +482,7 @@ struct zapi_route { #define ZEBRA_FLAG_RR_USE_DISTANCE 0x40 /* The older XXX_MESSAGE flags live here */ - uint8_t message; + uint32_t message; /* * This is an enum but we are going to treat it as a uint8_t @@ -494,6 +511,9 @@ struct zapi_route { vrf_id_t vrf_id; uint32_t tableid; + + /* SR-TE color (used for nexthop updates only). */ + uint32_t srte_color; }; struct zapi_labels { @@ -516,6 +536,21 @@ struct zapi_labels { struct zapi_nexthop backup_nexthops[MULTIPATH_NUM]; }; +struct zapi_srte_tunnel { + enum lsp_types_t type; + mpls_label_t local_label; + uint8_t label_num; + mpls_label_t labels[MPLS_MAX_LABELS]; +}; + +struct zapi_sr_policy { + uint32_t color; + struct ipaddr endpoint; + char name[SRTE_POLICY_NAME_MAX_LENGTH]; + struct zapi_srte_tunnel segment_list; + int status; +}; + struct zapi_pw { char ifname[IF_NAMESIZE]; ifindex_t ifindex; @@ -659,6 +694,9 @@ extern void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, extern void zclient_send_reg_requests(struct zclient *, vrf_id_t); extern void zclient_send_dereg_requests(struct zclient *, vrf_id_t); +extern int zclient_send_router_id_update(struct zclient *zclient, + zebra_message_types_t type, afi_t afi, + vrf_id_t vrf_id); extern void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, @@ -775,6 +813,14 @@ extern int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size, extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start, uint32_t end); +extern int zebra_send_sr_policy(struct zclient *zclient, int cmd, + struct zapi_sr_policy *zp); +extern int zapi_sr_policy_encode(struct stream *s, int cmd, + struct zapi_sr_policy *zp); +extern int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp); +extern int zapi_sr_policy_notify_status_decode(struct stream *s, + struct zapi_sr_policy *zp); + extern int zebra_send_mpls_labels(struct zclient *zclient, int cmd, struct zapi_labels *zl); extern int zapi_labels_encode(struct stream *s, int cmd, @@ -791,7 +837,7 @@ extern int zclient_send_rnh(struct zclient *zclient, int command, const struct prefix *p, bool exact_match, vrf_id_t vrf_id); int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, - uint32_t api_flags); + uint32_t api_flags, uint32_t api_message); extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *); extern int zapi_route_decode(struct stream *, struct zapi_route *); bool zapi_route_notify_decode(struct stream *s, struct prefix *p, @@ -916,6 +962,10 @@ enum zapi_opaque_registry { */ extern int zclient_send_hello(struct zclient *client); +extern int zclient_send_neigh_discovery_req(struct zclient *zclient, + const struct interface *ifp, + const struct prefix *p); + #ifdef __cplusplus } #endif |
