Hi!

On Wed, Sep 17, 2008 at 05:45:23PM +0200, Mikael Jansson wrote:
> I use relayd with redirects to loadbalance between two webservers
> one redirect is used for http requests and the other for https.
> the redirects looks like the following:
> 
> redirect web_http {
>   listen on $ext_ip1 port http
>   sticky-address
>   forward to <webservers> port http check script "/usr/local/sbin/chksrvs"
> }
> 
> redirect web_https {
>   listen on $ext_ip1 port https
>   sticky-address
>   forward to <webservers> port https check script "/usr/local/sbin/chksrvs"
> }
> 
> The redirects works fine separately and sticks to the same machine,
> but when the user navigates from http to https the requests sometimes
> move over to the other machine.  I need the same source-ip to always
> stay on the same server regardless of which destination port (http or https)
> is being used.  Any suggestions on how to achive this would be greatly
> appreciated.
> 

it does not work without a patch.  the problem is that the pf Source
Tracking table includes a reference to the rule but your example above
will load two different rules into pf - one matching http and another
one matching https.

the trick is to combine both statements into one rule.  we don't
support "port tables" in pf yet (which whould be very helpful in many
cases) but there is support for a port range.  so the "hack" is to
allow port ranges in the relayd redirection block

redirect web {
        listen on $ext_ip1 port 80:443
        sticky-address
        forward to <webservers> port http check script "/usr/local/sbin/chksrvs"
}

note that this will match any traffic in the 80 - 443 port range, make
sure that you add additional pf rules to filter any other ports except
80 and 443.  but it works with Source Tracking and should allow your
clients to move between http and https on the same server.  another
limitation is that it only runs checks on one of the ports.

reyk

Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/parse.y,v
retrieving revision 1.122
diff -u -p -I$OpenBSD.*$ -r1.122 parse.y
--- parse.y     22 Jul 2008 23:17:37 -0000      1.122
+++ parse.y     17 Sep 2008 19:21:53 -0000
@@ -30,6 +30,7 @@
 #include <sys/queue.h>
 
 #include <net/if.h>
+#include <net/pfvar.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
@@ -100,11 +101,12 @@ static in_port_t   tableport = 0;
 struct address *host_v4(const char *);
 struct address *host_v6(const char *);
 int             host_dns(const char *, struct addresslist *,
-                   int, in_port_t, const char *);
+                   int, struct portrange *, const char *);
 int             host(const char *, struct addresslist *,
-                   int, in_port_t, const char *);
+                   int, struct portrange *, const char *);
 
 struct table   *table_inherit(struct table *);
