msantos / procket

Erlang interface to low level socket operations

Home Page:http://blog.listincomprehension.com/search/label/procket

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Setting network namespace

vasu-dasari opened this issue · comments

Hello,

Is there a way I can specify network namespace to use for the socket? For example, gen_udp/gen_tcp has an option to specify netns via {netns,NS} tuple. Is there anything similar to that while using procket? If this is achieved by some other means, can someone point me to the fist or snippet?

Thanks
-Vasu

I tried the following snippet, but, bind function returns {error,eafnosupport}:
I do get right IfIndex(in my case case 8) via packet:ifindex(Fd,"veth1"). Just that bind is failing.

{ok,Port} = gen_udp:open(0,[{netns,"/var/run/netns/blue"}]),
packet:bind(inet:getfd(Port), packet:ifindex(Fd,"veth1")),

Commands I have used to create namespace:

ip netns add blue
ip link add veth0 type veth peer name veth1
ip link set veth1 netns blue
ip netns exec blue ifconfig veth1 10.1.1.1/24 up
ifconfig veth0 10.1.1.100/24 up

This is a great question. About the failure: we're returned the {error, eafnosupport} tuple because we're attempting to bind an AF_INET socket. packet:bind/2 expects an AF_PACKET socket. If we try specifying an AF_INET socket:

1> {ok,Port} = gen_udp:open(0,[{netns,"/var/run/netns/blue"}]).
{ok,#Port<0.37928>}
2> {ok, Fd} = inet:getfd(Port).
{ok,12}
3> Ifindex = packet:ifindex(Fd,"veth1").
6
4> packet:bind(Fd, Ifindex).
{error,eafnosupport}
5> SA = <<2:16/native, 0:16, 6:32/native, 0:96>>.
<<2,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>
6> procket:bind(Fd, SA).
{error,eaddrnotavail}

For namespace support, we'd probably want to support any type of socket.

1> {ok, S} = procket:open(0, [{protocol, 16#0008}, {type, raw}, {family, packet}, {namespace, "/var/run/netns/blue"}]).                                                          
{ok,14}                                                                                                                                                                          
                                                                                                                                                                                 
2> {ok, Q} = procket:read(S, 16#ffff).                                                                                                                                           
{ok,<<1,0,94,0,0,251,246,255,141,18,35,1,8,0,69,0,0,68,                                                                                                                          
      51,22,64,0,1,17,90,51,10,...>>}                                                                                                                                            
                                                                                                                                                                                 
3> pkt:decode(Q).                                                                                                                                                                
{ok,{[{ether,<<1,0,94,0,0,251>>,                                                                                                                                                 
             <<246,255,141,18,35,1>>,                                                                                                                                            
             2048,0},                                                                                                                                                            
      {ipv4,4,5,0,68,13078,1,0,0,1,17,23091,                                                                                                                                     
            {10,1,1,100},                                                                                                                                                        
            {224,0,0,251},                                                                                                                                                       
            <<>>},                                                                                                                                                               
      {udp,5353,5353,48,60577}],                                                                                                                                                 
     <<0,0,0,0,0,1,0,0,0,0,0,0,11,95,103,111,111,103,108,101,                                                                                                                    
       99,97,115,116,4,...>>}}              

Here is patch to support namespaces (without tests or portability goop):

diff --git a/c_src/procket.h b/c_src/procket.h
index ec7c9b7..30646b2 100644
--- a/c_src/procket.h
+++ b/c_src/procket.h
@@ -82,6 +82,7 @@ typedef struct {
     char *port;             /* Port */
     char *ifname;           /* network interface name */
     char *dev;              /* Open a character device */
+    char *ns;               /* Open a namespace */
     int verbose;            /* Debug messages */
     int s;                  /* socket fd */
     int family;             /* socket family: PF_INET */
diff --git a/c_src/procket_cmd.c b/c_src/procket_cmd.c
index 9078043..5eda94e 100644
--- a/c_src/procket_cmd.c
+++ b/c_src/procket_cmd.c
@@ -29,12 +29,15 @@
  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */
+#define _GNU_SOURCE
+#include <sched.h>
+
 #include "procket.h"
 #include "ancillary.h"
 
-
 #define BACKLOG     128  /* default backlog for TCP connections */
 
+int procket_open_ns(PROCKET_STATE *ps);
 int procket_open_fd(PROCKET_STATE *ps);
 int procket_check_devname(char *dev, size_t len);
 int procket_pipe(PROCKET_STATE *ps);
@@ -72,7 +75,7 @@ main(int argc, char *argv[])
 
     ps->fdtype = PROCKET_FD_SOCKET;
 
-    while ( (ch = getopt(argc, argv, "b:d:F:hI:p:P:T:u:v")) != -1) {
+    while ( (ch = getopt(argc, argv, "b:d:F:hI:N:p:P:T:u:v")) != -1) {
         switch (ch) {
             case 'b':   /* listen backlog */
                 ps->backlog = atoi(optarg);
@@ -124,6 +127,12 @@ main(int argc, char *argv[])
 
                 ps->fdtype = PROCKET_FD_CHARDEV;
                 break;
+            case 'N':   /* namespace */
+                ps->ns = strdup(optarg);
+
+                if (ps->ns == NULL)
+                    error_result(ps, errno);
+                break;
             case 'v':
                 ps->verbose++;
                 break;
@@ -149,6 +158,9 @@ main(int argc, char *argv[])
             error_result(ps, ENAMETOOLONG);
     }
 
+    if (procket_open_ns(ps) < 0)
+        error_result(ps, errno);
+
     if (procket_open_fd(ps) < 0)
         error_result(ps, errno);
 
@@ -161,6 +173,24 @@ main(int argc, char *argv[])
     exit(0);
 }
 
+    int
+procket_open_ns(PROCKET_STATE *ps)
+{
+    int fd = 0;
+
+    if (ps->ns == NULL)
+        return 0;
+
+    fd = open(ps->ns, O_RDONLY);
+
+    if (fd < 0)
+        return -1;
+
+    if (setns(fd, 0 /* join all namespaces */) < 0)
+        return -1;
+
+    return 0;
+}
 
     int
 procket_open_fd(PROCKET_STATE *ps)
diff --git a/src/procket.erl b/src/procket.erl
index fc9f488..993cdd2 100644
--- a/src/procket.erl
+++ b/src/procket.erl
@@ -435,6 +435,9 @@ optarg({dev, Dev}) when is_list(Dev) ->
             erlang:error(badarg, [{dev, Dev}])
     end;
 
+optarg({namespace, NS}) when is_list(NS) ->
+    switch("N", NS);
+
 % Ignore any other arguments
 optarg(_Arg) ->
     "".

Thanks. Actually, I have a patch which is working. I just did a pull request can you take a look?