diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/command.c | 2 | ||||
| -rw-r--r-- | lib/command.h | 2 | ||||
| -rw-r--r-- | lib/frr_pthread.c | 35 | ||||
| -rw-r--r-- | lib/frr_pthread.h | 11 | ||||
| -rw-r--r-- | lib/keychain_nb.c | 8 | ||||
| -rw-r--r-- | lib/libfrr.c | 11 | ||||
| -rw-r--r-- | lib/libfrr.h | 11 | ||||
| -rw-r--r-- | lib/libospf.h | 1 | ||||
| -rw-r--r-- | lib/link_state.c | 124 | ||||
| -rw-r--r-- | lib/link_state.h | 23 | ||||
| -rw-r--r-- | lib/mgmt_fe_client.c | 54 | ||||
| -rw-r--r-- | lib/mgmt_fe_client.h | 45 | ||||
| -rw-r--r-- | lib/mgmt_msg_native.c | 2 | ||||
| -rw-r--r-- | lib/mgmt_msg_native.h | 63 | ||||
| -rw-r--r-- | lib/nexthop.c | 8 | ||||
| -rw-r--r-- | lib/nexthop.h | 4 | ||||
| -rw-r--r-- | lib/northbound.c | 217 | ||||
| -rw-r--r-- | lib/northbound.h | 38 | ||||
| -rw-r--r-- | lib/northbound_oper.c | 34 | ||||
| -rw-r--r-- | lib/plist.c | 30 | ||||
| -rw-r--r-- | lib/routemap_cli.c | 4 | ||||
| -rw-r--r-- | lib/stream.c | 6 | ||||
| -rw-r--r-- | lib/stream.h | 2 | ||||
| -rw-r--r-- | lib/subdir.am | 1 | ||||
| -rw-r--r-- | lib/vrf.c | 2 | ||||
| -rw-r--r-- | lib/vty.c | 40 | ||||
| -rw-r--r-- | lib/vty.h | 4 | ||||
| -rw-r--r-- | lib/yang.c | 63 | ||||
| -rw-r--r-- | lib/yang.h | 13 | ||||
| -rw-r--r-- | lib/zclient.c | 38 | ||||
| -rw-r--r-- | lib/zclient.h | 2 |
31 files changed, 804 insertions, 94 deletions
diff --git a/lib/command.c b/lib/command.c index 0288ab7599..51f2529e3e 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1374,7 +1374,7 @@ DEFUN (disable, } /* Down vty node level. */ -DEFUN (config_exit, +DEFUN_YANG (config_exit, config_exit_cmd, "exit", "Exit current mode and down to previous mode\n") diff --git a/lib/command.h b/lib/command.h index ef1815c0bd..e4c575e8d7 100644 --- a/lib/command.h +++ b/lib/command.h @@ -462,6 +462,8 @@ struct cmd_node { #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" +#define BGP_AF_STR "Address Family\n" +#define BGP_AF_MODIFIER_STR "Address Family modifier\n" /* Command warnings. */ #define NO_PASSWD_CMD_WARNING \ diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 1ffa5934aa..3a4bc712fc 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -92,9 +92,14 @@ struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr, MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t)); fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM, sizeof(pthread_cond_t)); + pthread_mutex_init(fpt->running_cond_mtx, NULL); pthread_cond_init(fpt->running_cond, NULL); + pthread_mutex_init(&fpt->startup_cond_mtx, NULL); + pthread_cond_init(&fpt->startup_cond, NULL); + fpt->started = false; + frr_with_mutex (&frr_pthread_list_mtx) { listnode_add(frr_pthread_list, fpt); } @@ -108,6 +113,8 @@ static void frr_pthread_destroy_nolock(struct frr_pthread *fpt) pthread_mutex_destroy(&fpt->mtx); pthread_mutex_destroy(fpt->running_cond_mtx); pthread_cond_destroy(fpt->running_cond); + pthread_mutex_destroy(&fpt->startup_cond_mtx); + pthread_cond_destroy(&fpt->startup_cond); XFREE(MTYPE_FRR_PTHREAD, fpt->name); XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx); XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond); @@ -140,11 +147,34 @@ int frr_pthread_set_name(struct frr_pthread *fpt) return ret; } +/* New pthread waits before running */ +static void frr_pthread_wait_startup(struct frr_pthread *fpt) +{ + frr_with_mutex (&fpt->startup_cond_mtx) { + while (!fpt->started) + pthread_cond_wait(&fpt->startup_cond, + &fpt->startup_cond_mtx); + } +} + +/* Parent pthread allows new pthread to start running */ +static void frr_pthread_notify_startup(struct frr_pthread *fpt) +{ + frr_with_mutex (&fpt->startup_cond_mtx) { + fpt->started = true; + pthread_cond_signal(&fpt->startup_cond); + } +} + static void *frr_pthread_inner(void *arg) { struct frr_pthread *fpt = arg; + /* The new pthead waits until the parent allows it to continue. */ + frr_pthread_wait_startup(fpt); + rcu_thread_start(fpt->rcu_thread); + return fpt->attr.start(fpt); } @@ -169,6 +199,9 @@ int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr) /* Restore caller's signals */ pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); + /* Allow new child pthread to start */ + frr_pthread_notify_startup(fpt); + /* * Per pthread_create(3), the contents of fpt->thread are undefined if * pthread_create() did not succeed. Reset this value to zero. @@ -250,6 +283,8 @@ int frr_pthread_non_controlled_startup(pthread_t thread, const char *name, fpt->thread = thread; fpt->rcu_thread = rcu_thread; + fpt->started = true; + frr_pthread_inner(fpt); return 0; diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index 1e1b8d7fd3..bb751b7071 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -47,6 +47,17 @@ struct frr_pthread { struct frr_pthread_attr attr; /* + * Startup serialization: newly-started pthreads wait at a point + * very early in life so that there isn't a race with the + * starting pthread. The OS 'start' apis don't make any guarantees + * about which pthread runs first - the existing pthread that has + * called the 'start' api, or the new pthread that is just starting. + */ + pthread_cond_t startup_cond; + pthread_mutex_t startup_cond_mtx; + atomic_bool started; + + /* * Notification mechanism for allowing pthreads to notify their parents * when they are ready to do work. This mechanism has two associated * functions: diff --git a/lib/keychain_nb.c b/lib/keychain_nb.c index 6838268a93..57967b30a5 100644 --- a/lib/keychain_nb.c +++ b/lib/keychain_nb.c @@ -545,10 +545,6 @@ static int key_chains_key_chain_key_crypto_algorithm_modify(struct nb_cb_modify_ if (args->event != NB_EV_VALIDATE && args->event != NB_EV_APPLY) return NB_OK; - name = yang_dnode_get_string(args->dnode, "../../name"); - keychain = keychain_lookup(name); - index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../key-id"); - key = key_lookup(keychain, index); name = yang_dnode_get_string(args->dnode, NULL); if (!strncmp(name, prefix, prefix_len)) name += prefix_len; @@ -570,6 +566,10 @@ static int key_chains_key_chain_key_crypto_algorithm_modify(struct nb_cb_modify_ } assert(args->event == NB_EV_APPLY); + name = yang_dnode_get_string(args->dnode, "../../name"); + keychain = keychain_lookup(name); + index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../key-id"); + key = key_lookup(keychain, index); key->hash_algo = hash_algo; keychain_touch(keychain); diff --git a/lib/libfrr.c b/lib/libfrr.c index c5c7e7837a..876efe23a8 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -59,7 +59,7 @@ char config_default[512]; char frr_zclientpath[512]; static char pidfile_default[1024]; #ifdef HAVE_SQLITE3 -static char dbfile_default[512]; +static char dbfile_default[1024]; #endif static char vtypath_default[512]; @@ -1450,3 +1450,12 @@ void _libfrr_version(void) write(1, banner, sizeof(banner) - 1); _exit(0); } + +/* Render simple version tuple to string */ +const char *frr_vers2str(uint32_t version, char *buf, int buflen) +{ + snprintf(buf, buflen, "%d.%d.%d", MAJOR_FRRVERSION(version), + MINOR_FRRVERSION(version), SUB_FRRVERSION(version)); + + return buf; +} diff --git a/lib/libfrr.h b/lib/libfrr.h index ee436d9f8f..77d70448a9 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -233,6 +233,17 @@ extern bool frr_is_after_fork; extern bool debug_memstats_at_exit; +/* + * Version numbering: MAJOR (8) | MINOR (16) | SUB (8) + */ +#define MAKE_FRRVERSION(maj, min, sub) \ + ((((maj) & 0xff) << 24) | (((min) & 0xffff) << 8) | ((sub) & 0xff)) +#define MAJOR_FRRVERSION(v) (((v) >> 24) & 0xff) +#define MINOR_FRRVERSION(v) (((v) >> 8) & 0xffff) +#define SUB_FRRVERSION(v) ((v) & 0xff) + +const char *frr_vers2str(uint32_t version, char *buf, int buflen); + #ifdef __cplusplus } #endif diff --git a/lib/libospf.h b/lib/libospf.h index 45e7fb1870..0ac490a00e 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -69,6 +69,7 @@ extern "C" { #define OSPF_MTU_IGNORE_DEFAULT 0 #define OSPF_FAST_HELLO_DEFAULT 0 #define OSPF_P2MP_DELAY_REFLOOD_DEFAULT false +#define OSPF_P2MP_NON_BROADCAST_DEFAULT false #define OSPF_OPAQUE_CAPABLE_DEFAULT true #define OSPF_PREFIX_SUPPRESSION_DEFAULT false #define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */ diff --git a/lib/link_state.c b/lib/link_state.c index 25373bdb20..c758b7f575 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -140,6 +140,12 @@ int ls_node_same(struct ls_node *n1, struct ls_node *n2) if (CHECK_FLAG(n1->flags, LS_NODE_MSD) && (n1->msd != n2->msd)) return 0; } + if (CHECK_FLAG(n1->flags, LS_NODE_SRV6)) { + if (n1->srv6_cap_flags != n2->srv6_cap_flags) + return 0; + if (memcmp(&n1->srv6_msd, &n2->srv6_msd, sizeof(n1->srv6_msd))) + return 0; + } /* OK, n1 & n2 are equal */ return 1; @@ -320,6 +326,23 @@ int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) &l2->adj_sid[i].neighbor.addr))) return 0; } + for (int i = 0; i < ADJ_SRV6_MAX; i++) { + if (!CHECK_FLAG(l1->flags, (LS_ATTR_ADJ_SRV6SID << i))) + continue; + if (memcmp(&l1->adj_srv6_sid[i].sid, &l2->adj_srv6_sid[i].sid, + sizeof(struct in6_addr)) || + (l1->adj_srv6_sid[i].flags != l2->adj_srv6_sid[i].flags) || + (l1->adj_srv6_sid[i].weight != l2->adj_srv6_sid[i].weight) || + (l1->adj_srv6_sid[i].endpoint_behavior != + l2->adj_srv6_sid[i].endpoint_behavior)) + return 0; + if (((l1->adv.origin == ISIS_L1) || + (l1->adv.origin == ISIS_L2)) && + (memcmp(&l1->adj_srv6_sid[i].neighbor.sysid, + &l2->adj_srv6_sid[i].neighbor.sysid, + ISO_SYS_ID_LEN) != 0)) + return 0; + } if (CHECK_FLAG(l1->flags, LS_ATTR_SRLG) && ((l1->srlg_len != l2->srlg_len) || memcmp(l1->srlgs, l2->srlgs, @@ -1298,6 +1321,26 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s) STREAM_GET(attr->adj_sid[ADJ_BCK_IPV6].neighbor.sysid, s, ISO_SYS_ID_LEN); } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID)) { + STREAM_GET(&attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].sid, s, + sizeof(struct in6_addr)); + STREAM_GETC(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].flags); + STREAM_GETC(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].weight); + STREAM_GETW(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6] + .endpoint_behavior); + STREAM_GET(attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].neighbor.sysid, + s, ISO_SYS_ID_LEN); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID)) { + STREAM_GET(&attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].sid, s, + sizeof(struct in6_addr)); + STREAM_GETC(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].flags); + STREAM_GETC(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].weight); + STREAM_GETW(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6] + .endpoint_behavior); + STREAM_GET(attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].neighbor.sysid, + s, ISO_SYS_ID_LEN); + } if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { STREAM_GETC(s, len); attr->srlgs = XCALLOC(MTYPE_LS_DB, len*sizeof(uint32_t)); @@ -1532,6 +1575,28 @@ static int ls_format_attributes(struct stream *s, struct ls_attributes *attr) stream_put(s, attr->adj_sid[ADJ_BCK_IPV6].neighbor.sysid, ISO_SYS_ID_LEN); } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID)) { + stream_put(s, &attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].sid, + sizeof(struct in6_addr)); + stream_putc(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].flags); + stream_putc(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].weight); + stream_putw(s, attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6] + .endpoint_behavior); + stream_put(s, + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].neighbor.sysid, + ISO_SYS_ID_LEN); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID)) { + stream_put(s, &attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].sid, + sizeof(struct in6_addr)); + stream_putc(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].flags); + stream_putc(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].weight); + stream_putw(s, attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6] + .endpoint_behavior); + stream_put(s, + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].neighbor.sysid, + ISO_SYS_ID_LEN); + } if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { stream_putc(s, attr->srlg_len); for (len = 0; len < attr->srlg_len; len++) @@ -2351,6 +2416,24 @@ static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty, attr->adj_sid[ADJ_BCK_IPV6].flags, attr->adj_sid[ADJ_BCK_IPV6].weight); } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID)) { + sbuf_push(&sbuf, 4, "IPv6 Adjacency-SRV6-SID: %pI6", + &attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].sid); + sbuf_push(&sbuf, 0, + "\tFlags: 0x%x\tWeight: 0x%x\tbehavior: 0x%x\n", + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].flags, + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].weight, + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].endpoint_behavior); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID)) { + sbuf_push(&sbuf, 4, "IPv6 Bck. Adjacency-SRV6-SID: %pI6", + &attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].sid); + sbuf_push(&sbuf, 0, + "\tFlags: 0x%x\tWeight: 0x%x\tbehavior: 0x%x\n", + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].flags, + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].weight, + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].endpoint_behavior); + } if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { sbuf_push(&sbuf, 4, "SRLGs: %d", attr->srlg_len); for (int i = 1; i < attr->srlg_len; i++) { @@ -2372,7 +2455,7 @@ static void ls_show_edge_json(struct ls_edge *edge, struct json_object *json) struct ls_attributes *attr; struct json_object *jte, *jbw, *jobj, *jsr = NULL, *jsrlg, *js_ext_ag, *js_ext_ag_arr_word, - *js_ext_ag_arr_bit; + *js_ext_ag_arr_bit, *jsrv6 = NULL; char buf[INET6_BUFSIZ]; char buf_ag[strlen("0xffffffff") + 1]; uint32_t bitmap; @@ -2557,6 +2640,45 @@ static void ls_show_edge_json(struct ls_edge *edge, struct json_object *json) attr->adj_sid[ADJ_BCK_IPV6].weight); json_object_array_add(jsr, jobj); } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SRV6SID)) { + jsrv6 = json_object_new_array(); + json_object_object_add(json, "segment-routing-ipv6", jsrv6); + jobj = json_object_new_object(); + snprintfrr(buf, INET6_BUFSIZ, "%pI6", + &attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].sid); + json_object_string_add(jobj, "adj-sid", buf); + snprintfrr(buf, 6, "0x%x", + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].flags); + json_object_string_add(jobj, "flags", buf); + json_object_int_add(jobj, "weight", + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6].weight); + snprintfrr(buf, 6, "0x%x", + attr->adj_srv6_sid[ADJ_SRV6_PRI_IPV6] + .endpoint_behavior); + json_object_string_add(jobj, "endpoint-behavior", buf); + json_object_array_add(jsr, jobj); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SRV6SID)) { + if (!jsrv6) { + jsrv6 = json_object_new_array(); + json_object_object_add(json, "segment-routing-ipv6", + jsrv6); + } + jobj = json_object_new_object(); + snprintfrr(buf, INET6_BUFSIZ, "%pI6", + &attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].sid); + json_object_string_add(jobj, "adj-sid", buf); + snprintfrr(buf, 6, "0x%x", + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].flags); + json_object_string_add(jobj, "flags", buf); + json_object_int_add(jobj, "weight", + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6].weight); + snprintfrr(buf, 6, "0x%x", + attr->adj_srv6_sid[ADJ_SRV6_BCK_IPV6] + .endpoint_behavior); + json_object_string_add(jobj, "endpoint-behavior", buf); + json_object_array_add(jsr, jobj); + } } void ls_show_edge(struct ls_edge *edge, struct vty *vty, diff --git a/lib/link_state.h b/lib/link_state.h index d3a0ce39da..d819c20db7 100644 --- a/lib/link_state.h +++ b/lib/link_state.h @@ -106,6 +106,7 @@ extern int ls_node_id_same(struct ls_node_id i1, struct ls_node_id i2); #define LS_NODE_SR 0x0040 #define LS_NODE_SRLB 0x0080 #define LS_NODE_MSD 0x0100 +#define LS_NODE_SRV6 0x0200 /* Link State Node structure */ struct ls_node { @@ -128,6 +129,14 @@ struct ls_node { } srlb; uint8_t algo[LIB_LS_SR_ALGO_COUNT]; /* Segment Routing Algorithms */ uint8_t msd; /* Maximum Stack Depth */ + + uint16_t srv6_cap_flags; /* draft-ietf-idr-bgpls-srv6-ext, 3.1., flags field */ + struct ls_srv6_msd { /* draft-ietf-idr-bgpls-srv6-ext, 3.2. */ + uint8_t max_seg_left_msd; + uint8_t max_end_pop_msd; + uint8_t max_h_encaps_msd; + uint8_t max_end_d_msd; + } srv6_msd; }; /* Link State flags to indicate which Attribute parameters are valid */ @@ -161,6 +170,8 @@ struct ls_node { #define LS_ATTR_BCK_ADJ_SID6 0x08000000 #define LS_ATTR_SRLG 0x10000000 #define LS_ATTR_EXT_ADM_GRP 0x20000000 +#define LS_ATTR_ADJ_SRV6SID 0x40000000 +#define LS_ATTR_BCK_ADJ_SRV6SID 0x80000000 /* Link State Attributes */ struct ls_attributes { @@ -209,6 +220,18 @@ struct ls_attributes { uint8_t sysid[ISO_SYS_ID_LEN]; /* or Sys-ID for ISIS */ } neighbor; } adj_sid[4]; /* IPv4/IPv6 & Primary/Backup (LAN)-Adj. SID */ +#define ADJ_SRV6_PRI_IPV6 0 +#define ADJ_SRV6_BCK_IPV6 1 +#define ADJ_SRV6_MAX 2 + struct ls_srv6_adjacency { /* Adjacency SID for IS-IS */ + struct in6_addr sid; /* SID as IPv6 address */ + uint8_t flags; /* Flags */ + uint8_t weight; /* Administrative weight */ + uint16_t endpoint_behavior; /* Endpoint Behavior */ + union { + uint8_t sysid[ISO_SYS_ID_LEN]; /* Sys-ID for ISIS */ + } neighbor; + } adj_srv6_sid[2]; uint32_t *srlgs; /* List of Shared Risk Link Group */ uint8_t srlg_len; /* number of SRLG in the list */ }; diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c index a107582bea..3345505213 100644 --- a/lib/mgmt_fe_client.c +++ b/lib/mgmt_fe_client.c @@ -329,6 +329,36 @@ int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client, return ret; } +int mgmt_fe_send_edit_req(struct mgmt_fe_client *client, uint64_t session_id, + uint64_t req_id, uint8_t datastore, + LYD_FORMAT request_type, uint8_t flags, + uint8_t operation, const char *xpath, const char *data) +{ + struct mgmt_msg_edit *msg; + int ret; + + msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_edit, 0, + MTYPE_MSG_NATIVE_EDIT); + msg->refer_id = session_id; + msg->req_id = req_id; + msg->code = MGMT_MSG_CODE_EDIT; + msg->request_type = request_type; + msg->flags = flags; + msg->datastore = datastore; + msg->operation = operation; + + mgmt_msg_native_xpath_encode(msg, xpath); + if (data) + mgmt_msg_native_append(msg, data, strlen(data) + 1); + + debug_fe_client("Sending EDIT_REQ session-id %" PRIu64 + " req-id %" PRIu64 " xpath: %s", + session_id, req_id, xpath); + + ret = mgmt_msg_native_send_msg(&client->client.conn, msg, false); + mgmt_msg_native_free_msg(msg); + return ret; +} static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, Mgmtd__FeMessage *fe_msg) @@ -503,7 +533,9 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client, struct mgmt_fe_client_session *session = NULL; struct mgmt_msg_notify_data *notify_msg; struct mgmt_msg_tree_data *tree_msg; + struct mgmt_msg_edit_reply *edit_msg; struct mgmt_msg_error *err_msg; + const char *xpath = NULL; const char *data = NULL; size_t dlen; @@ -554,6 +586,28 @@ static void fe_client_handle_native_msg(struct mgmt_fe_client *client, msg_len - sizeof(*tree_msg), tree_msg->partial_error); break; + case MGMT_MSG_CODE_EDIT_REPLY: + if (!session->client->cbs.edit_notify) + return; + + edit_msg = (typeof(edit_msg))msg; + if (msg_len < sizeof(*edit_msg)) { + log_err_fe_client("Corrupt edit-reply msg recv"); + return; + } + + xpath = mgmt_msg_native_xpath_decode(edit_msg, msg_len); + if (!xpath) { + log_err_fe_client("Corrupt edit-reply msg recv"); + return; + } + + session->client->cbs.edit_notify(client, client->user_data, + session->client_id, + msg->refer_id, + session->user_ctx, msg->req_id, + xpath); + break; case MGMT_MSG_CODE_NOTIFY: if (!session->client->cbs.async_notification) return; diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h index eee4594e17..9d569348ae 100644 --- a/lib/mgmt_fe_client.h +++ b/lib/mgmt_fe_client.h @@ -114,6 +114,12 @@ struct mgmt_fe_client_cbs { LYD_FORMAT result_type, void *result, size_t len, int partial_error); + /* Called when edit result is returned */ + int (*edit_notify)(struct mgmt_fe_client *client, uintptr_t user_data, + uint64_t client_id, uint64_t session_id, + uintptr_t session_ctx, uint64_t req_id, + const char *xpath); + /* Called with asynchronous notifications from backends */ int (*async_notification)(struct mgmt_fe_client *client, uintptr_t user_data, uint64_t client_id, @@ -410,6 +416,45 @@ extern int mgmt_fe_send_get_data_req(struct mgmt_fe_client *client, const char *xpath); /* + * Send EDIT to MGMTD daemon. + * + * client + * Client object. + * + * session_id + * Client session ID. + * + * req_id + * Client request ID. + * + * datastore + * Datastore for editing. + * + * request_type + * The LYD_FORMAT of the request. + * + * flags + * Flags to control the behavior of the request. + * + * operation + * NB_OP_* operation to perform. + * + * xpath + * the xpath to edit. + * + * data + * the data tree. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern int mgmt_fe_send_edit_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + uint8_t datastore, LYD_FORMAT request_type, + uint8_t flags, uint8_t operation, + const char *xpath, const char *data); + +/* * Destroy library and cleanup everything. */ extern void mgmt_fe_client_destroy(struct mgmt_fe_client *client); diff --git a/lib/mgmt_msg_native.c b/lib/mgmt_msg_native.c index 98b7da45ce..09ea43ece0 100644 --- a/lib/mgmt_msg_native.c +++ b/lib/mgmt_msg_native.c @@ -14,6 +14,8 @@ DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_TREE, "native get tree msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_TREE_DATA, "native tree data msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_DATA, "native get data msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_NOTIFY, "native get data msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT, "native edit msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_EDIT_REPLY, "native edit reply msg"); int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id, uint64_t req_id, bool short_circuit_ok, diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h index 53bb81be28..b7c29862aa 100644 --- a/lib/mgmt_msg_native.h +++ b/lib/mgmt_msg_native.h @@ -21,6 +21,7 @@ extern "C" { #include "memory.h" #include "mgmt_msg.h" #include "mgmt_defines.h" +#include "northbound.h" #include <stdalign.h> @@ -149,6 +150,8 @@ DECLARE_MTYPE(MSG_NATIVE_GET_TREE); DECLARE_MTYPE(MSG_NATIVE_TREE_DATA); DECLARE_MTYPE(MSG_NATIVE_GET_DATA); DECLARE_MTYPE(MSG_NATIVE_NOTIFY); +DECLARE_MTYPE(MSG_NATIVE_EDIT); +DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY); /* * Native message codes @@ -158,6 +161,8 @@ DECLARE_MTYPE(MSG_NATIVE_NOTIFY); #define MGMT_MSG_CODE_TREE_DATA 2 #define MGMT_MSG_CODE_GET_DATA 3 #define MGMT_MSG_CODE_NOTIFY 4 +#define MGMT_MSG_CODE_EDIT 5 +#define MGMT_MSG_CODE_EDIT_REPLY 6 /* * Datastores @@ -318,6 +323,60 @@ _Static_assert(sizeof(struct mgmt_msg_notify_data) == offsetof(struct mgmt_msg_notify_data, data), "Size mismatch"); +#define EDIT_FLAG_IMPLICIT_LOCK 0x01 +#define EDIT_FLAG_IMPLICIT_COMMIT 0x02 + +#define EDIT_OP_CREATE 0 +#define EDIT_OP_DELETE 4 +#define EDIT_OP_MERGE 2 +#define EDIT_OP_REPLACE 5 +#define EDIT_OP_REMOVE 3 + +_Static_assert(EDIT_OP_CREATE == NB_OP_CREATE_EXCL, "Operation mismatch"); +_Static_assert(EDIT_OP_DELETE == NB_OP_DELETE, "Operation mismatch"); +_Static_assert(EDIT_OP_MERGE == NB_OP_MODIFY, "Operation mismatch"); +_Static_assert(EDIT_OP_REPLACE == NB_OP_REPLACE, "Operation mismatch"); +_Static_assert(EDIT_OP_REMOVE == NB_OP_DESTROY, "Operation mismatch"); + +/** + * struct mgmt_msg_edit - frontend edit request. + * + * @request_type: ``LYD_FORMAT`` for the @data. + * @flags: combination of ``EDIT_FLAG_*`` flags. + * @datastore: the datastore to edit. + * @operation: one of ``EDIT_OP_*`` operations. + * @data: the xpath followed by the tree data for the operation. + * for CREATE, xpath points to the parent node. + */ +struct mgmt_msg_edit { + struct mgmt_msg_header; + uint8_t request_type; + uint8_t flags; + uint8_t datastore; + uint8_t operation; + uint8_t resv2[4]; + + alignas(8) char data[]; +}; +_Static_assert(sizeof(struct mgmt_msg_edit) == + offsetof(struct mgmt_msg_edit, data), + "Size mismatch"); + +/** + * struct mgmt_msg_edit_reply - frontend edit reply. + * + * @data: the xpath of the data node that was created. + */ +struct mgmt_msg_edit_reply { + struct mgmt_msg_header; + uint8_t resv2[8]; + + alignas(8) char data[]; +}; +_Static_assert(sizeof(struct mgmt_msg_edit_reply) == + offsetof(struct mgmt_msg_edit_reply, data), + "Size mismatch"); + /* * Validate that the message ends in a NUL terminating byte */ @@ -504,13 +563,13 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn, * The xpath string or NULL if there was an error decoding (i.e., the * message is corrupt). */ -#define mgmt_msg_native_xpath_data_decode(msg, msglen, data) \ +#define mgmt_msg_native_xpath_data_decode(msg, msglen, __data) \ ({ \ size_t __len = (msglen) - sizeof(*msg); \ const char *__s = NULL; \ if (msg->vsplit && msg->vsplit <= __len && \ msg->data[msg->vsplit - 1] == 0) { \ - (data) = msg->data + msg->vsplit; \ + (__data) = msg->data + msg->vsplit; \ __s = msg->data; \ } \ __s; \ diff --git a/lib/nexthop.c b/lib/nexthop.c index 4ddb53cd96..243b52d554 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -1150,11 +1150,7 @@ static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, return -1; } -bool nexthop_is_ifindex_type(const struct nexthop *nh) +bool nexthop_is_blackhole(const struct nexthop *nh) { - if (nh->type == NEXTHOP_TYPE_IFINDEX || - nh->type == NEXTHOP_TYPE_IPV4_IFINDEX || - nh->type == NEXTHOP_TYPE_IPV6_IFINDEX) - return true; - return false; + return nh->type == NEXTHOP_TYPE_BLACKHOLE; } diff --git a/lib/nexthop.h b/lib/nexthop.h index bed6447d49..958d06aa51 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -240,8 +240,8 @@ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop, extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, struct nexthop *rparent); -/* Check nexthop of IFINDEX type */ -extern bool nexthop_is_ifindex_type(const struct nexthop *nh); +/* Is this nexthop a blackhole? */ +extern bool nexthop_is_blackhole(const struct nexthop *nh); /* * Parse one or more backup index values, as comma-separated numbers, diff --git a/lib/northbound.c b/lib/northbound.c index 11dca732ba..9a5d67cd1b 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -813,6 +813,223 @@ int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node return NB_OK; } +static int nb_candidate_edit_tree_add(struct nb_config *candidate, + enum nb_operation operation, + LYD_FORMAT format, const char *xpath, + const char *data, char *xpath_created, + char *errmsg, size_t errmsg_len) +{ + struct lyd_node *tree = NULL; + struct lyd_node *parent = NULL; + struct lyd_node *dnode = NULL; + struct lyd_node *existing = NULL; + struct lyd_node *ex_parent = NULL; + char *parent_xpath = NULL; + struct ly_in *in; + LY_ERR err; + bool root; + int ret; + + ly_in_new_memory(data, &in); + + root = xpath[0] == 0 || (xpath[0] == '/' && xpath[1] == 0); + + /* get parent xpath if xpath is not root */ + if (!root) { + /* NB_OP_CREATE_EXCT already expects parent xpath */ + parent_xpath = XSTRDUP(MTYPE_TMP, xpath); + + /* for other operations - pop one level */ + if (operation != NB_OP_CREATE_EXCL) { + ret = yang_xpath_pop_node(parent_xpath); + if (ret) { + snprintf(errmsg, errmsg_len, "Invalid xpath"); + goto done; + } + + /* root is not actually a parent */ + if (parent_xpath[0] == 0) + XFREE(MTYPE_TMP, parent_xpath); + } + } + + /* + * Create parent if it's not root. We're creating a new tree here to be + * merged later with candidate. + */ + if (parent_xpath) { + err = lyd_new_path2(NULL, ly_native_ctx, parent_xpath, NULL, 0, + 0, 0, &tree, &parent); + if (err) { + yang_print_errors(ly_native_ctx, errmsg, errmsg_len); + ret = NB_ERR; + goto done; + } + assert(parent); + } + + /* parse data */ + err = yang_lyd_parse_data(ly_native_ctx, parent, in, format, + LYD_PARSE_ONLY | LYD_PARSE_STRICT | + LYD_PARSE_NO_STATE, + 0, &dnode); + if (err) { + yang_print_errors(ly_native_ctx, errmsg, errmsg_len); + ret = NB_ERR; + goto done; + } + + /* set the tree if we created a top-level node */ + if (!parent) + tree = dnode; + + /* save xpath of the created node */ + lyd_path(dnode, LYD_PATH_STD, xpath_created, XPATH_MAXLEN); + + /* verify that list keys are the same in the xpath and the data tree */ + if (!root && (operation == NB_OP_REPLACE || operation == NB_OP_MODIFY)) { + if (lyd_find_path(tree, xpath, 0, NULL)) { + snprintf(errmsg, errmsg_len, + "List keys in xpath and data tree are different"); + ret = NB_ERR; + goto done; + } + } + + /* check if the node already exists in candidate */ + if (operation == NB_OP_CREATE_EXCL || operation == NB_OP_REPLACE) { + existing = yang_dnode_get(candidate->dnode, xpath_created); + + /* if the existing node is implicit default, ignore */ + if (existing && (existing->flags & LYD_DEFAULT)) + existing = NULL; + + if (existing) { + if (operation == NB_OP_CREATE_EXCL) { + snprintf(errmsg, errmsg_len, + "Data already exists"); + ret = NB_ERR; + goto done; + } + + if (root) { + candidate->dnode = NULL; + } else { + /* if it's the first top-level node, update candidate */ + if (candidate->dnode == existing) + candidate->dnode = + candidate->dnode->next; + + ex_parent = lyd_parent(existing); + lyd_unlink_tree(existing); + } + } + } + + err = lyd_merge_siblings(&candidate->dnode, tree, + LYD_MERGE_DESTRUCT | LYD_MERGE_WITH_FLAGS); + if (err) { + /* if replace failed, restore the original node */ + if (existing) { + if (root) { + candidate->dnode = existing; + } else { + if (ex_parent) + lyd_insert_child(ex_parent, existing); + else + lyd_insert_sibling(candidate->dnode, + existing, + &candidate->dnode); + } + } + yang_print_errors(ly_native_ctx, errmsg, errmsg_len); + ret = NB_ERR; + goto done; + } else { + /* + * Free existing node after replace. + * We're using `lyd_free_siblings` here to free the whole + * tree if we replaced the root node. It won't affect other + * siblings if it wasn't root, because the existing node + * was unlinked from the tree. + */ + if (existing) + lyd_free_siblings(existing); + + tree = NULL; /* LYD_MERGE_DESTRUCT deleted the tree */ + } + + ret = NB_OK; +done: + if (tree) + lyd_free_all(tree); + XFREE(MTYPE_TMP, parent_xpath); + ly_in_free(in, 0); + + return ret; +} + +static int nb_candidate_edit_tree_del(struct nb_config *candidate, + enum nb_operation operation, + const char *xpath, char *errmsg, + size_t errmsg_len) +{ + struct lyd_node *dnode; + + /* deleting root - remove the whole config */ + if (xpath[0] == 0 || (xpath[0] == '/' && xpath[1] == 0)) { + lyd_free_all(candidate->dnode); + candidate->dnode = NULL; + return NB_OK; + } + + dnode = yang_dnode_get(candidate->dnode, xpath); + if (!dnode || (dnode->flags & LYD_DEFAULT)) { + if (operation == NB_OP_DELETE) { + snprintf(errmsg, errmsg_len, "Data missing"); + return NB_ERR; + } else + return NB_OK; + } + + /* if it's the first top-level node, update candidate */ + if (candidate->dnode == dnode) + candidate->dnode = candidate->dnode->next; + + lyd_free_tree(dnode); + + return NB_OK; +} + +int nb_candidate_edit_tree(struct nb_config *candidate, + enum nb_operation operation, LYD_FORMAT format, + const char *xpath, const char *data, + char *xpath_created, char *errmsg, size_t errmsg_len) +{ + int ret = NB_ERR; + + switch (operation) { + case NB_OP_CREATE_EXCL: + case NB_OP_CREATE: + case NB_OP_MODIFY: + case NB_OP_REPLACE: + ret = nb_candidate_edit_tree_add(candidate, operation, format, + xpath, data, xpath_created, + errmsg, errmsg_len); + break; + case NB_OP_DESTROY: + case NB_OP_DELETE: + ret = nb_candidate_edit_tree_del(candidate, operation, xpath, + errmsg, errmsg_len); + break; + case NB_OP_MOVE: + /* not supported yet */ + break; + } + + return ret; +} + const char *nb_operation_name(enum nb_operation operation) { switch (operation) { diff --git a/lib/northbound.h b/lib/northbound.h index 01e23c8160..3bf1eacd61 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -999,6 +999,44 @@ extern int nb_candidate_edit(struct nb_config *candidate, const struct yang_data *data); /* + * Edit a candidate configuration. Value is given as JSON/XML. + * + * candidate + * Candidate configuration to edit. + * + * operation + * Operation to apply. + * + * format + * LYD_FORMAT of the value. + * + * xpath + * XPath of the configuration node being edited. + * For create, it must be the parent. + * + * data + * New data tree for the node. + * + * xpath_created + * XPath of the created node if operation is "create". + * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. + * + * Returns: + * - NB_OK on success. + * - NB_ERR for other errors. + */ +extern int nb_candidate_edit_tree(struct nb_config *candidate, + enum nb_operation operation, + LYD_FORMAT format, const char *xpath, + const char *data, char *xpath_created, + char *errmsg, size_t errmsg_len); + +/* * Create diff for configuration. * * dnode diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c index 2394b5e865..7e7190f5a4 100644 --- a/lib/northbound_oper.c +++ b/lib/northbound_oper.c @@ -342,38 +342,6 @@ static void nb_op_resume_data_tree(struct nb_op_yield_state *ys) /* ======================= */ /** - * __xpath_pop_node() - remove the last node from xpath string - * @xpath: an xpath string - * - * Return: NB_OK or NB_ERR_NOT_FOUND if nothing left to pop. - */ -static int __xpath_pop_node(char *xpath) -{ - int len = strlen(xpath); - bool abs = xpath[0] == '/'; - char *slash; - - /* "//" or "/" => NULL */ - if (abs && (len == 1 || (len == 2 && xpath[1] == '/'))) - return NB_ERR_NOT_FOUND; - - slash = (char *)frrstr_back_to_char(xpath, '/'); - /* "/foo/bar/" or "/foo/bar//" => "/foo " */ - if (slash && slash == &xpath[len - 1]) { - xpath[--len] = 0; - slash = (char *)frrstr_back_to_char(xpath, '/'); - if (slash && slash == &xpath[len - 1]) { - xpath[--len] = 0; - slash = (char *)frrstr_back_to_char(xpath, '/'); - } - } - if (!slash) - return NB_ERR_NOT_FOUND; - *slash = 0; - return NB_OK; -} - -/** * nb_op_xpath_to_trunk() - generate a lyd_node tree (trunk) using an xpath. * @xpath_in: xpath query string to build trunk from. * @dnode: resulting tree (trunk) @@ -398,7 +366,7 @@ static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in, if (err == LY_SUCCESS) break; - ret = __xpath_pop_node(xpath); + ret = yang_xpath_pop_node(xpath); if (ret != NB_OK) break; } diff --git a/lib/plist.c b/lib/plist.c index 618d92b549..2cfaa7d81d 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -1073,17 +1073,13 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name, struct prefix_master *master; int64_t seqnum = 0; json_object *json = NULL; - json_object *json_proto = NULL; master = prefix_master_get(afi, 0); if (master == NULL) return CMD_WARNING; - if (uj) { + if (uj) json = json_object_new_object(); - json_proto = json_object_new_object(); - json_object_object_add(json, frr_protoname, json_proto); - } if (seq) seqnum = (int64_t)atol(seq); @@ -1096,8 +1092,8 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name, "%% Can't find specified prefix-list\n"); return CMD_WARNING; } - vty_show_prefix_entry(vty, json_proto, afi, plist, master, - dtype, seqnum); + vty_show_prefix_entry(vty, json, afi, plist, master, dtype, + seqnum); } else { if (dtype == detail_display || dtype == summary_display) { if (master->recent && !uj) @@ -1107,8 +1103,8 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name, } frr_each (plist, &master->str, plist) - vty_show_prefix_entry(vty, json_proto, afi, plist, - master, dtype, seqnum); + vty_show_prefix_entry(vty, json, afi, plist, master, + dtype, seqnum); } return vty_json(vty, json); @@ -1227,7 +1223,7 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name, #include "lib/plist_clippy.c" -DEFPY (show_ip_prefix_list, +DEFPY_NOSH (show_ip_prefix_list, show_ip_prefix_list_cmd, "show ip prefix-list [PREFIXLIST4_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]", SHOW_STR @@ -1239,6 +1235,7 @@ DEFPY (show_ip_prefix_list, JSON_STR) { enum display_type dtype = normal_display; + if (dseq) dtype = sequential_display; @@ -1258,6 +1255,7 @@ DEFPY (show_ip_prefix_list_prefix, "First matched prefix\n") { enum display_type dtype = normal_display; + if (dl) dtype = longer_display; else if (dfm) @@ -1267,7 +1265,7 @@ DEFPY (show_ip_prefix_list_prefix, dtype); } -DEFPY (show_ip_prefix_list_summary, +DEFPY_NOSH (show_ip_prefix_list_summary, show_ip_prefix_list_summary_cmd, "show ip prefix-list summary [PREFIXLIST4_NAME$name] [json$uj]", SHOW_STR @@ -1281,7 +1279,7 @@ DEFPY (show_ip_prefix_list_summary, summary_display, !!uj); } -DEFPY (show_ip_prefix_list_detail, +DEFPY_NOSH (show_ip_prefix_list_detail, show_ip_prefix_list_detail_cmd, "show ip prefix-list detail [PREFIXLIST4_NAME$name] [json$uj]", SHOW_STR @@ -1307,7 +1305,7 @@ DEFPY (clear_ip_prefix_list, return vty_clear_prefix_list(vty, AFI_IP, name, prefix_str); } -DEFPY (show_ipv6_prefix_list, +DEFPY_NOSH(show_ipv6_prefix_list, show_ipv6_prefix_list_cmd, "show ipv6 prefix-list [PREFIXLIST6_NAME$name [seq$dseq (1-4294967295)$arg]] [json$uj]", SHOW_STR @@ -1319,6 +1317,7 @@ DEFPY (show_ipv6_prefix_list, JSON_STR) { enum display_type dtype = normal_display; + if (dseq) dtype = sequential_display; @@ -1338,6 +1337,7 @@ DEFPY (show_ipv6_prefix_list_prefix, "First matched prefix\n") { enum display_type dtype = normal_display; + if (dl) dtype = longer_display; else if (dfm) @@ -1347,7 +1347,7 @@ DEFPY (show_ipv6_prefix_list_prefix, prefix_str, dtype); } -DEFPY (show_ipv6_prefix_list_summary, +DEFPY_NOSH (show_ipv6_prefix_list_summary, show_ipv6_prefix_list_summary_cmd, "show ipv6 prefix-list summary [PREFIXLIST6_NAME$name] [json$uj]", SHOW_STR @@ -1361,7 +1361,7 @@ DEFPY (show_ipv6_prefix_list_summary, summary_display, !!uj); } -DEFPY (show_ipv6_prefix_list_detail, +DEFPY_NOSH (show_ipv6_prefix_list_detail, show_ipv6_prefix_list_detail_cmd, "show ipv6 prefix-list detail [PREFIXLIST6_NAME$name] [json$uj]", SHOW_STR diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 88b341cac0..a12a07c14f 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1252,14 +1252,14 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, } else if (IS_SET_EXTCOMMUNITY_LB(action)) { enum ecommunity_lb_type lb_type; char str[VTY_BUFSIZ]; - uint16_t bandwidth; + uint32_t bandwidth; lb_type = yang_dnode_get_enum( dnode, "./rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type"); switch (lb_type) { case EXPLICIT_BANDWIDTH: - bandwidth = yang_dnode_get_uint16( + bandwidth = yang_dnode_get_uint32( dnode, "./rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth"); snprintf(str, sizeof(str), "%d", bandwidth); diff --git a/lib/stream.c b/lib/stream.c index c6de3aefa1..fa20ebdbe7 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -1241,9 +1241,7 @@ void stream_fifo_init(struct stream_fifo *fifo) /* Add new stream to fifo. */ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s) { -#if defined DEV_BUILD size_t max, curmax; -#endif if (fifo->tail) fifo->tail->next = s; @@ -1252,15 +1250,11 @@ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s) fifo->tail = s; fifo->tail->next = NULL; -#if !defined DEV_BUILD - atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release); -#else max = atomic_fetch_add_explicit(&fifo->count, 1, memory_order_release); curmax = atomic_load_explicit(&fifo->max_count, memory_order_relaxed); if (max > curmax) atomic_store_explicit(&fifo->max_count, max, memory_order_relaxed); -#endif } void stream_fifo_push_safe(struct stream_fifo *fifo, struct stream *s) diff --git a/lib/stream.h b/lib/stream.h index 85eebb47be..61eaa46c95 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -105,9 +105,7 @@ struct stream_fifo { /* number of streams in this fifo */ atomic_size_t count; -#if defined DEV_BUILD atomic_size_t max_count; -#endif struct stream *head; struct stream *tail; diff --git a/lib/subdir.am b/lib/subdir.am index 5ec6adf4c0..221c0b1e1d 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -153,6 +153,7 @@ nodist_lib_libfrr_la_SOURCES = \ yang/frr-nexthop.yang.c \ yang/ietf/frr-deviations-ietf-key-chain.yang.c \ yang/ietf/ietf-routing-types.yang.c \ + yang/ietf/ietf-netconf-acm.yang.c \ yang/ietf/ietf-key-chain.yang.c \ yang/ietf/ietf-interfaces.yang.c \ yang/ietf/ietf-bgp-types.yang.c \ @@ -636,7 +636,7 @@ int vrf_configure_backend(enum vrf_backend_type backend) } /* vrf CLI commands */ -DEFUN_NOSH(vrf_exit, +DEFUN_YANG_NOSH (vrf_exit, vrf_exit_cmd, "exit-vrf", "Exit current mode and down to previous mode\n") @@ -3826,6 +3826,23 @@ static int vty_mgmt_get_tree_result_notified( return 0; } +static int vty_mgmt_edit_result_notified(struct mgmt_fe_client *client, + uintptr_t user_data, + uint64_t client_id, uint64_t session_id, + uintptr_t session_ctx, uint64_t req_id, + const char *xpath) +{ + struct vty *vty = (struct vty *)session_ctx; + + debug_fe_client("EDIT request for client 0x%" PRIx64 " req-id %" PRIu64 + " was successful, xpath: %s", + client_id, req_id, xpath); + + vty_mgmt_resume_response(vty, CMD_SUCCESS); + + return 0; +} + static int vty_mgmt_error_notified(struct mgmt_fe_client *client, uintptr_t user_data, uint64_t client_id, uint64_t session_id, uintptr_t session_ctx, @@ -3867,6 +3884,7 @@ static struct mgmt_fe_client_cbs mgmt_cbs = { .commit_config_notify = vty_mgmt_commit_config_result_notified, .get_data_notify = vty_mgmt_get_data_result_notified, .get_tree_notify = vty_mgmt_get_tree_result_notified, + .edit_notify = vty_mgmt_edit_result_notified, .error_notify = vty_mgmt_error_notified, }; @@ -4122,6 +4140,28 @@ int vty_mgmt_send_get_data_req(struct vty *vty, uint8_t datastore, return 0; } +int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore, + LYD_FORMAT request_type, uint8_t flags, + uint8_t operation, const char *xpath, + const char *data) +{ + vty->mgmt_req_id++; + + if (mgmt_fe_send_edit_req(mgmt_fe_client, vty->mgmt_session_id, + vty->mgmt_req_id, datastore, request_type, + flags, operation, xpath, data)) { + zlog_err("Failed to send EDIT to MGMTD session-id: %" PRIu64 + " req-id %" PRIu64 ".", + vty->mgmt_session_id, vty->mgmt_req_id); + vty_out(vty, "Failed to send EDIT to MGMTD!\n"); + return -1; + } + + vty->mgmt_req_pending_cmd = "MESSAGE_EDIT_REQ"; + + return 0; +} + /* Install vty's own commands like `who' command. */ void vty_init(struct event_loop *master_thread, bool do_command_logging) { @@ -419,6 +419,10 @@ extern int vty_mgmt_send_get_req(struct vty *vty, bool is_config, extern int vty_mgmt_send_get_data_req(struct vty *vty, uint8_t datastore, LYD_FORMAT result_type, uint8_t flags, uint8_t defaults, const char *xpath); +extern int vty_mgmt_send_edit_req(struct vty *vty, uint8_t datastore, + LYD_FORMAT request_type, uint8_t flags, + uint8_t operation, const char *xpath, + const char *data); extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id, bool lock, bool scok); extern void vty_mgmt_resume_response(struct vty *vty, int ret); diff --git a/lib/yang.c b/lib/yang.c index d71cb2f498..013a762842 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -12,6 +12,7 @@ #include "yang.h" #include "yang_translator.h" #include "northbound.h" +#include "frrstr.h" #include "lib/config_paths.h" @@ -1122,6 +1123,32 @@ int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys) return NB_OK; } +int yang_xpath_pop_node(char *xpath) +{ + int len = strlen(xpath); + bool abs = xpath[0] == '/'; + char *slash; + + /* "//" or "/" => NULL */ + if (abs && (len == 1 || (len == 2 && xpath[1] == '/'))) + return NB_ERR_NOT_FOUND; + + slash = (char *)frrstr_back_to_char(xpath, '/'); + /* "/foo/bar/" or "/foo/bar//" => "/foo " */ + if (slash && slash == &xpath[len - 1]) { + xpath[--len] = 0; + slash = (char *)frrstr_back_to_char(xpath, '/'); + if (slash && slash == &xpath[len - 1]) { + xpath[--len] = 0; + slash = (char *)frrstr_back_to_char(xpath, '/'); + } + } + if (!slash) + return NB_ERR_NOT_FOUND; + *slash = 0; + return NB_OK; +} + /* * ------------------------ * Libyang Future Functions @@ -1275,6 +1302,42 @@ LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath) #endif } +/* Can be replaced by `lyd_parse_data` with libyang >= 2.1.156 */ +LY_ERR yang_lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, + struct ly_in *in, LYD_FORMAT format, + uint32_t parse_options, uint32_t validate_options, + struct lyd_node **tree) +{ + struct lyd_node *child; + LY_ERR err; + + err = lyd_parse_data(ctx, parent, in, format, parse_options, + validate_options, tree); + if (err) + return err; + + if (!parent || !(parse_options & LYD_PARSE_ONLY)) + return LY_SUCCESS; + + /* + * Versions prior to 2.1.156 don't return `tree` if `parent` is not NULL + * and validation is disabled (`LYD_PARSE_ONLY`). To work around this, + * go through the children and find the one with `LYD_NEW` flag set. + */ + *tree = NULL; + + LY_LIST_FOR (lyd_child_no_keys(parent), child) { + if (child->flags & LYD_NEW) { + *tree = child; + break; + } + } + + assert(tree); + + return LY_SUCCESS; +} + /* * Safe to remove after libyang v2.1.128 is required */ diff --git a/lib/yang.h b/lib/yang.h index 85be38bf81..25703b1879 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -768,6 +768,14 @@ extern int yang_get_key_preds(char *s, const struct lysc_node *snode, extern int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys); /** + * yang_xpath_pop_node() - remove the last node from xpath string + * @xpath: an xpath string + * + * Return: NB_OK or NB_ERR_NOT_FOUND if nothing left to pop. + */ +extern int yang_xpath_pop_node(char *xpath); + +/** * yang_resolve_snodes() - Resolve an XPath to matching schema nodes. * @ly_ctx: libyang context to operate on. * @xpath: the path or XPath to resolve. @@ -797,6 +805,11 @@ extern LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent, const struct yang_list_keys *keys, struct lyd_node **nodes); extern LY_ERR yang_lyd_trim_xpath(struct lyd_node **rootp, const char *xpath); +extern LY_ERR yang_lyd_parse_data(const struct ly_ctx *ctx, + struct lyd_node *parent, struct ly_in *in, + LYD_FORMAT format, uint32_t parse_options, + uint32_t validate_options, + struct lyd_node **tree); #ifdef __cplusplus } diff --git a/lib/zclient.c b/lib/zclient.c index 4cbd04c116..4cf3934502 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1040,7 +1040,7 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, } if (api_nh->weight) - stream_putl(s, api_nh->weight); + stream_putq(s, api_nh->weight); /* Router MAC for EVPN routes. */ if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_EVPN)) @@ -1412,7 +1412,7 @@ int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, } if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) - STREAM_GETL(s, api_nh->weight); + STREAM_GETQ(s, api_nh->weight); /* Router MAC for EVPN routes. */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) @@ -4670,21 +4670,25 @@ char *zclient_dump_route_flags(uint32_t flags, char *buf, size_t len) return buf; } - snprintfrr( - buf, len, "%s%s%s%s%s%s%s%s%s%s", - CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION) ? "Recursion " - : "", - CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE) ? "Self " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_IBGP) ? "iBGP " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_SELECTED) ? "Selected " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE) ? "Override " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE) ? "Evpn " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_RR_USE_DISTANCE) ? "RR Distance " - : "", - CHECK_FLAG(flags, ZEBRA_FLAG_TRAPPED) ? "Trapped " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOADED) ? "Offloaded " : "", - CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOAD_FAILED) ? "Offload Failed " - : ""); + snprintfrr(buf, len, "%s%s%s%s%s%s%s%s%s%s%s", + CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION) ? "Recursion " + : "", + + CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE) ? "Self " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_IBGP) ? "iBGP " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_SELECTED) ? "Selected " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE) ? "Override " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE) ? "Evpn " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_RR_USE_DISTANCE) ? "RR Distance " + : "", + + CHECK_FLAG(flags, ZEBRA_FLAG_TRAPPED) ? "Trapped " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOADED) ? "Offloaded " : "", + CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOAD_FAILED) + ? "Offload Failed " + : "", + CHECK_FLAG(flags, ZEBRA_FLAG_OUTOFSYNC) ? "OutOfSync " : ""); + return buf; } diff --git a/lib/zclient.h b/lib/zclient.h index 1bf91064e2..3759f94542 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -441,7 +441,7 @@ struct zapi_nexthop { struct ethaddr rmac; - uint32_t weight; + uint64_t weight; /* Backup nexthops, for IP-FRR, TI-LFA, etc */ uint8_t backup_num; |