+int             getservice(char *);
 
 typedef struct {
        union {
@@ -113,6 +115,7 @@ typedef struct {
                struct host     *host;
                struct timeval   tv;
                struct table    *table;
+               struct portrange port;
                struct {
                        enum digest_type         type;
                        char                    *digest;
@@ -134,8 +137,9 @@ typedef struct {
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.string>      interface hostname table
-%type  <v.number>      port http_type loglevel sslcache optssl mark parent
+%type  <v.number>      http_type loglevel sslcache optssl mark parent
 %type  <v.number>      proto_type dstmode retry log flag direction forwardmode
+%type  <v.port>        port
 %type  <v.host>        host
 %type  <v.tv>          timeout
 %type  <v.digest>      digest
@@ -231,15 +235,29 @@ eflags            : STYLE STRING
                ;
 
 port           : PORT STRING {
-                       struct servent  *servent;
+                       char            *a, *b;
+                       int              p[2];
 
-                       servent = getservbyname($2, "tcp");
-                       if (servent == NULL) {
-                               yyerror("port %s is invalid", $2);
+                       p[0] = p[1] = 0;
+
+                       a = $2;
+                       b = strchr($2, ':');
+                       if (b == NULL)
+                               $$.op = PF_OP_EQ;
+                       else {
+                               *b++ = '\0';
+                               if ((p[1] = getservice(b)) == -1) {
+                                       free($2);
+                                       YYERROR;
+                               }
+                               $$.op = PF_OP_RRG;
+                       }
+                       if ((p[0] = getservice(a)) == -1) {
                                free($2);
                                YYERROR;
                        }
-                       $$ = servent->s_port;
+                       $$.val[0] = p[0];
+                       $$.val[1] = p[1];
                        free($2);
                }
                | PORT NUMBER {
@@ -247,7 +265,8 @@ port                : PORT STRING {
                                yyerror("invalid port: %d", $2);
                                YYERROR;
                        }
-                       $$ = htons($2);
+                       $$.val[0] = htons($2);
+                       $$.op = PF_OP_EQ;
                }
                ;
 
@@ -424,7 +443,7 @@ rdroptsl    : forwardmode TO tablespec inte
                }
                | LISTEN ON STRING port interface {
                        if (host($3, &rdr->virts,
-                                SRV_MAX_VIRTS, $4, $5) <= 0) {
+                                SRV_MAX_VIRTS, &$4, $5) <= 0) {
                                yyerror("invalid virtual ip: %s", $3);
                                free($3);
                                free($5);
@@ -433,7 +452,7 @@ rdroptsl    : forwardmode TO tablespec inte
                        free($3);
                        free($5);
                        if (rdr->conf.port == 0)
-                               rdr->conf.port = $4;
+                               rdr->conf.port = $4.val[0];
                        tableport = rdr->conf.port;
                }
                | DISABLE               { rdr->conf.flags |= F_DISABLE; }
@@ -539,6 +558,8 @@ tablespec   : table                 {
                        struct table    *tb;
                        if (table->conf.port == 0)
                                table->conf.port = tableport;
+                       else
+                               table->conf.flags |= F_PORT;
                        if ((tb = table_inherit(table)) == NULL)
                                YYERROR;
                        $$ = tb;
@@ -550,7 +571,13 @@ tableopts_l        : tableopts tableopts_l
                ;
 
 tableopts      : CHECK tablecheck
-               | port                  { table->conf.port = $1; }
+               | port                  {
+                       if ($1.op != PF_OP_EQ) {
+                               yyerror("invalid port");
+                               YYERROR;
+                       }
+                       table->conf.port = $1.val[0];
+               }
                | TIMEOUT timeout       {
                        bcopy(&$2, &table->conf.timeout,
                            sizeof(struct timeval));
@@ -1117,11 +1144,17 @@ relayoptsl      : LISTEN ON STRING port optss
                        if (rlay->rl_conf.ss.ss_family != AF_UNSPEC) {
                                yyerror("relay %s listener already specified",
                                    rlay->rl_conf.name);
+                               free($3);
+                               YYERROR;
+                       }
+                       if ($4.op != PF_OP_EQ) {
+                               yyerror("invalid port");
+                               free($3);
                                YYERROR;
                        }
 
                        TAILQ_INIT(&al);
-                       if (host($3, &al, 1, $4, NULL) <= 0) {
+                       if (host($3, &al, 1, &$4, NULL) <= 0) {
                                yyerror("invalid listen ip: %s", $3);
                                free($3);
                                YYERROR;
@@ -1129,12 +1162,12 @@ relayoptsl      : LISTEN ON STRING port optss
                        free($3);
                        h = TAILQ_FIRST(&al);
                        bcopy(&h->ss, &rlay->rl_conf.ss, 
sizeof(rlay->rl_conf.ss));
-                       rlay->rl_conf.port = h->port;
+                       rlay->rl_conf.port = h->port.val[0];
                        if ($5) {
                                rlay->rl_conf.flags |= F_SSL;
                                conf->sc_flags |= F_SSL;
                        }
-                       tableport = h->port;
+                       tableport = h->port.val[0];
                }
                | forwardmode TO forwardspec interface dstaf    {
                        rlay->rl_conf.fwdmode = $1;
@@ -1207,9 +1240,14 @@ forwardspec      : tablespec     {
                                free($1);
                                YYERROR;
                        }
+                       if ($2.op != PF_OP_EQ) {
+                               yyerror("invalid port");
+                               free($1);
+                               YYERROR;
+                       }
 
                        TAILQ_INIT(&al);
-                       if (host($1, &al, 1, $2, NULL) <= 0) {
+                       if (host($1, &al, 1, &$2, NULL) <= 0) {
                                yyerror("invalid listen ip: %s", $1);
                                free($1);
                                YYERROR;
@@ -1218,7 +1256,7 @@ forwardspec       : tablespec     {
                        h = TAILQ_FIRST(&al);
                        bcopy(&h->ss, &rlay->rl_conf.dstss,
                            sizeof(rlay->rl_conf.dstss));
-                       rlay->rl_conf.dstport = h->port;
+                       rlay->rl_conf.dstport = h->port.val[0];
                        rlay->rl_conf.dstretry = $3;
                }
                | NAT LOOKUP retry              {
@@ -1268,7 +1306,7 @@ host              : STRING retry parent   {
                                fatal("out of memory");
 
                        TAILQ_INIT(&al);
-                       if (host($1, &al, 1, 0, NULL) <= 0) {
+                       if (host($1, &al, 1, NULL, NULL) <= 0) {
                                yyerror("invalid host %s", $2);
                                free($1);
                                free($$);
@@ -2041,7 +2079,7 @@ host_v6(const char *s)
 
 int
 host_dns(const char *s, struct addresslist *al, int max,
-        in_port_t port, const char *ifname)
+    struct portrange *port, const char *ifname)
 {
        struct addrinfo          hints, *res0, *res;
        int                      error, cnt = 0;
@@ -2068,7 +2106,8 @@ host_dns(const char *s, struct addressli
                if ((h = calloc(1, sizeof(*h))) == NULL)
                        fatal(NULL);
 
-               h->port = port;
+               if (port != NULL)
+                       bcopy(port, &h->port, sizeof(h->port));
                if (ifname != NULL) {
                        if (strlcpy(h->ifname, ifname, sizeof(h->ifname)) >=
                            sizeof(h->ifname))
@@ -2102,7 +2141,7 @@ host_dns(const char *s, struct addressli
 
 int
 host(const char *s, struct addresslist *al, int max,
-    in_port_t port, const char *ifname)
+    struct portrange *port, const char *ifname)
 {
        struct address *h;
 
@@ -2113,7 +2152,8 @@ host(const char *s, struct addresslist *
                h = host_v6(s);
 
        if (h != NULL) {
-               h->port = port;
+               if (port != NULL)
+                       bcopy(port, &h->port, sizeof(h->port));
                if (ifname != NULL) {
                        if (strlcpy(h->ifname, ifname, sizeof(h->ifname)) >=
                            sizeof(h->ifname)) {
@@ -2199,4 +2239,26 @@ table_inherit(struct table *tb)
        TAILQ_INSERT_TAIL(conf->sc_tables, tb, entry);
 
        return (tb);
+}
+
+int
+getservice(char *n)
+{
+       struct servent  *s;
+       const char      *errstr;
+       long long        llval;
+
+       llval = strtonum(n, 0, UINT16_MAX, &errstr);
+       if (errstr) {
+               s = getservbyname(n, "tcp");
+               if (s == NULL)
+                       s = getservbyname(n, "udp");
+               if (s == NULL) {
+                       yyerror("unknown port %s", n);
+                       return (-1);
+               }
+               return (s->s_port);
+       }
+
+       return (htons((u_short)llval));
 }
Index: pfe_filter.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/pfe_filter.c,v
retrieving revision 1.32
diff -u -p -I$OpenBSD.*$ -r1.32 pfe_filter.c
--- pfe_filter.c        16 Jul 2008 14:38:33 -0000      1.32
+++ pfe_filter.c        17 Sep 2008 19:21:53 -0000
@@ -385,8 +385,9 @@ sync_ruleset(struct relayd *env, struct 
                rio.rule.proto = IPPROTO_TCP;
                rio.rule.src.addr.type = PF_ADDR_ADDRMASK;
                rio.rule.dst.addr.type = PF_ADDR_ADDRMASK;
-               rio.rule.dst.port_op = PF_OP_EQ;
-               rio.rule.dst.port[0] = address->port;
+               rio.rule.dst.port_op = address->port.op;
+               rio.rule.dst.port[0] = address->port.val[0];
+               rio.rule.dst.port[1] = address->port.val[1];
                rio.rule.rtableid = -1; /* stay in the main routing table */
 
                if (strlen(rdr->conf.tag))
@@ -422,8 +423,12 @@ sync_ruleset(struct relayd *env, struct 
                if (ioctl(env->sc_pf->dev, DIOCADDADDR, &pio) == -1)
                        fatal("sync_ruleset: cannot add address to pool");
 
-               rio.rule.rpool.proxy_port[0] = ntohs(rdr->table->conf.port);
-               rio.rule.rpool.port_op = PF_OP_EQ;
+               if (address->port.op == PF_OP_EQ ||
+                   rdr->table->conf.flags & F_PORT) {
+                       rio.rule.rpool.proxy_port[0] =
+                           ntohs(rdr->table->conf.port);
+                       rio.rule.rpool.port_op = PF_OP_EQ;
+               }
                rio.rule.rpool.opts = PF_POOL_ROUNDROBIN;
                if (rdr->conf.flags & F_STICKY)
                        rio.rule.rpool.opts |= PF_POOL_STICKYADDR;
Index: relayd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.conf.5,v
retrieving revision 1.93
diff -u -p -I$OpenBSD.*$ -r1.93 relayd.conf.5
--- relayd.conf.5       8 Aug 2008 22:49:33 -0000       1.93
+++ relayd.conf.5       17 Sep 2008 19:21:54 -0000
@@ -414,7 +414,7 @@ Specify the tables of target hosts to be
 section above for information about table options.
 If the
 .Ic port
-option is not specified, the port from the
+option is not specified, the first port from the
 .Ic listen on
 directive will be used.
 This directive can be specified twice \(en the second entry will be used
@@ -432,6 +432,11 @@ to listen on.
 .Xr pf 4
 will redirect incoming connections for the specified target to the
 hosts in the main or backup table.
+The
+.Ar port
+argument can optionally specify a port range instead of a single port,
+the format is
+.Ar min-port Ic : Ar max-port .
 The rdr rule can be optionally restricted to a given interface name.
 .It Xo
 .Ic route to
Index: relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.110
diff -u -p -I$OpenBSD.*$ -r1.110 relayd.h
--- relayd.h    11 Aug 2008 08:07:14 -0000      1.110
+++ relayd.h    17 Sep 2008 19:21:54 -0000
@@ -309,9 +309,14 @@ struct ctl_stats {
        u_int32_t                last_day;
 };
 
+struct portrange {
+       in_port_t                val[2];
+       u_int8_t                 op;
+};
+
 struct address {
        struct sockaddr_storage  ss;
-       in_port_t                port;
+       struct portrange         port;
        char                     ifname[IFNAMSIZ];
        TAILQ_ENTRY(address)     entry;
 };
@@ -337,6 +342,7 @@ TAILQ_HEAD(addresslist, address);
 #define F_RETURN               0x00020000
 #define F_TRAP                 0x00040000
 #define F_NEEDPF               0x00080000
+#define F_PORT                 0x00100000
 
 enum forwardmode {
        FWD_NORMAL              = 0,

Reply via email to