Support source-hash and random with tables and dynifs; not just pools.
authorreyk <reyk@openbsd.org>
Fri, 19 Dec 2014 13:04:07 +0000 (13:04 +0000)
committerreyk <reyk@openbsd.org>
Fri, 19 Dec 2014 13:04:07 +0000 (13:04 +0000)
This finally allows to use source-hash for dynamic loadbalancing, eg.
"rdr-to <hosts> source-hash", instead of just round-robin and least-states.

An older pre-siphash version of this diff was tested by many people.

OK tedu@ benno@

sbin/pfctl/parse.y
share/man/man5/pf.conf.5
sys/net/pf_lb.c
sys/net/pfvar.h

index db77052..083c2dd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.642 2014/11/20 05:51:20 jsg Exp $ */
+/*     $OpenBSD: parse.y,v 1.643 2014/12/19 13:04:07 reyk Exp $        */
 
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
@@ -1805,13 +1805,9 @@ pfrule           : action dir logquick interface af proto fromto
                                    DYNIF_MULTIADDR($8.route.host->addr)))
                                        r.route.opts |= PF_POOL_ROUNDROBIN;
                                if ($8.route.host->next != NULL) {
-                                       if (((r.route.opts & PF_POOL_TYPEMASK) !=
-                                           PF_POOL_ROUNDROBIN) &&
-                                           ((r.route.opts & PF_POOL_TYPEMASK) !=
-                                           PF_POOL_LEASTSTATES)) {
-                                               yyerror("r.route.opts must "
-                                                   "be PF_POOL_ROUNDROBIN "
-                                                   "or PF_POOL_LEASTSTATES");
+                                       if (!PF_POOL_DYNTYPE(r.route.opts)) {
+                                               yyerror("address pool option "
+                                                   "not supported by type");
                                                YYERROR;
                                        }
                                }
@@ -4387,10 +4383,8 @@ collapse_redirspec(struct pf_pool *rpool, struct pf_rule *r,
                        hprev = h; /* in case we need to conver to a table */
                } else {                /* multiple hosts */
                        if (rs->pool_opts.type &&
-                           (rs->pool_opts.type != PF_POOL_ROUNDROBIN) &&
-                           (rs->pool_opts.type != PF_POOL_LEASTSTATES)) {
-                               yyerror("only round-robin or "
-                                   "least-states valid for multiple "
+                           !PF_POOL_DYNTYPE(rs->pool_opts.type)) {
+                               yyerror("pool type is not valid for multiple "
                                    "translation or routing addresses");
                                return (1);
                        }
@@ -4488,16 +4482,16 @@ apply_redirspec(struct pf_pool *rpool, struct pf_rule *r, struct redirspec *rs,
        }
 
        rpool->opts = rs->pool_opts.type;
-       if (rpool->addr.type == PF_ADDR_TABLE ||
-           DYNIF_MULTIADDR(rpool->addr))
+       if ((rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_NONE &&
+           (rpool->addr.type == PF_ADDR_TABLE ||
+           DYNIF_MULTIADDR(rpool->addr)))
                rpool->opts |= PF_POOL_ROUNDROBIN;
 
-       if (((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) &&
-           ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_LEASTSTATES) &&
-           (disallow_table(rs->rdr->host, "tables are only supported "
-           "in round-robin or least-states address pools") ||
-           disallow_alias(rs->rdr->host, "interface (%s) is only supported "
-           "in round-robin or least-states address pools")))
+       if (!PF_POOL_DYNTYPE(rpool->opts) &&
+           (disallow_table(rs->rdr->host,
+           "tables are not supported by pool type") ||
+           disallow_alias(rs->rdr->host,
+           "interface (%s) is not supported by pool type")))
                return (1);
 
        if (rs->pool_opts.key != NULL)
index 2ca907f..4c6b2c8 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: pf.conf.5,v 1.539 2014/10/23 20:38:37 kspillner Exp $
+.\"    $OpenBSD: pf.conf.5,v 1.540 2014/12/19 13:04:08 reyk Exp $
 .\"
 .\" Copyright (c) 2002, Daniel Hartmeier
 .\" Copyright (c) 2003 - 2013 Henning Brauer <henning@openbsd.org>
@@ -28,7 +28,7 @@
 .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: October 23 2014 $
+.Dd $Mdocdate: December 19 2014 $
 .Dt PF.CONF 5
 .Os
 .Sh NAME
@@ -1034,10 +1034,8 @@ from modifying the source port on TCP and UDP packets.
 .El
 .Pp
 When more than one redirection address or a table is specified,
-.Ar round-robin
-and
-.Ar least-states
-are the only permitted pool types.
+.Ar bitmask
+is not permitted as a pool type.
 .Ss Routing
 If a packet matches a rule with one of the following route options set,
 the packet filter will route the packet according to the type of route option.
@@ -1566,10 +1564,8 @@ They can also be used for the redirect address of
 .Ar nat-to
 and
 .Ar rdr-to
-and in the routing options of filter rules, but only for
-.Ar least-states
-and
-.Ar round-robin
+and in the routing options of filter rules, but not for
+.Ar bitmask
 pools.
 .Pp
 Tables can be defined with any of the following
index e08b409..bd7180c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pf_lb.c,v 1.38 2014/12/19 12:31:03 mcbride Exp $ */
+/*     $OpenBSD: pf_lb.c,v 1.39 2014/12/19 13:04:08 reyk Exp $ */
 
 /*
  * Copyright (c) 2001 Daniel Hartmeier
@@ -92,7 +92,7 @@
  * Global variables
  */
 
-void                    pf_hash(struct pf_addr *, struct pf_addr *,
+u_int64_t               pf_hash(struct pf_addr *, struct pf_addr *,
                            struct pf_poolhashkey *, sa_family_t);
 int                     pf_get_sport(struct pf_pdesc *, struct pf_rule *,
                            struct pf_addr *, u_int16_t *, u_int16_t,
@@ -104,10 +104,11 @@ int                        pf_map_addr_sticky(sa_family_t, struct pf_rule *,
                            struct pf_src_node **, struct pf_pool *,
                            enum pf_sn_types);
 
-void
+u_int64_t
 pf_hash(struct pf_addr *inaddr, struct pf_addr *hash,
     struct pf_poolhashkey *key, sa_family_t af)
 {
+       uint64_t res = 0;
 #ifdef INET6
        union {
                uint64_t hash64;
@@ -118,14 +119,16 @@ pf_hash(struct pf_addr *inaddr, struct pf_addr *hash,
        switch (af) {
 #ifdef INET
        case AF_INET:
-               hash->addr32[0] = SipHash24((SIPHASH_KEY *)key,
+               res = SipHash24((SIPHASH_KEY *)key,
                    &inaddr->addr32[0], sizeof(inaddr->addr32[0]));
+               hash->addr32[0] = res;
                break;
 #endif /* INET */
 #ifdef INET6
        case AF_INET6:
-               h.hash64 = SipHash24((SIPHASH_KEY *)key, &inaddr->addr32[0],
+               res = SipHash24((SIPHASH_KEY *)key, &inaddr->addr32[0],
                    4 * sizeof(inaddr->addr32[0]));
+               h.hash64 = res;
                hash->addr32[0] = h.hash32[0];
                hash->addr32[1] = h.hash32[1];
                /*
@@ -137,6 +140,7 @@ pf_hash(struct pf_addr *inaddr, struct pf_addr *hash,
                break;
 #endif /* INET6 */
        }
+       return (res);
 }
 
 int
@@ -337,6 +341,8 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
        u_int16_t                weight;
        u_int64_t                load;
        u_int64_t                cload;
+       u_int64_t                hashidx;
+       int                      cnt;
 
        if (sns[type] == NULL && rpool->opts & PF_POOL_STICKYADDR &&
            (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE &&
@@ -350,10 +356,7 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
 #ifdef INET
                case AF_INET:
                        if (rpool->addr.p.dyn->pfid_acnt4 < 1 &&
-                           ((rpool->opts & PF_POOL_TYPEMASK) !=
-                           PF_POOL_ROUNDROBIN) &&
-                           ((rpool->opts & PF_POOL_TYPEMASK) !=
-                           PF_POOL_LEASTSTATES))
+                           !PF_POOL_DYNTYPE(rpool->opts))
                                return (1);
                        raddr = &rpool->addr.p.dyn->pfid_addr4;
                        rmask = &rpool->addr.p.dyn->pfid_mask4;
@@ -362,10 +365,7 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
 #ifdef INET6
                case AF_INET6:
                        if (rpool->addr.p.dyn->pfid_acnt6 < 1 &&
-                           ((rpool->opts & PF_POOL_TYPEMASK) !=
-                           PF_POOL_ROUNDROBIN) &&
-                           ((rpool->opts & PF_POOL_TYPEMASK) !=
-                           PF_POOL_LEASTSTATES))
+                           !PF_POOL_DYNTYPE(rpool->opts))
                                return (1);
                        raddr = &rpool->addr.p.dyn->pfid_addr6;
                        rmask = &rpool->addr.p.dyn->pfid_mask6;
@@ -373,8 +373,7 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
 #endif /* INET6 */
                }
        } else if (rpool->addr.type == PF_ADDR_TABLE) {
-               if (((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) &&
-                   ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_LEASTSTATES))
+               if (!PF_POOL_DYNTYPE(rpool->opts))
                        return (1); /* unsupported */
        } else {
                raddr = &rpool->addr.v.a.addr;
@@ -389,7 +388,21 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
                PF_POOLMASK(naddr, raddr, rmask, saddr, af);
                break;
        case PF_POOL_RANDOM:
-               if (init_addr != NULL && PF_AZERO(init_addr, af)) {
+               if (rpool->addr.type == PF_ADDR_TABLE) {
+                       cnt = rpool->addr.p.tbl->pfrkt_cnt;
+                       rpool->tblidx = (int)arc4random_uniform(cnt);
+                       memset(&rpool->counter, 0, sizeof(rpool->counter));
+                       if (pfr_pool_get(rpool, &raddr, &rmask, af))
+                               return (1);
+                       PF_ACPY(naddr, &rpool->counter, af);
+               } else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
+                       cnt = rpool->addr.p.dyn->pfid_kt->pfrkt_cnt;
+                       rpool->tblidx = (int)arc4random_uniform(cnt);
+                       memset(&rpool->counter, 0, sizeof(rpool->counter));
+                       if (pfr_pool_get(rpool, &raddr, &rmask, af))
+                               return (1);
+                       PF_ACPY(naddr, &rpool->counter, af);
+               } else if (init_addr != NULL && PF_AZERO(init_addr, af)) {
                        switch (af) {
 #ifdef INET
                        case AF_INET:
@@ -428,8 +441,26 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
                }
                break;
        case PF_POOL_SRCHASH:
-               pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af);
-               PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af);
+               hashidx =
+                   pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af);
+               if (rpool->addr.type == PF_ADDR_TABLE) {
+                       cnt = rpool->addr.p.tbl->pfrkt_cnt;
+                       rpool->tblidx = (int)(hashidx % cnt);
+                       memset(&rpool->counter, 0, sizeof(rpool->counter));
+                       if (pfr_pool_get(rpool, &raddr, &rmask, af))
+                               return (1);
+                       PF_ACPY(naddr, &rpool->counter, af);
+               } else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
+                       cnt = rpool->addr.p.dyn->pfid_kt->pfrkt_cnt;
+                       rpool->tblidx = (int)(hashidx % cnt);
+                       memset(&rpool->counter, 0, sizeof(rpool->counter));
+                       if (pfr_pool_get(rpool, &raddr, &rmask, af))
+                               return (1);
+                       PF_ACPY(naddr, &rpool->counter, af);
+               } else {
+                       PF_POOLMASK(naddr, raddr, rmask,
+                           (struct pf_addr *)&hash, af);
+               }
                break;
        case PF_POOL_ROUNDROBIN:
                if (rpool->addr.type == PF_ADDR_TABLE ||
index 25700e1..08f6a98 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pfvar.h,v 1.404 2014/12/19 05:36:28 tedu Exp $ */
+/*     $OpenBSD: pfvar.h,v 1.405 2014/12/19 13:04:08 reyk Exp $ */
 
 /*
  * Copyright (c) 2001 Daniel Hartmeier
@@ -118,6 +118,12 @@ enum       { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL,
 #define        PF_WSCALE_FLAG          0x80
 #define        PF_WSCALE_MASK          0x0f
 
+#define PF_POOL_DYNTYPE(_o)                                            \
+       ((((_o) & PF_POOL_TYPEMASK) == PF_POOL_ROUNDROBIN) ||           \
+       (((_o) & PF_POOL_TYPEMASK) == PF_POOL_LEASTSTATES) ||           \
+       (((_o) & PF_POOL_TYPEMASK) == PF_POOL_RANDOM) ||                \
+       (((_o) & PF_POOL_TYPEMASK) == PF_POOL_SRCHASH))
+
 #define        PF_LOG                  0x01
 #define        PF_LOG_ALL              0x02
 #define        PF_LOG_SOCKET_LOOKUP    0x04