summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--babeld/babel_main.c1
-rw-r--r--bfdd/bfdd.c1
-rw-r--r--bgpd/bgp_io.c6
-rw-r--r--bgpd/bgp_main.c1
-rw-r--r--bgpd/bgp_route.c11
-rw-r--r--bgpd/bgp_vty.c21
-rw-r--r--bgpd/bgpd.c3
-rw-r--r--doc/developer/workflow.rst15
-rw-r--r--doc/user/sharp.rst22
-rwxr-xr-xdocker/alpine/docker-start2
-rw-r--r--eigrpd/eigrp_main.c1
-rw-r--r--isisd/isis_adjacency.c17
-rw-r--r--isisd/isis_circuit.c26
-rw-r--r--isisd/isis_csm.c17
-rw-r--r--isisd/isis_main.c1
-rw-r--r--isisd/isis_nb_config.c105
-rw-r--r--ldpd/ldpd.c1
-rw-r--r--lib/command.h1
-rw-r--r--lib/filter.c2154
-rw-r--r--lib/filter.h120
-rw-r--r--lib/filter_cli.c1693
-rw-r--r--lib/filter_nb.c1286
-rw-r--r--lib/if.c14
-rw-r--r--lib/libfrr.c16
-rw-r--r--lib/northbound.c394
-rw-r--r--lib/northbound.h161
-rw-r--r--lib/northbound_cli.c101
-rw-r--r--lib/northbound_confd.c29
-rw-r--r--lib/northbound_db.c2
-rw-r--r--lib/northbound_grpc.cpp124
-rw-r--r--lib/northbound_sysrepo.c22
-rw-r--r--lib/plist.c774
-rw-r--r--lib/plist.h14
-rw-r--r--lib/plist_int.h10
-rw-r--r--lib/routemap.c7
-rw-r--r--lib/subdir.am4
-rw-r--r--lib/vrf.c4
-rw-r--r--lib/vty.c16
-rw-r--r--lib/vty.h2
-rw-r--r--lib/yang.c54
-rw-r--r--lib/yang.h40
-rw-r--r--lib/zclient.c80
-rw-r--r--lib/zclient.h27
-rw-r--r--nhrpd/nhrp_main.c2
-rw-r--r--ospf6d/ospf6_main.c1
-rw-r--r--ospfd/ospf_asbr.c38
-rw-r--r--ospfd/ospf_ext.c251
-rw-r--r--ospfd/ospf_main.c1
-rw-r--r--ospfd/ospf_route.h12
-rw-r--r--ospfd/ospf_spf.c2
-rw-r--r--ospfd/ospf_sr.c1093
-rw-r--r--ospfd/ospf_sr.h48
-rw-r--r--ospfd/ospf_zebra.c115
-rw-r--r--ospfd/ospf_zebra.h8
-rw-r--r--pbrd/pbr_main.c1
-rw-r--r--pimd/pim_main.c1
-rw-r--r--pimd/pim_mlag.c31
-rw-r--r--pimd/pim_oil.c14
-rw-r--r--pimd/pim_upstream.h3
-rw-r--r--ripd/rip_main.c1
-rw-r--r--ripngd/ripng_main.c1
-rw-r--r--sharpd/sharp_main.c1
-rw-r--r--sharpd/sharp_vty.c34
-rw-r--r--sharpd/sharp_zebra.c23
-rw-r--r--sharpd/sharp_zebra.h3
-rw-r--r--staticd/static_main.c1
-rw-r--r--tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py4
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py6
-rw-r--r--tests/topotests/lib/bgp.py2
-rw-r--r--tests/topotests/lib/topogen.py4
-rw-r--r--tests/topotests/lib/topotest.py51
-rw-r--r--tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json91
-rw-r--r--tests/topotests/ospf-sr-topo1/r1/ospfd.conf5
-rw-r--r--tests/topotests/ospf-sr-topo1/r1/zebra.conf3
-rw-r--r--tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json70
-rw-r--r--tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json129
-rw-r--r--tests/topotests/ospf-sr-topo1/r2/ospfd.conf4
-rw-r--r--tests/topotests/ospf-sr-topo1/r2/zebra.conf7
-rw-r--r--tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json71
-rw-r--r--tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json52
-rw-r--r--tests/topotests/ospf-sr-topo1/r3/ospfd.conf1
-rw-r--r--tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json18
-rw-r--r--tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json63
-rw-r--r--tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json23
-rwxr-xr-xtests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py110
-rw-r--r--tests/topotests/pbr-topo1/__init__.py0
-rw-r--r--tests/topotests/pbr-topo1/r1/linux-rules.json19
-rw-r--r--tests/topotests/pbr-topo1/r1/pbr-interface.json15
-rw-r--r--tests/topotests/pbr-topo1/r1/pbr-map.json86
-rw-r--r--tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json47
-rw-r--r--tests/topotests/pbr-topo1/r1/pbrd.conf64
-rw-r--r--tests/topotests/pbr-topo1/r1/zebra.conf5
-rwxr-xr-xtests/topotests/pbr-topo1/test_pbr_topo1.py97
-rw-r--r--vrrpd/vrrp_main.c1
-rwxr-xr-xvtysh/extract.pl.in2
-rw-r--r--yang/frr-filter.yang336
-rw-r--r--yang/frr-nexthop.yang56
-rw-r--r--yang/frr-zebra.yang4
-rw-r--r--zebra/if_netlink.c9
-rw-r--r--zebra/main.c1
-rw-r--r--zebra/rule_netlink.c196
-rw-r--r--zebra/rule_socket.c17
-rw-r--r--zebra/zapi_msg.c15
-rw-r--r--zebra/zapi_msg.h2
-rw-r--r--zebra/zebra_dplane.c314
-rw-r--r--zebra/zebra_dplane.h41
-rw-r--r--zebra/zebra_errors.h1
-rw-r--r--zebra/zebra_fpm_netlink.c2
-rw-r--r--zebra/zebra_mpls.c16
-rw-r--r--zebra/zebra_nb.c88
-rw-r--r--zebra/zebra_nb.h50
-rw-r--r--zebra/zebra_nb_config.c50
-rw-r--r--zebra/zebra_nb_state.c132
-rw-r--r--zebra/zebra_nhg.c3
-rw-r--r--zebra/zebra_opaque.c49
-rw-r--r--zebra/zebra_pbr.c50
-rw-r--r--zebra/zebra_pbr.h22
-rw-r--r--zebra/zebra_pw.c9
-rw-r--r--zebra/zebra_rib.c6
-rw-r--r--zebra/zebra_rnh.c28
-rw-r--r--zebra/zebra_rnh.h2
-rw-r--r--zebra/zserv.c17
122 files changed, 7000 insertions, 4651 deletions
diff --git a/babeld/babel_main.c b/babeld/babel_main.c
index e7ba29ed06..14e583a35c 100644
--- a/babeld/babel_main.c
+++ b/babeld/babel_main.c
@@ -137,6 +137,7 @@ struct option longopts[] =
};
static const struct frr_yang_module_info *const babeld_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_vrf_info,
};
diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c
index 39d51eb649..258f074e8c 100644
--- a/bfdd/bfdd.c
+++ b/bfdd/bfdd.c
@@ -111,6 +111,7 @@ static struct quagga_signal_t bfd_signals[] = {
};
static const struct frr_yang_module_info *const bfdd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_bfdd_info,
&frr_vrf_info,
diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c
index ab50c545b5..412c8e3e5e 100644
--- a/bgpd/bgp_io.c
+++ b/bgpd/bgp_io.c
@@ -201,7 +201,6 @@ static int bgp_process_reads(struct thread *thread)
while (more) {
/* static buffer for transferring packets */
- static unsigned char pktbuf[BGP_MAX_PACKET_SIZE];
/* shorter alias to peer's input buffer */
struct ringbuf *ibw = peer->ibuf_work;
/* packet size as given by header */
@@ -231,8 +230,9 @@ static int bgp_process_reads(struct thread *thread)
*/
if (ringbuf_remain(ibw) >= pktsize) {
struct stream *pkt = stream_new(pktsize);
- assert(ringbuf_get(ibw, pktbuf, pktsize) == pktsize);
- stream_put(pkt, pktbuf, pktsize);
+ assert(STREAM_WRITEABLE(pkt) == pktsize);
+ assert(ringbuf_get(ibw, pkt->data, pktsize) == pktsize);
+ stream_set_endp(pkt, pktsize);
frr_with_mutex(&peer->io_mtx) {
stream_fifo_push(peer->ibuf, pkt);
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 04be8d83eb..f9ff99cab0 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -359,6 +359,7 @@ static void bgp_vrf_terminate(void)
}
static const struct frr_yang_module_info *const bgpd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_route_map_info,
&frr_vrf_info,
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 32489459cb..b0fbdbcf01 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -13525,14 +13525,21 @@ void bgp_route_init(void)
install_element(BGP_IPV6M_NODE,
&no_ipv6_bgp_distance_source_access_list_cmd);
+ /* BGP dampening */
install_element(BGP_NODE, &bgp_damp_set_cmd);
install_element(BGP_NODE, &bgp_damp_unset_cmd);
install_element(BGP_IPV4_NODE, &bgp_damp_set_cmd);
install_element(BGP_IPV4_NODE, &bgp_damp_unset_cmd);
-
- /* IPv4 Multicast Mode */
install_element(BGP_IPV4M_NODE, &bgp_damp_set_cmd);
install_element(BGP_IPV4M_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV4L_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV4L_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV6M_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV6M_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV6L_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV6L_NODE, &bgp_damp_unset_cmd);
/* Large Communities */
install_element(VIEW_NODE, &show_ip_bgp_large_community_list_cmd);
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index e16030d82d..aa5857b9d4 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -14815,11 +14815,6 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
vty_out(vty, "\n");
}
- /* BGP flag dampening. */
- if (CHECK_FLAG(bgp->af_flags[afi][safi],
- BGP_CONFIG_DAMPENING))
- bgp_config_write_damp(vty, afi, safi);
-
/* Route reflector client. */
if (peergroup_af_flag_check(peer, afi, safi,
PEER_FLAG_REFLECTOR_CLIENT)) {
@@ -15044,6 +15039,10 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
bgp_config_write_redistribute(vty, bgp, afi, safi);
+ /* BGP flag dampening. */
+ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
+ bgp_config_write_damp(vty, afi, safi);
+
for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi);
@@ -17620,8 +17619,7 @@ DEFUN (extcommunity_list_standard,
argv_find(argv, argc, "WORD", &idx);
cl_number_or_name = argv[idx]->arg;
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
+ if (argv_find(argv, argc, "(1-4294967295)", &idx))
seq = argv[idx]->arg;
direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT
@@ -17666,8 +17664,7 @@ DEFUN (extcommunity_list_name_expanded,
argv_find(argv, argc, "WORD", &idx);
cl_number_or_name = argv[idx]->arg;
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
+ if (argv_find(argv, argc, "(1-4294967295)", &idx))
seq = argv[idx]->arg;
direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT
@@ -17710,8 +17707,7 @@ DEFUN (no_extcommunity_list_standard_all,
char *seq = NULL;
int idx = 0;
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
+ if (argv_find(argv, argc, "(1-4294967295)", &idx))
seq = argv[idx]->arg;
idx = 0;
@@ -17775,8 +17771,7 @@ DEFUN (no_extcommunity_list_expanded_all,
char *seq = NULL;
int idx = 0;
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
+ if (argv_find(argv, argc, "(1-4294967295)", &idx))
seq = argv[idx]->arg;
idx = 0;
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index faee7dad4a..ec6e00ea34 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -2511,6 +2511,9 @@ static void peer_group2peer_config_copy(struct peer_group *group,
}
}
+ /* Update GR flags for the peer. */
+ bgp_peer_gr_flags_update(peer);
+
bgp_bfd_peer_group2peer_copy(conf, peer);
}
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index 49fa4fe832..eaf6c67bc9 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -191,6 +191,21 @@ review can be made and the branch merged into master. If a development
branch is becomes un-maintained or not being actively worked on after
three months then the Maintainers can decide to remove the branch.
+Debian Branches
+---------------
+
+The Debian project contains "official" packages for FRR. While FRR
+Maintainers may participate in creating these, it is entirely the Debian
+project's decision what to ship and how to work on this.
+
+As a courtesy and for FRR's benefit, this packaging work is currently visible
+in git branches named ``debian/*`` on the main FRR git repository. These
+branches are for the exclusive use by people involved in Debian packaging work
+for FRR. Direct commit access may be handed out and FRR git rules (review,
+testing, etc.) do not apply. Do not push to these branches without talking
+to the people noted under ``Maintainer:`` and ``Uploaders:`` in
+``debian/control`` on the target branch -- even if you are a FRR Maintainer.
+
Changelog
---------
The changelog will be the base for the release notes. A changelog entry for
diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst
index 199685cdfb..dd0e67d4b7 100644
--- a/doc/user/sharp.rst
+++ b/doc/user/sharp.rst
@@ -103,3 +103,25 @@ keyword. At present, no sharp commands will be preserved in the config.
nexthops are specified in nexthop-group ``NAME``. If ``prefix`` is
specified, remove label bindings from the route of type ``TYPE``
also.
+
+.. index:: sharp send opaque
+.. clicmd:: sharp send opaque type (1-255) (1-1000)
+
+ Send opaque ZAPI messages with subtype ``type``. Sharpd will send
+ a stream of messages if the count is greater than one.
+
+.. index:: sharp send opaque unicast
+.. clicmd:: sharp send opaque unicast type (1-255) $proto_str [{instance (0-1000) | session (1-1000)}] (1-1000)
+
+ Send unicast opaque ZAPI messages with subtype ``type``. The
+ protocol, instance, and session_id identify a single target zapi
+ client. Sharpd will send a stream of messages if the count is
+ greater than one.
+
+.. index:: sharp send opaque reg unreg
+.. clicmd:: sharp send opaque <reg | unreg> $proto_str [{instance (0-1000) | session (1-1000)}] type (1-1000)
+
+ Send opaque ZAPI registration and unregistration messages for a
+ single subtype. The messages must specify a protocol daemon by
+ name, and can include optional zapi ``instance`` and ``session``
+ values.
diff --git a/docker/alpine/docker-start b/docker/alpine/docker-start
index 52cfb664ce..3f7737d3bf 100755
--- a/docker/alpine/docker-start
+++ b/docker/alpine/docker-start
@@ -5,7 +5,7 @@ set -e
##
# For volume mounts...
##
-chown -R frr:frr /etc/frr
+chown -R frr:frr /etc/frr || true
/usr/lib/frr/frrinit.sh start
# Sleep forever
diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c
index cdf1c6acdb..6c44ce361c 100644
--- a/eigrpd/eigrp_main.c
+++ b/eigrpd/eigrp_main.c
@@ -139,6 +139,7 @@ struct quagga_signal_t eigrp_signals[] = {
static const struct frr_yang_module_info *const eigrpd_yang_modules[] = {
&frr_eigrpd_info,
+ &frr_filter_info,
&frr_interface_info,
&frr_route_map_info,
&frr_vrf_info,
diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c
index acfe3e2e1f..94e38435a3 100644
--- a/isisd/isis_adjacency.c
+++ b/isisd/isis_adjacency.c
@@ -268,10 +268,11 @@ void isis_adj_state_change(struct isis_adjacency **padj,
struct isis_circuit *circuit = adj->circuit;
bool del = false;
+ if (new_state == old_state)
+ return;
+
adj->adj_state = new_state;
- if (new_state != old_state) {
- send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY);
- }
+ send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY);
if (isis->debugs & DEBUG_ADJ_PACKETS) {
zlog_debug("ISIS-Adj (%s): Adjacency state change %d->%d: %s",
@@ -314,7 +315,7 @@ void isis_adj_state_change(struct isis_adjacency **padj,
* purposes */
adj->last_flap = time(NULL);
adj->flaps++;
- } else if (new_state == ISIS_ADJ_DOWN) {
+ } else if (old_state == ISIS_ADJ_UP) {
listnode_delete(circuit->u.bc.adjdb[level - 1],
adj);
@@ -323,7 +324,8 @@ void isis_adj_state_change(struct isis_adjacency **padj,
isis_tx_queue_clean(circuit->tx_queue);
hook_call(isis_adj_state_change_hook, adj);
- del = true;
+ if (new_state == ISIS_ADJ_DOWN)
+ del = true;
}
if (circuit->u.bc.lan_neighs[level - 1]) {
@@ -362,7 +364,7 @@ void isis_adj_state_change(struct isis_adjacency **padj,
circuit, 0,
&circuit->t_send_csnp[1]);
}
- } else if (new_state == ISIS_ADJ_DOWN) {
+ } else if (old_state == ISIS_ADJ_UP) {
if (adj->circuit->u.p2p.neighbor == adj)
adj->circuit->u.p2p.neighbor = NULL;
circuit->upadjcount[level - 1]--;
@@ -370,7 +372,8 @@ void isis_adj_state_change(struct isis_adjacency **padj,
isis_tx_queue_clean(circuit->tx_queue);
hook_call(isis_adj_state_change_hook, adj);
- del = true;
+ if (new_state == ISIS_ADJ_DOWN)
+ del = true;
}
}
}
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index 003be8d682..e0013e4f1f 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -1233,23 +1233,35 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router,
bool ipv6_router)
{
struct isis_area *area = circuit->area;
- bool change = circuit->ip_router != ip_router
- || circuit->ipv6_router != ipv6_router;
+ int old_ipr = circuit->ip_router;
+ int old_ipv6r = circuit->ipv6_router;
+
+ /* is there something to do? */
+ if (old_ipr == ip_router && old_ipv6r == ipv6_router)
+ return;
- area->ip_circuits += ip_router - circuit->ip_router;
- area->ipv6_circuits += ipv6_router - circuit->ipv6_router;
circuit->ip_router = ip_router;
circuit->ipv6_router = ipv6_router;
+ circuit_update_nlpids(circuit);
- if (!change)
+ /* the area should always be there if we get here, but in the past
+ * there were corner cases where the area was NULL (e.g. because the
+ * circuit was deconfigured following a validation error). Do not
+ * segfault if this happens again.
+ */
+ if (!area) {
+ zlog_err("%s: NULL area for circuit %u", __func__,
+ circuit->circuit_id);
return;
+ }
- circuit_update_nlpids(circuit);
+ area->ip_circuits += ip_router - old_ipr;
+ area->ipv6_circuits += ipv6_router - old_ipv6r;
if (!ip_router && !ipv6_router)
isis_csm_state_change(ISIS_DISABLE, circuit, area);
else
- lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
+ lsp_regenerate_schedule(area, circuit->is_type, 0);
}
ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive)
diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c
index 9202ed5a88..9ff5c86fb0 100644
--- a/isisd/isis_csm.c
+++ b/isisd/isis_csm.c
@@ -141,23 +141,6 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg)
EC_ISIS_CONFIG,
"Could not bring up %s because of invalid config.",
circuit->interface->name);
- flog_err(
- EC_ISIS_CONFIG,
- "Clearing config for %s. Please re-examine it.",
- circuit->interface->name);
- if (circuit->ip_router) {
- circuit->ip_router = 0;
- circuit->area->ip_circuits--;
- }
- if (circuit->ipv6_router) {
- circuit->ipv6_router = 0;
- circuit->area->ipv6_circuits--;
- }
- circuit_update_nlpids(circuit);
- isis_circuit_deconfigure(circuit,
- circuit->area);
- listnode_add(isis->init_circ_list, circuit);
- circuit->state = C_STATE_INIT;
break;
}
circuit->state = C_STATE_UP;
diff --git a/isisd/isis_main.c b/isisd/isis_main.c
index 78654b2f1c..7d45dd9c2e 100644
--- a/isisd/isis_main.c
+++ b/isisd/isis_main.c
@@ -166,6 +166,7 @@ struct quagga_signal_t isisd_signals[] = {
static const struct frr_yang_module_info *const isisd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
#ifndef FABRICD
&frr_isisd_info,
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index a649e896fa..9633e46415 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -116,8 +116,8 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args)
addr.addr_len = dotformat2buff(buff, net_title);
memcpy(addr.area_addr, buff, addr.addr_len);
if (addr.area_addr[addr.addr_len - 1] != 0) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
+ snprintf(
+ args->errmsg, args->errmsg_len,
"nsel byte (last byte) in area address must be 0");
return NB_ERR_VALIDATION;
}
@@ -125,8 +125,8 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args)
/* Check that the SystemID portions match */
if (memcmp(isis->sysid, GETSYSID((&addr)),
ISIS_SYS_ID_LEN)) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
+ snprintf(
+ args->errmsg, args->errmsg_len,
"System ID must not change when defining additional area addresses");
return NB_ERR_VALIDATION;
}
@@ -332,8 +332,8 @@ int isis_instance_lsp_mtu_modify(struct nb_cb_modify_args *args)
&& circuit->state != C_STATE_UP)
continue;
if (lsp_mtu > isis_circuit_pdu_size(circuit)) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
+ snprintf(
+ args->errmsg, args->errmsg_len,
"ISIS area contains circuit %s, which has a maximum PDU size of %zu",
circuit->interface->name,
isis_circuit_pdu_size(circuit));
@@ -1047,6 +1047,7 @@ int isis_instance_redistribute_ipv6_metric_modify(
*/
static int isis_multi_topology_common(enum nb_event event,
const struct lyd_node *dnode,
+ char *errmsg, size_t errmsg_len,
const char *topology, bool create)
{
struct isis_area *area;
@@ -1056,8 +1057,8 @@ static int isis_multi_topology_common(enum nb_event event,
switch (event) {
case NB_EV_VALIDATE:
if (mtid == (uint16_t)-1) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Unknown topology %s", topology);
+ snprintf(errmsg, errmsg_len, "Unknown topology %s",
+ topology);
return NB_ERR_VALIDATION;
}
break;
@@ -1100,6 +1101,7 @@ int isis_instance_multi_topology_ipv4_multicast_create(
struct nb_cb_create_args *args)
{
return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
"ipv4-multicast", true);
}
@@ -1107,6 +1109,7 @@ int isis_instance_multi_topology_ipv4_multicast_destroy(
struct nb_cb_destroy_args *args)
{
return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
"ipv4-multicast", false);
}
@@ -1126,15 +1129,17 @@ int isis_instance_multi_topology_ipv4_multicast_overload_modify(
int isis_instance_multi_topology_ipv4_management_create(
struct nb_cb_create_args *args)
{
- return isis_multi_topology_common(args->event, args->dnode, "ipv4-mgmt",
- true);
+ return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
+ "ipv4-mgmt", true);
}
int isis_instance_multi_topology_ipv4_management_destroy(
struct nb_cb_destroy_args *args)
{
- return isis_multi_topology_common(args->event, args->dnode, "ipv4-mgmt",
- false);
+ return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
+ "ipv4-mgmt", false);
}
/*
@@ -1154,6 +1159,7 @@ int isis_instance_multi_topology_ipv6_unicast_create(
struct nb_cb_create_args *args)
{
return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
"ipv6-unicast", true);
}
@@ -1161,6 +1167,7 @@ int isis_instance_multi_topology_ipv6_unicast_destroy(
struct nb_cb_destroy_args *args)
{
return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
"ipv6-unicast", false);
}
@@ -1181,6 +1188,7 @@ int isis_instance_multi_topology_ipv6_multicast_create(
struct nb_cb_create_args *args)
{
return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
"ipv6-multicast", true);
}
@@ -1188,6 +1196,7 @@ int isis_instance_multi_topology_ipv6_multicast_destroy(
struct nb_cb_destroy_args *args)
{
return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
"ipv6-multicast", false);
}
@@ -1207,15 +1216,17 @@ int isis_instance_multi_topology_ipv6_multicast_overload_modify(
int isis_instance_multi_topology_ipv6_management_create(
struct nb_cb_create_args *args)
{
- return isis_multi_topology_common(args->event, args->dnode, "ipv6-mgmt",
- true);
+ return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
+ "ipv6-mgmt", true);
}
int isis_instance_multi_topology_ipv6_management_destroy(
struct nb_cb_destroy_args *args)
{
- return isis_multi_topology_common(args->event, args->dnode, "ipv6-mgmt",
- false);
+ return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
+ "ipv6-mgmt", false);
}
/*
@@ -1235,6 +1246,7 @@ int isis_instance_multi_topology_ipv6_dstsrc_create(
struct nb_cb_create_args *args)
{
return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
"ipv6-dstsrc", true);
}
@@ -1242,6 +1254,7 @@ int isis_instance_multi_topology_ipv6_dstsrc_destroy(
struct nb_cb_destroy_args *args)
{
return isis_multi_topology_common(args->event, args->dnode,
+ args->errmsg, args->errmsg_len,
"ipv6-dstsrc", false);
}
@@ -1721,10 +1734,10 @@ int lib_interface_isis_create(struct nb_cb_create_args *args)
min_mtu = DEFAULT_LSP_MTU;
#endif /* ifndef FABRICD */
if (actual_mtu < min_mtu) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Interface %s has MTU %" PRIu32
- ", minimum MTU for the area is %" PRIu32 "",
- ifp->name, actual_mtu, min_mtu);
+ snprintf(args->errmsg, args->errmsg_len,
+ "Interface %s has MTU %" PRIu32
+ ", minimum MTU for the area is %" PRIu32 "",
+ ifp->name, actual_mtu, min_mtu);
return NB_ERR_VALIDATION;
}
break;
@@ -1801,9 +1814,9 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args)
area_tag = yang_dnode_get_string(args->dnode, NULL);
if (circuit && circuit->area && circuit->area->area_tag
&& strcmp(circuit->area->area_tag, area_tag)) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "ISIS circuit is already defined on %s",
- circuit->area->area_tag);
+ snprintf(args->errmsg, args->errmsg_len,
+ "ISIS circuit is already defined on %s",
+ circuit->area->area_tag);
return NB_ERR_VALIDATION;
}
}
@@ -1839,9 +1852,9 @@ int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args)
if (circuit && circuit->state == C_STATE_UP
&& circuit->area->is_type != IS_LEVEL_1_AND_2
&& circuit->area->is_type != circ_type) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Invalid circuit level for area %s",
- circuit->area->area_tag);
+ snprintf(args->errmsg, args->errmsg_len,
+ "Invalid circuit level for area %s",
+ circuit->area->area_tag);
return NB_ERR_VALIDATION;
}
break;
@@ -2163,16 +2176,16 @@ int lib_interface_isis_network_type_modify(struct nb_cb_modify_args *args)
if (!circuit)
break;
if (circuit->circ_type == CIRCUIT_T_LOOPBACK) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
+ snprintf(
+ args->errmsg, args->errmsg_len,
"Cannot change network type on loopback interface");
return NB_ERR_VALIDATION;
}
if (net_type == CIRCUIT_T_BROADCAST
&& circuit->state == C_STATE_UP
&& !if_is_broadcast(circuit->interface)) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
+ snprintf(
+ args->errmsg, args->errmsg_len,
"Cannot configure non-broadcast interface for broadcast operation");
return NB_ERR_VALIDATION;
}
@@ -2208,8 +2221,8 @@ int lib_interface_isis_passive_modify(struct nb_cb_modify_args *args)
if (!ifp)
return NB_OK;
if (if_is_loopback(ifp)) {
- flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
- "Loopback is always passive");
+ snprintf(args->errmsg, args->errmsg_len,
+ "Loopback is always passive");
return NB_ERR_VALIDATION;
}
}
@@ -2312,7 +2325,8 @@ int lib_interface_isis_disable_three_way_handshake_modify(
* /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast
*/
static int lib_interface_isis_multi_topology_common(
- enum nb_event event, const struct lyd_node *dnode, uint16_t mtid)
+ enum nb_event event, const struct lyd_node *dnode, char *errmsg,
+ size_t errmsg_len, uint16_t mtid)
{
struct isis_circuit *circuit;
bool value;
@@ -2321,8 +2335,8 @@ static int lib_interface_isis_multi_topology_common(
case NB_EV_VALIDATE:
circuit = nb_running_get_entry(dnode, NULL, false);
if (circuit && circuit->area && circuit->area->oldmetric) {
- flog_warn(
- EC_LIB_NB_CB_CONFIG_VALIDATE,
+ snprintf(
+ errmsg, errmsg_len,
"Multi topology IS-IS can only be used with wide metrics");
return NB_ERR_VALIDATION;
}
@@ -2344,7 +2358,8 @@ int lib_interface_isis_multi_topology_ipv4_unicast_modify(
struct nb_cb_modify_args *args)
{
return lib_interface_isis_multi_topology_common(
- args->event, args->dnode, ISIS_MT_IPV4_UNICAST);
+ args->event, args->dnode, args->errmsg, args->errmsg_len,
+ ISIS_MT_IPV4_UNICAST);
}
/*
@@ -2355,7 +2370,8 @@ int lib_interface_isis_multi_topology_ipv4_multicast_modify(
struct nb_cb_modify_args *args)
{
return lib_interface_isis_multi_topology_common(
- args->event, args->dnode, ISIS_MT_IPV4_MULTICAST);
+ args->event, args->dnode, args->errmsg, args->errmsg_len,
+ ISIS_MT_IPV4_MULTICAST);
}
/*
@@ -2366,7 +2382,8 @@ int lib_interface_isis_multi_topology_ipv4_management_modify(
struct nb_cb_modify_args *args)
{
return lib_interface_isis_multi_topology_common(
- args->event, args->dnode, ISIS_MT_IPV4_MGMT);
+ args->event, args->dnode, args->errmsg, args->errmsg_len,
+ ISIS_MT_IPV4_MGMT);
}
/*
@@ -2377,7 +2394,8 @@ int lib_interface_isis_multi_topology_ipv6_unicast_modify(
struct nb_cb_modify_args *args)
{
return lib_interface_isis_multi_topology_common(
- args->event, args->dnode, ISIS_MT_IPV6_UNICAST);
+ args->event, args->dnode, args->errmsg, args->errmsg_len,
+ ISIS_MT_IPV6_UNICAST);
}
/*
@@ -2388,7 +2406,8 @@ int lib_interface_isis_multi_topology_ipv6_multicast_modify(
struct nb_cb_modify_args *args)
{
return lib_interface_isis_multi_topology_common(
- args->event, args->dnode, ISIS_MT_IPV6_MULTICAST);
+ args->event, args->dnode, args->errmsg, args->errmsg_len,
+ ISIS_MT_IPV6_MULTICAST);
}
/*
@@ -2399,7 +2418,8 @@ int lib_interface_isis_multi_topology_ipv6_management_modify(
struct nb_cb_modify_args *args)
{
return lib_interface_isis_multi_topology_common(
- args->event, args->dnode, ISIS_MT_IPV6_MGMT);
+ args->event, args->dnode, args->errmsg, args->errmsg_len,
+ ISIS_MT_IPV6_MGMT);
}
/*
@@ -2409,5 +2429,6 @@ int lib_interface_isis_multi_topology_ipv6_dstsrc_modify(
struct nb_cb_modify_args *args)
{
return lib_interface_isis_multi_topology_common(
- args->event, args->dnode, ISIS_MT_IPV6_DSTSRC);
+ args->event, args->dnode, args->errmsg, args->errmsg_len,
+ ISIS_MT_IPV6_DSTSRC);
}
diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c
index c6126b9396..56734a4f76 100644
--- a/ldpd/ldpd.c
+++ b/ldpd/ldpd.c
@@ -180,6 +180,7 @@ static struct quagga_signal_t ldp_signals[] =
};
static const struct frr_yang_module_info *const ldpd_yang_modules[] = {
+ &frr_filter_info,
&frr_vrf_info,
};
diff --git a/lib/command.h b/lib/command.h
index ed0ea18d6f..21bb613540 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -406,6 +406,7 @@ struct cmd_node {
#define FILTER_LOG_STR "Filter Logs\n"
#define BFD_PROFILE_STR "BFD profile.\n"
#define BFD_PROFILE_NAME_STR "BFD profile name.\n"
+#define SHARP_STR "Sharp Routing Protocol\n"
#define CMD_VNI_RANGE "(1-16777215)"
#define CONF_BACKUP_EXT ".sav"
diff --git a/lib/filter.c b/lib/filter.c
index 4a83b8b043..e6add0462b 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -29,70 +29,12 @@
#include "log.h"
#include "routemap.h"
#include "libfrr.h"
+#include "northbound_cli.h"
DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST, "Access List")
DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST_STR, "Access List Str")
DEFINE_MTYPE_STATIC(LIB, ACCESS_FILTER, "Access Filter")
-struct filter_cisco {
- /* Cisco access-list */
- int extended;
- struct in_addr addr;
- struct in_addr addr_mask;
- struct in_addr mask;
- struct in_addr mask_mask;
-};
-
-struct filter_zebra {
- /* If this filter is "exact" match then this flag is set. */
- int exact;
-
- /* Prefix information. */
- struct prefix prefix;
-};
-
-/* Filter element of access list */
-struct filter {
- /* For doubly linked list. */
- struct filter *next;
- struct filter *prev;
-
- /* Filter type information. */
- enum filter_type type;
-
- /* Sequence number */
- int64_t seq;
-
- /* Cisco access-list */
- int cisco;
-
- union {
- struct filter_cisco cfilter;
- struct filter_zebra zfilter;
- } u;
-};
-
-/* List of access_list. */
-struct access_list_list {
- struct access_list *head;
- struct access_list *tail;
-};
-
-/* Master structure of access_list. */
-struct access_master {
- /* List of access_list which name is number. */
- struct access_list_list num;
-
- /* List of access_list which name is string. */
- struct access_list_list str;
-
- /* Hook function which is executed when new access_list is added. */
- void (*add_hook)(struct access_list *);
-
- /* Hook function which is executed when access_list is deleted. */
- void (*delete_hook)(struct access_list *);
-};
-
/* Static structure for mac access_list's master. */
static struct access_master access_master_mac = {
{NULL, NULL},
@@ -129,7 +71,7 @@ static struct access_master *access_master_get(afi_t afi)
}
/* Allocate new filter structure. */
-static struct filter *filter_new(void)
+struct filter *filter_new(void)
{
return XCALLOC(MTYPE_ACCESS_FILTER, sizeof(struct filter));
}
@@ -210,7 +152,7 @@ static void access_list_free(struct access_list *access)
}
/* Delete access_list from access_master and free it. */
-static void access_list_delete(struct access_list *access)
+void access_list_delete(struct access_list *access)
{
struct filter *filter;
struct filter *next;
@@ -356,7 +298,7 @@ struct access_list *access_list_lookup(afi_t afi, const char *name)
/* Get access list from list of access_list. If there isn't matched
access_list create new one and return it. */
-static struct access_list *access_list_get(afi_t afi, const char *name)
+struct access_list *access_list_get(afi_t afi, const char *name)
{
struct access_list *access;
@@ -406,7 +348,7 @@ void access_list_delete_hook(void (*func)(struct access_list *access))
}
/* Calculate new sequential number. */
-static int64_t filter_new_seq_get(struct access_list *access)
+int64_t filter_new_seq_get(struct access_list *access)
{
int64_t maxseq;
int64_t newseq;
@@ -436,22 +378,12 @@ static struct filter *filter_seq_check(struct access_list *access,
return NULL;
}
-/* If access_list has no filter then return 1. */
-static bool access_list_empty(struct access_list *access)
-{
- if (access->head == NULL && access->tail == NULL)
- return true;
- else
- return false;
-}
-
/* Delete filter from specified access_list. If there is hook
function execute it. */
-static void access_list_filter_delete(struct access_list *access,
- struct filter *filter)
+void access_list_filter_delete(struct access_list *access,
+ struct filter *filter)
{
struct access_master *master;
- struct filter *replace = filter;
master = access->master;
@@ -471,15 +403,11 @@ static void access_list_filter_delete(struct access_list *access,
/* Run hook function. */
if (master->delete_hook)
(*master->delete_hook)(access);
-
- /* If access_list becomes empty delete it from access_master. */
- if (access_list_empty(access) && !replace)
- access_list_delete(access);
}
/* Add new filter to the end of specified access_list. */
-static void access_list_filter_add(struct access_list *access,
- struct filter *filter)
+void access_list_filter_add(struct access_list *access,
+ struct filter *filter)
{
struct filter *replace;
struct filter *point;
@@ -541,8 +469,8 @@ static void access_list_filter_add(struct access_list *access,
host A single host address
*/
-static struct filter *filter_lookup_cisco(struct access_list *access,
- struct filter *mnew)
+struct filter *filter_lookup_cisco(struct access_list *access,
+ struct filter *mnew)
{
struct filter *mfilter;
struct filter_cisco *filter;
@@ -573,8 +501,8 @@ static struct filter *filter_lookup_cisco(struct access_list *access,
return NULL;
}
-static struct filter *filter_lookup_zebra(struct access_list *access,
- struct filter *mnew)
+struct filter *filter_lookup_zebra(struct access_list *access,
+ struct filter *mnew)
{
struct filter *mfilter;
struct filter_zebra *filter;
@@ -594,1905 +522,6 @@ static struct filter *filter_lookup_zebra(struct access_list *access,
return NULL;
}
-static int vty_access_list_remark_unset(struct vty *vty, afi_t afi,
- const char *name)
-{
- struct access_list *access;
-
- access = access_list_lookup(afi, name);
- if (!access) {
- vty_out(vty, "%% access-list %s doesn't exist\n", name);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- XFREE(MTYPE_TMP, access->remark);
-
- if (access->head == NULL && access->tail == NULL)
- access_list_delete(access);
-
- return CMD_SUCCESS;
-}
-
-static int filter_set_cisco(struct vty *vty, const char *name_str,
- const char *seq, const char *type_str,
- const char *addr_str, const char *addr_mask_str,
- const char *mask_str, const char *mask_mask_str,
- int extended, int set)
-{
- int ret;
- enum filter_type type = FILTER_DENY;
- struct filter *mfilter;
- struct filter_cisco *filter;
- struct access_list *access;
- struct in_addr addr;
- struct in_addr addr_mask;
- struct in_addr mask;
- struct in_addr mask_mask;
- int64_t seqnum = -1;
-
- if (seq)
- seqnum = (int64_t)atol(seq);
-
- /* Check of filter type. */
- if (type_str) {
- if (strncmp(type_str, "p", 1) == 0)
- type = FILTER_PERMIT;
- else if (strncmp(type_str, "d", 1) == 0)
- type = FILTER_DENY;
- else {
- vty_out(vty, "%% filter type must be permit or deny\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- }
-
- ret = inet_aton(addr_str, &addr);
- if (ret <= 0) {
- vty_out(vty, "%%Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- ret = inet_aton(addr_mask_str, &addr_mask);
- if (ret <= 0) {
- vty_out(vty, "%%Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- if (extended) {
- ret = inet_aton(mask_str, &mask);
- if (ret <= 0) {
- vty_out(vty, "%%Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- ret = inet_aton(mask_mask_str, &mask_mask);
- if (ret <= 0) {
- vty_out(vty, "%%Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- }
-
- mfilter = filter_new();
- mfilter->type = type;
- mfilter->cisco = 1;
- mfilter->seq = seqnum;
- filter = &mfilter->u.cfilter;
- filter->extended = extended;
- filter->addr.s_addr = addr.s_addr & ~addr_mask.s_addr;
- filter->addr_mask.s_addr = addr_mask.s_addr;
-
- if (extended) {
- filter->mask.s_addr = mask.s_addr & ~mask_mask.s_addr;
- filter->mask_mask.s_addr = mask_mask.s_addr;
- }
-
- /* Install new filter to the access_list. */
- access = access_list_get(AFI_IP, name_str);
-
- if (set) {
- if (filter_lookup_cisco(access, mfilter))
- filter_free(mfilter);
- else
- access_list_filter_add(access, mfilter);
- } else {
- struct filter *delete_filter;
-
- delete_filter = filter_lookup_cisco(access, mfilter);
- if (delete_filter)
- access_list_filter_delete(access, delete_filter);
-
- filter_free(mfilter);
- }
-
- return CMD_SUCCESS;
-}
-
-/* Standard access-list */
-DEFUN (access_list_standard,
- access_list_standard_cmd,
- "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D A.B.C.D",
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP standard access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Address to match\n"
- "Wildcard bits\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *address = NULL;
- char *wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- address = argv[idx]->arg;
- wildcard = argv[idx + 1]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- address, wildcard, NULL, NULL, 0, 1);
-}
-
-DEFUN (access_list_standard_nomask,
- access_list_standard_nomask_cmd,
- "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D",
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP standard access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Address to match\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *address = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx)
- address = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- address, "0.0.0.0", NULL, NULL, 0, 1);
-}
-
-DEFUN (access_list_standard_host,
- access_list_standard_host_cmd,
- "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> host A.B.C.D",
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP standard access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "A single host address\n"
- "Address to match\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *address = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx)
- address = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- address, "0.0.0.0", NULL, NULL, 0, 1);
-}
-
-DEFUN (access_list_standard_any,
- access_list_standard_any_cmd,
- "access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> any",
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP standard access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any source host\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- "0.0.0.0", "255.255.255.255", NULL, NULL, 0, 1);
-}
-
-DEFUN (no_access_list_standard,
- no_access_list_standard_cmd,
- "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D A.B.C.D",
- NO_STR
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP standard access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Address to match\n"
- "Wildcard bits\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *address = NULL;
- char *wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- address = argv[idx]->arg;
- wildcard = argv[idx + 1]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- address, wildcard, NULL, NULL, 0, 0);
-}
-
-DEFUN (no_access_list_standard_nomask,
- no_access_list_standard_nomask_cmd,
- "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> A.B.C.D",
- NO_STR
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP standard access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Address to match\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *address = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx)
- address = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- address, "0.0.0.0", NULL, NULL, 0, 0);
-}
-
-DEFUN (no_access_list_standard_host,
- no_access_list_standard_host_cmd,
- "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> host A.B.C.D",
- NO_STR
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP standard access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "A single host address\n"
- "Address to match\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *address = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx)
- address = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- address, "0.0.0.0", NULL, NULL, 0, 0);
-}
-
-DEFUN (no_access_list_standard_any,
- no_access_list_standard_any_cmd,
- "no access-list <(1-99)|(1300-1999)> [seq (1-4294967295)] <deny|permit> any",
- NO_STR
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP standard access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any source host\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- "0.0.0.0", "255.255.255.255", NULL, NULL, 0, 0);
-}
-
-/* Extended access-list */
-DEFUN (access_list_extended,
- access_list_extended_cmd,
- "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Source address\n"
- "Source wildcard bits\n"
- "Destination address\n"
- "Destination Wildcard bits\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *dst = NULL;
- char *src_wildcard = NULL;
- char *dst_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- src_wildcard = argv[idx + 1]->arg;
- dst = argv[idx + 2]->arg;
- dst_wildcard = argv[idx + 3]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- src_wildcard, dst, dst_wildcard, 1, 1);
-}
-
-DEFUN (access_list_extended_mask_any,
- access_list_extended_mask_any_cmd,
- "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D any",
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Source address\n"
- "Source wildcard bits\n"
- "Any destination host\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *src_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- src_wildcard = argv[idx + 1]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- src_wildcard, "0.0.0.0", "255.255.255.255", 1,
- 1);
-}
-
-DEFUN (access_list_extended_any_mask,
- access_list_extended_any_mask_cmd,
- "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any A.B.C.D A.B.C.D",
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Any source host\n"
- "Destination address\n"
- "Destination Wildcard bits\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *dst = NULL;
- char *dst_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- dst = argv[idx]->arg;
- dst_wildcard = argv[idx + 1]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- "0.0.0.0", "255.255.255.255", dst, dst_wildcard,
- 1, 1);
-}
-
-DEFUN (access_list_extended_any_any,
- access_list_extended_any_any_cmd,
- "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any any",
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Any source host\n"
- "Any destination host\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- "0.0.0.0", "255.255.255.255", "0.0.0.0",
- "255.255.255.255", 1, 1);
-}
-
-DEFUN (access_list_extended_mask_host,
- access_list_extended_mask_host_cmd,
- "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D",
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Source address\n"
- "Source wildcard bits\n"
- "A single destination host\n"
- "Destination address\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *dst = NULL;
- char *src_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- src_wildcard = argv[idx + 1]->arg;
- dst = argv[idx + 3]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- src_wildcard, dst, "0.0.0.0", 1, 1);
-}
-
-DEFUN (access_list_extended_host_mask,
- access_list_extended_host_mask_cmd,
- "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D",
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "A single source host\n"
- "Source address\n"
- "Destination address\n"
- "Destination Wildcard bits\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *dst = NULL;
- char *dst_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- dst = argv[idx + 1]->arg;
- dst_wildcard = argv[idx + 2]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- "0.0.0.0", dst, dst_wildcard, 1, 1);
-}
-
-DEFUN (access_list_extended_host_host,
- access_list_extended_host_host_cmd,
- "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D host A.B.C.D",
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "A single source host\n"
- "Source address\n"
- "A single destination host\n"
- "Destination address\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *dst = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- dst = argv[idx + 2]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- "0.0.0.0", dst, "0.0.0.0", 1, 1);
-}
-
-DEFUN (access_list_extended_any_host,
- access_list_extended_any_host_cmd,
- "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any host A.B.C.D",
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Any source host\n"
- "A single destination host\n"
- "Destination address\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *dst = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx)
- dst = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- "0.0.0.0", "255.255.255.255", dst, "0.0.0.0", 1,
- 1);
-}
-
-DEFUN (access_list_extended_host_any,
- access_list_extended_host_any_cmd,
- "access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D any",
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "A single source host\n"
- "Source address\n"
- "Any destination host\n")
-{
- int idx_acl = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx)
- src = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 1);
-}
-
-DEFUN (no_access_list_extended,
- no_access_list_extended_cmd,
- "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D",
- NO_STR
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Source address\n"
- "Source wildcard bits\n"
- "Destination address\n"
- "Destination Wildcard bits\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *dst = NULL;
- char *src_wildcard = NULL;
- char *dst_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- src_wildcard = argv[idx + 1]->arg;
- dst = argv[idx + 2]->arg;
- dst_wildcard = argv[idx + 3]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- src_wildcard, dst, dst_wildcard, 1, 0);
-}
-
-DEFUN (no_access_list_extended_mask_any,
- no_access_list_extended_mask_any_cmd,
- "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D any",
- NO_STR
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Source address\n"
- "Source wildcard bits\n"
- "Any destination host\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *src_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- src_wildcard = argv[idx + 1]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- src_wildcard, "0.0.0.0", "255.255.255.255", 1,
- 0);
-}
-
-DEFUN (no_access_list_extended_any_mask,
- no_access_list_extended_any_mask_cmd,
- "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any A.B.C.D A.B.C.D",
- NO_STR
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Any source host\n"
- "Destination address\n"
- "Destination Wildcard bits\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *dst = NULL;
- char *dst_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- dst = argv[idx]->arg;
- dst_wildcard = argv[idx + 1]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- "0.0.0.0", "255.255.255.255", dst, dst_wildcard,
- 1, 0);
-}
-
-DEFUN (no_access_list_extended_any_any,
- no_access_list_extended_any_any_cmd,
- "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any any",
- NO_STR
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Any source host\n"
- "Any destination host\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- "0.0.0.0", "255.255.255.255", "0.0.0.0",
- "255.255.255.255", 1, 0);
-}
-
-DEFUN (no_access_list_extended_mask_host,
- no_access_list_extended_mask_host_cmd,
- "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D",
- NO_STR
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Source address\n"
- "Source wildcard bits\n"
- "A single destination host\n"
- "Destination address\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *dst = NULL;
- char *src_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- src_wildcard = argv[idx + 1]->arg;
- dst = argv[idx + 3]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- src_wildcard, dst, "0.0.0.0", 1, 0);
-}
-
-DEFUN (no_access_list_extended_host_mask,
- no_access_list_extended_host_mask_cmd,
- "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D",
- NO_STR
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "A single source host\n"
- "Source address\n"
- "Destination address\n"
- "Destination Wildcard bits\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *dst = NULL;
- char *dst_wildcard = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- dst = argv[idx + 1]->arg;
- dst_wildcard = argv[idx + 2]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- "0.0.0.0", dst, dst_wildcard, 1, 0);
-}
-
-DEFUN (no_access_list_extended_host_host,
- no_access_list_extended_host_host_cmd,
- "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D host A.B.C.D",
- NO_STR
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "A single source host\n"
- "Source address\n"
- "A single destination host\n"
- "Destination address\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
- char *dst = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx) {
- src = argv[idx]->arg;
- dst = argv[idx + 2]->arg;
- }
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- "0.0.0.0", dst, "0.0.0.0", 1, 0);
-}
-
-DEFUN (no_access_list_extended_any_host,
- no_access_list_extended_any_host_cmd,
- "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip any host A.B.C.D",
- NO_STR
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "Any source host\n"
- "A single destination host\n"
- "Destination address\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *dst = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx)
- dst = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny,
- "0.0.0.0", "255.255.255.255", dst, "0.0.0.0", 1,
- 0);
-}
-
-DEFUN (no_access_list_extended_host_any,
- no_access_list_extended_host_any_cmd,
- "no access-list <(100-199)|(2000-2699)> [seq (1-4294967295)] <deny|permit> ip host A.B.C.D any",
- NO_STR
- "Add an access list entry\n"
- "IP extended access list\n"
- "IP extended access list (expanded range)\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any Internet Protocol\n"
- "A single source host\n"
- "Source address\n"
- "Any destination host\n")
-{
- int idx_acl = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *src = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D", &idx);
- if (idx)
- src = argv[idx]->arg;
-
- return filter_set_cisco(vty, argv[idx_acl]->arg, seq, permit_deny, src,
- "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 0);
-}
-
-static int filter_set_zebra(struct vty *vty, const char *name_str,
- const char *seq, const char *type_str, afi_t afi,
- const char *prefix_str, int exact, int set)
-{
- int ret;
- enum filter_type type = FILTER_DENY;
- struct filter *mfilter;
- struct filter_zebra *filter;
- struct access_list *access;
- struct prefix p;
- int64_t seqnum = -1;
-
- if (strlen(name_str) > ACL_NAMSIZ) {
- vty_out(vty,
- "%% ACL name %s is invalid: length exceeds "
- "%d characters\n",
- name_str, ACL_NAMSIZ);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- if (seq)
- seqnum = (int64_t)atol(seq);
-
- /* Check of filter type. */
- if (type_str) {
- if (strncmp(type_str, "p", 1) == 0)
- type = FILTER_PERMIT;
- else if (strncmp(type_str, "d", 1) == 0)
- type = FILTER_DENY;
- else {
- vty_out(vty, "filter type must be [permit|deny]\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- }
-
- /* Check string format of prefix and prefixlen. */
- if (afi == AFI_IP) {
- ret = str2prefix_ipv4(prefix_str, (struct prefix_ipv4 *)&p);
- if (ret <= 0) {
- vty_out(vty,
- "IP address prefix/prefixlen is malformed\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- } else if (afi == AFI_IP6) {
- ret = str2prefix_ipv6(prefix_str, (struct prefix_ipv6 *)&p);
- if (ret <= 0) {
- vty_out(vty,
- "IPv6 address prefix/prefixlen is malformed\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- } else if (afi == AFI_L2VPN) {
- ret = str2prefix_eth(prefix_str, (struct prefix_eth *)&p);
- if (ret <= 0) {
- vty_out(vty, "MAC address is malformed\n");
- return CMD_WARNING;
- }
- } else
- return CMD_WARNING_CONFIG_FAILED;
-
- mfilter = filter_new();
- mfilter->type = type;
- mfilter->seq = seqnum;
- filter = &mfilter->u.zfilter;
- prefix_copy(&filter->prefix, &p);
-
- /* "exact-match" */
- if (exact)
- filter->exact = 1;
-
- /* Install new filter to the access_list. */
- access = access_list_get(afi, name_str);
-
- if (set) {
- if (filter_lookup_zebra(access, mfilter))
- filter_free(mfilter);
- else
- access_list_filter_add(access, mfilter);
- } else {
- struct filter *delete_filter;
- delete_filter = filter_lookup_zebra(access, mfilter);
- if (delete_filter)
- access_list_filter_delete(access, delete_filter);
-
- filter_free(mfilter);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN (mac_access_list,
- mac_access_list_cmd,
- "mac access-list WORD [seq (1-4294967295)] <deny|permit> X:X:X:X:X:X",
- "Add a mac access-list\n"
- "Add an access list entry\n"
- "MAC zebra access-list name\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "MAC address to match. e.g. 00:01:00:01:00:01\n")
-{
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *mac = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "X:X:X:X:X:X", &idx);
- if (idx)
- mac = argv[idx]->arg;
- assert(mac);
-
- return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN,
- mac, 0, 1);
-}
-
-DEFUN (no_mac_access_list,
- no_mac_access_list_cmd,
- "no mac access-list WORD [seq (1-4294967295)] <deny|permit> X:X:X:X:X:X",
- NO_STR
- "Remove a mac access-list\n"
- "Remove an access list entry\n"
- "MAC zebra access-list name\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "MAC address to match. e.g. 00:01:00:01:00:01\n")
-{
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *mac = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "X:X:X:X:X:X", &idx);
- if (idx)
- mac = argv[idx]->arg;
- assert(mac);
-
- return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN,
- mac, 0, 0);
-}
-
-DEFUN (mac_access_list_any,
- mac_access_list_any_cmd,
- "mac access-list WORD [seq (1-4294967295)] <deny|permit> any",
- "Add a mac access-list\n"
- "Add an access list entry\n"
- "MAC zebra access-list name\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "MAC address to match. e.g. 00:01:00:01:00:01\n")
-{
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN,
- "00:00:00:00:00:00", 0, 1);
-}
-
-DEFUN (no_mac_access_list_any,
- no_mac_access_list_any_cmd,
- "no mac access-list WORD [seq (1-4294967295)] <deny|permit> any",
- NO_STR
- "Remove a mac access-list\n"
- "Remove an access list entry\n"
- "MAC zebra access-list name\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "MAC address to match. e.g. 00:01:00:01:00:01\n")
-{
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny, AFI_L2VPN,
- "00:00:00:00:00:00", 0, 0);
-}
-
-DEFUN (access_list_exact,
- access_list_exact_cmd,
- "access-list WORD [seq (1-4294967295)] <deny|permit> A.B.C.D/M [exact-match]",
- "Add an access list entry\n"
- "IP zebra access-list name\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Prefix to match. e.g. 10.0.0.0/8\n"
- "Exact match of the prefixes\n")
-{
- int idx = 0;
- int exact = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *prefix = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D/M", &idx);
- if (idx)
- prefix = argv[idx]->arg;
- assert(prefix);
-
- idx = 0;
- if (argv_find(argv, argc, "exact-match", &idx))
- exact = 1;
-
- return filter_set_zebra(vty, argv[1]->arg, seq, permit_deny,
- AFI_IP, prefix, exact, 1);
-}
-
-DEFUN (access_list_any,
- access_list_any_cmd,
- "access-list WORD [seq (1-4294967295)] <deny|permit> any",
- "Add an access list entry\n"
- "IP zebra access-list name\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Prefix to match. e.g. 10.0.0.0/8\n")
-{
- int idx_word = 1;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny,
- AFI_IP, "0.0.0.0/0", 0, 1);
-}
-
-DEFUN (no_access_list_exact,
- no_access_list_exact_cmd,
- "no access-list WORD [seq (1-4294967295)] <deny|permit> A.B.C.D/M [exact-match]",
- NO_STR
- "Add an access list entry\n"
- "IP zebra access-list name\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Prefix to match. e.g. 10.0.0.0/8\n"
- "Exact match of the prefixes\n")
-{
- int idx = 0;
- int exact = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *prefix = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "A.B.C.D/M", &idx);
- if (idx)
- prefix = argv[idx]->arg;
- assert(prefix);
-
- idx = 0;
- if (argv_find(argv, argc, "exact-match", &idx))
- exact = 1;
-
- return filter_set_zebra(vty, argv[2]->arg, seq, permit_deny,
- AFI_IP, prefix, exact, 0);
-}
-
-DEFUN (no_access_list_any,
- no_access_list_any_cmd,
- "no access-list WORD [seq (1-4294967295)] <deny|permit> any",
- NO_STR
- "Add an access list entry\n"
- "IP zebra access-list name\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Prefix to match. e.g. 10.0.0.0/8\n")
-{
- int idx_word = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny,
- AFI_IP, "0.0.0.0/0", 0, 0);
-}
-
-DEFUN (no_access_list_all,
- no_access_list_all_cmd,
- "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD>",
- NO_STR
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP extended access list\n"
- "IP standard access list (expanded range)\n"
- "IP extended access list (expanded range)\n"
- "IP zebra access-list name\n")
-{
- int idx_acl = 2;
- struct access_list *access;
- struct access_master *master;
-
- /* Looking up access_list. */
- access = access_list_lookup(AFI_IP, argv[idx_acl]->arg);
- if (access == NULL) {
- vty_out(vty, "%% access-list %s doesn't exist\n",
- argv[idx_acl]->arg);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- master = access->master;
-
- route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED);
- /* Run hook function. */
- if (master->delete_hook)
- (*master->delete_hook)(access);
-
- /* Delete all filter from access-list. */
- access_list_delete(access);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (access_list_remark,
- access_list_remark_cmd,
- "access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark LINE...",
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP extended access list\n"
- "IP standard access list (expanded range)\n"
- "IP extended access list (expanded range)\n"
- "IP zebra access-list\n"
- "Access list entry comment\n"
- "Comment up to 100 characters\n")
-{
- int idx_acl = 1;
- int idx_remark = 3;
- struct access_list *access;
-
- access = access_list_get(AFI_IP, argv[idx_acl]->arg);
-
- if (access->remark) {
- XFREE(MTYPE_TMP, access->remark);
- access->remark = NULL;
- }
- access->remark = argv_concat(argv, argc, idx_remark);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_access_list_remark,
- no_access_list_remark_cmd,
- "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark",
- NO_STR
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP extended access list\n"
- "IP standard access list (expanded range)\n"
- "IP extended access list (expanded range)\n"
- "IP zebra access-list\n"
- "Access list entry comment\n")
-{
- int idx_acl = 2;
- return vty_access_list_remark_unset(vty, AFI_IP, argv[idx_acl]->arg);
-}
-
-/* ALIAS_FIXME */
-DEFUN (no_access_list_remark_comment,
- no_access_list_remark_comment_cmd,
- "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark LINE...",
- NO_STR
- "Add an access list entry\n"
- "IP standard access list\n"
- "IP extended access list\n"
- "IP standard access list (expanded range)\n"
- "IP extended access list (expanded range)\n"
- "IP zebra access-list\n"
- "Access list entry comment\n"
- "Comment up to 100 characters\n")
-{
- return no_access_list_remark(self, vty, argc, argv);
-}
-
-DEFUN (ipv6_access_list_exact,
- ipv6_access_list_exact_cmd,
- "ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> X:X::X:X/M [exact-match]",
- IPV6_STR
- "Add an access list entry\n"
- "IPv6 zebra access-list\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "IPv6 prefix\n"
- "Exact match of the prefixes\n")
-{
- int idx = 0;
- int exact = 0;
- int idx_word = 2;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *prefix = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "X:X::X:X/M", &idx);
- if (idx)
- prefix = argv[idx]->arg;
-
- idx = 0;
- if (argv_find(argv, argc, "exact-match", &idx))
- exact = 1;
-
- assert(prefix);
- return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny,
- AFI_IP6, prefix, exact, 1);
-}
-
-DEFUN (ipv6_access_list_any,
- ipv6_access_list_any_cmd,
- "ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> any",
- IPV6_STR
- "Add an access list entry\n"
- "IPv6 zebra access-list\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any prefixi to match\n")
-{
- int idx_word = 2;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny,
- AFI_IP6, "::/0", 0, 1);
-}
-
-DEFUN (no_ipv6_access_list_exact,
- no_ipv6_access_list_exact_cmd,
- "no ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> X:X::X:X/M [exact-match]",
- NO_STR
- IPV6_STR
- "Add an access list entry\n"
- "IPv6 zebra access-list\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Prefix to match. e.g. 3ffe:506::/32\n"
- "Exact match of the prefixes\n")
-{
- int idx = 0;
- int exact = 0;
- int idx_word = 3;
- char *seq = NULL;
- char *permit_deny = NULL;
- char *prefix = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "X:X::X:X/M", &idx);
- if (idx)
- prefix = argv[idx]->arg;
- assert(prefix);
-
- idx = 0;
- if (argv_find(argv, argc, "exact-match", &idx))
- exact = 1;
-
- return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny,
- AFI_IP6, prefix, exact, 0);
-}
-
-DEFUN (no_ipv6_access_list_any,
- no_ipv6_access_list_any_cmd,
- "no ipv6 access-list WORD [seq (1-4294967295)] <deny|permit> any",
- NO_STR
- IPV6_STR
- "Add an access list entry\n"
- "IPv6 zebra access-list\n"
- "Sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any prefixi to match\n")
-{
- int idx_word = 3;
- int idx = 0;
- char *seq = NULL;
- char *permit_deny = NULL;
-
- argv_find(argv, argc, "(1-4294967295)", &idx);
- if (idx)
- seq = argv[idx]->arg;
-
- idx = 0;
- argv_find(argv, argc, "permit", &idx);
- argv_find(argv, argc, "deny", &idx);
- if (idx)
- permit_deny = argv[idx]->arg;
-
- return filter_set_zebra(vty, argv[idx_word]->arg, seq, permit_deny,
- AFI_IP6, "::/0", 0, 0);
-}
-
-
-DEFUN (no_ipv6_access_list_all,
- no_ipv6_access_list_all_cmd,
- "no ipv6 access-list WORD",
- NO_STR
- IPV6_STR
- "Add an access list entry\n"
- "IPv6 zebra access-list\n")
-{
- int idx_word = 3;
- struct access_list *access;
- struct access_master *master;
-
- /* Looking up access_list. */
- access = access_list_lookup(AFI_IP6, argv[idx_word]->arg);
- if (access == NULL) {
- vty_out(vty, "%% access-list %s doesn't exist\n",
- argv[idx_word]->arg);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- master = access->master;
-
- route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED);
- /* Run hook function. */
- if (master->delete_hook)
- (*master->delete_hook)(access);
-
- /* Delete all filter from access-list. */
- access_list_delete(access);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (ipv6_access_list_remark,
- ipv6_access_list_remark_cmd,
- "ipv6 access-list WORD remark LINE...",
- IPV6_STR
- "Add an access list entry\n"
- "IPv6 zebra access-list\n"
- "Access list entry comment\n"
- "Comment up to 100 characters\n")
-{
- int idx_word = 2;
- int idx_line = 4;
- struct access_list *access;
-
- access = access_list_get(AFI_IP6, argv[idx_word]->arg);
-
- if (access->remark) {
- XFREE(MTYPE_TMP, access->remark);
- access->remark = NULL;
- }
- access->remark = argv_concat(argv, argc, idx_line);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_ipv6_access_list_remark,
- no_ipv6_access_list_remark_cmd,
- "no ipv6 access-list WORD remark",
- NO_STR
- IPV6_STR
- "Add an access list entry\n"
- "IPv6 zebra access-list\n"
- "Access list entry comment\n")
-{
- int idx_word = 3;
- return vty_access_list_remark_unset(vty, AFI_IP6, argv[idx_word]->arg);
-}
-
-/* ALIAS_FIXME */
-DEFUN (no_ipv6_access_list_remark_comment,
- no_ipv6_access_list_remark_comment_cmd,
- "no ipv6 access-list WORD remark LINE...",
- NO_STR
- IPV6_STR
- "Add an access list entry\n"
- "IPv6 zebra access-list\n"
- "Access list entry comment\n"
- "Comment up to 100 characters\n")
-{
- return no_ipv6_access_list_remark(self, vty, argc, argv);
-}
-
static void config_write_access_zebra(struct vty *, struct filter *);
static void config_write_access_cisco(struct vty *, struct filter *);
@@ -2747,86 +776,12 @@ static void config_write_access_zebra(struct vty *vty, struct filter *mfilter)
vty_out(vty, "\n");
}
-static int config_write_access(struct vty *vty, afi_t afi)
-{
- struct access_list *access;
- struct access_master *master;
- struct filter *mfilter;
- int write = 0;
-
- master = access_master_get(afi);
- if (master == NULL)
- return 0;
-
- for (access = master->num.head; access; access = access->next) {
- if (access->remark) {
- vty_out(vty, "%saccess-list %s remark %s\n",
- (afi == AFI_IP) ? ("")
- : ((afi == AFI_IP6) ? ("ipv6 ")
- : ("mac ")),
- access->name, access->remark);
- write++;
- }
-
- for (mfilter = access->head; mfilter; mfilter = mfilter->next) {
- vty_out(vty, "%saccess-list %s seq %" PRId64 " %s",
- (afi == AFI_IP) ? ("")
- : ((afi == AFI_IP6) ? ("ipv6 ")
- : ("mac ")),
- access->name, mfilter->seq,
- filter_type_str(mfilter));
-
- if (mfilter->cisco)
- config_write_access_cisco(vty, mfilter);
- else
- config_write_access_zebra(vty, mfilter);
-
- write++;
- }
- }
-
- for (access = master->str.head; access; access = access->next) {
- if (access->remark) {
- vty_out(vty, "%saccess-list %s remark %s\n",
- (afi == AFI_IP) ? ("")
- : ((afi == AFI_IP6) ? ("ipv6 ")
- : ("mac ")),
- access->name, access->remark);
- write++;
- }
-
- for (mfilter = access->head; mfilter; mfilter = mfilter->next) {
- vty_out(vty, "%saccess-list %s seq %" PRId64 " %s",
- (afi == AFI_IP) ? ("")
- : ((afi == AFI_IP6) ? ("ipv6 ")
- : ("mac ")),
- access->name, mfilter->seq,
- filter_type_str(mfilter));
-
- if (mfilter->cisco)
- config_write_access_cisco(vty, mfilter);
- else
- config_write_access_zebra(vty, mfilter);
-
- write++;
- }
- }
- return write;
-}
-
-static int config_write_access_mac(struct vty *vty);
static struct cmd_node access_mac_node = {
.name = "MAC access list",
.node = ACCESS_MAC_NODE,
.prompt = "",
- .config_write = config_write_access_mac,
};
-static int config_write_access_mac(struct vty *vty)
-{
- return config_write_access(vty, AFI_L2VPN);
-}
-
static void access_list_reset_mac(void)
{
struct access_list *access;
@@ -2860,26 +815,29 @@ static void access_list_init_mac(void)
install_element(ENABLE_NODE, &show_mac_access_list_cmd);
install_element(ENABLE_NODE, &show_mac_access_list_name_cmd);
-
- /* Zebra access-list */
- install_element(CONFIG_NODE, &mac_access_list_cmd);
- install_element(CONFIG_NODE, &no_mac_access_list_cmd);
- install_element(CONFIG_NODE, &mac_access_list_any_cmd);
- install_element(CONFIG_NODE, &no_mac_access_list_any_cmd);
}
/* Access-list node. */
-static int config_write_access_ipv4(struct vty *vty);
+static int config_write_access(struct vty *vty);
static struct cmd_node access_node = {
.name = "ipv4 access list",
.node = ACCESS_NODE,
.prompt = "",
- .config_write = config_write_access_ipv4,
+ .config_write = config_write_access,
};
-static int config_write_access_ipv4(struct vty *vty)
+static int config_write_access(struct vty *vty)
{
- return config_write_access(vty, AFI_IP);
+ struct lyd_node *dnode;
+ int written = 0;
+
+ dnode = yang_dnode_get(running_config->dnode, "/frr-filter:lib");
+ if (dnode) {
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+ written = 1;
+ }
+
+ return written;
}
static void access_list_reset_ipv4(void)
@@ -2915,62 +873,14 @@ static void access_list_init_ipv4(void)
install_element(ENABLE_NODE, &show_ip_access_list_cmd);
install_element(ENABLE_NODE, &show_ip_access_list_name_cmd);
-
- /* Zebra access-list */
- install_element(CONFIG_NODE, &access_list_exact_cmd);
- install_element(CONFIG_NODE, &access_list_any_cmd);
- install_element(CONFIG_NODE, &no_access_list_exact_cmd);
- install_element(CONFIG_NODE, &no_access_list_any_cmd);
-
- /* Standard access-list */
- install_element(CONFIG_NODE, &access_list_standard_cmd);
- install_element(CONFIG_NODE, &access_list_standard_nomask_cmd);
- install_element(CONFIG_NODE, &access_list_standard_host_cmd);
- install_element(CONFIG_NODE, &access_list_standard_any_cmd);
- install_element(CONFIG_NODE, &no_access_list_standard_cmd);
- install_element(CONFIG_NODE, &no_access_list_standard_nomask_cmd);
- install_element(CONFIG_NODE, &no_access_list_standard_host_cmd);
- install_element(CONFIG_NODE, &no_access_list_standard_any_cmd);
-
- /* Extended access-list */
- install_element(CONFIG_NODE, &access_list_extended_cmd);
- install_element(CONFIG_NODE, &access_list_extended_any_mask_cmd);
- install_element(CONFIG_NODE, &access_list_extended_mask_any_cmd);
- install_element(CONFIG_NODE, &access_list_extended_any_any_cmd);
- install_element(CONFIG_NODE, &access_list_extended_host_mask_cmd);
- install_element(CONFIG_NODE, &access_list_extended_mask_host_cmd);
- install_element(CONFIG_NODE, &access_list_extended_host_host_cmd);
- install_element(CONFIG_NODE, &access_list_extended_any_host_cmd);
- install_element(CONFIG_NODE, &access_list_extended_host_any_cmd);
- install_element(CONFIG_NODE, &no_access_list_extended_cmd);
- install_element(CONFIG_NODE, &no_access_list_extended_any_mask_cmd);
- install_element(CONFIG_NODE, &no_access_list_extended_mask_any_cmd);
- install_element(CONFIG_NODE, &no_access_list_extended_any_any_cmd);
- install_element(CONFIG_NODE, &no_access_list_extended_host_mask_cmd);
- install_element(CONFIG_NODE, &no_access_list_extended_mask_host_cmd);
- install_element(CONFIG_NODE, &no_access_list_extended_host_host_cmd);
- install_element(CONFIG_NODE, &no_access_list_extended_any_host_cmd);
- install_element(CONFIG_NODE, &no_access_list_extended_host_any_cmd);
-
- install_element(CONFIG_NODE, &access_list_remark_cmd);
- install_element(CONFIG_NODE, &no_access_list_all_cmd);
- install_element(CONFIG_NODE, &no_access_list_remark_cmd);
- install_element(CONFIG_NODE, &no_access_list_remark_comment_cmd);
}
-static int config_write_access_ipv6(struct vty *vty);
static struct cmd_node access_ipv6_node = {
.name = "ipv6 access list",
.node = ACCESS_IPV6_NODE,
.prompt = "",
- .config_write = config_write_access_ipv6,
};
-static int config_write_access_ipv6(struct vty *vty)
-{
- return config_write_access(vty, AFI_IP6);
-}
-
static void access_list_reset_ipv6(void)
{
struct access_list *access;
@@ -3003,16 +913,6 @@ static void access_list_init_ipv6(void)
install_element(ENABLE_NODE, &show_ipv6_access_list_cmd);
install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd);
-
- install_element(CONFIG_NODE, &ipv6_access_list_exact_cmd);
- install_element(CONFIG_NODE, &ipv6_access_list_any_cmd);
- install_element(CONFIG_NODE, &no_ipv6_access_list_exact_cmd);
- install_element(CONFIG_NODE, &no_ipv6_access_list_any_cmd);
-
- install_element(CONFIG_NODE, &no_ipv6_access_list_all_cmd);
- install_element(CONFIG_NODE, &ipv6_access_list_remark_cmd);
- install_element(CONFIG_NODE, &no_ipv6_access_list_remark_cmd);
- install_element(CONFIG_NODE, &no_ipv6_access_list_remark_comment_cmd);
}
void access_list_init(void)
@@ -3020,6 +920,8 @@ void access_list_init(void)
access_list_init_ipv4();
access_list_init_ipv6();
access_list_init_mac();
+
+ filter_cli_init();
}
void access_list_reset(void)
diff --git a/lib/filter.h b/lib/filter.h
index 3dd2eaaf15..76e992bf8e 100644
--- a/lib/filter.h
+++ b/lib/filter.h
@@ -23,6 +23,7 @@
#define _ZEBRA_FILTER_H
#include "if.h"
+#include "prefix.h"
#ifdef __cplusplus
extern "C" {
@@ -41,6 +42,50 @@ enum filter_type { FILTER_DENY, FILTER_PERMIT, FILTER_DYNAMIC };
enum access_type { ACCESS_TYPE_STRING, ACCESS_TYPE_NUMBER };
+struct filter_cisco {
+ /* Cisco access-list */
+ int extended;
+ struct in_addr addr;
+ struct in_addr addr_mask;
+ struct in_addr mask;
+ struct in_addr mask_mask;
+};
+
+struct filter_zebra {
+ /* If this filter is "exact" match then this flag is set. */
+ int exact;
+
+ /* Prefix information. */
+ struct prefix prefix;
+};
+
+/* Forward declaration of access-list struct. */
+struct access_list;
+
+/* Filter element of access list */
+struct filter {
+ /* For doubly linked list. */
+ struct filter *next;
+ struct filter *prev;
+
+ /* Parent access-list pointer. */
+ struct access_list *acl;
+
+ /* Filter type information. */
+ enum filter_type type;
+
+ /* Sequence number */
+ int64_t seq;
+
+ /* Cisco access-list */
+ int cisco;
+
+ union {
+ struct filter_cisco cfilter;
+ struct filter_zebra zfilter;
+ } u;
+};
+
/* Access list */
struct access_list {
char *name;
@@ -57,6 +102,28 @@ struct access_list {
struct filter *tail;
};
+/* List of access_list. */
+struct access_list_list {
+ struct access_list *head;
+ struct access_list *tail;
+};
+
+/* Master structure of access_list. */
+struct access_master {
+ /* List of access_list which name is number. */
+ struct access_list_list num;
+
+ /* List of access_list which name is string. */
+ struct access_list_list str;
+
+ /* Hook function which is executed when new access_list is added. */
+ void (*add_hook)(struct access_list *);
+
+ /* Hook function which is executed when access_list is deleted. */
+ void (*delete_hook)(struct access_list *);
+};
+
+
/* Prototypes for access-list. */
extern void access_list_init(void);
extern void access_list_reset(void);
@@ -66,6 +133,59 @@ extern struct access_list *access_list_lookup(afi_t, const char *);
extern enum filter_type access_list_apply(struct access_list *access,
const void *object);
+struct access_list *access_list_get(afi_t afi, const char *name);
+void access_list_delete(struct access_list *access);
+struct filter *filter_new(void);
+void access_list_filter_add(struct access_list *access,
+ struct filter *filter);
+void access_list_filter_delete(struct access_list *access,
+ struct filter *filter);
+int64_t filter_new_seq_get(struct access_list *access);
+struct filter *filter_lookup_cisco(struct access_list *access,
+ struct filter *mnew);
+struct filter *filter_lookup_zebra(struct access_list *access,
+ struct filter *mnew);
+
+extern const struct frr_yang_module_info frr_filter_info;
+
+
+/* filter_nb.c */
+enum yang_access_list_type {
+ YALT_IPV4 = 0,
+ YALT_IPV6 = 1,
+ YALT_MAC = 2,
+};
+
+enum yang_prefix_list_type {
+ YPLT_IPV4 = 0,
+ YPLT_IPV6 = 1,
+};
+
+enum yang_prefix_list_action {
+ YPLA_DENY = 0,
+ YPLA_PERMIT = 1,
+};
+
+/* filter_cli.c */
+struct lyd_node;
+struct vty;
+
+extern void access_list_legacy_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void access_list_legacy_remark_show(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void access_list_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void access_list_remark_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void prefix_list_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+
+void filter_cli_init(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/filter_cli.c b/lib/filter_cli.c
new file mode 100644
index 0000000000..0091d8b03f
--- /dev/null
+++ b/lib/filter_cli.c
@@ -0,0 +1,1693 @@
+/*
+ * FRR filter CLI implementation.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include "northbound.h"
+#include "prefix.h"
+#include "zebra.h"
+
+#include "lib/command.h"
+#include "lib/filter.h"
+#include "lib/northbound_cli.h"
+#include "lib/plist.h"
+#include "lib/plist_int.h"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "lib/filter_cli_clippy.c"
+#endif /* VTYSH_EXTRACT_PL */
+
+#define ACCESS_LIST_STR "Access list entry\n"
+#define ACCESS_LIST_LEG_STR "IP standard access list\n"
+#define ACCESS_LIST_LEG_EXT_STR "IP standard access list (expanded range)\n"
+#define ACCESS_LIST_ELEG_STR "IP extended access list\n"
+#define ACCESS_LIST_ELEG_EXT_STR "IP extended access list (expanded range)\n"
+#define ACCESS_LIST_XLEG_STR \
+ ACCESS_LIST_LEG_STR \
+ ACCESS_LIST_LEG_EXT_STR \
+ ACCESS_LIST_ELEG_STR \
+ ACCESS_LIST_ELEG_EXT_STR
+#define ACCESS_LIST_ZEBRA_STR "Access list entry\n"
+#define ACCESS_LIST_SEQ_STR \
+ "Sequence number of an entry\n" \
+ "Sequence number\n"
+#define ACCESS_LIST_ACTION_STR \
+ "Specify packets to reject\n" \
+ "Specify packets to forward\n"
+#define ACCESS_LIST_REMARK_STR "Access list entry comment\n"
+#define ACCESS_LIST_REMARK_LINE_STR "Comment up to 100 characters\n"
+
+#define PREFIX_LIST_NAME_STR "Prefix list entry name\n"
+
+/*
+ * Helper function to locate filter data structures for Cisco-style ACLs.
+ */
+static int64_t acl_cisco_get_seq(struct access_list *acl, const char *action,
+ const char *src, const char *src_mask,
+ const char *dst, const char *dst_mask)
+{
+ struct filter_cisco *fc;
+ struct filter f, *fn;
+
+ memset(&f, 0, sizeof(f));
+ memset(&fc, 0, sizeof(fc));
+ f.cisco = 1;
+ if (strcmp(action, "permit") == 0)
+ f.type = FILTER_PERMIT;
+ else
+ f.type = FILTER_DENY;
+
+ fc = &f.u.cfilter;
+ inet_pton(AF_INET, src, &fc->addr);
+ inet_pton(AF_INET, src_mask, &fc->addr_mask);
+ fc->addr.s_addr &= ~fc->addr_mask.s_addr;
+ if (dst != NULL) {
+ fc->extended = 1;
+ inet_pton(AF_INET, dst, &fc->mask);
+ inet_pton(AF_INET, dst_mask, &fc->mask_mask);
+ fc->mask.s_addr &= ~fc->mask_mask.s_addr;
+ }
+
+ fn = filter_lookup_cisco(acl, &f);
+ if (fn == NULL)
+ return -1;
+
+ return fn->seq;
+}
+
+/*
+ * Helper function to locate filter data structures for zebra-style ACLs.
+ */
+static int64_t acl_zebra_get_seq(struct access_list *acl, const char *action,
+ const struct prefix *p, bool exact)
+{
+ struct filter_zebra *fz;
+ struct filter f, *fn;
+
+ memset(&f, 0, sizeof(f));
+ memset(&fz, 0, sizeof(fz));
+ if (strcmp(action, "permit") == 0)
+ f.type = FILTER_PERMIT;
+ else
+ f.type = FILTER_DENY;
+
+ fz = &f.u.zfilter;
+ fz->prefix = *p;
+ fz->exact = exact;
+
+ fn = filter_lookup_zebra(acl, &f);
+ if (fn == NULL)
+ return -1;
+
+ return fn->seq;
+}
+
+/*
+ * Helper function to concatenate address with mask in Cisco style.
+ */
+static void concat_addr_mask_v4(const char *addr, const char *mask, char *dst,
+ size_t dstlen)
+{
+ struct in_addr ia;
+ int plen;
+
+ assert(inet_pton(AF_INET, mask, &ia) == 1);
+ plen = ip_masklen(ia);
+ snprintf(dst, dstlen, "%s/%d", addr, plen);
+}
+
+/*
+ * Helper function to generate a sequence number for legacy commands.
+ */
+static int acl_get_seq_cb(const struct lyd_node *dnode, void *arg)
+{
+ int64_t *seq = arg;
+ int64_t cur_seq = yang_dnode_get_uint32(dnode, "sequence");
+
+ if (cur_seq > *seq)
+ *seq = cur_seq;
+
+ return YANG_ITER_CONTINUE;
+}
+
+/**
+ * Helper function that iterates over the XPath `xpath` on the candidate
+ * configuration in `vty->candidate_config`.
+ *
+ * \param[in] vty shell context with the candidate configuration.
+ * \param[in] xpath the XPath to look for the sequence leaf.
+ * \returns next unused sequence number.
+ */
+static long acl_get_seq(struct vty *vty, const char *xpath)
+{
+ int64_t seq = 0;
+
+ yang_dnode_iterate(acl_get_seq_cb, &seq, vty->candidate_config->dnode,
+ "%s/entry", xpath);
+
+ return seq + 5;
+}
+
+/*
+ * Cisco (legacy) access lists.
+ */
+DEFPY(
+ access_list_std, access_list_std_cmd,
+ "access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>",
+ ACCESS_LIST_STR
+ ACCESS_LIST_LEG_STR
+ ACCESS_LIST_LEG_EXT_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "A single host address\n"
+ "Address to match\n"
+ "Address to match\n"
+ "Wildcard bits\n"
+ "Any source host\n")
+{
+ int64_t sseq;
+ char ipmask[64];
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 128];
+
+ /*
+ * Create the access-list first, so we can generate sequence if
+ * none given (backward compatibility).
+ */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ if (seq_str == NULL) {
+ /* Use XPath to find the next sequence number. */
+ sseq = acl_get_seq(vty, xpath);
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ } else
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%s']", xpath, seq_str);
+
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
+
+ nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
+ if (host_str != NULL && mask_str == NULL) {
+ nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, host_str);
+ } else if (host_str != NULL && mask_str != NULL) {
+ concat_addr_mask_v4(host_str, mask_str, ipmask, sizeof(ipmask));
+ nb_cli_enqueue_change(vty, "./network", NB_OP_MODIFY, ipmask);
+ } else {
+ nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
+ }
+
+ return nb_cli_apply_changes(vty, xpath_entry);
+}
+
+DEFPY(
+ no_access_list_std, no_access_list_std_cmd,
+ "no access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>",
+ NO_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_LEG_STR
+ ACCESS_LIST_LEG_EXT_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "A single host address\n"
+ "Address to match\n"
+ "Address to match\n"
+ "Wildcard bits\n"
+ "Any source host\n")
+{
+ struct access_list *acl;
+ struct lyd_node *dnode;
+ int64_t sseq;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 32];
+
+ /* If the user provided sequence number, then just go for it. */
+ if (seq_str != NULL) {
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list-legacy[number='%s']/entry[sequence='%s']",
+ number_str, seq_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+ }
+
+ /* Otherwise, to keep compatibility, we need to figure it out. */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+
+ /* Access-list must exist before entries. */
+ if (yang_dnode_exists(running_config->dnode, xpath) == false)
+ return CMD_WARNING;
+
+ /* Use access-list data structure to fetch sequence. */
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ acl = nb_running_get_entry(dnode, NULL, true);
+ if (host_str != NULL)
+ sseq = acl_cisco_get_seq(acl, action, host_str,
+ mask_str ? mask_str : "0.0.0.0", NULL,
+ NULL);
+ else
+ sseq = acl_cisco_get_seq(acl, action, "0.0.0.0",
+ "255.255.255.255", NULL, NULL);
+ if (sseq == -1)
+ return CMD_WARNING;
+
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ access_list_ext, access_list_ext_cmd,
+ "access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
+ ACCESS_LIST_STR
+ ACCESS_LIST_ELEG_STR
+ ACCESS_LIST_ELEG_EXT_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "IPv4 address\n"
+ "Source address to match\n"
+ "Source address mask to apply\n"
+ "Single source host\n"
+ "Source address to match\n"
+ "Any source host\n"
+ "Destination address to match\n"
+ "Destination address mask to apply\n"
+ "Single destination host\n"
+ "Destination address to match\n"
+ "Any destination host\n")
+{
+ int64_t sseq;
+ char ipmask[64];
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 128];
+
+ /*
+ * Create the access-list first, so we can generate sequence if
+ * none given (backward compatibility).
+ */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ if (seq_str == NULL) {
+ /* Use XPath to find the next sequence number. */
+ sseq = acl_get_seq(vty, xpath);
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ } else
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%s']", xpath, seq_str);
+
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
+
+ nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
+ if (src_str != NULL && src_mask_str == NULL) {
+ nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, src_str);
+ } else if (src_str != NULL && src_mask_str != NULL) {
+ concat_addr_mask_v4(src_str, src_mask_str, ipmask,
+ sizeof(ipmask));
+ nb_cli_enqueue_change(vty, "./network", NB_OP_MODIFY, ipmask);
+ } else {
+ nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
+ }
+
+ if (dst_str != NULL && dst_mask_str == NULL) {
+ nb_cli_enqueue_change(vty, "./destination-host", NB_OP_MODIFY,
+ src_str);
+ } else if (dst_str != NULL && dst_mask_str != NULL) {
+ concat_addr_mask_v4(dst_str, dst_mask_str, ipmask,
+ sizeof(ipmask));
+ nb_cli_enqueue_change(vty, "./destination-network",
+ NB_OP_MODIFY, ipmask);
+ } else {
+ nb_cli_enqueue_change(vty, "./destination-any", NB_OP_CREATE,
+ NULL);
+ }
+
+ return nb_cli_apply_changes(vty, xpath_entry);
+}
+
+DEFPY(
+ no_access_list_ext, no_access_list_ext_cmd,
+ "no access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
+ NO_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ELEG_STR
+ ACCESS_LIST_ELEG_EXT_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "Any Internet Protocol\n"
+ "Source address to match\n"
+ "Source address mask to apply\n"
+ "Single source host\n"
+ "Source address to match\n"
+ "Any source host\n"
+ "Destination address to match\n"
+ "Destination address mask to apply\n"
+ "Single destination host\n"
+ "Destination address to match\n"
+ "Any destination host\n")
+{
+ struct access_list *acl;
+ struct lyd_node *dnode;
+ int64_t sseq;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 32];
+
+ /* If the user provided sequence number, then just go for it. */
+ if (seq_str != NULL) {
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list-legacy[number='%s']/entry[sequence='%s']",
+ number_str, seq_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+ }
+
+ /* Otherwise, to keep compatibility, we need to figure it out. */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+
+ /* Access-list must exist before entries. */
+ if (yang_dnode_exists(running_config->dnode, xpath) == false)
+ return CMD_WARNING;
+
+ /* Use access-list data structure to fetch sequence. */
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ acl = nb_running_get_entry(dnode, NULL, true);
+ if (src_str != NULL) {
+ if (dst_str != NULL)
+ sseq = acl_cisco_get_seq(
+ acl, action, src_str,
+ src_mask_str ? src_mask_str : "0.0.0.0",
+ dst_str,
+ dst_mask_str ? dst_mask_str : "0.0.0.0");
+ else
+ sseq = acl_cisco_get_seq(acl, action, src_str,
+ src_mask_str ? src_mask_str
+ : "0.0.0.0",
+ "0.0.0.0", "255.255.255.255");
+ } else {
+ if (dst_str != NULL)
+ sseq = acl_cisco_get_seq(acl, action, "0.0.0.0",
+ "255.255.255.255", dst_str,
+ dst_mask_str ? dst_mask_str
+ : "0.0.0.0");
+ else
+ sseq = acl_cisco_get_seq(acl, action, "0.0.0.0",
+ "255.255.255.255", "0.0.0.0",
+ "255.255.255.255");
+ }
+ if (sseq == -1)
+ return CMD_WARNING;
+
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_access_list_legacy, no_access_list_legacy_cmd,
+ "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number",
+ NO_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_XLEG_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void access_list_legacy_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ uint16_t number = yang_dnode_get_uint16(dnode, "../number");
+ bool extended;
+ struct prefix p;
+ struct in_addr mask;
+
+ vty_out(vty, "access-list %d seq %s %s", number,
+ yang_dnode_get_string(dnode, "./sequence"),
+ yang_dnode_get_string(dnode, "./action"));
+
+ extended = (number >= 100 && number <= 199)
+ || (number >= 2000 && number <= 2699);
+ if (extended)
+ vty_out(vty, " ip");
+
+ if (yang_dnode_exists(dnode, "./network")) {
+ yang_dnode_get_prefix(&p, dnode, "./network");
+ masklen2ip(p.prefixlen, &mask);
+ vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask);
+ } else if (yang_dnode_exists(dnode, "./host")) {
+ if (extended)
+ vty_out(vty, " host");
+
+ vty_out(vty, " %s", yang_dnode_get_string(dnode, "./host"));
+ } else if (yang_dnode_exists(dnode, "./any"))
+ vty_out(vty, " any");
+
+ if (extended) {
+ if (yang_dnode_exists(dnode, "./destination-network")) {
+ yang_dnode_get_prefix(&p, dnode,
+ "./destination-network");
+ masklen2ip(p.prefixlen, &mask);
+ vty_out(vty, " %pI4 %pI4", &p.u.prefix4, &mask);
+ } else if (yang_dnode_exists(dnode, "./destination-host"))
+ vty_out(vty, " host %s",
+ yang_dnode_get_string(dnode,
+ "./destination-host"));
+ else if (yang_dnode_exists(dnode, "./destination-any"))
+ vty_out(vty, " any");
+ }
+
+ vty_out(vty, "\n");
+}
+
+DEFPY(
+ access_list_legacy_remark, access_list_legacy_remark_cmd,
+ "access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number remark LINE...",
+ ACCESS_LIST_STR
+ ACCESS_LIST_XLEG_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+{
+ int rv;
+ char *remark;
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list-legacy[number='%s']", number_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ remark = argv_concat(argv, argc, 3);
+ nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
+ rv = nb_cli_apply_changes(vty, xpath);
+ XFREE(MTYPE_TMP, remark);
+
+ return rv;
+}
+
+DEFPY(
+ no_access_list_legacy_remark, no_access_list_legacy_remark_cmd,
+ "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number remark",
+ NO_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_XLEG_STR
+ ACCESS_LIST_REMARK_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list-legacy[number='%s']/remark",
+ number_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS(
+ no_access_list_legacy_remark, no_access_list_legacy_remark_line_cmd,
+ "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)>$number remark LINE...",
+ NO_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_XLEG_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+
+void access_list_legacy_remark_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, "access-list %s remark %s\n",
+ yang_dnode_get_string(dnode, "../number"),
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * Zebra access lists.
+ */
+DEFPY(
+ access_list, access_list_cmd,
+ "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "Prefix to match. e.g. 10.0.0.0/8\n"
+ "Exact match of the prefixes\n"
+ "Match any IPv4\n")
+{
+ int64_t sseq;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 128];
+
+ /*
+ * Create the access-list first, so we can generate sequence if
+ * none given (backward compatibility).
+ */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ if (seq_str == NULL) {
+ /* Use XPath to find the next sequence number. */
+ sseq = acl_get_seq(vty, xpath);
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ } else
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%s']", xpath, seq_str);
+
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
+
+ nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
+ if (prefix_str != NULL) {
+ nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY,
+ prefix_str);
+ nb_cli_enqueue_change(vty, "./ipv4-exact-match", NB_OP_MODIFY,
+ exact ? "true" : "false");
+ } else {
+ nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
+ }
+
+ return nb_cli_apply_changes(vty, xpath_entry);
+}
+
+DEFPY(
+ no_access_list, no_access_list_cmd,
+ "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
+ NO_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "Prefix to match. e.g. 10.0.0.0/8\n"
+ "Exact match of the prefixes\n"
+ "Match any IPv4\n")
+{
+ struct access_list *acl;
+ struct lyd_node *dnode;
+ int64_t sseq;
+ struct prefix pany;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 32];
+
+ /* If the user provided sequence number, then just go for it. */
+ if (seq_str != NULL) {
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']",
+ name, seq_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+ }
+
+ /* Otherwise, to keep compatibility, we need to figure it out. */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
+
+ /* Access-list must exist before entries. */
+ if (yang_dnode_exists(running_config->dnode, xpath) == false)
+ return CMD_WARNING;
+
+ /* Use access-list data structure to fetch sequence. */
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ acl = nb_running_get_entry(dnode, NULL, true);
+ if (prefix == NULL) {
+ memset(&pany, 0, sizeof(pany));
+ pany.family = AF_INET;
+ sseq = acl_zebra_get_seq(acl, action, &pany, exact);
+ } else
+ sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix,
+ exact);
+ if (sseq == -1)
+ return CMD_WARNING;
+
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_access_list_all, no_access_list_all_cmd,
+ "no access-list WORD$name",
+ NO_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ access_list_remark, access_list_remark_cmd,
+ "access-list WORD$name remark LINE...",
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+{
+ int rv;
+ char *remark;
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ remark = argv_concat(argv, argc, 3);
+ nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
+ rv = nb_cli_apply_changes(vty, xpath);
+ XFREE(MTYPE_TMP, remark);
+
+ return rv;
+}
+
+DEFPY(
+ no_access_list_remark, no_access_list_remark_cmd,
+ "no access-list WORD$name remark",
+ NO_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_REMARK_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv4'][name='%s']/remark",
+ name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS(
+ no_access_list_remark, no_access_list_remark_line_cmd,
+ "no access-list WORD$name remark LINE...",
+ NO_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+
+DEFPY(
+ ipv6_access_list, ipv6_access_list_cmd,
+ "ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
+ IPV6_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "IPv6 prefix\n"
+ "Exact match of the prefixes\n"
+ "Match any IPv6\n")
+{
+ int64_t sseq;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 128];
+
+ /*
+ * Create the access-list first, so we can generate sequence if
+ * none given (backward compatibility).
+ */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ if (seq_str == NULL) {
+ /* Use XPath to find the next sequence number. */
+ sseq = acl_get_seq(vty, xpath);
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ } else
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%s']", xpath, seq_str);
+
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
+
+ nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
+ if (prefix_str != NULL) {
+ nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY,
+ prefix_str);
+ nb_cli_enqueue_change(vty, "./ipv6-exact-match", NB_OP_MODIFY,
+ exact ? "true" : "false");
+ } else {
+ nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
+ }
+
+ return nb_cli_apply_changes(vty, xpath_entry);
+}
+
+DEFPY(
+ no_ipv6_access_list, no_ipv6_access_list_cmd,
+ "no ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
+ NO_STR
+ IPV6_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "IPv6 prefix\n"
+ "Exact match of the prefixes\n"
+ "Match any IPv6\n")
+{
+ struct access_list *acl;
+ struct lyd_node *dnode;
+ int64_t sseq;
+ struct prefix pany;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 32];
+
+ /* If the user provided sequence number, then just go for it. */
+ if (seq_str != NULL) {
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv6'][name='%s']/entry[sequence='%s']",
+ name, seq_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+ }
+
+ /* Otherwise, to keep compatibility, we need to figure it out. */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name);
+
+ /* Access-list must exist before entries. */
+ if (yang_dnode_exists(running_config->dnode, xpath) == false)
+ return CMD_WARNING;
+
+ /* Use access-list data structure to fetch sequence. */
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ acl = nb_running_get_entry(dnode, NULL, true);
+ if (prefix == NULL) {
+ memset(&pany, 0, sizeof(pany));
+ pany.family = AF_INET6;
+ sseq = acl_zebra_get_seq(acl, action, &pany, exact);
+ } else
+ sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix,
+ exact);
+ if (sseq == -1)
+ return CMD_WARNING;
+
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_ipv6_access_list_all, no_ipv6_access_list_all_cmd,
+ "no ipv6 access-list WORD$name",
+ NO_STR
+ IPV6_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ ipv6_access_list_remark, ipv6_access_list_remark_cmd,
+ "ipv6 access-list WORD$name remark LINE...",
+ IPV6_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+{
+ int rv;
+ char *remark;
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ remark = argv_concat(argv, argc, 4);
+ nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
+ rv = nb_cli_apply_changes(vty, xpath);
+ XFREE(MTYPE_TMP, remark);
+
+ return rv;
+}
+
+DEFPY(
+ no_ipv6_access_list_remark, no_ipv6_access_list_remark_cmd,
+ "no ipv6 access-list WORD$name remark",
+ NO_STR
+ IPV6_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_REMARK_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='ipv6'][name='%s']/remark",
+ name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS(
+ no_ipv6_access_list_remark, no_ipv6_access_list_remark_line_cmd,
+ "no ipv6 access-list WORD$name remark LINE...",
+ NO_STR
+ IPV6_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+
+DEFPY(
+ mac_access_list, mac_access_list_cmd,
+ "mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>",
+ MAC_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "MAC address\n"
+ "Match any MAC address\n")
+{
+ int64_t sseq;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 128];
+
+ /*
+ * Create the access-list first, so we can generate sequence if
+ * none given (backward compatibility).
+ */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='mac'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ if (seq_str == NULL) {
+ /* Use XPath to find the next sequence number. */
+ sseq = acl_get_seq(vty, xpath);
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ } else
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%s']", xpath, seq_str);
+
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
+
+ nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
+ if (mac_str != NULL) {
+ nb_cli_enqueue_change(vty, "./mac", NB_OP_MODIFY, mac_str);
+ } else {
+ nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
+ }
+
+ return nb_cli_apply_changes(vty, xpath_entry);
+}
+
+DEFPY(
+ no_mac_access_list, no_mac_access_list_cmd,
+ "no mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$prefix|any>",
+ NO_STR
+ MAC_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "MAC address\n"
+ "Match any MAC address\n")
+{
+ struct access_list *acl;
+ struct lyd_node *dnode;
+ int64_t sseq;
+ struct prefix pany;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 32];
+
+ /* If the user provided sequence number, then just go for it. */
+ if (seq_str != NULL) {
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='mac'][name='%s']/entry[sequence='%s']",
+ name, seq_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+ }
+
+ /* Otherwise, to keep compatibility, we need to figure it out. */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='mac'][name='%s']", name);
+
+ /* Access-list must exist before entries. */
+ if (yang_dnode_exists(running_config->dnode, xpath) == false)
+ return CMD_WARNING;
+
+ /* Use access-list data structure to fetch sequence. */
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ acl = nb_running_get_entry(dnode, NULL, true);
+ if (prefix == NULL) {
+ memset(&pany, 0, sizeof(pany));
+ pany.family = AF_ETHERNET;
+ sseq = acl_zebra_get_seq(acl, action, &pany, false);
+ } else
+ sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix,
+ false);
+ if (sseq == -1)
+ return CMD_WARNING;
+
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_mac_access_list_all, no_mac_access_list_all_cmd,
+ "no mac access-list WORD$name",
+ NO_STR
+ MAC_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='mac'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ mac_access_list_remark, mac_access_list_remark_cmd,
+ "mac access-list WORD$name remark LINE...",
+ MAC_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+{
+ int rv;
+ char *remark;
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='mac'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ remark = argv_concat(argv, argc, 4);
+ nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
+ rv = nb_cli_apply_changes(vty, xpath);
+ XFREE(MTYPE_TMP, remark);
+
+ return rv;
+}
+
+DEFPY(
+ no_mac_access_list_remark, no_mac_access_list_remark_cmd,
+ "no mac access-list WORD$name remark",
+ NO_STR
+ MAC_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_REMARK_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/access-list[type='mac'][name='%s']/remark",
+ name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS(
+ no_mac_access_list_remark, no_mac_access_list_remark_line_cmd,
+ "no mac access-list WORD$name remark LINE...",
+ NO_STR
+ MAC_STR
+ ACCESS_LIST_STR
+ ACCESS_LIST_ZEBRA_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+
+void access_list_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ int type = yang_dnode_get_enum(dnode, "../type");
+ struct prefix p;
+ bool is_any;
+ bool is_exact = false;
+ char macstr[PREFIX2STR_BUFFER];
+
+ is_any = yang_dnode_exists(dnode, "./any");
+ switch (type) {
+ case YALT_IPV4:
+ if (is_any)
+ break;
+
+ yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix");
+ is_exact = yang_dnode_get_bool(dnode, "./ipv4-exact-match");
+ break;
+ case YALT_IPV6: /* ipv6 */
+ vty_out(vty, "ipv6 ");
+ if (is_any)
+ break;
+
+ yang_dnode_get_prefix(&p, dnode, "./ipv6-prefix");
+ is_exact = yang_dnode_get_bool(dnode, "./ipv6-exact-match");
+ break;
+ case YALT_MAC: /* mac */
+ vty_out(vty, "mac ");
+ if (is_any)
+ break;
+
+ yang_dnode_get_prefix(&p, dnode, "./mac");
+ break;
+ }
+
+ vty_out(vty, "access-list %s seq %s %s",
+ yang_dnode_get_string(dnode, "../name"),
+ yang_dnode_get_string(dnode, "./sequence"),
+ yang_dnode_get_string(dnode, "./action"));
+
+ if (!is_any) {
+ /* If type is MAC don't show '/mask'. */
+ if (type == 2 /* mac */) {
+ prefix_mac2str(&p.u.prefix_eth, macstr, sizeof(macstr));
+ vty_out(vty, " %s", macstr);
+ } else
+ vty_out(vty, " %pFX", &p);
+ } else
+ vty_out(vty, " any");
+
+ if (is_exact)
+ vty_out(vty, " exact-match");
+
+ vty_out(vty, "\n");
+}
+
+void access_list_remark_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ int type = yang_dnode_get_enum(dnode, "../type");
+
+ switch (type) {
+ case YALT_IPV4:
+ break;
+ case YALT_IPV6:
+ vty_out(vty, "ipv6 ");
+ break;
+ case YALT_MAC:
+ vty_out(vty, "mac ");
+ break;
+ }
+
+ vty_out(vty, "access-list %s remark %s\n",
+ yang_dnode_get_string(dnode, "../name"),
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * Prefix lists.
+ */
+
+/**
+ * Remove main data structure prefix list if there are no more entries or
+ * remark. This fixes compatibility with old CLI and tests.
+ */
+static int plist_remove_if_empty(struct vty *vty, const char *iptype,
+ const char *name)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='%s'][name='%s']/remark",
+ iptype, name);
+ /* List is not empty if there is a remark, check that: */
+ if (yang_dnode_exists(vty->candidate_config->dnode, xpath))
+ return CMD_SUCCESS;
+
+ /* Check if we have any entries: */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype,
+ name);
+ /*
+ * NOTE: if the list is empty it will return the first sequence
+ * number: 5.
+ */
+ if (acl_get_seq(vty, xpath) != 5)
+ return CMD_SUCCESS;
+
+ /* Nobody is using this list, lets remove it. */
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+static int plist_remove(struct vty *vty, const char *iptype, const char *name,
+ const char *seq, const char *action, struct prefix *p,
+ long ge, long le)
+{
+ struct prefix_list_entry *pentry;
+ enum prefix_list_type plt;
+ struct prefix_list *pl;
+ struct lyd_node *dnode;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 32];
+ int rv;
+
+ /* If the user provided sequence number, then just go for it. */
+ if (seq != NULL) {
+ snprintf(
+ xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%s']",
+ iptype, name, seq);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ rv = nb_cli_apply_changes(vty, NULL);
+ if (rv == CMD_SUCCESS)
+ return plist_remove_if_empty(vty, iptype, name);
+
+ return rv;
+ }
+
+ /* Otherwise, to keep compatibility, we need to figure it out. */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype,
+ name);
+
+ /* Access-list must exist before entries. */
+ if (yang_dnode_exists(running_config->dnode, xpath) == false)
+ return CMD_WARNING;
+
+ /* Use access-list data structure to fetch sequence. */
+ assert(action != NULL);
+ if (strcmp(action, "permit") == 0)
+ plt = PREFIX_PERMIT;
+ else
+ plt = PREFIX_DENY;
+
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ pl = nb_running_get_entry(dnode, NULL, true);
+ pentry = prefix_list_entry_lookup(pl, p, plt, -1, le, ge);
+ if (pentry == NULL)
+ return CMD_WARNING;
+
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, pentry->seq);
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL);
+
+ rv = nb_cli_apply_changes(vty, NULL);
+ if (rv == CMD_SUCCESS)
+ return plist_remove_if_empty(vty, iptype, name);
+
+ return rv;
+}
+
+DEFPY(
+ ip_prefix_list, ip_prefix_list_cmd,
+ "ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)$ge|le (0-32)$le}]>",
+ IP_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n"
+ "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+ "Minimum prefix length to be matched\n"
+ "Minimum prefix length\n"
+ "Maximum prefix length to be matched\n"
+ "Maximum prefix length\n")
+{
+ int64_t sseq;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 128];
+
+ /*
+ * Create the prefix-list first, so we can generate sequence if
+ * none given (backward compatibility).
+ */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ if (seq_str == NULL) {
+ /* Use XPath to find the next sequence number. */
+ sseq = acl_get_seq(vty, xpath);
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ } else
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%s']", xpath, seq_str);
+
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
+
+ nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
+ if (prefix_str != NULL) {
+ nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY,
+ prefix_str);
+
+ if (ge_str)
+ nb_cli_enqueue_change(
+ vty, "./ipv4-prefix-length-greater-or-equal",
+ NB_OP_MODIFY, ge_str);
+ if (le_str)
+ nb_cli_enqueue_change(
+ vty, "./ipv4-prefix-length-lesser-or-equal",
+ NB_OP_MODIFY, le_str);
+ } else {
+ nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
+ }
+
+ return nb_cli_apply_changes(vty, xpath_entry);
+}
+
+DEFPY(
+ no_ip_prefix_list, no_ip_prefix_list_cmd,
+ "no ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)|le (0-32)}]>",
+ NO_STR
+ IP_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n"
+ "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+ "Minimum prefix length to be matched\n"
+ "Minimum prefix length\n"
+ "Maximum prefix length to be matched\n"
+ "Maximum prefix length\n")
+{
+ return plist_remove(vty, "ipv4", name, seq_str, action,
+ (struct prefix *)prefix, ge, le);
+}
+
+DEFPY(
+ no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd,
+ "no ip prefix-list WORD$name seq (1-4294967295)$seq",
+ NO_STR
+ IP_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_SEQ_STR)
+{
+ return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0);
+}
+
+DEFPY(
+ no_ip_prefix_list_all, no_ip_prefix_list_all_cmd,
+ "no ip prefix-list WORD$name",
+ NO_STR
+ IP_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ ip_prefix_list_remark, ip_prefix_list_remark_cmd,
+ "ip prefix-list WORD$name description LINE...",
+ IP_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+{
+ int rv;
+ char *remark;
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ remark = argv_concat(argv, argc, 4);
+ nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
+ rv = nb_cli_apply_changes(vty, xpath);
+ XFREE(MTYPE_TMP, remark);
+
+ return rv;
+}
+
+DEFPY(
+ no_ip_prefix_list_remark, no_ip_prefix_list_remark_cmd,
+ "no ip prefix-list WORD$name description",
+ NO_STR
+ IP_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_REMARK_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']/remark",
+ name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS(
+ no_ip_prefix_list_remark, no_ip_prefix_list_remark_line_cmd,
+ "no ip prefix-list WORD$name description LINE...",
+ NO_STR
+ IP_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+
+DEFPY(
+ ipv6_prefix_list, ipv6_prefix_list_cmd,
+ "ipv6 prefix-list WORD$name [seq (1-4294967295)] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
+ IPV6_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "Any prefix match. Same as \"::0/0 le 128\"\n"
+ "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+ "Maximum prefix length to be matched\n"
+ "Maximum prefix length\n"
+ "Minimum prefix length to be matched\n"
+ "Minimum prefix length\n")
+{
+ int64_t sseq;
+ char xpath[XPATH_MAXLEN];
+ char xpath_entry[XPATH_MAXLEN + 128];
+
+ /*
+ * Create the prefix-list first, so we can generate sequence if
+ * none given (backward compatibility).
+ */
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ if (seq_str == NULL) {
+ /* Use XPath to find the next sequence number. */
+ sseq = acl_get_seq(vty, xpath);
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%" PRId64 "']", xpath, sseq);
+ } else
+ snprintf(xpath_entry, sizeof(xpath_entry),
+ "%s/entry[sequence='%s']", xpath, seq_str);
+
+ nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL);
+
+ nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action);
+ if (prefix_str != NULL) {
+ nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY,
+ prefix_str);
+
+ if (ge_str)
+ nb_cli_enqueue_change(
+ vty, "./ipv6-prefix-length-greater-or-equal",
+ NB_OP_MODIFY, ge_str);
+ if (le_str)
+ nb_cli_enqueue_change(
+ vty, "./ipv6-prefix-length-lesser-or-equal",
+ NB_OP_MODIFY, le_str);
+ } else {
+ nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL);
+ }
+
+ return nb_cli_apply_changes(vty, xpath_entry);
+}
+
+DEFPY(
+ no_ipv6_prefix_list, no_ipv6_prefix_list_cmd,
+ "no ipv6 prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
+ NO_STR
+ IPV6_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_SEQ_STR
+ ACCESS_LIST_ACTION_STR
+ "Any prefix match. Same as \"::0/0 le 128\"\n"
+ "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+ "Maximum prefix length to be matched\n"
+ "Maximum prefix length\n"
+ "Minimum prefix length to be matched\n"
+ "Minimum prefix length\n")
+{
+ return plist_remove(vty, "ipv6", name, seq_str, action,
+ (struct prefix *)prefix, ge, le);
+}
+
+DEFPY(
+ no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd,
+ "no ipv6 prefix-list WORD$name seq (1-4294967295)$seq",
+ NO_STR
+ IPV6_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_SEQ_STR)
+{
+ return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0);
+}
+
+DEFPY(
+ no_ipv6_prefix_list_all, no_ipv6_prefix_list_all_cmd,
+ "no ipv6 prefix-list WORD$name",
+ NO_STR
+ IPV6_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ ipv6_prefix_list_remark, ipv6_prefix_list_remark_cmd,
+ "ipv6 prefix-list WORD$name description LINE...",
+ IPV6_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+{
+ int rv;
+ char *remark;
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ remark = argv_concat(argv, argc, 4);
+ nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark);
+ rv = nb_cli_apply_changes(vty, xpath);
+ XFREE(MTYPE_TMP, remark);
+
+ return rv;
+}
+
+DEFPY(
+ no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_cmd,
+ "no ipv6 prefix-list WORD$name description",
+ NO_STR
+ IPV6_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_REMARK_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']/remark",
+ name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS(
+ no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_line_cmd,
+ "no ipv6 prefix-list WORD$name description LINE...",
+ NO_STR
+ IPV6_STR
+ PREFIX_LIST_STR
+ PREFIX_LIST_NAME_STR
+ ACCESS_LIST_REMARK_STR
+ ACCESS_LIST_REMARK_LINE_STR)
+
+void prefix_list_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ int type = yang_dnode_get_enum(dnode, "../type");
+ const char *ge_str = NULL, *le_str = NULL;
+ bool is_any;
+ struct prefix p;
+
+ is_any = yang_dnode_exists(dnode, "./any");
+ switch (type) {
+ case YPLT_IPV4:
+ if (!is_any)
+ yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix");
+ if (yang_dnode_exists(dnode,
+ "./ipv4-prefix-length-greater-or-equal"))
+ ge_str = yang_dnode_get_string(
+ dnode, "./ipv4-prefix-length-greater-or-equal");
+ if (yang_dnode_exists(dnode,
+ "./ipv4-prefix-length-lesser-or-equal"))
+ le_str = yang_dnode_get_string(
+ dnode, "./ipv4-prefix-length-lesser-or-equal");
+
+ vty_out(vty, "ip ");
+ break;
+ case YPLT_IPV6:
+ if (!is_any)
+ yang_dnode_get_prefix(&p, dnode, "ipv6-prefix");
+ if (yang_dnode_exists(dnode,
+ "./ipv6-prefix-length-greater-or-equal"))
+ ge_str = yang_dnode_get_string(
+ dnode, "./ipv6-prefix-length-greater-or-equal");
+ if (yang_dnode_exists(dnode,
+ "./ipv6-prefix-length-lesser-or-equal"))
+ le_str = yang_dnode_get_string(
+ dnode, "./ipv6-prefix-length-lesser-or-equal");
+
+ vty_out(vty, "ipv6 ");
+ break;
+ }
+
+ vty_out(vty, "prefix-list %s seq %s %s",
+ yang_dnode_get_string(dnode, "../name"),
+ yang_dnode_get_string(dnode, "./sequence"),
+ yang_dnode_get_string(dnode, "./action"));
+
+ if (is_any) {
+ vty_out(vty, " any\n");
+ return;
+ }
+
+ vty_out(vty, " %pFX", &p);
+ if (ge_str)
+ vty_out(vty, " ge %s", ge_str);
+ if (le_str)
+ vty_out(vty, " le %s", le_str);
+
+ vty_out(vty, "\n");
+}
+
+void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ int type = yang_dnode_get_enum(dnode, "../type");
+
+ switch (type) {
+ case YPLT_IPV4:
+ vty_out(vty, "ip ");
+ break;
+ case YPLT_IPV6:
+ vty_out(vty, "ipv6 ");
+ break;
+ }
+
+ vty_out(vty, "prefix-list %s description %s\n",
+ yang_dnode_get_string(dnode, "../name"),
+ yang_dnode_get_string(dnode, NULL));
+}
+
+void filter_cli_init(void)
+{
+ /* access-list cisco-style (legacy). */
+ install_element(CONFIG_NODE, &access_list_std_cmd);
+ install_element(CONFIG_NODE, &no_access_list_std_cmd);
+ install_element(CONFIG_NODE, &access_list_ext_cmd);
+ install_element(CONFIG_NODE, &no_access_list_ext_cmd);
+ install_element(CONFIG_NODE, &no_access_list_legacy_cmd);
+ install_element(CONFIG_NODE, &access_list_legacy_remark_cmd);
+ install_element(CONFIG_NODE, &no_access_list_legacy_remark_cmd);
+ install_element(CONFIG_NODE, &no_access_list_legacy_remark_line_cmd);
+
+ /* access-list zebra-style. */
+ install_element(CONFIG_NODE, &access_list_cmd);
+ install_element(CONFIG_NODE, &no_access_list_cmd);
+ install_element(CONFIG_NODE, &no_access_list_all_cmd);
+ install_element(CONFIG_NODE, &access_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_access_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_access_list_remark_line_cmd);
+
+ install_element(CONFIG_NODE, &ipv6_access_list_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_access_list_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_access_list_all_cmd);
+ install_element(CONFIG_NODE, &ipv6_access_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_access_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_access_list_remark_line_cmd);
+
+ install_element(CONFIG_NODE, &mac_access_list_cmd);
+ install_element(CONFIG_NODE, &no_mac_access_list_cmd);
+ install_element(CONFIG_NODE, &no_mac_access_list_all_cmd);
+ install_element(CONFIG_NODE, &mac_access_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_mac_access_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_mac_access_list_remark_line_cmd);
+
+ /* prefix lists. */
+ install_element(CONFIG_NODE, &ip_prefix_list_cmd);
+ install_element(CONFIG_NODE, &no_ip_prefix_list_cmd);
+ install_element(CONFIG_NODE, &no_ip_prefix_list_seq_cmd);
+ install_element(CONFIG_NODE, &no_ip_prefix_list_all_cmd);
+ install_element(CONFIG_NODE, &ip_prefix_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_ip_prefix_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_ip_prefix_list_remark_line_cmd);
+
+ install_element(CONFIG_NODE, &ipv6_prefix_list_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_prefix_list_seq_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_prefix_list_all_cmd);
+ install_element(CONFIG_NODE, &ipv6_prefix_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_line_cmd);
+}
diff --git a/lib/filter_nb.c b/lib/filter_nb.c
new file mode 100644
index 0000000000..83cf586ed3
--- /dev/null
+++ b/lib/filter_nb.c
@@ -0,0 +1,1286 @@
+/*
+ * FRR filter northbound implementation.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include "zebra.h"
+
+#include "lib/northbound.h"
+#include "lib/prefix.h"
+
+#include "lib/filter.h"
+#include "lib/plist.h"
+#include "lib/plist_int.h"
+
+/* Helper function. */
+static in_addr_t
+ipv4_network_addr(in_addr_t hostaddr, int masklen)
+{
+ struct in_addr mask;
+
+ masklen2ip(masklen, &mask);
+ return hostaddr & mask.s_addr;
+}
+
+static enum nb_error
+prefix_list_length_validate(const struct lyd_node *dnode)
+{
+ int type = yang_dnode_get_enum(dnode, "../../type");
+ const char *xpath_le = NULL, *xpath_ge = NULL;
+ struct prefix p;
+ uint8_t le, ge;
+
+ if (type == YPLT_IPV4) {
+ yang_dnode_get_prefix(&p, dnode, "../ipv4-prefix");
+ xpath_le = "../ipv4-prefix-length-lesser-or-equal";
+ xpath_ge = "../ipv4-prefix-length-greater-or-equal";
+ } else {
+ yang_dnode_get_prefix(&p, dnode, "../ipv6-prefix");
+ xpath_le = "../ipv6-prefix-length-lesser-or-equal";
+ xpath_ge = "../ipv6-prefix-length-greater-or-equal";
+ }
+
+ /*
+ * Check rule:
+ * prefix length <= le.
+ */
+ if (yang_dnode_exists(dnode, xpath_le)) {
+ le = yang_dnode_get_uint8(dnode, xpath_le);
+ if (p.prefixlen > le)
+ goto log_and_fail;
+
+ }
+
+ /*
+ * Check rule:
+ * prefix length < ge.
+ */
+ if (yang_dnode_exists(dnode, xpath_ge)) {
+ ge = yang_dnode_get_uint8(dnode, xpath_ge);
+ if (p.prefixlen >= ge)
+ goto log_and_fail;
+ }
+
+ /*
+ * Check rule:
+ * ge <= le.
+ */
+ if (yang_dnode_exists(dnode, xpath_le) &&
+ yang_dnode_exists(dnode, xpath_ge)) {
+ le = yang_dnode_get_uint8(dnode, xpath_le);
+ ge = yang_dnode_get_uint8(dnode, xpath_ge);
+ if (ge > le)
+ goto log_and_fail;
+ }
+
+ return NB_OK;
+
+ log_and_fail:
+ zlog_info("prefix-list: invalid prefix range for %pFX: "
+ "Make sure that mask length < ge <= le", &p);
+ return NB_ERR_VALIDATION;
+}
+
+/**
+ * Sets prefix list entry to blank value.
+ *
+ * \param[out] ple prefix list entry to modify.
+ */
+static void prefix_list_entry_set_empty(struct prefix_list_entry *ple)
+{
+ ple->any = false;
+ memset(&ple->prefix, 0, sizeof(ple->prefix));
+ ple->ge = 0;
+ ple->le = 0;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy
+ */
+static int lib_access_list_legacy_create(struct nb_cb_create_args *args)
+{
+ struct access_list *acl;
+ const char *acl_name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ acl_name = yang_dnode_get_string(args->dnode, "./number");
+ acl = access_list_get(AFI_IP, acl_name);
+ nb_running_set_entry(args->dnode, acl);
+
+ return NB_OK;
+}
+
+static int lib_access_list_legacy_destroy(struct nb_cb_destroy_args *args)
+{
+ struct access_master *am;
+ struct access_list *acl;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ acl = nb_running_unset_entry(args->dnode);
+ am = acl->master;
+ if (am->delete_hook)
+ am->delete_hook(acl);
+
+ access_list_delete(acl);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy/remark
+ */
+static int lib_access_list_legacy_remark_modify(struct nb_cb_modify_args *args)
+{
+ struct access_list *acl;
+ const char *remark;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ acl = nb_running_get_entry(args->dnode, NULL, true);
+ if (acl->remark)
+ XFREE(MTYPE_TMP, acl->remark);
+
+ remark = yang_dnode_get_string(args->dnode, NULL);
+ acl->remark = XSTRDUP(MTYPE_TMP, remark);
+
+ return NB_OK;
+}
+
+static int
+lib_access_list_legacy_remark_destroy(struct nb_cb_destroy_args *args)
+{
+ struct access_list *acl;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ acl = nb_running_get_entry(args->dnode, NULL, true);
+ if (acl->remark)
+ XFREE(MTYPE_TMP, acl->remark);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy/entry
+ */
+static int lib_access_list_legacy_entry_create(struct nb_cb_create_args *args)
+{
+ struct filter_cisco *fc;
+ struct access_list *acl;
+ struct filter *f;
+ uint32_t aclno;
+
+ /* TODO: validate `filter_lookup_cisco` returns NULL. */
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ aclno = yang_dnode_get_uint16(args->dnode, "../number");
+
+ f = filter_new();
+ f->cisco = 1;
+ f->seq = yang_dnode_get_uint32(args->dnode, "./sequence");
+ fc = &f->u.cfilter;
+ if ((aclno >= 1 && aclno <= 99) || (aclno >= 1300 && aclno <= 1999))
+ fc->extended = 0;
+ else
+ fc->extended = 1;
+
+ acl = nb_running_get_entry(args->dnode, NULL, true);
+ f->acl = acl;
+ access_list_filter_add(acl, f);
+ nb_running_set_entry(args->dnode, f);
+
+ return NB_OK;
+}
+
+static int lib_access_list_legacy_entry_destroy(struct nb_cb_destroy_args *args)
+{
+ struct access_list *acl;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_unset_entry(args->dnode);
+ acl = f->acl;
+ access_list_filter_delete(acl, f);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy/entry/action
+ */
+static int
+lib_access_list_legacy_entry_action_modify(struct nb_cb_modify_args *args)
+{
+ const char *filter_type;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ filter_type = yang_dnode_get_string(args->dnode, NULL);
+ if (strcmp(filter_type, "permit") == 0)
+ f->type = FILTER_PERMIT;
+ else
+ f->type = FILTER_DENY;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy/entry/host
+ */
+static int
+lib_access_list_legacy_entry_host_modify(struct nb_cb_modify_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL);
+ fc->addr_mask.s_addr = INADDR_ANY;
+
+ return NB_OK;
+}
+
+static int
+lib_access_list_legacy_entry_host_destroy(struct nb_cb_destroy_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ fc->addr.s_addr = INADDR_ANY;
+ fc->addr_mask.s_addr = INADDR_NONE;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy/entry/network
+ */
+static int
+lib_access_list_legacy_entry_network_modify(struct nb_cb_modify_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+ struct prefix p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ yang_dnode_get_prefix(&p, args->dnode, NULL);
+ fc->addr.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen);
+ masklen2ip(p.prefixlen, &fc->addr_mask);
+
+ return NB_OK;
+}
+
+static int
+lib_access_list_legacy_entry_network_destroy(struct nb_cb_destroy_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ fc->addr.s_addr = INADDR_ANY;
+ fc->addr_mask.s_addr = INADDR_NONE;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy/entry/any
+ */
+static int
+lib_access_list_legacy_entry_any_create(struct nb_cb_create_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ fc->addr.s_addr = INADDR_ANY;
+ fc->addr_mask.s_addr = INADDR_NONE;
+
+ return NB_OK;
+}
+
+static int
+lib_access_list_legacy_entry_any_destroy(struct nb_cb_destroy_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ fc->addr.s_addr = INADDR_ANY;
+ fc->addr_mask.s_addr = INADDR_NONE;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy/entry/destination-host
+ */
+static int lib_access_list_legacy_entry_destination_host_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL);
+ fc->mask_mask.s_addr = INADDR_ANY;
+
+ return NB_OK;
+}
+
+static int lib_access_list_legacy_entry_destination_host_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ fc->mask.s_addr = INADDR_ANY;
+ fc->mask_mask.s_addr = INADDR_NONE;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy/entry/destination-network
+ */
+static int lib_access_list_legacy_entry_destination_network_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+ struct prefix p;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ yang_dnode_get_prefix(&p, args->dnode, NULL);
+ fc->mask.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen);
+ masklen2ip(p.prefixlen, &fc->mask_mask);
+
+ return NB_OK;
+}
+
+static int lib_access_list_legacy_entry_destination_network_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ fc->mask.s_addr = INADDR_ANY;
+ fc->mask_mask.s_addr = INADDR_NONE;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list-legacy/entry/destination-any
+ */
+static int lib_access_list_legacy_entry_destination_any_create(
+ struct nb_cb_create_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ fc->mask.s_addr = INADDR_ANY;
+ fc->mask_mask.s_addr = INADDR_NONE;
+
+ return NB_OK;
+}
+
+static int lib_access_list_legacy_entry_destination_any_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct filter_cisco *fc;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fc = &f->u.cfilter;
+ fc->mask.s_addr = INADDR_ANY;
+ fc->mask_mask.s_addr = INADDR_NONE;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list
+ */
+static int lib_access_list_create(struct nb_cb_create_args *args)
+{
+ struct access_list *acl = NULL;
+ const char *acl_name;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ type = yang_dnode_get_enum(args->dnode, "./type");
+ acl_name = yang_dnode_get_string(args->dnode, "./name");
+
+ switch (type) {
+ case YALT_IPV4:
+ acl = access_list_get(AFI_IP, acl_name);
+ break;
+ case YALT_IPV6:
+ acl = access_list_get(AFI_IP6, acl_name);
+ break;
+ case YALT_MAC:
+ acl = access_list_get(AFI_L2VPN, acl_name);
+ break;
+ }
+
+ nb_running_set_entry(args->dnode, acl);
+
+ return NB_OK;
+}
+
+static int lib_access_list_destroy(struct nb_cb_destroy_args *args)
+{
+ struct access_master *am;
+ struct access_list *acl;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ acl = nb_running_unset_entry(args->dnode);
+ am = acl->master;
+ if (am->delete_hook)
+ am->delete_hook(acl);
+
+ access_list_delete(acl);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list/entry
+ */
+static int lib_access_list_entry_create(struct nb_cb_create_args *args)
+{
+ struct access_list *acl;
+ struct filter *f;
+
+ /* TODO: validate `filter_lookup_zebra` returns NULL. */
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = filter_new();
+ f->seq = yang_dnode_get_uint32(args->dnode, "./sequence");
+
+ acl = nb_running_get_entry(args->dnode, NULL, true);
+ f->acl = acl;
+ access_list_filter_add(acl, f);
+ nb_running_set_entry(args->dnode, f);
+
+ return NB_OK;
+}
+
+static int lib_access_list_entry_destroy(struct nb_cb_destroy_args *args)
+{
+ struct access_list *acl;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_unset_entry(args->dnode);
+ acl = f->acl;
+ access_list_filter_delete(acl, f);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list/entry/ipv4-prefix
+ */
+static int
+lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
+{
+ struct filter_zebra *fz;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fz = &f->u.zfilter;
+ yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL);
+
+ return NB_OK;
+}
+
+static int
+lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args)
+{
+ struct filter_zebra *fz;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fz = &f->u.zfilter;
+ memset(&fz->prefix, 0, sizeof(fz->prefix));
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list/entry/ipv4-exact-match
+ */
+static int
+lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args)
+{
+ struct filter_zebra *fz;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fz = &f->u.zfilter;
+ fz->exact = yang_dnode_get_bool(args->dnode, NULL);
+
+ return NB_OK;
+}
+
+static int
+lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args)
+{
+ struct filter_zebra *fz;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fz = &f->u.zfilter;
+ fz->exact = 0;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/access-list/entry/any
+ */
+static int lib_access_list_entry_any_create(struct nb_cb_create_args *args)
+{
+ struct filter_zebra *fz;
+ struct filter *f;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fz = &f->u.zfilter;
+ memset(&fz->prefix, 0, sizeof(fz->prefix));
+
+ type = yang_dnode_get_enum(args->dnode, "../../type");
+ switch (type) {
+ case YALT_IPV4:
+ fz->prefix.family = AF_INET;
+ break;
+ case YALT_IPV6:
+ fz->prefix.family = AF_INET6;
+ break;
+ case YALT_MAC:
+ fz->prefix.family = AF_ETHERNET;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int lib_access_list_entry_any_destroy(struct nb_cb_destroy_args *args)
+{
+ struct filter_zebra *fz;
+ struct filter *f;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ f = nb_running_get_entry(args->dnode, NULL, true);
+ fz = &f->u.zfilter;
+ fz->prefix.family = 0;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list
+ */
+static int lib_prefix_list_create(struct nb_cb_create_args *args)
+{
+ struct prefix_list *pl = NULL;
+ const char *name;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ type = yang_dnode_get_enum(args->dnode, "./type");
+ name = yang_dnode_get_string(args->dnode, "./name");
+ switch (type) {
+ case 0: /* ipv4 */
+ pl = prefix_list_get(AFI_IP, 0, name);
+ break;
+ case 1: /* ipv6 */
+ pl = prefix_list_get(AFI_IP6, 0, name);
+ break;
+ }
+
+ nb_running_set_entry(args->dnode, pl);
+
+ return NB_OK;
+}
+
+static int lib_prefix_list_destroy(struct nb_cb_destroy_args *args)
+{
+ struct prefix_list *pl;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ pl = nb_running_unset_entry(args->dnode);
+ prefix_list_delete(pl);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list/remark
+ */
+static int lib_prefix_list_remark_modify(struct nb_cb_modify_args *args)
+{
+ struct prefix_list *pl;
+ const char *remark;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ pl = nb_running_get_entry(args->dnode, NULL, true);
+ if (pl->desc)
+ XFREE(MTYPE_TMP, pl->desc);
+
+ remark = yang_dnode_get_string(args->dnode, NULL);
+ pl->desc = XSTRDUP(MTYPE_TMP, remark);
+
+ return NB_OK;
+}
+
+static int lib_prefix_list_remark_destroy(struct nb_cb_destroy_args *args)
+{
+ struct prefix_list *pl;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ pl = nb_running_get_entry(args->dnode, NULL, true);
+ if (pl->desc)
+ XFREE(MTYPE_TMP, pl->desc);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry
+ */
+static int lib_prefix_list_entry_create(struct nb_cb_create_args *args)
+{
+ struct prefix_list_entry *ple;
+ struct prefix_list *pl;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ pl = nb_running_get_entry(args->dnode, NULL, true);
+ ple = prefix_list_entry_new();
+ ple->pl = pl;
+ ple->seq = yang_dnode_get_uint32(args->dnode, "./sequence");
+ prefix_list_entry_set_empty(ple);
+ nb_running_set_entry(args->dnode, ple);
+
+ return NB_OK;
+}
+
+static int lib_prefix_list_entry_destroy(struct nb_cb_destroy_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_unset_entry(args->dnode);
+ if (ple->installed)
+ prefix_list_entry_delete2(ple);
+ else
+ prefix_list_entry_free(ple);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry/action
+ */
+static int lib_prefix_list_entry_action_modify(struct nb_cb_modify_args *args)
+{
+ struct prefix_list_entry *ple;
+ int action_type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ action_type = yang_dnode_get_enum(args->dnode, NULL);
+ if (action_type == YPLA_PERMIT)
+ ple->type = PREFIX_PERMIT;
+ else
+ ple->type = PREFIX_DENY;
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix
+ */
+static int
+lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args)
+{
+ struct prefix_list_entry *ple;
+ struct prefix p;
+
+ if (args->event == NB_EV_VALIDATE) {
+ /*
+ * TODO: validate prefix_entry_dup_check() passes.
+ *
+ * This needs to be implemented using YANG lyd_node
+ * navigation, because the `priv` data structures are not
+ * available at `NB_EV_VALIDATE` phase. An easier
+ * alternative would be mark `ipvx-prefix` as unique
+ * (see RFC 7950, Section 7.8.3. The list "unique" Statement).
+ */
+ return NB_OK;
+ }
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ yang_dnode_get_prefix(&ple->prefix, args->dnode, NULL);
+
+ /* Apply mask and correct original address if necessary. */
+ prefix_copy(&p, &ple->prefix);
+ apply_mask(&p);
+ if (!prefix_same(&ple->prefix, &p)) {
+ zlog_info("%s: bad network %pFX correcting it to %pFX",
+ __func__, &ple->prefix, &p);
+ prefix_copy(&ple->prefix, &p);
+ }
+
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+static int
+lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ memset(&ple->prefix, 0, sizeof(ple->prefix));
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal
+ */
+static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event == NB_EV_VALIDATE &&
+ prefix_list_length_validate(args->dnode) != NB_OK)
+ return NB_ERR_VALIDATION;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ ple->ge = yang_dnode_get_uint8(args->dnode, NULL);
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ ple->ge = 0;
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal
+ */
+static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event == NB_EV_VALIDATE &&
+ prefix_list_length_validate(args->dnode) != NB_OK)
+ return NB_ERR_VALIDATION;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ ple->le = yang_dnode_get_uint8(args->dnode, NULL);
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ ple->le = 0;
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-filter:lib/prefix-list/entry/any
+ */
+static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args)
+{
+ struct prefix_list_entry *ple;
+ int type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ ple->any = true;
+
+ /* Fill prefix struct from scratch. */
+ memset(&ple->prefix, 0, sizeof(ple->prefix));
+
+ type = yang_dnode_get_enum(args->dnode, "../../type");
+ switch (type) {
+ case YPLT_IPV4:
+ ple->prefix.family = AF_INET;
+ ple->ge = 0;
+ ple->le = IPV4_MAX_BITLEN;
+ break;
+ case YPLT_IPV6:
+ ple->prefix.family = AF_INET6;
+ ple->ge = 0;
+ ple->le = IPV6_MAX_BITLEN;
+ break;
+ }
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+static int lib_prefix_list_entry_any_destroy(struct nb_cb_destroy_args *args)
+{
+ struct prefix_list_entry *ple;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ple = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Start prefix entry update procedure. */
+ prefix_list_entry_update_start(ple);
+
+ prefix_list_entry_set_empty(ple);
+
+ /* Finish prefix entry update procedure. */
+ prefix_list_entry_update_finish(ple);
+
+ return NB_OK;
+}
+
+/* clang-format off */
+const struct frr_yang_module_info frr_filter_info = {
+ .name = "frr-filter",
+ .nodes = {
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy",
+ .cbs = {
+ .create = lib_access_list_legacy_create,
+ .destroy = lib_access_list_legacy_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy/remark",
+ .cbs = {
+ .modify = lib_access_list_legacy_remark_modify,
+ .destroy = lib_access_list_legacy_remark_destroy,
+ .cli_show = access_list_legacy_remark_show,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy/entry",
+ .cbs = {
+ .create = lib_access_list_legacy_entry_create,
+ .destroy = lib_access_list_legacy_entry_destroy,
+ .cli_show = access_list_legacy_show,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy/entry/action",
+ .cbs = {
+ .modify = lib_access_list_legacy_entry_action_modify,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy/entry/host",
+ .cbs = {
+ .modify = lib_access_list_legacy_entry_host_modify,
+ .destroy = lib_access_list_legacy_entry_host_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy/entry/network",
+ .cbs = {
+ .modify = lib_access_list_legacy_entry_network_modify,
+ .destroy = lib_access_list_legacy_entry_network_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy/entry/any",
+ .cbs = {
+ .create = lib_access_list_legacy_entry_any_create,
+ .destroy = lib_access_list_legacy_entry_any_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy/entry/destination-host",
+ .cbs = {
+ .modify = lib_access_list_legacy_entry_destination_host_modify,
+ .destroy = lib_access_list_legacy_entry_destination_host_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy/entry/destination-network",
+ .cbs = {
+ .modify = lib_access_list_legacy_entry_destination_network_modify,
+ .destroy = lib_access_list_legacy_entry_destination_network_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list-legacy/entry/destination-any",
+ .cbs = {
+ .create = lib_access_list_legacy_entry_destination_any_create,
+ .destroy = lib_access_list_legacy_entry_destination_any_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list",
+ .cbs = {
+ .create = lib_access_list_create,
+ .destroy = lib_access_list_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/remark",
+ .cbs = {
+ .modify = lib_access_list_legacy_remark_modify,
+ .destroy = lib_access_list_legacy_remark_destroy,
+ .cli_show = access_list_remark_show,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/entry",
+ .cbs = {
+ .create = lib_access_list_entry_create,
+ .destroy = lib_access_list_entry_destroy,
+ .cli_show = access_list_show,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/entry/action",
+ .cbs = {
+ .modify = lib_access_list_legacy_entry_action_modify,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/entry/ipv4-prefix",
+ .cbs = {
+ .modify = lib_access_list_entry_ipv4_prefix_modify,
+ .destroy = lib_access_list_entry_ipv4_prefix_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/entry/ipv4-exact-match",
+ .cbs = {
+ .modify = lib_access_list_entry_ipv4_exact_match_modify,
+ .destroy = lib_access_list_entry_ipv4_exact_match_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/entry/ipv6-prefix",
+ .cbs = {
+ .modify = lib_access_list_entry_ipv4_prefix_modify,
+ .destroy = lib_access_list_entry_ipv4_prefix_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/entry/ipv6-exact-match",
+ .cbs = {
+ .modify = lib_access_list_entry_ipv4_exact_match_modify,
+ .destroy = lib_access_list_entry_ipv4_exact_match_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/entry/mac",
+ .cbs = {
+ .modify = lib_access_list_entry_ipv4_prefix_modify,
+ .destroy = lib_access_list_entry_ipv4_prefix_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/access-list/entry/any",
+ .cbs = {
+ .create = lib_access_list_entry_any_create,
+ .destroy = lib_access_list_entry_any_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list",
+ .cbs = {
+ .create = lib_prefix_list_create,
+ .destroy = lib_prefix_list_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/remark",
+ .cbs = {
+ .modify = lib_prefix_list_remark_modify,
+ .destroy = lib_prefix_list_remark_destroy,
+ .cli_show = prefix_list_remark_show,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry",
+ .cbs = {
+ .create = lib_prefix_list_entry_create,
+ .destroy = lib_prefix_list_entry_destroy,
+ .cli_show = prefix_list_show,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry/action",
+ .cbs = {
+ .modify = lib_prefix_list_entry_action_modify,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix",
+ .cbs = {
+ .modify = lib_prefix_list_entry_ipv4_prefix_modify,
+ .destroy = lib_prefix_list_entry_ipv4_prefix_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal",
+ .cbs = {
+ .modify = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify,
+ .destroy = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal",
+ .cbs = {
+ .modify = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify,
+ .destroy = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix",
+ .cbs = {
+ .modify = lib_prefix_list_entry_ipv4_prefix_modify,
+ .destroy = lib_prefix_list_entry_ipv4_prefix_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-greater-or-equal",
+ .cbs = {
+ .modify = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify,
+ .destroy = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-lesser-or-equal",
+ .cbs = {
+ .modify = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify,
+ .destroy = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-filter:lib/prefix-list/entry/any",
+ .cbs = {
+ .create = lib_prefix_list_entry_any_create,
+ .destroy = lib_prefix_list_entry_any_destroy,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/lib/if.c b/lib/if.c
index 9efd298a4f..f03574e787 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -263,15 +263,21 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id)
*/
if (yang_module_find("frr-interface")) {
struct lyd_node *if_dnode;
+ char oldpath[XPATH_MAXLEN];
+ char newpath[XPATH_MAXLEN];
if_dnode = yang_dnode_get(
running_config->dnode,
"/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf",
ifp->name, old_vrf->name);
+
if (if_dnode) {
- nb_running_unset_entry(if_dnode->parent);
+ yang_dnode_get_path(if_dnode->parent, oldpath,
+ sizeof(oldpath));
yang_dnode_change_leaf(if_dnode, vrf->name);
- nb_running_set_entry(if_dnode->parent, ifp);
+ yang_dnode_get_path(if_dnode->parent, newpath,
+ sizeof(newpath));
+ nb_running_move_tree(oldpath, newpath);
running_config->version++;
}
}
@@ -1561,8 +1567,8 @@ static int lib_interface_destroy(struct nb_cb_destroy_args *args)
case NB_EV_VALIDATE:
ifp = nb_running_get_entry(args->dnode, NULL, true);
if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
- zlog_warn("%s: only inactive interfaces can be deleted",
- __func__);
+ snprintf(args->errmsg, args->errmsg_len,
+ "only inactive interfaces can be deleted");
return NB_ERR_VALIDATION;
}
break;
diff --git a/lib/libfrr.c b/lib/libfrr.c
index ac165f254e..b3df7de6d3 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -661,7 +661,6 @@ struct thread_master *frr_init(void)
zlog_init(di->progname, di->logname, di->instance,
ids.uid_normal, ids.gid_normal);
- zlog_tls_buffer_init();
command_setup_early_logging(di->early_logging, di->early_loglevel);
@@ -902,14 +901,18 @@ static int frr_config_read_in(struct thread *t)
* reading the configuration file.
*/
if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) {
+ struct nb_context context = {};
+ char errmsg[BUFSIZ] = {0};
int ret;
- ret = nb_candidate_commit(vty_shared_candidate_config,
- NB_CLIENT_CLI, NULL, true,
- "Read configuration file", NULL);
+ context.client = NB_CLIENT_CLI;
+ ret = nb_candidate_commit(&context, vty_shared_candidate_config,
+ true, "Read configuration file", NULL,
+ errmsg, sizeof(errmsg));
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
- zlog_err("%s: failed to read configuration file.",
- __func__);
+ zlog_err(
+ "%s: failed to read configuration file: %s (%s)",
+ __func__, nb_err_name(ret), errmsg);
}
return 0;
@@ -936,6 +939,7 @@ void frr_config_fork(void)
if (!di->pid_file)
di->pid_file = pidfile_default;
pid_output(di->pid_file);
+ zlog_tls_buffer_init();
}
static void frr_vty_serv(void)
diff --git a/lib/northbound.c b/lib/northbound.c
index 18bd4f5fd8..48b8499bfc 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -30,6 +30,7 @@
#include "northbound.h"
#include "northbound_cli.h"
#include "northbound_db.h"
+#include "frrstr.h"
DEFINE_MTYPE_STATIC(LIB, NB_NODE, "Northbound Node")
DEFINE_MTYPE_STATIC(LIB, NB_CONFIG, "Northbound Configuration")
@@ -62,19 +63,24 @@ static struct {
*/
static bool transaction_in_progress;
-static int nb_callback_pre_validate(const struct nb_node *nb_node,
- const struct lyd_node *dnode);
-static int nb_callback_configuration(const enum nb_event event,
- struct nb_config_change *change);
-static struct nb_transaction *nb_transaction_new(struct nb_config *config,
- struct nb_config_cbs *changes,
- enum nb_client client,
- const void *user,
- const char *comment);
+static int nb_callback_pre_validate(struct nb_context *context,
+ const struct nb_node *nb_node,
+ const struct lyd_node *dnode, char *errmsg,
+ size_t errmsg_len);
+static int nb_callback_configuration(struct nb_context *context,
+ const enum nb_event event,
+ struct nb_config_change *change,
+ char *errmsg, size_t errmsg_len);
+static struct nb_transaction *
+nb_transaction_new(struct nb_context *context, struct nb_config *config,
+ struct nb_config_cbs *changes, const char *comment,
+ char *errmsg, size_t errmsg_len);
static void nb_transaction_free(struct nb_transaction *transaction);
static int nb_transaction_process(enum nb_event event,
- struct nb_transaction *transaction);
-static void nb_transaction_apply_finish(struct nb_transaction *transaction);
+ struct nb_transaction *transaction,
+ char *errmsg, size_t errmsg_len);
+static void nb_transaction_apply_finish(struct nb_transaction *transaction,
+ char *errmsg, size_t errmsg_len);
static int nb_oper_data_iter_node(const struct lys_node *snode,
const char *xpath, const void *list_entry,
const struct yang_list_keys *list_keys,
@@ -580,20 +586,25 @@ int nb_candidate_update(struct nb_config *candidate)
* WARNING: lyd_validate() can change the configuration as part of the
* validation process.
*/
-static int nb_candidate_validate_yang(struct nb_config *candidate)
+static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
+ size_t errmsg_len)
{
if (lyd_validate(&candidate->dnode,
LYD_OPT_STRICT | LYD_OPT_CONFIG | LYD_OPT_WHENAUTODEL,
ly_native_ctx)
- != 0)
+ != 0) {
+ yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
return NB_ERR_VALIDATION;
+ }
return NB_OK;
}
/* Perform code-level validation using the northbound callbacks. */
-static int nb_candidate_validate_code(struct nb_config *candidate,
- struct nb_config_cbs *changes)
+static int nb_candidate_validate_code(struct nb_context *context,
+ struct nb_config *candidate,
+ struct nb_config_cbs *changes,
+ char *errmsg, size_t errmsg_len)
{
struct nb_config_cb *cb;
struct lyd_node *root, *next, *child;
@@ -608,7 +619,8 @@ static int nb_candidate_validate_code(struct nb_config *candidate,
if (!nb_node->cbs.pre_validate)
goto next;
- ret = nb_callback_pre_validate(nb_node, child);
+ ret = nb_callback_pre_validate(context, nb_node, child,
+ errmsg, errmsg_len);
if (ret != NB_OK)
return NB_ERR_VALIDATION;
@@ -621,7 +633,8 @@ static int nb_candidate_validate_code(struct nb_config *candidate,
RB_FOREACH (cb, nb_config_cbs, changes) {
struct nb_config_change *change = (struct nb_config_change *)cb;
- ret = nb_callback_configuration(NB_EV_VALIDATE, change);
+ ret = nb_callback_configuration(context, NB_EV_VALIDATE, change,
+ errmsg, errmsg_len);
if (ret != NB_OK)
return NB_ERR_VALIDATION;
}
@@ -629,30 +642,36 @@ static int nb_candidate_validate_code(struct nb_config *candidate,
return NB_OK;
}
-int nb_candidate_validate(struct nb_config *candidate)
+int nb_candidate_validate(struct nb_context *context,
+ struct nb_config *candidate, char *errmsg,
+ size_t errmsg_len)
{
struct nb_config_cbs changes;
int ret;
- if (nb_candidate_validate_yang(candidate) != NB_OK)
+ if (nb_candidate_validate_yang(candidate, errmsg, sizeof(errmsg_len))
+ != NB_OK)
return NB_ERR_VALIDATION;
RB_INIT(nb_config_cbs, &changes);
nb_config_diff(running_config, candidate, &changes);
- ret = nb_candidate_validate_code(candidate, &changes);
+ ret = nb_candidate_validate_code(context, candidate, &changes, errmsg,
+ errmsg_len);
nb_config_diff_del_changes(&changes);
return ret;
}
-int nb_candidate_commit_prepare(struct nb_config *candidate,
- enum nb_client client, const void *user,
+int nb_candidate_commit_prepare(struct nb_context *context,
+ struct nb_config *candidate,
const char *comment,
- struct nb_transaction **transaction)
+ struct nb_transaction **transaction,
+ char *errmsg, size_t errmsg_len)
{
struct nb_config_cbs changes;
- if (nb_candidate_validate_yang(candidate) != NB_OK) {
+ if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len)
+ != NB_OK) {
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
"%s: failed to validate candidate configuration",
__func__);
@@ -664,7 +683,9 @@ int nb_candidate_commit_prepare(struct nb_config *candidate,
if (RB_EMPTY(nb_config_cbs, &changes))
return NB_ERR_NO_CHANGES;
- if (nb_candidate_validate_code(candidate, &changes) != NB_OK) {
+ if (nb_candidate_validate_code(context, candidate, &changes, errmsg,
+ errmsg_len)
+ != NB_OK) {
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
"%s: failed to validate candidate configuration",
__func__);
@@ -672,29 +693,37 @@ int nb_candidate_commit_prepare(struct nb_config *candidate,
return NB_ERR_VALIDATION;
}
- *transaction =
- nb_transaction_new(candidate, &changes, client, user, comment);
+ *transaction = nb_transaction_new(context, candidate, &changes, comment,
+ errmsg, errmsg_len);
if (*transaction == NULL) {
flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
- "%s: failed to create transaction", __func__);
+ "%s: failed to create transaction: %s", __func__,
+ errmsg);
nb_config_diff_del_changes(&changes);
return NB_ERR_LOCKED;
}
- return nb_transaction_process(NB_EV_PREPARE, *transaction);
+ return nb_transaction_process(NB_EV_PREPARE, *transaction, errmsg,
+ errmsg_len);
}
void nb_candidate_commit_abort(struct nb_transaction *transaction)
{
- (void)nb_transaction_process(NB_EV_ABORT, transaction);
+ char errmsg[BUFSIZ] = {0};
+
+ (void)nb_transaction_process(NB_EV_ABORT, transaction, errmsg,
+ sizeof(errmsg));
nb_transaction_free(transaction);
}
void nb_candidate_commit_apply(struct nb_transaction *transaction,
bool save_transaction, uint32_t *transaction_id)
{
- (void)nb_transaction_process(NB_EV_APPLY, transaction);
- nb_transaction_apply_finish(transaction);
+ char errmsg[BUFSIZ] = {0};
+
+ (void)nb_transaction_process(NB_EV_APPLY, transaction, errmsg,
+ sizeof(errmsg));
+ nb_transaction_apply_finish(transaction, errmsg, sizeof(errmsg));
/* Replace running by candidate. */
transaction->config->version++;
@@ -709,15 +738,16 @@ void nb_candidate_commit_apply(struct nb_transaction *transaction,
nb_transaction_free(transaction);
}
-int nb_candidate_commit(struct nb_config *candidate, enum nb_client client,
- const void *user, bool save_transaction,
- const char *comment, uint32_t *transaction_id)
+int nb_candidate_commit(struct nb_context *context, struct nb_config *candidate,
+ bool save_transaction, const char *comment,
+ uint32_t *transaction_id, char *errmsg,
+ size_t errmsg_len)
{
struct nb_transaction *transaction = NULL;
int ret;
- ret = nb_candidate_commit_prepare(candidate, client, user, comment,
- &transaction);
+ ret = nb_candidate_commit_prepare(context, candidate, comment,
+ &transaction, errmsg, errmsg_len);
/*
* Apply the changes if the preparation phase succeeded. Otherwise abort
* the transaction.
@@ -735,7 +765,7 @@ int nb_running_lock(enum nb_client client, const void *user)
{
int ret = -1;
- frr_with_mutex(&running_config_mgmt_lock.mtx) {
+ frr_with_mutex (&running_config_mgmt_lock.mtx) {
if (!running_config_mgmt_lock.locked) {
running_config_mgmt_lock.locked = true;
running_config_mgmt_lock.owner_client = client;
@@ -751,7 +781,7 @@ int nb_running_unlock(enum nb_client client, const void *user)
{
int ret = -1;
- frr_with_mutex(&running_config_mgmt_lock.mtx) {
+ frr_with_mutex (&running_config_mgmt_lock.mtx) {
if (running_config_mgmt_lock.locked
&& running_config_mgmt_lock.owner_client == client
&& running_config_mgmt_lock.owner_user == user) {
@@ -769,7 +799,7 @@ int nb_running_lock_check(enum nb_client client, const void *user)
{
int ret = -1;
- frr_with_mutex(&running_config_mgmt_lock.mtx) {
+ frr_with_mutex (&running_config_mgmt_lock.mtx) {
if (!running_config_mgmt_lock.locked
|| (running_config_mgmt_lock.owner_client == client
&& running_config_mgmt_lock.owner_user == user))
@@ -801,78 +831,237 @@ static void nb_log_config_callback(const enum nb_event event,
value);
}
-static int nb_callback_create(const struct nb_node *nb_node,
+static int nb_callback_create(struct nb_context *context,
+ const struct nb_node *nb_node,
enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
+ union nb_resource *resource, char *errmsg,
+ size_t errmsg_len)
{
struct nb_cb_create_args args = {};
+ bool unexpected_error = false;
+ int ret;
nb_log_config_callback(event, NB_OP_CREATE, dnode);
+ args.context = context;
args.event = event;
args.dnode = dnode;
args.resource = resource;
- return nb_node->cbs.create(&args);
+ args.errmsg = errmsg;
+ args.errmsg_len = errmsg_len;
+ ret = nb_node->cbs.create(&args);
+
+ /* Detect and log unexpected errors. */
+ switch (ret) {
+ case NB_OK:
+ case NB_ERR:
+ break;
+ case NB_ERR_VALIDATION:
+ if (event != NB_EV_VALIDATE)
+ unexpected_error = true;
+ break;
+ case NB_ERR_RESOURCE:
+ if (event != NB_EV_PREPARE)
+ unexpected_error = true;
+ break;
+ case NB_ERR_INCONSISTENCY:
+ if (event == NB_EV_VALIDATE)
+ unexpected_error = true;
+ break;
+ default:
+ unexpected_error = true;
+ break;
+ }
+ if (unexpected_error)
+ DEBUGD(&nb_dbg_cbs_config,
+ "northbound callback: unexpected return value: %s",
+ nb_err_name(ret));
+
+ return ret;
}
-static int nb_callback_modify(const struct nb_node *nb_node,
+static int nb_callback_modify(struct nb_context *context,
+ const struct nb_node *nb_node,
enum nb_event event, const struct lyd_node *dnode,
- union nb_resource *resource)
+ union nb_resource *resource, char *errmsg,
+ size_t errmsg_len)
{
struct nb_cb_modify_args args = {};
+ bool unexpected_error = false;
+ int ret;
nb_log_config_callback(event, NB_OP_MODIFY, dnode);
+ args.context = context;
args.event = event;
args.dnode = dnode;
args.resource = resource;
- return nb_node->cbs.modify(&args);
+ args.errmsg = errmsg;
+ args.errmsg_len = errmsg_len;
+ ret = nb_node->cbs.modify(&args);
+
+ /* Detect and log unexpected errors. */
+ switch (ret) {
+ case NB_OK:
+ case NB_ERR:
+ break;
+ case NB_ERR_VALIDATION:
+ if (event != NB_EV_VALIDATE)
+ unexpected_error = true;
+ break;
+ case NB_ERR_RESOURCE:
+ if (event != NB_EV_PREPARE)
+ unexpected_error = true;
+ break;
+ case NB_ERR_INCONSISTENCY:
+ if (event == NB_EV_VALIDATE)
+ unexpected_error = true;
+ break;
+ default:
+ unexpected_error = true;
+ break;
+ }
+ if (unexpected_error)
+ DEBUGD(&nb_dbg_cbs_config,
+ "northbound callback: unexpected return value: %s",
+ nb_err_name(ret));
+
+ return ret;
}
-static int nb_callback_destroy(const struct nb_node *nb_node,
+static int nb_callback_destroy(struct nb_context *context,
+ const struct nb_node *nb_node,
enum nb_event event,
- const struct lyd_node *dnode)
+ const struct lyd_node *dnode, char *errmsg,
+ size_t errmsg_len)
{
struct nb_cb_destroy_args args = {};
+ bool unexpected_error = false;
+ int ret;
nb_log_config_callback(event, NB_OP_DESTROY, dnode);
+ args.context = context;
args.event = event;
args.dnode = dnode;
- return nb_node->cbs.destroy(&args);
+ args.errmsg = errmsg;
+ args.errmsg_len = errmsg_len;
+ ret = nb_node->cbs.destroy(&args);
+
+ /* Detect and log unexpected errors. */
+ switch (ret) {
+ case NB_OK:
+ case NB_ERR:
+ break;
+ case NB_ERR_VALIDATION:
+ if (event != NB_EV_VALIDATE)
+ unexpected_error = true;
+ break;
+ case NB_ERR_INCONSISTENCY:
+ if (event == NB_EV_VALIDATE)
+ unexpected_error = true;
+ break;
+ default:
+ unexpected_error = true;
+ break;
+ }
+ if (unexpected_error)
+ DEBUGD(&nb_dbg_cbs_config,
+ "northbound callback: unexpected return value: %s",
+ nb_err_name(ret));
+
+ return ret;
}
-static int nb_callback_move(const struct nb_node *nb_node, enum nb_event event,
- const struct lyd_node *dnode)
+static int nb_callback_move(struct nb_context *context,
+ const struct nb_node *nb_node, enum nb_event event,
+ const struct lyd_node *dnode, char *errmsg,
+ size_t errmsg_len)
{
struct nb_cb_move_args args = {};
+ bool unexpected_error = false;
+ int ret;
nb_log_config_callback(event, NB_OP_MOVE, dnode);
+ args.context = context;
args.event = event;
args.dnode = dnode;
- return nb_node->cbs.move(&args);
+ args.errmsg = errmsg;
+ args.errmsg_len = errmsg_len;
+ ret = nb_node->cbs.move(&args);
+
+ /* Detect and log unexpected errors. */
+ switch (ret) {
+ case NB_OK:
+ case NB_ERR:
+ break;
+ case NB_ERR_VALIDATION:
+ if (event != NB_EV_VALIDATE)
+ unexpected_error = true;
+ break;
+ case NB_ERR_INCONSISTENCY:
+ if (event == NB_EV_VALIDATE)
+ unexpected_error = true;
+ break;
+ default:
+ unexpected_error = true;
+ break;
+ }
+ if (unexpected_error)
+ DEBUGD(&nb_dbg_cbs_config,
+ "northbound callback: unexpected return value: %s",
+ nb_err_name(ret));
+
+ return ret;
}
-static int nb_callback_pre_validate(const struct nb_node *nb_node,
- const struct lyd_node *dnode)
+static int nb_callback_pre_validate(struct nb_context *context,
+ const struct nb_node *nb_node,
+ const struct lyd_node *dnode, char *errmsg,
+ size_t errmsg_len)
{
struct nb_cb_pre_validate_args args = {};
+ bool unexpected_error = false;
+ int ret;
nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode);
args.dnode = dnode;
- return nb_node->cbs.pre_validate(&args);
+ args.errmsg = errmsg;
+ args.errmsg_len = errmsg_len;
+ ret = nb_node->cbs.pre_validate(&args);
+
+ /* Detect and log unexpected errors. */
+ switch (ret) {
+ case NB_OK:
+ case NB_ERR_VALIDATION:
+ break;
+ default:
+ unexpected_error = true;
+ break;
+ }
+ if (unexpected_error)
+ DEBUGD(&nb_dbg_cbs_config,
+ "northbound callback: unexpected return value: %s",
+ nb_err_name(ret));
+
+ return ret;
}
-static void nb_callback_apply_finish(const struct nb_node *nb_node,
- const struct lyd_node *dnode)
+static void nb_callback_apply_finish(struct nb_context *context,
+ const struct nb_node *nb_node,
+ const struct lyd_node *dnode, char *errmsg,
+ size_t errmsg_len)
{
struct nb_cb_apply_finish_args args = {};
nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode);
+ args.context = context;
args.dnode = dnode;
+ args.errmsg = errmsg;
+ args.errmsg_len = errmsg_len;
nb_node->cbs.apply_finish(&args);
}
@@ -952,8 +1141,10 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
* Call the northbound configuration callback associated to a given
* configuration change.
*/
-static int nb_callback_configuration(const enum nb_event event,
- struct nb_config_change *change)
+static int nb_callback_configuration(struct nb_context *context,
+ const enum nb_event event,
+ struct nb_config_change *change,
+ char *errmsg, size_t errmsg_len)
{
enum nb_operation operation = change->cb.operation;
char xpath[XPATH_MAXLEN];
@@ -962,7 +1153,6 @@ static int nb_callback_configuration(const enum nb_event event,
union nb_resource *resource;
int ret = NB_ERR;
-
if (event == NB_EV_VALIDATE)
resource = NULL;
else
@@ -970,16 +1160,20 @@ static int nb_callback_configuration(const enum nb_event event,
switch (operation) {
case NB_OP_CREATE:
- ret = nb_callback_create(nb_node, event, dnode, resource);
+ ret = nb_callback_create(context, nb_node, event, dnode,
+ resource, errmsg, errmsg_len);
break;
case NB_OP_MODIFY:
- ret = nb_callback_modify(nb_node, event, dnode, resource);
+ ret = nb_callback_modify(context, nb_node, event, dnode,
+ resource, errmsg, errmsg_len);
break;
case NB_OP_DESTROY:
- ret = nb_callback_destroy(nb_node, event, dnode);
+ ret = nb_callback_destroy(context, nb_node, event, dnode,
+ errmsg, errmsg_len);
break;
case NB_OP_MOVE:
- ret = nb_callback_move(nb_node, event, dnode);
+ ret = nb_callback_move(context, nb_node, event, dnode, errmsg,
+ errmsg_len);
break;
default:
yang_dnode_get_path(dnode, xpath, sizeof(xpath));
@@ -1014,45 +1208,48 @@ static int nb_callback_configuration(const enum nb_event event,
break;
default:
flog_err(EC_LIB_DEVELOPMENT,
- "%s: unknown event (%u) [xpath %s]",
- __func__, event, xpath);
+ "%s: unknown event (%u) [xpath %s]", __func__,
+ event, xpath);
exit(1);
}
flog(priority, ref,
- "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]",
- __func__, nb_err_name(ret), nb_event_name(event),
+ "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]",
+ nb_err_name(ret), nb_event_name(event),
nb_operation_name(operation), xpath);
+ if (strlen(errmsg) > 0)
+ flog(priority, ref,
+ "error processing configuration change: %s",
+ errmsg);
}
return ret;
}
static struct nb_transaction *
-nb_transaction_new(struct nb_config *config, struct nb_config_cbs *changes,
- enum nb_client client, const void *user, const char *comment)
+nb_transaction_new(struct nb_context *context, struct nb_config *config,
+ struct nb_config_cbs *changes, const char *comment,
+ char *errmsg, size_t errmsg_len)
{
struct nb_transaction *transaction;
- if (nb_running_lock_check(client, user)) {
- flog_warn(
- EC_LIB_NB_TRANSACTION_CREATION_FAILED,
- "%s: running configuration is locked by another client",
- __func__);
+ if (nb_running_lock_check(context->client, context->user)) {
+ strlcpy(errmsg,
+ "running configuration is locked by another client",
+ errmsg_len);
return NULL;
}
if (transaction_in_progress) {
- flog_warn(
- EC_LIB_NB_TRANSACTION_CREATION_FAILED,
- "%s: error - there's already another transaction in progress",
- __func__);
+ strlcpy(errmsg,
+ "there's already another transaction in progress",
+ errmsg_len);
return NULL;
}
transaction_in_progress = true;
transaction = XCALLOC(MTYPE_TMP, sizeof(*transaction));
- transaction->client = client;
+ transaction->context = context;
if (comment)
strlcpy(transaction->comment, comment,
sizeof(transaction->comment));
@@ -1071,7 +1268,8 @@ static void nb_transaction_free(struct nb_transaction *transaction)
/* Process all configuration changes associated to a transaction. */
static int nb_transaction_process(enum nb_event event,
- struct nb_transaction *transaction)
+ struct nb_transaction *transaction,
+ char *errmsg, size_t errmsg_len)
{
struct nb_config_cb *cb;
@@ -1087,7 +1285,8 @@ static int nb_transaction_process(enum nb_event event,
break;
/* Call the appropriate callback. */
- ret = nb_callback_configuration(event, change);
+ ret = nb_callback_configuration(transaction->context, event,
+ change, errmsg, errmsg_len);
switch (event) {
case NB_EV_PREPARE:
if (ret != NB_OK)
@@ -1141,7 +1340,8 @@ nb_apply_finish_cb_find(struct nb_config_cbs *cbs,
}
/* Call the 'apply_finish' callbacks. */
-static void nb_transaction_apply_finish(struct nb_transaction *transaction)
+static void nb_transaction_apply_finish(struct nb_transaction *transaction,
+ char *errmsg, size_t errmsg_len)
{
struct nb_config_cbs cbs;
struct nb_config_cb *cb;
@@ -1200,7 +1400,8 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction)
/* Call the 'apply_finish' callbacks, sorted by their priorities. */
RB_FOREACH (cb, nb_config_cbs, &cbs)
- nb_callback_apply_finish(cb->nb_node, cb->dnode);
+ nb_callback_apply_finish(transaction->context, cb->nb_node,
+ cb->dnode, errmsg, errmsg_len);
/* Release memory. */
while (!RB_EMPTY(nb_config_cbs, &cbs)) {
@@ -1789,6 +1990,29 @@ void nb_running_set_entry(const struct lyd_node *dnode, void *entry)
config->entry = entry;
}
+void nb_running_move_tree(const char *xpath_from, const char *xpath_to)
+{
+ struct nb_config_entry *entry;
+ struct list *entries = hash_to_list(running_config_entries);
+ struct listnode *ln;
+
+ for (ALL_LIST_ELEMENTS_RO(entries, ln, entry)) {
+ if (!frrstr_startswith(entry->xpath, xpath_from))
+ continue;
+
+ hash_release(running_config_entries, entry);
+
+ char *newpath =
+ frrstr_replace(entry->xpath, xpath_from, xpath_to);
+ strlcpy(entry->xpath, newpath, sizeof(entry->xpath));
+ XFREE(MTYPE_TMP, newpath);
+
+ hash_get(running_config_entries, entry, hash_alloc_intern);
+ }
+
+ list_delete(&entries);
+}
+
static void *nb_running_unset_entry_helper(const struct lyd_node *dnode)
{
struct nb_config_entry *config, s;
@@ -1915,7 +2139,7 @@ const char *nb_err_name(enum nb_error error)
case NB_ERR_LOCKED:
return "resource is locked";
case NB_ERR_VALIDATION:
- return "validation error";
+ return "validation";
case NB_ERR_RESOURCE:
return "failed to allocate resource";
case NB_ERR_INCONSISTENCY:
diff --git a/lib/northbound.h b/lib/northbound.h
index 84382eeb60..bd57013f59 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -91,6 +91,9 @@ union nb_resource {
*/
struct nb_cb_create_args {
+ /* Context of the configuration transaction. */
+ struct nb_context *context;
+
/*
* The transaction phase. Refer to the documentation comments of
* nb_event for more details.
@@ -107,9 +110,18 @@ struct nb_cb_create_args {
* resource(s). It's set to NULL when the event is NB_EV_VALIDATE.
*/
union nb_resource *resource;
+
+ /* Buffer to store human-readable error message in case of error. */
+ char *errmsg;
+
+ /* Size of errmsg. */
+ size_t errmsg_len;
};
struct nb_cb_modify_args {
+ /* Context of the configuration transaction. */
+ struct nb_context *context;
+
/*
* The transaction phase. Refer to the documentation comments of
* nb_event for more details.
@@ -126,9 +138,18 @@ struct nb_cb_modify_args {
* resource(s). It's set to NULL when the event is NB_EV_VALIDATE.
*/
union nb_resource *resource;
+
+ /* Buffer to store human-readable error message in case of error. */
+ char *errmsg;
+
+ /* Size of errmsg. */
+ size_t errmsg_len;
};
struct nb_cb_destroy_args {
+ /* Context of the configuration transaction. */
+ struct nb_context *context;
+
/*
* The transaction phase. Refer to the documentation comments of
* nb_event for more details.
@@ -137,9 +158,18 @@ struct nb_cb_destroy_args {
/* libyang data node that is being deleted. */
const struct lyd_node *dnode;
+
+ /* Buffer to store human-readable error message in case of error. */
+ char *errmsg;
+
+ /* Size of errmsg. */
+ size_t errmsg_len;
};
struct nb_cb_move_args {
+ /* Context of the configuration transaction. */
+ struct nb_context *context;
+
/*
* The transaction phase. Refer to the documentation comments of
* nb_event for more details.
@@ -148,16 +178,40 @@ struct nb_cb_move_args {
/* libyang data node that is being moved. */
const struct lyd_node *dnode;
+
+ /* Buffer to store human-readable error message in case of error. */
+ char *errmsg;
+
+ /* Size of errmsg. */
+ size_t errmsg_len;
};
struct nb_cb_pre_validate_args {
+ /* Context of the configuration transaction. */
+ struct nb_context *context;
+
/* libyang data node associated with the 'pre_validate' callback. */
const struct lyd_node *dnode;
+
+ /* Buffer to store human-readable error message in case of error. */
+ char *errmsg;
+
+ /* Size of errmsg. */
+ size_t errmsg_len;
};
struct nb_cb_apply_finish_args {
+ /* Context of the configuration transaction. */
+ struct nb_context *context;
+
/* libyang data node associated with the 'apply_finish' callback. */
const struct lyd_node *dnode;
+
+ /* Buffer to store human-readable error message in case of error. */
+ char *errmsg;
+
+ /* Size of errmsg. */
+ size_t errmsg_len;
};
struct nb_cb_get_elem_args {
@@ -305,6 +359,10 @@ struct nb_callbacks {
* args
* Refer to the documentation comments of nb_cb_pre_validate_args for
* details.
+ *
+ * Returns:
+ * - NB_OK on success.
+ * - NB_ERR_VALIDATION when a validation error occurred.
*/
int (*pre_validate)(struct nb_cb_pre_validate_args *args);
@@ -538,6 +596,29 @@ enum nb_client {
NB_CLIENT_GRPC,
};
+/* Northbound context. */
+struct nb_context {
+ /* Northbound client. */
+ enum nb_client client;
+
+ /* Northbound user (can be NULL). */
+ const void *user;
+
+ /* Client-specific data. */
+#if 0
+ union {
+ struct {
+ } cli;
+ struct {
+ } confd;
+ struct {
+ } sysrepo;
+ struct {
+ } grpc;
+ } client_data;
+#endif
+};
+
/* Northbound configuration. */
struct nb_config {
struct lyd_node *dnode;
@@ -564,7 +645,7 @@ struct nb_config_change {
/* Northbound configuration transaction. */
struct nb_transaction {
- enum nb_client client;
+ struct nb_context *context;
char comment[80];
struct nb_config *config;
struct nb_config_cbs changes;
@@ -762,28 +843,36 @@ extern int nb_candidate_update(struct nb_config *candidate);
* WARNING: the candidate can be modified as part of the validation process
* (e.g. add default nodes).
*
+ * context
+ * Context of the northbound transaction.
+ *
* candidate
* Candidate configuration to validate.
*
+ * errmsg
+ * Buffer to store human-readable error message in case of error.
+ *
+ * errmsg_len
+ * Size of errmsg.
+ *
* Returns:
* NB_OK on success, NB_ERR_VALIDATION otherwise.
*/
-extern int nb_candidate_validate(struct nb_config *candidate);
+extern int nb_candidate_validate(struct nb_context *context,
+ struct nb_config *candidate, char *errmsg,
+ size_t errmsg_len);
/*
* Create a new configuration transaction but do not commit it yet. Only
* validate the candidate and prepare all resources required to apply the
* configuration changes.
*
+ * context
+ * Context of the northbound transaction.
+ *
* candidate
* Candidate configuration to commit.
*
- * client
- * Northbound client performing the commit.
- *
- * user
- * Northbound user performing the commit (can be NULL).
- *
* comment
* Optional comment describing the commit.
*
@@ -793,6 +882,12 @@ extern int nb_candidate_validate(struct nb_config *candidate);
* nb_candidate_commit_abort() or committed using
* nb_candidate_commit_apply().
*
+ * errmsg
+ * Buffer to store human-readable error message in case of error.
+ *
+ * errmsg_len
+ * Size of errmsg.
+ *
* Returns:
* - NB_OK on success.
* - NB_ERR_NO_CHANGES when the candidate is identical to the running
@@ -803,10 +898,11 @@ extern int nb_candidate_validate(struct nb_config *candidate);
* the candidate configuration.
* - NB_ERR for other errors.
*/
-extern int nb_candidate_commit_prepare(struct nb_config *candidate,
- enum nb_client client, const void *user,
+extern int nb_candidate_commit_prepare(struct nb_context *context,
+ struct nb_config *candidate,
const char *comment,
- struct nb_transaction **transaction);
+ struct nb_transaction **transaction,
+ char *errmsg, size_t errmsg_len);
/*
* Abort a previously created configuration transaction, releasing all resources
@@ -842,16 +938,13 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction,
* take into account the results of the preparation phase of multiple managed
* entities.
*
+ * context
+ * Context of the northbound transaction.
+ *
* candidate
* Candidate configuration to commit. It's preserved regardless if the commit
* operation fails or not.
*
- * client
- * Northbound client performing the commit.
- *
- * user
- * Northbound user performing the commit (can be NULL).
- *
* save_transaction
* Specify whether the transaction should be recorded in the transactions log
* or not.
@@ -862,6 +955,12 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction,
* transaction_id
* Optional output parameter providing the ID of the committed transaction.
*
+ * errmsg
+ * Buffer to store human-readable error message in case of error.
+ *
+ * errmsg_len
+ * Size of errmsg.
+ *
* Returns:
* - NB_OK on success.
* - NB_ERR_NO_CHANGES when the candidate is identical to the running
@@ -872,10 +971,11 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction,
* the candidate configuration.
* - NB_ERR for other errors.
*/
-extern int nb_candidate_commit(struct nb_config *candidate,
- enum nb_client client, const void *user,
+extern int nb_candidate_commit(struct nb_context *context,
+ struct nb_config *candidate,
bool save_transaction, const char *comment,
- uint32_t *transaction_id);
+ uint32_t *transaction_id, char *errmsg,
+ size_t errmsg_len);
/*
* Lock the running configuration.
@@ -993,6 +1093,23 @@ extern int nb_notification_send(const char *xpath, struct list *arguments);
extern void nb_running_set_entry(const struct lyd_node *dnode, void *entry);
/*
+ * Move an entire tree of user pointer nodes.
+ *
+ * Suppose we have xpath A/B/C/D, with user pointers associated to C and D. We
+ * need to move B to be under Z, so the new xpath is Z/B/C/D. Because user
+ * pointers are indexed with their absolute path, We need to move all user
+ * pointers at and below B to their new absolute paths; this function does
+ * that.
+ *
+ * xpath_from
+ * base xpath of tree to move (A/B)
+ *
+ * xpath_to
+ * base xpath of new location of tree (Z/B)
+ */
+extern void nb_running_move_tree(const char *xpath_from, const char *xpath_to);
+
+/*
* Unset the user pointer associated to a configuration node.
*
* This should be called by northbound 'destroy' callbacks in the NB_EV_APPLY
@@ -1044,8 +1161,8 @@ extern void *nb_running_unset_entry(const struct lyd_node *dnode);
* Returns:
* User pointer if found, NULL otherwise.
*/
-extern void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
- bool abort_if_not_found);
+extern void *nb_running_get_entry(const struct lyd_node *dnode,
+ const char *xpath, bool abort_if_not_found);
/*
* Return a human-readable string representing a northbound event.
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index d4467facaf..105fc83cef 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -46,23 +46,11 @@ struct debug nb_dbg_libyang = {0, "libyang debugging"};
struct nb_config *vty_shared_candidate_config;
static struct thread_master *master;
-static void vty_show_libyang_errors(struct vty *vty, struct ly_ctx *ly_ctx)
+static void vty_show_nb_errors(struct vty *vty, int error, const char *errmsg)
{
- struct ly_err_item *ei;
- const char *path;
-
- ei = ly_err_first(ly_ctx);
- if (!ei)
- return;
-
- for (; ei; ei = ei->next)
- vty_out(vty, "%s\n", ei->msg);
-
- path = ly_errpath(ly_ctx);
- if (path)
- vty_out(vty, "YANG path: %s\n", path);
-
- ly_err_clean(ly_ctx, NULL);
+ vty_out(vty, "Error type: %s\n", nb_err_name(error));
+ if (strlen(errmsg) > 0)
+ vty_out(vty, "Error description: %s\n", errmsg);
}
void nb_cli_enqueue_change(struct vty *vty, const char *xpath,
@@ -158,24 +146,31 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
}
if (error) {
+ char buf[BUFSIZ];
+
/*
* Failure to edit the candidate configuration should never
* happen in practice, unless there's a bug in the code. When
* that happens, log the error but otherwise ignore it.
*/
vty_out(vty, "%% Failed to edit configuration.\n\n");
- vty_show_libyang_errors(vty, ly_native_ctx);
+ vty_out(vty, "%s",
+ yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
}
/* Do an implicit "commit" when using the classic CLI mode. */
if (frr_get_cli_mode() == FRR_CLI_CLASSIC) {
- ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI,
- vty, false, NULL, NULL);
+ struct nb_context context = {};
+ char errmsg[BUFSIZ] = {0};
+
+ context.client = NB_CLIENT_CLI;
+ context.user = vty;
+ ret = nb_candidate_commit(&context, vty->candidate_config,
+ false, NULL, NULL, errmsg,
+ sizeof(errmsg));
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
- vty_out(vty, "%% Configuration failed: %s.\n\n",
- nb_err_name(ret));
- vty_out(vty,
- "Please check the logs for more details.\n");
+ vty_out(vty, "%% Configuration failed.\n\n");
+ vty_show_nb_errors(vty, ret, errmsg);
/* Regenerate candidate for consistency. */
nb_config_replace(vty->candidate_config, running_config,
@@ -217,20 +212,27 @@ void nb_cli_confirmed_commit_clean(struct vty *vty)
int nb_cli_confirmed_commit_rollback(struct vty *vty)
{
+ struct nb_context context = {};
uint32_t transaction_id;
+ char errmsg[BUFSIZ] = {0};
int ret;
/* Perform the rollback. */
+ context.client = NB_CLIENT_CLI;
+ context.user = vty;
ret = nb_candidate_commit(
- vty->confirmed_commit_rollback, NB_CLIENT_CLI, vty, true,
+ &context, vty->confirmed_commit_rollback, true,
"Rollback to previous configuration - confirmed commit has timed out",
- &transaction_id);
+ &transaction_id, errmsg, sizeof(errmsg));
if (ret == NB_OK)
vty_out(vty,
"Rollback performed successfully (Transaction ID #%u).\n",
transaction_id);
- else
- vty_out(vty, "Failed to rollback to previous configuration.\n");
+ else {
+ vty_out(vty,
+ "Failed to rollback to previous configuration.\n\n");
+ vty_show_nb_errors(vty, ret, errmsg);
+ }
return ret;
}
@@ -252,7 +254,9 @@ static int nb_cli_confirmed_commit_timeout(struct thread *thread)
static int nb_cli_commit(struct vty *vty, bool force,
unsigned int confirmed_timeout, char *comment)
{
+ struct nb_context context = {};
uint32_t transaction_id = 0;
+ char errmsg[BUFSIZ] = {0};
int ret;
/* Check if there's a pending confirmed commit. */
@@ -295,8 +299,11 @@ static int nb_cli_commit(struct vty *vty, bool force,
&vty->t_confirmed_commit_timeout);
}
- ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, vty,
- true, comment, &transaction_id);
+ context.client = NB_CLIENT_CLI;
+ context.user = vty;
+ ret = nb_candidate_commit(&context, vty->candidate_config, true,
+ comment, &transaction_id, errmsg,
+ sizeof(errmsg));
/* Map northbound return code to CLI return code. */
switch (ret) {
@@ -312,9 +319,8 @@ static int nb_cli_commit(struct vty *vty, bool force,
return CMD_SUCCESS;
default:
vty_out(vty,
- "%% Failed to commit candidate configuration: %s.\n\n",
- nb_err_name(ret));
- vty_out(vty, "Please check the logs for more details.\n");
+ "%% Failed to commit candidate configuration.\n\n");
+ vty_show_nb_errors(vty, ret, errmsg);
return CMD_WARNING;
}
}
@@ -328,6 +334,7 @@ static int nb_cli_candidate_load_file(struct vty *vty,
struct lyd_node *dnode;
struct ly_ctx *ly_ctx;
int ly_format;
+ char buf[BUFSIZ];
switch (format) {
case NB_CFG_FMT_CMDS:
@@ -350,7 +357,9 @@ static int nb_cli_candidate_load_file(struct vty *vty,
flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_path() failed",
__func__);
vty_out(vty, "%% Failed to load configuration:\n\n");
- vty_show_libyang_errors(vty, ly_ctx);
+ vty_out(vty, "%s",
+ yang_print_errors(ly_native_ctx, buf,
+ sizeof(buf)));
return CMD_WARNING;
}
if (translator
@@ -371,7 +380,8 @@ static int nb_cli_candidate_load_file(struct vty *vty,
!= NB_OK) {
vty_out(vty,
"%% Failed to merge the loaded configuration:\n\n");
- vty_show_libyang_errors(vty, ly_native_ctx);
+ vty_out(vty, "%s",
+ yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
return CMD_WARNING;
}
@@ -383,6 +393,7 @@ static int nb_cli_candidate_load_transaction(struct vty *vty,
bool replace)
{
struct nb_config *loaded_config;
+ char buf[BUFSIZ];
loaded_config = nb_db_transaction_load(transaction_id);
if (!loaded_config) {
@@ -397,7 +408,8 @@ static int nb_cli_candidate_load_transaction(struct vty *vty,
!= NB_OK) {
vty_out(vty,
"%% Failed to merge the loaded configuration:\n\n");
- vty_show_libyang_errors(vty, ly_native_ctx);
+ vty_out(vty, "%s",
+ yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
return CMD_WARNING;
}
@@ -690,13 +702,18 @@ DEFPY (config_commit_check,
"Commit changes into the running configuration\n"
"Check if the configuration changes are valid\n")
{
+ struct nb_context context = {};
+ char errmsg[BUFSIZ] = {0};
int ret;
- ret = nb_candidate_validate(vty->candidate_config);
+ context.client = NB_CLIENT_CLI;
+ context.user = vty;
+ ret = nb_candidate_validate(&context, vty->candidate_config, errmsg,
+ sizeof(errmsg));
if (ret != NB_OK) {
vty_out(vty,
"%% Failed to validate candidate configuration.\n\n");
- vty_show_libyang_errors(vty, ly_native_ctx);
+ vty_show_nb_errors(vty, ret, errmsg);
return CMD_WARNING;
}
@@ -1530,8 +1547,10 @@ DEFPY (show_yang_module_translator,
static int nb_cli_rollback_configuration(struct vty *vty,
uint32_t transaction_id)
{
+ struct nb_context context = {};
struct nb_config *candidate;
char comment[80];
+ char errmsg[BUFSIZ] = {0};
int ret;
candidate = nb_db_transaction_load(transaction_id);
@@ -1544,8 +1563,10 @@ static int nb_cli_rollback_configuration(struct vty *vty,
snprintf(comment, sizeof(comment), "Rollback to transaction %u",
transaction_id);
- ret = nb_candidate_commit(candidate, NB_CLIENT_CLI, vty, true, comment,
- NULL);
+ context.client = NB_CLIENT_CLI;
+ context.user = vty;
+ ret = nb_candidate_commit(&context, candidate, true, comment, NULL,
+ errmsg, sizeof(errmsg));
nb_config_free(candidate);
switch (ret) {
case NB_OK:
@@ -1558,7 +1579,7 @@ static int nb_cli_rollback_configuration(struct vty *vty,
return CMD_WARNING;
default:
vty_out(vty, "%% Rollback failed.\n\n");
- vty_out(vty, "Please check the logs for more details.\n");
+ vty_show_nb_errors(vty, ret, errmsg);
return CMD_WARNING;
}
}
diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c
index 2fc3c81cf2..a3aaf02f08 100644
--- a/lib/northbound_confd.c
+++ b/lib/northbound_confd.c
@@ -285,8 +285,10 @@ frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op,
static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen)
{
+ struct nb_context context = {};
struct nb_config *candidate;
struct cdb_iter_args iter_args;
+ char errmsg[BUFSIZ] = {0};
int ret;
candidate = nb_config_dup(running_config);
@@ -321,24 +323,21 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen)
* required to apply them.
*/
transaction = NULL;
- ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_CONFD, NULL,
- NULL, &transaction);
+ context.client = NB_CLIENT_CONFD;
+ ret = nb_candidate_commit_prepare(&context, candidate, NULL,
+ &transaction, errmsg, sizeof(errmsg));
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
enum confd_errcode errcode;
- const char *errmsg;
switch (ret) {
case NB_ERR_LOCKED:
errcode = CONFD_ERRCODE_IN_USE;
- errmsg = "Configuration is locked by another process";
break;
case NB_ERR_RESOURCE:
errcode = CONFD_ERRCODE_RESOURCE_DENIED;
- errmsg = "Failed do allocate resources";
break;
default:
- errcode = CONFD_ERRCODE_INTERNAL;
- errmsg = "Internal error";
+ errcode = CONFD_ERRCODE_APPLICATION;
break;
}
@@ -612,7 +611,7 @@ static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx,
confd_hkeypath_t *kp)
{
struct nb_node *nb_node;
- char xpath[BUFSIZ];
+ char xpath[XPATH_MAXLEN];
struct yang_data *data;
confd_value_t v;
const void *list_entry = NULL;
@@ -650,7 +649,7 @@ static int frr_confd_data_get_next(struct confd_trans_ctx *tctx,
confd_hkeypath_t *kp, long next)
{
struct nb_node *nb_node;
- char xpath[BUFSIZ];
+ char xpath[XPATH_MAXLEN];
struct yang_data *data;
const void *parent_list_entry, *nb_next;
confd_value_t v[LIST_MAXKEYS];
@@ -758,8 +757,8 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx,
{
struct nb_node *nb_node;
const struct lys_node *child;
- char xpath[BUFSIZ];
- char xpath_child[XPATH_MAXLEN];
+ char xpath[XPATH_MAXLEN];
+ char xpath_child[XPATH_MAXLEN * 2];
struct list *elements;
struct yang_data *data;
const void *list_entry;
@@ -832,7 +831,7 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx,
static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx,
confd_hkeypath_t *kp, long next)
{
- char xpath[BUFSIZ];
+ char xpath[XPATH_MAXLEN];
struct nb_node *nb_node;
struct list *elements;
const void *parent_list_entry;
@@ -916,7 +915,7 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx,
/* Loop through list child nodes. */
LY_TREE_FOR (nb_node->snode->child, child) {
struct nb_node *nb_node_child = child->priv;
- char xpath_child[XPATH_MAXLEN];
+ char xpath_child[XPATH_MAXLEN * 2];
confd_value_t *v;
if (nvalues > CONFD_MAX_CHILD_NODES)
@@ -1059,7 +1058,7 @@ static int frr_confd_action_execute(struct confd_user_info *uinfo,
struct xml_tag *name, confd_hkeypath_t *kp,
confd_tag_value_t *params, int nparams)
{
- char xpath[BUFSIZ];
+ char xpath[XPATH_MAXLEN];
struct nb_node *nb_node;
struct list *input;
struct list *output;
@@ -1091,7 +1090,7 @@ static int frr_confd_action_execute(struct confd_user_info *uinfo,
/* Process input nodes. */
for (int i = 0; i < nparams; i++) {
- char xpath_input[BUFSIZ];
+ char xpath_input[XPATH_MAXLEN * 2];
char value_str[YANG_VALUE_MAXLEN];
snprintf(xpath_input, sizeof(xpath_input), "%s/%s", xpath,
diff --git a/lib/northbound_db.c b/lib/northbound_db.c
index 598805b961..244e760b2b 100644
--- a/lib/northbound_db.c
+++ b/lib/northbound_db.c
@@ -86,7 +86,7 @@ int nb_db_transaction_save(const struct nb_transaction *transaction,
if (!ss)
goto exit;
- client_name = nb_client_name(transaction->client);
+ client_name = nb_client_name(transaction->context->client);
/* Always record configurations in the XML format. */
if (lyd_print_mem(&config_str, transaction->config->dnode, LYD_XML,
LYP_FORMAT | LYP_WITHSIBLINGS)
diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp
index 2962a977eb..83d7e0ce95 100644
--- a/lib/northbound_grpc.cpp
+++ b/lib/northbound_grpc.cpp
@@ -175,14 +175,13 @@ class NorthboundImpl
void HandleGetCapabilities(RpcState<frr::GetCapabilitiesRequest,
frr::GetCapabilitiesResponse> *tag)
{
- if (nb_dbg_client_grpc)
- zlog_debug("received RPC GetCapabilities()");
-
switch (tag->state) {
case CREATE:
REQUEST_RPC(GetCapabilities);
tag->state = PROCESS;
case PROCESS: {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC GetCapabilities()");
// Response: string frr_version = 1;
tag->response.set_frr_version(FRR_VERSION);
@@ -298,14 +297,14 @@ class NorthboundImpl
void HandleCreateCandidate(RpcState<frr::CreateCandidateRequest,
frr::CreateCandidateResponse> *tag)
{
- if (nb_dbg_client_grpc)
- zlog_debug("received RPC CreateCandidate()");
-
switch (tag->state) {
case CREATE:
REQUEST_RPC(CreateCandidate);
tag->state = PROCESS;
case PROCESS: {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC CreateCandidate()");
+
struct candidate *candidate = create_candidate();
if (!candidate) {
tag->responder.Finish(
@@ -672,15 +671,22 @@ class NorthboundImpl
// Execute the user request.
+ struct nb_context context = {};
+ context.client = NB_CLIENT_GRPC;
+ char errmsg[BUFSIZ] = {0};
+
switch (phase) {
case frr::CommitRequest::VALIDATE:
- ret = nb_candidate_validate(candidate->config);
+ ret = nb_candidate_validate(
+ &context, candidate->config, errmsg,
+ sizeof(errmsg));
break;
case frr::CommitRequest::PREPARE:
ret = nb_candidate_commit_prepare(
- candidate->config, NB_CLIENT_GRPC, NULL,
+ &context, candidate->config,
comment.c_str(),
- &candidate->transaction);
+ &candidate->transaction, errmsg,
+ sizeof(errmsg));
break;
case frr::CommitRequest::ABORT:
nb_candidate_commit_abort(
@@ -693,71 +699,51 @@ class NorthboundImpl
break;
case frr::CommitRequest::ALL:
ret = nb_candidate_commit(
- candidate->config, NB_CLIENT_GRPC, NULL,
- true, comment.c_str(), &transaction_id);
+ &context, candidate->config, true,
+ comment.c_str(), &transaction_id,
+ errmsg, sizeof(errmsg));
break;
}
- // Map northbound error codes to gRPC error codes.
+ // Map northbound error codes to gRPC status codes.
+ grpc::Status status;
switch (ret) {
+ case NB_OK:
+ status = grpc::Status::OK;
+ break;
case NB_ERR_NO_CHANGES:
- tag->responder.Finish(
- tag->response,
- grpc::Status(
- grpc::StatusCode::ABORTED,
- "No configuration changes detected"),
- tag);
- tag->state = FINISH;
- return;
+ status = grpc::Status(grpc::StatusCode::ABORTED,
+ errmsg);
+ break;
case NB_ERR_LOCKED:
- tag->responder.Finish(
- tag->response,
- grpc::Status(
- grpc::StatusCode::UNAVAILABLE,
- "There's already a transaction in progress"),
- tag);
- tag->state = FINISH;
- return;
+ status = grpc::Status(
+ grpc::StatusCode::UNAVAILABLE, errmsg);
+ break;
case NB_ERR_VALIDATION:
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::
- INVALID_ARGUMENT,
- "Validation error"),
- tag);
- tag->state = FINISH;
- return;
+ status = grpc::Status(
+ grpc::StatusCode::INVALID_ARGUMENT,
+ errmsg);
+ break;
case NB_ERR_RESOURCE:
- tag->responder.Finish(
- tag->response,
- grpc::Status(
- grpc::StatusCode::
- RESOURCE_EXHAUSTED,
- "Failed do allocate resources"),
- tag);
- tag->state = FINISH;
- return;
+ status = grpc::Status(
+ grpc::StatusCode::RESOURCE_EXHAUSTED,
+ errmsg);
+ break;
case NB_ERR:
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::INTERNAL,
- "Internal error"),
- tag);
- tag->state = FINISH;
- return;
default:
+ status = grpc::Status(
+ grpc::StatusCode::INTERNAL, errmsg);
break;
}
+ if (ret == NB_OK) {
+ // Response: uint32 transaction_id = 1;
+ if (transaction_id)
+ tag->response.set_transaction_id(
+ transaction_id);
+ }
- // Response: uint32 transaction_id = 1;
- if (transaction_id)
- tag->response.set_transaction_id(
- transaction_id);
-
- tag->responder.Finish(tag->response, grpc::Status::OK,
- tag);
+ tag->responder.Finish(tag->response, status, tag);
tag->state = FINISH;
-
break;
}
case FINISH:
@@ -769,9 +755,6 @@ class NorthboundImpl
HandleListTransactions(RpcState<frr::ListTransactionsRequest,
frr::ListTransactionsResponse> *tag)
{
- if (nb_dbg_client_grpc)
- zlog_debug("received RPC ListTransactions()");
-
switch (tag->state) {
case CREATE:
REQUEST_RPC_STREAMING(ListTransactions);
@@ -781,6 +764,9 @@ class NorthboundImpl
tag->context);
tag->state = PROCESS;
case PROCESS: {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC ListTransactions()");
+
auto list = static_cast<std::list<std::tuple<
int, std::string, std::string, std::string>> *>(
tag->context);
@@ -889,14 +875,14 @@ class NorthboundImpl
void HandleLockConfig(
RpcState<frr::LockConfigRequest, frr::LockConfigResponse> *tag)
{
- if (nb_dbg_client_grpc)
- zlog_debug("received RPC LockConfig()");
-
switch (tag->state) {
case CREATE:
REQUEST_RPC(LockConfig);
tag->state = PROCESS;
case PROCESS: {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC LockConfig()");
+
if (nb_running_lock(NB_CLIENT_GRPC, NULL)) {
tag->responder.Finish(
tag->response,
@@ -922,14 +908,14 @@ class NorthboundImpl
void HandleUnlockConfig(RpcState<frr::UnlockConfigRequest,
frr::UnlockConfigResponse> *tag)
{
- if (nb_dbg_client_grpc)
- zlog_debug("received RPC UnlockConfig()");
-
switch (tag->state) {
case CREATE:
REQUEST_RPC(UnlockConfig);
tag->state = PROCESS;
case PROCESS: {
+ if (nb_dbg_client_grpc)
+ zlog_debug("received RPC UnlockConfig()");
+
if (nb_running_unlock(NB_CLIENT_GRPC, NULL)) {
tag->responder.Finish(
tag->response,
diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c
index b94c939763..500203173c 100644
--- a/lib/northbound_sysrepo.c
+++ b/lib/northbound_sysrepo.c
@@ -245,7 +245,9 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session,
sr_change_oper_t sr_op;
sr_val_t *sr_old_val, *sr_new_val;
char xpath[XPATH_MAXLEN];
+ struct nb_context context = {};
struct nb_config *candidate;
+ char errmsg[BUFSIZ] = {0};
snprintf(xpath, sizeof(xpath), "/%s:*", module_name);
ret = sr_get_changes_iter(session, xpath, &it);
@@ -276,21 +278,33 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session,
}
transaction = NULL;
+ context.client = NB_CLIENT_SYSREPO;
if (startup_config) {
/*
* sysrepod sends the entire startup configuration using a
* single event (SR_EV_ENABLED). This means we need to perform
* the full two-phase commit protocol in one go here.
*/
- ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, NULL,
- true, NULL, NULL);
+ ret = nb_candidate_commit(&context, candidate, true, NULL, NULL,
+ errmsg, sizeof(errmsg));
+ if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
+ flog_warn(
+ EC_LIB_LIBSYSREPO,
+ "%s: failed to apply startup configuration: %s (%s)",
+ __func__, nb_err_name(ret), errmsg);
} else {
/*
* Validate the configuration changes and allocate all resources
* required to apply them.
*/
- ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_SYSREPO,
- NULL, NULL, &transaction);
+ ret = nb_candidate_commit_prepare(&context, candidate, NULL,
+ &transaction, errmsg,
+ sizeof(errmsg));
+ if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
+ flog_warn(
+ EC_LIB_LIBSYSREPO,
+ "%s: failed to prepare configuration transaction: %s (%s)",
+ __func__, nb_err_name(ret), errmsg);
}
/* Map northbound return code to sysrepo return code. */
diff --git a/lib/plist.c b/lib/plist.c
index d18d51618a..981e86e2ac 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -178,7 +178,7 @@ static void prefix_list_free(struct prefix_list *plist)
XFREE(MTYPE_PREFIX_LIST, plist);
}
-static struct prefix_list_entry *prefix_list_entry_new(void)
+struct prefix_list_entry *prefix_list_entry_new(void)
{
struct prefix_list_entry *new;
@@ -187,7 +187,7 @@ static struct prefix_list_entry *prefix_list_entry_new(void)
return new;
}
-static void prefix_list_entry_free(struct prefix_list_entry *pentry)
+void prefix_list_entry_free(struct prefix_list_entry *pentry)
{
XFREE(MTYPE_PREFIX_LIST_ENTRY, pentry);
}
@@ -279,7 +279,7 @@ static struct prefix_list *prefix_list_insert(afi_t afi, int orf,
return plist;
}
-static struct prefix_list *prefix_list_get(afi_t afi, int orf, const char *name)
+struct prefix_list *prefix_list_get(afi_t afi, int orf, const char *name)
{
struct prefix_list *plist;
@@ -294,7 +294,7 @@ static void prefix_list_trie_del(struct prefix_list *plist,
struct prefix_list_entry *pentry);
/* Delete prefix-list from prefix_list_master and free it. */
-static void prefix_list_delete(struct prefix_list *plist)
+void prefix_list_delete(struct prefix_list *plist)
{
struct prefix_list_list *list;
struct prefix_master *master;
@@ -381,7 +381,7 @@ void prefix_list_delete_hook(void (*func)(struct prefix_list *plist))
}
/* Calculate new sequential number. */
-static int64_t prefix_new_seq_get(struct prefix_list *plist)
+int64_t prefix_new_seq_get(struct prefix_list *plist)
{
int64_t maxseq;
int64_t newseq;
@@ -411,7 +411,7 @@ static struct prefix_list_entry *prefix_seq_check(struct prefix_list *plist,
return NULL;
}
-static struct prefix_list_entry *
+struct prefix_list_entry *
prefix_list_entry_lookup(struct prefix_list *plist, struct prefix *prefix,
enum prefix_list_type type, int64_t seq,
int le, int ge)
@@ -502,9 +502,9 @@ static void prefix_list_trie_del(struct prefix_list *plist,
}
-static void prefix_list_entry_delete(struct prefix_list *plist,
- struct prefix_list_entry *pentry,
- int update_list)
+void prefix_list_entry_delete(struct prefix_list *plist,
+ struct prefix_list_entry *pentry,
+ int update_list)
{
if (plist == NULL || pentry == NULL)
return;
@@ -646,6 +646,133 @@ static void prefix_list_entry_add(struct prefix_list *plist,
plist->master->recent = plist;
}
+/**
+ * Prefix list entry update start procedure:
+ * Remove entry from previosly installed master list, tries and notify
+ * observers.
+ *
+ * \param[in] ple prefix list entry.
+ */
+void prefix_list_entry_update_start(struct prefix_list_entry *ple)
+{
+ struct prefix_list *pl = ple->pl;
+
+ /* Not installed, nothing to do. */
+ if (!ple->installed)
+ return;
+
+ prefix_list_trie_del(pl, ple);
+
+ /* List manipulation: shameless copy from `prefix_list_entry_delete`. */
+ if (ple->prev)
+ ple->prev->next = ple->next;
+ else
+ pl->head = ple->next;
+ if (ple->next)
+ ple->next->prev = ple->prev;
+ else
+ pl->tail = ple->prev;
+
+ route_map_notify_pentry_dependencies(pl->name, ple,
+ RMAP_EVENT_PLIST_DELETED);
+ pl->count--;
+
+ route_map_notify_dependencies(pl->name, RMAP_EVENT_PLIST_DELETED);
+ if (pl->master->delete_hook)
+ (*pl->master->delete_hook)(pl);
+
+ if (pl->head || pl->tail || pl->desc)
+ pl->master->recent = pl;
+
+ ple->installed = false;
+}
+
+/**
+ * Prefix list entry update finish procedure:
+ * Add entry back master list, to the trie, notify observers and call master
+ * hook.
+ *
+ * \param[in] ple prefix list entry.
+ */
+void prefix_list_entry_update_finish(struct prefix_list_entry *ple)
+{
+ struct prefix_list *pl = ple->pl;
+ struct prefix_list_entry *point;
+
+ /* Already installed, nothing to do. */
+ if (ple->installed)
+ return;
+
+ /*
+ * Check if the entry is installable:
+ * We can only install entry if at least the prefix is provided (IPv4
+ * or IPv6).
+ */
+ if (ple->prefix.family != AF_INET && ple->prefix.family != AF_INET6)
+ return;
+
+ /* List manipulation: shameless copy from `prefix_list_entry_add`. */
+ if (pl->tail && ple->seq > pl->tail->seq)
+ point = NULL;
+ else {
+ /* Check insert point. */
+ for (point = pl->head; point; point = point->next)
+ if (point->seq >= ple->seq)
+ break;
+ }
+
+ /* In case of this is the first element of the list. */
+ ple->next = point;
+
+ if (point) {
+ if (point->prev)
+ point->prev->next = ple;
+ else
+ pl->head = ple;
+
+ ple->prev = point->prev;
+ point->prev = ple;
+ } else {
+ if (pl->tail)
+ pl->tail->next = ple;
+ else
+ pl->head = ple;
+
+ ple->prev = pl->tail;
+ pl->tail = ple;
+ }
+
+ prefix_list_trie_add(pl, ple);
+ pl->count++;
+
+ route_map_notify_pentry_dependencies(pl->name, ple,
+ RMAP_EVENT_PLIST_ADDED);
+
+ /* Run hook function. */
+ if (pl->master->add_hook)
+ (*pl->master->add_hook)(pl);
+
+ route_map_notify_dependencies(pl->name, RMAP_EVENT_PLIST_ADDED);
+ pl->master->recent = pl;
+
+ ple->installed = true;
+}
+
+/**
+ * Same as `prefix_list_entry_delete` but without `free()`ing the list if its
+ * empty.
+ *
+ * \param[in] ple prefix list entry.
+ */
+void prefix_list_entry_delete2(struct prefix_list_entry *ple)
+{
+ /* Does the boiler plate list removal and entry removal notification. */
+ prefix_list_entry_update_start(ple);
+
+ /* Effective `free()` memory. */
+ prefix_list_entry_free(ple);
+}
+
/* Return string of prefix_list_type. */
static const char *prefix_list_type_str(struct prefix_list_entry *pentry)
{
@@ -832,280 +959,6 @@ prefix_entry_dup_check(struct prefix_list *plist, struct prefix_list_entry *new)
return NULL;
}
-static int vty_invalid_prefix_range(struct vty *vty, const char *prefix)
-{
- vty_out(vty,
- "%% Invalid prefix range for %s, make sure: len < ge-value <= le-value\n",
- prefix);
- return CMD_WARNING_CONFIG_FAILED;
-}
-
-static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name,
- const char *seq, const char *typestr,
- const char *prefix, const char *ge,
- const char *le)
-{
- int ret;
- enum prefix_list_type type;
- struct prefix_list *plist;
- struct prefix_list_entry *pentry;
- struct prefix_list_entry *dup;
- struct prefix p, p_tmp;
- bool any = false;
- int64_t seqnum = -1;
- int lenum = 0;
- int genum = 0;
-
- if (name == NULL || prefix == NULL || typestr == NULL) {
- vty_out(vty, "%% Missing prefix or type\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* Sequential number. */
- if (seq)
- seqnum = (int64_t)atol(seq);
-
- /* ge and le number */
- if (ge)
- genum = atoi(ge);
- if (le)
- lenum = atoi(le);
-
- /* Check filter type. */
- if (strncmp("permit", typestr, 1) == 0)
- type = PREFIX_PERMIT;
- else if (strncmp("deny", typestr, 1) == 0)
- type = PREFIX_DENY;
- else {
- vty_out(vty, "%% prefix type must be permit or deny\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* "any" is special token for matching any IPv4 addresses. */
- switch (afi) {
- case AFI_IP:
- if (strncmp("any", prefix, strlen(prefix)) == 0) {
- ret = str2prefix_ipv4("0.0.0.0/0",
- (struct prefix_ipv4 *)&p);
- genum = 0;
- lenum = IPV4_MAX_BITLEN;
- any = true;
- } else
- ret = str2prefix_ipv4(prefix, (struct prefix_ipv4 *)&p);
-
- if (ret <= 0) {
- vty_out(vty, "%% Malformed IPv4 prefix\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* make a copy to verify prefix matches mask length */
- prefix_copy(&p_tmp, &p);
- apply_mask_ipv4((struct prefix_ipv4 *)&p_tmp);
-
- break;
- case AFI_IP6:
- if (strncmp("any", prefix, strlen(prefix)) == 0) {
- ret = str2prefix_ipv6("::/0", (struct prefix_ipv6 *)&p);
- genum = 0;
- lenum = IPV6_MAX_BITLEN;
- any = true;
- } else
- ret = str2prefix_ipv6(prefix, (struct prefix_ipv6 *)&p);
-
- if (ret <= 0) {
- vty_out(vty, "%% Malformed IPv6 prefix\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* make a copy to verify prefix matches mask length */
- prefix_copy(&p_tmp, &p);
- apply_mask_ipv6((struct prefix_ipv6 *)&p_tmp);
-
- break;
- case AFI_L2VPN:
- default:
- vty_out(vty, "%% Unrecognized AFI (%d)\n", afi);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* If prefix has bits not under the mask, adjust it to fit */
- if (!prefix_same(&p_tmp, &p)) {
- char buf[PREFIX2STR_BUFFER];
- char buf_tmp[PREFIX2STR_BUFFER];
- prefix2str(&p, buf, sizeof(buf));
- prefix2str(&p_tmp, buf_tmp, sizeof(buf_tmp));
- vty_out(vty,
- "%% Prefix-list %s prefix changed from %s to %s to match length\n",
- name, buf, buf_tmp);
- zlog_info(
- "Prefix-list %s prefix changed from %s to %s to match length",
- name, buf, buf_tmp);
- p = p_tmp;
- }
-
- /* ge and le check. */
- if (genum && (genum <= p.prefixlen))
- return vty_invalid_prefix_range(vty, prefix);
-
- if (lenum && (lenum < p.prefixlen))
- return vty_invalid_prefix_range(vty, prefix);
-
- if (lenum && (genum > lenum))
- return vty_invalid_prefix_range(vty, prefix);
-
- if (genum && (lenum == (afi == AFI_IP ? 32 : 128)))
- lenum = 0;
-
- /* Get prefix_list with name. */
- plist = prefix_list_get(afi, 0, name);
-
- /* Make prefix entry. */
- pentry = prefix_list_entry_make(&p, type, seqnum, lenum, genum, any);
-
- /* Check same policy. */
- dup = prefix_entry_dup_check(plist, pentry);
-
- if (dup) {
- prefix_list_entry_free(pentry);
- return CMD_SUCCESS;
- }
-
- /* Install new filter to the access_list. */
- prefix_list_entry_add(plist, pentry);
-
- return CMD_SUCCESS;
-}
-
-static int vty_prefix_list_uninstall(struct vty *vty, afi_t afi,
- const char *name, const char *seq,
- const char *typestr, const char *prefix,
- const char *ge, const char *le)
-{
- int ret;
- enum prefix_list_type type;
- struct prefix_list *plist;
- struct prefix_list_entry *pentry;
- struct prefix p;
- int64_t seqnum = -1;
- int lenum = 0;
- int genum = 0;
-
- /* Check prefix list name. */
- plist = prefix_list_lookup(afi, name);
- if (!plist) {
- vty_out(vty, "%% Can't find specified prefix-list\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* Only prefix-list name specified, delete the entire prefix-list. */
- if (seq == NULL && typestr == NULL && prefix == NULL && ge == NULL
- && le == NULL) {
- prefix_list_delete(plist);
- return CMD_SUCCESS;
- }
-
- /* Check sequence number. */
- if (seq)
- seqnum = (int64_t)atol(seq);
-
- /* Sequence number specified, but nothing else. */
- if (seq && typestr == NULL && prefix == NULL && ge == NULL
- && le == NULL) {
- pentry = prefix_seq_check(plist, seqnum);
-
- if (pentry == NULL) {
- vty_out(vty,
- "%% Can't find prefix-list %s with sequence number %" PRIu64 "\n",
- name, seqnum);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- prefix_list_entry_delete(plist, pentry, 1);
- return CMD_SUCCESS;
- }
-
- /* ge and le number */
- if (ge)
- genum = atoi(ge);
- if (le)
- lenum = atoi(le);
-
- /* We must have, at a minimum, both the type and prefix here */
- if ((typestr == NULL) || (prefix == NULL))
- return CMD_WARNING_CONFIG_FAILED;
-
- /* Check of filter type. */
- if (strncmp("permit", typestr, 1) == 0)
- type = PREFIX_PERMIT;
- else if (strncmp("deny", typestr, 1) == 0)
- type = PREFIX_DENY;
- else {
- vty_out(vty, "%% prefix type must be permit or deny\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* "any" is special token for matching any IPv4 addresses. */
- if (afi == AFI_IP) {
- if (strncmp("any", prefix, strlen(prefix)) == 0) {
- ret = str2prefix_ipv4("0.0.0.0/0",
- (struct prefix_ipv4 *)&p);
- genum = 0;
- lenum = IPV4_MAX_BITLEN;
- } else
- ret = str2prefix_ipv4(prefix, (struct prefix_ipv4 *)&p);
-
- if (ret <= 0) {
- vty_out(vty, "%% Malformed IPv4 prefix\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- } else if (afi == AFI_IP6) {
- if (strncmp("any", prefix, strlen(prefix)) == 0) {
- ret = str2prefix_ipv6("::/0", (struct prefix_ipv6 *)&p);
- genum = 0;
- lenum = IPV6_MAX_BITLEN;
- } else
- ret = str2prefix_ipv6(prefix, (struct prefix_ipv6 *)&p);
-
- if (ret <= 0) {
- vty_out(vty, "%% Malformed IPv6 prefix\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- }
-
- /* Lookup prefix entry. */
- pentry =
- prefix_list_entry_lookup(plist, &p, type, seqnum, lenum, genum);
-
- if (pentry == NULL) {
- vty_out(vty, "%% Can't find specified prefix-list\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* Install new filter to the access_list. */
- prefix_list_entry_delete(plist, pentry, 1);
-
- return CMD_SUCCESS;
-}
-
-static int vty_prefix_list_desc_unset(struct vty *vty, afi_t afi,
- const char *name)
-{
- struct prefix_list *plist;
-
- plist = prefix_list_lookup(afi, name);
- if (!plist) {
- vty_out(vty, "%% Can't find specified prefix-list\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- XFREE(MTYPE_TMP, plist->desc);
-
- if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL)
- prefix_list_delete(plist);
-
- return CMD_SUCCESS;
-}
-
enum display_type {
normal_display,
summary_display,
@@ -1349,72 +1202,6 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name,
#include "lib/plist_clippy.c"
#endif
-DEFPY (ip_prefix_list,
- ip_prefix_list_cmd,
- "ip prefix-list WORD [seq (1-4294967295)] <deny|permit>$action <any$dest|A.B.C.D/M$dest [{ge (0-32)|le (0-32)}]>",
- IP_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n"
- "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
- "Minimum prefix length to be matched\n"
- "Minimum prefix length\n"
- "Maximum prefix length to be matched\n"
- "Maximum prefix length\n")
-{
- return vty_prefix_list_install(vty, AFI_IP, prefix_list, seq_str,
- action, dest, ge_str, le_str);
-}
-
-DEFPY (no_ip_prefix_list,
- no_ip_prefix_list_cmd,
- "no ip prefix-list WORD [seq (1-4294967295)] <deny|permit>$action <any$dest|A.B.C.D/M$dest [{ge (0-32)|le (0-32)}]>",
- NO_STR
- IP_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n"
- "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
- "Minimum prefix length to be matched\n"
- "Minimum prefix length\n"
- "Maximum prefix length to be matched\n"
- "Maximum prefix length\n")
-{
- return vty_prefix_list_uninstall(vty, AFI_IP, prefix_list, seq_str,
- action, dest, ge_str, le_str);
-}
-
-DEFPY(no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd,
- "no ip prefix-list WORD seq (1-4294967295)",
- NO_STR IP_STR PREFIX_LIST_STR
- "Name of a prefix list\n"
- "sequence number of an entry\n"
- "Sequence number\n")
-{
- return vty_prefix_list_uninstall(vty, AFI_IP, prefix_list, seq_str,
- NULL, NULL, NULL, NULL);
-}
-
-DEFPY (no_ip_prefix_list_all,
- no_ip_prefix_list_all_cmd,
- "no ip prefix-list WORD",
- NO_STR
- IP_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n")
-{
- return vty_prefix_list_uninstall(vty, AFI_IP, prefix_list, NULL, NULL,
- NULL, NULL, NULL);
-}
-
DEFPY (ip_prefix_list_sequence_number,
ip_prefix_list_sequence_number_cmd,
"[no] ip prefix-list sequence-number",
@@ -1427,56 +1214,6 @@ DEFPY (ip_prefix_list_sequence_number,
return CMD_SUCCESS;
}
-DEFUN (ip_prefix_list_description,
- ip_prefix_list_description_cmd,
- "ip prefix-list WORD description LINE...",
- IP_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "Prefix-list specific description\n"
- "Up to 80 characters describing this prefix-list\n")
-{
- int idx_word = 2;
- int idx_line = 4;
- struct prefix_list *plist;
-
- plist = prefix_list_get(AFI_IP, 0, argv[idx_word]->arg);
-
- if (plist->desc) {
- XFREE(MTYPE_TMP, plist->desc);
- plist->desc = NULL;
- }
- plist->desc = argv_concat(argv, argc, idx_line);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_ip_prefix_list_description,
- no_ip_prefix_list_description_cmd,
- "no ip prefix-list WORD description",
- NO_STR
- IP_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "Prefix-list specific description\n")
-{
- int idx_word = 3;
- return vty_prefix_list_desc_unset(vty, AFI_IP, argv[idx_word]->arg);
-}
-
-/* ALIAS_FIXME */
-DEFUN (no_ip_prefix_list_description_comment,
- no_ip_prefix_list_description_comment_cmd,
- "no ip prefix-list WORD description LINE...",
- NO_STR
- IP_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "Prefix-list specific description\n"
- "Up to 80 characters describing this prefix-list\n")
-{
- return no_ip_prefix_list_description(self, vty, argc, argv);
-}
DEFPY (show_ip_prefix_list,
show_ip_prefix_list_cmd,
@@ -1554,61 +1291,6 @@ DEFPY (clear_ip_prefix_list,
return vty_clear_prefix_list(vty, AFI_IP, prefix_list, prefix_str);
}
-DEFPY (ipv6_prefix_list,
- ipv6_prefix_list_cmd,
- "ipv6 prefix-list WORD [seq (1-4294967295)] <deny|permit>$action <any$dest|X:X::X:X/M$dest [{ge (0-128)|le (0-128)}]>",
- IPV6_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any prefix match. Same as \"::0/0 le 128\"\n"
- "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
- "Maximum prefix length to be matched\n"
- "Maximum prefix length\n"
- "Minimum prefix length to be matched\n"
- "Minimum prefix length\n")
-{
- return vty_prefix_list_install(vty, AFI_IP6, prefix_list, seq_str,
- action, dest, ge_str, le_str);
-}
-
-DEFPY (no_ipv6_prefix_list,
- no_ipv6_prefix_list_cmd,
- "no ipv6 prefix-list WORD [seq (1-4294967295)] <deny|permit>$action <any$dest|X:X::X:X/M$dest [{ge (0-128)|le (0-128)}]>",
- NO_STR
- IPV6_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "sequence number of an entry\n"
- "Sequence number\n"
- "Specify packets to reject\n"
- "Specify packets to forward\n"
- "Any prefix match. Same as \"::0/0 le 128\"\n"
- "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
- "Maximum prefix length to be matched\n"
- "Maximum prefix length\n"
- "Minimum prefix length to be matched\n"
- "Minimum prefix length\n")
-{
- return vty_prefix_list_uninstall(vty, AFI_IP6, prefix_list, seq_str,
- action, dest, ge_str, le_str);
-}
-
-DEFPY (no_ipv6_prefix_list_all,
- no_ipv6_prefix_list_all_cmd,
- "no ipv6 prefix-list WORD",
- NO_STR
- IPV6_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n")
-{
- return vty_prefix_list_uninstall(vty, AFI_IP6, prefix_list, NULL, NULL,
- NULL, NULL, NULL);
-}
-
DEFPY (ipv6_prefix_list_sequence_number,
ipv6_prefix_list_sequence_number_cmd,
"[no] ipv6 prefix-list sequence-number",
@@ -1621,58 +1303,6 @@ DEFPY (ipv6_prefix_list_sequence_number,
return CMD_SUCCESS;
}
-DEFUN (ipv6_prefix_list_description,
- ipv6_prefix_list_description_cmd,
- "ipv6 prefix-list WORD description LINE...",
- IPV6_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "Prefix-list specific description\n"
- "Up to 80 characters describing this prefix-list\n")
-{
- int idx_word = 2;
- int iddx_line = 4;
- struct prefix_list *plist;
-
- plist = prefix_list_get(AFI_IP6, 0, argv[idx_word]->arg);
-
- if (plist->desc) {
- XFREE(MTYPE_TMP, plist->desc);
- plist->desc = NULL;
- }
- plist->desc = argv_concat(argv, argc, iddx_line);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_ipv6_prefix_list_description,
- no_ipv6_prefix_list_description_cmd,
- "no ipv6 prefix-list WORD description",
- NO_STR
- IPV6_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "Prefix-list specific description\n")
-{
- int idx_word = 3;
- return vty_prefix_list_desc_unset(vty, AFI_IP6, argv[idx_word]->arg);
-}
-
-/* ALIAS_FIXME */
-DEFUN (no_ipv6_prefix_list_description_comment,
- no_ipv6_prefix_list_description_comment_cmd,
- "no ipv6 prefix-list WORD description LINE...",
- NO_STR
- IPV6_STR
- PREFIX_LIST_STR
- "Name of a prefix list\n"
- "Prefix-list specific description\n"
- "Up to 80 characters describing this prefix-list\n")
-{
- return no_ipv6_prefix_list_description(self, vty, argc, argv);
-}
-
-
DEFPY (show_ipv6_prefix_list,
show_ipv6_prefix_list_cmd,
"show ipv6 prefix-list [WORD [seq$dseq (1-4294967295)$arg]]",
@@ -1749,104 +1379,6 @@ DEFPY (clear_ipv6_prefix_list,
return vty_clear_prefix_list(vty, AFI_IP6, prefix_list, prefix_str);
}
-/* Configuration write function. */
-static int config_write_prefix_afi(afi_t afi, struct vty *vty)
-{
- struct prefix_list *plist;
- struct prefix_list_entry *pentry;
- struct prefix_master *master;
- int write = 0;
-
- master = prefix_master_get(afi, 0);
- if (master == NULL)
- return 0;
-
- if (!master->seqnum) {
- vty_out(vty, "no ip%s prefix-list sequence-number\n",
- afi == AFI_IP ? "" : "v6");
- vty_out(vty, "!\n");
- }
-
- for (plist = master->num.head; plist; plist = plist->next) {
- if (plist->desc) {
- vty_out(vty, "ip%s prefix-list %s description %s\n",
- afi == AFI_IP ? "" : "v6", plist->name,
- plist->desc);
- write++;
- }
-
- for (pentry = plist->head; pentry; pentry = pentry->next) {
- vty_out(vty, "ip%s prefix-list %s ",
- afi == AFI_IP ? "" : "v6", plist->name);
-
- if (master->seqnum)
- vty_out(vty, "seq %" PRId64 " ", pentry->seq);
-
- vty_out(vty, "%s ", prefix_list_type_str(pentry));
-
- if (pentry->any)
- vty_out(vty, "any");
- else {
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out(vty, "%s/%d",
- inet_ntop(p->family, p->u.val, buf,
- BUFSIZ),
- p->prefixlen);
-
- if (pentry->ge)
- vty_out(vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out(vty, " le %d", pentry->le);
- }
- vty_out(vty, "\n");
- write++;
- }
- /* vty_out (vty, "!\n"); */
- }
-
- for (plist = master->str.head; plist; plist = plist->next) {
- if (plist->desc) {
- vty_out(vty, "ip%s prefix-list %s description %s\n",
- afi == AFI_IP ? "" : "v6", plist->name,
- plist->desc);
- write++;
- }
-
- for (pentry = plist->head; pentry; pentry = pentry->next) {
- vty_out(vty, "ip%s prefix-list %s ",
- afi == AFI_IP ? "" : "v6", plist->name);
-
- if (master->seqnum)
- vty_out(vty, "seq %" PRId64 " ", pentry->seq);
-
- vty_out(vty, "%s", prefix_list_type_str(pentry));
-
- if (pentry->any)
- vty_out(vty, " any");
- else {
- struct prefix *p = &pentry->prefix;
- char buf[BUFSIZ];
-
- vty_out(vty, " %s/%d",
- inet_ntop(p->family, p->u.val, buf,
- BUFSIZ),
- p->prefixlen);
-
- if (pentry->ge)
- vty_out(vty, " ge %d", pentry->ge);
- if (pentry->le)
- vty_out(vty, " le %d", pentry->le);
- }
- vty_out(vty, "\n");
- write++;
- }
- }
-
- return write;
-}
-
struct stream *prefix_bgp_orf_entry(struct stream *s, struct prefix_list *plist,
uint8_t init_flag, uint8_t permit_flag,
uint8_t deny_flag)
@@ -2042,21 +1574,13 @@ static void prefix_list_reset_afi(afi_t afi, int orf)
master->recent = NULL;
}
-
-static int config_write_prefix_ipv4(struct vty *vty);
/* Prefix-list node. */
static struct cmd_node prefix_node = {
.name = "ipv4 prefix list",
.node = PREFIX_NODE,
.prompt = "",
- .config_write = config_write_prefix_ipv4,
};
-static int config_write_prefix_ipv4(struct vty *vty)
-{
- return config_write_prefix_afi(AFI_IP, vty);
-}
-
static void plist_autocomplete_afi(afi_t afi, vector comps,
struct cmd_token *token)
{
@@ -2090,16 +1614,6 @@ static void prefix_list_init_ipv4(void)
{
install_node(&prefix_node);
- install_element(CONFIG_NODE, &ip_prefix_list_cmd);
- install_element(CONFIG_NODE, &no_ip_prefix_list_cmd);
- install_element(CONFIG_NODE, &no_ip_prefix_list_seq_cmd);
- install_element(CONFIG_NODE, &no_ip_prefix_list_all_cmd);
-
- install_element(CONFIG_NODE, &ip_prefix_list_description_cmd);
- install_element(CONFIG_NODE, &no_ip_prefix_list_description_cmd);
- install_element(CONFIG_NODE,
- &no_ip_prefix_list_description_comment_cmd);
-
install_element(CONFIG_NODE, &ip_prefix_list_sequence_number_cmd);
install_element(VIEW_NODE, &show_ip_prefix_list_cmd);
@@ -2110,33 +1624,17 @@ static void prefix_list_init_ipv4(void)
install_element(ENABLE_NODE, &clear_ip_prefix_list_cmd);
}
-static int config_write_prefix_ipv6(struct vty *vty);
/* Prefix-list node. */
static struct cmd_node prefix_ipv6_node = {
.name = "ipv6 prefix list",
.node = PREFIX_IPV6_NODE,
.prompt = "",
- .config_write = config_write_prefix_ipv6,
};
-static int config_write_prefix_ipv6(struct vty *vty)
-{
- return config_write_prefix_afi(AFI_IP6, vty);
-}
-
static void prefix_list_init_ipv6(void)
{
install_node(&prefix_ipv6_node);
- install_element(CONFIG_NODE, &ipv6_prefix_list_cmd);
- install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd);
- install_element(CONFIG_NODE, &no_ipv6_prefix_list_all_cmd);
-
- install_element(CONFIG_NODE, &ipv6_prefix_list_description_cmd);
- install_element(CONFIG_NODE, &no_ipv6_prefix_list_description_cmd);
- install_element(CONFIG_NODE,
- &no_ipv6_prefix_list_description_comment_cmd);
-
install_element(CONFIG_NODE, &ipv6_prefix_list_sequence_number_cmd);
install_element(VIEW_NODE, &show_ipv6_prefix_list_cmd);
diff --git a/lib/plist.h b/lib/plist.h
index ba2846d74a..57eb763a68 100644
--- a/lib/plist.h
+++ b/lib/plist.h
@@ -79,6 +79,20 @@ extern void prefix_bgp_orf_remove_all(afi_t, char *);
extern int prefix_bgp_show_prefix_list(struct vty *vty, afi_t afi, char *name,
bool use_json);
+extern struct prefix_list *prefix_list_get(afi_t afi, int orf,
+ const char *name);
+extern void prefix_list_delete(struct prefix_list *plist);
+extern int64_t prefix_new_seq_get(struct prefix_list *plist);
+
+extern struct prefix_list_entry *prefix_list_entry_new(void);
+extern void prefix_list_entry_delete(struct prefix_list *plist,
+ struct prefix_list_entry *pentry,
+ int update_list);
+extern struct prefix_list_entry *
+prefix_list_entry_lookup(struct prefix_list *plist, struct prefix *prefix,
+ enum prefix_list_type type, int64_t seq, int le,
+ int ge);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/plist_int.h b/lib/plist_int.h
index ec8bbe1315..5e0beabbc6 100644
--- a/lib/plist_int.h
+++ b/lib/plist_int.h
@@ -65,13 +65,23 @@ struct prefix_list_entry {
unsigned long refcnt;
unsigned long hitcnt;
+ struct prefix_list *pl;
+
struct prefix_list_entry *next;
struct prefix_list_entry *prev;
/* up the chain for best match search */
struct prefix_list_entry *next_best;
+
+ /* Flag to track trie/list installation status. */
+ bool installed;
};
+extern void prefix_list_entry_free(struct prefix_list_entry *pentry);
+extern void prefix_list_entry_delete2(struct prefix_list_entry *ple);
+extern void prefix_list_entry_update_start(struct prefix_list_entry *ple);
+extern void prefix_list_entry_update_finish(struct prefix_list_entry *ple);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/routemap.c b/lib/routemap.c
index 210512212d..3d69a3495a 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -2747,7 +2747,12 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name,
memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data));
tmp_dep_data.rname = rname;
dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data);
- dep_data->refcnt--;
+
+ if (!dep_data)
+ goto out;
+
+ if (dep_data->refcnt)
+ dep_data->refcnt--;
if (!dep_data->refcnt) {
ret_dep_data = hash_release(dep->dep_rmap_hash,
diff --git a/lib/subdir.am b/lib/subdir.am
index b2f3e7c5de..57b2cea832 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -22,6 +22,8 @@ lib_libfrr_la_SOURCES = \
lib/distribute.c \
lib/ferr.c \
lib/filter.c \
+ lib/filter_cli.c \
+ lib/filter_nb.c \
lib/frrcu.c \
lib/frrlua.c \
lib/frr_pthread.c \
@@ -127,6 +129,7 @@ nodist_lib_libfrr_la_SOURCES = \
vtysh_scan += \
lib/distribute.c \
lib/filter.c \
+ lib/filter_cli.c \
lib/if.c \
lib/if_rmap.c \
lib/keychain.c \
@@ -148,6 +151,7 @@ endif
clippy_scan += \
lib/if.c \
+ lib/filter_cli.c \
lib/log_vty.c \
lib/nexthop_group.c \
lib/northbound_cli.c \
diff --git a/lib/vrf.c b/lib/vrf.c
index 9df5d19516..fb64589287 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -1082,8 +1082,8 @@ static int lib_vrf_destroy(struct nb_cb_destroy_args *args)
case NB_EV_VALIDATE:
vrfp = nb_running_get_entry(args->dnode, NULL, true);
if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) {
- zlog_debug("%s Only inactive VRFs can be deleted",
- __func__);
+ snprintf(args->errmsg, args->errmsg_len,
+ "Only inactive VRFs can be deleted");
return NB_ERR_VALIDATION;
}
break;
diff --git a/lib/vty.c b/lib/vty.c
index 784f9cf2ac..ffef05e4dc 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -2349,12 +2349,18 @@ static void vty_read_file(struct nb_config *config, FILE *confp)
* reading the configuration file.
*/
if (config == NULL) {
- ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI,
- vty, true, "Read configuration file",
- NULL);
+ struct nb_context context = {};
+ char errmsg[BUFSIZ] = {0};
+
+ context.client = NB_CLIENT_CLI;
+ context.user = vty;
+ ret = nb_candidate_commit(&context, vty->candidate_config, true,
+ "Read configuration file", NULL,
+ errmsg, sizeof(errmsg));
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
- zlog_err("%s: failed to read configuration file.",
- __func__);
+ zlog_err(
+ "%s: failed to read configuration file: %s (%s)",
+ __func__, nb_err_name(ret), errmsg);
}
vty_close(vty);
diff --git a/lib/vty.h b/lib/vty.h
index 694aac3944..5d5676199b 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -43,7 +43,7 @@ extern "C" {
#define VTY_MAXHIST 20
#define VTY_MAXDEPTH 8
-#define VTY_MAXCFGCHANGES 8
+#define VTY_MAXCFGCHANGES 16
struct vty_error {
char error_buf[VTY_BUFSIZ];
diff --git a/lib/yang.c b/lib/yang.c
index c80bf20306..0714ddf7bb 100644
--- a/lib/yang.c
+++ b/lib/yang.c
@@ -448,6 +448,32 @@ bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath_fmt, ...)
return found;
}
+void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg,
+ const struct lyd_node *dnode, const char *xpath_fmt,
+ ...)
+{
+ va_list ap;
+ char xpath[XPATH_MAXLEN];
+ struct ly_set *set;
+
+ va_start(ap, xpath_fmt);
+ vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
+ va_end(ap);
+
+ set = lyd_find_path(dnode, xpath);
+ assert(set);
+ for (unsigned int i = 0; i < set->number; i++) {
+ int ret;
+
+ dnode = set->set.d[i];
+ ret = (*cb)(dnode, arg);
+ if (ret == YANG_ITER_STOP)
+ return;
+ }
+
+ ly_set_free(set);
+}
+
bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath_fmt,
...)
{
@@ -621,6 +647,34 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
zlog(priority, "libyang: %s", msg);
}
+const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len)
+{
+ struct ly_err_item *ei;
+ const char *path;
+
+ ei = ly_err_first(ly_ctx);
+ if (!ei)
+ return "";
+
+ strlcpy(buf, "YANG error(s):\n", buf_len);
+ for (; ei; ei = ei->next) {
+ strlcat(buf, " ", buf_len);
+ strlcat(buf, ei->msg, buf_len);
+ strlcat(buf, "\n", buf_len);
+ }
+
+ path = ly_errpath(ly_ctx);
+ if (path) {
+ strlcat(buf, " YANG path: ", buf_len);
+ strlcat(buf, path, buf_len);
+ strlcat(buf, "\n", buf_len);
+ }
+
+ ly_err_clean(ly_ctx, NULL);
+
+ return buf;
+}
+
void yang_debugging_set(bool enable)
{
if (enable) {
diff --git a/lib/yang.h b/lib/yang.h
index 126521707b..85ef0d758c 100644
--- a/lib/yang.h
+++ b/lib/yang.h
@@ -107,6 +107,9 @@ enum yang_iter_flags {
/* Callback used by the yang_snodes_iterate_*() family of functions. */
typedef int (*yang_iterate_cb)(const struct lys_node *snode, void *arg);
+/* Callback used by the yang_dnode_iterate() function. */
+typedef int (*yang_dnode_iter_cb)(const struct lyd_node *dnode, void *arg);
+
/* Return values of the 'yang_iterate_cb' callback. */
#define YANG_ITER_CONTINUE 0
#define YANG_ITER_STOP -1
@@ -358,6 +361,25 @@ extern bool yang_dnode_exists(const struct lyd_node *dnode,
const char *xpath_fmt, ...);
/*
+ * Iterate over all libyang data nodes that satisfy an XPath query.
+ *
+ * cb
+ * Function to call with each data node.
+ *
+ * arg
+ * Arbitrary argument passed as the second parameter in each call to 'cb'.
+ *
+ * dnode
+ * Base libyang data node to operate on.
+ *
+ * xpath_fmt
+ * XPath expression (absolute or relative).
+ */
+void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg,
+ const struct lyd_node *dnode, const char *xpath_fmt,
+ ...);
+
+/*
* Check if the libyang data node contains a default value. Non-presence
* containers are assumed to always contain a default value.
*
@@ -497,6 +519,24 @@ extern struct ly_ctx *yang_ctx_new_setup(bool embedded_modules);
extern void yang_debugging_set(bool enable);
/*
+ * Print libyang error messages into the provided buffer.
+ *
+ * ly_ctx
+ * libyang context to operate on.
+ *
+ * buf
+ * Buffer to store the libyang error messages.
+ *
+ * buf_len
+ * Size of buf.
+ *
+ * Returns:
+ * The provided buffer.
+ */
+extern const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf,
+ size_t buf_len);
+
+/*
* Initialize the YANG subsystem. Should be called only once during the
* daemon initialization process.
*
diff --git a/lib/zclient.c b/lib/zclient.c
index 9b1ff7237f..aa5fa082a1 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -3121,6 +3121,7 @@ int zclient_send_opaque(struct zclient *zclient, uint32_t type,
{
int ret;
struct stream *s;
+ uint16_t flags = 0;
/* Check buffer size */
if (STREAM_SIZE(zclient->obuf) <
@@ -3132,8 +3133,9 @@ int zclient_send_opaque(struct zclient *zclient, uint32_t type,
zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
- /* Send sub-type */
+ /* Send sub-type and flags */
stream_putl(s, type);
+ stream_putw(s, flags);
/* Send opaque data */
stream_write(s, data, datasize);
@@ -3147,6 +3149,77 @@ int zclient_send_opaque(struct zclient *zclient, uint32_t type,
}
/*
+ * Send an OPAQUE message to a specific zclient. The contents are opaque
+ * to zebra.
+ */
+int zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type,
+ uint8_t proto, uint16_t instance,
+ uint32_t session_id, const uint8_t *data,
+ size_t datasize)
+{
+ int ret;
+ struct stream *s;
+ uint16_t flags = 0;
+
+ /* Check buffer size */
+ if (STREAM_SIZE(zclient->obuf) <
+ (ZEBRA_HEADER_SIZE + sizeof(struct zapi_opaque_msg) + datasize))
+ return -1;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
+
+ /* Send sub-type and flags */
+ SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST);
+ stream_putl(s, type);
+ stream_putw(s, flags);
+
+ /* Send destination client info */
+ stream_putc(s, proto);
+ stream_putw(s, instance);
+ stream_putl(s, session_id);
+
+ /* Send opaque data */
+ stream_write(s, data, datasize);
+
+ /* Put length into the header at the start of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ ret = zclient_send_message(zclient);
+
+ return ret;
+}
+
+/*
+ * Decode incoming opaque message into info struct
+ */
+int zclient_opaque_decode(struct stream *s, struct zapi_opaque_msg *info)
+{
+ memset(info, 0, sizeof(*info));
+
+ /* Decode subtype and flags */
+ STREAM_GETL(s, info->type);
+ STREAM_GETW(s, info->flags);
+
+ /* Decode unicast client info if present */
+ if (CHECK_FLAG(info->flags, ZAPI_OPAQUE_FLAG_UNICAST)) {
+ STREAM_GETC(s, info->proto);
+ STREAM_GETW(s, info->instance);
+ STREAM_GETL(s, info->session_id);
+ }
+
+ info->len = STREAM_READABLE(s);
+
+ return 0;
+
+stream_failure:
+
+ return -1;
+}
+
+/*
* Send a registration request for opaque messages with a specified subtype.
*/
int zclient_register_opaque(struct zclient *zclient, uint32_t type)
@@ -3204,9 +3277,8 @@ int zclient_unregister_opaque(struct zclient *zclient, uint32_t type)
return ret;
}
-/* Utility to parse opaque registration info */
-int zapi_parse_opaque_reg(struct stream *s,
- struct zapi_opaque_reg_info *info)
+/* Utility to decode opaque registration info */
+int zapi_opaque_reg_decode(struct stream *s, struct zapi_opaque_reg_info *info)
{
STREAM_GETL(s, info->type);
STREAM_GETC(s, info->proto);
diff --git a/lib/zclient.h b/lib/zclient.h
index d99b6ddf8f..3ded2f55d7 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -457,6 +457,7 @@ struct zapi_route {
*/
#define ZEBRA_FLAG_RR_USE_DISTANCE 0x40
+ /* The older XXX_MESSAGE flags live here */
uint8_t message;
/*
@@ -849,6 +850,25 @@ extern void zclient_send_mlag_data(struct zclient *client,
int zclient_send_opaque(struct zclient *zclient, uint32_t type,
const uint8_t *data, size_t datasize);
+int zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type,
+ uint8_t proto, uint16_t instance,
+ uint32_t session_id, const uint8_t *data,
+ size_t datasize);
+
+/* Struct representing the decoded opaque header info */
+struct zapi_opaque_msg {
+ uint32_t type; /* Subtype */
+ uint16_t len; /* len after zapi header and this info */
+ uint16_t flags;
+
+ /* Client-specific info - *if* UNICAST flag is set */
+ uint8_t proto;
+ uint16_t instance;
+ uint32_t session_id;
+};
+
+#define ZAPI_OPAQUE_FLAG_UNICAST 0x01
+
/* Simple struct to convey registration/unreg requests */
struct zapi_opaque_reg_info {
/* Message subtype */
@@ -860,10 +880,13 @@ struct zapi_opaque_reg_info {
uint32_t session_id;
};
+/* Decode incoming opaque */
+int zclient_opaque_decode(struct stream *msg, struct zapi_opaque_msg *info);
+
int zclient_register_opaque(struct zclient *zclient, uint32_t type);
int zclient_unregister_opaque(struct zclient *zclient, uint32_t type);
-int zapi_parse_opaque_reg(struct stream *msg,
- struct zapi_opaque_reg_info *info);
+int zapi_opaque_reg_decode(struct stream *msg,
+ struct zapi_opaque_reg_info *info);
/*
* Registry of opaque message types. Please do not reuse an in-use
diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c
index b58fe776ab..9fc13761c8 100644
--- a/nhrpd/nhrp_main.c
+++ b/nhrpd/nhrp_main.c
@@ -23,6 +23,7 @@
#include "memory.h"
#include "command.h"
#include "libfrr.h"
+#include "filter.h"
#include "nhrpd.h"
#include "netlink.h"
@@ -116,6 +117,7 @@ static struct quagga_signal_t sighandlers[] = {
};
static const struct frr_yang_module_info *const nhrpd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
};
diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c
index 4dbe5ca321..8ae5fdcf06 100644
--- a/ospf6d/ospf6_main.c
+++ b/ospf6d/ospf6_main.c
@@ -166,6 +166,7 @@ struct quagga_signal_t ospf6_signals[] = {
};
static const struct frr_yang_module_info *const ospf6d_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_route_map_info,
&frr_vrf_info,
diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c
index 3682b4cdd3..1480b0e391 100644
--- a/ospfd/ospf_asbr.c
+++ b/ospfd/ospf_asbr.c
@@ -213,25 +213,35 @@ struct ospf_lsa *ospf_external_info_find_lsa(struct ospf *ospf,
struct as_external_lsa *al;
struct in_addr mask, id;
- lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA,
- p->prefix, ospf->router_id);
-
- if (!lsa)
- return NULL;
-
- al = (struct as_external_lsa *)lsa->data;
+ /* Fisrt search the lsdb with address specifc LSID
+ * where all the host bits are set, if there a matched
+ * LSA, return.
+ * Ex: For route 10.0.0.0/16, LSID is 10.0.255.255
+ * If no lsa with above LSID, use received address as
+ * LSID and check if any LSA in LSDB.
+ * If LSA found, check if the mask is same b/w the matched
+ * LSA and received prefix, if same then it is the LSA for
+ * this prefix.
+ * Ex: For route 10.0.0.0/16, LSID is 10.0.0.0
+ */
masklen2ip(p->prefixlen, &mask);
+ id.s_addr = p->prefix.s_addr | (~mask.s_addr);
+ lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, id,
+ ospf->router_id);
+ if (lsa)
+ return lsa;
- if (mask.s_addr != al->mask.s_addr) {
- id.s_addr = p->prefix.s_addr | (~mask.s_addr);
- lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA,
- id, ospf->router_id);
- if (!lsa)
- return NULL;
+ lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA,
+ p->prefix, ospf->router_id);
+
+ if (lsa) {
+ al = (struct as_external_lsa *)lsa->data;
+ if (mask.s_addr == al->mask.s_addr)
+ return lsa;
}
- return lsa;
+ return NULL;
}
diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c
index 1543e2015d..86088a7137 100644
--- a/ospfd/ospf_ext.c
+++ b/ospfd/ospf_ext.c
@@ -443,6 +443,32 @@ static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif)
exti->rmt_itf_addr.value = rmtif;
}
+/* Delete Extended LSA */
+static void ospf_extended_lsa_delete(struct ext_itf *exti)
+{
+
+ /* Process only Active Extended Prefix/Link LSA */
+ if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))
+ return;
+
+ osr_debug("EXT (%s): Disable %s%s%s-SID on interface %s", __func__,
+ exti->stype == PREF_SID ? "Prefix" : "",
+ exti->stype == ADJ_SID ? "Adjacency" : "",
+ exti->stype == LAN_ADJ_SID ? "LAN-Adjacency" : "",
+ exti->ifp->name);
+
+ /* Flush LSA if already engaged */
+ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+ ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA);
+ UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+ }
+
+ /* De-activate this Extended Prefix/Link and remove corresponding
+ * Segment-Routing Prefix-SID or (LAN)-ADJ-SID */
+ exti->flags = EXT_LPFLG_LSA_INACTIVE;
+ ospf_sr_ext_itf_delete(exti);
+}
+
/*
* Update Extended prefix SID index for Loopback interface type
*
@@ -465,12 +491,8 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index,
return rc;
if (p != NULL) {
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(
- "EXT (%s): Schedule new prefix %s/%u with "
- "index %u on interface %s",
- __func__, inet_ntoa(p->prefix), p->prefixlen,
- index, ifp->name);
+ osr_debug("EXT (%s): Schedule new prefix %pFX with index %u "
+ "on interface %s", __func__, p, index, ifp->name);
/* Set first Extended Prefix then the Prefix SID information */
set_ext_prefix(exti, OSPF_PATH_INTRA_AREA, EXT_TLV_PREF_NFLG,
@@ -488,9 +510,8 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index,
exti, REORIGINATE_THIS_LSA);
}
} else {
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("EXT (%s): Remove prefix for interface %s",
- __func__, ifp->name);
+ osr_debug("EXT (%s): Remove prefix for interface %s", __func__,
+ ifp->name);
if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
ospf_ext_pref_lsa_schedule(exti, FLUSH_THIS_LSA);
@@ -513,15 +534,15 @@ void ospf_ext_update_sr(bool enable)
struct listnode *node;
struct ext_itf *exti;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("EXT (%s): %s Extended LSAs for Segment Routing ",
- __func__, enable ? "Enable" : "Disable");
+ osr_debug("EXT (%s): %s Extended LSAs for Segment Routing ", __func__,
+ enable ? "Enable" : "Disable");
if (enable) {
OspfEXT.enabled = true;
/* Refresh LSAs if already engaged or originate */
for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
+ /* Skip inactive Extended Link */
if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))
continue;
@@ -532,17 +553,15 @@ void ospf_ext_update_sr(bool enable)
REORIGINATE_THIS_LSA);
}
} else {
- /* Start by Flushing engaged LSAs */
- for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
- if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
- ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA);
- exti->flags = EXT_LPFLG_LSA_INACTIVE;
- }
+ /* Start by Removing Extended LSA */
+ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+ ospf_extended_lsa_delete(exti);
/* And then disable Extended Link/Prefix */
OspfEXT.enabled = false;
}
}
+
/*
* -----------------------------------------------------------------------
* Followings are callback functions against generic Opaque-LSAs handling
@@ -580,10 +599,11 @@ static int ospf_ext_link_del_if(struct interface *ifp)
exti = lookup_ext_by_ifp(ifp);
if (exti != NULL) {
- struct list *iflist = OspfEXT.iflist;
+ /* Flush LSA and remove Adjacency SID */
+ ospf_extended_lsa_delete(exti);
/* Dequeue listnode entry from the list. */
- listnode_delete(iflist, exti);
+ listnode_delete(OspfEXT.iflist, exti);
XFREE(MTYPE_OSPF_EXT_PARAMS, exti);
@@ -616,6 +636,7 @@ static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status)
/* Reset Extended information if ospf interface goes Down */
if (oi->state == ISM_Down) {
+ ospf_extended_lsa_delete(exti);
exti->area = NULL;
exti->flags = EXT_LPFLG_LSA_INACTIVE;
return;
@@ -629,12 +650,12 @@ static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status)
exti->instance = get_ext_pref_instance_value();
exti->area = oi->area;
- zlog_debug("EXT (%s): Set Prefix SID to interface %s ",
- __func__, oi->ifp->name);
+ osr_debug("EXT (%s): Set Prefix SID to interface %s ",
+ __func__, oi->ifp->name);
/* Complete SRDB if the interface belongs to a Prefix */
if (OspfEXT.enabled)
- ospf_sr_update_prefix(oi->ifp, oi->address);
+ ospf_sr_update_local_prefix(oi->ifp, oi->address);
} else {
/* Determine if interface is related to Adj. or LAN Adj. SID */
if (oi->state == ISM_DR)
@@ -650,9 +671,9 @@ static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status)
* Note: Adjacency SID information are completed when ospf
* adjacency become up see ospf_ext_link_nsm_change()
*/
- zlog_debug("EXT (%s): Set %sAdjacency SID for interface %s ",
- __func__, exti->stype == ADJ_SID ? "" : "LAN-",
- oi->ifp->name);
+ osr_debug("EXT (%s): Set %sAdjacency SID for interface %s ",
+ __func__, exti->stype == ADJ_SID ? "" : "LAN-",
+ oi->ifp->name);
}
}
@@ -666,8 +687,8 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
struct ext_itf *exti;
uint32_t label;
- /* Process Neighbor only when its state is NSM Full */
- if (nbr->state != NSM_Full)
+ /* Process Link only when neighbor old or new state is NSM Full */
+ if (nbr->state != NSM_Full && old_status != NSM_Full)
return;
/* Get interface information for Segment Routing */
@@ -679,6 +700,23 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
return;
}
+ /* Check that we have a valid area and ospf context */
+ if (oi->area == NULL || oi->area->ospf == NULL) {
+ flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED,
+ "EXT (%s): Cannot refer to OSPF from OI(%s)",
+ __func__, IF_NAME(oi));
+ return;
+ }
+
+ /* Remove Extended Link if Neighbor State goes Down or Deleted */
+ if (nbr->state == NSM_Down || nbr->state == NSM_Deleted) {
+ ospf_extended_lsa_delete(exti);
+ return;
+ }
+
+ /* Keep Area information in combination with SR info. */
+ exti->area = oi->area;
+
/* Process only Adjacency/LAN SID */
if (exti->stype == PREF_SID)
return;
@@ -742,11 +780,9 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
return;
}
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(
- "EXT (%s): Complete %sAdjacency SID for interface %s ",
- __func__, exti->stype == ADJ_SID ? "" : "LAN-",
- oi->ifp->name);
+ osr_debug("EXT (%s): Complete %sAdjacency SID for interface %s ",
+ __func__, exti->stype == ADJ_SID ? "" : "LAN-",
+ oi->ifp->name);
/* flood this links params if everything is ok */
SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
@@ -756,6 +792,9 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
else
ospf_ext_link_lsa_schedule(exti, REORIGINATE_THIS_LSA);
}
+
+ /* Finally install (LAN)Adjacency-SID in the SRDB */
+ ospf_sr_ext_itf_add(exti);
}
/* Callbacks to handle Extended Link Segment Routing LSA information */
@@ -778,6 +817,10 @@ static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa)
!= OPAQUE_TYPE_EXTENDED_LINK_LSA)
return 0;
+ /* Check if it is not my LSA */
+ if (IS_LSA_SELF(lsa))
+ return 0;
+
/* Check if Extended is enable */
if (!OspfEXT.enabled)
return 0;
@@ -962,12 +1005,10 @@ static struct ospf_lsa *ospf_ext_pref_lsa_new(struct ospf_area *area,
/* Set opaque-LSA header fields. */
lsa_header_set(s, options, lsa_type, lsa_id, router_id);
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
- zlog_debug(
- "EXT (%s): LSA[Type%u:%s]: Create an Opaque-LSA "
- "Extended Prefix Opaque LSA instance",
- __func__, lsa_type, inet_ntoa(lsa_id));
-
+ osr_debug(
+ "EXT (%s): LSA[Type%u:%pI4]: Create an Opaque-LSA Extended "
+ "Prefix Opaque LSA instance",
+ __func__, lsa_type, &lsa_id);
/* Set opaque-LSA body fields. */
ospf_ext_pref_lsa_body_set(s, exti);
@@ -1022,11 +1063,10 @@ static struct ospf_lsa *ospf_ext_link_lsa_new(struct ospf_area *area,
tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance);
lsa_id.s_addr = htonl(tmp);
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
- zlog_debug(
- "EXT (%s) LSA[Type%u:%s]: Create an Opaque-LSA "
- "Extended Link Opaque LSA instance",
- __func__, lsa_type, inet_ntoa(lsa_id));
+ osr_debug(
+ "EXT (%s) LSA[Type%u:%pI4]: Create an Opaque-LSA Extended "
+ "Link Opaque LSA instance",
+ __func__, lsa_type, &lsa_id);
/* Set opaque-LSA header fields. */
lsa_header_set(s, options, lsa_type, lsa_id, area->ospf->router_id);
@@ -1089,17 +1129,13 @@ static int ospf_ext_pref_lsa_originate1(struct ospf_area *area,
/* Flood new LSA through area. */
ospf_flood_through_area(area, NULL /*nbr */, new);
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
- char area_id[INET_ADDRSTRLEN];
-
- inet_ntop(AF_INET, &area->area_id, area_id, sizeof(area_id));
- zlog_debug(
- "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA "
- "Extended Prefix Opaque LSA: Area(%s), Link(%s)",
- __func__, new->data->type, inet_ntoa(new->data->id),
- area_id, exti->ifp->name);
+ osr_debug(
+ "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA"
+ "Extended Prefix Opaque LSA: Area(%pI4), Link(%s)",
+ __func__, new->data->type, &new->data->id,
+ &area->area_id, exti->ifp->name);
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
ospf_lsa_header_dump(new->data);
- }
rc = 0;
@@ -1141,17 +1177,13 @@ static int ospf_ext_link_lsa_originate1(struct ospf_area *area,
/* Flood new LSA through area. */
ospf_flood_through_area(area, NULL /*nbr */, new);
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
- char area_id[INET_ADDRSTRLEN];
-
- inet_ntop(AF_INET, &area->area_id, area_id, sizeof(area_id));
- zlog_debug(
- "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA "
- "Extended Link Opaque LSA: Area(%s), Link(%s)",
- __func__, new->data->type, inet_ntoa(new->data->id),
- area_id, exti->ifp->name);
+ osr_debug(
+ "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA "
+ "Extended Link Opaque LSA: Area(%pI4), Link(%s)",
+ __func__, new->data->type, &new->data->id,
+ &area->area_id, exti->ifp->name);
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
ospf_lsa_header_dump(new->data);
- }
rc = 0;
@@ -1168,15 +1200,13 @@ static int ospf_ext_pref_lsa_originate(void *arg)
if (!OspfEXT.enabled) {
zlog_info(
- "EXT (%s): Segment Routing "
- "functionality is Disabled now",
+ "EXT (%s): Segment Routing functionality is Disabled now",
__func__);
rc = 0; /* This is not an error case. */
return rc;
}
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("EXT (%s): Start Originate Prefix LSA for area %s",
- __func__, inet_ntoa(area->area_id));
+ osr_debug("EXT (%s): Start Originate Prefix LSA for area %pI4",
+ __func__, &area->area_id);
/* Check if Extended Prefix Opaque LSA is already engaged */
for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
@@ -1206,12 +1236,10 @@ static int ospf_ext_pref_lsa_originate(void *arg)
}
/* Ok, let's try to originate an LSA */
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(
- "EXT (%s): Let's finally reoriginate the "
- "LSA 7.0.0.%u for Itf %s",
- __func__, exti->instance,
- exti->ifp ? exti->ifp->name : "");
+ osr_debug(
+ "EXT (%s): Let's finally re-originate the LSA 7.0.0.%u "
+ "for Itf %s", __func__, exti->instance,
+ exti->ifp ? exti->ifp->name : "");
ospf_ext_pref_lsa_originate1(area, exti);
}
@@ -1229,8 +1257,7 @@ static int ospf_ext_link_lsa_originate(void *arg)
if (!OspfEXT.enabled) {
zlog_info(
- "EXT (%s): Segment Routing "
- "functionality is Disabled now",
+ "EXT (%s): Segment Routing functionality is Disabled now",
__func__);
rc = 0; /* This is not an error case. */
return rc;
@@ -1242,6 +1269,10 @@ static int ospf_ext_link_lsa_originate(void *arg)
if (exti->stype == PREF_SID)
continue;
+ /* Skip Inactive Extended Link */
+ if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))
+ continue;
+
/* Process only Extended Link with valid Area ID */
if ((exti->area == NULL)
|| (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id)))
@@ -1268,13 +1299,11 @@ static int ospf_ext_link_lsa_originate(void *arg)
}
/* Ok, let's try to originate an LSA */
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(
- "EXT (%s): Let's finally reoriginate the "
- "LSA 8.0.0.%u for Itf %s through the Area %s",
- __func__, exti->instance,
- exti->ifp ? exti->ifp->name : "-",
- inet_ntoa(area->area_id));
+ osr_debug(
+ "EXT (%s): Let's finally reoriginate the LSA 8.0.0.%u "
+ "for Itf %s through the Area %pI4", __func__,
+ exti->instance, exti->ifp ? exti->ifp->name : "-",
+ &area->area_id);
ospf_ext_link_lsa_originate1(area, exti);
}
@@ -1297,8 +1326,7 @@ static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa)
* It seems a slip among routers in the routing domain.
*/
zlog_info(
- "EXT (%s): Segment Routing functionality is "
- "Disabled",
+ "EXT (%s): Segment Routing functionality is Disabled",
__func__);
/* Flush it anyway. */
lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
@@ -1362,12 +1390,11 @@ static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa)
ospf_flood_through_area(area, NULL /*nbr */, new);
/* Debug logging. */
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
- zlog_debug(
- "EXT (%s): LSA[Type%u:%s] Refresh Extended Prefix LSA",
- __func__, new->data->type, inet_ntoa(new->data->id));
+ osr_debug("EXT (%s): LSA[Type%u:%pI4] Refresh Extended Prefix LSA",
+ __func__, new->data->type, &new->data->id);
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
ospf_lsa_header_dump(new->data);
- }
+
return new;
}
@@ -1438,12 +1465,10 @@ static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa)
ospf_flood_through_area(area, NULL /*nbr */, new);
/* Debug logging. */
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
- zlog_debug(
- "EXT (%s): LSA[Type%u:%s]: Refresh Extended Link LSA",
- __func__, new->data->type, inet_ntoa(new->data->id));
+ osr_debug("EXT (%s): LSA[Type%u:%pI4]: Refresh Extended Link LSA",
+ __func__, new->data->type, &new->data->id);
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
ospf_lsa_header_dump(new->data);
- }
return new;
}
@@ -1468,18 +1493,17 @@ static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti,
if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
return;
- zlog_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
- opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
- opcode == REFRESH_THIS_LSA ? "Refresh" : "",
- opcode == FLUSH_THIS_LSA ? "Flush" : "",
- exti->ifp ? exti->ifp->name : "-");
+ osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
+ opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+ opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+ opcode == FLUSH_THIS_LSA ? "Flush" : "",
+ exti->ifp ? exti->ifp->name : "-");
/* Verify Area */
if (exti->area == NULL) {
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
- zlog_debug(
- "EXT (%s): Area is not yet set. Try to use Backbone Area",
- __func__);
+ osr_debug(
+ "EXT (%s): Area is not yet set. Try to use Backbone Area",
+ __func__);
top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
struct in_addr backbone = {.s_addr = INADDR_ANY};
@@ -1533,18 +1557,17 @@ static void ospf_ext_link_lsa_schedule(struct ext_itf *exti,
if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
return;
- zlog_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
- opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
- opcode == REFRESH_THIS_LSA ? "Refresh" : "",
- opcode == FLUSH_THIS_LSA ? "Flush" : "",
- exti->ifp ? exti->ifp->name : "-");
+ osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
+ opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+ opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+ opcode == FLUSH_THIS_LSA ? "Flush" : "",
+ exti->ifp ? exti->ifp->name : "-");
/* Verify Area */
if (exti->area == NULL) {
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
- zlog_debug(
- "EXT (%s): Area is not yet set. Try to use Backbone Area",
- __func__);
+ osr_debug(
+ "EXT (%s): Area is not yet set. Try to use Backbone Area",
+ __func__);
top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
struct in_addr backbone = {.s_addr = INADDR_ANY};
diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c
index 6a3ba9902d..e534e72a64 100644
--- a/ospfd/ospf_main.c
+++ b/ospfd/ospf_main.c
@@ -126,6 +126,7 @@ struct quagga_signal_t ospf_signals[] = {
};
static const struct frr_yang_module_info *const ospfd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_route_map_info,
&frr_vrf_info,
diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h
index 8cb5d32a8a..20cdc75fe8 100644
--- a/ospfd/ospf_route.h
+++ b/ospfd/ospf_route.h
@@ -33,12 +33,24 @@
#define OSPF_PATH_TYPE2_EXTERNAL 4
#define OSPF_PATH_MAX 5
+/* Segment Routing information to complement ospf_path structure */
+struct sr_nexthop_info {
+ /* Output label associated to this route */
+ mpls_label_t label_out;
+ /*
+ * Pointer to SR Node which is the next hop for this route
+ * or NULL if next hop is the destination of the prefix
+ */
+ struct sr_node *nexthop;
+};
+
/* OSPF Path. */
struct ospf_path {
struct in_addr nexthop;
struct in_addr adv_router;
ifindex_t ifindex;
unsigned char unnumbered;
+ struct sr_nexthop_info srni;
};
/* Below is the structure linked to every
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index ae70a5c789..64c59c9abf 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -1369,7 +1369,7 @@ static int ospf_spf_calculate_timer(struct thread *thread)
abr_time = monotime_since(&start_time, NULL);
/* Schedule Segment Routing update */
- ospf_sr_update_timer_add(ospf);
+ ospf_sr_update_task(ospf);
total_spf_time =
monotime_since(&spf_start_time, &ospf->ts_spf_duration);
diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c
index 2a35bd2ef9..1a65bfa411 100644
--- a/ospfd/ospf_sr.c
+++ b/ospfd/ospf_sr.c
@@ -33,6 +33,7 @@
#include <stdlib.h>
#include <zebra.h>
+#include "printfrr.h"
#include "command.h"
#include "hash.h"
#include "if.h"
@@ -51,6 +52,7 @@
#include "thread.h"
#include "vty.h"
#include "zclient.h"
+#include "sbuf.h"
#include <lib/json.h>
#include "ospf_errors.h"
@@ -79,7 +81,7 @@
*/
static struct ospf_sr_db OspfSR;
static void ospf_sr_register_vty(void);
-static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe);
+static inline void del_adj_sid(struct sr_nhlfe nhlfe);
/*
* Segment Routing Data Base functions
@@ -107,8 +109,8 @@ static void del_sr_link(void *val)
{
struct sr_link *srl = (struct sr_link *)val;
- del_sid_nhlfe(srl->nhlfe[0]);
- del_sid_nhlfe(srl->nhlfe[1]);
+ del_adj_sid(srl->nhlfe[0]);
+ del_adj_sid(srl->nhlfe[1]);
XFREE(MTYPE_OSPF_SR_PARAMS, val);
}
@@ -117,7 +119,7 @@ static void del_sr_pref(void *val)
{
struct sr_prefix *srp = (struct sr_prefix *)val;
- del_sid_nhlfe(srp->nhlfe);
+ ospf_zebra_delete_prefix_sid(srp);
XFREE(MTYPE_OSPF_SR_PARAMS, val);
}
@@ -151,9 +153,7 @@ static struct sr_node *sr_node_new(struct in_addr *rid)
new->neighbor = NULL;
new->instance = 0;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Created new SR node for %s",
- inet_ntoa(new->adv_router));
+ osr_debug(" |- Created new SR node for %pI4", &new->adv_router);
return new;
}
@@ -164,6 +164,8 @@ static void sr_node_del(struct sr_node *srn)
if (srn == NULL)
return;
+ osr_debug(" |- Delete SR node for %pI4", &srn->adv_router);
+
/* Clean Extended Link */
list_delete(&srn->ext_link);
@@ -188,9 +190,7 @@ static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf,
if (OspfSR.neighbors == NULL)
return NULL;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Search SR-Node for nexthop %s",
- inet_ntoa(nexthop));
+ osr_debug(" |- Search SR-Node for nexthop %pI4", &nexthop);
/* First, search neighbor Router ID for this nexthop */
found = false;
@@ -209,9 +209,8 @@ static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf,
if (!found)
return NULL;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Found nexthop Router ID %s",
- inet_ntoa(nbr->router_id));
+ osr_debug(" |- Found nexthop Router ID %pI4", &nbr->router_id);
+
/* Then, search SR Node */
srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, &nbr->router_id);
@@ -230,8 +229,7 @@ static int ospf_sr_start(struct ospf *ospf)
struct sr_node *srn;
int rc = 0;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("SR (%s): Start Segment Routing", __func__);
+ osr_debug("SR (%s): Start Segment Routing", __func__);
/* Initialize self SR Node */
srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id),
@@ -244,8 +242,7 @@ static int ospf_sr_start(struct ospf *ospf)
srn->msd = OspfSR.msd;
OspfSR.self = srn;
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("SR (%s): Update SR-DB from LSDB", __func__);
+ osr_debug("SR (%s): Update SR-DB from LSDB", __func__);
/* Start by looking to Router Info & Extended LSA in lsdb */
if ((ospf != NULL) && (ospf->backbone != NULL)) {
@@ -278,14 +275,15 @@ static int ospf_sr_start(struct ospf *ospf)
static void ospf_sr_stop(void)
{
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("SR (%s): Stop Segment Routing", __func__);
+ osr_debug("SR (%s): Stop Segment Routing", __func__);
/*
* Remove all SR Nodes from the Hash table. Prefix and Link SID will
* be remove though list_delete() call. See sr_node_del()
*/
hash_clean(OspfSR.neighbors, (void *)sr_node_del);
+ OspfSR.self = NULL;
+ OspfSR.enabled = false;
}
/*
@@ -299,8 +297,7 @@ int ospf_sr_init(void)
{
int rc = -1;
- if (IS_DEBUG_OSPF_SR)
- zlog_info("SR (%s): Initialize SR Data Base", __func__);
+ osr_debug("SR (%s): Initialize SR Data Base", __func__);
memset(&OspfSR, 0, sizeof(struct ospf_sr_db));
OspfSR.enabled = false;
@@ -353,9 +350,6 @@ void ospf_sr_term(void)
/* Clear Prefix Table */
if (OspfSR.prefix)
route_table_finish(OspfSR.prefix);
-
- OspfSR.enabled = false;
- OspfSR.self = NULL;
}
/*
@@ -368,8 +362,6 @@ void ospf_sr_finish(void)
{
/* Stop Segment Routing */
ospf_sr_stop();
-
- OspfSR.enabled = false;
}
/*
@@ -417,21 +409,17 @@ static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top,
}
/* Get OSPF Path from address */
-static struct ospf_path *get_nexthop_by_addr(struct ospf *top,
- struct prefix_ipv4 p)
+static struct ospf_route *get_nexthop_by_addr(struct ospf *top,
+ struct prefix_ipv4 p)
{
- struct ospf_route * or ;
- struct ospf_path *path;
- struct listnode *node;
struct route_node *rn;
/* Sanity Check */
if (top == NULL)
return NULL;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Search Nexthop for prefix %s/%u",
- inet_ntoa(p.prefix), p.prefixlen);
+ osr_debug(" |- Search Nexthop for prefix %pFX",
+ (struct prefix *)&p);
rn = route_node_lookup(top->new_table, (struct prefix *)&p);
@@ -443,16 +431,7 @@ static struct ospf_path *get_nexthop_by_addr(struct ospf *top,
return NULL;
route_unlock_node(rn);
- or = rn->info;
- if (or == NULL)
- return NULL;
-
- /* Then search path from this route */
- for (ALL_LIST_ELEMENTS_RO(or->paths, node, path))
- if (path->nexthop.s_addr != INADDR_ANY || path->ifindex != 0)
- return path;
-
- return NULL;
+ return rn->info;
}
/* Compute NHLFE entry for Extended Link */
@@ -462,10 +441,7 @@ static int compute_link_nhlfe(struct sr_link *srl)
struct ospf_neighbor *nh;
int rc = 0;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Compute NHLFE for link %s/%u",
- inet_ntoa(srl->nhlfe[0].prefv4.prefix),
- srl->nhlfe[0].prefv4.prefixlen);
+ osr_debug(" |- Compute NHLFE for link %pI4", &srl->itf_addr);
/* First determine the OSPF Neighbor */
nh = get_neighbor_by_addr(top, srl->nhlfe[0].nexthop);
@@ -477,9 +453,7 @@ static int compute_link_nhlfe(struct sr_link *srl)
if (nh == NULL)
return rc;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Found nexthop NHLFE %s",
- inet_ntoa(nh->router_id));
+ osr_debug(" |- Found nexthop %pI4", &nh->router_id);
/* Set ifindex for this neighbor */
srl->nhlfe[0].ifindex = nh->oi->ifp->ifindex;
@@ -515,150 +489,108 @@ static int compute_link_nhlfe(struct sr_link *srl)
*
* @param srp - Segment Routing Prefix
*
- * @return -1 if next hop is not found, 0 if nexthop has not changed
- * and 1 if success
+ * @return -1 if no route is found, 0 if there is no SR route ready
+ * and 1 if success or update
*/
static int compute_prefix_nhlfe(struct sr_prefix *srp)
{
struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
- struct ospf_path *nh = NULL;
+ struct ospf_path *path;
+ struct listnode *node;
struct sr_node *srnext;
int rc = -1;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Compute NHLFE for prefix %s/%u",
- inet_ntoa(srp->nhlfe.prefv4.prefix),
- srp->nhlfe.prefv4.prefixlen);
+ osr_debug(" |- Compute NHLFE for prefix %pFX",
+ (struct prefix *)&srp->prefv4);
+
/* First determine the nexthop */
- nh = get_nexthop_by_addr(top, srp->nhlfe.prefv4);
+ srp->route = get_nexthop_by_addr(top, srp->prefv4);
/* Nexthop could be not found when OSPF Adjacency just fire up
* because SPF don't yet populate routing table. This NHLFE will
* be fixed later when SR SPF schedule will be called.
*/
- if (nh == NULL)
+ if (srp->route == NULL)
return rc;
- /* Check if NextHop has changed when call after running a new SPF */
- if (IPV4_ADDR_SAME(&nh->nexthop, &srp->nhlfe.nexthop)
- && (nh->ifindex == srp->nhlfe.ifindex))
- return 0;
-
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Found new next hop for this NHLFE: %s",
- inet_ntoa(nh->nexthop));
+ /* Compute Input Label with self SRGB */
+ srp->label_in = index2label(srp->sid, OspfSR.srgb);
- /*
- * Get SR-Node for this nexthop. Could be not yet available
- * as Extende Link / Prefix and Router Information are flooded
- * after LSA Type 1 & 2 which populate the OSPF Route Table
- */
- srnext = get_sr_node_by_nexthop(top, nh->nexthop);
- if (srnext == NULL)
- return rc;
+ rc = 0;
+ for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) {
- /* And store this information for later update if SR Node is found */
- srnext->neighbor = OspfSR.self;
- if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router))
- srp->nexthop = NULL;
- else
- srp->nexthop = srnext;
+ osr_debug(" |- Process new route via %pI4 for this prefix",
+ &path->nexthop);
- /*
- * SR Node could be known, but SRGB could be not initialize
- * This is due to the fact that Extended Link / Prefix could
- * be received before corresponding Router Information LSA
- */
- if ((srnext == NULL) || (srnext->srgb.lower_bound == 0)
- || (srnext->srgb.range_size == 0))
- return rc;
+ /*
+ * Get SR-Node for this nexthop. Could be not yet available
+ * as Extended Link / Prefix and Router Information are flooded
+ * after LSA Type 1 & 2 which populate the OSPF Route Table
+ */
+ srnext = get_sr_node_by_nexthop(top, path->nexthop);
+ if (srnext == NULL)
+ continue;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Found SRGB %u/%u for next hop SR-Node %s",
- srnext->srgb.range_size, srnext->srgb.lower_bound,
- inet_ntoa(srnext->adv_router));
+ /* And store this information for later update */
+ srnext->neighbor = OspfSR.self;
+ if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router))
+ path->srni.nexthop = NULL;
+ else
+ path->srni.nexthop = srnext;
- /* Set ip addr & ifindex for this neighbor */
- IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &nh->nexthop);
- srp->nhlfe.ifindex = nh->ifindex;
+ /*
+ * SR Node could be known, but SRGB could be not initialize
+ * This is due to the fact that Extended Link / Prefix could
+ * be received before corresponding Router Information LSA
+ */
+ if (srnext == NULL || srnext->srgb.lower_bound == 0
+ || srnext->srgb.range_size == 0)
+ continue;
- /* Compute Input Label with self SRGB */
- srp->nhlfe.label_in = index2label(srp->sid, OspfSR.srgb);
- /*
- * and Output Label with Next hop SR Node SRGB or Implicit Null label
- * if next hop is the destination and request PHP
- */
- if ((srp->nexthop == NULL)
- && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)))
- srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL;
- else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
- srp->nhlfe.label_out = srp->sid;
- else
- srp->nhlfe.label_out = index2label(srp->sid, srnext->srgb);
+ osr_debug(" |- Found SRGB %u/%u for next hop SR-Node %pI4",
+ srnext->srgb.range_size, srnext->srgb.lower_bound,
+ &srnext->adv_router);
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Computed new labels in: %u out: %u",
- srp->nhlfe.label_in, srp->nhlfe.label_out);
+ /*
+ * Compute Output Label with Nexthop SR Node SRGB or Implicit
+ * Null label if next hop is the destination and request PHP
+ */
+ if ((path->srni.nexthop == NULL)
+ && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)))
+ path->srni.label_out = MPLS_LABEL_IMPLICIT_NULL;
+ else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+ path->srni.label_out = srp->sid;
+ else
+ path->srni.label_out =
+ index2label(srp->sid, srnext->srgb);
- rc = 1;
+ osr_debug(" |- Computed new labels in: %u out: %u",
+ srp->label_in, path->srni.label_out);
+ rc = 1;
+ }
return rc;
}
-/* Send MPLS Label entry to Zebra for installation or deletion */
-static int ospf_zebra_send_mpls_labels(int cmd, struct sr_nhlfe nhlfe)
-{
- struct zapi_labels zl = {};
- struct zapi_nexthop *znh;
-
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- %s LSP %u/%u for %s/%u via %u",
- cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete",
- nhlfe.label_in, nhlfe.label_out,
- inet_ntoa(nhlfe.prefv4.prefix),
- nhlfe.prefv4.prefixlen, nhlfe.ifindex);
-
- zl.type = ZEBRA_LSP_OSPF_SR;
- zl.local_label = nhlfe.label_in;
-
- SET_FLAG(zl.message, ZAPI_LABELS_FTN);
- zl.route.prefix.family = nhlfe.prefv4.family;
- zl.route.prefix.prefixlen = nhlfe.prefv4.prefixlen;
- zl.route.prefix.u.prefix4 = nhlfe.prefv4.prefix;
- zl.route.type = ZEBRA_ROUTE_OSPF;
- zl.route.instance = 0;
-
- zl.nexthop_num = 1;
- znh = &zl.nexthops[0];
- znh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- znh->gate.ipv4 = nhlfe.nexthop;
- znh->ifindex = nhlfe.ifindex;
- znh->label_num = 1;
- znh->labels[0] = nhlfe.label_out;
-
- return zebra_send_mpls_labels(zclient, cmd, &zl);
-}
-
-/* Add new NHLFE entry for SID */
-static inline void add_sid_nhlfe(struct sr_nhlfe nhlfe)
+/* Add new NHLFE entry for Adjacency SID */
+static inline void add_adj_sid(struct sr_nhlfe nhlfe)
{
- if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0))
- ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, nhlfe);
+ if (nhlfe.label_in != 0)
+ ospf_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, nhlfe);
}
-/* Remove NHLFE entry for SID */
-static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe)
+/* Remove NHLFE entry for Adjacency SID */
+static inline void del_adj_sid(struct sr_nhlfe nhlfe)
{
- if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0))
- ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, nhlfe);
+ if (nhlfe.label_in != 0)
+ ospf_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, nhlfe);
}
-/* Update NHLFE entry for SID */
-static inline void update_sid_nhlfe(struct sr_nhlfe n1, struct sr_nhlfe n2)
+/* Update NHLFE entry for Adjacency SID */
+static inline void update_adj_sid(struct sr_nhlfe n1, struct sr_nhlfe n2)
{
-
- del_sid_nhlfe(n1);
- add_sid_nhlfe(n2);
+ del_adj_sid(n1);
+ add_adj_sid(n2);
}
/*
@@ -729,23 +661,10 @@ static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh)
sum += TLV_SIZE(sub_tlvh);
}
- IPV4_ADDR_COPY(&srl->nhlfe[0].prefv4.prefix, &link->link_data);
- srl->nhlfe[0].prefv4.prefixlen = IPV4_MAX_PREFIXLEN;
- srl->nhlfe[0].prefv4.family = AF_INET;
- apply_mask_ipv4(&srl->nhlfe[0].prefv4);
- IPV4_ADDR_COPY(&srl->nhlfe[1].prefv4.prefix, &link->link_data);
- srl->nhlfe[1].prefv4.prefixlen = IPV4_MAX_PREFIXLEN;
- srl->nhlfe[1].prefv4.family = AF_INET;
- apply_mask_ipv4(&srl->nhlfe[1].prefv4);
+ IPV4_ADDR_COPY(&srl->itf_addr, &link->link_data);
- if (IS_DEBUG_OSPF_SR) {
- zlog_debug(" |- Found primary Adj/Lan Sid %u for %s/%u",
- srl->sid[0], inet_ntoa(srl->nhlfe[0].prefv4.prefix),
- srl->nhlfe[0].prefv4.prefixlen);
- zlog_debug(" |- Found backup Adj/Lan Sid %u for %s/%u",
- srl->sid[1], inet_ntoa(srl->nhlfe[1].prefv4.prefix),
- srl->nhlfe[1].prefv4.prefixlen);
- }
+ osr_debug(" |- Found primary %u and backup %u Adj/Lan Sid for %pI4",
+ srl->sid[0], srl->sid[1], &srl->itf_addr);
return srl;
}
@@ -784,11 +703,10 @@ static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh)
srp->sid = GET_LABEL(ntohl(psid->value));
else
srp->sid = ntohl(psid->value);
- IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix,
- &pref->address);
- srp->nhlfe.prefv4.prefixlen = pref->pref_length;
- srp->nhlfe.prefv4.family = AF_INET;
- apply_mask_ipv4(&srp->nhlfe.prefv4);
+ IPV4_ADDR_COPY(&srp->prefv4.prefix, &pref->address);
+ srp->prefv4.prefixlen = pref->pref_length;
+ srp->prefv4.family = AF_INET;
+ apply_mask_ipv4(&srp->prefv4);
break;
default:
break;
@@ -796,10 +714,9 @@ static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh)
sum += TLV_SIZE(sub_tlvh);
}
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Found SID %u for prefix %s/%u", srp->sid,
- inet_ntoa(srp->nhlfe.prefv4.prefix),
- srp->nhlfe.prefv4.prefixlen);
+ osr_debug(" |- Found SID %u for prefix %pFX", srp->sid,
+ (struct prefix *)&srp->prefv4);
+
return srp;
}
@@ -839,13 +756,12 @@ static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl,
if ((srn == NULL) || (srl == NULL))
return;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Process Extended Link Adj/Lan-SID");
+ osr_debug(" |- Process Extended Link Adj/Lan-SID");
- /* Process only Local Adj/Lan_Adj SID coming from LSA SELF */
- if (!CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG)
- || !CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG)
- || !CHECK_FLAG(lsa_flags, OSPF_LSA_SELF))
+ /* Skip Local Adj/Lan_Adj SID coming from neighbors */
+ if (!CHECK_FLAG(lsa_flags, OSPF_LSA_SELF)
+ && (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG)
+ || CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG)))
return;
/* Search for existing Segment Link */
@@ -855,11 +771,9 @@ static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl,
break;
}
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- %s SR Link 8.0.0.%u for SR node %s",
- found ? "Update" : "Add",
- GET_OPAQUE_ID(srl->instance),
- inet_ntoa(srn->adv_router));
+ osr_debug(" |- %s SR Link 8.0.0.%u for SR node %pI4",
+ found ? "Update" : "Add", GET_OPAQUE_ID(srl->instance),
+ &srn->adv_router);
/* if not found, add new Segment Link and install NHLFE */
if (!found) {
@@ -869,14 +783,14 @@ static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl,
listnode_add(srn->ext_link, srl);
/* Try to set MPLS table */
if (compute_link_nhlfe(srl)) {
- add_sid_nhlfe(srl->nhlfe[0]);
- add_sid_nhlfe(srl->nhlfe[1]);
+ add_adj_sid(srl->nhlfe[0]);
+ add_adj_sid(srl->nhlfe[1]);
}
} else {
if (sr_link_cmp(lk, srl)) {
if (compute_link_nhlfe(srl)) {
- update_sid_nhlfe(lk->nhlfe[0], srl->nhlfe[0]);
- update_sid_nhlfe(lk->nhlfe[1], srl->nhlfe[1]);
+ update_adj_sid(lk->nhlfe[0], srl->nhlfe[0]);
+ update_adj_sid(lk->nhlfe[1], srl->nhlfe[1]);
/* Replace Segment List */
listnode_delete(srn->ext_link, lk);
XFREE(MTYPE_OSPF_SR_PARAMS, lk);
@@ -912,8 +826,7 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp)
if (srn == NULL || srp == NULL)
return;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Process Extended Prefix SID %u", srp->sid);
+ osr_debug(" |- Process Extended Prefix SID %u", srp->sid);
/* Process only Global Prefix SID */
if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_LFLG))
@@ -926,11 +839,9 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp)
break;
}
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- %s SR LSA ID 7.0.0.%u for SR node %s",
- found ? "Update" : "Add",
- GET_OPAQUE_ID(srp->instance),
- inet_ntoa(srn->adv_router));
+ osr_debug(" |- %s SR LSA ID 7.0.0.%u for SR node %pI4",
+ found ? "Update" : "Add", GET_OPAQUE_ID(srp->instance),
+ &srn->adv_router);
/* if not found, add new Segment Prefix and install NHLFE */
if (!found) {
@@ -940,11 +851,11 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp)
listnode_add(srn->ext_prefix, srp);
/* Try to set MPLS table */
if (compute_prefix_nhlfe(srp) == 1)
- add_sid_nhlfe(srp->nhlfe);
+ ospf_zebra_update_prefix_sid(srp);
} else {
if (sr_prefix_cmp(pref, srp)) {
if (compute_prefix_nhlfe(srp) == 1) {
- update_sid_nhlfe(pref->nhlfe, srp->nhlfe);
+ ospf_zebra_delete_prefix_sid(pref);
/* Replace Segment Prefix */
listnode_delete(srn->ext_prefix, pref);
XFREE(MTYPE_OSPF_SR_PARAMS, pref);
@@ -952,6 +863,7 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp)
IPV4_ADDR_COPY(&srp->adv_router,
&srn->adv_router);
listnode_add(srn->ext_prefix, srp);
+ ospf_zebra_update_prefix_sid(srp);
} else {
/* New NHLFE was not found.
* Just free the SR Prefix
@@ -976,7 +888,6 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args)
struct listnode *node;
struct sr_node *srn = (struct sr_node *)bucket->data;
struct sr_prefix *srp;
- struct sr_nhlfe new;
/* Process Every Extended Prefix for this SR-Node */
for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
@@ -989,13 +900,10 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args)
if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
continue;
- /* OK. Compute new NHLFE */
- memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe));
- new.label_in = index2label(srp->sid, OspfSR.srgb);
- /* Update MPLS LFIB */
- update_sid_nhlfe(srp->nhlfe, new);
- /* Finally update Input Label */
- srp->nhlfe.label_in = new.label_in;
+ /* OK. Compute new input label ... */
+ srp->label_in = index2label(srp->sid, OspfSR.srgb);
+ /* ... and update MPLS LFIB */
+ ospf_zebra_update_prefix_sid(srp);
}
}
@@ -1005,21 +913,27 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args)
*/
static void update_out_nhlfe(struct hash_bucket *bucket, void *args)
{
- struct listnode *node;
+ struct listnode *node, *pnode;
struct sr_node *srn = (struct sr_node *)bucket->data;
struct sr_node *srnext = (struct sr_node *)args;
struct sr_prefix *srp;
- struct sr_nhlfe new;
+ struct ospf_path *path;
for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
- /* Process only SID Index for next hop without PHP */
- if ((srp->nexthop == NULL)
- && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)))
+ /* Process only SID Index with valid route */
+ if (srp->route == NULL)
continue;
- memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe));
- new.label_out = index2label(srp->sid, srnext->srgb);
- update_sid_nhlfe(srp->nhlfe, new);
- srp->nhlfe.label_out = new.label_out;
+
+ for (ALL_LIST_ELEMENTS_RO(srp->route->paths, pnode, path)) {
+ /* Process only SID Index for next hop without PHP */
+ if ((path->srni.nexthop == NULL)
+ && (!CHECK_FLAG(srp->flags,
+ EXT_SUBTLV_PREFIX_SID_NPFLG)))
+ continue;
+ path->srni.label_out =
+ index2label(srp->sid, srnext->srgb);
+ ospf_zebra_update_prefix_sid(srp);
+ }
}
}
@@ -1036,17 +950,15 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
struct sr_node *srn;
struct tlv_header *tlvh;
struct lsa_header *lsah = lsa->data;
- struct ri_sr_tlv_sid_label_range *ri_srgb;
- struct ri_sr_tlv_sr_algorithm *algo;
+ struct ri_sr_tlv_sid_label_range *ri_srgb = NULL;
+ struct ri_sr_tlv_sr_algorithm *algo = NULL;
struct sr_srgb srgb;
uint16_t length = 0, sum = 0;
+ uint8_t msd = 0;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(
- "SR (%s): Process Router "
- "Information LSA 4.0.0.%u from %s",
- __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
- inet_ntoa(lsah->adv_router));
+ osr_debug("SR (%s): Process Router Information LSA 4.0.0.%u from %pI4",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ &lsah->adv_router);
/* Sanity check */
if (IS_LSA_SELF(lsa))
@@ -1058,26 +970,10 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
return;
}
- /* Get SR Node in hash table from Router ID */
- srn = hash_get(OspfSR.neighbors, (void *)&(lsah->adv_router),
- (void *)sr_node_new);
-
- /* Sanity check */
- if (srn == NULL) {
- flog_err(EC_OSPF_SR_NODE_CREATE,
- "SR (%s): Abort! can't create SR node in hash table",
- __func__);
- return;
- }
+ /* Search SR Node in hash table from Router ID */
+ srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+ &lsah->adv_router);
- if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) {
- flog_err(EC_OSPF_SR_INVALID_LSA_ID,
- "SR (%s): Abort! Wrong "
- "LSA ID 4.0.0.%u for SR node %s/%u",
- __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
- inet_ntoa(lsah->adv_router), srn->instance);
- return;
- }
/* Collect Router Information Sub TLVs */
/* Initialize TLV browsing */
@@ -1090,23 +986,14 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
switch (ntohs(tlvh->type)) {
case RI_SR_TLV_SR_ALGORITHM:
algo = (struct ri_sr_tlv_sr_algorithm *)tlvh;
- int i;
-
- for (i = 0; i < ntohs(algo->header.length); i++)
- srn->algo[i] = algo->value[0];
- for (; i < ALGORITHM_COUNT; i++)
- srn->algo[i] = SR_ALGORITHM_UNSET;
sum += TLV_SIZE(tlvh);
break;
case RI_SR_TLV_SID_LABEL_RANGE:
ri_srgb = (struct ri_sr_tlv_sid_label_range *)tlvh;
- srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size));
- srgb.lower_bound =
- GET_LABEL(ntohl(ri_srgb->lower.value));
sum += TLV_SIZE(tlvh);
break;
case RI_SR_TLV_NODE_MSD:
- srn->msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value;
+ msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value;
sum += TLV_SIZE(tlvh);
break;
default:
@@ -1115,38 +1002,82 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
}
}
- /* Check that we collect mandatory parameters */
- if (srn->algo[0] == SR_ALGORITHM_UNSET || srgb.range_size == 0
- || srgb.lower_bound == 0) {
- flog_err(EC_OSPF_SR_NODE_CREATE,
- "SR (%s): Missing mandatory parameters. Abort!",
- __func__);
- hash_release(OspfSR.neighbors, &(srn->adv_router));
- XFREE(MTYPE_OSPF_SR_PARAMS, srn);
+ /* Check if Segment Routing Capabilities has been found */
+ if (ri_srgb == NULL) {
+ /* Skip Router Information without SR capabilities
+ * advertise by a non SR Node */
+ if (srn == NULL) {
+ return;
+ } else {
+ /* Remove SR Node that advertise Router Information
+ * without SR capabilities. This could correspond to a
+ * Node stopping Segment Routing */
+ hash_release(OspfSR.neighbors, &(srn->adv_router));
+ sr_node_del(srn);
+ return;
+ }
+ }
+
+ /* Check that RI LSA belongs to the correct SR Node */
+ if ((srn != NULL) && (srn->instance != 0)
+ && (srn->instance != ntohl(lsah->id.s_addr))) {
+ flog_err(EC_OSPF_SR_INVALID_LSA_ID,
+ "SR (%s): Abort! Wrong "
+ "LSA ID 4.0.0.%u for SR node %pI4/%u",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ &lsah->adv_router, srn->instance);
return;
}
+ /* OK. All things look good. Get SRGB */
+ srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size));
+ srgb.lower_bound = GET_LABEL(ntohl(ri_srgb->lower.value));
+
/* Check if it is a new SR Node or not */
- if (srn->instance == 0) {
+ if (srn == NULL) {
+ /* Get a new SR Node in hash table from Router ID */
+ srn = (struct sr_node *)hash_get(OspfSR.neighbors,
+ &lsah->adv_router,
+ (void *)sr_node_new);
+ /* Sanity check */
+ if (srn == NULL) {
+ flog_err(
+ EC_OSPF_SR_NODE_CREATE,
+ "SR (%s): Abort! can't create SR node in hash table",
+ __func__);
+ return;
+ }
/* update LSA ID */
srn->instance = ntohl(lsah->id.s_addr);
/* Copy SRGB */
srn->srgb.range_size = srgb.range_size;
srn->srgb.lower_bound = srgb.lower_bound;
+ /* Set Algorithm */
+ if (algo != NULL) {
+ int i;
+ for (i = 0; i < ntohs(algo->header.length); i++)
+ srn->algo[i] = algo->value[0];
+ for (; i < ALGORITHM_COUNT; i++)
+ srn->algo[i] = SR_ALGORITHM_UNSET;
+ } else {
+ srn->algo[0] = SR_ALGORITHM_SPF;
+ }
+ /* set MSD */
+ srn->msd = msd;
+ return;
}
/* Check if SRGB has changed */
- if ((srn->srgb.range_size != srgb.range_size)
- || (srn->srgb.lower_bound != srgb.lower_bound)) {
- srn->srgb.range_size = srgb.range_size;
- srn->srgb.lower_bound = srgb.lower_bound;
- /* Update NHLFE if it is a neighbor SR node */
- if (srn->neighbor == OspfSR.self)
- hash_iterate(OspfSR.neighbors,
- (void (*)(struct hash_bucket *,
- void *))update_out_nhlfe,
- (void *)srn);
- }
+ if ((srn->srgb.range_size == srgb.range_size)
+ && (srn->srgb.lower_bound == srgb.lower_bound))
+ return;
+
+ /* Update SRGB ... */
+ srn->srgb.range_size = srgb.range_size;
+ srn->srgb.lower_bound = srgb.lower_bound;
+ /* ... and NHLFE if it is a neighbor SR node */
+ if (srn->neighbor == OspfSR.self)
+ hash_iterate(OspfSR.neighbors, update_out_nhlfe, srn);
}
/*
@@ -1158,10 +1089,8 @@ void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa)
struct sr_node *srn;
struct lsa_header *lsah = lsa->data;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("SR (%s): Remove SR node %s from lsa_id 4.0.0.%u",
- __func__, inet_ntoa(lsah->adv_router),
- GET_OPAQUE_ID(ntohl(lsah->id.s_addr)));
+ osr_debug("SR (%s): Remove SR node %pI4 from lsa_id 4.0.0.%u", __func__,
+ &lsah->adv_router, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)));
/* Sanity check */
if (OspfSR.neighbors == NULL) {
@@ -1176,16 +1105,17 @@ void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa)
/* Sanity check */
if (srn == NULL) {
flog_err(EC_OSPF_SR_NODE_CREATE,
- "SR (%s): Abort! no entry in SRDB for SR Node %s",
- __func__, inet_ntoa(lsah->adv_router));
+ "SR (%s): Abort! no entry in SRDB for SR Node %pI4",
+ __func__, &lsah->adv_router);
return;
}
if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) {
- flog_err(EC_OSPF_SR_INVALID_LSA_ID,
- "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %s",
- __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
- inet_ntoa(lsah->adv_router));
+ flog_err(
+ EC_OSPF_SR_INVALID_LSA_ID,
+ "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %pI4",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ &lsah->adv_router);
return;
}
@@ -1203,11 +1133,9 @@ void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa)
uint16_t length, sum;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(
- "SR (%s): Process Extended Link LSA 8.0.0.%u from %s",
- __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
- inet_ntoa(lsah->adv_router));
+ osr_debug("SR (%s): Process Extended Link LSA 8.0.0.%u from %pI4",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ &lsah->adv_router);
/* Sanity check */
if (OspfSR.neighbors == NULL) {
@@ -1253,13 +1181,12 @@ void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa)
struct listnode *node;
struct sr_link *srl;
struct sr_node *srn;
- struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+ struct lsa_header *lsah = lsa->data;
uint32_t instance = ntohl(lsah->id.s_addr);
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("SR (%s): Remove Extended Link LSA 8.0.0.%u from %s",
- __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
- inet_ntoa(lsah->adv_router));
+ osr_debug("SR (%s): Remove Extended Link LSA 8.0.0.%u from %pI4",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ &lsah->adv_router);
/* Sanity check */
if (OspfSR.neighbors == NULL) {
@@ -1278,8 +1205,8 @@ void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa)
*/
if (srn == NULL) {
flog_err(EC_OSPF_SR_INVALID_DB,
- "SR (%s): Stop! no entry in SRDB for SR Node %s",
- __func__, inet_ntoa(lsah->adv_router));
+ "SR (%s): Stop! no entry in SRDB for SR Node %pI4",
+ __func__, &lsah->adv_router);
return;
}
@@ -1288,18 +1215,143 @@ void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa)
if (srl->instance == instance)
break;
- /* Remove Segment Link if found */
+ /* Remove Segment Link if found. Note that for Neighbors, only Global
+ * Adj/Lan-Adj SID are stored in the SR-DB */
if ((srl != NULL) && (srl->instance == instance)) {
- del_sid_nhlfe(srl->nhlfe[0]);
- del_sid_nhlfe(srl->nhlfe[1]);
+ del_adj_sid(srl->nhlfe[0]);
+ del_adj_sid(srl->nhlfe[1]);
listnode_delete(srn->ext_link, srl);
XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+ }
+}
+
+/* Add (LAN)Adjacency-SID from Extended Link Information */
+void ospf_sr_ext_itf_add(struct ext_itf *exti)
+{
+ struct sr_node *srn = OspfSR.self;
+ struct sr_link *srl;
+
+ osr_debug("SR (%s): Add Extended Link LSA 8.0.0.%u from self", __func__,
+ exti->instance);
+
+ /* Sanity check */
+ if (srn == NULL)
+ return;
+
+ /* Initialize new Segment Routing Link */
+ srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link));
+ srl->srn = srn;
+ srl->adv_router = srn->adv_router;
+ srl->itf_addr = exti->link.link_data;
+ srl->instance =
+ SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance);
+ switch (exti->stype) {
+ case ADJ_SID:
+ srl->type = ADJ_SID;
+ /* Primary information */
+ srl->flags[0] = exti->adj_sid[0].flags;
+ if (CHECK_FLAG(exti->adj_sid[0].flags,
+ EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+ srl->sid[0] = GET_LABEL(ntohl(exti->adj_sid[0].value));
+ else
+ srl->sid[0] = ntohl(exti->adj_sid[0].value);
+ if (exti->rmt_itf_addr.header.type == 0)
+ srl->nhlfe[0].nexthop = exti->link.link_id;
+ else
+ srl->nhlfe[0].nexthop = exti->rmt_itf_addr.value;
+ /* Backup Information if set */
+ if (exti->adj_sid[1].header.type == 0)
+ break;
+ srl->flags[1] = exti->adj_sid[1].flags;
+ if (CHECK_FLAG(exti->adj_sid[1].flags,
+ EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+ srl->sid[1] = GET_LABEL(ntohl(exti->adj_sid[1].value));
+ else
+ srl->sid[1] = ntohl(exti->adj_sid[1].value);
+ if (exti->rmt_itf_addr.header.type == 0)
+ srl->nhlfe[1].nexthop = exti->link.link_id;
+ else
+ srl->nhlfe[1].nexthop = exti->rmt_itf_addr.value;
+ break;
+ case LAN_ADJ_SID:
+ srl->type = LAN_ADJ_SID;
+ /* Primary information */
+ srl->flags[0] = exti->lan_sid[0].flags;
+ if (CHECK_FLAG(exti->lan_sid[0].flags,
+ EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+ srl->sid[0] = GET_LABEL(ntohl(exti->lan_sid[0].value));
+ else
+ srl->sid[0] = ntohl(exti->lan_sid[0].value);
+ if (exti->rmt_itf_addr.header.type == 0)
+ srl->nhlfe[0].nexthop = exti->lan_sid[0].neighbor_id;
+ else
+ srl->nhlfe[0].nexthop = exti->rmt_itf_addr.value;
+ /* Backup Information if set */
+ if (exti->lan_sid[1].header.type == 0)
+ break;
+ srl->flags[1] = exti->lan_sid[1].flags;
+ if (CHECK_FLAG(exti->lan_sid[1].flags,
+ EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+ srl->sid[1] = GET_LABEL(ntohl(exti->lan_sid[1].value));
+ else
+ srl->sid[1] = ntohl(exti->lan_sid[1].value);
+ if (exti->rmt_itf_addr.header.type == 0)
+ srl->nhlfe[1].nexthop = exti->lan_sid[1].neighbor_id;
+ else
+ srl->nhlfe[1].nexthop = exti->rmt_itf_addr.value;
+ break;
+ default:
+ /* Wrong SID Type. Abort! */
+ XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+ return;
+ }
+
+ /* Segment Routing Link is ready, update it */
+ update_ext_link_sid(srn, srl, OSPF_LSA_SELF);
+}
+
+/* Delete Prefix or (LAN)Adjacency-SID from Extended Link Information */
+void ospf_sr_ext_itf_delete(struct ext_itf *exti)
+{
+ struct listnode *node;
+ struct sr_node *srn = OspfSR.self;
+ struct sr_prefix *srp = NULL;
+ struct sr_link *srl = NULL;
+ uint32_t instance;
+
+ osr_debug("SR (%s): Remove Extended LSA %u.0.0.%u from self",
+ __func__, exti->stype == PREF_SID ? 7 : 8, exti->instance);
+
+ /* Sanity check: SR-Node and Extended Prefix/Link list may have been
+ * removed earlier when stopping OSPF or OSPF-SR */
+ if (srn == NULL || srn->ext_prefix == NULL || srn->ext_link == NULL)
+ return;
+
+ if (exti->stype == PREF_SID) {
+ instance = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA,
+ exti->instance);
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp))
+ if (srp->instance == instance)
+ break;
+
+ /* Uninstall Segment Prefix SID if found */
+ if ((srp != NULL) && (srp->instance == instance))
+ ospf_zebra_delete_prefix_sid(srp);
} else {
- flog_err(EC_OSPF_SR_INVALID_DB,
- "SR (%s): Didn't found corresponding SR Link 8.0.0.%u "
- "for SR Node %s",
- __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
- inet_ntoa(lsah->adv_router));
+ /* Search for corresponding Segment Link for self SR-Node */
+ instance = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA,
+ exti->instance);
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl))
+ if (srl->instance == instance)
+ break;
+
+ /* Remove Segment Link if found */
+ if ((srl != NULL) && (srl->instance == instance)) {
+ del_adj_sid(srl->nhlfe[0]);
+ del_adj_sid(srl->nhlfe[1]);
+ listnode_delete(srn->ext_link, srl);
+ XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+ }
}
}
@@ -1308,17 +1360,14 @@ void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa)
{
struct sr_node *srn;
struct tlv_header *tlvh;
- struct lsa_header *lsah = lsa->data;
+ struct lsa_header *lsah = (struct lsa_header *)lsa->data;
struct sr_prefix *srp;
uint16_t length, sum;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(
- "SR (%s): Process Extended Prefix LSA "
- "7.0.0.%u from %s",
- __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
- inet_ntoa(lsah->adv_router));
+ osr_debug("SR (%s): Process Extended Prefix LSA 7.0.0.%u from %pI4",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ &lsah->adv_router);
/* Sanity check */
if (OspfSR.neighbors == NULL) {
@@ -1367,11 +1416,9 @@ void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa)
struct lsa_header *lsah = (struct lsa_header *)lsa->data;
uint32_t instance = ntohl(lsah->id.s_addr);
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(
- "SR (%s): Remove Extended Prefix LSA 7.0.0.%u from %s",
- __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
- inet_ntoa(lsah->adv_router));
+ osr_debug("SR (%s): Remove Extended Prefix LSA 7.0.0.%u from %pI4",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ &lsah->adv_router);
/* Sanity check */
if (OspfSR.neighbors == NULL) {
@@ -1390,27 +1437,27 @@ void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa)
*/
if (srn == NULL) {
flog_err(EC_OSPF_SR_INVALID_DB,
- "SR (%s): Stop! no entry in SRDB for SR Node %s",
- __func__, inet_ntoa(lsah->adv_router));
+ "SR (%s): Stop! no entry in SRDB for SR Node %pI4",
+ __func__, &lsah->adv_router);
return;
}
- /* Search for corresponding Segment Link */
+ /* Search for corresponding Segment Prefix */
for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp))
if (srp->instance == instance)
break;
- /* Remove Segment Link if found */
+ /* Remove Prefix if found */
if ((srp != NULL) && (srp->instance == instance)) {
- del_sid_nhlfe(srp->nhlfe);
- listnode_delete(srn->ext_link, srp);
+ ospf_zebra_delete_prefix_sid(srp);
+ listnode_delete(srn->ext_prefix, srp);
XFREE(MTYPE_OSPF_SR_PARAMS, srp);
} else {
flog_err(
EC_OSPF_SR_INVALID_DB,
- "SR (%s): Didn't found corresponding SR Prefix 7.0.0.%u for SR Node %s",
+ "SR (%s): Didn't found corresponding SR Prefix 7.0.0.%u for SR Node %pI4",
__func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
- inet_ntoa(lsah->adv_router));
+ &lsah->adv_router);
}
}
@@ -1428,14 +1475,14 @@ uint32_t get_ext_link_label_value(void)
/*
* Update Prefix SID. Call by ospf_ext_pref_ism_change to
- * complete initial CLI command at startutp.
+ * complete initial CLI command at startup.
*
* @param ifp - Loopback interface
* @param pref - Prefix address of this interface
*
* @return - void
*/
-void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p)
+void ospf_sr_update_local_prefix(struct interface *ifp, struct prefix *p)
{
struct listnode *node;
struct sr_prefix *srp;
@@ -1450,29 +1497,31 @@ void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p)
*/
for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
if ((srp->nhlfe.ifindex == ifp->ifindex)
- || ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix,
- &p->u.prefix4))
- && (srp->nhlfe.prefv4.prefixlen == p->prefixlen))) {
+ || ((IPV4_ADDR_SAME(&srp->prefv4.prefix, &p->u.prefix4))
+ && (srp->prefv4.prefixlen == p->prefixlen))) {
/* Update Interface & Prefix info */
srp->nhlfe.ifindex = ifp->ifindex;
- IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix,
- &p->u.prefix4);
- srp->nhlfe.prefv4.prefixlen = p->prefixlen;
- srp->nhlfe.prefv4.family = p->family;
+ IPV4_ADDR_COPY(&srp->prefv4.prefix, &p->u.prefix4);
+ srp->prefv4.prefixlen = p->prefixlen;
+ srp->prefv4.family = p->family;
IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &p->u.prefix4);
/* OK. Let's Schedule Extended Prefix LSA */
srp->instance = ospf_ext_schedule_prefix_index(
- ifp, srp->sid, &srp->nhlfe.prefv4, srp->flags);
+ ifp, srp->sid, &srp->prefv4, srp->flags);
+
+ osr_debug(
+ " |- Update Node SID %pFX - %u for self SR Node",
+ (struct prefix *)&srp->prefv4, srp->sid);
/* Install NHLFE if NO-PHP is requested */
if (CHECK_FLAG(srp->flags,
EXT_SUBTLV_PREFIX_SID_NPFLG)) {
- srp->nhlfe.label_in = index2label(
- srp->sid, OspfSR.self->srgb);
+ srp->label_in = index2label(srp->sid,
+ OspfSR.self->srgb);
srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL;
- add_sid_nhlfe(srp->nhlfe);
+ ospf_zebra_update_prefix_sid(srp);
}
}
}
@@ -1488,12 +1537,10 @@ static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args)
struct sr_node *srn = (struct sr_node *)bucket->data;
struct listnode *node;
struct sr_prefix *srp;
- struct sr_nhlfe old;
+ bool old;
int rc;
- if (IS_DEBUG_OSPF_SR)
- zlog_debug(" |- Update Prefix for SR Node %s",
- inet_ntoa(srn->adv_router));
+ osr_debug(" |- Update Prefix for SR Node %pI4", &srn->adv_router);
/* Skip Self SR Node */
if (srn == OspfSR.self)
@@ -1502,24 +1549,25 @@ static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args)
/* Update Extended Prefix */
for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
- /* Backup current NHLFE */
- memcpy(&old, &srp->nhlfe, sizeof(struct sr_nhlfe));
+ /* Keep track of valid route */
+ old = srp->route != NULL;
/* Compute the new NHLFE */
rc = compute_prefix_nhlfe(srp);
/* Check computation result */
switch (rc) {
- /* next hop is not know, remove old NHLFE to avoid loop */
+ /* Routes are not know, remove old NHLFE if any to avoid loop */
case -1:
- del_sid_nhlfe(srp->nhlfe);
+ if (old)
+ ospf_zebra_delete_prefix_sid(srp);
break;
- /* next hop has not changed, skip it */
+ /* Routes exist but are not ready, skip it */
case 0:
break;
- /* there is a new next hop, update NHLFE */
+ /* There is at least one route, update NHLFE */
case 1:
- update_sid_nhlfe(old, srp->nhlfe);
+ ospf_zebra_update_prefix_sid(srp);
break;
default:
break;
@@ -1527,22 +1575,17 @@ static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args)
}
}
-static int ospf_sr_update_schedule(struct thread *t)
+void ospf_sr_update_task(struct ospf *ospf)
{
- struct ospf *ospf;
struct timeval start_time, stop_time;
- ospf = THREAD_ARG(t);
- ospf->t_sr_update = NULL;
-
- if (!OspfSR.update)
- return 0;
+ if (ospf == NULL)
+ return;
monotime(&start_time);
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("SR (%s): Start SPF update", __func__);
+ osr_debug("SR (%s): Start SPF update", __func__);
hash_iterate(OspfSR.neighbors, (void (*)(struct hash_bucket *,
void *))ospf_sr_nhlfe_update,
@@ -1550,32 +1593,9 @@ static int ospf_sr_update_schedule(struct thread *t)
monotime(&stop_time);
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("SR (%s): SPF Processing Time(usecs): %lld",
- __func__,
- (stop_time.tv_sec - start_time.tv_sec) * 1000000LL
- + (stop_time.tv_usec - start_time.tv_usec));
-
- OspfSR.update = false;
- return 1;
-}
-
-#define OSPF_SR_UPDATE_INTERVAL 1
-
-void ospf_sr_update_timer_add(struct ospf *ospf)
-{
-
- if (ospf == NULL)
- return;
-
- /* Check if an update is not alreday engage */
- if (OspfSR.update)
- return;
-
- OspfSR.update = true;
-
- thread_add_timer(master, ospf_sr_update_schedule, ospf,
- OSPF_SR_UPDATE_INTERVAL, &ospf->t_sr_update);
+ osr_debug("SR (%s): SPF Processing Time(usecs): %lld", __func__,
+ (stop_time.tv_sec - start_time.tv_sec) * 1000000LL
+ + (stop_time.tv_usec - start_time.tv_usec));
}
/*
@@ -1620,8 +1640,8 @@ void ospf_sr_config_write_router(struct vty *vty)
vty_out(vty,
" segment-routing prefix %s/%u "
"index %u%s\n",
- inet_ntoa(srp->nhlfe.prefv4.prefix),
- srp->nhlfe.prefv4.prefixlen, srp->sid,
+ inet_ntoa(srp->prefv4.prefix),
+ srp->prefv4.prefixlen, srp->sid,
CHECK_FLAG(srp->flags,
EXT_SUBTLV_PREFIX_SID_NPFLG)
? " no-php-flag"
@@ -1650,22 +1670,19 @@ DEFUN(ospf_sr_enable,
return CMD_WARNING_CONFIG_FAILED;
}
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("SR: Segment Routing: OFF -> ON");
+ osr_debug("SR: Segment Routing: OFF -> ON");
/* Start Segment Routing */
OspfSR.enabled = true;
ospf_sr_start(ospf);
/* Set Router Information SR parameters */
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("SR: Activate SR for Router Information LSA");
+ osr_debug("SR: Activate SR for Router Information LSA");
ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
/* Update Ext LSA */
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("SR: Activate SR for Extended Link/Prefix LSA");
+ osr_debug("SR: Activate SR for Extended Link/Prefix LSA");
ospf_ext_update_sr(true);
@@ -1683,8 +1700,7 @@ DEFUN (no_ospf_sr_enable,
if (!OspfSR.enabled)
return CMD_SUCCESS;
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("SR: Segment Routing: ON -> OFF");
+ osr_debug("SR: Segment Routing: ON -> OFF");
/* Start by Disabling Extended Link & Prefix LSA */
ospf_ext_update_sr(false);
@@ -1694,7 +1710,6 @@ DEFUN (no_ospf_sr_enable,
/* Finally, stop Segment Routing */
ospf_sr_stop();
- OspfSR.enabled = false;
return CMD_SUCCESS;
}
@@ -1913,22 +1928,20 @@ DEFUN (sr_prefix_sid,
/* Create new Extended Prefix to SRDB if not found */
new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
- IPV4_ADDR_COPY(&new->nhlfe.prefv4.prefix, &p.u.prefix4);
- IPV4_ADDR_COPY(&new->nhlfe.nexthop, &p.u.prefix4);
- new->nhlfe.prefv4.prefixlen = p.prefixlen;
- new->nhlfe.prefv4.family = p.family;
+ IPV4_ADDR_COPY(&new->prefv4.prefix, &p.u.prefix4);
+ new->prefv4.prefixlen = p.prefixlen;
+ new->prefv4.family = p.family;
new->sid = index;
+ new->type = LOCAL_SID;
/* Set NO PHP flag if present and compute NHLFE */
if (argv_find(argv, argc, "no-php-flag", &idx)) {
SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG);
- new->nhlfe.label_in = index2label(new->sid, OspfSR.self->srgb);
+ new->label_in = index2label(new->sid, OspfSR.self->srgb);
new->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL;
}
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("SR (%s): Add new index %u to Prefix %s/%u",
- __func__, index, inet_ntoa(new->nhlfe.prefv4.prefix),
- new->nhlfe.prefv4.prefixlen);
+ osr_debug("SR (%s): Add new index %u to Prefix %pFX", __func__, index,
+ (struct prefix *)&new->prefv4);
/* Get Interface and check if it is a Loopback */
ifp = if_lookup_prefix(&p, VRF_DEFAULT);
@@ -1941,9 +1954,9 @@ DEFUN (sr_prefix_sid,
*/
listnode_add(OspfSR.self->ext_prefix, new);
zlog_info(
- "Interface for prefix %s/%u not found. Deferred LSA "
+ "Interface for prefix %pFX not found. Deferred LSA "
"flooding",
- inet_ntoa(p.u.prefix4), p.prefixlen);
+ &p);
return CMD_SUCCESS;
}
@@ -1956,8 +1969,8 @@ DEFUN (sr_prefix_sid,
/* Search if this prefix already exist */
for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
- if ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4)
- && srp->nhlfe.prefv4.prefixlen == p.prefixlen))
+ if ((IPV4_ADDR_SAME(&srp->prefv4.prefix, &p.u.prefix4)
+ && srp->prefv4.prefixlen == p.prefixlen))
break;
else
srp = NULL;
@@ -1965,17 +1978,16 @@ DEFUN (sr_prefix_sid,
/* Update or Add this new SR Prefix */
if (srp) {
- update_sid_nhlfe(srp->nhlfe, new->nhlfe);
listnode_delete(OspfSR.self->ext_prefix, srp);
listnode_add(OspfSR.self->ext_prefix, new);
} else {
listnode_add(OspfSR.self->ext_prefix, new);
- add_sid_nhlfe(new->nhlfe);
}
+ ospf_zebra_update_prefix_sid(new);
/* Finally, update Extended Prefix LSA */
new->instance = ospf_ext_schedule_prefix_index(
- ifp, new->sid, &new->nhlfe.prefv4, new->flags);
+ ifp, new->sid, &new->prefv4, new->flags);
if (new->instance == 0) {
vty_out(vty, "Unable to set index %u for prefix %s/%u\n", index,
inet_ntoa(p.u.prefix4), p.prefixlen);
@@ -2017,8 +2029,8 @@ DEFUN (no_sr_prefix_sid,
/* check that the prefix is already set */
for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp))
- if (IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4)
- && (srp->nhlfe.prefv4.prefixlen == p.prefixlen)) {
+ if (IPV4_ADDR_SAME(&srp->prefv4.prefix, &p.u.prefix4)
+ && (srp->prefv4.prefixlen == p.prefixlen)) {
found = true;
break;
}
@@ -2043,14 +2055,12 @@ DEFUN (no_sr_prefix_sid,
return CMD_WARNING;
}
- if (IS_DEBUG_OSPF_SR)
- zlog_debug("SR (%s): Remove Prefix %s/%u with index %u",
- __func__, inet_ntoa(srp->nhlfe.prefv4.prefix),
- srp->nhlfe.prefv4.prefixlen, srp->sid);
+ osr_debug("SR (%s): Remove Prefix %pFX with index %u", __func__,
+ (struct prefix *)&srp->prefv4, srp->sid);
- /* Delete NHLFE is NO-PHP is set */
+ /* Delete NHLFE if NO-PHP is set */
if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))
- del_sid_nhlfe(srp->nhlfe);
+ ospf_zebra_delete_prefix_sid(srp);
/* OK, all is clean, remove SRP from SRDB */
listnode_delete(OspfSR.self->ext_prefix, srp);
@@ -2060,6 +2070,116 @@ DEFUN (no_sr_prefix_sid,
}
+static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in,
+ mpls_label_t label_out)
+{
+ if (size < 24)
+ return NULL;
+
+ switch (label_out) {
+ case MPLS_LABEL_IMPLICIT_NULL:
+ snprintf(buf, size, "Pop(%u)", label_in);
+ break;
+ case MPLS_LABEL_IPV4_EXPLICIT_NULL:
+ snprintf(buf, size, "Swap(%u, null)", label_in);
+ break;
+ case MPLS_INVALID_LABEL:
+ snprintf(buf, size, "no-op.");
+ break;
+ default:
+ snprintf(buf, size, "Swap(%u, %u)", label_in, label_out);
+ break;
+ }
+ return buf;
+}
+
+static void show_sr_prefix(struct sbuf *sbuf, struct json_object *json,
+ struct sr_prefix *srp)
+{
+
+ struct listnode *node;
+ struct ospf_path *path;
+ struct interface *itf;
+ json_object *json_route = NULL, *json_obj;
+ char pref[19];
+ char sid[22];
+ char op[32];
+ int indent = 0;
+
+ snprintfrr(pref, 19, "%pFX", (struct prefix *)&srp->prefv4);
+ snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid);
+ if (json) {
+ json_object_string_add(json, "prefix", pref);
+ json_object_int_add(json, "sid", srp->sid);
+ json_object_int_add(json, "inputLabel", srp->label_in);
+ } else {
+ sbuf_push(sbuf, 0, "%18s %21s ", pref, sid);
+ }
+
+ /* Check if it is a Local Node SID */
+ if (srp->type == LOCAL_SID) {
+ itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT);
+ if (json) {
+ if (!json_route) {
+ json_route = json_object_new_array();
+ json_object_object_add(json, "prefixRoute",
+ json_route);
+ }
+ json_obj = json_object_new_object();
+ json_object_int_add(json_obj, "outputLabel",
+ srp->nhlfe.label_out);
+ json_object_string_add(json_obj, "interface",
+ itf ? itf->name : "-");
+ json_object_string_add(json_obj, "nexthop",
+ inet_ntoa(srp->nhlfe.nexthop));
+ json_object_array_add(json_route, json_obj);
+ } else {
+ sbuf_push(sbuf, 0, "%20s %9s %15s\n",
+ sr_op2str(op, 32, srp->label_in,
+ srp->nhlfe.label_out),
+ itf ? itf->name : "-",
+ inet_ntoa(srp->nhlfe.nexthop));
+ }
+ return;
+ }
+
+ /* Check if we have a valid path for this prefix */
+ if (srp->route == NULL) {
+ if (!json) {
+ sbuf_push(sbuf, 0, "\n");
+ }
+ return;
+ }
+
+ /* Process list of OSPF paths */
+ for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) {
+ itf = if_lookup_by_index(path->ifindex, VRF_DEFAULT);
+ if (json) {
+ if (!json_route) {
+ json_route = json_object_new_array();
+ json_object_object_add(json, "prefixRoute",
+ json_route);
+ }
+ json_obj = json_object_new_object();
+ json_object_int_add(json_obj, "outputLabel",
+ path->srni.label_out);
+ json_object_string_add(json_obj, "interface",
+ itf ? itf->name : "-");
+ json_object_string_add(json_obj, "nexthop",
+ inet_ntoa(path->nexthop));
+ json_object_array_add(json_route, json_obj);
+ } else {
+ sbuf_push(sbuf, indent, "%20s %9s %15s\n",
+ sr_op2str(op, 32, srp->label_in,
+ path->srni.label_out),
+ itf ? itf->name : "-",
+ inet_ntoa(path->nexthop));
+ /* Offset to align information for ECMP */
+ indent = 43;
+ }
+ }
+}
+
static void show_sr_node(struct vty *vty, struct json_object *json,
struct sr_node *srn)
{
@@ -2068,9 +2188,10 @@ static void show_sr_node(struct vty *vty, struct json_object *json,
struct sr_link *srl;
struct sr_prefix *srp;
struct interface *itf;
+ struct sbuf sbuf;
char pref[19];
char sid[22];
- char label[8];
+ char op[32];
json_object *json_node = NULL, *json_algo, *json_obj;
json_object *json_prefix = NULL, *json_link = NULL;
@@ -2078,6 +2199,8 @@ static void show_sr_node(struct vty *vty, struct json_object *json,
if (srn == NULL)
return;
+ sbuf_init(&sbuf, NULL, 0);
+
if (json) {
json_node = json_object_new_object();
json_object_string_add(json_node, "routerID",
@@ -2104,41 +2227,31 @@ static void show_sr_node(struct vty *vty, struct json_object *json,
if (srn->msd != 0)
json_object_int_add(json_node, "nodeMsd", srn->msd);
} else {
- vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router));
- vty_out(vty, "\tSRGB (Size/Label): %u/%u", srn->srgb.range_size,
- srn->srgb.lower_bound);
- vty_out(vty, "\tAlgorithm(s): %s",
- srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
+ sbuf_push(&sbuf, 0, "SR-Node: %s", inet_ntoa(srn->adv_router));
+ sbuf_push(&sbuf, 0, "\tSRGB (Size/Label): %u/%u",
+ srn->srgb.range_size, srn->srgb.lower_bound);
+ sbuf_push(&sbuf, 0, "\tAlgorithm(s): %s",
+ srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
for (int i = 1; i < ALGORITHM_COUNT; i++) {
if (srn->algo[i] == SR_ALGORITHM_UNSET)
continue;
- vty_out(vty, "/%s",
- srn->algo[i] == SR_ALGORITHM_SPF ? "SPF"
- : "S-SPF");
+ sbuf_push(&sbuf, 0, "/%s",
+ srn->algo[i] == SR_ALGORITHM_SPF ? "SPF"
+ : "S-SPF");
}
if (srn->msd != 0)
- vty_out(vty, "\tMSD: %u", srn->msd);
+ sbuf_push(&sbuf, 0, "\tMSD: %u", srn->msd);
}
if (!json) {
- vty_out(vty,
- "\n\n Prefix or Link Label In Label Out "
- "Node or Adj. SID Interface Nexthop\n");
- vty_out(vty,
- "------------------ -------- --------- "
- "--------------------- --------- ---------------\n");
+ sbuf_push(&sbuf, 0,
+ "\n\n Prefix or Link Node or Adj. SID "
+ " Label Operation Interface Nexthop\n");
+ sbuf_push(&sbuf, 0,
+ "------------------ --------------------- "
+ "-------------------- --------- ---------------\n");
}
for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
- snprintf(pref, sizeof(pref), "%s/%u",
- inet_ntoa(srp->nhlfe.prefv4.prefix),
- srp->nhlfe.prefv4.prefixlen);
- snprintf(sid, sizeof(sid), "SR Pfx (idx %u)", srp->sid);
- if (srp->nhlfe.label_out == MPLS_LABEL_IMPLICIT_NULL)
- snprintf(label, sizeof(label), "pop");
- else
- snprintf(label, sizeof(label), "%u",
- srp->nhlfe.label_out);
- itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT);
if (json) {
if (!json_prefix) {
json_prefix = json_object_new_array();
@@ -2147,34 +2260,16 @@ static void show_sr_node(struct vty *vty, struct json_object *json,
json_prefix);
}
json_obj = json_object_new_object();
- json_object_string_add(json_obj, "prefix", pref);
- json_object_int_add(json_obj, "sid", srp->sid);
- json_object_int_add(json_obj, "inputLabel",
- srp->nhlfe.label_in);
- json_object_string_add(json_obj, "outputLabel", label);
- json_object_string_add(json_obj, "interface",
- itf ? itf->name : "-");
- json_object_string_add(json_obj, "nexthop",
- inet_ntoa(srp->nhlfe.nexthop));
+ show_sr_prefix(NULL, json_obj, srp);
json_object_array_add(json_prefix, json_obj);
} else {
- vty_out(vty, "%18s %8u %9s %21s %9s %15s\n", pref,
- srp->nhlfe.label_in, label, sid,
- itf ? itf->name : "-",
- inet_ntoa(srp->nhlfe.nexthop));
+ show_sr_prefix(&sbuf, NULL, srp);
}
}
for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) {
- snprintf(pref, sizeof(pref), "%s/%u",
- inet_ntoa(srl->nhlfe[0].prefv4.prefix),
- srl->nhlfe[0].prefv4.prefixlen);
- snprintf(sid, sizeof(sid), "SR Adj. (lbl %u)", srl->sid[0]);
- if (srl->nhlfe[0].label_out == MPLS_LABEL_IMPLICIT_NULL)
- snprintf(label, sizeof(label), "pop");
- else
- snprintf(label, sizeof(label), "%u",
- srl->nhlfe[0].label_out);
+ snprintfrr(pref, 19, "%pI4/32", &srl->itf_addr);
+ snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]);
itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT);
if (json) {
if (!json_link) {
@@ -2188,7 +2283,8 @@ static void show_sr_node(struct vty *vty, struct json_object *json,
json_object_int_add(json_obj, "sid", srl->sid[0]);
json_object_int_add(json_obj, "inputLabel",
srl->nhlfe[0].label_in);
- json_object_string_add(json_obj, "outputLabel", label);
+ json_object_int_add(json_obj, "outputLabel",
+ srl->nhlfe[0].label_out);
json_object_string_add(json_obj, "interface",
itf ? itf->name : "-");
json_object_string_add(
@@ -2197,18 +2293,13 @@ static void show_sr_node(struct vty *vty, struct json_object *json,
json_object_array_add(json_link, json_obj);
/* Backup Link */
json_obj = json_object_new_object();
- snprintf(sid, sizeof(sid), "SR Adj. (lbl %u)",
- srl->sid[1]);
- if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL)
- snprintf(label, sizeof(label), "pop");
- else
- snprintf(label, sizeof(label), "%u",
- srl->nhlfe[0].label_out);
+ snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]);
json_object_string_add(json_obj, "prefix", pref);
json_object_int_add(json_obj, "sid", srl->sid[1]);
json_object_int_add(json_obj, "inputLabel",
srl->nhlfe[1].label_in);
- json_object_string_add(json_obj, "outputLabel", label);
+ json_object_int_add(json_obj, "outputLabel",
+ srl->nhlfe[1].label_out);
json_object_string_add(json_obj, "interface",
itf ? itf->name : "-");
json_object_string_add(
@@ -2216,27 +2307,27 @@ static void show_sr_node(struct vty *vty, struct json_object *json,
inet_ntoa(srl->nhlfe[1].nexthop));
json_object_array_add(json_link, json_obj);
} else {
- vty_out(vty, "%18s %8u %9s %21s %9s %15s\n", pref,
- srl->nhlfe[0].label_in, label, sid,
- itf ? itf->name : "-",
- inet_ntoa(srl->nhlfe[0].nexthop));
- snprintf(sid, sizeof(sid), "SR Adj. (lbl %u)",
- srl->sid[1]);
- if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL)
- snprintf(label, sizeof(label), "pop");
- else
- snprintf(label, sizeof(label), "%u",
- srl->nhlfe[1].label_out);
- vty_out(vty, "%18s %8u %9s %21s %9s %15s\n", pref,
- srl->nhlfe[1].label_in, label, sid,
- itf ? itf->name : "-",
- inet_ntoa(srl->nhlfe[1].nexthop));
+ sbuf_push(&sbuf, 0, "%18s %21s %20s %9s %15s\n",
+ pref, sid,
+ sr_op2str(op, 32, srl->nhlfe[0].label_in,
+ srl->nhlfe[0].label_out),
+ itf ? itf->name : "-",
+ inet_ntoa(srl->nhlfe[0].nexthop));
+ snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]);
+ sbuf_push(&sbuf, 0, "%18s %21s %20s %9s %15s\n",
+ pref, sid,
+ sr_op2str(op, 32, srl->nhlfe[1].label_in,
+ srl->nhlfe[1].label_out),
+ itf ? itf->name : "-",
+ inet_ntoa(srl->nhlfe[1].nexthop));
}
}
if (json)
json_object_array_add(json, json_node);
else
- vty_out(vty, "\n");
+ vty_out(vty, "%s\n", sbuf_buf(&sbuf));
+
+ sbuf_free(&sbuf);
}
static void show_vty_srdb(struct hash_bucket *bucket, void *args)
diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h
index df923e970f..3621ea53de 100644
--- a/ospfd/ospf_sr.h
+++ b/ospfd/ospf_sr.h
@@ -51,6 +51,17 @@
#define SID_INDEX 4
#define SID_INDEX_SIZE(U) (U)
+/* Macro to log debug message */
+#define osr_debug(...) \
+ do { \
+ if (IS_DEBUG_OSPF_SR) \
+ zlog_debug(__VA_ARGS__); \
+ } while (0)
+
+/* Macro to check if SR Prefix has no valid route */
+#define IS_NO_ROUTE(srp) ((srp->route == NULL) || (srp->route->paths == NULL) \
+ || list_isempty(srp->route->paths))
+
/* SID/Label Sub TLV - section 2.1 */
#define SUBTLV_SID_LABEL 1
#define SUBTLV_SID_LABEL_SIZE 8
@@ -180,16 +191,13 @@ struct sr_srgb {
};
/* SID type to make difference between loopback interfaces and others */
-enum sid_type { PREF_SID, ADJ_SID, LAN_ADJ_SID };
+enum sid_type { PREF_SID, LOCAL_SID, ADJ_SID, LAN_ADJ_SID };
/* Structure aggregating all OSPF Segment Routing information for the node */
struct ospf_sr_db {
/* Status of Segment Routing: enable or disable */
bool enabled;
- /* Ongoing Update following an OSPF SPF */
- bool update;
-
/* Flooding Scope: Area = 10 or AS = 11 */
uint8_t scope;
@@ -237,7 +245,6 @@ struct sr_node {
/* Segment Routing - NHLFE info: support IPv4 Only */
struct sr_nhlfe {
- struct prefix_ipv4 prefv4;
struct in_addr nexthop;
ifindex_t ifindex;
mpls_label_t label_in;
@@ -251,6 +258,9 @@ struct sr_link {
/* 24-bit Opaque-ID field value according to RFC 7684 specification */
uint32_t instance;
+ /* Interface address */
+ struct in_addr itf_addr;
+
/* Flags to manage this link parameters. */
uint8_t flags[2];
@@ -258,7 +268,7 @@ struct sr_link {
uint32_t sid[2];
enum sid_type type;
- /* SR NHLFE for this link */
+ /* SR NHLFE (Primary + Backup) for this link */
struct sr_nhlfe nhlfe[2];
/* Back pointer to SR Node which advertise this Link */
@@ -271,6 +281,9 @@ struct sr_prefix {
/* 24-bit Opaque-ID field value according to RFC 7684 specification */
uint32_t instance;
+ /* Prefix itself */
+ struct prefix_ipv4 prefv4;
+
/* Flags to manage this prefix parameters. */
uint8_t flags;
@@ -278,17 +291,17 @@ struct sr_prefix {
uint32_t sid;
enum sid_type type;
- /* SR NHLFE for this prefix */
+ /* Incoming label for this prefix */
+ mpls_label_t label_in;
+
+ /* Back pointer to OSPF Route for remote prefix */
+ struct ospf_route *route;
+
+ /* NHLFE for local prefix */
struct sr_nhlfe nhlfe;
/* Back pointer to SR Node which advertise this Prefix */
struct sr_node *srn;
-
- /*
- * Pointer to SR Node which is the next hop for this Prefix
- * or NULL if next hop is the destination of the prefix
- */
- struct sr_node *nexthop;
};
/* Prototypes definition */
@@ -303,10 +316,15 @@ extern void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa);
extern void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa);
extern void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa);
extern void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa);
+/* Segment Routing Extending Link management */
+struct ext_itf;
+extern void ospf_sr_ext_itf_add(struct ext_itf *exti);
+extern void ospf_sr_ext_itf_delete(struct ext_itf *exti);
/* Segment Routing configuration functions */
extern uint32_t get_ext_link_label_value(void);
extern void ospf_sr_config_write_router(struct vty *vty);
-extern void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p);
+extern void ospf_sr_update_local_prefix(struct interface *ifp,
+ struct prefix *p);
/* Segment Routing re-routing function */
-extern void ospf_sr_update_timer_add(struct ospf *ospf);
+extern void ospf_sr_update_task(struct ospf *ospf);
#endif /* _FRR_OSPF_SR_H */
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index e8e2fbeaa6..644ea7f922 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -50,6 +50,7 @@
#include "ospfd/ospf_nsm.h"
#include "ospfd/ospf_zebra.h"
#include "ospfd/ospf_te.h"
+#include "ospfd/ospf_sr.h"
DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table")
DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute")
@@ -415,6 +416,119 @@ void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance)
}
}
+/* Update NHLFE for Prefix SID */
+void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp)
+{
+ struct zapi_labels zl;
+ struct zapi_nexthop *znh;
+ struct listnode *node;
+ struct ospf_path *path;
+
+ osr_debug("SR (%s): Update Labels %u for Prefix %pFX", __func__,
+ srp->label_in, (struct prefix *)&srp->prefv4);
+
+ /* Prepare message. */
+ memset(&zl, 0, sizeof(zl));
+ zl.type = ZEBRA_LSP_OSPF_SR;
+ zl.local_label = srp->label_in;
+
+ switch (srp->type) {
+ case LOCAL_SID:
+ /* Set Label for local Prefix */
+ znh = &zl.nexthops[zl.nexthop_num++];
+ znh->type = NEXTHOP_TYPE_IFINDEX;
+ znh->ifindex = srp->nhlfe.ifindex;
+ znh->label_num = 1;
+ znh->labels[0] = srp->nhlfe.label_out;
+ break;
+
+ case PREF_SID:
+ /* Update route in the RIB too. */
+ SET_FLAG(zl.message, ZAPI_LABELS_FTN);
+ zl.route.prefix.u.prefix4 = srp->prefv4.prefix;
+ zl.route.prefix.prefixlen = srp->prefv4.prefixlen;
+ zl.route.prefix.family = srp->prefv4.family;
+ zl.route.type = ZEBRA_ROUTE_OSPF;
+ zl.route.instance = 0;
+
+ /* Check that SRP contains at least one valid path */
+ if (srp->route == NULL) {
+ return;
+ }
+ for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) {
+ if (path->srni.label_out == MPLS_INVALID_LABEL)
+ continue;
+
+ if (zl.nexthop_num >= MULTIPATH_NUM)
+ break;
+
+ znh = &zl.nexthops[zl.nexthop_num++];
+ znh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ znh->gate.ipv4 = path->nexthop;
+ znh->ifindex = path->ifindex;
+ znh->label_num = 1;
+ znh->labels[0] = path->srni.label_out;
+ }
+ break;
+ default:
+ return;
+ }
+
+ /* Finally, send message to zebra. */
+ (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_REPLACE, &zl);
+}
+
+/* Remove NHLFE for Prefix-SID */
+void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp)
+{
+ struct zapi_labels zl;
+
+ osr_debug("SR (%s): Delete Labels %u for Prefix %pFX", __func__,
+ srp->label_in, (struct prefix *)&srp->prefv4);
+
+ /* Prepare message. */
+ memset(&zl, 0, sizeof(zl));
+ zl.type = ZEBRA_LSP_OSPF_SR;
+ zl.local_label = srp->label_in;
+
+ if (srp->type == PREF_SID) {
+ /* Update route in the RIB too */
+ SET_FLAG(zl.message, ZAPI_LABELS_FTN);
+ zl.route.prefix.u.prefix4 = srp->prefv4.prefix;
+ zl.route.prefix.prefixlen = srp->prefv4.prefixlen;
+ zl.route.prefix.family = srp->prefv4.family;
+ zl.route.type = ZEBRA_ROUTE_OSPF;
+ zl.route.instance = 0;
+ }
+
+ /* Send message to zebra. */
+ (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_DELETE, &zl);
+}
+
+/* Send MPLS Label entry to Zebra for installation or deletion */
+void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe)
+{
+ struct zapi_labels zl;
+ struct zapi_nexthop *znh;
+
+ osr_debug("SR (%s): %s Labels %u/%u for Adjacency via %u", __func__,
+ cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete",
+ nhlfe.label_in, nhlfe.label_out, nhlfe.ifindex);
+
+ memset(&zl, 0, sizeof(zl));
+ zl.type = ZEBRA_LSP_OSPF_SR;
+ zl.local_label = nhlfe.label_in;
+ zl.nexthop_num = 1;
+ znh = &zl.nexthops[0];
+ znh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ znh->gate.ipv4 = nhlfe.nexthop;
+ znh->ifindex = nhlfe.ifindex;
+ znh->label_num = 1;
+ znh->labels[0] = nhlfe.label_out;
+
+ (void)zebra_send_mpls_labels(zclient, cmd, &zl);
+}
+
struct ospf_redist *ospf_redist_lookup(struct ospf *ospf, uint8_t type,
unsigned short instance)
{
@@ -1351,6 +1465,7 @@ void ospf_zebra_vrf_deregister(struct ospf *ospf)
zclient_send_dereg_requests(zclient, ospf->vrf_id);
}
}
+
static void ospf_zebra_connected(struct zclient *zclient)
{
/* Send the client registration */
diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h
index d3f8a0380b..253d2e0a3f 100644
--- a/ospfd/ospf_zebra.h
+++ b/ospfd/ospf_zebra.h
@@ -63,6 +63,13 @@ extern struct ospf_external *ospf_external_lookup(struct ospf *, uint8_t,
unsigned short);
extern struct ospf_external *ospf_external_add(struct ospf *, uint8_t,
unsigned short);
+
+struct sr_prefix;
+struct sr_nhlfe;
+extern void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp);
+extern void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp);
+extern void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe);
+
extern void ospf_external_del(struct ospf *, uint8_t, unsigned short);
extern struct ospf_redist *ospf_redist_lookup(struct ospf *, uint8_t,
unsigned short);
@@ -70,7 +77,6 @@ extern struct ospf_redist *ospf_redist_add(struct ospf *, uint8_t,
unsigned short);
extern void ospf_redist_del(struct ospf *, uint8_t, unsigned short);
-
extern int ospf_redistribute_set(struct ospf *, int, unsigned short, int, int);
extern int ospf_redistribute_unset(struct ospf *, int, unsigned short);
extern int ospf_redistribute_default_set(struct ospf *, int, int, int);
diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c
index b228847f06..57338738f6 100644
--- a/pbrd/pbr_main.c
+++ b/pbrd/pbr_main.c
@@ -113,6 +113,7 @@ struct quagga_signal_t pbr_signals[] = {
#define PBR_VTY_PORT 2615
static const struct frr_yang_module_info *const pbrd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
};
diff --git a/pimd/pim_main.c b/pimd/pim_main.c
index 5c4c7151a5..132d913f68 100644
--- a/pimd/pim_main.c
+++ b/pimd/pim_main.c
@@ -73,6 +73,7 @@ struct zebra_privs_t pimd_privs = {
.cap_num_i = 0};
static const struct frr_yang_module_info *const pimd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_route_map_info,
&frr_vrf_info,
diff --git a/pimd/pim_mlag.c b/pimd/pim_mlag.c
index 78be914cee..02f206144f 100644
--- a/pimd/pim_mlag.c
+++ b/pimd/pim_mlag.c
@@ -721,14 +721,19 @@ static void pim_mlag_process_vxlan_update(struct mlag_vxlan *msg)
static void pim_mlag_process_mroute_add(struct mlag_mroute_add msg)
{
if (PIM_DEBUG_MLAG) {
+ struct prefix_sg sg;
+
+ sg.grp.s_addr = ntohl(msg.group_ip);
+ sg.src.s_addr = ntohl(msg.source_ip);
+
zlog_debug(
- "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x cost: %u",
- __func__, msg.vrf_name, msg.source_ip,
- msg.group_ip, msg.cost_to_rp);
+ "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x (%pSG4) cost: %u",
+ __func__, msg.vrf_name, msg.source_ip, msg.group_ip,
+ &sg, msg.cost_to_rp);
zlog_debug(
- "owner_id: %d, DR: %d, Dual active: %d, vrf_id: 0x%x intf_name: %s",
- msg.owner_id, msg.am_i_dr, msg.am_i_dual_active,
- msg.vrf_id, msg.intf_name);
+ "(%pSG4)owner_id: %d, DR: %d, Dual active: %d, vrf_id: 0x%x intf_name: %s",
+ &sg, msg.owner_id, msg.am_i_dr, msg.am_i_dual_active,
+ msg.vrf_id, msg.intf_name);
}
if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) {
@@ -746,12 +751,16 @@ static void pim_mlag_process_mroute_add(struct mlag_mroute_add msg)
static void pim_mlag_process_mroute_del(struct mlag_mroute_del msg)
{
if (PIM_DEBUG_MLAG) {
+ struct prefix_sg sg;
+
+ sg.grp.s_addr = ntohl(msg.group_ip);
+ sg.src.s_addr = ntohl(msg.source_ip);
zlog_debug(
- "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x ",
- __func__, msg.vrf_name, msg.source_ip,
- msg.group_ip);
- zlog_debug("owner_id: %d, vrf_id: 0x%x intf_name: %s",
- msg.owner_id, msg.vrf_id, msg.intf_name);
+ "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x(%pSG4)",
+ __func__, msg.vrf_name, msg.source_ip, msg.group_ip,
+ &sg);
+ zlog_debug("(%pSG4)owner_id: %d, vrf_id: 0x%x intf_name: %s",
+ &sg, msg.owner_id, msg.vrf_id, msg.intf_name);
}
if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) {
diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c
index 21febcc969..3ec0720fc4 100644
--- a/pimd/pim_oil.c
+++ b/pimd/pim_oil.c
@@ -375,6 +375,20 @@ static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil,
return do_mute;
}
+ if (PIM_I_am_DualActive(pim_ifp)) {
+ struct pim_upstream *starup = c_oil->up->parent;
+ if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil->up->flags)
+ && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags)))
+ do_mute = true;
+
+ /* In case entry is (S,G), Negotiation happens at (*.G) */
+ if (starup
+
+ && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup->flags)
+ && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup->flags)))
+ do_mute = true;
+ return do_mute;
+ }
return do_mute;
}
diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h
index b3379c67b2..8030835fb2 100644
--- a/pimd/pim_upstream.h
+++ b/pimd/pim_upstream.h
@@ -286,7 +286,8 @@ static inline bool pim_up_mlag_is_local(struct pim_upstream *up)
/* XXX: extend this to also return true if the channel-oil has
* any AA devices
*/
- return (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN);
+ return (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN
+ | PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE));
}
struct pim_upstream *pim_upstream_find(struct pim_instance *pim,
diff --git a/ripd/rip_main.c b/ripd/rip_main.c
index 9ec32a53e3..7e381887fc 100644
--- a/ripd/rip_main.c
+++ b/ripd/rip_main.c
@@ -114,6 +114,7 @@ static struct quagga_signal_t ripd_signals[] = {
};
static const struct frr_yang_module_info *const ripd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_ripd_info,
&frr_route_map_info,
diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c
index fbac750db3..010bac851b 100644
--- a/ripngd/ripng_main.c
+++ b/ripngd/ripng_main.c
@@ -114,6 +114,7 @@ struct quagga_signal_t ripng_signals[] = {
};
static const struct frr_yang_module_info *const ripngd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_ripngd_info,
&frr_route_map_info,
diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c
index 120d704918..db65836388 100644
--- a/sharpd/sharp_main.c
+++ b/sharpd/sharp_main.c
@@ -112,6 +112,7 @@ struct quagga_signal_t sharp_signals[] = {
#define SHARP_VTY_PORT 2614
static const struct frr_yang_module_info *const sharpd_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_route_map_info,
&frr_vrf_info,
diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c
index 77d0d02e7d..72e9c22f17 100644
--- a/sharpd/sharp_vty.c
+++ b/sharpd/sharp_vty.c
@@ -550,14 +550,41 @@ DEFPY (logpump,
DEFPY (send_opaque,
send_opaque_cmd,
"sharp send opaque type (1-255) (1-1000)$count",
- "Sharp Routing Protocol\n"
+ SHARP_STR
"Send messages for testing\n"
"Send opaque messages\n"
"Type code to send\n"
"Type code to send\n"
"Number of messages to send\n")
{
- sharp_opaque_send(type, count);
+ sharp_opaque_send(type, 0, 0, 0, count);
+ return CMD_SUCCESS;
+}
+
+DEFPY (send_opaque_unicast,
+ send_opaque_unicast_cmd,
+ "sharp send opaque unicast type (1-255) \
+ " FRR_IP_REDIST_STR_ZEBRA "$proto_str \
+ [{instance (0-1000) | session (1-1000)}] (1-1000)$count",
+ SHARP_STR
+ "Send messages for testing\n"
+ "Send opaque messages\n"
+ "Send unicast messages\n"
+ "Type code to send\n"
+ "Type code to send\n"
+ FRR_IP_REDIST_HELP_STR_ZEBRA
+ "Daemon instance\n"
+ "Daemon instance\n"
+ "Session ID\n"
+ "Session ID\n"
+ "Number of messages to send\n")
+{
+ uint32_t proto;
+
+ proto = proto_redistnum(AFI_IP, proto_str);
+
+ sharp_opaque_send(type, proto, instance, session, count);
+
return CMD_SUCCESS;
}
@@ -566,7 +593,7 @@ DEFPY (send_opaque_reg,
"sharp send opaque <reg$reg | unreg> \
" FRR_IP_REDIST_STR_ZEBRA "$proto_str \
[{instance (0-1000) | session (1-1000)}] type (1-1000)",
- "Sharp Routing Protocol\n"
+ SHARP_STR
"Send messages for testing\n"
"Send opaque messages\n"
"Send opaque registration\n"
@@ -600,6 +627,7 @@ void sharp_vty_init(void)
install_element(ENABLE_NODE, &sharp_remove_lsp_prefix_v4_cmd);
install_element(ENABLE_NODE, &logpump_cmd);
install_element(ENABLE_NODE, &send_opaque_cmd);
+ install_element(ENABLE_NODE, &send_opaque_unicast_cmd);
install_element(ENABLE_NODE, &send_opaque_reg_cmd);
install_element(VIEW_NODE, &show_debugging_sharpd_cmd);
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index 34cc1a4b5a..6ebc04b9eb 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -483,16 +483,16 @@ static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS)
/* Handler for opaque messages */
static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS)
{
- uint32_t type;
struct stream *s;
+ struct zapi_opaque_msg info;
s = zclient->ibuf;
- STREAM_GETL(s, type);
-
- zlog_debug("%s: received opaque type %u", __func__, type);
+ if (zclient_opaque_decode(s, &info) != 0)
+ return -1;
-stream_failure:
+ zlog_debug("%s: [%d] received opaque type %u", __func__,
+ zclient->session_id, info.type);
return 0;
}
@@ -500,7 +500,8 @@ stream_failure:
/*
* Send OPAQUE messages, using subtype 'type'.
*/
-void sharp_opaque_send(uint32_t type, uint32_t count)
+void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance,
+ uint32_t session_id, uint32_t count)
{
uint8_t buf[32];
int ret;
@@ -514,9 +515,15 @@ void sharp_opaque_send(uint32_t type, uint32_t count)
buf[i] = 255;
}
- /* Send some messages */
+ /* Send some messages - broadcast and unicast are supported */
for (i = 0; i < count; i++) {
- ret = zclient_send_opaque(zclient, type, buf, sizeof(buf));
+ if (proto == 0)
+ ret = zclient_send_opaque(zclient, type, buf,
+ sizeof(buf));
+ else
+ ret = zclient_send_opaque_unicast(zclient, type, proto,
+ instance, session_id,
+ buf, sizeof(buf));
if (ret < 0) {
zlog_debug("%s: send_opaque() failed => %d",
__func__, ret);
diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h
index 7c714b52d3..7604f4b0a4 100644
--- a/sharpd/sharp_zebra.h
+++ b/sharpd/sharp_zebra.h
@@ -46,7 +46,8 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p,
const struct nexthop_group *backup_nhg);
/* Send OPAQUE messages, using subtype 'type'. */
-void sharp_opaque_send(uint32_t type, uint32_t count);
+void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance,
+ uint32_t session_id, uint32_t count);
/* Send OPAQUE registration messages, using subtype 'type'. */
void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
diff --git a/staticd/static_main.c b/staticd/static_main.c
index c77a99f280..08062f19d8 100644
--- a/staticd/static_main.c
+++ b/staticd/static_main.c
@@ -104,6 +104,7 @@ struct quagga_signal_t static_signals[] = {
};
static const struct frr_yang_module_info *const staticd_yang_modules[] = {
+ &frr_filter_info,
&frr_vrf_info,
};
diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py
index ebd6075b52..459af486ff 100644
--- a/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py
@@ -102,12 +102,12 @@ def test_bgp_as_wide_bgp_identifier():
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge, tgen.gears["r1"])
- success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5)
assert result is None, 'Failed to converge: "{}"'.format(tgen.gears["r1"])
test_func = functools.partial(_bgp_failed, tgen.gears["r3"])
- success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5)
assert result is None, 'Bad BGP Identifier notification not sent: "{}"'.format(
tgen.gears["r3"]
diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
index 18e36311ad..5c2af2b30b 100644
--- a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
+++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
@@ -121,7 +121,7 @@ def test_ebgp_requires_policy():
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge, "r2")
- success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ success, result = topotest.run_and_expect(test_func, None, count=65, wait=2)
assert success is True, 'Failed bgp convergence (r2) in "{}"'.format(tgen.gears["r2"])
test_func = functools.partial(_bgp_has_routes, "r2")
@@ -129,7 +129,7 @@ def test_ebgp_requires_policy():
assert success is True, 'eBGP policy is not working (r2) in "{}"'.format(tgen.gears["r2"])
test_func = functools.partial(_bgp_converge, "r4")
- success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ success, result = topotest.run_and_expect(test_func, None, count=65, wait=2)
assert success is True, 'Failed bgp convergence (r4) in "{}"'.format(tgen.gears["r4"])
test_func = functools.partial(_bgp_has_routes, "r4")
@@ -137,7 +137,7 @@ def test_ebgp_requires_policy():
assert success is False, 'eBGP policy is not working (r4) in "{}"'.format(tgen.gears["r4"])
test_func = functools.partial(_bgp_converge, "r6")
- success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ success, result = topotest.run_and_expect(test_func, None, count=65, wait=2)
assert success is True, 'Failed bgp convergence (r6) in "{}"'.format(tgen.gears["r6"])
test_func = functools.partial(_bgp_has_routes, "r6")
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
index 69c807f300..2ba0c68c2f 100644
--- a/tests/topotests/lib/bgp.py
+++ b/tests/topotests/lib/bgp.py
@@ -868,7 +868,7 @@ def verify_router_id(tgen, topo, input_dict):
return True
-@retry(attempts=20, wait=2, return_is_str=True)
+@retry(attempts=44, wait=3, return_is_str=True)
def verify_bgp_convergence(tgen, topo, dut=None):
"""
API will verify if BGP is converged with in the given time frame.
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index 414dc17874..efd5b90685 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -658,7 +658,7 @@ class TopoRouter(TopoGear):
Possible daemon values are: TopoRouter.RD_ZEBRA, TopoRouter.RD_RIP,
TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6,
TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP,
- TopoRouter.RD_PIM.
+ TopoRouter.RD_PIM, TopoRouter.RD_PBR.
"""
daemonstr = self.RD.get(daemon)
self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source))
@@ -1064,6 +1064,7 @@ def diagnose_env_linux():
"isisd",
"pimd",
"ldpd",
+ "pbrd"
]:
path = os.path.join(frrdir, fname)
if not os.path.isfile(path):
@@ -1121,6 +1122,7 @@ def diagnose_env_linux():
"ripngd",
"isisd",
"pimd",
+ "pbrd"
]:
path = os.path.join(quaggadir, fname)
if not os.path.isfile(path):
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index 6262082193..9d945d5262 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -727,6 +727,57 @@ def ip6_route(node):
return result
+def ip_rules(node):
+ """
+ Gets a structured return of the command 'ip rule'. It can be used in
+ conjuction with json_cmp() to provide accurate assert explanations.
+
+ Return example:
+ [
+ {
+ "pref": "0"
+ "from": "all"
+ },
+ {
+ "pref": "32766"
+ "from": "all"
+ },
+ {
+ "to": "3.4.5.0/24",
+ "iif": "r1-eth2",
+ "pref": "304",
+ "from": "1.2.0.0/16",
+ "proto": "zebra"
+ }
+ ]
+ """
+ output = normalize_text(node.run("ip rule")).splitlines()
+ result = []
+ for line in output:
+ columns = line.split(" ")
+
+ route = {}
+ # remove last character, since it is ':'
+ pref = columns[0][:-1]
+ route["pref"] = pref
+ prev = None
+ for column in columns:
+ if prev == "from":
+ route["from"] = column
+ if prev == "to":
+ route["to"] = column
+ if prev == "proto":
+ route["proto"] = column
+ if prev == "iif":
+ route["iif"] = column
+ if prev == "fwmark":
+ route["fwmark"] = column
+ prev = column
+
+ result.append(route)
+ return result
+
+
def sleep(amount, reason=None):
"""
Sleep wrapper that registers in the log the amount of sleep
diff --git a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json
index 4ffca6f940..0416bd6ce2 100644
--- a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json
+++ b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json
@@ -15,9 +15,18 @@
"prefix":"10.0.255.2\/32",
"sid":200,
"inputLabel":20200,
- "outputLabel":"pop",
- "interface":"r1-eth0",
- "nexthop":"10.0.1.2"
+ "prefixRoute":[
+ {
+ "outputLabel":3,
+ "interface":"r1-eth0",
+ "nexthop":"10.0.0.2"
+ },
+ {
+ "outputLabel":3,
+ "interface":"r1-eth1",
+ "nexthop":"10.0.1.2"
+ }
+ ]
}
]
},
@@ -36,9 +45,18 @@
"prefix":"10.0.255.4\/32",
"sid":400,
"inputLabel":20400,
- "outputLabel":"8400",
- "interface":"r1-eth0",
- "nexthop":"10.0.1.2"
+ "prefixRoute":[
+ {
+ "outputLabel":8400,
+ "interface":"r1-eth0",
+ "nexthop":"10.0.0.2"
+ },
+ {
+ "outputLabel":8400,
+ "interface":"r1-eth1",
+ "nexthop":"10.0.1.2"
+ }
+ ]
}
]
},
@@ -47,19 +65,24 @@
"srgbSize":10000,
"srgbLabel":10000,
"algorithms":[
- {
- "0":"SPF"
- }
],
- "nodeMsd":8,
"extendedPrefix":[
{
"prefix":"10.0.255.3\/32",
"sid":300,
"inputLabel":20300,
- "outputLabel":"8300",
- "interface":"r1-eth0",
- "nexthop":"10.0.1.2"
+ "prefixRoute":[
+ {
+ "outputLabel":8300,
+ "interface":"r1-eth0",
+ "nexthop":"10.0.0.2"
+ },
+ {
+ "outputLabel":8300,
+ "interface":"r1-eth1",
+ "nexthop":"10.0.1.2"
+ }
+ ]
}
]
},
@@ -78,26 +101,46 @@
"prefix":"10.0.255.1\/32",
"sid":100,
"inputLabel":20100,
- "outputLabel":"pop",
- "interface":"lo",
- "nexthop":"10.0.255.1"
+ "prefixRoute":[
+ {
+ "outputLabel":3,
+ "interface":"lo",
+ "nexthop":"10.0.255.1"
+ }
+ ]
}
],
"extendedLink":[
{
- "prefix":"10.0.1.1\/32",
- "sid":50001,
- "inputLabel":50001,
- "outputLabel":"pop",
+ "prefix":"10.0.0.1\/32",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r1-eth0",
+ "nexthop":"10.0.0.2"
+ },
+ {
+ "prefix":"10.0.0.1\/32",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
"interface":"r1-eth0",
+ "nexthop":"10.0.0.2"
+ },
+ {
+ "prefix":"10.0.1.1\/32",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r1-eth1",
"nexthop":"10.0.1.2"
},
{
"prefix":"10.0.1.1\/32",
- "sid":50000,
- "inputLabel":50000,
- "outputLabel":"pop",
- "interface":"r1-eth0",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r1-eth1",
"nexthop":"10.0.1.2"
}
]
diff --git a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf
index e8593d1a1a..292d4e6367 100644
--- a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf
+++ b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf
@@ -3,6 +3,11 @@ interface lo
ip ospf area 0.0.0.0
!
interface r1-eth0
+ ip ospf network point-to-point
+ ip ospf area 0.0.0.0
+!
+interface r1-eth1
+ ip ospf network point-to-point
ip ospf area 0.0.0.0
!
router ospf
diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra.conf b/tests/topotests/ospf-sr-topo1/r1/zebra.conf
index f1fcc7dd3b..faf71db25c 100644
--- a/tests/topotests/ospf-sr-topo1/r1/zebra.conf
+++ b/tests/topotests/ospf-sr-topo1/r1/zebra.conf
@@ -3,6 +3,9 @@ interface lo
ip address 10.0.255.1/32
!
interface r1-eth0
+ ip address 10.0.0.1/24
+!
+interface r1-eth1
ip address 10.0.1.1/24
!
ip forwarding
diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
index 6b1fe76b6e..5ae2399e5c 100644
--- a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
@@ -1,5 +1,5 @@
-{
- "20100":{
+[
+ {
"inLabel":20100,
"installed":true,
"nexthops":[
@@ -7,12 +7,11 @@
"type":"SR (OSPF)",
"outLabel":3,
"distance":150,
- "installed":true,
- "nexthop":"10.0.255.1"
+ "installed":true
}
]
},
- "20200":{
+ {
"inLabel":20200,
"installed":true,
"nexthops":[
@@ -22,10 +21,17 @@
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.0.2"
}
]
},
- "20300":{
+ {
"inLabel":20300,
"installed":true,
"nexthops":[
@@ -35,10 +41,17 @@
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":8300,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.0.2"
}
]
},
- "20400":{
+ {
"inLabel":20400,
"installed":true,
"nexthops":[
@@ -48,11 +61,44 @@
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":8400,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.0.2"
+ }
+ ]
+ },
+ {
+ "inLabel":"*",
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.0.2"
+ }
+ ]
+ },
+ {
+ "inLabel":"*",
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.0.2"
}
]
},
- "50000":{
- "inLabel":50000,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -64,8 +110,8 @@
}
]
},
- "50001":{
- "inLabel":50001,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -77,4 +123,4 @@
}
]
}
-}
+]
diff --git a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json
index 2548299cc7..eb202b82cd 100644
--- a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json
+++ b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json
@@ -15,59 +15,79 @@
"prefix":"10.0.255.2\/32",
"sid":200,
"inputLabel":0,
- "outputLabel":"0",
- "interface":"lo",
- "nexthop":"10.0.255.2"
+ "prefixRoute":[
+ {
+ "outputLabel":0,
+ "interface":"lo",
+ "nexthop":"10.0.255.2"
+ }
+ ]
}
],
"extendedLink":[
{
"prefix":"10.0.4.2\/32",
- "sid":50001,
- "inputLabel":50001,
- "outputLabel":"pop",
- "interface":"r2-eth2",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r2-eth3",
"nexthop":"10.0.4.1"
},
{
"prefix":"10.0.4.2\/32",
- "sid":50000,
- "inputLabel":50000,
- "outputLabel":"pop",
- "interface":"r2-eth2",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r2-eth3",
"nexthop":"10.0.4.1"
},
{
- "prefix":"10.0.3.2\/32",
- "sid":50003,
- "inputLabel":50003,
- "outputLabel":"pop",
- "interface":"r2-eth1",
- "nexthop":"10.0.3.1"
+ "prefix":"10.0.0.2\/32",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r2-eth0",
+ "nexthop":"10.0.0.1"
},
{
- "prefix":"10.0.3.2\/32",
- "sid":50002,
- "inputLabel":50002,
- "outputLabel":"pop",
- "interface":"r2-eth1",
- "nexthop":"10.0.3.1"
+ "prefix":"10.0.0.2\/32",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r2-eth0",
+ "nexthop":"10.0.0.1"
},
{
"prefix":"10.0.1.2\/32",
- "sid":50005,
- "inputLabel":50005,
- "outputLabel":"pop",
- "interface":"r2-eth0",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r2-eth1",
"nexthop":"10.0.1.1"
},
{
"prefix":"10.0.1.2\/32",
- "sid":50004,
- "inputLabel":50004,
- "outputLabel":"pop",
- "interface":"r2-eth0",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r2-eth1",
"nexthop":"10.0.1.1"
+ },
+ {
+ "prefix":"10.0.3.2\/32",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r2-eth2",
+ "nexthop":"10.0.3.1"
+ },
+ {
+ "prefix":"10.0.3.2\/32",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
+ "interface":"r2-eth2",
+ "nexthop":"10.0.3.1"
}
]
},
@@ -76,19 +96,19 @@
"srgbSize":10000,
"srgbLabel":10000,
"algorithms":[
- {
- "0":"SPF"
- }
],
- "nodeMsd":12,
"extendedPrefix":[
{
"prefix":"10.0.255.4\/32",
"sid":400,
"inputLabel":8400,
- "outputLabel":"10400",
- "interface":"r2-eth2",
- "nexthop":"10.0.4.1"
+ "prefixRoute":[
+ {
+ "outputLabel":10400,
+ "interface":"r2-eth3",
+ "nexthop":"10.0.4.1"
+ }
+ ]
}
]
},
@@ -97,19 +117,19 @@
"srgbSize":10000,
"srgbLabel":10000,
"algorithms":[
- {
- "0":"SPF"
- }
],
- "nodeMsd":8,
"extendedPrefix":[
{
"prefix":"10.0.255.3\/32",
"sid":300,
"inputLabel":8300,
- "outputLabel":"pop",
- "interface":"r2-eth1",
- "nexthop":"10.0.3.1"
+ "prefixRoute":[
+ {
+ "outputLabel":3,
+ "interface":"r2-eth2",
+ "nexthop":"10.0.3.1"
+ }
+ ]
}
]
},
@@ -118,19 +138,24 @@
"srgbSize":10000,
"srgbLabel":20000,
"algorithms":[
- {
- "0":"SPF"
- }
],
- "nodeMsd":16,
"extendedPrefix":[
{
"prefix":"10.0.255.1\/32",
"sid":100,
"inputLabel":8100,
- "outputLabel":"20100",
- "interface":"r2-eth0",
- "nexthop":"10.0.1.1"
+ "prefixRoute":[
+ {
+ "outputLabel":20100,
+ "interface":"r2-eth0",
+ "nexthop":"10.0.0.1"
+ },
+ {
+ "outputLabel":20100,
+ "interface":"r2-eth1",
+ "nexthop":"10.0.1.1"
+ }
+ ]
}
]
}
diff --git a/tests/topotests/ospf-sr-topo1/r2/ospfd.conf b/tests/topotests/ospf-sr-topo1/r2/ospfd.conf
index 4d6146aaa7..b8c7b999e8 100644
--- a/tests/topotests/ospf-sr-topo1/r2/ospfd.conf
+++ b/tests/topotests/ospf-sr-topo1/r2/ospfd.conf
@@ -5,6 +5,7 @@ interface lo
ip ospf area 0.0.0.0
!
interface r2-eth0
+ ip ospf network point-to-point
ip ospf area 0.0.0.0
!
interface r2-eth1
@@ -12,6 +13,9 @@ interface r2-eth1
ip ospf area 0.0.0.0
!
interface r2-eth2
+ ip ospf area 0.0.0.0
+!
+interface r2-eth3
ip ospf network point-to-point
ip ospf area 0.0.0.0
!
diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra.conf b/tests/topotests/ospf-sr-topo1/r2/zebra.conf
index f89548d8c5..ba1d833f50 100644
--- a/tests/topotests/ospf-sr-topo1/r2/zebra.conf
+++ b/tests/topotests/ospf-sr-topo1/r2/zebra.conf
@@ -3,12 +3,15 @@ interface lo
ip address 10.0.255.2/32
!
interface r2-eth0
- ip address 10.0.1.2/24
+ ip address 10.0.0.2/24
!
interface r2-eth1
- ip address 10.0.3.2/24
+ ip address 10.0.1.2/24
!
interface r2-eth2
+ ip address 10.0.3.2/24
+!
+interface r2-eth3
ip address 10.0.4.2/24
!
ip forwarding
diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
index 79965d280a..aedcc5b8f8 100644
--- a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
@@ -1,5 +1,5 @@
-{
- "8100":{
+[
+ {
"inLabel":8100,
"installed":true,
"nexthops":[
@@ -9,10 +9,17 @@
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":20100,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.0.1"
}
]
},
- "8300":{
+ {
"inLabel":8300,
"installed":true,
"nexthops":[
@@ -25,7 +32,7 @@
}
]
},
- "8400":{
+ {
"inLabel":8400,
"installed":true,
"nexthops":[
@@ -38,8 +45,8 @@
}
]
},
- "50000":{
- "inLabel":50000,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -51,8 +58,8 @@
}
]
},
- "50001":{
- "inLabel":50001,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -64,8 +71,8 @@
}
]
},
- "50002":{
- "inLabel":50002,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -73,12 +80,12 @@
"outLabel":3,
"distance":150,
"installed":true,
- "nexthop":"10.0.3.1"
+ "nexthop":"10.0.0.1"
}
]
},
- "50003":{
- "inLabel":50003,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -86,12 +93,12 @@
"outLabel":3,
"distance":150,
"installed":true,
- "nexthop":"10.0.3.1"
+ "nexthop":"10.0.0.1"
}
]
},
- "50004":{
- "inLabel":50004,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -103,8 +110,8 @@
}
]
},
- "50005":{
- "inLabel":50005,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -115,5 +122,31 @@
"nexthop":"10.0.1.1"
}
]
+ },
+ {
+ "inLabel":"*",
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.3.1"
+ }
+ ]
+ },
+ {
+ "inLabel":"*",
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.3.1"
+ }
+ ]
}
-}
+]
diff --git a/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json
index 4b618cc7ad..b36fe674ad 100644
--- a/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json
+++ b/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json
@@ -15,9 +15,13 @@
"prefix":"10.0.255.2\/32",
"sid":200,
"inputLabel":10200,
- "outputLabel":"pop",
- "interface":"r3-eth0",
- "nexthop":"10.0.3.2"
+ "prefixRoute":[
+ {
+ "outputLabel":3,
+ "interface":"r3-eth0",
+ "nexthop":"10.0.3.2"
+ }
+ ]
}
]
},
@@ -36,9 +40,13 @@
"prefix":"10.0.255.4\/32",
"sid":400,
"inputLabel":10400,
- "outputLabel":"8400",
- "interface":"r3-eth0",
- "nexthop":"10.0.3.2"
+ "prefixRoute":[
+ {
+ "outputLabel":8400,
+ "interface":"r3-eth0",
+ "nexthop":"10.0.3.2"
+ }
+ ]
}
]
},
@@ -57,25 +65,29 @@
"prefix":"10.0.255.3\/32",
"sid":300,
"inputLabel":0,
- "outputLabel":"0",
- "interface":"lo",
- "nexthop":"10.0.255.3"
+ "prefixRoute":[
+ {
+ "outputLabel":0,
+ "interface":"lo",
+ "nexthop":"10.0.255.3"
+ }
+ ]
}
],
"extendedLink":[
{
"prefix":"10.0.3.1\/32",
- "sid":50001,
- "inputLabel":50001,
- "outputLabel":"pop",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
"interface":"r3-eth0",
"nexthop":"10.0.3.2"
},
{
"prefix":"10.0.3.1\/32",
- "sid":50000,
- "inputLabel":50000,
- "outputLabel":"pop",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
"interface":"r3-eth0",
"nexthop":"10.0.3.2"
}
@@ -96,9 +108,13 @@
"prefix":"10.0.255.1\/32",
"sid":100,
"inputLabel":10100,
- "outputLabel":"8100",
- "interface":"r3-eth0",
- "nexthop":"10.0.3.2"
+ "prefixRoute":[
+ {
+ "outputLabel":8100,
+ "interface":"r3-eth0",
+ "nexthop":"10.0.3.2"
+ }
+ ]
}
]
}
diff --git a/tests/topotests/ospf-sr-topo1/r3/ospfd.conf b/tests/topotests/ospf-sr-topo1/r3/ospfd.conf
index 5aaa14fac4..cf274bed2e 100644
--- a/tests/topotests/ospf-sr-topo1/r3/ospfd.conf
+++ b/tests/topotests/ospf-sr-topo1/r3/ospfd.conf
@@ -3,7 +3,6 @@ interface lo
ip ospf area 0.0.0.0
!
interface r3-eth0
- ip ospf network point-to-point
ip ospf area 0.0.0.0
!
!
diff --git a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
index ceb2f7a0e5..71e8366137 100644
--- a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
@@ -1,5 +1,5 @@
-{
- "10100":{
+[
+ {
"inLabel":10100,
"installed":true,
"nexthops":[
@@ -12,7 +12,7 @@
}
]
},
- "10200":{
+ {
"inLabel":10200,
"installed":true,
"nexthops":[
@@ -25,7 +25,7 @@
}
]
},
- "10400":{
+ {
"inLabel":10400,
"installed":true,
"nexthops":[
@@ -38,8 +38,8 @@
}
]
},
- "50000":{
- "inLabel":50000,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -51,8 +51,8 @@
}
]
},
- "50001":{
- "inLabel":50001,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -64,4 +64,4 @@
}
]
}
-}
+]
diff --git a/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json
index 098e87dc25..d92ec91c72 100644
--- a/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json
+++ b/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json
@@ -6,18 +6,19 @@
"srgbSize":20000,
"srgbLabel":8000,
"algorithms":[
- {
- "0":"SPF"
- }
],
"extendedPrefix":[
{
"prefix":"10.0.255.2\/32",
"sid":200,
"inputLabel":10200,
- "outputLabel":"pop",
- "interface":"r4-eth0",
- "nexthop":"10.0.4.2"
+ "prefixRoute":[
+ {
+ "outputLabel":3,
+ "interface":"r4-eth0",
+ "nexthop":"10.0.4.2"
+ }
+ ]
}
]
},
@@ -36,25 +37,29 @@
"prefix":"10.0.255.4\/32",
"sid":400,
"inputLabel":10400,
- "outputLabel":"pop",
- "interface":"lo",
- "nexthop":"10.0.255.4"
+ "prefixRoute":[
+ {
+ "outputLabel":3,
+ "interface":"lo",
+ "nexthop":"10.0.255.4"
+ }
+ ]
}
],
"extendedLink":[
{
"prefix":"10.0.4.1\/32",
- "sid":50001,
- "inputLabel":50001,
- "outputLabel":"pop",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
"interface":"r4-eth0",
"nexthop":"10.0.4.2"
},
{
"prefix":"10.0.4.1\/32",
- "sid":50000,
- "inputLabel":50000,
- "outputLabel":"pop",
+ "sid":"*",
+ "inputLabel":"*",
+ "outputLabel":3,
"interface":"r4-eth0",
"nexthop":"10.0.4.2"
}
@@ -65,19 +70,19 @@
"srgbSize":10000,
"srgbLabel":10000,
"algorithms":[
- {
- "0":"SPF"
- }
],
- "nodeMsd":8,
"extendedPrefix":[
{
"prefix":"10.0.255.3\/32",
"sid":300,
"inputLabel":10300,
- "outputLabel":"8300",
- "interface":"r4-eth0",
- "nexthop":"10.0.4.2"
+ "prefixRoute":[
+ {
+ "outputLabel":8300,
+ "interface":"r4-eth0",
+ "nexthop":"10.0.4.2"
+ }
+ ]
}
]
},
@@ -86,19 +91,19 @@
"srgbSize":10000,
"srgbLabel":20000,
"algorithms":[
- {
- "0":"SPF"
- }
],
- "nodeMsd":16,
"extendedPrefix":[
{
"prefix":"10.0.255.1\/32",
"sid":100,
"inputLabel":10100,
- "outputLabel":"8100",
- "interface":"r4-eth0",
- "nexthop":"10.0.4.2"
+ "prefixRoute":[
+ {
+ "outputLabel":8100,
+ "interface":"r4-eth0",
+ "nexthop":"10.0.4.2"
+ }
+ ]
}
]
}
diff --git a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
index d7f54b224d..b5767e1d7d 100644
--- a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
@@ -1,5 +1,5 @@
-{
- "10100":{
+[
+ {
"inLabel":10100,
"installed":true,
"nexthops":[
@@ -12,7 +12,7 @@
}
]
},
- "10200":{
+ {
"inLabel":10200,
"installed":true,
"nexthops":[
@@ -25,7 +25,7 @@
}
]
},
- "10300":{
+ {
"inLabel":10300,
"installed":true,
"nexthops":[
@@ -38,7 +38,7 @@
}
]
},
- "10400":{
+ {
"inLabel":10400,
"installed":true,
"nexthops":[
@@ -46,13 +46,12 @@
"type":"SR (OSPF)",
"outLabel":3,
"distance":150,
- "installed":true,
- "nexthop":"10.0.255.4"
+ "installed":true
}
]
},
- "50000":{
- "inLabel":50000,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -64,8 +63,8 @@
}
]
},
- "50001":{
- "inLabel":50001,
+ {
+ "inLabel":"*",
"installed":true,
"nexthops":[
{
@@ -77,4 +76,4 @@
}
]
}
-}
+]
diff --git a/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py
index 92cebfe0b6..6792c56b3b 100755
--- a/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py
+++ b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py
@@ -27,7 +27,9 @@ test_ospf_sr_topo1.py: Test the FRR OSPF routing daemon with Segment Routing.
"""
import os
+import re
import sys
+import json
from functools import partial
# Save the Current Working Directory to find configuration files.
@@ -62,20 +64,23 @@ class OspfSrTopo(Topo):
for routern in range(1, 5):
tgen.add_router("r{}".format(routern))
- # Interconect router 1 and 2
- switch = tgen.add_switch("s1")
- switch.add_link(tgen.gears["r1"])
- switch.add_link(tgen.gears["r2"])
+ # Interconect router 1 and 2 with 2 links
+ switch = tgen.add_switch('s1')
+ switch.add_link(tgen.gears['r1'])
+ switch.add_link(tgen.gears['r2'])
+ switch = tgen.add_switch('s2')
+ switch.add_link(tgen.gears['r1'])
+ switch.add_link(tgen.gears['r2'])
# Interconect router 3 and 2
- switch = tgen.add_switch("s2")
- switch.add_link(tgen.gears["r3"])
- switch.add_link(tgen.gears["r2"])
+ switch = tgen.add_switch('s3')
+ switch.add_link(tgen.gears['r3'])
+ switch.add_link(tgen.gears['r2'])
# Interconect router 4 and 2
- switch = tgen.add_switch("s3")
- switch.add_link(tgen.gears["r4"])
- switch.add_link(tgen.gears["r2"])
+ switch = tgen.add_switch('s4')
+ switch.add_link(tgen.gears['r4'])
+ switch.add_link(tgen.gears['r2'])
def setup_module(mod):
@@ -99,19 +104,6 @@ def setup_module(mod):
# Initialize all routers.
tgen.start_router()
- # Verify that version, MPLS and Segment Routing are OK
- for router in router_list.values():
- # Check for Version
- if router.has_version("<", "4"):
- tgen.set_error("Unsupported FRR version")
- break
- # Check that Segment Routing is available
- output = tgen.gears[router.name].vtysh_cmd(
- "show ip ospf database segment-routing json"
- )
- if output.find("Unknown") != -1:
- tgen.set_error("Segment Routing is not available")
-
def teardown_module(mod):
"Teardown the pytest environment"
@@ -122,31 +114,6 @@ def teardown_module(mod):
logger.info("\n\n---- OSPF Segment Routing tests End ----\n")
-# Shared test function to validate expected output.
-def compare_ospf_srdb(rname, expected):
- """
- Calls 'show ip ospf database segment-routing json' for router `rname`
- and compare the obtained result with the expected output.
- """
- tgen = get_topogen()
- current = tgen.gears[rname].vtysh_cmd("show ip ospf database segment-routing json")
- return topotest.difflines(
- current, expected, title1="Current output", title2="Expected output"
- )
-
-
-def compare_mpls_table(rname, expected):
- """
- Calls 'show mpls table json' for router `rname` and compare the obtained
- result with the expected output.
- """
- tgen = get_topogen()
- current = tgen.gears[rname].vtysh_cmd("show mpls table json")
- return topotest.difflines(
- current, expected, title1="Current output", title2="Expected output"
- )
-
-
def test_ospf_sr():
"Test OSPF daemon Segment Routing"
tgen = get_topogen()
@@ -162,12 +129,15 @@ def test_ospf_sr():
# Load expected results from the command
reffile = os.path.join(CWD, "{}/ospf_srdb.json".format(router))
- expected = open(reffile).read()
+ expected = json.loads(open(reffile).read())
# Run test function until we get an result. Wait at most 60 seconds.
- test_func = partial(compare_ospf_srdb, router, expected)
- result, diff = topotest.run_and_expect(test_func, "", count=25, wait=3)
- assert result, ("OSPF did not start Segment Routing on {}:\n{}").format(
+ rt = tgen.gears[router]
+ test_func = partial(
+ topotest.router_json_cmp, rt, 'show ip ospf database segment-routing json', expected
+ )
+ rv, diff = topotest.run_and_expect(test_func, None, count=25, wait=3)
+ assert rv, "OSPF did not start Segment Routing on {}:\n{}".format(
router, diff
)
@@ -180,6 +150,33 @@ def test_ospf_kernel_route():
logger.info("--- test OSPF Segment Routing MPLS tables ---")
+ def show_mpls_table_json_cmp(rt, expected):
+ """
+ Reformat MPLS table output to use a list of labels instead of dict.
+
+ Original:
+ {
+ "X": {
+ inLabel: "X",
+ # ...
+ }
+ }
+
+ List format:
+ [
+ {
+ inLabel: "X",
+ }
+ ]
+ """
+ out = rt.vtysh_cmd('show mpls table json', isjson=True)
+
+ outlist = []
+ for key in out.keys():
+ outlist.append(out[key])
+
+ return topotest.json_cmp(outlist, expected)
+
for rnum in range(1, 5):
router = "r{}".format(rnum)
@@ -187,12 +184,13 @@ def test_ospf_kernel_route():
# Load expected results from the command
reffile = os.path.join(CWD, "{}/zebra_mpls.json".format(router))
- expected = open(reffile).read()
+ expected = json.loads(open(reffile).read())
# Run test function until we get an result. Wait at most 60 seconds.
- test_func = partial(compare_mpls_table, router, expected)
- result, diff = topotest.run_and_expect(test_func, "", count=25, wait=3)
- assert result, ("OSPF did not properly instal MPLS table on {}:\n{}").format(
+ rt = tgen.gears[router]
+ test_func = partial(show_mpls_table_json_cmp, rt, expected)
+ rv, diff = topotest.run_and_expect(test_func, None, count=25, wait=3)
+ assert rv, "OSPF did not properly instal MPLS table on {}:\n{}".format(
router, diff
)
diff --git a/tests/topotests/pbr-topo1/__init__.py b/tests/topotests/pbr-topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/pbr-topo1/__init__.py
diff --git a/tests/topotests/pbr-topo1/r1/linux-rules.json b/tests/topotests/pbr-topo1/r1/linux-rules.json
new file mode 100644
index 0000000000..5af4363418
--- /dev/null
+++ b/tests/topotests/pbr-topo1/r1/linux-rules.json
@@ -0,0 +1,19 @@
+[
+ {
+ "iif": "r1-eth1",
+ "pref": "304",
+ "from": "4.5.6.7"
+ },
+ {
+ "to": "3.4.5.0/24",
+ "iif": "r1-eth2",
+ "pref": "304",
+ "from": "1.2.0.0/16"
+ },
+ {
+ "to": "9.9.9.9",
+ "iif": "r1-eth1",
+ "pref": "309",
+ "from": "all"
+ }
+]
diff --git a/tests/topotests/pbr-topo1/r1/pbr-interface.json b/tests/topotests/pbr-topo1/r1/pbr-interface.json
index 452b24dcd7..e28d9fb149 100644
--- a/tests/topotests/pbr-topo1/r1/pbr-interface.json
+++ b/tests/topotests/pbr-topo1/r1/pbr-interface.json
@@ -8,5 +8,20 @@
"name":"r1-eth2",
"policy":"DONNA",
"valid":true
+ },
+ {
+ "name":"r1-eth3",
+ "policy":"AKIHABARA",
+ "valid":true
+ },
+ {
+ "name":"r1-eth4",
+ "policy":"ASAKUSA",
+ "valid":true
+ },
+ {
+ "name":"r1-noexist",
+ "policy":"NOEXIST",
+ "valid":false
}
]
diff --git a/tests/topotests/pbr-topo1/r1/pbr-map.json b/tests/topotests/pbr-topo1/r1/pbr-map.json
index 6b9eaa9ceb..f0738dc540 100644
--- a/tests/topotests/pbr-topo1/r1/pbr-map.json
+++ b/tests/topotests/pbr-topo1/r1/pbr-map.json
@@ -1,21 +1,92 @@
[
{
- "name":"DONNA",
- "valid":true,
+ "name":"AKIHABARA",
+ "valid":false,
"policies":[
{
- "id":3,
"sequenceNumber":5,
- "ruleNumber":304,
"vrfUnchanged":false,
"installed":true,
"installedReason":"Valid",
+ "nexthopGroup": {
+ "name":"C",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchDst":"192.168.4.0\/24"
+ },
+ {
+ "sequenceNumber":10,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Invalid Src or Dst",
"nexthopGroup":{
- "tableId":10002,
"name":"C",
"installed":true,
"installedInternally":1
+ }
+ },
+ {
+ "sequenceNumber":15,
+ "vrfUnchanged":false,
+ "installed":false,
+ "installedReason":"No Nexthops"
+ }
+ ]
+ },
+ {
+ "name":"ASAKUSA",
+ "valid":true,
+ "policies":[
+ {
+ "sequenceNumber":5,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "matchDst":"c0ff:ee::\/64",
+ "nexthopGroup":{
+ "name":"D",
+ "installed":true,
+ "installedInternally":1
+ }
+ },
+ {
+ "sequenceNumber":10,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"ASAKUSA10",
+ "installed":true,
+ "installedInternally":1
},
+ "matchDst":"dead:beef::\/64",
+ "matchMark":314159
+ }
+ ]
+ },
+ {
+ "name":"DONNA",
+ "valid":false,
+ "policies":[
+ {
+ "sequenceNumber":5,
+ "vrfUnchanged":false,
+ "installed":false,
+ "installedReason":"Invalid NH-group",
+ "nexthopGroup":{
+ "name":"B",
+ "installed":false,
+ "installedInternally":0
+ },
+ "matchSrc":"1.2.0.0\/16",
+ "matchDst":"3.4.5.0\/24"
+ },
+ {
+ "sequenceNumber":10,
+ "vrfUnchanged":true,
+ "installed":false,
+ "installedReason":"Valid",
"matchSrc":"1.2.0.0\/16",
"matchDst":"3.4.5.0\/24"
}
@@ -26,14 +97,11 @@
"valid":true,
"policies":[
{
- "id":1,
"sequenceNumber":5,
- "ruleNumber":304,
"vrfUnchanged":false,
"installed":true,
"installedReason":"Valid",
"nexthopGroup":{
- "tableId":10003,
"name":"EVA5",
"installed":true,
"installedInternally":1
@@ -41,14 +109,12 @@
"matchSrc":"4.5.6.7\/32"
},
{
- "id":2,
"sequenceNumber":10,
"ruleNumber":309,
"vrfUnchanged":false,
"installed":true,
"installedReason":"Valid",
"nexthopGroup":{
- "tableId":10000,
"name":"A",
"installed":true,
"installedInternally":1
diff --git a/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json
index ff85438ad5..540ea28158 100644
--- a/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json
+++ b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json
@@ -1,6 +1,16 @@
[
{
- "id":10000,
+ "name":"ASAKUSA10",
+ "valid":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "nexthop":"c0ff:ee::1",
+ "valid":true
+ }
+ ]
+ },
+ {
"name":"A",
"valid":true,
"installed":true,
@@ -20,19 +30,47 @@
]
},
{
- "id":10002,
+ "name":"D",
+ "valid":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "nexthop":"c0ff:ee::3",
+ "valid":true
+ },
+ {
+ "nexthop":"c0ff:ee::2",
+ "valid":true
+ },
+ {
+ "nexthop":"c0ff:ee::1",
+ "valid":true
+ }
+ ]
+ },
+ {
"name":"C",
"valid":true,
"installed":true,
"nexthops":[
{
- "nexthop":"192.168.1.44",
+ "nexthop":"192.168.4.3",
+ "targetVrf":"vrf-chiyoda",
+ "valid":true
+ },
+ {
+ "nexthop":"192.168.4.2",
+ "targetVrf":"vrf-chiyoda",
+ "valid":true
+ },
+ {
+ "nexthop":"192.168.4.1",
+ "targetVrf":"vrf-chiyoda",
"valid":true
}
]
},
{
- "id":10001,
"name":"B",
"valid":false,
"installed":false,
@@ -44,7 +82,6 @@
]
},
{
- "id":10003,
"name":"EVA5",
"valid":true,
"installed":true,
diff --git a/tests/topotests/pbr-topo1/r1/pbrd.conf b/tests/topotests/pbr-topo1/r1/pbrd.conf
index 234683f307..4a126151b0 100644
--- a/tests/topotests/pbr-topo1/r1/pbrd.conf
+++ b/tests/topotests/pbr-topo1/r1/pbrd.conf
@@ -1,8 +1,16 @@
+debug pbr
+# Valid table range
+pbr table range 10000 50000
+# Try to set invalid bounds
+pbr table range 10000 10001
+pbr table range 50000 10000
+# Reset table range
+no pbr table range
+!
nexthop-group A
nexthop 192.168.1.2
nexthop 192.168.2.2
nexthop 192.168.3.2
- nexhtop 192.168.4.2
!
# This one is bogus and should
# never work
@@ -10,7 +18,14 @@ nexthop-group B
nexthop 192.168.50.1
!
nexthop-group C
- nexthop 192.168.1.44
+ nexthop 192.168.4.1 nexthop-vrf vrf-chiyoda
+ nexthop 192.168.4.2 nexthop-vrf vrf-chiyoda
+ nexthop 192.168.4.3 nexthop-vrf vrf-chiyoda
+!
+nexthop-group D
+ nexthop c0ff:ee::1
+ nexthop c0ff:ee::2
+ nexthop c0ff:ee::3
!
pbr-map EVA seq 5
match src-ip 4.5.6.7/32
@@ -23,11 +38,50 @@ pbr-map EVA seq 10
pbr-map DONNA seq 5
match dst-ip 3.4.5.0/24
match src-ip 1.2.0.0/16
- set nexthop-group C
-!
-
+ set nexthop-group B
+!
+pbr-map DONNA seq 10
+ match dst-ip 3.4.5.0/24
+ match src-ip 1.2.0.0/16
+ set vrf unchanged
+!
+pbr-map AKIHABARA seq 5
+ no set vrf unchanged
+ match dst-ip 192.168.4.0/24
+ set nexthop-group C
+!
+pbr-map AKIHABARA seq 10
+ match dst-ip 192.168.4.0/24
+ no match dst-ip 192.168.4.0/24
+ set nexthop-group C
+!
+pbr-map AKIHABARA seq 15
+ set vrf noexist-vrf
+ match dst-ip 192.168.4.0/24
+ set nexthop-group C
+ no set nexthop-group C
+!
+pbr-map ASAKUSA seq 5
+ match dst-ip c0ff:ee::/64
+ set nexthop-group D
+!
+pbr-map ASAKUSA seq 10
+ match dst-ip dead:beef::/64
+ match mark 314159
+ set nexthop c0ff:ee::1
+!
+# Interface policies
int r1-eth1
pbr-policy EVA
!
int r1-eth2
pbr-policy DONNA
+!
+int r1-eth3
+ pbr-policy AKIHABARA
+!
+int r1-eth4
+ pbr-policy ASAKUSA
+!
+int r1-noexist
+ pbr-policy NOEXIST
diff --git a/tests/topotests/pbr-topo1/r1/zebra.conf b/tests/topotests/pbr-topo1/r1/zebra.conf
index f29b146a62..2ec947c275 100644
--- a/tests/topotests/pbr-topo1/r1/zebra.conf
+++ b/tests/topotests/pbr-topo1/r1/zebra.conf
@@ -7,5 +7,8 @@ int r1-eth1
int r1-eth2
ip address 192.168.3.1/24
-int r1-eth3
+int r1-eth3 vrf vrf-chiyoda
ip address 192.168.4.1/24
+
+int r1-eth4
+ ipv6 address c0ff:ee::/64
diff --git a/tests/topotests/pbr-topo1/test_pbr_topo1.py b/tests/topotests/pbr-topo1/test_pbr_topo1.py
index 2853165d45..7de1cfa519 100755
--- a/tests/topotests/pbr-topo1/test_pbr_topo1.py
+++ b/tests/topotests/pbr-topo1/test_pbr_topo1.py
@@ -42,6 +42,7 @@ sys.path.append(os.path.join(CWD, "../"))
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
+from lib.common_config import shutdown_bringup_interface
# Required to instantiate the topology builder class.
from mininet.topo import Topo
@@ -61,26 +62,14 @@ class NetworkTopo(Topo):
tgen = get_topogen(self)
+ # Populate routers
for routern in range(1, 2):
tgen.add_router("r{}".format(routern))
- # On main router
- # First switch is for a dummy interface (for local network)
- switch = tgen.add_switch("sw1")
- switch.add_link(tgen.gears["r1"])
-
- # Switches for PBR
- # switch 2 switch is for connection to PBR router
- switch = tgen.add_switch("sw2")
- switch.add_link(tgen.gears["r1"])
-
- # switch 4 is stub on remote PBR router
- switch = tgen.add_switch("sw4")
- switch.add_link(tgen.gears["r1"])
-
- # switch 3 is between PBR routers
- switch = tgen.add_switch("sw3")
- switch.add_link(tgen.gears["r1"])
+ # Populate switches
+ for switchn in range(1, 6):
+ switch = tgen.add_switch("sw{}".format(switchn))
+ switch.add_link(tgen.gears["r1"])
#####################################################
@@ -95,9 +84,13 @@ def setup_module(module):
tgen = Topogen(NetworkTopo, module.__name__)
tgen.start_topology()
- # This is a sample of configuration loading.
router_list = tgen.routers()
for rname, router in router_list.iteritems():
+ # Install vrf into the kernel and slave eth3
+ router.run("ip link add vrf-chiyoda type vrf table 1000")
+ router.run("ip link set dev {}-eth3 master vrf-chiyoda".format(rname))
+ router.run("ip link set vrf-chiyoda up")
+
router.load_config(
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
)
@@ -106,7 +99,7 @@ def setup_module(module):
)
tgen.start_router()
- #gen.mininet_cli()
+
def teardown_module(_mod):
"Teardown the pytest environment"
@@ -141,19 +134,19 @@ def test_pbr_data():
router_list = tgen.routers().values()
for router in router_list:
intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name)
-
logger.info(intf_file)
+
# Read expected result from file
expected = json.loads(open(intf_file).read())
# Actual output from router
actual = router.vtysh_cmd("show pbr interface json", isjson=True)
-
assertmsg = '"show pbr interface" mismatches on {}'.format(router.name)
assert topotest.json_cmp(actual, expected) is None, assertmsg
map_file = "{}/{}/pbr-map.json".format(CWD, router.name)
logger.info(map_file)
+
# Read expected result from file
expected = json.loads(open(map_file).read())
@@ -164,7 +157,8 @@ def test_pbr_data():
assert topotest.json_cmp(actual, expected) is None, assertmsg
nexthop_file = "{}/{}/pbr-nexthop-groups.json".format(CWD, router.name)
-
+ logger.info(nexthop_file)
+
# Read expected result from file
expected = json.loads(open(nexthop_file).read())
@@ -174,7 +168,64 @@ def test_pbr_data():
assertmsg = '"show pbr nexthop-groups" mismatches on {}'.format(router.name)
assert topotest.json_cmp(actual, expected) is None, assertmsg
+
+def test_pbr_flap():
+ "Test PBR interface flapping"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Verify PBR Status
+ logger.info("Flapping PBR Interfaces")
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ # Flap interface to see if route-map properties are intact
+ # Shutdown interface
+
+ for i in range(5):
+ intf = "r1-eth{}".format(i)
+
+ # Down and back again
+ shutdown_bringup_interface(tgen, router.name, intf, False)
+ shutdown_bringup_interface(tgen, router.name, intf, True)
+
+ intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name)
+ logger.info(intf_file)
+
+ # Read expected result from file
+ expected = json.loads(open(intf_file).read())
+
+ # Actual output from router
+ actual = router.vtysh_cmd("show pbr interface json", isjson=True)
+ assertmsg = '"show pbr interface" mismatches on {}'.format(router.name)
+
+ assert topotest.json_cmp(actual, expected) is None, assertmsg
+
+
+def test_rule_linux_installation():
+ "Ensure that rule is installed in the kernel"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking for installed PBR rules in OS")
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ rules_file = "{}/{}/linux-rules.json".format(CWD, router.name)
+
+ actual = topotest.ip_rules(router)
+ expected = json.loads(open(rules_file).read())
+
+ assertmsg = "Router {} OS rules mismatch".format(router.name)
+ assert topotest.json_cmp(actual, expected) is None, assertmsg
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
-
diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c
index 95b3cfad8f..d8c35dd2a3 100644
--- a/vrrpd/vrrp_main.c
+++ b/vrrpd/vrrp_main.c
@@ -111,6 +111,7 @@ struct quagga_signal_t vrrp_signals[] = {
};
static const struct frr_yang_module_info *const vrrp_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_vrrpd_info,
};
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index d5142b1b55..794e1f4c73 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -98,7 +98,7 @@ sub scan_file {
elsif ($file =~ /lib\/if\.c$/) {
$protocol = "VTYSH_INTERFACE";
}
- elsif ($file =~ /lib\/(filter|lib_vty)\.c$/) {
+ elsif ($file =~ /lib\/(filter|filter_cli|lib_vty)\.c$/) {
$protocol = "VTYSH_ALL";
}
elsif ($file =~ /lib\/agentx\.c$/) {
diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang
index c55af34161..78db201ea1 100644
--- a/yang/frr-filter.yang
+++ b/yang/frr-filter.yang
@@ -73,7 +73,9 @@ module frr-filter {
typedef access-list-name {
description "Access list name formatting";
- type string;
+ type string {
+ length 1..128;
+ }
}
typedef access-list-sequence {
@@ -100,69 +102,76 @@ module frr-filter {
/*
* Configuration data.
*/
- container filter-list {
+ container lib {
list access-list-legacy {
description "Access list legacy instance";
- key "number sequence";
+ key "number";
leaf number {
description "Access list sequence value";
type access-list-legacy;
}
- leaf sequence {
- description "Access list sequence value";
- type access-list-sequence;
- }
-
- leaf action {
- description "Access list action on match";
- type access-list-action;
- mandatory true;
- }
-
leaf remark {
description "Access list remark";
type string;
}
- choice value {
- description
- "Standard access list: value to match.
- Extended access list: source value to match.";
- mandatory true;
+ list entry {
+ description "Access list legacy entry";
- leaf host {
- description "Host to match";
- type inet:ipv4-address;
- }
- leaf network {
- description "Network to match";
- type inet:ipv4-prefix;
+ key "sequence";
+
+ leaf sequence {
+ description "Access list sequence value";
+ type access-list-sequence;
}
- leaf any {
- description "Match any";
- type empty;
+
+ leaf action {
+ description "Access list action on match";
+ type access-list-action;
+ mandatory true;
}
- }
- choice extended-value {
- when "./sequence >= 100 and ./sequence <= 199 or
- ./sequence >= 2000 and ./sequence <= 2699";
- description "Destination value to match";
+ choice value {
+ description
+ "Standard access list: value to match.
+ Extended access list: source value to match.";
+ mandatory true;
- leaf destination-host {
- description "Host to match";
- type inet:ipv4-address;
- }
- leaf destination-network {
- description "Network to match";
- type inet:ipv4-prefix;
+ leaf host {
+ description "Host to match";
+ type inet:ipv4-address;
+ }
+ leaf network {
+ description "Network to match";
+ type inet:ipv4-prefix;
+ }
+ leaf any {
+ description "Match any";
+ type empty;
+ }
}
- leaf destination-any {
- description "Match any";
- type empty;
+
+ choice extended-value {
+ when "../number >= 100 and ../number <= 199 or
+ ../number >= 2000 and ../number <= 2699";
+ description "Destination value to match";
+ mandatory true;
+
+ leaf destination-host {
+ description "Host to match";
+ type inet:ipv4-address;
+ }
+ leaf destination-network {
+ description "Network to match";
+ type inet:ipv4-prefix;
+ }
+ leaf destination-any {
+ description "Match any";
+ type empty;
+ }
}
}
}
@@ -170,7 +179,7 @@ module frr-filter {
list access-list {
description "Access list instance";
- key "type identifier sequence";
+ key "type name";
leaf type {
description "Access list content type";
@@ -187,85 +196,80 @@ module frr-filter {
description "Media Access Control address";
value 2;
}
-
- /*
- * Protocol YANG models should augment the parent node to
- * contain the routing protocol specific value. The protocol
- * must also augment `value` leaf to include its specific
- * values or expand the `when` statement on the existing cases.
- */
- enum custom {
- description "Custom data type";
- value 100;
- }
}
}
- leaf identifier {
- description "Access list identifier";
+ leaf name {
+ description "Access list name";
type access-list-name;
}
- leaf sequence {
- description "Access list sequence value";
- type access-list-sequence;
- }
-
- leaf action {
- description "Access list action on match";
- type access-list-action;
- mandatory true;
- }
-
leaf remark {
description "Access list remark";
type string;
}
- choice value {
- description "Access list value to match";
- mandatory true;
+ list entry {
+ description "Access list entry";
- case ipv4-prefix {
- when "./type = 'ipv4'";
+ key "sequence";
- leaf ipv4-prefix {
- description "Configure IPv4 prefix to match";
- type inet:ipv4-prefix;
- }
+ leaf sequence {
+ description "Access list sequence value";
+ type access-list-sequence;
+ }
- leaf ipv4-exact-match {
- description "Exact match of prefix";
- type boolean;
- default false;
- }
+ leaf action {
+ description "Access list action on match";
+ type access-list-action;
+ mandatory true;
}
- case ipv6-prefix {
- when "./type = 'ipv6'";
- leaf ipv6-prefix {
- description "Configure IPv6 prefix to match";
- type inet:ipv6-prefix;
+ choice value {
+ description "Access list value to match";
+ mandatory true;
+
+ case ipv4-prefix {
+ when "../type = 'ipv4'";
+
+ leaf ipv4-prefix {
+ description "Configure IPv4 prefix to match";
+ type inet:ipv4-prefix;
+ }
+
+ leaf ipv4-exact-match {
+ description "Exact match of prefix";
+ type boolean;
+ default false;
+ }
}
+ case ipv6-prefix {
+ when "../type = 'ipv6'";
+
+ leaf ipv6-prefix {
+ description "Configure IPv6 prefix to match";
+ type inet:ipv6-prefix;
+ }
- leaf ipv6-exact-match {
- description "Exact match of prefix";
- type boolean;
- default false;
+ leaf ipv6-exact-match {
+ description "Exact match of prefix";
+ type boolean;
+ default false;
+ }
}
- }
- case mac {
- when "./type = 'mac'";
+ case mac {
+ when "../type = 'mac'";
- leaf mac {
- description "Configure MAC address to match";
- type yang:mac-address;
+ leaf mac {
+ description "Configure MAC address to match";
+ type yang:mac-address;
+ }
}
- }
- case any {
- leaf any {
- description "Match anything";
- type empty;
+ case any {
+ leaf any {
+ description "Match anything";
+ type empty;
+ }
}
}
}
@@ -274,7 +278,7 @@ module frr-filter {
list prefix-list {
description "Prefix list instance";
- key "type name sequence";
+ key "type name";
leaf type {
description "Prefix list type";
@@ -295,82 +299,88 @@ module frr-filter {
type access-list-name;
}
- leaf sequence {
- description "Access list sequence value";
- type access-list-sequence;
- }
-
- leaf action {
- description "Prefix list action on match";
- type access-list-action;
- mandatory true;
- }
-
- leaf description {
+ leaf remark {
description "Prefix list user description";
type string;
}
- choice value {
- description "Prefix list value to match";
- mandatory true;
+ list entry {
+ description "Prefix list entry";
- case ipv4-prefix {
- when "./type = 'ipv4'";
+ key "sequence";
- leaf ipv4-prefix {
- description "Configure IPv4 prefix to match";
- type inet:ipv4-prefix;
- }
+ leaf sequence {
+ description "Access list sequence value";
+ type access-list-sequence;
+ }
+
+ leaf action {
+ description "Prefix list action on match";
+ type access-list-action;
+ mandatory true;
+ }
- leaf ipv4-prefix-length-greater-or-equal {
- description
- "Specifies if matching prefixes with length greater than
- or equal to value";
- type uint8 {
- range "0..32";
+ choice value {
+ description "Prefix list value to match";
+ mandatory true;
+
+ case ipv4-prefix {
+ when "../type = 'ipv4'";
+
+ leaf ipv4-prefix {
+ description "Configure IPv4 prefix to match";
+ type inet:ipv4-prefix;
}
- }
- leaf ipv4-prefix-length-lesser-or-equal {
- description
- "Specifies if matching prefixes with length lesser than
- or equal to value";
- type uint8 {
- range "0..32";
+ leaf ipv4-prefix-length-greater-or-equal {
+ description
+ "Specifies if matching prefixes with length greater than
+ or equal to value";
+ type uint8 {
+ range "0..32";
+ }
}
- }
- }
- case ipv6-prefix {
- when "./type = 'ipv6'";
- leaf ipv6-prefix {
- description "Configure IPv6 prefix to match";
- type inet:ipv6-prefix;
+ leaf ipv4-prefix-length-lesser-or-equal {
+ description
+ "Specifies if matching prefixes with length lesser than
+ or equal to value";
+ type uint8 {
+ range "0..32";
+ }
+ }
}
+ case ipv6-prefix {
+ when "../type = 'ipv6'";
- leaf ipv6-prefix-length-greater-or-equal {
- description
- "Specifies if matching prefixes with length greater than
- or equal to value";
- type uint8 {
- range "0..128";
+ leaf ipv6-prefix {
+ description "Configure IPv6 prefix to match";
+ type inet:ipv6-prefix;
+ }
+
+ leaf ipv6-prefix-length-greater-or-equal {
+ description
+ "Specifies if matching prefixes with length greater than
+ or equal to value";
+ type uint8 {
+ range "0..128";
+ }
}
- }
- leaf ipv6-prefix-length-lesser-or-equal {
- description
- "Specifies if matching prefixes with length lesser than
- or equal to value";
- type uint8 {
- range "0..128";
+ leaf ipv6-prefix-length-lesser-or-equal {
+ description
+ "Specifies if matching prefixes with length lesser than
+ or equal to value";
+ type uint8 {
+ range "0..128";
+ }
}
}
- }
- case any {
- leaf any {
- description "Match anything";
- type empty;
+ case any {
+ leaf any {
+ description "Match anything";
+ type empty;
+ }
}
}
}
diff --git a/yang/frr-nexthop.yang b/yang/frr-nexthop.yang
index ce6f21a663..779c56df7f 100644
--- a/yang/frr-nexthop.yang
+++ b/yang/frr-nexthop.yang
@@ -137,6 +137,13 @@ module frr-nexthop {
"Nexthop blackhole types.";
}
+ typedef nexthop-group-ref {
+ type leafref {
+ path "/frr-nexthop:frr-nexthop-group/frr-nexthop:nexthop-groups/frr-nexthop:name";
+ require-instance false;
+ }
+ }
+
/*
* Common nexthop attributes grouping.
*/
@@ -147,6 +154,7 @@ module frr-nexthop {
description
"The nexthop type.";
}
+
leaf vrf {
type frr-vrf:vrf-ref;
description
@@ -157,17 +165,20 @@ module frr-nexthop {
description
"The nexthop gateway address.";
}
+
leaf interface {
type frr-interface:interface-ref;
description
"The nexthop egress interface.";
}
+
leaf bh-type {
when "../nh-type = 'blackhole'";
type blackhole-type;
description
"A blackhole sub-type, if the nexthop is a blackhole type.";
}
+
leaf onlink {
when "../nh-type = 'ip4-ifindex' or
../nh-type = 'ip6-ifindex'";
@@ -223,6 +234,15 @@ module frr-nexthop {
}
}
+ grouping nexthop-grouping {
+ list nexthop {
+ key "nh-type gateway interface";
+ description
+ "A list of nexthop objects.";
+ uses frr-nexthop-attributes;
+ }
+ }
+
/*
* Single nexthop grouping.
*/
@@ -230,12 +250,7 @@ module frr-nexthop {
container frr-nexthops {
description
"FRR nexthop object.";
- list nexthop {
- key "nh-type gateway interface";
- description
- "A list of nexthop objects.";
- uses frr-nexthop-attributes;
- }
+ uses nexthop-grouping;
}
}
@@ -244,11 +259,10 @@ module frr-nexthop {
* Container for FRR nexthop group.
*/
grouping frr-nexthop-grouping {
- list nexthop-group {
+ list nexthop-groups {
key "name";
description
- "A group of nexthops.";
-
+ "List of nexthop groups, each contains group of nexthops";
leaf name {
type string;
description
@@ -259,23 +273,31 @@ module frr-nexthop {
}
}
+ /* Operational nexthop-group */
+ grouping frr-nexthop-group-operational {
+ container nexthop-group {
+ description
+ "A group of nexthops.";
+ leaf id {
+ type uint32;
+ description
+ "The nexthop-group id.";
+ }
+
+ uses nexthop-grouping;
+ }
+ }
+
container frr-nexthop-group {
description
"A nexthop-group, represented as a list of nexthop objects.";
uses frr-nexthop-grouping;
}
- typedef nexthop-group-ref {
- type leafref {
- require-instance false;
- path "/frr-nexthop:frr-nexthop-group/frr-nexthop:nexthop-group/frr-nexthop:name";
- }
- }
-
/*
* Augment weight attributes to nexthop group.
*/
- augment "/frr-nexthop-group/nexthop-group/frr-nexthops/nexthop" {
+ augment "/frr-nexthop-group/nexthop-groups/frr-nexthops/nexthop" {
leaf weight {
type uint8;
description
diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang
index 4aeba14129..159dd8f791 100644
--- a/yang/frr-zebra.yang
+++ b/yang/frr-zebra.yang
@@ -316,7 +316,7 @@ module frr-zebra {
"Uptime for the route.";
}
- uses frr-nh:frr-nexthop-grouping;
+ uses frr-nh:frr-nexthop-group-operational;
}
// End of route-common
@@ -2074,7 +2074,7 @@ module frr-zebra {
}
}
- augment "/frr-vrf:lib/frr-vrf:vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop" {
+ augment "/frr-vrf:lib/frr-vrf:vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop" {
uses frr-nh:frr-nexthop-operational;
}
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index c743011224..7677db7efd 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -1121,16 +1121,23 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
nl_msg_type_to_str(h->nlmsg_type));
return -1;
}
+
if (h->nlmsg_type == RTM_NEWADDR)
connected_add_ipv4(ifp, flags, (struct in_addr *)addr,
ifa->ifa_prefixlen,
(struct in_addr *)broad, label,
metric);
- else
+ else if (CHECK_FLAG(flags, ZEBRA_IFA_PEER)) {
+ /* Delete with a peer address */
+ connected_delete_ipv4(
+ ifp, flags, (struct in_addr *)addr,
+ ifa->ifa_prefixlen, broad);
+ } else
connected_delete_ipv4(
ifp, flags, (struct in_addr *)addr,
ifa->ifa_prefixlen, NULL);
}
+
if (ifa->ifa_family == AF_INET6) {
if (ifa->ifa_prefixlen > IPV6_MAX_BITLEN) {
zlog_err(
diff --git a/zebra/main.c b/zebra/main.c
index 05dd70ff7a..71c7ebb62f 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -253,6 +253,7 @@ struct quagga_signal_t zebra_signals[] = {
};
static const struct frr_yang_module_info *const zebra_yang_modules[] = {
+ &frr_filter_info,
&frr_interface_info,
&frr_route_map_info,
&frr_zebra_info,
diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c
index a5a605f27e..9da008bb61 100644
--- a/zebra/rule_netlink.c
+++ b/zebra/rule_netlink.c
@@ -41,6 +41,7 @@
#include "zebra/rule_netlink.h"
#include "zebra/zebra_pbr.h"
#include "zebra/zebra_errors.h"
+#include "zebra/zebra_dplane.h"
/* definitions */
@@ -48,11 +49,19 @@
/* Private functions */
-/* Install or uninstall specified rule for a specific interface.
- * Form netlink message and ship it. Currently, notify status after
- * waiting for netlink status.
+
+/*
+ * netlink_rule_msg_encode
+ *
+ * Encodes netlink RTM_ADDRULE/RTM_DELRULE message to buffer buf of size buflen.
+ *
+ * Returns -1 on failure or the number of bytes
+ * written to buf.
*/
-static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule)
+static ssize_t netlink_rule_msg_encode(
+ int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
+ uint32_t priority, uint32_t table, const struct prefix *src_ip,
+ const struct prefix *dst_ip, uint32_t fwmark, void *buf, size_t buflen)
{
uint8_t protocol = RTPROT_ZEBRA;
int family;
@@ -60,142 +69,133 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule)
struct {
struct nlmsghdr n;
struct fib_rule_hdr frh;
- char buf[NL_PKT_BUF_SIZE];
- } req;
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
- struct sockaddr_nl snl;
+ char buf[];
+ } *req = buf;
+
+ const char *ifname = dplane_ctx_get_ifname(ctx);
char buf1[PREFIX_STRLEN];
char buf2[PREFIX_STRLEN];
- memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE);
- family = PREFIX_FAMILY(&rule->rule.filter.src_ip);
+ memset(req, 0, sizeof(*req));
+ family = PREFIX_FAMILY(src_ip);
bytelen = (family == AF_INET ? 4 : 16);
- req.n.nlmsg_type = cmd;
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
- req.n.nlmsg_flags = NLM_F_REQUEST;
- req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid;
+ req->n.nlmsg_type = cmd;
+ req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req->n.nlmsg_flags = NLM_F_REQUEST;
- req.frh.family = family;
- req.frh.action = FR_ACT_TO_TBL;
+ req->frh.family = family;
+ req->frh.action = FR_ACT_TO_TBL;
- addattr_l(&req.n, sizeof(req),
- FRA_PROTOCOL, &protocol, sizeof(protocol));
+ addattr_l(&req->n, buflen, FRA_PROTOCOL, &protocol, sizeof(protocol));
/* rule's pref # */
- addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority);
+ addattr32(&req->n, buflen, FRA_PRIORITY, priority);
/* interface on which applied */
- addattr_l(&req.n, sizeof(req), FRA_IFNAME, rule->ifname,
- strlen(rule->ifname) + 1);
+ addattr_l(&req->n, buflen, FRA_IFNAME, ifname, strlen(ifname) + 1);
/* source IP, if specified */
- if (IS_RULE_FILTERING_ON_SRC_IP(rule)) {
- req.frh.src_len = rule->rule.filter.src_ip.prefixlen;
- addattr_l(&req.n, sizeof(req), FRA_SRC,
- &rule->rule.filter.src_ip.u.prefix, bytelen);
+ if (filter_bm & PBR_FILTER_SRC_IP) {
+ req->frh.src_len = src_ip->prefixlen;
+ addattr_l(&req->n, buflen, FRA_SRC, &src_ip->u.prefix, bytelen);
}
+
/* destination IP, if specified */
- if (IS_RULE_FILTERING_ON_DST_IP(rule)) {
- req.frh.dst_len = rule->rule.filter.dst_ip.prefixlen;
- addattr_l(&req.n, sizeof(req), FRA_DST,
- &rule->rule.filter.dst_ip.u.prefix, bytelen);
+ if (filter_bm & PBR_FILTER_DST_IP) {
+ req->frh.dst_len = dst_ip->prefixlen;
+ addattr_l(&req->n, buflen, FRA_DST, &dst_ip->u.prefix, bytelen);
}
/* fwmark, if specified */
- if (IS_RULE_FILTERING_ON_FWMARK(rule)) {
- addattr32(&req.n, sizeof(req), FRA_FWMARK,
- rule->rule.filter.fwmark);
- }
+ if (filter_bm & PBR_FILTER_FWMARK)
+ addattr32(&req->n, buflen, FRA_FWMARK, fwmark);
/* Route table to use to forward, if filter criteria matches. */
- if (rule->rule.action.table < 256)
- req.frh.table = rule->rule.action.table;
+ if (table < 256)
+ req->frh.table = table;
else {
- req.frh.table = RT_TABLE_UNSPEC;
- addattr32(&req.n, sizeof(req), FRA_TABLE,
- rule->rule.action.table);
+ req->frh.table = RT_TABLE_UNSPEC;
+ addattr32(&req->n, buflen, FRA_TABLE, table);
}
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
"Tx %s family %s IF %s(%u) Pref %u Fwmark %u Src %s Dst %s Table %u",
nl_msg_type_to_str(cmd), nl_family_to_str(family),
- rule->ifname, rule->rule.ifindex, rule->rule.priority,
- rule->rule.filter.fwmark,
- prefix2str(&rule->rule.filter.src_ip, buf1,
- sizeof(buf1)),
- prefix2str(&rule->rule.filter.dst_ip, buf2,
- sizeof(buf2)),
- rule->rule.action.table);
+ ifname, dplane_ctx_get_ifindex(ctx), priority, fwmark,
+ prefix2str(src_ip, buf1, sizeof(buf1)),
+ prefix2str(dst_ip, buf2, sizeof(buf2)), table);
- /* Ship off the message.
- * Note: Currently, netlink_talk() is a blocking call which returns
- * back the status.
- */
- memset(&snl, 0, sizeof(snl));
- snl.nl_family = AF_NETLINK;
- return netlink_talk(netlink_talk_filter, &req.n,
- &zns->netlink_cmd, zns, 0);
+ return NLMSG_ALIGN(req->n.nlmsg_len);
}
-
-/* Public functions */
-/*
- * Install specified rule for a specific interface. The preference is what
- * goes in the rule to denote relative ordering; it may or may not be the
- * same as the rule's user-defined sequence number.
+/* Install or uninstall specified rule for a specific interface.
+ * Form netlink message and ship it.
*/
-enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule)
+static int
+netlink_rule_update_internal(int cmd, const struct zebra_dplane_ctx *ctx,
+ uint32_t filter_bm, uint32_t priority,
+ uint32_t table, const struct prefix *src_ip,
+ const struct prefix *dst_ip, uint32_t fwmark)
{
- int ret = 0;
+ char buf[NL_PKT_BUF_SIZE];
- ret = netlink_rule_update(RTM_NEWRULE, rule);
- kernel_pbr_rule_add_del_status(rule,
- (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS
- : ZEBRA_DPLANE_INSTALL_FAILURE);
-
- return ZEBRA_DPLANE_REQUEST_SUCCESS;
+ netlink_rule_msg_encode(cmd, ctx, filter_bm, priority, table, src_ip,
+ dst_ip, fwmark, buf, sizeof(buf));
+ return netlink_talk_info(netlink_talk_filter, (void *)&buf,
+ dplane_ctx_get_ns(ctx), 0);
}
+/* Public functions */
/*
- * Uninstall specified rule for a specific interface.
+ * Add, update or delete a rule from the
+ * kernel, using info from a dataplane context.
*/
-enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule)
+enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
{
- int ret = 0;
-
- ret = netlink_rule_update(RTM_DELRULE, rule);
- kernel_pbr_rule_add_del_status(rule,
- (!ret) ? ZEBRA_DPLANE_DELETE_SUCCESS
- : ZEBRA_DPLANE_DELETE_FAILURE);
-
- return ZEBRA_DPLANE_REQUEST_SUCCESS;
-}
+ enum dplane_op_e op;
+ int cmd;
+ int ret;
-/*
- * Update specified rule for a specific interface.
- */
-enum zebra_dplane_result kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule,
- struct zebra_pbr_rule *new_rule)
-{
- int ret = 0;
+ op = dplane_ctx_get_op(ctx);
+ if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE)
+ cmd = RTM_NEWRULE;
+ else if (op == DPLANE_OP_RULE_DELETE)
+ cmd = RTM_DELRULE;
+ else {
+ flog_err(
+ EC_ZEBRA_PBR_RULE_UPDATE,
+ "Context received for kernel rule update with incorrect OP code (%u)",
+ op);
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
+ }
- /* Add the new, updated one */
- ret = netlink_rule_update(RTM_NEWRULE, new_rule);
+ ret = netlink_rule_update_internal(
+ cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx),
+ dplane_ctx_rule_get_priority(ctx),
+ dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
+ dplane_ctx_rule_get_dst_ip(ctx),
+ dplane_ctx_rule_get_fwmark(ctx));
/**
* Delete the old one.
*
* Don't care about this result right?
*/
- netlink_rule_update(RTM_DELRULE, old_rule);
-
- kernel_pbr_rule_add_del_status(new_rule,
- (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS
- : ZEBRA_DPLANE_INSTALL_FAILURE);
-
- return ZEBRA_DPLANE_REQUEST_SUCCESS;
+ if (op == DPLANE_OP_RULE_UPDATE)
+ netlink_rule_update_internal(
+ RTM_DELRULE, ctx,
+ dplane_ctx_rule_get_old_filter_bm(ctx),
+ dplane_ctx_rule_get_old_priority(ctx),
+ dplane_ctx_rule_get_old_table(ctx),
+ dplane_ctx_rule_get_old_src_ip(ctx),
+ dplane_ctx_rule_get_old_dst_ip(ctx),
+ dplane_ctx_rule_get_old_fwmark(ctx));
+
+
+ return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
+ : ZEBRA_DPLANE_REQUEST_FAILURE);
}
/*
@@ -296,14 +296,16 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
* It should have been flushed on a previous shutdown.
*/
if (startup && proto == RTPROT_ZEBRA) {
- int ret;
+ enum zebra_dplane_result ret;
- ret = netlink_rule_update(RTM_DELRULE, &rule);
+ ret = dplane_pbr_rule_delete(&rule);
zlog_debug(
"%s: %s leftover rule: family %s IF %s(%u) Pref %u Src %s Dst %s Table %u",
__func__,
- ((ret == 0) ? "Removed" : "Failed to remove"),
+ ((ret == ZEBRA_DPLANE_REQUEST_FAILURE)
+ ? "Failed to remove"
+ : "Removed"),
nl_family_to_str(frh->family), rule.ifname,
rule.rule.ifindex, rule.rule.priority,
prefix2str(&rule.rule.filter.src_ip, buf1,
diff --git a/zebra/rule_socket.c b/zebra/rule_socket.c
index 219fa7de6f..e629017bdf 100644
--- a/zebra/rule_socket.c
+++ b/zebra/rule_socket.c
@@ -43,26 +43,11 @@
#include "zebra/zebra_pbr.h"
#include "zebra/zebra_errors.h"
-enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule)
+enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
{
flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform",
__func__);
return ZEBRA_DPLANE_REQUEST_FAILURE;
}
-enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule)
-{
- flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform",
- __func__);
- return ZEBRA_DPLANE_REQUEST_FAILURE;
-}
-
-enum zebra_dplane_result kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule,
- struct zebra_pbr_rule *new_rule)
-{
- flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform",
- __PRETTY_FUNCTION__);
- return ZEBRA_DPLANE_REQUEST_FAILURE;
-}
-
#endif
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index cea8edf752..a40aa8b643 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -785,7 +785,7 @@ int zsend_route_notify_owner_ctx(const struct zebra_dplane_ctx *ctx,
note));
}
-void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
+void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx,
enum zapi_rule_notify_owner note)
{
struct listnode *node;
@@ -793,10 +793,11 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
struct stream *s;
if (IS_ZEBRA_DEBUG_PACKET)
- zlog_debug("%s: Notifying %u", __func__, rule->rule.unique);
+ zlog_debug("%s: Notifying %u", __func__,
+ dplane_ctx_rule_get_unique(ctx));
for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) {
- if (rule->sock == client->sock)
+ if (dplane_ctx_rule_get_sock(ctx) == client->sock)
break;
}
@@ -807,10 +808,10 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
zclient_create_header(s, ZEBRA_RULE_NOTIFY_OWNER, VRF_DEFAULT);
stream_put(s, &note, sizeof(note));
- stream_putl(s, rule->rule.seq);
- stream_putl(s, rule->rule.priority);
- stream_putl(s, rule->rule.unique);
- stream_putl(s, rule->rule.ifindex);
+ stream_putl(s, dplane_ctx_rule_get_seq(ctx));
+ stream_putl(s, dplane_ctx_rule_get_priority(ctx));
+ stream_putl(s, dplane_ctx_rule_get_unique(ctx));
+ stream_putl(s, dplane_ctx_get_ifindex(ctx));
stream_putw_at(s, 0, stream_get_endp(s));
diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h
index 6d655e11aa..eb50e3c410 100644
--- a/zebra/zapi_msg.h
+++ b/zebra/zapi_msg.h
@@ -81,7 +81,7 @@ extern int zsend_route_notify_owner(struct route_entry *re,
extern int zsend_route_notify_owner_ctx(const struct zebra_dplane_ctx *ctx,
enum zapi_route_notify_owner note);
-extern void zsend_rule_notify_owner(struct zebra_pbr_rule *rule,
+extern void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx,
enum zapi_rule_notify_owner note);
extern void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset,
enum zapi_ipset_notify_owner note);
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 568b398924..eb3d48d784 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -35,6 +35,7 @@
#include "zebra/zebra_mpls.h"
#include "zebra/rt.h"
#include "zebra/debug.h"
+#include "zebra/zebra_pbr.h"
/* Memory type for context blocks */
DEFINE_MTYPE_STATIC(ZEBRA, DP_CTX, "Zebra DPlane Ctx")
@@ -192,6 +193,36 @@ struct dplane_neigh_info {
};
/*
+ * Policy based routing rule info for the dataplane
+ */
+struct dplane_ctx_rule {
+ uint32_t priority;
+
+ /* The route table pointed by this rule */
+ uint32_t table;
+
+ /* Filter criteria */
+ uint32_t filter_bm;
+ uint32_t fwmark;
+ struct prefix src_ip;
+ struct prefix dst_ip;
+};
+
+struct dplane_rule_info {
+ /*
+ * Originating zclient sock fd, so we can know who to send
+ * back to.
+ */
+ int sock;
+
+ int unique;
+ int seq;
+
+ struct dplane_ctx_rule new;
+ struct dplane_ctx_rule old;
+};
+
+/*
* The context block used to exchange info about route updates across
* the boundary between the zebra main context (and pthread) and the
* dataplane layer (and pthread).
@@ -238,6 +269,7 @@ struct zebra_dplane_ctx {
struct dplane_intf_info intf;
struct dplane_mac_info macinfo;
struct dplane_neigh_info neigh;
+ struct dplane_rule_info rule;
} u;
/* Namespace info, used especially for netlink kernel communication */
@@ -361,6 +393,9 @@ static struct zebra_dplane_globals {
_Atomic uint32_t dg_neighs_in;
_Atomic uint32_t dg_neigh_errors;
+ _Atomic uint32_t dg_rules_in;
+ _Atomic uint32_t dg_rule_errors;
+
_Atomic uint32_t dg_update_yields;
/* Dataplane pthread */
@@ -564,6 +599,9 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_NEIGH_DELETE:
case DPLANE_OP_VTEP_ADD:
case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
case DPLANE_OP_NONE:
break;
}
@@ -786,6 +824,16 @@ const char *dplane_op2str(enum dplane_op_e op)
case DPLANE_OP_VTEP_DELETE:
ret = "VTEP_DELETE";
break;
+
+ case DPLANE_OP_RULE_ADD:
+ ret = "RULE_ADD";
+ break;
+ case DPLANE_OP_RULE_DELETE:
+ ret = "RULE_DELETE";
+ break;
+ case DPLANE_OP_RULE_UPDATE:
+ ret = "RULE_UPDATE";
+ break;
}
return ret;
@@ -1517,6 +1565,116 @@ uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx)
return ctx->u.neigh.state;
}
+/* Accessors for PBR rule information */
+int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.sock;
+}
+
+int dplane_ctx_rule_get_unique(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.unique;
+}
+
+int dplane_ctx_rule_get_seq(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.seq;
+}
+
+uint32_t dplane_ctx_rule_get_priority(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.new.priority;
+}
+
+uint32_t dplane_ctx_rule_get_old_priority(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.old.priority;
+}
+
+uint32_t dplane_ctx_rule_get_table(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.new.table;
+}
+
+uint32_t dplane_ctx_rule_get_old_table(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.old.table;
+}
+
+uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.new.filter_bm;
+}
+
+uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.old.filter_bm;
+}
+
+uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.new.fwmark;
+}
+
+uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.old.fwmark;
+}
+
+const struct prefix *
+dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return &(ctx->u.rule.new.src_ip);
+}
+
+const struct prefix *
+dplane_ctx_rule_get_old_src_ip(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return &(ctx->u.rule.old.src_ip);
+}
+
+const struct prefix *
+dplane_ctx_rule_get_dst_ip(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return &(ctx->u.rule.new.dst_ip);
+}
+
+const struct prefix *
+dplane_ctx_rule_get_old_dst_ip(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return &(ctx->u.rule.old.dst_ip);
+}
+
/*
* End of dplane context accessors
*/
@@ -1913,6 +2071,76 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
return AOK;
}
+/**
+ * dplane_ctx_rule_init_single() - Initialize a dataplane representation of a
+ * PBR rule.
+ *
+ * @dplane_rule: Dataplane internal representation of a rule
+ * @rule: PBR rule
+ */
+static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule,
+ struct zebra_pbr_rule *rule)
+{
+ dplane_rule->priority = rule->rule.priority;
+ dplane_rule->table = rule->rule.action.table;
+
+ dplane_rule->filter_bm = rule->rule.filter.filter_bm;
+ dplane_rule->fwmark = rule->rule.filter.fwmark;
+ prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip);
+ prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip);
+}
+
+/**
+ * dplane_ctx_rule_init() - Initialize a context block for a PBR rule update.
+ *
+ * @ctx: Dataplane context to init
+ * @op: Operation being performed
+ * @new_rule: PBR rule
+ *
+ * Return: Result status
+ */
+static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx,
+ enum dplane_op_e op,
+ struct zebra_pbr_rule *new_rule,
+ struct zebra_pbr_rule *old_rule)
+{
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ char buf1[PREFIX_STRLEN];
+ char buf2[PREFIX_STRLEN];
+
+ zlog_debug(
+ "init dplane ctx %s: IF %s(%u) Prio %u Fwmark %u Src %s Dst %s Table %u",
+ dplane_op2str(op), new_rule->ifname,
+ new_rule->rule.ifindex, new_rule->rule.priority,
+ new_rule->rule.filter.fwmark,
+ prefix2str(&new_rule->rule.filter.src_ip, buf1,
+ sizeof(buf1)),
+ prefix2str(&new_rule->rule.filter.dst_ip, buf2,
+ sizeof(buf2)),
+ new_rule->rule.action.table);
+ }
+
+ ctx->zd_op = op;
+ ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+
+ dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
+ op == DPLANE_OP_RULE_UPDATE);
+
+ ctx->zd_vrf_id = new_rule->vrf_id;
+ memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname));
+ ctx->zd_ifindex = new_rule->rule.ifindex;
+
+ ctx->u.rule.sock = new_rule->sock;
+ ctx->u.rule.unique = new_rule->rule.unique;
+ ctx->u.rule.seq = new_rule->rule.seq;
+
+ dplane_ctx_rule_init_single(&ctx->u.rule.new, new_rule);
+ if (op == DPLANE_OP_RULE_UPDATE)
+ dplane_ctx_rule_init_single(&ctx->u.rule.old, old_rule);
+
+ return AOK;
+}
+
/*
* Enqueue a new update,
* and ensure an event is active for the dataplane pthread.
@@ -2840,6 +3068,56 @@ neigh_update_internal(enum dplane_op_e op,
}
/*
+ * Common helper api for PBR rule updates
+ */
+static enum zebra_dplane_result
+rule_update_internal(enum dplane_op_e op, struct zebra_pbr_rule *new_rule,
+ struct zebra_pbr_rule *old_rule)
+{
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ struct zebra_dplane_ctx *ctx;
+ int ret;
+
+ ctx = dplane_ctx_alloc();
+
+ ret = dplane_ctx_rule_init(ctx, op, new_rule, old_rule);
+ if (ret != AOK)
+ goto done;
+
+ ret = dplane_update_enqueue(ctx);
+
+done:
+ atomic_fetch_add_explicit(&zdplane_info.dg_rules_in, 1,
+ memory_order_relaxed);
+
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else {
+ atomic_fetch_add_explicit(&zdplane_info.dg_rule_errors, 1,
+ memory_order_relaxed);
+ dplane_ctx_free(&ctx);
+ }
+
+ return result;
+}
+
+enum zebra_dplane_result dplane_pbr_rule_add(struct zebra_pbr_rule *rule)
+{
+ return rule_update_internal(DPLANE_OP_RULE_ADD, rule, NULL);
+}
+
+enum zebra_dplane_result dplane_pbr_rule_delete(struct zebra_pbr_rule *rule)
+{
+ return rule_update_internal(DPLANE_OP_RULE_DELETE, rule, NULL);
+}
+
+enum zebra_dplane_result dplane_pbr_rule_update(struct zebra_pbr_rule *old_rule,
+ struct zebra_pbr_rule *new_rule)
+{
+ return rule_update_internal(DPLANE_OP_RULE_UPDATE, new_rule, old_rule);
+}
+
+/*
* Handler for 'show dplane'
*/
int dplane_show_helper(struct vty *vty, bool detailed)
@@ -2909,6 +3187,13 @@ int dplane_show_helper(struct vty *vty, bool detailed)
vty_out(vty, "EVPN neigh updates: %"PRIu64"\n", incoming);
vty_out(vty, "EVPN neigh errors: %"PRIu64"\n", errs);
+ incoming = atomic_load_explicit(&zdplane_info.dg_rules_in,
+ memory_order_relaxed);
+ errs = atomic_load_explicit(&zdplane_info.dg_rule_errors,
+ memory_order_relaxed);
+ vty_out(vty, "Rule updates: %" PRIu64 "\n", incoming);
+ vty_out(vty, "Rule errors: %" PRIu64 "\n", errs);
+
return CMD_SUCCESS;
}
@@ -3398,6 +3683,29 @@ kernel_dplane_neigh_update(struct zebra_dplane_ctx *ctx)
}
/*
+ * Handler for kernel PBR rule updates
+ */
+static enum zebra_dplane_result
+kernel_dplane_rule_update(struct zebra_dplane_ctx *ctx)
+{
+ enum zebra_dplane_result res;
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ zlog_debug("Dplane rule update op %s, if %s(%u), ctx %p",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifname(ctx),
+ dplane_ctx_get_ifindex(ctx), ctx);
+
+ res = kernel_pbr_rule_update(ctx);
+
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_rule_errors, 1,
+ memory_order_relaxed);
+
+ return res;
+}
+
+/*
* Kernel provider callback
*/
static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
@@ -3470,6 +3778,12 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
res = kernel_dplane_neigh_update(ctx);
break;
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ res = kernel_dplane_rule_update(ctx);
+ break;
+
/* Ignore 'notifications' - no-op */
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 7f8049b767..c93b95a6ad 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -144,6 +144,11 @@ enum dplane_op_e {
/* EVPN VTEP updates */
DPLANE_OP_VTEP_ADD,
DPLANE_OP_VTEP_DELETE,
+
+ /* Policy based routing rule update */
+ DPLANE_OP_RULE_ADD,
+ DPLANE_OP_RULE_DELETE,
+ DPLANE_OP_RULE_UPDATE,
};
/*
@@ -384,6 +389,27 @@ const struct ethaddr *dplane_ctx_neigh_get_mac(
uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx);
uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx);
+/* Accessors for policy based routing rule information */
+int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx);
+int dplane_ctx_rule_get_unique(const struct zebra_dplane_ctx *ctx);
+int dplane_ctx_rule_get_seq(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_rule_get_priority(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_rule_get_old_priority(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_rule_get_table(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_rule_get_old_table(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx);
+uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx);
+const struct prefix *
+dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx);
+const struct prefix *
+dplane_ctx_rule_get_old_src_ip(const struct zebra_dplane_ctx *ctx);
+const struct prefix *
+dplane_ctx_rule_get_dst_ip(const struct zebra_dplane_ctx *ctx);
+const struct prefix *
+dplane_ctx_rule_get_old_dst_ip(const struct zebra_dplane_ctx *ctx);
+
/* Namespace info - esp. for netlink communication */
const struct zebra_dplane_info *dplane_ctx_get_ns(
const struct zebra_dplane_ctx *ctx);
@@ -509,6 +535,21 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp,
const struct in_addr *ip,
vni_t vni);
+/* Forward ref of zebra_pbr_rule */
+struct zebra_pbr_rule;
+
+/*
+ * Enqueue policy based routing rule for the dataplane.
+ * It is possible that the user-defined sequence number and the one in the
+ * forwarding plane may not coincide, hence the API requires a separate
+ * rule priority - maps to preference/FRA_PRIORITY on Linux.
+ */
+enum zebra_dplane_result dplane_pbr_rule_add(struct zebra_pbr_rule *rule);
+enum zebra_dplane_result dplane_pbr_rule_delete(struct zebra_pbr_rule *rule);
+enum zebra_dplane_result
+dplane_pbr_rule_update(struct zebra_pbr_rule *old_rule,
+ struct zebra_pbr_rule *new_rule);
+
/* Encode route information into data plane context. */
int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
struct route_node *rn, struct route_entry *re);
diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h
index 395004d0bb..5f2a7a12c6 100644
--- a/zebra/zebra_errors.h
+++ b/zebra/zebra_errors.h
@@ -77,6 +77,7 @@ enum zebra_log_refs {
EC_ZEBRA_NHG_FIB_UPDATE,
EC_ZEBRA_IF_LOOKUP_FAILED,
EC_ZEBRA_NS_NO_DEFAULT,
+ EC_ZEBRA_PBR_RULE_UPDATE,
/* warnings */
EC_ZEBRA_NS_NOTIFY_READ,
EC_ZEBRAING_LM_PROTO_MISMATCH,
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index c580fe40d5..b194f80fc7 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -339,7 +339,7 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd,
}
/* If there is no useful nexthop then return. */
- if (ri->num_nhs == 0) {
+ if (ri->rtm_type != RTN_BLACKHOLE && ri->num_nhs == 0) {
zfpm_debug("netlink_encode_route(): No useful nexthop.");
return 0;
}
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index 1210430b06..31b100598f 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -1258,8 +1258,7 @@ static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type,
zebra_nhlfe_t *nhlfe;
struct nexthop *nexthop;
- if (!lsp)
- return NULL;
+ assert(lsp);
nhlfe = XCALLOC(MTYPE_NHLFE, sizeof(zebra_nhlfe_t));
@@ -1268,10 +1267,6 @@ static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type,
nhlfe->distance = lsp_distance(lsp_type);
nexthop = nexthop_new();
- if (!nexthop) {
- XFREE(MTYPE_NHLFE, nhlfe);
- return NULL;
- }
nexthop_add_labels(nexthop, lsp_type, num_labels, labels);
@@ -1322,8 +1317,7 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type,
labels);
/* Enqueue to LSP, at head of list. */
- if (nhlfe)
- nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe);
+ nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe);
return nhlfe;
}
@@ -1351,8 +1345,7 @@ static zebra_nhlfe_t *nhlfe_backup_add(zebra_lsp_t *lsp,
SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP);
/* Enqueue to LSP, at tail of list. */
- if (nhlfe)
- nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe);
+ nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe);
return nhlfe;
}
@@ -1568,7 +1561,8 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty)
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
vty_out(vty, " via %s",
- inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ));
+ inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
+ sizeof(buf)));
if (nexthop->ifindex)
vty_out(vty, " dev %s",
ifindex2ifname(nexthop->ifindex,
diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c
index 25b8b44ec9..53fe8e8e3f 100644
--- a/zebra/zebra_nb.c
+++ b/zebra/zebra_nb.c
@@ -558,117 +558,115 @@ const struct frr_yang_module_info frr_zebra_info = {
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/id",
.cbs = {
- .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_next,
- .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_keys,
- .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_lookup_entry,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_id_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/name",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_name_get_elem,
+ .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_next,
+ .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys,
+ .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_lookup_entry,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/nh-type",
.cbs = {
- .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_next,
- .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_keys,
- .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_lookup_entry,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_nh_type_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/nh-type",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/vrf",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_nh_type_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_vrf_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/vrf",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/gateway",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_vrf_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_gateway_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/gateway",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/interface",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_gateway_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_interface_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/interface",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/bh-type",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_interface_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/bh-type",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/onlink",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_bh_type_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/onlink",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_onlink_get_elem,
+ .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next,
+ .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys,
+ .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_lookup_entry,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/id",
.cbs = {
- .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_next,
- .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_keys,
- .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_lookup_entry,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_id_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/id",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/label",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_id_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_label_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/label",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/ttl",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_label_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_ttl_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/ttl",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/traffic-class",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_traffic_class_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/duplicate",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_duplicate_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/duplicate",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/recursive",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_duplicate_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_recursive_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/recursive",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/active",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_recursive_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_active_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/active",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/fib",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_active_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem,
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/fib",
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/weight",
.cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_fib_get_elem,
+ .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem,
}
},
{
@@ -714,12 +712,6 @@ const struct frr_yang_module_info frr_zebra_info = {
}
},
{
- .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/weight",
- .cbs = {
- .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_weight_get_elem,
- }
- },
- {
.xpath = NULL,
},
}
diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h
index 15350eb53b..a9e7fd5fb0 100644
--- a/zebra/zebra_nb.h
+++ b/zebra/zebra_nb.h
@@ -256,75 +256,69 @@ lib_vrf_zebra_ribs_rib_route_route_entry_internal_status_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_uptime_get_elem(
struct nb_cb_get_elem_args *args);
-const void *lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_next(
- struct nb_cb_get_next_args *args);
-int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_keys(
- struct nb_cb_get_keys_args *args);
-const void *lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_lookup_entry(
- struct nb_cb_lookup_entry_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_name_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_id_get_elem(
struct nb_cb_get_elem_args *args);
const void *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_next(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_next(
struct nb_cb_get_next_args *args);
-int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_keys(
+int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys(
struct nb_cb_get_keys_args *args);
const void *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_lookup_entry(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_lookup_entry(
struct nb_cb_lookup_entry_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_nh_type_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_nh_type_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_vrf_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_vrf_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_gateway_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_gateway_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_interface_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_interface_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_bh_type_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_onlink_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem(
struct nb_cb_get_elem_args *args);
const void *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_next(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next(
struct nb_cb_get_next_args *args);
-int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_keys(
+int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys(
struct nb_cb_get_keys_args *args);
const void *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_lookup_entry(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_lookup_entry(
struct nb_cb_lookup_entry_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_id_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_id_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_label_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_label_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_ttl_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_traffic_class_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_duplicate_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_duplicate_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_recursive_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_recursive_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_active_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_active_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_fib_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem(
struct nb_cb_get_elem_args *args);
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_weight_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem(
struct nb_cb_get_elem_args *args);
#ifdef __cplusplus
diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c
index 5b87a18a06..948ef51320 100644
--- a/zebra/zebra_nb_config.c
+++ b/zebra/zebra_nb_config.c
@@ -941,13 +941,15 @@ int lib_interface_zebra_ip_addrs_create(struct nb_cb_create_args *args)
case NB_EV_VALIDATE:
if (prefix.family == AF_INET
&& ipv4_martian(&prefix.u.prefix4)) {
- zlog_debug("invalid address %s",
- prefix2str(&prefix, buf, sizeof(buf)));
+ snprintf(args->errmsg, args->errmsg_len,
+ "invalid address %s",
+ prefix2str(&prefix, buf, sizeof(buf)));
return NB_ERR_VALIDATION;
} else if (prefix.family == AF_INET6
&& ipv6_martian(&prefix.u.prefix6)) {
- zlog_debug("invalid address %s",
- prefix2str(&prefix, buf, sizeof(buf)));
+ snprintf(args->errmsg, args->errmsg_len,
+ "invalid address %s",
+ prefix2str(&prefix, buf, sizeof(buf)));
return NB_ERR_VALIDATION;
}
break;
@@ -982,16 +984,18 @@ int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args)
/* Check current interface address. */
ifc = connected_check_ptp(ifp, &prefix, NULL);
if (!ifc) {
- zlog_debug("interface %s Can't find address\n",
- ifp->name);
+ snprintf(args->errmsg, args->errmsg_len,
+ "interface %s Can't find address\n",
+ ifp->name);
return NB_ERR_VALIDATION;
}
} else if (prefix.family == AF_INET6) {
/* Check current interface address. */
ifc = connected_check(ifp, &prefix);
if (!ifc) {
- zlog_debug("interface can't find address %s",
- ifp->name);
+ snprintf(args->errmsg, args->errmsg_len,
+ "interface can't find address %s",
+ ifp->name);
return NB_ERR_VALIDATION;
}
} else
@@ -999,7 +1003,8 @@ int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args)
/* This is not configured address. */
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) {
- zlog_debug("interface %s not configured", ifp->name);
+ snprintf(args->errmsg, args->errmsg_len,
+ "interface %s not configured", ifp->name);
return NB_ERR_VALIDATION;
}
@@ -1244,8 +1249,8 @@ int lib_vrf_zebra_ribs_rib_create(struct nb_cb_create_args *args)
switch (args->event) {
case NB_EV_VALIDATE:
if (!zrt) {
- zlog_debug("%s: vrf %s table is not found.", __func__,
- vrf->name);
+ snprintf(args->errmsg, args->errmsg_len,
+ "vrf %s table is not found.", vrf->name);
return NB_ERR_VALIDATION;
}
break;
@@ -1376,7 +1381,8 @@ int lib_route_map_entry_match_condition_source_protocol_modify(
case NB_EV_VALIDATE:
type = yang_dnode_get_string(args->dnode, NULL);
if (proto_name2num(type) == -1) {
- zlog_warn("%s: invalid protocol: %s", __func__, type);
+ snprintf(args->errmsg, args->errmsg_len,
+ "invalid protocol: %s", type);
return NB_ERR_VALIDATION;
}
return NB_OK;
@@ -1470,8 +1476,9 @@ int lib_route_map_entry_set_action_source_v4_modify(
memset(&p, 0, sizeof(p));
yang_dnode_get_ipv4p(&p, args->dnode, NULL);
if (zebra_check_addr(&p) == 0) {
- zlog_warn("%s: invalid IPv4 address: %s", __func__,
- yang_dnode_get_string(args->dnode, NULL));
+ snprintf(args->errmsg, args->errmsg_len,
+ "invalid IPv4 address: %s",
+ yang_dnode_get_string(args->dnode, NULL));
return NB_ERR_VALIDATION;
}
@@ -1482,8 +1489,9 @@ int lib_route_map_entry_set_action_source_v4_modify(
break;
}
if (pif == NULL) {
- zlog_warn("%s: is not a local adddress: %s", __func__,
- yang_dnode_get_string(args->dnode, NULL));
+ snprintf(args->errmsg, args->errmsg_len,
+ "is not a local adddress: %s",
+ yang_dnode_get_string(args->dnode, NULL));
return NB_ERR_VALIDATION;
}
return NB_OK;
@@ -1536,8 +1544,9 @@ int lib_route_map_entry_set_action_source_v6_modify(
memset(&p, 0, sizeof(p));
yang_dnode_get_ipv6p(&p, args->dnode, NULL);
if (zebra_check_addr(&p) == 0) {
- zlog_warn("%s: invalid IPv6 address: %s", __func__,
- yang_dnode_get_string(args->dnode, NULL));
+ snprintf(args->errmsg, args->errmsg_len,
+ "invalid IPv6 address: %s",
+ yang_dnode_get_string(args->dnode, NULL));
return NB_ERR_VALIDATION;
}
@@ -1548,8 +1557,9 @@ int lib_route_map_entry_set_action_source_v6_modify(
break;
}
if (pif == NULL) {
- zlog_warn("%s: is not a local adddress: %s", __func__,
- yang_dnode_get_string(args->dnode, NULL));
+ snprintf(args->errmsg, args->errmsg_len,
+ "is not a local adddress: %s",
+ yang_dnode_get_string(args->dnode, NULL));
return NB_ERR_VALIDATION;
}
return NB_OK;
diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c
index afbabe342c..4bca3b36d6 100644
--- a/zebra/zebra_nb_state.c
+++ b/zebra/zebra_nb_state.c
@@ -490,78 +490,38 @@ struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_uptime_get_elem(
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group
- */
-const void *lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_next(
- struct nb_cb_get_next_args *args)
-{
- struct route_entry *re = (struct route_entry *)args->parent_list_entry;
- struct nhg_hash_entry *nhe = (struct nhg_hash_entry *)args->list_entry;
-
- if (nhe == NULL) {
- nhe = re->nhe;
- return nhe;
- }
- return NULL;
-}
-
-int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_get_keys(
- struct nb_cb_get_keys_args *args)
-{
- struct nhg_hash_entry *nhe = (struct nhg_hash_entry *)args->list_entry;
-
- args->keys->num = 1;
- snprintfrr(args->keys->key[0], sizeof(args->keys->key[0]), "%" PRIu32,
- nhe->id);
-
- return NB_OK;
-}
-
-const void *lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_lookup_entry(
- struct nb_cb_lookup_entry_args *args)
-{
- struct route_entry *re = (struct route_entry *)args->parent_list_entry;
-
- return re->nhe;
-}
-
-/*
- * XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/name
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/id
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_name_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_id_get_elem(
struct nb_cb_get_elem_args *args)
{
- struct nhg_hash_entry *nhe = (struct nhg_hash_entry *)args->list_entry;
- char name[20] = {'\0'};
-
- snprintfrr(name, sizeof(name), "%" PRIu32, nhe->id);
+ struct route_entry *re = (struct route_entry *)args->list_entry;
- return yang_data_new_string(args->xpath, name);
+ return yang_data_new_uint32(args->xpath, re->nhe->id);
}
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop
*/
const void *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_next(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_next(
struct nb_cb_get_next_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
- struct nhg_hash_entry *nhe =
- (struct nhg_hash_entry *)args->parent_list_entry;
+ struct route_entry *re = (struct route_entry *)args->parent_list_entry;
+ struct nhg_hash_entry *nhe = re->nhe;
- if (args->list_entry == NULL)
+ if (args->list_entry == NULL) {
nexthop = nhe->nhg.nexthop;
- else
+ } else
nexthop = nexthop_next(nexthop);
return nexthop;
}
-int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_get_keys(
+int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys(
struct nb_cb_get_keys_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -623,7 +583,7 @@ int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_
}
const void *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_lookup_entry(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_lookup_entry(
struct nb_cb_lookup_entry_args *args)
{
struct nhg_hash_entry *nhe;
@@ -697,10 +657,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_look
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/nh-type
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/nh-type
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_nh_type_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_nh_type_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -730,10 +690,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_nh_t
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/vrf
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/vrf
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_vrf_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_vrf_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -744,10 +704,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_vrf_
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/gateway
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/gateway
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_gateway_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_gateway_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -780,10 +740,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_gate
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/interface
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/interface
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_interface_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_interface_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -798,10 +758,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_inte
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/bh-type
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/bh-type
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_bh_type_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -830,10 +790,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_bh_t
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/onlink
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/onlink
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_onlink_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -846,17 +806,17 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_onli
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry
*/
const void *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_next(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next(
struct nb_cb_get_next_args *args)
{
/* TODO: implement me. */
return NULL;
}
-int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_get_keys(
+int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys(
struct nb_cb_get_keys_args *args)
{
/* TODO: implement me. */
@@ -864,7 +824,7 @@ int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_
}
const void *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_lookup_entry(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_lookup_entry(
struct nb_cb_lookup_entry_args *args)
{
/* TODO: implement me. */
@@ -873,10 +833,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/id
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/id
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_id_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_id_get_elem(
struct nb_cb_get_elem_args *args)
{
/* TODO: implement me. */
@@ -885,10 +845,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/label
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/label
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_label_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_label_get_elem(
struct nb_cb_get_elem_args *args)
{
/* TODO: implement me. */
@@ -897,10 +857,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/ttl
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/ttl
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_ttl_get_elem(
struct nb_cb_get_elem_args *args)
{
/* TODO: implement me. */
@@ -909,10 +869,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/traffic-class
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_traffic_class_get_elem(
struct nb_cb_get_elem_args *args)
{
/* TODO: implement me. */
@@ -921,10 +881,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_mpls
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/duplicate
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/duplicate
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_duplicate_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_duplicate_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -937,10 +897,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_dupl
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/recursive
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/recursive
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_recursive_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_recursive_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -953,10 +913,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_recu
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/active
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/active
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_active_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_active_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -969,10 +929,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_acti
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/fib
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/fib
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_fib_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
@@ -985,10 +945,10 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_fib_
/*
* XPath:
- * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/frr-nexthops/nexthop/weight
+ * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/weight
*/
struct yang_data *
-lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_frr_nexthops_nexthop_weight_get_elem(
+lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem(
struct nb_cb_get_elem_args *args)
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index d07ceb652c..8723bd8d30 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -2563,6 +2563,9 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_NEIGH_DELETE:
case DPLANE_OP_VTEP_ADD:
case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
case DPLANE_OP_NONE:
break;
}
diff --git a/zebra/zebra_opaque.c b/zebra/zebra_opaque.c
index d1e0497154..41e278f71b 100644
--- a/zebra/zebra_opaque.c
+++ b/zebra/zebra_opaque.c
@@ -347,8 +347,9 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo)
{
struct stream *msg, *dup;
struct zmsghdr hdr;
+ struct zapi_opaque_msg info;
struct opq_msg_reg *reg;
- uint32_t type;
+ int ret;
struct opq_client_reg *client;
struct zserv *zclient;
char buf[50];
@@ -372,15 +373,17 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo)
/* Dispatch to any registered ZAPI client(s) */
- /* Extract subtype */
- STREAM_GETL(msg, type);
+ /* Extract subtype and flags */
+ ret = zclient_opaque_decode(msg, &info);
+ if (ret != 0)
+ goto drop_it;
/* Look up registered ZAPI client(s) */
- reg = opq_reg_lookup(type);
+ reg = opq_reg_lookup(info.type);
if (reg == NULL) {
if (IS_ZEBRA_DEBUG_RECV)
- zlog_debug("%s: no registrations for opaque type %u",
- __func__, type);
+ zlog_debug("%s: no registrations for opaque type %u, flags %#x",
+ __func__, info.type, info.flags);
goto drop_it;
}
@@ -391,9 +394,25 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo)
for (client = reg->clients; client; client = client->next) {
dup = NULL;
- /* Copy message if necessary */
- if (client->next)
- dup = stream_dup(msg);
+ if (CHECK_FLAG(info.flags, ZAPI_OPAQUE_FLAG_UNICAST)) {
+
+ if (client->proto != info.proto ||
+ client->instance != info.instance ||
+ client->session_id != info.session_id)
+ continue;
+
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("%s: found matching unicast client %s",
+ __func__,
+ opq_client2str(buf,
+ sizeof(buf),
+ client));
+
+ } else {
+ /* Copy message if more clients */
+ if (client->next)
+ dup = stream_dup(msg);
+ }
/*
* TODO -- this isn't ideal: we're going through an
@@ -427,7 +446,7 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo)
} else {
if (IS_ZEBRA_DEBUG_RECV)
zlog_debug("%s: type %u: no zclient for %s",
- __func__, type,
+ __func__, info.type,
opq_client2str(buf,
sizeof(buf),
client));
@@ -435,10 +454,14 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo)
if (dup)
stream_free(dup);
}
+
+ /* If unicast, we're done */
+ if (CHECK_FLAG(info.flags, ZAPI_OPAQUE_FLAG_UNICAST))
+ break;
}
drop_it:
-stream_failure:
+
if (msg)
stream_free(msg);
}
@@ -460,7 +483,7 @@ static int handle_opq_registration(const struct zmsghdr *hdr,
memset(&info, 0, sizeof(info));
- if (zapi_parse_opaque_reg(msg, &info) < 0) {
+ if (zapi_opaque_reg_decode(msg, &info) < 0) {
ret = -1;
goto done;
}
@@ -540,7 +563,7 @@ static int handle_opq_unregistration(const struct zmsghdr *hdr,
memset(&info, 0, sizeof(info));
- if (zapi_parse_opaque_reg(msg, &info) < 0) {
+ if (zapi_opaque_reg_decode(msg, &info) < 0) {
ret = -1;
goto done;
}
diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c
index 62cbcbda4b..c5a7795273 100644
--- a/zebra/zebra_pbr.c
+++ b/zebra/zebra_pbr.c
@@ -131,7 +131,7 @@ void zebra_pbr_rules_free(void *arg)
rule = (struct zebra_pbr_rule *)arg;
- (void)kernel_del_pbr_rule(rule);
+ (void)dplane_pbr_rule_delete(rule);
XFREE(MTYPE_TMP, rule);
}
@@ -460,7 +460,7 @@ void zebra_pbr_add_rule(struct zebra_pbr_rule *rule)
/* If found, this is an update */
if (found) {
- (void)kernel_update_pbr_rule(found, rule);
+ (void)dplane_pbr_rule_update(found, rule);
if (pbr_rule_release(found))
zlog_debug(
@@ -468,12 +468,12 @@ void zebra_pbr_add_rule(struct zebra_pbr_rule *rule)
__PRETTY_FUNCTION__);
} else
- (void)kernel_add_pbr_rule(rule);
+ (void)dplane_pbr_rule_add(rule);
}
void zebra_pbr_del_rule(struct zebra_pbr_rule *rule)
{
- (void)kernel_del_pbr_rule(rule);
+ (void)dplane_pbr_rule_delete(rule);
if (pbr_rule_release(rule))
zlog_debug("%s: Rule being deleted we know nothing about",
@@ -486,7 +486,7 @@ static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data)
int *sock = data;
if (rule->sock == *sock) {
- (void)kernel_del_pbr_rule(rule);
+ (void)dplane_pbr_rule_delete(rule);
if (hash_release(zrouter.rules_hash, rule))
XFREE(MTYPE_TMP, rule);
else
@@ -735,25 +735,29 @@ void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable)
/*
* Handle success or failure of rule (un)install in the kernel.
*/
-void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule,
- enum zebra_dplane_status res)
+void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx)
{
- switch (res) {
- case ZEBRA_DPLANE_INSTALL_SUCCESS:
- zsend_rule_notify_owner(rule, ZAPI_RULE_INSTALLED);
- break;
- case ZEBRA_DPLANE_INSTALL_FAILURE:
- zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_INSTALL);
- break;
- case ZEBRA_DPLANE_DELETE_SUCCESS:
- zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED);
- break;
- case ZEBRA_DPLANE_DELETE_FAILURE:
- zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_REMOVE);
- break;
- case ZEBRA_DPLANE_STATUS_NONE:
- break;
- }
+ enum zebra_dplane_result res;
+ enum dplane_op_e op;
+
+ res = dplane_ctx_get_status(ctx);
+ op = dplane_ctx_get_op(ctx);
+ if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE)
+ zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_RULE_INSTALLED
+ : ZAPI_RULE_FAIL_INSTALL);
+ else if (op == DPLANE_OP_RULE_DELETE)
+ zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS
+ ? ZAPI_RULE_REMOVED
+ : ZAPI_RULE_FAIL_REMOVE);
+ else
+ flog_err(
+ EC_ZEBRA_PBR_RULE_UPDATE,
+ "Context received in pbr rule dplane result handler with incorrect OP code (%u)",
+ op);
+
+
+ dplane_ctx_fini(&ctx);
}
/*
diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h
index 83797b9521..4bc0f40037 100644
--- a/zebra/zebra_pbr.h
+++ b/zebra/zebra_pbr.h
@@ -170,24 +170,11 @@ void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable);
void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable);
/*
- * Install specified rule for a specific interface.
- * It is possible that the user-defined sequence number and the one in the
- * forwarding plane may not coincide, hence the API requires a separate
- * rule priority - maps to preference/FRA_PRIORITY on Linux.
- */
-extern enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule);
-
-/*
- * Uninstall specified rule for a specific interface.
- */
-extern enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule);
-
-/*
- * Update specified rule for a specific interface.
+ * Add, update or delete a rule from the
+ * kernel, using info from a dataplane context.
*/
extern enum zebra_dplane_result
-kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule,
- struct zebra_pbr_rule *new_rule);
+kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx);
/*
* Get to know existing PBR rules in the kernel - typically called at startup.
@@ -197,8 +184,7 @@ extern void kernel_read_pbr_rules(struct zebra_ns *zns);
/*
* Handle success or failure of rule (un)install in the kernel.
*/
-extern void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule,
- enum zebra_dplane_status res);
+extern void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx);
/*
* Handle success or failure of ipset kinds (un)install in the kernel.
diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c
index 8f255ecfb0..8f0c964c18 100644
--- a/zebra/zebra_pw.c
+++ b/zebra/zebra_pw.c
@@ -125,9 +125,12 @@ void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af,
pw->flags = flags;
pw->data = *data;
- if (zebra_pw_enabled(pw))
- zebra_register_rnh_pseudowire(pw->vrf_id, pw);
- else {
+ if (zebra_pw_enabled(pw)) {
+ bool nht_exists;
+ zebra_register_rnh_pseudowire(pw->vrf_id, pw, &nht_exists);
+ if (nht_exists)
+ zebra_pw_update(pw);
+ } else {
if (pw->protocol == ZEBRA_ROUTE_STATIC)
zebra_deregister_rnh_pseudowire(pw->vrf_id, pw);
zebra_pw_uninstall(pw);
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 0fc716db17..75619520dc 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -3583,6 +3583,12 @@ static int rib_process_dplane_results(struct thread *thread)
zebra_vxlan_handle_result(ctx);
break;
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ zebra_pbr_dplane_result(ctx);
+ break;
+
/* Some op codes not handled here */
case DPLANE_OP_ADDR_INSTALL:
case DPLANE_OP_ADDR_UNINSTALL:
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index ad2e00b1ec..20af96a557 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -343,25 +343,32 @@ static void addr2hostprefix(int af, const union g_addr *addr,
}
}
-void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw)
+void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw,
+ bool *nht_exists)
{
struct prefix nh;
struct rnh *rnh;
bool exists;
struct zebra_vrf *zvrf;
+ *nht_exists = false;
+
zvrf = vrf_info_lookup(vrf_id);
if (!zvrf)
return;
addr2hostprefix(pw->af, &pw->nexthop, &nh);
rnh = zebra_add_rnh(&nh, vrf_id, RNH_NEXTHOP_TYPE, &exists);
- if (rnh && !listnode_lookup(rnh->zebra_pseudowire_list, pw)) {
+ if (!rnh)
+ return;
+
+ if (!listnode_lookup(rnh->zebra_pseudowire_list, pw)) {
listnode_add(rnh->zebra_pseudowire_list, pw);
pw->rnh = rnh;
zebra_evaluate_rnh(zvrf, family2afi(pw->af), 1,
RNH_NEXTHOP_TYPE, &nh);
- }
+ } else
+ *nht_exists = true;
}
void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw)
@@ -1001,12 +1008,13 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2)
static int send_client(struct rnh *rnh, struct zserv *client,
enum rnh_type type, vrf_id_t vrf_id)
{
- struct stream *s;
+ struct stream *s = NULL;
struct route_entry *re;
unsigned long nump;
uint8_t num;
struct nexthop *nh;
struct route_node *rn;
+ int ret;
int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE
: ZEBRA_NEXTHOP_UPDATE;
@@ -1032,7 +1040,7 @@ static int send_client(struct rnh *rnh, struct zserv *client,
flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY,
"%s: Unknown family (%d) notification attempted\n",
__func__, rn->p.family);
- break;
+ goto failure;
}
if (re) {
struct zapi_nexthop znh;
@@ -1047,7 +1055,10 @@ static int send_client(struct rnh *rnh, struct zserv *client,
for (ALL_NEXTHOPS(re->nhe->nhg, nh))
if (rnh_nexthop_valid(re, nh)) {
zapi_nexthop_from_nexthop(&znh, nh);
- zapi_nexthop_encode(s, &znh, 0 /* flags */);
+ ret = zapi_nexthop_encode(s, &znh, 0/*flags*/);
+ if (ret < 0)
+ goto failure;
+
num++;
}
stream_putc_at(s, nump, num);
@@ -1063,6 +1074,11 @@ static int send_client(struct rnh *rnh, struct zserv *client,
client->nh_last_upd_time = monotime(NULL);
client->last_write_cmd = cmd;
return zserv_send_message(client, s);
+
+failure:
+
+ stream_free(s);
+ return -1;
}
static void print_nh(struct nexthop *nexthop, struct vty *vty)
diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h
index f07e5bc791..e744504920 100644
--- a/zebra/zebra_rnh.h
+++ b/zebra/zebra_rnh.h
@@ -50,7 +50,7 @@ extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid,
extern void zebra_free_rnh(struct rnh *rnh);
extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client,
enum rnh_type type, vrf_id_t vrfid);
-extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *);
+extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *, bool *);
extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *);
extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client,
enum rnh_type type);
diff --git a/zebra/zserv.c b/zebra/zserv.c
index cb863b258c..99a85fd2ce 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -811,14 +811,6 @@ struct zserv *zserv_acquire_client(uint8_t proto, unsigned short instance,
*/
void zserv_release_client(struct zserv *client)
{
- bool cleanup_p = false;
- const char *proto_str;
- uint16_t instance;
-
- /* Capture some info for debugging */
- proto_str = zebra_route_string(client->proto);
- instance = client->instance;
-
/*
* Once we've decremented the client object's refcount, it's possible
* for it to be deleted as soon as we release the lock, so we won't
@@ -833,13 +825,10 @@ void zserv_release_client(struct zserv *client)
* session is closed, schedule cleanup on the zebra
* main pthread.
*/
- if (client->is_closed) {
+ if (client->is_closed)
thread_add_event(zrouter.master,
zserv_handle_client_fail,
client, 0, &client->t_cleanup);
-
- cleanup_p = true;
- }
}
}
@@ -847,10 +836,6 @@ void zserv_release_client(struct zserv *client)
* Cleanup must take place on the zebra main pthread, so we've
* scheduled an event.
*/
- if (IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("%s: %s clean-up for client '%s'[%u]",
- __func__, (cleanup_p ? "scheduled" : "NO"),
- proto_str, instance);
}
/*