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;