diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2008-06-11 18:21:21 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2008-06-11 18:21:21 +0000 |
commit | ae29edd6b675a26c741a63b65725a7225af083bb (patch) | |
tree | c0e7a3d34d64cecff06dc7a1e8910c1bcf97a346 /usr.sbin/relayd | |
parent | d4aa7b0d42217bd71d39e0188cb451fcd0c55f72 (diff) |
add support for "transparent" forwarding in relays: normally the l7
relay will connect to the target host with its own ip address, but
this mode will let it use the address of the client that is connecting
from the other side. for example, there is no need to add the
X-Forwarded-For HTTP headers for internal webservers in this mode
anymore since they magically see the remote client ip address in the
connection. it also allows to build fully-transparent ssl
encapsulation for tcp sessions and many other things...
based on an initial idea from dlg@ and pascoe@ (dlg's talk at opencon)
using the new BINDANY and divert-reply interfaces from markus@ (since n2k8)
ok markus@ pyr@
Diffstat (limited to 'usr.sbin/relayd')
-rw-r--r-- | usr.sbin/relayd/parse.y | 57 | ||||
-rw-r--r-- | usr.sbin/relayd/pfe_filter.c | 15 | ||||
-rw-r--r-- | usr.sbin/relayd/relay.c | 117 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.c | 54 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.conf.5 | 8 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 25 |
6 files changed, 234 insertions, 42 deletions
diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index 2b6e879364b..71723419367 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.114 2008/05/08 02:15:34 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.115 2008/06/11 18:21:19 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 +%token VIRTUAL WITH ERROR ROUTE TRANSPARENT %token <v.string> STRING %token <v.number> NUMBER %type <v.string> interface hostname table @@ -380,17 +380,25 @@ rdropts_l : rdropts_l rdroptsl nl ; rdroptsl : forwardmode TO tablespec interface { + switch ($1) { + case FWD_NORMAL: + if ($4 == NULL) + break; + yyerror("superfluous interface"); + YYERROR; + case FWD_ROUTE: + if ($4 != NULL) + break; + yyerror("missing interface to route to"); + YYERROR; + case FWD_TRANS: + yyerror("no transparent forward here"); + YYERROR; + } if ($4 != NULL) { strlcpy($3->conf.ifname, $4, sizeof($3->conf.ifname)); free($4); - if (($1 & F_ROUTE) == 0) { - yyerror("superfluous interface"); - YYERROR; - } - } else if ($1 & F_ROUTE) { - yyerror("missing interface to route to"); - YYERROR; } if ($3->conf.check == CHECK_NOCHECK) { @@ -410,6 +418,7 @@ rdroptsl : forwardmode TO tablespec interface { rdr->table = $3; rdr->conf.table_id = $3->conf.id; } + $3->conf.fwdmode = $1; $3->conf.rdrid = rdr->conf.id; $3->conf.flags |= F_USED | $1; } @@ -449,8 +458,9 @@ rdroptsl : forwardmode TO tablespec interface { | include ; -forwardmode : FORWARD { $$ = 0; } - | ROUTE { $$ = F_ROUTE; } +forwardmode : FORWARD { $$ = FWD_NORMAL; } + | ROUTE { $$ = FWD_ROUTE; } + | TRANSPARENT FORWARD { $$ = FWD_TRANS; } ; table : '<' STRING '>' { @@ -1130,7 +1140,29 @@ relayoptsl : LISTEN ON STRING port optssl { } tableport = h->port; } - | FORWARD TO forwardspec + | forwardmode TO forwardspec interface { + rlay->rl_conf.fwdmode = $1; + switch ($1) { + case FWD_NORMAL: + if ($4 == NULL) + break; + yyerror("superfluous interface"); + YYERROR; + case FWD_ROUTE: + yyerror("no route for redirections"); + YYERROR; + case FWD_TRANS: + if ($4 != NULL) + break; + yyerror("missing interface"); + YYERROR; + } + if ($4 != NULL) { + strlcpy(rlay->rl_conf.ifname, $4, + sizeof(rlay->rl_conf.ifname)); + free($4); + } + } | SESSION TIMEOUT NUMBER { if ((rlay->rl_conf.timeout.tv_sec = $3) < 0) { yyerror("invalid timeout: %d", $3); @@ -1383,6 +1415,7 @@ lookup(char *s) { "tcp", TCP }, { "timeout", TIMEOUT }, { "to", TO }, + { "transparent", TRANSPARENT }, { "trap", TRAP }, { "updates", UPDATES }, { "url", URL }, diff --git a/usr.sbin/relayd/pfe_filter.c b/usr.sbin/relayd/pfe_filter.c index f9fb944834c..da754cb435f 100644 --- a/usr.sbin/relayd/pfe_filter.c +++ b/usr.sbin/relayd/pfe_filter.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfe_filter.c,v 1.29 2008/06/10 23:12:36 reyk Exp $ */ +/* $OpenBSD: pfe_filter.c,v 1.30 2008/06/11 18:21:19 reyk Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -351,11 +351,13 @@ sync_ruleset(struct relayd *env, struct rdr *rdr, int enable) memset(&pio, 0, sizeof(pio)); (void)strlcpy(rio.anchor, anchor, sizeof(rio.anchor)); - if ((t->conf.flags & F_ROUTE) == 0) { + switch (t->conf.fwdmode) { + case FWD_NORMAL: /* traditional redirection in the rdr-anchor */ rs = PF_RULESET_RDR; rio.rule.action = PF_RDR; - } else { + break; + case FWD_ROUTE: /* re-route with pf for DSR (direct server return) */ rs = PF_RULESET_FILTER; rio.rule.action = PF_PASS; @@ -366,6 +368,10 @@ sync_ruleset(struct relayd *env, struct rdr *rdr, int enable) /* Use sloppy state handling for half connections */ rio.rule.keep_state = PF_STATE_NORMAL; rio.rule.rule_flag = PFRULE_STATESLOPPY; + break; + default: + fatalx("sync_ruleset: invalid forward mode"); + /* NOTREACHED */ } rio.rule.timeout[PFTM_TCP_ESTABLISHED] = @@ -425,7 +431,8 @@ sync_ruleset(struct relayd *env, struct rdr *rdr, int enable) if (ioctl(env->sc_pf->dev, DIOCADDRULE, &rio) == -1) fatal("cannot add rule"); log_debug("sync_ruleset: rule added to %sanchor \"%s\"", - rdr->table->conf.flags & F_ROUTE ? "" : "rdr-", anchor); + rdr->table->conf.fwdmode == FWD_ROUTE ? + "" : "rdr-", anchor); } if (transaction_commit(env) == -1) log_warn("sync_ruleset: add rules transaction failed"); diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index a51ed7ab813..325c4195bd2 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.89 2008/05/08 02:27:58 reyk Exp $ */ +/* $OpenBSD: relay.c,v 1.90 2008/06/11 18:21:19 reyk Exp $ */ /* * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org> @@ -60,17 +60,19 @@ void relay_protodebug(struct relay *); void relay_init(void); void relay_launch(void); int relay_socket(struct sockaddr_storage *, in_port_t, - struct protocol *); + struct protocol *, int); int relay_socket_listen(struct sockaddr_storage *, in_port_t, struct protocol *); int relay_socket_connect(struct sockaddr_storage *, in_port_t, - struct protocol *); + struct protocol *, int); void relay_accept(int, short, void *); void relay_input(struct session *); int relay_connect(struct session *); void relay_connected(int, short, void *); +void relay_bindanyreq(struct session *, in_port_t, int); +void relay_bindany(int, short, void *); u_int32_t relay_hash_addr(struct sockaddr_storage *, u_int32_t); @@ -601,7 +603,7 @@ relay_socket_af(struct sockaddr_storage *ss, in_port_t port) int relay_socket(struct sockaddr_storage *ss, in_port_t port, - struct protocol *proto) + struct protocol *proto, int fd) { int s = -1, val; struct linger lng; @@ -609,7 +611,8 @@ relay_socket(struct sockaddr_storage *ss, in_port_t port, if (relay_socket_af(ss, port) == -1) goto bad; - if ((s = socket(ss->ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) + s = fd == -1 ? socket(ss->ss_family, SOCK_STREAM, IPPROTO_TCP) : fd; + if (s == -1) goto bad; /* @@ -682,11 +685,11 @@ relay_socket(struct sockaddr_storage *ss, in_port_t port, int relay_socket_connect(struct sockaddr_storage *ss, in_port_t port, - struct protocol *proto) + struct protocol *proto, int fd) { - int s; + int s; - if ((s = relay_socket(ss, port, proto)) == -1) + if ((s = relay_socket(ss, port, proto, fd)) == -1) return (-1); if (connect(s, (struct sockaddr *)ss, ss->ss_len) == -1) { @@ -707,7 +710,7 @@ relay_socket_listen(struct sockaddr_storage *ss, in_port_t port, { int s; - if ((s = relay_socket(ss, port, proto)) == -1) + if ((s = relay_socket(ss, port, proto, -1)) == -1) return (-1); if (bind(s, (struct sockaddr *)ss, ss->ss_len) == -1) @@ -1504,9 +1507,13 @@ relay_read_http(struct bufferevent *bev, void *arg) cre->chunked = 0; if (cre->dir == RELAY_DIR_REQUEST && - proto->lateconnect && cre->dst->bev == NULL && - relay_connect(con) == -1) { - relay_close_http(con, 502, "session failed", 0); + proto->lateconnect && cre->dst->bev == NULL) { + if (rlay->rl_conf.fwdmode == FWD_TRANS) { + relay_bindanyreq(con, 0, IPPROTO_TCP); + return; + } + if (relay_connect(con) == -1) + relay_close_http(con, 502, "session failed", 0); return; } } @@ -1908,6 +1915,7 @@ relay_accept(int fd, short sig, void *arg) con->se_in.dir = RELAY_DIR_REQUEST; con->se_out.dir = RELAY_DIR_RESPONSE; con->se_retry = rlay->rl_conf.dstretry; + con->se_bnds = -1; if (gettimeofday(&con->se_tv_start, NULL)) goto err; bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last)); @@ -2109,18 +2117,57 @@ relay_session(struct session *con) return; } - if (!rlay->rl_proto->lateconnect && relay_connect(con) == -1) { - relay_close(con, "session failed"); - return; + if (!rlay->rl_proto->lateconnect) { + if (rlay->rl_conf.fwdmode == FWD_TRANS) + relay_bindanyreq(con, 0, IPPROTO_TCP); + else if (relay_connect(con) == -1) { + relay_close(con, "session failed"); + return; + } } relay_input(con); } +void +relay_bindanyreq(struct session *con, in_port_t port, int proto) +{ + struct relay *rlay = (struct relay *)con->se_relay; + struct ctl_bindany bnd; + struct timeval tv; + + bzero(&bnd, sizeof(bnd)); + bnd.bnd_id = con->se_id; + bnd.bnd_proc = proc_id; + bnd.bnd_port = port; + bnd.bnd_proto = proto; + bcopy(&con->se_in.ss, &bnd.bnd_ss, sizeof(bnd.bnd_ss)); + imsg_compose(ibuf_main, IMSG_BINDANY, 0, 0, -1, &bnd, sizeof(bnd)); + + /* Schedule timeout */ + evtimer_set(&con->se_ev, relay_bindany, con); + bcopy(&rlay->rl_conf.timeout, &tv, sizeof(tv)); + evtimer_add(&con->se_ev, &tv); +} + +void +relay_bindany(int fd, short event, void *arg) +{ + struct session *con = (struct session *)arg; + + if (con->se_bnds == -1) { + relay_close(con, "bindany failed, invalid socket"); + return; + } + + relay_connect((struct session *)con); +} + int relay_connect(struct session *con) { struct relay *rlay = (struct relay *)con->se_relay; + int bnds = -1; if (gettimeofday(&con->se_tv_start, NULL)) return (-1); @@ -2129,13 +2176,22 @@ relay_connect(struct session *con) if (relay_from_table(con) != 0) return (-1); } else if (con->se_out.ss.ss_family == AF_UNSPEC) { - bcopy(&rlay->rl_conf.dstss, &con->se_out.ss, sizeof(con->se_out.ss)); + bcopy(&rlay->rl_conf.dstss, &con->se_out.ss, + sizeof(con->se_out.ss)); con->se_out.port = rlay->rl_conf.dstport; } + if (rlay->rl_conf.fwdmode == FWD_TRANS) { + if (con->se_bnds == -1) { + log_debug("relay_connect: could not bind any sock"); + return (-1); + } + bnds = con->se_bnds; + } + retry: - if ((con->se_out.s = relay_socket_connect(&con->se_out.ss, con->se_out.port, - rlay->rl_proto)) == -1) { + if ((con->se_out.s = relay_socket_connect(&con->se_out.ss, + con->se_out.port, rlay->rl_proto, bnds)) == -1) { if (con->se_retry) { con->se_retry--; log_debug("relay_connect: session %d: " @@ -2367,9 +2423,12 @@ relay_dispatch_pfe(int fd, short event, void *ptr) void relay_dispatch_parent(int fd, short event, void * ptr) { - struct imsgbuf *ibuf; - struct imsg imsg; - ssize_t n; + struct session *con; + struct imsgbuf *ibuf; + struct imsg imsg; + ssize_t n; + struct timeval tv; + objid_t id; ibuf = ptr; switch (event) { @@ -2399,6 +2458,22 @@ relay_dispatch_parent(int fd, short event, void * ptr) break; switch (imsg.hdr.type) { + case IMSG_BINDANY: + bcopy(imsg.data, &id, sizeof(id)); + if ((con = session_find(env, id)) == NULL) { + log_debug("relay_dispatch_parent: " + "session expired"); + break; + } + + /* Will validate the result later */ + con->se_bnds = imsg_get_fd(ibuf); + + evtimer_del(&con->se_ev); + evtimer_set(&con->se_ev, relay_bindany, con); + bzero(&tv, sizeof(tv)); + evtimer_add(&con->se_ev, &tv); + break; default: log_debug("relay_dispatch_parent: unexpected imsg %d", imsg.hdr.type); diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index e047b5854cb..f1588bd5fc3 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.76 2008/05/17 23:31:52 sobrado Exp $ */ +/* $OpenBSD: relayd.c,v 1.77 2008/06/11 18:21:20 reyk Exp $ */ /* * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org> @@ -56,6 +56,7 @@ int send_all(struct relayd *, enum imsg_type, void *, u_int16_t); void reconfigure(void); void purge_tree(struct proto_tree *); +int bindany(struct ctl_bindany *); int pipe_parent2pfe[2]; int pipe_parent2hce[2]; @@ -704,9 +705,12 @@ main_dispatch_hce(int fd, short event, void * ptr) void main_dispatch_relay(int fd, short event, void * ptr) { + struct relayd *env = relayd_env; struct imsgbuf *ibuf; struct imsg imsg; ssize_t n; + struct ctl_bindany bnd; + int s; ibuf = ptr; switch (event) { @@ -736,6 +740,26 @@ main_dispatch_relay(int fd, short event, void * ptr) break; switch (imsg.hdr.type) { + case IMSG_BINDANY: + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(bnd)) + fatalx("invalid imsg header len"); + bcopy(imsg.data, &bnd, sizeof(bnd)); + if (bnd.bnd_proc > env->sc_prefork_relay) + fatalx("pfe_dispatch_relay: " + "invalid relay proc"); + switch (bnd.bnd_proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + break; + default: + fatalx("pfe_dispatch_relay: requested socket " + "for invalid protocol"); + /* NOTREACHED */ + } + s = bindany(&bnd); + imsg_compose(&ibuf_relay[bnd.bnd_proc], IMSG_BINDANY, + 0, 0, s, &bnd.bnd_id, sizeof(bnd.bnd_id)); + break; default: log_debug("main_dispatch_relay: unexpected imsg %d", imsg.hdr.type); @@ -1146,3 +1170,31 @@ protonode_add(enum direction dir, struct protocol *proto, return (0); } + +int +bindany(struct ctl_bindany *bnd) +{ + int s, v; + + s = -1; + v = 1; + + if (relay_socket_af(&bnd->bnd_ss, bnd->bnd_port) == -1) + goto fail; + if ((s = socket(bnd->bnd_ss.ss_family, SOCK_STREAM, + bnd->bnd_proto)) == -1) + goto fail; + if (setsockopt(s, SOL_SOCKET, SO_BINDANY, + &v, sizeof(v)) == -1) + goto fail; + if (bind(s, (struct sockaddr *)&bnd->bnd_ss, + bnd->bnd_ss.ss_len) == -1) + goto fail; + + return (s); + + fail: + if (s != -1) + close(s); + return (-1); +} diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5 index 5bf261b157e..16c609b0f69 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.87 2008/06/11 07:28:02 jmc Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.88 2008/06/11 18:21:20 reyk Exp $ .\" .\" Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org> .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -474,6 +474,7 @@ configuration directives are described below: .It Ic disable Start the relay but immediately close any accepted connections. .It Xo +.Op Ic transparent .Ic forward to .Ar address .Op Ic port Ar port @@ -486,6 +487,11 @@ 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 host connections; the connection will be retried for .Ar number diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index bfe368e4ec2..c30812f05d2 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.102 2008/05/08 02:27:58 reyk Exp $ */ +/* $OpenBSD: relayd.h,v 1.103 2008/06/11 18:21:20 reyk Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -157,7 +157,8 @@ enum imsg_type { IMSG_RECONF_RELAY, IMSG_RECONF_END, IMSG_SCRIPT, - IMSG_SNMPSOCK + IMSG_SNMPSOCK, + IMSG_BINDANY }; struct imsg_hdr { @@ -276,6 +277,15 @@ struct ctl_natlook { int in; }; +struct ctl_bindany { + objid_t bnd_id; + int bnd_proc; + + struct sockaddr_storage bnd_ss; + in_port_t bnd_port; + int bnd_proto; +}; + struct ctl_stats { objid_t id; int proc; @@ -319,7 +329,12 @@ TAILQ_HEAD(addresslist, address); #define F_RETURN 0x00020000 #define F_TRAP 0x00040000 #define F_NEEDPF 0x00080000 -#define F_ROUTE 0x00100000 + +enum forwardmode { + FWD_NORMAL = 0, + FWD_ROUTE, + FWD_TRANS +}; struct host_config { objid_t id; @@ -373,6 +388,7 @@ struct table_config { char exbuf[64]; char digest[41]; /* length of sha1 digest * 2 */ u_int8_t digest_type; + enum forwardmode fwdmode; }; struct table { @@ -436,6 +452,7 @@ struct session { struct evbuffer *se_log; struct relay *se_relay; struct ctl_natlook *se_cnl; + int se_bnds; SPLAY_ENTRY(session) se_nodes; }; @@ -555,6 +572,7 @@ struct relay_config { u_int32_t flags; objid_t proto; char name[MAXHOSTNAMELEN]; + char ifname[IFNAMSIZ]; in_port_t port; in_port_t dstport; int dstmode; @@ -563,6 +581,7 @@ struct relay_config { struct sockaddr_storage ss; struct sockaddr_storage dstss; struct timeval timeout; + enum forwardmode fwdmode; }; struct relay { |