Update relayd to use siphash instead of sys/hash. The source-hash,
authorreyk <reyk@openbsd.org>
Thu, 18 Dec 2014 20:55:01 +0000 (20:55 +0000)
committerreyk <reyk@openbsd.org>
Thu, 18 Dec 2014 20:55:01 +0000 (20:55 +0000)
loadbalance and hash modes use a random key by default that can be
forced to be a static key with a new configuration argument.

With input from Max Fillinger.

ok tedu@

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

index f014e29..4812869 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.196 2014/12/12 10:05:09 reyk Exp $        */
+/*     $OpenBSD: parse.y,v 1.197 2014/12/18 20:55:01 reyk Exp $        */
 
 /*
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -30,7 +30,6 @@
 #include <sys/stat.h>
 #include <sys/queue.h>
 #include <sys/ioctl.h>
-#include <sys/hash.h>
 
 #include <net/if.h>
 #include <net/pfvar.h>
@@ -52,6 +51,7 @@
 #include <string.h>
 #include <ifaddrs.h>
 #include <syslog.h>
+#include <md5.h>
 
 #include <openssl/ssl.h>
 
@@ -118,6 +118,7 @@ static int           dstmode;
 static enum key_type    keytype = KEY_TYPE_NONE;
 static enum direction   dir = RELAY_DIR_ANY;
 static char            *rulefile = NULL;
+static union hashkey   *hashkey = NULL;
 
 struct address *host_v4(const char *);
 struct address *host_v6(const char *);
@@ -143,6 +144,10 @@ typedef struct {
                struct timeval           tv;
                struct table            *table;
                struct portrange         port;
+               struct {
+                       union hashkey    key;
+                       int              keyset;
+               }                        key;
                enum direction           dir;
                struct {
                        struct sockaddr_storage  ss;
@@ -185,6 +190,7 @@ typedef struct {
 %type  <v.digest>      digest optdigest
 %type  <v.table>       tablespec
 %type  <v.dir>         dir
+%type  <v.key>         hashkey
 
 %%
 
@@ -486,6 +492,11 @@ rdropts_l  : rdropts_l rdroptsl nl
                ;
 
 rdroptsl       : forwardmode TO tablespec interface    {
+                       if (hashkey != NULL) {
+                               free(hashkey);
+                               hashkey = NULL;
+                       }
+
                        switch ($1) {
                        case FWD_NORMAL:
                                if ($4 == NULL)
@@ -682,6 +693,7 @@ tablespec   : table                 {
                        free($1);
                        table = tb;
                        dstmode = RELAY_DSTMODE_DEFAULT;
+                       hashkey = NULL;
                } tableopts_l           {
                        struct table    *tb;
                        if (table->conf.port == 0)
@@ -737,12 +749,35 @@ tableopts : CHECK tablecheck
                        table->conf.skip_cnt =
                            ($2 / conf->sc_interval.tv_sec) - 1;
                }
-               | MODE dstmode          {
+               | MODE dstmode hashkey  {
                        switch ($2) {
                        case RELAY_DSTMODE_LOADBALANCE:
                        case RELAY_DSTMODE_HASH:
                        case RELAY_DSTMODE_SRCHASH:
+                               if (hashkey != NULL) {
+                                       yyerror("key already specified");
+                                       free(hashkey);
+                                       YYERROR;
+                               }
+                               if ((hashkey = calloc(1,
+                                   sizeof(*hashkey))) == NULL)
+                                       fatal("out of memory");
+                               memcpy(hashkey, &$3.key, sizeof(*hashkey));
+                               break;
+                       default:
+                               if ($3.keyset) {
+                                       yyerror("key not supported by mode");
+                                       YYERROR;
+                               }
+                               hashkey = NULL;
+                               break;
+                       }
+
+                       switch ($2) {
+                       case RELAY_DSTMODE_LOADBALANCE:
+                       case RELAY_DSTMODE_HASH:
                        case RELAY_DSTMODE_RANDOM:
+                       case RELAY_DSTMODE_SRCHASH:
                                if (rdr != NULL) {
                                        yyerror("mode not supported "
                                            "for redirections");
@@ -764,6 +799,50 @@ tableopts  : CHECK tablecheck
                }
                ;
 
+/* should be in sync with sbin/pfctl/parse.y's hashkey */
+hashkey                : /* empty */           {
+                       $$.keyset = 0;
+                       $$.key.data[0] = arc4random();
+                       $$.key.data[1] = arc4random();
+                       $$.key.data[2] = arc4random();
+                       $$.key.data[3] = arc4random();
+               }
+               | STRING                {
+                       /* manual key configuration */
+                       $$.keyset = 1;
+
+                       if (!strncmp($1, "0x", 2)) {
+                               if (strlen($1) != 34) {
+                                       free($1);
+                                       yyerror("hex key must be 128 bits "
+                                           "(32 hex digits) long");
+                                       YYERROR;
+                               }
+
+                               if (sscanf($1, "0x%8x%8x%8x%8x",
+                                   &$$.key.data[0], &$$.key.data[1],
+                                   &$$.key.data[2], &$$.key.data[3]) != 4) {
+                                       free($1);
+                                       yyerror("invalid hex key");
+                                       YYERROR;
+                               }
+                       } else {
+                               MD5_CTX context;
+
+                               MD5Init(&context);
+                               MD5Update(&context, (unsigned char *)$1,
+                                   strlen($1));
+                               MD5Final((unsigned char *)$$.key.data,
+                                   &context);
+                               HTONL($$.key.data[0]);
+                               HTONL($$.key.data[1]);
+                               HTONL($$.key.data[2]);
+                               HTONL($$.key.data[3]);
+                       }
+                       free($1);
+               }
+               ;
+
 tablecheck     : ICMP                  { table->conf.check = CHECK_ICMP; }
                | TCP                   { table->conf.check = CHECK_TCP; }
                | ssltls                {
@@ -1722,6 +1801,15 @@ forwardspec      : STRING port retry     {
                        if (!TAILQ_EMPTY(&rlay->rl_tables))
                                rlt->rlt_flags |= F_BACKUP;
 
+                       if (hashkey != NULL &&
+                           (rlay->rl_conf.flags & F_HASHKEY) == 0) {
+                               memcpy(&rlay->rl_conf.hashkey,
+                                   hashkey, sizeof(rlay->rl_conf.hashkey));
+                               rlay->rl_conf.flags |= F_HASHKEY;
+                       }
+                       free(hashkey);
+                       hashkey = NULL;
+
                        TAILQ_INSERT_TAIL(&rlay->rl_tables, rlt, rlt_entry);
                }
                ;
@@ -1834,6 +1922,9 @@ routeoptsl        : ROUTE address '/' NUMBER {
                        TAILQ_INSERT_TAIL(conf->sc_routes, nr, nr_route);
                }
                | FORWARD TO tablespec {
+                       free(hashkey);
+                       hashkey = NULL;
+
                        if (router->rt_gwtable) {
                                yyerror("router %s table already specified",
                                    router->rt_conf.name);
index 34c5be4..55b444e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: relay.c,v 1.182 2014/12/12 10:05:09 reyk Exp $        */
+/*     $OpenBSD: relay.c,v 1.183 2014/12/18 20:55:01 reyk Exp $        */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -23,7 +23,6 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/tree.h>
-#include <sys/hash.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
@@ -72,7 +71,7 @@ int            relay_socket_connect(struct sockaddr_storage *, in_port_t,
 void            relay_accept(int, short, void *);
 void            relay_input(struct rsession *);
 
-u_int32_t       relay_hash_addr(struct sockaddr_storage *, u_int32_t);
+void            relay_hash_addr(SIPHASH_CTX *, struct sockaddr_storage *, int);
 
 DH *            relay_tls_get_dhparams(int);
 void            relay_tls_callback_info(const SSL *, int, int);
@@ -438,21 +437,7 @@ relay_launch(void)
                         */
                        rule_settable(&rlay->rl_proto->rules, rlt);
 
-                       switch (rlt->rlt_mode) {
-                       case RELAY_DSTMODE_ROUNDROBIN:
-                       case RELAY_DSTMODE_RANDOM:
-                               rlt->rlt_key = 0;
-                               break;
-                       case RELAY_DSTMODE_LOADBALANCE:
-                       case RELAY_DSTMODE_HASH:
-                       case RELAY_DSTMODE_SRCHASH:
-                               rlt->rlt_key =
-                                   hash32_str(rlay->rl_conf.name, HASHINIT);
-                               rlt->rlt_key =
-                                   hash32_str(rlt->rlt_table->conf.name,
-                                   rlt->rlt_key);
-                               break;
-                       }
+                       rlt->rlt_index = 0;
                        rlt->rlt_nhosts = 0;
                        TAILQ_FOREACH(host, &rlt->rlt_table->hosts, entry) {
                                if (rlt->rlt_nhosts >= RELAY_MAXHOSTS)
@@ -1091,6 +1076,11 @@ relay_accept(int fd, short event, void *arg)
        getmonotime(&con->se_tv_start);
        bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last));
 
+       if (rlay->rl_conf.flags & F_HASHKEY) {
+               SipHash24_Init(&con->se_siphashctx,
+                   &rlay->rl_conf.hashkey.siphashkey);
+       }
+
        relay_sessions++;
        SPLAY_INSERT(session_tree, &rlay->rl_sessions, con);
        relay_session_publish(con);
@@ -1180,23 +1170,27 @@ relay_accept(int fd, short event, void *arg)
        }
 }
 
-u_int32_t
-relay_hash_addr(struct sockaddr_storage *ss, u_int32_t p)
+void
+relay_hash_addr(SIPHASH_CTX *ctx, struct sockaddr_storage *ss, int portset)
 {
        struct sockaddr_in      *sin4;
        struct sockaddr_in6     *sin6;
+       in_port_t                port;
 
        if (ss->ss_family == AF_INET) {
                sin4 = (struct sockaddr_in *)ss;
-               p = hash32_buf(&sin4->sin_addr,
-                   sizeof(struct in_addr), p);
+               SipHash24_Update(ctx, &sin4->sin_addr,
+                   sizeof(struct in_addr));
        } else {
                sin6 = (struct sockaddr_in6 *)ss;
-               p = hash32_buf(&sin6->sin6_addr,
-                   sizeof(struct in6_addr), p);
+               SipHash24_Update(ctx, &sin6->sin6_addr,
+                   sizeof(struct in6_addr));
        }
 
-       return (p);
+       if (portset != -1) {
+               port = (in_port_t)portset;
+               SipHash24_Update(ctx, &port, sizeof(port));
+       }
 }
 
 int
@@ -1206,8 +1200,8 @@ relay_from_table(struct rsession *con)
        struct host             *host;
        struct relay_table      *rlt = NULL;
        struct table            *table = NULL;
-       u_int32_t                p = con->se_hashkey;
        int                      idx = -1;
+       u_int64_t                p = 0;
 
        /* the table is already selected */
        if (con->se_table != NULL) {
@@ -1234,39 +1228,43 @@ relay_from_table(struct rsession *con)
                    __func__, con->se_id);
                return (-1);
        }
-       if (!con->se_hashkeyset) {
-               p = con->se_hashkey = rlt->rlt_key;
-               con->se_hashkeyset = 1;
-       }
 
        switch (rlt->rlt_mode) {
        case RELAY_DSTMODE_ROUNDROBIN:
-               if ((int)rlt->rlt_key >= rlt->rlt_nhosts)
-                       rlt->rlt_key = 0;
-               idx = (int)rlt->rlt_key;
+               if ((int)rlt->rlt_index >= rlt->rlt_nhosts)
+                       rlt->rlt_index = 0;
+               idx = (int)rlt->rlt_index;
                break;
        case RELAY_DSTMODE_RANDOM:
                idx = (int)arc4random_uniform(rlt->rlt_nhosts);
                break;
        case RELAY_DSTMODE_SRCHASH:
+               /* Source IP address without port */
+               relay_hash_addr(&con->se_siphashctx, &con->se_in.ss, -1);
+               break;
        case RELAY_DSTMODE_LOADBALANCE:
                /* Source IP address without port */
-               p = relay_hash_addr(&con->se_in.ss, p);
-               if (rlt->rlt_mode == RELAY_DSTMODE_SRCHASH)
-                       break;
+               relay_hash_addr(&con->se_siphashctx, &con->se_in.ss, -1);
                /* FALLTHROUGH */
        case RELAY_DSTMODE_HASH:
                /* Local "destination" IP address and port */
-               p = relay_hash_addr(&rlay->rl_conf.ss, p);
-               p = hash32_buf(&rlay->rl_conf.port,
-                   sizeof(rlay->rl_conf.port), p);
+               relay_hash_addr(&con->se_siphashctx, &rlay->rl_conf.ss,
+                   rlay->rl_conf.port);
                break;
        default:
                fatalx("relay_from_table: unsupported mode");
                /* NOTREACHED */
        }
-       if (idx == -1 && (idx = p % rlt->rlt_nhosts) >= RELAY_MAXHOSTS)
-               return (-1);
+       if (idx == -1) {
+               p = SipHash24_End(&con->se_siphashctx);
+
+               /* Reset hash context */
+               SipHash24_Init(&con->se_siphashctx,
+                   &rlay->rl_conf.hashkey.siphashkey);
+
+               if ((idx = p % rlt->rlt_nhosts) >= RELAY_MAXHOSTS)
+                       return (-1);
+       }
        host = rlt->rlt_host[idx];
        DPRINTF("%s: session %d: table %s host %s, p 0x%08x, idx %d",
            __func__, con->se_id, table->conf.name, host->conf.name, p, idx);
@@ -1289,7 +1287,7 @@ relay_from_table(struct rsession *con)
 
  found:
        if (rlt->rlt_mode == RELAY_DSTMODE_ROUNDROBIN)
-               rlt->rlt_key = host->idx + 1;
+               rlt->rlt_index = host->idx + 1;
        con->se_retry = host->conf.retry;
        con->se_out.port = table->conf.port;
        bcopy(&host->conf.ss, &con->se_out.ss, sizeof(con->se_out.ss));
index 96aca50..82820cc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: relay_http.c,v 1.35 2014/10/25 03:23:49 lteo Exp $    */
+/*     $OpenBSD: relay_http.c,v 1.36 2014/12/18 20:55:01 reyk Exp $    */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -23,7 +23,6 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/tree.h>
-#include <sys/hash.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
@@ -1487,12 +1486,8 @@ relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions)
                                value = match->kv_value;
                                break;
                        }
-                       if (!con->se_hashkeyset)
-                               con->se_hashkey = HASHINIT;
-                       con->se_hashkey = hash32_str(value, con->se_hashkey);
-                       con->se_hashkeyset = 1;
-                       log_debug("%s: hashkey 0x%04x", __func__,
-                           con->se_hashkey);
+                       SipHash24_Update(&con->se_siphashctx,
+                           value, strlen(value));
                        break;
                case KEY_OPTION_LOG:
                        /* perform this later */
index 62b3270..d447c71 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: relay_udp.c,v 1.35 2014/12/12 10:05:09 reyk Exp $     */
+/*     $OpenBSD: relay_udp.c,v 1.36 2014/12/18 20:55:01 reyk Exp $     */
 
 /*
  * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org>
@@ -23,7 +23,6 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/tree.h>
-#include <sys/hash.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
index c1e61f2..eceeb2e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: relayd.c,v 1.134 2014/12/12 10:05:09 reyk Exp $       */
+/*     $OpenBSD: relayd.c,v 1.135 2014/12/18 20:55:01 reyk Exp $       */
 
 /*
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -22,7 +22,6 @@
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include <sys/resource.h>
-#include <sys/hash.h>
 
 #include <net/if.h>
 #include <netinet/in.h>
index e570fc8..6c8d23d 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: relayd.conf.5,v 1.153 2014/12/12 10:05:09 reyk Exp $
+.\"    $OpenBSD: relayd.conf.5,v 1.154 2014/12/18 20:55:01 reyk Exp $
 .\"
 .\" Copyright (c) 2006 - 2014 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: December 12 2014 $
+.Dd $Mdocdate: December 18 2014 $
 .Dt RELAYD.CONF 5
 .Os
 .Sh NAME
@@ -390,10 +390,10 @@ This will override the global timeout, which is 200 milliseconds by default.
 The following options will set the scheduling algorithm to select a
 host from the specified table:
 .Bl -tag -width Ds
-.It Ic mode hash
+.It Ic mode hash Op Ar key
 Balances the outgoing connections across the active hosts based on the
-hashed name of the relay, the hashed name of the table, and the IP
-address and port of the relay.
+.Ar key ,
+IP address and port of the relay.
 Additional input can be fed into the
 hash by looking at HTTP headers and GET variables;
 see the
@@ -406,10 +406,10 @@ active
 .Xr pf 4
 states.
 This mode is only supported by redirections.
-.It Ic mode loadbalance
+.It Ic mode loadbalance Op Ar key
 Balances the outgoing connections across the active hosts based on the
-hashed name of the relay, the hashed name of the table, the source IP
-address of the client, and the IP address and port of the relay.
+.Ar key ,
+the source IP address of the client, and the IP address and port of the relay.
 This mode is only supported by relays.
 .It Ic mode random
 Distributes the outgoing connections randomly through all active hosts.
@@ -419,12 +419,26 @@ Distributes the outgoing connections using a round-robin scheduler
 through all active hosts.
 This is the default mode and will be used if no option has been specified.
 This mode is supported by redirections and relays.
-.It Ic mode source-hash
+.It Ic mode source-hash Op Ar key
 Balances the outgoing connections across the active hosts based on the
-hashed name of the redirection or relay, the hashed name of the table,
+.Ar key
 and the source IP address of the client.
 This mode is only supported by relays.
 .El
+.Pp
+The optional
+.Ar key
+argument can be specified for the
+.Ic hash ,
+.Ic loadbalance ,
+and
+.Ic source-hash
+modes as either an hex value with a leading
+.Ar 0x
+or as a string.
+If omitted,
+.Xr relayd 8
+generates a random key when the configuration is loaded.
 .Sh REDIRECTIONS
 Redirections represent a
 .Xr pf 4
index 8389e13..cd89450 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: relayd.h,v 1.199 2014/12/17 13:54:27 reyk Exp $       */
+/*     $OpenBSD: relayd.h,v 1.200 2014/12/18 20:55:01 reyk Exp $       */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -26,6 +26,7 @@
 #include <sys/param.h>         /* MAXHOSTNAMELEN */
 #include <limits.h>
 #include <imsg.h>
+#include <siphash.h>
 
 #ifndef nitems
 #define        nitems(_a)      (sizeof((_a)) / sizeof((_a)[0]))
@@ -332,6 +333,12 @@ struct address {
 };
 TAILQ_HEAD(addresslist, address);
 
+union hashkey {
+       /* Simplified version of pf_poolhashkey */
+       u_int32_t                data[4];
+       SIPHASH_KEY              siphashkey;
+};
+
 #define F_DISABLE              0x00000001
 #define F_BACKUP               0x00000002
 #define F_USED                 0x00000004
@@ -359,13 +366,14 @@ TAILQ_HEAD(addresslist, address);
 #define F_DIVERT               0x01000000
 #define F_SCRIPT               0x02000000
 #define F_TLSINSPECT           0x04000000
+#define F_HASHKEY              0x08000000
 
 #define F_BITS                                                         \
        "\10\01DISABLE\02BACKUP\03USED\04DOWN\05ADD\06DEL\07CHANGED"    \
        "\10STICKY-ADDRESS\11CHECK_DONE\12ACTIVE_RULESET\13CHECK_SENT"  \
        "\14TLS\15NAT_LOOKUP\16DEMOTE\17LOOKUP_PATH\20DEMOTED\21UDP"    \
        "\22RETURN\23TRAP\24NEEDPF\25PORT\26TLS_CLIENT\27NEEDRT"        \
-       "\30MATCH\31DIVERT\32SCRIPT\33TLS_INSPECT"
+       "\30MATCH\31DIVERT\32SCRIPT\33TLS_INSPECT\34HASHKEY"
 
 enum forwardmode {
        FWD_NORMAL              = 0,
@@ -495,6 +503,7 @@ struct rdr_config {
        objid_t                  table_id;
        objid_t                  backup_id;
        int                      mode;
+       union hashkey            key;
        char                     name[SRV_NAME_SIZE];
        char                     tag[RD_TAG_NAME_SIZE];
        struct timeval           timeout;
@@ -517,8 +526,7 @@ struct rsession {
        struct ctl_relay_event           se_in;
        struct ctl_relay_event           se_out;
        void                            *se_priv;
-       u_int32_t                        se_hashkey;
-       int                              se_hashkeyset;
+       SIPHASH_CTX                      se_siphashctx;
        struct relay_table              *se_table;
        struct event                     se_ev;
        struct timeval                   se_timeout;
@@ -701,7 +709,7 @@ struct relay_table {
        struct table            *rlt_table;
        u_int32_t                rlt_flags;
        int                      rlt_mode;
-       u_int32_t                rlt_key;
+       u_int32_t                rlt_index;
        struct host             *rlt_host[RELAY_MAXHOSTS];
        int                      rlt_nhosts;
        TAILQ_ENTRY(relay_table) rlt_entry;
@@ -728,6 +736,7 @@ struct relay_config {
        struct sockaddr_storage  dstaf;
        struct timeval           timeout;
        enum forwardmode         fwdmode;
+       union hashkey            hashkey;
        off_t                    tls_cert_len;
        off_t                    tls_key_len;
        objid_t                  tls_keyid;