Hi!

pf currently only supports the round-robin and least-states methods
when using dynamic address pools like tables or interface pools.  The
following diff adds support for source-hash and random with dynamic
pools.  source-hash can be used in some cases as an alternative to
"sticky-address" to get mapping stability without the extra memory
cost.  In contrast to sticky-address, it can also guarantee the same
mapping after reloading or flushing pf.  random is useful for some
security applications, testing and kind of the counterpart to the
"prob" keyword.

I already got positive feedback for this diff from mcbride@ and jmc@,
but I'd like to get feedback and testing from a few more people here
on tech@.  And some OKs from OpenBSD hackers, of course :-)

btw.: This will also fix the first "relayd: add new load balancing
scheduling algorithms" diff that I sent to tech@ one month ago.

reyk

Index: share/man/man5/pf.conf.5
===================================================================
RCS file: /cvs/src/share/man/man5/pf.conf.5,v
retrieving revision 1.523
diff -u -p -r1.523 pf.conf.5
--- share/man/man5/pf.conf.5    18 Oct 2012 15:18:56 -0000      1.523
+++ share/man/man5/pf.conf.5    14 Nov 2012 14:07:30 -0000
@@ -1040,10 +1040,8 @@ from modifying the source port on TCP an
 .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.
@@ -1724,10 +1722,8 @@ They can also be used for the redirect a
 .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: sys/net/pf_lb.c
===================================================================
RCS file: /cvs/src/sys/net/pf_lb.c,v
retrieving revision 1.21
diff -u -p -r1.21 pf_lb.c
--- sys/net/pf_lb.c     9 Jul 2012 15:20:57 -0000       1.21
+++ sys/net/pf_lb.c     14 Nov 2012 14:07:31 -0000
@@ -99,7 +99,7 @@
  * Global variables
  */
 
