Reyk Floeter writes:
> Hi,
>
> the attached diff fixes filter rules with "forward to" statement in
> persistent (keep-alive) connections. See the XXX comment below.
>
> ```relayd.conf
> log connection
> table <a> {
> 127.0.0.1
> }
> table <b> {
> 127.0.0.1
> }
> table <c> {
> 127.0.0.1
> }
> http protocol pathfwd {
> return error
>
> # XXX The following workaround is not needed anymore:
> #match header set "Connection" value "close"
>
> pass path "/a/*" forward to <a>
> pass path "/b/*" forward to <b>
> #match request path log "*"
> }
> relay pathfwd {
> listen on 0.0.0.0 port 80
> protocol pathfwd
> forward to <c> port 8082
> forward to <a> port 8080
> forward to <b> port 8081
> }
> ```
>
> OK?
>
Works great for us. FWIW, OK mikeb
> reyk
>
> Index: usr.sbin/relayd/relay.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/relayd/relay.c,v
> retrieving revision 1.242
> diff -u -p -u -p -r1.242 relay.c
> --- usr.sbin/relayd/relay.c 4 Mar 2019 21:25:03 -0000 1.242
> +++ usr.sbin/relayd/relay.c 8 May 2019 14:26:40 -0000
> @@ -76,11 +76,14 @@ int relay_tls_ctx_create(struct relay
> void relay_tls_transaction(struct rsession *,
> struct ctl_relay_event *);
> void relay_tls_handshake(int, short, void *);
> -void relay_connect_retry(int, short, void *);
> void relay_tls_connected(struct ctl_relay_event *);
> void relay_tls_readcb(int, short, void *);
> void relay_tls_writecb(int, short, void *);
>
> +void relay_connect_retry(int, short, void *);
> +void relay_connect_state(struct rsession *,
> + struct ctl_relay_event *, enum relay_state);
> +
> extern void bufferevent_read_pressure_cb(struct evbuffer *, size_t,
> size_t, void *);
>
> @@ -654,6 +657,7 @@ relay_socket_listen(struct sockaddr_stor
> void
> relay_connected(int fd, short sig, void *arg)
> {
> + char obuf[128];
> struct rsession *con = arg;
> struct relay *rlay = con->se_relay;
> struct protocol *proto = rlay->rl_proto;
> @@ -696,6 +700,22 @@ relay_connected(int fd, short sig, void
>
> DPRINTF("%s: session %d: successful", __func__, con->se_id);
>
> + /* Log destination if it was changed in a keep-alive connection */
> + if ((con->se_table != con->se_table0) &&
> + (env->sc_conf.opts & (RELAYD_OPT_LOGCON|RELAYD_OPT_LOGCONERR))) {
> + con->se_table0 = con->se_table;
> + memset(&obuf, 0, sizeof(obuf));
> + (void)print_host(&con->se_out.ss, obuf, sizeof(obuf));
> + if (asprintf(&msg, " -> %s:%d",
> + obuf, ntohs(con->se_out.port)) == -1) {
> + relay_abort_http(con, 500,
> + "connection changed and asprintf failed", 0);
> + return;
> + }
> + relay_log(con, msg);
> + free(msg);
> + }
> +
> switch (rlay->rl_proto->type) {
> case RELAY_PROTO_HTTP:
> if (relay_httpdesc_init(out) == -1) {
> @@ -1465,6 +1485,17 @@ relay_bindany(int fd, short event, void
> }
>
> void
> +relay_connect_state(struct rsession *con, struct ctl_relay_event *cre,
> + enum relay_state new)
> +{
> + DPRINTF("%s: session %d: %s state %s -> %s",
> + __func__, con->se_id,
> + cre->dir == RELAY_DIR_REQUEST ? "accept" : "connect",
> + relay_state(cre->state), relay_state(new));
> + cre->state = new;
> +}
> +
> +void
> relay_connect_retry(int fd, short sig, void *arg)
> {
> struct timeval evtpause = { 1, 0 };
> @@ -1533,9 +1564,9 @@ relay_connect_retry(int fd, short sig, v
> }
>
> if (rlay->rl_conf.flags & F_TLSINSPECT)
> - con->se_out.state = STATE_PRECONNECT;
> + relay_connect_state(con, &con->se_out, STATE_PRECONNECT);
> else
> - con->se_out.state = STATE_CONNECTED;
> + relay_connect_state(con, &con->se_out, STATE_CONNECTED);
> relay_inflight--;
> DPRINTF("%s: inflight decremented, now %d",__func__, relay_inflight);
>
> @@ -1560,7 +1591,7 @@ relay_preconnect(struct rsession *con)
> con->se_id, privsep_process);
> rv = relay_connect(con);
> if (con->se_out.state == STATE_CONNECTED)
> - con->se_out.state = STATE_PRECONNECT;
> + relay_connect_state(con, &con->se_out, STATE_PRECONNECT);
> return (rv);
> }
>
> @@ -1585,7 +1616,7 @@ relay_connect(struct rsession *con)
> return (-1);
> }
> relay_connected(con->se_out.s, EV_WRITE, con);
> - con->se_out.state = STATE_CONNECTED;
> + relay_connect_state(con, &con->se_out, STATE_CONNECTED);
> return (0);
> }
>
> @@ -1642,7 +1673,7 @@ relay_connect(struct rsession *con)
> evtimer_add(&rlay->rl_evt, &evtpause);
>
> /* this connect is pending */
> - con->se_out.state = STATE_PENDING;
> + relay_connect_state(con, &con->se_out, STATE_PENDING);
> return (0);
> } else {
> if (con->se_retry) {
> @@ -1660,7 +1691,7 @@ relay_connect(struct rsession *con)
> }
> }
>
> - con->se_out.state = STATE_CONNECTED;
> + relay_connect_state(con, &con->se_out, STATE_CONNECTED);
> relay_inflight--;
> DPRINTF("%s: inflight decremented, now %d",__func__,
> relay_inflight);
> @@ -1686,10 +1717,6 @@ relay_close(struct rsession *con, const
> relay_session_unpublish(con);
>
> event_del(&con->se_ev);
> - if (con->se_in.bev != NULL)
> - bufferevent_disable(con->se_in.bev, EV_READ|EV_WRITE);
> - if (con->se_out.bev != NULL)
> - bufferevent_disable(con->se_out.bev, EV_READ|EV_WRITE);
>
> if ((env->sc_conf.opts & (RELAYD_OPT_LOGCON|RELAYD_OPT_LOGCONERR)) &&
> msg != NULL) {
> @@ -1726,7 +1753,8 @@ relay_close(struct rsession *con, const
>
> free(con->se_priv);
>
> - if (relay_reset_event(&con->se_in)) {
> + relay_connect_state(con, &con->se_in, STATE_DONE);
> + if (relay_reset_event(con, &con->se_in)) {
> if (con->se_out.s == -1) {
> /*
> * the output was never connected,
> @@ -1740,7 +1768,8 @@ relay_close(struct rsession *con, const
> if (con->se_in.output != NULL)
> evbuffer_free(con->se_in.output);
>
> - if (relay_reset_event(&con->se_out)) {
> + relay_connect_state(con, &con->se_out, STATE_DONE);
> + if (relay_reset_event(con, &con->se_out)) {
> /* Some file descriptors are available again. */
> if (evtimer_pending(&rlay->rl_evt, NULL)) {
> evtimer_del(&rlay->rl_evt);
> @@ -1766,14 +1795,16 @@ relay_close(struct rsession *con, const
> }
>
> int
> -relay_reset_event(struct ctl_relay_event *cre)
> +relay_reset_event(struct rsession *con, struct ctl_relay_event *cre)
> {
> int rv = 0;
>
> - DPRINTF("%s: state %d dir %d", __func__, cre->state, cre->dir);
> -
> - if (cre->bev != NULL)
> + if (cre->state != STATE_DONE)
> + relay_connect_state(con, cre, STATE_CLOSED);
> + if (cre->bev != NULL) {
> + bufferevent_disable(cre->bev, EV_READ|EV_WRITE);
> bufferevent_free(cre->bev);
> + }
> if (cre->tls != NULL)
> tls_close(cre->tls);
> tls_free(cre->tls);
> @@ -1784,7 +1815,6 @@ relay_reset_event(struct ctl_relay_event
> close(cre->s);
> rv = 1;
> }
> - cre->state = STATE_DONE;
> cre->bev = NULL;
> cre->tls = NULL;
> cre->tls_cfg = NULL;
> Index: usr.sbin/relayd/relay_http.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v
> retrieving revision 1.72
> diff -u -p -u -p -r1.72 relay_http.c
> --- usr.sbin/relayd/relay_http.c 4 Mar 2019 21:25:03 -0000 1.72
> +++ usr.sbin/relayd/relay_http.c 8 May 2019 14:26:40 -0000
> @@ -71,9 +71,11 @@ int relay_httpurl_test(struct ctl_rela
> struct relay_rule *, struct kvlist *);
> int relay_httpcookie_test(struct ctl_relay_event *,
> struct relay_rule *, struct kvlist *);
> -int relay_apply_actions(struct ctl_relay_event *, struct kvlist *);
> +int relay_apply_actions(struct ctl_relay_event *, struct kvlist *,
> + struct relay_table *);
> int relay_match_actions(struct ctl_relay_event *,
> - struct relay_rule *, struct kvlist *, struct kvlist *);
> + struct relay_rule *, struct kvlist *, struct kvlist *,
> + struct relay_table **);
> void relay_httpdesc_free(struct http_descriptor *);
>
> static struct relayd *env = NULL;
> @@ -1509,7 +1511,7 @@ relay_httpcookie_test(struct ctl_relay_e
>
> int
> relay_match_actions(struct ctl_relay_event *cre, struct relay_rule *rule,
> - struct kvlist *matches, struct kvlist *actions)
> + struct kvlist *matches, struct kvlist *actions, struct relay_table **tbl)
> {
> struct rsession *con = cre->con;
> struct kv *kv, *tmp;
> @@ -1518,11 +1520,9 @@ relay_match_actions(struct ctl_relay_eve
> * Apply the following options instantly (action per match).
> */
> if (rule->rule_table != NULL)
> - con->se_table = rule->rule_table;
> -
> + *tbl = rule->rule_table;
> if (rule->rule_tag != 0)
> con->se_tag = rule->rule_tag == -1 ? 0 : rule->rule_tag;
> -
> if (rule->rule_label != 0)
> con->se_label = rule->rule_label == -1 ? 0 : rule->rule_label;
>
> @@ -1546,7 +1546,8 @@ relay_match_actions(struct ctl_relay_eve
> }
>
> int
> -relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions)
> +relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions,
> + struct relay_table *tbl)
> {
> struct rsession *con = cre->con;
> struct http_descriptor *desc = cre->desc;
> @@ -1735,12 +1736,22 @@ relay_apply_actions(struct ctl_relay_eve
> }
>
> /*
> + * Change the backend if the forward table has been changed.
> + * This only works in the request direction.
> + */
> + if (cre->dir == RELAY_DIR_REQUEST && con->se_table != tbl) {
> + relay_reset_event(con, &con->se_out);
> + con->se_table = tbl;
> + con->se_haslog = 1;
> + }
> +
> + /*
> * log tag for request and response, request method
> * and end of request marker ","
> */
> if ((con->se_log != NULL) &&
> ((meth = relay_httpmethod_byid(desc->http_method)) != NULL) &&
> - (asprintf(&msg, " %s",meth) >= 0))
> + (asprintf(&msg, " %s", meth) != -1))
> evbuffer_add(con->se_log, msg, strlen(msg));
> free(msg);
> relay_log(con, cre->dir == RELAY_DIR_REQUEST ? "" : ";");
> @@ -1770,6 +1781,7 @@ relay_test(struct protocol *proto, struc
> struct rsession *con;
> struct http_descriptor *desc = cre->desc;
> struct relay_rule *r = NULL, *rule = NULL;
> + struct relay_table *tbl = NULL;
> u_int cnt = 0;
> u_int action = RES_PASS;
> struct kvlist actions, matches;
> @@ -1820,7 +1832,7 @@ relay_test(struct protocol *proto, struc
>
> if (r->rule_action == RULE_ACTION_MATCH) {
> if (relay_match_actions(cre, r, &matches,
> - &actions) != 0) {
> + &actions, &tbl) != 0) {
> /* Something bad happened, drop */
> action = RES_DROP;
> break;
> @@ -1854,13 +1866,13 @@ relay_test(struct protocol *proto, struc
> }
> }
>
> - if (rule != NULL && relay_match_actions(cre, rule, NULL, &actions)
> + if (rule != NULL && relay_match_actions(cre, rule, NULL, &actions, &tbl)
> != 0) {
> /* Something bad happened, drop */
> action = RES_DROP;
> }
>
> - if (relay_apply_actions(cre, &actions) != 0) {
> + if (relay_apply_actions(cre, &actions, tbl) != 0) {
> /* Something bad happened, drop */
> action = RES_DROP;
> }
> Index: usr.sbin/relayd/relayd.h
> ===================================================================
> RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
> retrieving revision 1.252
> diff -u -p -u -p -r1.252 relayd.h
> --- usr.sbin/relayd/relayd.h 4 Mar 2019 21:25:03 -0000 1.252
> +++ usr.sbin/relayd/relayd.h 8 May 2019 14:26:40 -0000
> @@ -193,6 +193,7 @@ enum relay_state {
> STATE_PENDING,
> STATE_PRECONNECT,
> STATE_CONNECTED,
> + STATE_CLOSED,
> STATE_DONE
> };
>
> @@ -555,6 +556,7 @@ struct rsession {
> void *se_priv;
> SIPHASH_CTX se_siphashctx;
> struct relay_table *se_table;
> + struct relay_table *se_table0;
> struct event se_ev;
> struct timeval se_timeout;
> struct timeval se_tv_start;
> @@ -1134,6 +1136,9 @@ int cmdline_symset(char *);
> const char *host_error(enum host_error);
> const char *host_status(enum host_status);
> const char *table_check(enum table_check);
> +#ifdef DEBUG
> +const char *relay_state(enum relay_state);
> +#endif
> const char *print_availability(u_long, u_long);
> const char *print_host(struct sockaddr_storage *, char *, size_t);
> const char *print_time(struct timeval *, struct timeval *, char *, size_t);
> @@ -1178,7 +1183,7 @@ int relay_session_cmp(struct rsession *
> char *relay_load_fd(int, off_t *);
> int relay_load_certfiles(struct relay *);
> void relay_close(struct rsession *, const char *, int);
> -int relay_reset_event(struct ctl_relay_event *);
> +int relay_reset_event(struct rsession *, struct ctl_relay_event *);
> void relay_natlook(int, short, void *);
> void relay_session(struct rsession *);
> int relay_from_table(struct rsession *);
> Index: usr.sbin/relayd/util.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/relayd/util.c,v
> retrieving revision 1.1
> diff -u -p -u -p -r1.1 util.c
> --- usr.sbin/relayd/util.c 21 Nov 2015 12:37:42 -0000 1.1
> +++ usr.sbin/relayd/util.c 8 May 2019 14:26:41 -0000
> @@ -177,6 +177,29 @@ table_check(enum table_check check)
> return ("invalid");
> }
>
> +#ifdef DEBUG
> +const char *
> +relay_state(enum relay_state state)
> +{
> + switch (state) {
> + case STATE_INIT:
> + return ("init");
> + case STATE_PENDING:
> + return ("pending");
> + case STATE_PRECONNECT:
> + return ("preconnect");
> + case STATE_CONNECTED:
> + return ("connected");
> + case STATE_CLOSED:
> + return ("closed");
> + case STATE_DONE:
> + return ("done");
> + };
> + /* NOTREACHED */
> + return ("invalid");
> +}
> +#endif
> +
> const char *
> print_availability(u_long cnt, u_long up)
> {