Add dynamic IPv6-to-IPv4 and IPv4-to-IPv6 translation inspired by
authorreyk <reyk@openbsd.org>
Tue, 22 Jul 2008 23:17:37 +0000 (23:17 +0000)
committerreyk <reyk@openbsd.org>
Tue, 22 Jul 2008 23:17:37 +0000 (23:17 +0000)
faithd(8) by doing a similar mapping of IPv4/6 addresses with
relayd(8) and pf(4) redirections without the need of the faith(4)
interface.  The trick works in both directions, it can accept IPv6
connections and relay them to IPv4 hosts by extracting the last 4
octets from the IPv6 destination (like faithd(8)), and it can accept
IPv4 connections and relay them to IPv6 hosts by prepending the 4
octets of the original IPv4 destination to a configured IPv6 prefix.
An access list is not needed because the classification is done in
pf.conf(5).  It helps to get more faith in relayd.

manpage bits ok jmc@
yes, sounds good todd@

usr.sbin/relayd/parse.y
usr.sbin/relayd/relay.c
usr.sbin/relayd/relayd.c
usr.sbin/relayd/relayd.conf.5
usr.sbin/relayd/relayd.h

index b8812ca..1fa08a7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.121 2008/07/19 11:38:54 reyk Exp $        */
+/*     $OpenBSD: parse.y,v 1.122 2008/07/22 23:17:37 reyk Exp $        */
 
 /*
  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -130,7 +130,7 @@ typedef struct {
 %token ON PATH PORT PREFORK PROTO QUERYSTR REAL REDIRECT RELAY REMOVE TRAP
 %token REQUEST RESPONSE RETRY RETURN ROUNDROBIN SACK SCRIPT SEND SESSION
 %token SOCKET SSL STICKYADDR STYLE TABLE TAG TCP TIMEOUT TO UPDATES URL
-%token VIRTUAL WITH ERROR ROUTE TRANSPARENT PARENT
+%token VIRTUAL WITH ERROR ROUTE TRANSPARENT PARENT INET INET6
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.string>      interface hostname table
@@ -1136,7 +1136,7 @@ relayoptsl        : LISTEN ON STRING port optssl {
                        }
                        tableport = h->port;
                }
-               | forwardmode TO forwardspec interface          {
+               | forwardmode TO forwardspec interface dstaf    {
                        rlay->rl_conf.fwdmode = $1;
                        switch ($1) {
                        case FWD_NORMAL:
@@ -1234,6 +1234,28 @@ dstmode          : /* empty */           { $$ = RELAY_DSTMODE_DEFAULT; }
                | HASH                  { $$ = RELAY_DSTMODE_HASH; }
                ;
 
+dstaf          : /* empty */           {
+                       rlay->rl_conf.dstaf.ss_family = AF_UNSPEC;
+               }
+               | INET                  {
+                       rlay->rl_conf.dstaf.ss_family = AF_INET;
+               }
+               | INET6 STRING          {
+                       struct sockaddr_in6     *sin6;
+
+                       sin6 = (struct sockaddr_in6 *)&rlay->rl_conf.dstaf;
+                       if (inet_pton(AF_INET6, $2, &sin6->sin6_addr) == -1) {
+                               yyerror("invalid ipv6 address %s", $2);
+                               free($2);
+                               YYERROR;
+                       }
+                       free($2);
+
+                       sin6->sin6_family = AF_INET6;
+                       sin6->sin6_len = sizeof(*sin6);
+               }
+               ;
+
 interface      : /*empty*/             { $$ = NULL; }
                | INTERFACE STRING      { $$ = $2; }
                ;
@@ -1373,6 +1395,8 @@ lookup(char *s)
                { "host",               HOST },
                { "icmp",               ICMP },
                { "include",            INCLUDE },
+               { "inet",               INET },
+               { "inet6",              INET6 },
                { "interface",          INTERFACE },
                { "interval",           INTERVAL },
                { "ip",                 IP },
