]> andersk Git - openssh.git/blobdiff - openbsd-compat/port-tun.c
- dtucker@cvs.openbsd.org 2010/01/12 08:33:17
[openssh.git] / openbsd-compat / port-tun.c
index 479b46b7aa3bd08aafc7562b2407faa64b4b6841..ddc92d0f3f5562bbf36c274d2b86f950025632f7 100644 (file)
 
 #include "includes.h"
 
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "openbsd-compat/sys-queue.h"
 #include "log.h"
 #include "misc.h"
-#include "bufaux.h"
+#include "buffer.h"
+#include "channels.h"
 
 /*
  * This is the portable version of the SSH tunnel forwarding, it
@@ -26,6 +41,7 @@
  * settings.
  *
  * SSH_TUN_LINUX       Use the (newer) Linux tun/tap device
+ * SSH_TUN_FREEBSD     Use the FreeBSD tun/tap device
  * SSH_TUN_COMPAT_AF   Translate the OpenBSD address family
  * SSH_TUN_PREPEND_AF  Prepend/remove the address family
  */
@@ -35,6 +51,7 @@
  */
 
 #if defined(SSH_TUN_LINUX)
+#include <linux/if.h>
 #include <linux/if_tun.h>
 
 int
@@ -89,6 +106,91 @@ sys_tun_open(int tun, int mode)
 }
 #endif /* SSH_TUN_LINUX */
 
+#ifdef SSH_TUN_FREEBSD
+#include <sys/socket.h>
+#include <net/if.h>
+
+#ifdef HAVE_NET_IF_TUN_H
+#include <net/if_tun.h>
+#endif
+
+int
+sys_tun_open(int tun, int mode)
+{
+       struct ifreq ifr;
+       char name[100];
+       int fd = -1, sock, flag;
+       const char *tunbase = "tun";
+
+       if (mode == SSH_TUNMODE_ETHERNET) {
+#ifdef SSH_TUN_NO_L2
+               debug("%s: no layer 2 tunnelling support", __func__);
+               return (-1);
+#else
+               tunbase = "tap";
+#endif
+       }
+
+       /* Open the tunnel device */
+       if (tun <= SSH_TUNID_MAX) {
+               snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun);
+               fd = open(name, O_RDWR);
+       } else if (tun == SSH_TUNID_ANY) {
+               for (tun = 100; tun >= 0; tun--) {
+                       snprintf(name, sizeof(name), "/dev/%s%d",
+                           tunbase, tun);
+                       if ((fd = open(name, O_RDWR)) >= 0)
+                               break;
+               }
+       } else {
+               debug("%s: invalid tunnel %u\n", __func__, tun);
+               return (-1);
+       }
+
+       if (fd < 0) {
+               debug("%s: %s open failed: %s", __func__, name,
+                   strerror(errno));
+               return (-1);
+       }
+
+       /* Turn on tunnel headers */
+       flag = 1;
+#if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF)
+       if (mode != SSH_TUNMODE_ETHERNET &&
+           ioctl(fd, TUNSIFHEAD, &flag) == -1) {
+               debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd,
+                   strerror(errno));
+               close(fd);
+       }
+#endif
+
+       debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
+
+       /* Set the tunnel device operation mode */
+       snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun);
+       if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
+               goto failed;
+
+       if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
+               goto failed;
+       ifr.ifr_flags |= IFF_UP;
+       if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
+               goto failed;
+
+       close(sock);
+       return (fd);
+
+ failed:
+       if (fd >= 0)
+               close(fd);
+       if (sock >= 0)
+               close(sock);
+       debug("%s: failed to set %s mode %d: %s", __func__, name,
+           mode, strerror(errno));
+       return (-1);
+}
+#endif /* SSH_TUN_FREEBSD */
+
 /*
  * System-specific channel filters
  */
@@ -102,16 +204,29 @@ sys_tun_infilter(struct Channel *c, char *buf, int len)
 {
 #if defined(SSH_TUN_PREPEND_AF)
        char rbuf[CHAN_RBUF];
+       struct ip *iph;
 #endif
        u_int32_t *af;
        char *ptr = buf;
 
 #if defined(SSH_TUN_PREPEND_AF)
-       if (len > (int)(sizeof(rbuf) - sizeof(*af)))
+       if (len <= 0 || len > (int)(sizeof(rbuf) - sizeof(*af)))
                return (-1);
        ptr = (char *)&rbuf[0];
        bcopy(buf, ptr + sizeof(u_int32_t), len);
        len += sizeof(u_int32_t);
+       af = (u_int32_t *)ptr;
+
+       iph = (struct ip *)(ptr + sizeof(u_int32_t));
+       switch (iph->ip_v) {
+       case 6:
+               *af = AF_INET6;
+               break;
+       case 4:
+       default:
+               *af = AF_INET;
+               break;
+       }
 #endif
 
 #if defined(SSH_TUN_COMPAT_AF)
@@ -124,6 +239,7 @@ sys_tun_infilter(struct Channel *c, char *buf, int len)
        else
                *af = htonl(OPENBSD_AF_INET);
 #endif
+
        buffer_put_string(&c->input, ptr, len);
        return (0);
 }
This page took 0.229013 seconds and 4 git commands to generate.