Merge pull request #17736 from opensourcerouting/table-direct

bgpd,lib,zebra: permit table-direct on VRFs
This commit is contained in:
Russ White 2025-01-28 10:24:00 -05:00 committed by GitHub
commit 7b6f686a9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 560 additions and 101 deletions

View File

@ -17575,12 +17575,6 @@ DEFUN (bgp_redistribute_ipv4_ospf,
if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
protocol = ZEBRA_ROUTE_OSPF; protocol = ZEBRA_ROUTE_OSPF;
else { else {
if (bgp->vrf_id != VRF_DEFAULT) {
vty_out(vty,
"%% Only default BGP instance can use '%s'\n",
argv[idx_ospf_table]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
if (strncmp(argv[idx_ospf_table]->arg, "table-direct", if (strncmp(argv[idx_ospf_table]->arg, "table-direct",
strlen("table-direct")) == 0) { strlen("table-direct")) == 0) {
protocol = ZEBRA_ROUTE_TABLE_DIRECT; protocol = ZEBRA_ROUTE_TABLE_DIRECT;
@ -17634,12 +17628,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap,
if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
protocol = ZEBRA_ROUTE_OSPF; protocol = ZEBRA_ROUTE_OSPF;
else { else {
if (bgp->vrf_id != VRF_DEFAULT) {
vty_out(vty,
"%% Only default BGP instance can use '%s'\n",
argv[idx_ospf_table]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
if (strncmp(argv[idx_ospf_table]->arg, "table-direct", if (strncmp(argv[idx_ospf_table]->arg, "table-direct",
strlen("table-direct")) == 0) { strlen("table-direct")) == 0) {
protocol = ZEBRA_ROUTE_TABLE_DIRECT; protocol = ZEBRA_ROUTE_TABLE_DIRECT;
@ -17697,12 +17685,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric,
if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
protocol = ZEBRA_ROUTE_OSPF; protocol = ZEBRA_ROUTE_OSPF;
else { else {
if (bgp->vrf_id != VRF_DEFAULT) {
vty_out(vty,
"%% Only default BGP instance can use '%s'\n",
argv[idx_ospf_table]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
if (strncmp(argv[idx_ospf_table]->arg, "table-direct", if (strncmp(argv[idx_ospf_table]->arg, "table-direct",
strlen("table-direct")) == 0) { strlen("table-direct")) == 0) {
protocol = ZEBRA_ROUTE_TABLE_DIRECT; protocol = ZEBRA_ROUTE_TABLE_DIRECT;
@ -17767,12 +17749,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap_metric,
if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
protocol = ZEBRA_ROUTE_OSPF; protocol = ZEBRA_ROUTE_OSPF;
else { else {
if (bgp->vrf_id != VRF_DEFAULT) {
vty_out(vty,
"%% Only default BGP instance can use '%s'\n",
argv[idx_ospf_table]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
if (strncmp(argv[idx_ospf_table]->arg, "table-direct", if (strncmp(argv[idx_ospf_table]->arg, "table-direct",
strlen("table-direct")) == 0) { strlen("table-direct")) == 0) {
protocol = ZEBRA_ROUTE_TABLE_DIRECT; protocol = ZEBRA_ROUTE_TABLE_DIRECT;
@ -17842,13 +17818,7 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric_rmap,
if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
protocol = ZEBRA_ROUTE_OSPF; protocol = ZEBRA_ROUTE_OSPF;
else { else {
if (bgp->vrf_id != VRF_DEFAULT) { if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) {
vty_out(vty,
"%% Only default BGP instance can use '%s'\n",
argv[idx_ospf_table]->arg);
return CMD_WARNING_CONFIG_FAILED;
} else if (strncmp(argv[idx_ospf_table]->arg, "table-direct",
strlen("table-direct")) == 0) {
protocol = ZEBRA_ROUTE_TABLE_DIRECT; protocol = ZEBRA_ROUTE_TABLE_DIRECT;
if (instance == RT_TABLE_MAIN || if (instance == RT_TABLE_MAIN ||
instance == RT_TABLE_LOCAL) { instance == RT_TABLE_LOCAL) {
@ -17911,12 +17881,6 @@ DEFUN (no_bgp_redistribute_ipv4_ospf,
if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
protocol = ZEBRA_ROUTE_OSPF; protocol = ZEBRA_ROUTE_OSPF;
else { else {
if (bgp->vrf_id != VRF_DEFAULT) {
vty_out(vty,
"%% Only default BGP instance can use '%s'\n",
argv[idx_ospf_table]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
if (strncmp(argv[idx_ospf_table]->arg, "table-direct", if (strncmp(argv[idx_ospf_table]->arg, "table-direct",
strlen("table-direct")) == 0) { strlen("table-direct")) == 0) {
protocol = ZEBRA_ROUTE_TABLE_DIRECT; protocol = ZEBRA_ROUTE_TABLE_DIRECT;

View File

@ -2042,11 +2042,34 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type,
/* Return if already redistribute flag is set. */ /* Return if already redistribute flag is set. */
if (instance) { if (instance) {
if (redist_check_instance(&zclient->mi_redist[afi][type], if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
instance)) /*
* When redistribution type is `table-direct` the
* instance means `table identification`.
*
* `table_id` support 32bit integers, however since
* `instance` is being overloaded to `table_id` it
* will only be possible to use the first 65535
* entries.
*
* Also the ZAPI must also support `int`
* (see `zebra_redistribute_add`).
*/
struct redist_table_direct table = {
.table_id = instance,
.vrf_id = bgp->vrf_id,
};
if (redist_lookup_table_direct(&zclient->mi_redist[afi][type], &table) !=
NULL)
return CMD_WARNING;
redist_add_table_direct(&zclient->mi_redist[afi][type], &table);
} else {
if (redist_check_instance(&zclient->mi_redist[afi][type], instance))
return CMD_WARNING; return CMD_WARNING;
redist_add_instance(&zclient->mi_redist[afi][type], instance); redist_add_instance(&zclient->mi_redist[afi][type], instance);
}
} else { } else {
if (vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id)) if (vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id))
return CMD_WARNING; return CMD_WARNING;
@ -2174,10 +2197,22 @@ int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type,
/* Return if zebra connection is disabled. */ /* Return if zebra connection is disabled. */
if (instance) { if (instance) {
if (!redist_check_instance(&zclient->mi_redist[afi][type], if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
instance)) struct redist_table_direct table = {
.table_id = instance,
.vrf_id = bgp->vrf_id,
};
if (redist_lookup_table_direct(&zclient->mi_redist[afi][type], &table) ==
NULL)
return CMD_WARNING; return CMD_WARNING;
redist_del_table_direct(&zclient->mi_redist[afi][type], &table);
} else {
if (!redist_check_instance(&zclient->mi_redist[afi][type], instance))
return CMD_WARNING;
redist_del_instance(&zclient->mi_redist[afi][type], instance); redist_del_instance(&zclient->mi_redist[afi][type], instance);
}
} else { } else {
if (!vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id)) if (!vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id))
return CMD_WARNING; return CMD_WARNING;

View File

@ -31,6 +31,7 @@
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient"); DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient");
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs"); DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs");
DEFINE_MTYPE_STATIC(LIB, REDIST_TABLE_DIRECT, "Redistribution table direct");
/* Zebra client events. */ /* Zebra client events. */
enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT };
@ -104,6 +105,11 @@ void zclient_free(struct zclient *zclient)
XFREE(MTYPE_ZCLIENT, zclient); XFREE(MTYPE_ZCLIENT, zclient);
} }
static void redist_free_instance(void *data)
{
XFREE(MTYPE_REDIST_INST, data);
}
unsigned short *redist_check_instance(struct redist_proto *red, unsigned short *redist_check_instance(struct redist_proto *red,
unsigned short instance) unsigned short instance)
{ {
@ -126,8 +132,10 @@ void redist_add_instance(struct redist_proto *red, unsigned short instance)
red->enabled = 1; red->enabled = 1;
if (!red->instances) if (!red->instances) {
red->instances = list_new(); red->instances = list_new();
red->instances->del = redist_free_instance;
}
in = XMALLOC(MTYPE_REDIST_INST, sizeof(unsigned short)); in = XMALLOC(MTYPE_REDIST_INST, sizeof(unsigned short));
*in = instance; *in = instance;
@ -143,23 +151,100 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance)
return; return;
listnode_delete(red->instances, id); listnode_delete(red->instances, id);
XFREE(MTYPE_REDIST_INST, id); red->instances->del(id);
if (!red->instances->count) { if (!red->instances->count) {
red->enabled = 0; red->enabled = 0;
list_delete(&red->instances); list_delete(&red->instances);
} }
} }
static void redist_free_table_direct(void *data)
{
XFREE(MTYPE_REDIST_TABLE_DIRECT, data);
}
struct redist_table_direct *redist_lookup_table_direct(const struct redist_proto *red,
const struct redist_table_direct *table)
{
struct redist_table_direct *ntable;
struct listnode *node;
if (red->instances == NULL)
return NULL;
for (ALL_LIST_ELEMENTS_RO(red->instances, node, ntable)) {
if (table->vrf_id != ntable->vrf_id)
continue;
if (table->table_id != ntable->table_id)
continue;
return ntable;
}
return NULL;
}
bool redist_table_direct_has_id(const struct redist_proto *red, int table_id)
{
struct redist_table_direct *table;
struct listnode *node;
if (red->instances == NULL)
return false;
for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) {
if (table->table_id != table_id)
continue;
return true;
}
return false;
}
void redist_add_table_direct(struct redist_proto *red, const struct redist_table_direct *table)
{
struct redist_table_direct *ntable;
ntable = redist_lookup_table_direct(red, table);
if (ntable != NULL)
return;
if (red->instances == NULL) {
red->instances = list_new();
red->instances->del = redist_free_table_direct;
}
red->enabled = 1;
ntable = XCALLOC(MTYPE_REDIST_TABLE_DIRECT, sizeof(*ntable));
ntable->vrf_id = table->vrf_id;
ntable->table_id = table->table_id;
listnode_add(red->instances, ntable);
}
void redist_del_table_direct(struct redist_proto *red, const struct redist_table_direct *table)
{
struct redist_table_direct *ntable;
ntable = redist_lookup_table_direct(red, table);
if (ntable == NULL)
return;
listnode_delete(red->instances, ntable);
red->instances->del(ntable);
if (red->instances->count == 0) {
red->enabled = 0;
list_delete(&red->instances);
}
}
void redist_del_all_instances(struct redist_proto *red) void redist_del_all_instances(struct redist_proto *red)
{ {
struct listnode *ln, *nn;
unsigned short *id;
if (!red->instances) if (!red->instances)
return; return;
for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id)) list_delete(&red->instances);
redist_del_instance(red, *id);
} }
/* Stop zebra client services. */ /* Stop zebra client services. */
@ -480,6 +565,17 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient,
return zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); return zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api);
} }
static void zclient_send_table_direct(struct zclient *zclient, afi_t afi, int type)
{
struct redist_table_direct *table;
struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT];
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(red->instances, node, table))
zebra_redistribute_send(type, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT,
table->table_id, table->vrf_id);
}
/* Send register requests to zebra daemon for the information in a VRF. */ /* Send register requests to zebra daemon for the information in a VRF. */
void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)
{ {
@ -513,6 +609,12 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)
if (!zclient->mi_redist[afi][i].enabled) if (!zclient->mi_redist[afi][i].enabled)
continue; continue;
if (i == ZEBRA_ROUTE_TABLE_DIRECT) {
zclient_send_table_direct(zclient, afi,
ZEBRA_REDISTRIBUTE_ADD);
continue;
}
struct listnode *node; struct listnode *node;
unsigned short *id; unsigned short *id;
@ -580,6 +682,12 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id)
if (!zclient->mi_redist[afi][i].enabled) if (!zclient->mi_redist[afi][i].enabled)
continue; continue;
if (i == ZEBRA_ROUTE_TABLE_DIRECT) {
zclient_send_table_direct(zclient, afi,
ZEBRA_REDISTRIBUTE_DELETE);
continue;
}
struct listnode *node; struct listnode *node;
unsigned short *id; unsigned short *id;
@ -4634,9 +4742,52 @@ static void zclient_read(struct event *thread)
zclient_event(ZCLIENT_READ, zclient); zclient_event(ZCLIENT_READ, zclient);
} }
static void zclient_redistribute_table_direct(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi,
int instance, int command)
{
struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT];
bool has_table;
struct redist_table_direct table = {
.vrf_id = vrf_id,
.table_id = instance,
};
has_table = redist_lookup_table_direct(red, &table);
if (command == ZEBRA_REDISTRIBUTE_ADD) {
if (has_table)
return;
redist_add_table_direct(red, &table);
} else {
if (!has_table)
return;
redist_del_table_direct(red, &table);
}
if (zclient->sock > 0)
zebra_redistribute_send(command, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, instance,
vrf_id);
}
void zclient_redistribute(int command, struct zclient *zclient, afi_t afi, void zclient_redistribute(int command, struct zclient *zclient, afi_t afi,
int type, unsigned short instance, vrf_id_t vrf_id) int type, unsigned short instance, vrf_id_t vrf_id)
{ {
/*
* When asking for table-direct redistribution the parameter
* `instance` has a different meaning: it means table
* identification.
*
* The table identification information is stored in
* `zclient->mi_redist` along with the VRF identification
* information in a pair (different from the usual single protocol
* instance value).
*/
if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
zclient_redistribute_table_direct(zclient, vrf_id, afi, instance, command);
return;
}
if (instance) { if (instance) {
if (command == ZEBRA_REDISTRIBUTE_ADD) { if (command == ZEBRA_REDISTRIBUTE_ADD) {

View File

@ -268,6 +268,21 @@ struct redist_proto {
struct list *instances; struct list *instances;
}; };
/**
* Redistribute table direct instance data structure: keeps the VRF
* that subscribed to the table ID.
*
* **NOTE**
* `table_id` is an integer because that is what the netlink interface
* uses for route attribute RTA_TABLE (32bit int), however the whole
* zclient API uses `unsigned short` (and CLI commands) so it will be
* limited to the range 1 to 65535.
*/
struct redist_table_direct {
vrf_id_t vrf_id;
int table_id;
};
struct zclient_capabilities { struct zclient_capabilities {
uint32_t ecmp; uint32_t ecmp;
bool mpls_enabled; bool mpls_enabled;
@ -924,6 +939,15 @@ extern void redist_add_instance(struct redist_proto *, unsigned short);
extern void redist_del_instance(struct redist_proto *, unsigned short); extern void redist_del_instance(struct redist_proto *, unsigned short);
extern void redist_del_all_instances(struct redist_proto *red); extern void redist_del_all_instances(struct redist_proto *red);
extern struct redist_table_direct *
redist_lookup_table_direct(const struct redist_proto *red, const struct redist_table_direct *table);
extern bool redist_table_direct_has_id(const struct redist_proto *red, int table_id);
extern void redist_add_table_direct(struct redist_proto *red,
const struct redist_table_direct *table);
extern void redist_del_table_direct(struct redist_proto *red,
const struct redist_table_direct *table);
/* /*
* Send to zebra that the specified vrf is using label to resolve * Send to zebra that the specified vrf is using label to resolve
* itself for L3VPN's. Repeated calls of this function with * itself for L3VPN's. Repeated calls of this function with

View File

@ -0,0 +1,31 @@
log commands
!
debug bgp zebra
debug zebra events
!
ip route 10.254.254.1/32 lo table 2000
ip route 10.254.254.2/32 lo table 2000
ip route 10.254.254.3/32 lo table 2000
!
interface r1-eth0
ip address 192.168.10.1/24
!
interface r1-eth1 vrf blue
ip address 192.168.20.1/24
!
router bgp 65001
no bgp ebgp-requires-policy
no bgp network import-check
neighbor 192.168.10.2 remote-as external
address-family ipv4 unicast
redistribute table-direct 2000
exit-address-family
!
router bgp 65001 vrf blue
no bgp ebgp-requires-policy
no bgp network import-check
neighbor 192.168.20.2 remote-as external
address-family ipv4 unicast
redistribute table-direct 2000
exit-address-family
!

View File

@ -0,0 +1,10 @@
log commands
!
interface r2-eth0
ip address 192.168.10.2/24
!
router bgp 65002
no bgp ebgp-requires-policy
no bgp network import-check
neighbor 192.168.10.1 remote-as external
!

View File

@ -0,0 +1,10 @@
log commands
!
interface r3-eth0
ip address 192.168.20.2/24
!
router bgp 65003
no bgp ebgp-requires-policy
no bgp network import-check
neighbor 192.168.20.1 remote-as external
!

View File

@ -0,0 +1,201 @@
#!/usr/bin/env python
# SPDX-License-Identifier: ISC
#
# test_bgp_table_direct_topo1.py
# Part of NetDEF Topology Tests
#
# Copyright (c) 2025 by
# Network Device Education Foundation, Inc. ("NetDEF")
#
"""
test_bgp_table_direct_topo1.py: Test the FRR PIM MSDP peer.
"""
import os
import sys
import json
from functools import partial
import re
import pytest
# 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
# Required to instantiate the topology builder class.
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
from lib.pim import McastTesterHelper
pytestmark = [pytest.mark.bgpd, pytest.mark.pimd]
app_helper = McastTesterHelper()
def build_topo(tgen):
"""
+----+ +----+
| r1 | <-> | r2 |
+----+ +----+
|
| +----+
--------| r3 |
+----+
"""
# Create 3 routers
for routern in range(1, 4):
tgen.add_router(f"r{routern}")
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
switch = tgen.add_switch("s2")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r3"])
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
for _, router in router_list.items():
router.load_frr_config(os.path.join(CWD, f"{router.name}/frr.conf"))
tgen.gears["r1"].run("ip link add blue type vrf table 10")
tgen.gears["r1"].run("ip link set blue up")
tgen.gears["r1"].run("ip link set r1-eth1 master blue")
# Initialize all routers.
tgen.start_router()
app_helper.init(tgen)
def teardown_module():
"Teardown the pytest environment"
tgen = get_topogen()
app_helper.cleanup()
tgen.stop_topology()
def expect_bgp_route(router, iptype, route, missing=False):
"Wait until route is present on RIB for protocol."
if missing:
logger.info("waiting route {} go missing in {}".format(route, router))
else:
logger.info("waiting route {} in {}".format(route, router))
tgen = get_topogen()
expected_output = {route: [{"protocol": "bgp"}]}
wait_time = 130
if missing:
expected_output = {route: None}
wait_time = 5
test_func = partial(
topotest.router_json_cmp,
tgen.gears[router],
"show {} route json".format(iptype),
expected_output
)
_, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
assertmsg = f'"{router}" convergence failure'
assert result is None, assertmsg
def test_bgp_convergence():
"Wait for BGP protocol convergence"
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("waiting for protocols to converge")
# Wait for R2
expect_bgp_route("r2", "ip", "10.254.254.1/32")
expect_bgp_route("r2", "ip", "10.254.254.2/32")
expect_bgp_route("r2", "ip", "10.254.254.3/32")
# Wait for R3
expect_bgp_route("r3", "ip", "10.254.254.1/32")
expect_bgp_route("r3", "ip", "10.254.254.2/32")
expect_bgp_route("r3", "ip", "10.254.254.3/32")
def test_route_change_convergence():
"Change routes in table 2000 to test zebra redistribution."
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
tgen.gears["r1"].vtysh_cmd("""
configure terminal
no ip route 10.254.254.2/32 lo table 2000
ip route 10.254.254.10/32 lo table 2000
""")
# Check R2
expect_bgp_route("r2", "ip", "10.254.254.2/32", missing=True)
expect_bgp_route("r2", "ip", "10.254.254.10/32")
# Check R3
expect_bgp_route("r3", "ip", "10.254.254.2/32", missing=True)
expect_bgp_route("r3", "ip", "10.254.254.10/32")
def test_configuration_removal_convergence():
"Remove table direct configuration and check if routes went missing."
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
tgen.gears["r1"].vtysh_cmd("""
configure terminal
router bgp 65001
address-family ipv4 unicast
no redistribute table-direct 2000
exit-address-family
exit
router bgp 65001 vrf blue
address-family ipv4 unicast
no redistribute table-direct 2000
exit-address-family
exit
""")
# Check R2
expect_bgp_route("r2", "ip", "10.254.254.1/32", missing=True)
expect_bgp_route("r2", "ip", "10.254.254.3/32", missing=True)
expect_bgp_route("r2", "ip", "10.254.254.10/32", missing=True)
# Check R3
expect_bgp_route("r3", "ip", "10.254.254.1/32", missing=True)
expect_bgp_route("r3", "ip", "10.254.254.3/32", missing=True)
expect_bgp_route("r3", "ip", "10.254.254.10/32", missing=True)
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))

View File

@ -82,8 +82,8 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)
RNODE_FOREACH_RE (rn, newre) { RNODE_FOREACH_RE (rn, newre) {
if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED)) if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED))
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
client, rn, newre, false); newre, NULL);
} }
route_unlock_node(rn); route_unlock_node(rn);
@ -91,6 +91,24 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)
} }
/* Redistribute routes. */ /* Redistribute routes. */
static void redistribute_table_direct(struct zserv *client, int type, const struct route_node *rn,
const struct route_entry *re)
{
struct redist_table_direct *table;
struct redist_proto *red;
struct listnode *node;
afi_t afi = family2afi(rn->p.family);
red = &client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT];
for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) {
if (table->table_id != (int)re->table)
continue;
zsend_redistribute_route(type, client, rn, re, &table->vrf_id);
}
}
static void zebra_redistribute(struct zserv *client, int type, static void zebra_redistribute(struct zserv *client, int type,
unsigned short instance, struct zebra_vrf *zvrf, unsigned short instance, struct zebra_vrf *zvrf,
int afi) int afi)
@ -102,13 +120,9 @@ static void zebra_redistribute(struct zserv *client, int type,
vrf_id_t vrf_id = zvrf_id(zvrf); vrf_id_t vrf_id = zvrf_id(zvrf);
if (type == ZEBRA_ROUTE_TABLE_DIRECT) { if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
if (vrf_id == VRF_DEFAULT) { table = zebra_router_find_table(zvrf, instance, afi, SAFI_UNICAST);
table = zebra_router_find_table(zvrf, instance, afi,
SAFI_UNICAST);
type = ZEBRA_ROUTE_ALL; type = ZEBRA_ROUTE_ALL;
is_table_direct = true; is_table_direct = true;
} else
return;
} else } else
table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id);
@ -140,15 +154,20 @@ static void zebra_redistribute(struct zserv *client, int type,
if (!zebra_check_addr(&rn->p)) if (!zebra_check_addr(&rn->p))
continue; continue;
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, if (is_table_direct)
client, rn, newre, is_table_direct); redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn,
newre);
else
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
newre, NULL);
} }
} }
/* /*
* Function to return a valid table id value if table-direct is used * Checks if the route entry can be used as table-direct or not.
* return 0 otherwise * `table-direct` routes always belong to `VRF_DEFAULT` and has an table
* This function can be called only if zebra_redistribute_check returns TRUE * ID different than the VRF it belongs (example main VRF table is 254,
* so in order to be `table-direct` the route's table ID must be != 254).
*/ */
static bool zebra_redistribute_is_table_direct(const struct route_entry *re) static bool zebra_redistribute_is_table_direct(const struct route_entry *re)
{ {
@ -177,15 +196,14 @@ static bool zebra_redistribute_check(const struct route_node *rn,
afi = family2afi(rn->p.family); afi = family2afi(rn->p.family);
zvrf = zebra_vrf_lookup_by_id(re->vrf_id); zvrf = zebra_vrf_lookup_by_id(re->vrf_id);
if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table) { if (zvrf->table_id != re->table) {
if (re->table && /*
redist_check_instance(&client->mi_redist * Routes with table ID different from VRFs can be used as
[afi][ZEBRA_ROUTE_TABLE_DIRECT], * `table-direct` if enabled.
re->table)) {
/* table-direct redistribution only for route entries which
* are on the default vrf, and that have table id different
* from the default table.
*/ */
if (re->table &&
redist_table_direct_has_id(&client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT],
re->table)) {
return true; return true;
} }
return false; return false;
@ -227,7 +245,6 @@ void redistribute_update(const struct route_node *rn,
{ {
struct listnode *node, *nnode; struct listnode *node, *nnode;
struct zserv *client; struct zserv *client;
bool is_table_direct;
if (IS_ZEBRA_DEBUG_RIB) if (IS_ZEBRA_DEBUG_RIB)
zlog_debug( zlog_debug(
@ -242,7 +259,6 @@ void redistribute_update(const struct route_node *rn,
return; return;
} }
for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
if (zebra_redistribute_check(rn, re, client)) { if (zebra_redistribute_check(rn, re, client)) {
if (IS_ZEBRA_DEBUG_RIB) { if (IS_ZEBRA_DEBUG_RIB) {
@ -253,15 +269,19 @@ void redistribute_update(const struct route_node *rn,
re->vrf_id, re->table, re->type, re->vrf_id, re->table, re->type,
re->distance, re->metric); re->distance, re->metric);
} }
is_table_direct = zebra_redistribute_is_table_direct(re); if (zebra_redistribute_is_table_direct(re))
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn,
client, rn, re, re);
is_table_direct); else
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
re, NULL);
} else if (zebra_redistribute_check(rn, prev_re, client)) { } else if (zebra_redistribute_check(rn, prev_re, client)) {
is_table_direct = zebra_redistribute_is_table_direct(prev_re); if (zebra_redistribute_is_table_direct(prev_re))
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn,
client, rn, prev_re, prev_re);
is_table_direct); else
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn,
prev_re, NULL);
} }
} }
} }
@ -281,7 +301,6 @@ void redistribute_delete(const struct route_node *rn,
struct listnode *node, *nnode; struct listnode *node, *nnode;
struct zserv *client; struct zserv *client;
vrf_id_t vrfid; vrf_id_t vrfid;
bool is_table_direct;
if (old_re) if (old_re)
vrfid = old_re->vrf_id; vrfid = old_re->vrf_id;
@ -344,10 +363,12 @@ void redistribute_delete(const struct route_node *rn,
* happy. * happy.
*/ */
assert(old_re); assert(old_re);
is_table_direct = zebra_redistribute_is_table_direct(old_re); if (zebra_redistribute_is_table_direct(old_re))
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn,
client, rn, old_re, old_re);
is_table_direct); else
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn,
old_re, NULL);
} }
} }
} }
@ -383,8 +404,16 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS)
} }
if (instance) { if (instance) {
if (!redist_check_instance(&client->mi_redist[afi][type], if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
instance)) { struct redist_table_direct table = {
.vrf_id = zvrf->vrf->vrf_id,
.table_id = instance,
};
if (!redist_lookup_table_direct(&client->mi_redist[afi][type], &table)) {
redist_add_table_direct(&client->mi_redist[afi][type], &table);
zebra_redistribute(client, type, instance, zvrf, afi);
}
} else if (!redist_check_instance(&client->mi_redist[afi][type], instance)) {
redist_add_instance(&client->mi_redist[afi][type], redist_add_instance(&client->mi_redist[afi][type],
instance); instance);
zebra_redistribute(client, type, instance, zvrf, afi); zebra_redistribute(client, type, instance, zvrf, afi);
@ -443,7 +472,13 @@ void zebra_redistribute_delete(ZAPI_HANDLER_ARGS)
* themselves should keep track of the received routes from zebra and * themselves should keep track of the received routes from zebra and
* withdraw them when necessary. * withdraw them when necessary.
*/ */
if (instance) if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
struct redist_table_direct table = {
.vrf_id = zvrf->vrf->vrf_id,
.table_id = instance,
};
redist_del_table_direct(&client->mi_redist[afi][type], &table);
} else if (instance)
redist_del_instance(&client->mi_redist[afi][type], instance); redist_del_instance(&client->mi_redist[afi][type], instance);
else else
vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf)); vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf));

