peer, tunnels: New file-descriptor opening interface.
authorMark Wooding <mdw@distorted.org.uk>
Sat, 20 Dec 2008 17:06:10 +0000 (17:06 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 20 Dec 2008 17:06:10 +0000 (17:06 +0000)
Separate initializing tunnel devices into distinct stages of obtaining
an appropriate file descriptor, and configuring it and plumbing it into
the select loop.  Alas, this reduces the quality of error reporting when
tunnel acquisition fails.

This is a preliminary stage to implementing privilege separation in the
TrIPE server.

server/peer.c
server/tripe.h
server/tun-bsd.c
server/tun-linux.c
server/tun-slip.c
server/tun-unet.c

index 7f4d094..3cb12ea 100644 (file)
@@ -724,6 +724,8 @@ static void p_setkatimer(peer *p)
 peer *p_create(peerspec *spec)
 {
   peer *p = CREATE(peer);
+  const tunnel_ops *tops = spec->tops;
+  int fd;
   unsigned f;
 
   p->byname = sym_find(&byname, spec->name, -1, sizeof(peer_byname), &f);
@@ -740,11 +742,17 @@ peer *p_create(peerspec *spec)
   p->ifname = 0;
   memset(&p->st, 0, sizeof(stats));
   p->st.t_start = time(0);
-  if ((p->t = spec->tops->create(p, &p->ifname)) == 0)
+  if (!tops->open)
+    fd = -1;
+  else if ((fd = tops->open(&p->ifname)) < 0)
     goto tidy_2;
+  if ((p->t = tops->create(p, fd, &p->ifname)) == 0)
+    goto tidy_3;
+  T( trace(T_TUNNEL, "peer: attached interface %s to peer `%s'",
+          p->ifname, p_name(p)); )
   p_setkatimer(p);
   if (kx_init(&p->kx, p, &p->ks, p->spec.kxf))
-    goto tidy_3;
+    goto tidy_4;
   a_notify("ADD",
           "?PEER", p,
           "%s", p->ifname,
@@ -756,11 +764,13 @@ peer *p_create(peerspec *spec)
   }
   return (p);
 
-tidy_3:
+tidy_4:
   if (spec->t_ka)
     sel_rmtimer(&p->tka);
   xfree(p->ifname);
   p->t->ops->destroy(p->t);
+tidy_3:
+  if (fd >= 0) close(fd);
 tidy_2:
   am_remove(&byaddr, p->byaddr);
 tidy_1:
index 71c6023..d955c88 100644 (file)
@@ -288,7 +288,8 @@ struct peer;
 typedef struct tunnel_ops {
   const char *name;                    /* Name of this tunnel driver */
   void (*init)(void);                  /* Initializes the system */
-  tunnel *(*create)(struct peer */*p*/, char **/*ifn*/);
+  int (*open)(char **/*ifn*/);         /* Open tunnel and report ifname */
+  tunnel *(*create)(struct peer */*p*/, int /*fd*/, char **/*ifn*/);
                                        /* Initializes a new tunnel */
   void (*setifname)(tunnel */*t*/, const char */*ifn*/);
                                        /*  Notifies ifname change */
index 49b5e0e..200b5d9 100644 (file)
@@ -38,7 +38,6 @@ struct tunnel {
   const tunnel_ops *ops;               /* Pointer to operations */
   sel_file f;                          /* Selector for tunnel device */
   struct peer *p;                      /* Pointer to my peer */
-  unsigned n;                          /* Number of my tunnel device */
 };
 
 /* --- @t_read@ --- *
@@ -83,21 +82,20 @@ static void t_read(int fd, unsigned mode, void *v)
 
 static void t_init(void) { return; }
 
-/* --- @t_create@ --- *
+/* --- @t_open@ --- *
  *
- * Arguments:  @peer *p@ = pointer to peer block
- *             @char **ifn@ = where to put the interface name
+ * Arguments:  @char **ifn@ = where to put the interface name
  *
- * Returns:    A tunnel block if it worked, or null on failure.
+ * Returns:    A file descriptor, or @-1@ on failure.
  *
- * Use:                Initializes a new tunnel.
+ * Use:                Opens a tunnel device.  This will run with root privileges
+ *             even if the rest of the server has dropped them.
  */
 
-static tunnel *t_create(peer *p, char **ifn)
+static int t_open(char **ifn)
 {
   int fd;
   unsigned n;
-  tunnel *t;
   char buf[16];
 
   n = 0;
@@ -107,28 +105,41 @@ static tunnel *t_create(peer *p, char **ifn)
       break;
     switch (errno) {
       case EBUSY:
-       T( trace(T_TUNNEL, "tunnel device %u busy: skipping", n); )
-       break;
+       T( trace(T_TUNNEL, "tunnel device %u busy: skipping", n); )
+       break;
       case ENOENT:
-       a_warn("TUN", "-", "bsd", "no-tunnel-devices", A_END);
-       return (0);
+       a_warn("TUN", "-", "bsd", "no-tunnel-devices", A_END);
+       return (-1);
       default:
-       a_warn("TUN", "-", "open-error", "%s", buf, "?ERRNO", A_END);
-       break;
+       a_warn("TUN", "-", "open-error", "%s", buf, "?ERRNO", A_END);
+       break;
     }
     n++;
   }
+  return (fd);
+}
+
+/* --- @t_create@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to peer block
+ *             @int fd@ = file descriptor of tunnel device
+ *             @char **ifn@ = where to put the interface name
+ *
+ * Returns:    A tunnel block if it worked, or null on failure.
+ *
+ * Use:                Initializes a new tunnel.
+ */
 
+static tunnel *t_create(peer *p, int fd, char **ifn)
+{
+  tunnel *t;
+
+  fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   t = CREATE(tunnel);
   t->ops = &tun_bsd;
-  fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   t->p = p;
-  t->n = n;
   sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
   sel_addfile(&t->f);
-  *ifn = xstrdup(buf + 5);
-  T( trace(T_TUNNEL, "tun-bsd: attached interface %s to peer `%s'",
-          *ifn, p_name(p)); )
   return (t);
 }
 
@@ -166,6 +177,7 @@ static void t_destroy(tunnel *t)
 const tunnel_ops tun_bsd = {
   "bsd",
   t_init,
+  t_open,
   t_create,
   0,
   t_inject,
index cffeb56..11c7f37 100644 (file)
@@ -88,47 +88,61 @@ static void t_read(int fd, unsigned mode, void *v)
 
 static void t_init(void) { return; }
 
-/* --- @t_create@ --- *
+/* --- @t_open@ --- *
  *
- * Arguments:  @peer *p@ = pointer to peer block
- *             @char **ifn@ = where to put the interface name
+ * Arguments:  @char **ifn@ = where to put the interface name
  *
- * Returns:    A tunnel block if it worked, or null on failure.
+ * Returns:    A file descriptor, or @-1@ on failure.
  *
- * Use:                Initializes a new tunnel.
+ * Use:                Opens a tunnel device.  This will run with root privileges
+ *             even if the rest of the server has dropped them.
  */
 
-static tunnel *t_create(peer *p, char **ifn)
+static int t_open(char **ifn)
 {
   int fd;
-  int f;
   struct ifreq iff;
-  tunnel *t;
 
   if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
     a_warn("TUN", "-", "linux",
-           "open-error", "/dev/net/tun", "?ERRNO",
-           A_END);
-    return (0);
+          "open-error", "/dev/net/tun", "?ERRNO",
+          A_END);
+    return (-1);
   }
-  fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   memset(&iff, 0, sizeof(iff));
   iff.ifr_name[0] = 0;
   iff.ifr_flags = IFF_TUN | IFF_NO_PI;
-  if ((f = ioctl(fd, TUNSETIFF, &iff)) < 0) {
+  if (ioctl(fd, TUNSETIFF, &iff) < 0) {
     a_warn("TUN", "-", "linux", "config-error", "?ERRNO", A_END);
     close(fd);
-    return (0);
+    return (-1);
   }
+  iff.ifr_name[IFNAMSIZ - 1] = 0;
+  *ifn = xstrdup(iff.ifr_name);
+  return (fd);
+}
+
+/* --- @t_create@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to peer block
+ *             @int fd@ = file descriptor of tunnel device
+ *             @char **ifn@ = where to put the interface name
+ *
+ * Returns:    A tunnel block if it worked, or null on failure.
+ *
+ * Use:                Initializes a new tunnel.
+ */
+
+static tunnel *t_create(peer *p, int fd, char **ifn)
+{
+  tunnel *t;
+
+  fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   t = CREATE(tunnel);
   t->ops = &tun_linux;
   t->p = p;
   sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
   sel_addfile(&t->f);
-  iff.ifr_name[IFNAMSIZ - 1] = 0;
-  *ifn = xstrdup(iff.ifr_name);
-  T( trace(T_TUNNEL, "tun-linux: attached interface %s to peer `%s'",
-          *ifn, p_name(p)); )
   return (t);
 }
 
@@ -166,6 +180,7 @@ static void t_destroy(tunnel *t)
 const tunnel_ops tun_linux = {
   "linux",
   t_init,
+  t_open,
   t_create,
   0,
   t_inject,
index d405cc4..4afef75 100644 (file)
@@ -246,6 +246,7 @@ whine:
 /* --- @t_create@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
+ *             @int fd@ = file descriptor of tunnel device (unused)
  *             @char **ifn@ = where to put the interface name
  *
  * Returns:    A tunnel block if it worked, or null on failure.
@@ -253,7 +254,7 @@ whine:
  * Use:                Initializes a new tunnel.
  */
 
-static tunnel *t_create(peer *p, char **ifn)
+static tunnel *t_create(peer *p, int fd, char **ifn)
 {
   slipif *sl = 0;
   int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
@@ -440,6 +441,7 @@ static void t_destroy(tunnel *t)
 const tunnel_ops tun_slip = {
   "slip",
   t_init,
+  0,
   t_create,
   t_setifname,
   t_inject,
index 2d11ca0..88ce83d 100644 (file)
@@ -88,48 +88,65 @@ static void t_read(int fd, unsigned mode, void *v)
 
 static void t_init(void) { return; }
 
-/* --- @t_create@ --- *
+/* --- @t_open@ --- *
  *
- * Arguments:  @tunnel *t@ = pointer to tunnel block
- *             @peer *p@ = pointer to peer block
- *             @char *ifn@ = where to put the interface name
+ * Arguments:  @char **ifn@ = where to put the interface name
  *
- * Returns:    A tunnel block if it worked, or null on failure.
+ * Returns:    A file descriptor, or @-1@ on failure.
  *
- * Use:                Initializes a new tunnel.
+ * Use:                Opens a tunnel device.  This will run with root privileges
+ *             even if the rest of the server has dropped them.
  */
 
-static tunnel *t_create(peer *p, char **ifn)
+static int t_open(char **ifn)
 {
   int fd;
-  tunnel *t;
   int f;
   struct unet_info uni;
 
   if ((fd = open("/dev/unet", O_RDWR)) < 0) {
     a_warn("TUN", "-", "unet", "open-error", "/dev/unet", "?ERRNO", A_END);
-    return (0);
+    goto fail_0;
   }
-  fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0 ||
       ioctl(fd, UNIOCSIFFLAGS, f | IFF_POINTOPOINT)) {
     a_warn("TUN", "-", "unet", "config-error", "?ERRNO", A_END);
-    close(fd);
-    return (0);
+    goto fail_1;
   }
+  if (ioctl(t->f.fd, UNIOCGINFO, &uni)) {
+    a_warn("TUN", "-", "unet", "getinfo-error", "?ERRNO", A_END);
+    goto fail_1;
+  }
+  *ifn = xstrdup(uni.uni_ifname);
+  return (fd);
+
+fail_1:
+  close(fd);
+fail_0:
+  return (-1);
+}
+
+/* --- @t_create@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to peer block
+ *             @int fd@ = file descriptor of tunnel device
+ *             @char **ifn@ = where to put the interface name
+ *
+ * Returns:    A tunnel block if it worked, or null on failure.
+ *
+ * Use:                Initializes a new tunnel.
+ */
+
+static tunnel *t_create(peer *p, int fd, char **ifn)
+{
+  tunnel *t;
+
+  fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   t = CREATE(tunnel);
   t->ops = &tun_unet;
   t->p = p;
   sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
   sel_addfile(&t->f);
-
-  if (ioctl(t->f.fd, UNIOCGINFO, &uni)) {
-    a_warn("TUN", "-", "unet", "getinfo-error", "?ERRNO", A_END);
-    return (0);
-  }
-  *ifn = xstrdup(uni.uni_ifname);
-  T( trace(T_TUNNEL, "tun-unet: attached interface %s to peer `%s'",
-          *ifn, p_name(p)); )
   return (t);
 }
 
@@ -167,6 +184,7 @@ static void t_destroy(tunnel *t)
 const tunnel_ops tun_unet = {
   "unet",
   t_init,
+  t_open,
   t_create,
   0,
   t_inject,