diff options
author | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2003-12-15 00:02:05 +0000 |
---|---|---|
committer | Ryan Thomas McBride <mcbride@cvs.openbsd.org> | 2003-12-15 00:02:05 +0000 |
commit | bcbefdbeb6961a98675a03e10371e908592d2742 (patch) | |
tree | 44fc5938d025a365526a21723a1004d25f125611 /sys | |
parent | 7177de71616eff6b6f4d44f5b1c99fe17c82545e (diff) |
Add support to track stateful connections by source ip. This allows us
to:
- Ensure that clients get a consistent IP mapping with load-balanced
translation/routing rules
- Limit the number of simultaneous connections a client can make
- Limit the number of clients which can connect through a rule
ok dhartmei@ deraadt@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/pf.c | 661 | ||||
-rw-r--r-- | sys/net/pf_ioctl.c | 97 | ||||
-rw-r--r-- | sys/net/pfvar.h | 62 |
3 files changed, 648 insertions, 172 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c index 7a82b833300..0392850a2fa 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.407 2003/12/12 20:05:45 cedric Exp $ */ +/* $OpenBSD: pf.c,v 1.408 2003/12/15 00:02:03 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -107,7 +107,7 @@ u_int32_t ticket_pabuf; struct timeout pf_expire_to; /* expire timeout */ -struct pool pf_rule_pl, pf_addr_pl; +struct pool pf_src_tree_pl, pf_rule_pl, pf_addr_pl; struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl; void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t); @@ -138,7 +138,7 @@ struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t, int); struct pf_rule *pf_get_translation(struct pf_pdesc *, struct mbuf *, - int, int, struct ifnet *, + int, int, struct ifnet *, struct pf_src_node **, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t *); @@ -175,16 +175,16 @@ int pf_test_state_other(struct pf_state **, int, struct ifnet *, struct pf_pdesc *); 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 *); + struct pf_rule *, struct pf_tag *, int *); 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 *, +int pf_map_addr(u_int8_t, struct pf_rule *, struct pf_addr *, struct pf_addr *, - struct pf_addr *); -int pf_get_sport(sa_family_t, u_int8_t, struct pf_pool *, + struct pf_addr *, struct pf_src_node **); +int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, struct pf_addr *, struct pf_addr *, u_int16_t, - struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t); + struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t, + struct pf_src_node **); void pf_route(struct mbuf **, struct pf_rule *, int, struct ifnet *, struct pf_state *); void pf_route6(struct mbuf **, struct pf_rule *, int, @@ -235,20 +235,67 @@ struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = (s)->lan.addr.addr32[3] != (s)->gwy.addr.addr32[3])) || \ (s)->lan.port != (s)->gwy.port +static __inline int pf_src_compare(struct pf_src_node *, struct pf_src_node *); static __inline int pf_state_compare_lan_ext(struct pf_state *, - struct pf_state *); + struct pf_state *); static __inline int pf_state_compare_ext_gwy(struct pf_state *, - struct pf_state *); + struct pf_state *); +struct pf_src_tree tree_src_tracking; struct pf_state_tree_lan_ext tree_lan_ext; struct pf_state_tree_ext_gwy tree_ext_gwy; +RB_GENERATE(pf_src_tree, pf_src_node, entry, pf_src_compare); RB_GENERATE(pf_state_tree_lan_ext, pf_state, entry_lan_ext, pf_state_compare_lan_ext); RB_GENERATE(pf_state_tree_ext_gwy, pf_state, entry_ext_gwy, pf_state_compare_ext_gwy); static __inline int +pf_src_compare(struct pf_src_node *a, struct pf_src_node *b) +{ + int diff; + + if (a->rule.ptr > b->rule.ptr) + return (1); + if (a->rule.ptr < b->rule.ptr) + return (-1); + if ((diff = a->af - b->af) != 0) + return (diff); + switch (a->af) { +#ifdef INET + case AF_INET: + if (a->addr.addr32[0] > b->addr.addr32[0]) + return (1); + if (a->addr.addr32[0] < b->addr.addr32[0]) + return (-1); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (a->addr.addr32[3] > b->addr.addr32[3]) + return (1); + if (a->addr.addr32[3] < b->addr.addr32[3]) + return (-1); + if (a->addr.addr32[2] > b->addr.addr32[2]) + return (1); + if (a->addr.addr32[2] < b->addr.addr32[2]) + return (-1); + if (a->addr.addr32[1] > b->addr.addr32[1]) + return (1); + if (a->addr.addr32[1] < b->addr.addr32[1]) + return (-1); + if (a->addr.addr32[0] > b->addr.addr32[0]) + return (1); + if (a->addr.addr32[0] < b->addr.addr32[0]) + return (-1); + break; +#endif /* INET6 */ + } + return (0); +} + +static __inline int pf_state_compare_lan_ext(struct pf_state *a, struct pf_state *b) { int diff; @@ -428,6 +475,61 @@ pf_find_state(struct pf_state *key, u_int8_t tree) } int +pf_insert_src_node(struct pf_src_node **sn, struct pf_rule *rule, + struct pf_addr *src, sa_family_t af) +{ + struct pf_src_node k; + + if (*sn == NULL) { + k.af = af; + PF_ACPY(&k.addr, src, af); + if (rule->rule_flag & PFRULE_RULESRCTRACK || + rule->rpool.opts & PF_POOL_STICKYADDR) + k.rule.ptr = rule; + else + k.rule.ptr = NULL; + pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; + *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k); + } + if (*sn == NULL) { + if (!rule->max_src_nodes || + rule->src_nodes < rule->max_src_nodes) + (*sn) = pool_get(&pf_src_tree_pl, PR_NOWAIT); + if ((*sn) == NULL) + return (-1); + bzero(*sn, sizeof(struct pf_src_node)); + (*sn)->af = af; + if (rule->rule_flag & PFRULE_RULESRCTRACK || + rule->rpool.opts & PF_POOL_STICKYADDR) + (*sn)->rule.ptr = rule; + else + (*sn)->rule.ptr = NULL; + PF_ACPY(&(*sn)->addr, src, af); + if (RB_INSERT(pf_src_tree, + &tree_src_tracking, *sn) != NULL) { + if (pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: src_tree insert failed: "); + pf_print_host(&(*sn)->addr, 0, af); + printf("\n"); + } + pool_put(&pf_src_tree_pl, *sn); + return (-1); + } + (*sn)->creation = time.tv_sec; + (*sn)->ruletype = rule->action; + if ((*sn)->rule.ptr != NULL) + (*sn)->rule.ptr->src_nodes++; + pf_status.scounters[SCNT_SRC_NODE_INSERT]++; + pf_status.src_nodes++; + } else { + if (rule->max_src_states && + (*sn)->states >= rule->max_src_states) + return (-1); + } + return (0); +} + +int pf_insert_state(struct pf_state *state) { /* Thou MUST NOT insert multiple duplicate keys */ @@ -445,6 +547,7 @@ pf_insert_state(struct pf_state *state) state->af); printf("\n"); } + pf_src_tree_remove_state(state); return (-1); } @@ -463,6 +566,7 @@ pf_insert_state(struct pf_state *state) printf("\n"); } RB_REMOVE(pf_state_tree_lan_ext, &tree_lan_ext, state); + pf_src_tree_remove_state(state); return (-1); } @@ -483,6 +587,7 @@ pf_purge_timeout(void *arg) s = splsoftnet(); pf_purge_expired_states(); pf_purge_expired_fragments(); + pf_purge_expired_src_nodes(); splx(s); timeout_add(to, pf_default_rule.timeout[PFTM_INTERVAL] * hz); @@ -525,6 +630,52 @@ pf_state_expires(const struct pf_state *state) } void +pf_purge_expired_src_nodes(void) +{ + struct pf_src_node *cur, *next; + + for (cur = RB_MIN(pf_src_tree, &tree_src_tracking); cur; cur = next) { + next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur); + + if (cur->states <= 0 && cur->expire <= time.tv_sec) { + if (cur->rule.ptr != NULL) { + cur->rule.ptr->src_nodes--; + if (cur->rule.ptr->states <= 0 && + cur->rule.ptr->max_src_nodes <= 0) + pf_rm_rule(NULL, cur->rule.ptr); + } + RB_REMOVE(pf_src_tree, &tree_src_tracking, cur); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, cur); + } + } +} + +void +pf_src_tree_remove_state(struct pf_state *s) +{ + u_int32_t timeout; + + if (s->src_node != NULL) { + if (--s->src_node->states <= 0) { + timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; + if (!timeout) + timeout = pf_default_rule.timeout[PFTM_SRC_NODE]; + s->src_node->expire = time.tv_sec + timeout; + } + } + if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) { + if (--s->nat_src_node->states <= 0) { + timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; + if (!timeout) + timeout = pf_default_rule.timeout[PFTM_SRC_NODE]; + s->nat_src_node->expire = time.tv_sec + timeout; + } + } +} + +void pf_purge_expired_states(void) { struct pf_state *cur, *next; @@ -545,10 +696,13 @@ pf_purge_expired_states(void) #if NPFSYNC pfsync_delete_state(cur); #endif - if (--cur->rule.ptr->states <= 0) + pf_src_tree_remove_state(cur); + if (--cur->rule.ptr->states <= 0 && + cur->rule.ptr->src_nodes <= 0) pf_rm_rule(NULL, cur->rule.ptr); if (cur->nat_rule.ptr != NULL) - if (--cur->nat_rule.ptr->states <= 0) + if (--cur->nat_rule.ptr->states <= 0 && + cur->nat_rule.ptr->src_nodes <= 0) pf_rm_rule(NULL, cur->nat_rule.ptr); if (cur->anchor.ptr != NULL) if (--cur->anchor.ptr->states <= 0) @@ -1319,8 +1473,8 @@ pf_get_tag(struct mbuf *m) } int -pf_match_tag(struct mbuf *m, struct pf_rule *r, struct pf_rule *nat, - struct pf_rule *rdr, struct pf_tag *pftag, int *tag) +pf_match_tag(struct mbuf *m, struct pf_rule *r, struct pf_rule *nat_rule, + struct pf_tag *pftag, int *tag) { if (*tag == -1) { /* find mbuf tag */ pftag = pf_get_tag(m); @@ -1328,10 +1482,8 @@ pf_match_tag(struct mbuf *m, struct pf_rule *r, struct pf_rule *nat, *tag = pftag->tag; else *tag = 0; - if (nat != NULL && nat->tag) - *tag = nat->tag; - if (rdr != NULL && rdr->tag) - *tag = rdr->tag; + if (nat_rule != NULL && nat_rule->tag) + *tag = nat_rule->tag; } return ((!r->match_tag_not && r->match_tag == *tag) || @@ -1415,7 +1567,7 @@ pf_poolmask(struct pf_addr *naddr, struct pf_addr *raddr, } void -pf_addr_inc(struct pf_addr *addr, u_int8_t af) +pf_addr_inc(struct pf_addr *addr, sa_family_t af) { switch (af) { #ifdef INET @@ -1504,13 +1656,39 @@ pf_hash(struct pf_addr *inaddr, struct pf_addr *hash, } int -pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr, - struct pf_addr *naddr, struct pf_addr *init_addr) +pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, + struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sn) { unsigned char hash[16]; - struct pf_addr *raddr; - struct pf_addr *rmask; - struct pf_pooladdr *acur = rpool->cur; + struct pf_pool *rpool = &r->rpool; + struct pf_addr *raddr = &rpool->cur->addr.v.a.addr; + struct pf_addr *rmask = &rpool->cur->addr.v.a.mask; + struct pf_pooladdr *acur = rpool->cur; + struct pf_src_node k; + + if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR && + (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { + k.af = af; + PF_ACPY(&k.addr, saddr, af); + if (r->rule_flag & PFRULE_RULESRCTRACK || + r->rpool.opts & PF_POOL_STICKYADDR) + k.rule.ptr = r; + else + k.rule.ptr = NULL; + pf_status.scounters[SCNT_SRC_NODE_SEARCH]++; + *sn = RB_FIND(pf_src_tree, &tree_src_tracking, &k); + if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) { + PF_ACPY(naddr, &(*sn)->raddr, af); + if (pf_status.debug >= PF_DEBUG_MISC) { + printf("pf_map_addr: src tracking maps "); + pf_print_host(&k.addr, 0, af); + printf(" to "); + pf_print_host(naddr, 0, af); + printf("\n"); + } + return(0); + } + } if (rpool->cur->addr.type == PF_ADDR_NOROUTE) return (1); @@ -1604,10 +1782,12 @@ pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr, PF_AINC(&rpool->counter, af); break; } + if (*sn != NULL) + PF_ACPY(&(*sn)->raddr, naddr, af); if (pf_status.debug >= PF_DEBUG_MISC && (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { - printf("pf_map_addr: selected address: "); + printf("pf_map_addr: selected address "); pf_print_host(naddr, 0, af); printf("\n"); } @@ -1616,16 +1796,17 @@ pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr, } int -pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_pool *rpool, +pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport, - struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high) + struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high, + struct pf_src_node **sn) { struct pf_state key; struct pf_addr init_addr; u_int16_t cut; bzero(&init_addr, sizeof(init_addr)); - if (pf_map_addr(af, rpool, saddr, naddr, &init_addr)) + if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) return (1); do { @@ -1683,10 +1864,10 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_pool *rpool, } } - switch (rpool->opts & PF_POOL_TYPEMASK) { + switch (r->rpool.opts & PF_POOL_TYPEMASK) { case PF_POOL_RANDOM: case PF_POOL_ROUNDROBIN: - if (pf_map_addr(af, rpool, saddr, naddr, &init_addr)) + if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) return (1); break; case PF_POOL_NONE: @@ -1771,7 +1952,7 @@ pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, struct pf_rule * pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, - struct ifnet *ifp, + struct ifnet *ifp, struct pf_src_node **sn, struct pf_addr *saddr, u_int16_t sport, struct pf_addr *daddr, u_int16_t dport, struct pf_addr *naddr, u_int16_t *nport) @@ -1800,9 +1981,9 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, return (NULL); break; case PF_NAT: - if (pf_get_sport(pd->af, pd->proto, &r->rpool, saddr, + if (pf_get_sport(pd->af, pd->proto, r, saddr, daddr, dport, naddr, nport, r->rpool.proxy_port[0], - r->rpool.proxy_port[1])) { + r->rpool.proxy_port[1], sn)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: NAT proxy port allocation " "(%u-%u) failed\n", @@ -1837,7 +2018,7 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, } break; case PF_RDR: { - if (pf_map_addr(r->af, &r->rpool, saddr, naddr, NULL)) + if (pf_map_addr(r->af, r, saddr, naddr, NULL, sn)) return (NULL); if (r->rpool.proxy_port[1]) { @@ -2069,15 +2250,13 @@ pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) switch (s->af) { #ifdef INET case AF_INET: - pf_map_addr(AF_INET, &r->rpool, saddr, - &s->rt_addr, NULL); + pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, &s->nat_src_node); s->rt_ifp = r->rpool.cur->ifp; break; #endif /* INET */ #ifdef INET6 case AF_INET6: - pf_map_addr(AF_INET6, &r->rpool, saddr, - &s->rt_addr, NULL); + pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, &s->nat_src_node); s->rt_ifp = r->rpool.cur->ifp; break; #endif /* INET6 */ @@ -2089,7 +2268,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, struct ifnet *ifp, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) { - struct pf_rule *nat = NULL, *rdr = NULL; + struct pf_rule *nr = NULL; struct pf_addr *saddr = pd->src, *daddr = pd->dst; struct tcphdr *th = pd->hdr.tcp; u_int16_t bport, nport = 0; @@ -2099,6 +2278,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, gid_t gid; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; + struct pf_src_node *nsn = NULL; u_short reason; int rewrite = 0; struct pf_tag *pftag = NULL; @@ -2110,30 +2290,30 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, if (direction == PF_OUT) { bport = nport = th->th_sport; /* check outgoing packet for BINAT/NAT */ - if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp, + if ((nr = pf_get_translation(pd, m, off, PF_OUT, ifp, &nsn, saddr, th->th_sport, daddr, th->th_dport, &pd->naddr, &nport)) != NULL) { PF_ACPY(&pd->baddr, saddr, af); pf_change_ap(saddr, &th->th_sport, pd->ip_sum, &th->th_sum, &pd->naddr, nport, 0, af); rewrite++; - if (nat->natpass) + if (nr->natpass) r = NULL; - pd->nat_rule = nat; + pd->nat_rule = nr; } } else { bport = nport = th->th_dport; /* check incoming packet for BINAT/RDR */ - if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr, - th->th_sport, daddr, th->th_dport, + if ((nr = pf_get_translation(pd, m, off, PF_IN, ifp, &nsn, + saddr, th->th_sport, daddr, th->th_dport, &pd->naddr, &nport)) != NULL) { PF_ACPY(&pd->baddr, daddr, af); pf_change_ap(daddr, &th->th_dport, pd->ip_sum, &th->th_sum, &pd->naddr, nport, 0, af); rewrite++; - if (rdr->natpass) + if (nr->natpass) r = NULL; - pd->nat_rule = rdr; + pd->nat_rule = nr; } } @@ -2174,8 +2354,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], gid)) r = TAILQ_NEXT(r, entries); - else if (r->match_tag && - !pf_match_tag(m, r, nat, rdr, pftag, &tag)) + else if (r->match_tag && !pf_match_tag(m, r, nr, pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->anchorname[0] && r->anchor == NULL) r = TAILQ_NEXT(r, entries); @@ -2217,14 +2396,16 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, (r->rule_flag & PFRULE_RETURNICMP) || (r->rule_flag & PFRULE_RETURN))) { /* undo NAT changes, if they have taken place */ - if (nat != NULL) { - pf_change_ap(saddr, &th->th_sport, pd->ip_sum, - &th->th_sum, &pd->baddr, bport, 0, af); - rewrite++; - } else if (rdr != NULL) { - pf_change_ap(daddr, &th->th_dport, pd->ip_sum, - &th->th_sum, &pd->baddr, bport, 0, af); - rewrite++; + if (nr != NULL) { + if (direction == PF_OUT) { + pf_change_ap(saddr, &th->th_sport, pd->ip_sum, + &th->th_sum, &pd->baddr, bport, 0, af); + rewrite++; + } else { + pf_change_ap(daddr, &th->th_dport, pd->ip_sum, + &th->th_sum, &pd->baddr, bport, 0, af); + rewrite++; + } } if (((r->rule_flag & PFRULE_RETURNRST) || (r->rule_flag & PFRULE_RETURN)) && @@ -2255,16 +2436,45 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, return (PF_DROP); } - if (r->keep_state || nat != NULL || rdr != NULL || + if (r->keep_state || nr != NULL || (pd->flags & PFDESC_TCP_NORM)) { /* create new state */ u_int16_t len; struct pf_state *s = NULL; + struct pf_src_node *sn = NULL; len = pd->tot_len - off - (th->th_off << 2); - if (!r->max_states || r->states < r->max_states) - s = pool_get(&pf_state_pl, PR_NOWAIT); + + /* check maximums */ + if (r->max_states && (r->states >= r->max_states)) + goto cleanup; + /* src node for flter rule */ + if ((r->rule_flag & PFRULE_SRCTRACK || + r->rpool.opts & PF_POOL_STICKYADDR) && + pf_insert_src_node(&sn, r, saddr, af) != 0) + goto cleanup; + /* src node for translation rule */ + if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && + ((direction == PF_OUT && + pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || + (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) + goto cleanup; + s = pool_get(&pf_state_pl, PR_NOWAIT); if (s == NULL) { +cleanup: + if (sn != NULL && sn->states == 0 && sn->expire == 0) { + RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, sn); + } + if (nsn != sn && nsn != NULL && nsn->states == 0 && + nsn->expire == 0) { + RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, nsn); + } REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } @@ -2273,10 +2483,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, if (a != NULL) a->states++; s->rule.ptr = r; - if (nat != NULL) - s->nat_rule.ptr = nat; - else - s->nat_rule.ptr = rdr; + s->nat_rule.ptr = nr; if (s->nat_rule.ptr != NULL) s->nat_rule.ptr->states++; s->anchor.ptr = a; @@ -2290,7 +2497,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, s->gwy.port = th->th_sport; /* sport */ PF_ACPY(&s->ext.addr, daddr, af); s->ext.port = th->th_dport; - if (nat != NULL) { + if (nr != NULL) { PF_ACPY(&s->lan.addr, &pd->baddr, af); s->lan.port = bport; } else { @@ -2302,7 +2509,7 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, s->lan.port = th->th_dport; PF_ACPY(&s->ext.addr, saddr, af); s->ext.port = th->th_sport; - if (rdr != NULL) { + if (nr != NULL) { PF_ACPY(&s->gwy.addr, &pd->baddr, af); s->gwy.port = bport; } else { @@ -2345,10 +2552,19 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, s->expire = time.tv_sec; s->timeout = PFTM_TCP_FIRST_PACKET; pf_set_rt_ifp(s, saddr); - + if (sn != NULL) { + s->src_node = sn; + s->src_node->states++; + } + if (nsn != NULL) { + PF_ACPY(&nsn->raddr, &pd->naddr, af); + s->nat_src_node = nsn; + s->nat_src_node->states++; + } if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m, off, pd, th, &s->src, &s->dst)) { REASON_SET(&reason, PFRES_MEMORY); + pf_src_tree_remove_state(s); pool_put(&pf_state_pl, s); return (PF_DROP); } @@ -2356,12 +2572,14 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, pf_normalize_tcp_stateful(m, off, pd, &reason, th, &s->src, &s->dst, &rewrite)) { pf_normalize_tcp_cleanup(s); + pf_src_tree_remove_state(s); pool_put(&pf_state_pl, s); return (PF_DROP); } if (pf_insert_state(s)) { pf_normalize_tcp_cleanup(s); REASON_SET(&reason, PFRES_MEMORY); + pf_src_tree_remove_state(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else @@ -2369,14 +2587,17 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && r->keep_state == PF_STATE_SYNPROXY) { s->src.state = PF_TCPS_PROXY_SRC; - if (nat != NULL) - pf_change_ap(saddr, &th->th_sport, - pd->ip_sum, &th->th_sum, &pd->baddr, - bport, 0, af); - else if (rdr != NULL) - pf_change_ap(daddr, &th->th_dport, - pd->ip_sum, &th->th_sum, &pd->baddr, - bport, 0, af); + if (nr != NULL) { + if (direction == PF_OUT) { + pf_change_ap(saddr, &th->th_sport, + pd->ip_sum, &th->th_sum, &pd->baddr, + bport, 0, af); + } else { + pf_change_ap(daddr, &th->th_dport, + pd->ip_sum, &th->th_sum, &pd->baddr, + bport, 0, af); + } + } s->src.seqhi = arc4random(); /* Find mss option */ mss = pf_get_mss(m, off, th->th_off, af); @@ -2402,7 +2623,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, struct ifnet *ifp, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) { - struct pf_rule *nat = NULL, *rdr = NULL; + struct pf_rule *nr = NULL; struct pf_addr *saddr = pd->src, *daddr = pd->dst; struct udphdr *uh = pd->hdr.udp; u_int16_t bport, nport = 0; @@ -2412,6 +2633,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, gid_t gid; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; + struct pf_src_node *nsn = NULL; u_short reason; int rewrite = 0; struct pf_tag *pftag = NULL; @@ -2422,30 +2644,30 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, if (direction == PF_OUT) { bport = nport = uh->uh_sport; /* check outgoing packet for BINAT/NAT */ - if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp, + if ((nr = pf_get_translation(pd, m, off, PF_OUT, ifp, &nsn, saddr, uh->uh_sport, daddr, uh->uh_dport, &pd->naddr, &nport)) != NULL) { PF_ACPY(&pd->baddr, saddr, af); pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, &uh->uh_sum, &pd->naddr, nport, 1, af); rewrite++; - if (nat->natpass) + if (nr->natpass) r = NULL; - pd->nat_rule = nat; + pd->nat_rule = nr; } } else { bport = nport = uh->uh_dport; /* check incoming packet for BINAT/RDR */ - if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr, - uh->uh_sport, daddr, uh->uh_dport, &pd->naddr, &nport)) - != NULL) { + if ((nr = pf_get_translation(pd, m, off, PF_IN, ifp, &nsn, + saddr, uh->uh_sport, daddr, uh->uh_dport, &pd->naddr, + &nport)) != NULL) { PF_ACPY(&pd->baddr, daddr, af); pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, &uh->uh_sum, &pd->naddr, nport, 1, af); rewrite++; - if (rdr->natpass) + if (nr->natpass) r = NULL; - pd->nat_rule = rdr; + pd->nat_rule = nr; } } @@ -2484,8 +2706,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], gid)) r = TAILQ_NEXT(r, entries); - else if (r->match_tag && - !pf_match_tag(m, r, nat, rdr, pftag, &tag)) + else if (r->match_tag && !pf_match_tag(m, r, nr, pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->anchorname[0] && r->anchor == NULL) r = TAILQ_NEXT(r, entries); @@ -2525,14 +2746,16 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, ((r->rule_flag & PFRULE_RETURNICMP) || (r->rule_flag & PFRULE_RETURN))) { /* undo NAT changes, if they have taken place */ - if (nat != NULL) { - pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, - &uh->uh_sum, &pd->baddr, bport, 1, af); - rewrite++; - } else if (rdr != NULL) { - pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, - &uh->uh_sum, &pd->baddr, bport, 1, af); - rewrite++; + if (nr != NULL) { + if (direction == PF_OUT) { + pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, + &uh->uh_sum, &pd->baddr, bport, 1, af); + rewrite++; + } else { + pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, + &uh->uh_sum, &pd->baddr, bport, 1, af); + rewrite++; + } } if ((af == AF_INET) && r->return_icmp) pf_send_icmp(m, r->return_icmp >> 8, @@ -2550,13 +2773,41 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, return (PF_DROP); } - if (r->keep_state || nat != NULL || rdr != NULL) { + if (r->keep_state || nr != NULL) { /* create new state */ struct pf_state *s = NULL; - - if (!r->max_states || r->states < r->max_states) - s = pool_get(&pf_state_pl, PR_NOWAIT); + struct pf_src_node *sn = NULL; + + /* check maximums */ + if (r->max_states && (r->states >= r->max_states)) + goto cleanup; + /* src node for flter rule */ + if ((r->rule_flag & PFRULE_SRCTRACK || + r->rpool.opts & PF_POOL_STICKYADDR) && + pf_insert_src_node(&sn, r, saddr, af) != 0) + goto cleanup; + /* src node for translation rule */ + if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && + ((direction == PF_OUT && + pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || + (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) + goto cleanup; + s = pool_get(&pf_state_pl, PR_NOWAIT); if (s == NULL) { +cleanup: + if (sn != NULL && sn->states == 0 && sn->expire == 0) { + RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, sn); + } + if (nsn != sn && nsn != NULL && nsn->states == 0 && + nsn->expire == 0) { + RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, nsn); + } REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } @@ -2565,10 +2816,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, if (a != NULL) a->states++; s->rule.ptr = r; - if (nat != NULL) - s->nat_rule.ptr = nat; - else - s->nat_rule.ptr = rdr; + s->nat_rule.ptr = nr; if (s->nat_rule.ptr != NULL) s->nat_rule.ptr->states++; s->anchor.ptr = a; @@ -2582,7 +2830,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, s->gwy.port = uh->uh_sport; PF_ACPY(&s->ext.addr, daddr, af); s->ext.port = uh->uh_dport; - if (nat != NULL) { + if (nr != NULL) { PF_ACPY(&s->lan.addr, &pd->baddr, af); s->lan.port = bport; } else { @@ -2594,7 +2842,7 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, s->lan.port = uh->uh_dport; PF_ACPY(&s->ext.addr, saddr, af); s->ext.port = uh->uh_sport; - if (rdr != NULL) { + if (nr != NULL) { PF_ACPY(&s->gwy.addr, &pd->baddr, af); s->gwy.port = bport; } else { @@ -2608,8 +2856,18 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, s->expire = time.tv_sec; s->timeout = PFTM_UDP_FIRST_PACKET; pf_set_rt_ifp(s, saddr); + if (sn != NULL) { + s->src_node = sn; + s->src_node->states++; + } + if (nsn != NULL) { + PF_ACPY(&nsn->raddr, &pd->naddr, af); + s->nat_src_node = nsn; + s->nat_src_node->states++; + } if (pf_insert_state(s)) { REASON_SET(&reason, PFRES_MEMORY); + pf_src_tree_remove_state(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else @@ -2628,10 +2886,11 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, struct ifnet *ifp, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) { - struct pf_rule *nat = NULL, *rdr = NULL; + struct pf_rule *nr = NULL; struct pf_addr *saddr = pd->src, *daddr = pd->dst; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; + struct pf_src_node *nsn = NULL; u_short reason; u_int16_t icmpid; sa_family_t af = pd->af; @@ -2677,8 +2936,8 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, if (direction == PF_OUT) { /* check outgoing packet for BINAT/NAT */ - if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp, saddr, 0, - daddr, 0, &pd->naddr, NULL)) != NULL) { + if ((nr = pf_get_translation(pd, m, off, PF_OUT, ifp, &nsn, + saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { PF_ACPY(&pd->baddr, saddr, af); switch (af) { #ifdef INET @@ -2695,14 +2954,14 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, break; #endif /* INET6 */ } - if (nat->natpass) + if (nr->natpass) r = NULL; - pd->nat_rule = nat; + pd->nat_rule = nr; } } else { /* check incoming packet for BINAT/RDR */ - if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr, 0, - daddr, 0, &pd->naddr, NULL)) != NULL) { + if ((nr = pf_get_translation(pd, m, off, PF_IN, ifp, &nsn, + saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { PF_ACPY(&pd->baddr, daddr, af); switch (af) { #ifdef INET @@ -2719,9 +2978,9 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, break; #endif /* INET6 */ } - if (rdr->natpass) + if (nr->natpass) r = NULL; - pd->nat_rule = rdr; + pd->nat_rule = nr; } } @@ -2748,8 +3007,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, r = TAILQ_NEXT(r, entries); else if (r->rule_flag & PFRULE_FRAGMENT) r = TAILQ_NEXT(r, entries); - else if (r->match_tag && - !pf_match_tag(m, r, nat, rdr, pftag, &tag)) + else if (r->match_tag && !pf_match_tag(m, r, nr, pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->anchorname[0] && r->anchor == NULL) r = TAILQ_NEXT(r, entries); @@ -2796,14 +3054,41 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, return (PF_DROP); } - if (!state_icmp && (r->keep_state || - nat != NULL || rdr != NULL)) { + if (!state_icmp && (r->keep_state || nr != NULL)) { /* create new state */ struct pf_state *s = NULL; - - if (!r->max_states || r->states < r->max_states) - s = pool_get(&pf_state_pl, PR_NOWAIT); + struct pf_src_node *sn = NULL; + + /* check maximums */ + if (r->max_states && (r->states >= r->max_states)) + goto cleanup; + /* src node for flter rule */ + if ((r->rule_flag & PFRULE_SRCTRACK || + r->rpool.opts & PF_POOL_STICKYADDR) && + pf_insert_src_node(&sn, r, saddr, af) != 0) + goto cleanup; + /* src node for translation rule */ + if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && + ((direction == PF_OUT && + pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || + (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) + goto cleanup; + s = pool_get(&pf_state_pl, PR_NOWAIT); if (s == NULL) { +cleanup: + if (sn != NULL && sn->states == 0 && sn->expire == 0) { + RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, sn); + } + if (nsn != sn && nsn != NULL && nsn->states == 0 && + nsn->expire == 0) { + RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, nsn); + } REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } @@ -2812,10 +3097,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, if (a != NULL) a->states++; s->rule.ptr = r; - if (nat != NULL) - s->nat_rule.ptr = nat; - else - s->nat_rule.ptr = rdr; + s->nat_rule.ptr = nr; if (s->nat_rule.ptr != NULL) s->nat_rule.ptr->states++; s->anchor.ptr = a; @@ -2829,7 +3111,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, s->gwy.port = icmpid; PF_ACPY(&s->ext.addr, daddr, af); s->ext.port = icmpid; - if (nat != NULL) + if (nr != NULL) PF_ACPY(&s->lan.addr, &pd->baddr, af); else PF_ACPY(&s->lan.addr, &s->gwy.addr, af); @@ -2839,7 +3121,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, s->lan.port = icmpid; PF_ACPY(&s->ext.addr, saddr, af); s->ext.port = icmpid; - if (rdr != NULL) + if (nr != NULL) PF_ACPY(&s->gwy.addr, &pd->baddr, af); else PF_ACPY(&s->gwy.addr, &s->lan.addr, af); @@ -2849,8 +3131,18 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, s->expire = time.tv_sec; s->timeout = PFTM_ICMP_FIRST_PACKET; pf_set_rt_ifp(s, saddr); + if (sn != NULL) { + s->src_node = sn; + s->src_node->states++; + } + if (nsn != NULL) { + PF_ACPY(&nsn->raddr, &pd->naddr, af); + s->nat_src_node = nsn; + s->nat_src_node->states++; + } if (pf_insert_state(s)) { REASON_SET(&reason, PFRES_MEMORY); + pf_src_tree_remove_state(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else @@ -2872,9 +3164,10 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, struct ifnet *ifp, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) { - struct pf_rule *nat = NULL, *rdr = NULL; + struct pf_rule *nr = NULL; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; + struct pf_src_node *nsn = NULL; struct pf_addr *saddr = pd->src, *daddr = pd->dst; sa_family_t af = pd->af; u_short reason; @@ -2885,8 +3178,8 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, if (direction == PF_OUT) { /* check outgoing packet for BINAT/NAT */ - if ((nat = pf_get_translation(pd, m, off, PF_OUT, ifp, saddr, 0, - daddr, 0, &pd->naddr, NULL)) != NULL) { + if ((nr = pf_get_translation(pd, m, off, PF_OUT, ifp, &nsn, + saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { PF_ACPY(&pd->baddr, saddr, af); switch (af) { #ifdef INET @@ -2901,14 +3194,14 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, break; #endif /* INET6 */ } - if (nat->natpass) + if (nr->natpass) r = NULL; - pd->nat_rule = nat; + pd->nat_rule = nr; } } else { /* check incoming packet for BINAT/RDR */ - if ((rdr = pf_get_translation(pd, m, off, PF_IN, ifp, saddr, 0, - daddr, 0, &pd->naddr, NULL)) != NULL) { + if ((nr = pf_get_translation(pd, m, off, PF_IN, ifp, &nsn, + saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { PF_ACPY(&pd->baddr, daddr, af); switch (af) { #ifdef INET @@ -2923,9 +3216,9 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, break; #endif /* INET6 */ } - if (rdr->natpass) + if (nr->natpass) r = NULL; - pd->nat_rule = rdr; + pd->nat_rule = nr; } } @@ -2948,8 +3241,7 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, r = TAILQ_NEXT(r, entries); else if (r->rule_flag & PFRULE_FRAGMENT) r = TAILQ_NEXT(r, entries); - else if (r->match_tag && - !pf_match_tag(m, r, nat, rdr, pftag, &tag)) + else if (r->match_tag && !pf_match_tag(m, r, nr, pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->anchorname[0] && r->anchor == NULL) r = TAILQ_NEXT(r, entries); @@ -2987,10 +3279,12 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, (r->rule_flag & PFRULE_RETURN))) { struct pf_addr *a = NULL; - if (nat != NULL) - a = saddr; - else if (rdr != NULL) - a = daddr; + if (nr != NULL) { + if (direction == PF_OUT) + a = saddr; + else + a = daddr; + } if (a != NULL) { switch (af) { #ifdef INET @@ -3022,13 +3316,41 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, return (PF_DROP); } - if (r->keep_state || nat != NULL || rdr != NULL) { + if (r->keep_state || nr != NULL) { /* create new state */ struct pf_state *s = NULL; - - if (!r->max_states || r->states < r->max_states) - s = pool_get(&pf_state_pl, PR_NOWAIT); + struct pf_src_node *sn = NULL; + + /* check maximums */ + if (r->max_states && (r->states >= r->max_states)) + goto cleanup; + /* src node for flter rule */ + if ((r->rule_flag & PFRULE_SRCTRACK || + r->rpool.opts & PF_POOL_STICKYADDR) && + pf_insert_src_node(&sn, r, saddr, af) != 0) + goto cleanup; + /* src node for translation rule */ + if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) && + ((direction == PF_OUT && + pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) || + (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) + goto cleanup; + s = pool_get(&pf_state_pl, PR_NOWAIT); if (s == NULL) { +cleanup: + if (sn != NULL && sn->states == 0 && sn->expire == 0) { + RB_REMOVE(pf_src_tree, &tree_src_tracking, sn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, sn); + } + if (nsn != sn && nsn != NULL && nsn->states == 0 && + nsn->expire == 0) { + RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, nsn); + } REASON_SET(&reason, PFRES_MEMORY); return (PF_DROP); } @@ -3037,10 +3359,7 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, if (a != NULL) a->states++; s->rule.ptr = r; - if (nat != NULL) - s->nat_rule.ptr = nat; - else - s->nat_rule.ptr = rdr; + s->nat_rule.ptr = nr; if (s->nat_rule.ptr != NULL) s->nat_rule.ptr->states++; s->anchor.ptr = a; @@ -3052,14 +3371,14 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, if (direction == PF_OUT) { PF_ACPY(&s->gwy.addr, saddr, af); PF_ACPY(&s->ext.addr, daddr, af); - if (nat != NULL) + if (nr != NULL) PF_ACPY(&s->lan.addr, &pd->baddr, af); else PF_ACPY(&s->lan.addr, &s->gwy.addr, af); } else { PF_ACPY(&s->lan.addr, daddr, af); PF_ACPY(&s->ext.addr, saddr, af); - if (rdr != NULL) + if (nr != NULL) PF_ACPY(&s->gwy.addr, &pd->baddr, af); else PF_ACPY(&s->gwy.addr, &s->lan.addr, af); @@ -3070,11 +3389,21 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, s->expire = time.tv_sec; s->timeout = PFTM_OTHER_FIRST_PACKET; pf_set_rt_ifp(s, saddr); + if (sn != NULL) { + s->src_node = sn; + s->src_node->states++; + } + if (nsn != NULL) { + PF_ACPY(&nsn->raddr, &pd->naddr, af); + s->nat_src_node = nsn; + s->nat_src_node->states++; + } if (pf_insert_state(s)) { REASON_SET(&reason, PFRES_MEMORY); if (r->log) PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + pf_src_tree_remove_state(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else @@ -3118,8 +3447,7 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct ifnet *ifp, r->flagset || r->type || r->code || r->os_fingerprint != PF_OSFP_ANY) r = TAILQ_NEXT(r, entries); - else if (r->match_tag && - !pf_match_tag(m, r, NULL, NULL, pftag, &tag)) + else if (r->match_tag && !pf_match_tag(m, r, NULL, pftag, &tag)) r = TAILQ_NEXT(r, entries); else if (r->anchorname[0] && r->anchor == NULL) r = TAILQ_NEXT(r, entries); @@ -4351,6 +4679,7 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct ifnet *ifp = NULL; struct m_tag *mtag; struct pf_addr naddr; + struct pf_src_node *sn = NULL; int error = 0; if (m == NULL || *m == NULL || r == NULL || @@ -4402,9 +4731,8 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, if (TAILQ_EMPTY(&r->rpool.list)) panic("pf_route: TAILQ_EMPTY(&r->rpool.list)"); if (s == NULL) { - pf_map_addr(AF_INET, &r->rpool, - (struct pf_addr *)&ip->ip_src, - &naddr, NULL); + pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src, + &naddr, NULL, &sn); if (!PF_AZERO(&naddr, AF_INET)) dst->sin_addr.s_addr = naddr.v4.s_addr; ifp = r->rpool.cur->ifp; @@ -4544,6 +4872,7 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct ip6_hdr *ip6; struct ifnet *ifp = NULL; struct pf_addr naddr; + struct pf_src_node *sn = NULL; int error = 0; if (m == NULL || *m == NULL || r == NULL || @@ -4592,8 +4921,8 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, if (TAILQ_EMPTY(&r->rpool.list)) panic("pf_route6: TAILQ_EMPTY(&r->rpool.list)"); if (s == NULL) { - pf_map_addr(AF_INET6, &r->rpool, - (struct pf_addr *)&ip6->ip6_src, &naddr, NULL); + pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src, + &naddr, NULL, &sn); if (!PF_AZERO(&naddr, AF_INET6)) PF_ACPY((struct pf_addr *)&dst->sin6_addr, &naddr, AF_INET6); @@ -4978,6 +5307,14 @@ done: s->nat_rule.ptr->packets++; s->nat_rule.ptr->bytes += pd.tot_len; } + if (s->src_node != NULL) { + s->src_node->packets++; + s->src_node->bytes += pd.tot_len; + } + if (s->nat_src_node != NULL) { + s->nat_src_node->packets++; + s->nat_src_node->bytes += pd.tot_len; + } } tr = r; nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; @@ -5265,6 +5602,14 @@ done: s->nat_rule.ptr->packets++; s->nat_rule.ptr->bytes += pd.tot_len; } + if (s->src_node != NULL) { + s->src_node->packets++; + s->src_node->bytes += pd.tot_len; + } + if (s->nat_src_node != NULL) { + s->nat_src_node->packets++; + s->nat_src_node->bytes += pd.tot_len; + } } tr = r; nr = (s != NULL) ? s->nat_rule.ptr : pd.nat_rule; diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c index f0c38de9731..bd1e7db5921 100644 --- a/sys/net/pf_ioctl.c +++ b/sys/net/pf_ioctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf_ioctl.c,v 1.88 2003/12/12 20:05:45 cedric Exp $ */ +/* $OpenBSD: pf_ioctl.c,v 1.89 2003/12/15 00:02:04 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -106,6 +106,8 @@ pfattach(int num) &pool_allocator_nointr); pool_init(&pf_addr_pl, sizeof(struct pf_addr_dyn), 0, 0, 0, "pfaddrpl", &pool_allocator_nointr); + pool_init(&pf_src_tree_pl, sizeof(struct pf_src_node), 0, 0, 0, + "pfsrctrpl", NULL); pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl", NULL); pool_init(&pf_altq_pl, sizeof(struct pf_altq), 0, 0, 0, "pfaltqpl", @@ -120,6 +122,7 @@ pfattach(int num) RB_INIT(&tree_lan_ext); RB_INIT(&tree_ext_gwy); + RB_INIT(&tree_src_tracking); TAILQ_INIT(&pf_anchors); pf_init_ruleset(&pf_main_ruleset); TAILQ_INIT(&pf_altqs[0]); @@ -150,11 +153,13 @@ pfattach(int num) timeout[PFTM_OTHER_MULTIPLE] = 60; /* Bidirectional */ timeout[PFTM_FRAG] = 30; /* Fragment expire */ timeout[PFTM_INTERVAL] = 10; /* Expire interval */ + timeout[PFTM_SRC_NODE] = 0; /* Source tracking */ timeout_set(&pf_expire_to, pf_purge_timeout, &pf_expire_to); timeout_add(&pf_expire_to, timeout[PFTM_INTERVAL] * hz); pf_normalize_init(); + bzero(&pf_status, sizeof(pf_status)); pf_status.debug = PF_DEBUG_URGENT; } @@ -414,7 +419,9 @@ pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule) rule->entries.tqe_prev = NULL; rule->nr = -1; } - if (rule->states > 0 || rule->entries.tqe_prev != NULL) + + if (rule->states > 0 || rule->src_nodes > 0 || + rule->entries.tqe_prev != NULL) return; pf_tag_unref(rule->tag); pf_tag_unref(rule->match_tag); @@ -732,6 +739,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCRCLRASTATS: case DIOCRTSTADDRS: case DIOCOSFPGET: + case DIOCGETSRCNODES: + case DIOCCLRSRCNODES: break; default: return (EPERM); @@ -761,6 +770,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCRGETASTATS: case DIOCRTSTADDRS: case DIOCOSFPGET: + case DIOCGETSRCNODES: break; default: return (EACCES); @@ -774,10 +784,12 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) else { u_int32_t states = pf_status.states; u_int32_t debug = pf_status.debug; + u_int32_t src_nodes = pf_status.src_nodes; bzero(&pf_status, sizeof(struct pf_status)); pf_status.running = 1; pf_status.states = states; pf_status.debug = debug; + pf_status.states = src_nodes; pf_status.since = time.tv_sec; if (status_ifp != NULL) strlcpy(pf_status.ifname, @@ -847,6 +859,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) TAILQ_INIT(&rule->rpool.list); /* initialize refcounting */ rule->states = 0; + rule->src_nodes = 0; rule->entries.tqe_prev = NULL; #ifndef INET if (rule->af == AF_INET) { @@ -1363,12 +1376,14 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCCLRSTATUS: { u_int32_t running = pf_status.running; u_int32_t states = pf_status.states; + u_int32_t src_nodes = pf_status.src_nodes; u_int32_t since = pf_status.since; u_int32_t debug = pf_status.debug; bzero(&pf_status, sizeof(struct pf_status)); pf_status.running = running; pf_status.states = states; + pf_status.src_nodes = src_nodes; pf_status.since = since; pf_status.debug = debug; if (status_ifp != NULL) @@ -1488,6 +1503,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } old_limit = pf_pool_limits[pl->index].limit; pf_pool_limits[pl->index].limit = pl->limit; + if (pl->index == PF_LIMIT_SRC_NODES) + pf_default_rule.max_src_nodes = pl->limit; pl->limit = old_limit; break; } @@ -2204,12 +2221,6 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } - case DIOCOSFPFLUSH: - s = splsoftnet(); - pf_osfp_flush(); - splx(s); - break; - case DIOCOSFPADD: { struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; s = splsoftnet(); @@ -2411,6 +2422,76 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } + case DIOCGETSRCNODES: { + struct pfioc_src_nodes *psn = (struct pfioc_src_nodes *)addr; + struct pf_src_node *n; + struct pf_src_node *p, pstore; + u_int32_t nr = 0; + int space = psn->psn_len; + + if (space == 0) { + s = splsoftnet(); + RB_FOREACH(n, pf_src_tree, &tree_src_tracking) + nr++; + splx(s); + psn->psn_len = sizeof(struct pf_src_node) * nr; + return (0); + } + + s = splsoftnet(); + p = psn->psn_src_nodes; + RB_FOREACH(n, pf_src_tree, &tree_src_tracking) { + int secs = time.tv_sec; + + if ((nr + 1) * sizeof(*p) > (unsigned)psn->psn_len) + break; + + bcopy(n, &pstore, sizeof(pstore)); + if (n->rule.ptr != NULL) + pstore.rule.nr = n->rule.ptr->nr; + pstore.creation = secs - pstore.creation; + if (pstore.expire > secs) + pstore.expire -= secs; + else + pstore.expire = 0; + error = copyout(&pstore, p, sizeof(*p)); + if (error) { + splx(s); + goto fail; + } + p++; + nr++; + } + psn->psn_len = sizeof(struct pf_src_node) * nr; + splx(s); + break; + } + + case DIOCCLRSRCNODES: { + struct pf_src_node *n; + struct pf_state *state; + + s = splsoftnet(); + RB_FOREACH(state, pf_state_tree_lan_ext, &tree_lan_ext) { + state->src_node = NULL; + state->nat_src_node = NULL; + } + RB_FOREACH(n, pf_src_tree, &tree_src_tracking) { + n->expire = 1; + n->states = 0; + } + pf_purge_expired_src_nodes(); + pf_status.src_nodes = 0; + splx(s); + break; + } + + case DIOCOSFPFLUSH: + s = splsoftnet(); + pf_osfp_flush(); + splx(s); + break; + default: error = ENODEV; break; diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 71e31f90d79..4b4e4219a97 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar.h,v 1.176 2003/12/12 20:05:45 cedric Exp $ */ +/* $OpenBSD: pfvar.h,v 1.177 2003/12/15 00:02:04 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -68,16 +68,17 @@ enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED, PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY, PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE, PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL, - PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_MAX, - PFTM_PURGE, PFTM_UNTIL_PACKET }; + PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_SRC_NODE, + PFTM_MAX, PFTM_PURGE, PFTM_UNTIL_PACKET }; enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; -enum { PF_LIMIT_STATES, PF_LIMIT_FRAGS, PF_LIMIT_MAX }; +enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, PF_LIMIT_MAX }; #define PF_POOL_IDMASK 0x0f enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, PF_ADDR_TABLE }; #define PF_POOL_TYPEMASK 0x0f +#define PF_POOL_STICKYADDR 0x20 #define PF_WSCALE_FLAG 0x80 #define PF_WSCALE_MASK 0x0f @@ -447,7 +448,6 @@ struct pf_rule { union pf_rule_ptr skip[PF_SKIP_COUNT]; #define PF_RULE_LABEL_SIZE 64 char label[PF_RULE_LABEL_SIZE]; - u_int32_t timeout[PFTM_MAX]; #define PF_QNAME_SIZE 16 char ifname[IFNAMSIZ]; char qname[PF_QNAME_SIZE]; @@ -469,8 +469,13 @@ struct pf_rule { struct pf_anchor *anchor; pf_osfp_t os_fingerprint; + + u_int32_t timeout[PFTM_MAX]; u_int32_t states; u_int32_t max_states; + u_int32_t src_nodes; + u_int32_t max_src_nodes; + u_int32_t max_src_states; u_int32_t qid; u_int32_t pqid; u_int32_t rt_listid; @@ -518,6 +523,8 @@ struct pf_rule { #define PFRULE_RETURNICMP 0x0004 #define PFRULE_RETURN 0x0008 #define PFRULE_NOSYNC 0x0010 +#define PFRULE_SRCTRACK 0x0020 /* track source states */ +#define PFRULE_RULESRCTRACK 0x0040 /* per rule */ /* scrub flags */ #define PFRULE_NODF 0x0100 @@ -528,6 +535,20 @@ struct pf_rule { #define PFSTATE_HIWAT 10000 /* default state table size */ +struct pf_src_node { + RB_ENTRY(pf_src_node) entry; + struct pf_addr addr; + struct pf_addr raddr; + union pf_rule_ptr rule; + struct ifnet *ifp; + u_int32_t bytes; + u_int32_t packets; + u_int32_t states; + u_int32_t creation; + u_int32_t expire; + sa_family_t af; + u_int8_t ruletype; +}; struct pf_state_scrub { u_int16_t pfss_flags; @@ -567,6 +588,8 @@ struct pf_state { union pf_rule_ptr nat_rule; struct pf_addr rt_addr; struct ifnet *rt_ifp; + struct pf_src_node *src_node; + struct pf_src_node *nat_src_node; u_int32_t creation; u_int32_t expire; u_int32_t packets[2]; @@ -796,6 +819,10 @@ struct pf_pdesc { #define FCNT_STATE_REMOVALS 2 #define FCNT_MAX 3 +#define SCNT_SRC_NODE_SEARCH 0 +#define SCNT_SRC_NODE_INSERT 1 +#define SCNT_SRC_NODE_REMOVALS 2 +#define SCNT_MAX 3 #define ACTION_SET(a, x) \ do { \ @@ -814,10 +841,12 @@ struct pf_pdesc { struct pf_status { u_int64_t counters[PFRES_MAX]; u_int64_t fcounters[FCNT_MAX]; + u_int64_t scounters[SCNT_MAX]; u_int64_t pcounters[2][2][3]; u_int64_t bcounters[2][2]; u_int32_t running; u_int32_t states; + u_int32_t src_nodes; u_int32_t since; u_int32_t debug; char ifname[IFNAMSIZ]; @@ -963,6 +992,16 @@ struct pfioc_states { #define ps_states ps_u.psu_states }; +struct pfioc_src_nodes { + int psn_len; + union { + caddr_t psu_buf; + struct pf_src_node *psu_src_nodes; + } psn_u; +#define psn_buf psn_u.psu_buf +#define psn_src_nodes psn_u.psu_src_nodes +}; + struct pfioc_if { char ifname[IFNAMSIZ]; }; @@ -1116,9 +1155,15 @@ struct pfioc_table { #define DIOCXBEGIN _IOWR('D', 81, struct pfioc_trans) #define DIOCXCOMMIT _IOWR('D', 82, struct pfioc_trans) #define DIOCXROLLBACK _IOWR('D', 83, struct pfioc_trans) +#define DIOCGETSRCNODES _IOWR('D', 84, struct pfioc_src_nodes) +#define DIOCCLRSRCNODES _IO('D', 85) #ifdef _KERNEL +RB_HEAD(pf_src_tree, pf_src_node); +RB_PROTOTYPE(pf_src_tree, pf_src_node, entry, pf_src_compare); +extern struct pf_src_tree tree_src_tracking; + RB_HEAD(pf_state_tree_lan_ext, pf_state); RB_PROTOTYPE(pf_state_tree_lan_ext, pf_state, entry_lan_ext, pf_state_compare_lan_ext); @@ -1154,12 +1199,17 @@ extern void pf_calc_skip_steps(struct pf_rulequeue *); extern void pf_rule_set_qid(struct pf_rulequeue *); extern u_int32_t pf_qname_to_qid(char *); extern void pf_update_anchor_rules(void); -extern struct pool pf_rule_pl, pf_addr_pl; +extern struct pool pf_src_tree_pl, pf_rule_pl, pf_addr_pl; extern struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl; extern struct pool pf_state_scrub_pl; extern void pf_purge_timeout(void *); +extern void pf_purge_expired_src_nodes(void); extern void pf_purge_expired_states(void); extern int pf_insert_state(struct pf_state *); +extern int pf_insert_src_node(struct pf_src_node **, + struct pf_rule *, struct pf_addr *, + sa_family_t); +void pf_src_tree_remove_state(struct pf_state *); extern struct pf_state *pf_find_state(struct pf_state *, u_int8_t); extern struct pf_anchor *pf_find_anchor(const char *); extern struct pf_ruleset *pf_find_ruleset(char *, char *); |