View File

@ -509,9 +509,8 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp)
return zserv_send_message(client, s); return zserv_send_message(client, s);
} }
int zsend_redistribute_route(int cmd, struct zserv *client, int zsend_redistribute_route(int cmd, struct zserv *client, const struct route_node *rn,
const struct route_node *rn, const struct route_entry *re, vrf_id_t *to_vrf)
const struct route_entry *re, bool is_table_direct)
{ {
struct zapi_route api; struct zapi_route api;
struct zapi_nexthop *api_nh; struct zapi_nexthop *api_nh;
@ -527,9 +526,10 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
api.vrf_id = re->vrf_id; api.vrf_id = re->vrf_id;
api.type = re->type; api.type = re->type;
api.safi = SAFI_UNICAST; api.safi = SAFI_UNICAST;
if (is_table_direct) { if (to_vrf != NULL) {
api.instance = re->table; api.instance = re->table;
api.type = ZEBRA_ROUTE_TABLE_DIRECT; api.type = ZEBRA_ROUTE_TABLE_DIRECT;
api.vrf_id = *to_vrf;
} else } else
api.instance = re->instance; api.instance = re->instance;
api.flags = re->flags; api.flags = re->flags;
@ -598,7 +598,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
/* Attributes. */ /* Attributes. */
SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
if (is_table_direct) if (to_vrf != NULL)
api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT; api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT;
else else
api.distance = re->distance; api.distance = re->distance;

View File

@ -51,10 +51,8 @@ extern void nbr_connected_delete_ipv6(struct interface *ifp,
struct in6_addr *address); struct in6_addr *address);
extern int zsend_interface_update(int cmd, struct zserv *client, extern int zsend_interface_update(int cmd, struct zserv *client,
struct interface *ifp); struct interface *ifp);
extern int zsend_redistribute_route(int cmd, struct zserv *zclient, extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct route_node *rn,
const struct route_node *rn, const struct route_entry *re, vrf_id_t *to_vrf);
const struct route_entry *re,
bool is_table_direct);
extern int zsend_router_id_update(struct zserv *zclient, afi_t afi, extern int zsend_router_id_update(struct zserv *zclient, afi_t afi,
struct prefix *p, vrf_id_t vrf_id); struct prefix *p, vrf_id_t vrf_id);