-void                    pf_hash(struct pf_addr *, struct pf_addr *,
+u_int32_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,
@@ -124,7 +124,7 @@ int                  pf_islinklocal(sa_family_t, struc
 /*
  * hash function based on bridge_hash in if_bridge.c
  */
-void
+u_int32_t
 pf_hash(struct pf_addr *inaddr, struct pf_addr *hash,
     struct pf_poolhashkey *key, sa_family_t af)
 {
@@ -137,7 +137,7 @@ pf_hash(struct pf_addr *inaddr, struct p
                b += key->key32[1];
                mix(a, b, c);
                hash->addr32[0] = c + key->key32[2];
-               break;
+               return (hash->addr32[0]);
 #endif /* INET */
 #ifdef INET6
        case AF_INET6:
@@ -160,9 +160,10 @@ pf_hash(struct pf_addr *inaddr, struct p
                c += key->key32[3];
                mix(a, b, c);
                hash->addr32[3] = c;
-               break;
+               return (c);
 #endif /* INET6 */
        }
+       return (0);
 }
 
 int
@@ -258,8 +259,8 @@ pf_get_sport(struct pf_pdesc *pd, struct
                            &init_addr, sn, &r->nat, PF_SN_NAT))
                                return (1);
                        break;
-               case PF_POOL_NONE:
                case PF_POOL_SRCHASH:
+               case PF_POOL_NONE:
                case PF_POOL_BITMASK:
                default:
                        return (1);
@@ -290,6 +291,8 @@ pf_map_addr(sa_family_t af, struct pf_ru
        u_int16_t                weight;
        u_int64_t                load;
        u_int64_t                cload;
+       u_int32_t                hashidx;
+       int                      cnt;
 
        if (sns[type] == NULL && rpool->opts & PF_POOL_STICKYADDR &&
            (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
@@ -321,10 +324,7 @@ pf_map_addr(sa_family_t af, struct pf_ru
 #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;
@@ -333,10 +333,7 @@ pf_map_addr(sa_family_t af, struct pf_ru
 #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;
@@ -344,8 +341,7 @@ pf_map_addr(sa_family_t af, struct pf_ru
 #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;
@@ -360,7 +356,27 @@ pf_map_addr(sa_family_t af, struct pf_ru
                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);
+                       bzero(naddr, sizeof(*naddr));
+                       if (pfr_pool_get(rpool->addr.p.tbl,
+                           &rpool->tblidx, naddr,
+                           &raddr, &rmask, &rpool->kif,
+                           &rpool->states, &rpool->weight,
+                           &rpool->curweight, af, NULL))
+                               return (1);
+               } else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
+                       cnt = rpool->addr.p.dyn->pfid_kt->pfrkt_cnt;
+                       rpool->tblidx = (int)arc4random_uniform(cnt);
+                       bzero(naddr, sizeof(*naddr));
+                       if (pfr_pool_get(rpool->addr.p.dyn->pfid_kt,
+                           &rpool->tblidx, naddr,
+                           &raddr, &rmask, &rpool->kif,
+                           &rpool->states, &rpool->weight,
+                           &rpool->curweight, af, pf_islinklocal))
+                               return (1);
+               } else if (init_addr != NULL && PF_AZERO(init_addr, af)) {
                        switch (af) {
 #ifdef INET
                        case AF_INET:
@@ -392,15 +408,38 @@ pf_map_addr(sa_family_t af, struct pf_ru
                        }
                        PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
                        PF_ACPY(init_addr, naddr, af);
-
                } else {
                        PF_AINC(&rpool->counter, af);
                        PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
                }
                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);
+                       bzero(naddr, sizeof(*naddr));
+                       if (pfr_pool_get(rpool->addr.p.tbl,
+                           &rpool->tblidx, naddr,
+                           &raddr, &rmask, &rpool->kif,
+                           &rpool->states, &rpool->weight,
+                           &rpool->curweight, af, NULL))
+                               return (1);
+               } else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
+                       cnt = rpool->addr.p.dyn->pfid_kt->pfrkt_cnt;
+                       rpool->tblidx = (int)(hashidx % cnt);
+                       bzero(naddr, sizeof(*naddr));
+                       if (pfr_pool_get(rpool->addr.p.dyn->pfid_kt,
+                           &rpool->tblidx, naddr,
+                           &raddr, &rmask, &rpool->kif,
+                           &rpool->states, &rpool->weight,
+                           &rpool->curweight, af, pf_islinklocal))
+                               return (1);
+               } else {
+                       PF_POOLMASK(naddr, raddr, rmask,
+                           (struct pf_addr *)&hash, af);
+               }
                break;
        case PF_POOL_ROUNDROBIN:
                if (rpool->addr.type == PF_ADDR_TABLE) {
Index: sys/net/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.374
diff -u -p -r1.374 pfvar.h
--- sys/net/pfvar.h     6 Nov 2012 12:32:41 -0000       1.374
+++ sys/net/pfvar.h     14 Nov 2012 14:07:31 -0000
@@ -118,6 +118,12 @@ enum       { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE
 #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
Index: sbin/pfctl/parse.y
===================================================================
RCS file: /cvs/src/sbin/pfctl/parse.y,v
retrieving revision 1.620
diff -u -p -r1.620 parse.y
--- sbin/pfctl/parse.y  18 Oct 2012 15:18:57 -0000      1.620
+++ sbin/pfctl/parse.y  14 Nov 2012 14:07:32 -0000
@@ -2021,13 +2021,9 @@ pfrule           : action dir logquick interface 
                                    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;
                                        }
                                }
@@ -4728,10 +4724,8 @@ collapse_redirspec(struct pf_pool *rpool
                return (0);
        } else {                /* more than one address */
                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);
                }
@@ -4811,12 +4805,11 @@ apply_redirspec(struct pf_pool *rpool, s
            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(rs->pool_opts.type) &&
+           (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)

Reply via email to