mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 06:34:51 +00:00
Merge pull request #17736 from opensourcerouting/table-direct
bgpd,lib,zebra: permit table-direct on VRFs
This commit is contained in:
commit
7b6f686a9f
@ -17575,12 +17575,6 @@ DEFUN (bgp_redistribute_ipv4_ospf,
|
||||
if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
|
||||
protocol = ZEBRA_ROUTE_OSPF;
|
||||
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",
|
||||
strlen("table-direct")) == 0) {
|
||||
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)
|
||||
protocol = ZEBRA_ROUTE_OSPF;
|
||||
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",
|
||||
strlen("table-direct")) == 0) {
|
||||
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)
|
||||
protocol = ZEBRA_ROUTE_OSPF;
|
||||
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",
|
||||
strlen("table-direct")) == 0) {
|
||||
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)
|
||||
protocol = ZEBRA_ROUTE_OSPF;
|
||||
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",
|
||||
strlen("table-direct")) == 0) {
|
||||
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)
|
||||
protocol = ZEBRA_ROUTE_OSPF;
|
||||
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;
|
||||
} else if (strncmp(argv[idx_ospf_table]->arg, "table-direct",
|
||||
strlen("table-direct")) == 0) {
|
||||
if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) {
|
||||
protocol = ZEBRA_ROUTE_TABLE_DIRECT;
|
||||
if (instance == RT_TABLE_MAIN ||
|
||||
instance == RT_TABLE_LOCAL) {
|
||||
@ -17911,12 +17881,6 @@ DEFUN (no_bgp_redistribute_ipv4_ospf,
|
||||
if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
|
||||
protocol = ZEBRA_ROUTE_OSPF;
|
||||
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",
|
||||
strlen("table-direct")) == 0) {
|
||||
protocol = ZEBRA_ROUTE_TABLE_DIRECT;
|
||||
|
@ -2042,11 +2042,34 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type,
|
||||
|
||||
/* Return if already redistribute flag is set. */
|
||||
if (instance) {
|
||||
if (redist_check_instance(&zclient->mi_redist[afi][type],
|
||||
instance))
|
||||
return CMD_WARNING;
|
||||
if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
|
||||
/*
|
||||
* 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_instance(&zclient->mi_redist[afi][type], instance);
|
||||
redist_add_table_direct(&zclient->mi_redist[afi][type], &table);
|
||||
} else {
|
||||
if (redist_check_instance(&zclient->mi_redist[afi][type], instance))
|
||||
return CMD_WARNING;
|
||||
|
||||
redist_add_instance(&zclient->mi_redist[afi][type], instance);
|
||||
}
|
||||
} else {
|
||||
if (vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id))
|
||||
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. */
|
||||
if (instance) {
|
||||
if (!redist_check_instance(&zclient->mi_redist[afi][type],
|
||||
instance))
|
||||
return CMD_WARNING;
|
||||
redist_del_instance(&zclient->mi_redist[afi][type], instance);
|
||||
if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
|
||||
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_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);
|
||||
}
|
||||
} else {
|
||||
if (!vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id))
|
||||
return CMD_WARNING;
|
||||
|
165
lib/zclient.c
165
lib/zclient.c
@ -31,6 +31,7 @@
|
||||
|
||||
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient");
|
||||
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs");
|
||||
DEFINE_MTYPE_STATIC(LIB, REDIST_TABLE_DIRECT, "Redistribution table direct");
|
||||
|
||||
/* Zebra client events. */
|
||||
enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT };
|
||||
@ -104,6 +105,11 @@ void zclient_free(struct 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 instance)
|
||||
{
|
||||
@ -126,8 +132,10 @@ void redist_add_instance(struct redist_proto *red, unsigned short instance)
|
||||
|
||||
red->enabled = 1;
|
||||
|
||||
if (!red->instances)
|
||||
if (!red->instances) {
|
||||
red->instances = list_new();
|
||||
red->instances->del = redist_free_instance;
|
||||
}
|
||||
|
||||
in = XMALLOC(MTYPE_REDIST_INST, sizeof(unsigned short));
|
||||
*in = instance;
|
||||
@ -143,23 +151,100 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance)
|
||||
return;
|
||||
|
||||
listnode_delete(red->instances, id);
|
||||
XFREE(MTYPE_REDIST_INST, id);
|
||||
red->instances->del(id);
|
||||
if (!red->instances->count) {
|
||||
red->enabled = 0;
|
||||
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)
|
||||
{
|
||||
struct listnode *ln, *nn;
|
||||
unsigned short *id;
|
||||
|
||||
if (!red->instances)
|
||||
return;
|
||||
|
||||
for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id))
|
||||
redist_del_instance(red, *id);
|
||||
list_delete(&red->instances);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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. */
|
||||
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)
|
||||
continue;
|
||||
|
||||
if (i == ZEBRA_ROUTE_TABLE_DIRECT) {
|
||||
zclient_send_table_direct(zclient, afi,
|
||||
ZEBRA_REDISTRIBUTE_ADD);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct listnode *node;
|
||||
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)
|
||||
continue;
|
||||
|
||||
if (i == ZEBRA_ROUTE_TABLE_DIRECT) {
|
||||
zclient_send_table_direct(zclient, afi,
|
||||
ZEBRA_REDISTRIBUTE_DELETE);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct listnode *node;
|
||||
unsigned short *id;
|
||||
|
||||
@ -4634,9 +4742,52 @@ static void zclient_read(struct event *thread)
|
||||
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,
|
||||
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 (command == ZEBRA_REDISTRIBUTE_ADD) {
|
||||
|
@ -268,6 +268,21 @@ struct redist_proto {
|
||||
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 {
|
||||
uint32_t ecmp;
|
||||
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_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
|
||||
* itself for L3VPN's. Repeated calls of this function with
|
||||
|
0
tests/topotests/bgp_table_direct_topo1/__init__.py
Normal file
0
tests/topotests/bgp_table_direct_topo1/__init__.py
Normal file
31
tests/topotests/bgp_table_direct_topo1/r1/frr.conf
Normal file
31
tests/topotests/bgp_table_direct_topo1/r1/frr.conf
Normal 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
|
||||
!
|
10
tests/topotests/bgp_table_direct_topo1/r2/frr.conf
Normal file
10
tests/topotests/bgp_table_direct_topo1/r2/frr.conf
Normal 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
|
||||
!
|
10
tests/topotests/bgp_table_direct_topo1/r3/frr.conf
Normal file
10
tests/topotests/bgp_table_direct_topo1/r3/frr.conf
Normal 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
|
||||
!
|
@ -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))
|
@ -82,8 +82,8 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)
|
||||
|
||||
RNODE_FOREACH_RE (rn, newre) {
|
||||
if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED))
|
||||
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD,
|
||||
client, rn, newre, false);
|
||||
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
|
||||
newre, NULL);
|
||||
}
|
||||
|
||||
route_unlock_node(rn);
|
||||
@ -91,6 +91,24 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id)
|
||||
}
|
||||
|
||||
/* 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,
|
||||
unsigned short instance, struct zebra_vrf *zvrf,
|
||||
int afi)
|
||||
@ -102,13 +120,9 @@ static void zebra_redistribute(struct zserv *client, int type,
|
||||
vrf_id_t vrf_id = zvrf_id(zvrf);
|
||||
|
||||
if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
|
||||
if (vrf_id == VRF_DEFAULT) {
|
||||
table = zebra_router_find_table(zvrf, instance, afi,
|
||||
SAFI_UNICAST);
|
||||
type = ZEBRA_ROUTE_ALL;
|
||||
is_table_direct = true;
|
||||
} else
|
||||
return;
|
||||
table = zebra_router_find_table(zvrf, instance, afi, SAFI_UNICAST);
|
||||
type = ZEBRA_ROUTE_ALL;
|
||||
is_table_direct = true;
|
||||
} else
|
||||
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))
|
||||
continue;
|
||||
|
||||
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD,
|
||||
client, rn, newre, is_table_direct);
|
||||
if (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
|
||||
* return 0 otherwise
|
||||
* This function can be called only if zebra_redistribute_check returns TRUE
|
||||
* Checks if the route entry can be used as table-direct or not.
|
||||
* `table-direct` routes always belong to `VRF_DEFAULT` and has an table
|
||||
* 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)
|
||||
{
|
||||
@ -177,15 +196,14 @@ static bool zebra_redistribute_check(const struct route_node *rn,
|
||||
|
||||
afi = family2afi(rn->p.family);
|
||||
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) {
|
||||
/*
|
||||
* Routes with table ID different from VRFs can be used as
|
||||
* `table-direct` if enabled.
|
||||
*/
|
||||
if (re->table &&
|
||||
redist_check_instance(&client->mi_redist
|
||||
[afi][ZEBRA_ROUTE_TABLE_DIRECT],
|
||||
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.
|
||||
*/
|
||||
redist_table_direct_has_id(&client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT],
|
||||
re->table)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -227,7 +245,6 @@ void redistribute_update(const struct route_node *rn,
|
||||
{
|
||||
struct listnode *node, *nnode;
|
||||
struct zserv *client;
|
||||
bool is_table_direct;
|
||||
|
||||
if (IS_ZEBRA_DEBUG_RIB)
|
||||
zlog_debug(
|
||||
@ -242,7 +259,6 @@ void redistribute_update(const struct route_node *rn,
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
|
||||
if (zebra_redistribute_check(rn, re, client)) {
|
||||
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->distance, re->metric);
|
||||
}
|
||||
is_table_direct = zebra_redistribute_is_table_direct(re);
|
||||
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD,
|
||||
client, rn, re,
|
||||
is_table_direct);
|
||||
if (zebra_redistribute_is_table_direct(re))
|
||||
redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn,
|
||||
re);
|
||||
else
|
||||
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn,
|
||||
re, NULL);
|
||||
} else if (zebra_redistribute_check(rn, prev_re, client)) {
|
||||
is_table_direct = zebra_redistribute_is_table_direct(prev_re);
|
||||
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL,
|
||||
client, rn, prev_re,
|
||||
is_table_direct);
|
||||
if (zebra_redistribute_is_table_direct(prev_re))
|
||||
redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn,
|
||||
prev_re);
|
||||
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 zserv *client;
|
||||
vrf_id_t vrfid;
|
||||
bool is_table_direct;
|
||||
|
||||
if (old_re)
|
||||
vrfid = old_re->vrf_id;
|
||||
@ -344,10 +363,12 @@ void redistribute_delete(const struct route_node *rn,
|
||||
* happy.
|
||||
*/
|
||||
assert(old_re);
|
||||
is_table_direct = zebra_redistribute_is_table_direct(old_re);
|
||||
zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL,
|
||||
client, rn, old_re,
|
||||
is_table_direct);
|
||||
if (zebra_redistribute_is_table_direct(old_re))
|
||||
redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn,
|
||||
old_re);
|
||||
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 (!redist_check_instance(&client->mi_redist[afi][type],
|
||||
instance)) {
|
||||
if (type == ZEBRA_ROUTE_TABLE_DIRECT) {
|
||||
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],
|
||||
instance);
|
||||
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
|
||||
* 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);
|
||||
else
|
||||
vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf));
|
||||
|
@ -509,9 +509,8 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp)
|
||||
return zserv_send_message(client, s);
|
||||
}
|
||||
|
||||
int zsend_redistribute_route(int cmd, struct zserv *client,
|
||||
const struct route_node *rn,
|
||||
const struct route_entry *re, bool is_table_direct)
|
||||
int zsend_redistribute_route(int cmd, struct zserv *client, const struct route_node *rn,
|
||||
const struct route_entry *re, vrf_id_t *to_vrf)
|
||||
{
|
||||
struct zapi_route api;
|
||||
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.type = re->type;
|
||||
api.safi = SAFI_UNICAST;
|
||||
if (is_table_direct) {
|
||||
if (to_vrf != NULL) {
|
||||
api.instance = re->table;
|
||||
api.type = ZEBRA_ROUTE_TABLE_DIRECT;
|
||||
api.vrf_id = *to_vrf;
|
||||
} else
|
||||
api.instance = re->instance;
|
||||
api.flags = re->flags;
|
||||
@ -598,7 +598,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
|
||||
|
||||
/* Attributes. */
|
||||
SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
|
||||
if (is_table_direct)
|
||||
if (to_vrf != NULL)
|
||||
api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT;
|
||||
else
|
||||
api.distance = re->distance;
|
||||
|
@ -51,10 +51,8 @@ extern void nbr_connected_delete_ipv6(struct interface *ifp,
|
||||
struct in6_addr *address);
|
||||
extern int zsend_interface_update(int cmd, struct zserv *client,
|
||||
struct interface *ifp);
|
||||
extern int zsend_redistribute_route(int cmd, struct zserv *zclient,
|
||||
const struct route_node *rn,
|
||||
const struct route_entry *re,
|
||||
bool is_table_direct);
|
||||
extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct route_node *rn,
|
||||
const struct route_entry *re, vrf_id_t *to_vrf);
|
||||
|
||||
extern int zsend_router_id_update(struct zserv *zclient, afi_t afi,
|
||||
struct prefix *p, vrf_id_t vrf_id);
|
||||
|
Loading…
Reference in New Issue
Block a user