summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/command.c2
-rw-r--r--lib/command.h2
-rw-r--r--lib/frr_pthread.c35
-rw-r--r--lib/frr_pthread.h11
-rw-r--r--lib/keychain_nb.c8
-rw-r--r--lib/libfrr.c11
-rw-r--r--lib/libfrr.h11
-rw-r--r--lib/libospf.h1
-rw-r--r--lib/link_state.c124
-rw-r--r--lib/link_state.h23
-rw-r--r--lib/mgmt_fe_client.c54
-rw-r--r--lib/mgmt_fe_client.h45
-rw-r--r--lib/mgmt_msg_native.c2
-rw-r--r--lib/mgmt_msg_native.h63
-rw-r--r--lib/nexthop.c8
-rw-r--r--lib/nexthop.h4
-rw-r--r--lib/northbound.c217
-rw-r--r--lib/northbound.h38
-rw-r--r--lib/northbound_oper.c34
-rw-r--r--lib/plist.c30
-rw-r--r--lib/routemap_cli.c4
-rw-r--r--lib/stream.c6
-rw-r--r--lib/stream.h2
-rw-r--r--lib/subdir.am1
-rw-r--r--lib/vrf.c2
-rw-r--r--lib/vty.c40
-rw-r--r--lib/vty.h4
-rw-r--r--lib/yang.c63
-rw-r--r--lib/yang.h13
-rw-r--r--lib/zclient.c38
-rw-r--r--lib/zclient.h2
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 \
diff --git a/lib/vrf.c b/lib/vrf.c
index e907626bae..31632a80d5 100644
--- a/lib/vrf.c
+++ b/lib/vrf.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")
diff --git a/lib/vty.c b/lib/vty.c
index 912c893556..d69b5eebd2 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -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)
{
diff --git a/lib/vty.h b/lib/vty.h
index a59ac7a652..0e0c92c336 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -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;