zebra: rework RA handling for vrf-lite

Rework RA handling for vrf-lite scenarios.

Before we were using a single FD descriptor for polling
across multiple zvrf's. This would cause us to hit this
assert() in some bgp unnumbered and vrrp configs:

```
/*
 * What happens if we have a thread already
 * created for this event?
 */
if (thread_array[fd])
	assert(!"Thread already scheduled for file descriptor");
```

We were scheduling a thread_read on the same FD for every zvrf.

With vrf-lite, RAs and ARPs are not vrf-bound, so we can just use one
rtadv instance to manage them for all VRFs. We will choose the default
VRF for this.

This patch removes the rtadv_sock altogether for zrouter and moves the
functionality this represented to the default VRF. All RAs will be
handled in the default VRF under vrf-lite configs with only one poll
thread started for it.

This patch also extends how we track subscribed interfaces (s or msec)
to use an actual sorted list by interface names rather than just a
counter. With multiple daemons turning interfaces/on/off these counters
can get very wrong during ifup/down events. Making them a sorted list
prevents this from happening by preventing duplicates.

With netns-vrf's nothing should change other than the interface list.

Signed-off-by: Stephen Worley <sworley@nvidia.com>
This commit is contained in:
Stephen Worley 2021-05-26 17:36:16 -04:00
parent fdb7f5d54f
commit 7c2ddfb976
7 changed files with 290 additions and 76 deletions

View File

@ -186,7 +186,6 @@ static void sigint(void)
work_queue_free_and_null(&zrouter.lsp_process_q);
vrf_terminate();
rtadv_terminate();
ns_walk_func(zebra_ns_early_shutdown, NULL, NULL);
zebra_ns_notify_close();

View File

