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? 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) {