session: Add sock_name option to add_ns

This adds a new API call to add session namespaces
It now takes a netns and a sock_name.
(1) If no netns is passed, sock_name will be used as
socket path. Defaulting to /run/vpp/app_ns_sockets/${ns_id}
(2) If a netns is passed, the sock_name has to be
abstract (i.e. start with '@'). It will default to
`@vpp/session/${ns_id}` and will be created in the provided
netns.

Type: feature

Change-Id: I90e9a8e5ecca2cabe7c05335663e33c8506dc9e7
Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
diff --git a/src/vnet/session/application_namespace.c b/src/vnet/session/application_namespace.c
index 6d91fc3..c1061c0 100644
--- a/src/vnet/session/application_namespace.c
+++ b/src/vnet/session/application_namespace.c
@@ -40,7 +40,7 @@
 }
 
 app_namespace_t *
-app_namespace_get_from_id (const u8 * ns_id)
+app_namespace_get_from_id (const u8 *ns_id)
 {
   u32 index = app_namespace_index_from_id (ns_id);
   if (index == APP_NAMESPACE_INVALID_INDEX)
@@ -55,14 +55,19 @@
 }
 
 app_namespace_t *
-app_namespace_alloc (u8 * ns_id)
+app_namespace_alloc (const u8 *ns_id)
 {
   app_namespace_t *app_ns;
+
   pool_get (app_namespace_pool, app_ns);
   clib_memset (app_ns, 0, sizeof (*app_ns));
-  app_ns->ns_id = vec_dup (ns_id);
+
+  app_ns->ns_id = vec_dup ((u8 *) ns_id);
+  vec_terminate_c_string (app_ns->ns_id);
+
   hash_set_mem (app_namespace_lookup_table, app_ns->ns_id,
 		app_ns - app_namespace_pool);
+
   return app_ns;
 }
 
