Hi,

the relayd code already had a few bits for from/to specifiers in
filter rules, but it wasn't finished.  I did get occasional requests
if it would be possible to filter based on IPs (much like Allow/Deny
rules elsewhere).  Simple blocking should better be done in pf but the
purpose of this is to match URLs/headers related to IPs (see below).

```relayd.conf
http protocol foo {
        block from 10.1.1.1
        block from 10.1.1.2 to 192.168.0.0/24
        pass from 10.1.0.0/16 path "/hello/*" forward to <a>
        pass from 10.2.0.0/16 path "/hello/*" forward to <b>
}
```

Ok?

Reyk

Index: usr.sbin/relayd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/parse.y,v
retrieving revision 1.233
diff -u -p -u -p -r1.233 parse.y
--- usr.sbin/relayd/parse.y     13 Mar 2019 23:29:32 -0000      1.233
+++ usr.sbin/relayd/parse.y     9 May 2019 17:12:09 -0000
@@ -153,6 +153,7 @@ typedef struct {
                enum direction           dir;
                struct {
                        struct sockaddr_storage  ss;
+                       int                      prefixlen;
                        char                     name[HOST_NAME_MAX+1];
                }                        addr;
                struct {
@@ -187,7 +188,7 @@ typedef struct {
 %type  <v.number>      action ruleaf key_option
 %type  <v.port>        port
 %type  <v.host>        host
-%type  <v.addr>        address
+%type  <v.addr>        address rulesrc ruledst addrprefix
 %type  <v.tv>          timeout
 %type  <v.digest>      digest optdigest
 %type  <v.table>       tablespec
@@ -1293,6 +1294,20 @@ filterrule       : action dir quick ruleaf rul
                        rule->rule_dir = $2;
                        rule->rule_flags |= $3;
                        rule->rule_af = $4;
+                       rule->rule_src.addr = $5.ss;
+                       rule->rule_src.addr_mask = $5.prefixlen;
+                       rule->rule_dst.addr = $6.ss;
+                       rule->rule_dst.addr_mask = $6.prefixlen;
+
+                       if (RELAY_AF_NEQ(rule->rule_af,
+                           rule->rule_src.addr.ss_family) ||
+                           RELAY_AF_NEQ(rule->rule_af,
+                           rule->rule_dst.addr.ss_family) ||
+                           RELAY_AF_NEQ(rule->rule_src.addr.ss_family,
+                           rule->rule_dst.addr.ss_family)) {
+                               yyerror("address family mismatch");
+                               YYERROR;
+                       }
 
                        rulefile = NULL;
                } ruleopts_l {
@@ -1341,10 +1356,20 @@ ruleaf          : /* empty */                   { $$ = 
AF_UNSPEC
                | INET                          { $$ = AF_INET; }
                ;
 
-rulesrc                : /* XXX */
+rulesrc                : /* empty */           {
+                       memset(&$$, 0, sizeof($$));
+               }
+               | FROM addrprefix               {
+                       $$ = $2;
+               }
                ;
 
-ruledst                : /* XXX */
+ruledst                : /* empty */                   {
+                       memset(&$$, 0, sizeof($$));
+               }
+               | TO addrprefix                 {
+                       $$ = $2;
+               }
                ;
 
 ruleopts_l     : /* empty */
@@ -1967,7 +1992,7 @@ routeopts_l       : routeopts_l routeoptsl nl
                | routeoptsl optnl
                ;
 
-routeoptsl     : ROUTE address '/' NUMBER {
+routeoptsl     : ROUTE addrprefix {
                        struct netroute *nr;
 
                        if (router->rt_conf.af == AF_UNSPEC)
@@ -1978,14 +2003,6 @@ routeoptsl       : ROUTE address '/' NUMBER {
                                YYERROR;
                        }
 
-                       if ((router->rt_conf.af == AF_INET &&
-                           ($4 > 32 || $4 < 0)) ||
-                           (router->rt_conf.af == AF_INET6 &&
-                           ($4 > 128 || $4 < 0))) {
-                               yyerror("invalid prefixlen %d", $4);
-                               YYERROR;
-                       }
-
                        if ((nr = calloc(1, sizeof(*nr))) == NULL)
                                fatal("out of memory");
 
@@ -1995,7 +2012,7 @@ routeoptsl        : ROUTE address '/' NUMBER {
                                free(nr);
                                YYERROR;
                        }
-                       nr->nr_conf.prefixlen = $4;
+                       nr->nr_conf.prefixlen = $2.prefixlen;
                        nr->nr_conf.routerid = router->rt_conf.id;
                        nr->nr_router = router;
                        bcopy(&$2.ss, &nr->nr_conf.ss, sizeof($2.ss));
@@ -2166,6 +2183,26 @@ address          : STRING        {
                        h = TAILQ_FIRST(&al);
                        memcpy(&$$.ss, &h->ss, sizeof($$.ss));
                        host_free(&al);
+               }
+               ;
+
+addrprefix     : address '/' NUMBER            {
+                       $$ = $1;
+                       if (($$.ss.ss_family == AF_INET &&
+                           ($3 > 32 || $3 < 0)) ||
+                           ($$.ss.ss_family == AF_INET6 &&
+                           ($3 > 128 || $3 < 0))) {
+                               yyerror("invalid prefixlen %d", $3);
+                               YYERROR;
+                       }
+                       $$.prefixlen = $3;
+               }
+               | address                       {
+                       $$ = $1;
+                       if ($$.ss.ss_family == AF_INET)
+                               $$.prefixlen = 32;
+                       else if ($$.ss.ss_family == AF_INET6)
+                               $$.prefixlen = 128;
                }
                ;
 
Index: usr.sbin/relayd/relay.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.243
diff -u -p -u -p -r1.243 relay.c
--- usr.sbin/relayd/relay.c     8 May 2019 23:22:19 -0000       1.243
+++ usr.sbin/relayd/relay.c     9 May 2019 17:12:09 -0000
@@ -28,6 +28,7 @@
 #include <arpa/inet.h>
 
 #include <limits.h>
+#include <netdb.h>
 #include <poll.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -117,6 +118,7 @@ relay_ruledebug(struct relay_rule *rule)
 {
        struct kv       *kv = NULL;
        u_int            i;
+       char             buf[NI_MAXHOST];
 
        fprintf(stderr, "\t\t");
 
@@ -150,6 +152,25 @@ relay_ruledebug(struct relay_rule *rule)
        if (rule->rule_flags & RULE_FLAG_QUICK)
                fprintf(stderr, "quick ");
 
+       switch (rule->rule_af) {
+       case AF_INET:
+               fprintf(stderr, "inet ");
+               break;
+       case AF_INET6:
+               fprintf(stderr, "inet6 ");
+               break;
+       }
+
+       if (rule->rule_src.addr.ss_family != AF_UNSPEC)
+               fprintf(stderr, "from %s/%d ",
+                   print_host(&rule->rule_src.addr, buf, sizeof(buf)),
+                   rule->rule_src.addr_mask);
+
+       if (rule->rule_dst.addr.ss_family != AF_UNSPEC)
+               fprintf(stderr, "to %s/%d ",
+                   print_host(&rule->rule_dst.addr, buf, sizeof(buf)),
+                   rule->rule_dst.addr_mask);
+
        for (i = 1; i < KEY_TYPE_MAX; i++) {
                kv = &rule->rule_kv[i];
                if (kv->kv_type != i)
@@ -1118,7 +1139,13 @@ relay_accept(int fd, short event, void *
                con->se_in.port = ((struct sockaddr_in6 *)&ss)->sin6_port;
                break;
        }
-       bcopy(&ss, &con->se_in.ss, sizeof(con->se_in.ss));
+       memcpy(&con->se_in.ss, &ss, sizeof(con->se_in.ss));
+
+       slen = sizeof(con->se_sockname);
+       if (getsockname(s, (struct sockaddr *)&con->se_sockname, &slen) == -1) {
+               relay_close(con, "sockname lookup failed", 1);
+               return;
+       }
 
        getmonotime(&con->se_tv_start);
        bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last));
@@ -1143,12 +1170,8 @@ relay_accept(int fd, short event, void *
        }
 
        if (rlay->rl_conf.flags & F_DIVERT) {
-               slen = sizeof(con->se_out.ss);
-               if (getsockname(s, (struct sockaddr *)&con->se_out.ss,
-                   &slen) == -1) {
-                       relay_close(con, "peer lookup failed", 1);
-                       return;
-               }
+               memcpy(&con->se_out.ss, &con->se_sockname,
+                   sizeof(con->se_out.ss));
                con->se_out.port = relay_socket_getport(&con->se_out.ss);
 
                /* Detect loop and fall back to the alternate forward target */
@@ -1169,13 +1192,8 @@ relay_accept(int fd, short event, void *
                cnl->proc = ps->ps_instance;
                cnl->proto = IPPROTO_TCP;
 
-               bcopy(&con->se_in.ss, &cnl->src, sizeof(cnl->src));
-               slen = sizeof(cnl->dst);
-               if (getsockname(s,
-                   (struct sockaddr *)&cnl->dst, &slen) == -1) {
-                       relay_close(con, "failed to get local address", 1);
-                       return;
-               }
+               memcpy(&cnl->src, &con->se_in.ss, sizeof(cnl->src));
+               memcpy(&cnl->dst, &con->se_sockname, sizeof(cnl->dst));
 
                proc_compose(env->sc_ps, PROC_PFE, IMSG_NATLOOK,
                    cnl, sizeof(*cnl));
Index: usr.sbin/relayd/relay_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v
retrieving revision 1.73
diff -u -p -u -p -r1.73 relay_http.c
--- usr.sbin/relayd/relay_http.c        8 May 2019 23:22:19 -0000       1.73
+++ usr.sbin/relayd/relay_http.c        9 May 2019 17:12:09 -0000
@@ -1765,13 +1765,12 @@ relay_test(struct protocol *proto, struc
                        RELAY_GET_SKIP_STEP(RULE_SKIP_DIR);
                else if (proto->type != r->rule_proto)
                        RELAY_GET_SKIP_STEP(RULE_SKIP_PROTO);
-               else if (r->rule_af != AF_UNSPEC &&
-                   (cre->ss.ss_family != r->rule_af ||
-                    cre->dst->ss.ss_family != r->rule_af))
+               else if (RELAY_AF_NEQ(r->rule_af, cre->ss.ss_family) ||
+                    RELAY_AF_NEQ(r->rule_af, cre->dst->ss.ss_family))
                        RELAY_GET_SKIP_STEP(RULE_SKIP_AF);
                else if (RELAY_ADDR_CMP(&r->rule_src, &cre->ss) != 0)
                        RELAY_GET_SKIP_STEP(RULE_SKIP_SRC);
-               else if (RELAY_ADDR_CMP(&r->rule_dst, &cre->dst->ss) != 0)
+               else if (RELAY_ADDR_CMP(&r->rule_dst, &con->se_sockname) != 0)
                        RELAY_GET_SKIP_STEP(RULE_SKIP_DST);
                else if (r->rule_method != HTTP_METHOD_NONE &&
                    (desc->http_method == HTTP_METHOD_RESPONSE ||
@@ -1870,7 +1869,7 @@ relay_calc_skip_steps(struct relay_rules
                        RELAY_SET_SKIP_STEPS(RULE_SKIP_DIR);
                else if (cur->rule_proto != prev->rule_proto)
                        RELAY_SET_SKIP_STEPS(RULE_SKIP_PROTO);
-               else if (cur->rule_af != prev->rule_af)
+               else if (RELAY_AF_NEQ(cur->rule_af, prev->rule_af))
                        RELAY_SET_SKIP_STEPS(RULE_SKIP_AF);
                else if (RELAY_ADDR_NEQ(&cur->rule_src, &prev->rule_src))
                        RELAY_SET_SKIP_STEPS(RULE_SKIP_SRC);
Index: usr.sbin/relayd/relayd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.conf.5,v
retrieving revision 1.188
diff -u -p -u -p -r1.188 relayd.conf.5
--- usr.sbin/relayd/relayd.conf.5       4 Mar 2019 21:25:03 -0000       1.188
+++ usr.sbin/relayd/relayd.conf.5       9 May 2019 17:12:09 -0000
@@ -1080,6 +1080,13 @@ evaluation is skipped.
 .It Ic inet No or Ic inet6
 Only match connections with the specified address family,
 either of type IPv4 or IPv6.
+.It Ic from Ar address Ns Oo Li / Ns Ar prefix Oc
+This rule only matches for connections from the specified source.
+.It Ic to Ar address Ns Oo Li / Ns Ar prefix Oc
+This rule only matches for connections to the specified destination.
+The destination is the address the client was connecting to,
+typically the relay's listen address in non-transparent mode,
+not the address of the forwarded backend connection.
 .It Ic forward to Pf < Ar table Ns >
 Forward the request to a server in the specified table.
 With this option, requests can be passed to specific backend servers.
Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.253
diff -u -p -u -p -r1.253 relayd.h
--- usr.sbin/relayd/relayd.h    8 May 2019 23:22:19 -0000       1.253
+++ usr.sbin/relayd/relayd.h    9 May 2019 17:12:09 -0000
@@ -550,6 +550,7 @@ TAILQ_HEAD(rdrlist, rdr);
 struct rsession {
        objid_t                          se_id;
        objid_t                          se_relayid;
+       struct sockaddr_storage          se_sockname;
        struct ctl_relay_event           se_in;
        struct ctl_relay_event           se_out;
        void                            *se_priv;
@@ -601,11 +602,9 @@ enum rule_action {
 };
 
 struct rule_addr {
-       int                              addr_af;
        struct sockaddr_storage          addr;
        u_int8_t                         addr_mask;
-       int                              addr_net;
-       in_port_t                        addr_port;
+       int                              addr_port;
 };
 
 #define RELAY_ADDR_EQ(_a, _b)                                          \
@@ -621,6 +620,10 @@ struct rule_addr {
        ((_a)->addr_mask != (_b)->addr_mask ||                          \
        sockaddr_cmp((struct sockaddr *)&(_a)->addr,                    \
        (struct sockaddr *)&(_b)->addr, (_a)->addr_mask) != 0)
+
+#define RELAY_AF_NEQ(_a, _b)                                           \
+       (((_a) != AF_UNSPEC) && ((_b) != AF_UNSPEC) &&                  \
+       ((_a) != (_b)))
 
 struct relay_rule {
        objid_t                  rule_id;

Reply via email to