relayd: fix filter rules with forward to statement

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

relayd: fix filter rules with forward to statement

Reyk Floeter-2
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)
 {

Reply | Threaded
Open this post in threaded view
|

Re: relayd: fix filter rules with forward to statement

Mike Belopuhov-5

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