@@ -71,6 +76,7 @@
 {
   app_namespace_t *app_ns;
   session_table_t *st;
+  int rv;
 
   if (a->is_add)
     {
@@ -105,6 +111,7 @@
 	}
       app_ns->ns_secret = a->secret;
       app_ns->netns = a->netns ? vec_dup (a->netns) : 0;
+      app_ns->sock_name = a->sock_name ? vec_dup (a->sock_name) : 0;
       app_ns->sw_if_index = a->sw_if_index;
       app_ns->ip4_fib_index =
 	fib_table_find (FIB_PROTOCOL_IP4, a->ip4_fib_id);
@@ -114,7 +121,11 @@
 
       /* Add socket for namespace */
       if (app_sapi_enabled)
-	appns_sapi_add_ns_socket (app_ns);
+	{
+	  rv = appns_sapi_add_ns_socket (app_ns);
+	  if (rv)
+	    return rv;
+	}
     }
   else
     {
@@ -133,7 +144,13 @@
 app_namespace_index_from_id (const u8 * ns_id)
 {
   uword *indexp;
-  indexp = hash_get_mem (app_namespace_lookup_table, ns_id);
+  u8 *key;
+
+  key = vec_dup ((u8 *) ns_id);
+  vec_terminate_c_string (key);
+
+  indexp = hash_get_mem (app_namespace_lookup_table, key);
+  vec_free (key);
   if (!indexp)
     return APP_NAMESPACE_INVALID_INDEX;
   return *indexp;
@@ -190,6 +207,7 @@
   vnet_app_namespace_add_del_args_t a = {
     .ns_id = ns_id,
     .netns = 0,
+    .sock_name = 0,
     .secret = 0,
     .sw_if_index = APP_NAMESPACE_INVALID_INDEX,
     .is_add = 1
@@ -204,9 +222,11 @@
 app_ns_fn (vlib_main_t * vm, unformat_input_t * input,
 	   vlib_cli_command_t * cmd)
 {
-  u8 is_add = 0, *ns_id = 0, secret_set = 0, sw_if_index_set = 0, *netns = 0;
+  u8 is_add = 0, *ns_id = 0, secret_set = 0, sw_if_index_set = 0;
+  u8 *netns = 0, *sock_name = 0;
   unformat_input_t _line_input, *line_input = &_line_input;
   u32 sw_if_index, fib_id = APP_NAMESPACE_INVALID_INDEX;
+  vnet_main_t *vnm = vnet_get_main ();
   u64 secret;
   clib_error_t *error = 0;
   int rv;
@@ -226,10 +246,15 @@
 	secret_set = 1;
       else if (unformat (line_input, "sw_if_index %u", &sw_if_index))
 	sw_if_index_set = 1;
+      else if (unformat (line_input, "if %U", unformat_vnet_sw_interface, vnm,
+			 &sw_if_index))
+	sw_if_index_set = 1;
       else if (unformat (line_input, "fib_id", &fib_id))
 	;
       else if (unformat (line_input, "netns %_%v%_", &netns))
 	;
+      else if (unformat (line_input, "sock-name %_%v%_", &sock_name))
+	;
       else
 	{
 	  error = clib_error_return (0, "unknown input `%U'",
@@ -240,8 +265,8 @@
 
   if (!ns_id || !secret_set || !sw_if_index_set)
     {
-      vlib_cli_output (vm, "namespace-id, secret and sw_if_index must be "
-		       "provided");
+      vlib_cli_output (vm, "namespace-id, secret and interface must be "
+			   "provided");
       goto done;
     }
 
@@ -251,6 +276,7 @@
       vnet_app_namespace_add_del_args_t args = {
 	.ns_id = ns_id,
 	.netns = netns,
+	.sock_name = sock_name,
 	.secret = secret,
 	.sw_if_index = sw_if_index,
 	.ip4_fib_id = fib_id,
@@ -266,6 +292,7 @@
 
   vec_free (ns_id);
   vec_free (netns);
+  vec_free (sock_name);
   unformat_free (line_input);
 
   return error;
@@ -275,7 +302,7 @@
 VLIB_CLI_COMMAND (app_ns_command, static) = {
   .path = "app ns",
   .short_help = "app ns [add] id <namespace-id> secret <secret> "
-		"sw_if_index <sw_if_index> [netns <ns>]",
+		"if <intfc> [netns <ns>]",
   .function = app_ns_fn,
 };
 /* *INDENT-ON* */
@@ -284,11 +311,18 @@
 format_app_namespace (u8 * s, va_list * args)
 {
   app_namespace_t *app_ns = va_arg (*args, app_namespace_t *);
+  vnet_main_t *vnm = vnet_get_main ();
 
-  s =
-    format (s, "%-10u%-10lu%-15d%-15v%-15v%-40v", app_namespace_index (app_ns),
-	    app_ns->ns_secret, app_ns->sw_if_index, app_ns->ns_id,
-	    app_ns->netns, app_ns->sock_name);
+  s = format (s, "Application namespace [%u]\nid:        %s\nsecret:    %lu",
+	      app_namespace_index (app_ns), app_ns->ns_id, app_ns->ns_secret);
+  if (app_ns->sw_if_index != (u32) ~0)
+    s = format (s, "\nInterface: %U", format_vnet_sw_if_index_name, vnm,
+		app_ns->sw_if_index);
+  if (app_ns->netns)
+    s = format (s, "\nNetns:     %s", app_ns->netns);
+  if (app_ns->sock_name)
+    s = format (s, "\nSocket:    %s", app_ns->sock_name);
+
   return s;
 }
 
@@ -335,8 +369,9 @@
 		vlib_cli_command_t * cmd)
 {
   unformat_input_t _line_input, *line_input = &_line_input;
-  u8 *ns_id, do_table = 0, had_input = 1, do_api = 0;
+  u8 *ns_id = 0, do_table = 0, had_input = 1, do_api = 0;
   app_namespace_t *app_ns;
+  vnet_main_t *vnm = vnet_get_main ();
   session_table_t *st;
 
   session_cli_return_if_not_enabled ();
@@ -349,7 +384,7 @@
 
   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     {
-      if (unformat (line_input, "table %_%v%_", &ns_id))
+      if (unformat (line_input, "id %_%v%_", &ns_id))
 	do_table = 1;
       else if (unformat (line_input, "api-clients"))
 	do_api = 1;
@@ -386,18 +421,22 @@
 	  vlib_cli_output (vm, "table for ns %v could not be found", ns_id);
 	  goto done;
 	}
+      vlib_cli_output (vm, "%U", format_app_namespace, app_ns);
       session_lookup_show_table_entries (vm, st, 0, 1);
       vec_free (ns_id);
       goto done;
     }
 
 do_ns_list:
-  vlib_cli_output (vm, "%-10s%-10s%-15s%-15s%-15s%-40s", "Index", "Secret",
-		   "sw_if_index", "Id", "netns", "Socket");
+  vlib_cli_output (vm, "%-10s%-10s%-12s%-15s%-20s%-40s", "Index", "Secret",
+		   "Interface", "Id", "Netns", "Socket");
 
   /* *INDENT-OFF* */
   pool_foreach (app_ns, app_namespace_pool)  {
-    vlib_cli_output (vm, "%U", format_app_namespace, app_ns);
+      vlib_cli_output (vm, "%-10u%-10lu%-12U%-15s%-20s%-40v",
+		       app_namespace_index (app_ns), app_ns->ns_secret,
+		       format_vnet_sw_if_index_name, vnm, app_ns->sw_if_index,
+		       app_ns->ns_id, app_ns->netns, app_ns->sock_name);
   }
   /* *INDENT-ON* */
 
@@ -408,10 +447,9 @@
 }
 
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (show_app_ns_command, static) =
-{
+VLIB_CLI_COMMAND (show_app_ns_command, static) = {
   .path = "show app ns",
-  .short_help = "show app ns [table <id> [api-clients]]",
+  .short_help = "show app ns [id <id> [api-clients]]",
   .function = show_app_ns_fn,
 };
 /* *INDENT-ON* */
diff --git a/src/vnet/session/application_namespace.h b/src/vnet/session/application_namespace.h
index 313b2d0..5449d47 100644
--- a/src/vnet/session/application_namespace.h
+++ b/src/vnet/session/application_namespace.h
@@ -70,6 +70,7 @@
 {
   u8 *ns_id;
   u8 *netns;
+  u8 *sock_name;
   u64 secret;
   u32 sw_if_index;
   u32 ip4_fib_id;
@@ -79,13 +80,13 @@
 
 #define APP_NAMESPACE_INVALID_INDEX ((u32)~0)
 
-app_namespace_t *app_namespace_alloc (u8 * ns_id);
+app_namespace_t *app_namespace_alloc (const u8 *ns_id);
 app_namespace_t *app_namespace_get (u32 index);
-app_namespace_t *app_namespace_get_from_id (const u8 * ns_id);
+app_namespace_t *app_namespace_get_from_id (const u8 *ns_id);
 u32 app_namespace_index (app_namespace_t * app_ns);
 const u8 *app_namespace_id (app_namespace_t * app_ns);
 const u8 *app_namespace_id_from_index (u32 index);
-u32 app_namespace_index_from_id (const u8 * ns_id);
+u32 app_namespace_index_from_id (const u8 *ns_id);
 void app_namespaces_init (void);
 int vnet_app_namespace_add_del (vnet_app_namespace_add_del_args_t * a);
 u32 app_namespace_get_fib_index (app_namespace_t * app_ns, u8 fib_proto);
diff --git a/src/vnet/session/session.api b/src/vnet/session/session.api
index 43bde1a..0eb773c 100644
--- a/src/vnet/session/session.api
+++ b/src/vnet/session/session.api
@@ -252,6 +252,34 @@
   string netns[64];
 };
 
+/** \brief add/del application namespace
+    @param client_index - opaque cookie to identify the sender
+                          client to vpp direction only
+    @param context - sender context, to match reply w/ request
+    @param secret - secret shared between app and vpp
+    @param sw_if_index - local interface that "supports" namespace. Set to
+                         ~0 if no preference
+    @param ip4_fib_id - id of ip4 fib that "supports" the namespace. Ignored
+                        if sw_if_index set.
+    @param ip6_fib_id - id of ip6 fib that "supports" the namespace. Ignored
+                        if sw_if_index set.
+    @param namespace_id - namespace id
+    @param netns - linux net namespace
+    @param sock_name - socket name (path, abstract socket name)
+*/
+define app_namespace_add_del_v3 {
+  u32 client_index;
+  u32 context;
+  u64 secret;
+  bool is_add [default=true];
+  vl_api_interface_index_t sw_if_index [default=0xffffffff];
+  u32 ip4_fib_id;
+  u32 ip6_fib_id;
+  string namespace_id[64];
+  string netns[64];
+  string sock_name[];
+};
+
 /** \brief Reply for app namespace add/del
     @param context - returned sender context, to match reply w/ request
     @param retval - return code
@@ -277,6 +305,13 @@
   u32 appns_index;
 };
 
+define app_namespace_add_del_v3_reply
+{
+  u32 context;
+  i32 retval;
+  u32 appns_index;
+};
+
 enum session_rule_scope {
 	SESSION_RULE_SCOPE_API_GLOBAL = 0,
 	SESSION_RULE_SCOPE_API_LOCAL = 1,
diff --git a/src/vnet/session/session_api.c b/src/vnet/session/session_api.c
index 371fcfc..c0ed125 100644
--- a/src/vnet/session/session_api.c
+++ b/src/vnet/session/session_api.c
@@ -834,6 +834,8 @@
 
   vnet_app_namespace_add_del_args_t args = {
     .ns_id = ns_id,
+    .netns = 0,
+    .sock_name = 0,
     .secret = clib_net_to_host_u64 (mp->secret),
     .sw_if_index = clib_net_to_host_u32 (mp->sw_if_index),
     .ip4_fib_id = clib_net_to_host_u32 (mp->ip4_fib_id),
@@ -884,6 +886,7 @@
   vnet_app_namespace_add_del_args_t args = {
     .ns_id = ns_id,
     .netns = netns,
+    .sock_name = 0,
     .secret = clib_net_to_host_u64 (mp->secret),
     .sw_if_index = clib_net_to_host_u32 (mp->sw_if_index),
     .ip4_fib_id = clib_net_to_host_u32 (mp->ip4_fib_id),
@@ -896,7 +899,7 @@
       appns_index = app_namespace_index_from_id (ns_id);
       if (appns_index == APP_NAMESPACE_INVALID_INDEX)
 	{
-	  clib_warning ("app ns lookup failed");
+	  clib_warning ("app ns lookup failed id:%s", ns_id);
 	  rv = VNET_API_ERROR_UNSPECIFIED;
 	}
     }
@@ -911,6 +914,54 @@
 }
 
 static void
+vl_api_app_namespace_add_del_v3_t_handler (
+  vl_api_app_namespace_add_del_v3_t *mp)
+{
+  vl_api_app_namespace_add_del_v3_reply_t *rmp;
+  u8 *ns_id = 0, *netns = 0, *sock_name = 0;
+  u32 appns_index = 0;
+  int rv = 0;
+  if (session_main_is_enabled () == 0)
+    {
+      rv = VNET_API_ERROR_FEATURE_DISABLED;
+      goto done;
+    }
+  mp->namespace_id[sizeof (mp->namespace_id) - 1] = 0;
+  mp->netns[sizeof (mp->netns) - 1] = 0;
+  ns_id = format (0, "%s", &mp->namespace_id);
+  netns = format (0, "%s", &mp->netns);
+  sock_name = format (0, "%s", &mp->sock_name);
+  vnet_app_namespace_add_del_args_t args = {
+    .ns_id = ns_id,
+    .netns = netns,
+    .sock_name = sock_name,
+    .secret = clib_net_to_host_u64 (mp->secret),
+    .sw_if_index = clib_net_to_host_u32 (mp->sw_if_index),
+    .ip4_fib_id = clib_net_to_host_u32 (mp->ip4_fib_id),
+    .ip6_fib_id = clib_net_to_host_u32 (mp->ip6_fib_id),
+    .is_add = mp->is_add,
+  };
+  rv = vnet_app_namespace_add_del (&args);
+  if (!rv && mp->is_add)
+    {
+      appns_index = app_namespace_index_from_id (ns_id);
+      if (appns_index == APP_NAMESPACE_INVALID_INDEX)
+	{
+	  clib_warning ("app ns lookup failed id:%s", ns_id);
+	  rv = VNET_API_ERROR_UNSPECIFIED;
+	}
+    }
+  vec_free (ns_id);
+  vec_free (netns);
+  vec_free (sock_name);
+done:
+  REPLY_MACRO2 (VL_API_APP_NAMESPACE_ADD_DEL_V3_REPLY, ({
+		  if (!rv)
+		    rmp->appns_index = clib_host_to_net_u32 (appns_index);
+		}));
+}
+
+static void
 vl_api_session_rule_add_del_t_handler (vl_api_session_rule_add_del_t * mp)
 {
   vl_api_session_rule_add_del_reply_t *rmp;
@@ -1722,19 +1773,27 @@
   clib_socket_t *cs;
   char dir[4096];
 
-  snprintf (dir, sizeof (dir), "%s%s", vlib_unix_get_runtime_dir (), subdir);
-  err = vlib_unix_recursive_mkdir ((char *) dir);
-  if (err)
-    {
-      clib_error_report (err);
-      return -1;
-    }
-
-  /* Use abstract sockets if a netns was provided */
   if (app_ns->netns)
-    app_ns->sock_name = format (0, "@vpp/session/%v%c", app_ns->ns_id, 0);
+    {
+      if (!app_ns->sock_name)
+	app_ns->sock_name = format (0, "@vpp/session/%v%c", app_ns->ns_id, 0);
+      if (app_ns->sock_name[0] != '@')
+	return VNET_API_ERROR_INVALID_VALUE;
+    }
   else
-    app_ns->sock_name = format (0, "%s%v%c", dir, app_ns->ns_id, 0);
+    {
+      snprintf (dir, sizeof (dir), "%s%s", vlib_unix_get_runtime_dir (),
+		subdir);
+      err = vlib_unix_recursive_mkdir ((char *) dir);
+      if (err)
+	{
+	  clib_error_report (err);
+	  return VNET_API_ERROR_SYSCALL_ERROR_1;
+	}
+
+      if (!app_ns->sock_name)
+	app_ns->sock_name = format (0, "%s%v%c", dir, app_ns->ns_id, 0);
+    }
 
   /*
    * Create and initialize socket to listen on
diff --git a/src/vnet/session/session_lookup.c b/src/vnet/session/session_lookup.c
index 6e060cb..5cd1712 100644
--- a/src/vnet/session/session_lookup.c
+++ b/src/vnet/session/session_lookup.c
@@ -1452,6 +1452,7 @@
 			 vlib_cli_command_t * cmd)
 {
   u32 proto = ~0, lcl_port, rmt_port, action = 0, lcl_plen = 0, rmt_plen = 0;
+  clib_error_t *error = 0;
   u32 appns_index, scope = 0;
   ip46_address_t lcl_ip, rmt_ip;
   u8 is_ip4 = 1, conn_set = 0;
@@ -1501,29 +1502,32 @@
       else if (unformat (input, "tag %_%v%_", &tag))
 	;
       else
-	return clib_error_return (0, "unknown input `%U'",
-				  format_unformat_error, input);
+	{
+	  error = clib_error_return (0, "unknown input `%U'",
+				     format_unformat_error, input);
+	  goto done;
+	}
     }
 
   if (proto == ~0)
     {
       vlib_cli_output (vm, "proto must be set");
-      return 0;
+      goto done;
     }
   if (is_add && !conn_set && action == ~0)
     {
       vlib_cli_output (vm, "connection and action must be set for add");
-      return 0;
+      goto done;
     }
   if (!is_add && !tag && !conn_set)
     {
       vlib_cli_output (vm, "connection or tag must be set for delete");
-      return 0;
+      goto done;
     }
   if (vec_len (tag) > SESSION_RULE_TAG_MAX_LEN)
     {
       vlib_cli_output (vm, "tag too long (max u64)");
-      return 0;
+      goto done;
     }
 
   if (ns_id)
@@ -1532,7 +1536,7 @@
       if (!app_ns)
 	{
 	  vlib_cli_output (vm, "namespace %v does not exist", ns_id);
-	  return 0;
+	  goto done;
 	}
     }
   else
@@ -1559,10 +1563,12 @@
     .scope = scope,
   };
   if ((rv = vnet_session_rule_add_del (&args)))
-    return clib_error_return (0, "rule add del returned %u", rv);
+    error = clib_error_return (0, "rule add del returned %u", rv);
 
+done:
+  vec_free (ns_id);
   vec_free (tag);
-  return 0;
+  return error;
 }
 
 /* *INDENT-OFF* */