@ -288,16 +288,30 @@ DECLARE_LIST(re_list, struct route_entry, next);
#define RNODE_NEXT_RE(rn, re) RE_DEST_NEXT_ROUTE(rib_dest_from_rnode(rn), re)
#if defined(HAVE_RTADV)
PREDECL_SORTLIST_UNIQ(adv_if_list);
/* Structure which hold status of router advertisement. */
struct rtadv {
int sock;
int adv_if_count;
int adv_msec_if_count;
struct adv_if_list_head adv_if;
struct adv_if_list_head adv_msec_if;
struct thread *ra_read;
struct thread *ra_timer;
};
/* adv list node */
struct adv_if {
char name[INTERFACE_NAMSIZ];
struct adv_if_list_item list_item;
};
static int adv_if_cmp(const struct adv_if *a, const struct adv_if *b)
{
return if_cmp_name_func(a->name, b->name);
}
DECLARE_SORTLIST_UNIQ(adv_if_list, struct adv_if, list_item, adv_if_cmp);
#endif /* HAVE_RTADV */
/*

View File

@ -54,6 +54,7 @@ extern struct zebra_privs_t zserv_privs;
#endif
DEFINE_MTYPE_STATIC(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix");
DEFINE_MTYPE_STATIC(ZEBRA, ADV_IF, "Advertised Interface");
#ifdef OPEN_BSD
#include <netinet/icmp6.h>
@ -92,11 +93,13 @@ static void rtadv_event(struct zebra_vrf *, enum rtadv_event, int);
static int if_join_all_router(int, struct interface *);
static int if_leave_all_router(int, struct interface *);
static int rtadv_get_socket(struct zebra_vrf *zvrf)
static struct zebra_vrf *rtadv_interface_get_zvrf(const struct interface *ifp)
{
if (zvrf->rtadv.sock > 0)
return zvrf->rtadv.sock;
return zrouter.rtadv_sock;
/* We use the default vrf for rtadv handling except in netns */
if (!vrf_is_backend_netns())
return vrf_info_lookup(VRF_DEFAULT);
return vrf_info_lookup(ifp->vrf_id);
}
static int rtadv_increment_received(struct zebra_vrf *zvrf, ifindex_t *ifindex)
@ -480,7 +483,7 @@ static int rtadv_timer(struct thread *thread)
int period;
zvrf->rtadv.ra_timer = NULL;
if (zvrf->rtadv.adv_msec_if_count == 0) {
if (adv_if_list_count(&zvrf->rtadv.adv_msec_if) == 0) {
period = 1000; /* 1 s */
rtadv_event(zvrf, RTADV_TIMER, 1 /* 1 s */);
} else {
@ -520,8 +523,8 @@ static int rtadv_timer(struct thread *thread)
ifp->ifindex);
}
rtadv_send_packet(rtadv_get_socket(zvrf),
ifp, RA_ENABLE);
rtadv_send_packet(zvrf->rtadv.sock, ifp,
RA_ENABLE);
} else {
zif->rtadv.AdvIntervalTimer -= period;
if (zif->rtadv.AdvIntervalTimer <= 0) {
@ -534,8 +537,8 @@ static int rtadv_timer(struct thread *thread)
zif->rtadv
.MaxRtrAdvInterval;
rtadv_send_packet(
rtadv_get_socket(zvrf),
ifp, RA_ENABLE);
zvrf->rtadv.sock, ifp,
RA_ENABLE);
}
}
}
@ -546,9 +549,10 @@ static int rtadv_timer(struct thread *thread)
static void rtadv_process_solicit(struct interface *ifp)
{
struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id);
struct zebra_vrf *zvrf;
struct zebra_if *zif;
zvrf = rtadv_interface_get_zvrf(ifp);
assert(zvrf);
zif = ifp->info;
@ -565,7 +569,7 @@ static void rtadv_process_solicit(struct interface *ifp)
if ((zif->rtadv.UseFastRexmit)
|| (zif->rtadv.AdvIntervalTimer <=
(zif->rtadv.MaxRtrAdvInterval - MIN_DELAY_BETWEEN_RAS))) {
rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_ENABLE);
rtadv_send_packet(zvrf->rtadv.sock, ifp, RA_ENABLE);
zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval;
} else
zif->rtadv.AdvIntervalTimer = MIN_DELAY_BETWEEN_RAS;
@ -788,7 +792,7 @@ static int rtadv_read(struct thread *thread)
zvrf->rtadv.ra_read = NULL;
/* Register myself. */
rtadv_event(zvrf, RTADV_READ, sock);
rtadv_event(zvrf, RTADV_READ, 0);
len = rtadv_recv_packet(zvrf, sock, buf, sizeof(buf), &from, &ifindex,
&hoplimit);
@ -862,6 +866,201 @@ static int rtadv_make_socket(ns_id_t ns_id)
return sock;
}
static struct adv_if *adv_if_new(const char *name)
{
struct adv_if *new;
new = XCALLOC(MTYPE_ADV_IF, sizeof(struct adv_if));
strlcpy(new->name, name, sizeof(new->name));
return new;
}
static void adv_if_free(struct adv_if *adv_if)
{
XFREE(MTYPE_ADV_IF, adv_if);
}
static bool adv_if_is_empty_internal(struct adv_if_list_head *adv_if_head)
{
return adv_if_list_count(adv_if_head) ? false : true;
}
static struct adv_if *adv_if_add_internal(struct adv_if_list_head *adv_if_head,
const char *name)
{
struct adv_if adv_if_lookup = {};
struct adv_if *adv_if = NULL;
strlcpy(adv_if_lookup.name, name, sizeof(adv_if_lookup.name));
adv_if = adv_if_list_find(adv_if_head, &adv_if_lookup);
if (adv_if != NULL)
return adv_if;
adv_if = adv_if_new(adv_if_lookup.name);
adv_if_list_add(adv_if_head, adv_if);
return NULL;
}
static struct adv_if *adv_if_del_internal(struct adv_if_list_head *adv_if_head,
const char *name)
{
struct adv_if adv_if_lookup = {};
struct adv_if *adv_if = NULL;
strlcpy(adv_if_lookup.name, name, sizeof(adv_if_lookup.name));
adv_if = adv_if_list_find(adv_if_head, &adv_if_lookup);
if (adv_if == NULL)
return NULL;
adv_if_list_del(adv_if_head, adv_if);
return adv_if;
}
static void adv_if_clean_internal(struct adv_if_list_head *adv_if_head)
{
struct adv_if *node = NULL;
if (!adv_if_is_empty_internal(adv_if_head)) {
frr_each_safe (adv_if_list, adv_if_head, node) {
adv_if_list_del(adv_if_head, node);
adv_if_free(node);
}
}
adv_if_list_fini(adv_if_head);
}
/*
* Add to list. On Success, return NULL, otherwise return already existing
* adv_if.
*/
static struct adv_if *adv_if_add(struct zebra_vrf *zvrf, const char *name)
{
struct adv_if *adv_if = NULL;
adv_if = adv_if_add_internal(&zvrf->rtadv.adv_if, name);
if (adv_if != NULL)
return adv_if;
if (IS_ZEBRA_DEBUG_EVENT) {
struct vrf *vrf = zvrf->vrf;
zlog_debug("%s: %s:%u IF %s count: %lu", __func__,
VRF_LOGNAME(vrf), zvrf_id(zvrf), name,
adv_if_list_count(&zvrf->rtadv.adv_if));
}
return NULL;
}
/*
* Del from list. On Success, return the adv_if, otherwise return NULL. Caller
* frees.
*/
static struct adv_if *adv_if_del(struct zebra_vrf *zvrf, const char *name)
{
struct adv_if *adv_if = NULL;
adv_if = adv_if_del_internal(&zvrf->rtadv.adv_if, name);
if (adv_if == NULL)
return NULL;
if (IS_ZEBRA_DEBUG_EVENT) {
struct vrf *vrf = zvrf->vrf;
zlog_debug("%s: %s:%u IF %s count: %lu", __func__,
VRF_LOGNAME(vrf), zvrf_id(zvrf), name,
adv_if_list_count(&zvrf->rtadv.adv_if));
}
return adv_if;
}
/*
* Add to list. On Success, return NULL, otherwise return already existing
* adv_if.
*/
static struct adv_if *adv_msec_if_add(struct zebra_vrf *zvrf, const char *name)
{
struct adv_if *adv_if = NULL;
adv_if = adv_if_add_internal(&zvrf->rtadv.adv_msec_if, name);
if (adv_if != NULL)
return adv_if;
if (IS_ZEBRA_DEBUG_EVENT) {
struct vrf *vrf = zvrf->vrf;
zlog_debug("%s: %s:%u IF %s count: %lu", __func__,
VRF_LOGNAME(vrf), zvrf_id(zvrf), name,
adv_if_list_count(&zvrf->rtadv.adv_msec_if));
}
return NULL;
}
/*
* Del from list. On Success, return the adv_if, otherwise return NULL. Caller
* frees.
*/
static struct adv_if *adv_msec_if_del(struct zebra_vrf *zvrf, const char *name)
{
struct adv_if *adv_if = NULL;
adv_if = adv_if_del_internal(&zvrf->rtadv.adv_msec_if, name);
if (adv_if == NULL)
return NULL;
if (IS_ZEBRA_DEBUG_EVENT) {
struct vrf *vrf = zvrf->vrf;
zlog_debug("%s: %s:%u IF %s count: %lu", __func__,
VRF_LOGNAME(vrf), zvrf_id(zvrf), name,
adv_if_list_count(&zvrf->rtadv.adv_msec_if));
}
return adv_if;
}
/* Clean adv_if list, called on vrf terminate */
static void adv_if_clean(struct zebra_vrf *zvrf)
{
if (IS_ZEBRA_DEBUG_EVENT) {
struct vrf *vrf = zvrf->vrf;
zlog_debug("%s: %s:%u count: %lu -> 0", __func__,
VRF_LOGNAME(vrf), zvrf_id(zvrf),
adv_if_list_count(&zvrf->rtadv.adv_if));
}
adv_if_clean_internal(&zvrf->rtadv.adv_if);
}
/* Clean adv_msec_if list, called on vrf terminate */
static void adv_msec_if_clean(struct zebra_vrf *zvrf)
{
if (IS_ZEBRA_DEBUG_EVENT) {
struct vrf *vrf = zvrf->vrf;
zlog_debug("%s: %s:%u count: %lu -> 0", __func__,
VRF_LOGNAME(vrf), zvrf_id(zvrf),
adv_if_list_count(&zvrf->rtadv.adv_msec_if));
}
adv_if_clean_internal(&zvrf->rtadv.adv_msec_if);
}
static struct rtadv_prefix *rtadv_prefix_new(void)
{
return XCALLOC(MTYPE_RTADV_PREFIX, sizeof(struct rtadv_prefix));
@ -1006,30 +1205,34 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp,
{
struct zebra_if *zif;
struct zebra_vrf *zvrf;
struct adv_if *adv_if = NULL;
zif = ifp->info;
zvrf = vrf_info_lookup(ifp->vrf_id);
zvrf = rtadv_interface_get_zvrf(ifp);
if (status == RA_SUPPRESS) {
/* RA is currently enabled */
if (zif->rtadv.AdvSendAdvertisements) {
rtadv_send_packet(rtadv_get_socket(zvrf), ifp,
RA_SUPPRESS);
rtadv_send_packet(zvrf->rtadv.sock, ifp, RA_SUPPRESS);
zif->rtadv.AdvSendAdvertisements = 0;
zif->rtadv.AdvIntervalTimer = 0;
zvrf->rtadv.adv_if_count--;
if_leave_all_router(rtadv_get_socket(zvrf), ifp);
adv_if = adv_if_del(zvrf, ifp->name);
if (adv_if == NULL)
return; /* Nothing to delete */
if (zvrf->rtadv.adv_if_count == 0)
adv_if_free(adv_if);
if_leave_all_router(zvrf->rtadv.sock, ifp);
if (adv_if_list_count(&zvrf->rtadv.adv_if) == 0)
rtadv_event(zvrf, RTADV_STOP, 0);
}
} else {
if (!zif->rtadv.AdvSendAdvertisements) {
zif->rtadv.AdvSendAdvertisements = 1;
zif->rtadv.AdvIntervalTimer = 0;
zvrf->rtadv.adv_if_count++;
if ((zif->rtadv.MaxRtrAdvInterval >= 1000)
&& zif->rtadv.UseFastRexmit) {
/*
@ -1041,11 +1244,14 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp,
RTADV_NUM_FAST_REXMITS;
}
if_join_all_router(rtadv_get_socket(zvrf), ifp);
adv_if = adv_if_add(zvrf, ifp->name);
if (adv_if != NULL)
return; /* Alread added */
if (zvrf->rtadv.adv_if_count == 1)
rtadv_event(zvrf, RTADV_START,
rtadv_get_socket(zvrf));
if_join_all_router(zvrf->rtadv.sock, ifp);
if (adv_if_list_count(&zvrf->rtadv.adv_if) == 1)
rtadv_event(zvrf, RTADV_START, 0);
}
}
}
@ -1092,7 +1298,7 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable)
zebra_route_string(client->proto));
return;
}
if (ifp->vrf_id != zvrf_id(zvrf)) {
if (vrf_is_backend_netns() && ifp->vrf_id != zvrf_id(zvrf)) {
struct vrf *vrf = zvrf->vrf;
zlog_debug(
@ -1136,10 +1342,10 @@ void rtadv_stop_ra(struct interface *ifp)
struct zebra_vrf *zvrf;
zif = ifp->info;
zvrf = vrf_info_lookup(ifp->vrf_id);
zvrf = rtadv_interface_get_zvrf(ifp);
if (zif->rtadv.AdvSendAdvertisements)
rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_SUPPRESS);
rtadv_send_packet(zvrf->rtadv.sock, ifp, RA_SUPPRESS);
}
/*
@ -1379,8 +1585,9 @@ DEFUN (ipv6_nd_ra_interval_msec,
unsigned interval;
struct zebra_if *zif = ifp->info;
struct zebra_vrf *zvrf;
struct adv_if *adv_if;
zvrf = vrf_info_lookup(ifp->vrf_id);
zvrf = rtadv_interface_get_zvrf(ifp);
interval = strtoul(argv[idx_number]->arg, NULL, 10);
if ((zif->rtadv.AdvDefaultLifetime != -1
@ -1390,11 +1597,14 @@ DEFUN (ipv6_nd_ra_interval_msec,
return CMD_WARNING_CONFIG_FAILED;
}
if (zif->rtadv.MaxRtrAdvInterval % 1000)
zvrf->rtadv.adv_msec_if_count--;
if (zif->rtadv.MaxRtrAdvInterval % 1000) {
adv_if = adv_msec_if_del(zvrf, ifp->name);
if (adv_if != NULL)
adv_if_free(adv_if);
}
if (interval % 1000)
zvrf->rtadv.adv_msec_if_count++;
(void)adv_msec_if_add(zvrf, ifp->name);
SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
zif->rtadv.MaxRtrAdvInterval = interval;
@ -1417,8 +1627,9 @@ DEFUN (ipv6_nd_ra_interval,
unsigned interval;
struct zebra_if *zif = ifp->info;
struct zebra_vrf *zvrf;
struct adv_if *adv_if;
zvrf = vrf_info_lookup(ifp->vrf_id);
zvrf = rtadv_interface_get_zvrf(ifp);
interval = strtoul(argv[idx_number]->arg, NULL, 10);
if ((zif->rtadv.AdvDefaultLifetime != -1
@ -1428,8 +1639,11 @@ DEFUN (ipv6_nd_ra_interval,
return CMD_WARNING_CONFIG_FAILED;
}
if (zif->rtadv.MaxRtrAdvInterval % 1000)
zvrf->rtadv.adv_msec_if_count--;
if (zif->rtadv.MaxRtrAdvInterval % 1000) {
adv_if = adv_msec_if_del(zvrf, ifp->name);
if (adv_if != NULL)
adv_if_free(adv_if);
}
/* convert to milliseconds */
interval = interval * 1000;
@ -1456,11 +1670,15 @@ DEFUN (no_ipv6_nd_ra_interval,
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
struct zebra_vrf *zvrf = NULL;
struct adv_if *adv_if;
zvrf = vrf_info_lookup(ifp->vrf_id);
zvrf = rtadv_interface_get_zvrf(ifp);
if (zif->rtadv.MaxRtrAdvInterval % 1000)
zvrf->rtadv.adv_msec_if_count--;
if (zif->rtadv.MaxRtrAdvInterval % 1000) {
adv_if = adv_msec_if_del(zvrf, ifp->name);
if (adv_if != NULL)
adv_if_free(adv_if);
}
UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
@ -2451,7 +2669,7 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp)
static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val)
{
struct rtadv *rtadv = &zvrf->rtadv;
struct rtadv *rtadv;
if (IS_ZEBRA_DEBUG_EVENT) {
struct vrf *vrf = zvrf->vrf;
@ -2460,9 +2678,11 @@ static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val)
VRF_LOGNAME(vrf), event, val);
}
rtadv = &zvrf->rtadv;
switch (event) {
case RTADV_START:
thread_add_read(zrouter.master, rtadv_read, zvrf, val,
thread_add_read(zrouter.master, rtadv_read, zvrf, rtadv->sock,
&rtadv->ra_read);
thread_add_event(zrouter.master, rtadv_timer, zvrf, 0,
&rtadv->ra_timer);
@ -2480,7 +2700,7 @@ static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val)
&rtadv->ra_timer);
break;
case RTADV_READ:
thread_add_read(zrouter.master, rtadv_read, zvrf, val,
thread_add_read(zrouter.master, rtadv_read, zvrf, rtadv->sock,
&rtadv->ra_read);
break;
default:
@ -2489,37 +2709,27 @@ static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val)
return;
}
void rtadv_init(struct zebra_vrf *zvrf)
void rtadv_vrf_init(struct zebra_vrf *zvrf)
{
if (vrf_is_backend_netns()) {
zvrf->rtadv.sock = rtadv_make_socket(zvrf->zns->ns_id);
zrouter.rtadv_sock = -1;
} else {
zvrf->rtadv.sock = -1;
if (zrouter.rtadv_sock < 0)
zrouter.rtadv_sock =
rtadv_make_socket(zvrf->zns->ns_id);
}
if (!vrf_is_backend_netns() && (zvrf_id(zvrf) != VRF_DEFAULT))
return;
zvrf->rtadv.sock = rtadv_make_socket(zvrf->zns->ns_id);
}
void rtadv_vrf_terminate(struct zebra_vrf *zvrf)
{
if (!vrf_is_backend_netns() && (zvrf_id(zvrf) != VRF_DEFAULT))
return;
rtadv_event(zvrf, RTADV_STOP, 0);
if (zvrf->rtadv.sock >= 0) {
close(zvrf->rtadv.sock);
zvrf->rtadv.sock = -1;
}
zvrf->rtadv.adv_if_count = 0;
zvrf->rtadv.adv_msec_if_count = 0;
}
void rtadv_terminate(void)
{
if (zrouter.rtadv_sock >= 0) {
close(zrouter.rtadv_sock);
zrouter.rtadv_sock = -1;
}
adv_if_clean(zvrf);
adv_msec_if_clean(zvrf);
}
void rtadv_cmd_init(void)
@ -2629,14 +2839,11 @@ static int if_leave_all_router(int sock, struct interface *ifp)
}
#else
void rtadv_init(struct zebra_vrf *zvrf)
{
/* Empty.*/;
}
void rtadv_terminate(void)
void rtadv_vrf_init(struct zebra_vrf *zvrf)
{
/* Empty.*/;
}
void rtadv_cmd_init(void)
{
/* Empty.*/;

View File

@ -152,9 +152,8 @@ enum ipv6_nd_suppress_ra_status {
RA_SUPPRESS,
};
extern void rtadv_init(struct zebra_vrf *zvrf);
extern void rtadv_vrf_init(struct zebra_vrf *zvrf);
extern void rtadv_vrf_terminate(struct zebra_vrf *zvrf);
extern void rtadv_terminate(void);
extern void rtadv_stop_ra(struct interface *ifp);
extern void rtadv_stop_ra_all(void);
extern void rtadv_cmd_init(void);

View File

@ -268,8 +268,6 @@ void zebra_router_init(bool asic_offload, bool notify_on_ack)
zrouter.packets_to_process = ZEBRA_ZAPI_PACKETS_TO_PROCESS;
zrouter.rtadv_sock = -1;
zebra_vxlan_init();
zebra_mlag_init();

View File

@ -162,9 +162,6 @@ struct zebra_router {
struct hash *iptable_hash;
/* used if vrf backend is not network namespace */
int rtadv_sock;
/* A sequence number used for tracking routes */
_Atomic uint32_t sequence_num;

View File

@ -131,7 +131,7 @@ static int zebra_vrf_enable(struct vrf *vrf)
else
zvrf->zns = zebra_ns_lookup(NS_DEFAULT);
#if defined(HAVE_RTADV)
rtadv_init(zvrf);
rtadv_vrf_init(zvrf);
#endif
/* Inform clients that the VRF is now active. This is an