diff options
author | Cedric Berger <cedric@cvs.openbsd.org> | 2003-08-09 14:56:49 +0000 |
---|---|---|
committer | Cedric Berger <cedric@cvs.openbsd.org> | 2003-08-09 14:56:49 +0000 |
commit | b7323c0ae2f827695d5f116067f71a7ad66431e4 (patch) | |
tree | f6ecc8a86c5cf9a94655c88ee236f4c7979d31bf /sys/net | |
parent | 0876dde502ec6049eb887678ab0a3bad64d94127 (diff) |
This patch remove the restriction that tables cannot be used in routing or
redirection rules...
The advantage of using tables in redirection/routing rules is not efficiency,
in fact it will run slower than straight address pools. However, this brings
a lot of flexibility to PF, allowing simple scripts/daemons to add/remove
addresses from redirection/routing pools easily.
This implementation support all table features, including cidr blocks and
negated addresses. So specifying { 10.0.0.0/29 !10.0.0.0 !10.0.0.7 } will
correctly round-robin between the six addresses: .1, .2, .3, .4, .5, .6.
Tables can also be combined with simple addresses, so the following rule
will work as expected: "nat on foo0 -> { 1.1.1.1 <bar> }"
ok henning@ mcbride@
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/pf.c | 63 | ||||
-rw-r--r-- | sys/net/pf_ioctl.c | 24 | ||||
-rw-r--r-- | sys/net/pf_table.c | 124 | ||||
-rw-r--r-- | sys/net/pfvar.h | 10 |
4 files changed, 190 insertions, 31 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c index 9a1554da4a5..01c1d371d97 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.381 2003/08/07 14:20:50 henning Exp $ */ +/* $OpenBSD: pf.c,v 1.382 2003/08/09 14:56:48 cedric Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -176,13 +176,6 @@ struct pf_tag *pf_get_tag(struct mbuf *); int pf_match_tag(struct mbuf *, struct pf_rule *, struct pf_rule *, struct pf_rule *, struct pf_tag *, int *); - -#ifdef INET6 -void pf_poolmask(struct pf_addr *, struct pf_addr*, - struct pf_addr *, struct pf_addr *, u_int8_t); -void pf_addr_inc(struct pf_addr *, sa_family_t); -#endif /* INET6 */ - void pf_hash(struct pf_addr *, struct pf_addr *, struct pf_poolhashkey *, sa_family_t); int pf_map_addr(u_int8_t, struct pf_pool *, @@ -1573,15 +1566,22 @@ pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr, struct pf_addr *naddr, struct pf_addr *init_addr) { unsigned char hash[16]; - struct pf_addr *raddr = &rpool->cur->addr.v.a.addr; - struct pf_addr *rmask = &rpool->cur->addr.v.a.mask; + struct pf_addr *raddr; + struct pf_addr *rmask; + struct pf_pooladdr *acur = rpool->cur; - if (rpool->cur->addr.type == PF_ADDR_NOROUTE || - rpool->cur->addr.type == PF_ADDR_TABLE) + if (rpool->cur->addr.type == PF_ADDR_NOROUTE) return (1); if (rpool->cur->addr.type == PF_ADDR_DYNIFTL && rpool->cur->addr.p.dyn->undefined) return (1); + if (rpool->cur->addr.type == PF_ADDR_TABLE) { + if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) + return (1); /* unsupported */ + } else { + raddr = &rpool->cur->addr.v.a.addr; + rmask = &rpool->cur->addr.v.a.mask; + } switch (rpool->opts & PF_POOL_TYPEMASK) { case PF_POOL_NONE: @@ -1630,19 +1630,36 @@ pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr, PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af); break; case PF_POOL_ROUNDROBIN: - if (pf_match_addr(0, &rpool->cur->addr.v.a.addr, - &rpool->cur->addr.v.a.mask, &rpool->counter, af)) { - PF_ACPY(naddr, &rpool->counter, af); - PF_AINC(&rpool->counter, af); + if (rpool->cur->addr.type == PF_ADDR_TABLE) { + if (!pfr_pool_get(rpool->cur->addr.p.tbl, + &rpool->tblidx, &rpool->counter, + &raddr, &rmask, af)) + goto get_addr; + } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af)) + goto get_addr; + + try_next: + if ((rpool->cur = TAILQ_NEXT(rpool->cur, entries)) == NULL) + rpool->cur = TAILQ_FIRST(&rpool->list); + if (rpool->cur->addr.type == PF_ADDR_TABLE) { + rpool->tblidx = -1; + if (pfr_pool_get(rpool->cur->addr.p.tbl, + &rpool->tblidx, &rpool->counter, + &raddr, &rmask, af)) { + /* table contain no address of type 'af' */ + if (rpool->cur != acur) + goto try_next; + return (1); + } } else { - if ((rpool->cur = - TAILQ_NEXT(rpool->cur, entries)) == NULL) - rpool->cur = TAILQ_FIRST(&rpool->list); - PF_ACPY(naddr, &rpool->cur->addr.v.a.addr, af); - PF_ACPY(&rpool->counter, - &rpool->cur->addr.v.a.addr, af); - PF_AINC(&rpool->counter, af); + raddr = &rpool->cur->addr.v.a.addr; + rmask = &rpool->cur->addr.v.a.mask; + PF_ACPY(&rpool->counter, raddr, af); } + + get_addr: + PF_ACPY(naddr, &rpool->counter, af); + PF_AINC(&rpool->counter, af); break; } diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c index 83d32550c16..62def34e1dd 100644 --- a/sys/net/pf_ioctl.c +++ b/sys/net/pf_ioctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf_ioctl.c,v 1.77 2003/07/31 22:25:54 cedric Exp $ */ +/* $OpenBSD: pf_ioctl.c,v 1.78 2003/08/09 14:56:48 cedric Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -383,6 +383,7 @@ pf_empty_pool(struct pf_palist *poola) while ((empty_pool_pa = TAILQ_FIRST(poola)) != NULL) { pf_dynaddr_remove(&empty_pool_pa->addr); + pf_tbladdr_remove(&empty_pool_pa->addr); TAILQ_REMOVE(poola, empty_pool_pa, entries); pool_put(&pf_pooladdr_pl, empty_pool_pa); } @@ -626,6 +627,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pfioc_rule *pr = (struct pfioc_rule *)addr; struct pf_ruleset *ruleset; struct pf_rule *rule, *tail; + struct pf_pooladdr *pa; int rs_num; ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); @@ -712,6 +714,9 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EINVAL; if (pf_tbladdr_setup(ruleset, &rule->dst.addr)) error = EINVAL; + TAILQ_FOREACH(pa, &pf_pabuf, entries) + if (pf_tbladdr_setup(ruleset, &pa->addr)) + error = EINVAL; pf_mv_pool(&pf_pabuf, &rule->rpool.list); if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) || @@ -1647,7 +1652,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } #endif /* INET6 */ if (pp->addr.addr.type != PF_ADDR_ADDRMASK && - pp->addr.addr.type != PF_ADDR_DYNIFTL) { + pp->addr.addr.type != PF_ADDR_DYNIFTL && + pp->addr.addr.type != PF_ADDR_TABLE) { error = EINVAL; break; } @@ -1717,6 +1723,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } bcopy(pa, &pp->addr, sizeof(struct pf_pooladdr)); pf_dynaddr_copyout(&pp->addr.addr); + pf_tbladdr_copyout(&pp->addr.addr); splx(s); break; } @@ -1724,6 +1731,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCCHANGEADDR: { struct pfioc_pooladdr *pca = (struct pfioc_pooladdr *)addr; struct pf_pooladdr *oldpa = NULL, *newpa = NULL; + struct pf_ruleset *ruleset; if (pca->action < PF_CHANGE_ADD_HEAD || pca->action > PF_CHANGE_REMOVE) { @@ -1731,11 +1739,17 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } if (pca->addr.addr.type != PF_ADDR_ADDRMASK && - pca->addr.addr.type != PF_ADDR_DYNIFTL) { + pca->addr.addr.type != PF_ADDR_DYNIFTL && + pca->addr.addr.type != PF_ADDR_TABLE) { error = EINVAL; break; } + ruleset = pf_find_ruleset(pca->anchor, pca->ruleset); + if (ruleset == NULL) { + error = EBUSY; + break; + } pool = pf_get_pool(pca->anchor, pca->ruleset, 0, pca->r_action, pca->r_num, pca->r_last, 1, 1); if (pool == NULL) { @@ -1772,7 +1786,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } } else newpa->ifp = NULL; - if (pf_dynaddr_setup(&newpa->addr, pca->af)) { + if (pf_dynaddr_setup(&newpa->addr, pca->af) || + pf_tbladdr_setup(ruleset, &newpa->addr)) { pf_dynaddr_remove(&newpa->addr); pool_put(&pf_pooladdr_pl, newpa); error = EINVAL; @@ -1804,6 +1819,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (pca->action == PF_CHANGE_REMOVE) { TAILQ_REMOVE(&pool->list, oldpa, entries); pf_dynaddr_remove(&oldpa->addr); + pf_tbladdr_remove(&oldpa->addr); pool_put(&pf_pooladdr_pl, oldpa); } else { if (oldpa == NULL) diff --git a/sys/net/pf_table.c b/sys/net/pf_table.c index 3844d701413..d5f44375126 100644 --- a/sys/net/pf_table.c +++ b/sys/net/pf_table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf_table.c,v 1.39 2003/07/31 22:25:55 cedric Exp $ */ +/* $OpenBSD: pf_table.c,v 1.40 2003/08/09 14:56:48 cedric Exp $ */ /* * Copyright (c) 2002 Cedric Berger @@ -70,6 +70,10 @@ a2 = tmp; \ } while (0) +#define SUNION2PF(su, af) (((af)==AF_INET) ? \ + (struct pf_addr *)&(su)->sin.sin_addr : \ + (struct pf_addr *)&(su)->sin6.sin6_addr) + #define AF_BITS(af) (((af)==AF_INET)?32:128) #define ADDR_NETWORK(ad) ((ad)->pfra_net < AF_BITS((ad)->pfra_af)) #define KENTRY_NETWORK(ke) ((ke)->pfrke_net < AF_BITS((ke)->pfrke_af)) @@ -86,18 +90,21 @@ struct pfr_walktree { PFRW_SWEEP, PFRW_ENQUEUE, PFRW_GET_ADDRS, - PFRW_GET_ASTATS + PFRW_GET_ASTATS, + PFRW_POOL_GET } pfrw_op; union { struct pfr_addr *pfrw1_addr; struct pfr_astats *pfrw1_astats; struct pfr_kentryworkq *pfrw1_workq; + struct pfr_kentry *pfrw1_kentry; } pfrw_1; int pfrw_free; }; #define pfrw_addr pfrw_1.pfrw1_addr #define pfrw_astats pfrw_1.pfrw1_astats #define pfrw_workq pfrw_1.pfrw1_workq +#define pfrw_kentry pfrw_1.pfrw1_kentry #define pfrw_cnt pfrw_free #define senderr(e) do { rv = (e); goto _bad; } while (0) @@ -106,6 +113,8 @@ struct pool pfr_ktable_pl; struct pool pfr_kentry_pl; struct sockaddr_in pfr_sin; struct sockaddr_in6 pfr_sin6; +union sockaddr_union pfr_mask; +struct pf_addr pfr_ffaddr; void pfr_copyout_addr(struct pfr_addr *, struct pfr_kentry *ke); @@ -151,6 +160,7 @@ void pfr_clean_node_mask(struct pfr_ktable *, int pfr_table_count(struct pfr_table *, int); int pfr_skip_table(struct pfr_table *, struct pfr_ktable *, int); +struct pfr_kentry *pfr_kentry_byidx(struct pfr_ktable *, int, int); RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); @@ -171,6 +181,8 @@ pfr_initialize(void) pfr_sin.sin_family = AF_INET; pfr_sin6.sin6_len = sizeof(pfr_sin6); pfr_sin6.sin6_family = AF_INET6; + + memset(&pfr_ffaddr, 0xff, sizeof(pfr_ffaddr)); } int @@ -984,6 +996,14 @@ pfr_walktree(struct radix_node *rn, void *arg) w->pfrw_astats++; } break; + case PFRW_POOL_GET: + if (ke->pfrke_not) + break; /* negative entries are ignored */ + if (!w->pfrw_cnt--) { + w->pfrw_kentry = ke; + return (1); /* finish search */ + } + break; } return (0); } @@ -1896,3 +1916,103 @@ pfr_detach_table(struct pfr_ktable *kt) else if (!--kt->pfrkt_refcnt[PFR_REFCNT_RULE]) pfr_setflags_ktable(kt, kt->pfrkt_flags&~PFR_TFLAG_REFERENCED); } + +int +pfr_pool_get(struct pfr_ktable *kt, int *pidx, struct pf_addr *counter, + struct pf_addr **raddr, struct pf_addr **rmask, sa_family_t af) +{ + struct pfr_kentry *ke, *ke2; + struct pf_addr *addr; + union sockaddr_union mask; + int idx = -1, use_counter = 0; + + addr = (af == AF_INET) ? (struct pf_addr *)&pfr_sin.sin_addr : + (struct pf_addr *)&pfr_sin6.sin6_addr; + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) + kt = kt->pfrkt_root; + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (-1); + + if (pidx != NULL) + idx = *pidx; + if (counter != NULL && idx >= 0) + use_counter = 1; + if (idx < 0) + idx = 0; + +_next_block: + ke = pfr_kentry_byidx(kt, idx, af); + if (ke == NULL) + return (1); + pfr_prepare_network(&pfr_mask, af, ke->pfrke_net); + *raddr = SUNION2PF(&ke->pfrke_sa, af); + *rmask = SUNION2PF(&pfr_mask, af); + + if (use_counter) { + /* is supplied address within block? */ + if(!PF_MATCHA(0, *raddr, *rmask, counter, af)) { + /* no, go to next block in table */ + idx++; + use_counter = 0; + goto _next_block; + } + PF_ACPY(addr, counter, af); + } else { + /* use first address of block */ + PF_ACPY(addr, *raddr, af); + } + + if (!KENTRY_NETWORK(ke)) { + /* this is a single IP address - no possible nested block */ + PF_ACPY(counter, addr, af); + *pidx = idx; + return (0); + } + for(;;) { + /* we don't want to use a nested block */ + ke2 = (struct pfr_kentry *)(af == AF_INET ? + rn_match(&pfr_sin, kt->pfrkt_ip4) : + rn_match(&pfr_sin6, kt->pfrkt_ip6)); + /* no need to check KENTRY_RNF_ROOT() here */ + if (ke2 == ke) { + /* lookup return the same block - perfect */ + PF_ACPY(counter, addr, af); + *pidx = idx; + return (0); + } + + /* we need to increase the counter past the nested block */ + pfr_prepare_network(&mask, AF_INET, ke2->pfrke_net); + PF_POOLMASK(addr, addr, SUNION2PF(&mask, af), &pfr_ffaddr, af); + PF_AINC(addr, af); + if(!PF_MATCHA(0, *raddr, *rmask, addr, af)) { + /* ok, we reached the end of our main block */ + /* go to next block in table */ + idx++; + use_counter = 0; + goto _next_block; + } + } +} + +struct pfr_kentry * +pfr_kentry_byidx(struct pfr_ktable *kt, int idx, int af) +{ + struct pfr_walktree w; + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_POOL_GET; + w.pfrw_cnt = idx; + + switch(af) { + case AF_INET: + rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); + return w.pfrw_kentry; + case AF_INET6: + rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); + return w.pfrw_kentry; + default: + return NULL; + } +} + diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 9c4d4a4e3cf..c4e2c19d8db 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar.h,v 1.165 2003/08/07 14:20:50 henning Exp $ */ +/* $OpenBSD: pfvar.h,v 1.166 2003/08/09 14:56:48 cedric Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -316,6 +316,7 @@ struct pf_pool { struct pf_pooladdr *cur; struct pf_poolhashkey key; struct pf_addr counter; + int tblidx; u_int16_t proxy_port[2]; u_int8_t port_op; u_int8_t opts; @@ -1053,7 +1054,10 @@ int pf_test(int, struct ifnet *, struct mbuf **); #ifdef INET6 int pf_test6(int, struct ifnet *, struct mbuf **); -#endif /* INET */ +void pf_poolmask(struct pf_addr *, struct pf_addr*, + struct pf_addr *, struct pf_addr *, u_int8_t); +void pf_addr_inc(struct pf_addr *, sa_family_t); +#endif /* INET6 */ void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *, sa_family_t); @@ -1086,6 +1090,8 @@ void pfr_initialize(void); int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t); void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, sa_family_t, u_int64_t, int, int, int); +int pfr_pool_get(struct pfr_ktable *, int *, struct pf_addr *, + struct pf_addr **, struct pf_addr **, sa_family_t); struct pfr_ktable * pfr_attach_table(struct pf_ruleset *, char *); void pfr_detach_table(struct pfr_ktable *); |