summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_routemap.c10
-rw-r--r--configure.ac1
-rw-r--r--debian/frr-pythontools.install1
-rw-r--r--doc/developer/ospf-sr.rst3
-rw-r--r--doc/developer/workflow.rst8
-rw-r--r--doc/user/installation.rst3
-rw-r--r--doc/user/ospfd.rst16
-rw-r--r--doc/user/routemap.rst5
-rw-r--r--isisd/isis_cli.c47
-rw-r--r--isisd/isis_mt.c6
-rw-r--r--isisd/isis_mt.h3
-rw-r--r--isisd/isis_nb.c6
-rw-r--r--isisd/isis_nb.h7
-rw-r--r--isisd/isis_nb_config.c12
-rw-r--r--isisd/isis_tlvs.c12
-rw-r--r--lib/explicit_bzero.c39
-rw-r--r--lib/md5.c1
-rw-r--r--lib/sha256.c16
-rw-r--r--lib/subdir.am3
-rw-r--r--lib/zebra.h4
-rw-r--r--ospf6d/ospf6_message.c27
-rwxr-xr-xospfclient/ospfclient.py1133
-rw-r--r--ospfclient/subdir.am8
-rw-r--r--ospfd/ospf_api.c31
-rw-r--r--ospfd/ospf_api.h18
-rw-r--r--ospfd/ospf_apiserver.c358
-rw-r--r--ospfd/ospf_apiserver.h21
-rw-r--r--ospfd/ospf_dump.c46
-rw-r--r--ospfd/ospf_dump.h4
-rw-r--r--ospfd/ospf_opaque.c96
-rw-r--r--ospfd/ospf_opaque.h2
-rw-r--r--ospfd/ospf_route.c23
-rw-r--r--ospfd/ospf_route.h5
-rw-r--r--ospfd/ospf_spf.c44
-rw-r--r--ospfd/ospf_spf.h3
-rw-r--r--ospfd/ospf_sr.c80
-rw-r--r--ospfd/ospf_ti_lfa.c13
-rw-r--r--ospfd/ospf_vty.c120
-rw-r--r--ospfd/ospfd.h3
-rw-r--r--redhat/frr.spec.in11
-rw-r--r--tests/ospfd/test_ospf_spf.c6
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py1
-rw-r--r--tests/topotests/lib/micronet.py5
-rw-r--r--tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py12
-rw-r--r--tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py12
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf1
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf1
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf1
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf1
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf1
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf1
-rw-r--r--tests/topotests/ospf_te_topo1/r4/ospfd.conf3
-rw-r--r--tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py2
-rwxr-xr-xtests/topotests/ospfapi/ctester.py137
l---------tests/topotests/ospfapi/lib1
-rw-r--r--tests/topotests/ospfapi/r1/ospfd.conf10
-rw-r--r--tests/topotests/ospfapi/r1/zebra.conf4
-rw-r--r--tests/topotests/ospfapi/r2/ospfd.conf15
-rw-r--r--tests/topotests/ospfapi/r2/zebra.conf7
-rw-r--r--tests/topotests/ospfapi/r3/ospfd.conf15
-rw-r--r--tests/topotests/ospfapi/r3/zebra.conf7
-rw-r--r--tests/topotests/ospfapi/r4/ospfd.conf10
-rw-r--r--tests/topotests/ospfapi/r4/zebra.conf4
-rw-r--r--tests/topotests/ospfapi/test_ospf_clientapi.py470
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py2
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py2
-rwxr-xr-x[-rw-r--r--]tools/frrcommon.sh.in4
-rwxr-xr-x[-rw-r--r--]tools/releasedate.py0
-rw-r--r--yang/frr-isisd.yang4
-rw-r--r--zebra/kernel_netlink.c13
70 files changed, 2656 insertions, 345 deletions
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 20ee2e4d49..c7f5e0433b 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -263,10 +263,14 @@ route_match_peer(void *rule, const struct prefix *prefix, void *object)
peer = ((struct bgp_path_info *)object)->peer;
if (pc->interface) {
- if (!peer->conf_if)
+ if (!peer->conf_if || !peer->group)
return RMAP_NOMATCH;
- if (strcmp(peer->conf_if, pc->interface) == 0)
+ if (peer->conf_if && strcmp(peer->conf_if, pc->interface) == 0)
+ return RMAP_MATCH;
+
+ if (peer->group &&
+ strcmp(peer->group->name, pc->interface) == 0)
return RMAP_MATCH;
return RMAP_NOMATCH;
@@ -4567,7 +4571,7 @@ DEFPY_YANG (match_peer,
"Match peer address\n"
"IP address of peer\n"
"IPv6 address of peer\n"
- "Interface name of peer\n")
+ "Interface name of peer or peer group name\n")
{
const char *xpath =
"./match-condition[condition='frr-bgp-route-map:peer']";
diff --git a/configure.ac b/configure.ac
index 36058c1db5..29d13b16cc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1317,6 +1317,7 @@ AC_CHECK_FUNCS([ \
unlinkat \
posix_fallocate \
sendmmsg \
+ explicit_bzero \
])
AC_CHECK_MEMBERS([struct mmsghdr.msg_hdr], [], [], FRR_INCLUDES)
diff --git a/debian/frr-pythontools.install b/debian/frr-pythontools.install
index 820895ce68..662fbe0f51 100644
--- a/debian/frr-pythontools.install
+++ b/debian/frr-pythontools.install
@@ -1,3 +1,4 @@
usr/lib/frr/frr-reload.py
usr/lib/frr/generate_support_bundle.py
usr/lib/frr/frr_babeltrace.py
+usr/lib/frr/ospfclient.py
diff --git a/doc/developer/ospf-sr.rst b/doc/developer/ospf-sr.rst
index 263db9dfb9..1c164433dc 100644
--- a/doc/developer/ospf-sr.rst
+++ b/doc/developer/ospf-sr.rst
@@ -310,8 +310,7 @@ Routing.
ospf router-id 192.168.1.11
capability opaque
segment-routing on
- segment-routing global-block 10000 19999
- segment-routing local-block 5000 5999
+ segment-routing global-block 10000 19999 local-block 5000 5999
segment-routing node-msd 8
segment-routing prefix 192.168.1.11/32 index 1100
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index adab9725d9..688ce545fb 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -163,13 +163,13 @@ as early as possible, i.e. the first 2-week window.
For reference, the expected release schedule according to the above is:
+---------+------------+------------+------------+------------+------------+
-| Release | 2021-11-02 | 2022-03-01 | 2022-07-05 | 2022-11-01 | 2023-03-07 |
+| Release | 2022-07-05 | 2022-11-01 | 2023-03-07 | 2023-07-04 | 2023-10-31 |
+---------+------------+------------+------------+------------+------------+
-| RC | 2021-10-19 | 2022-02-15 | 2022-06-21 | 2022-10-18 | 2023-02-21 |
+| RC | 2022-06-21 | 2022-10-18 | 2023-02-21 | 2023-06-20 | 2023-10-17 |
+---------+------------+------------+------------+------------+------------+
-| dev/X.Y | 2021-10-05 | 2022-02-01 | 2022-06-07 | 2022-10-04 | 2023-02-07 |
+| dev/X.Y | 2022-06-07 | 2022-10-04 | 2023-02-07 | 2023-06-06 | 2023-10-03 |
+---------+------------+------------+------------+------------+------------+
-| freeze | 2021-09-21 | 2022-01-18 | 2022-05-24 | 2022-09-20 | 2023-01-24 |
+| freeze | 2022-05-24 | 2022-09-20 | 2023-01-24 | 2023-05-23 | 2023-09-19 |
+---------+------------+------------+------------+------------+------------+
Each release is managed by one or more volunteer release managers from the FRR
diff --git a/doc/user/installation.rst b/doc/user/installation.rst
index b24a9fb471..401a1f2721 100644
--- a/doc/user/installation.rst
+++ b/doc/user/installation.rst
@@ -212,7 +212,8 @@ options from the list below.
.. option:: --disable-ospfclient
- Disable building of the example OSPF-API client.
+ Disable installation of the python ospfclient and building of the example
+ OSPF-API client.
.. option:: --disable-isisd
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index d1a0bb6f7b..e438d04e93 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -867,6 +867,12 @@ Opaque LSA
Show Opaque LSA from the database.
+.. clicmd:: show ip ospf (1-65535) reachable-routers
+
+.. clicmd:: show ip ospf [vrf <NAME|all>] reachable-routers
+
+ Show routing table of reachable routers.
+
.. _ospf-traffic-engineering:
Traffic Engineering
@@ -985,12 +991,6 @@ dataplane.
Block, i.e. the label range used for Adjacency SID. The negative version
of the command always unsets both ranges.
-.. clicmd:: segment-routing local-block (16-1048575) (16-1048575)
-
- Set the Segment Routing Local Block i.e. the label range used by MPLS to
- store label in the MPLS FIB for Adjacency SID. This command is deprecated
- in favor of the combined command above.
-
.. clicmd:: segment-routing node-msd (1-16)
Fix the Maximum Stack Depth supported by the router. The value depend of the
@@ -1064,6 +1064,10 @@ Debugging OSPF
library messages and OSPF BFD integration messages that are mostly state
transitions and validation problems.
+.. clicmd:: debug ospf client-api
+
+ Show debug information for the OSPF opaque data client API.
+
.. clicmd:: debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]
diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst
index ef7aef9c5e..754b709173 100644
--- a/doc/user/routemap.rst
+++ b/doc/user/routemap.rst
@@ -205,6 +205,11 @@ Route Map Match Command
interface name specified if the neighbor was specified
in this manner.
+.. clicmd:: match peer PEER_GROUP_NAME
+
+ This is a BGP specific match command. Matches the peer
+ group name specified for the peer in question.
+
.. clicmd:: match source-protocol PROTOCOL_NAME
This is a ZEBRA specific match command. Matches the
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index c23b0f0dc1..2bf9eadaba 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -1348,26 +1348,30 @@ void cli_show_isis_redistribute_ipv6(struct vty *vty,
/*
* XPath: /frr-isisd:isis/instance/multi-topology
*/
-DEFPY_YANG(isis_topology, isis_topology_cmd,
- "[no] topology <ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology [overload]$overload",
- NO_STR
- "Configure IS-IS topologies\n"
- "IPv4 unicast topology\n"
- "IPv4 management topology\n"
- "IPv6 unicast topology\n"
- "IPv4 multicast topology\n"
- "IPv6 multicast topology\n"
- "IPv6 management topology\n"
- "IPv6 dst-src topology\n"
- "Set overload bit for topology\n")
+DEFPY_YANG(
+ isis_topology, isis_topology_cmd,
+ "[no] topology <standard|ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology [overload]$overload",
+ NO_STR
+ "Configure IS-IS topologies\n"
+ "standard topology\n"
+ "IPv4 unicast topology\n"
+ "IPv4 management topology\n"
+ "IPv6 unicast topology\n"
+ "IPv4 multicast topology\n"
+ "IPv6 multicast topology\n"
+ "IPv6 management topology\n"
+ "IPv6 dst-src topology\n"
+ "Set overload bit for topology\n")
{
char base_xpath[XPATH_MAXLEN];
- /* Since IPv4-unicast is not configurable it is not present in the
+ /* Since standard is not configurable it is not present in the
* YANG model, so we need to validate it here
*/
- if (strmatch(topology, "ipv4-unicast")) {
- vty_out(vty, "Cannot configure IPv4 unicast topology\n");
+ if (strmatch(topology, "standard") ||
+ strmatch(topology, "ipv4-unicast")) {
+ vty_out(vty,
+ "Cannot configure IPv4 unicast (Standard) topology\n");
return CMD_WARNING_CONFIG_FAILED;
}
@@ -2333,10 +2337,11 @@ void cli_show_ip_isis_psnp_interval(struct vty *vty,
* XPath: /frr-interface:lib/interface/frr-isisd:isis/multi-topology
*/
DEFPY_YANG(circuit_topology, circuit_topology_cmd,
- "[no] isis topology<ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology",
+ "[no] isis topology<standard|ipv4-unicast|ipv4-mgmt|ipv6-unicast|ipv4-multicast|ipv6-multicast|ipv6-mgmt|ipv6-dstsrc>$topology",
NO_STR
"IS-IS routing protocol\n"
"Configure interface IS-IS topologies\n"
+ "Standard topology\n"
"IPv4 unicast topology\n"
"IPv4 management topology\n"
"IPv6 unicast topology\n"
@@ -2353,18 +2358,20 @@ DEFPY_YANG(circuit_topology, circuit_topology_cmd,
else if (strmatch(topology, "ipv6-mgmt"))
return nb_cli_apply_changes(
vty, "./frr-isisd:isis/multi-topology/ipv6-management");
+ if (strmatch(topology, "ipv4-unicast"))
+ return nb_cli_apply_changes(
+ vty, "./frr-isisd:isis/multi-topology/standard");
else
return nb_cli_apply_changes(
vty, "./frr-isisd:isis/multi-topology/%s", topology);
}
-void cli_show_ip_isis_mt_ipv4_unicast(struct vty *vty,
- const struct lyd_node *dnode,
- bool show_defaults)
+void cli_show_ip_isis_mt_standard(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
{
if (!yang_dnode_get_bool(dnode, NULL))
vty_out(vty, " no");
- vty_out(vty, " isis topology ipv4-unicast\n");
+ vty_out(vty, " isis topology standard\n");
}
void cli_show_ip_isis_mt_ipv4_multicast(struct vty *vty,
diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c
index f937bdf55c..6bbb01c307 100644
--- a/isisd/isis_mt.c
+++ b/isisd/isis_mt.c
@@ -56,8 +56,8 @@ const char *isis_mtid2str(uint16_t mtid)
static char buf[sizeof("65535")];
switch (mtid) {
- case ISIS_MT_IPV4_UNICAST:
- return "ipv4-unicast";
+ case ISIS_MT_STANDARD:
+ return "standard";
case ISIS_MT_IPV4_MGMT:
return "ipv4-mgmt";
case ISIS_MT_IPV6_UNICAST:
@@ -80,6 +80,8 @@ uint16_t isis_str2mtid(const char *name)
{
if (!strcmp(name, "ipv4-unicast"))
return ISIS_MT_IPV4_UNICAST;
+ if (!strcmp(name, "standard"))
+ return ISIS_MT_STANDARD;
if (!strcmp(name, "ipv4-mgmt"))
return ISIS_MT_IPV4_MGMT;
if (!strcmp(name, "ipv6-unicast"))
diff --git a/isisd/isis_mt.h b/isisd/isis_mt.h
index 2952a2f171..13e508451b 100644
--- a/isisd/isis_mt.h
+++ b/isisd/isis_mt.h
@@ -27,6 +27,7 @@
#define ISIS_MT_AT_MASK 0x4000
#define ISIS_MT_IPV4_UNICAST 0
+#define ISIS_MT_STANDARD ISIS_MT_IPV4_UNICAST
#define ISIS_MT_IPV4_MGMT 1
#define ISIS_MT_IPV6_UNICAST 2
#define ISIS_MT_IPV4_MULTICAST 3
@@ -37,7 +38,7 @@
#define ISIS_MT_DISABLE 4096
#define ISIS_MT_NAMES \
- "<ipv4-unicast" \
+ "<standard" \
"|ipv4-mgmt" \
"|ipv6-unicast" \
"|ipv4-multicast" \
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index 20e4806525..a2ba33d078 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -899,10 +899,10 @@ const struct frr_yang_module_info frr_isisd_info = {
},
},
{
- .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast",
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/standard",
.cbs = {
- .cli_show = cli_show_ip_isis_mt_ipv4_unicast,
- .modify = lib_interface_isis_multi_topology_ipv4_unicast_modify,
+ .cli_show = cli_show_ip_isis_mt_standard,
+ .modify = lib_interface_isis_multi_topology_standard_modify,
},
},
{
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
index 96de64a68b..00ca8be3b0 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -291,7 +291,7 @@ int lib_interface_isis_password_password_type_modify(
struct nb_cb_modify_args *args);
int lib_interface_isis_disable_three_way_handshake_modify(
struct nb_cb_modify_args *args);
-int lib_interface_isis_multi_topology_ipv4_unicast_modify(
+int lib_interface_isis_multi_topology_standard_modify(
struct nb_cb_modify_args *args);
int lib_interface_isis_multi_topology_ipv4_multicast_modify(
struct nb_cb_modify_args *args);
@@ -543,9 +543,8 @@ void cli_show_ip_isis_csnp_interval(struct vty *vty,
void cli_show_ip_isis_psnp_interval(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
-void cli_show_ip_isis_mt_ipv4_unicast(struct vty *vty,
- const struct lyd_node *dnode,
- bool show_defaults);
+void cli_show_ip_isis_mt_standard(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_ip_isis_mt_ipv4_multicast(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index cf4c2aea0a..79b167718b 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -3070,10 +3070,6 @@ int lib_interface_isis_disable_three_way_handshake_modify(
return NB_OK;
}
-/*
- * XPath:
- * /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, char *errmsg,
size_t errmsg_len, uint16_t mtid)
@@ -3104,12 +3100,16 @@ static int lib_interface_isis_multi_topology_common(
return NB_OK;
}
-int lib_interface_isis_multi_topology_ipv4_unicast_modify(
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/standard
+ */
+int lib_interface_isis_multi_topology_standard_modify(
struct nb_cb_modify_args *args)
{
return lib_interface_isis_multi_topology_common(
args->event, args->dnode, args->errmsg, args->errmsg_len,
- ISIS_MT_IPV4_UNICAST);
+ ISIS_MT_STANDARD);
}
/*
diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c
index 3ba5c6ccfa..11be3c3a71 100644
--- a/isisd/isis_tlvs.c
+++ b/isisd/isis_tlvs.c
@@ -119,6 +119,14 @@ static const struct tlv_ops *const tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX];
static void append_item(struct isis_item_list *dest, struct isis_item *item);
static void init_item_list(struct isis_item_list *items);
+/* For tests/isisd, TLV text requires ipv4-unicast instead of standard */
+static const char *isis_mtid2str_fake(uint16_t mtid)
+{
+ if (mtid == ISIS_MT_STANDARD)
+ return "ipv4-unicast";
+ return isis_mtid2str(mtid);
+}
+
/* Functions for Extended IS Reachability SubTLVs a.k.a Traffic Engineering */
struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void)
{
@@ -2391,7 +2399,7 @@ static void format_item_mt_router_info(uint16_t mtid, struct isis_item *i,
json_object_string_add(mt_json, "attached", info->attached?"true":"false");
} else
sbuf_push(buf, indent, "MT Router Info: %s%s%s\n",
- isis_mtid2str(info->mtid),
+ isis_mtid2str_fake(info->mtid),
info->overload ? " Overload" : "",
info->attached ? " Attached" : "");
}
@@ -4306,7 +4314,7 @@ static int unpack_tlv_with_items(enum isis_tlv_context context,
mtid = stream_getw(s) & ISIS_MT_MASK;
tlv_pos += 2;
sbuf_push(log, indent, "Unpacking as MT %s item TLV...\n",
- isis_mtid2str(mtid));
+ isis_mtid2str_fake(mtid));
} else {
sbuf_push(log, indent, "Unpacking as item TLV...\n");
mtid = ISIS_MT_IPV4_UNICAST;
diff --git a/lib/explicit_bzero.c b/lib/explicit_bzero.c
new file mode 100644
index 0000000000..fa64ed85bf
--- /dev/null
+++ b/lib/explicit_bzero.c
@@ -0,0 +1,39 @@
+/*
+ * Public domain.
+ * Written by Matthew Dempsky.
+ * Adapted for frr.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#ifndef HAVE_EXPLICIT_BZERO
+#undef explicit_bzero
+
+
+void explicit_bzero(void *buf, size_t len);
+__attribute__((__weak__)) void
+__explicit_bzero_hook(void *buf, size_t len);
+
+__attribute__((__weak__)) void
+__explicit_bzero_hook(void *buf, size_t len)
+{
+}
+
+#if defined(__clang__)
+#pragma clang optimize off
+#else
+#pragma GCC optimize("00")
+#endif
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ memset(buf, 0, len);
+ __explicit_bzero_hook(buf, len);
+}
+
+#endif
diff --git a/lib/md5.c b/lib/md5.c
index 5c93c7bc1f..20da6488c4 100644
--- a/lib/md5.c
+++ b/lib/md5.c
@@ -439,4 +439,5 @@ void hmac_md5(unsigned char *text, int text_len, unsigned char *key,
MD5Update(&context, digest, 16); /* then results of 1st
* hash */
MD5Final(digest, &context); /* finish up 2nd pass */
+ explicit_bzero(&context, sizeof(context));
}
diff --git a/lib/sha256.c b/lib/sha256.c
index a9b7a4aefc..f1727b6323 100644
--- a/lib/sha256.c
+++ b/lib/sha256.c
@@ -186,10 +186,10 @@ static void SHA256_Transform(uint32_t *state, const unsigned char block[64])
state[i] += S[i];
/* Clean the stack. */
- memset(W, 0, 256);
- memset(S, 0, 32);
- memset(&t0, 0, sizeof(t0));
- memset(&t1, 0, sizeof(t0));
+ explicit_bzero(W, 256);
+ explicit_bzero(S, 32);
+ explicit_bzero(&t0, sizeof(t0));
+ explicit_bzero(&t1, sizeof(t0));
}
static unsigned char PAD[64] = {
@@ -292,7 +292,7 @@ void SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
be32enc_vect(digest, ctx->state, 32);
/* Clear the context state */
- memset((void *)ctx, 0, sizeof(*ctx));
+ explicit_bzero((void *)ctx, sizeof(*ctx));
}
/* Initialize an HMAC-SHA256 operation with the given key. */
@@ -327,7 +327,7 @@ void HMAC__SHA256_Init(HMAC_SHA256_CTX *ctx, const void *_K, size_t Klen)
SHA256_Update(&ctx->octx, pad, 64);
/* Clean the stack. */
- memset(khash, 0, 32);
+ explicit_bzero(khash, 32);
}
/* Add bytes to the HMAC-SHA256 operation. */
@@ -353,7 +353,7 @@ void HMAC__SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX *ctx)
SHA256_Final(digest, &ctx->octx);
/* Clean the stack. */
- memset(ihash, 0, 32);
+ explicit_bzero(ihash, 32);
}
/**
@@ -409,5 +409,5 @@ void PBKDF2_SHA256(const uint8_t *passwd, size_t passwdlen, const uint8_t *salt,
}
/* Clean PShctx, since we never called _Final on it. */
- memset(&PShctx, 0, sizeof(HMAC_SHA256_CTX));
+ explicit_bzero(&PShctx, sizeof(HMAC_SHA256_CTX));
}
diff --git a/lib/subdir.am b/lib/subdir.am
index c3899c4e0f..b0c311e7f2 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -1,6 +1,7 @@
#
# libfrr
#
+
lib_LTLIBRARIES += lib/libfrr.la
lib_libfrr_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0 -Xlinker -e_libfrr_version
lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST_LIBS) $(LIBCRYPT) $(LIBDL) $(LIBM)
@@ -22,6 +23,7 @@ lib_libfrr_la_SOURCES = \
lib/debug.c \
lib/defaults.c \
lib/distribute.c \
+ lib/explicit_bzero.c \
lib/ferr.c \
lib/filter.c \
lib/filter_cli.c \
@@ -337,6 +339,7 @@ lib_libfrrsnmp_la_SOURCES = \
lib/snmp.c \
# end
+
#
# c-ares support
#
diff --git a/lib/zebra.h b/lib/zebra.h
index e8ddd869bb..53ae5b4e9e 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -230,6 +230,10 @@ size_t strlcpy(char *__restrict dest,
const char *__restrict src, size_t destsize);
#endif
+#ifndef HAVE_EXPLICIT_BZERO
+void explicit_bzero(void *buf, size_t len);
+#endif
+
#if !defined(HAVE_STRUCT_MMSGHDR_MSG_HDR) || !defined(HAVE_SENDMMSG)
/* avoid conflicts in case we have partial support */
#define mmsghdr frr_mmsghdr
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index ae7f16a9f6..93a062b215 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -1733,19 +1733,22 @@ static unsigned int iobuflen = 0;
int ospf6_iobuf_size(unsigned int size)
{
- uint8_t *recvnew, *sendnew;
+ /* NB: there was previously code here that tried to dynamically size
+ * the buffer for whatever we see in MTU on interfaces. Which is
+ * _unconditionally wrong_ - we can always receive fragmented IPv6
+ * up to the regular 64k length limit. (No jumbograms, thankfully.)
+ */
- if (size <= iobuflen)
- return iobuflen;
+ if (!iobuflen) {
+ /* the + 128 is to have some runway at the end */
+ size_t alloc_size = 65536 + 128;
- recvnew = XMALLOC(MTYPE_OSPF6_MESSAGE, size);
- sendnew = XMALLOC(MTYPE_OSPF6_MESSAGE, size);
+ assert(!recvbuf && !sendbuf);
- XFREE(MTYPE_OSPF6_MESSAGE, recvbuf);
- XFREE(MTYPE_OSPF6_MESSAGE, sendbuf);
- recvbuf = recvnew;
- sendbuf = sendnew;
- iobuflen = size;
+ recvbuf = XMALLOC(MTYPE_OSPF6_MESSAGE, alloc_size);
+ sendbuf = XMALLOC(MTYPE_OSPF6_MESSAGE, alloc_size);
+ iobuflen = alloc_size;
+ }
return iobuflen;
}
@@ -1779,7 +1782,6 @@ static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6)
memset(&src, 0, sizeof(src));
memset(&dst, 0, sizeof(dst));
ifindex = 0;
- memset(recvbuf, 0, iobuflen);
iovector[0].iov_base = recvbuf;
iovector[0].iov_len = iobuflen;
iovector[1].iov_base = NULL;
@@ -1795,6 +1797,9 @@ static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6)
return OSPF6_READ_ERROR;
}
+ /* ensure some zeroes past the end, just as a security precaution */
+ memset(recvbuf + len, 0, MIN(128, iobuflen - len));
+
oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id);
if (oi == NULL || oi->area == NULL
|| CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) {
diff --git a/ospfclient/ospfclient.py b/ospfclient/ospfclient.py
new file mode 100755
index 0000000000..be8b51f007
--- /dev/null
+++ b/ospfclient/ospfclient.py
@@ -0,0 +1,1133 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# December 22 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2021, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+import argparse
+import asyncio
+import errno
+import logging
+import socket
+import struct
+import sys
+from asyncio import Event, Lock
+from ipaddress import ip_address as ip
+
+FMT_APIMSGHDR = ">BBHL"
+FMT_APIMSGHDR_SIZE = struct.calcsize(FMT_APIMSGHDR)
+
+FMT_LSA_FILTER = ">HBB" # + plus x"I" areas
+LSAF_ORIGIN_NON_SELF = 0
+LSAF_ORIGIN_SELF = 1
+LSAF_ORIGIN_ANY = 2
+
+FMT_LSA_HEADER = ">HBBIILHH"
+FMT_LSA_HEADER_SIZE = struct.calcsize(FMT_LSA_HEADER)
+
+# ------------------------
+# Messages to OSPF daemon.
+# ------------------------
+
+MSG_REGISTER_OPAQUETYPE = 1
+MSG_UNREGISTER_OPAQUETYPE = 2
+MSG_REGISTER_EVENT = 3
+MSG_SYNC_LSDB = 4
+MSG_ORIGINATE_REQUEST = 5
+MSG_DELETE_REQUEST = 6
+MSG_SYNC_REACHABLE = 7
+MSG_SYNC_ISM = 8
+MSG_SYNC_NSM = 9
+
+smsg_info = {
+ MSG_REGISTER_OPAQUETYPE: ("REGISTER_OPAQUETYPE", "BBxx"),
+ MSG_UNREGISTER_OPAQUETYPE: ("UNREGISTER_OPAQUETYPE", "BBxx"),
+ MSG_REGISTER_EVENT: ("REGISTER_EVENT", FMT_LSA_FILTER),
+ MSG_SYNC_LSDB: ("SYNC_LSDB", FMT_LSA_FILTER),
+ MSG_ORIGINATE_REQUEST: ("ORIGINATE_REQUEST", ">II" + FMT_LSA_HEADER[1:]),
+ MSG_DELETE_REQUEST: ("DELETE_REQUEST", ">IBBxxL"),
+ MSG_SYNC_REACHABLE: ("MSG_SYNC_REACHABLE", ""),
+ MSG_SYNC_ISM: ("MSG_SYNC_ISM", ""),
+ MSG_SYNC_NSM: ("MSG_SYNC_NSM", ""),
+}
+
+# --------------------------
+# Messages from OSPF daemon.
+# --------------------------
+
+MSG_REPLY = 10
+MSG_READY_NOTIFY = 11
+MSG_LSA_UPDATE_NOTIFY = 12
+MSG_LSA_DELETE_NOTIFY = 13
+MSG_NEW_IF = 14
+MSG_DEL_IF = 15
+MSG_ISM_CHANGE = 16
+MSG_NSM_CHANGE = 17
+MSG_REACHABLE_CHANGE = 18
+
+amsg_info = {
+ MSG_REPLY: ("REPLY", "bxxx"),
+ MSG_READY_NOTIFY: ("READY_NOTIFY", ">BBxxI"),
+ MSG_LSA_UPDATE_NOTIFY: ("LSA_UPDATE_NOTIFY", ">IIBxxx" + FMT_LSA_HEADER[1:]),
+ MSG_LSA_DELETE_NOTIFY: ("LSA_DELETE_NOTIFY", ">IIBxxx" + FMT_LSA_HEADER[1:]),
+ MSG_NEW_IF: ("NEW_IF", ">II"),
+ MSG_DEL_IF: ("DEL_IF", ">I"),
+ MSG_ISM_CHANGE: ("ISM_CHANGE", ">IIBxxx"),
+ MSG_NSM_CHANGE: ("NSM_CHANGE", ">IIIBxxx"),
+ MSG_REACHABLE_CHANGE: ("REACHABLE_CHANGE", ">HH"),
+}
+
+OSPF_API_OK = 0
+OSPF_API_NOSUCHINTERFACE = -1
+OSPF_API_NOSUCHAREA = -2
+OSPF_API_NOSUCHLSA = -3
+OSPF_API_ILLEGALLSATYPE = -4
+OSPF_API_OPAQUETYPEINUSE = -5
+OSPF_API_OPAQUETYPENOTREGISTERED = -6
+OSPF_API_NOTREADY = -7
+OSPF_API_NOMEMORY = -8
+OSPF_API_ERROR = -9
+OSPF_API_UNDEF = -10
+
+msg_errname = {
+ OSPF_API_OK: "OSPF_API_OK",
+ OSPF_API_NOSUCHINTERFACE: "OSPF_API_NOSUCHINTERFACE",
+ OSPF_API_NOSUCHAREA: "OSPF_API_NOSUCHAREA",
+ OSPF_API_NOSUCHLSA: "OSPF_API_NOSUCHLSA",
+ OSPF_API_ILLEGALLSATYPE: "OSPF_API_ILLEGALLSATYPE",
+ OSPF_API_OPAQUETYPEINUSE: "OSPF_API_OPAQUETYPEINUSE",
+ OSPF_API_OPAQUETYPENOTREGISTERED: "OSPF_API_OPAQUETYPENOTREGISTERED",
+ OSPF_API_NOTREADY: "OSPF_API_NOTREADY",
+ OSPF_API_NOMEMORY: "OSPF_API_NOMEMORY",
+ OSPF_API_ERROR: "OSPF_API_ERROR",
+ OSPF_API_UNDEF: "OSPF_API_UNDEF",
+}
+
+# msg_info = {**smsg_info, **amsg_info}
+msg_info = {}
+msg_info.update(smsg_info)
+msg_info.update(amsg_info)
+msg_name = {k: v[0] for k, v in msg_info.items()}
+msg_fmt = {k: v[1] for k, v in msg_info.items()}
+msg_size = {k: struct.calcsize(v) for k, v in msg_fmt.items()}
+
+
+def api_msgname(mt):
+ return msg_name.get(mt, str(mt))
+
+
+def api_errname(ecode):
+ return msg_errname.get(ecode, str(ecode))
+
+
+# -------------------
+# API Semantic Errors
+# -------------------
+
+
+class APIError(Exception):
+ pass
+
+
+class MsgTypeError(Exception):
+ pass
+
+
+class SeqNumError(Exception):
+ pass
+
+
+# ---------
+# LSA Types
+# ---------
+
+LSA_TYPE_UNKNOWN = 0
+LSA_TYPE_ROUTER = 1
+LSA_TYPE_NETWORK = 2
+LSA_TYPE_SUMMARY = 3
+LSA_TYPE_ASBR_SUMMARY = 4
+LSA_TYPE_AS_EXTERNAL = 5
+LSA_TYPE_GROUP_MEMBER = 6
+LSA_TYPE_AS_NSSA = 7
+LSA_TYPE_EXTERNAL_ATTRIBUTES = 8
+LSA_TYPE_OPAQUE_LINK = 9
+LSA_TYPE_OPAQUE_AREA = 10
+LSA_TYPE_OPAQUE_AS = 11
+
+
+def lsa_typename(lsa_type):
+ names = {
+ LSA_TYPE_ROUTER: "LSA:ROUTER",
+ LSA_TYPE_NETWORK: "LSA:NETWORK",
+ LSA_TYPE_SUMMARY: "LSA:SUMMARY",
+ LSA_TYPE_ASBR_SUMMARY: "LSA:ASBR_SUMMARY",
+ LSA_TYPE_AS_EXTERNAL: "LSA:AS_EXTERNAL",
+ LSA_TYPE_GROUP_MEMBER: "LSA:GROUP_MEMBER",
+ LSA_TYPE_AS_NSSA: "LSA:AS_NSSA",
+ LSA_TYPE_EXTERNAL_ATTRIBUTES: "LSA:EXTERNAL_ATTRIBUTES",
+ LSA_TYPE_OPAQUE_LINK: "LSA:OPAQUE_LINK",
+ LSA_TYPE_OPAQUE_AREA: "LSA:OPAQUE_AREA",
+ LSA_TYPE_OPAQUE_AS: "LSA:OPAQUE_AS",
+ }
+ return names.get(lsa_type, str(lsa_type))
+
+
+# ------------------------------
+# Interface State Machine States
+# ------------------------------
+
+ISM_DEPENDUPON = 0
+ISM_DOWN = 1
+ISM_LOOPBACK = 2
+ISM_WAITING = 3
+ISM_POINTTOPOINT = 4
+ISM_DROTHER = 5
+ISM_BACKUP = 6
+ISM_DR = 7
+
+
+def ism_name(state):
+ names = {
+ ISM_DEPENDUPON: "ISM_DEPENDUPON",
+ ISM_DOWN: "ISM_DOWN",
+ ISM_LOOPBACK: "ISM_LOOPBACK",
+ ISM_WAITING: "ISM_WAITING",
+ ISM_POINTTOPOINT: "ISM_POINTTOPOINT",
+ ISM_DROTHER: "ISM_DROTHER",
+ ISM_BACKUP: "ISM_BACKUP",
+ ISM_DR: "ISM_DR",
+ }
+ return names.get(state, str(state))
+
+
+# -----------------------------
+# Neighbor State Machine States
+# -----------------------------
+
+NSM_DEPENDUPON = 0
+NSM_DELETED = 1
+NSM_DOWN = 2
+NSM_ATTEMPT = 3
+NSM_INIT = 4
+NSM_TWOWAY = 5
+NSM_EXSTART = 6
+NSM_EXCHANGE = 7
+NSM_LOADING = 8
+NSM_FULL = 9
+
+
+def nsm_name(state):
+ names = {
+ NSM_DEPENDUPON: "NSM_DEPENDUPON",
+ NSM_DELETED: "NSM_DELETED",
+ NSM_DOWN: "NSM_DOWN",
+ NSM_ATTEMPT: "NSM_ATTEMPT",
+ NSM_INIT: "NSM_INIT",
+ NSM_TWOWAY: "NSM_TWOWAY",
+ NSM_EXSTART: "NSM_EXSTART",
+ NSM_EXCHANGE: "NSM_EXCHANGE",
+ NSM_LOADING: "NSM_LOADING",
+ NSM_FULL: "NSM_FULL",
+ }
+ return names.get(state, str(state))
+
+
+# --------------
+# Client Classes
+# --------------
+
+
+class OspfApiClient:
+ def __str__(self):
+ return "OspfApiClient({})".format(self.server)
+
+ @staticmethod
+ def _get_bound_sockets(port):
+ s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+ try:
+ s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ # s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ s1.bind(("", port))
+ s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+ try:
+ s2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ # s2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ s2.bind(("", port + 1))
+ return s1, s2
+ except Exception:
+ s2.close()
+ raise
+ except Exception:
+ s1.close()
+ raise
+
+ def __init__(self, server="localhost", handlers=None):
+ """A client connection to OSPF Daemon using the OSPF API
+
+ The client object is not created in a connected state. To connect to the server
+ the `connect` method should be called. If an error is encountered when sending
+ messages to the server an exception will be raised and the connection will be
+ closed. When this happens `connect` may be called again to restore the
+ connection.
+
+ Args:
+ server: hostname or IP address of server default is "localhost"
+ handlers: dict of message handlers, the key is the API message
+ type, the value is a function. The functions signature is:
+ `handler(msg_type, msg, msg_extra, *params)`, where `msg` is the
+ message data after the API header, `*params` will be the
+ unpacked message values, and msg_extra are any bytes beyond the
+ fixed parameters of the message.
+ Raises:
+ Will raise exceptions for failures with various `socket` modules
+ functions such as `socket.socket`, `socket.setsockopt`, `socket.bind`.
+ """
+ self._seq = 0
+ self._s = None
+ self._as = None
+ self._ls = None
+ self._ar = self._r = self._w = None
+ self.server = server
+ self.handlers = handlers if handlers is not None else dict()
+ self.write_lock = Lock()
+
+ # try and get consecutive 2 ports
+ PORTSTART = 49152
+ PORTEND = 65534
+ for port in range(PORTSTART, PORTEND + 2, 2):
+ try:
+ logging.debug("%s: binding to ports %s, %s", self, port, port + 1)
+ self._s, self._ls = self._get_bound_sockets(port)
+ break
+ except OSError as error:
+ if error.errno != errno.EADDRINUSE or port == PORTEND:
+ logging.warning("%s: binding port %s error %s", self, port, error)
+ raise
+ logging.debug("%s: ports %s, %s in use.", self, port, port + 1)
+ else:
+ assert False, "Should not reach this code execution point"
+
+ async def _connect_locked(self):
+ logging.debug("%s: connect to OSPF API", self)
+
+ loop = asyncio.get_event_loop()
+
+ self._ls.listen()
+ try:
+ logging.debug("%s: connecting sync socket to server", self)
+ await loop.sock_connect(self._s, (self.server, 2607))
+
+ logging.debug("%s: accepting connect from server", self)
+ self._as, _ = await loop.sock_accept(self._ls)
+ except Exception:
+ await self._close_locked()
+ raise
+
+ logging.debug("%s: success", self)
+ self._r, self._w = await asyncio.open_connection(sock=self._s)
+ self._ar, _ = await asyncio.open_connection(sock=self._as)
+ self._seq = 1
+
+ async def connect(self):
+ async with self.write_lock:
+ await self._connect_locked()
+
+ @property
+ def closed(self):
+ "True if the connection is closed."
+ return self._seq == 0
+
+ async def _close_locked(self):
+ logging.debug("%s: closing", self)
+ if self._s:
+ if self._w:
+ self._w.close()
+ await self._w.wait_closed()
+ self._w = None
+ else:
+ self._s.close()
+ self._s = None
+ self._r = None
+ assert self._w is None
+ if self._as:
+ self._as.close()
+ self._as = None
+ self._ar = None
+ if self._ls:
+ self._ls.close()
+ self._ls = None
+ self._seq = 0
+
+ async def close(self):
+ async with self.write_lock:
+ await self._close_locked()
+
+ @staticmethod
+ async def _msg_read(r, expseq=-1):
+ """Read an OSPF API message from the socket `r`
+
+ Args:
+ r: socket to read msg from
+ expseq: sequence number to expect or -1 for any.
+ Raises:
+ Will raise exceptions for failures with various `socket` modules,
+ Additionally may raise SeqNumError if unexpected seqnum is received.
+ """
+ try:
+ mh = await r.readexactly(FMT_APIMSGHDR_SIZE)
+ v, mt, l, seq = struct.unpack(FMT_APIMSGHDR, mh)
+ if v != 1:
+ raise Exception("received unexpected OSPF API version {}".format(v))
+ if expseq == -1:
+ logging.debug("_msg_read: got seq: 0x%x on async read", seq)
+ elif seq != expseq:
+ raise SeqNumError("rx {} != {}".format(seq, expseq))
+ msg = await r.readexactly(l) if l else b""
+ return mt, msg
+ except asyncio.IncompleteReadError:
+ raise EOFError
+
+ async def msg_read(self):
+ """Read a message from the async notify channel.
+
+ Raises:
+ May raise exceptions for failures with various `socket` modules.
+ """
+ return await OspfApiClient._msg_read(self._ar, -1)
+
+ async def msg_send(self, mt, mp):
+ """Send a message to OSPF API and wait for error code reply.
+
+ Args:
+ mt: the messaage type
+ mp: the message payload
+ Returns:
+ error: an OSPF_API_XXX error code, 0 for OK.
+ Raises:
+ Raises SeqNumError if the synchronous reply is the wrong sequence number;
+ MsgTypeError if the synchronous reply is not MSG_REPLY. Also,
+ may raise exceptions for failures with various `socket` modules,
+
+ The connection will be closed.
+ """
+ logging.debug("SEND: %s: sending %s seq 0x%x", self, api_msgname(mt), self._seq)
+ mh = struct.pack(FMT_APIMSGHDR, 1, mt, len(mp), self._seq)
+
+ seq = self._seq
+ self._seq = seq + 1
+
+ try:
+ async with self.write_lock:
+ self._w.write(mh + mp)
+ await self._w.drain()
+ mt, mp = await OspfApiClient._msg_read(self._r, seq)
+
+ if mt != MSG_REPLY:
+ raise MsgTypeError(
+ "rx {} != {}".format(api_msgname(mt), api_msgname(MSG_REPLY))
+ )
+
+ return struct.unpack(msg_fmt[MSG_REPLY], mp)[0]
+ except Exception:
+ # We've written data with a sequence number
+ await self.close()
+ raise
+
+ async def msg_send_raises(self, mt, mp=b"\x00" * 4):
+ """Send a message to OSPF API and wait for error code reply.
+
+ Args:
+ mt: the messaage type
+ mp: the message payload
+ Raises:
+ APIError if the server replies with an error.
+
+ Also may raise exceptions for failures with various `socket` modules,
+ as well as MsgTypeError if the synchronous reply is incorrect.
+ The connection will be closed for these non-API error exceptions.
+ """
+ ecode = await self.msg_send(mt, mp)
+ if ecode:
+ raise APIError("{} error {}".format(api_msgname(mt), api_errname(ecode)))
+
+ async def handle_async_msg(self, mt, msg):
+ if mt not in msg_fmt:
+ logging.debug("RECV: %s: unknown async msg type %s", self, mt)
+ return
+
+ fmt = msg_fmt[mt]
+ sz = msg_size[mt]
+ tup = struct.unpack(fmt, msg[:sz])
+ extra = msg[sz:]
+
+ if mt not in self.handlers:
+ logging.debug(
+ "RECV: %s: no handlers for msg type %s", self, api_msgname(mt)
+ )
+ return
+
+ logging.debug("RECV: %s: calling handler for %s", self, api_msgname(mt))
+ await self.handlers[mt](mt, msg, extra, *tup)
+
+ #
+ # Client to Server Messaging
+ #
+ @staticmethod
+ def lsa_type_mask(*lsa_types):
+ "Return a 16 bit mask for each LSA type passed."
+ if not lsa_types:
+ return 0xFFFF
+ mask = 0
+ for t in lsa_types:
+ assert 0 < t < 16, "LSA type {} out of range [1, 15]".format(t)
+ mask |= 1 << t
+ return mask
+
+ @staticmethod
+ def lsa_filter(origin, areas, lsa_types):
+ """Return an LSA filter.
+
+ Return the filter message bytes based on `origin` the `areas` list and the LSAs
+ types in the `lsa_types` list.
+ """
+ mask = OspfApiClient.lsa_type_mask(*lsa_types)
+ narea = len(areas)
+ fmt = FMT_LSA_FILTER + ("{}I".format(narea) if narea else "")
+ # lsa type mask, origin, number of areas, each area
+ return struct.pack(fmt, mask, origin, narea, *areas)
+
+ async def req_lsdb_sync(self):
+ "Register for all LSA notifications and request an LSDB synchronoization."
+ logging.debug("SEND: %s: request LSDB events", self)
+ mp = OspfApiClient.lsa_filter(LSAF_ORIGIN_ANY, [], [])
+ await self.msg_send_raises(MSG_REGISTER_EVENT, mp)
+
+ logging.debug("SEND: %s: request LSDB sync", self)
+ await self.msg_send_raises(MSG_SYNC_LSDB, mp)
+
+ async def req_reachable_routers(self):
+ "Request a dump of all reachable routers."
+ logging.debug("SEND: %s: request reachable changes", self)
+ await self.msg_send_raises(MSG_SYNC_REACHABLE)
+
+ async def req_ism_states(self):
+ "Request a dump of the current ISM states of all interfaces."
+ logging.debug("SEND: %s: request ISM changes", self)
+ await self.msg_send_raises(MSG_SYNC_ISM)
+
+ async def req_nsm_states(self):
+ "Request a dump of the current NSM states of all neighbors."
+ logging.debug("SEND: %s: request NSM changes", self)
+ await self.msg_send_raises(MSG_SYNC_NSM)
+
+
+class OspfOpaqueClient(OspfApiClient):
+ """A client connection to OSPF Daemon for manipulating Opaque LSA data.
+
+ The client object is not created in a connected state. To connect to the server
+ the `connect` method should be called. If an error is encountered when sending
+ messages to the server an exception will be raised and the connection will be
+ closed. When this happens `connect` may be called again to restore the
+ connection.
+
+ Args:
+ server: hostname or IP address of server default is "localhost"
+
+ Raises:
+ Will raise exceptions for failures with various `socket` modules
+ functions such as `socket.socket`, `socket.setsockopt`, `socket.bind`.
+ """
+
+ def __init__(self, server="localhost"):
+ handlers = {
+ MSG_READY_NOTIFY: self._ready_msg,
+ MSG_LSA_UPDATE_NOTIFY: self._lsa_change_msg,
+ MSG_LSA_DELETE_NOTIFY: self._lsa_change_msg,
+ MSG_NEW_IF: self._if_msg,
+ MSG_DEL_IF: self._if_msg,
+ MSG_ISM_CHANGE: self._if_change_msg,
+ MSG_NSM_CHANGE: self._nbr_change_msg,
+ MSG_REACHABLE_CHANGE: self._reachable_msg,
+ }
+ super().__init__(server, handlers)
+
+ self.ready_lock = Lock()
+ self.ready_cond = {
+ LSA_TYPE_OPAQUE_LINK: {},
+ LSA_TYPE_OPAQUE_AREA: {},
+ LSA_TYPE_OPAQUE_AS: {},
+ }
+ self.lsid_seq_num = {}
+
+ self.lsa_change_cb = None
+ self.opaque_change_cb = {}
+
+ self.reachable_routers = set()
+ self.reachable_change_cb = None
+
+ self.if_area = {}
+ self.ism_states = {}
+ self.ism_change_cb = None
+
+ self.nsm_states = {}
+ self.nsm_change_cb = None
+
+ async def _register_opaque_data(self, lsa_type, otype):
+ async with self.ready_lock:
+ cond = self.ready_cond[lsa_type].get(otype)
+ assert cond is None, "multiple registers for {} opaque-type {}".format(
+ lsa_typename(lsa_type), otype
+ )
+
+ logging.debug("register %s opaque-type %s", lsa_typename(lsa_type), otype)
+
+ mt = MSG_REGISTER_OPAQUETYPE
+ mp = struct.pack(msg_fmt[mt], lsa_type, otype)
+ await self.msg_send_raises(mt, mp)
+
+ async def _assure_opaque_ready(self, lsa_type, otype):
+ async with self.ready_lock:
+ if self.ready_cond[lsa_type].get(otype) is True:
+ return
+
+ await self._register_opaque_data(lsa_type, otype)
+ await self.wait_opaque_ready(lsa_type, otype)
+
+ async def _handle_msg_loop(self):
+ try:
+ logging.debug("entering async msg handling loop")
+ while True:
+ mt, msg = await self.msg_read()
+ if mt in amsg_info:
+ await self.handle_async_msg(mt, msg)
+ else:
+ mts = api_msgname(mt)
+ logging.warning(
+ "ignoring unexpected msg: %s len: %s", mts, len(msg)
+ )
+ except EOFError:
+ logging.info("Got EOF from OSPF API server on async notify socket")
+ return 2
+
+ @staticmethod
+ def _opaque_args(lsa_type, otype, oid, mp):
+ lsid = (otype << 24) | oid
+ return 0, 0, lsa_type, lsid, 0, 0, 0, FMT_LSA_HEADER_SIZE + len(mp)
+
+ @staticmethod
+ def _make_opaque_lsa(lsa_type, otype, oid, mp):
+ # /* Make a new LSA from parameters */
+ lsa = struct.pack(
+ FMT_LSA_HEADER, *OspfOpaqueClient._opaque_args(lsa_type, otype, oid, mp)
+ )
+ lsa += mp
+ return lsa
+
+ async def _ready_msg(self, mt, msg, extra, lsa_type, otype, addr):
+ if lsa_type == LSA_TYPE_OPAQUE_LINK:
+ e = "ifaddr {}".format(ip(addr))
+ elif lsa_type == LSA_TYPE_OPAQUE_AREA:
+ e = "area {}".format(ip(addr))
+ else:
+ e = ""
+ logging.info(
+ "RECV: %s ready notify for %s opaque-type %s%s",
+ self,
+ lsa_typename(lsa_type),
+ otype,
+ e,
+ )
+
+ # Signal all waiting senders they can send now.
+ async with self.ready_lock:
+ cond = self.ready_cond[lsa_type].get(otype)
+ self.ready_cond[lsa_type][otype] = True
+
+ if cond is True:
+ logging.warning(
+ "RECV: dup ready received for %s opaque-type %s",
+ lsa_typename(lsa_type),
+ otype,
+ )
+ elif cond:
+ for evt in cond:
+ evt.set()
+
+ async def _if_msg(self, mt, msg, extra, *args):
+ if mt == MSG_NEW_IF:
+ ifaddr, aid = args
+ else:
+ assert mt == MSG_DEL_IF
+ ifaddr, aid = args[0], 0
+ logging.info(
+ "RECV: %s ifaddr %s areaid %s", api_msgname(mt), ip(ifaddr), ip(aid)
+ )
+
+ async def _if_change_msg(self, mt, msg, extra, ifaddr, aid, state):
+ ifaddr = ip(ifaddr)
+ aid = ip(aid)
+
+ logging.info(
+ "RECV: %s ifaddr %s areaid %s state %s",
+ api_msgname(mt),
+ ifaddr,
+ aid,
+ ism_name(state),
+ )
+
+ self.if_area[ifaddr] = aid
+ self.ism_states[ifaddr] = state
+
+ if self.ism_change_cb:
+ self.ism_change_cb(ifaddr, aid, state)
+
+ async def _nbr_change_msg(self, mt, msg, extra, ifaddr, nbraddr, router_id, state):
+ ifaddr = ip(ifaddr)
+ nbraddr = ip(nbraddr)
+ router_id = ip(router_id)
+
+ logging.info(
+ "RECV: %s ifaddr %s nbraddr %s router_id %s state %s",
+ api_msgname(mt),
+ ifaddr,
+ nbraddr,
+ router_id,
+ nsm_name(state),
+ )
+
+ if ifaddr not in self.nsm_states:
+ self.nsm_states[ifaddr] = {}
+ self.nsm_states[ifaddr][(nbraddr, router_id)] = state
+
+ if self.nsm_change_cb:
+ self.nsm_change_cb(ifaddr, nbraddr, router_id, state)
+
+ async def _lsa_change_msg(self, mt, msg, extra, ifaddr, aid, is_self, *ls_header):
+ (
+ lsa_age, # ls_age,
+ _, # ls_options,
+ lsa_type,
+ ls_id,
+ _, # ls_adv_router,
+ ls_seq,
+ _, # ls_cksum,
+ ls_len,
+ ) = ls_header
+
+ otype = (ls_id >> 24) & 0xFF
+
+ if mt == MSG_LSA_UPDATE_NOTIFY:
+ ts = "update"
+ else:
+ assert mt == MSG_LSA_DELETE_NOTIFY
+ ts = "delete"
+
+ logging.info(
+ "RECV: LSA %s msg for LSA %s in area %s seq 0x%x len %s age %s",
+ ts,
+ ip(ls_id),
+ ip(aid),
+ ls_seq,
+ ls_len,
+ lsa_age,
+ )
+ idx = (lsa_type, otype)
+
+ pre_lsa_size = msg_size[mt] - FMT_LSA_HEADER_SIZE
+ lsa = msg[pre_lsa_size:]
+
+ if idx in self.opaque_change_cb:
+ self.opaque_change_cb[idx](mt, ifaddr, aid, ls_header, extra, lsa)
+
+ if self.lsa_change_cb:
+ self.lsa_change_cb(mt, ifaddr, aid, ls_header, extra, lsa)
+
+ async def _reachable_msg(self, mt, msg, extra, nadd, nremove):
+ router_ids = struct.unpack(">{}I".format(nadd + nremove), extra)
+ router_ids = [ip(x) for x in router_ids]
+ logging.info(
+ "RECV: %s added %s removed %s",
+ api_msgname(mt),
+ router_ids[:nadd],
+ router_ids[nadd:],
+ )
+ self.reachable_routers |= set(router_ids[:nadd])
+ self.reachable_routers -= set(router_ids[nadd:])
+ logging.info("RECV: %s new set %s", api_msgname(mt), self.reachable_routers)
+
+ if self.reachable_change_cb:
+ logging.info("RECV: %s calling callback", api_msgname(mt))
+ await self.reachable_change_cb(router_ids[:nadd], router_ids[nadd:])
+
+ async def add_opaque_data(self, addr, lsa_type, otype, oid, data):
+ """Add an instance of opaque data.
+
+ Add an instance of opaque data. This call will register for the given
+ LSA and opaque type if not already done.
+
+ Args:
+ addr: depends on lsa_type, LINK => ifaddr, AREA => area ID, AS => ignored
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type
+ oid: (3 octets) ID of this opaque data
+ data: the opaque data
+ Raises:
+ See `msg_send_raises`
+ """
+
+ if lsa_type == LSA_TYPE_OPAQUE_LINK:
+ ifaddr, aid = int(addr), 0
+ elif lsa_type == LSA_TYPE_OPAQUE_AREA:
+ ifaddr, aid = 0, int(addr)
+ else:
+ assert lsa_type == LSA_TYPE_OPAQUE_AS
+ ifaddr, aid = 0, 0
+
+ mt = MSG_ORIGINATE_REQUEST
+ msg = struct.pack(
+ msg_fmt[mt],
+ ifaddr,
+ aid,
+ *OspfOpaqueClient._opaque_args(lsa_type, otype, oid, data),
+ )
+ msg += data
+ await self._assure_opaque_ready(lsa_type, otype)
+ await self.msg_send_raises(mt, msg)
+
+ async def delete_opaque_data(self, addr, lsa_type, otype, oid):
+ """Delete an instance of opaque data.
+
+ Delete an instance of opaque data. This call will register for the given
+ LSA and opaque type if not already done.
+
+ Args:
+ addr: depends on lsa_type, LINK => ifaddr, AREA => area ID, AS => ignored
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type. Note: the type will be registered if the user
+ has not explicity done that yet with `register_opaque_data`.
+ oid: (3 octets) ID of this opaque data
+ Raises:
+ See `msg_send_raises`
+ """
+ if (lsa_type, otype) in self.opaque_change_cb:
+ del self.opaque_change_cb[(lsa_type, otype)]
+
+ mt = MSG_DELETE_REQUEST
+ await self._assure_opaque_ready(lsa_type, otype)
+ mp = struct.pack(msg_fmt[mt], int(addr), lsa_type, otype, oid)
+ await self.msg_send_raises(mt, mp)
+
+ async def register_opaque_data(self, lsa_type, otype, callback=None):
+ """Register intent to advertise opaque data.
+
+ The application should wait for the async notificaiton that the server is
+ ready to advertise the given opaque data type. The API currently only allows
+ a single "owner" of each unique (lsa_type,otype). To wait call `wait_opaque_ready`
+
+ Args:
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type. Note: the type will be registered if the user
+ has not explicity done that yet with `register_opaque_data`.
+ callback: if given, callback will be called when changes are received for
+ LSA of the given (lsa_type, otype). The callbacks signature is:
+
+ `callback(msg_type, ifaddr, area_id, lsa_header, data, lsa)`
+
+ Args:
+ msg_type: MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY
+ ifaddr: integer identifying an interface (by IP address)
+ area_id: integer identifying an area
+ lsa_header: the LSA header as an unpacked tuple (fmt: ">HBBIILHH")
+ data: the opaque data that follows the LSA header
+ lsa: the octets of the full lsa
+ Raises:
+ See `msg_send_raises`
+ """
+ if callback:
+ self.opaque_change_cb[(lsa_type, otype)] = callback
+ elif (lsa_type, otype) in self.opaque_change_cb:
+ logging.warning(
+ "OSPFCLIENT: register: removing callback for %s opaque-type %s",
+ lsa_typename(lsa_type),
+ otype,
+ )
+ del self.opaque_change_cb[(lsa_type, otype)]
+
+ await self._register_opaque_data(lsa_type, otype)
+
+ async def wait_opaque_ready(self, lsa_type, otype):
+ async with self.ready_lock:
+ cond = self.ready_cond[lsa_type].get(otype)
+ if cond is True:
+ return
+
+ logging.debug(
+ "waiting for ready %s opaque-type %s", lsa_typename(lsa_type), otype
+ )
+
+ if not cond:
+ cond = self.ready_cond[lsa_type][otype] = []
+
+ evt = Event()
+ cond.append(evt)
+
+ await evt.wait()
+ logging.debug("READY for %s opaque-type %s", lsa_typename(lsa_type), otype)
+
+ async def register_opaque_data_wait(self, lsa_type, otype, callback=None):
+ """Register intent to advertise opaque data and wait for ready.
+
+ The API currently only allows a single "owner" of each unique (lsa_type,otype).
+
+ Args:
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type. Note: the type will be registered if the user
+ has not explicity done that yet with `register_opaque_data`.
+ callback: if given, callback will be called when changes are received for
+ LSA of the given (lsa_type, otype). The callbacks signature is:
+
+ `callback(msg_type, ifaddr, area_id, lsa_header, data, lsa)`
+
+ Args:
+ msg_type: MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY
+ ifaddr: integer identifying an interface (by IP address)
+ area_id: integer identifying an area
+ lsa_header: the LSA header as an unpacked tuple (fmt: ">HBBIILHH")
+ data: the opaque data that follows the LSA header
+ lsa: the octets of the full lsa
+ Raises:
+
+ See `msg_send_raises`
+ """
+ if callback:
+ self.opaque_change_cb[(lsa_type, otype)] = callback
+ elif (lsa_type, otype) in self.opaque_change_cb:
+ logging.warning(
+ "OSPFCLIENT: register: removing callback for %s opaque-type %s",
+ lsa_typename(lsa_type),
+ otype,
+ )
+ del self.opaque_change_cb[(lsa_type, otype)]
+
+ return await self._assure_opaque_ready(lsa_type, otype)
+
+ async def unregister_opaque_data(self, lsa_type, otype):
+ """Unregister intent to advertise opaque data.
+
+ This will also cause the server to flush/delete all opaque data of
+ the given (lsa_type,otype).
+
+ Args:
+ lsa_type: LSA_TYPE_OPAQUE_{LINK,AREA,AS}
+ otype: (octet) opaque type. Note: the type will be registered if the user
+ has not explicity done that yet with `register_opaque_data`.
+ Raises:
+ See `msg_send_raises`
+ """
+
+ if (lsa_type, otype) in self.opaque_change_cb:
+ del self.opaque_change_cb[(lsa_type, otype)]
+
+ mt = MSG_UNREGISTER_OPAQUETYPE
+ mp = struct.pack(msg_fmt[mt], lsa_type, otype)
+ await self.msg_send_raises(mt, mp)
+
+ async def monitor_lsa(self, callback=None):
+ """Monitor changes to LSAs.
+
+ Args:
+ callback: if given, callback will be called when changes are received for
+ any LSA. The callback signature is:
+
+ `callback(msg_type, ifaddr, area_id, lsa_header, extra, lsa)`
+
+ Args:
+ msg_type: MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY
+ ifaddr: integer identifying an interface (by IP address)
+ area_id: integer identifying an area
+ lsa_header: the LSA header as an unpacked tuple (fmt: ">HBBIILHH")
+ extra: the octets that follow the LSA header
+ lsa: the octets of the full lsa
+ """
+ self.lsa_change_cb = callback
+ await self.req_lsdb_sync()
+
+ async def monitor_reachable(self, callback=None):
+ """Monitor the set of reachable routers.
+
+ The property `reachable_routers` contains the set() of reachable router IDs
+ as integers. This set is updated prior to calling the `callback`
+
+ Args:
+ callback: callback will be called when the set of reachable
+ routers changes. The callback signature is:
+
+ `callback(added, removed)`
+
+ Args:
+ added: list of integer router IDs being added
+ removed: list of integer router IDs being removed
+ """
+ self.reachable_change_cb = callback
+ await self.req_reachable_routers()
+
+ async def monitor_ism(self, callback=None):
+ """Monitor the state of OSPF enabled interfaces.
+
+ Args:
+ callback: callback will be called when an interface changes state.
+ The callback signature is:
+
+ `callback(ifaddr, area_id, state)`
+
+ Args:
+ ifaddr: integer identifying an interface (by IP address)
+ area_id: integer identifying an area
+ state: ISM_*
+ """
+ self.ism_change_cb = callback
+ await self.req_ism_states()
+
+ async def monitor_nsm(self, callback=None):
+ """Monitor the state of OSPF neighbors.
+
+ Args:
+ callback: callback will be called when a neighbor changes state.
+ The callback signature is:
+
+ `callback(ifaddr, nbr_addr, router_id, state)`
+
+ Args:
+ ifaddr: integer identifying an interface (by IP address)
+ nbr_addr: integer identifying neighbor by IP address
+ router_id: integer identifying neighbor router ID
+ state: NSM_*
+ """
+ self.nsm_change_cb = callback
+ await self.req_nsm_states()
+
+
+# ================
+# CLI/Script Usage
+# ================
+
+
+async def async_main(args):
+ c = OspfOpaqueClient(args.server)
+ await c.connect()
+
+ try:
+ # Start handling async messages from server.
+ if sys.version_info[1] > 6:
+ asyncio.create_task(c._handle_msg_loop())
+ else:
+ asyncio.get_event_loop().create_task(c._handle_msg_loop())
+
+ await c.req_lsdb_sync()
+ await c.req_reachable_routers()
+ await c.req_ism_states()
+ await c.req_nsm_states()
+
+ if args.actions:
+ for action in args.actions:
+ _s = action.split(",")
+ what = _s.pop(False)
+ ltype = int(_s.pop(False))
+ if ltype == 11:
+ addr = ip(0)
+ else:
+ aval = _s.pop(False)
+ try:
+ addr = ip(int(aval))
+ except ValueError:
+ addr = ip(aval)
+ oargs = [addr, ltype, int(_s.pop(False)), int(_s.pop(False))]
+ assert len(_s) <= 1, "Bad format for action argument"
+ try:
+ b = bytes.fromhex(_s.pop(False))
+ except IndexError:
+ b = b""
+ logging.info("opaque data is %s octets", len(b))
+ # Needs to be multiple of 4 in length
+ mod = len(b) % 4
+ if mod:
+ b += b"\x00" * (4 - mod)
+ logging.info("opaque padding to %s octets", len(b))
+
+ if what.casefold() == "add":
+ await c.add_opaque_data(*oargs, b)
+ else:
+ assert what.casefold().startswith("del")
+ await c.delete_opaque_data(*oargs)
+ if args.exit:
+ return 0
+ except Exception as error:
+ logging.error("async_main: unexpected error: %s", error, exc_info=True)
+ return 2
+
+ try:
+ logging.info("Sleeping forever")
+ while True:
+ await asyncio.sleep(120)
+ except EOFError:
+ logging.info("Got EOF from OSPF API server on async notify socket")
+ return 2
+
+
+def main(*args):
+ ap = argparse.ArgumentParser(args)
+ ap.add_argument("--exit", action="store_true", help="Exit after commands")
+ ap.add_argument("--server", default="localhost", help="OSPF API server")
+ ap.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+ ap.add_argument(
+ "actions", nargs="*", help="(ADD|DEL),LSATYPE,[ADDR,],OTYPE,OID,[HEXDATA]"
+ )
+ args = ap.parse_args()
+
+ level = logging.DEBUG if args.verbose else logging.INFO
+ logging.basicConfig(
+ level=level, format="%(asctime)s %(levelname)s: CLIENT: %(name)s %(message)s"
+ )
+
+ logging.info("ospfclient: starting")
+
+ status = 3
+ try:
+ if sys.version_info[1] > 6:
+ # python >= 3.7
+ status = asyncio.run(async_main(args))
+ else:
+ loop = asyncio.get_event_loop()
+ try:
+ status = loop.run_until_complete(async_main(args))
+ finally:
+ loop.close()
+ except KeyboardInterrupt:
+ logging.info("Exiting, received KeyboardInterrupt in main")
+ except Exception as error:
+ logging.info("Exiting, unexpected exception %s", error, exc_info=True)
+ else:
+ logging.info("ospfclient: clean exit")
+
+ return status
+
+
+if __name__ == "__main__":
+ exit_status = main()
+ sys.exit(exit_status)
diff --git a/ospfclient/subdir.am b/ospfclient/subdir.am
index 1f9547ab87..b8c82c0bcf 100644
--- a/ospfclient/subdir.am
+++ b/ospfclient/subdir.am
@@ -6,6 +6,10 @@ if OSPFCLIENT
lib_LTLIBRARIES += ospfclient/libfrrospfapiclient.la
noinst_PROGRAMS += ospfclient/ospfclient
#man8 += $(MANBUILD)/frr-ospfclient.8
+
+sbin_SCRIPTS += \
+ ospfclient/ospfclient.py \
+ # end
endif
ospfclient_libfrrospfapiclient_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
@@ -41,3 +45,7 @@ endif
ospfclient_ospfclient_SOURCES = \
ospfclient/ospfclient.c \
# end
+
+EXTRA_DIST += \
+ ospfclient/ospfclient.py \
+ # end
diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c
index 81de882754..99bc6c0b03 100644
--- a/ospfd/ospf_api.c
+++ b/ospfd/ospf_api.c
@@ -177,6 +177,10 @@ const char *ospf_api_typename(int msgtype)
{
MSG_NSM_CHANGE, "NSM change",
},
+ {
+ MSG_REACHABLE_CHANGE,
+ "Reachable change",
+ },
};
int i, n = array_size(NameTab);
@@ -651,4 +655,31 @@ struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum,
return msg_new(msgtype, nmsg, seqnum, len);
}
+struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd,
+ struct in_addr *add, uint16_t nremove,
+ struct in_addr *remove)
+{
+ uint8_t buf[OSPF_API_MAX_MSG_SIZE];
+ struct msg_reachable_change *nmsg = (void *)buf;
+ const uint insz = sizeof(*nmsg->router_ids);
+ const uint nmax = (sizeof(buf) - sizeof(*nmsg)) / insz;
+ uint len;
+
+ if (nadd > nmax)
+ nadd = nmax;
+ if (nremove > (nmax - nadd))
+ nremove = (nmax - nadd);
+
+ if (nadd)
+ memcpy(nmsg->router_ids, add, nadd * insz);
+ if (nremove)
+ memcpy(&nmsg->router_ids[nadd], remove, nremove * insz);
+
+ nmsg->nadd = htons(nadd);
+ nmsg->nremove = htons(nremove);
+ len = sizeof(*nmsg) + insz * (nadd + nremove);
+
+ return msg_new(MSG_REACHABLE_CHANGE, nmsg, seqnum, len);
+}
+
#endif /* SUPPORT_OSPF_API */
diff --git a/ospfd/ospf_api.h b/ospfd/ospf_api.h
index c20284aed5..50b0c21c77 100644
--- a/ospfd/ospf_api.h
+++ b/ospfd/ospf_api.h
@@ -26,6 +26,9 @@
#ifndef _OSPF_API_H
#define _OSPF_API_H
+#include <zebra.h>
+#include "ospf_lsa.h"
+
#define OSPF_API_VERSION 1
/* MTYPE definition is not reflected to "memory.h". */
@@ -112,6 +115,9 @@ extern void msg_fifo_free(struct msg_fifo *fifo);
#define MSG_SYNC_LSDB 4
#define MSG_ORIGINATE_REQUEST 5
#define MSG_DELETE_REQUEST 6
+#define MSG_SYNC_REACHABLE 7
+#define MSG_SYNC_ISM 8
+#define MSG_SYNC_NSM 9
/* Messages from OSPF daemon. */
#define MSG_REPLY 10
@@ -122,6 +128,7 @@ extern void msg_fifo_free(struct msg_fifo *fifo);
#define MSG_DEL_IF 15
#define MSG_ISM_CHANGE 16
#define MSG_NSM_CHANGE 17
+#define MSG_REACHABLE_CHANGE 18
struct msg_register_opaque_type {
uint8_t lsatype;
@@ -247,6 +254,12 @@ struct msg_nsm_change {
uint8_t pad[3];
};
+struct msg_reachable_change {
+ uint16_t nadd;
+ uint16_t nremove;
+ struct in_addr router_ids[]; /* add followed by remove */
+};
+
/* We make use of a union to define a structure that covers all
possible API messages. This allows us to find out how much memory
needs to be reserved for the largest API message. */
@@ -265,6 +278,7 @@ struct apimsg {
struct msg_ism_change ism_change;
struct msg_nsm_change nsm_change;
struct msg_lsa_change_notify lsa_change_notify;
+ struct msg_reachable_change reachable_change;
} u;
};
@@ -320,6 +334,10 @@ extern struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum,
uint8_t is_self_originated,
struct lsa_header *data);
+extern struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd,
+ struct in_addr *add,
+ uint16_t nremove,
+ struct in_addr *remove);
/* string printing functions */
extern const char *ospf_api_errname(int errcode);
extern const char *ospf_api_typename(int msgtype);
diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c
index 5229c6f44c..7c3fef4536 100644
--- a/ospfd/ospf_apiserver.c
+++ b/ospfd/ospf_apiserver.c
@@ -726,6 +726,7 @@ static int ospf_apiserver_send_msg(struct ospf_apiserver *apiserv,
case MSG_DEL_IF:
case MSG_ISM_CHANGE:
case MSG_NSM_CHANGE:
+ case MSG_REACHABLE_CHANGE:
fifo = apiserv->out_async_fifo;
fd = apiserv->fd_async;
event = OSPF_APISERVER_ASYNC_WRITE;
@@ -799,6 +800,15 @@ int ospf_apiserver_handle_msg(struct ospf_apiserver *apiserv, struct msg *msg)
case MSG_DELETE_REQUEST:
rc = ospf_apiserver_handle_delete_request(apiserv, msg);
break;
+ case MSG_SYNC_REACHABLE:
+ rc = ospf_apiserver_handle_sync_reachable(apiserv, msg);
+ break;
+ case MSG_SYNC_ISM:
+ rc = ospf_apiserver_handle_sync_ism(apiserv, msg);
+ break;
+ case MSG_SYNC_NSM:
+ rc = ospf_apiserver_handle_sync_nsm(apiserv, msg);
+ break;
default:
zlog_warn("ospf_apiserver_handle_msg: Unknown message type: %d",
msg->hdr.msgtype);
@@ -1343,6 +1353,131 @@ int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv,
return rc;
}
+/*
+ * -----------------------------------------------------------
+ * Followings are functions for synchronization.
+ * -----------------------------------------------------------
+ */
+
+int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv,
+ struct msg *msg)
+{
+ struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ struct route_table *rt = ospf->all_rtrs;
+ uint32_t seqnum = msg_get_seq(msg);
+ struct in_addr *a, *abuf;
+ struct msg_reachable_change *areach;
+ struct msg *amsg;
+ uint mcount, count;
+ int _rc, rc = 0;
+
+ if (!rt)
+ goto out;
+
+ /* send all adds based on current reachable routers */
+ a = abuf = XCALLOC(MTYPE_OSPF_APISERVER,
+ sizeof(struct in_addr) * rt->count);
+ for (struct route_node *rn = route_top(rt); rn; rn = route_next(rn))
+ if (listhead((struct list *)rn->info))
+ *a++ = rn->p.u.prefix4;
+
+ assert((a - abuf) <= (long)rt->count);
+ count = (a - abuf);
+
+ a = abuf;
+ while (count && !rc) {
+ amsg = new_msg_reachable_change(seqnum, count, a, 0, NULL);
+ areach = (struct msg_reachable_change *)STREAM_DATA(amsg->s);
+ mcount = ntohs(areach->nadd) + ntohs(areach->nremove);
+ assert(mcount <= count);
+ a = a + mcount;
+ count -= mcount;
+ rc = ospf_apiserver_send_msg(apiserv, amsg);
+ msg_free(amsg);
+ }
+ XFREE(MTYPE_OSPF_APISERVER, abuf);
+
+out:
+ /* Send a reply back to client with return code */
+ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
+ rc = rc ? rc : _rc;
+ apiserv->reachable_sync = !rc;
+ return rc;
+}
+
+int ospf_apiserver_handle_sync_ism(struct ospf_apiserver *apiserv,
+ struct msg *msg)
+{
+ struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ struct listnode *anode, *inode;
+ struct ospf_area *area;
+ struct ospf_interface *oi;
+ struct msg *m;
+ uint32_t seqnum = msg_get_seq(msg);
+ int _rc, rc = 0;
+
+ /* walk all areas */
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) {
+ /* walk all interfaces */
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) {
+ m = new_msg_ism_change(seqnum, oi->address->u.prefix4,
+ area->area_id, oi->state);
+ rc = ospf_apiserver_send_msg(apiserv, m);
+ msg_free(m);
+ if (rc)
+ break;
+ }
+ if (rc)
+ break;
+ }
+ /* Send a reply back to client with return code */
+ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
+ return rc ? rc : _rc;
+}
+
+
+int ospf_apiserver_handle_sync_nsm(struct ospf_apiserver *apiserv,
+ struct msg *msg)
+{
+ struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ struct listnode *anode, *inode;
+ struct ospf_area *area;
+ struct ospf_interface *oi;
+ struct ospf_neighbor *nbr;
+ struct route_node *rn;
+ struct msg *m;
+ uint32_t seqnum = msg_get_seq(msg);
+ int _rc, rc = 0;
+
+ /* walk all areas */
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) {
+ /* walk all interfaces */
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) {
+ /* walk all neighbors */
+ for (rn = route_top(oi->nbrs); rn;
+ rn = route_next(rn)) {
+ nbr = rn->info;
+ if (!nbr)
+ continue;
+ m = new_msg_nsm_change(
+ seqnum, oi->address->u.prefix4,
+ nbr->src, nbr->router_id, nbr->state);
+ rc = ospf_apiserver_send_msg(apiserv, m);
+ msg_free(m);
+ if (rc)
+ break;
+ }
+ if (rc)
+ break;
+ }
+ if (rc)
+ break;
+ }
+ /* Send a reply back to client with return code */
+ _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc);
+ return rc ? rc : _rc;
+}
+
/* -----------------------------------------------------------
* Following are functions to originate or update LSA
@@ -1427,45 +1562,20 @@ struct ospf_lsa *ospf_apiserver_opaque_lsa_new(struct ospf_area *area,
int ospf_apiserver_is_ready_type9(struct ospf_interface *oi)
{
- /* Type 9 opaque LSA can be originated if there is at least one
- active opaque-capable neighbor attached to the outgoing
- interface. */
-
- return (ospf_nbr_count_opaque_capable(oi) > 0);
+ /* We can always handle getting opaque's even if we can't flood them */
+ return 1;
}
int ospf_apiserver_is_ready_type10(struct ospf_area *area)
{
- /* Type 10 opaque LSA can be originated if there is at least one
- interface belonging to the area that has an active opaque-capable
- neighbor. */
- struct listnode *node, *nnode;
- struct ospf_interface *oi;
-
- for (ALL_LIST_ELEMENTS(area->oiflist, node, nnode, oi))
- /* Is there an active neighbor attached to this interface? */
- if (ospf_apiserver_is_ready_type9(oi))
- return 1;
-
- /* No active neighbor in area */
- return 0;
+ /* We can always handle getting opaque's even if we can't flood them */
+ return 1;
}
int ospf_apiserver_is_ready_type11(struct ospf *ospf)
{
- /* Type 11 opaque LSA can be originated if there is at least one
- interface
- that has an active opaque-capable neighbor. */
- struct listnode *node, *nnode;
- struct ospf_interface *oi;
-
- for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi))
- /* Is there an active neighbor attached to this interface? */
- if (ospf_apiserver_is_ready_type9(oi))
- return 1;
-
- /* No active neighbor at all */
- return 0;
+ /* We can always handle getting opaque's even if we can't flood them */
+ return 1;
}
@@ -1570,9 +1680,9 @@ int ospf_apiserver_handle_originate_request(struct ospf_apiserver *apiserv,
/* Determine if LSA is new or an update for an existing one. */
old = ospf_lsdb_lookup(lsdb, new);
- if (!old) {
+ if (!old || !ospf_opaque_is_owned(old)) {
/* New LSA install in LSDB. */
- rc = ospf_apiserver_originate1(new);
+ rc = ospf_apiserver_originate1(new, old);
} else {
/*
* Keep the new LSA instance in the "waiting place" until the
@@ -1639,17 +1749,33 @@ void ospf_apiserver_flood_opaque_lsa(struct ospf_lsa *lsa)
}
}
-int ospf_apiserver_originate1(struct ospf_lsa *lsa)
+int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old)
{
struct ospf *ospf;
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
assert(ospf);
+ if (old) {
+ /*
+ * An old LSA exists that we didn't originate it in this
+ * session. Dump it, but increment past it's seqnum.
+ */
+ assert(!ospf_opaque_is_owned(old));
+ if (IS_LSA_MAX_SEQ(old)) {
+ flog_warn(
+ EC_OSPF_LSA_INSTALL_FAILURE,
+ "ospf_apiserver_originate1: old LSA at maxseq");
+ return -1;
+ }
+ lsa->data->ls_seqnum = lsa_seqnum_increment(old);
+ ospf_discard_from_db(ospf, old->lsdb, old);
+ }
+
/* Install this LSA into LSDB. */
if (ospf_lsa_install(ospf, lsa->oi, lsa) == NULL) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
- "ospf_apiserver_originate1: ospf_lsa_install failed");
+ "%s: ospf_lsa_install failed", __func__);
return -1;
}
@@ -2015,6 +2141,7 @@ int ospf_apiserver_del_if(struct interface *ifp)
if (!oi) {
/* This interface is known to Zebra but not to OSPF daemon
anymore. No need to tell clients about it */
+ zlog_warn("ifp name=%s not known to OSPFd", ifp->name);
return 0;
}
@@ -2061,9 +2188,6 @@ void ospf_apiserver_show_info(struct vty *vty, struct json_object *json,
struct opaque_lsa *olsa;
int opaquelen;
- if (json)
- return;
-
olsa = (struct opaque_lsa *)lsa->data;
if (VALID_OPAQUE_INFO_LEN(lsa->data))
@@ -2072,7 +2196,10 @@ void ospf_apiserver_show_info(struct vty *vty, struct json_object *json,
opaquelen = 0;
/* Output information about opaque LSAs */
- if (vty != NULL) {
+ if (json)
+ json_object_string_addf(json, "opaqueData", "%*pHXn",
+ (int)opaquelen, olsa->data);
+ else if (vty != NULL) {
int i;
vty_out(vty,
" Added using OSPF API: %u octets of opaque data %s\n",
@@ -2333,8 +2460,8 @@ void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr)
msg_free(msg);
}
-static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
- struct ospf_lsa *lsa)
+static int apiserver_clients_lsa_change_notify(uint8_t msgtype,
+ struct ospf_lsa *lsa)
{
struct msg *msg;
struct listnode *node, *nnode;
@@ -2362,7 +2489,7 @@ static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
if (!msg) {
zlog_warn(
"apiserver_clients_lsa_change_notify: msg_new failed");
- return;
+ return -1;
}
/* Now send message to all clients with a matching filter */
@@ -2413,6 +2540,8 @@ static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
}
/* Free message since it is not used anymore */
msg_free(msg);
+
+ return 0;
}
@@ -2422,53 +2551,132 @@ static void apiserver_clients_lsa_change_notify(uint8_t msgtype,
*/
-static int apiserver_notify_clients_lsa(uint8_t msgtype, struct ospf_lsa *lsa)
+int ospf_apiserver_lsa_update(struct ospf_lsa *lsa)
{
- struct msg *msg;
- /* default area for AS-External and Opaque11 LSAs */
- struct in_addr area_id = {.s_addr = 0L};
-
- /* default interface for non Opaque9 LSAs */
- struct in_addr ifaddr = {.s_addr = 0L};
/* Only notify this update if the LSA's age is smaller than
MAXAGE. Otherwise clients would see LSA updates with max age just
before they are deleted from the LSDB. LSA delete messages have
MAXAGE too but should not be filtered. */
- if (IS_LSA_MAXAGE(lsa) && (msgtype == MSG_LSA_UPDATE_NOTIFY)) {
+ if (IS_LSA_MAXAGE(lsa))
return 0;
- }
-
- if (lsa->area) {
- area_id = lsa->area->area_id;
- }
- if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) {
- ifaddr = lsa->oi->address->u.prefix4;
- }
- msg = new_msg_lsa_change_notify(msgtype, 0L, /* no sequence number */
- ifaddr, area_id,
- lsa->flags & OSPF_LSA_SELF, lsa->data);
- if (!msg) {
- zlog_warn("notify_clients_lsa: msg_new failed");
- return -1;
- }
- /* Notify all clients that new LSA is added/updated */
- apiserver_clients_lsa_change_notify(msgtype, lsa);
-
- /* Clients made their own copies of msg so we can free msg here */
- msg_free(msg);
+ return apiserver_clients_lsa_change_notify(MSG_LSA_UPDATE_NOTIFY, lsa);
+}
- return 0;
+int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa)
+{
+ return apiserver_clients_lsa_change_notify(MSG_LSA_DELETE_NOTIFY, lsa);
}
-int ospf_apiserver_lsa_update(struct ospf_lsa *lsa)
+/* -------------------------------------------------------------
+ * Reachable functions
+ * -------------------------------------------------------------
+ */
+
+static inline int cmp_route_nodes(struct route_node *orn,
+ struct route_node *nrn)
{
- return apiserver_notify_clients_lsa(MSG_LSA_UPDATE_NOTIFY, lsa);
+ if (!orn)
+ return 1;
+ else if (!nrn)
+ return -1;
+ else if (orn->p.u.prefix4.s_addr < nrn->p.u.prefix4.s_addr)
+ return -1;
+ else if (orn->p.u.prefix4.s_addr > nrn->p.u.prefix4.s_addr)
+ return 1;
+ else
+ return 0;
}
-int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa)
+void ospf_apiserver_notify_reachable(struct route_table *ort,
+ struct route_table *nrt)
{
- return apiserver_notify_clients_lsa(MSG_LSA_DELETE_NOTIFY, lsa);
+ struct msg *msg;
+ struct msg_reachable_change *areach;
+ struct route_node *orn, *nrn;
+ const uint insz = sizeof(struct in_addr);
+ struct in_addr *abuf = NULL, *dbuf = NULL;
+ struct in_addr *a = NULL, *d = NULL;
+ uint nadd, nremove;
+ int cmp;
+
+ if (!ort && !nrt) {
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("%s: no routing tables", __func__);
+ return;
+ }
+ if (nrt && nrt->count)
+ a = abuf = XCALLOC(MTYPE_OSPF_APISERVER, insz * nrt->count);
+ if (ort && ort->count)
+ d = dbuf = XCALLOC(MTYPE_OSPF_APISERVER, insz * ort->count);
+
+ /* walk both tables */
+ orn = ort ? route_top(ort) : NULL;
+ nrn = nrt ? route_top(nrt) : NULL;
+ while (orn || nrn) {
+ if (orn && !listhead((struct list *)orn->info)) {
+ orn = route_next(orn);
+ continue;
+ }
+ if (nrn && !listhead((struct list *)nrn->info)) {
+ nrn = route_next(nrn);
+ continue;
+ }
+ cmp = cmp_route_nodes(orn, nrn);
+ if (!cmp) {
+ /* if old == new advance old and new */
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("keeping router id: %pI4",
+ &orn->p.u.prefix4);
+ orn = route_next(orn);
+ nrn = route_next(nrn);
+ } else if (cmp < 0) {
+ assert(d != NULL); /* Silence SA warning */
+
+ /* if old < new, delete old, advance old */
+ *d++ = orn->p.u.prefix4;
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("removing router id: %pI4",
+ &orn->p.u.prefix4);
+ orn = route_next(orn);
+ } else {
+ assert(a != NULL); /* Silence SA warning */
+
+ /* if new < old, add new, advance new */
+ *a++ = nrn->p.u.prefix4;
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("adding router id: %pI4",
+ &nrn->p.u.prefix4);
+ nrn = route_next(nrn);
+ }
+ }
+
+ nadd = abuf ? (a - abuf) : 0;
+ nremove = dbuf ? (d - dbuf) : 0;
+ a = abuf;
+ d = dbuf;
+
+ while (nadd + nremove) {
+ msg = new_msg_reachable_change(0, nadd, a, nremove, d);
+ areach = (struct msg_reachable_change *)STREAM_DATA(msg->s);
+
+ a += ntohs(areach->nadd);
+ nadd = nadd - ntohs(areach->nadd);
+
+ d += ntohs(areach->nremove);
+ nremove = nremove - ntohs(areach->nremove);
+
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("%s: adding %d removing %d", __func__,
+ ntohs(areach->nadd), ntohs(areach->nremove));
+ ospf_apiserver_clients_notify_all(msg);
+ msg_free(msg);
+ }
+ if (abuf)
+ XFREE(MTYPE_OSPF_APISERVER, abuf);
+ if (dbuf)
+ XFREE(MTYPE_OSPF_APISERVER, dbuf);
}
+
#endif /* SUPPORT_OSPF_API */
diff --git a/ospfd/ospf_apiserver.h b/ospfd/ospf_apiserver.h
index b4d8bb2f52..7d728ead93 100644
--- a/ospfd/ospf_apiserver.h
+++ b/ospfd/ospf_apiserver.h
@@ -22,6 +22,10 @@
#ifndef _OSPF_APISERVER_H
#define _OSPF_APISERVER_H
+#include <zebra.h>
+#include "ospf_api.h"
+#include "ospf_lsdb.h"
+
/* MTYPE definition is not reflected to "memory.h". */
#define MTYPE_OSPF_APISERVER MTYPE_TMP
#define MTYPE_OSPF_APISERVER_MSGFILTER MTYPE_TMP
@@ -52,6 +56,9 @@ struct ospf_apiserver {
/* Temporary storage for LSA instances to be refreshed. */
struct ospf_lsdb reserve;
+ /* Sync reachable routers */
+ bool reachable_sync;
+
/* filter for LSA update/delete notifies */
struct lsa_filter_type *filter;
@@ -144,7 +151,15 @@ extern int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv,
struct msg *msg);
extern int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv,
struct msg *msg);
+extern int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv,
+ struct msg *msg);
+extern int ospf_apiserver_handle_sync_ism(struct ospf_apiserver *apiserv,
+ struct msg *msg);
+extern int ospf_apiserver_handle_sync_nsm(struct ospf_apiserver *apiserv,
+ struct msg *msg);
+extern void ospf_apiserver_notify_reachable(struct route_table *ort,
+ struct route_table *nrt);
/* -----------------------------------------------------------
* Following are functions for LSA origination/deletion
@@ -164,7 +179,8 @@ extern struct ospf_interface *
ospf_apiserver_if_lookup_by_addr(struct in_addr address);
extern struct ospf_interface *
ospf_apiserver_if_lookup_by_ifp(struct interface *ifp);
-extern int ospf_apiserver_originate1(struct ospf_lsa *lsa);
+extern int ospf_apiserver_originate1(struct ospf_lsa *lsa,
+ struct ospf_lsa *old);
extern void ospf_apiserver_flood_opaque_lsa(struct ospf_lsa *lsa);
@@ -201,7 +217,4 @@ extern void ospf_apiserver_flush_opaque_lsa(struct ospf_apiserver *apiserv,
extern int ospf_apiserver_lsa_update(struct ospf_lsa *lsa);
extern int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa);
-extern void ospf_apiserver_clients_lsa_change_notify(uint8_t msgtype,
- struct ospf_lsa *lsa);
-
#endif /* _OSPF_APISERVER_H */
diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c
index f40d056742..4d76181c6a 100644
--- a/ospfd/ospf_dump.c
+++ b/ospfd/ospf_dump.c
@@ -62,6 +62,7 @@ unsigned long conf_debug_ospf_defaultinfo = 0;
unsigned long conf_debug_ospf_ldp_sync = 0;
unsigned long conf_debug_ospf_gr = 0;
unsigned long conf_debug_ospf_bfd;
+unsigned long conf_debug_ospf_client_api;
/* Enable debug option variables -- valid only session. */
unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
@@ -79,6 +80,7 @@ unsigned long term_debug_ospf_defaultinfo;
unsigned long term_debug_ospf_ldp_sync;
unsigned long term_debug_ospf_gr = 0;
unsigned long term_debug_ospf_bfd;
+unsigned long term_debug_ospf_client_api;
const char *ospf_redist_string(unsigned int route_type)
{
@@ -1620,6 +1622,33 @@ DEFPY(debug_ospf_bfd, debug_ospf_bfd_cmd,
return CMD_SUCCESS;
}
+DEFUN(debug_ospf_client_api,
+ debug_ospf_client_api_cmd,
+ "debug ospf client-api",
+ DEBUG_STR OSPF_STR
+ "OSPF client API information\n")
+{
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_ON(client_api, CLIENT_API);
+ TERM_DEBUG_ON(client_api, CLIENT_API);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_debug_ospf_client_api,
+ no_debug_ospf_client_api_cmd,
+ "no debug ospf client-api",
+ NO_STR
+ DEBUG_STR
+ OSPF_STR
+ "OSPF client API information\n")
+{
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_OFF(client_api, CLIENT_API);
+ TERM_DEBUG_OFF(client_api, CLIENT_API);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_ospf,
no_debug_ospf_cmd,
"no debug ospf",
@@ -1654,6 +1683,7 @@ DEFUN (no_debug_ospf,
DEBUG_OFF(te, TE);
DEBUG_OFF(sr, SR);
DEBUG_OFF(ti_lfa, TI_LFA);
+ DEBUG_OFF(client_api, CLIENT_API);
/* BFD debugging is two parts: OSPF and library. */
DEBUG_OFF(bfd, BFD_LIB);
@@ -1690,6 +1720,7 @@ DEFUN (no_debug_ospf,
TERM_DEBUG_OFF(sr, SR);
TERM_DEBUG_OFF(ti_lfa, TI_LFA);
TERM_DEBUG_OFF(bfd, BFD_LIB);
+ TERM_DEBUG_OFF(client_api, CLIENT_API);
return CMD_SUCCESS;
}
@@ -1815,6 +1846,10 @@ static int show_debugging_ospf_common(struct vty *vty)
vty_out(vty,
" OSPF BFD integration library debugging is on\n");
+ /* Show debug status for LDP-SYNC. */
+ if (IS_DEBUG_OSPF(client_api, CLIENT_API) == OSPF_DEBUG_CLIENT_API)
+ vty_out(vty, " OSPF client-api debugging is on\n");
+
vty_out(vty, "\n");
return CMD_SUCCESS;
@@ -2007,6 +2042,13 @@ static int config_write_debug(struct vty *vty)
write = 1;
}
+ /* debug ospf client-api */
+ if (IS_CONF_DEBUG_OSPF(client_api, CLIENT_API) ==
+ OSPF_DEBUG_CLIENT_API) {
+ vty_out(vty, "debug ospf%s client-api\n", str);
+ write = 1;
+ }
+
return write;
}
@@ -2027,6 +2069,7 @@ void ospf_debug_init(void)
install_element(ENABLE_NODE, &debug_ospf_ti_lfa_cmd);
install_element(ENABLE_NODE, &debug_ospf_default_info_cmd);
install_element(ENABLE_NODE, &debug_ospf_ldp_sync_cmd);
+ install_element(ENABLE_NODE, &debug_ospf_client_api_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd);
@@ -2038,6 +2081,7 @@ void ospf_debug_init(void)
install_element(ENABLE_NODE, &no_debug_ospf_ti_lfa_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_default_info_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_ldp_sync_cmd);
+ install_element(ENABLE_NODE, &no_debug_ospf_client_api_cmd);
install_element(ENABLE_NODE, &debug_ospf_gr_cmd);
install_element(ENABLE_NODE, &debug_ospf_bfd_cmd);
@@ -2072,6 +2116,7 @@ void ospf_debug_init(void)
install_element(CONFIG_NODE, &debug_ospf_ti_lfa_cmd);
install_element(CONFIG_NODE, &debug_ospf_default_info_cmd);
install_element(CONFIG_NODE, &debug_ospf_ldp_sync_cmd);
+ install_element(CONFIG_NODE, &debug_ospf_client_api_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd);
@@ -2082,6 +2127,7 @@ void ospf_debug_init(void)
install_element(CONFIG_NODE, &no_debug_ospf_ti_lfa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_default_info_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_ldp_sync_cmd);
+ install_element(CONFIG_NODE, &no_debug_ospf_client_api_cmd);
install_element(CONFIG_NODE, &debug_ospf_gr_cmd);
install_element(CONFIG_NODE, &debug_ospf_bfd_cmd);
diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h
index 58227d038e..251be7c8d1 100644
--- a/ospfd/ospf_dump.h
+++ b/ospfd/ospf_dump.h
@@ -68,6 +68,8 @@
#define OSPF_DEBUG_BFD_LIB 0x01
+#define OSPF_DEBUG_CLIENT_API 0x01
+
/* Macro for setting debug option. */
#define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b)
#define CONF_DEBUG_PACKET_OFF(a, b) conf_debug_ospf_packet[a] &= ~(b)
@@ -118,6 +120,7 @@
#define IS_DEBUG_OSPF_LDP_SYNC IS_DEBUG_OSPF(ldp_sync, LDP_SYNC)
#define IS_DEBUG_OSPF_GR IS_DEBUG_OSPF(gr, GR)
+#define IS_DEBUG_OSPF_CLIENT_API IS_DEBUG_OSPF(client_api, CLIENT_API)
#define IS_CONF_DEBUG_OSPF_PACKET(a, b) \
(conf_debug_ospf_packet[a] & OSPF_DEBUG_##b)
@@ -142,6 +145,7 @@ extern unsigned long term_debug_ospf_defaultinfo;
extern unsigned long term_debug_ospf_ldp_sync;
extern unsigned long term_debug_ospf_gr;
extern unsigned long term_debug_ospf_bfd;
+extern unsigned long term_debug_ospf_client_api;
/* Message Strings. */
extern char *ospf_lsa_type_str[];
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index 947454c0df..7e95cb591a 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -74,9 +74,8 @@ int ospf_apiserver_enable;
static void ospf_opaque_register_vty(void);
static void ospf_opaque_funclist_init(void);
static void ospf_opaque_funclist_term(void);
-static void free_opaque_info_per_type(void *val);
+static void free_opaque_info_per_type_del(void *val);
static void free_opaque_info_per_id(void *val);
-static void free_opaque_info_owner(void *val);
static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa);
static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa);
@@ -141,7 +140,7 @@ int ospf_opaque_type9_lsa_init(struct ospf_interface *oi)
list_delete(&oi->opaque_lsa_self);
oi->opaque_lsa_self = list_new();
- oi->opaque_lsa_self->del = free_opaque_info_per_type;
+ oi->opaque_lsa_self->del = free_opaque_info_per_type_del;
oi->t_opaque_lsa_self = NULL;
return 0;
}
@@ -161,7 +160,7 @@ int ospf_opaque_type10_lsa_init(struct ospf_area *area)
list_delete(&area->opaque_lsa_self);
area->opaque_lsa_self = list_new();
- area->opaque_lsa_self->del = free_opaque_info_per_type;
+ area->opaque_lsa_self->del = free_opaque_info_per_type_del;
area->t_opaque_lsa_self = NULL;
#ifdef MONITOR_LSDB_CHANGE
@@ -189,7 +188,7 @@ int ospf_opaque_type11_lsa_init(struct ospf *top)
list_delete(&top->opaque_lsa_self);
top->opaque_lsa_self = list_new();
- top->opaque_lsa_self->del = free_opaque_info_per_type;
+ top->opaque_lsa_self->del = free_opaque_info_per_type_del;
top->t_opaque_lsa_self = NULL;
#ifdef MONITOR_LSDB_CHANGE
@@ -263,6 +262,9 @@ static const char *ospf_opaque_type_name(uint8_t opaque_type)
struct opaque_info_per_type; /* Forward declaration. */
+static void free_opaque_info_per_type(struct opaque_info_per_type *oipt,
+ bool cleanup_owner);
+
struct ospf_opaque_functab {
uint8_t opaque_type;
struct opaque_info_per_type *oipt;
@@ -433,12 +435,9 @@ void ospf_delete_opaque_functab(uint8_t lsa_type, uint8_t opaque_type)
if (functab->opaque_type == opaque_type) {
/* Cleanup internal control information, if it
* still remains. */
- if (functab->oipt != NULL) {
- free_opaque_info_owner(functab->oipt);
- free_opaque_info_per_type(
- functab->oipt);
- }
-
+ if (functab->oipt != NULL)
+ free_opaque_info_per_type(functab->oipt,
+ true);
/* Dequeue listnode entry from the list. */
listnode_delete(funclist, functab);
@@ -558,8 +557,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
case OSPF_OPAQUE_AS_LSA:
top = ospf_lookup_by_vrf_id(new->vrf_id);
if (new->area != NULL && (top = new->area->ospf) == NULL) {
- free_opaque_info_owner(oipt);
- free_opaque_info_per_type(oipt);
+ free_opaque_info_per_type(oipt, true);
oipt = NULL;
goto out; /* This case may not exist. */
}
@@ -571,8 +569,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
EC_OSPF_LSA_UNEXPECTED,
"register_opaque_info_per_type: Unexpected LSA-type(%u)",
new->data->type);
- free_opaque_info_owner(oipt);
- free_opaque_info_per_type(oipt);
+ free_opaque_info_per_type(oipt, true);
oipt = NULL;
goto out; /* This case may not exist. */
}
@@ -589,42 +586,13 @@ out:
return oipt;
}
-/* Remove "oipt" from its owner's self-originated LSA list. */
-static void free_opaque_info_owner(void *val)
-{
- struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
-
- switch (oipt->lsa_type) {
- case OSPF_OPAQUE_LINK_LSA: {
- struct ospf_interface *oi =
- (struct ospf_interface *)(oipt->owner);
- listnode_delete(oi->opaque_lsa_self, oipt);
- break;
- }
- case OSPF_OPAQUE_AREA_LSA: {
- struct ospf_area *area = (struct ospf_area *)(oipt->owner);
- listnode_delete(area->opaque_lsa_self, oipt);
- break;
- }
- case OSPF_OPAQUE_AS_LSA: {
- struct ospf *top = (struct ospf *)(oipt->owner);
- listnode_delete(top->opaque_lsa_self, oipt);
- break;
- }
- default:
- flog_warn(EC_OSPF_LSA_UNEXPECTED,
- "free_opaque_info_owner: Unexpected LSA-type(%u)",
- oipt->lsa_type);
- break; /* This case may not exist. */
- }
-}
-
-static void free_opaque_info_per_type(void *val)
+static void free_opaque_info_per_type(struct opaque_info_per_type *oipt,
+ bool cleanup_owner)
{
- struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
struct opaque_info_per_id *oipi;
struct ospf_lsa *lsa;
struct listnode *node, *nnode;
+ struct list *l;
/* Control information per opaque-id may still exist. */
for (ALL_LIST_ELEMENTS(oipt->id_list, node, nnode, oipi)) {
@@ -637,10 +605,37 @@ static void free_opaque_info_per_type(void *val)
OSPF_TIMER_OFF(oipt->t_opaque_lsa_self);
list_delete(&oipt->id_list);
+ if (cleanup_owner) {
+ /* Remove from its owner's self-originated LSA list. */
+ switch (oipt->lsa_type) {
+ case OSPF_OPAQUE_LINK_LSA:
+ l = ((struct ospf_interface *)oipt->owner)
+ ->opaque_lsa_self;
+ break;
+ case OSPF_OPAQUE_AREA_LSA:
+ l = ((struct ospf_area *)oipt->owner)->opaque_lsa_self;
+ break;
+ case OSPF_OPAQUE_AS_LSA:
+ l = ((struct ospf *)oipt->owner)->opaque_lsa_self;
+ break;
+ default:
+ flog_warn(
+ EC_OSPF_LSA_UNEXPECTED,
+ "free_opaque_info_owner: Unexpected LSA-type(%u)",
+ oipt->lsa_type);
+ return;
+ }
+ listnode_delete(l, oipt);
+ }
XFREE(MTYPE_OPAQUE_INFO_PER_TYPE, oipt);
return;
}
+static void free_opaque_info_per_type_del(void *val)
+{
+ free_opaque_info_per_type((struct opaque_info_per_type *)val, false);
+}
+
static struct opaque_info_per_type *
lookup_opaque_info_by_type(struct ospf_lsa *lsa)
{
@@ -758,6 +753,13 @@ out:
return oipi;
}
+int ospf_opaque_is_owned(struct ospf_lsa *lsa)
+{
+ struct opaque_info_per_type *oipt = lookup_opaque_info_by_type(lsa);
+
+ return (oipt != NULL && lookup_opaque_info_by_id(oipt, lsa) != NULL);
+}
+
/*------------------------------------------------------------------------*
* Following are (vty) configuration functions for Opaque-LSAs handling.
*------------------------------------------------------------------------*/
diff --git a/ospfd/ospf_opaque.h b/ospfd/ospf_opaque.h
index 59d4288bf2..9c76877908 100644
--- a/ospfd/ospf_opaque.h
+++ b/ospfd/ospf_opaque.h
@@ -173,4 +173,6 @@ extern void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr,
struct ospf_lsa *lsa);
extern struct ospf *oi_to_top(struct ospf_interface *oi);
+extern int ospf_opaque_is_owned(struct ospf_lsa *lsa);
+
#endif /* _ZEBRA_OSPF_OPAQUE_H */
diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c
index ec0c5524c9..c5b26bbd76 100644
--- a/ospfd/ospf_route.c
+++ b/ospfd/ospf_route.c
@@ -363,7 +363,7 @@ void ospf_route_install(struct ospf *ospf, struct route_table *rt)
/* RFC2328 16.1. (4). For "router". */
void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
- struct ospf_area *area)
+ struct ospf_area *area, bool add_all)
{
struct route_node *rn;
struct ospf_route * or ;
@@ -388,7 +388,8 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
/* If the newly added vertex is an area border router or AS boundary
router, a routing table entry is added whose destination type is
"router". */
- if (!IS_ROUTER_LSA_BORDER(lsa) && !IS_ROUTER_LSA_EXTERNAL(lsa)) {
+ if (!add_all && !IS_ROUTER_LSA_BORDER(lsa) &&
+ !IS_ROUTER_LSA_EXTERNAL(lsa)) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"ospf_intra_add_router: this router is neither ASBR nor ABR, skipping it");
@@ -733,6 +734,24 @@ void ospf_route_table_dump(struct route_table *rt)
zlog_debug("========================================");
}
+void ospf_router_route_table_dump(struct route_table *rt)
+{
+ struct route_node *rn;
+ struct ospf_route *or;
+ struct listnode *node;
+
+ zlog_debug("========== OSPF routing table ==========");
+ for (rn = route_top(rt); rn; rn = route_next(rn)) {
+ for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or)) {
+ assert(or->type == OSPF_DESTINATION_ROUTER);
+ zlog_debug("R %-18pI4 %-15pI4 %s %d", &rn->p.u.prefix4,
+ &or->u.std.area_id,
+ ospf_path_type_str[or->path_type], or->cost);
+ }
+ }
+ zlog_debug("========================================");
+}
+
/* This is 16.4.1 implementation.
o Intra-area paths using non-backbone areas are always the most preferred.
o The other paths, intra-area backbone paths and inter-area paths,
diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h
index 5463e70ffb..fa9478fced 100644
--- a/ospfd/ospf_route.h
+++ b/ospfd/ospf_route.h
@@ -139,9 +139,10 @@ extern void ospf_route_table_free(struct route_table *);
extern void ospf_route_install(struct ospf *, struct route_table *);
extern void ospf_route_table_dump(struct route_table *);
+extern void ospf_router_route_table_dump(struct route_table *rt);
-extern void ospf_intra_add_router(struct route_table *, struct vertex *,
- struct ospf_area *);
+extern void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
+ struct ospf_area *area, bool add_all);
extern void ospf_intra_add_transit(struct route_table *, struct vertex *,
struct ospf_area *);
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index baf02365a2..44549b980c 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -48,6 +48,7 @@
#include "ospfd/ospf_sr.h"
#include "ospfd/ospf_ti_lfa.h"
#include "ospfd/ospf_errors.h"
+#include "ospfd/ospf_apiserver.h"
/* Variables to ensure a SPF scheduled log message is printed only once */
@@ -1669,6 +1670,7 @@ void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list)
/* Calculating the shortest-path tree for an area, see RFC2328 16.1. */
void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs, bool is_dry_run,
bool is_root_node)
{
@@ -1737,10 +1739,13 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
ospf_vertex_add_parent(v);
/* RFC2328 16.1. (4). */
- if (v->type == OSPF_VERTEX_ROUTER)
- ospf_intra_add_router(new_rtrs, v, area);
- else
+ if (v->type != OSPF_VERTEX_ROUTER)
ospf_intra_add_transit(new_table, v, area);
+ else {
+ ospf_intra_add_router(new_rtrs, v, area, false);
+ if (all_rtrs)
+ ospf_intra_add_router(all_rtrs, v, area, true);
+ }
/* Iterate back to (2), see RFC2328 16.1. (5). */
}
@@ -1748,6 +1753,8 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
if (IS_DEBUG_OSPF_EVENT) {
ospf_spf_dump(area->spf, 0);
ospf_route_table_dump(new_table);
+ if (all_rtrs)
+ ospf_router_route_table_dump(all_rtrs);
}
/*
@@ -1771,10 +1778,11 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa,
void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs)
{
- ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
- false, true);
+ ospf_spf_calculate(area, area->router_lsa_self, new_table, all_rtrs,
+ new_rtrs, false, true);
if (ospf->ti_lfa_enabled)
ospf_ti_lfa_compute(area, new_table,
@@ -1787,6 +1795,7 @@ void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
}
void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs)
{
struct ospf_area *area;
@@ -1799,13 +1808,14 @@ void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table,
if (ospf->backbone && ospf->backbone == area)
continue;
- ospf_spf_calculate_area(ospf, area, new_table, new_rtrs);
+ ospf_spf_calculate_area(ospf, area, new_table, all_rtrs,
+ new_rtrs);
}
/* SPF for backbone, if required */
if (ospf->backbone)
ospf_spf_calculate_area(ospf, ospf->backbone, new_table,
- new_rtrs);
+ all_rtrs, new_rtrs);
}
/* Worker for SPF calculation scheduler. */
@@ -1813,6 +1823,7 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
{
struct ospf *ospf = THREAD_ARG(thread);
struct route_table *new_table, *new_rtrs;
+ struct route_table *all_rtrs = NULL;
struct timeval start_time, spf_start_time;
unsigned long ia_time, prune_time, rt_time;
unsigned long abr_time, total_spf_time, spf_time;
@@ -1829,7 +1840,12 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
monotime(&spf_start_time);
new_table = route_table_init(); /* routing table */
new_rtrs = route_table_init(); /* ABR/ASBR routing table */
- ospf_spf_calculate_areas(ospf, new_table, new_rtrs);
+
+ /* If we have opaque enabled then track all router reachability */
+ if (CHECK_FLAG(ospf->opaque, OPAQUE_OPERATION_READY_BIT))
+ all_rtrs = route_table_init();
+
+ ospf_spf_calculate_areas(ospf, new_table, all_rtrs, new_rtrs);
spf_time = monotime_since(&spf_start_time, NULL);
ospf_vl_shut_unapproved(ospf);
@@ -1842,6 +1858,8 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
/* Get rid of transit networks and routers we cannot reach anyway. */
monotime(&start_time);
ospf_prune_unreachable_networks(new_table);
+ if (all_rtrs)
+ ospf_prune_unreachable_routers(all_rtrs);
ospf_prune_unreachable_routers(new_rtrs);
prune_time = monotime_since(&start_time, NULL);
@@ -1866,6 +1884,16 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread)
ospf_route_install(ospf, new_table);
rt_time = monotime_since(&start_time, NULL);
+ /* Free old all routers routing table */
+ if (ospf->oall_rtrs)
+ /* ospf_route_delete (ospf->old_rtrs); */
+ ospf_rtrs_free(ospf->oall_rtrs);
+
+ /* Update all routers routing table */
+ ospf->oall_rtrs = ospf->all_rtrs;
+ ospf->all_rtrs = all_rtrs;
+ ospf_apiserver_notify_reachable(ospf->oall_rtrs, ospf->all_rtrs);
+
/* Free old ABR/ASBR routing table */
if (ospf->old_rtrs)
/* ospf_route_delete (ospf->old_rtrs); */
diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h
index 20f38440aa..834bfd0bb0 100644
--- a/ospfd/ospf_spf.h
+++ b/ospfd/ospf_spf.h
@@ -76,13 +76,16 @@ extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t);
extern void ospf_spf_calculate(struct ospf_area *area,
struct ospf_lsa *root_lsa,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs, bool is_dry_run,
bool is_root_node);
extern void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs);
extern void ospf_spf_calculate_areas(struct ospf *ospf,
struct route_table *new_table,
+ struct route_table *all_rtrs,
struct route_table *new_rtrs);
extern void ospf_rtrs_free(struct route_table *);
extern void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list);
diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c
index 8fa5ce77bb..2c7c80686c 100644
--- a/ospfd/ospf_sr.c
+++ b/ospfd/ospf_sr.c
@@ -2302,84 +2302,6 @@ DEFUN(no_sr_global_label_range, no_sr_global_label_range_cmd,
return CMD_SUCCESS;
}
-#if CONFDATE > 20220528
-CPP_NOTICE(
- "Use of the segment-routing local-block command is deprecated, use the combined global-block command instead")
-#endif
-
-DEFUN_HIDDEN(sr_local_label_range, sr_local_label_range_cmd,
- "segment-routing local-block (16-1048575) (16-1048575)",
- SR_STR
- "Segment Routing Local Block label range\n"
- "Lower-bound range in decimal (16-1048575)\n"
- "Upper-bound range in decimal (16-1048575)\n")
-{
- uint32_t upper;
- uint32_t lower;
- uint32_t srgb_upper;
- int idx_low = 2;
- int idx_up = 3;
-
- /* Get lower and upper bound */
- lower = strtoul(argv[idx_low]->arg, NULL, 10);
- upper = strtoul(argv[idx_up]->arg, NULL, 10);
-
- /* check correctness of SRLB */
- if (!sr_range_is_valid(lower, upper, MIN_SRLB_SIZE)) {
- vty_out(vty, "Invalid SRLB range\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* Check if values have changed */
- if ((OspfSR.srlb.start == lower)
- && (OspfSR.srlb.end == upper))
- return CMD_SUCCESS;
-
- /* Validate SRLB against SRGB */
- srgb_upper = OspfSR.srgb.start + OspfSR.srgb.size - 1;
-
- if (ranges_overlap(OspfSR.srgb.start, srgb_upper, lower, upper)) {
- vty_out(vty,
- "New SR Local Block (%u/%u) conflicts with Global Block (%u/%u)\n",
- lower, upper, OspfSR.srgb.start, srgb_upper);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- if (update_sr_blocks(OspfSR.srgb.start, srgb_upper, lower, upper) < 0)
- return CMD_WARNING_CONFIG_FAILED;
- else
- return CMD_SUCCESS;
-}
-
-DEFUN_HIDDEN(no_sr_local_label_range, no_sr_local_label_range_cmd,
- "no segment-routing local-block [(16-1048575) (16-1048575)]",
- NO_STR SR_STR
- "Segment Routing Local Block label range\n"
- "Lower-bound range in decimal (16-1048575)\n"
- "Upper-bound range in decimal (16-1048575)\n")
-{
-
- uint32_t srgb_end;
-
- /* Validate SRLB against SRGB */
- srgb_end = OspfSR.srgb.start + OspfSR.srgb.size - 1;
- if (ranges_overlap(OspfSR.srgb.start, srgb_end, DEFAULT_SRLB_LABEL,
- DEFAULT_SRLB_END)) {
- vty_out(vty,
- "New SR Local Block (%u/%u) conflicts with Global Block (%u/%u)\n",
- DEFAULT_SRLB_LABEL, DEFAULT_SRLB_END, OspfSR.srgb.start,
- srgb_end);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- if (update_sr_blocks(OspfSR.srgb.start, srgb_end, DEFAULT_SRLB_LABEL,
- DEFAULT_SRLB_END)
- < 0)
- return CMD_WARNING_CONFIG_FAILED;
- else
- return CMD_SUCCESS;
-}
-
DEFUN (sr_node_msd,
sr_node_msd_cmd,
"segment-routing node-msd (1-16)",
@@ -3047,8 +2969,6 @@ void ospf_sr_register_vty(void)
install_element(OSPF_NODE, &no_ospf_sr_enable_cmd);
install_element(OSPF_NODE, &sr_global_label_range_cmd);
install_element(OSPF_NODE, &no_sr_global_label_range_cmd);
- install_element(OSPF_NODE, &sr_local_label_range_cmd);
- install_element(OSPF_NODE, &no_sr_local_label_range_cmd);
install_element(OSPF_NODE, &sr_node_msd_cmd);
install_element(OSPF_NODE, &no_sr_node_msd_cmd);
install_element(OSPF_NODE, &sr_prefix_sid_cmd);
diff --git a/ospfd/ospf_ti_lfa.c b/ospfd/ospf_ti_lfa.c
index 347128a4f4..28d24bcbe6 100644
--- a/ospfd/ospf_ti_lfa.c
+++ b/ospfd/ospf_ti_lfa.c
@@ -326,8 +326,8 @@ static void ospf_ti_lfa_generate_inner_label_stack(
XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_spaces_head));
/* dry run true, root node false */
- ospf_spf_calculate(area, start_vertex->lsa_p, new_table, new_rtrs, true,
- false);
+ ospf_spf_calculate(area, start_vertex->lsa_p, new_table, NULL, new_rtrs,
+ true, false);
q_node = ospf_spf_vertex_find(end_vertex->id, area->spf_vertex_list);
@@ -676,6 +676,7 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area,
sizeof(struct ospf_ti_lfa_node_info));
new_table = route_table_init();
+ /* XXX do these get freed?? */
new_rtrs = route_table_init();
/*
@@ -683,7 +684,8 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area,
* dry run true, root node false
*/
area->spf_reversed = true;
- ospf_spf_calculate(area, dest->lsa_p, new_table, new_rtrs, true, false);
+ ospf_spf_calculate(area, dest->lsa_p, new_table, NULL, new_rtrs, true,
+ false);
/* Reset the flag for reverse SPF */
area->spf_reversed = false;
@@ -750,6 +752,7 @@ static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area,
struct route_table *new_table, *new_rtrs;
new_table = route_table_init();
+ /* XXX do these get freed?? */
new_rtrs = route_table_init();
area->spf_protected_resource = p_space->protected_resource;
@@ -769,8 +772,8 @@ static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area,
* endeavour (because LSAs are stored as a 'raw' stream), so we go with
* this rather hacky way for now.
*/
- ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
- true, false);
+ ospf_spf_calculate(area, area->router_lsa_self, new_table, NULL,
+ new_rtrs, true, false);
p_space->pc_spf = area->spf;
p_space->pc_vertex_list = area->spf_vertex_list;
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 20b9180cff..e5985a8660 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -10741,8 +10741,9 @@ static void show_ip_ospf_route_router(struct vty *vty, struct ospf *ospf,
*json_nexthop = NULL;
if (!json)
- vty_out(vty,
- "============ OSPF router routing table =============\n");
+ vty_out(vty, "============ OSPF %s table =============\n",
+ ospf->all_rtrs == rtrs ? "reachable routers"
+ : "router routing");
for (rn = route_top(rtrs); rn; rn = route_next(rn)) {
if (rn->info == NULL)
@@ -11004,6 +11005,114 @@ static void show_ip_ospf_route_external(struct vty *vty, struct ospf *ospf,
vty_out(vty, "\n");
}
+static int show_ip_ospf_reachable_routers_common(struct vty *vty,
+ struct ospf *ospf,
+ uint8_t use_vrf)
+{
+ if (ospf->instance)
+ vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance);
+
+ ospf_show_vrf_name(ospf, vty, NULL, use_vrf);
+
+ if (ospf->all_rtrs == NULL) {
+ vty_out(vty, "No OSPF reachable router information exist\n");
+ return CMD_SUCCESS;
+ }
+
+ /* Show Router routes. */
+ show_ip_ospf_route_router(vty, ospf, ospf->all_rtrs, NULL);
+
+ vty_out(vty, "\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_ospf_reachable_routers,
+ show_ip_ospf_reachable_routers_cmd,
+ "show ip ospf [vrf <NAME|all>] reachable-routers",
+ SHOW_STR
+ IP_STR
+ "OSPF information\n"
+ VRF_CMD_HELP_STR
+ "All VRFs\n"
+ "Show all the reachable OSPF routers\n")
+{
+ struct ospf *ospf = NULL;
+ struct listnode *node = NULL;
+ char *vrf_name = NULL;
+ bool all_vrf = false;
+ int ret = CMD_SUCCESS;
+ int inst = 0;
+ int idx_vrf = 0;
+ uint8_t use_vrf = 0;
+
+ OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
+
+ if (vrf_name) {
+ bool ospf_output = false;
+
+ use_vrf = 1;
+
+ if (all_vrf) {
+ for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) {
+ if (!ospf->oi_running)
+ continue;
+
+ ospf_output = true;
+ ret = show_ip_ospf_reachable_routers_common(
+ vty, ospf, use_vrf);
+ }
+
+ if (!ospf_output)
+ vty_out(vty, "%% OSPF instance not found\n");
+ } else {
+ ospf = ospf_lookup_by_inst_name(inst, vrf_name);
+ if (ospf == NULL || !ospf->oi_running) {
+ vty_out(vty, "%% OSPF instance not found\n");
+ return CMD_SUCCESS;
+ }
+
+ ret = show_ip_ospf_reachable_routers_common(vty, ospf,
+ use_vrf);
+ }
+ } else {
+ /* Display default ospf (instance 0) info */
+ ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ if (ospf == NULL || !ospf->oi_running) {
+ vty_out(vty, "%% OSPF instance not found\n");
+ return CMD_SUCCESS;
+ }
+
+ ret = show_ip_ospf_reachable_routers_common(vty, ospf, use_vrf);
+ }
+
+ return ret;
+}
+
+DEFUN (show_ip_ospf_instance_reachable_routers,
+ show_ip_ospf_instance_reachable_routers_cmd,
+ "show ip ospf (1-65535) reachable-routers",
+ SHOW_STR
+ IP_STR
+ "OSPF information\n"
+ "Instance ID\n"
+ "Show all the reachable OSPF routers\n")
+{
+ int idx_number = 3;
+ struct ospf *ospf;
+ unsigned short instance = 0;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+ if (instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+ ospf = ospf_lookup_instance(instance);
+ if (!ospf || !ospf->oi_running)
+ return CMD_SUCCESS;
+
+ return show_ip_ospf_reachable_routers_common(vty, ospf, 0);
+}
+
static int show_ip_ospf_border_routers_common(struct vty *vty,
struct ospf *ospf,
uint8_t use_vrf)
@@ -11146,6 +11255,10 @@ static int show_ip_ospf_route_common(struct vty *vty, struct ospf *ospf,
/* Show Router routes. */
show_ip_ospf_route_router(vty, ospf, ospf->new_rtrs, json_vrf);
+ /* Show Router routes. */
+ if (ospf->all_rtrs)
+ show_ip_ospf_route_router(vty, ospf, ospf->all_rtrs, json_vrf);
+
/* Show AS External routes. */
show_ip_ospf_route_external(vty, ospf, ospf->old_external_route,
json_vrf);
@@ -12603,9 +12716,12 @@ void ospf_vty_show_init(void)
/* "show ip ospf route" commands. */
install_element(VIEW_NODE, &show_ip_ospf_route_cmd);
install_element(VIEW_NODE, &show_ip_ospf_border_routers_cmd);
+ install_element(VIEW_NODE, &show_ip_ospf_reachable_routers_cmd);
install_element(VIEW_NODE, &show_ip_ospf_instance_route_cmd);
install_element(VIEW_NODE, &show_ip_ospf_instance_border_routers_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_ospf_instance_reachable_routers_cmd);
/* "show ip ospf vrfs" commands. */
install_element(VIEW_NODE, &show_ip_ospf_vrfs_cmd);
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 268e4d6f8d..76501dd6bb 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -234,6 +234,9 @@ struct ospf {
struct route_table *old_table; /* Old routing table. */
struct route_table *new_table; /* Current routing table. */
+ struct route_table *oall_rtrs; /* Old router RT. */
+ struct route_table *all_rtrs; /* New routers RT. */
+
struct route_table *old_rtrs; /* Old ABR/ASBR RT. */
struct route_table *new_rtrs; /* New ABR/ASBR RT. */
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index c479a7d4cf..91829122ee 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -466,6 +466,9 @@ install -d -m750 %{buildroot}%{rundir}
# avoid `ERROR: ambiguous python shebang in` errors
pathfix.py -pni "%{__python3} %{py3_shbang_opts}" %{buildroot}/usr/lib/frr/*.py
%py_byte_compile %{__python3} %{buildroot}/usr/lib/frr/*.py
+%else
+# remove ospfclient.py (if present) as it requires > python36
+rm -f %{buildroot}%{_sbindir}/ospfclient.py
%endif
%pre
@@ -719,11 +722,13 @@ fi
%files contrib
%doc tools
-
%files pythontools
%{_sbindir}/generate_support_bundle.py
%{_sbindir}/frr-reload.py
%{_sbindir}/frr_babeltrace.py
+%if %{with_ospfclient} && (0%{?rhel} > 7 || 0%{?fedora} > 29)
+%{_sbindir}/ospfclient.py
+%endif
%if 0%{?rhel} > 7 || 0%{?fedora} > 29
%{_sbindir}/__pycache__/*
%else
@@ -774,6 +779,10 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons
%changelog
+* Sun May 29 2022 Christian Hopps <chopps@labn.net> - %{version}
+- ospfclient:
+- Add OSPF API python client ospfclient.py
+
* Tue Mar 1 2022 Martin Winter <mwinter@opensourcerouting.org> - %{version}
* Tue Mar 1 2022 Jafar Al-Gharaibeh <jafar@atcorp.com> - 8.2
diff --git a/tests/ospfd/test_ospf_spf.c b/tests/ospfd/test_ospf_spf.c
index 3562b5f2f8..73f2e29834 100644
--- a/tests/ospfd/test_ospf_spf.c
+++ b/tests/ospfd/test_ospf_spf.c
@@ -52,6 +52,7 @@ static void test_run_spf(struct vty *vty, struct ospf *ospf,
enum protection_type protection_type, bool verbose)
{
struct route_table *new_table, *new_rtrs;
+ struct route_table *all_rtrs = NULL;
struct ospf_area *area;
struct p_space *p_space;
struct q_space *q_space;
@@ -63,10 +64,11 @@ static void test_run_spf(struct vty *vty, struct ospf *ospf,
new_table = route_table_init();
new_rtrs = route_table_init();
+ all_rtrs = route_table_init();
/* dryrun true, root_node false */
- ospf_spf_calculate(area, area->router_lsa_self, new_table, new_rtrs,
- true, false);
+ ospf_spf_calculate(area, area->router_lsa_self, new_table, all_rtrs,
+ new_rtrs, true, false);
if (verbose) {
vty_out(vty, "SPF Tree without TI-LFA backup paths:\n\n");
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
index 16d6b1993d..fc2d2364c6 100644
--- a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
@@ -436,7 +436,6 @@ def test_restart_frr_p2(request):
step("Activate same IPv6 nbr from IPv4 unicast family")
step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
- reset_config_on_routers(tgen)
bgp_convergence = verify_bgp_convergence(tgen, topo)
assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
tc_name, bgp_convergence
diff --git a/tests/topotests/lib/micronet.py b/tests/topotests/lib/micronet.py
index d7b680da18..02f66e9c26 100644
--- a/tests/topotests/lib/micronet.py
+++ b/tests/topotests/lib/micronet.py
@@ -555,8 +555,11 @@ class LinuxNamespace(Commander):
self.base_pre_cmd.append("-F")
self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + self.cwd])
- # Remount /sys to pickup any changes
+ # Remount sysfs and cgroup to pickup any changes
self.cmd_raises("mount -t sysfs sysfs /sys")
+ self.cmd_raises(
+ "mount -o rw,nosuid,nodev,noexec,relatime -t cgroup2 cgroup /sys/fs/cgroup"
+ )
# Set the hostname to the namespace name
if uts and set_hostname:
diff --git a/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
index c0ea1b5561..ceac78d88b 100644
--- a/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
+++ b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
@@ -377,7 +377,6 @@ def test_BSR_higher_prefer_ip_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
step("pre-configure BSM packet")
step("Configure cisco-1 as BSR1 1.1.2.7")
result = pre_config_to_bsm(
@@ -583,7 +582,6 @@ def test_BSR_CRP_with_blackhole_address_p1(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
step("pre-configure BSM packet")
step("Configure cisco-1 as BSR1 1.1.2.7")
result = pre_config_to_bsm(
@@ -760,8 +758,6 @@ def test_new_router_fwd_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -901,8 +897,6 @@ def test_int_bsm_config_p1(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -1062,8 +1056,6 @@ def test_static_rp_override_p1(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -1213,8 +1205,6 @@ def test_bsmp_stress_add_del_restart_p2(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -1538,8 +1528,6 @@ def test_iif_join_state_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
diff --git a/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
index 9f771b8a2b..2d6062bf3c 100644
--- a/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
+++ b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
@@ -335,8 +335,6 @@ def test_starg_mroute_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -487,8 +485,6 @@ def test_overlapping_group_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -593,8 +589,6 @@ def test_RP_priority_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -724,8 +718,6 @@ def test_BSR_election_p0(request):
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -840,8 +832,6 @@ def test_RP_hash_p0(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
@@ -933,8 +923,6 @@ def test_BSM_fragmentation_p1(request):
reset_config_on_routers(tgen)
clear_pim_interface_traffic(tgen, topo)
- reset_config_on_routers(tgen)
-
result = pre_config_to_bsm(
tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
)
diff --git a/tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf
index a440fa6fdf..064a4bf286 100644
--- a/tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf
+++ b/tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf
@@ -30,7 +30,6 @@ router ospf
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
- !segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 1.1.1.1/32 index 10
!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf
index 7bec98cd76..65b20f0e63 100644
--- a/tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf
+++ b/tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf
@@ -41,7 +41,6 @@ router ospf
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
- !segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 2.2.2.2/32 index 20
!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf
index 40b85c4601..5be0c49a85 100644
--- a/tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf
+++ b/tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf
@@ -40,7 +40,6 @@ router ospf
router-info area 0.0.0.0
segment-routing on
segment-routing global-block 16000 23999
- !segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 3.3.3.3/32 index 30
!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf
index 4d3380d107..7cdf032eaa 100644
--- a/tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf
+++ b/tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf
@@ -47,7 +47,6 @@ router ospf
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
- !segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 4.4.4.4/32 index 40
!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf
index b111ce588a..8f71cda443 100644
--- a/tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf
+++ b/tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf
@@ -47,7 +47,6 @@ router ospf
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
-! segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 5.5.5.5/32 index 50
!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf
index f0c5a9c0ba..20c89757a8 100644
--- a/tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf
+++ b/tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf
@@ -35,7 +35,6 @@ router ospf
passive-interface lo
segment-routing on
segment-routing global-block 16000 23999
-! segment-routing local-block 15000 15999
segment-routing node-msd 8
segment-routing prefix 6.6.6.6/32 index 60
!
diff --git a/tests/topotests/ospf_te_topo1/r4/ospfd.conf b/tests/topotests/ospf_te_topo1/r4/ospfd.conf
index e454673153..cd508017d3 100644
--- a/tests/topotests/ospf_te_topo1/r4/ospfd.conf
+++ b/tests/topotests/ospf_te_topo1/r4/ospfd.conf
@@ -15,8 +15,7 @@ router ospf
mpls-te on
mpls-te router-address 10.0.255.4
segment-routing on
- segment-routing local-block 5000 5999
- segment-routing global-block 10000 19999
+ segment-routing global-block 10000 19999 local-block 5000 5999
segment-routing node-msd 12
segment-routing prefix 10.0.255.4/32 index 400 no-php-flag
!
diff --git a/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py b/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py
index 7de23dc34e..699cdc9054 100644
--- a/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py
+++ b/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py
@@ -224,7 +224,7 @@ def test_step4():
'vtysh -c "conf t" -c "router ospf" -c "segment-routing node-msd 16"'
)
tgen.net["r2"].cmd(
- 'vtysh -c "conf t" -c "router ospf" -c "segment-routing local-block 5000 6999"'
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing global-block 16000 23999 local-block 5000 6999"'
)
tgen.net["r2"].cmd(
'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 10.0.255.2/32 index 20 explicit-null"'
diff --git a/tests/topotests/ospfapi/ctester.py b/tests/topotests/ospfapi/ctester.py
new file mode 100755
index 0000000000..243fc0613f
--- /dev/null
+++ b/tests/topotests/ospfapi/ctester.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# January 17 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2022, LabN Consulting, L.L.C.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+import argparse
+import asyncio
+import logging
+import os
+import sys
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
+if not os.path.exists(CLIENTDIR):
+ CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
+assert os.path.exists(
+ os.path.join(CLIENTDIR, "ospfclient.py")
+), "can't locate ospfclient.py"
+
+sys.path[0:0] = [CLIENTDIR]
+
+import ospfclient as api # pylint: disable=E0401 # noqa: E402
+
+
+async def do_wait(c, args):
+ cv = asyncio.Condition()
+
+ async def cb(added, removed):
+ logging.debug("callback: added: %s removed: %s", added, removed)
+ sys.stdout.flush()
+ async with cv:
+ cv.notify_all()
+
+ logging.debug("API using callback")
+ await c.monitor_reachable(callback=cb)
+
+ for w in args.wait:
+ check = ",".join(sorted(list(w.split(","))))
+ logging.info("Waiting for %s", check)
+
+ while True:
+ async with cv:
+ got = ",".join(sorted([str(x) for x in c.reachable_routers]))
+ if check == got:
+ break
+ logging.debug("expected '%s' != '%s'\nwaiting on notify", check, got)
+ await cv.wait()
+
+ logging.info("SUCCESS: %s", check)
+ print("SUCCESS: {}".format(check))
+ sys.stdout.flush()
+
+
+async def async_main(args):
+ c = api.OspfOpaqueClient(args.server)
+ await c.connect()
+ if sys.version_info[1] > 6:
+ asyncio.create_task(c._handle_msg_loop()) # pylint: disable=W0212
+ else:
+ asyncio.get_event_loop().create_task(
+ c._handle_msg_loop() # pylint: disable=W0212
+ )
+
+ if args.wait:
+ await do_wait(c, args)
+ return 0
+
+
+def main(*args):
+ ap = argparse.ArgumentParser(args)
+ ap.add_argument("--server", default="localhost", help="OSPF API server")
+ ap.add_argument(
+ "--wait", action="append", help="wait for comma-sep set of reachable routers"
+ )
+ ap.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+ args = ap.parse_args()
+
+ level = logging.DEBUG if args.verbose else logging.INFO
+ logging.basicConfig(
+ level=level, format="%(asctime)s %(levelname)s: TESTER: %(name)s: %(message)s"
+ )
+
+ # We need to flush this output to stdout right away
+ h = logging.StreamHandler(sys.stdout)
+ h.flush = sys.stdout.flush
+ f = logging.Formatter("%(asctime)s %(name)s: %(levelname)s: %(message)s")
+ h.setFormatter(f)
+ logger = logging.getLogger("ospfclient")
+ logger.addHandler(h)
+ logger.propagate = False
+
+ logging.info("ctester: starting")
+ sys.stdout.flush()
+
+ status = 3
+ try:
+ if sys.version_info[1] > 6:
+ status = asyncio.run(async_main(args))
+ else:
+ loop = asyncio.get_event_loop()
+ try:
+ status = loop.run_until_complete(async_main(args))
+ finally:
+ loop.close()
+ except KeyboardInterrupt:
+ logging.info("Exiting, received KeyboardInterrupt in main")
+ except Exception as error:
+ logging.info("Exiting, unexpected exception %s", error, exc_info=True)
+ else:
+ logging.info("api: clean exit")
+
+ return status
+
+
+if __name__ == "__main__":
+ exit_status = main()
+ sys.exit(exit_status)
diff --git a/tests/topotests/ospfapi/lib b/tests/topotests/ospfapi/lib
new file mode 120000
index 0000000000..dc598c56dc
--- /dev/null
+++ b/tests/topotests/ospfapi/lib
@@ -0,0 +1 @@
+../lib \ No newline at end of file
diff --git a/tests/topotests/ospfapi/r1/ospfd.conf b/tests/topotests/ospfapi/r1/ospfd.conf
new file mode 100644
index 0000000000..8d13556847
--- /dev/null
+++ b/tests/topotests/ospfapi/r1/ospfd.conf
@@ -0,0 +1,10 @@
+!
+interface r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 192.168.0.1
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r1/zebra.conf b/tests/topotests/ospfapi/r1/zebra.conf
new file mode 100644
index 0000000000..aae87408c3
--- /dev/null
+++ b/tests/topotests/ospfapi/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r1-eth0
+ ip address 10.0.1.1/24
+!
diff --git a/tests/topotests/ospfapi/r2/ospfd.conf b/tests/topotests/ospfapi/r2/ospfd.conf
new file mode 100644
index 0000000000..be0712742b
--- /dev/null
+++ b/tests/topotests/ospfapi/r2/ospfd.conf
@@ -0,0 +1,15 @@
+!
+interface r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+interface r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 192.168.0.2
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r2/zebra.conf b/tests/topotests/ospfapi/r2/zebra.conf
new file mode 100644
index 0000000000..e66d52da49
--- /dev/null
+++ b/tests/topotests/ospfapi/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r2-eth0
+ ip address 10.0.1.2/24
+!
+interface r2-eth1
+ ip address 10.0.2.2/24
+!
diff --git a/tests/topotests/ospfapi/r3/ospfd.conf b/tests/topotests/ospfapi/r3/ospfd.conf
new file mode 100644
index 0000000000..77cd86a975
--- /dev/null
+++ b/tests/topotests/ospfapi/r3/ospfd.conf
@@ -0,0 +1,15 @@
+!
+interface r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+interface r3-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 192.168.0.3
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r3/zebra.conf b/tests/topotests/ospfapi/r3/zebra.conf
new file mode 100644
index 0000000000..072c297926
--- /dev/null
+++ b/tests/topotests/ospfapi/r3/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r3-eth0
+ ip address 10.0.2.3/24
+!
+interface r3-eth1
+ ip address 10.0.3.3/24
+!
diff --git a/tests/topotests/ospfapi/r4/ospfd.conf b/tests/topotests/ospfapi/r4/ospfd.conf
new file mode 100644
index 0000000000..32e55d546b
--- /dev/null
+++ b/tests/topotests/ospfapi/r4/ospfd.conf
@@ -0,0 +1,10 @@
+!
+interface r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 192.168.0.4
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r4/zebra.conf b/tests/topotests/ospfapi/r4/zebra.conf
new file mode 100644
index 0000000000..702219720d
--- /dev/null
+++ b/tests/topotests/ospfapi/r4/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r4-eth0
+ ip address 10.0.3.4/24
+!
diff --git a/tests/topotests/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py
new file mode 100644
index 0000000000..dca91412dc
--- /dev/null
+++ b/tests/topotests/ospfapi/test_ospf_clientapi.py
@@ -0,0 +1,470 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+"""
+test_ospf_clientapi.py: Test the OSPF client API.
+"""
+
+import logging
+import os
+import re
+import signal
+import subprocess
+import sys
+import time
+from datetime import datetime, timedelta
+
+import pytest
+
+from lib.common_config import retry, run_frr_cmd, step
+from lib.micronet import comm_error
+from lib.topogen import Topogen, TopoRouter
+from lib.topotest import interface_set_status, json_cmp
+
+pytestmark = [pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+TESTDIR = os.path.abspath(CWD)
+
+CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
+if not os.path.exists(CLIENTDIR):
+ CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
+
+assert os.path.exists(
+ os.path.join(CLIENTDIR, "ospfclient.py")
+), "can't locate ospfclient.py"
+
+
+# ----------
+# Test Setup
+# ----------
+
+
+@pytest.fixture(scope="function", name="tgen")
+def _tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+ nrouters = request.param
+ topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)}
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for _, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF, "ospfd.conf")
+ router.net.daemons_options["ospfd"] = "--apiserver"
+
+ tgen.start_router()
+
+ yield tgen
+
+ tgen.stop_topology()
+
+
+# Fixture that executes before each test
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+# ------------
+# Test Utility
+# ------------
+
+
+@retry(retry_timeout=45)
+def verify_ospf_database(tgen, dut, input_dict, cmd="show ip ospf database json"):
+ del tgen
+ show_ospf_json = run_frr_cmd(dut, cmd, isjson=True)
+ if not bool(show_ospf_json):
+ return "ospf is not running"
+ result = json_cmp(show_ospf_json, input_dict)
+ return str(result) if result else None
+
+
+def myreadline(f):
+ buf = b""
+ while True:
+ # logging.info("READING 1 CHAR")
+ c = f.read(1)
+ if not c:
+ return buf if buf else None
+ buf += c
+ # logging.info("READ CHAR: '%s'", c)
+ if c == b"\n":
+ return buf
+
+
+def _wait_output(p, regex, timeout=120):
+ retry_until = datetime.now() + timedelta(seconds=timeout)
+ while datetime.now() < retry_until:
+ # line = p.stdout.readline()
+ line = myreadline(p.stdout)
+ if not line:
+ assert None, "Timeout waiting for '{}'".format(regex)
+ line = line.decode("utf-8")
+ line = line.rstrip()
+ if line:
+ logging.debug("GOT LINE: '%s'", line)
+ m = re.search(regex, line)
+ if m:
+ return m
+ assert None, "Failed to get output withint {}s".format(timeout)
+
+
+# -----
+# Tests
+# -----
+
+
+def _test_reachability(tgen, testbin):
+ waitlist = [
+ "192.168.0.1,192.168.0.2,192.168.0.4",
+ "192.168.0.2,192.168.0.4",
+ "192.168.0.1,192.168.0.2,192.168.0.4",
+ ]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+
+ wait_args = [f"--wait={x}" for x in waitlist]
+
+ p = None
+ try:
+ step("reachable: check for initial reachability")
+ p = r3.popen(
+ ["/usr/bin/timeout", "120", testbin, "-v", *wait_args],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ _wait_output(p, "SUCCESS: {}".format(waitlist[0]))
+
+ step("reachable: check for modified reachability")
+ interface_set_status(r2, "r2-eth0", False)
+ _wait_output(p, "SUCCESS: {}".format(waitlist[1]))
+
+ step("reachable: check for restored reachability")
+ interface_set_status(r2, "r2-eth0", True)
+ _wait_output(p, "SUCCESS: {}".format(waitlist[2]))
+ except Exception as error:
+ logging.error("ERROR: %s", error)
+ raise
+ finally:
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [4], indirect=True)
+def test_ospf_reachability(tgen):
+ testbin = os.path.join(TESTDIR, "ctester.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([testbin, "--help"])
+ logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
+ _test_reachability(tgen, testbin)
+
+
+def _test_add_data(tgen, apibin):
+ "Test adding opaque data to domain"
+
+ r1 = tgen.gears["r1"]
+
+ step("add opaque: add opaque link local")
+
+ p = None
+ try:
+ p = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,2,00000202"])
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.2",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ }
+ ],
+ }
+ },
+ }
+ # Wait for it to show up
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "linkLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "230.0.0.2",
+ "advertisingRouter": "192.168.0.1",
+ "lsaSeqNumber": "80000001",
+ "opaqueData": "00000202",
+ },
+ ],
+ }
+ },
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-link json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ step("reset client, add opaque area, verify link local flushing")
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+ p = r1.popen([apibin, "-v", "add,10,1.2.3.4,231,1,00010101"])
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.2",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ "lsaAge": 3600,
+ }
+ ],
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ }
+ },
+ }
+ # Wait for it to show up
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "192.168.0.1",
+ "lsaSeqNumber": "80000001",
+ "opaqueData": "00010101",
+ },
+ ],
+ }
+ },
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-area json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ step("reset client, add opaque AS, verify area flushing")
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ p = r1.popen([apibin, "-v", "add,11,232,3,deadbeaf01234567"])
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "areas": {
+ "1.2.3.4": {
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ "lsaAge": 3600,
+ },
+ ],
+ }
+ },
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.3",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ }
+ # Wait for it to show up
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "asExternalOpaqueLsa": [
+ {
+ "linkStateId": "232.0.0.3",
+ "advertisingRouter": "192.168.0.1",
+ "lsaSeqNumber": "80000001",
+ "opaqueData": "deadbeaf01234567",
+ },
+ ]
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-as json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ step("stop client, verify AS flushing")
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.3",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000001",
+ "lsaAge": 3600,
+ },
+ ],
+ }
+ # Wait for it to be flushed
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ step("start client adding opaque domain, verify new sequence number and data")
+
+ # Originate it again
+ p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"])
+ input_dict = {
+ "routerId": "192.168.0.1",
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.3",
+ "advertisedRouter": "192.168.0.1",
+ "sequenceNumber": "80000002",
+ },
+ ],
+ }
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "asExternalOpaqueLsa": [
+ {
+ "linkStateId": "232.0.0.3",
+ "advertisingRouter": "192.168.0.1",
+ "lsaSeqNumber": "80000002",
+ "opaqueData": "ebadf00d",
+ },
+ ]
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-as json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ except Exception:
+ if p:
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+ p = None
+ raise
+ finally:
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_opaque_add_data3(tgen):
+ apibin = os.path.join(CLIENTDIR, "ospfclient.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
+ logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
+ _test_add_data(tgen, apibin)
+
+
+def _test_opaque_add_del(tgen, apibin):
+ "Test adding opaque data to domain"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ p = None
+ pread = None
+ try:
+ step("reachable: check for add notification")
+ pread = r2.popen(
+ ["/usr/bin/timeout", "120", apibin, "-v"],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"])
+
+ # Wait for add notification
+ # RECV: LSA update msg for LSA 232.0.0.3 in area 0.0.0.0 seq 0x80000001 len 24 age 9
+
+ ls_id = "232.0.0.3"
+ waitfor = "RECV:.*update msg.*LSA {}.*age ([0-9]+)".format(ls_id)
+ _ = _wait_output(pread, waitfor)
+
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+
+ # step("reachable: check for flush/age out")
+ # # Wait for max age notification
+ # waitfor = "RECV:.*update msg.*LSA {}.*age 3600".format(ls_id)
+ # _wait_output(pread, waitfor)
+
+ step("reachable: check for delete")
+ # Wait for delete notification
+ waitfor = "RECV:.*delete msg.*LSA {}.*".format(ls_id)
+ _wait_output(pread, waitfor)
+ except Exception:
+ if p:
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+ p = None
+ raise
+ finally:
+ if pread:
+ pread.terminate()
+ pread.wait()
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_opaque_delete_data3(tgen):
+ apibin = os.path.join(CLIENTDIR, "ospfclient.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
+ logging.info("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
+ _test_opaque_add_del(tgen, apibin)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
index 6c76c928ec..088ac600c1 100644
--- a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
@@ -435,11 +435,9 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ebgp(request):
pytest.skip(tgen.errors)
reset_config_on_routers(tgen)
- NEXT_HOP_IP = populate_nh()
step("Configure 8 interfaces / links between R1 and R2,")
step("Configure IBGP IPv4 peering between R2 and R3 router.")
- reset_config_on_routers(tgen)
NEXT_HOP_IP = populate_nh()
nh_all = {}
for addr_type in ADDR_TYPES:
diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
index 1861d9ad49..cdd7e13f75 100644
--- a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
@@ -434,11 +434,9 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ibgp(request):
pytest.skip(tgen.errors)
reset_config_on_routers(tgen)
- NEXT_HOP_IP = populate_nh()
step("Configure 8 interfaces / links between R1 and R2,")
step("Configure IBGP IPv4 peering between R2 and R3 router.")
- reset_config_on_routers(tgen)
NEXT_HOP_IP = populate_nh()
nh_all = {}
for addr_type in ADDR_TYPES:
diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in
index 4683ceef14..e5286d14f6 100644..100755
--- a/tools/frrcommon.sh.in
+++ b/tools/frrcommon.sh.in
@@ -194,10 +194,14 @@ daemon_stop() {
is_user_root || exit 1
+ all=false
+ [ "$2" = "--reallyall" ] && all=true
+
pidfile="$V_PATH/$daemon${inst:+-$inst}.pid"
vtyfile="$V_PATH/$daemon${inst:+-$inst}.vty"
[ -r "$pidfile" ] || fail="pid file not found"
+ $all && [ -n "$fail" ] && return 0
[ -z "$fail" ] && pid="`cat \"$pidfile\"`"
[ -z "$fail" -a -z "$pid" ] && fail="pid file is empty"
[ -n "$fail" ] || kill -0 "$pid" 2>/dev/null || fail="pid $pid not running"
diff --git a/tools/releasedate.py b/tools/releasedate.py
index 3df1ea48fb..3df1ea48fb 100644..100755
--- a/tools/releasedate.py
+++ b/tools/releasedate.py
diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang
index defb2b2038..0812c86fac 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -733,11 +733,11 @@ module frr-isisd {
container multi-topology {
description
"IS-IS topologies configured on this circuit.";
- leaf ipv4-unicast {
+ leaf standard {
type boolean;
default "true";
description
- "IPv4 unicast topology.";
+ "Standard (IPV4 unicast) topology.";
}
leaf ipv4-multicast {
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index 7adbb5ab85..7e47822a2c 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -309,9 +309,16 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
snl.nl_groups = groups;
#if defined SOL_NETLINK
- if (ext_groups)
- setsockopt(sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
- &ext_groups, sizeof(ext_groups));
+ if (ext_groups) {
+ ret = setsockopt(sock, SOL_NETLINK,
+ NETLINK_ADD_MEMBERSHIP, &ext_groups,
+ sizeof(ext_groups));
+ if (ret < 0) {
+ zlog_notice(
+ "can't setsockopt NETLINK_ADD_MEMBERSHIP: %s(%d)",
+ safe_strerror(errno), errno);
+ }
+ }
#endif
/* Bind the socket to the netlink structure for anything. */