index 21cbc69..f1dc6b0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: relay.c,v 1.94 2008/07/16 15:02:19 reyk Exp $ */
+/*     $OpenBSD: relay.c,v 1.95 2008/07/22 23:17:37 reyk Exp $ */
 
 /*
  * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -2186,7 +2186,7 @@ int
 relay_connect(struct session *con)
 {
        struct relay    *rlay = (struct relay *)con->se_relay;
-       int              bnds = -1;
+       int              bnds = -1, ret;
 
        if (gettimeofday(&con->se_tv_start, NULL))
                return (-1);
@@ -2208,6 +2208,22 @@ relay_connect(struct session *con)
                bnds = con->se_bnds;
        }
 
+       /* Do the IPv4-to-IPv6 or IPv6-to-IPv4 translation if requested */
+       if (rlay->rl_conf.dstaf.ss_family != AF_UNSPEC) {
+               if (con->se_out.ss.ss_family == AF_INET &&
+                   rlay->rl_conf.dstaf.ss_family == AF_INET6)
+                       ret = map4to6(&con->se_out.ss, &rlay->rl_conf.dstaf);
+               else if (con->se_out.ss.ss_family == AF_INET6 &&
+                   rlay->rl_conf.dstaf.ss_family == AF_INET)
+                       ret = map6to4(&con->se_out.ss);
+               else
+                       ret = 0;
+               if (ret != 0) {
+                       log_debug("relay_connect: mapped to invalid address");
+                       return (-1);
+               }
+       }
+
  retry:
        if ((con->se_out.s = relay_socket_connect(&con->se_out.ss,
            con->se_out.port, rlay->rl_proto, bnds)) == -1) {
index fad7f12..accff77 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: relayd.c,v 1.78 2008/07/09 14:06:44 reyk Exp $        */
+/*     $OpenBSD: relayd.c,v 1.79 2008/07/22 23:17:37 reyk Exp $        */
 
 /*
  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -1199,3 +1199,54 @@ bindany(struct ctl_bindany *bnd)
                close(s);
        return (-1);
 }
+
+int
+map6to4(struct sockaddr_storage *in6)
+{
+       struct sockaddr_storage  out4;
+       struct sockaddr_in      *sin4 = (struct sockaddr_in *)&out4;
+       struct sockaddr_in6     *sin6 = (struct sockaddr_in6 *)in6;
+
+       bzero(sin4, sizeof(*sin4));
+       sin4->sin_len = sizeof(*sin4);
+       sin4->sin_family = AF_INET;
+       sin4->sin_port = sin6->sin6_port;
+
+       bcopy(&sin6->sin6_addr.s6_addr[12], &sin4->sin_addr.s_addr,
+           sizeof(sin4->sin_addr));
+
+       if (sin4->sin_addr.s_addr == INADDR_ANY ||
+           sin4->sin_addr.s_addr == INADDR_BROADCAST ||
+           IN_MULTICAST(ntohl(sin4->sin_addr.s_addr)))
+               return (-1);
+
+       bcopy(&out4, in6, sizeof(*in6));
+
+       return (0);
+}
+
+int
+map4to6(struct sockaddr_storage *in4, struct sockaddr_storage *map)
+{
+       struct sockaddr_storage  out6;
+       struct sockaddr_in      *sin4 = (struct sockaddr_in *)in4;
+       struct sockaddr_in6     *sin6 = (struct sockaddr_in6 *)&out6;
+       struct sockaddr_in6     *map6 = (struct sockaddr_in6 *)map;
+
+       if (sin4->sin_addr.s_addr == INADDR_ANY ||
+           sin4->sin_addr.s_addr == INADDR_BROADCAST ||
+           IN_MULTICAST(ntohl(sin4->sin_addr.s_addr)))
+               return (-1);
+
+       bcopy(map6, sin6, sizeof(*sin6));
+       sin6->sin6_len = sizeof(*sin6);
+       sin6->sin6_family = AF_INET6;
+       sin6->sin6_port = sin4->sin_port;
+
+       bcopy(&sin4->sin_addr.s_addr, &sin6->sin6_addr.s6_addr[12],
+           sizeof(sin4->sin_addr));
+
+       bcopy(&out6, in4, sizeof(*in4));
+
+       return (0);
+}
index 8314273..19956ae 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: relayd.conf.5,v 1.90 2008/07/19 16:35:50 jmc Exp $
+.\"    $OpenBSD: relayd.conf.5,v 1.91 2008/07/22 23:17:37 reyk Exp $
 .\"
 .\" Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org>
 .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: July 19 2008 $
+.Dd $Mdocdate: July 22 2008 $
 .Dt RELAYD.CONF 5
 .Os
 .Sh NAME
@@ -489,7 +489,7 @@ Start the relay but immediately close any accepted connections.
 .Ic forward to
 .Ar address
 .Op Ic port Ar port
-.Op Ic retry Ar number
+.Ar options ...
 .Xc
 Specify the address and port of the target host to connect to.
 If the
@@ -497,16 +497,40 @@ If the
 option is not specified, the port from the
 .Ic listen on
 directive will be used.
-.Pp
 Use the
 .Ic transparent
 keyword to enable fully-transparent mode; the source address of the
 client will be retained in this case.
 .Pp
-The optional host retry option will be used as a tolerance for failed
+The following options may be specified for forward directives:
+.Pp
+.Bl -tag -width Ds
+.It Ic retry Ar number
+The optional host
+.Ic retry
+option will be used as a tolerance for failed
 host connections; the connection will be retried for
 .Ar number
 more times.
+.It Ic inet
+If the requested destination is an IPv6 address,
+.Xr relayd 8
+will forward the connection to an IPv4 address which is determined by
+the last 4 octets of the original IPv6 destination.
+For example, if the original IPv6 destination address is
+2001:db8:7395:ffff::a01:101, the session is relayed to the IPv4
+address 10.1.1.1 (a01:101).
+.It Ic inet6 Ar address-prefix
+If the requested destination is an IPv4 address,
+.Xr relayd 8
+will forward the connection to an IPv6 address which is determined by
+setting the last 4 octets of the specified IPv6
+.Ar address-prefix
+to the 4 octets of the original IPv4 destination.
+For example, if the original IPv4 destination address is 10.1.1.1 and
+the specified address prefix is 2001:db8:7395:ffff::, the session is
+relayed to the IPv6 address 2001:db8:7395:ffff::a01:101.
+.El
 .It Xo
 .Ic forward to
 .Aq Ar table
@@ -520,7 +544,7 @@ section above for information about table options.
 .It Xo
 .Ic forward to
 .Ic nat lookup
-.Op Ic retry Ar number
+.Ar options ...
 .Xc
 When redirecting connections with an
 .Ar rdr
index c6ebf8e..30143f1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: relayd.h,v 1.108 2008/07/19 11:38:54 reyk Exp $       */
+/*     $OpenBSD: relayd.h,v 1.109 2008/07/22 23:17:37 reyk Exp $       */
 
 /*
  * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -590,6 +590,7 @@ struct relay_config {
        objid_t                  dsttable;
        struct sockaddr_storage  ss;
        struct sockaddr_storage  dstss;
+       struct sockaddr_storage  dstaf;
        struct timeval           timeout;
        enum forwardmode         fwdmode;
 };
@@ -863,6 +864,8 @@ struct protonode *protonode_header(enum direction, struct protocol *,
                    struct protonode *);
 int             protonode_add(enum direction, struct protocol *,
                    struct protonode *);
+int             map6to4(struct sockaddr_storage *);
+int             map4to6(struct sockaddr_storage *, struct sockaddr_storage *);
 
 /* carp.c */
 int     carp_demote_init(char *, int);