summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfdd/bfd.c13
-rw-r--r--bfdd/bfd_packet.c20
-rw-r--r--bfdd/ptm_adapter.c30
-rw-r--r--bgpd/bgp_attr.c5
-rw-r--r--bgpd/bgp_attr_evpn.c10
-rw-r--r--bgpd/bgp_ecommunity.c24
-rw-r--r--bgpd/bgp_ecommunity.h2
-rw-r--r--bgpd/bgp_evpn.c16
-rw-r--r--bgpd/bgp_main.c5
-rw-r--r--bgpd/bgp_mplsvpn.c3
-rw-r--r--bgpd/bgp_pbr.c2
-rw-r--r--bgpd/bgp_routemap.c210
-rw-r--r--bgpd/bgp_script.c192
-rw-r--r--bgpd/bgp_script.h34
-rw-r--r--bgpd/bgp_updgrp_adv.c5
-rw-r--r--bgpd/rfapi/rfapi_import.c8
-rw-r--r--bgpd/rfapi/vnc_export_bgp.c2
-rw-r--r--bgpd/subdir.am2
-rwxr-xr-xconfigure.ac35
-rw-r--r--debian/control3
-rwxr-xr-xdebian/rules7
-rw-r--r--doc/developer/library.rst2
-rw-r--r--doc/developer/lua.rst65
-rw-r--r--doc/developer/scripting.rst433
-rw-r--r--doc/developer/subdir.am2
-rw-r--r--doc/user/index.rst1
-rw-r--r--doc/user/installation.rst8
-rw-r--r--doc/user/scripting.rst28
-rw-r--r--doc/user/subdir.am1
-rw-r--r--lib/command.c30
-rw-r--r--lib/compiler.h23
-rw-r--r--lib/frrlua.c387
-rw-r--r--lib/frrlua.h205
-rw-r--r--lib/frrscript.c272
-rw-r--r--lib/frrscript.h138
-rw-r--r--lib/libfrr.c16
-rw-r--r--lib/libfrr.h2
-rw-r--r--lib/northbound_cli.c6
-rw-r--r--lib/subdir.am2
-rw-r--r--pbrd/pbr_nht.c26
-rw-r--r--staticd/static_nb.c2
-rw-r--r--staticd/static_nb.h4
-rw-r--r--staticd/static_nb_config.c126
-rw-r--r--tests/topotests/bfd-ospf-topo1/__init__.py0
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/bfdd.conf9
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/ospf6d.conf21
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/ospfd.conf26
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step1/show_ip_route.ref74
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step1/show_ipv6_route.ref70
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step2/show_bfd_peers.ref26
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_healthy.ref28
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt2_down.ref15
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt3_down.ref15
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_healthy.ref74
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt2_down.ref74
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt3_down.ref74
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_healthy.ref70
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt2_down.ref70
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt3_down.ref70
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt1/zebra.conf25
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt2/bfdd.conf7
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt2/ospf6d.conf19
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt2/ospfd.conf24
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt2/step2/show_bfd_peers.ref14
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt3/bfdd.conf7
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt3/ospf6d.conf19
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt3/ospfd.conf24
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt3/step2/show_bfd_peers.ref14
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt3/zebra.conf22
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt4/bfdd.conf5
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt4/ospf6d.conf18
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt4/ospfd.conf23
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt4/zebra.conf22
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt5/bfdd.conf5
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt5/ospf6d.conf18
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt5/ospfd.conf23
-rw-r--r--tests/topotests/bfd-ospf-topo1/rt5/zebra.conf22
-rwxr-xr-xtests/topotests/bfd-ospf-topo1/test_bfd_ospf_topo1.py307
-rw-r--r--yang/frr-nexthop.yang6
-rw-r--r--zebra/dplane_fpm_nl.c8
81 files changed, 3313 insertions, 464 deletions
diff --git a/bfdd/bfd.c b/bfdd/bfd.c
index 9667ba8708..ca4bf94955 100644
--- a/bfdd/bfd.c
+++ b/bfdd/bfd.c
@@ -313,6 +313,13 @@ int bfd_session_enable(struct bfd_session *bs)
}
}
+ if (!vrf_is_backend_netns() && vrf && vrf->vrf_id != VRF_DEFAULT
+ && !if_lookup_by_name(vrf->name, vrf->vrf_id)) {
+ zlog_err("session-enable: vrf interface %s not available yet",
+ vrf->name);
+ return 0;
+ }
+
if (bs->key.ifname[0]) {
if (vrf)
ifp = if_lookup_by_name(bs->key.ifname, vrf->vrf_id);
@@ -320,14 +327,16 @@ int bfd_session_enable(struct bfd_session *bs)
ifp = if_lookup_by_name_all_vrf(bs->key.ifname);
if (ifp == NULL) {
zlog_err(
- "session-enable: specified interface doesn't exists.");
+ "session-enable: specified interface %s (VRF %s) doesn't exist.",
+ bs->key.ifname, vrf ? vrf->name : "<all>");
return 0;
}
if (bs->key.ifname[0] && !vrf) {
vrf = vrf_lookup_by_id(ifp->vrf_id);
if (vrf == NULL) {
zlog_err(
- "session-enable: specified VRF doesn't exists.");
+ "session-enable: specified VRF %u doesn't exist.",
+ ifp->vrf_id);
return 0;
}
}
diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c
index 0a71c18a42..076318e6ca 100644
--- a/bfdd/bfd_packet.c
+++ b/bfdd/bfd_packet.c
@@ -543,6 +543,7 @@ int bfd_recv_cb(struct thread *t)
ifindex_t ifindex = IFINDEX_INTERNAL;
struct sockaddr_any local, peer;
uint8_t msgbuf[1516];
+ struct interface *ifp = NULL;
struct bfd_vrf_global *bvrf = THREAD_ARG(t);
vrfid = bvrf->vrf->vrf_id;
@@ -572,6 +573,15 @@ int bfd_recv_cb(struct thread *t)
&local, &peer);
}
+ /* update vrf-id because when in vrf-lite mode,
+ * the socket is on default namespace
+ */
+ if (ifindex) {
+ ifp = if_lookup_by_index(ifindex, vrfid);
+ if (ifp)
+ vrfid = ifp->vrf_id;
+ }
+
/* Implement RFC 5880 6.8.6 */
if (mlen < BFD_PKT_LEN) {
cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
@@ -951,8 +961,9 @@ int bp_peer_socket(const struct bfd_session *bs)
if (bs->key.ifname[0])
device_to_bind = (const char *)bs->key.ifname;
- else if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
- && bs->key.vrfname[0])
+ else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
+ || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
+ && bs->key.vrfname[0])))
device_to_bind = (const char *)bs->key.vrfname;
frr_with_privs(&bglobal.bfdd_privs) {
@@ -1018,8 +1029,9 @@ int bp_peer_socketv6(const struct bfd_session *bs)
if (bs->key.ifname[0])
device_to_bind = (const char *)bs->key.ifname;
- else if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
- && bs->key.vrfname[0])
+ else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
+ || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
+ && bs->key.vrfname[0])))
device_to_bind = (const char *)bs->key.vrfname;
frr_with_privs(&bglobal.bfdd_privs) {
diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c
index 44519c47b5..57fb81aa27 100644
--- a/bfdd/ptm_adapter.c
+++ b/bfdd/ptm_adapter.c
@@ -669,17 +669,24 @@ static void bfdd_sessions_enable_interface(struct interface *ifp)
struct bfd_session *bs;
struct vrf *vrf;
+ vrf = vrf_lookup_by_id(ifp->vrf_id);
+ if (!vrf)
+ return;
+
TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
bs = bso->bso_bs;
- /* Interface name mismatch. */
- if (strcmp(ifp->name, bs->key.ifname))
- continue;
- vrf = vrf_lookup_by_id(ifp->vrf_id);
- if (!vrf)
- continue;
+ /* check vrf name */
if (bs->key.vrfname[0] &&
strcmp(vrf->name, bs->key.vrfname))
continue;
+
+ /* If Interface matches vrfname, then bypass iface check */
+ if (vrf_is_backend_netns() || strcmp(ifp->name, vrf->name)) {
+ /* Interface name mismatch. */
+ if (strcmp(ifp->name, bs->key.ifname))
+ continue;
+ }
+
/* Skip enabled sessions. */
if (bs->sock != -1)
continue;
@@ -759,7 +766,8 @@ void bfdd_sessions_disable_vrf(struct vrf *vrf)
static int bfd_ifp_destroy(struct interface *ifp)
{
if (bglobal.debug_zebra)
- zlog_debug("zclient: delete interface %s", ifp->name);
+ zlog_debug("zclient: delete interface %s (VRF %u)", ifp->name,
+ ifp->vrf_id);
bfdd_sessions_disable_interface(ifp);
@@ -812,10 +820,10 @@ static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS)
return 0;
if (bglobal.debug_zebra)
- zlog_debug("zclient: %s local address %pFX",
+ zlog_debug("zclient: %s local address %pFX (VRF %u)",
cmd == ZEBRA_INTERFACE_ADDRESS_ADD ? "add"
: "delete",
- ifc->address);
+ ifc->address, vrf_id);
if (cmd == ZEBRA_INTERFACE_ADDRESS_ADD)
bfdd_sessions_enable_address(ifc);
@@ -828,8 +836,8 @@ static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS)
static int bfd_ifp_create(struct interface *ifp)
{
if (bglobal.debug_zebra)
- zlog_debug("zclient: add interface %s", ifp->name);
-
+ zlog_debug("zclient: add interface %s (VRF %u)", ifp->name,
+ ifp->vrf_id);
bfdd_sessions_enable_interface(ifp);
return 0;
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index ce22e8404d..c25d0e269a 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -3395,7 +3395,8 @@ void bgp_attr_extcom_tunnel_type(struct attr *attr,
bgp_encap_types *tunnel_type)
{
struct ecommunity *ecom;
- int i;
+ uint32_t i;
+
if (!attr)
return;
@@ -4021,7 +4022,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
uint8_t *pnt;
int tbit;
int ecom_tr_size = 0;
- int i;
+ uint32_t i;
for (i = 0; i < attr->ecommunity->size; i++) {
pnt = attr->ecommunity->val + (i * 8);
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c
index 7cc9ecd79e..1df646c346 100644
--- a/bgpd/bgp_attr_evpn.c
+++ b/bgpd/bgp_attr_evpn.c
@@ -89,7 +89,7 @@ char *ecom_mac2str(char *ecom_mac)
/* Fetch router-mac from extended community */
bool bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac)
{
- int i = 0;
+ uint32_t i = 0;
struct ecommunity *ecom;
ecom = attr->ecommunity;
@@ -122,7 +122,7 @@ bool bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac)
uint8_t bgp_attr_default_gw(struct attr *attr)
{
struct ecommunity *ecom;
- int i;
+ uint32_t i;
ecom = attr->ecommunity;
if (!ecom || !ecom->size)
@@ -153,7 +153,7 @@ uint8_t bgp_attr_default_gw(struct attr *attr)
uint16_t bgp_attr_df_pref_from_ec(struct attr *attr, uint8_t *alg)
{
struct ecommunity *ecom;
- int i;
+ uint32_t i;
uint16_t df_pref = 0;
*alg = EVPN_MH_DF_ALG_SERVICE_CARVING;
@@ -190,7 +190,7 @@ uint16_t bgp_attr_df_pref_from_ec(struct attr *attr, uint8_t *alg)
uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, uint8_t *sticky)
{
struct ecommunity *ecom;
- int i;
+ uint32_t i;
uint8_t flags = 0;
ecom = attr->ecommunity;
@@ -237,7 +237,7 @@ void bgp_attr_evpn_na_flag(struct attr *attr,
uint8_t *router_flag, bool *proxy)
{
struct ecommunity *ecom;
- int i;
+ uint32_t i;
uint8_t val;
ecom = attr->ecommunity;
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 43bfb3e2bc..c358d4203e 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -95,7 +95,7 @@ static bool ecommunity_add_val_internal(struct ecommunity *ecom,
bool unique, bool overwrite,
uint8_t ecom_size)
{
- int c, ins_idx;
+ uint32_t c, ins_idx;
const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval;
const struct ecommunity_val_ipv6 *eval6 =
(struct ecommunity_val_ipv6 *)eval;
@@ -113,7 +113,7 @@ static bool ecommunity_add_val_internal(struct ecommunity *ecom,
/* check also if the extended community itself exists. */
c = 0;
- ins_idx = -1;
+ ins_idx = UINT32_MAX;
for (uint8_t *p = ecom->val; c < ecom->size;
p += ecom_size, c++) {
if (unique) {
@@ -145,12 +145,12 @@ static bool ecommunity_add_val_internal(struct ecommunity *ecom,
if (ret > 0) {
if (!unique)
break;
- if (ins_idx == -1)
+ if (ins_idx == UINT32_MAX)
ins_idx = c;
}
}
- if (ins_idx == -1)
+ if (ins_idx == UINT32_MAX)
ins_idx = c;
/* Add the value to the structure with numerical sorting. */
@@ -193,7 +193,7 @@ static struct ecommunity *
ecommunity_uniq_sort_internal(struct ecommunity *ecom,
unsigned short ecom_size)
{
- int i;
+ uint32_t i;
struct ecommunity *new;
const void *eval;
@@ -895,7 +895,7 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt)
*/
char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
{
- int i;
+ uint32_t i;
uint8_t *pnt;
uint8_t type = 0;
uint8_t sub_type = 0;
@@ -1176,8 +1176,8 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
bool ecommunity_match(const struct ecommunity *ecom1,
const struct ecommunity *ecom2)
{
- int i = 0;
- int j = 0;
+ uint32_t i = 0;
+ uint32_t j = 0;
if (ecom1 == NULL && ecom2 == NULL)
return true;
@@ -1209,7 +1209,7 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
uint8_t type, uint8_t subtype)
{
uint8_t *p;
- int c;
+ uint32_t c;
/* If the value already exists in the structure return 0. */
c = 0;
@@ -1230,7 +1230,7 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
uint8_t subtype)
{
uint8_t *p, *q, *new;
- int c, found = 0;
+ uint32_t c, found = 0;
/* When this is fist value, just add it. */
if (ecom == NULL || ecom->val == NULL)
return false;
@@ -1278,7 +1278,7 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
{
uint8_t *p;
- int c, found = 0;
+ uint32_t c, found = 0;
/* Make sure specified value exists. */
if (ecom == NULL || ecom->val == NULL)
@@ -1512,7 +1512,7 @@ void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
{
const uint8_t *eval;
- int i;
+ uint32_t i;
if (bw)
*bw = 0;
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 6318e7edb1..6d0275a0c3 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -114,7 +114,7 @@ struct ecommunity {
uint8_t unit_size;
/* Size of Extended Communities attribute. */
- int size;
+ uint32_t size;
/* Extended Communities value. */
uint8_t *val;
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 96f4b0aa78..88747b14b1 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -870,7 +870,7 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr)
struct ecommunity ecom_tmp;
struct ecommunity_val eval;
uint8_t *ecom_val_ptr;
- int i;
+ uint32_t i;
uint8_t *pnt;
int type = 0;
int sub_type = 0;
@@ -2710,7 +2710,7 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf,
{
struct attr *attr = pi->attr;
struct ecommunity *ecom;
- int i;
+ uint32_t i;
assert(attr);
/* Route should have valid RT to be even considered. */
@@ -2777,7 +2777,7 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
{
struct attr *attr = pi->attr;
struct ecommunity *ecom;
- int i;
+ uint32_t i;
assert(attr);
/* Route should have valid RT to be even considered. */
@@ -3260,7 +3260,7 @@ static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi,
struct prefix_evpn *evp = (struct prefix_evpn *)p;
struct attr *attr = pi->attr;
struct ecommunity *ecom;
- int i;
+ uint32_t i;
struct prefix_evpn ad_evp;
assert(attr);
@@ -4906,7 +4906,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
*/
void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)
{
- int i = 0;
+ uint32_t i = 0;
struct ecommunity_val *eval = NULL;
struct listnode *node = NULL, *nnode = NULL;
struct ecommunity *ecom = NULL;
@@ -4926,7 +4926,7 @@ void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)
*/
void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf)
{
- int i;
+ uint32_t i;
struct ecommunity_val *eval;
struct listnode *node, *nnode;
struct ecommunity *ecom;
@@ -4963,7 +4963,7 @@ void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf)
*/
void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
{
- int i;
+ uint32_t i;
struct ecommunity_val *eval;
struct listnode *node, *nnode;
struct ecommunity *ecom;
@@ -4983,7 +4983,7 @@ void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
*/
void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
{
- int i;
+ uint32_t i;
struct ecommunity_val *eval;
struct listnode *node, *nnode;
struct ecommunity *ecom;
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 287555b1fc..3cb3d06217 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -60,6 +60,7 @@
#include "bgpd/bgp_keepalives.h"
#include "bgpd/bgp_network.h"
#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_script.h"
#include "lib/routing_nb.h"
#include "bgpd/bgp_nb.h"
#include "bgpd/bgp_evpn_mh.h"
@@ -510,6 +511,10 @@ int main(int argc, char **argv)
/* Initializations. */
bgp_vrf_init();
+#ifdef HAVE_SCRIPTING
+ bgp_script_init();
+#endif
+
hook_register(routing_conf_event,
routing_control_plane_protocols_name_validate);
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 3bc4c03233..1d66d75288 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -419,8 +419,7 @@ int vpn_leak_label_callback(
static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
{
- int i;
- int j;
+ uint32_t i, j;
if (!e1 || !e2)
return false;
diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c
index a3f1eb8401..4f22f5bcfe 100644
--- a/bgpd/bgp_pbr.c
+++ b/bgpd/bgp_pbr.c
@@ -754,7 +754,7 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
struct bgp_pbr_entry_main *api)
{
int ret;
- int i, action_count = 0;
+ uint32_t i, action_count = 0;
struct ecommunity *ecom;
struct ecommunity_val *ecom_eval;
struct bgp_pbr_entry_action *api_action;
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 0f4f26e3ee..ceaf8c0963 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -65,6 +65,7 @@
#include "bgpd/bgp_flowspec_util.h"
#include "bgpd/bgp_encap_types.h"
#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_script.h"
#ifdef ENABLE_BGP_VNC
#include "bgpd/rfapi/bgp_rfapi_cfg.h"
@@ -337,99 +338,138 @@ static const struct route_map_rule_cmd route_match_peer_cmd = {
route_match_peer_free
};
-#if defined(HAVE_LUA)
-static enum route_map_cmd_result_t
-route_match_command(void *rule, const struct prefix *prefix, void *object)
-{
- int status = RMAP_NOMATCH;
- u_int32_t locpref = 0;
- u_int32_t newlocpref = 0;
- enum lua_rm_status lrm_status;
- struct bgp_path_info *path = (struct bgp_path_info *)object;
- lua_State *L = lua_initialize("/etc/frr/lua.scr");
-
- if (L == NULL)
- return status;
+#ifdef HAVE_SCRIPTING
+enum frrlua_rm_status {
/*
- * Setup the prefix information to pass in
+ * Script function run failure. This will translate into a deny
*/
- lua_setup_prefix_table(L, prefix);
-
- zlog_debug("Set up prefix table");
+ LUA_RM_FAILURE = 0,
/*
- * Setup the bgp_path_info information
+ * No Match was found for the route map function
*/
- lua_newtable(L);
- lua_pushinteger(L, path->attr->med);
- lua_setfield(L, -2, "metric");
- lua_pushinteger(L, path->attr->nh_ifindex);
- lua_setfield(L, -2, "ifindex");
- lua_pushstring(L, path->attr->aspath->str);
- lua_setfield(L, -2, "aspath");
- lua_pushinteger(L, path->attr->local_pref);
- lua_setfield(L, -2, "localpref");
- zlog_debug("%s %d", path->attr->aspath->str, path->attr->nh_ifindex);
- lua_setglobal(L, "nexthop");
-
- zlog_debug("Set up nexthop information");
+ LUA_RM_NOMATCH,
/*
- * Run the rule
+ * Match was found but no changes were made to the incoming data.
*/
- lrm_status = lua_run_rm_rule(L, rule);
- switch (lrm_status) {
+ LUA_RM_MATCH,
+ /*
+ * Match was found and data was modified, so figure out what changed
+ */
+ LUA_RM_MATCH_AND_CHANGE,
+};
+
+static enum route_map_cmd_result_t
+route_match_script(void *rule, const struct prefix *prefix, void *object)
+{
+ const char *scriptname = rule;
+ struct bgp_path_info *path = (struct bgp_path_info *)object;
+
+ struct frrscript *fs = frrscript_load(scriptname, NULL);
+
+ if (!fs) {
+ zlog_err("Issue loading script rule; defaulting to no match");
+ return RMAP_NOMATCH;
+ }
+
+ enum frrlua_rm_status status_failure = LUA_RM_FAILURE,
+ status_nomatch = LUA_RM_NOMATCH,
+ status_match = LUA_RM_MATCH,
+ status_match_and_change = LUA_RM_MATCH_AND_CHANGE;
+
+ /* Make result values available */
+ struct frrscript_env env[] = {
+ {"integer", "RM_FAILURE", &status_failure},
+ {"integer", "RM_NOMATCH", &status_nomatch},
+ {"integer", "RM_MATCH", &status_match},
+ {"integer", "RM_MATCH_AND_CHANGE", &status_match_and_change},
+ {"integer", "action", &status_failure},
+ {"prefix", "prefix", prefix},
+ {"attr", "attributes", path->attr},
+ {"peer", "peer", path->peer},
+ {}};
+
+ struct frrscript_env results[] = {
+ {"integer", "action"},
+ {"attr", "attributes"},
+ {},
+ };
+
+ int result = frrscript_call(fs, env);
+
+ if (result) {
+ zlog_err("Issue running script rule; defaulting to no match");
+ return RMAP_NOMATCH;
+ }
+
+ enum frrlua_rm_status *lrm_status =
+ frrscript_get_result(fs, &results[0]);
+
+ int status = RMAP_NOMATCH;
+
+ switch (*lrm_status) {
case LUA_RM_FAILURE:
- zlog_debug("RM_FAILURE");
+ zlog_err(
+ "Executing route-map match script '%s' failed; defaulting to no match",
+ scriptname);
+ status = RMAP_NOMATCH;
break;
case LUA_RM_NOMATCH:
- zlog_debug("RM_NOMATCH");
+ status = RMAP_NOMATCH;
break;
case LUA_RM_MATCH_AND_CHANGE:
- zlog_debug("MATCH AND CHANGE");
- lua_getglobal(L, "nexthop");
- path->attr->med = get_integer(L, "metric");
- /*
- * This needs to be abstraced with the set function
- */
+ status = RMAP_MATCH;
+ zlog_debug("Updating attribute based on script's values");
+
+ uint32_t locpref = 0;
+ struct attr *newattr = frrscript_get_result(fs, &results[1]);
+
+ path->attr->med = newattr->med;
+
if (path->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
locpref = path->attr->local_pref;
- newlocpref = get_integer(L, "localpref");
- if (newlocpref != locpref) {
- path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
- path->attr->local_pref = newlocpref;
+ if (locpref != newattr->local_pref) {
+ SET_FLAG(path->attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF));
+ path->attr->local_pref = newattr->local_pref;
}
- status = RMAP_MATCH;
+
+ aspath_free(newattr->aspath);
+ XFREE(MTYPE_TMP, newattr);
break;
case LUA_RM_MATCH:
- zlog_debug("MATCH ONLY");
status = RMAP_MATCH;
break;
}
- lua_close(L);
+
+ XFREE(MTYPE_TMP, lrm_status);
+ frrscript_unload(fs);
+
return status;
}
-static void *route_match_command_compile(const char *arg)
+static void *route_match_script_compile(const char *arg)
{
- char *command;
+ char *scriptname;
+
+ scriptname = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
- command = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
- return command;
+ return scriptname;
}
-static void
-route_match_command_free(void *rule)
+static void route_match_script_free(void *rule)
{
XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
}
-static const struct route_map_rule_cmd route_match_command_cmd = {
- "command",
- route_match_command,
- route_match_command_compile,
- route_match_command_free
+static const struct route_map_rule_cmd route_match_script_cmd = {
+ "script",
+ route_match_script,
+ route_match_script_compile,
+ route_match_script_free
};
-#endif
+
+#endif /* HAVE_SCRIPTING */
/* `match ip address IP_ACCESS_LIST' */
@@ -4096,30 +4136,29 @@ DEFUN (no_match_peer,
RMAP_EVENT_MATCH_DELETED);
}
-#if defined(HAVE_LUA)
-DEFUN (match_command,
- match_command_cmd,
- "match command WORD",
- MATCH_STR
- "Run a command to match\n"
- "The command to run\n")
-{
- return bgp_route_match_add(vty, "command", argv[2]->arg,
- RMAP_EVENT_FILTER_ADDED);
-}
-
-DEFUN (no_match_command,
- no_match_command_cmd,
- "no match command WORD",
+#ifdef HAVE_SCRIPTING
+DEFUN (match_script,
+ match_script_cmd,
+ "[no] match script WORD",
NO_STR
MATCH_STR
- "Run a command to match\n"
- "The command to run\n")
+ "Execute script to determine match\n"
+ "The script name to run, without .lua; e.g. 'myroutemap' to run myroutemap.lua\n")
{
- return bgp_route_match_delete(vty, "command", argv[3]->arg,
- RMAP_EVENT_FILTER_DELETED);
+ bool no = strmatch(argv[0]->text, "no");
+ int i = 0;
+ argv_find(argv, argc, "WORD", &i);
+ const char *script = argv[i]->arg;
+
+ if (no) {
+ return bgp_route_match_delete(vty, "script", script,
+ RMAP_EVENT_FILTER_DELETED);
+ } else {
+ return bgp_route_match_add(vty, "script", script,
+ RMAP_EVENT_FILTER_ADDED);
+ }
}
-#endif
+#endif /* HAVE_SCRIPTING */
/* match probability */
DEFUN (match_probability,
@@ -5633,8 +5672,8 @@ void bgp_route_map_init(void)
route_map_install_match(&route_match_peer_cmd);
route_map_install_match(&route_match_local_pref_cmd);
-#if defined(HAVE_LUA)
- route_map_install_match(&route_match_command_cmd);
+#ifdef HAVE_SCRIPTING
+ route_map_install_match(&route_match_script_cmd);
#endif
route_map_install_match(&route_match_ip_address_cmd);
route_map_install_match(&route_match_ip_next_hop_cmd);
@@ -5798,9 +5837,8 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &no_set_ipv6_nexthop_prefer_global_cmd);
install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd);
install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd);
-#if defined(HAVE_LUA)
- install_element(RMAP_NODE, &match_command_cmd);
- install_element(RMAP_NODE, &no_match_command_cmd);
+#ifdef HAVE_SCRIPTING
+ install_element(RMAP_NODE, &match_script_cmd);
#endif
}
diff --git a/bgpd/bgp_script.c b/bgpd/bgp_script.c
new file mode 100644
index 0000000000..0cda1927f8
--- /dev/null
+++ b/bgpd/bgp_script.c
@@ -0,0 +1,192 @@
+/* BGP scripting foo
+ * Copyright (C) 2020 NVIDIA Corporation
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_SCRIPTING
+
+#include "bgpd.h"
+#include "bgp_script.h"
+#include "bgp_debug.h"
+#include "bgp_aspath.h"
+#include "frratomic.h"
+#include "frrscript.h"
+#include "frrlua.h"
+
+static void lua_pushpeer(lua_State *L, const struct peer *peer)
+{
+ lua_newtable(L);
+ lua_pushinteger(L, peer->as);
+ lua_setfield(L, -2, "remote_as");
+ lua_pushinteger(L, peer->local_as);
+ lua_setfield(L, -2, "local_as");
+ lua_pushinaddr(L, &peer->remote_id);
+ lua_setfield(L, -2, "remote_id");
+ lua_pushinaddr(L, &peer->local_id);
+ lua_setfield(L, -2, "local_id");
+ lua_pushstring(L, lookup_msg(bgp_status_msg, peer->status, NULL));
+ lua_setfield(L, -2, "state");
+ lua_pushstring(L, peer->desc ? peer->desc : "");
+ lua_setfield(L, -2, "description");
+ lua_pushtimet(L, &peer->uptime);
+ lua_setfield(L, -2, "uptime");
+ lua_pushtimet(L, &peer->readtime);
+ lua_setfield(L, -2, "last_readtime");
+ lua_pushtimet(L, &peer->resettime);
+ lua_setfield(L, -2, "last_resettime");
+ lua_pushsockunion(L, peer->su_local);
+ lua_setfield(L, -2, "local_address");
+ lua_pushsockunion(L, peer->su_remote);
+ lua_setfield(L, -2, "remote_address");
+ lua_pushinteger(L, peer->cap);
+ lua_setfield(L, -2, "capabilities");
+ lua_pushinteger(L, peer->flags);
+ lua_setfield(L, -2, "flags");
+ lua_pushstring(L, peer->password ? peer->password : "");
+ lua_setfield(L, -2, "password");
+
+ /* Nested tables here */
+ lua_newtable(L);
+ {
+ lua_newtable(L);
+ {
+ lua_pushinteger(L, peer->holdtime);
+ lua_setfield(L, -2, "hold");
+ lua_pushinteger(L, peer->keepalive);
+ lua_setfield(L, -2, "keepalive");
+ lua_pushinteger(L, peer->connect);
+ lua_setfield(L, -2, "connect");
+ lua_pushinteger(L, peer->routeadv);
+ lua_setfield(L, -2, "route_advertisement");
+ }
+ lua_setfield(L, -2, "configured");
+
+ lua_newtable(L);
+ {
+ lua_pushinteger(L, peer->v_holdtime);
+ lua_setfield(L, -2, "hold");
+ lua_pushinteger(L, peer->v_keepalive);
+ lua_setfield(L, -2, "keepalive");
+ lua_pushinteger(L, peer->v_connect);
+ lua_setfield(L, -2, "connect");
+ lua_pushinteger(L, peer->v_routeadv);
+ lua_setfield(L, -2, "route_advertisement");
+ }
+ lua_setfield(L, -2, "negotiated");
+ }
+ lua_setfield(L, -2, "timers");
+
+ lua_newtable(L);
+ {
+ lua_pushinteger(L, atomic_load_explicit(&peer->open_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "open_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->open_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "open_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->update_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "update_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->update_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "update_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->update_time,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "update_time");
+ lua_pushinteger(L, atomic_load_explicit(&peer->keepalive_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "keepalive_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->keepalive_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "keepalive_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->notify_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "notify_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->notify_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "notify_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->refresh_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "refresh_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->refresh_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "refresh_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->dynamic_cap_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "dynamic_cap_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->dynamic_cap_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "dynamic_cap_out");
+ lua_pushinteger(L, peer->established);
+ lua_setfield(L, -2, "times_established");
+ lua_pushinteger(L, peer->dropped);
+ lua_setfield(L, -2, "times_dropped");
+ }
+ lua_setfield(L, -2, "stats");
+}
+
+static void lua_pushattr(lua_State *L, const struct attr *attr)
+{
+ lua_newtable(L);
+ lua_pushinteger(L, attr->med);
+ lua_setfield(L, -2, "metric");
+ lua_pushinteger(L, attr->nh_ifindex);
+ lua_setfield(L, -2, "ifindex");
+ lua_pushstring(L, attr->aspath->str);
+ lua_setfield(L, -2, "aspath");
+ lua_pushinteger(L, attr->local_pref);
+ lua_setfield(L, -2, "localpref");
+}
+
+static void *lua_toattr(lua_State *L, int idx)
+{
+ struct attr *attr = XCALLOC(MTYPE_TMP, sizeof(struct attr));
+
+ lua_getfield(L, -1, "metric");
+ attr->med = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, -1, "ifindex");
+ attr->nh_ifindex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, -1, "aspath");
+ attr->aspath = aspath_str2aspath(lua_tostring(L, -1));
+ lua_pop(L, 1);
+ lua_getfield(L, -1, "localpref");
+ attr->local_pref = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ return attr;
+}
+
+struct frrscript_codec frrscript_codecs_bgpd[] = {
+ {.typename = "peer",
+ .encoder = (encoder_func)lua_pushpeer,
+ .decoder = NULL},
+ {.typename = "attr",
+ .encoder = (encoder_func)lua_pushattr,
+ .decoder = lua_toattr},
+ {}};
+
+void bgp_script_init(void)
+{
+ frrscript_register_type_codecs(frrscript_codecs_bgpd);
+}
+
+#endif /* HAVE_SCRIPTING */
diff --git a/bgpd/bgp_script.h b/bgpd/bgp_script.h
new file mode 100644
index 0000000000..6682c2eebd
--- /dev/null
+++ b/bgpd/bgp_script.h
@@ -0,0 +1,34 @@
+/* BGP scripting foo
+ * Copyright (C) 2020 NVIDIA Corporation
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#ifndef __BGP_SCRIPT__
+#define __BGP_SCRIPT__
+
+#include <zebra.h>
+
+#ifdef HAVE_SCRIPTING
+
+/*
+ * Initialize scripting stuff.
+ */
+void bgp_script_init(void);
+
+#endif /* HAVE_SCRIPTING */
+
+#endif /* __BGP_SCRIPT__ */
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index b1ff9ac251..30babb7b76 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -518,10 +518,7 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
/* bgp_path_info adj_out reference */
adv->pathi = bgp_path_info_lock(path);
- if (attr)
- adv->baa = bgp_advertise_intern(subgrp->hash, attr);
- else
- adv->baa = baa_new();
+ adv->baa = bgp_advertise_intern(subgrp->hash, attr);
adv->adj = adj;
adj->attr_hash = attr_hash;
diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c
index 3d87b63542..b2732a40b4 100644
--- a/bgpd/rfapi/rfapi_import.c
+++ b/bgpd/rfapi/rfapi_import.c
@@ -984,7 +984,7 @@ static int rfapiEcommunitiesMatchBeec(struct ecommunity *ecom,
int rfapiEcommunitiesIntersect(struct ecommunity *e1, struct ecommunity *e2)
{
- int i, j;
+ uint32_t i, j;
if (!e1 || !e2)
return 0;
@@ -1014,7 +1014,8 @@ int rfapiEcommunitiesIntersect(struct ecommunity *e1, struct ecommunity *e2)
int rfapiEcommunityGetLNI(struct ecommunity *ecom, uint32_t *lni)
{
if (ecom) {
- int i;
+ uint32_t i;
+
for (i = 0; i < ecom->size; ++i) {
uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE);
@@ -1034,7 +1035,8 @@ int rfapiEcommunityGetEthernetTag(struct ecommunity *ecom, uint16_t *tag_id)
struct bgp *bgp = bgp_get_default();
*tag_id = 0; /* default to untagged */
if (ecom) {
- int i;
+ uint32_t i;
+
for (i = 0; i < ecom->size; ++i) {
as_t as = 0;
int encode = 0;
diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c
index 762cd2596f..bc29f05aeb 100644
--- a/bgpd/rfapi/vnc_export_bgp.c
+++ b/bgpd/rfapi/vnc_export_bgp.c
@@ -134,7 +134,7 @@ static void encap_attr_export_ce(struct attr *new, struct attr *orig,
static int getce(struct bgp *bgp, struct attr *attr, struct prefix *pfx_ce)
{
uint8_t *ecp;
- int i;
+ uint32_t i;
uint16_t localadmin = bgp->rfapi_cfg->resolve_nve_roo_local_admin;
for (ecp = attr->ecommunity->val, i = 0; i < attr->ecommunity->size;
diff --git a/bgpd/subdir.am b/bgpd/subdir.am
index ac84f4b9e4..df1555c32a 100644
--- a/bgpd/subdir.am
+++ b/bgpd/subdir.am
@@ -96,6 +96,7 @@ bgpd_libbgp_a_SOURCES = \
bgpd/bgp_regex.c \
bgpd/bgp_route.c \
bgpd/bgp_routemap.c \
+ bgpd/bgp_script.c \
bgpd/bgp_table.c \
bgpd/bgp_updgrp.c \
bgpd/bgp_updgrp_adv.c \
@@ -175,6 +176,7 @@ noinst_HEADERS += \
bgpd/bgp_rd.h \
bgpd/bgp_regex.h \
bgpd/bgp_route.h \
+ bgpd/bgp_script.h \
bgpd/bgp_table.h \
bgpd/bgp_updgrp.h \
bgpd/bgp_vpn.h \
diff --git a/configure.ac b/configure.ac
index 495019ee14..8f9517763d 100755
--- a/configure.ac
+++ b/configure.ac
@@ -138,6 +138,12 @@ AC_ARG_WITH([moduledir], [AS_HELP_STRING([--with-moduledir=DIR], [module directo
])
AC_SUBST([moduledir], [$moduledir])
+AC_ARG_WITH([scriptdir], [AS_HELP_STRING([--with-scriptdir=DIR], [script directory (${sysconfdir}/scripts)])], [
+ scriptdir="$withval"
+], [
+ scriptdir="\${sysconfdir}/scripts"
+])
+AC_SUBST([scriptdir], [$scriptdir])
AC_ARG_WITH([yangmodelsdir], [AS_HELP_STRING([--with-yangmodelsdir=DIR], [yang models directory (${datarootdir}/yang)])], [
yangmodelsdir="$withval"
@@ -274,24 +280,22 @@ if test "$enable_clang_coverage" = "yes"; then
])
fi
+if test "$enable_scripting" = "yes"; then
+ AX_PROG_LUA([5.3])
+ AX_LUA_HEADERS
+ AX_LUA_LIBS([
+ AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting])
+ LIBS="$LIBS $LUA_LIB"
+ ])
+fi
+
if test "$enable_dev_build" = "yes"; then
AC_DEFINE([DEV_BUILD], [1], [Build for development])
if test "$orig_cflags" = ""; then
AC_C_FLAG([-g3])
AC_C_FLAG([-O0])
fi
- if test "$enable_lua" = "yes"; then
- AX_PROG_LUA([5.3])
- AX_LUA_HEADERS
- AX_LUA_LIBS([
- AC_DEFINE([HAVE_LUA], [1], [Have support for Lua interpreter])
- LIBS="$LIBS $LUA_LIB"
- ])
- fi
else
- if test "$enable_lua" = "yes"; then
- AC_MSG_ERROR([Lua is not meant to be built/used outside of development at this time])
- fi
if test "$orig_cflags" = ""; then
AC_C_FLAG([-g])
AC_C_FLAG([-O2])
@@ -697,8 +701,8 @@ fi
AC_ARG_ENABLE([dev_build],
AS_HELP_STRING([--enable-dev-build], [build for development]))
-AC_ARG_ENABLE([lua],
- AS_HELP_STRING([--enable-lua], [Build Lua scripting]))
+AC_ARG_ENABLE([scripting],
+ AS_HELP_STRING([--enable-scripting], [Build with scripting support]))
AC_ARG_ENABLE([netlink-debug],
AS_HELP_STRING([--disable-netlink-debug], [pretty print netlink debug messages]))
@@ -2446,19 +2450,23 @@ CFG_SBIN="$sbindir"
CFG_STATE="$frr_statedir"
CFG_MODULE="$moduledir"
CFG_YANGMODELS="$yangmodelsdir"
+CFG_SCRIPT="$scriptdir"
for I in 1 2 3 4 5 6 7 8 9 10; do
eval CFG_SYSCONF="\"$CFG_SYSCONF\""
eval CFG_SBIN="\"$CFG_SBIN\""
eval CFG_STATE="\"$CFG_STATE\""
eval CFG_MODULE="\"$CFG_MODULE\""
eval CFG_YANGMODELS="\"$CFG_YANGMODELS\""
+ eval CFG_SCRIPT="\"$CFG_SCRIPT\""
done
AC_SUBST([CFG_SYSCONF])
AC_SUBST([CFG_SBIN])
AC_SUBST([CFG_STATE])
AC_SUBST([CFG_MODULE])
+AC_SUBST([CFG_SCRIPT])
AC_SUBST([CFG_YANGMODELS])
AC_DEFINE_UNQUOTED([MODULE_PATH], ["$CFG_MODULE"], [path to modules])
+AC_DEFINE_UNQUOTED([SCRIPT_PATH], ["$CFG_SCRIPT"], [path to scripts])
AC_DEFINE_UNQUOTED([YANG_MODELS_PATH], ["$CFG_YANGMODELS"], [path to YANG data models])
AC_DEFINE_UNQUOTED([WATCHFRR_SH_PATH], ["${CFG_SBIN%/}/watchfrr.sh"], [path to watchfrr.sh])
@@ -2582,6 +2590,7 @@ state file directory : ${frr_statedir}
config file directory : `eval echo \`echo ${sysconfdir}\``
example directory : `eval echo \`echo ${exampledir}\``
module directory : ${CFG_MODULE}
+script directory : ${CFG_SCRIPT}
user to run as : ${enable_user}
group to run as : ${enable_group}
group for vty sockets : ${enable_vty_group}
diff --git a/debian/control b/debian/control
index 4aaa9f21bf..b9e96b55d0 100644
--- a/debian/control
+++ b/debian/control
@@ -29,7 +29,8 @@ Build-Depends: bison,
python3-dev,
python3-pytest <!nocheck>,
python3-sphinx,
- texinfo (>= 4.7)
+ texinfo (>= 4.7),
+ liblua5.3-dev <pkg.frr.lua>
Standards-Version: 4.5.0.3
Homepage: https://www.frrouting.org/
Vcs-Browser: https://github.com/FRRouting/frr/tree/debian/master
diff --git a/debian/rules b/debian/rules
index 6cc03c378a..25ae04261d 100755
--- a/debian/rules
+++ b/debian/rules
@@ -29,6 +29,12 @@ else
CONF_SYSTEMD=--enable-systemd=no
endif
+ifeq ($(filter pkg.frr.lua,$(DEB_BUILD_PROFILES)),)
+ CONF_LUA=--disable-scripting
+else
+ CONF_LUA=--enable-scripting
+endif
+
export PYTHON=python3
%:
@@ -49,6 +55,7 @@ override_dh_auto_configure:
\
$(CONF_SYSTEMD) \
$(CONF_RPKI) \
+ $(CONF_LUA) \
--with-libpam \
--enable-doc \
--enable-doc-html \
diff --git a/doc/developer/library.rst b/doc/developer/library.rst
index 3d5c6a2a15..1bfe5df2f0 100644
--- a/doc/developer/library.rst
+++ b/doc/developer/library.rst
@@ -15,6 +15,6 @@ Library Facilities (libfrr)
hooks
cli
modules
- lua
+ scripting
diff --git a/doc/developer/lua.rst b/doc/developer/lua.rst
deleted file mode 100644
index 3315c31ad7..0000000000
--- a/doc/developer/lua.rst
+++ /dev/null
@@ -1,65 +0,0 @@
-.. _lua:
-
-Lua
-===
-
-Lua is currently experimental within FRR and has very limited
-support. If you would like to compile FRR with Lua you must
-follow these steps:
-
-1. Installation of Relevant Libraries
-
- .. code-block:: shell
-
- apt-get install lua5.3 liblua5-3 liblua5.3-dev
-
- These are the Debian libraries that are needed. There should
- be equivalent RPM's that can be found
-
-2. Compilation
-
- Configure needs these options
-
- .. code-block:: shell
-
- ./configure --enable-dev-build --enable-lua <all other interesting options>
-
- Typically you just include the two new enable lines to build with it.
-
-3. Using Lua
-
- * Copy tools/lua.scr into /etc/frr
-
- * Create a route-map match command
-
- .. code-block:: console
-
- !
- router bgp 55
- neighbor 10.50.11.116 remote-as external
- address-family ipv4 unicast
- neighbor 10.50.11.116 route-map TEST in
- exit-address-family
- !
- route-map TEST permit 10
- match command mooey
- !
-
- * In the lua.scr file make sure that you have a function named 'mooey'
-
- .. code-block:: console
-
- function mooey ()
- zlog_debug(string.format("afi: %d: %s %d ifdx: %d aspath: %s localpref: %d",
- prefix.family, prefix.route, nexthop.metric,
- nexthop.ifindex, nexthop.aspath, nexthop.localpref))
-
- nexthop.metric = 33
- nexthop.localpref = 13
- return 3
- end
-
-4. General Comments
-
- Please be aware that this is extremely experimental and needs a ton of work
- to get this up into a state that is usable.
diff --git a/doc/developer/scripting.rst b/doc/developer/scripting.rst
new file mode 100644
index 0000000000..b0413619ab
--- /dev/null
+++ b/doc/developer/scripting.rst
@@ -0,0 +1,433 @@
+.. _scripting:
+
+Scripting
+=========
+
+.. seealso:: User docs for scripting
+
+Overview
+--------
+
+FRR has the ability to call Lua scripts to perform calculations, make
+decisions, or otherwise extend builtin behavior with arbitrary user code. This
+is implemented using the standard Lua C bindings. The supported version of Lua
+is 5.3.
+
+C objects may be passed into Lua and Lua objects may be retrieved by C code via
+a marshalling system. In this way, arbitrary data from FRR may be passed to
+scripts. It is possible to pass C functions as well.
+
+The Lua environment is isolated from the C environment; user scripts cannot
+access FRR's address space unless explicitly allowed by FRR.
+
+For general information on how Lua is used to extend C, refer to Part IV of
+"Programming in Lua".
+
+https://www.lua.org/pil/contents.html#24
+
+
+Design
+------
+
+Why Lua
+^^^^^^^
+
+Lua is designed to be embedded in C applications. It is very small; the
+standard library is 220K. It is relatively fast. It has a simple, minimal
+syntax that is relatively easy to learn and can be understood by someone with
+little to no programming experience. Moreover it is widely used to add
+scripting capabilities to applications. In short it is designed for this task.
+
+Reasons against supporting multiple scripting languages:
+
+- Each language would require different FFI methods, and specifically
+ different object encoders; a lot of code
+- Languages have different capabilities that would have to be brought to
+ parity with each other; a lot of work
+- Languages have vastly different performance characteristics; this would
+ create alot of basically unfixable issues, and result in a single de facto
+ standard scripting language (the fastest)
+- Each language would need a dedicated maintainer for the above reasons;
+ this is pragmatically difficult
+- Supporting multiple languages fractures the community and limits the audience
+ with which a given script can be shared
+
+General
+^^^^^^^
+
+FRR's concept of a script is somewhat abstracted away from the fact that it is
+Lua underneath. A script in has two things:
+
+- name
+- state
+
+In code:
+
+.. code-block:: c
+
+ struct frrscript {
+ /* Script name */
+ char *name;
+
+ /* Lua state */
+ struct lua_State *L;
+ };
+
+
+``name`` is simply a string. Everything else is in ``state``, which is itself a
+Lua library object (``lua_State``). This is an opaque struct that is
+manipulated using ``lua_*`` functions. The basic ones are imported from
+``lua.h`` and the rest are implemented within FRR to fill our use cases. The
+thing to remember is that all operations beyond the initial loading the script
+take place on this opaque state object.
+
+There are four basic actions that can be done on a script:
+
+- load
+- execute
+- query state
+- unload
+
+They are typically done in this order.
+
+
+Loading
+^^^^^^^
+
+A snippet of Lua code is referred to as a "chunk". These are simply text. FRR
+presently assumes chunks are located in individual files specific to one task.
+These files are stored in the scripts directory and must end in ``.lua``.
+
+A script object is created by loading a script. This is done with
+``frrscript_load()``. This function takes the name of the script and an
+optional callback function. The string ".lua" is appended to the script name,
+and the resultant filename is looked for in the scripts directory.
+
+For example, to load ``/etc/frr/scripts/bingus.lua``:
+
+.. code-block:: c
+
+ struct frrscript *fs = frrscript_load("bingus", NULL);
+
+During loading the script is validated for syntax and its initial environment
+is setup. By default this does not include the Lua standard library; there are
+security issues to consider, though for practical purposes untrusted users
+should not be able to write the scripts directory anyway. If desired the Lua
+standard library may be added to the script environment using
+``luaL_openlibs(fs->L)`` after loading the script. Further information on
+setting up the script environment is in the Lua manual.
+
+
+Executing
+^^^^^^^^^
+
+After loading, scripts may be executed. A script may take input in the form of
+variable bindings set in its environment prior to being run, and may provide
+results by setting the value of variables. Arbitrary C values may be
+transferred into the script environment, including functions.
+
+A typical execution call looks something like this:
+
+.. code-block:: c
+
+ struct frrscript *fs = frrscript_load(...);
+
+ int status_ok = 0, status_fail = 1;
+ struct prefix p = ...;
+
+ struct frrscript_env env[] = {
+ {"integer", "STATUS_FAIL", &status_fail},
+ {"integer", "STATUS_OK", &status_ok},
+ {"prefix", "myprefix", &p},
+ {}};
+
+ int result = frrscript_call(fs, env);
+
+
+To execute a loaded script, we need to define the inputs. These inputs are
+passed by binding values to variable names that will be accessible within the
+Lua environment. Basically, all communication with the script takes place via
+global variables within the script, and to provide inputs we predefine globals
+before the script runs. This is done by passing ``frrscript_call()`` an array
+of ``struct frrscript_env``. Each struct has three fields. The first identifies
+the type of the value being passed; more on this later. The second defines the
+name of the global variable within the script environment to bind the third
+argument (the value) to.
+
+The script is then executed and returns a general status code. In the success
+case this will be 0, otherwise it will be nonzero. The script itself does not
+determine this code, it is provided by the Lua interpreter.
+
+
+Querying State
+^^^^^^^^^^^^^^
+
+When a chunk is executed, its state at exit is preserved and can be inspected.
+
+After running a script, results may be retrieved by querying the script's
+state. Again this is done by retrieving the values of global variables, which
+are known to the script author to be "output" variables.
+
+A result is retrieved like so:
+
+.. code-block:: c
+
+ struct frrscript_env myresult = {"string", "myresult"};
+
+ char *myresult = frrscript_get_result(fs, &myresult);
+
+ ... do something ...
+
+ XFREE(MTYPE_TMP, myresult);
+
+
+As with arguments, results are retrieved by providing a ``struct
+frrscript_env`` specifying a type and a global name. No value is necessary, nor
+is it modified by ``frrscript_get_result()``. That function simply extracts the
+requested value from the script state and returns it.
+
+In most cases the returned value will be allocated with ``MTYPE_TMP`` and will
+need to be freed after use.
+
+
+Unloading
+^^^^^^^^^
+
+To destroy a script and its associated state:
+
+.. code-block:: c
+
+ frrscript_unload(fs);
+
+Values returned by ``frrscript_get_result`` are still valid after the script
+they were retrieved from is unloaded.
+
+Note that you must unload and then load the script if you want to reset its
+state, for example to run it again with different inputs. Otherwise the state
+from the previous run carries over into subsequent runs.
+
+
+.. _marshalling:
+
+Marshalling
+^^^^^^^^^^^
+
+Earlier sections glossed over the meaning of the type name field in ``struct
+frrscript_env`` and how data is passed between C and Lua. Lua, as a dynamically
+typed, garbage collected language, cannot directly use C values without some
+kind of marshalling / unmarshalling system to translate types between the two
+runtimes.
+
+Lua communicates with C code using a stack. C code wishing to provide data to
+Lua scripts must provide a function that marshalls the C data into a Lua
+representation and pushes it on the stack. C code wishing to retrieve data from
+Lua must provide a corresponding unmarshalling function that retrieves a Lua
+value from the stack and converts it to the corresponding C type. These two
+functions, together with a chosen name of the type they operate on, are
+referred to as ``codecs`` in FRR.
+
+A codec is defined as:
+
+.. code-block:: c
+
+ typedef void (*encoder_func)(lua_State *, const void *);
+ typedef void *(*decoder_func)(lua_State *, int);
+
+ struct frrscript_codec {
+ const char *typename;
+ encoder_func encoder;
+ decoder_func decoder;
+ };
+
+A typename string and two function pointers.
+
+``typename`` can be anything you want. For example, for the combined types of
+``struct prefix`` and its equivalent in Lua I have chosen the name ``prefix``.
+There is no restriction on naming here, it is just a human name used as a key
+and specified when passing and retrieving values.
+
+``encoder`` is a function that takes a ``lua_State *`` and a C type and pushes
+onto the Lua stack a value representing the C type. For C structs, the usual
+case, this will typically be a Lua table (tables are the only datastructure Lua
+has). For example, here is the encoder function for ``struct prefix``:
+
+
+.. code-block:: c
+
+ void lua_pushprefix(lua_State *L, const struct prefix *prefix)
+ {
+ char buffer[PREFIX_STRLEN];
+
+ zlog_debug("frrlua: pushing prefix table");
+
+ lua_newtable(L);
+ lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN));
+ lua_setfield(L, -2, "network");
+ lua_pushinteger(L, prefix->prefixlen);
+ lua_setfield(L, -2, "length");
+ lua_pushinteger(L, prefix->family);
+ lua_setfield(L, -2, "family");
+ }
+
+This function pushes a single value onto the Lua stack. It is a table whose equivalent in Lua is:
+
+.. code-block::
+
+ { ["network"] = "1.2.3.4/24", ["prefixlen"] = 24, ["family"] = 2 }
+
+
+``decoder`` does the reverse; it takes a ``lua_State *`` and an index into the
+stack, and unmarshalls a Lua value there into the corresponding C type. Again
+for ``struct prefix``:
+
+
+.. code-block:: c
+
+ void *lua_toprefix(lua_State *L, int idx)
+ {
+ struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
+
+ lua_getfield(L, idx, "network");
+ str2prefix(lua_tostring(L, -1), p);
+ lua_pop(L, 1);
+
+ return p;
+ }
+
+By convention these functions should be called ``lua_to*``, as this is the
+naming convention used by the Lua C library for the basic types e.g.
+``lua_tointeger`` and ``lua_tostring``.
+
+The returned data must always be copied off the stack and the copy must be
+allocated with ``MTYPE_TMP``. This way it is possible to unload the script
+(destroy the state) without invalidating any references to values stored in it.
+
+To register a new type with its corresponding encoding functions:
+
+.. code-block:: c
+
+ struct frrscript_codec frrscript_codecs_lib[] = {
+ {.typename = "prefix",
+ .encoder = (encoder_func)lua_pushprefix,
+ .decoder = lua_toprefix},
+ {.typename = "sockunion",
+ .encoder = (encoder_func)lua_pushsockunion,
+ .decoder = lua_tosockunion},
+ ...
+ {}};
+
+ frrscript_register_type_codecs(frrscript_codecs_lib);
+
+From this point on the type names are available to be used when calling any
+script and getting its results.
+
+.. note::
+
+ Marshalled types are not restricted to simple values like integers, strings
+ and tables. It is possible to marshall a type such that the resultant object
+ in Lua is an actual object-oriented object, complete with methods that call
+ back into defined C functions. See the Lua manual for how to do this; for a
+ code example, look at how zlog is exported into the script environment.
+
+
+Script Environment
+------------------
+
+Logging
+^^^^^^^
+
+For convenience, script environments are populated by default with a ``log``
+object which contains methods corresponding to each of the ``zlog`` levels:
+
+.. code-block:: lua
+
+ log.info("info")
+ log.warn("warn")
+ log.error("error")
+ log.notice("notice")
+ log.debug("debug")
+
+The log messages will show up in the daemon's log output.
+
+
+Examples
+--------
+
+For a complete code example involving passing custom types, retrieving results,
+and doing complex calculations in Lua, look at the implementation of the
+``match script SCRIPT`` command for BGP routemaps. This example calls into a
+script with a route prefix and attributes received from a peer and expects the
+script to return a match / no match / match and update result.
+
+An example script to use with this follows. This script matches, does not match
+or updates a route depending on how many BGP UPDATE messages the peer has
+received when the script is called, simply as a demonstration of what can be
+accomplished with scripting.
+
+.. code-block:: lua
+
+
+ -- Example route map matching
+ -- author: qlyoung
+ --
+ -- The following variables are available to us:
+ -- log
+ -- logging library, with the usual functions
+ -- prefix
+ -- the route under consideration
+ -- attributes
+ -- the route's attributes
+ -- peer
+ -- the peer which received this route
+ -- RM_FAILURE
+ -- status code in case of failure
+ -- RM_NOMATCH
+ -- status code for no match
+ -- RM_MATCH
+ -- status code for match
+ -- RM_MATCH_AND_CHANGE
+ -- status code for match-and-set
+ --
+ -- We need to set the following out values:
+ -- action
+ -- Set to the appropriate status code to indicate what we did
+ -- attributes
+ -- Setting fields on here will propagate them back up to the caller if
+ -- 'action' is set to RM_MATCH_AND_CHANGE.
+
+
+ log.info("Evaluating route " .. prefix.network .. " from peer " .. peer.remote_id.string)
+
+ function on_match (prefix, attrs)
+ log.info("Match")
+ action = RM_MATCH
+ end
+
+ function on_nomatch (prefix, attrs)
+ log.info("No match")
+ action = RM_NOMATCH
+ end
+
+ function on_match_and_change (prefix, attrs)
+ action = RM_MATCH_AND_CHANGE
+ log.info("Match and change")
+ attrs["metric"] = attrs["metric"] + 7
+ end
+
+ special_routes = {
+ ["172.16.10.4/24"] = on_match,
+ ["172.16.13.1/8"] = on_nomatch,
+ ["192.168.0.24/8"] = on_match_and_change,
+ }
+
+
+ if special_routes[prefix.network] then
+ special_routes[prefix.network](prefix, attributes)
+ elseif peer.stats.update_in % 3 == 0 then
+ on_match(prefix, attributes)
+ elseif peer.stats.update_in % 2 == 0 then
+ on_nomatch(prefix, attributes)
+ else
+ on_match_and_change(prefix, attributes)
+ end
+
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index 0129be6bf1..07a25886d0 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -37,7 +37,6 @@ dev_RSTFILES = \
doc/developer/lists.rst \
doc/developer/locking.rst \
doc/developer/logging.rst \
- doc/developer/lua.rst \
doc/developer/memtypes.rst \
doc/developer/modules.rst \
doc/developer/next-hop-tracking.rst \
@@ -52,6 +51,7 @@ dev_RSTFILES = \
doc/developer/path-internals.rst \
doc/developer/path.rst \
doc/developer/rcu.rst \
+ doc/developer/scripting.rst \
doc/developer/static-linking.rst \
doc/developer/tracing.rst \
doc/developer/testing.rst \
diff --git a/doc/user/index.rst b/doc/user/index.rst
index 993acf3b4c..7b9464668b 100644
--- a/doc/user/index.rst
+++ b/doc/user/index.rst
@@ -29,6 +29,7 @@ Basics
ipv6
kernel
snmp
+ scripting
.. modules
#########
diff --git a/doc/user/installation.rst b/doc/user/installation.rst
index 382d71b71f..a13e6ce43b 100644
--- a/doc/user/installation.rst
+++ b/doc/user/installation.rst
@@ -362,6 +362,10 @@ options from the list below.
Set hardcoded rpaths in the executable [default=yes].
+.. option:: --enable-scripting
+
+ Enable Lua scripting [default=no].
+
You may specify any combination of the above options to the configure
script. By default, the executables are placed in :file:`/usr/local/sbin`
and the configuration files in :file:`/usr/local/etc`. The :file:`/usr/local/`
@@ -382,6 +386,10 @@ options to the configuration script.
Configure zebra to use `dir` for local state files, such as pid files and
unix sockets.
+.. option:: --with-scriptdir <dir>
+
+ Look for Lua scripts in ``dir`` [``prefix``/etc/frr/scripts].
+
.. option:: --with-yangmodelsdir <dir>
Look for YANG modules in `dir` [`prefix`/share/yang]. Note that the FRR
diff --git a/doc/user/scripting.rst b/doc/user/scripting.rst
new file mode 100644
index 0000000000..b0295e5706
--- /dev/null
+++ b/doc/user/scripting.rst
@@ -0,0 +1,28 @@
+.. _scripting:
+
+*********
+Scripting
+*********
+
+The behavior of FRR may be extended or customized using its built-in scripting
+capabilities.
+
+Some configuration commands accept the name of a Lua script to call to perform
+some task or make some decision. These scripts have their environments
+populated with some set of inputs, and are expected to populate some set of
+output variables, which are read by FRR after the script completes. The names
+and expected contents of these scripts are documented alongside the commands
+that support them.
+
+These scripts live in :file:`/etc/frr/scripts/` by default. This is
+configurable at compile time via ``--with-scriptdir``. It may be
+overriden at runtime with the ``--scriptdir`` daemon option.
+
+In order to use scripting, FRR must be built with ``--enable-scripting``.
+
+.. note::
+
+ Scripts are typically loaded just-in-time. This means you can change the
+ contents of a script that is in use without restarting FRR. Not all
+ scripting locations may behave this way; refer to the documentation for the
+ particular location.
diff --git a/doc/user/subdir.am b/doc/user/subdir.am
index a78d261863..3585245e85 100644
--- a/doc/user/subdir.am
+++ b/doc/user/subdir.am
@@ -35,6 +35,7 @@ user_RSTFILES = \
doc/user/routemap.rst \
doc/user/routeserver.rst \
doc/user/rpki.rst \
+ doc/user/scripting.rst \
doc/user/setup.rst \
doc/user/sharp.rst \
doc/user/snmp.rst \
diff --git a/lib/command.c b/lib/command.c
index f40fe6e2c5..b9d607a101 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -49,6 +49,8 @@
#include "northbound_cli.h"
#include "network.h"
+#include "frrscript.h"
+
DEFINE_MTYPE_STATIC(LIB, HOST, "Host config")
DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
@@ -2303,6 +2305,30 @@ done:
return CMD_SUCCESS;
}
+#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
+DEFUN(script,
+ script_cmd,
+ "script SCRIPT",
+ "Test command - execute a script\n"
+ "Script name (same as filename in /etc/frr/scripts/\n")
+{
+ struct prefix p;
+ str2prefix("1.2.3.4/24", &p);
+
+ struct frrscript *fs = frrscript_load(argv[1]->arg, NULL);
+
+ if (fs == NULL) {
+ vty_out(vty, "Script '/etc/frr/scripts/%s.lua' not found\n",
+ argv[1]->arg);
+ } else {
+ int ret = frrscript_call(fs, NULL);
+ vty_out(vty, "Script result: %d\n", ret);
+ }
+
+ return CMD_SUCCESS;
+}
+#endif
+
/* Set config filename. Called from vty.c */
void host_config_set(const char *filename)
{
@@ -2397,6 +2423,10 @@ void cmd_init(int terminal)
install_element(VIEW_NODE, &echo_cmd);
install_element(VIEW_NODE, &autocomplete_cmd);
install_element(VIEW_NODE, &find_cmd);
+#if defined(DEV_BUILD) && defined(HAVE_SCRIPTING)
+ install_element(VIEW_NODE, &script_cmd);
+#endif
+
install_element(ENABLE_NODE, &config_end_cmd);
install_element(ENABLE_NODE, &config_disable_cmd);
diff --git a/lib/compiler.h b/lib/compiler.h
index 217a60d888..70ef8e9bc8 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -279,6 +279,29 @@ extern "C" {
#define array_size(ar) (sizeof(ar) / sizeof(ar[0]))
+/* Some insane macros to count number of varargs to a functionlike macro */
+#define PP_ARG_N( \
+ _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
+ _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
+ _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
+ _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
+ _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
+ _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
+ _61, _62, _63, N, ...) N
+
+#define PP_RSEQ_N() \
+ 62, 61, 60, \
+ 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \
+ 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
+ 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \
+ 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \
+ 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
+ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+
+#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
+#define PP_NARG(...) PP_NARG_(_, ##__VA_ARGS__, PP_RSEQ_N())
+
+
/* sigh. this is so ugly, it overflows and wraps to being nice again.
*
* printfrr() supports "%Ld" for <int64_t>, whatever that is typedef'd to.
diff --git a/lib/frrlua.c b/lib/frrlua.c
index 9f9cf8c1f6..3c270b2340 100644
--- a/lib/frrlua.c
+++ b/lib/frrlua.c
@@ -2,128 +2,365 @@
* This file defines the lua interface into
* FRRouting.
*
- * Copyright (C) 2016 Cumulus Networks, Inc.
- * Donald Sharp
+ * Copyright (C) 2016-2019 Cumulus Networks, Inc.
+ * Donald Sharp, Quentin Young
*
- * This file is part of FRRouting (FRR).
+ * 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.
*
- * FRR 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, or (at your option) any later version.
- *
- * FRR 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.
+ * 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 FRR; see the file COPYING. If not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
-#if defined(HAVE_LUA)
+#ifdef HAVE_SCRIPTING
+
#include "prefix.h"
#include "frrlua.h"
#include "log.h"
+#include "buffer.h"
+
+/* Lua stuff */
-static int lua_zlog_debug(lua_State *L)
+/*
+ * FRR convenience functions.
+ *
+ * This section has convenience functions used to make interacting with the Lua
+ * stack easier.
+ */
+
+int frrlua_table_get_integer(lua_State *L, const char *key)
{
- int debug_lua = 1;
- const char *string = lua_tostring(L, 1);
+ int result;
- if (debug_lua)
- zlog_debug("%s", string);
+ lua_pushstring(L, key);
+ lua_gettable(L, -2);
- return 0;
+ result = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ return result;
}
-const char *get_string(lua_State *L, const char *key)
+/*
+ * Encoders.
+ *
+ * This section has functions that convert internal FRR datatypes into Lua
+ * datatypes.
+ */
+
+void lua_pushprefix(lua_State *L, const struct prefix *prefix)
{
- const char *str;
+ char buffer[PREFIX_STRLEN];
- lua_pushstring(L, key);
- lua_gettable(L, -2);
+ lua_newtable(L);
+ lua_pushstring(L, prefix2str(prefix, buffer, PREFIX_STRLEN));
+ lua_setfield(L, -2, "network");
+ lua_pushinteger(L, prefix->prefixlen);
+ lua_setfield(L, -2, "length");
+ lua_pushinteger(L, prefix->family);
+ lua_setfield(L, -2, "family");
+}
- str = (const char *)lua_tostring(L, -1);
+void *lua_toprefix(lua_State *L, int idx)
+{
+ struct prefix *p = XCALLOC(MTYPE_TMP, sizeof(struct prefix));
+
+ lua_getfield(L, idx, "network");
+ str2prefix(lua_tostring(L, -1), p);
lua_pop(L, 1);
- return str;
+ return p;
}
-int get_integer(lua_State *L, const char *key)
+void lua_pushinterface(lua_State *L, const struct interface *ifp)
{
- int result;
+ lua_newtable(L);
+ lua_pushstring(L, ifp->name);
+ lua_setfield(L, -2, "name");
+ lua_pushinteger(L, ifp->ifindex);
+ lua_setfield(L, -2, "ifindex");
+ lua_pushinteger(L, ifp->status);
+ lua_setfield(L, -2, "status");
+ lua_pushinteger(L, ifp->flags);
+ lua_setfield(L, -2, "flags");
+ lua_pushinteger(L, ifp->metric);
+ lua_setfield(L, -2, "metric");
+ lua_pushinteger(L, ifp->speed);
+ lua_setfield(L, -2, "speed");
+ lua_pushinteger(L, ifp->mtu);
+ lua_setfield(L, -2, "mtu");
+ lua_pushinteger(L, ifp->mtu6);
+ lua_setfield(L, -2, "mtu6");
+ lua_pushinteger(L, ifp->bandwidth);
+ lua_setfield(L, -2, "bandwidth");
+ lua_pushinteger(L, ifp->link_ifindex);
+ lua_setfield(L, -2, "link_ifindex");
+ lua_pushinteger(L, ifp->ll_type);
+ lua_setfield(L, -2, "linklayer_type");
+}
- lua_pushstring(L, key);
- lua_gettable(L, -2);
+void *lua_tointerface(lua_State *L, int idx)
+{
+ struct interface *ifp = XCALLOC(MTYPE_TMP, sizeof(struct interface));
- result = lua_tointeger(L, -1);
+ lua_getfield(L, idx, "name");
+ strlcpy(ifp->name, lua_tostring(L, -1), sizeof(ifp->name));
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "ifindex");
+ ifp->ifindex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "status");
+ ifp->status = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "flags");
+ ifp->flags = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "metric");
+ ifp->metric = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "speed");
+ ifp->speed = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "mtu");
+ ifp->mtu = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "mtu6");
+ ifp->mtu6 = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "bandwidth");
+ ifp->bandwidth = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "link_ifindex");
+ ifp->link_ifindex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "linklayer_type");
+ ifp->ll_type = lua_tointeger(L, -1);
lua_pop(L, 1);
- return result;
+ return ifp;
}
-static void *lua_alloc(void *ud, void *ptr, size_t osize,
- size_t nsize)
+void lua_pushinaddr(lua_State *L, const struct in_addr *addr)
{
- (void)ud; (void)osize; /* not used */
- if (nsize == 0) {
- free(ptr);
- return NULL;
- } else
- return realloc(ptr, nsize);
+ char buf[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, addr, buf, sizeof(buf));
+
+ lua_newtable(L);
+ lua_pushinteger(L, addr->s_addr);
+ lua_setfield(L, -2, "value");
+ lua_pushstring(L, buf);
+ lua_setfield(L, -2, "string");
}
-lua_State *lua_initialize(const char *file)
+void *lua_toinaddr(lua_State *L, int idx)
{
- int status;
- lua_State *L = lua_newstate(lua_alloc, NULL);
+ struct in_addr *inaddr = XCALLOC(MTYPE_TMP, sizeof(struct in_addr));
- zlog_debug("Newstate: %p", L);
- luaL_openlibs(L);
- zlog_debug("Opened lib");
- status = luaL_loadfile(L, file);
- if (status) {
- zlog_debug("Failure to open %s %d", file, status);
- lua_close(L);
- return NULL;
- }
+ lua_getfield(L, idx, "value");
+ inaddr->s_addr = lua_tointeger(L, -1);
+ lua_pop(L, 1);
- lua_pcall(L, 0, LUA_MULTRET, 0);
- zlog_debug("Setting global function");
- lua_pushcfunction(L, lua_zlog_debug);
- lua_setglobal(L, "zlog_debug");
+ return inaddr;
+}
+
+
+void lua_pushin6addr(lua_State *L, const struct in6_addr *addr)
+{
+ char buf[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+
+ lua_newtable(L);
+ lua_pushlstring(L, (const char *)addr->s6_addr, 16);
+ lua_setfield(L, -2, "value");
+ lua_pushstring(L, buf);
+ lua_setfield(L, -2, "string");
+}
+
+void *lua_toin6addr(lua_State *L, int idx)
+{
+ struct in6_addr *in6addr = XCALLOC(MTYPE_TMP, sizeof(struct in6_addr));
+
+ lua_getfield(L, idx, "string");
+ inet_pton(AF_INET6, lua_tostring(L, -1), in6addr);
+ lua_pop(L, 1);
- return L;
+ return in6addr;
}
-void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix)
+void lua_pushsockunion(lua_State *L, const union sockunion *su)
{
- char buffer[100];
+ char buf[SU_ADDRSTRLEN];
+ sockunion2str(su, buf, sizeof(buf));
lua_newtable(L);
- lua_pushstring(L, prefix2str(prefix, buffer, 100));
- lua_setfield(L, -2, "route");
- lua_pushinteger(L, prefix->family);
- lua_setfield(L, -2, "family");
- lua_setglobal(L, "prefix");
+ lua_pushlstring(L, (const char *)sockunion_get_addr(su),
+ sockunion_get_addrlen(su));
+ lua_setfield(L, -2, "value");
+ lua_pushstring(L, buf);
+ lua_setfield(L, -2, "string");
}
-enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule)
+void *lua_tosockunion(lua_State *L, int idx)
{
- int status;
+ union sockunion *su = XCALLOC(MTYPE_TMP, sizeof(union sockunion));
+
+ lua_getfield(L, idx, "string");
+ str2sockunion(lua_tostring(L, -1), su);
+
+ return su;
+}
- lua_getglobal(L, rule);
- status = lua_pcall(L, 0, 1, 0);
- if (status) {
- zlog_debug("Executing Failure with function: %s: %d",
- rule, status);
- return LUA_RM_FAILURE;
+void lua_pushtimet(lua_State *L, const time_t *time)
+{
+ lua_pushinteger(L, *time);
+}
+
+void *lua_totimet(lua_State *L, int idx)
+{
+ time_t *t = XCALLOC(MTYPE_TMP, sizeof(time_t));
+
+ *t = lua_tointeger(L, idx);
+
+ return t;
+}
+
+void lua_pushintegerp(lua_State *L, const long long *num)
+{
+ lua_pushinteger(L, *num);
+}
+
+void *lua_tointegerp(lua_State *L, int idx)
+{
+ int isnum;
+ long long *num = XCALLOC(MTYPE_TMP, sizeof(long long));
+
+ *num = lua_tonumberx(L, idx, &isnum);
+ assert(isnum);
+
+ return num;
+}
+
+void *lua_tostringp(lua_State *L, int idx)
+{
+ char *string = XSTRDUP(MTYPE_TMP, lua_tostring(L, idx));
+
+ return string;
+}
+
+/*
+ * Logging.
+ *
+ * Lua-compatible wrappers for FRR logging functions.
+ */
+static const char *frrlua_log_thunk(lua_State *L)
+{
+ int nargs;
+
+ nargs = lua_gettop(L);
+ assert(nargs == 1);
+
+ return lua_tostring(L, 1);
+}
+
+static int frrlua_log_debug(lua_State *L)
+{
+ zlog_debug("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static int frrlua_log_info(lua_State *L)
+{
+ zlog_info("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static int frrlua_log_notice(lua_State *L)
+{
+ zlog_notice("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static int frrlua_log_warn(lua_State *L)
+{
+ zlog_warn("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static int frrlua_log_error(lua_State *L)
+{
+ zlog_err("%s", frrlua_log_thunk(L));
+ return 0;
+}
+
+static const luaL_Reg log_funcs[] = {
+ {"debug", frrlua_log_debug},
+ {"info", frrlua_log_info},
+ {"notice", frrlua_log_notice},
+ {"warn", frrlua_log_warn},
+ {"error", frrlua_log_error},
+ {},
+};
+
+void frrlua_export_logging(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_setfuncs(L, log_funcs, 0);
+ lua_setglobal(L, "log");
+}
+
+/*
+ * Debugging.
+ */
+
+char *frrlua_stackdump(lua_State *L)
+{
+ int top = lua_gettop(L);
+
+ char tmpbuf[64];
+ struct buffer *buf = buffer_new(4098);
+
+ for (int i = 1; i <= top; i++) {
+ int t = lua_type(L, i);
+
+ switch (t) {
+ case LUA_TSTRING: /* strings */
+ snprintf(tmpbuf, sizeof(tmpbuf), "\"%s\"\n",
+ lua_tostring(L, i));
+ buffer_putstr(buf, tmpbuf);
+ break;
+ case LUA_TBOOLEAN: /* booleans */
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s\n",
+ lua_toboolean(L, i) ? "true" : "false");
+ buffer_putstr(buf, tmpbuf);
+ break;
+ case LUA_TNUMBER: /* numbers */
+ snprintf(tmpbuf, sizeof(tmpbuf), "%g\n",
+ lua_tonumber(L, i));
+ buffer_putstr(buf, tmpbuf);
+ break;
+ default: /* other values */
+ snprintf(tmpbuf, sizeof(tmpbuf), "%s\n",
+ lua_typename(L, t));
+ buffer_putstr(buf, tmpbuf);
+ break;
+ }
}
- status = lua_tonumber(L, -1);
- return status;
+ char *result = XSTRDUP(MTYPE_TMP, buffer_getstr(buf));
+
+ buffer_free(buf);
+
+ return result;
}
-#endif
+
+#endif /* HAVE_SCRIPTING */
diff --git a/lib/frrlua.h b/lib/frrlua.h
index 40c7a67b89..8e52931e50 100644
--- a/lib/frrlua.h
+++ b/lib/frrlua.h
@@ -1,88 +1,173 @@
/*
- * This file defines the lua interface into
- * FRRouting.
+ * Copyright (C) 2016-2019 Cumulus Networks, Inc.
+ * Donald Sharp, Quentin Young
*
- * Copyright (C) 2016 Cumulus Networks, Inc.
- * Donald Sharp
+ * 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 file is part of FRRouting (FRR).
- *
- * FRR 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, or (at your option) any later version.
- *
- * FRR 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.
+ * 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 FRR; see the file COPYING. If not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef __LUA_H__
-#define __LUA_H__
+#ifndef __FRRLUA_H__
+#define __FRRLUA_H__
+
+#include <zebra.h>
+
+#ifdef HAVE_SCRIPTING
-#if defined(HAVE_LUA)
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
-#include "lua.h"
-#include "lualib.h"
-#include "lauxlib.h"
+#include "prefix.h"
+#include "frrscript.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
- * These functions are helper functions that
- * try to glom some of the lua_XXX functionality
- * into what we actually need, instead of having
- * to make multiple calls to set up what
- * we want
+ * Converts a prefix to a Lua value and pushes it on the stack.
+ */
+void lua_pushprefix(lua_State *L, const struct prefix *prefix);
+
+/*
+ * Converts the Lua value at idx to a prefix.
+ *
+ * Returns:
+ * struct prefix allocated with MTYPE_TMP
+ */
+void *lua_toprefix(lua_State *L, int idx);
+
+/*
+ * Converts an interface to a Lua value and pushes it on the stack.
+ */
+void lua_pushinterface(lua_State *L, const struct interface *ifp);
+
+/*
+ * Converts the Lua value at idx to an interface.
+ *
+ * Returns:
+ * struct interface allocated with MTYPE_TMP. This interface is not hooked
+ * to anything, nor is it inserted in the global interface tree.
+ */
+void *lua_tointerface(lua_State *L, int idx);
+
+/*
+ * Converts an in_addr to a Lua value and pushes it on the stack.
+ */
+void lua_pushinaddr(lua_State *L, const struct in_addr *addr);
+
+/*
+ * Converts the Lua value at idx to an in_addr.
+ *
+ * Returns:
+ * struct in_addr allocated with MTYPE_TMP.
+ */
+void *lua_toinaddr(lua_State *L, int idx);
+
+/*
+ * Converts an in6_addr to a Lua value and pushes it on the stack.
*/
-enum lua_rm_status {
- /*
- * Script function run failure. This will translate into a
- * deny
- */
- LUA_RM_FAILURE = 0,
- /*
- * No Match was found for the route map function
- */
- LUA_RM_NOMATCH,
- /*
- * Match was found but no changes were made to the
- * incoming data.
- */
- LUA_RM_MATCH,
- /*
- * Match was found and data was modified, so
- * figure out what changed
- */
- LUA_RM_MATCH_AND_CHANGE,
-};
+void lua_pushin6addr(lua_State *L, const struct in6_addr *addr);
/*
- * Open up the lua.scr file and parse
- * initial global values, if any.
+ * Converts the Lua value at idx to an in6_addr.
+ *
+ * Returns:
+ * struct in6_addr allocated with MTYPE_TMP.
*/
-lua_State *lua_initialize(const char *file);
+void *lua_toin6addr(lua_State *L, int idx);
-void lua_setup_prefix_table(lua_State *L, const struct prefix *prefix);
+/*
+ * Converts a time_t to a Lua value and pushes it on the stack.
+ */
+void lua_pushtimet(lua_State *L, const time_t *time);
-enum lua_rm_status lua_run_rm_rule(lua_State *L, const char *rule);
+/*
+ * Converts the Lua value at idx to a time_t.
+ *
+ * Returns:
+ * time_t allocated with MTYPE_TMP.
+ */
+void *lua_totimet(lua_State *L, int idx);
/*
- * Get particular string/integer information
- * from a table. It is *assumed* that
- * the table has already been selected
+ * Converts a sockunion to a Lua value and pushes it on the stack.
*/
-const char *get_string(lua_State *L, const char *key);
-int get_integer(lua_State *L, const char *key);
+void lua_pushsockunion(lua_State *L, const union sockunion *su);
+
+/*
+ * Converts the Lua value at idx to a sockunion.
+ *
+ * Returns:
+ * sockunion allocated with MTYPE_TMP.
+ */
+void *lua_tosockunion(lua_State *L, int idx);
+
+/*
+ * Converts an int to a Lua value and pushes it on the stack.
+ */
+void lua_pushintegerp(lua_State *L, const long long *num);
+
+/*
+ * Converts the Lua value at idx to an int.
+ *
+ * Returns:
+ * int allocated with MTYPE_TMP.
+ */
+void *lua_tointegerp(lua_State *L, int idx);
+
+/*
+ * Pop string.
+ *
+ * Sets *string to a copy of the string at the top of the stack. The copy is
+ * allocated with MTYPE_TMP and the caller is responsible for freeing it.
+ */
+void *lua_tostringp(lua_State *L, int idx);
+
+/*
+ * Retrieve an integer from table on the top of the stack.
+ *
+ * key
+ * Key of string value in table
+ */
+int frrlua_table_get_integer(lua_State *L, const char *key);
+
+/*
+ * Exports a new table containing bindings to FRR zlog functions into the
+ * global namespace.
+ *
+ * From Lua, these functions may be accessed as:
+ *
+ * - log.debug()
+ * - log.info()
+ * - log.warn()
+ * - log.error()
+ *
+ * They take a single string argument.
+ */
+void frrlua_export_logging(lua_State *L);
+
+/*
+ * Dump Lua stack to a string.
+ *
+ * Return value must be freed with XFREE(MTYPE_TMP, ...);
+ */
+char *frrlua_stackdump(lua_State *L);
#ifdef __cplusplus
}
#endif
-#endif
-#endif
+#endif /* HAVE_SCRIPTING */
+
+#endif /* __FRRLUA_H__ */
diff --git a/lib/frrscript.c b/lib/frrscript.c
new file mode 100644
index 0000000000..a3de474a4e
--- /dev/null
+++ b/lib/frrscript.c
@@ -0,0 +1,272 @@
+/* Scripting foo
+ * Copyright (C) 2020 NVIDIA Corporation
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#ifdef HAVE_SCRIPTING
+
+#include <stdarg.h>
+#include <lua.h>
+
+#include "frrscript.h"
+#include "frrlua.h"
+#include "memory.h"
+#include "hash.h"
+#include "log.h"
+
+
+DEFINE_MTYPE_STATIC(LIB, SCRIPT, "Scripting");
+
+/* Codecs */
+
+struct frrscript_codec frrscript_codecs_lib[] = {
+ {.typename = "integer",
+ .encoder = (encoder_func)lua_pushintegerp,
+ .decoder = lua_tointegerp},
+ {.typename = "string",
+ .encoder = (encoder_func)lua_pushstring,
+ .decoder = lua_tostringp},
+ {.typename = "prefix",
+ .encoder = (encoder_func)lua_pushprefix,
+ .decoder = lua_toprefix},
+ {.typename = "interface",
+ .encoder = (encoder_func)lua_pushinterface,
+ .decoder = lua_tointerface},
+ {.typename = "in_addr",
+ .encoder = (encoder_func)lua_pushinaddr,
+ .decoder = lua_toinaddr},
+ {.typename = "in6_addr",
+ .encoder = (encoder_func)lua_pushin6addr,
+ .decoder = lua_toin6addr},
+ {.typename = "sockunion",
+ .encoder = (encoder_func)lua_pushsockunion,
+ .decoder = lua_tosockunion},
+ {.typename = "time_t",
+ .encoder = (encoder_func)lua_pushtimet,
+ .decoder = lua_totimet},
+ {}};
+
+/* Type codecs */
+
+struct hash *codec_hash;
+char scriptdir[MAXPATHLEN];
+
+static unsigned int codec_hash_key(const void *data)
+{
+ const struct frrscript_codec *c = data;
+
+ return string_hash_make(c->typename);
+}
+
+static bool codec_hash_cmp(const void *d1, const void *d2)
+{
+ const struct frrscript_codec *e1 = d1;
+ const struct frrscript_codec *e2 = d2;
+
+ return strmatch(e1->typename, e2->typename);
+}
+
+static void *codec_alloc(void *arg)
+{
+ struct frrscript_codec *tmp = arg;
+
+ struct frrscript_codec *e =
+ XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript_codec));
+ e->typename = XSTRDUP(MTYPE_SCRIPT, tmp->typename);
+ e->encoder = tmp->encoder;
+ e->decoder = tmp->decoder;
+
+ return e;
+}
+
+#if 0
+static void codec_free(struct codec *c)
+{
+ XFREE(MTYPE_TMP, c->typename);
+ XFREE(MTYPE_TMP, c);
+}
+#endif
+
+/* Generic script APIs */
+
+int frrscript_call(struct frrscript *fs, struct frrscript_env *env)
+{
+ struct frrscript_codec c = {};
+ const void *arg;
+ const char *bindname;
+
+ /* Encode script arguments */
+ for (int i = 0; env && env[i].val != NULL; i++) {
+ bindname = env[i].name;
+ c.typename = env[i].typename;
+ arg = env[i].val;
+
+ struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
+ assert(codec && "No encoder for type");
+ codec->encoder(fs->L, arg);
+
+ lua_setglobal(fs->L, bindname);
+ }
+
+ int ret = lua_pcall(fs->L, 0, 0, 0);
+
+ switch (ret) {
+ case LUA_OK:
+ break;
+ case LUA_ERRRUN:
+ zlog_err("Script '%s' runtime error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRMEM:
+ zlog_err("Script '%s' memory error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRERR:
+ zlog_err("Script '%s' error handler error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRGCMM:
+ zlog_err("Script '%s' garbage collector error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ default:
+ zlog_err("Script '%s' unknown error: %s", fs->name,
+ lua_tostring(fs->L, -1));
+ break;
+ }
+
+ if (ret != LUA_OK) {
+ lua_pop(fs->L, 1);
+ goto done;
+ }
+
+done:
+ /* LUA_OK is 0, so we can just return lua_pcall's result directly */
+ return ret;
+}
+
+void *frrscript_get_result(struct frrscript *fs,
+ const struct frrscript_env *result)
+{
+ void *r;
+ struct frrscript_codec c = {.typename = result->typename};
+
+ struct frrscript_codec *codec = hash_lookup(codec_hash, &c);
+ assert(codec && "No encoder for type");
+
+ if (!codec->decoder) {
+ zlog_err("No script decoder for type '%s'", result->typename);
+ return NULL;
+ }
+
+ lua_getglobal(fs->L, result->name);
+ r = codec->decoder(fs->L, -1);
+ lua_pop(fs->L, 1);
+
+ return r;
+}
+
+void frrscript_register_type_codec(struct frrscript_codec *codec)
+{
+ struct frrscript_codec c = *codec;
+
+ if (hash_lookup(codec_hash, &c)) {
+ zlog_backtrace(LOG_ERR);
+ assert(!"Type codec double-registered.");
+ }
+
+ assert(hash_get(codec_hash, &c, codec_alloc));
+}
+
+void frrscript_register_type_codecs(struct frrscript_codec *codecs)
+{
+ for (int i = 0; codecs[i].typename != NULL; i++)
+ frrscript_register_type_codec(&codecs[i]);
+}
+
+struct frrscript *frrscript_load(const char *name,
+ int (*load_cb)(struct frrscript *))
+{
+ struct frrscript *fs = XCALLOC(MTYPE_SCRIPT, sizeof(struct frrscript));
+
+ fs->name = XSTRDUP(MTYPE_SCRIPT, name);
+ fs->L = luaL_newstate();
+ frrlua_export_logging(fs->L);
+
+ char fname[MAXPATHLEN];
+ snprintf(fname, sizeof(fname), "%s/%s.lua", scriptdir, fs->name);
+
+ int ret = luaL_loadfile(fs->L, fname);
+
+ switch (ret) {
+ case LUA_OK:
+ break;
+ case LUA_ERRSYNTAX:
+ zlog_err("Failed loading script '%s': syntax error: %s", fname,
+ lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRMEM:
+ zlog_err("Failed loading script '%s': out-of-memory error: %s",
+ fname, lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRGCMM:
+ zlog_err(
+ "Failed loading script '%s': garbage collector error: %s",
+ fname, lua_tostring(fs->L, -1));
+ break;
+ case LUA_ERRFILE:
+ zlog_err("Failed loading script '%s': file read error: %s",
+ fname, lua_tostring(fs->L, -1));
+ break;
+ default:
+ zlog_err("Failed loading script '%s': unknown error: %s", fname,
+ lua_tostring(fs->L, -1));
+ break;
+ }
+
+ if (ret != LUA_OK)
+ goto fail;
+
+ if (load_cb && (*load_cb)(fs) != 0)
+ goto fail;
+
+ return fs;
+fail:
+ frrscript_unload(fs);
+ return NULL;
+}
+
+void frrscript_unload(struct frrscript *fs)
+{
+ lua_close(fs->L);
+ XFREE(MTYPE_SCRIPT, fs->name);
+ XFREE(MTYPE_SCRIPT, fs);
+}
+
+void frrscript_init(const char *sd)
+{
+ codec_hash = hash_create(codec_hash_key, codec_hash_cmp,
+ "Lua type encoders");
+
+ strlcpy(scriptdir, sd, sizeof(scriptdir));
+
+ /* Register core library types */
+ frrscript_register_type_codecs(frrscript_codecs_lib);
+}
+
+#endif /* HAVE_SCRIPTING */
diff --git a/lib/frrscript.h b/lib/frrscript.h
new file mode 100644
index 0000000000..f4057f531b
--- /dev/null
+++ b/lib/frrscript.h
@@ -0,0 +1,138 @@
+/* Scripting foo
+ * Copyright (C) 2020 NVIDIA Corporation
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __FRRSCRIPT_H__
+#define __FRRSCRIPT_H__
+
+#include <zebra.h>
+
+#ifdef HAVE_SCRIPTING
+
+#include <lua.h>
+#include "frrlua.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*encoder_func)(lua_State *, const void *);
+typedef void *(*decoder_func)(lua_State *, int);
+
+struct frrscript_codec {
+ const char *typename;
+ encoder_func encoder;
+ decoder_func decoder;
+};
+
+struct frrscript {
+ /* Script name */
+ char *name;
+
+ /* Lua state */
+ struct lua_State *L;
+};
+
+struct frrscript_env {
+ /* Value type */
+ const char *typename;
+
+ /* Binding name */
+ const char *name;
+
+ /* Value */
+ const void *val;
+};
+
+/*
+ * Create new FRR script.
+ */
+struct frrscript *frrscript_load(const char *name,
+ int (*load_cb)(struct frrscript *));
+
+/*
+ * Destroy FRR script.
+ */
+void frrscript_unload(struct frrscript *fs);
+
+/*
+ * Register a Lua codec for a type.
+ *
+ * tname
+ * Name of type; e.g., "peer", "ospf_interface", etc. Chosen at will.
+ *
+ * codec(s)
+ * Function pointer to codec struct. Encoder function should push a Lua
+ * table representing the passed argument - which will have the C type
+ * associated with the chosen 'tname' to the provided stack. The decoder
+ * function should pop a value from the top of the stack and return a heap
+ * chunk containing that value. Allocations should be made with MTYPE_TMP.
+ *
+ * If using the plural function variant, pass a NULL-terminated array.
+ *
+ */
+void frrscript_register_type_codec(struct frrscript_codec *codec);
+void frrscript_register_type_codecs(struct frrscript_codec *codecs);
+
+/*
+ * Initialize scripting subsystem. Call this before anything else.
+ *
+ * scriptdir
+ * Directory in which to look for scripts
+ */
+void frrscript_init(const char *scriptdir);
+
+
+/*
+ * Call script.
+ *
+ * fs
+ * The script to call; this is obtained from frrscript_load().
+ *
+ * env
+ * The script's environment. Specify this as an array of frrscript_env.
+ *
+ * Returns:
+ * 0 if the script ran successfully, nonzero otherwise.
+ */
+int frrscript_call(struct frrscript *fs, struct frrscript_env *env);
+
+
+/*
+ * Get result from finished script.
+ *
+ * fs
+ * The script. This script must have been run already.
+ *
+ * result
+ * The result to extract from the script.
+ * This reuses the frrscript_env type, but only the typename and name fields
+ * need to be set. The value is returned directly.
+ *
+ * Returns:
+ * The script result of the specified name and type, or NULL.
+ */
+void *frrscript_get_result(struct frrscript *fs,
+ const struct frrscript_env *result);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* HAVE_SCRIPTING */
+
+#endif /* __FRRSCRIPT_H__ */
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 8e7777a1a9..b83883779c 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -43,6 +43,7 @@
#include "frrcu.h"
#include "frr_pthread.h"
#include "defaults.h"
+#include "frrscript.h"
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
DEFINE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm))
@@ -55,6 +56,7 @@ char frr_vtydir[256];
const char frr_dbdir[] = DAEMON_DB_DIR;
#endif
const char frr_moduledir[] = MODULE_PATH;
+const char frr_scriptdir[] = SCRIPT_PATH;
char frr_protoname[256] = "NONE";
char frr_protonameinst[256] = "NONE";
@@ -100,6 +102,7 @@ static void opt_extend(const struct optspec *os)
#define OPTION_DB_FILE 1006
#define OPTION_LOGGING 1007
#define OPTION_LIMIT_FDS 1008
+#define OPTION_SCRIPTDIR 1009
static const struct option lo_always[] = {
{"help", no_argument, NULL, 'h'},
@@ -110,6 +113,7 @@ static const struct option lo_always[] = {
{"pathspace", required_argument, NULL, 'N'},
{"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
{"moduledir", required_argument, NULL, OPTION_MODULEDIR},
+ {"scriptdir", required_argument, NULL, OPTION_SCRIPTDIR},
{"log", required_argument, NULL, OPTION_LOG},
{"log-level", required_argument, NULL, OPTION_LOGLEVEL},
{"tcli", no_argument, NULL, OPTION_TCLI},
@@ -126,6 +130,7 @@ static const struct optspec os_always = {
" -N, --pathspace Insert prefix into config & socket paths\n"
" --vty_socket Override vty socket path\n"
" --moduledir Override modules directory\n"
+ " --scriptdir Override scripts directory\n"
" --log Set Logging to stdout, syslog, or file:<name>\n"
" --log-level Set Logging Level to use, debug, info, warn, etc\n"
" --tcli Use transaction-based CLI\n"
@@ -533,6 +538,14 @@ static int frr_opt(int opt)
}
di->module_path = optarg;
break;
+ case OPTION_SCRIPTDIR:
+ if (di->script_path) {
+ fprintf(stderr, "--scriptdir option specified more than once!\n");
+ errors++;
+ break;
+ }
+ di->script_path = optarg;
+ break;
case OPTION_TCLI:
di->cli_mode = FRR_CLI_TRANSACTIONAL;
break;
@@ -717,6 +730,9 @@ struct thread_master *frr_init(void)
lib_cmd_init();
frr_pthread_init();
+#ifdef HAVE_SCRIPTING
+ frrscript_init(di->script_path ? di->script_path : frr_scriptdir);
+#endif
log_ref_init();
log_ref_vty_init();
diff --git a/lib/libfrr.h b/lib/libfrr.h
index 2e4dcbe093..c446931468 100644
--- a/lib/libfrr.h
+++ b/lib/libfrr.h
@@ -81,6 +81,7 @@ struct frr_daemon_info {
#endif
const char *vty_path;
const char *module_path;
+ const char *script_path;
const char *pathspace;
bool zpathspace;
@@ -162,6 +163,7 @@ extern char frr_zclientpath[256];
extern const char frr_sysconfdir[];
extern char frr_vtydir[256];
extern const char frr_moduledir[];
+extern const char frr_scriptdir[];
extern char frr_protoname[];
extern char frr_protonameinst[];
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index 7048df99fb..853f643472 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -693,6 +693,12 @@ static int nb_write_config(struct nb_config *config, enum nb_cfg_format format,
__func__, safe_strerror(errno));
return -1;
}
+ if (fchmod(fd, CONFIGFILE_MASK) != 0) {
+ flog_warn(EC_LIB_SYSTEM_CALL,
+ "%s: fchmod() failed: %s(%d):", __func__,
+ safe_strerror(errno), errno);
+ return -1;
+ }
/* Make vty for configuration file. */
file_vty = vty_new();
diff --git a/lib/subdir.am b/lib/subdir.am
index ee9e827ee8..570e0c3d28 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -26,6 +26,7 @@ lib_libfrr_la_SOURCES = \
lib/filter_nb.c \
lib/frrcu.c \
lib/frrlua.c \
+ lib/frrscript.c \
lib/frr_pthread.c \
lib/frrstr.c \
lib/getopt.c \
@@ -185,6 +186,7 @@ pkginclude_HEADERS += \
lib/filter.h \
lib/freebsd-queue.h \
lib/frrlua.h \
+ lib/frrscript.h \
lib/frr_pthread.h \
lib/frratomic.h \
lib/frrcu.h \
diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c
index dbe5de724c..f99971ab7b 100644
--- a/pbrd/pbr_nht.c
+++ b/pbrd/pbr_nht.c
@@ -770,18 +770,20 @@ pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc,
goto done;
}
- switch (pnhi->nhr->prefix.family) {
- case AF_INET:
- if (pnhc->nexthop.gate.ipv4.s_addr
- != pnhi->nhr->prefix.u.prefix4.s_addr)
- goto done; /* Unrelated change */
- break;
- case AF_INET6:
- if (memcmp(&pnhc->nexthop.gate.ipv6,
- &pnhi->nhr->prefix.u.prefix6, 16)
- != 0)
- goto done; /* Unrelated change */
- break;
+ if (pnhi->nhr) {
+ switch (pnhi->nhr->prefix.family) {
+ case AF_INET:
+ if (pnhc->nexthop.gate.ipv4.s_addr
+ != pnhi->nhr->prefix.u.prefix4.s_addr)
+ goto done; /* Unrelated change */
+ break;
+ case AF_INET6:
+ if (memcmp(&pnhc->nexthop.gate.ipv6,
+ &pnhi->nhr->prefix.u.prefix6, 16)
+ != 0)
+ goto done; /* Unrelated change */
+ break;
+ }
}
pnhi->nhr_matched = true;
diff --git a/staticd/static_nb.c b/staticd/static_nb.c
index 2fdd0d2989..a2a14751cf 100644
--- a/staticd/static_nb.c
+++ b/staticd/static_nb.c
@@ -66,7 +66,6 @@ const struct frr_yang_module_info frr_staticd_info = {
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/onlink",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify,
- .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy,
}
},
{
@@ -145,7 +144,6 @@ const struct frr_yang_module_info frr_staticd_info = {
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink",
.cbs = {
.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify,
- .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy,
}
},
{
diff --git a/staticd/static_nb.h b/staticd/static_nb.h
index b293224dd1..e85e1d0e9f 100644
--- a/staticd/static_nb.h
+++ b/staticd/static_nb.h
@@ -41,8 +41,6 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify(
struct nb_cb_modify_args *args);
-int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy(
- struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy(
@@ -83,8 +81,6 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr
struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify(
struct nb_cb_modify_args *args);
-int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy(
- struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify(
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy(
diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c
index 3778960cd1..bf669957bf 100644
--- a/staticd/static_nb_config.c
+++ b/staticd/static_nb_config.c
@@ -287,9 +287,27 @@ static int static_nexthop_mpls_label_modify(struct nb_cb_modify_args *args)
static int static_nexthop_onlink_modify(struct nb_cb_modify_args *args)
{
struct static_nexthop *nh;
+ static_types nh_type;
- nh = nb_running_get_entry(args->dnode, NULL, true);
- nh->onlink = yang_dnode_get_bool(args->dnode, NULL);
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ nh_type = yang_dnode_get_enum(args->dnode, "../nh-type");
+ if ((nh_type != STATIC_IPV4_GATEWAY_IFNAME)
+ && (nh_type != STATIC_IPV6_GATEWAY_IFNAME)) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "nexthop type is not the ipv4 or ipv6 interface type");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ nh->onlink = yang_dnode_get_bool(args->dnode, NULL);
+ break;
+ }
return NB_OK;
}
@@ -317,9 +335,25 @@ static int static_nexthop_color_destroy(struct nb_cb_destroy_args *args)
static int static_nexthop_bh_type_modify(struct nb_cb_modify_args *args)
{
struct static_nexthop *nh;
+ static_types nh_type;
- nh = nb_running_get_entry(args->dnode, NULL, true);
- nh->bh_type = yang_dnode_get_enum(args->dnode, NULL);
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ nh_type = yang_dnode_get_enum(args->dnode, "../nh-type");
+ if (nh_type != STATIC_BLACKHOLE) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "nexthop type is not the blackhole type");
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ nh = nb_running_get_entry(args->dnode, NULL, true);
+ nh->bh_type = yang_dnode_get_enum(args->dnode, NULL);
+ break;
+ }
return NB_OK;
}
@@ -589,7 +623,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
info = route_table_get_info(rn->table);
if (static_nexthop_create(args, rn_dnode, info) != NB_OK)
- return NB_ERR_VALIDATION;
+ return NB_ERR_INCONSISTENCY;
break;
}
return NB_OK;
@@ -626,17 +660,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify(
struct nb_cb_modify_args *args)
{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_APPLY:
- if (static_nexthop_bh_type_modify(args) != NB_OK)
- return NB_ERR;
- break;
- }
- return NB_OK;
+ return static_nexthop_bh_type_modify(args);
}
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_destroy(
@@ -662,33 +686,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify(
struct nb_cb_modify_args *args)
{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_APPLY:
- if (static_nexthop_onlink_modify(args) != NB_OK)
- return NB_ERR;
-
- break;
- }
- return NB_OK;
-}
-int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy(
- struct nb_cb_destroy_args *args)
-{
- /* onlink has a boolean type with default value,
- * so no need to do any operations in destroy callback
- */
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- break;
- }
- return NB_OK;
+ return static_nexthop_onlink_modify(args);
}
/*
@@ -1042,17 +1040,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify(
struct nb_cb_modify_args *args)
{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_APPLY:
- if (static_nexthop_bh_type_modify(args) != NB_OK)
- return NB_ERR;
- break;
- }
- return NB_OK;
+ return static_nexthop_bh_type_modify(args);
}
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_destroy(
@@ -1079,35 +1067,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify(
struct nb_cb_modify_args *args)
{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- break;
- case NB_EV_APPLY:
- if (static_nexthop_onlink_modify(args) != NB_OK)
- return NB_ERR;
-
- break;
- }
- return NB_OK;
-}
-
-
-int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy(
- struct nb_cb_destroy_args *args)
-{
- /* onlink has a boolean type with default value,
- * so no need to do any operations in destroy callback
- */
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- break;
- }
- return NB_OK;
+ return static_nexthop_onlink_modify(args);
}
/*
diff --git a/tests/topotests/bfd-ospf-topo1/__init__.py b/tests/topotests/bfd-ospf-topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/__init__.py
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt1/bfdd.conf
new file mode 100644
index 0000000000..610a20f88a
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/bfdd.conf
@@ -0,0 +1,9 @@
+log file bfdd.log
+log timestamp precision 3
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt1/ospf6d.conf
new file mode 100644
index 0000000000..18def599b4
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/ospf6d.conf
@@ -0,0 +1,21 @@
+log file ospf6d.log
+log timestamp precision 3
+!
+hostname rt1
+!
+password 1
+!
+interface eth-rt2
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+interface eth-rt3
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+router ospf6
+ ospf6 router-id 1.1.1.1
+ interface eth-rt2 area 0.0.0.0
+ interface eth-rt3 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt1/ospfd.conf
new file mode 100644
index 0000000000..07b42f9885
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/ospfd.conf
@@ -0,0 +1,26 @@
+log file ospfd.log
+log timestamp precision 3
+!
+hostname rt1
+!
+password 1
+!
+debug ospf event
+debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt2
+ ip ospf area 0.0.0.0
+ ip ospf bfd
+!
+interface eth-rt3
+ ip ospf area 0.0.0.0
+ ip ospf bfd
+!
+router ospf
+ ospf router-id 1.1.1.1
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000000..f354eff697
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000000..6465efb8b5
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step2/show_bfd_peers.ref b/tests/topotests/bfd-ospf-topo1/rt1/step2/show_bfd_peers.ref
new file mode 100644
index 0000000000..63f0d50784
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step2/show_bfd_peers.ref
@@ -0,0 +1,26 @@
+[
+ {
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_healthy.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_healthy.ref
new file mode 100644
index 0000000000..42051f9582
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_healthy.ref
@@ -0,0 +1,28 @@
+[
+ {
+ "peer": "10.0.2.2",
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "peer": "10.0.1.2",
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt2_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt2_down.ref
new file mode 100644
index 0000000000..d844ee6813
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt2_down.ref
@@ -0,0 +1,15 @@
+[
+ {
+ "peer": "10.0.2.2",
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt3_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt3_down.ref
new file mode 100644
index 0000000000..32799084fb
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_bfd_peers_rt3_down.ref
@@ -0,0 +1,15 @@
+[
+ {
+ "peer": "10.0.1.2",
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_healthy.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_healthy.ref
new file mode 100644
index 0000000000..f354eff697
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_healthy.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt2_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt2_down.ref
new file mode 100644
index 0000000000..43eecd0b7a
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt2_down.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt3_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt3_down.ref
new file mode 100644
index 0000000000..409af6308b
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ip_route_rt3_down.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_healthy.ref
new file mode 100644
index 0000000000..6465efb8b5
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_healthy.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt2_down.ref
new file mode 100644
index 0000000000..cfb1ef1bb6
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt2_down.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt3_down.ref
new file mode 100644
index 0000000000..58b44da5c2
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/step3/show_ipv6_route_rt3_down.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd-ospf-topo1/rt1/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt1/zebra.conf
new file mode 100644
index 0000000000..6003125b6b
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt1/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+log timestamp precision 3
+!
+hostname rt1
+!
+debug zebra kernel
+debug zebra packet
+debug zebra events
+debug zebra rib
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address ::ffff:0101:0101/128
+!
+interface eth-rt2
+ ip address 10.0.1.1/24
+!
+interface eth-rt3
+ ip address 10.0.2.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt2/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt2/bfdd.conf
new file mode 100644
index 0000000000..437f063d8f
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt2/bfdd.conf
@@ -0,0 +1,7 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt2/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt2/ospf6d.conf
new file mode 100644
index 0000000000..2f35099564
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt2/ospf6d.conf
@@ -0,0 +1,19 @@
+log file ospf6d.log
+!
+hostname rt2
+!
+password 1
+!
+interface eth-rt1
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+interface eth-rt5
+ ipv6 ospf6 network broadcast
+!
+router ospf6
+ ospf6 router-id 2.2.2.2
+ interface eth-rt1 area 0.0.0.0
+ interface eth-rt5 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt2/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt2/ospfd.conf
new file mode 100644
index 0000000000..a05d8b58c8
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt2/ospfd.conf
@@ -0,0 +1,24 @@
+log file ospfd.log
+!
+hostname rt2
+!
+password 1
+!
+debug ospf event
+debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt1
+ ip ospf area 0.0.0.0
+ ip ospf bfd
+!
+interface eth-rt5
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 2.2.2.2
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt2/step2/show_bfd_peers.ref b/tests/topotests/bfd-ospf-topo1/rt2/step2/show_bfd_peers.ref
new file mode 100644
index 0000000000..d6df1ebfb2
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt2/step2/show_bfd_peers.ref
@@ -0,0 +1,14 @@
+[
+ {
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd-ospf-topo1/rt2/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt2/zebra.conf
new file mode 100644
index 0000000000..5fc7fc5b28
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt2/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt2
+!
+debug zebra kernel
+debug zebra packet
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address ::ffff:0202:0202/128
+!
+interface eth-rt1
+ ip address 10.0.1.2/24
+!
+interface eth-rt5
+ ip address 10.0.3.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt3/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt3/bfdd.conf
new file mode 100644
index 0000000000..437f063d8f
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt3/bfdd.conf
@@ -0,0 +1,7 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt3/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt3/ospf6d.conf
new file mode 100644
index 0000000000..3e8777019e
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt3/ospf6d.conf
@@ -0,0 +1,19 @@
+log file ospf6d.log
+!
+hostname rt3
+!
+password 1
+!
+interface eth-rt1
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+interface eth-rt4
+ ipv6 ospf6 network broadcast
+!
+router ospf6
+ ospf6 router-id 3.3.3.3
+ interface eth-rt1 area 0.0.0.0
+ interface eth-rt4 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt3/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt3/ospfd.conf
new file mode 100644
index 0000000000..1196e6d189
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt3/ospfd.conf
@@ -0,0 +1,24 @@
+log file ospfd.log
+!
+hostname rt3
+!
+password 1
+!
+debug ospf event
+debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt1
+ ip ospf area 0.0.0.0
+ ip ospf bfd
+!
+interface eth-rt4
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 3.3.3.3
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt3/step2/show_bfd_peers.ref b/tests/topotests/bfd-ospf-topo1/rt3/step2/show_bfd_peers.ref
new file mode 100644
index 0000000000..d6df1ebfb2
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt3/step2/show_bfd_peers.ref
@@ -0,0 +1,14 @@
+[
+ {
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd-ospf-topo1/rt3/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt3/zebra.conf
new file mode 100644
index 0000000000..d368de9bbe
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt3
+!
+debug zebra kernel
+debug zebra packet
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address ::ffff:0303:0303/128
+!
+interface eth-rt1
+ ip address 10.0.2.2/24
+!
+interface eth-rt4
+ ip address 10.0.4.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt4/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt4/bfdd.conf
new file mode 100644
index 0000000000..f35e772790
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt4/bfdd.conf
@@ -0,0 +1,5 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt4/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt4/ospf6d.conf
new file mode 100644
index 0000000000..bccd1e75bd
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt4/ospf6d.conf
@@ -0,0 +1,18 @@
+log file ospf6d.log
+!
+hostname rt4
+!
+password 1
+!
+interface eth-rt3
+ ipv6 ospf6 network broadcast
+!
+interface eth-rt5
+ ipv6 ospf6 network broadcast
+!
+router ospf6
+ ospf6 router-id 4.4.4.4
+ interface eth-rt3 area 0.0.0.0
+ interface eth-rt5 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt4/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt4/ospfd.conf
new file mode 100644
index 0000000000..3a2568b4ab
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt4/ospfd.conf
@@ -0,0 +1,23 @@
+log file ospfd.log
+!
+hostname rt4
+!
+password 1
+!
+debug ospf event
+debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt3
+ ip ospf area 0.0.0.0
+!
+interface eth-rt5
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 4.4.4.4
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt4/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt4/zebra.conf
new file mode 100644
index 0000000000..7b053bac35
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt4/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt4
+!
+debug zebra kernel
+debug zebra packet
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address ::ffff:0404:0404/128
+!
+interface eth-rt3
+ ip address 10.0.4.2/24
+!
+interface eth-rt5
+ ip address 10.0.5.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt5/bfdd.conf b/tests/topotests/bfd-ospf-topo1/rt5/bfdd.conf
new file mode 100644
index 0000000000..f35e772790
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt5/bfdd.conf
@@ -0,0 +1,5 @@
+!
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt5/ospf6d.conf b/tests/topotests/bfd-ospf-topo1/rt5/ospf6d.conf
new file mode 100644
index 0000000000..766862276c
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt5/ospf6d.conf
@@ -0,0 +1,18 @@
+log file ospf6d.log
+!
+hostname rt5
+!
+password 1
+!
+interface eth-rt2
+ ipv6 ospf6 network broadcast
+!
+interface eth-rt4
+ ipv6 ospf6 network broadcast
+!
+router ospf6
+ ospf6 router-id 5.5.5.5
+ interface eth-rt2 area 0.0.0.0
+ interface eth-rt4 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt5/ospfd.conf b/tests/topotests/bfd-ospf-topo1/rt5/ospfd.conf
new file mode 100644
index 0000000000..a35de5f45f
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt5/ospfd.conf
@@ -0,0 +1,23 @@
+log file ospfd.log
+!
+hostname rt5
+!
+password 1
+!
+debug ospf event
+debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt2
+ ip ospf area 0.0.0.0
+!
+interface eth-rt4
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 5.5.5.5
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd-ospf-topo1/rt5/zebra.conf b/tests/topotests/bfd-ospf-topo1/rt5/zebra.conf
new file mode 100644
index 0000000000..0b7c9e02f3
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/rt5/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt5
+!
+debug zebra kernel
+debug zebra packet
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address ::ffff:0505:0505/128
+!
+interface eth-rt2
+ ip address 10.0.3.2/24
+!
+interface eth-rt4
+ ip address 10.0.5.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd-ospf-topo1/test_bfd_ospf_topo1.py b/tests/topotests/bfd-ospf-topo1/test_bfd_ospf_topo1.py
new file mode 100755
index 0000000000..1cec62789b
--- /dev/null
+++ b/tests/topotests/bfd-ospf-topo1/test_bfd_ospf_topo1.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_ospf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bfd_ospf_topo1.py:
+
+ +---------+
+ | |
+ eth-rt2 (.1) | RT1 | eth-rt3 (.1)
+ +----------+ 1.1.1.1 +----------+
+ | | | |
+ | +---------+ |
+ | |
+ | 10.0.2.0/24 |
+ | |
+ | eth-rt1 | (.2)
+ | 10.0.1.0/24 +----+----+
+ | | |
+ | | RT3 |
+ | | 3.3.3.3 |
+ | | |
+ (.2) | eth-rt1 +----+----+
+ +----+----+ eth-rt4 | (.1)
+ | | |
+ | RT2 | |
+ | 2.2.2.2 | 10.0.4.0/24 |
+ | | |
+ +----+----+ |
+ (.1) | eth-rt5 eth-rt3 | (.2)
+ | +----+----+
+ | | |
+ | | RT4 |
+ | | 4.4.4.4 |
+ | | |
+ | +----+----+
+ | 10.0.3.0/24 eth-rt5 | (.1)
+ | |
+ | |
+ | 10.0.5.0/24 |
+ | |
+ | +---------+ |
+ | | | |
+ +----------+ RT5 +----------+
+ eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2)
+ | |
+ +---------+
+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+from time import sleep
+from time import time
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def print_cmd_result(rname, command):
+ print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
+
+
+def router_compare_json_output(rname, command, reference, count=120, wait=0.5):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+## TEST STEPS
+
+
+def test_rib_ospf_step1():
+ logger.info("Test (step 1): verify RIB for OSPF")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step1/show_ip_route.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step1/show_ipv6_route.ref"
+ )
+
+
+def test_bfd_ospf_sessions_step2():
+ logger.info("Test (step 2): verify BFD peers for OSPF")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # BFD is just used on three routers
+ for rt in ["rt1", "rt2", "rt3"]:
+ router_compare_json_output(
+ rt, "show bfd peers json", "step2/show_bfd_peers.ref"
+ )
+
+
+def test_bfd_ospf_interface_failure_rt2_step3():
+ logger.info("Test (step 3): Check failover handling with RT2 down")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Let's kill the interface on rt2 and see what happens with the RIB and BFD on rt1
+ tgen.gears["rt2"].link_enable("eth-rt1", enabled=False)
+
+ # By default BFD provides a recovery time of 900ms plus jitter, so let's wait
+ # initial 2 seconds to let the CI not suffer.
+ # TODO: add check for array size
+ sleep(2)
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_rt2_down.ref", 1, 0
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt2_down.ref", 1, 0
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 1, 0
+ )
+
+ # Check recovery, this can take some time
+ tgen.gears["rt2"].link_enable("eth-rt1", enabled=True)
+
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref"
+ )
+
+
+def test_bfd_ospf_interface_failure_rt3_step3():
+ logger.info("Test (step 3): Check failover handling with RT3 down")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Let's kill the interface on rt3 and see what happens with the RIB and BFD on rt1
+ tgen.gears["rt3"].link_enable("eth-rt1", enabled=False)
+
+ # By default BFD provides a recovery time of 900ms plus jitter, so let's wait
+ # initial 2 seconds to let the CI not suffer.
+ # TODO: add check for array size
+ sleep(2)
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_rt3_down.ref", 1, 0
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt3_down.ref", 1, 0
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 1, 0
+ )
+
+ # Check recovery, this can take some time
+ tgen.gears["rt3"].link_enable("eth-rt1", enabled=True)
+
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref"
+ )
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/yang/frr-nexthop.yang b/yang/frr-nexthop.yang
index 619514de7d..2df2e2958e 100644
--- a/yang/frr-nexthop.yang
+++ b/yang/frr-nexthop.yang
@@ -61,7 +61,7 @@ module frr-nexthop {
type union {
type inet:ip-address;
type string {
- pattern '';
+ pattern "";
}
}
}
@@ -160,6 +160,7 @@ module frr-nexthop {
description
"The nexthop vrf name, if different from the route.";
}
+
leaf gateway {
type frr-nexthop:optional-ip-address;
description
@@ -173,15 +174,12 @@ module frr-nexthop {
}
leaf bh-type {
- when "../nh-type = 'blackhole'";
type blackhole-type;
description
"A blackhole sub-type, if the nexthop is a blackhole type.";
}
leaf onlink {
- when "../nh-type = 'ip4-ifindex' or
- ../nh-type = 'ip6-ifindex'";
type boolean;
default "false";
description
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c
index 51ce59c477..bf733e38f7 100644
--- a/zebra/dplane_fpm_nl.c
+++ b/zebra/dplane_fpm_nl.c
@@ -1236,7 +1236,13 @@ static int fpm_process_queue(struct thread *t)
if (ctx == NULL)
break;
- fpm_nl_enqueue(fnc, ctx);
+ /*
+ * Intentionally ignoring the return value
+ * as that we are ensuring that we can write to
+ * the output data in the STREAM_WRITEABLE
+ * check above, so we can ignore the return
+ */
+ (void)fpm_nl_enqueue(fnc, ctx);
/* Account the processed entries. */
processed_contexts++;