diff options
Diffstat (limited to 'sys/net/pf.c')
-rw-r--r-- | sys/net/pf.c | 507 |
1 files changed, 8 insertions, 499 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c index 62bafb7d481..a56f33f5c36 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.113 2001/07/15 23:05:04 dhartmei Exp $ */ +/* $OpenBSD: pf.c,v 1.114 2001/07/17 20:34:51 provos Exp $ */ /* * Copyright (c) 2001, Daniel Hartmeier @@ -45,7 +45,6 @@ #include <net/if_types.h> #include <net/bpf.h> #include <net/route.h> -#include <net/pfvar.h> #include <net/if_pflog.h> #include <netinet/in.h> @@ -58,6 +57,8 @@ #include <netinet/udp.h> #include <netinet/ip_icmp.h> +#include <net/pfvar.h> + #include "bpfilter.h" #include "pflog.h" @@ -69,11 +70,7 @@ int pf_debug = 0; */ struct pf_tree_node { - struct pf_tree_key { - struct in_addr addr[2]; - u_int16_t port[2]; - u_int8_t proto; - } key; + struct pf_tree_key key; struct pf_state *state; struct pf_tree_node *parent; struct pf_tree_node *left; @@ -81,34 +78,13 @@ struct pf_tree_node { int balance; }; -struct pf_frent { - LIST_ENTRY(pf_frent) fr_next; - struct ip *fr_ip; - struct mbuf *fr_m; -}; - -#define PFFRAG_SEENLAST 0x0001 /* Seen the last fragment for this */ - -struct pf_fragment { - TAILQ_ENTRY(pf_fragment) frag_next; - struct in_addr fr_src; - struct in_addr fr_dst; - u_int8_t fr_p; /* protocol of this fragment */ - u_int8_t fr_flags; /* status flags */ - u_int16_t fr_id; /* fragment id for reassemble */ - u_int16_t fr_max; /* fragment data max */ - struct timeval fr_timeout; - LIST_HEAD(pf_fragq, pf_frent) fr_queue; -}; - /* * Global variables */ -TAILQ_HEAD(pf_fragqueue, pf_fragment) pf_fragqueue; -TAILQ_HEAD(pf_rulequeue, pf_rule) pf_rules[2]; TAILQ_HEAD(pf_natqueue, pf_nat) pf_nats[2]; TAILQ_HEAD(pf_rdrqueue, pf_rdr) pf_rdrs[2]; +struct pf_rulequeue pf_rules[2]; struct pf_rulequeue *pf_rules_active; struct pf_rulequeue *pf_rules_inactive; struct pf_natqueue *pf_nats_active; @@ -116,7 +92,6 @@ struct pf_natqueue *pf_nats_inactive; struct pf_rdrqueue *pf_rdrs_active; struct pf_rdrqueue *pf_rdrs_inactive; struct pf_tree_node *tree_lan_ext, *tree_ext_gwy; -struct pf_tree_node *tree_fragment; struct timeval pftv; struct pf_status pf_status; struct ifnet *status_ifp; @@ -132,24 +107,16 @@ u_int16_t pf_next_port_tcp = 50001; u_int16_t pf_next_port_udp = 50001; struct pool pf_tree_pl, pf_rule_pl, pf_nat_pl; -struct pool pf_rdr_pl, pf_state_pl, pf_frent_pl, pf_frag_pl; -int pf_nfrents; +struct pool pf_rdr_pl, pf_state_pl; int pf_tree_key_compare(struct pf_tree_key *, struct pf_tree_key *); void pf_tree_rotate_left(struct pf_tree_node **); void pf_tree_rotate_right(struct pf_tree_node **); -int pf_tree_insert(struct pf_tree_node **, - struct pf_tree_node *, struct pf_tree_key *, - struct pf_state *); -int pf_tree_remove(struct pf_tree_node **, - struct pf_tree_node *, struct pf_tree_key *); struct pf_tree_node *pf_tree_first(struct pf_tree_node *); struct pf_tree_node *pf_tree_next(struct pf_tree_node *); struct pf_tree_node *pf_tree_search(struct pf_tree_node *, struct pf_tree_key *); -struct pf_state *pf_find_state(struct pf_tree_node *, - struct pf_tree_key *); void pf_insert_state(struct pf_state *); void pf_purge_expired_states(void); @@ -171,8 +138,6 @@ void pf_change_icmp(u_int32_t *, u_int16_t *, u_int32_t *, u_int16_t *, u_int16_t *); void pf_send_reset(struct ip *, int, struct tcphdr *); void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t); -int pf_match_addr(u_int8_t, u_int32_t, u_int32_t, - u_int32_t); int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t); u_int16_t pf_map_port_range(struct pf_rdr *, u_int16_t); @@ -199,20 +164,6 @@ int pf_test_state_icmp(struct pf_state **, int, struct ip *, struct icmp *); void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *); -int pflog_packet(struct mbuf *, int, u_short, u_short, - struct pf_rule *); - -int pf_normalize_ip(struct mbuf **, int, struct ifnet *, - u_short *); - -void pf_purge_expired_fragments(void); -void pf_ip2key(struct pf_tree_key *, struct ip *); -void pf_remove_fragment(struct pf_fragment *); -void pf_flush_fragments(void); -void pf_free_fragment(struct pf_fragment *); -struct pf_fragment *pf_find_fragment(struct ip *); -struct mbuf *pf_reassemble(struct mbuf **, struct pf_fragment *, - struct pf_frent *, int); #if NPFLOG > 0 #define PFLOG_PACKET(x,a,b,c,d,e) \ @@ -227,20 +178,6 @@ struct mbuf *pf_reassemble(struct mbuf **, struct pf_fragment *, #define PFLOG_PACKET #endif -#define MATCH_TUPLE(h,r,d,i) \ - ( \ - (r->direction == d) && \ - (r->ifp == NULL || r->ifp == i) && \ - (!r->proto || r->proto == h->ip_p) && \ - (!r->src.mask || pf_match_addr(r->src.not, r->src.addr, \ - r->src.mask, h->ip_src.s_addr)) && \ - (!r->dst.mask || pf_match_addr(r->dst.not, r->dst.addr, \ - r->dst.mask, h->ip_dst.s_addr)) \ - ) - -#define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */ -#define PFFRAG_FRAG_HIWAT 1000 /* Number of fragmented packets */ - int pf_tree_key_compare(struct pf_tree_key *a, struct pf_tree_key *b) { @@ -669,15 +606,7 @@ pfattach(int num) 0, NULL, NULL, 0); pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl", 0, NULL, NULL, 0); - pool_init(&pf_frent_pl, sizeof(struct pf_frent), 0, 0, 0, "pffrent", - 0, NULL, NULL, 0); - pool_init(&pf_frag_pl, sizeof(struct pf_fragment), 0, 0, 0, "pffrag", - 0, NULL, NULL, 0); - - pool_sethiwat(&pf_frag_pl, PFFRAG_FRAG_HIWAT); - pool_sethardlimit(&pf_frent_pl, PFFRAG_FRENT_HIWAT, NULL, 0); - TAILQ_INIT(&pf_fragqueue); TAILQ_INIT(&pf_rules[0]); TAILQ_INIT(&pf_rules[1]); TAILQ_INIT(&pf_nats[0]); @@ -690,6 +619,8 @@ pfattach(int num) pf_nats_inactive = &pf_nats[1]; pf_rdrs_active = &pf_rdrs[0]; pf_rdrs_inactive = &pf_rdrs[1]; + + pf_normalize_init(); } int @@ -1440,20 +1371,6 @@ pf_get_rdr(struct ifnet *ifp, u_int8_t proto, u_int32_t saddr, u_int32_t daddr, return (rm); } -#define ACTION_SET(a, x) \ - do { \ - if ((a) != NULL) \ - *(a) = (x); \ - } while (0) - -#define REASON_SET(a, x) \ - do { \ - if ((a) != NULL) \ - *(a) = (x); \ - if (x < PFRES_MAX) \ - pf_status.counters[x]++; \ - } while (0) - u_int16_t pf_map_port_range(struct pf_rdr *rdr, u_int16_t port) { @@ -2314,414 +2231,6 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, } } -#define FRAG_EXPIRE 30 - -void -pf_purge_expired_fragments(void) -{ - struct pf_fragment *frag; - struct timeval now, expire; - - microtime(&now); - - timerclear(&expire); - expire.tv_sec = FRAG_EXPIRE; - timersub(&now, &expire, &expire); - - while ((frag = TAILQ_LAST(&pf_fragqueue, pf_fragqueue)) != NULL) { - if (timercmp(&frag->fr_timeout, &expire, >)) - break; - - DPFPRINTF((__FUNCTION__": expiring %p\n", frag)); - pf_free_fragment(frag); - } -} - -/* - * Try to flush old fragments to make space for new ones - */ - -void -pf_flush_fragments(void) -{ - struct pf_fragment *frag; - int goal = pf_nfrents * 9 / 10; - - DPFPRINTF((__FUNCTION__": trying to free > %d frents\n", - pf_nfrents - goal)); - - while (goal < pf_nfrents) { - frag = TAILQ_LAST(&pf_fragqueue, pf_fragqueue); - if (frag == NULL) - break; - pf_free_fragment(frag); - } -} - -/* Frees the fragments and all associated entries */ - -void -pf_free_fragment(struct pf_fragment *frag) -{ - struct pf_frent *frent; - - /* Free all fragments */ - for (frent = LIST_FIRST(&frag->fr_queue); frent; - frent = LIST_FIRST(&frag->fr_queue)) { - LIST_REMOVE(frent, fr_next); - - m_freem(frent->fr_m); - pool_put(&pf_frent_pl, frent); - pf_nfrents--; - } - - pf_remove_fragment(frag); -} - -void -pf_ip2key(struct pf_tree_key *key, struct ip *ip) -{ - key->proto = ip->ip_p; - key->addr[0] = ip->ip_src; - key->addr[1] = ip->ip_dst; - key->port[0] = ip->ip_id; - key->port[1] = 0; -} - -struct pf_fragment * -pf_find_fragment(struct ip *ip) -{ - struct pf_tree_key key; - struct pf_fragment *frag; - - pf_ip2key(&key, ip); - - frag = (struct pf_fragment *)pf_find_state(tree_fragment, &key); - - if (frag != NULL) { - microtime(&frag->fr_timeout); - TAILQ_REMOVE(&pf_fragqueue, frag, frag_next); - TAILQ_INSERT_HEAD(&pf_fragqueue, frag, frag_next); - } - - return (frag); -} - -/* Removes a fragment from the fragment queue and frees the fragment */ - -void -pf_remove_fragment(struct pf_fragment *frag) -{ - struct pf_tree_key key; - - key.proto = frag->fr_p; - key.addr[0] = frag->fr_src; - key.addr[1] = frag->fr_dst; - key.port[0] = frag->fr_id; - key.port[1] = 0; - - pf_tree_remove(&tree_fragment, NULL, &key); - TAILQ_REMOVE(&pf_fragqueue, frag, frag_next); - - pool_put(&pf_frag_pl, frag); -} - -struct mbuf * -pf_reassemble(struct mbuf **m0, struct pf_fragment *frag, - struct pf_frent *frent, int mff) -{ - struct mbuf *m = *m0, *m2; - struct pf_frent *frep, *frea, *next; - struct ip *ip = frent->fr_ip; - int hlen = ip->ip_hl << 2; - u_int16_t off = ip->ip_off; - u_int16_t max = ip->ip_len + off; - - /* Strip off ip header */ - m->m_data += hlen; - m->m_len -= hlen; - - /* Create a new reassembly queue for this packet */ - if (frag == NULL) { - struct pf_tree_key key; - - frag = pool_get(&pf_frag_pl, M_NOWAIT); - if (frag == NULL) { - pf_flush_fragments(); - frag = pool_get(&pf_frag_pl, M_NOWAIT); - if (frag == NULL) - goto drop_fragment; - } - - frag->fr_flags = 0; - frag->fr_max = 0; - frag->fr_src = frent->fr_ip->ip_src; - frag->fr_dst = frent->fr_ip->ip_dst; - frag->fr_p = frent->fr_ip->ip_p; - frag->fr_id = frent->fr_ip->ip_id; - LIST_INIT(&frag->fr_queue); - - pf_ip2key(&key, frent->fr_ip); - - pf_tree_insert(&tree_fragment, NULL, &key, - (struct pf_state *)frag); - TAILQ_INSERT_HEAD(&pf_fragqueue, frag, frag_next); - - /* We do not have a previous fragment */ - frep = NULL; - goto insert; - } - - /* - * Find a fragment after the current one: - * - off contains the real shifted offset. - */ - LIST_FOREACH(frea, &frag->fr_queue, fr_next) { - if (frea->fr_ip->ip_off > off) - break; - frep = frea; - } - - KASSERT(frep != NULL || frea != NULL); - - if (frep != NULL) { - u_int16_t precut; - - precut = frep->fr_ip->ip_off + frep->fr_ip->ip_len - off; - if (precut > ip->ip_len) - goto drop_fragment; - if (precut) { - m_adj(frent->fr_m, precut); - - DPFPRINTF((__FUNCTION__": overlap -%d\n", precut)); - /* Enforce 8 byte boundaries */ - off = ip->ip_off += precut; - ip->ip_len -= precut; - } - } - - for (; frea != NULL && ip->ip_len + off > frea->fr_ip->ip_off; - frea = next) { - u_int16_t aftercut; - - aftercut = (ip->ip_len + off) - frea->fr_ip->ip_off; - DPFPRINTF((__FUNCTION__": adjust overlap %d\n", aftercut)); - if (aftercut < frea->fr_ip->ip_len) { - frea->fr_ip->ip_len -= aftercut; - frea->fr_ip->ip_off += aftercut; - m_adj(frea->fr_m, aftercut); - break; - } - - /* This fragment is completely overlapped, loose it */ - next = LIST_NEXT(frea, fr_next); - m_freem(frea->fr_m); - LIST_REMOVE(frea, fr_next); - pool_put(&pf_frent_pl, frea); - pf_nfrents--; - } - - insert: - /* Update maxmimum data size */ - if (frag->fr_max < max) - frag->fr_max = max; - /* This is the last segment */ - if (!mff) - frag->fr_flags |= PFFRAG_SEENLAST; - - if (frep == NULL) - LIST_INSERT_HEAD(&frag->fr_queue, frent, fr_next); - else - LIST_INSERT_AFTER(frep, frent, fr_next); - - /* Check if we are completely reassembled */ - if (!(frag->fr_flags & PFFRAG_SEENLAST)) - return (NULL); - - /* Check if we have all the data */ - off = 0; - for (frep = LIST_FIRST(&frag->fr_queue); frep; frep = next) { - next = LIST_NEXT(frep, fr_next); - - off += frep->fr_ip->ip_len; - if (off < frag->fr_max && - (next == NULL || next->fr_ip->ip_off != off)) { - DPFPRINTF((__FUNCTION__": missing fragment at %d, next %d, max %d\n", - off, next == NULL ? -1 : next->fr_ip->ip_off, frag->fr_max)); - return (NULL); - } - } - DPFPRINTF((__FUNCTION__": %d < %d?\n", off, frag->fr_max)); - if (off < frag->fr_max) - return (NULL); - - /* We have all the data */ - frent = LIST_FIRST(&frag->fr_queue); - KASSERT(frent != NULL); - if ((frent->fr_ip->ip_hl << 2) + off > IP_MAXPACKET) { - DPFPRINTF((__FUNCTION__": drop: too big: %d\n", off)); - pf_free_fragment(frag); - return (NULL); - } - next = LIST_NEXT(frent, fr_next); - - /* Magic from ip_input */ - ip = frent->fr_ip; - m = frent->fr_m; - m2 = m->m_next; - m->m_next = NULL; - m_cat(m, m2); - pool_put(&pf_frent_pl, frent); - pf_nfrents--; - for (frent = next; frent != NULL; frent = next) { - next = LIST_NEXT(frent, fr_next); - - m2 = frent->fr_m; - pool_put(&pf_frent_pl, frent); - pf_nfrents--; - m_cat(m, m2); - } - - ip->ip_src = frag->fr_src; - ip->ip_dst = frag->fr_dst; - - /* Remove from fragment queue */ - pf_remove_fragment(frag); - - hlen = ip->ip_hl << 2; - ip->ip_len = off + hlen; - m->m_len += hlen; - m->m_data -= hlen; - - /* some debugging cruft by sklower, below, will go away soon */ - /* XXX this should be done elsewhere */ - if (m->m_flags & M_PKTHDR) { - int plen = 0; - for (m2 = m; m2; m2 = m2->m_next) - plen += m2->m_len; - m->m_pkthdr.len = plen; - } - - DPFPRINTF((__FUNCTION__": complete: %p(%d)\n", m, ip->ip_len)); - return (m); - - drop_fragment: - /* Oops - fail safe - drop packet */ - m_freem(m); - return (NULL); -} - -int -pf_normalize_ip(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) -{ - struct mbuf *m = *m0; - struct pf_rule *r; - struct pf_frent *frent; - struct pf_fragment *frag; - struct ip *h = mtod(m, struct ip *); - int mff = (h->ip_off & IP_MF), hlen = h->ip_hl << 2; - u_int16_t fragoff = (h->ip_off & IP_OFFMASK) << 3; - u_int16_t max; - - TAILQ_FOREACH(r, pf_rules_active, entries) { - if ((r->action == PF_SCRUB) && - MATCH_TUPLE(h, r, dir, ifp)) - break; - } - - if (r == NULL) - return (PF_PASS); - - /* Check for illegal packets */ - if (hlen < sizeof(struct ip)) - goto drop; - - if (hlen > h->ip_len) - goto drop; - - /* We will need other tests here */ - if (!fragoff && !mff) - goto no_fragment; - - /* Now we are dealing with a fragmented packet */ - frag = pf_find_fragment(h); - - /* This can not happen */ - if (h->ip_off & IP_DF) { - DPFPRINTF((__FUNCTION__": IP_DF\n")); - goto bad; - } - - h->ip_len -= hlen; - h->ip_off <<= 3; - - /* All fragments are 8 byte aligned */ - if (mff && (h->ip_len & 0x7)) { - DPFPRINTF((__FUNCTION__": mff and %d\n", h->ip_len)); - goto bad; - } - - max = fragoff + h->ip_len; - /* Respect maximum length */ - if (max > IP_MAXPACKET) { - DPFPRINTF((__FUNCTION__": max packet %d\n", max)); - goto bad; - } - /* Check if we saw the last fragment already */ - if (frag != NULL && (frag->fr_flags & PFFRAG_SEENLAST) && - max > frag->fr_max) - goto bad; - - /* Get an entry for the fragment queue */ - frent = pool_get(&pf_frent_pl, PR_NOWAIT); - if (frent == NULL) { - /* Try to clean up old fragments */ - pf_flush_fragments(); - frent = pool_get(&pf_frent_pl, PR_NOWAIT); - if (frent == NULL) { - REASON_SET(reason, PFRES_MEMORY); - return (PF_DROP); - } - } - pf_nfrents++; - frent->fr_ip = h; - frent->fr_m = m; - - /* Might return a completely reassembled mbuf, or NULL */ - DPFPRINTF((__FUNCTION__": reass frag %d @ %d\n", h->ip_id, fragoff)); - *m0 = m = pf_reassemble(m0, frag, frent, mff); - - if (m == NULL) - return (PF_DROP); - - h = mtod(m, struct ip *); - - no_fragment: - if (dir != PF_OUT) - return (PF_PASS); - - return (PF_PASS); - - drop: - REASON_SET(reason, PFRES_NORM); - if (r != NULL && r->log) - PFLOG_PACKET(h, m, AF_INET, dir, *reason, r); - return (PF_DROP); - - bad: - DPFPRINTF((__FUNCTION__": dropping bad fragment\n")); - - /* Free assoicated fragments */ - if (frag != NULL) - pf_free_fragment(frag); - - REASON_SET(reason, PFRES_FRAG); - if (r != NULL && r->log) - PFLOG_PACKET(h, m, AF_INET, dir, *reason, r); - - return (PF_DROP); -} - /* * ipoff and off are measured from the start of the mbuf chain. * h must be at "ipoff" on the mbuf chain. |