GNS3 / ubridge

Bridge for UDP tunnels, Ethernet, TAP and VMnet interfaces.
GNU General Public License v3.0
115 stars 46 forks source link

ubridge doesn't work with tap interfaces on OS X #45

Closed ghost closed 7 years ago

ghost commented 7 years ago

ubridge 0.9.11 and 0.9.12, GNS3 version 2.0.4dev1 on Darwin (64-bit)

While TAP connections work in GNS3 v1.5, I wasn't able to use them on GNS3 V2.0, see also https://www.gns3.com/qa/how-to-connect-os-x-terminal-to-

Test project, router and cloud are running on the local OS X: osxtap

Cloud configuration: cloud_config

Other than in linux the TAP interfaces are not automatically detected in OS X. I have to use "/dev/tap0", otherwise the TAP device won't open, "tap0" as TAP device doesn't work.

After opening the TAP device I configured 10.1.1.1/24 on OS X and 10.1.1.100/24 on the router. But the router sees no (really 0) packets from OS X. Therefore no communication can happen.

In debug mode the ubridge.log looks like this:

/Users/behlers/GNS3/projects/osxtap/project-files/builtin/831e1592-8482-4b13-8605-2cfcca83c5c3# cat ubridge.log
recv: Input/output error
Hypervisor TCP control server started (IP 192.168.1.10 port 50638).
UDP tunnel connecting from local port 10000 to IPv4 addresss 192.168.1.10 on port 10001
Source NIO listener thread for 831e1592-8482-4b13-8605-2cfcca83c5c3-3 has started
Destination NIO listener thread for 831e1592-8482-4b13-8605-2cfcca83c5c3-3 has started
Destination NIO listener thread for 831e1592-8482-4b13-8605-2cfcca83c5c3-3 has stopped
Received 60 bytes on bridge '831e1592-8482-4b13-8605-2cfcca83c5c3-3' (source NIO)
0000: ff ff ff ff ff ff c2 01 03 bf 00 00 08 06 00 01 ................
0010: 08 00 06 04 00 02 c2 01 03 bf 00 00 0a 01 01 64 ...............d
0020: ff ff ff ff ff ff 0a 01 01 64 00 00 00 00 00 00 .........d......
0030: 00 00 00 00 00 00 00 00 00 00 00 00             ............

The hypervisor shows the following:

/Users/behlers/GNS3/projects/osxtap/project-files/builtin/831e1592-8482-4b13-8605-2cfcca83c5c3# telnet 192.168.1.10 50638
Trying 192.168.1.10...
Connected to imac.lan.
Escape character is '^]'.
bridge list
101 831e1592-8482-4b13-8605-2cfcca83c5c3-3 (NIOs = 2)
100-OK
bridge show 831e1592-8482-4b13-8605-2cfcca83c5c3-3
101 bridge '831e1592-8482-4b13-8605-2cfcca83c5c3-3' is running
101 Source NIO: 10000:192.168.1.10:10001
101 Destination NIO: /dev/tap0
100-OK
ghost commented 7 years ago

Found it!!!

The first issue is, that stdout is buffered, therefore the log output created by printf is shown, when the buffer is full. That delays the output, and messages send to stderr may be shown earlier. Therefore I changed the buffering of stdout and stderr to line buffering.

diff --git a/src/ubridge.c b/src/ubridge.c
index 39f1dc3..ccbf13a 100644
--- a/src/ubridge.c
+++ b/src/ubridge.c
@@ -338,6 +338,9 @@ int main(int argc, char **argv)
   char *index;
   size_t len;

+  setvbuf(stdout, NULL, _IOLBF, 0);
+  setvbuf(stderr, NULL, _IOLBF, 0);
+
   printf("uBridge version %s running with %s\n", VERSION, pcap_lib_version());
   while ((opt = getopt(argc, argv, "hved:f:H:")) != -1) {
     switch (opt) {

That gives a better log output:

/Users/behlers/GNS3/projects/osxtap/project-files/builtin/831e1592-8482-4b13-8605-2cfcca83c5c3# cat ubridge.log
uBridge version 0.9.13 running with libpcap version 1.5.3 - Apple version 54
Hypervisor TCP control server started (IP 192.168.1.10 port 50401).
UDP tunnel connecting from local port 10000 to IPv4 addresss 192.168.1.10 on port 10001
Source NIO listener thread for 831e1592-8482-4b13-8605-2cfcca83c5c3-3 has started
Destination NIO listener thread for 831e1592-8482-4b13-8605-2cfcca83c5c3-3 has started
recv: Input/output error
Destination NIO listener thread for 831e1592-8482-4b13-8605-2cfcca83c5c3-3 has stopped

That shows, that the read from the TAP devices returns EIO and therefore the destination listener thread has stopped. According to the TUNTAP FAQ http://tuntaposx.sourceforge.net/faq.xhtml (last question) this is normal, the user hasn't activated the TAP IP interface yet. So I updated nio_tap.c to ignore EIO on OSX.

diff --git a/src/nio_tap.c b/src/nio_tap.c
index d6cfdcb..b7eb61e 100644
--- a/src/nio_tap.c
+++ b/src/nio_tap.c
@@ -111,7 +111,15 @@ static ssize_t nio_tap_send(nio_tap_t *nio_tap, void *pkt, size_t pkt_len)

 static ssize_t nio_tap_recv(nio_tap_t *nio_tap, void *pkt, size_t max_len)
 {
+#ifdef __APPLE__
+   /* ignore EIO error, TAP IP interface is not yet up */
+   ssize_t ret;
+   while ((ret = read(nio_tap->fd, pkt, max_len)) < 0 && errno == EIO)
+     sleep(1);
+   return ret;
+#else
    return (read(nio_tap->fd, pkt, max_len));
+#endif
 }

 /* Create a new NIO TAP */
ghost commented 7 years ago

And the PR https://github.com/GNS3/dynamips/pull/79 might be valuable here as well. That should allow to use "tap0" as the TAP device, instead of "/dev/tap0".

ghost commented 7 years ago

Instead of polling with read() for an active interface, select() can be used (at least on my OS X v10.11).

The patch would then look like this:

diff --git a/src/nio_tap.c b/src/nio_tap.c
index d6cfdcb..57c2f71 100644
--- a/src/nio_tap.c
+++ b/src/nio_tap.c
@@ -111,6 +111,14 @@ static ssize_t nio_tap_send(nio_tap_t *nio_tap, void *pkt, size_t pkt_len)

 static ssize_t nio_tap_recv(nio_tap_t *nio_tap, void *pkt, size_t max_len)
 {
+#ifdef __APPLE__
+   /* wait for an active IP interface and incoming data */
+   fd_set tap_fd_set;
+   FD_ZERO(&tap_fd_set);
+   FD_SET(nio_tap->fd, &tap_fd_set);
+   if (select(nio_tap->fd + 1, &tap_fd_set, NULL, NULL, NULL) < 0)
+     perror("nio_tap_recv: select");
+#endif
    return (read(nio_tap->fd, pkt, max_len));
 }
grossmj commented 7 years ago

Thanks a lot! :+1:

ghost commented 7 years ago

Well, integration of https://github.com/GNS3/dynamips/pull/79 isn't that simple. NETIO_DEV_MAXLEN is used in dynamips, in ubridge it's named NIO_DEV_MAXLEN. So currently ubridge doesn't compile.

Another comment to my perror("nio_tap_recv: select");: Maybe a simple return -1 is better. The other send/recv functions normally return a status, errors are printed in the calling function. But for me both variants are fine.

grossmj commented 7 years ago

I will make the changes soon. I compiled on Linux so I couldn't test the changes before I commit.