chiark / gitweb /
server/, mon/: Introduce transport of TrIPE over IPv6.
authorMark Wooding <mdw@distorted.org.uk>
Fri, 29 Sep 2017 09:08:52 +0000 (10:08 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Thu, 28 Jun 2018 23:29:23 +0000 (00:29 +0100)
This depends on ADNS for IPv6 name resolution.

mon/tripemon.in
server/addrmap.c
server/peer.c
server/servutil.c
server/tripe-admin.5.in
server/tripe.8.in
server/tripe.c
server/tripe.h
svc/tripe-ifup.in

index 593b2e8bda2953f2ee0496eda55ebdb672a6a710..1c70d1ff0f446dbccf1787aaaf1e97f8aefbb58c 100644 (file)
@@ -323,13 +323,17 @@ class Peer (MonitorObject):
 
   def _setaddr(me, addr):
     """Set the peer's address."""
-    if addr[0] == 'INET':
+    if addr[0] in ['INET', 'INET6']:
       af, ipaddr, port = addr
       try:
         name, _ = S.getnameinfo((ipaddr, int(port)),
                                 S.NI_NUMERICSERV | S.NI_NAMEREQD)
       except S.gaierror:
-        me.addr = '%s %s:%s' % (af, ipaddr, port)
+        me.addr = '%s %s%s%s:%s' % (af,
+                                    af == 'INET6' and '[' or '',
+                                    ipaddr,
+                                    af == 'INET6' and ']' or '',
+                                    port)
       else:
         me.addr = '%s %s:%s [%s]' % (af, name, port, ipaddr)
     else:
@@ -1044,6 +1048,8 @@ class AddPeerDialog (MyDialog):
     * e_name, e_addr, e_port, c_keepalive, l_tunnel: widgets in the dialog
   """
 
+  AFS = ['ANY', 'INET', 'INET6']
+
   def __init__(me):
     """Initialize the dialogue."""
     MyDialog.__init__(me, 'Add peer',
@@ -1059,8 +1065,13 @@ class AddPeerDialog (MyDialog):
     me.e_name = table.labelled('Name',
                                ValidatingEntry(r'^[^\s.:]+$', '', 16),
                                width = 3)
+    me.l_af = table.labelled('Family', combo_box_text(),
+                             newlinep = True, width = 3)
+    for af in me.AFS:
+      me.l_af.append_text(af)
+    me.l_af.set_active(0)
     me.e_addr = table.labelled('Address',
-                               ValidatingEntry(r'^[a-zA-Z0-9.-]+$', '', 24),
+                               ValidatingEntry(r'^[a-zA-Z0-9.-:]+$', '', 24),
                                newlinep = True)
     me.e_port = table.labelled('Port',
                                ValidatingEntry(numericvalidate(0, 65535),
@@ -1106,7 +1117,9 @@ class AddPeerDialog (MyDialog):
     """Handle an OK press: create the peer."""
     try:
       t = me.l_tunnel.get_active()
+      afix = me.l_af.get_active()
       me._addpeer(me.e_name.get_text(),
+                  me.AFS[afix],
                   me.e_addr.get_text(),
                   me.e_port.get_text(),
                   keepalive = (me.c_keepalive.get_active() and
index 6091dbfc3a779817976d28c799ac660ea94f7122..76a358ea34eec387e54fe419615169f593ad8a35 100644 (file)
@@ -74,11 +74,21 @@ void am_destroy(addrmap *m)
 
 static uint32 hash(const addr *a)
 {
+  size_t i;
+  uint32 h;
+
   switch (a->sa.sa_family) {
     case AF_INET:
       return (U32(0x4eaac1b7ul*AF_INET +
                  0xa5dbc837ul*a->sin.sin_addr.s_addr +
                  0x3b049e83ul*a->sin.sin_port));
+    case AF_INET6:
+      for (i = 0, h = 0; i < 16; i++)
+       h = 0x6bd26a67ul*h + a->sin6.sin6_addr.s6_addr[i];
+      return (U32(0x4eaac1b7ul*AF_INET6 +
+                 0xa5dbc837ul*h +
+                 0x1d94eab4ul*a->sin6.sin6_scope_id +
+                 0x3b049e83ul*a->sin6.sin6_port));
     default:
       abort();
   }
@@ -99,6 +109,10 @@ static int addreq(const addr *a, const addr *b)
     case AF_INET:
       return (a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr &&
              a->sin.sin_port == b->sin.sin_port);
+    case AF_INET6:
+      return (!memcmp(a->sin6.sin6_addr.s6_addr,
+                     b->sin6.sin6_addr.s6_addr, 16) &&
+             a->sin6.sin6_port == b->sin6.sin6_port);
     default:
       abort();
   }
index 34bfb6c4d2b3653de18bb77424c9e5b409048b6c..94629213ca4e37ea85e30bfc33fd00fba9265fff 100644 (file)
@@ -791,6 +791,7 @@ void p_init(struct addrinfo *ailist)
 {
   int fd;
   int len = PKBUFSZ;
+  int yes = 1;
   int i;
   struct addrinfo *ai;
   unsigned port, lastport = 0;
@@ -813,6 +814,11 @@ void p_init(struct addrinfo *ailist)
 
     if ((fd = socket(ai->ai_family, SOCK_DGRAM, 0)) < 0)
       die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno));
+    if (i == AFIX_INET6 &&
+       setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes))) {
+      die(EXIT_FAILURE, "failed to set IPv6-only state: %s",
+         strerror(errno));
+    }
     assert(ai->ai_addrlen <= sizeof(a));
     memcpy(&a, ai->ai_addr, ai->ai_addrlen);
     if ((port = getport(&a)) == 0 && lastport) setport(&a, lastport);
index a4de37843152ddbeaa409454852d4168ee31cf17..f95541a8af609216a7f55ebeea32f365d441ba18 100644 (file)
@@ -160,6 +160,7 @@ socklen_t addrsz(const addr *a)
 {
   switch (a->sa.sa_family) {
     case AF_INET: return (sizeof(a->sin));
+    case AF_INET6: return (sizeof(a->sin6));
     default: abort();
   }
 }
@@ -178,6 +179,7 @@ unsigned getport(addr *a)
 {
   switch (a->sa.sa_family) {
     case AF_INET: return (ntohs(a->sin.sin_port)); break;
+    case AF_INET6: return (ntohs(a->sin6.sin6_port)); break;
     default: abort();
   }
 }
@@ -186,6 +188,7 @@ void setport(addr *a, unsigned port)
 {
   switch (a->sa.sa_family) {
     case AF_INET: a->sin.sin_port = htons(port); break;
+    case AF_INET6: a->sin6.sin6_port = htons(port); break;
     default: abort();
   }
 }
index 44af6a5828870a7691268cf8d84d6787ab7a58fd..81dd570d68ccb344bb109670ce33d8a52a8109b7 100644 (file)
@@ -275,6 +275,16 @@ is always in numeric dotted-quad form, and the
 is given as a plain decimal number.  On input, DNS hostnames and
 symbolic port names are permitted; if omitted, the default port 4070 is
 used.
+.TP
+.BI "INET6 " address " \fR[" port \fR]
+An Internet socket, naming an IPv6 address and UDP port.  On output, the
+.I address
+is always in numeric hex-and-colons form, and the
+.I port
+is given as a plain decimal number.  On input, DNS hostnames and
+symbolic port names may be permitted, depending on how
+.B tripe
+was compiled; if omitted, the default port 4070 is used.
 .PP
 If, on input, no recognized address family token is found, the following
 tokens are assumed to represent an
@@ -490,12 +500,16 @@ tunnel interface.  If
 is the MTU of the path to the peer, then the tunnel MTU should be
 .IP
 .I MTU
-\- 29 \-
+\-
+.I header-length
+\- 9 \-
 .I bulk-overhead
 .PP
-allowing 20 bytes of IP header, 8 bytes of UDP header, a packet type
-octet, and the bulk-crypto transform overhead (which includes the
-sequence number).
+allowing
+.I header-length
+= 20 (IPv4) or 40 (IPv6) bytes of IP header, 8 bytes of UDP header, a
+packet type octet, and the bulk-crypto transform overhead (which
+includes the sequence number).
 .RE
 .SP
 .BI "BGCANCEL " tag
@@ -1019,6 +1033,15 @@ An unknown watch option was requested.
 An error occurred during the attempt to become a daemon, as reported by
 .IR message .
 .SP
+.BI "disabled-address-family " afam
+(For
+.B ADD
+and
+.BR PORT .)
+The address family
+.I afam
+is supported, but was disabled using command-line arguments.
+.SP
 .BI "invalid-port " number
 (For
 .BR ADD .)
index aaaf2678b99a8cedb07f9909cc829e449cbce11f..8b7868283ad1ea8920c39acaf173a508edd81f76 100644 (file)
@@ -37,7 +37,7 @@ tripe \- a simple VPN daemon
 .SH "SYNOPSIS"
 .
 .B tripe
-.RB [ \-DF ]
+.RB [ \-46DF ]
 .RB [ \-d
 .IR dir ]
 .RB [ \-b
@@ -165,6 +165,15 @@ Writes to standard output a list of the configured tunnel drivers, one
 per line, and exits with status 0.  This is intended for the use of the
 start-up script, so that it can check that it will actually work.
 .TP
+.B "\-4, \-\-ipv4"
+Use only IPv4 addresses.  The server will resolve names only to IPv4
+addresses, and not attempt to create IPv6 sockets.
+.TP
+.B "\-6, \-\-ipv6"
+Use only IPv6 addresses.  The server will resolve names only to IPv6
+addresses, and not attempt to create IPv4 sockets.  Note that v6-mapped
+IPv4 addresses won't work either.
+.TP
 .B "\-D, \-\-daemon"
 Dissociates from its terminal and starts running in the background after
 completing the initialization procedure described above.  If running as
index d8a6cc130efd79d19faba8a2cc24bbf6a8d58c03..565c83dd2d16b6f12d0de7d0ee59e6bbb5e60ae0 100644 (file)
@@ -91,6 +91,8 @@ Options:\n\
 -u, --usage            Display pointless usage message.\n\
     --tunnels          Display IP tunnel drivers and exit.\n\
 \n\
+-4, --ipv4             Transport over IPv4 only.\n\
+-6, --ipv6             Transport over IPv6 only.\n\
 -D, --daemon           Run in the background.\n\
 -F, --foreground       Quit when stdin reports end-of-file.\n\
 -d, --directory=DIR    Switch to directory DIR [default " CONFIGDIR "].\n\
@@ -141,7 +143,7 @@ int main(int argc, char *argv[])
   if ((p = getenv("TRIPESOCK")) != 0)
     csock = p;
   tun_default = tunnels[0];
-  aihint.ai_family = AF_INET;
+  aihint.ai_family = AF_UNSPEC;
 
   for (;;) {
     static const struct option opts[] = {
@@ -150,6 +152,8 @@ int main(int argc, char *argv[])
       { "usage",       0,              0,      'u' },
       { "tunnels",     0,              0,      '0' },
 
+      { "ipv4",                0,              0,      '4' },
+      { "ipv6",                0,              0,      '6' },
       { "daemon",      0,              0,      'D' },
       { "foreground",  0,              0,      'F' },
       { "uid",         OPTF_ARGREQ,    0,      'U' },
@@ -172,7 +176,7 @@ int main(int argc, char *argv[])
       { 0,             0,              0,      0 }
     };
 
-    i = mdwopt(argc, argv, "hvuDFU:G:b:n:p:d:k:K:t:a:m:" T("T:"),
+    i = mdwopt(argc, argv, "hvu46DFU:G:b:n:p:d:k:K:t:a:m:" T("T:"),
               opts, 0, 0, 0);
     if (i < 0)
       break;
@@ -187,6 +191,12 @@ int main(int argc, char *argv[])
        usage(stdout);
        exit(0);
 
+      case '4':
+       aihint.ai_family = AF_INET;
+       break;
+      case '6':
+       aihint.ai_family = AF_INET6;
+       break;
       case 'D':
        f |= f_daemon;
        break;
index f447be50bc11a84f58a063557a6e3be4dabb9096..29403993af5a60839745238277ecbf4e9a3b1e76 100644 (file)
@@ -420,7 +420,8 @@ extern const bulkops bulktab[];
 /* --- The address-family table --- */
 
 #define ADDRFAM(_)                                                     \
-  _(INET,      want_ipv4)
+  _(INET,      want_ipv4)                                              \
+  _(INET6,     want_ipv6)
 
 enum {
 #define ENUM(af, qf) AFIX_##af,
@@ -445,6 +446,7 @@ extern const struct addrfam {
 typedef union addr {
   struct sockaddr sa;
   struct sockaddr_in sin;
+  struct sockaddr_in6 sin6;
 } addr;
 
 /* --- Mapping keyed on addresses --- */
index 032142c5716f845e773db8357ac23e0a38ce0c90..e9e9cb929bde385bea97be14f8188808a938d6f8 100644 (file)
@@ -46,11 +46,16 @@ fi
 peer=$1 ifname=$2 family=$3; shift 3
 
 ## Parse the address family.
+case "$family" in
+  INET) ipsz=20 ;;
+  INET6) ipsz=40 ;;
+  *) echo >&2 "$0: unknown address family $family"; exit 1 ;;
+esac
 case "$family,$#" in
-  INET,1) addr=$1 port=4070 ;;
-  INET,2) addr=$1 port=$2 ;;
-  INET,*) echo >&2 "$0: bad INET address"; exit 1 ;;
-  *)      echo >&2 "$0: unknown address family $family"; exit 1 ;;
+  INET,1 | INET6,1) addr=$1 port=4070 ;;
+  INET,2 | INET6,2) addr=$1 port=$2 ;;
+  INET,* | INET6,*) echo >&2 "$0: bad $family address"; exit 1 ;;
+  *) echo >&2 "$0: unknown address family $family"; exit 1 ;;
 esac
 
 ###--------------------------------------------------------------------------
@@ -137,7 +142,7 @@ case $haveaddr4,$haveaddr6 in
        mtu=$P_MTU;;
       *)
        pathmtu=$(pathmtu "$addr")
-       mtu=$(expr "$pathmtu" - 29 - $A_BULK_OVERHEAD)
+       mtu=$(( $pathmtu - $ipsz - 9 - $A_BULK_OVERHEAD ))
        ;;
     esac
     try ip link set dev "$ifname" up mtu "$mtu"