mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-01 23:29:38 +00:00

Currently, it is possible to rename the default VRF either by passing `-o` option to zebra or by creating a file in `/var/run/netns` and binding it to `/proc/self/ns/net`. In both cases, only zebra knows about the rename and other daemons learn about it only after they connect to zebra. This is a problem, because daemons may read their config before they connect to zebra. To handle this rename after the config is read, we have some special code in every single daemon, which is not very bad but not desirable in my opinion. But things are getting worse when we need to handle this in northbound layer as we have to manually rewrite the config nodes. This approach is already hacky, but still works as every daemon handles its own NB structures. But it is completely incompatible with the central management daemon architecture we are aiming for, as mgmtd doesn't even have a connection with zebra to learn from it. And it shouldn't have it, because operational state changes should never affect configuration. To solve the problem and simplify the code, I propose to expand the `-o` option to all daemons. By using the startup option, we let daemons know about the rename before they read their configs so we don't need any special code to deal with it. There's an easy way to pass the option to all daemons by using `frr_global_options` variable. Unfortunately, the second way of renaming by creating a file in `/var/run/netns` is incompatible with the new mgmtd architecture. Theoretically, we could force daemons to read their configs only after they connect to zebra, but it means adding even more code to handle a very specific use-case. And anyway this won't work for mgmtd as it doesn't have a connection with zebra. So I had to remove this option. Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
706 lines
17 KiB
C
706 lines
17 KiB
C
/*
|
|
* Copyright (C) 2016 CumulusNetworks
|
|
* Donald Sharp
|
|
*
|
|
* This file is part of Quagga
|
|
*
|
|
* Quagga is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2, or (at your option) any
|
|
* later version.
|
|
*
|
|
* Quagga is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; see the file COPYING; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
#include <zebra.h>
|
|
|
|
/* for basename */
|
|
#include <libgen.h>
|
|
|
|
#include "log.h"
|
|
#include "linklist.h"
|
|
#include "command.h"
|
|
#include "memory.h"
|
|
#include "srcdest_table.h"
|
|
#include "vrf.h"
|
|
#include "vty.h"
|
|
|
|
#include "zebra/zebra_router.h"
|
|
#include "zebra/rtadv.h"
|
|
#include "zebra/debug.h"
|
|
#include "zebra/zapi_msg.h"
|
|
#include "zebra/rib.h"
|
|
#include "zebra/zebra_vrf.h"
|
|
#include "zebra/zebra_rnh.h"
|
|
#include "zebra/router-id.h"
|
|
#include "zebra/interface.h"
|
|
#include "zebra/zebra_mpls.h"
|
|
#include "zebra/zebra_vxlan.h"
|
|
#include "zebra/zebra_netns_notify.h"
|
|
#include "zebra/zebra_routemap.h"
|
|
#ifndef VTYSH_EXTRACT_PL
|
|
#include "zebra/zebra_vrf_clippy.c"
|
|
#endif
|
|
#include "zebra/table_manager.h"
|
|
|
|
static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi,
|
|
safi_t safi);
|
|
static void zebra_rnhtable_node_cleanup(struct route_table *table,
|
|
struct route_node *node);
|
|
|
|
DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_VRF, "ZEBRA VRF");
|
|
DEFINE_MTYPE_STATIC(ZEBRA, OTHER_TABLE, "Other Table");
|
|
|
|
/* VRF information update. */
|
|
static void zebra_vrf_add_update(struct zebra_vrf *zvrf)
|
|
{
|
|
struct listnode *node, *nnode;
|
|
struct zserv *client;
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT)
|
|
zlog_debug("MESSAGE: ZEBRA_VRF_ADD %s", zvrf_name(zvrf));
|
|
|
|
for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
|
|
/* Do not send unsolicited messages to synchronous clients. */
|
|
if (client->synchronous)
|
|
continue;
|
|
|
|
zsend_vrf_add(client, zvrf);
|
|
}
|
|
}
|
|
|
|
static void zebra_vrf_delete_update(struct zebra_vrf *zvrf)
|
|
{
|
|
struct listnode *node, *nnode;
|
|
struct zserv *client;
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT)
|
|
zlog_debug("MESSAGE: ZEBRA_VRF_DELETE %s", zvrf_name(zvrf));
|
|
|
|
for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
|
|
/* Do not send unsolicited messages to synchronous clients. */
|
|
if (client->synchronous)
|
|
continue;
|
|
|
|
zsend_vrf_delete(client, zvrf);
|
|
}
|
|
}
|
|
|
|
void zebra_vrf_update_all(struct zserv *client)
|
|
{
|
|
struct vrf *vrf;
|
|
|
|
RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
|
|
if (vrf->vrf_id != VRF_UNKNOWN)
|
|
zsend_vrf_add(client, vrf_info_lookup(vrf->vrf_id));
|
|
}
|
|
}
|
|
|
|
/* Callback upon creating a new VRF. */
|
|
static int zebra_vrf_new(struct vrf *vrf)
|
|
{
|
|
struct zebra_vrf *zvrf;
|
|
|
|
if (IS_ZEBRA_DEBUG_EVENT)
|
|
zlog_debug("VRF %s created, id %u", vrf->name, vrf->vrf_id);
|
|
|
|
zvrf = zebra_vrf_alloc(vrf);
|
|
if (!vrf_is_backend_netns())
|
|
zvrf->zns = zebra_ns_lookup(NS_DEFAULT);
|
|
|
|
otable_init(&zvrf->other_tables);
|
|
|
|
router_id_init(zvrf);
|
|
|
|
/* Initiate Table Manager per ZNS */
|
|
table_manager_enable(zvrf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Callback upon enabling a VRF. */
|
|
static int zebra_vrf_enable(struct vrf *vrf)
|
|
{
|
|
struct zebra_vrf *zvrf = vrf->info;
|
|
struct route_table *table;
|
|
afi_t afi;
|
|
safi_t safi;
|
|
|
|
assert(zvrf);
|
|
if (IS_ZEBRA_DEBUG_EVENT)
|
|
zlog_debug("VRF %s id %u is now active", zvrf_name(zvrf),
|
|
zvrf_id(zvrf));
|
|
|
|
if (vrf_is_backend_netns())
|
|
zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id);
|
|
else
|
|
zvrf->zns = zebra_ns_lookup(NS_DEFAULT);
|
|
#if defined(HAVE_RTADV)
|
|
rtadv_vrf_init(zvrf);
|
|
#endif
|
|
|
|
/* Inform clients that the VRF is now active. This is an
|
|
* add for the clients.
|
|
*/
|
|
|
|
zebra_vrf_add_update(zvrf);
|
|
/* Allocate tables */
|
|
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
|
|
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
|
|
zebra_vrf_table_create(zvrf, afi, safi);
|
|
|
|
table = route_table_init();
|
|
table->cleanup = zebra_rnhtable_node_cleanup;
|
|
zvrf->rnh_table[afi] = table;
|
|
|
|
table = route_table_init();
|
|
table->cleanup = zebra_rnhtable_node_cleanup;
|
|
zvrf->rnh_table_multicast[afi] = table;
|
|
}
|
|
|
|
/* Kick off any VxLAN-EVPN processing. */
|
|
zebra_vxlan_vrf_enable(zvrf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Callback upon disabling a VRF. */
|
|
static int zebra_vrf_disable(struct vrf *vrf)
|
|
{
|
|
struct zebra_vrf *zvrf = vrf->info;
|
|
struct interface *ifp;
|
|
afi_t afi;
|
|
safi_t safi;
|
|
unsigned i;
|
|
|
|
assert(zvrf);
|
|
if (IS_ZEBRA_DEBUG_EVENT)
|
|
zlog_debug("VRF %s id %u is now inactive", zvrf_name(zvrf),
|
|
zvrf_id(zvrf));
|
|
|
|
/* Stop any VxLAN-EVPN processing. */
|
|
zebra_vxlan_vrf_disable(zvrf);
|
|
|
|
#if defined(HAVE_RTADV)
|
|
rtadv_vrf_terminate(zvrf);
|
|
#endif
|
|
|
|
/* Inform clients that the VRF is now inactive. This is a
|
|
* delete for the clients.
|
|
*/
|
|
zebra_vrf_delete_update(zvrf);
|
|
|
|
/* If asked to retain routes, there's nothing more to do. */
|
|
if (CHECK_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN))
|
|
return 0;
|
|
|
|
/* Remove all routes. */
|
|
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
|
|
route_table_finish(zvrf->rnh_table[afi]);
|
|
zvrf->rnh_table[afi] = NULL;
|
|
route_table_finish(zvrf->rnh_table_multicast[afi]);
|
|
zvrf->rnh_table_multicast[afi] = NULL;
|
|
|
|
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
|
|
rib_close_table(zvrf->table[afi][safi]);
|
|
}
|
|
|
|
/* Cleanup Vxlan, MPLS and PW tables. */
|
|
zebra_vxlan_cleanup_tables(zvrf);
|
|
zebra_mpls_cleanup_tables(zvrf);
|
|
zebra_pw_exit(zvrf);
|
|
|
|
/* Remove link-local IPv4 addresses created for BGP unnumbered peering.
|
|
*/
|
|
FOR_ALL_INTERFACES (vrf, ifp)
|
|
if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(ifp);
|
|
|
|
/* clean-up work queues */
|
|
for (i = 0; i < MQ_SIZE; i++) {
|
|
struct listnode *lnode, *nnode;
|
|
struct route_node *rnode;
|
|
rib_dest_t *dest;
|
|
|
|
for (ALL_LIST_ELEMENTS(zrouter.mq->subq[i], lnode, nnode,
|
|
rnode)) {
|
|
dest = rib_dest_from_rnode(rnode);
|
|
if (dest && rib_dest_vrf(dest) == zvrf) {
|
|
route_unlock_node(rnode);
|
|
list_delete_node(zrouter.mq->subq[i], lnode);
|
|
zrouter.mq->size--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Cleanup (free) routing tables and NHT tables. */
|
|
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
|
|
/*
|
|
* Set the table pointer to NULL as that
|
|
* we no-longer need a copy of it, nor do we
|
|
* own this data, the zebra_router structure
|
|
* owns these tables. Once we've cleaned up the
|
|
* table, see rib_close_table above
|
|
* we no-longer need this pointer.
|
|
*/
|
|
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
|
|
zebra_router_release_table(zvrf, zvrf->table_id, afi,
|
|
safi);
|
|
zvrf->table[afi][safi] = NULL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int zebra_vrf_delete(struct vrf *vrf)
|
|
{
|
|
struct zebra_vrf *zvrf = vrf->info;
|
|
struct other_route_table *otable;
|
|
struct route_table *table;
|
|
afi_t afi;
|
|
safi_t safi;
|
|
unsigned i;
|
|
|
|
assert(zvrf);
|
|
if (IS_ZEBRA_DEBUG_EVENT)
|
|
zlog_debug("VRF %s id %u deleted", zvrf_name(zvrf),
|
|
zvrf_id(zvrf));
|
|
|
|
table_manager_disable(zvrf);
|
|
|
|
/* clean-up work queues */
|
|
for (i = 0; i < MQ_SIZE; i++) {
|
|
struct listnode *lnode, *nnode;
|
|
struct route_node *rnode;
|
|
rib_dest_t *dest;
|
|
|
|
for (ALL_LIST_ELEMENTS(zrouter.mq->subq[i], lnode, nnode,
|
|
rnode)) {
|
|
dest = rib_dest_from_rnode(rnode);
|
|
if (dest && rib_dest_vrf(dest) == zvrf) {
|
|
route_unlock_node(rnode);
|
|
list_delete_node(zrouter.mq->subq[i], lnode);
|
|
zrouter.mq->size--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Free Vxlan and MPLS. */
|
|
zebra_vxlan_close_tables(zvrf);
|
|
zebra_mpls_close_tables(zvrf);
|
|
|
|
/* release allocated memory */
|
|
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
|
|
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
|
|
table = zvrf->table[afi][safi];
|
|
if (table) {
|
|
zebra_router_release_table(zvrf, zvrf->table_id,
|
|
afi, safi);
|
|
zvrf->table[afi][safi] = NULL;
|
|
}
|
|
}
|
|
|
|
if (zvrf->rnh_table[afi])
|
|
route_table_finish(zvrf->rnh_table[afi]);
|
|
if (zvrf->rnh_table_multicast[afi])
|
|
route_table_finish(zvrf->rnh_table[afi]);
|
|
}
|
|
|
|
otable = otable_pop(&zvrf->other_tables);
|
|
while (otable) {
|
|
zebra_router_release_table(zvrf, otable->table_id,
|
|
otable->afi, otable->safi);
|
|
XFREE(MTYPE_OTHER_TABLE, otable);
|
|
|
|
otable = otable_pop(&zvrf->other_tables);
|
|
}
|
|
|
|
/* Cleanup EVPN states for vrf */
|
|
zebra_vxlan_vrf_delete(zvrf);
|
|
|
|
list_delete_all_node(zvrf->rid_all_sorted_list);
|
|
list_delete_all_node(zvrf->rid_lo_sorted_list);
|
|
|
|
list_delete_all_node(zvrf->rid6_all_sorted_list);
|
|
list_delete_all_node(zvrf->rid6_lo_sorted_list);
|
|
|
|
otable_fini(&zvrf->other_tables);
|
|
XFREE(MTYPE_ZEBRA_VRF, zvrf);
|
|
vrf->info = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Lookup the routing table in a VRF based on both VRF-Id and table-id.
|
|
* NOTE: Table-id is relevant on two modes:
|
|
* - case VRF backend is default : on default VRF only
|
|
* - case VRF backend is netns : on all VRFs
|
|
*/
|
|
struct route_table *zebra_vrf_lookup_table_with_table_id(afi_t afi, safi_t safi,
|
|
vrf_id_t vrf_id,
|
|
uint32_t table_id)
|
|
{
|
|
struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id);
|
|
struct other_route_table ort, *otable;
|
|
|
|
if (!zvrf)
|
|
return NULL;
|
|
|
|
if (afi >= AFI_MAX || safi >= SAFI_MAX)
|
|
return NULL;
|
|
|
|
if (table_id == zvrf->table_id)
|
|
return zebra_vrf_table(afi, safi, vrf_id);
|
|
|
|
ort.afi = afi;
|
|
ort.safi = safi;
|
|
ort.table_id = table_id;
|
|
otable = otable_find(&zvrf->other_tables, &ort);
|
|
|
|
if (otable)
|
|
return otable->table;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct route_table *zebra_vrf_get_table_with_table_id(afi_t afi, safi_t safi,
|
|
vrf_id_t vrf_id,
|
|
uint32_t table_id)
|
|
{
|
|
struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id);
|
|
struct other_route_table *otable;
|
|
struct route_table *table;
|
|
|
|
table = zebra_vrf_lookup_table_with_table_id(afi, safi, vrf_id,
|
|
table_id);
|
|
|
|
if (table)
|
|
goto done;
|
|
|
|
/* Create it as an `other` table */
|
|
table = zebra_router_get_table(zvrf, table_id, afi, safi);
|
|
|
|
otable = XCALLOC(MTYPE_OTHER_TABLE, sizeof(*otable));
|
|
otable->afi = afi;
|
|
otable->safi = safi;
|
|
otable->table_id = table_id;
|
|
otable->table = table;
|
|
otable_add(&zvrf->other_tables, otable);
|
|
|
|
done:
|
|
return table;
|
|
}
|
|
|
|
static void zebra_rnhtable_node_cleanup(struct route_table *table,
|
|
struct route_node *node)
|
|
{
|
|
if (node->info)
|
|
zebra_free_rnh(node->info);
|
|
}
|
|
|
|
/*
|
|
* Create a routing table for the specific AFI/SAFI in the given VRF.
|
|
*/
|
|
static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi,
|
|
safi_t safi)
|
|
{
|
|
struct route_node *rn;
|
|
struct prefix p;
|
|
|
|
assert(!zvrf->table[afi][safi]);
|
|
|
|
zvrf->table[afi][safi] =
|
|
zebra_router_get_table(zvrf, zvrf->table_id, afi, safi);
|
|
|
|
memset(&p, 0, sizeof(p));
|
|
p.family = afi2family(afi);
|
|
|
|
rn = srcdest_rnode_get(zvrf->table[afi][safi], &p, NULL);
|
|
zebra_rib_create_dest(rn);
|
|
}
|
|
|
|
/* Allocate new zebra VRF. */
|
|
struct zebra_vrf *zebra_vrf_alloc(struct vrf *vrf)
|
|
{
|
|
struct zebra_vrf *zvrf;
|
|
|
|
zvrf = XCALLOC(MTYPE_ZEBRA_VRF, sizeof(struct zebra_vrf));
|
|
|
|
zvrf->vrf = vrf;
|
|
vrf->info = zvrf;
|
|
|
|
zebra_vxlan_init_tables(zvrf);
|
|
zebra_mpls_init_tables(zvrf);
|
|
zebra_pw_init(zvrf);
|
|
zvrf->table_id = RT_TABLE_MAIN;
|
|
/* by default table ID is default one */
|
|
return zvrf;
|
|
}
|
|
|
|
/* Lookup VRF by identifier. */
|
|
struct zebra_vrf *zebra_vrf_lookup_by_id(vrf_id_t vrf_id)
|
|
{
|
|
return vrf_info_lookup(vrf_id);
|
|
}
|
|
|
|
/* Lookup VRF by name. */
|
|
struct zebra_vrf *zebra_vrf_lookup_by_name(const char *name)
|
|
{
|
|
struct vrf *vrf;
|
|
|
|
if (!name)
|
|
name = VRF_DEFAULT_NAME;
|
|
|
|
vrf = vrf_lookup_by_name(name);
|
|
if (vrf)
|
|
return ((struct zebra_vrf *)vrf->info);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Lookup the routing table in an enabled VRF. */
|
|
struct route_table *zebra_vrf_table(afi_t afi, safi_t safi, vrf_id_t vrf_id)
|
|
{
|
|
struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id);
|
|
|
|
if (!zvrf)
|
|
return NULL;
|
|
|
|
if (afi >= AFI_MAX || safi >= SAFI_MAX)
|
|
return NULL;
|
|
|
|
return zvrf->table[afi][safi];
|
|
}
|
|
|
|
static int vrf_config_write(struct vty *vty)
|
|
{
|
|
struct vrf *vrf;
|
|
struct zebra_vrf *zvrf;
|
|
|
|
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
|
|
zvrf = vrf->info;
|
|
|
|
if (!zvrf)
|
|
continue;
|
|
|
|
if (zvrf_id(zvrf) == VRF_DEFAULT) {
|
|
if (zvrf->l3vni)
|
|
vty_out(vty, "vni %u%s\n", zvrf->l3vni,
|
|
is_l3vni_for_prefix_routes_only(
|
|
zvrf->l3vni)
|
|
? " prefix-routes-only"
|
|
: "");
|
|
if (zvrf->zebra_rnh_ip_default_route)
|
|
vty_out(vty, "ip nht resolve-via-default\n");
|
|
|
|
if (zvrf->zebra_rnh_ipv6_default_route)
|
|
vty_out(vty, "ipv6 nht resolve-via-default\n");
|
|
|
|
if (zvrf->tbl_mgr
|
|
&& (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end))
|
|
vty_out(vty, "ip table range %u %u\n",
|
|
zvrf->tbl_mgr->start,
|
|
zvrf->tbl_mgr->end);
|
|
} else {
|
|
vty_frame(vty, "vrf %s\n", zvrf_name(zvrf));
|
|
if (zvrf->l3vni)
|
|
vty_out(vty, " vni %u%s\n", zvrf->l3vni,
|
|
is_l3vni_for_prefix_routes_only(
|
|
zvrf->l3vni)
|
|
? " prefix-routes-only"
|
|
: "");
|
|
zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt);
|
|
if (zvrf->zebra_rnh_ip_default_route)
|
|
vty_out(vty, " ip nht resolve-via-default\n");
|
|
|
|
if (zvrf->zebra_rnh_ipv6_default_route)
|
|
vty_out(vty, " ipv6 nht resolve-via-default\n");
|
|
|
|
if (zvrf->tbl_mgr && vrf_is_backend_netns()
|
|
&& (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end))
|
|
vty_out(vty, " ip table range %u %u\n",
|
|
zvrf->tbl_mgr->start,
|
|
zvrf->tbl_mgr->end);
|
|
}
|
|
|
|
|
|
zebra_routemap_config_write_protocol(vty, zvrf);
|
|
router_id_write(vty, zvrf);
|
|
|
|
if (zvrf_id(zvrf) != VRF_DEFAULT)
|
|
vty_endframe(vty, "exit-vrf\n!\n");
|
|
else
|
|
vty_out(vty, "!\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEFPY (vrf_netns,
|
|
vrf_netns_cmd,
|
|
"netns NAME$netns_name",
|
|
"Attach VRF to a Namespace\n"
|
|
"The file name in " NS_RUN_DIR ", or a full pathname\n")
|
|
{
|
|
char *pathname = ns_netns_pathname(vty, netns_name);
|
|
int ret;
|
|
|
|
VTY_DECLVAR_CONTEXT(vrf, vrf);
|
|
|
|
if (!pathname)
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
|
|
frr_with_privs(&zserv_privs) {
|
|
ret = zebra_vrf_netns_handler_create(
|
|
vty, vrf, pathname, NS_UNKNOWN, NS_UNKNOWN, NS_UNKNOWN);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
DEFUN (no_vrf_netns,
|
|
no_vrf_netns_cmd,
|
|
"no netns [NAME]",
|
|
NO_STR
|
|
"Detach VRF from a Namespace\n"
|
|
"The file name in " NS_RUN_DIR ", or a full pathname\n")
|
|
{
|
|
struct ns *ns = NULL;
|
|
|
|
VTY_DECLVAR_CONTEXT(vrf, vrf);
|
|
|
|
if (!vrf_is_backend_netns()) {
|
|
vty_out(vty, "VRF backend is not Netns. Aborting\n");
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
if (!vrf->ns_ctxt) {
|
|
vty_out(vty, "VRF %s(%u) is not configured with NetNS\n",
|
|
vrf->name, vrf->vrf_id);
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
|
|
ns = (struct ns *)vrf->ns_ctxt;
|
|
|
|
ns->vrf_ctxt = NULL;
|
|
vrf_disable(vrf);
|
|
/* vrf ID from VRF is necessary for Zebra
|
|
* so that propagate to other clients is done
|
|
*/
|
|
ns_delete(ns);
|
|
vrf->ns_ctxt = NULL;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* if ns_id is different and not VRF_UNKNOWN,
|
|
* then update vrf identifier, and enable VRF
|
|
*/
|
|
static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr)
|
|
{
|
|
ns_id_t vrf_id = (vrf_id_t)ns_id;
|
|
vrf_id_t old_vrf_id;
|
|
struct vrf *vrf = (struct vrf *)opaqueptr;
|
|
|
|
if (!vrf)
|
|
return;
|
|
old_vrf_id = vrf->vrf_id;
|
|
if (vrf_id == vrf->vrf_id)
|
|
return;
|
|
if (vrf->vrf_id != VRF_UNKNOWN)
|
|
RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf);
|
|
vrf->vrf_id = vrf_id;
|
|
RB_INSERT(vrf_id_head, &vrfs_by_id, vrf);
|
|
if (old_vrf_id == VRF_UNKNOWN)
|
|
vrf_enable(vrf);
|
|
}
|
|
|
|
int zebra_vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
|
|
char *pathname, ns_id_t ns_id,
|
|
ns_id_t internal_ns_id,
|
|
ns_id_t rel_def_ns_id)
|
|
{
|
|
struct ns *ns = NULL;
|
|
|
|
if (!vrf)
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) {
|
|
if (vty)
|
|
vty_out(vty,
|
|
"VRF %u is already configured with VRF %s\n",
|
|
vrf->vrf_id, vrf->name);
|
|
else
|
|
zlog_info("VRF %u is already configured with VRF %s",
|
|
vrf->vrf_id, vrf->name);
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
if (vrf->ns_ctxt != NULL) {
|
|
ns = (struct ns *)vrf->ns_ctxt;
|
|
if (!strcmp(ns->name, pathname)) {
|
|
if (vty)
|
|
vty_out(vty,
|
|
"VRF %u already configured with NETNS %s\n",
|
|
vrf->vrf_id, ns->name);
|
|
else
|
|
zlog_info(
|
|
"VRF %u already configured with NETNS %s",
|
|
vrf->vrf_id, ns->name);
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
}
|
|
ns = ns_lookup_name(pathname);
|
|
if (ns && ns->vrf_ctxt) {
|
|
struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt;
|
|
|
|
if (vrf2 == vrf)
|
|
return CMD_SUCCESS;
|
|
if (vty)
|
|
vty_out(vty,
|
|
"NS %s is already configured with VRF %u(%s)\n",
|
|
ns->name, vrf2->vrf_id, vrf2->name);
|
|
else
|
|
zlog_info("NS %s is already configured with VRF %u(%s)",
|
|
ns->name, vrf2->vrf_id, vrf2->name);
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
ns = ns_get_created(ns, pathname, ns_id);
|
|
ns->internal_ns_id = internal_ns_id;
|
|
ns->relative_default_ns = rel_def_ns_id;
|
|
ns->vrf_ctxt = (void *)vrf;
|
|
vrf->ns_ctxt = (void *)ns;
|
|
/* update VRF netns NAME */
|
|
strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ);
|
|
|
|
if (!ns_enable(ns, vrf_update_vrf_id)) {
|
|
if (vty)
|
|
vty_out(vty, "Can not associate NS %u with NETNS %s\n",
|
|
ns->ns_id, ns->name);
|
|
else
|
|
zlog_info("Can not associate NS %u with NETNS %s",
|
|
ns->ns_id, ns->name);
|
|
return CMD_WARNING_CONFIG_FAILED;
|
|
}
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
/* Zebra VRF initialization. */
|
|
void zebra_vrf_init(void)
|
|
{
|
|
vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable,
|
|
zebra_vrf_delete);
|
|
|
|
hook_register(zserv_client_close, release_daemon_table_chunks);
|
|
|
|
vrf_cmd_init(vrf_config_write);
|
|
|
|
if (vrf_is_backend_netns() && ns_have_netns()) {
|
|
/* Install NS commands. */
|
|
install_element(VRF_NODE, &vrf_netns_cmd);
|
|
install_element(VRF_NODE, &no_vrf_netns_cmd);
|
|
}
|
|
}
|