From: reyk Date: Thu, 18 Dec 2014 20:55:01 +0000 (+0000) Subject: Update relayd to use siphash instead of sys/hash. The source-hash, X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=acb89df41ab799db61a1eaeb2814b514fde65d32;p=openbsd Update relayd to use siphash instead of sys/hash. The source-hash, 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@ --- diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index f014e298058..48128698ed9 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -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 @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -52,6 +51,7 @@ #include #include #include +#include #include @@ -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 digest optdigest %type tablespec %type dir +%type 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); diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index 34c5be4372e..55b444ee581 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -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 @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -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)); diff --git a/usr.sbin/relayd/relay_http.c b/usr.sbin/relayd/relay_http.c index 96aca504ee4..82820ccf420 100644 --- a/usr.sbin/relayd/relay_http.c +++ b/usr.sbin/relayd/relay_http.c @@ -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 @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -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 */ diff --git a/usr.sbin/relayd/relay_udp.c b/usr.sbin/relayd/relay_udp.c index 62b32702e01..d447c712cbd 100644 --- a/usr.sbin/relayd/relay_udp.c +++ b/usr.sbin/relayd/relay_udp.c @@ -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 @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index c1e61f29760..eceeb2e3893 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -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 @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5 index e570fc80c32..6c8d23d54f2 100644 --- a/usr.sbin/relayd/relayd.conf.5 +++ b/usr.sbin/relayd/relayd.conf.5 @@ -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 .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard @@ -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 diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index 8389e135823..cd894508000 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -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 @@ -26,6 +26,7 @@ #include /* MAXHOSTNAMELEN */ #include #include +#include #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;