summaryrefslogtreecommitdiff
path: root/lib/vrf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vrf.c')
-rw-r--r--lib/vrf.c381
1 files changed, 357 insertions, 24 deletions
diff --git a/lib/vrf.c b/lib/vrf.c
index 2e15fa2f5c..ea106b90a2 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -21,6 +21,9 @@
#include <zebra.h>
+/* for basename */
+#include <libgen.h>
+
#include "if.h"
#include "vrf.h"
#include "vrf_int.h"
@@ -29,6 +32,10 @@
#include "log.h"
#include "memory.h"
#include "command.h"
+#include "ns.h"
+
+/* default VRF ID value used when VRF backend is not NETNS */
+#define VRF_DEFAULT_INTERNAL 0
DEFINE_MTYPE_STATIC(LIB, VRF, "VRF")
DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map")
@@ -44,6 +51,8 @@ RB_GENERATE(vrf_name_head, vrf, name_entry, vrf_name_compare);
struct vrf_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id);
struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name);
+static int vrf_backend;
+
/*
* Turn on/off debug code
* for vrf.
@@ -61,7 +70,6 @@ struct vrf_master {
};
static int vrf_is_enabled(struct vrf *vrf);
-static void vrf_disable(struct vrf *vrf);
/* VRF list existance check by name. */
struct vrf *vrf_lookup_by_name(const char *name)
@@ -81,6 +89,54 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b)
return strcmp(a->name, b->name);
}
+/* 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((struct vrf *)vrf);
+}
+
+int vrf_switch_to_netns(vrf_id_t vrf_id)
+{
+ char *name;
+ struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+
+ /* VRF is default VRF. silently ignore */
+ if (!vrf || vrf->vrf_id == VRF_DEFAULT)
+ return 0;
+ /* VRF has no NETNS backend. silently ignore */
+ if (vrf->data.l.netns_name[0] == '\0')
+ return 0;
+ name = ns_netns_pathname(NULL, vrf->data.l.netns_name);
+ if (debug_vrf)
+ zlog_debug("VRF_SWITCH: %s(%u)", name, vrf->vrf_id);
+ return ns_switch_to_netns(name);
+}
+
+int vrf_switchback_to_initial(void)
+{
+ int ret = ns_switchback_to_initial();
+
+ if (ret == 0 && debug_vrf)
+ zlog_debug("VRF_SWITCHBACK");
+ return ret;
+}
+
/* Get a VRF. If not found, create one.
* Arg:
* name - The name of the vrf. May be NULL if unknown.
@@ -94,7 +150,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
int new = 0;
if (debug_vrf)
- zlog_debug("VRF_GET: %s(%u)", name, vrf_id);
+ zlog_debug("VRF_GET: %s(%u)",
+ name == NULL ? "(NULL)" : name, vrf_id);
/* Nothing to see, move along here */
if (!name && vrf_id == VRF_UNKNOWN)
@@ -109,8 +166,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
if (vrf == NULL) {
vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
vrf->vrf_id = VRF_UNKNOWN;
- RB_INIT(if_name_head, &vrf->ifaces_by_name);
- RB_INIT(if_index_head, &vrf->ifaces_by_index);
QOBJ_REG(vrf, vrf);
new = 1;
@@ -134,7 +189,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
strlcpy(vrf->name, name, sizeof(vrf->name));
RB_INSERT(vrf_name_head, &vrfs_by_name, vrf);
}
-
if (new &&vrf_master.vrf_new_hook)
(*vrf_master.vrf_new_hook)(vrf);
@@ -219,7 +273,7 @@ int vrf_enable(struct vrf *vrf)
* The VRF_DELETE_HOOK callback will be called to inform
* that they must release the resources in the VRF.
*/
-static void vrf_disable(struct vrf *vrf)
+void vrf_disable(struct vrf *vrf)
{
if (!vrf_is_enabled(vrf))
return;
@@ -385,6 +439,8 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
{
struct vrf *default_vrf;
+ /* initialise NS, in case VRF backend if NETNS */
+ ns_init();
if (debug_vrf)
zlog_debug("%s: Initializing VRF subsystem",
__PRETTY_FUNCTION__);
@@ -419,12 +475,17 @@ void vrf_terminate(void)
zlog_debug("%s: Shutting down vrf subsystem",
__PRETTY_FUNCTION__);
- while ((vrf = RB_ROOT(vrf_id_head, &vrfs_by_id)) != NULL) {
+ while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) {
+ vrf = RB_ROOT(vrf_id_head, &vrfs_by_id);
+
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
}
- while ((vrf = RB_ROOT(vrf_name_head, &vrfs_by_name)) != NULL) {
+
+ while (!RB_EMPTY(vrf_name_head, &vrfs_by_name)) {
+ vrf = RB_ROOT(vrf_name_head, &vrfs_by_name);
+
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
@@ -432,41 +493,163 @@ void vrf_terminate(void)
}
/* Create a socket for the VRF. */
-int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id)
+int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
+ char *interfacename)
{
- int ret = -1;
+ int ret, save_errno, ret2;
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0)
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
ret = socket(domain, type, protocol);
-
+ save_errno = errno;
+ ret2 = vrf_switchback_to_initial();
+ if (ret2 < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = save_errno;
+ if (ret <= 0)
+ return ret;
+ ret2 = vrf_bind(vrf_id, ret, interfacename);
+ if (ret2 < 0) {
+ close(ret);
+ ret = ret2;
+ }
return ret;
}
-/* vrf CLI commands */
-DEFUN_NOSH (vrf,
- vrf_cmd,
- "vrf NAME",
- "Select a VRF to configure\n"
- "VRF's name\n")
+int vrf_is_backend_netns(void)
+{
+ return (vrf_backend == VRF_BACKEND_NETNS);
+}
+
+int vrf_get_backend(void)
+{
+ return vrf_backend;
+}
+
+void vrf_configure_backend(int vrf_backend_netns)
+{
+ vrf_backend = vrf_backend_netns;
+}
+
+int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf)
{
- int idx_name = 1;
- const char *vrfname = argv[idx_name]->arg;
struct vrf *vrfp;
if (strlen(vrfname) > VRF_NAMSIZ) {
- vty_out(vty,
- "%% VRF name %s is invalid: length exceeds "
- "%d characters\n",
- vrfname, VRF_NAMSIZ);
+ if (vty)
+ vty_out(vty,
+ "%% VRF name %s invalid: length exceeds %d bytes\n",
+ vrfname, VRF_NAMSIZ);
+ else
+ zlog_warn(
+ "%% VRF name %s invalid: length exceeds %d bytes\n",
+ vrfname, VRF_NAMSIZ);
return CMD_WARNING_CONFIG_FAILED;
}
vrfp = vrf_get(VRF_UNKNOWN, vrfname);
- VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+ if (vty)
+ VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+
+ if (vrf)
+ *vrf = vrfp;
+ return CMD_SUCCESS;
+}
+
+int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
+ char *pathname, ns_id_t 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_warn("VRF %u is already configured with VRF %s\n",
+ vrf->vrf_id, vrf->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (vrf->ns_ctxt != NULL) {
+ ns = (struct ns *) vrf->ns_ctxt;
+ if (ns && 0 != strcmp(ns->name, pathname)) {
+ if (vty)
+ vty_out(vty,
+ "VRF %u already configured with NETNS %s\n",
+ vrf->vrf_id, ns->name);
+ else
+ zlog_warn(
+ "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_warn("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->vrf_ctxt = (void *)vrf;
+ vrf->ns_ctxt = (void *)ns;
+ /* update VRF netns NAME */
+ if (vrf)
+ 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_warn("Can not associate NS %u with NETNS %s",
+ ns->ns_id, ns->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
return CMD_SUCCESS;
}
+int vrf_is_mapped_on_netns(vrf_id_t vrf_id)
+{
+ struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+
+ if (!vrf || vrf->data.l.netns_name[0] == '\0')
+ return 0;
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return 0;
+ return 1;
+}
+
+/* vrf CLI commands */
+DEFUN_NOSH (vrf,
+ vrf_cmd,
+ "vrf NAME",
+ "Select a VRF to configure\n"
+ "VRF's name\n")
+{
+ int idx_name = 1;
+ const char *vrfname = argv[idx_name]->arg;
+
+ return vrf_handler_create(vty, vrfname, NULL);
+}
+
DEFUN_NOSH (no_vrf,
no_vrf_cmd,
"no vrf NAME",
@@ -500,6 +683,55 @@ DEFUN_NOSH (no_vrf,
struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1};
+DEFUN_NOSH (vrf_netns,
+ vrf_netns_cmd,
+ "netns NAME",
+ "Attach VRF to a Namespace\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+ int idx_name = 1;
+ char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
+
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+
+ if (!pathname)
+ return CMD_WARNING_CONFIG_FAILED;
+ return vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN);
+}
+
+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;
+}
+
/*
* Debug CLI for vrf's
*/
@@ -552,4 +784,105 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty))
install_element(CONFIG_NODE, &no_vrf_cmd);
install_node(&vrf_node, writefunc);
install_default(VRF_NODE);
+ 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);
+ }
+}
+
+vrf_id_t vrf_get_default_id(void)
+{
+ struct vrf *vrf = vrf_lookup_by_name(VRF_DEFAULT_NAME);
+
+ if (vrf)
+ return vrf->vrf_id;
+ if (vrf_is_backend_netns())
+ return ns_get_default_id();
+ else
+ return VRF_DEFAULT_INTERNAL;
+}
+
+int vrf_bind(vrf_id_t vrf_id, int fd, char *name)
+{
+ int ret = 0;
+
+ if (fd < 0 || name == NULL)
+ return fd;
+ if (vrf_is_mapped_on_netns(vrf_id))
+ return fd;
+#ifdef SO_BINDTODEVICE
+ ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name,
+ strlen(name));
+ if (ret < 0)
+ zlog_debug("bind to interface %s failed, errno=%d",
+ name, errno);
+#endif /* SO_BINDTODEVICE */
+ return ret;
+}
+int vrf_getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res, vrf_id_t vrf_id)
+{
+ int ret, ret2, save_errno;
+
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0)
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ ret = getaddrinfo(node, service, hints, res);
+ save_errno = errno;
+ ret2 = vrf_switchback_to_initial();
+ if (ret2 < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = save_errno;
+ return ret;
+}
+
+int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params)
+{
+ int ret, saved_errno, rc;
+
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0) {
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ return 0;
+ }
+ rc = ioctl(d, request, params);
+ saved_errno = errno;
+ ret = vrf_switchback_to_initial();
+ if (ret < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = saved_errno;
+ return rc;
+}
+
+int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id,
+ char *interfacename)
+{
+ int ret, save_errno, ret2;
+
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0)
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ ret = sockunion_socket(su);
+ save_errno = errno;
+ ret2 = vrf_switchback_to_initial();
+ if (ret2 < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = save_errno;
+
+ if (ret <= 0)
+ return ret;
+ ret2 = vrf_bind(vrf_id, ret, interfacename);
+ if (ret2 < 0) {
+ close(ret);
+ ret = ret2;
+ }
+ return ret;
}