Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting network namespace #36

Closed
vasu-dasari opened this issue Sep 19, 2017 · 3 comments
Closed

Setting network namespace #36

vasu-dasari opened this issue Sep 19, 2017 · 3 comments

Comments

@vasu-dasari
Copy link
Contributor

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

@vasu-dasari
Copy link
Contributor Author

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

@msantos
Copy link
Owner

msantos commented Sep 20, 2017

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) ->
     "".

@vasu-dasari
Copy link
Contributor Author

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

msantos added a commit that referenced this issue Sep 21, 2017
Add support for setns(2) to the setuid helper. The helper is used to:

* avoid running beam with root privs
* avoid the beam process joining the namespace

Unlike inet(3), the option tuple is {namespace, NS} vs {netns, NS}. A
future change may allow the caller to specify the namespaces, e.g.:

    {namespace, NS, [net,ipc]}

Currently all namespaces are joined.

#36

Thanks @vasu-dasari!
@msantos msantos closed this as completed Sep 22, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants