On Sat, Dec 13, 2014 at 04:08:11PM +0100, Max Fillinger wrote:
> On Fri, Dec 12, 2014 at 12:57:08PM -0500, Ted Unangst wrote:
> > On Wed, Dec 10, 2014 at 21:51, Max Fillinger wrote:
> > > Here's a version without malloc'ing the key.
> > 
> > Looks like it still does?
> 
> Yes, I managed to just repost the previous diff...at least the thing is
> in more competent hands now.
> 

The attached diff adopts siphash and changes the semantics a bit - the
key generation is now similar to pf's source-hash key.  there is no
need to feed in the table names anymore: the user can specify either a
static key for consistency or use the default random key.

I also changed rlt_rr_key to rlt_index to be a bit more generic.  The
parse.y bits will also work with redirections later.

Reyk

Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/parse.y,v
retrieving revision 1.196
diff -u -p -u -p -r1.196 parse.y
--- parse.y     12 Dec 2014 10:05:09 -0000      1.196
+++ parse.y     17 Dec 2014 14:47:00 -0000
@@ -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: relay.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.182
diff -u -p -u -p -r1.182 relay.c
--- relay.c     12 Dec 2014 10:05:09 -0000      1.182
+++ relay.c     17 Dec 2014 14:47:01 -0000
@@ -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 sockad
 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 *
        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 *
        }
 }
 
-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,42 @@ 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);
+               p = SipHash24_End(&con->se_siphashctx);
+               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);
+               p = SipHash24_End(&con->se_siphashctx);
                break;
        default:
                fatalx("relay_from_table: unsupported mode");
                /* NOTREACHED */
        }
-       if (idx == -1 && (idx = p % rlt->rlt_nhosts) >= RELAY_MAXHOSTS)
-               return (-1);
+       if (idx == -1) {
+               /* 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 +1286,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: relay_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v
retrieving revision 1.35
diff -u -p -u -p -r1.35 relay_http.c
--- relay_http.c        25 Oct 2014 03:23:49 -0000      1.35
+++ relay_http.c        17 Dec 2014 14:47:01 -0000
@@ -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_eve
                                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: relay_udp.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay_udp.c,v
retrieving revision 1.35
diff -u -p -u -p -r1.35 relay_udp.c
--- relay_udp.c 12 Dec 2014 10:05:09 -0000      1.35
+++ relay_udp.c 17 Dec 2014 14:47:02 -0000
@@ -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: relayd.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v
retrieving revision 1.134
diff -u -p -u -p -r1.134 relayd.c
--- relayd.c    12 Dec 2014 10:05:09 -0000      1.134
+++ relayd.c    17 Dec 2014 14:47:02 -0000
@@ -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: relayd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.conf.5,v
retrieving revision 1.153
diff -u -p -u -p -r1.153 relayd.conf.5
--- relayd.conf.5       12 Dec 2014 10:05:09 -0000      1.153
+++ relayd.conf.5       17 Dec 2014 14:47:02 -0000
@@ -390,10 +390,10 @@ This will override the global timeout, w
 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 usi
 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: relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.199
diff -u -p -u -p -r1.199 relayd.h
--- relayd.h    17 Dec 2014 13:54:27 -0000      1.199
+++ relayd.h    17 Dec 2014 14:47:02 -0000
@@ -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;

Reply via email to