summaryrefslogtreecommitdiff
path: root/sys/net
diff options
context:
space:
mode:
authorCedric Berger <cedric@cvs.openbsd.org>2003-08-09 14:56:49 +0000
committerCedric Berger <cedric@cvs.openbsd.org>2003-08-09 14:56:49 +0000
commitb7323c0ae2f827695d5f116067f71a7ad66431e4 (patch)
treef6ecc8a86c5cf9a94655c88ee236f4c7979d31bf /sys/net
parent0876dde502ec6049eb887678ab0a3bad64d94127 (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.c63
-rw-r--r--sys/net/pf_ioctl.c24
-rw-r--r--sys/net/pf_table.c124
-rw-r--r--sys/net/pfvar.h10
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 *);