lib/vrf: enable / disable a VRF

A new API vrf_is_enabled() is defined to check whether a VRF is ready
to use, that is, to allocate resources in that VRF. Currently there's
only one type of resource: socket.

Two new hooks VRF_ENABLE_HOOK/VRF_DISABLE_HOOK are introduced to tell
the user when a VRF gets ready or to be unavailable.

The VRF_ENABLE_HOOK callback is called in the new function vrf_enable(),
which is used to let the VRF be ready to use. Till now, only the default
VRF can be enabled, and we need do nothing to enable the default, except
calling the hook.

The VRF_DISABLE_HOOK callback is called in the new function
vrf_disable(), which is used to let the VRF be unusable. Till now,
it is called only when the VRF is to be deleted.

A new utility vrf_socket() is defined to provide a socket in a given
VRF to the user.

Till now before introducing a way of VRF realization, only the default
VRF is enabled since its birth, and vrf_socket() creates socket for
only the default VRF.

This patch defines the framework of the VRF APIs. The way they serve
the users is:
- vrf_is_enabled() is used to tell the user whether a VRF is usable;
- users are informed by the VRF_ENABLE_HOOK that a VRF gets usable;
  they can allocate resources after that;
- users are informed by the VRF_DISABLE_HOOK that a VRF is to be
  unavailable, and they must release the resources instantly;
- vrf_socket() is used to provide a socket in a given VRF.

Signed-off-by: Feng Lu <lu.feng@6wind.com>
Reviewed-by: Alain Ritoux <alain.ritoux@6wind.com>
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Acked-by: Vincent JARDIN <vincent.jardin@6wind.com>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/lib/vrf.c b/lib/vrf.c
index ea14fd3..d11a982 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -29,6 +29,8 @@
 #include "log.h"
 #include "memory.h"
 
+#define VRF_DEFAULT_NAME    "Default-IP-Routing-Table"
+
 struct vrf
 {
   /* Identifier, same as the vector index */
@@ -48,11 +50,18 @@
 {
   int (*vrf_new_hook) (vrf_id_t, void **);
   int (*vrf_delete_hook) (vrf_id_t, void **);
+  int (*vrf_enable_hook) (vrf_id_t, void **);
+  int (*vrf_disable_hook) (vrf_id_t, void **);
 } vrf_master = {0,};
 
 /* VRF table */
 struct route_table *vrf_table = NULL;
 
+static int vrf_is_enabled (struct vrf *vrf);
+static int vrf_enable (struct vrf *vrf);
+static void vrf_disable (struct vrf *vrf);
+
+
 /* Build the table key */
 static void
 vrf_build_key (vrf_id_t vrf_id, struct prefix *p)
@@ -100,6 +109,9 @@
 {
   zlog_info ("VRF %u is to be deleted.", vrf->vrf_id);
 
+  if (vrf_is_enabled (vrf))
+    vrf_disable (vrf);
+
   if (vrf_master.vrf_delete_hook)
     (*vrf_master.vrf_delete_hook) (vrf->vrf_id, &vrf->info);
 
@@ -129,6 +141,61 @@
   return vrf;
 }
 
+/*
+ * Check whether the VRF is enabled - that is, whether the VRF
+ * is ready to allocate resources. Currently there's only one
+ * type of resource: socket.
+ */
+static int
+vrf_is_enabled (struct vrf *vrf)
+{
+  return vrf && vrf->vrf_id == VRF_DEFAULT;
+}
+
+/*
+ * Enable a VRF - that is, let the VRF be ready to use.
+ * The VRF_ENABLE_HOOK callback will be called to inform
+ * that they can allocate resources in this VRF.
+ *
+ * RETURN: 1 - enabled successfully; otherwise, 0.
+ */
+static int
+vrf_enable (struct vrf *vrf)
+{
+  /* Till now, only the default VRF can be enabled. */
+  if (vrf->vrf_id == VRF_DEFAULT)
+    {
+      zlog_info ("VRF %u is enabled.", vrf->vrf_id);
+
+      if (vrf_master.vrf_enable_hook)
+        (*vrf_master.vrf_enable_hook) (vrf->vrf_id, &vrf->info);
+
+      return 1;
+    }
+
+  return 0;
+}
+
+/*
+ * Disable a VRF - that is, let the VRF be unusable.
+ * 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)
+{
+  if (vrf_is_enabled (vrf))
+    {
+      zlog_info ("VRF %u is to be disabled.", vrf->vrf_id);
+
+      /* Till now, nothing to be done for the default VRF. */
+
+      if (vrf_master.vrf_disable_hook)
+        (*vrf_master.vrf_disable_hook) (vrf->vrf_id, &vrf->info);
+    }
+}
+
+
 /* Add a VRF hook. Please add hooks before calling vrf_init(). */
 void
 vrf_add_hook (int type, int (*func)(vrf_id_t, void **))
@@ -140,6 +207,12 @@
   case VRF_DELETE_HOOK:
     vrf_master.vrf_delete_hook = func;
     break;
+  case VRF_ENABLE_HOOK:
+    vrf_master.vrf_enable_hook = func;
+    break;
+  case VRF_DISABLE_HOOK:
+    vrf_master.vrf_disable_hook = func;
+    break;
   default:
     break;
   }
@@ -281,7 +354,14 @@
     }
 
   /* Set the default VRF name. */
-  default_vrf->name = XSTRDUP (MTYPE_VRF_NAME, "Default-IP-Routing-Table");
+  default_vrf->name = XSTRDUP (MTYPE_VRF_NAME, VRF_DEFAULT_NAME);
+
+  /* Enable the default VRF. */
+  if (!vrf_enable (default_vrf))
+    {
+      zlog_err ("vrf_init: failed to enable the default VRF!");
+      exit (1);
+    }
 }
 
 /* Terminate VRF module. */
@@ -299,3 +379,23 @@
   vrf_table = NULL;
 }
 
+/* Create a socket for the VRF. */
+int
+vrf_socket (int domain, int type, int protocol, vrf_id_t vrf_id)
+{
+  int ret = -1;
+
+  if (!vrf_is_enabled (vrf_lookup (vrf_id)))
+    {
+      errno = ENOSYS;
+      return -1;
+    }
+
+  if (vrf_id == VRF_DEFAULT)
+    ret = socket (domain, type, protocol);
+  else
+    errno = ENOSYS;
+
+  return ret;
+}
+