tunctl icon indicating copy to clipboard operation
tunctl copied to clipboard

don't down interface

Open alexshavelev opened this issue 8 years ago • 6 comments

for example we have two interfaces: eth1 and tap2. we connect to eth1 via ewpcap, work and than use ewpcap:close. after interface is still up. after we restart application which works with tap2 via tunctl we need every type to up interface manually like 'ifup tap2'. it's not useful to make it via tunctl:up, because application works on different nodes and we need custom configs with ips for interfaces.

is there any possibility to keep interface up? if we delete call of '_ = tunctl:down(Dev),' or 'procket:close(FD)' interface is up, but we get 'ebusy' error

alexshavelev avatar Aug 15 '17 06:08 alexshavelev

i've changed terminate function in tuncer to tunctl:persist(FD, true). but it looks strange. after stop application 'ifconfig' don't show tap2 interface and:

tcpdump -i tap2
tcpdump: tap2: That device is not up
ifup tap2
ioctl(TUNSETIFF): Device or resource busy

alexshavelev avatar Aug 15 '17 07:08 alexshavelev

hm, but when we start application tcpdump shows traffic but half the time

alexshavelev avatar Aug 15 '17 07:08 alexshavelev

@alexshavelev sorry I didn't see this issue!

Which OS are you using?

msantos avatar Aug 17 '17 14:08 msantos

centos 7

alexshavelev avatar Aug 17 '17 15:08 alexshavelev

@alexshavelev commenting out down/1 and persist/2 should've worked. I'll run some tests and check out what is going on.

About the persist flag being reset on terminate, that is probably a bug. Will take a look.

msantos avatar Aug 17 '17 16:08 msantos

After applying the fix you suggested to persist the interface after tuncer shuts down, I can see what is going on:

% $ ip addr show dev test0
% Device "test0" does not exist.

{ok, T} = tuncer:create(<<"test0">>).

% $ ip addr show dev test0
% 11: test0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 500
%     link/ether 6e:87:44:1f:6d:b3 brd ff:ff:ff:ff:ff:ff
%     inet 10.10.0.3/8 brd 10.255.255.255 scope global test0
%        valid_lft forever preferred_lft forever

tuncer:up(T, "10.10.0.3").
tuncer:setopt(T, {active, true}).

tuncer:persist(T, true).

tuncer:getfd(T). % fd 13

% $ ip addr show dev test0
% 12: test0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 500
%     link/ether b2:6b:d0:63:ed:19 brd ff:ff:ff:ff:ff:ff
%     inet 10.10.0.3/8 brd 10.255.255.255 scope global test0
%        valid_lft forever preferred_lft forever
%     inet6 fe80::b06b:d0ff:fe63:ed19/64 scope link
%        valid_lft forever preferred_lft forever

flush().

% {tuntap, ..., ...}

exit(T, kill).

{ok, T1} = tuncer:create(<<"test0">>).
** exception exit: {badmatch,{error,ebusy}}

procket:read(13, 1). % returns {ok, <<255>>}
procket:close(13). % ok

{ok, T1} = tuncer:create(<<"test0">>). % works!

So under some circumstances the terminate callback of the tuncer gen_server isn't called, leaving the tuntap fd open.

One workaround might be to store the fd number and close it:

{ok, T} = tuncer:create(<<"test0">>),
FD = tuncer:getfd(T),
procket:close(FD).

The fix will probably require adding a function to procket that stores the fd as an NIF resource. When the fd resource goes out of scope, the NIF will close the fd.

Some other bugs to fix:

  • tuncer:up/2: "ip addr show tap0" displays the interface status as UNKNOWN instead of UP

  • tuncer:create(<<"test">>, [{active,true}]) returns {error, einval}

  • add tuncer:up/1, similar to "ifconfig tap0 up" for already configured devices

I'll fix these over the next few days. Thanks for reporting this bug!

diff --git a/src/tuncer.erl b/src/tuncer.erl
index 6733cac..5c0cc78 100644
--- a/src/tuncer.erl
+++ b/src/tuncer.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2011-2016, Michael Santos <[email protected]>
+%% Copyright (c) 2011-2017, Michael Santos <[email protected]>
 %% All rights reserved.
 %%
 %% Redistribution and use in source and binary forms, with or without
@@ -64,7 +64,8 @@
         pid,        % PID of controlling process
         fd,         % TUN/TAP file descriptor
         dev,        % device name
-        flag        % TUNSETIFF ifr flags
+        flag,       % TUNSETIFF ifr flags
+        persist
     }).
 
 -define(IFNAMSIZ, 16).
@@ -254,7 +255,7 @@ handle_call({recv, _Len}, _From, State) ->
 
 handle_call({persist, Status}, _From, #state{fd = FD} = State) ->
     Reply = tunctl:persist(FD, Status),
-    {reply, Reply, State};
+    {reply, Reply, State#state{persist = Status}};
 
 handle_call({owner, Owner}, _From, #state{fd = FD} = State) ->
     Reply = tunctl:owner(FD, Owner),
@@ -290,10 +291,15 @@ handle_info({'EXIT',_,normal}, State) ->
 handle_info({'EXIT', _, _}, #state{port = false} = State) ->
     {noreply, State};
 
-handle_info({'EXIT', Port, Error}, #state{port = Port, pid = Pid, fd = FD, dev = Dev} = State) ->
+handle_info({'EXIT', Port, Error}, #state{port = Port, pid = Pid, fd = FD, dev = Dev, persist = Persist} = State) ->
     Pid ! {tuntap_error, self(), Error},
-    _ = tunctl:down(Dev),
-    tunctl:persist(FD, false),
+    case Persist of
+        true ->
+            ok;
+        false ->
+            _ = tunctl:down(Dev),
+            tunctl:persist(FD, false)
+    end,
     procket:close(FD),
     {stop, normal, State};
 

msantos avatar Aug 20 '17 14:08 msantos