forked from Mirror/frr

Currently, staticd configuration is tightly coupled with VRF existence. Because of that, it has to use a hack in NB infrastructure to create a VRF configuration when at least one static route is configured for this VRF. This hack is incompatible with mgmtd, because mgmtd doesn't execute configuration callbacks. Because of that, the configuration may become out of sync between mgmtd and staticd. There are two main cases: 1. Create static route in a VRF. The VRF data node will be created automatically in staticd by the NB hack, but not in mgmtd. 2. Delete VRF which has some static routes configured. The static route configuration will be deleted from staticd by the NB hack, but not from mgmtd. To fix the problem, decouple configuration of static routes from VRF configuration. Now it is possible to configure static routes even if the VRF doesn't exist yet. Once the VRF is created, staticd applies all the preconfigured routes. This change also fixes the problem with static routes being preserved in the system when staticd "control-plane-protocol" container is deleted but the VRF is still configured. Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
579 lines
14 KiB
C
579 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Zebra connect code.
|
|
* Copyright (C) 2018 Cumulus Networks, Inc.
|
|
* Donald Sharp
|
|
*/
|
|
#include <zebra.h>
|
|
|
|
#include "frrevent.h"
|
|
#include "command.h"
|
|
#include "network.h"
|
|
#include "prefix.h"
|
|
#include "routemap.h"
|
|
#include "table.h"
|
|
#include "srcdest_table.h"
|
|
#include "stream.h"
|
|
#include "memory.h"
|
|
#include "zclient.h"
|
|
#include "filter.h"
|
|
#include "plist.h"
|
|
#include "log.h"
|
|
#include "nexthop.h"
|
|
#include "nexthop_group.h"
|
|
#include "hash.h"
|
|
#include "jhash.h"
|
|
|
|
#include "static_vrf.h"
|
|
#include "static_routes.h"
|
|
#include "static_zebra.h"
|
|
#include "static_nht.h"
|
|
#include "static_vty.h"
|
|
#include "static_debug.h"
|
|
|
|
DEFINE_MTYPE_STATIC(STATIC, STATIC_NHT_DATA, "Static Nexthop tracking data");
|
|
PREDECL_HASH(static_nht_hash);
|
|
|
|
struct static_nht_data {
|
|
struct static_nht_hash_item itm;
|
|
|
|
struct prefix nh;
|
|
safi_t safi;
|
|
|
|
vrf_id_t nh_vrf_id;
|
|
|
|
uint32_t refcount;
|
|
uint8_t nh_num;
|
|
bool registered;
|
|
};
|
|
|
|
static int static_nht_data_cmp(const struct static_nht_data *nhtd1,
|
|
const struct static_nht_data *nhtd2)
|
|
{
|
|
if (nhtd1->nh_vrf_id != nhtd2->nh_vrf_id)
|
|
return numcmp(nhtd1->nh_vrf_id, nhtd2->nh_vrf_id);
|
|
if (nhtd1->safi != nhtd2->safi)
|
|
return numcmp(nhtd1->safi, nhtd2->safi);
|
|
|
|
return prefix_cmp(&nhtd1->nh, &nhtd2->nh);
|
|
}
|
|
|
|
static unsigned int static_nht_data_hash(const struct static_nht_data *nhtd)
|
|
{
|
|
unsigned int key = 0;
|
|
|
|
key = prefix_hash_key(&nhtd->nh);
|
|
return jhash_2words(nhtd->nh_vrf_id, nhtd->safi, key);
|
|
}
|
|
|
|
DECLARE_HASH(static_nht_hash, struct static_nht_data, itm, static_nht_data_cmp,
|
|
static_nht_data_hash);
|
|
|
|
static struct static_nht_hash_head static_nht_hash[1];
|
|
|
|
/* Zebra structure to hold current status. */
|
|
struct zclient *zclient;
|
|
uint32_t zebra_ecmp_count = MULTIPATH_NUM;
|
|
|
|
/* Interface addition message from zebra. */
|
|
static int static_ifp_create(struct interface *ifp)
|
|
{
|
|
static_ifindex_update(ifp, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int static_ifp_destroy(struct interface *ifp)
|
|
{
|
|
static_ifindex_update(ifp, false);
|
|
return 0;
|
|
}
|
|
|
|
static int interface_address_add(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int interface_address_delete(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
struct connected *c;
|
|
|
|
c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
|
|
|
|
if (!c)
|
|
return 0;
|
|
|
|
connected_free(&c);
|
|
return 0;
|
|
}
|
|
|
|
static int static_ifp_up(struct interface *ifp)
|
|
{
|
|
static_ifindex_update(ifp, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int static_ifp_down(struct interface *ifp)
|
|
{
|
|
static_ifindex_update(ifp, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int route_notify_owner(ZAPI_CALLBACK_ARGS)
|
|
{
|
|
struct prefix p;
|
|
enum zapi_route_notify_owner note;
|
|
uint32_t table_id;
|
|
safi_t safi;
|
|
|
|
if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, ¬e, NULL,
|
|
&safi))
|
|
return -1;
|
|
|
|
switch (note) {
|
|
case ZAPI_ROUTE_FAIL_INSTALL:
|
|
static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED);
|
|
zlog_warn("%s: Route %pFX failed to install for table: %u",
|
|
__func__, &p, table_id);
|
|
break;
|
|
case ZAPI_ROUTE_BETTER_ADMIN_WON:
|
|
static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED);
|
|
zlog_warn(
|
|
"%s: Route %pFX over-ridden by better route for table: %u",
|
|
__func__, &p, table_id);
|
|
break;
|
|
case ZAPI_ROUTE_INSTALLED:
|
|
static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED);
|
|
break;
|
|
case ZAPI_ROUTE_REMOVED:
|
|
static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED);
|
|
break;
|
|
case ZAPI_ROUTE_REMOVE_FAIL:
|
|
static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED);
|
|
zlog_warn("%s: Route %pFX failure to remove for table: %u",
|
|
__func__, &p, table_id);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void zebra_connected(struct zclient *zclient)
|
|
{
|
|
zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST, zclient, true);
|
|
zclient_send_reg_requests(zclient, VRF_DEFAULT);
|
|
|
|
static_fixup_vrf_ids(vrf_lookup_by_id(VRF_DEFAULT));
|
|
}
|
|
|
|
/* API to check whether the configured nexthop address is
|
|
* one of its local connected address or not.
|
|
*/
|
|
static bool
|
|
static_nexthop_is_local(vrf_id_t vrfid, struct prefix *addr, int family)
|
|
{
|
|
if (family == AF_INET) {
|
|
if (if_address_is_local(&addr->u.prefix4, AF_INET, vrfid))
|
|
return true;
|
|
} else if (family == AF_INET6) {
|
|
if (if_address_is_local(&addr->u.prefix6, AF_INET6, vrfid))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void static_zebra_nexthop_update(struct vrf *vrf, struct prefix *matched,
|
|
struct zapi_route *nhr)
|
|
{
|
|
struct static_nht_data *nhtd, lookup;
|
|
afi_t afi = AFI_IP;
|
|
|
|
if (zclient->bfd_integration)
|
|
bfd_nht_update(matched, nhr);
|
|
|
|
if (matched->family == AF_INET6)
|
|
afi = AFI_IP6;
|
|
|
|
if (nhr->type == ZEBRA_ROUTE_CONNECT) {
|
|
if (static_nexthop_is_local(vrf->vrf_id, matched,
|
|
nhr->prefix.family))
|
|
nhr->nexthop_num = 0;
|
|
}
|
|
|
|
memset(&lookup, 0, sizeof(lookup));
|
|
lookup.nh = *matched;
|
|
lookup.nh_vrf_id = vrf->vrf_id;
|
|
lookup.safi = nhr->safi;
|
|
|
|
nhtd = static_nht_hash_find(static_nht_hash, &lookup);
|
|
|
|
if (nhtd) {
|
|
nhtd->nh_num = nhr->nexthop_num;
|
|
|
|
static_nht_reset_start(matched, afi, nhr->safi, nhtd->nh_vrf_id);
|
|
static_nht_update(NULL, matched, nhr->nexthop_num, afi,
|
|
nhr->safi, nhtd->nh_vrf_id);
|
|
} else
|
|
zlog_err("No nhtd?");
|
|
}
|
|
|
|
static void static_zebra_capabilities(struct zclient_capabilities *cap)
|
|
{
|
|
mpls_enabled = cap->mpls_enabled;
|
|
zebra_ecmp_count = cap->ecmp;
|
|
}
|
|
|
|
static struct static_nht_data *
|
|
static_nht_hash_getref(const struct static_nht_data *ref)
|
|
{
|
|
struct static_nht_data *nhtd;
|
|
|
|
nhtd = static_nht_hash_find(static_nht_hash, ref);
|
|
if (!nhtd) {
|
|
nhtd = XCALLOC(MTYPE_STATIC_NHT_DATA, sizeof(*nhtd));
|
|
|
|
prefix_copy(&nhtd->nh, &ref->nh);
|
|
nhtd->nh_vrf_id = ref->nh_vrf_id;
|
|
nhtd->safi = ref->safi;
|
|
|
|
static_nht_hash_add(static_nht_hash, nhtd);
|
|
}
|
|
|
|
nhtd->refcount++;
|
|
return nhtd;
|
|
}
|
|
|
|
static bool static_nht_hash_decref(struct static_nht_data **nhtd_p)
|
|
{
|
|
struct static_nht_data *nhtd = *nhtd_p;
|
|
|
|
*nhtd_p = NULL;
|
|
|
|
if (--nhtd->refcount > 0)
|
|
return true;
|
|
|
|
static_nht_hash_del(static_nht_hash, nhtd);
|
|
XFREE(MTYPE_STATIC_NHT_DATA, nhtd);
|
|
return false;
|
|
}
|
|
|
|
static void static_nht_hash_clear(void)
|
|
{
|
|
struct static_nht_data *nhtd;
|
|
|
|
while ((nhtd = static_nht_hash_pop(static_nht_hash)))
|
|
XFREE(MTYPE_STATIC_NHT_DATA, nhtd);
|
|
}
|
|
|
|
static bool static_zebra_nht_get_prefix(const struct static_nexthop *nh,
|
|
struct prefix *p)
|
|
{
|
|
switch (nh->type) {
|
|
case STATIC_IFNAME:
|
|
case STATIC_BLACKHOLE:
|
|
p->family = AF_UNSPEC;
|
|
return false;
|
|
|
|
case STATIC_IPV4_GATEWAY:
|
|
case STATIC_IPV4_GATEWAY_IFNAME:
|
|
p->family = AF_INET;
|
|
p->prefixlen = IPV4_MAX_BITLEN;
|
|
p->u.prefix4 = nh->addr.ipv4;
|
|
return true;
|
|
|
|
case STATIC_IPV6_GATEWAY:
|
|
case STATIC_IPV6_GATEWAY_IFNAME:
|
|
p->family = AF_INET6;
|
|
p->prefixlen = IPV6_MAX_BITLEN;
|
|
p->u.prefix6 = nh->addr.ipv6;
|
|
return true;
|
|
}
|
|
|
|
assertf(0, "BUG: someone forgot to add nexthop type %u", nh->type);
|
|
return false;
|
|
}
|
|
|
|
void static_zebra_nht_register(struct static_nexthop *nh, bool reg)
|
|
{
|
|
struct static_path *pn = nh->pn;
|
|
struct route_node *rn = pn->rn;
|
|
struct static_route_info *si = static_route_info_from_rnode(rn);
|
|
struct static_nht_data *nhtd, lookup = {};
|
|
uint32_t cmd;
|
|
|
|
if (!static_zebra_nht_get_prefix(nh, &lookup.nh))
|
|
return;
|
|
lookup.nh_vrf_id = nh->nh_vrf_id;
|
|
lookup.safi = si->safi;
|
|
|
|
if (nh->nh_registered) {
|
|
/* nh->nh_registered means we own a reference on the nhtd */
|
|
nhtd = static_nht_hash_find(static_nht_hash, &lookup);
|
|
|
|
assertf(nhtd, "BUG: NH %pFX registered but not in hashtable",
|
|
&lookup.nh);
|
|
} else if (reg) {
|
|
nhtd = static_nht_hash_getref(&lookup);
|
|
|
|
if (nhtd->refcount > 1)
|
|
DEBUGD(&static_dbg_route,
|
|
"Reusing registered nexthop(%pFX) for %pRN %d",
|
|
&lookup.nh, rn, nhtd->nh_num);
|
|
} else {
|
|
/* !reg && !nh->nh_registered */
|
|
zlog_warn("trying to unregister nexthop %pFX twice",
|
|
&lookup.nh);
|
|
return;
|
|
}
|
|
|
|
nh->nh_registered = reg;
|
|
|
|
if (reg) {
|
|
if (nhtd->nh_num) {
|
|
/* refresh with existing data */
|
|
afi_t afi = prefix_afi(&lookup.nh);
|
|
|
|
if (nh->state == STATIC_NOT_INSTALLED)
|
|
nh->state = STATIC_START;
|
|
static_nht_update(&rn->p, &nhtd->nh, nhtd->nh_num, afi,
|
|
si->safi, nh->nh_vrf_id);
|
|
return;
|
|
}
|
|
|
|
if (nhtd->registered)
|
|
/* have no data, but did send register */
|
|
return;
|
|
|
|
cmd = ZEBRA_NEXTHOP_REGISTER;
|
|
DEBUGD(&static_dbg_route, "Registering nexthop(%pFX) for %pRN",
|
|
&lookup.nh, rn);
|
|
} else {
|
|
bool was_zebra_registered;
|
|
|
|
was_zebra_registered = nhtd->registered;
|
|
if (static_nht_hash_decref(&nhtd))
|
|
/* still got references alive */
|
|
return;
|
|
|
|
/* NB: nhtd is now NULL. */
|
|
if (!was_zebra_registered)
|
|
return;
|
|
|
|
cmd = ZEBRA_NEXTHOP_UNREGISTER;
|
|
DEBUGD(&static_dbg_route,
|
|
"Unregistering nexthop(%pFX) for %pRN", &lookup.nh, rn);
|
|
}
|
|
|
|
if (zclient_send_rnh(zclient, cmd, &lookup.nh, si->safi, false, false,
|
|
nh->nh_vrf_id) == ZCLIENT_SEND_FAILURE)
|
|
zlog_warn("%s: Failure to send nexthop %pFX for %pRN to zebra",
|
|
__func__, &lookup.nh, rn);
|
|
else if (reg)
|
|
nhtd->registered = true;
|
|
}
|
|
|
|
extern void static_zebra_route_add(struct static_path *pn, bool install)
|
|
{
|
|
struct route_node *rn = pn->rn;
|
|
struct static_route_info *si = rn->info;
|
|
struct static_nexthop *nh;
|
|
const struct prefix *p, *src_pp;
|
|
struct zapi_nexthop *api_nh;
|
|
struct zapi_route api;
|
|
uint32_t nh_num = 0;
|
|
|
|
if (!si->svrf->vrf)
|
|
return;
|
|
|
|
p = src_pp = NULL;
|
|
srcdest_rnode_prefixes(rn, &p, &src_pp);
|
|
|
|
memset(&api, 0, sizeof(api));
|
|
api.vrf_id = si->svrf->vrf->vrf_id;
|
|
api.type = ZEBRA_ROUTE_STATIC;
|
|
api.safi = si->safi;
|
|
memcpy(&api.prefix, p, sizeof(api.prefix));
|
|
|
|
if (src_pp) {
|
|
SET_FLAG(api.message, ZAPI_MESSAGE_SRCPFX);
|
|
memcpy(&api.src_prefix, src_pp, sizeof(api.src_prefix));
|
|
}
|
|
SET_FLAG(api.flags, ZEBRA_FLAG_RR_USE_DISTANCE);
|
|
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
|
|
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
|
|
if (pn->distance) {
|
|
SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
|
|
api.distance = pn->distance;
|
|
}
|
|
if (pn->tag) {
|
|
SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
|
|
api.tag = pn->tag;
|
|
}
|
|
if (pn->table_id != 0) {
|
|
SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
|
|
api.tableid = pn->table_id;
|
|
}
|
|
frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
|
|
/* Don't overrun the nexthop array */
|
|
if (nh_num == zebra_ecmp_count)
|
|
break;
|
|
|
|
api_nh = &api.nexthops[nh_num];
|
|
if (nh->nh_vrf_id == VRF_UNKNOWN)
|
|
continue;
|
|
/* Skip next hop which peer is down. */
|
|
if (nh->path_down)
|
|
continue;
|
|
|
|
api_nh->vrf_id = nh->nh_vrf_id;
|
|
if (nh->onlink)
|
|
SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
|
|
if (nh->color != 0) {
|
|
SET_FLAG(api.message, ZAPI_MESSAGE_SRTE);
|
|
api_nh->srte_color = nh->color;
|
|
}
|
|
|
|
nh->state = STATIC_SENT_TO_ZEBRA;
|
|
|
|
switch (nh->type) {
|
|
case STATIC_IFNAME:
|
|
if (nh->ifindex == IFINDEX_INTERNAL)
|
|
continue;
|
|
api_nh->ifindex = nh->ifindex;
|
|
api_nh->type = NEXTHOP_TYPE_IFINDEX;
|
|
break;
|
|
case STATIC_IPV4_GATEWAY:
|
|
if (!nh->nh_valid)
|
|
continue;
|
|
api_nh->type = NEXTHOP_TYPE_IPV4;
|
|
api_nh->gate = nh->addr;
|
|
break;
|
|
case STATIC_IPV4_GATEWAY_IFNAME:
|
|
if (nh->ifindex == IFINDEX_INTERNAL)
|
|
continue;
|
|
api_nh->ifindex = nh->ifindex;
|
|
api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
|
|
api_nh->gate = nh->addr;
|
|
break;
|
|
case STATIC_IPV6_GATEWAY:
|
|
if (!nh->nh_valid)
|
|
continue;
|
|
api_nh->type = NEXTHOP_TYPE_IPV6;
|
|
api_nh->gate = nh->addr;
|
|
break;
|
|
case STATIC_IPV6_GATEWAY_IFNAME:
|
|
if (nh->ifindex == IFINDEX_INTERNAL)
|
|
continue;
|
|
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
|
|
api_nh->ifindex = nh->ifindex;
|
|
api_nh->gate = nh->addr;
|
|
break;
|
|
case STATIC_BLACKHOLE:
|
|
api_nh->type = NEXTHOP_TYPE_BLACKHOLE;
|
|
switch (nh->bh_type) {
|
|
case STATIC_BLACKHOLE_DROP:
|
|
case STATIC_BLACKHOLE_NULL:
|
|
api_nh->bh_type = BLACKHOLE_NULL;
|
|
break;
|
|
case STATIC_BLACKHOLE_REJECT:
|
|
api_nh->bh_type = BLACKHOLE_REJECT;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (nh->snh_label.num_labels) {
|
|
int i;
|
|
|
|
SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
|
|
api_nh->label_num = nh->snh_label.num_labels;
|
|
for (i = 0; i < api_nh->label_num; i++)
|
|
api_nh->labels[i] = nh->snh_label.label[i];
|
|
}
|
|
if (nh->snh_seg.num_segs) {
|
|
int i;
|
|
|
|
api_nh->seg6local_action =
|
|
ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
|
|
SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6);
|
|
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
|
|
api.safi = SAFI_UNICAST;
|
|
|
|
api_nh->seg_num = nh->snh_seg.num_segs;
|
|
for (i = 0; i < api_nh->seg_num; i++)
|
|
memcpy(&api_nh->seg6_segs[i],
|
|
&nh->snh_seg.seg[i],
|
|
sizeof(struct in6_addr));
|
|
}
|
|
nh_num++;
|
|
}
|
|
|
|
api.nexthop_num = nh_num;
|
|
|
|
/*
|
|
* If we have been given an install but nothing is valid
|
|
* go ahead and delete the route for double plus fun
|
|
*/
|
|
if (!nh_num && install)
|
|
install = false;
|
|
|
|
zclient_route_send(install ?
|
|
ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
|
|
zclient, &api);
|
|
}
|
|
|
|
static zclient_handler *const static_handlers[] = {
|
|
[ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add,
|
|
[ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete,
|
|
[ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner,
|
|
};
|
|
|
|
void static_zebra_init(void)
|
|
{
|
|
hook_register_prio(if_real, 0, static_ifp_create);
|
|
hook_register_prio(if_up, 0, static_ifp_up);
|
|
hook_register_prio(if_down, 0, static_ifp_down);
|
|
hook_register_prio(if_unreal, 0, static_ifp_destroy);
|
|
|
|
zclient = zclient_new(master, &zclient_options_default, static_handlers,
|
|
array_size(static_handlers));
|
|
|
|
zclient_init(zclient, ZEBRA_ROUTE_STATIC, 0, &static_privs);
|
|
zclient->zebra_capabilities = static_zebra_capabilities;
|
|
zclient->zebra_connected = zebra_connected;
|
|
zclient->nexthop_update = static_zebra_nexthop_update;
|
|
|
|
static_nht_hash_init(static_nht_hash);
|
|
static_bfd_initialize(zclient, master);
|
|
}
|
|
|
|
/* static_zebra_stop used by tests/lib/test_grpc.cpp */
|
|
void static_zebra_stop(void)
|
|
{
|
|
static_nht_hash_clear();
|
|
static_nht_hash_fini(static_nht_hash);
|
|
|
|
if (!zclient)
|
|
return;
|
|
zclient_stop(zclient);
|
|
zclient_free(zclient);
|
|
zclient = NULL;
|
|
}
|
|
|
|
void static_zebra_vrf_register(struct vrf *vrf)
|
|
{
|
|
if (vrf->vrf_id == VRF_DEFAULT)
|
|
return;
|
|
zclient_send_reg_requests(zclient, vrf->vrf_id);
|
|
}
|
|
|
|
void static_zebra_vrf_unregister(struct vrf *vrf)
|
|
{
|
|
if (vrf->vrf_id == VRF_DEFAULT)
|
|
return;
|
|
zclient_send_dereg_requests(zclient, vrf->vrf_id);
|
|
}
|