On Wed, Nov 14, 2012 at 03:39:48PM +0100, Reyk Floeter wrote:
> 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.
> 

Ok, here are some examples that didn't work before.  I'm using static
hash keys for source-hash in this example to keep the same mapping
after reboot or a reload of the pf ruleset.

table <hosts> { 127.0.0.2 127.0.0.3 127.0.0.4 127.0.0.5 127.0.0.6 127.0.0.7
        127.0.0.8 127.0.0.9 127.0.0.10 127.0.0.11 127.0.0.12 127.0.0.13
        127.0.0.14 127.0.0.15 }
pass in on em0 proto tcp to port 81 rdr-to <hosts> port 80 random
pass in on em0 proto tcp to port 82 \
        rdr-to <hosts> port 80 source-hash 0xad493fe9ddaebac146e0e8650f56b2ef
pass in on em0 proto tcp to port 83 rdr-to (lo1) port 80 random
pass in on em0 proto tcp to port 84 \
        rdr-to (lo1) port 80 source-hash 0x53c63f4af63c866bacf24d61516fbc0e

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