diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/command.h | 5 | ||||
| -rw-r--r-- | lib/filter.h | 10 | ||||
| -rw-r--r-- | lib/filter_cli.c | 97 | ||||
| -rw-r--r-- | lib/filter_nb.c | 96 | ||||
| -rw-r--r-- | lib/grammar_sandbox_main.c | 2 | ||||
| -rw-r--r-- | lib/hash.c | 15 | ||||
| -rw-r--r-- | lib/if.c | 2 | ||||
| -rw-r--r-- | lib/if.h | 2 | ||||
| -rw-r--r-- | lib/keychain.c | 2 | ||||
| -rw-r--r-- | lib/ldp_sync.c | 93 | ||||
| -rw-r--r-- | lib/ldp_sync.h | 91 | ||||
| -rw-r--r-- | lib/libfrr.c | 5 | ||||
| -rw-r--r-- | lib/libfrr.h | 1 | ||||
| -rw-r--r-- | lib/mpls.h | 3 | ||||
| -rw-r--r-- | lib/nexthop_group.c | 36 | ||||
| -rw-r--r-- | lib/nexthop_group.h | 3 | ||||
| -rw-r--r-- | lib/northbound.c | 50 | ||||
| -rw-r--r-- | lib/northbound.h | 23 | ||||
| -rw-r--r-- | lib/northbound_cli.c | 27 | ||||
| -rw-r--r-- | lib/northbound_confd.c | 7 | ||||
| -rw-r--r-- | lib/northbound_grpc.cpp | 24 | ||||
| -rw-r--r-- | lib/northbound_sysrepo.c | 379 | ||||
| -rw-r--r-- | lib/pbr.h | 5 | ||||
| -rw-r--r-- | lib/prefix.c | 13 | ||||
| -rw-r--r-- | lib/prefix.h | 1 | ||||
| -rw-r--r-- | lib/privs.c | 6 | ||||
| -rw-r--r-- | lib/stream.c | 48 | ||||
| -rw-r--r-- | lib/stream.h | 23 | ||||
| -rw-r--r-- | lib/subdir.am | 2 | ||||
| -rw-r--r-- | lib/thread.c | 8 | ||||
| -rw-r--r-- | lib/vrf.c | 20 | ||||
| -rw-r--r-- | lib/yang.c | 3 | ||||
| -rw-r--r-- | lib/yang.h | 1 | ||||
| -rw-r--r-- | lib/zclient.c | 44 | ||||
| -rw-r--r-- | lib/zclient.h | 16 | ||||
| -rw-r--r-- | lib/zlog_targets.c | 5 |
36 files changed, 754 insertions, 414 deletions
diff --git a/lib/command.h b/lib/command.h index e20bfe3318..d828989118 100644 --- a/lib/command.h +++ b/lib/command.h @@ -421,6 +421,11 @@ struct cmd_node { #define CMD_VNI_RANGE "(1-16777215)" #define CONF_BACKUP_EXT ".sav" +#define MPLS_LDP_SYNC_STR "Enable MPLS LDP-SYNC\n" +#define NO_MPLS_LDP_SYNC_STR "Disable MPLS LDP-SYNC\n" +#define MPLS_LDP_SYNC_HOLDDOWN_STR \ + "Time to wait for LDP-SYNC to occur before restoring if cost\n" +#define NO_MPLS_LDP_SYNC_HOLDDOWN_STR "holddown timer disable\n" /* Command warnings. */ #define NO_PASSWD_CMD_WARNING \ 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 91691d2f1d..8838a48abd 100644 --- a/lib/filter_nb.c +++ b/lib/filter_nb.c @@ -29,6 +29,7 @@ #include "lib/filter.h" #include "lib/plist.h" #include "lib/plist_int.h" +#include "lib/routemap.h" /* Helper function. */ static in_addr_t @@ -40,6 +41,22 @@ ipv4_network_addr(in_addr_t hostaddr, int masklen) return hostaddr & mask.s_addr; } +static void acl_notify_route_map(struct access_list *acl, int route_map_event) +{ + 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"); @@ -112,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 */ @@ -255,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; } @@ -275,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; } @@ -291,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; } @@ -310,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; } @@ -326,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; } @@ -345,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; } @@ -361,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; } @@ -386,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; } @@ -401,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; } @@ -423,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; } @@ -439,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; } @@ -461,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; } @@ -478,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; } @@ -503,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; } @@ -519,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; } @@ -541,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; } @@ -558,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; } @@ -594,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; } @@ -609,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; } 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/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/ldp_sync.c b/lib/ldp_sync.c new file mode 100644 index 0000000000..5dd045d88d --- /dev/null +++ b/lib/ldp_sync.c @@ -0,0 +1,93 @@ +/* + * ldp_sync.c: LDP-SYNC handling routines + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 + */ + +#include <zebra.h> + +#include "command.h" +#include "memory.h" +#include "prefix.h" +#include "log.h" +#include "thread.h" +#include "stream.h" +#include "zclient.h" +#include "table.h" +#include "vty.h" +#include "ldp_sync.h" + +/* Library code */ +DEFINE_MTYPE_STATIC(LIB, LDP_SYNC_INFO, "LDP SYNC info") + +/* + * ldp_sync_info_create - Allocate the LDP_SYNC information + */ +struct ldp_sync_info *ldp_sync_info_create(void) +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = XCALLOC(MTYPE_LDP_SYNC_INFO, + sizeof(struct ldp_sync_info)); + assert(ldp_sync_info); + + ldp_sync_info->flags = 0; + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + ldp_sync_info->t_holddown = NULL; + return ldp_sync_info; +} + +/* + * ldp_sync_info_free - Free the LDP_SYNC information. + */ +void ldp_sync_info_free(struct ldp_sync_info **ldp_sync_info) +{ + if (*ldp_sync_info) + XFREE(MTYPE_LDP_SYNC_INFO, *ldp_sync_info); +} + +bool ldp_sync_if_is_enabled(struct ldp_sync_info *ldp_sync_info) +{ + /* return true if LDP-SYNC is configured on this interface */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) + return true; + + return false; +} + +bool ldp_sync_if_down(struct ldp_sync_info *ldp_sync_info) +{ + /* Stop LDP-SYNC on this interface: + * if holddown timer is running stop it + * update state + */ + if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { + if (ldp_sync_info->t_holddown != NULL) { + THREAD_TIMER_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->t_holddown = NULL; + } + if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_UP) + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + return true; + } + + return false; +} diff --git a/lib/ldp_sync.h b/lib/ldp_sync.h new file mode 100644 index 0000000000..daede566f0 --- /dev/null +++ b/lib/ldp_sync.h @@ -0,0 +1,91 @@ +/* + * Defines and structures common to LDP-Sync for OSPFv2 and OSPFv3 and ISIS + * Copyright (C) 2020 Volta Networks, Inc. + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 _LIBLDPSYNC_H +#define _LIBLDPSYNC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* LDP-IGP Sync values */ +#define LDP_SYNC_FLAG_ENABLE (1 << 0) /* LDP-SYNC enabled */ +#define LDP_SYNC_FLAG_HOLDDOWN (1 << 1) /* Holddown timer enabled */ +#define LDP_SYNC_FLAG_IF_CONFIG (1 << 2) /* LDP-SYNC enabled on interface */ +#define LDP_SYNC_FLAG_SET_METRIC (1 << 3) /* Metric has been set on ISIS intf */ + +#define LDP_IGP_SYNC_DEFAULT 0 +#define LDP_IGP_SYNC_ENABLED 1 + +#define LDP_IGP_SYNC_STATE_NOT_REQUIRED 0 +#define LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP 1 +#define LDP_IGP_SYNC_STATE_REQUIRED_UP 2 + +#define LDP_IGP_SYNC_HOLDDOWN_DEFAULT 0 + +#define LDP_IGP_SYNC_HELLO_TIMEOUT 1 + +/* LDP-IGP Sync structures */ +struct ldp_sync_info_cmd { + uint16_t flags; + uint16_t holddown; /* timer value */ + uint32_t sequence; /* hello sequence number */ + struct thread *t_hello; /* hello timer for detecting LDP going down */ +}; + +struct ldp_sync_info { + uint16_t flags; /* indicate if set on interface or globally */ + uint8_t enabled; /* enabled */ + uint8_t state; /* running state */ + uint16_t holddown; /* timer value */ + struct thread *t_holddown; /* holddown timer*/ + uint32_t metric[2]; /* isis interface metric */ +}; + +/* Prototypes. */ +extern struct ldp_sync_info *ldp_sync_info_create(void); +extern bool ldp_sync_if_is_enabled(struct ldp_sync_info *ldp_sync_info); +extern bool ldp_sync_if_down(struct ldp_sync_info *ldp_sync_info); +extern void ldp_sync_info_free(struct ldp_sync_info **ldp_sync_info); + +struct ldp_igp_sync_announce { + int proto; +}; + +struct ldp_igp_sync_if_state { + ifindex_t ifindex; + bool sync_start; +}; + +struct ldp_igp_sync_if_state_req { + int proto; + ifindex_t ifindex; + char name[INTERFACE_NAMSIZ]; +}; + +struct ldp_igp_sync_hello { + int proto; + unsigned int sequence; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBLDPSYNC_H */ diff --git a/lib/libfrr.c b/lib/libfrr.c index 2597eb61e2..800596c563 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -45,6 +45,7 @@ #include "defaults.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) +DEFINE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm)) DEFINE_KOOH(frr_early_fini, (), ()) DEFINE_KOOH(frr_fini, (), ()) @@ -719,7 +720,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", @@ -913,6 +914,8 @@ static int frr_config_read_in(struct thread *t) __func__, nb_err_name(ret), errmsg); } + hook_call(frr_very_late_init, master); + return 0; } diff --git a/lib/libfrr.h b/lib/libfrr.h index 9d91ea9154..ab72299206 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -136,6 +136,7 @@ extern const char *frr_get_progname(void); extern enum frr_cli_mode frr_get_cli_mode(void); DECLARE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) +DECLARE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm)) extern void frr_config_fork(void); extern void frr_run(struct thread_master *master); diff --git a/lib/mpls.h b/lib/mpls.h index 8922a36664..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 \ 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 11007e4309..895647cfb7 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. @@ -381,6 +383,10 @@ static void nb_config_diff_add_change(struct nb_config_cbs *changes, { struct nb_config_change *change; + /* Ignore unimplemented nodes. */ + if (!dnode->schema->priv) + return; + change = XCALLOC(MTYPE_TMP, sizeof(*change)); change->cb.operation = operation; change->cb.seq = *seq; @@ -414,6 +420,10 @@ static void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, enum nb_operation operation; struct lyd_node *child; + /* Ignore unimplemented nodes. */ + if (!dnode->schema->priv) + return; + switch (dnode->schema->nodetype) { case LYS_LEAF: case LYS_LEAFLIST: @@ -448,6 +458,10 @@ static void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq, struct nb_config_cbs *changes) { + /* Ignore unimplemented nodes. */ + if (!dnode->schema->priv) + return; + if (nb_operation_is_valid(NB_OP_DESTROY, dnode->schema)) nb_config_diff_add_change(changes, NB_OP_DESTROY, seq, dnode); else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) { @@ -616,7 +630,7 @@ static int nb_candidate_validate_code(struct nb_context *context, struct nb_node *nb_node; nb_node = child->schema->priv; - if (!nb_node->cbs.pre_validate) + if (!nb_node || !nb_node->cbs.pre_validate) goto next; ret = nb_callback_pre_validate(context, nb_node, child, @@ -680,8 +694,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 +725,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 +770,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; } @@ -1381,7 +1397,7 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction, struct nb_node *nb_node; nb_node = dnode->schema->priv; - if (!nb_node->cbs.apply_finish) + if (!nb_node || !nb_node->cbs.apply_finish) goto next; /* @@ -2214,7 +2230,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; @@ -2239,6 +2255,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 d5028ea7d2..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 @@ -1225,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..6ce520149a 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"); @@ -559,7 +573,7 @@ void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root, struct nb_node *nb_node; nb_node = child->schema->priv; - if (!nb_node->cbs.cli_show) + if (!nb_node || !nb_node->cbs.cli_show) goto next; /* Skip default values. */ @@ -577,7 +591,7 @@ void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root, parent = ly_iter_next_up(child); if (parent != NULL) { nb_node = parent->schema->priv; - if (nb_node->cbs.cli_show_end) + if (nb_node && nb_node->cbs.cli_show_end) (*nb_node->cbs.cli_show_end)(vty, parent); } @@ -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..b5ef040a3f 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. */ @@ -176,6 +174,9 @@ static int frr_sr_process_change(struct nb_config *candidate, xpath = sr_data->xpath; + DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: processing change [xpath %s]", + xpath); + /* Non-presence container - nothing to do. */ if (sr_data->type == SR_CONTAINER_T) return NB_OK; @@ -225,7 +226,7 @@ static int frr_sr_process_change(struct nb_config *candidate, ret = nb_candidate_edit(candidate, nb_node, nb_op, xpath, NULL, data); yang_data_free(data); - if (ret != NB_OK) { + if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) { flog_warn( EC_LIB_NB_CANDIDATE_EDIT_ERROR, "%s: failed to edit candidate configuration: operation [%s] xpath [%s]", @@ -236,25 +237,22 @@ 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) { 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; } @@ -279,40 +277,26 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, transaction = NULL; context.client = NB_CLIENT_SYSREPO; - if (startup_config) { - /* - * sysrepod sends the entire startup configuration using a - * single event (SR_EV_ENABLED). This means we need to perform - * the full two-phase commit protocol in one go here. - */ - ret = nb_candidate_commit(&context, candidate, true, NULL, NULL, - errmsg, sizeof(errmsg)); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) - flog_warn( - EC_LIB_LIBSYSREPO, - "%s: failed to apply startup configuration: %s (%s)", - __func__, nb_err_name(ret), errmsg); - } else { - /* - * Validate the configuration changes and allocate all resources - * required to apply them. - */ - ret = nb_candidate_commit_prepare(&context, candidate, NULL, - &transaction, errmsg, - sizeof(errmsg)); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) - flog_warn( - EC_LIB_LIBSYSREPO, - "%s: failed to prepare configuration transaction: %s (%s)", - __func__, nb_err_name(ret), errmsg); - } + /* + * Validate the configuration changes and allocate all resources + * required to apply them. + */ + ret = nb_candidate_commit_prepare(&context, candidate, NULL, + &transaction, errmsg, sizeof(errmsg)); + if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) + flog_warn( + EC_LIB_LIBSYSREPO, + "%s: failed to prepare configuration transaction: %s (%s)", + __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 +313,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 +329,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 +340,20 @@ 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: + case SR_EV_CHANGE: + return frr_sr_config_change_cb_prepare(session, module_name); + 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 +363,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 +512,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 +523,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 +537,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; } @@ -696,9 +546,13 @@ static void frr_sr_subscribe_config(struct yang_module *module) { int ret; + DEBUGD(&nb_dbg_client_sysrepo, + "sysrepo: subscribing for configuration changes made in the '%s' module", + module->name); + 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", @@ -719,14 +573,14 @@ static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg) nb_node = snode->priv; - DEBUGD(&nb_dbg_client_sysrepo, "%s: providing data to '%s'", __func__, + DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing data to '%s'", 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; @@ -743,11 +597,11 @@ static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg) nb_node = snode->priv; - DEBUGD(&nb_dbg_client_sysrepo, "%s: providing RPC to '%s'", __func__, + DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'", 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 +610,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 +657,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 +671,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 +680,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 +717,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,24 +726,26 @@ 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; } -static int frr_sr_module_late_init(struct thread_master *tm) +static int frr_sr_module_very_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; } hook_register(frr_fini, frr_sr_finish); + + return 0; +} + +static int frr_sr_module_late_init(struct thread_master *tm) +{ frr_sr_cli_init(); return 0; @@ -922,6 +754,7 @@ static int frr_sr_module_late_init(struct thread_master *tm) static int frr_sr_module_init(void) { hook_register(frr_late_init, frr_sr_module_late_init); + hook_register(frr_very_late_init, frr_sr_module_very_late_init); return 0; } @@ -97,7 +97,8 @@ struct pbr_rule { uint32_t unique; struct pbr_filter filter; struct pbr_action action; - ifindex_t ifindex; + + char ifname[INTERFACE_NAMSIZ + 1]; }; /* TCP flags value shared @@ -129,6 +130,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/stream.c b/lib/stream.c index 768114e69b..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) { diff --git a/lib/stream.h b/lib/stream.h index 1250b6944d..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); @@ -453,6 +456,24 @@ static inline const uint8_t *ptr_get_be32(const uint8_t *ptr, uint32_t *out) 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 1feaa56d13..b8bcee139b 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -42,6 +42,7 @@ lib_libfrr_la_SOURCES = \ lib/jhash.c \ lib/json.c \ lib/keychain.c \ + lib/ldp_sync.c \ lib/lib_errors.c \ lib/lib_vty.c \ lib/libfrr.c \ @@ -198,6 +199,7 @@ pkginclude_HEADERS += \ lib/jhash.h \ lib/json.h \ lib/keychain.h \ + lib/ldp_sync.h \ lib/lib_errors.h \ lib/lib_vty.h \ lib/libfrr.h \ diff --git a/lib/thread.c b/lib/thread.c index 19e4827283..db35a3f031 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -438,7 +438,8 @@ struct thread_master *thread_master_create(const char *name) pthread_cond_init(&rv->cancel_cond, NULL); /* Set name */ - rv->name = name ? XSTRDUP(MTYPE_THREAD_MASTER, name) : NULL; + name = name ? name : "default"; + rv->name = XSTRDUP(MTYPE_THREAD_MASTER, name); /* Initialize I/O task data structures */ getrlimit(RLIMIT_NOFILE, &limit); @@ -449,10 +450,13 @@ struct thread_master *thread_master_create(const char *name) rv->write = XCALLOC(MTYPE_THREAD_POLL, sizeof(struct thread *) * rv->fd_limit); + char tmhashname[strlen(name) + 32]; + snprintf(tmhashname, sizeof(tmhashname), "%s - threadmaster event hash", + name); rv->cpu_record = hash_create_size( 8, (unsigned int (*)(const void *))cpu_record_hash_key, (bool (*)(const void *, const void *))cpu_record_hash_cmp, - "Thread Hash"); + tmhashname); thread_list_init(&rv->event); thread_list_init(&rv->ready); @@ -159,10 +159,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) struct vrf *vrf = NULL; int new = 0; - if (debug_vrf) - zlog_debug("VRF_GET: %s(%u)", name == NULL ? "(NULL)" : name, - vrf_id); - /* Nothing to see, move along here */ if (!name && vrf_id == VRF_UNKNOWN) return NULL; @@ -225,7 +221,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) void vrf_delete(struct vrf *vrf) { if (debug_vrf) - zlog_debug("VRF %u is to be deleted.", vrf->vrf_id); + zlog_debug("VRF %s(%u) is to be deleted.", vrf->name, + vrf->vrf_id); if (vrf_is_enabled(vrf)) vrf_disable(vrf); @@ -282,7 +279,7 @@ int vrf_enable(struct vrf *vrf) return 1; if (debug_vrf) - zlog_debug("VRF %u is enabled.", vrf->vrf_id); + zlog_debug("VRF %s(%u) is enabled.", vrf->name, vrf->vrf_id); SET_FLAG(vrf->status, VRF_ACTIVE); @@ -312,11 +309,20 @@ void vrf_disable(struct vrf *vrf) UNSET_FLAG(vrf->status, VRF_ACTIVE); if (debug_vrf) - zlog_debug("VRF %u is to be disabled.", vrf->vrf_id); + zlog_debug("VRF %s(%u) is to be disabled.", vrf->name, + vrf->vrf_id); /* Till now, nothing to be done for the default VRF. */ // Pending: see why this statement. + + /* + * When the vrf is disabled let's + * handle all nexthop-groups associated + * with this vrf + */ + nexthop_group_disable_vrf(vrf); + if (vrf_master.vrf_disable_hook) (*vrf_master.vrf_disable_hook)(vrf); } 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/zclient.c b/lib/zclient.c index 808aa18bbe..c5016d22e2 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -871,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; } @@ -1393,7 +1424,7 @@ int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule) stream_putw(s, zrule->filter.fwmark); /* fwmark */ stream_putl(s, zrule->action.table); - stream_putl(s, zrule->ifindex); + stream_put(s, zrule->ifname, INTERFACE_NAMSIZ); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -1423,26 +1454,23 @@ stream_failure: } bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, - uint32_t *priority, uint32_t *unique, - ifindex_t *ifindex, + uint32_t *priority, uint32_t *unique, char *ifname, enum zapi_rule_notify_owner *note) { uint32_t prio, seq, uni; - ifindex_t ifi; STREAM_GET(note, s, sizeof(*note)); STREAM_GETL(s, seq); STREAM_GETL(s, prio); STREAM_GETL(s, uni); - STREAM_GETL(s, ifi); + STREAM_GET(ifname, s, INTERFACE_NAMSIZ); if (zclient_debug) - zlog_debug("%s: %u %u %u %u", __func__, seq, prio, uni, ifi); + zlog_debug("%s: %u %u %u %s", __func__, seq, prio, uni, ifname); *seqno = seq; *priority = prio; *unique = uni; - *ifindex = ifi; return true; @@ -2037,7 +2065,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); diff --git a/lib/zclient.h b/lib/zclient.h index dab384d5ec..f99b3ad743 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -391,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; @@ -839,8 +844,7 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note); bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, - uint32_t *priority, uint32_t *unique, - ifindex_t *ifindex, + uint32_t *priority, uint32_t *unique, char *ifname, enum zapi_rule_notify_owner *note); bool zapi_ipset_notify_decode(struct stream *s, uint32_t *unique, @@ -950,6 +954,14 @@ enum zapi_opaque_registry { LINK_STATE_REQUEST = 1, /* Update containing link-state db info */ LINK_STATE_UPDATE = 2, + /* Request LDP-SYNC state from LDP */ + LDP_IGP_SYNC_IF_STATE_REQUEST = 3, + /* Update containing LDP IGP Sync State info */ + LDP_IGP_SYNC_IF_STATE_UPDATE = 4, + /* Announce that LDP is up */ + LDP_IGP_SYNC_ANNOUNCE_UPDATE = 5, + /* Heartbeat indicating that LDP is running */ + LDP_IGP_SYNC_HELLO_UPDATE = 6, }; /* Send the hello message. diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c index b23ab073b4..8f4c2a46a8 100644 --- a/lib/zlog_targets.c +++ b/lib/zlog_targets.c @@ -225,10 +225,11 @@ static bool zlog_file_cycle(struct zlog_cfg_file *zcf) zlt->zt.logfn_sigsafe = zlog_fd_sigsafe; } while (0); - old = zlog_target_replace(&zcf->active->zt, &zlt->zt); + old = zlog_target_replace(zcf->active ? &zcf->active->zt : NULL, + zlt ? &zlt->zt : NULL); zcf->active = zlt; - zlog_file_target_free(container_of(old, struct zlt_fd, zt)); + zlog_file_target_free(container_of_null(old, struct zlt_fd, zt)); return rv; } |
