diff options
author | mlaier <mlaier@FreeBSD.org> | 2004-06-16 23:24:02 +0000 |
---|---|---|
committer | mlaier <mlaier@FreeBSD.org> | 2004-06-16 23:24:02 +0000 |
commit | 5eba7986748fd69671d135d7c2e6c08545799d55 (patch) | |
tree | 784e718bae8cf0d9b3a00c54f91661eb8aeee88f /sys/contrib | |
parent | 27ecf76342d463232e4a49959506df6807d5e5b9 (diff) | |
download | FreeBSD-src-5eba7986748fd69671d135d7c2e6c08545799d55.zip FreeBSD-src-5eba7986748fd69671d135d7c2e6c08545799d55.tar.gz |
Commit pf version 3.5 and link additional files to the kernel build.
Version 3.5 brings:
- Atomic commits of ruleset changes (reduce the chance of ending up in an
inconsistent state).
- A 30% reduction in the size of state table entries.
- Source-tracking (limit number of clients and states per client).
- Sticky-address (the flexibility of round-robin with the benefits of
source-hash).
- Significant improvements to interface handling.
- and many more ...
Diffstat (limited to 'sys/contrib')
-rw-r--r-- | sys/contrib/pf/net/if_pflog.c | 48 | ||||
-rw-r--r-- | sys/contrib/pf/net/if_pflog.h | 4 | ||||
-rw-r--r-- | sys/contrib/pf/net/if_pfsync.c | 1386 | ||||
-rw-r--r-- | sys/contrib/pf/net/if_pfsync.h | 242 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf.c | 2517 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_if.c | 293 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_ioctl.c | 1749 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_norm.c | 66 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_osfp.c | 5 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_subr.c | 127 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_table.c | 449 | ||||
-rw-r--r-- | sys/contrib/pf/net/pfvar.h | 355 |
12 files changed, 4856 insertions, 2385 deletions
diff --git a/sys/contrib/pf/net/if_pflog.c b/sys/contrib/pf/net/if_pflog.c index 13f6a6c..960f709 100644 --- a/sys/contrib/pf/net/if_pflog.c +++ b/sys/contrib/pf/net/if_pflog.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: if_pflog.c,v 1.9 2003/05/14 08:42:00 canacar Exp $ */ +/* $OpenBSD: if_pflog.c,v 1.11 2003/12/31 11:18:25 cedric Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and @@ -151,12 +151,7 @@ pflog_clone_create(struct if_clone *ifc, int unit) MALLOC(sc, struct pflog_softc *, sizeof(*sc), M_PFLOG, M_WAITOK|M_ZERO); -#if (__FreeBSD_version < 501113) - sc->sc_if.if_name = PFLOGNAME; - sc->sc_if.if_unit = unit; -#else if_initname(&sc->sc_if, ifc->ifc_name, unit); -#endif sc->sc_if.if_mtu = PFLOGMTU; sc->sc_if.if_ioctl = pflogioctl; sc->sc_if.if_output = pflogoutput; @@ -165,12 +160,6 @@ pflog_clone_create(struct if_clone *ifc, int unit) sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen; sc->sc_if.if_hdrlen = PFLOG_HDRLEN; sc->sc_if.if_softc = sc; - /* - * We would get a message like - * "in6_ifattach: pflog0 is not multicast capable, IPv6 not enabled". - * We need a patch to in6_ifattach() to exclude interface type - * IFT_PFLOG. - */ if_attach(&sc->sc_if); LIST_INSERT_HEAD(&pflog_list, sc, sc_next); @@ -218,24 +207,32 @@ void pflogstart(struct ifnet *ifp) { struct mbuf *m; +#ifndef __FreeBSD__ int s; +#endif for (;;) { - s = splimp(); #ifdef __FreeBSD__ IF_LOCK(&ifp->if_snd); _IF_DROP(&ifp->if_snd); _IF_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) { + IF_UNLOCK(&ifp->if_snd); + return; + } + else + m_freem(m); IF_UNLOCK(&ifp->if_snd); #else + s = splimp(); IF_DROP(&ifp->if_snd); IF_DEQUEUE(&ifp->if_snd, m); -#endif splx(s); if (m == NULL) return; else m_freem(m); +#endif } } @@ -277,38 +274,34 @@ pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } int -pflog_packet(struct ifnet *ifp, struct mbuf *m, sa_family_t af, u_int8_t dir, +pflog_packet(struct pfi_kif *kif, struct mbuf *m, sa_family_t af, u_int8_t dir, u_int8_t reason, struct pf_rule *rm, struct pf_rule *am, struct pf_ruleset *ruleset) { #if NBPFILTER > 0 struct ifnet *ifn; struct pfloghdr hdr; +#ifndef __FreeBSD__ struct mbuf m1; +#endif - if (ifp == NULL || m == NULL || rm == NULL) + if (kif == NULL || m == NULL || rm == NULL) return (-1); + bzero(&hdr, sizeof(hdr)); hdr.length = PFLOG_REAL_HDRLEN; hdr.af = af; hdr.action = rm->action; hdr.reason = reason; -#if defined(__FreeBSD__) && (__FreeBSD_version < 501113) - snprintf(hdr.ifname, IFNAMSIZ, "%s%d", ifp->if_name, ifp->if_unit); -#else - memcpy(hdr.ifname, ifp->if_xname, sizeof(hdr.ifname)); -#endif + memcpy(hdr.ifname, kif->pfik_name, sizeof(hdr.ifname)); if (am == NULL) { hdr.rulenr = htonl(rm->nr); hdr.subrulenr = -1; - bzero(hdr.ruleset, sizeof(hdr.ruleset)); } else { hdr.rulenr = htonl(am->nr); hdr.subrulenr = htonl(rm->nr); - if (ruleset == NULL) - bzero(hdr.ruleset, sizeof(hdr.ruleset)); - else + if (ruleset != NULL) memcpy(hdr.ruleset, ruleset->name, sizeof(hdr.ruleset)); @@ -326,20 +319,23 @@ pflog_packet(struct ifnet *ifp, struct mbuf *m, sa_family_t af, u_int8_t dir, } #endif /* INET */ +#ifndef __FreeBSD__ m1.m_next = m; m1.m_len = PFLOG_HDRLEN; m1.m_data = (char *) &hdr; +#endif #ifdef __FreeBSD__ KASSERT((!LIST_EMPTY(&pflog_list)), ("pflog: no interface")); ifn = &LIST_FIRST(&pflog_list)->sc_if; + BPF_MTAP2(ifn, &hdr, sizeof(hdr), m); #else ifn = &(pflogif[0].sc_if); -#endif if (ifn->if_bpf) bpf_mtap(ifn->if_bpf, &m1); #endif +#endif return (0); } diff --git a/sys/contrib/pf/net/if_pflog.h b/sys/contrib/pf/net/if_pflog.h index e598239..3050017 100644 --- a/sys/contrib/pf/net/if_pflog.h +++ b/sys/contrib/pf/net/if_pflog.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: if_pflog.h,v 1.9 2003/07/15 20:27:27 dhartmei Exp $ */ +/* $OpenBSD: if_pflog.h,v 1.10 2004/03/19 04:52:04 frantzen Exp $ */ /* * Copyright 2001 Niels Provos <provos@citi.umich.edu> * All rights reserved. @@ -55,7 +55,7 @@ struct pfloghdr { #define PFLOG_HDRLEN sizeof(struct pfloghdr) /* minus pad, also used as a signature */ -#define PFLOG_REAL_HDRLEN offsetof(struct pfloghdr, pad); +#define PFLOG_REAL_HDRLEN offsetof(struct pfloghdr, pad) /* XXX remove later when old format logs are no longer needed */ struct old_pfloghdr { diff --git a/sys/contrib/pf/net/if_pfsync.c b/sys/contrib/pf/net/if_pfsync.c index 06eb4b0..936479d 100644 --- a/sys/contrib/pf/net/if_pfsync.c +++ b/sys/contrib/pf/net/if_pfsync.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: if_pfsync.c,v 1.6 2003/06/21 09:07:01 djm Exp $ */ +/* $OpenBSD: if_pfsync.c,v 1.26 2004/03/28 18:14:20 mcbride Exp $ */ /* * Copyright (c) 2002 Michael Shalayeff @@ -30,6 +30,7 @@ #ifdef __FreeBSD__ #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_random_ip_id.h" #endif #ifndef __FreeBSD__ @@ -43,6 +44,7 @@ #endif #include <sys/param.h> +#include <sys/proc.h> #include <sys/systm.h> #include <sys/time.h> #include <sys/mbuf.h> @@ -52,6 +54,8 @@ #include <sys/malloc.h> #include <sys/module.h> #include <sys/sockio.h> +#include <sys/lock.h> +#include <sys/mutex.h> #else #include <sys/ioctl.h> #include <sys/timeout.h> @@ -64,7 +68,10 @@ #ifdef INET #include <netinet/in.h> +#include <netinet/in_systm.h> #include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> #endif #ifdef INET6 @@ -92,27 +99,50 @@ int pfsyncdebug; #endif #ifndef __FreeBSD__ -struct pfsync_softc pfsyncif; +struct pfsync_softc pfsyncif; +#endif +int pfsync_sync_ok; +struct pfsyncstats pfsyncstats; + +#ifndef RANDOM_IP_ID +extern u_int16_t ip_randomid(void); #endif #ifdef __FreeBSD__ + +/* + * Locking notes: + * Whenever we really touch/look at the state table we have to hold the + * PF_LOCK. Functions that do just the interface handling, grab the per + * softc lock instead. + * + */ + static void pfsync_clone_destroy(struct ifnet *); static int pfsync_clone_create(struct if_clone *, int); #else void pfsyncattach(int); #endif -void pfsync_setmtu(struct pfsync_softc *sc, int); +void pfsync_setmtu(struct pfsync_softc *, int); +int pfsync_insert_net_state(struct pfsync_state *); int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *, - struct rtentry *); + struct rtentry *); int pfsyncioctl(struct ifnet *, u_long, caddr_t); void pfsyncstart(struct ifnet *); -struct mbuf *pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action); -int pfsync_sendout(struct pfsync_softc *sc); -void pfsync_timeout(void *v); +struct mbuf *pfsync_get_mbuf(struct pfsync_softc *, u_int8_t, void **); +int pfsync_request_update(struct pfsync_state_upd *, struct in_addr *); +int pfsync_sendout(struct pfsync_softc *); +void pfsync_timeout(void *); +void pfsync_send_bus(struct pfsync_softc *, u_int8_t); +void pfsync_bulk_update(void *); +void pfsync_bulkfail(void *); #ifndef __FreeBSD__ extern int ifqmaxlen; +extern struct timeval time; +extern struct timeval mono_time; +extern int hz; #endif #ifdef __FreeBSD__ @@ -126,13 +156,10 @@ pfsync_clone_destroy(struct ifnet *ifp) { struct pfsync_softc *sc; - sc = ifp->if_softc; + sc = ifp->if_softc; callout_stop(&sc->sc_tmo); - - /* - * Does we really need this? - */ - IF_DRAIN(&ifp->if_snd); + callout_stop(&sc->sc_bulk_tmo); + callout_stop(&sc->sc_bulkfail_tmo); #if NBPFILTER > 0 bpfdetach(ifp); @@ -146,25 +173,31 @@ static int pfsync_clone_create(struct if_clone *ifc, int unit) { struct pfsync_softc *sc; + struct ifnet *ifp; MALLOC(sc, struct pfsync_softc *, sizeof(*sc), M_PFSYNC, - M_WAITOK|M_ZERO); - - sc->sc_count = 8; -#if (__FreeBSD_version < 501113) - sc->sc_if.if_name = PFSYNCNAME; - sc->sc_if.if_unit = unit; -#else - if_initname(&sc->sc_if, ifc->ifc_name, unit); -#endif - sc->sc_if.if_ioctl = pfsyncioctl; - sc->sc_if.if_output = pfsyncoutput; - sc->sc_if.if_start = pfsyncstart; - sc->sc_if.if_type = IFT_PFSYNC; - sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen; - sc->sc_if.if_hdrlen = PFSYNC_HDRLEN; - sc->sc_if.if_baudrate = IF_Mbps(100); - sc->sc_if.if_softc = sc; + M_WAITOK|M_ZERO); + + pfsync_sync_ok = 1; + sc->sc_mbuf = NULL; + sc->sc_mbuf_net = NULL; + sc->sc_statep.s = NULL; + sc->sc_statep_net.s = NULL; + sc->sc_maxupdates = 128; + sc->sc_sendaddr.s_addr = htonl(INADDR_PFSYNC_GROUP); + sc->sc_ureq_received = 0; + sc->sc_ureq_sent = 0; + + ifp = &sc->sc_if; + if_initname(ifp, ifc->ifc_name, unit); + ifp->if_ioctl = pfsyncioctl; + ifp->if_output = pfsyncoutput; + ifp->if_start = pfsyncstart; + ifp->if_type = IFT_PFSYNC; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + ifp->if_hdrlen = PFSYNC_HDRLEN; + ifp->if_baudrate = IF_Mbps(100); + ifp->if_softc = sc; pfsync_setmtu(sc, MCLBYTES); /* * XXX @@ -172,6 +205,8 @@ pfsync_clone_create(struct if_clone *ifc, int unit) * if Gaint lock is removed from the network stack. */ callout_init(&sc->sc_tmo, 0); + callout_init(&sc->sc_bulk_tmo, 0); + callout_init(&sc->sc_bulkfail_tmo, 0); if_attach(&sc->sc_if); LIST_INSERT_HEAD(&pfsync_list, sc, sc_next); @@ -187,9 +222,16 @@ pfsyncattach(int npfsync) { struct ifnet *ifp; + pfsync_sync_ok = 1; + bzero(&pfsyncif, sizeof(pfsyncif)); pfsyncif.sc_mbuf = NULL; - pfsyncif.sc_ptr = NULL; - pfsyncif.sc_count = 8; + pfsyncif.sc_mbuf_net = NULL; + pfsyncif.sc_statep.s = NULL; + pfsyncif.sc_statep_net.s = NULL; + pfsyncif.sc_maxupdates = 128; + pfsyncif.sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP; + pfsyncif.sc_ureq_received = 0; + pfsyncif.sc_ureq_sent = 0; ifp = &pfsyncif.sc_if; strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname); ifp->if_softc = &pfsyncif; @@ -199,9 +241,10 @@ pfsyncattach(int npfsync) ifp->if_type = IFT_PFSYNC; ifp->if_snd.ifq_maxlen = ifqmaxlen; ifp->if_hdrlen = PFSYNC_HDRLEN; - ifp->if_baudrate = IF_Mbps(100); pfsync_setmtu(&pfsyncif, MCLBYTES); timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif); + timeout_set(&pfsyncif.sc_bulk_tmo, pfsync_bulk_update, &pfsyncif); + timeout_set(&pfsyncif.sc_bulkfail_tmo, pfsync_bulkfail, &pfsyncif); if_attach(ifp); if_alloc_sadl(ifp); @@ -217,20 +260,19 @@ pfsyncattach(int npfsync) void pfsyncstart(struct ifnet *ifp) { +#ifdef __FreeBSD__ + IF_LOCK(&ifp->if_snd); + _IF_DROP(&ifp->if_snd); + _IF_DRAIN(&ifp->if_snd); + IF_UNLOCK(&ifp->if_snd); +#else struct mbuf *m; int s; for (;;) { s = splimp(); -#ifdef __FreeBSD__ - IF_LOCK(&ifp->if_snd); - _IF_DROP(&ifp->if_snd); - _IF_DEQUEUE(&ifp->if_snd, m); - IF_UNLOCK(&ifp->if_snd); -#else IF_DROP(&ifp->if_snd); IF_DEQUEUE(&ifp->if_snd, m); -#endif splx(s); if (m == NULL) @@ -238,6 +280,549 @@ pfsyncstart(struct ifnet *ifp) else m_freem(m); } +#endif +} + +int +pfsync_insert_net_state(struct pfsync_state *sp) +{ + struct pf_state *st = NULL; + struct pf_rule *r = NULL; + struct pfi_kif *kif; + +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif + if (sp->creatorid == 0 && pf_status.debug >= PF_DEBUG_MISC) { + printf("pfsync_insert_net_state: invalid creator id:" + " %08x\n", ntohl(sp->creatorid)); + return (EINVAL); + } + + kif = pfi_lookup_create(sp->ifname); + if (kif == NULL) { + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync_insert_net_state: " + "unknown interface: %s\n", sp->ifname); + /* skip this state */ + return (0); + } + + /* + * Just use the default rule until we have infrastructure to find the + * best matching rule. + */ + r = &pf_default_rule; + + if (!r->max_states || r->states < r->max_states) + st = pool_get(&pf_state_pl, PR_NOWAIT); + if (st == NULL) { + pfi_maybe_destroy(kif); + return (ENOMEM); + } + bzero(st, sizeof(*st)); + + st->rule.ptr = r; + /* XXX get pointers to nat_rule and anchor */ + + /* fill in the rest of the state entry */ + pf_state_host_ntoh(&sp->lan, &st->lan); + pf_state_host_ntoh(&sp->gwy, &st->gwy); + pf_state_host_ntoh(&sp->ext, &st->ext); + + pf_state_peer_ntoh(&sp->src, &st->src); + pf_state_peer_ntoh(&sp->dst, &st->dst); + + bcopy(&sp->rt_addr, &st->rt_addr, sizeof(st->rt_addr)); +#ifdef __FreeBSD__ + st->creation = ntohl(sp->creation) + time_second; + st->expire = ntohl(sp->expire) + time_second; +#else + st->creation = ntohl(sp->creation) + time.tv_sec; + st->expire = ntohl(sp->expire) + time.tv_sec; +#endif + + st->af = sp->af; + st->proto = sp->proto; + st->direction = sp->direction; + st->log = sp->log; + st->timeout = sp->timeout; + st->allow_opts = sp->allow_opts; + + bcopy(sp->id, &st->id, sizeof(st->id)); + st->creatorid = sp->creatorid; + st->sync_flags = sp->sync_flags | PFSTATE_FROMSYNC; + + + if (pf_insert_state(kif, st)) { + pfi_maybe_destroy(kif); + pool_put(&pf_state_pl, st); + return (EINVAL); + } + + return (0); +} + +void +#ifdef __FreeBSD__ +pfsync_input(struct mbuf *m, __unused int off) +#else +pfsync_input(struct mbuf *m, ...) +#endif +{ + struct ip *ip = mtod(m, struct ip *); + struct pfsync_header *ph; +#ifdef __FreeBSD__ + struct pfsync_softc *sc = LIST_FIRST(&pfsync_list); +#else + struct pfsync_softc *sc = &pfsyncif; +#endif + struct pf_state *st, key; + struct pfsync_state *sp; + struct pfsync_state_upd *up; + struct pfsync_state_del *dp; + struct pfsync_state_clr *cp; + struct pfsync_state_upd_req *rup; + struct pfsync_state_bus *bus; + struct in_addr src; + struct mbuf *mp; + int iplen, action, error, i, s, count, offp; + + pfsyncstats.pfsyncs_ipackets++; + + /* verify that we have a sync interface configured */ + if (!sc->sc_sync_ifp || !pf_status.running) /* XXX PF_LOCK? */ + goto done; + + /* verify that the packet came in on the right interface */ + if (sc->sc_sync_ifp != m->m_pkthdr.rcvif) { + pfsyncstats.pfsyncs_badif++; + goto done; + } + + /* verify that the IP TTL is 255. */ + if (ip->ip_ttl != PFSYNC_DFLTTL) { + pfsyncstats.pfsyncs_badttl++; + goto done; + } + + iplen = ip->ip_hl << 2; + + if (m->m_pkthdr.len < iplen + sizeof(*ph)) { + pfsyncstats.pfsyncs_hdrops++; + goto done; + } + + if (iplen + sizeof(*ph) > m->m_len) { + if ((m = m_pullup(m, iplen + sizeof(*ph))) == NULL) { + pfsyncstats.pfsyncs_hdrops++; + goto done; + } + ip = mtod(m, struct ip *); + } + ph = (struct pfsync_header *)((char *)ip + iplen); + + /* verify the version */ + if (ph->version != PFSYNC_VERSION) { + pfsyncstats.pfsyncs_badver++; + goto done; + } + + action = ph->action; + count = ph->count; + + /* make sure it's a valid action code */ + if (action >= PFSYNC_ACT_MAX) { + pfsyncstats.pfsyncs_badact++; + goto done; + } + + /* Cheaper to grab this now than having to mess with mbufs later */ + src = ip->ip_src; + + switch (action) { + case PFSYNC_ACT_CLR: { + struct pfi_kif *kif; + u_int32_t creatorid; + if ((mp = m_pulldown(m, iplen + sizeof(*ph), + sizeof(*cp), &offp)) == NULL) { + pfsyncstats.pfsyncs_badlen++; + return; + } + cp = (struct pfsync_state_clr *)(mp->m_data + offp); + creatorid = cp->creatorid; + + s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + if (cp->ifname[0] == '\0') { + RB_FOREACH(st, pf_state_tree_id, &tree_id) { + if (st->creatorid == creatorid) + st->timeout = PFTM_PURGE; + } + } else { + kif = pfi_lookup_if(cp->ifname); + if (kif == NULL) { + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync_input: PFSYNC_ACT_CLR " + "bad interface: %s\n", cp->ifname); + splx(s); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + goto done; + } + RB_FOREACH(st, pf_state_tree_lan_ext, + &kif->pfik_lan_ext) { + if (st->creatorid == creatorid) + st->timeout = PFTM_PURGE; + } + } + pf_purge_expired_states(); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + splx(s); + + break; + } + case PFSYNC_ACT_INS: + if ((mp = m_pulldown(m, iplen + sizeof(*ph), + count * sizeof(*sp), &offp)) == NULL) { + pfsyncstats.pfsyncs_badlen++; + return; + } + + s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp); + i < count; i++, sp++) { + /* check for invalid values */ + if (sp->timeout >= PFTM_MAX || + sp->src.state > PF_TCPS_PROXY_DST || + sp->dst.state > PF_TCPS_PROXY_DST || + sp->direction > PF_OUT || + (sp->af != AF_INET && sp->af != AF_INET6)) { + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync_insert: PFSYNC_ACT_INS: " + "invalid value\n"); + pfsyncstats.pfsyncs_badstate++; + continue; + } + + if ((error = pfsync_insert_net_state(sp))) { + if (error == ENOMEM) { + splx(s); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + goto done; + } + continue; + } + } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + splx(s); + break; + case PFSYNC_ACT_UPD: + if ((mp = m_pulldown(m, iplen + sizeof(*ph), + count * sizeof(*sp), &offp)) == NULL) { + pfsyncstats.pfsyncs_badlen++; + return; + } + + s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp); + i < count; i++, sp++) { + /* check for invalid values */ + if (sp->timeout >= PFTM_MAX || + sp->src.state > PF_TCPS_PROXY_DST || + sp->dst.state > PF_TCPS_PROXY_DST) { + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync_insert: PFSYNC_ACT_UPD: " + "invalid value\n"); + pfsyncstats.pfsyncs_badstate++; + continue; + } + + bcopy(sp->id, &key.id, sizeof(key.id)); + key.creatorid = sp->creatorid; + + st = pf_find_state_byid(&key); + if (st == NULL) { + /* insert the update */ + if (pfsync_insert_net_state(sp)) + pfsyncstats.pfsyncs_badstate++; + continue; + } + pf_state_peer_ntoh(&sp->src, &st->src); + pf_state_peer_ntoh(&sp->dst, &st->dst); +#ifdef __FreeBSD__ + st->expire = ntohl(sp->expire) + time_second; +#else + st->expire = ntohl(sp->expire) + time.tv_sec; +#endif + st->timeout = sp->timeout; + + } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + splx(s); + break; + /* + * It's not strictly necessary for us to support the "uncompressed" + * delete action, but it's relatively simple and maintains consistency. + */ + case PFSYNC_ACT_DEL: + if ((mp = m_pulldown(m, iplen + sizeof(*ph), + count * sizeof(*sp), &offp)) == NULL) { + pfsyncstats.pfsyncs_badlen++; + return; + } + + s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp); + i < count; i++, sp++) { + bcopy(sp->id, &key.id, sizeof(key.id)); + key.creatorid = sp->creatorid; + + st = pf_find_state_byid(&key); + if (st == NULL) { + pfsyncstats.pfsyncs_badstate++; + continue; + } + /* + * XXX + * pf_purge_expired_states() is expensive, + * we really want to purge the state directly. + */ + st->timeout = PFTM_PURGE; + st->sync_flags |= PFSTATE_FROMSYNC; + } + pf_purge_expired_states(); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + splx(s); + break; + case PFSYNC_ACT_UPD_C: { + int update_requested = 0; + + if ((mp = m_pulldown(m, iplen + sizeof(*ph), + count * sizeof(*up), &offp)) == NULL) { + pfsyncstats.pfsyncs_badlen++; + return; + } + + s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + for (i = 0, up = (struct pfsync_state_upd *)(mp->m_data + offp); + i < count; i++, up++) { + /* check for invalid values */ + if (up->timeout >= PFTM_MAX || + up->src.state > PF_TCPS_PROXY_DST || + up->dst.state > PF_TCPS_PROXY_DST) { + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync_insert: " + "PFSYNC_ACT_UPD_C: " + "invalid value\n"); + pfsyncstats.pfsyncs_badstate++; + continue; + } + + bcopy(up->id, &key.id, sizeof(key.id)); + key.creatorid = up->creatorid; + + st = pf_find_state_byid(&key); + if (st == NULL) { + /* We don't have this state. Ask for it. */ + pfsync_request_update(up, &src); + update_requested = 1; + pfsyncstats.pfsyncs_badstate++; + continue; + } + pf_state_peer_ntoh(&up->src, &st->src); + pf_state_peer_ntoh(&up->dst, &st->dst); +#ifdef __FreeBSD__ + st->expire = ntohl(up->expire) + time_second; +#else + st->expire = ntohl(up->expire) + time.tv_sec; +#endif + st->timeout = up->timeout; + } + if (update_requested) + pfsync_sendout(sc); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + splx(s); + break; + } + case PFSYNC_ACT_DEL_C: + if ((mp = m_pulldown(m, iplen + sizeof(*ph), + count * sizeof(*dp), &offp)) == NULL) { + pfsyncstats.pfsyncs_badlen++; + return; + } + + s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + for (i = 0, dp = (struct pfsync_state_del *)(mp->m_data + offp); + i < count; i++, dp++) { + bcopy(dp->id, &key.id, sizeof(key.id)); + key.creatorid = dp->creatorid; + + st = pf_find_state_byid(&key); + if (st == NULL) { + pfsyncstats.pfsyncs_badstate++; + continue; + } + /* + * XXX + * pf_purge_expired_states() is expensive, + * we really want to purge the state directly. + */ + st->timeout = PFTM_PURGE; + st->sync_flags |= PFSTATE_FROMSYNC; + } + pf_purge_expired_states(); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + splx(s); + break; + case PFSYNC_ACT_INS_F: + case PFSYNC_ACT_DEL_F: + /* not implemented */ + break; + case PFSYNC_ACT_UREQ: + if ((mp = m_pulldown(m, iplen + sizeof(*ph), + count * sizeof(*rup), &offp)) == NULL) { + pfsyncstats.pfsyncs_badlen++; + return; + } + + s = splsoftnet(); + /* XXX send existing. pfsync_pack_state should handle this. */ +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + if (sc->sc_mbuf != NULL) + pfsync_sendout(sc); + for (i = 0, + rup = (struct pfsync_state_upd_req *)(mp->m_data + offp); + i < count; i++, rup++) { + bcopy(rup->id, &key.id, sizeof(key.id)); + key.creatorid = rup->creatorid; + + if (key.id == 0 && key.creatorid == 0) { +#ifdef __FreeBSD__ + sc->sc_ureq_received = time_uptime; +#else + sc->sc_ureq_received = mono_time.tv_sec; +#endif + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received " + "bulk update request\n"); + pfsync_send_bus(sc, PFSYNC_BUS_START); +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulk_tmo, 1 * hz, + pfsync_bulk_update, + LIST_FIRST(&pfsync_list)); +#else + timeout_add(&sc->sc_bulk_tmo, 1 * hz); +#endif + } else { + st = pf_find_state_byid(&key); + if (st == NULL) { + pfsyncstats.pfsyncs_badstate++; + continue; + } + pfsync_pack_state(PFSYNC_ACT_UPD, st, 0); + } + } + if (sc->sc_mbuf != NULL) + pfsync_sendout(sc); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + splx(s); + break; + case PFSYNC_ACT_BUS: + /* If we're not waiting for a bulk update, who cares. */ + if (sc->sc_ureq_sent == 0) + break; + + if ((mp = m_pulldown(m, iplen + sizeof(*ph), + sizeof(*bus), &offp)) == NULL) { + pfsyncstats.pfsyncs_badlen++; + return; + } + bus = (struct pfsync_state_bus *)(mp->m_data + offp); + switch (bus->status) { + case PFSYNC_BUS_START: +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulkfail_tmo, + pf_pool_limits[PF_LIMIT_STATES].limit / + (PFSYNC_BULKPACKETS * sc->sc_maxcount), + pfsync_bulkfail, LIST_FIRST(&pfsync_list)); +#else + timeout_add(&sc->sc_bulkfail_tmo, + pf_pool_limits[PF_LIMIT_STATES].limit / + (PFSYNC_BULKPACKETS * sc->sc_maxcount)); +#endif + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received bulk " + "update start\n"); + break; + case PFSYNC_BUS_END: +#ifdef __FreeBSD__ + if (time_uptime - ntohl(bus->endtime) >= +#else + if (mono_time.tv_sec - ntohl(bus->endtime) >= +#endif + sc->sc_ureq_sent) { + /* that's it, we're happy */ + sc->sc_ureq_sent = 0; + sc->sc_bulk_tries = 0; +#ifdef __FreeBSD__ + callout_stop(&sc->sc_bulkfail_tmo); +#else + timeout_del(&sc->sc_bulkfail_tmo); +#endif + pfsync_sync_ok = 1; + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received valid " + "bulk update end\n"); + } else { + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received invalid " + "bulk update end: bad timestamp\n"); + } + break; + } + break; + } + +done: + if (m) + m_freem(m); } int @@ -252,9 +837,15 @@ pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, int pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { +#ifndef __FreeBSD__ + struct proc *p = curproc; +#endif struct pfsync_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; - int s; + struct ip_moptions *imo = &sc->sc_imo; + struct pfsyncreq pfsyncr; + struct ifnet *sifp; + int s, error; switch (cmd) { case SIOCSIFADDR: @@ -272,11 +863,135 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) if (ifr->ifr_mtu > MCLBYTES) ifr->ifr_mtu = MCLBYTES; s = splnet(); - if (ifr->ifr_mtu < ifp->if_mtu) +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + if (ifr->ifr_mtu < ifp->if_mtu) { pfsync_sendout(sc); + } pfsync_setmtu(sc, ifr->ifr_mtu); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; + case SIOCGETPFSYNC: +#ifdef __FreeBSD__ + /* XXX: read unlocked */ +#endif + bzero(&pfsyncr, sizeof(pfsyncr)); + if (sc->sc_sync_ifp) + strlcpy(pfsyncr.pfsyncr_syncif, + sc->sc_sync_ifp->if_xname, IFNAMSIZ); + pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates; + if ((error = copyout(&pfsyncr, ifr->ifr_data, sizeof(pfsyncr)))) + return (error); + break; + case SIOCSETPFSYNC: +#ifdef __FreeBSD__ + if ((error = suser(curthread)) != 0) +#else + if ((error = suser(p, p->p_acflag)) != 0) +#endif + return (error); + if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr)))) + return (error); + + if (pfsyncr.pfsyncr_maxupdates > 255) + return (EINVAL); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates; + + if (pfsyncr.pfsyncr_syncif[0] == 0) { + sc->sc_sync_ifp = NULL; + if (sc->sc_mbuf_net != NULL) { + /* Don't keep stale pfsync packets around. */ + s = splnet(); + m_freem(sc->sc_mbuf_net); + sc->sc_mbuf_net = NULL; + sc->sc_statep_net.s = NULL; + splx(s); + } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + break; + } + if ((sifp = ifunit(pfsyncr.pfsyncr_syncif)) == NULL) { +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + return (EINVAL); + } + else if (sifp == sc->sc_sync_ifp) { +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + break; + } + + s = splnet(); + if (sifp->if_mtu < sc->sc_if.if_mtu || + (sc->sc_sync_ifp != NULL && + sifp->if_mtu < sc->sc_sync_ifp->if_mtu) || + sifp->if_mtu < MCLBYTES - sizeof(struct ip)) + pfsync_sendout(sc); + sc->sc_sync_ifp = sifp; + + pfsync_setmtu(sc, sc->sc_if.if_mtu); + + if (imo->imo_num_memberships > 0) { + in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); + imo->imo_multicast_ifp = NULL; + } + + if (sc->sc_sync_ifp) { + struct in_addr addr; + +#ifdef __FreeBSD__ + PF_UNLOCK(); /* addmulti mallocs w/ WAITOK */ + addr.s_addr = htonl(INADDR_PFSYNC_GROUP); +#else + addr.s_addr = INADDR_PFSYNC_GROUP; +#endif + if ((imo->imo_membership[0] = + in_addmulti(&addr, sc->sc_sync_ifp)) == NULL) { + splx(s); + return (ENOBUFS); + } + imo->imo_num_memberships++; + imo->imo_multicast_ifp = sc->sc_sync_ifp; + imo->imo_multicast_ttl = PFSYNC_DFLTTL; + imo->imo_multicast_loop = 0; + + /* Request a full state table update. */ +#ifdef __FreeBSD__ + PF_LOCK(); + sc->sc_ureq_sent = time_uptime; +#else + sc->sc_ureq_sent = mono_time.tv_sec; +#endif + pfsync_sync_ok = 0; + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: requesting bulk update\n"); +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, + pfsync_bulkfail, LIST_FIRST(&pfsync_list)); +#else + timeout_add(&sc->sc_bulkfail_tmo, 5 * hz); +#endif + pfsync_request_update(NULL, NULL); + pfsync_sendout(sc); + } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + splx(s); + + break; + default: return (ENOTTY); } @@ -285,35 +1000,66 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } void -pfsync_setmtu(sc, mtu) - struct pfsync_softc *sc; - int mtu; +pfsync_setmtu(struct pfsync_softc *sc, int mtu_req) { - sc->sc_count = (mtu - sizeof(struct pfsync_header)) / - sizeof(struct pf_state); + int mtu; + + if (sc->sc_sync_ifp && sc->sc_sync_ifp->if_mtu < mtu_req) + mtu = sc->sc_sync_ifp->if_mtu; + else + mtu = mtu_req; + + sc->sc_maxcount = (mtu - sizeof(struct pfsync_header)) / + sizeof(struct pfsync_state); + if (sc->sc_maxcount > 254) + sc->sc_maxcount = 254; sc->sc_if.if_mtu = sizeof(struct pfsync_header) + - sc->sc_count * sizeof(struct pf_state); + sc->sc_maxcount * sizeof(struct pfsync_state); } struct mbuf * -pfsync_get_mbuf(sc, action) - struct pfsync_softc *sc; - u_int8_t action; +pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action, void **sp) { -#ifndef __FreeBSD__ - extern int hz; -#endif struct pfsync_header *h; struct mbuf *m; int len; +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { sc->sc_if.if_oerrors++; return (NULL); } - len = sc->sc_if.if_mtu; + switch (action) { + case PFSYNC_ACT_CLR: + len = sizeof(struct pfsync_header) + + sizeof(struct pfsync_state_clr); + break; + case PFSYNC_ACT_UPD_C: + len = (sc->sc_maxcount * sizeof(struct pfsync_state_upd)) + + sizeof(struct pfsync_header); + break; + case PFSYNC_ACT_DEL_C: + len = (sc->sc_maxcount * sizeof(struct pfsync_state_del)) + + sizeof(struct pfsync_header); + break; + case PFSYNC_ACT_UREQ: + len = (sc->sc_maxcount * sizeof(struct pfsync_state_upd_req)) + + sizeof(struct pfsync_header); + break; + case PFSYNC_ACT_BUS: + len = sizeof(struct pfsync_header) + + sizeof(struct pfsync_state_bus); + break; + default: + len = (sc->sc_maxcount * sizeof(struct pfsync_state)) + + sizeof(struct pfsync_header); + break; + } + if (len > MHLEN) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { @@ -321,146 +1067,318 @@ pfsync_get_mbuf(sc, action) sc->sc_if.if_oerrors++; return (NULL); } - } - m->m_pkthdr.rcvif = NULL; - m->m_pkthdr.len = m->m_len = len; + m->m_data += (MCLBYTES - len) &~ (sizeof(long) - 1); + } else + MH_ALIGN(m, len); + m->m_pkthdr.rcvif = NULL; + m->m_pkthdr.len = m->m_len = sizeof(struct pfsync_header); h = mtod(m, struct pfsync_header *); h->version = PFSYNC_VERSION; h->af = 0; h->count = 0; h->action = action; - sc->sc_mbuf = m; - sc->sc_ptr = (struct pf_state *)((char *)h + PFSYNC_HDRLEN); + *sp = (void *)((char *)h + PFSYNC_HDRLEN); #ifdef __FreeBSD__ callout_reset(&sc->sc_tmo, hz, pfsync_timeout, LIST_FIRST(&pfsync_list)); #else timeout_add(&sc->sc_tmo, hz); #endif - return (m); } -/* - * XXX: This function should be called with PF_LOCK held as it references - * pf_state. - */ int -pfsync_pack_state(action, st) - u_int8_t action; - struct pf_state *st; +pfsync_pack_state(u_int8_t action, struct pf_state *st, int compress) { #ifdef __FreeBSD__ - struct pfsync_softc *sc = LIST_FIRST(&pfsync_list); + struct ifnet *ifp = &(LIST_FIRST(&pfsync_list))->sc_if; #else - extern struct timeval time; struct ifnet *ifp = &pfsyncif.sc_if; - struct pfsync_softc *sc = ifp->if_softc; #endif - struct pfsync_header *h; - struct pf_state *sp; - struct pf_rule *r = st->rule.ptr; - struct mbuf *m; + struct pfsync_softc *sc = ifp->if_softc; + struct pfsync_header *h, *h_net; + struct pfsync_state *sp = NULL; + struct pfsync_state_upd *up = NULL; + struct pfsync_state_del *dp = NULL; + struct pf_rule *r; u_long secs; - int s, ret; - - if (action >= PFSYNC_ACT_MAX) - return (EINVAL); + int s, ret = 0; + u_int8_t i = 255, newaction = 0; #ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif /* - * XXX - * If we need to check mutex owned, PF_LOCK should be - * declared in pflog.ko. - * - * PF_LOCK_ASSERT(); + * If a packet falls in the forest and there's nobody around to + * hear, does it make a sound? */ - KASSERT((!LIST_EMPTY(&pfsync_list)), ("pfsync: no interface")); -#endif + if (ifp->if_bpf == NULL && sc->sc_sync_ifp == NULL) { + /* Don't leave any stale pfsync packets hanging around. */ + if (sc->sc_mbuf != NULL) { + m_freem(sc->sc_mbuf); + sc->sc_mbuf = NULL; + sc->sc_statep.s = NULL; + } + return (0); + } + + if (action >= PFSYNC_ACT_MAX) + return (EINVAL); + s = splnet(); - m = sc->sc_mbuf; - if (m == NULL) { - if ((m = pfsync_get_mbuf(sc, action)) == NULL) { + if (sc->sc_mbuf == NULL) { + if ((sc->sc_mbuf = pfsync_get_mbuf(sc, action, + (void *)&sc->sc_statep.s)) == NULL) { splx(s); return (ENOMEM); } - h = mtod(m, struct pfsync_header *); + h = mtod(sc->sc_mbuf, struct pfsync_header *); } else { - h = mtod(m, struct pfsync_header *); + h = mtod(sc->sc_mbuf, struct pfsync_header *); if (h->action != action) { pfsync_sendout(sc); - if ((m = pfsync_get_mbuf(sc, action)) == NULL) { + if ((sc->sc_mbuf = pfsync_get_mbuf(sc, action, + (void *)&sc->sc_statep.s)) == NULL) { splx(s); return (ENOMEM); } - h = mtod(m, struct pfsync_header *); + h = mtod(sc->sc_mbuf, struct pfsync_header *); + } else { + /* + * If it's an update, look in the packet to see if + * we already have an update for the state. + */ + if (action == PFSYNC_ACT_UPD && sc->sc_maxupdates) { + struct pfsync_state *usp = + (void *)((char *)h + PFSYNC_HDRLEN); + + for (i = 0; i < h->count; i++) { + if (!memcmp(usp->id, &st->id, + PFSYNC_ID_LEN) && + usp->creatorid == st->creatorid) { + sp = usp; + sp->updates++; + break; + } + usp++; + } + } } } - sp = sc->sc_ptr++; - h->count++; - bzero(sp, sizeof(*sp)); - - bcopy(&st->lan, &sp->lan, sizeof(sp->lan)); - bcopy(&st->gwy, &sp->gwy, sizeof(sp->gwy)); - bcopy(&st->ext, &sp->ext, sizeof(sp->ext)); - - pf_state_peer_hton(&st->src, &sp->src); - pf_state_peer_hton(&st->dst, &sp->dst); - - bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); #ifdef __FreeBSD__ secs = time_second; + + st->pfsync_time = time_uptime; #else secs = time.tv_sec; + + st->pfsync_time = mono_time.tv_sec; #endif - sp->creation = htonl(secs - st->creation); + TAILQ_REMOVE(&state_updates, st, u.s.entry_updates); + TAILQ_INSERT_TAIL(&state_updates, st, u.s.entry_updates); + + if (sp == NULL) { + /* not a "duplicate" update */ + i = 255; + sp = sc->sc_statep.s++; + sc->sc_mbuf->m_pkthdr.len = + sc->sc_mbuf->m_len += sizeof(struct pfsync_state); + h->count++; + bzero(sp, sizeof(*sp)); + + bcopy(&st->id, sp->id, sizeof(sp->id)); + sp->creatorid = st->creatorid; + + strlcpy(sp->ifname, st->u.s.kif->pfik_name, sizeof(sp->ifname)); + pf_state_host_hton(&st->lan, &sp->lan); + pf_state_host_hton(&st->gwy, &sp->gwy); + pf_state_host_hton(&st->ext, &sp->ext); + + bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); + + sp->creation = htonl(secs - st->creation); + sp->packets[0] = htonl(st->packets[0]); + sp->packets[1] = htonl(st->packets[1]); + sp->bytes[0] = htonl(st->bytes[0]); + sp->bytes[1] = htonl(st->bytes[1]); + if ((r = st->rule.ptr) == NULL) + sp->rule = htonl(-1); + else + sp->rule = htonl(r->nr); + if ((r = st->anchor.ptr) == NULL) + sp->anchor = htonl(-1); + else + sp->anchor = htonl(r->nr); + sp->af = st->af; + sp->proto = st->proto; + sp->direction = st->direction; + sp->log = st->log; + sp->allow_opts = st->allow_opts; + sp->timeout = st->timeout; + + sp->sync_flags = st->sync_flags & PFSTATE_NOSYNC; + } + + pf_state_peer_hton(&st->src, &sp->src); + pf_state_peer_hton(&st->dst, &sp->dst); + if (st->expire <= secs) sp->expire = htonl(0); else sp->expire = htonl(st->expire - secs); - sp->packets[0] = htonl(st->packets[0]); - sp->packets[1] = htonl(st->packets[1]); - sp->bytes[0] = htonl(st->bytes[0]); - sp->bytes[1] = htonl(st->bytes[1]); - if (r == NULL) - sp->rule.nr = htonl(-1); - else - sp->rule.nr = htonl(r->nr); - sp->af = st->af; - sp->proto = st->proto; - sp->direction = st->direction; - sp->log = st->log; - sp->allow_opts = st->allow_opts; - - ret = 0; - if (h->count == sc->sc_count) + + /* do we need to build "compressed" actions for network transfer? */ + if (sc->sc_sync_ifp && compress) { + switch (action) { + case PFSYNC_ACT_UPD: + newaction = PFSYNC_ACT_UPD_C; + break; + case PFSYNC_ACT_DEL: + newaction = PFSYNC_ACT_DEL_C; + break; + default: + /* by default we just send the uncompressed states */ + break; + } + } + + if (newaction) { + if (sc->sc_mbuf_net == NULL) { + if ((sc->sc_mbuf_net = pfsync_get_mbuf(sc, newaction, + (void *)&sc->sc_statep_net.s)) == NULL) { + splx(s); + return (ENOMEM); + } + } + h_net = mtod(sc->sc_mbuf_net, struct pfsync_header *); + + switch (newaction) { + case PFSYNC_ACT_UPD_C: + if (i != 255) { + up = (void *)((char *)h_net + + PFSYNC_HDRLEN + (i * sizeof(*up))); + up->updates++; + } else { + h_net->count++; + sc->sc_mbuf_net->m_pkthdr.len = + sc->sc_mbuf_net->m_len += sizeof(*up); + up = sc->sc_statep_net.u++; + + bzero(up, sizeof(*up)); + bcopy(&st->id, up->id, sizeof(up->id)); + up->creatorid = st->creatorid; + } + up->timeout = st->timeout; + up->expire = sp->expire; + up->src = sp->src; + up->dst = sp->dst; + break; + case PFSYNC_ACT_DEL_C: + sc->sc_mbuf_net->m_pkthdr.len = + sc->sc_mbuf_net->m_len += sizeof(*dp); + dp = sc->sc_statep_net.d++; + h_net->count++; + + bzero(dp, sizeof(*dp)); + bcopy(&st->id, dp->id, sizeof(dp->id)); + dp->creatorid = st->creatorid; + break; + } + } + + if (h->count == sc->sc_maxcount || + (sc->sc_maxupdates && (sp->updates >= sc->sc_maxupdates))) ret = pfsync_sendout(sc); splx(s); - return (0); + return (ret); } +/* This must be called in splnet() */ int -pfsync_clear_state(st) - struct pf_state *st; +pfsync_request_update(struct pfsync_state_upd *up, struct in_addr *src) { #ifdef __FreeBSD__ - struct pfsync_softc *sc = LIST_FIRST(&pfsync_list); + struct ifnet *ifp = &(LIST_FIRST(&pfsync_list))->sc_if; #else struct ifnet *ifp = &pfsyncif.sc_if; +#endif + struct pfsync_header *h; struct pfsync_softc *sc = ifp->if_softc; + struct pfsync_state_upd_req *rup; + int s, ret = 0; /* make the compiler happy */ + +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif + if (sc->sc_mbuf == NULL) { + if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_UREQ, + (void *)&sc->sc_statep.s)) == NULL) { + splx(s); + return (ENOMEM); + } + h = mtod(sc->sc_mbuf, struct pfsync_header *); + } else { + h = mtod(sc->sc_mbuf, struct pfsync_header *); + if (h->action != PFSYNC_ACT_UREQ) { + pfsync_sendout(sc); + if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_UREQ, + (void *)&sc->sc_statep.s)) == NULL) { + splx(s); + return (ENOMEM); + } + h = mtod(sc->sc_mbuf, struct pfsync_header *); + } + } + + if (src != NULL) + sc->sc_sendaddr = *src; + sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*rup); + h->count++; + rup = sc->sc_statep.r++; + bzero(rup, sizeof(*rup)); + if (up != NULL) { + bcopy(up->id, rup->id, sizeof(rup->id)); + rup->creatorid = up->creatorid; + } + + if (h->count == sc->sc_maxcount) + ret = pfsync_sendout(sc); + + return (ret); +} + +int +pfsync_clear_states(u_int32_t creatorid, char *ifname) +{ +#ifdef __FreeBSD__ + struct ifnet *ifp = &(LIST_FIRST(&pfsync_list))->sc_if; +#else + struct ifnet *ifp = &pfsyncif.sc_if; #endif - struct mbuf *m = sc->sc_mbuf; + struct pfsync_softc *sc = ifp->if_softc; + struct pfsync_state_clr *cp; int s, ret; s = splnet(); - if (m == NULL && (m = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR)) == NULL) { +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif + if (sc->sc_mbuf != NULL) + pfsync_sendout(sc); + if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR, + (void *)&sc->sc_statep.c)) == NULL) { splx(s); return (ENOMEM); } + sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*cp); + cp = sc->sc_statep.c; + cp->creatorid = creatorid; + if (ifname != NULL) + strlcpy(cp->ifname, ifname, IFNAMSIZ); ret = (pfsync_sendout(sc)); splx(s); @@ -473,10 +1391,141 @@ pfsync_timeout(void *v) struct pfsync_softc *sc = v; int s; - /* We don't need PF_LOCK/PF_UNLOCK here! */ s = splnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif pfsync_sendout(sc); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + splx(s); +} + +void +pfsync_send_bus(struct pfsync_softc *sc, u_int8_t status) +{ + struct pfsync_state_bus *bus; + +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif + if (sc->sc_mbuf != NULL) + pfsync_sendout(sc); + + if (pfsync_sync_ok && + (sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_BUS, + (void *)&sc->sc_statep.b)) != NULL) { + sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*bus); + bus = sc->sc_statep.b; + bus->creatorid = pf_status.hostid; + bus->status = status; +#ifdef __FreeBSD__ + bus->endtime = htonl(time_uptime - sc->sc_ureq_received); +#else + bus->endtime = htonl(mono_time.tv_sec - sc->sc_ureq_received); +#endif + pfsync_sendout(sc); + } +} + +void +pfsync_bulk_update(void *v) +{ + struct pfsync_softc *sc = v; + int s, i = 0; + struct pf_state *state; + +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + s = splnet(); + if (sc->sc_mbuf != NULL) + pfsync_sendout(sc); + + /* + * Grab at most PFSYNC_BULKPACKETS worth of states which have not + * been sent since the latest request was made. + */ + while ((state = TAILQ_FIRST(&state_updates)) != NULL && + ++i < (sc->sc_maxcount * PFSYNC_BULKPACKETS)) { + if (state->pfsync_time > sc->sc_ureq_received) { + /* we're done */ + pfsync_send_bus(sc, PFSYNC_BUS_END); + sc->sc_ureq_received = 0; +#ifdef __FreeBSD__ + callout_stop(&sc->sc_bulk_tmo); +#else + timeout_del(&sc->sc_bulk_tmo); +#endif + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: bulk update complete\n"); + break; + } else { + /* send an update and move to end of list */ + if (!state->sync_flags) + pfsync_pack_state(PFSYNC_ACT_UPD, state, 0); +#ifdef __FreeBSD__ + state->pfsync_time = time_uptime; +#else + state->pfsync_time = mono_time.tv_sec; +#endif + TAILQ_REMOVE(&state_updates, state, u.s.entry_updates); + TAILQ_INSERT_TAIL(&state_updates, state, + u.s.entry_updates); + + /* look again for more in a bit */ +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulk_tmo, 1, pfsync_timeout, + LIST_FIRST(&pfsync_list)); +#else + timeout_add(&sc->sc_bulk_tmo, 1); +#endif + } + } + if (sc->sc_mbuf != NULL) + pfsync_sendout(sc); splx(s); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif +} + +void +pfsync_bulkfail(void *v) +{ + struct pfsync_softc *sc = v; + +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) { + /* Try again in a bit */ +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulkfail, + LIST_FIRST(&pfsync_list)); +#else + timeout_add(&sc->sc_bulkfail_tmo, 5 * hz); +#endif + pfsync_request_update(NULL, NULL); + pfsync_sendout(sc); + } else { + /* Pretend like the transfer was ok */ + sc->sc_ureq_sent = 0; + sc->sc_bulk_tries = 0; + pfsync_sync_ok = 1; + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: failed to receive " + "bulk update status\n"); +#ifdef __FreeBSD__ + callout_stop(&sc->sc_bulkfail_tmo); +#else + timeout_del(&sc->sc_bulkfail_tmo); +#endif + } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif } int @@ -484,15 +1533,20 @@ pfsync_sendout(sc) struct pfsync_softc *sc; { struct ifnet *ifp = &sc->sc_if; - struct mbuf *m = sc->sc_mbuf; + struct mbuf *m; #ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); callout_stop(&sc->sc_tmo); #else timeout_del(&sc->sc_tmo); #endif + + if (sc->sc_mbuf == NULL) + return (0); + m = sc->sc_mbuf; sc->sc_mbuf = NULL; - sc->sc_ptr = NULL; + sc->sc_statep.s = NULL; #ifdef __FreeBSD__ KASSERT(m != NULL, ("pfsync_sendout: null mbuf")); @@ -502,7 +1556,75 @@ pfsync_sendout(sc) bpf_mtap(ifp->if_bpf, m); #endif - m_freem(m); + if (sc->sc_mbuf_net) { + m_freem(m); + m = sc->sc_mbuf_net; + sc->sc_mbuf_net = NULL; + sc->sc_statep_net.s = NULL; + } + + if (sc->sc_sync_ifp) { + struct ip *ip; + struct ifaddr *ifa; + struct sockaddr sa; + + M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); + if (m == NULL) { + pfsyncstats.pfsyncs_onomem++; + return (0); + } + ip = mtod(m, struct ip *); + ip->ip_v = IPVERSION; + ip->ip_hl = sizeof(*ip) >> 2; + ip->ip_tos = IPTOS_LOWDELAY; +#ifdef __FreeBSD__ + ip->ip_len = m->m_pkthdr.len; +#else + ip->ip_len = htons(m->m_pkthdr.len); +#endif + ip->ip_id = htons(ip_randomid()); +#ifdef __FreeBSD__ + ip->ip_off = IP_DF; +#else + ip->ip_off = htons(IP_DF); +#endif + ip->ip_ttl = PFSYNC_DFLTTL; + ip->ip_p = IPPROTO_PFSYNC; + ip->ip_sum = 0; + + bzero(&sa, sizeof(sa)); + sa.sa_family = AF_INET; + ifa = ifaof_ifpforaddr(&sa, sc->sc_sync_ifp); + if (ifa == NULL) + return (0); + ip->ip_src.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr; + +#ifdef __FreeBSD__ + if (sc->sc_sendaddr.s_addr == htonl(INADDR_PFSYNC_GROUP)) +#else + if (sc->sc_sendaddr.s_addr == INADDR_PFSYNC_GROUP) +#endif + m->m_flags |= M_MCAST; + ip->ip_dst = sc->sc_sendaddr; +#ifdef __FreeBSD__ + sc->sc_sendaddr.s_addr = htonl(INADDR_PFSYNC_GROUP); +#else + sc->sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP; +#endif + + pfsyncstats.pfsyncs_opackets++; + +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) + pfsyncstats.pfsyncs_oerrors++; + +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + } else + m_freem(m); return (0); } diff --git a/sys/contrib/pf/net/if_pfsync.h b/sys/contrib/pf/net/if_pfsync.h index 23539ba..b276b4a 100644 --- a/sys/contrib/pf/net/if_pfsync.h +++ b/sys/contrib/pf/net/if_pfsync.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: if_pfsync.h,v 1.2 2002/12/11 18:31:26 mickey Exp $ */ +/* $OpenBSD: if_pfsync.h,v 1.13 2004/03/22 04:54:17 mcbride Exp $ */ /* * Copyright (c) 2001 Michael Shalayeff @@ -30,47 +30,217 @@ #ifndef _NET_IF_PFSYNC_H_ #define _NET_IF_PFSYNC_H_ + +#define PFSYNC_ID_LEN sizeof(u_int64_t) + +struct pfsync_state_scrub { + u_int16_t pfss_flags; + u_int8_t pfss_ttl; /* stashed TTL */ + u_int8_t scrub_flag; + u_int32_t pfss_ts_mod; /* timestamp modulation */ +} __packed; + +struct pfsync_state_host { + struct pf_addr addr; + u_int16_t port; + u_int16_t pad[3]; +} __packed; + +struct pfsync_state_peer { + struct pfsync_state_scrub scrub; /* state is scrubbed */ + u_int32_t seqlo; /* Max sequence number sent */ + u_int32_t seqhi; /* Max the other end ACKd + win */ + u_int32_t seqdiff; /* Sequence number modulator */ + u_int16_t max_win; /* largest window (pre scaling) */ + u_int16_t mss; /* Maximum segment size option */ + u_int8_t state; /* active state level */ + u_int8_t wscale; /* window scaling factor */ + u_int8_t scrub_flag; + u_int8_t pad[5]; +} __packed; + +struct pfsync_state { + u_int32_t id[2]; + char ifname[IFNAMSIZ]; + struct pfsync_state_host lan; + struct pfsync_state_host gwy; + struct pfsync_state_host ext; + struct pfsync_state_peer src; + struct pfsync_state_peer dst; + struct pf_addr rt_addr; + u_int32_t rule; + u_int32_t anchor; + u_int32_t nat_rule; + u_int32_t creation; + u_int32_t expire; + u_int32_t packets[2]; + u_int32_t bytes[2]; + u_int32_t creatorid; + sa_family_t af; + u_int8_t proto; + u_int8_t direction; + u_int8_t log; + u_int8_t allow_opts; + u_int8_t timeout; + u_int8_t sync_flags; + u_int8_t updates; +} __packed; + +struct pfsync_state_upd { + u_int32_t id[2]; + struct pfsync_state_peer src; + struct pfsync_state_peer dst; + u_int32_t creatorid; + u_int32_t expire; + u_int8_t timeout; + u_int8_t updates; + u_int8_t pad[6]; +} __packed; + +struct pfsync_state_del { + u_int32_t id[2]; + u_int32_t creatorid; + struct { + u_int8_t state; + } src; + struct { + u_int8_t state; + } dst; + u_int8_t pad[2]; +} __packed; + +struct pfsync_state_upd_req { + u_int32_t id[2]; + u_int32_t creatorid; + u_int32_t pad; +} __packed; + +struct pfsync_state_clr { + char ifname[IFNAMSIZ]; + u_int32_t creatorid; + u_int32_t pad; +} __packed; + +struct pfsync_state_bus { + u_int32_t creatorid; + u_int32_t endtime; + u_int8_t status; +#define PFSYNC_BUS_START 1 +#define PFSYNC_BUS_END 2 + u_int8_t pad[7]; +} __packed; + #ifdef _KERNEL + +union sc_statep { + struct pfsync_state *s; + struct pfsync_state_upd *u; + struct pfsync_state_del *d; + struct pfsync_state_clr *c; + struct pfsync_state_bus *b; + struct pfsync_state_upd_req *r; +}; + +extern int pfsync_sync_ok; + struct pfsync_softc { - struct ifnet sc_if; + struct ifnet sc_if; + struct ifnet *sc_sync_ifp; + struct ip_moptions sc_imo; #ifdef __FreeBSD__ - struct callout sc_tmo; + struct callout sc_tmo; + struct callout sc_bulk_tmo; + struct callout sc_bulkfail_tmo; #else - struct timeout sc_tmo; + struct timeout sc_tmo; + struct timeout sc_bulk_tmo; + struct timeout sc_bulkfail_tmo; #endif - struct mbuf *sc_mbuf; /* current cummulative mbuf */ - struct pf_state *sc_ptr; /* current ongoing state */ - int sc_count; /* number of states in one mtu */ + struct in_addr sc_sendaddr; + struct mbuf *sc_mbuf; /* current cummulative mbuf */ + struct mbuf *sc_mbuf_net; /* current cummulative mbuf */ + union sc_statep sc_statep; + union sc_statep sc_statep_net; + u_int32_t sc_ureq_received; + u_int32_t sc_ureq_sent; + int sc_bulk_tries; + int sc_maxcount; /* number of states in mtu */ + int sc_maxupdates; /* number of updates/state */ #ifdef __FreeBSD__ LIST_ENTRY(pfsync_softc) sc_next; #endif }; #endif + struct pfsync_header { u_int8_t version; -#define PFSYNC_VERSION 1 +#define PFSYNC_VERSION 2 u_int8_t af; u_int8_t action; -#define PFSYNC_ACT_CLR 0 -#define PFSYNC_ACT_INS 1 -#define PFSYNC_ACT_UPD 2 -#define PFSYNC_ACT_DEL 3 -#define PFSYNC_ACT_MAX 4 +#define PFSYNC_ACT_CLR 0 /* clear all states */ +#define PFSYNC_ACT_INS 1 /* insert state */ +#define PFSYNC_ACT_UPD 2 /* update state */ +#define PFSYNC_ACT_DEL 3 /* delete state */ +#define PFSYNC_ACT_UPD_C 4 /* "compressed" state update */ +#define PFSYNC_ACT_DEL_C 5 /* "compressed" state delete */ +#define PFSYNC_ACT_INS_F 6 /* insert fragment */ +#define PFSYNC_ACT_DEL_F 7 /* delete fragments */ +#define PFSYNC_ACT_UREQ 8 /* request "uncompressed" state */ +#define PFSYNC_ACT_BUS 9 /* Bulk Update Status */ +#define PFSYNC_ACT_MAX 10 u_int8_t count; -}; +} __packed; +#define PFSYNC_BULKPACKETS 1 /* # of packets per timeout */ +#define PFSYNC_MAX_BULKTRIES 12 #define PFSYNC_HDRLEN sizeof(struct pfsync_header) #define PFSYNC_ACTIONS \ - "CLR ST", "INS ST", "UPD ST", "DEL ST" + "CLR ST", "INS ST", "UPD ST", "DEL ST", \ + "UPD ST COMP", "DEL ST COMP", "INS FR", "DEL FR", \ + "UPD REQ", "BLK UPD STAT" + +#define PFSYNC_DFLTTL 255 + +struct pfsyncstats { + u_long pfsyncs_ipackets; /* total input packets, IPv4 */ + u_long pfsyncs_ipackets6; /* total input packets, IPv6 */ + u_long pfsyncs_badif; /* not the right interface */ + u_long pfsyncs_badttl; /* TTL is not PFSYNC_DFLTTL */ + u_long pfsyncs_hdrops; /* packets shorter than header */ + u_long pfsyncs_badver; /* bad (incl unsupp) version */ + u_long pfsyncs_badact; /* bad action */ + u_long pfsyncs_badlen; /* data length does not match */ + u_long pfsyncs_badauth; /* bad authentication */ + u_long pfsyncs_badstate; /* insert/lookup failed */ + + u_long pfsyncs_opackets; /* total output packets, IPv4 */ + u_long pfsyncs_opackets6; /* total output packets, IPv6 */ + u_long pfsyncs_onomem; /* no memory for an mbuf for a send */ + u_long pfsyncs_oerrors; /* ip output error */ +}; + +/* + * Configuration structure for SIOCSETPFSYNC SIOCGETPFSYNC + */ +struct pfsyncreq { + char pfsyncr_syncif[IFNAMSIZ]; + int pfsyncr_maxupdates; + int pfsyncr_authlevel; +}; +#define SIOCSETPFSYNC _IOW('i', 247, struct ifreq) +#define SIOCGETPFSYNC _IOWR('i', 248, struct ifreq) + #define pf_state_peer_hton(s,d) do { \ (d)->seqlo = htonl((s)->seqlo); \ (d)->seqhi = htonl((s)->seqhi); \ (d)->seqdiff = htonl((s)->seqdiff); \ (d)->max_win = htons((s)->max_win); \ + (d)->mss = htons((s)->mss); \ (d)->state = (s)->state; \ + (d)->wscale = (s)->wscale; \ } while (0) #define pf_state_peer_ntoh(s,d) do { \ @@ -78,15 +248,47 @@ struct pfsync_header { (d)->seqhi = ntohl((s)->seqhi); \ (d)->seqdiff = ntohl((s)->seqdiff); \ (d)->max_win = ntohs((s)->max_win); \ + (d)->mss = ntohs((s)->mss); \ (d)->state = (s)->state; \ + (d)->wscale = (s)->wscale; \ +} while (0) + +#define pf_state_host_hton(s,d) do { \ + bcopy(&(s)->addr, &(d)->addr, sizeof((d)->addr)); \ + (d)->port = (s)->port; \ +} while (0) + +#define pf_state_host_ntoh(s,d) do { \ + bcopy(&(s)->addr, &(d)->addr, sizeof((d)->addr)); \ + (d)->port = (s)->port; \ } while (0) #ifdef _KERNEL -int pfsync_clear_state(struct pf_state *); -int pfsync_pack_state(u_int8_t, struct pf_state *); -#define pfsync_insert_state(st) pfsync_pack_state(PFSYNC_ACT_INS, (st)) -#define pfsync_update_state(st) pfsync_pack_state(PFSYNC_ACT_UPD, (st)) -#define pfsync_delete_state(st) pfsync_pack_state(PFSYNC_ACT_DEL, (st)) +#ifdef __FreeBSD__ +void pfsync_input(struct mbuf *, __unused int); +#else +void pfsync_input(struct mbuf *, ...); +#endif +int pfsync_clear_states(u_int32_t, char *); +int pfsync_pack_state(u_int8_t, struct pf_state *, int); +#define pfsync_insert_state(st) do { \ + if ((st->rule.ptr->rule_flag & PFRULE_NOSYNC) || \ + (st->proto == IPPROTO_PFSYNC)) \ + st->sync_flags |= PFSTATE_NOSYNC; \ + else if (!st->sync_flags) \ + pfsync_pack_state(PFSYNC_ACT_INS, (st), 1); \ + st->sync_flags &= ~PFSTATE_FROMSYNC; \ +} while (0) +#define pfsync_update_state(st) do { \ + if (!st->sync_flags) \ + pfsync_pack_state(PFSYNC_ACT_UPD, (st), 1); \ + st->sync_flags &= ~PFSTATE_FROMSYNC; \ +} while (0) +#define pfsync_delete_state(st) do { \ + if (!st->sync_flags) \ + pfsync_pack_state(PFSYNC_ACT_DEL, (st), 1); \ + st->sync_flags &= ~PFSTATE_FROMSYNC; \ +} while (0) #endif #endif /* _NET_IF_PFSYNC_H_ */ diff --git a/sys/contrib/pf/net/pf.c b/sys/contrib/pf/net/pf.c index 46f45c8..e7f2d11 100644 --- a/sys/contrib/pf/net/pf.c +++ b/sys/contrib/pf/net/pf.c @@ -1,8 +1,9 @@ /* $FreeBSD$ */ -/* $OpenBSD: pf.c,v 1.389.2.4 2004/04/30 23:27:57 brad Exp $ */ +/* $OpenBSD: pf.c,v 1.433 2004/03/26 22:20:57 dhartmei Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2002,2003 Henning Brauer * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -62,6 +63,7 @@ #include <sys/time.h> #ifdef __FreeBSD__ #include <sys/sysctl.h> +#include <sys/endian.h> #else #include <sys/pool.h> #endif @@ -91,7 +93,10 @@ #endif #include <net/pfvar.h> #include <net/if_pflog.h> + +#if NPFSYNC > 0 #include <net/if_pfsync.h> +#endif /* NPFSYNC > 0 */ #ifdef INET6 #include <netinet/ip6.h> @@ -104,30 +109,15 @@ #endif #endif /* INET6 */ -#ifdef ALTQ -#include <altq/if_altq.h> -#endif - #ifdef __FreeBSD__ #include <machine/in_cksum.h> -#if (__FreeBSD_version >= 500112) #include <sys/limits.h> -#else -#include <machine/limits.h> -#endif #include <sys/ucred.h> -#endif -#ifdef __FreeBSD__ extern int ip_optcopy(struct ip *, struct ip *); -#if (__FreeBSD_version < 501105) -int ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu, - u_long if_hwassist_flags, int sw_csum); -#endif #endif #define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x -struct pf_state_tree; /* * Global variables @@ -140,10 +130,10 @@ struct pf_palist pf_pabuf; struct pf_altqqueue *pf_altqs_active; struct pf_altqqueue *pf_altqs_inactive; struct pf_status pf_status; -struct ifnet *status_ifp; u_int32_t ticket_altqs_active; u_int32_t ticket_altqs_inactive; +int altqs_inactive_open; u_int32_t ticket_pabuf; #ifdef __FreeBSD__ @@ -154,17 +144,13 @@ struct timeout pf_expire_to; /* expire timeout */ #ifdef __FreeBSD__ -uma_zone_t pf_tree_pl, pf_rule_pl, pf_addr_pl; +uma_zone_t pf_src_tree_pl, pf_rule_pl; uma_zone_t pf_state_pl, pf_altq_pl, pf_pooladdr_pl; #else -struct pool pf_tree_pl, pf_rule_pl, pf_addr_pl; +struct pool pf_src_tree_pl, pf_rule_pl; struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl; #endif -void pf_dynaddr_update(void *); -#ifdef __FreeBSD__ -void pf_dynaddr_update_event(void *arg, struct ifnet *ifp); -#endif void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t); void pf_print_state(struct pf_state *); void pf_print_flags(u_int8_t); @@ -189,62 +175,62 @@ void pf_send_tcp(const struct pf_rule *, sa_family_t, void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, sa_family_t, struct pf_rule *); struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, - int, int, struct ifnet *, + int, int, struct pfi_kif *, 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 pfi_kif *, struct pf_src_node **, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t, struct pf_addr *, u_int16_t *); int pf_test_tcp(struct pf_rule **, struct pf_state **, - int, struct ifnet *, struct mbuf *, int, int, + int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, struct pf_rule **, struct pf_ruleset **); int pf_test_udp(struct pf_rule **, struct pf_state **, - int, struct ifnet *, struct mbuf *, int, int, + int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, struct pf_rule **, struct pf_ruleset **); int pf_test_icmp(struct pf_rule **, struct pf_state **, - int, struct ifnet *, struct mbuf *, int, int, + int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, struct pf_rule **, struct pf_ruleset **); int pf_test_other(struct pf_rule **, struct pf_state **, - int, struct ifnet *, struct mbuf *, int, void *, + int, struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, struct pf_rule **, struct pf_ruleset **); int pf_test_fragment(struct pf_rule **, int, - struct ifnet *, struct mbuf *, void *, + struct pfi_kif *, struct mbuf *, void *, struct pf_pdesc *, struct pf_rule **, struct pf_ruleset **); int pf_test_state_tcp(struct pf_state **, int, - struct ifnet *, struct mbuf *, int, int, + struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *, u_short *); int pf_test_state_udp(struct pf_state **, int, - struct ifnet *, struct mbuf *, int, int, + struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *); int pf_test_state_icmp(struct pf_state **, int, - struct ifnet *, struct mbuf *, int, int, + struct pfi_kif *, struct mbuf *, int, void *, struct pf_pdesc *); int pf_test_state_other(struct pf_state **, int, - struct ifnet *, struct pf_pdesc *); + struct pfi_kif *, 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, struct ifnet *, struct pf_state *); -int pf_socket_lookup(uid_t *, gid_t *, int, sa_family_t, +int pf_socket_lookup(uid_t *, gid_t *, int, struct pf_pdesc *); u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, sa_family_t); @@ -258,22 +244,30 @@ int pf_check_proto_cksum(struct mbuf *, int, int, u_int8_t, sa_family_t); int pf_addr_wrap_neq(struct pf_addr_wrap *, struct pf_addr_wrap *); +static int pf_add_mbuf_tag(struct mbuf *, u_int); +struct pf_state *pf_find_state_recurse(struct pfi_kif *, + struct pf_state *, u_int8_t); #ifdef __FreeBSD__ int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len); struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX]; #else -struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = - { { &pf_state_pl, PFSTATE_HIWAT }, { &pf_frent_pl, PFFRAG_FRENT_HIWAT } }; +struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = { + { &pf_state_pl, PFSTATE_HIWAT }, + { &pf_src_tree_pl, PFSNODE_HIWAT }, + { &pf_frent_pl, PFFRAG_FRENT_HIWAT } +}; #endif #define STATE_LOOKUP() \ do { \ if (direction == PF_IN) \ - *state = pf_find_state(&tree_ext_gwy, &key); \ + *state = pf_find_state_recurse( \ + kif, &key, PF_EXT_GWY); \ else \ - *state = pf_find_state(&tree_lan_ext, &key); \ + *state = pf_find_state_recurse( \ + kif, &key, PF_LAN_EXT); \ if (*state == NULL) \ return (PF_DROP); \ if (direction == PF_OUT && \ @@ -281,8 +275,8 @@ struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = (*state)->rule.ptr->direction == PF_OUT) || \ ((*state)->rule.ptr->rt == PF_REPLYTO && \ (*state)->rule.ptr->direction == PF_IN)) && \ - (*state)->rt_ifp != NULL && \ - (*state)->rt_ifp != ifp) \ + (*state)->rt_kif != NULL && \ + (*state)->rt_kif != kif) \ return (PF_PASS); \ } while (0) @@ -294,18 +288,85 @@ 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_state_compare(struct pf_tree_node *, - struct pf_tree_node *); +#define BOUND_IFACE(r, k) (((r)->rule_flag & PFRULE_IFBOUND) ? (k) : \ + ((r)->rule_flag & PFRULE_GRBOUND) ? (k)->pfik_parent : \ + (k)->pfik_parent->pfik_parent) + +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 *); +static __inline int pf_state_compare_ext_gwy(struct pf_state *, + struct pf_state *); +static __inline int pf_state_compare_id(struct pf_state *, + struct pf_state *); + +struct pf_src_tree tree_src_tracking; + +struct pf_state_tree_id tree_id; +struct pf_state_queue state_updates; -struct pf_state_tree tree_lan_ext, tree_ext_gwy; -RB_GENERATE(pf_state_tree, pf_tree_node, entry, pf_state_compare); +RB_GENERATE(pf_src_tree, pf_src_node, entry, pf_src_compare); +RB_GENERATE(pf_state_tree_lan_ext, pf_state, + u.s.entry_lan_ext, pf_state_compare_lan_ext); +RB_GENERATE(pf_state_tree_ext_gwy, pf_state, + u.s.entry_ext_gwy, pf_state_compare_ext_gwy); +RB_GENERATE(pf_state_tree_id, pf_state, + u.s.entry_id, pf_state_compare_id); #ifdef __FreeBSD__ static int #else static __inline int #endif -pf_state_compare(struct pf_tree_node *a, struct pf_tree_node *b) +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); +} + +#ifdef __FreeBSD__ +static int +#else +static __inline int +#endif +pf_state_compare_lan_ext(struct pf_state *a, struct pf_state *b) { int diff; @@ -316,62 +377,153 @@ pf_state_compare(struct pf_tree_node *a, struct pf_tree_node *b) switch (a->af) { #ifdef INET case AF_INET: - if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) + if (a->lan.addr.addr32[0] > b->lan.addr.addr32[0]) return (1); - if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) + if (a->lan.addr.addr32[0] < b->lan.addr.addr32[0]) return (-1); - if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) + if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) return (1); - if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) return (-1); break; #endif /* INET */ #ifdef INET6 case AF_INET6: - if (a->addr[0].addr32[3] > b->addr[0].addr32[3]) + if (a->lan.addr.addr32[3] > b->lan.addr.addr32[3]) return (1); - if (a->addr[0].addr32[3] < b->addr[0].addr32[3]) + if (a->lan.addr.addr32[3] < b->lan.addr.addr32[3]) return (-1); - if (a->addr[1].addr32[3] > b->addr[1].addr32[3]) + if (a->ext.addr.addr32[3] > b->ext.addr.addr32[3]) return (1); - if (a->addr[1].addr32[3] < b->addr[1].addr32[3]) + if (a->ext.addr.addr32[3] < b->ext.addr.addr32[3]) return (-1); - if (a->addr[0].addr32[2] > b->addr[0].addr32[2]) + if (a->lan.addr.addr32[2] > b->lan.addr.addr32[2]) return (1); - if (a->addr[0].addr32[2] < b->addr[0].addr32[2]) + if (a->lan.addr.addr32[2] < b->lan.addr.addr32[2]) return (-1); - if (a->addr[1].addr32[2] > b->addr[1].addr32[2]) + if (a->ext.addr.addr32[2] > b->ext.addr.addr32[2]) return (1); - if (a->addr[1].addr32[2] < b->addr[1].addr32[2]) + if (a->ext.addr.addr32[2] < b->ext.addr.addr32[2]) return (-1); - if (a->addr[0].addr32[1] > b->addr[0].addr32[1]) + if (a->lan.addr.addr32[1] > b->lan.addr.addr32[1]) return (1); - if (a->addr[0].addr32[1] < b->addr[0].addr32[1]) + if (a->lan.addr.addr32[1] < b->lan.addr.addr32[1]) return (-1); - if (a->addr[1].addr32[1] > b->addr[1].addr32[1]) + if (a->ext.addr.addr32[1] > b->ext.addr.addr32[1]) return (1); - if (a->addr[1].addr32[1] < b->addr[1].addr32[1]) + if (a->ext.addr.addr32[1] < b->ext.addr.addr32[1]) return (-1); - if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) + if (a->lan.addr.addr32[0] > b->lan.addr.addr32[0]) return (1); - if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) + if (a->lan.addr.addr32[0] < b->lan.addr.addr32[0]) return (-1); - if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) + if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) return (1); - if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) return (-1); break; #endif /* INET6 */ } - if ((diff = a->port[0] - b->port[0]) != 0) + if ((diff = a->lan.port - b->lan.port) != 0) return (diff); - if ((diff = a->port[1] - b->port[1]) != 0) + if ((diff = a->ext.port - b->ext.port) != 0) return (diff); return (0); } +#ifdef __FreeBSD__ +static int +#else +static __inline int +#endif +pf_state_compare_ext_gwy(struct pf_state *a, struct pf_state *b) +{ + int diff; + + if ((diff = a->proto - b->proto) != 0) + return (diff); + if ((diff = a->af - b->af) != 0) + return (diff); + switch (a->af) { +#ifdef INET + case AF_INET: + if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) + return (1); + if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) + return (-1); + if (a->gwy.addr.addr32[0] > b->gwy.addr.addr32[0]) + return (1); + if (a->gwy.addr.addr32[0] < b->gwy.addr.addr32[0]) + return (-1); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (a->ext.addr.addr32[3] > b->ext.addr.addr32[3]) + return (1); + if (a->ext.addr.addr32[3] < b->ext.addr.addr32[3]) + return (-1); + if (a->gwy.addr.addr32[3] > b->gwy.addr.addr32[3]) + return (1); + if (a->gwy.addr.addr32[3] < b->gwy.addr.addr32[3]) + return (-1); + if (a->ext.addr.addr32[2] > b->ext.addr.addr32[2]) + return (1); + if (a->ext.addr.addr32[2] < b->ext.addr.addr32[2]) + return (-1); + if (a->gwy.addr.addr32[2] > b->gwy.addr.addr32[2]) + return (1); + if (a->gwy.addr.addr32[2] < b->gwy.addr.addr32[2]) + return (-1); + if (a->ext.addr.addr32[1] > b->ext.addr.addr32[1]) + return (1); + if (a->ext.addr.addr32[1] < b->ext.addr.addr32[1]) + return (-1); + if (a->gwy.addr.addr32[1] > b->gwy.addr.addr32[1]) + return (1); + if (a->gwy.addr.addr32[1] < b->gwy.addr.addr32[1]) + return (-1); + if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) + return (1); + if (a->ext.addr.addr32[0] < b->ext.addr.addr32[0]) + return (-1); + if (a->gwy.addr.addr32[0] > b->gwy.addr.addr32[0]) + return (1); + if (a->gwy.addr.addr32[0] < b->gwy.addr.addr32[0]) + return (-1); + break; +#endif /* INET6 */ + } + + if ((diff = a->ext.port - b->ext.port) != 0) + return (diff); + if ((diff = a->gwy.port - b->gwy.port) != 0) + return (diff); + + return (0); +} + +#ifdef __FreeBSD__ +static int +#else +static __inline int +#endif +pf_state_compare_id(struct pf_state *a, struct pf_state *b) +{ + if (a->id > b->id) + return (1); + if (a->id < b->id) + return (-1); + if (a->creatorid > b->creatorid) + return (1); + if (a->creatorid < b->creatorid) + return (-1); + + return (0); +} + #ifdef INET6 void pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) @@ -393,36 +545,144 @@ pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) #endif struct pf_state * -pf_find_state(struct pf_state_tree *tree, struct pf_tree_node *key) +pf_find_state_byid(struct pf_state *key) +{ + pf_status.fcounters[FCNT_STATE_SEARCH]++; + return (RB_FIND(pf_state_tree_id, &tree_id, key)); +} + +struct pf_state * +pf_find_state_recurse(struct pfi_kif *kif, struct pf_state *key, u_int8_t tree) { - struct pf_tree_node *k; + struct pf_state *s; pf_status.fcounters[FCNT_STATE_SEARCH]++; - k = RB_FIND(pf_state_tree, tree, key); - if (k) - return (k->state); - else + + switch (tree) { + case PF_LAN_EXT: + for (; kif != NULL; kif = kif->pfik_parent) { + s = RB_FIND(pf_state_tree_lan_ext, + &kif->pfik_lan_ext, key); + if (s != NULL) + return (s); + } + return (NULL); + case PF_EXT_GWY: + for (; kif != NULL; kif = kif->pfik_parent) { + s = RB_FIND(pf_state_tree_ext_gwy, + &kif->pfik_ext_gwy, key); + if (s != NULL) + return (s); + } return (NULL); + default: + panic("pf_find_state_recurse"); + } } -int -pf_insert_state(struct pf_state *state) +struct pf_state * +pf_find_state_all(struct pf_state *key, u_int8_t tree, int *more) { - struct pf_tree_node *keya, *keyb; + struct pf_state *s, *ss = NULL; + struct pfi_kif *kif; - keya = pool_get(&pf_tree_pl, PR_NOWAIT); - if (keya == NULL) - return (-1); - keya->state = state; - keya->proto = state->proto; - keya->af = state->af; - PF_ACPY(&keya->addr[0], &state->lan.addr, state->af); - keya->port[0] = state->lan.port; - PF_ACPY(&keya->addr[1], &state->ext.addr, state->af); - keya->port[1] = state->ext.port; + pf_status.fcounters[FCNT_STATE_SEARCH]++; + switch (tree) { + case PF_LAN_EXT: + TAILQ_FOREACH(kif, &pfi_statehead, pfik_w_states) { + s = RB_FIND(pf_state_tree_lan_ext, + &kif->pfik_lan_ext, key); + if (s == NULL) + continue; + if (more == NULL) + return (s); + ss = s; + (*more)++; + } + return (ss); + case PF_EXT_GWY: + TAILQ_FOREACH(kif, &pfi_statehead, pfik_w_states) { + s = RB_FIND(pf_state_tree_ext_gwy, + &kif->pfik_ext_gwy, key); + if (s == NULL) + continue; + if (more == NULL) + return (s); + ss = s; + (*more)++; + } + return (ss); + default: + panic("pf_find_state_all"); + } +} + +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); + } +#ifdef __FreeBSD__ + (*sn)->creation = time_second; +#else + (*sn)->creation = time.tv_sec; +#endif + (*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 pfi_kif *kif, struct pf_state *state) +{ /* Thou MUST NOT insert multiple duplicate keys */ - if (RB_INSERT(pf_state_tree, &tree_lan_ext, keya) != NULL) { + state->u.s.kif = kif; + if (RB_INSERT(pf_state_tree_lan_ext, &kif->pfik_lan_ext, state)) { if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf: state insert failed: tree_lan_ext"); printf(" lan: "); @@ -434,28 +694,14 @@ pf_insert_state(struct pf_state *state) printf(" ext: "); pf_print_host(&state->ext.addr, state->ext.port, state->af); + if (state->sync_flags & PFSTATE_FROMSYNC) + printf(" (from sync)"); printf("\n"); } - pool_put(&pf_tree_pl, keya); return (-1); } - keyb = pool_get(&pf_tree_pl, PR_NOWAIT); - if (keyb == NULL) { - /* Need to pull out the other state */ - RB_REMOVE(pf_state_tree, &tree_lan_ext, keya); - pool_put(&pf_tree_pl, keya); - return (-1); - } - keyb->state = state; - keyb->proto = state->proto; - keyb->af = state->af; - PF_ACPY(&keyb->addr[0], &state->ext.addr, state->af); - keyb->port[0] = state->ext.port; - PF_ACPY(&keyb->addr[1], &state->gwy.addr, state->af); - keyb->port[1] = state->gwy.port; - - if (RB_INSERT(pf_state_tree, &tree_ext_gwy, keyb) != NULL) { + if (RB_INSERT(pf_state_tree_ext_gwy, &kif->pfik_ext_gwy, state)) { if (pf_status.debug >= PF_DEBUG_MISC) { printf("pf: state insert failed: tree_ext_gwy"); printf(" lan: "); @@ -467,16 +713,43 @@ pf_insert_state(struct pf_state *state) printf(" ext: "); pf_print_host(&state->ext.addr, state->ext.port, state->af); + if (state->sync_flags & PFSTATE_FROMSYNC) + printf(" (from sync)"); + printf("\n"); + } + RB_REMOVE(pf_state_tree_lan_ext, &kif->pfik_lan_ext, state); + return (-1); + } + + if (state->id == 0 && state->creatorid == 0) { + state->id = htobe64(pf_status.stateid++); + state->creatorid = pf_status.hostid; + } + if (RB_INSERT(pf_state_tree_id, &tree_id, state) != NULL) { + if (pf_status.debug >= PF_DEBUG_MISC) { +#ifdef __FreeBSD__ + printf("pf: state insert failed: " + "id: %016llx creatorid: %08x", + (long long)be64toh(state->id), + ntohl(state->creatorid)); +#else + printf("pf: state insert failed: " + "id: %016llx creatorid: %08x", + betoh64(state->id), ntohl(state->creatorid)); +#endif + if (state->sync_flags & PFSTATE_FROMSYNC) + printf(" (from sync)"); printf("\n"); } - RB_REMOVE(pf_state_tree, &tree_lan_ext, keya); - pool_put(&pf_tree_pl, keya); - pool_put(&pf_tree_pl, keyb); + RB_REMOVE(pf_state_tree_lan_ext, &kif->pfik_lan_ext, state); + RB_REMOVE(pf_state_tree_ext_gwy, &kif->pfik_ext_gwy, state); return (-1); } + TAILQ_INSERT_HEAD(&state_updates, state, u.s.entry_updates); pf_status.fcounters[FCNT_STATE_INSERT]++; pf_status.states++; + pfi_attach_state(kif); #if NPFSYNC pfsync_insert_state(state); #endif @@ -499,6 +772,7 @@ pf_purge_timeout(void *arg) s = splsoftnet(); pf_purge_expired_states(); pf_purge_expired_fragments(); + pf_purge_expired_src_nodes(); splx(s); #ifdef __FreeBSD__ PF_UNLOCK(); @@ -562,71 +836,109 @@ pf_state_expires(const struct pf_state *state) } void -pf_purge_expired_states(void) +pf_purge_expired_src_nodes(void) { - struct pf_tree_node *cur, *peer, *next; - struct pf_tree_node key; + struct pf_src_node *cur, *next; - for (cur = RB_MIN(pf_state_tree, &tree_ext_gwy); cur; cur = next) { - next = RB_NEXT(pf_state_tree, &tree_ext_gwy, cur); + for (cur = RB_MIN(pf_src_tree, &tree_src_tracking); cur; cur = next) { + next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur); #ifdef __FreeBSD__ - if (pf_state_expires(cur->state) <= (u_int32_t)time_second) { + if (cur->states <= 0 && cur->expire <= time_second) { #else - if (pf_state_expires(cur->state) <= time.tv_sec) { + if (cur->states <= 0 && cur->expire <= time.tv_sec) { #endif - if (cur->state->src.state == PF_TCPS_PROXY_DST) - pf_send_tcp(cur->state->rule.ptr, - cur->state->af, - &cur->state->ext.addr, - &cur->state->lan.addr, - cur->state->ext.port, - cur->state->lan.port, - cur->state->src.seqhi, - cur->state->src.seqlo + 1, - 0, - TH_RST|TH_ACK, 0, 0); - RB_REMOVE(pf_state_tree, &tree_ext_gwy, cur); - - /* Need this key's peer (in the other tree) */ - key.state = cur->state; - key.proto = cur->state->proto; - key.af = cur->state->af; - PF_ACPY(&key.addr[0], &cur->state->lan.addr, - cur->state->af); - key.port[0] = cur->state->lan.port; - PF_ACPY(&key.addr[1], &cur->state->ext.addr, - cur->state->af); - key.port[1] = cur->state->ext.port; - - peer = RB_FIND(pf_state_tree, &tree_lan_ext, &key); + 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]; #ifdef __FreeBSD__ - KASSERT((peer), ("peer null :%s", __FUNCTION__)); - KASSERT((peer->state == cur->state), - ("peer->state != cur->state: %s", __FUNCTION__)); + s->src_node->expire = time_second + timeout; #else - KASSERT(peer); - KASSERT(peer->state == cur->state); + s->src_node->expire = time.tv_sec + timeout; #endif - RB_REMOVE(pf_state_tree, &tree_lan_ext, peer); + } + } + 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]; +#ifdef __FreeBSD__ + s->nat_src_node->expire = time_second + timeout; +#else + s->nat_src_node->expire = time.tv_sec + timeout; +#endif + } + } + s->src_node = s->nat_src_node = NULL; +} +void +pf_purge_expired_states(void) +{ + struct pf_state *cur, *next; + + for (cur = RB_MIN(pf_state_tree_id, &tree_id); + cur; cur = next) { + next = RB_NEXT(pf_state_tree_id, &tree_id, cur); + +#ifdef __FreeBSD__ + if (pf_state_expires(cur) <= time_second) { +#else + if (pf_state_expires(cur) <= time.tv_sec) { +#endif + if (cur->src.state == PF_TCPS_PROXY_DST) + pf_send_tcp(cur->rule.ptr, cur->af, + &cur->ext.addr, &cur->lan.addr, + cur->ext.port, cur->lan.port, + cur->src.seqhi, cur->src.seqlo + 1, 0, + TH_RST|TH_ACK, 0, 0); + RB_REMOVE(pf_state_tree_ext_gwy, + &cur->u.s.kif->pfik_ext_gwy, cur); + RB_REMOVE(pf_state_tree_lan_ext, + &cur->u.s.kif->pfik_lan_ext, cur); + RB_REMOVE(pf_state_tree_id, &tree_id, cur); #if NPFSYNC - pfsync_delete_state(cur->state); + pfsync_delete_state(cur); #endif - if (--cur->state->rule.ptr->states <= 0) - pf_rm_rule(NULL, cur->state->rule.ptr); - if (cur->state->nat_rule.ptr != NULL) - if (--cur->state->nat_rule.ptr->states <= 0) - pf_rm_rule(NULL, - cur->state->nat_rule.ptr); - if (cur->state->anchor.ptr != NULL) - if (--cur->state->anchor.ptr->states <= 0) - pf_rm_rule(NULL, - cur->state->anchor.ptr); - pf_normalize_tcp_cleanup(cur->state); - pool_put(&pf_state_pl, cur->state); - pool_put(&pf_tree_pl, cur); - pool_put(&pf_tree_pl, peer); + 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 && + 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) + pf_rm_rule(NULL, cur->anchor.ptr); + pf_normalize_tcp_cleanup(cur); + pfi_detach_state(cur->u.s.kif); + TAILQ_REMOVE(&state_updates, cur, u.s.entry_updates); + pool_put(&pf_state_pl, cur); pf_status.fcounters[FCNT_STATE_REMOVALS]++; pf_status.states--; } @@ -666,129 +978,6 @@ pf_tbladdr_copyout(struct pf_addr_wrap *aw) kt->pfrkt_cnt : -1; } -int -pf_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af) -{ - if (aw->type != PF_ADDR_DYNIFTL) - return (0); - aw->p.dyn = pool_get(&pf_addr_pl, PR_NOWAIT); - if (aw->p.dyn == NULL) - return (1); - bcopy(aw->v.ifname, aw->p.dyn->ifname, sizeof(aw->p.dyn->ifname)); - aw->p.dyn->ifp = ifunit(aw->p.dyn->ifname); - if (aw->p.dyn->ifp == NULL) { - pool_put(&pf_addr_pl, aw->p.dyn); - aw->p.dyn = NULL; - return (1); - } - aw->p.dyn->addr = &aw->v.a.addr; - aw->p.dyn->af = af; - aw->p.dyn->undefined = 1; -#ifndef __FreeBSD__ - aw->p.dyn->hook_cookie = hook_establish( - aw->p.dyn->ifp->if_addrhooks, 1, - pf_dynaddr_update, aw->p.dyn); - if (aw->p.dyn->hook_cookie == NULL) { - pool_put(&pf_addr_pl, aw->p.dyn); - aw->p.dyn = NULL; - return (1); - } -#else - PF_UNLOCK(); - aw->p.dyn->hook_cookie = EVENTHANDLER_REGISTER(ifaddr_event, - pf_dynaddr_update_event, aw->p.dyn, EVENTHANDLER_PRI_ANY); - PF_LOCK(); - if (aw->p.dyn->hook_cookie == NULL) { - pool_put(&pf_addr_pl, aw->p.dyn); - aw->p.dyn = NULL; - return (1); - } -#endif - pf_dynaddr_update(aw->p.dyn); - return (0); -} - -#ifdef __FreeBSD__ -void -pf_dynaddr_update_event(void *arg, struct ifnet *ifp) -{ - PF_LOCK(); - pf_dynaddr_update(arg); - PF_UNLOCK(); -} -#endif - -void -pf_dynaddr_update(void *p) -{ - struct pf_addr_dyn *ad = (struct pf_addr_dyn *)p; - struct ifaddr *ia; - int s, changed = 0; - - if (ad == NULL || ad->ifp == NULL) - panic("pf_dynaddr_update"); - s = splsoftnet(); - TAILQ_FOREACH(ia, &ad->ifp->if_addrlist, ifa_list) - if (ia->ifa_addr != NULL && - ia->ifa_addr->sa_family == ad->af) { - if (ad->af == AF_INET) { - struct in_addr *a, *b; - - a = &ad->addr->v4; - b = &((struct sockaddr_in *)ia->ifa_addr) - ->sin_addr; - if (ad->undefined || - memcmp(a, b, sizeof(*a))) { - bcopy(b, a, sizeof(*a)); - changed = 1; - } - } else if (ad->af == AF_INET6) { - struct in6_addr *a, *b; - - a = &ad->addr->v6; - b = &((struct sockaddr_in6 *)ia->ifa_addr) - ->sin6_addr; - if (ad->undefined || - memcmp(a, b, sizeof(*a))) { - bcopy(b, a, sizeof(*a)); - changed = 1; - } - } - if (changed) - ad->undefined = 0; - break; - } - if (ia == NULL) - ad->undefined = 1; - splx(s); -} - -void -pf_dynaddr_remove(struct pf_addr_wrap *aw) -{ - if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL) - return; -#ifndef __FreeBSD__ - hook_disestablish(aw->p.dyn->ifp->if_addrhooks, - aw->p.dyn->hook_cookie); -#else - PF_UNLOCK(); - EVENTHANDLER_DEREGISTER(ifaddr_event, aw->p.dyn->hook_cookie); - PF_LOCK(); -#endif - pool_put(&pf_addr_pl, aw->p.dyn); - aw->p.dyn = NULL; -} - -void -pf_dynaddr_copyout(struct pf_addr_wrap *aw) -{ - if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL) - return; - bcopy(aw->p.dyn->ifname, aw->v.ifname, sizeof(aw->v.ifname)); - aw->p.dyn = (struct pf_addr_dyn *)1; -} - void pf_print_host(struct pf_addr *addr, u_int16_t p, sa_family_t af) { @@ -934,7 +1123,7 @@ pf_calc_skip_steps(struct pf_rulequeue *rules) head[i] = cur; while (cur != NULL) { - if (cur->ifp != prev->ifp || cur->ifnot != prev->ifnot) + if (cur->kif != prev->kif || cur->ifnot != prev->ifnot) PF_SET_SKIP_STEPS(PF_SKIP_IFP); if (cur->direction != prev->direction) PF_SET_SKIP_STEPS(PF_SKIP_DIR); @@ -977,11 +1166,7 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) return (1); return (0); case PF_ADDR_DYNIFTL: - if (aw1->p.dyn->ifp != aw2->p.dyn->ifp) - return (1); - if (PF_ANEQ(&aw1->v.a.mask, &aw2->v.a.mask, 0)) - return (1); - return (0); + return (aw1->p.dyn->pfid_kt != aw2->p.dyn->pfid_kt); case PF_ADDR_NOROUTE: return (0); case PF_ADDR_TABLE: @@ -993,33 +1178,6 @@ pf_addr_wrap_neq(struct pf_addr_wrap *aw1, struct pf_addr_wrap *aw2) } void -pf_rule_set_qid(struct pf_rulequeue *rules) -{ - struct pf_rule *rule; - - TAILQ_FOREACH(rule, rules, entries) - if (rule->qname[0] != 0) { - rule->qid = pf_qname_to_qid(rule->qname); - if (rule->pqname[0] != 0) - rule->pqid = pf_qname_to_qid(rule->pqname); - else - rule->pqid = rule->qid; - } -} - -u_int32_t -pf_qname_to_qid(char *qname) -{ - struct pf_altq *altq; - - TAILQ_FOREACH(altq, pf_altqs_active, entries) - if (!strcmp(altq->qname, qname)) - return (altq->qid); - - return (0); -} - -void pf_update_anchor_rules() { struct pf_rule *rule; @@ -1232,9 +1390,6 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, struct tcphdr *th = NULL; /* make the compiler happy */ #ifdef __FreeBSD__ struct ip *ip; -#if (__FreeBSD_version < 501114) - struct route ro; -#endif #endif char *opt; @@ -1342,39 +1497,20 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af, h->ip_hl = sizeof(*h) >> 2; h->ip_tos = IPTOS_LOWDELAY; #ifdef __FreeBSD__ - h->ip_off = htons(path_mtu_discovery ? IP_DF : 0); + h->ip_off = path_mtu_discovery ? IP_DF : 0; + h->ip_len = len; #else h->ip_off = htons(ip_mtudisc ? IP_DF : 0); -#endif h->ip_len = htons(len); +#endif h->ip_ttl = ttl ? ttl : ip_defttl; h->ip_sum = 0; #ifdef __FreeBSD__ ip = mtod(m, struct ip *); - /* - * XXX - * OpenBSD changed ip_len/ip_off byte ordering! - * Because FreeBSD assumes host byte ordering we need to - * change here. - */ - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); -#if (__FreeBSD_version < 501114) - bzero(&ro, sizeof(ro)); - ip_rtaddr(ip->ip_dst, &ro); - PF_UNLOCK(); - ip_output(m, (void *)NULL, &ro, 0, (void *)NULL, - (void *)NULL); - PF_LOCK(); - if(ro.ro_rt) { - RTFREE(ro.ro_rt); - } -#else /* __FreeBSD_version >= 501114 */ PF_UNLOCK(); ip_output(m, (void *)NULL, (void *)NULL, 0, (void *)NULL, (void *)NULL); PF_LOCK(); -#endif #else /* ! __FreeBSD__ */ ip_output(m, (void *)NULL, (void *)NULL, 0, (void *)NULL, (void *)NULL); @@ -1452,7 +1588,7 @@ pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, NTOHS(ip->ip_off); PF_UNLOCK(); #endif - icmp_error(m0, type, code, 0, NULL); + icmp_error(m0, type, code, 0, (void *)NULL); #ifdef __FreeBSD__ PF_LOCK(); #endif @@ -1581,8 +1717,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); @@ -1590,10 +1726,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) || @@ -1677,7 +1811,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 @@ -1766,20 +1900,59 @@ 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_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); - 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->cur->addr.type == PF_ADDR_DYNIFTL) { + if (af == AF_INET) { + if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 && + (rpool->opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN) + return (1); + raddr = &rpool->cur->addr.p.dyn->pfid_addr4; + rmask = &rpool->cur->addr.p.dyn->pfid_mask4; + } else { + if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 && + (rpool->opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN) + return (1); + raddr = &rpool->cur->addr.p.dyn->pfid_addr6; + rmask = &rpool->cur->addr.p.dyn->pfid_mask6; + } + } else if (rpool->cur->addr.type == PF_ADDR_TABLE) { if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) return (1); /* unsupported */ } else { @@ -1839,6 +2012,11 @@ pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr, &rpool->tblidx, &rpool->counter, &raddr, &rmask, af)) goto get_addr; + } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { + if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, + &rpool->tblidx, &rpool->counter, + &raddr, &rmask, af)) + goto get_addr; } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af)) goto get_addr; @@ -1850,7 +2028,17 @@ pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr, if (pfr_pool_get(rpool->cur->addr.p.tbl, &rpool->tblidx, &rpool->counter, &raddr, &rmask, af)) { - /* table contain no address of type 'af' */ + /* table contains no address of type 'af' */ + if (rpool->cur != acur) + goto try_next; + return (1); + } + } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { + rpool->tblidx = -1; + if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, + &rpool->tblidx, &rpool->counter, + &raddr, &rmask, af)) { + /* table contains no address of type 'af' */ if (rpool->cur != acur) goto try_next; return (1); @@ -1866,10 +2054,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"); } @@ -1878,41 +2068,41 @@ 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_tree_node key; + 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 { key.af = af; key.proto = proto; - PF_ACPY(&key.addr[0], daddr, key.af); - PF_ACPY(&key.addr[1], naddr, key.af); - key.port[0] = dport; + PF_ACPY(&key.ext.addr, daddr, key.af); + PF_ACPY(&key.gwy.addr, naddr, key.af); + key.ext.port = dport; /* * port search; start random, step; * similar 2 portloop in in_pcbbind */ if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP)) { - key.port[1] = 0; - if (pf_find_state(&tree_ext_gwy, &key) == NULL) + key.gwy.port = 0; + if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) return (0); } else if (low == 0 && high == 0) { - key.port[1] = *nport; - if (pf_find_state(&tree_ext_gwy, &key) == NULL) { + key.gwy.port = *nport; + if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) return (0); - } } else if (low == high) { - key.port[1] = htons(low); - if (pf_find_state(&tree_ext_gwy, &key) == NULL) { + key.gwy.port = htons(low); + if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) { *nport = htons(low); return (0); } @@ -1928,16 +2118,16 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_pool *rpool, cut = arc4random() % (1 + high - low) + low; /* low <= cut <= high */ for (tmp = cut; tmp <= high; ++(tmp)) { - key.port[1] = htons(tmp); - if (pf_find_state(&tree_ext_gwy, &key) == + key.gwy.port = htons(tmp); + if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) { *nport = htons(tmp); return (0); } } for (tmp = cut - 1; tmp >= low; --(tmp)) { - key.port[1] = htons(tmp); - if (pf_find_state(&tree_ext_gwy, &key) == + key.gwy.port = htons(tmp); + if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL) { *nport = htons(tmp); return (0); @@ -1945,10 +2135,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: @@ -1956,7 +2146,6 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_pool *rpool, case PF_POOL_BITMASK: default: return (1); - break; } } while (! PF_AEQ(&init_addr, naddr, af) ); @@ -1965,7 +2154,7 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_pool *rpool, struct pf_rule * pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, - int direction, struct ifnet *ifp, struct pf_addr *saddr, u_int16_t sport, + int direction, struct pfi_kif *kif, struct pf_addr *saddr, u_int16_t sport, struct pf_addr *daddr, u_int16_t dport, int rs_num) { struct pf_rule *r, *rm = NULL, *anchorrule = NULL; @@ -1986,8 +2175,8 @@ pf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, } r->evaluations++; - if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || - (r->ifp == ifp && r->ifnot))) + if (r->kif != NULL && + (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; @@ -2033,7 +2222,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 pfi_kif *kif, 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) @@ -2041,16 +2230,16 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, struct pf_rule *r = NULL; if (direction == PF_OUT) { - r = pf_match_translation(pd, m, off, direction, ifp, saddr, + r = pf_match_translation(pd, m, off, direction, kif, saddr, sport, daddr, dport, PF_RULESET_BINAT); if (r == NULL) - r = pf_match_translation(pd, m, off, direction, ifp, + r = pf_match_translation(pd, m, off, direction, kif, saddr, sport, daddr, dport, PF_RULESET_NAT); } else { - r = pf_match_translation(pd, m, off, direction, ifp, saddr, + r = pf_match_translation(pd, m, off, direction, kif, saddr, sport, daddr, dport, PF_RULESET_RDR); if (r == NULL) - r = pf_match_translation(pd, m, off, direction, ifp, + r = pf_match_translation(pd, m, off, direction, kif, saddr, sport, daddr, dport, PF_RULESET_BINAT); } @@ -2060,11 +2249,10 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, case PF_NOBINAT: case PF_NORDR: 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", @@ -2076,21 +2264,58 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, case PF_BINAT: switch (direction) { case PF_OUT: - if (r->rpool.cur->addr.type == - PF_ADDR_DYNIFTL && - r->rpool.cur->addr.p.dyn->undefined) - return (NULL); - else + if (r->rpool.cur->addr.type == PF_ADDR_DYNIFTL){ + if (pd->af == AF_INET) { + if (r->rpool.cur->addr.p.dyn-> + pfid_acnt4 < 1) + return (NULL); + PF_POOLMASK(naddr, + &r->rpool.cur->addr.p.dyn-> + pfid_addr4, + &r->rpool.cur->addr.p.dyn-> + pfid_mask4, + saddr, AF_INET); + } else { + if (r->rpool.cur->addr.p.dyn-> + pfid_acnt6 < 1) + return (NULL); + PF_POOLMASK(naddr, + &r->rpool.cur->addr.p.dyn-> + pfid_addr6, + &r->rpool.cur->addr.p.dyn-> + pfid_mask6, + saddr, AF_INET6); + } + } else PF_POOLMASK(naddr, &r->rpool.cur->addr.v.a.addr, &r->rpool.cur->addr.v.a.mask, saddr, pd->af); break; case PF_IN: - if (r->src.addr.type == PF_ADDR_DYNIFTL && - r->src.addr.p.dyn->undefined) - return (NULL); - else + if (r->rpool.cur->addr.type == PF_ADDR_DYNIFTL){ + if (pd->af == AF_INET) { + if (r->src.addr.p.dyn-> + pfid_acnt4 < 1) + return (NULL); + PF_POOLMASK(naddr, + &r->src.addr.p.dyn-> + pfid_addr4, + &r->src.addr.p.dyn-> + pfid_mask4, + daddr, AF_INET); + } else { + if (r->src.addr.p.dyn-> + pfid_acnt6 < 1) + return (NULL); + PF_POOLMASK(naddr, + &r->src.addr.p.dyn-> + pfid_addr6, + &r->src.addr.p.dyn-> + pfid_mask6, + daddr, AF_INET6); + } + } else PF_POOLMASK(naddr, &r->src.addr.v.a.addr, &r->src.addr.v.a.mask, daddr, @@ -2099,7 +2324,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]) { @@ -2121,7 +2346,6 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, } default: return (NULL); - break; } } @@ -2129,8 +2353,7 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, } int -pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, sa_family_t af, - int proto, struct pf_pdesc *pd) +pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, struct pf_pdesc *pd) { struct pf_addr *saddr, *daddr; u_int16_t sport, dport; @@ -2143,7 +2366,7 @@ pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, sa_family_t af, *uid = UID_MAX; *gid = GID_MAX; - switch (proto) { + switch (pd->proto) { case IPPROTO_TCP: sport = pd->hdr.tcp->th_sport; dport = pd->hdr.tcp->th_dport; @@ -2177,29 +2400,24 @@ pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, sa_family_t af, saddr = pd->dst; daddr = pd->src; } - switch(af) { + switch (pd->af) { case AF_INET: #ifdef __FreeBSD__ -#if (__FreeBSD_version >= 500043) INP_INFO_RLOCK(pi); /* XXX LOR */ -#endif inp = in_pcblookup_hash(pi, saddr->v4, sport, daddr->v4, dport, 0, NULL); if (inp == NULL) { inp = in_pcblookup_hash(pi, saddr->v4, sport, daddr->v4, dport, INPLOOKUP_WILDCARD, NULL); if(inp == NULL) { -#if (__FreeBSD_version >= 500043) INP_INFO_RUNLOCK(pi); -#endif return (0); } } #else inp = in_pcbhashlookup(tb, saddr->v4, sport, daddr->v4, dport); if (inp == NULL) { - inp = in_pcblookup(tb, &saddr->v4, sport, &daddr->v4, - dport, INPLOOKUP_WILDCARD); + inp = in_pcblookup_listen(tb, daddr->v4, dport, 0); if (inp == NULL) return (0); } @@ -2208,18 +2426,14 @@ pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, sa_family_t af, #ifdef INET6 case AF_INET6: #ifdef __FreeBSD__ -#if (__FreeBSD_version >= 500043) INP_INFO_RLOCK(pi); -#endif inp = in6_pcblookup_hash(pi, &saddr->v6, sport, &daddr->v6, dport, 0, NULL); if (inp == NULL) { inp = in6_pcblookup_hash(pi, &saddr->v6, sport, &daddr->v6, dport, INPLOOKUP_WILDCARD, NULL); if (inp == NULL) { -#if (__FreeBSD_version >= 500043) INP_INFO_RUNLOCK(pi); -#endif return (0); } } @@ -2227,8 +2441,7 @@ pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, sa_family_t af, inp = in6_pcbhashlookup(tb, &saddr->v6, sport, &daddr->v6, dport); if (inp == NULL) { - inp = in_pcblookup(tb, &saddr->v6, sport, &daddr->v6, - dport, INPLOOKUP_WILDCARD | INPLOOKUP_IPV6); + inp = in6_pcblookup_listen(tb, &daddr->v6, dport, 0); if (inp == NULL) return (0); } @@ -2240,15 +2453,11 @@ pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, sa_family_t af, return (0); } #ifdef __FreeBSD__ -#if (__FreeBSD_version >= 500043) INP_LOCK(inp); -#endif *uid = inp->inp_socket->so_cred->cr_uid; *gid = inp->inp_socket->so_cred->cr_groups[0]; -#if (__FreeBSD_version >= 500043) INP_UNLOCK(inp); INP_INFO_RUNLOCK(pi); -#endif #else *uid = inp->inp_socket->so_euid; *gid = inp->inp_socket->so_egid; @@ -2283,13 +2492,14 @@ pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) if (wscale > TCP_MAX_WINSHIFT) wscale = TCP_MAX_WINSHIFT; wscale |= PF_WSCALE_FLAG; - /* fallthrough */ + /* FALLTHROUGH */ default: optlen = opt[1]; if (optlen < 2) optlen = 2; hlen -= optlen; opt += optlen; + break; } } return (wscale); @@ -2319,13 +2529,14 @@ pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) break; case TCPOPT_MAXSEG: bcopy((caddr_t)(opt + 2), (caddr_t)&mss, 2); - /* fallthrough */ + /* FALLTHROUGH */ default: optlen = opt[1]; if (optlen < 2) optlen = 2; hlen -= optlen; opt += optlen; + break; } } return (mss); @@ -2405,22 +2616,22 @@ pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) { struct pf_rule *r = s->rule.ptr; - s->rt_ifp = NULL; + s->rt_kif = NULL; if (!r->rt || r->rt == PF_FASTROUTE) return; switch (s->af) { #ifdef INET case AF_INET: - pf_map_addr(AF_INET, &r->rpool, saddr, - &s->rt_addr, NULL); - s->rt_ifp = r->rpool.cur->ifp; + pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, + &s->nat_src_node); + s->rt_kif = r->rpool.cur->kif; break; #endif /* INET */ #ifdef INET6 case AF_INET6: - pf_map_addr(AF_INET6, &r->rpool, saddr, - &s->rt_addr, NULL); - s->rt_ifp = r->rpool.cur->ifp; + pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, + &s->nat_src_node); + s->rt_kif = r->rpool.cur->kif; break; #endif /* INET6 */ } @@ -2428,12 +2639,11 @@ pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) int pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, - struct ifnet *ifp, struct mbuf *m, int ipoff, int off, void *h, + struct pfi_kif *kif, 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_addr baddr, naddr; struct tcphdr *th = pd->hdr.tcp; u_int16_t bport, nport = 0; sa_family_t af = pd->af; @@ -2442,6 +2652,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; @@ -2453,35 +2664,37 @@ 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, kif, &nsn, saddr, th->th_sport, daddr, th->th_dport, - &naddr, &nport)) != NULL) { - PF_ACPY(&baddr, saddr, af); + &pd->naddr, &nport)) != NULL) { + PF_ACPY(&pd->baddr, saddr, af); pf_change_ap(saddr, &th->th_sport, pd->ip_sum, - &th->th_sum, &naddr, nport, 0, af); + &th->th_sum, &pd->naddr, nport, 0, af); rewrite++; - if (nat->natpass) + if (nr->natpass) r = NULL; + 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, - &naddr, &nport)) != NULL) { - PF_ACPY(&baddr, daddr, af); + if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &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, &naddr, nport, 0, af); + &th->th_sum, &pd->naddr, nport, 0, af); rewrite++; - if (rdr->natpass) + if (nr->natpass) r = NULL; + pd->nat_rule = nr; } } while (r != NULL) { r->evaluations++; - if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || - (r->ifp == ifp && r->ifnot))) + if (r->kif != NULL && + (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; @@ -2506,19 +2719,16 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, else if ((r->flagset & th->th_flags) != r->flags) r = TAILQ_NEXT(r, entries); else if (r->uid.op && (lookup != -1 || (lookup = - pf_socket_lookup(&uid, &gid, direction, af, IPPROTO_TCP, - pd), 1)) && + pf_socket_lookup(&uid, &gid, direction, pd), 1)) && !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], uid)) r = TAILQ_NEXT(r, entries); else if (r->gid.op && (lookup != -1 || (lookup = - pf_socket_lookup(&uid, &gid, direction, af, IPPROTO_TCP, - pd), 1)) && + pf_socket_lookup(&uid, &gid, direction, pd), 1)) && !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); @@ -2547,18 +2757,12 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, a = *am; ruleset = *rsm; - r->packets++; - r->bytes += pd->tot_len; - if (a != NULL) { - a->packets++; - a->bytes += pd->tot_len; - } REASON_SET(&reason, PFRES_MATCH); if (r->log) { if (rewrite) m_copyback(m, off, sizeof(*th), (caddr_t)th); - PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); } if ((r->action == PF_DROP) && @@ -2566,14 +2770,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, &baddr, bport, 0, af); - rewrite++; - } else if (rdr != NULL) { - pf_change_ap(daddr, &th->th_dport, pd->ip_sum, - &th->th_sum, &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)) && @@ -2604,16 +2810,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); } @@ -2622,10 +2857,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; @@ -2639,8 +2871,8 @@ 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) { - PF_ACPY(&s->lan.addr, &baddr, af); + if (nr != NULL) { + PF_ACPY(&s->lan.addr, &pd->baddr, af); s->lan.port = bport; } else { PF_ACPY(&s->lan.addr, &s->gwy.addr, af); @@ -2651,8 +2883,8 @@ 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) { - PF_ACPY(&s->gwy.addr, &baddr, af); + if (nr != NULL) { + PF_ACPY(&s->gwy.addr, &pd->baddr, af); s->gwy.port = bport; } else { PF_ACPY(&s->gwy.addr, &s->lan.addr, af); @@ -2686,10 +2918,8 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, } if (th->th_flags & TH_FIN) s->src.seqhi++; - s->dst.seqlo = 0; /* Haven't seen these yet */ s->dst.seqhi = 1; s->dst.max_win = 1; - s->dst.seqdiff = 0; /* Defer random generation */ s->src.state = TCPS_SYN_SENT; s->dst.state = TCPS_CLOSED; #ifdef __FreeBSD__ @@ -2700,13 +2930,20 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, s->expire = time.tv_sec; #endif s->timeout = PFTM_TCP_FIRST_PACKET; - s->packets[0] = 1; - s->bytes[0] = pd->tot_len; 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); } @@ -2714,12 +2951,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)) { + if (pf_insert_state(BOUND_IFACE(r, kif), 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 @@ -2727,14 +2966,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, &baddr, - bport, 0, af); - else if (rdr != NULL) - pf_change_ap(daddr, &th->th_dport, - pd->ip_sum, &th->th_sum, &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); @@ -2742,8 +2984,8 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, mss = pf_calc_mss(daddr, af, mss); s->src.mss = mss; pf_send_tcp(r, af, daddr, saddr, th->th_dport, - th->th_sport, s->src.seqhi, - ntohl(th->th_seq) + 1, TH_SYN|TH_ACK, 0, s->src.mss, 0); + th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, + TH_SYN|TH_ACK, 0, s->src.mss, 0); return (PF_SYNPROXY_DROP); } } @@ -2757,12 +2999,11 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, int pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, - struct ifnet *ifp, struct mbuf *m, int ipoff, int off, void *h, + struct pfi_kif *kif, 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_addr baddr, naddr; struct udphdr *uh = pd->hdr.udp; u_int16_t bport, nport = 0; sa_family_t af = pd->af; @@ -2771,6 +3012,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; @@ -2781,35 +3023,37 @@ 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, kif, &nsn, saddr, uh->uh_sport, daddr, uh->uh_dport, - &naddr, &nport)) != NULL) { - PF_ACPY(&baddr, saddr, af); + &pd->naddr, &nport)) != NULL) { + PF_ACPY(&pd->baddr, saddr, af); pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, - &uh->uh_sum, &naddr, nport, 1, af); + &uh->uh_sum, &pd->naddr, nport, 1, af); rewrite++; - if (nat->natpass) + if (nr->natpass) r = NULL; + 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, &naddr, &nport)) - != NULL) { - PF_ACPY(&baddr, daddr, af); + if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &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, &naddr, nport, 1, af); + &uh->uh_sum, &pd->naddr, nport, 1, af); rewrite++; - if (rdr->natpass) + if (nr->natpass) r = NULL; + pd->nat_rule = nr; } } while (r != NULL) { r->evaluations++; - if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || - (r->ifp == ifp && r->ifnot))) + if (r->kif != NULL && + (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; @@ -2832,19 +3076,16 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, else if (r->rule_flag & PFRULE_FRAGMENT) r = TAILQ_NEXT(r, entries); else if (r->uid.op && (lookup != -1 || (lookup = - pf_socket_lookup(&uid, &gid, direction, af, IPPROTO_UDP, - pd), 1)) && + pf_socket_lookup(&uid, &gid, direction, pd), 1)) && !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], uid)) r = TAILQ_NEXT(r, entries); else if (r->gid.op && (lookup != -1 || (lookup = - pf_socket_lookup(&uid, &gid, direction, af, IPPROTO_UDP, - pd), 1)) && + pf_socket_lookup(&uid, &gid, direction, pd), 1)) && !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); @@ -2872,32 +3113,28 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, a = *am; ruleset = *rsm; - r->packets++; - r->bytes += pd->tot_len; - if (a != NULL) { - a->packets++; - a->bytes += pd->tot_len; - } REASON_SET(&reason, PFRES_MATCH); if (r->log) { if (rewrite) m_copyback(m, off, sizeof(*uh), (caddr_t)uh); - PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); } if ((r->action == PF_DROP) && ((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, &baddr, bport, 1, af); - rewrite++; - } else if (rdr != NULL) { - pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, - &uh->uh_sum, &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, @@ -2915,13 +3152,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); } @@ -2930,10 +3195,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; @@ -2947,8 +3209,8 @@ 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) { - PF_ACPY(&s->lan.addr, &baddr, af); + if (nr != NULL) { + PF_ACPY(&s->lan.addr, &pd->baddr, af); s->lan.port = bport; } else { PF_ACPY(&s->lan.addr, &s->gwy.addr, af); @@ -2959,23 +3221,15 @@ 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) { - PF_ACPY(&s->gwy.addr, &baddr, af); + if (nr != NULL) { + PF_ACPY(&s->gwy.addr, &pd->baddr, af); s->gwy.port = bport; } else { PF_ACPY(&s->gwy.addr, &s->lan.addr, af); s->gwy.port = s->lan.port; } } - s->src.seqlo = 0; - s->src.seqhi = 0; - s->src.seqdiff = 0; - s->src.max_win = 0; s->src.state = PFUDPS_SINGLE; - s->dst.seqlo = 0; - s->dst.seqhi = 0; - s->dst.seqdiff = 0; - s->dst.max_win = 0; s->dst.state = PFUDPS_NO_TRAFFIC; #ifdef __FreeBSD__ s->creation = time_second; @@ -2985,11 +3239,19 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, s->expire = time.tv_sec; #endif s->timeout = PFTM_UDP_FIRST_PACKET; - s->packets[0] = 1; - s->bytes[0] = pd->tot_len; pf_set_rt_ifp(s, saddr); - if (pf_insert_state(s)) { + 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(BOUND_IFACE(r, kif), s)) { REASON_SET(&reason, PFRES_MEMORY); + pf_src_tree_remove_state(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else @@ -3005,14 +3267,14 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, int pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, - struct ifnet *ifp, struct mbuf *m, int ipoff, int off, void *h, + struct pfi_kif *kif, 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_addr baddr, naddr; struct pf_rule *r, *a = NULL; struct pf_ruleset *ruleset = NULL; + struct pf_src_node *nsn = NULL; u_short reason; u_int16_t icmpid = 0; /* make the compiler happy */ sa_family_t af = pd->af; @@ -3059,56 +3321,58 @@ 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, &naddr, NULL)) != NULL) { - PF_ACPY(&baddr, saddr, af); + if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, + saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { + PF_ACPY(&pd->baddr, saddr, af); switch (af) { #ifdef INET case AF_INET: pf_change_a(&saddr->v4.s_addr, pd->ip_sum, - naddr.v4.s_addr, 0); + pd->naddr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: pf_change_a6(saddr, &pd->hdr.icmp6->icmp6_cksum, - &naddr, 0); + &pd->naddr, 0); rewrite++; break; #endif /* INET6 */ } - if (nat->natpass) + if (nr->natpass) r = NULL; + 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, &naddr, NULL)) != NULL) { - PF_ACPY(&baddr, daddr, af); + if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, + saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { + PF_ACPY(&pd->baddr, daddr, af); switch (af) { #ifdef INET case AF_INET: pf_change_a(&daddr->v4.s_addr, - pd->ip_sum, naddr.v4.s_addr, 0); + pd->ip_sum, pd->naddr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, - &naddr, 0); + &pd->naddr, 0); rewrite++; break; #endif /* INET6 */ } - if (rdr->natpass) + if (nr->natpass) r = NULL; + pd->nat_rule = nr; } } while (r != NULL) { r->evaluations++; - if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || - (r->ifp == ifp && r->ifnot))) + if (r->kif != NULL && + (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; @@ -3128,8 +3392,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); @@ -3157,12 +3420,6 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, a = *am; ruleset = *rsm; - r->packets++; - r->bytes += pd->tot_len; - if (a != NULL) { - a->packets++; - a->bytes += pd->tot_len; - } REASON_SET(&reason, PFRES_MATCH); if (r->log) { @@ -3171,7 +3428,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, m_copyback(m, off, sizeof(struct icmp6_hdr), (caddr_t)pd->hdr.icmp6); #endif /* INET6 */ - PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); } if (r->action != PF_PASS) @@ -3182,14 +3439,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); } @@ -3198,10 +3482,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; @@ -3215,8 +3496,8 @@ 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) - PF_ACPY(&s->lan.addr, &baddr, af); + if (nr != NULL) + PF_ACPY(&s->lan.addr, &pd->baddr, af); else PF_ACPY(&s->lan.addr, &s->gwy.addr, af); s->lan.port = icmpid; @@ -3225,23 +3506,12 @@ 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) - PF_ACPY(&s->gwy.addr, &baddr, af); + if (nr != NULL) + PF_ACPY(&s->gwy.addr, &pd->baddr, af); else PF_ACPY(&s->gwy.addr, &s->lan.addr, af); s->gwy.port = icmpid; } - - s->src.seqlo = 0; - s->src.seqhi = 0; - s->src.seqdiff = 0; - s->src.max_win = 0; - s->src.state = 0; - s->dst.seqlo = 0; - s->dst.seqhi = 0; - s->dst.seqdiff = 0; - s->dst.max_win = 0; - s->dst.state = 0; #ifdef __FreeBSD__ s->creation = time_second; s->expire = time_second; @@ -3250,11 +3520,19 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, s->expire = time.tv_sec; #endif s->timeout = PFTM_ICMP_FIRST_PACKET; - s->packets[0] = 1; - s->bytes[0] = pd->tot_len; pf_set_rt_ifp(s, saddr); - if (pf_insert_state(s)) { + 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(BOUND_IFACE(r, kif), s)) { REASON_SET(&reason, PFRES_MEMORY); + pf_src_tree_remove_state(s); pool_put(&pf_state_pl, s); return (PF_DROP); } else @@ -3273,14 +3551,14 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, int 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 pfi_kif *kif, 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; - struct pf_addr baddr, naddr; sa_family_t af = pd->af; u_short reason; struct pf_tag *pftag = NULL; @@ -3290,52 +3568,54 @@ 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, &naddr, NULL)) != NULL) { - PF_ACPY(&baddr, saddr, af); + if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn, + saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { + PF_ACPY(&pd->baddr, saddr, af); switch (af) { #ifdef INET case AF_INET: pf_change_a(&saddr->v4.s_addr, pd->ip_sum, - naddr.v4.s_addr, 0); + pd->naddr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: - PF_ACPY(saddr, &naddr, af); + PF_ACPY(saddr, &pd->naddr, af); break; #endif /* INET6 */ } - if (nat->natpass) + if (nr->natpass) r = NULL; + 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, &naddr, NULL)) != NULL) { - PF_ACPY(&baddr, daddr, af); + if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn, + saddr, 0, daddr, 0, &pd->naddr, NULL)) != NULL) { + PF_ACPY(&pd->baddr, daddr, af); switch (af) { #ifdef INET case AF_INET: pf_change_a(&daddr->v4.s_addr, - pd->ip_sum, naddr.v4.s_addr, 0); + pd->ip_sum, pd->naddr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: - PF_ACPY(daddr, &naddr, af); + PF_ACPY(daddr, &pd->naddr, af); break; #endif /* INET6 */ } - if (rdr->natpass) + if (nr->natpass) r = NULL; + pd->nat_rule = nr; } } while (r != NULL) { r->evaluations++; - if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || - (r->ifp == ifp && r->ifnot))) + if (r->kif != NULL && + (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; @@ -3351,8 +3631,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); @@ -3380,36 +3659,33 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, a = *am; ruleset = *rsm; - r->packets++; - r->bytes += pd->tot_len; - if (a != NULL) { - a->packets++; - a->bytes += pd->tot_len; - } REASON_SET(&reason, PFRES_MATCH); + if (r->log) - PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); if ((r->action == PF_DROP) && ((r->rule_flag & PFRULE_RETURNICMP) || (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 case AF_INET: pf_change_a(&a->v4.s_addr, pd->ip_sum, - baddr.v4.s_addr, 0); + pd->baddr.v4.s_addr, 0); break; #endif /* INET */ #ifdef INET6 case AF_INET6: - PF_ACPY(a, &baddr, af); + PF_ACPY(a, &pd->baddr, af); break; #endif /* INET6 */ } @@ -3430,13 +3706,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); } @@ -3445,10 +3749,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; @@ -3459,34 +3760,20 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, s->af = af; if (direction == PF_OUT) { PF_ACPY(&s->gwy.addr, saddr, af); - s->gwy.port = 0; PF_ACPY(&s->ext.addr, daddr, af); - s->ext.port = 0; - if (nat != NULL) - PF_ACPY(&s->lan.addr, &baddr, af); + if (nr != NULL) + PF_ACPY(&s->lan.addr, &pd->baddr, af); else PF_ACPY(&s->lan.addr, &s->gwy.addr, af); - s->lan.port = 0; } else { PF_ACPY(&s->lan.addr, daddr, af); - s->lan.port = 0; PF_ACPY(&s->ext.addr, saddr, af); - s->ext.port = 0; - if (rdr != NULL) - PF_ACPY(&s->gwy.addr, &baddr, af); + if (nr != NULL) + PF_ACPY(&s->gwy.addr, &pd->baddr, af); else PF_ACPY(&s->gwy.addr, &s->lan.addr, af); - s->gwy.port = 0; } - s->src.seqlo = 0; - s->src.seqhi = 0; - s->src.seqdiff = 0; - s->src.max_win = 0; s->src.state = PFOTHERS_SINGLE; - s->dst.seqlo = 0; - s->dst.seqhi = 0; - s->dst.seqdiff = 0; - s->dst.max_win = 0; s->dst.state = PFOTHERS_NO_TRAFFIC; #ifdef __FreeBSD__ s->creation = time_second; @@ -3496,14 +3783,19 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, s->expire = time.tv_sec; #endif s->timeout = PFTM_OTHER_FIRST_PACKET; - s->packets[0] = 1; - s->bytes[0] = pd->tot_len; pf_set_rt_ifp(s, saddr); - if (pf_insert_state(s)) { + 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(BOUND_IFACE(r, kif), 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 @@ -3514,7 +3806,7 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, } int -pf_test_fragment(struct pf_rule **rm, int direction, struct ifnet *ifp, +pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif, struct mbuf *m, void *h, struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) { @@ -3528,8 +3820,8 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct ifnet *ifp, r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); while (r != NULL) { r->evaluations++; - if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || - (r->ifp == ifp && r->ifnot))) + if (r->kif != NULL && + (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != direction) r = r->skip[PF_SKIP_DIR].ptr; @@ -3547,8 +3839,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); @@ -3572,15 +3863,10 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct ifnet *ifp, a = *am; ruleset = *rsm; - r->packets++; - r->bytes += pd->tot_len; - if (a != NULL) { - a->packets++; - a->bytes += pd->tot_len; - } REASON_SET(&reason, PFRES_MATCH); + if (r->log) - PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + PFLOG_PACKET(kif, h, m, af, direction, reason, r, a, ruleset); if (r->action != PF_PASS) return (PF_DROP); @@ -3594,36 +3880,41 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct ifnet *ifp, } int -pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, - struct mbuf *m, int ipoff, int off, void *h, struct pf_pdesc *pd, +pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif, + struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) { - struct pf_tree_node key; + struct pf_state key; struct tcphdr *th = pd->hdr.tcp; u_int16_t win = ntohs(th->th_win); u_int32_t ack, end, seq; u_int8_t sws, dws; - int ackskew, dirndx; + int ackskew; int copyback = 0; struct pf_state_peer *src, *dst; key.af = pd->af; key.proto = IPPROTO_TCP; - PF_ACPY(&key.addr[0], pd->src, key.af); - PF_ACPY(&key.addr[1], pd->dst, key.af); - key.port[0] = th->th_sport; - key.port[1] = th->th_dport; + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, pd->src, key.af); + PF_ACPY(&key.gwy.addr, pd->dst, key.af); + key.ext.port = th->th_sport; + key.gwy.port = th->th_dport; + } else { + PF_ACPY(&key.lan.addr, pd->src, key.af); + PF_ACPY(&key.ext.addr, pd->dst, key.af); + key.lan.port = th->th_sport; + key.ext.port = th->th_dport; + } STATE_LOOKUP(); if (direction == (*state)->direction) { src = &(*state)->src; dst = &(*state)->dst; - dirndx = 0; } else { src = &(*state)->dst; dst = &(*state)->src; - dirndx = 1; } if ((*state)->src.state == PF_TCPS_PROXY_SRC) { @@ -3664,7 +3955,8 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, (*state)->dst.seqhi = arc4random(); pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr, &dst->addr, src->port, dst->port, - (*state)->dst.seqhi, 0, TH_SYN, 0, (*state)->src.mss, 0); + (*state)->dst.seqhi, 0, TH_SYN, 0, + (*state)->src.mss, 0); return (PF_SYNPROXY_DROP); } else if (((th->th_flags & (TH_SYN|TH_ACK)) != (TH_SYN|TH_ACK)) || @@ -3821,9 +4113,6 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, (ackskew <= (MAXACKWINDOW << sws))) { /* Acking not more than one window forward */ - (*state)->packets[dirndx]++; - (*state)->bytes[dirndx] += pd->tot_len; - /* update max window */ if (src->max_win < win) src->max_win = win; @@ -3912,9 +4201,6 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, (*state)->packets[0], (*state)->packets[1]); } - (*state)->packets[dirndx]++; - (*state)->bytes[dirndx] += pd->tot_len; - /* update max window */ if (src->max_win < win) src->max_win = win; @@ -3980,8 +4266,8 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, } if (dst->scrub || src->scrub) { - if (pf_normalize_tcp_stateful(m, off, pd, reason, th, src, dst, - ©back)) + if (pf_normalize_tcp_stateful(m, off, pd, reason, th, + src, dst, ©back)) return (PF_DROP); } @@ -4003,50 +4289,41 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, m_copyback(m, off, sizeof(*th), (caddr_t)th); } - (*state)->rule.ptr->packets++; - (*state)->rule.ptr->bytes += pd->tot_len; - if ((*state)->nat_rule.ptr != NULL) { - (*state)->nat_rule.ptr->packets++; - (*state)->nat_rule.ptr->bytes += pd->tot_len; - } - if ((*state)->anchor.ptr != NULL) { - (*state)->anchor.ptr->packets++; - (*state)->anchor.ptr->bytes += pd->tot_len; - } return (PF_PASS); } int -pf_test_state_udp(struct pf_state **state, int direction, struct ifnet *ifp, - struct mbuf *m, int ipoff, int off, void *h, struct pf_pdesc *pd) +pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif, + struct mbuf *m, int off, void *h, struct pf_pdesc *pd) { struct pf_state_peer *src, *dst; - struct pf_tree_node key; + struct pf_state key; struct udphdr *uh = pd->hdr.udp; - int dirndx; key.af = pd->af; key.proto = IPPROTO_UDP; - PF_ACPY(&key.addr[0], pd->src, key.af); - PF_ACPY(&key.addr[1], pd->dst, key.af); - key.port[0] = uh->uh_sport; - key.port[1] = uh->uh_dport; + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, pd->src, key.af); + PF_ACPY(&key.gwy.addr, pd->dst, key.af); + key.ext.port = uh->uh_sport; + key.gwy.port = uh->uh_dport; + } else { + PF_ACPY(&key.lan.addr, pd->src, key.af); + PF_ACPY(&key.ext.addr, pd->dst, key.af); + key.lan.port = uh->uh_sport; + key.ext.port = uh->uh_dport; + } STATE_LOOKUP(); if (direction == (*state)->direction) { src = &(*state)->src; dst = &(*state)->dst; - dirndx = 0; } else { src = &(*state)->dst; dst = &(*state)->src; - dirndx = 1; } - (*state)->packets[dirndx]++; - (*state)->bytes[dirndx] += pd->tot_len; - /* update states */ if (src->state < PFUDPS_SINGLE) src->state = PFUDPS_SINGLE; @@ -4077,28 +4354,18 @@ pf_test_state_udp(struct pf_state **state, int direction, struct ifnet *ifp, m_copyback(m, off, sizeof(*uh), (caddr_t)uh); } - (*state)->rule.ptr->packets++; - (*state)->rule.ptr->bytes += pd->tot_len; - if ((*state)->nat_rule.ptr != NULL) { - (*state)->nat_rule.ptr->packets++; - (*state)->nat_rule.ptr->bytes += pd->tot_len; - } - if ((*state)->anchor.ptr != NULL) { - (*state)->anchor.ptr->packets++; - (*state)->anchor.ptr->bytes += pd->tot_len; - } return (PF_PASS); } int -pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, - struct mbuf *m, int ipoff, int off, void *h, struct pf_pdesc *pd) +pf_test_state_icmp(struct pf_state **state, int direction, struct pfi_kif *kif, + struct mbuf *m, int off, void *h, struct pf_pdesc *pd) { struct pf_addr *saddr = pd->src, *daddr = pd->dst; u_int16_t icmpid = 0; /* make the compiler happy */ u_int16_t *icmpsum = NULL; /* make the compiler happy */ u_int8_t icmptype = 0; /* make the compiler happy */ - int state_icmp = 0, dirndx; + int state_icmp = 0; switch (pd->proto) { #ifdef INET @@ -4136,20 +4403,24 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, * ICMP query/reply message not related to a TCP/UDP packet. * Search for an ICMP state. */ - struct pf_tree_node key; + struct pf_state key; key.af = pd->af; key.proto = pd->proto; - PF_ACPY(&key.addr[0], saddr, key.af); - PF_ACPY(&key.addr[1], daddr, key.af); - key.port[0] = icmpid; - key.port[1] = icmpid; + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, pd->src, key.af); + PF_ACPY(&key.gwy.addr, pd->dst, key.af); + key.ext.port = icmpid; + key.gwy.port = icmpid; + } else { + PF_ACPY(&key.lan.addr, pd->src, key.af); + PF_ACPY(&key.ext.addr, pd->dst, key.af); + key.lan.port = icmpid; + key.ext.port = icmpid; + } STATE_LOOKUP(); - dirndx = (direction == (*state)->direction) ? 0 : 1; - (*state)->packets[dirndx]++; - (*state)->bytes[dirndx] += pd->tot_len; #ifdef __FreeBSD__ (*state)->expire = time_second; #else @@ -4309,7 +4580,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, case IPPROTO_TCP: { struct tcphdr th; u_int32_t seq; - struct pf_tree_node key; + struct pf_state key; struct pf_state_peer *src, *dst; u_int8_t dws; int copyback = 0; @@ -4328,10 +4599,17 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, key.af = pd2.af; key.proto = IPPROTO_TCP; - PF_ACPY(&key.addr[0], pd2.dst, pd2.af); - key.port[0] = th.th_dport; - PF_ACPY(&key.addr[1], pd2.src, pd2.af); - key.port[1] = th.th_sport; + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, pd2.dst, key.af); + PF_ACPY(&key.gwy.addr, pd2.src, key.af); + key.ext.port = th.th_dport; + key.gwy.port = th.th_sport; + } else { + PF_ACPY(&key.lan.addr, pd2.dst, key.af); + PF_ACPY(&key.ext.addr, pd2.src, key.af); + key.lan.port = th.th_dport; + key.ext.port = th.th_sport; + } STATE_LOOKUP(); @@ -4343,7 +4621,8 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, dst = &(*state)->dst; } - if (src->wscale && dst->wscale && !(th.th_flags & TH_SYN)) + if (src->wscale && dst->wscale && + !(th.th_flags & TH_SYN)) dws = dst->wscale & PF_WSCALE_MASK; else dws = 0; @@ -4416,7 +4695,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, } case IPPROTO_UDP: { struct udphdr uh; - struct pf_tree_node key; + struct pf_state key; if (!pf_pull_hdr(m, off2, &uh, sizeof(uh), NULL, NULL, pd2.af)) { @@ -4428,10 +4707,17 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, key.af = pd2.af; key.proto = IPPROTO_UDP; - PF_ACPY(&key.addr[0], pd2.dst, pd2.af); - key.port[0] = uh.uh_dport; - PF_ACPY(&key.addr[1], pd2.src, pd2.af); - key.port[1] = uh.uh_sport; + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, pd2.dst, key.af); + PF_ACPY(&key.gwy.addr, pd2.src, key.af); + key.ext.port = uh.uh_dport; + key.gwy.port = uh.uh_sport; + } else { + PF_ACPY(&key.lan.addr, pd2.dst, key.af); + PF_ACPY(&key.ext.addr, pd2.src, key.af); + key.lan.port = uh.uh_dport; + key.ext.port = uh.uh_sport; + } STATE_LOOKUP(); @@ -4478,7 +4764,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, #ifdef INET case IPPROTO_ICMP: { struct icmp iih; - struct pf_tree_node key; + struct pf_state key; if (!pf_pull_hdr(m, off2, &iih, ICMP_MINLEN, NULL, NULL, pd2.af)) { @@ -4490,10 +4776,17 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, key.af = pd2.af; key.proto = IPPROTO_ICMP; - PF_ACPY(&key.addr[0], pd2.dst, pd2.af); - key.port[0] = iih.icmp_id; - PF_ACPY(&key.addr[1], pd2.src, pd2.af); - key.port[1] = iih.icmp_id; + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, pd2.dst, key.af); + PF_ACPY(&key.gwy.addr, pd2.src, key.af); + key.ext.port = iih.icmp_id; + key.gwy.port = iih.icmp_id; + } else { + PF_ACPY(&key.lan.addr, pd2.dst, key.af); + PF_ACPY(&key.ext.addr, pd2.src, key.af); + key.lan.port = iih.icmp_id; + key.ext.port = iih.icmp_id; + } STATE_LOOKUP(); @@ -4526,7 +4819,7 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, #ifdef INET6 case IPPROTO_ICMPV6: { struct icmp6_hdr iih; - struct pf_tree_node key; + struct pf_state key; if (!pf_pull_hdr(m, off2, &iih, sizeof(struct icmp6_hdr), NULL, NULL, pd2.af)) { @@ -4538,10 +4831,17 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, key.af = pd2.af; key.proto = IPPROTO_ICMPV6; - PF_ACPY(&key.addr[0], pd2.dst, pd2.af); - key.port[0] = iih.icmp6_id; - PF_ACPY(&key.addr[1], pd2.src, pd2.af); - key.port[1] = iih.icmp6_id; + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, pd2.dst, key.af); + PF_ACPY(&key.gwy.addr, pd2.src, key.af); + key.ext.port = iih.icmp6_id; + key.gwy.port = iih.icmp6_id; + } else { + PF_ACPY(&key.lan.addr, pd2.dst, key.af); + PF_ACPY(&key.ext.addr, pd2.src, key.af); + key.lan.port = iih.icmp6_id; + key.ext.port = iih.icmp6_id; + } STATE_LOOKUP(); @@ -4572,14 +4872,21 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, } #endif /* INET6 */ default: { - struct pf_tree_node key; + struct pf_state key; key.af = pd2.af; key.proto = pd2.proto; - PF_ACPY(&key.addr[0], pd2.dst, pd2.af); - key.port[0] = 0; - PF_ACPY(&key.addr[1], pd2.src, pd2.af); - key.port[1] = 0; + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, pd2.dst, key.af); + PF_ACPY(&key.gwy.addr, pd2.src, key.af); + key.ext.port = 0; + key.gwy.port = 0; + } else { + PF_ACPY(&key.lan.addr, pd2.dst, key.af); + PF_ACPY(&key.ext.addr, pd2.src, key.af); + key.lan.port = 0; + key.ext.port = 0; + } STATE_LOOKUP(); @@ -4626,35 +4933,36 @@ pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, } int -pf_test_state_other(struct pf_state **state, int direction, struct ifnet *ifp, +pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif *kif, struct pf_pdesc *pd) { struct pf_state_peer *src, *dst; - struct pf_tree_node key; - int dirndx; + struct pf_state key; key.af = pd->af; key.proto = pd->proto; - PF_ACPY(&key.addr[0], pd->src, key.af); - PF_ACPY(&key.addr[1], pd->dst, key.af); - key.port[0] = 0; - key.port[1] = 0; + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, pd->src, key.af); + PF_ACPY(&key.gwy.addr, pd->dst, key.af); + key.ext.port = 0; + key.gwy.port = 0; + } else { + PF_ACPY(&key.lan.addr, pd->src, key.af); + PF_ACPY(&key.ext.addr, pd->dst, key.af); + key.lan.port = 0; + key.ext.port = 0; + } STATE_LOOKUP(); if (direction == (*state)->direction) { src = &(*state)->src; dst = &(*state)->dst; - dirndx = 0; } else { src = &(*state)->dst; dst = &(*state)->src; - dirndx = 1; } - (*state)->packets[dirndx]++; - (*state)->bytes[dirndx] += pd->tot_len; - /* update states */ if (src->state < PFOTHERS_SINGLE) src->state = PFOTHERS_SINGLE; @@ -4706,16 +5014,6 @@ pf_test_state_other(struct pf_state **state, int direction, struct ifnet *ifp, } } - (*state)->rule.ptr->packets++; - (*state)->rule.ptr->bytes += pd->tot_len; - if ((*state)->nat_rule.ptr != NULL) { - (*state)->nat_rule.ptr->packets++; - (*state)->nat_rule.ptr->bytes += pd->tot_len; - } - if ((*state)->anchor.ptr != NULL) { - (*state)->anchor.ptr->packets++; - (*state)->anchor.ptr->bytes += pd->tot_len; - } return (PF_PASS); } @@ -4742,7 +5040,8 @@ pf_pull_hdr(struct mbuf *m, int off, void *p, int len, } return (NULL); } - if (m->m_pkthdr.len < off + len || ntohs(h->ip_len) < off + len) { + if (m->m_pkthdr.len < off + len || + ntohs(h->ip_len) < off + len) { ACTION_SET(actionp, PF_DROP); REASON_SET(reasonp, PFRES_SHORT); return (NULL); @@ -4801,167 +5100,6 @@ pf_routable(struct pf_addr *addr, sa_family_t af) #ifdef INET -#if defined(__FreeBSD__) && (__FreeBSD_version < 501105) -int -ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu, - u_long if_hwassist_flags, int sw_csum) -{ - int error = 0; - int hlen = ip->ip_hl << 2; - int len = (mtu - hlen) & ~7; /* size of payload in each fragment */ - int off; - struct mbuf *m0 = *m_frag; /* the original packet */ - int firstlen; - struct mbuf **mnext; - int nfrags; - - if (ip->ip_off & IP_DF) { /* Fragmentation not allowed */ - ipstat.ips_cantfrag++; - return EMSGSIZE; - } - - /* - * Must be able to put at least 8 bytes per fragment. - */ - if (len < 8) - return EMSGSIZE; - - /* - * If the interface will not calculate checksums on - * fragmented packets, then do it here. - */ - if (m0->m_pkthdr.csum_flags & CSUM_DELAY_DATA && - (if_hwassist_flags & CSUM_IP_FRAGS) == 0) { - in_delayed_cksum(m0); - m0->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; - } - - if (len > PAGE_SIZE) { - /* - * Fragment large datagrams such that each segment - * contains a multiple of PAGE_SIZE amount of data, - * plus headers. This enables a receiver to perform - * page-flipping zero-copy optimizations. - * - * XXX When does this help given that sender and receiver - * could have different page sizes, and also mtu could - * be less than the receiver's page size ? - */ - int newlen; - struct mbuf *m; - - for (m = m0, off = 0; m && (off+m->m_len) <= mtu; m = m->m_next) - off += m->m_len; - - /* - * firstlen (off - hlen) must be aligned on an - * 8-byte boundary - */ - if (off < hlen) - goto smart_frag_failure; - off = ((off - hlen) & ~7) + hlen; - newlen = (~PAGE_MASK) & mtu; - if ((newlen + sizeof (struct ip)) > mtu) { - /* we failed, go back the default */ -smart_frag_failure: - newlen = len; - off = hlen + len; - } - len = newlen; - - } else { - off = hlen + len; - } - - firstlen = off - hlen; - mnext = &m0->m_nextpkt; /* pointer to next packet */ - - /* - * Loop through length of segment after first fragment, - * make new header and copy data of each part and link onto chain. - * Here, m0 is the original packet, m is the fragment being created. - * The fragments are linked off the m_nextpkt of the original - * packet, which after processing serves as the first fragment. - */ - for (nfrags = 1; off < ip->ip_len; off += len, nfrags++) { - struct ip *mhip; /* ip header on the fragment */ - struct mbuf *m; - int mhlen = sizeof (struct ip); - - MGETHDR(m, M_DONTWAIT, MT_HEADER); - if (m == 0) { - error = ENOBUFS; - ipstat.ips_odropped++; - goto done; - } - m->m_flags |= (m0->m_flags & M_MCAST) | M_FRAG; - /* - * In the first mbuf, leave room for the link header, then - * copy the original IP header including options. The payload - * goes into an additional mbuf chain returned by m_copy(). - */ - m->m_data += max_linkhdr; - mhip = mtod(m, struct ip *); - *mhip = *ip; - if (hlen > sizeof (struct ip)) { - mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); - mhip->ip_v = IPVERSION; - mhip->ip_hl = mhlen >> 2; - } - m->m_len = mhlen; - /* XXX do we need to add ip->ip_off below ? */ - mhip->ip_off = ((off - hlen) >> 3) + ip->ip_off; - if (off + len >= ip->ip_len) { /* last fragment */ - len = ip->ip_len - off; - m->m_flags |= M_LASTFRAG; - } else - mhip->ip_off |= IP_MF; - mhip->ip_len = htons((u_short)(len + mhlen)); - m->m_next = m_copy(m0, off, len); - if (m->m_next == 0) { /* copy failed */ - m_free(m); - error = ENOBUFS; /* ??? */ - ipstat.ips_odropped++; - goto done; - } - m->m_pkthdr.len = mhlen + len; - m->m_pkthdr.rcvif = (struct ifnet *)0; -#ifdef MAC - mac_create_fragment(m0, m); -#endif - m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags; - mhip->ip_off = htons(mhip->ip_off); - mhip->ip_sum = 0; - if (sw_csum & CSUM_DELAY_IP) - mhip->ip_sum = in_cksum(m, mhlen); - *mnext = m; - mnext = &m->m_nextpkt; - } - ipstat.ips_ofragments += nfrags; - - /* set first marker for fragment chain */ - m0->m_flags |= M_FIRSTFRAG | M_FRAG; - m0->m_pkthdr.csum_data = nfrags; - - /* - * Update first fragment by trimming what's been copied out - * and updating header. - */ - m_adj(m0, hlen + firstlen - ip->ip_len); - m0->m_pkthdr.len = hlen + firstlen; - ip->ip_len = htons((u_short)m0->m_pkthdr.len); - ip->ip_off |= IP_MF; - ip->ip_off = htons(ip->ip_off); - ip->ip_sum = 0; - if (sw_csum & CSUM_DELAY_IP) - ip->ip_sum = in_cksum(m0, hlen); - -done: - *m_frag = m0; - return error; -} -#endif /* __FreeBSD__ && __FreeBSD_version > 501105 */ - void pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct pf_state *s) @@ -4974,6 +5112,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; #ifdef __FreeBSD__ int sw_csum; @@ -5032,31 +5171,34 @@ 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; + ifp = r->rpool.cur->kif ? + r->rpool.cur->kif->pfik_ifp : NULL; } else { if (!PF_AZERO(&s->rt_addr, AF_INET)) dst->sin_addr.s_addr = s->rt_addr.v4.s_addr; - ifp = s->rt_ifp; + ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; } } if (ifp == NULL) goto bad; - if (m_tag_find(m0, PACKET_TAG_PF_ROUTED, NULL) != NULL) - goto bad; - mtag = m_tag_get(PACKET_TAG_PF_ROUTED, 0, M_NOWAIT); - if (mtag == NULL) - goto bad; - m_tag_prepend(m0, mtag); + mtag = m_tag_find(m0, PACKET_TAG_PF_ROUTED, NULL); + if (mtag == NULL) { + struct m_tag *mtag; - if (oifp != ifp) { + mtag = m_tag_get(PACKET_TAG_PF_ROUTED, 0, M_NOWAIT); + if (mtag == NULL) + goto bad; + m_tag_prepend(m0, mtag); + } + + if (oifp != ifp && mtag == NULL) { #ifdef __FreeBSD__ PF_UNLOCK(); if (pf_test(PF_OUT, ifp, &m0) != PF_PASS) { @@ -5120,6 +5262,34 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, #else /* Copied from ip_output. */ +#ifdef IPSEC + /* + * If deferred crypto processing is needed, check that the + * interface supports it. + */ + if ((mtag = m_tag_find(m0, PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED, NULL)) + != NULL && (ifp->if_capabilities & IFCAP_IPSEC) == 0) { + /* Notify IPsec to do its own crypto. */ + ipsp_skipcrypto_unmark((struct tdb_ident *)(mtag + 1)); + goto bad; + } +#endif /* IPSEC */ + + /* Catch routing changes wrt. hardware checksumming for TCP or UDP. */ + if (m0->m_pkthdr.csum & M_TCPV4_CSUM_OUT) { + if (!(ifp->if_capabilities & IFCAP_CSUM_TCPv4) || + ifp->if_bridge != NULL) { + in_delayed_cksum(m0); + m0->m_pkthdr.csum &= ~M_TCPV4_CSUM_OUT; /* Clear */ + } + } else if (m0->m_pkthdr.csum & M_UDPV4_CSUM_OUT) { + if (!(ifp->if_capabilities & IFCAP_CSUM_UDPv4) || + ifp->if_bridge != NULL) { + in_delayed_cksum(m0); + m0->m_pkthdr.csum &= ~M_UDPV4_CSUM_OUT; /* Clear */ + } + } + if (ntohs(ip->ip_len) <= ifp->if_mtu) { if ((ifp->if_capabilities & IFCAP_CSUM_IPv4) && ifp->if_bridge == NULL) { @@ -5227,6 +5397,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 || @@ -5285,17 +5456,17 @@ 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); - ifp = r->rpool.cur->ifp; + ifp = r->rpool.cur->kif ? r->rpool.cur->kif->pfik_ifp : NULL; } else { if (!PF_AZERO(&s->rt_addr, AF_INET6)) PF_ACPY((struct pf_addr *)&dst->sin6_addr, &s->rt_addr, AF_INET6); - ifp = s->rt_ifp; + ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL; } if (ifp == NULL) @@ -5505,7 +5676,8 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t a * returns 0 when the checksum is valid, otherwise returns 1. */ int -pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t af) +pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, + sa_family_t af) { u_int16_t flag_ok, flag_bad; u_int16_t sum; @@ -5587,19 +5759,33 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t a } #endif +static int +pf_add_mbuf_tag(struct mbuf *m, u_int tag) +{ + struct m_tag *mtag; + + if (m_tag_find(m, tag, NULL) != NULL) + return (0); + mtag = m_tag_get(tag, 0, M_NOWAIT); + if (mtag == NULL) + return (1); + m_tag_prepend(m, mtag); + return (0); +} + #ifdef INET int pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) { - u_short action, reason = 0, log = 0; - struct mbuf *m = *m0; - struct ip *h = NULL; /* XXX: was uninitialized */ - struct pf_rule *a = NULL, *r = &pf_default_rule, *tr; - struct pf_state *s = NULL; - struct pf_ruleset *ruleset = NULL; - struct pf_pdesc pd; - int off; - int pqid = 0; + struct pfi_kif *kif; + u_short action, reason = 0, log = 0; + struct mbuf *m = *m0; + struct ip *h = NULL; /* make the compiler happy */ + struct pf_rule *a = NULL, *r = &pf_default_rule, *tr, *nr; + struct pf_state *s = NULL; + struct pf_ruleset *ruleset = NULL; + struct pf_pdesc pd; + int off, dirndx, pqid = 0; #ifdef __FreeBSD__ PF_LOCK(); @@ -5612,7 +5798,15 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) return (PF_PASS); } -#if defined(__FreeBSD__) && (__FreeBSD_version >= 501000) + kif = pfi_index2kif[ifp->if_index]; + if (kif == NULL) { +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + return (PF_DROP); + } + +#ifdef __FreeBSD__ M_ASSERTPKTHDR(m); #else #ifdef DIAGNOSTIC @@ -5621,6 +5815,7 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) #endif #endif + memset(&pd, 0, sizeof(pd)); if (m->m_pkthdr.len < (int)sizeof(*h)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); @@ -5629,7 +5824,7 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) } /* We do IP header normalization and packet reassembly here */ - if (pf_normalize_ip(m0, dir, ifp, &reason) != PF_PASS) { + if (pf_normalize_ip(m0, dir, kif, &reason) != PF_PASS) { action = PF_DROP; goto done; } @@ -5644,9 +5839,9 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) goto done; } - memset(&pd, 0, sizeof(pd)); pd.src = (struct pf_addr *)&h->ip_src; pd.dst = (struct pf_addr *)&h->ip_dst; + PF_ACPY(&pd.baddr, dir == PF_OUT ? pd.src : pd.dst, AF_INET); pd.ip_sum = &h->ip_sum; pd.proto = h->ip_p; pd.af = AF_INET; @@ -5655,7 +5850,7 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) /* handle fragments that didn't get reassembled by normalization */ if (h->ip_off & htons(IP_MF | IP_OFFMASK)) { - action = pf_test_fragment(&r, dir, ifp, m, h, + action = pf_test_fragment(&r, dir, kif, m, h, &pd, &a, &ruleset); goto done; } @@ -5679,17 +5874,21 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) pd.p_len = pd.tot_len - off - (th.th_off << 2); if ((th.th_flags & TH_ACK) && pd.p_len == 0) pqid = 1; - action = pf_normalize_tcp(dir, ifp, m, 0, off, h, &pd); + action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); if (action == PF_DROP) - break; - action = pf_test_state_tcp(&s, dir, ifp, m, 0, off, h, &pd, + goto done; + action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { +#if NPFSYNC + pfsync_update_state(s); +#endif r = s->rule.ptr; + a = s->anchor.ptr; log = s->log; } else if (s == NULL) - action = pf_test_tcp(&r, &s, dir, ifp, - m, 0, off, h, &pd, &a, &ruleset); + action = pf_test_tcp(&r, &s, dir, kif, + m, off, h, &pd, &a, &ruleset); break; } @@ -5707,14 +5906,23 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) action = PF_DROP; goto done; } - action = pf_test_state_udp(&s, dir, ifp, m, 0, off, h, &pd); + if (uh.uh_dport == 0 || + ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || + ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { + action = PF_DROP; + goto done; + } + action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { +#if NPFSYNC + pfsync_update_state(s); +#endif r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) - action = pf_test_udp(&r, &s, dir, ifp, - m, 0, off, h, &pd, &a, &ruleset); + action = pf_test_udp(&r, &s, dir, kif, + m, off, h, &pd, &a, &ruleset); break; } @@ -5732,55 +5940,36 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) action = PF_DROP; goto done; } - action = pf_test_state_icmp(&s, dir, ifp, m, 0, off, h, &pd); + action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { +#if NPFSYNC + pfsync_update_state(s); +#endif r = s->rule.ptr; - r->packets++; - r->bytes += ntohs(h->ip_len); a = s->anchor.ptr; - if (a != NULL) { - a->packets++; - a->bytes += ntohs(h->ip_len); - } log = s->log; } else if (s == NULL) - action = pf_test_icmp(&r, &s, dir, ifp, - m, 0, off, h, &pd, &a, &ruleset); + action = pf_test_icmp(&r, &s, dir, kif, + m, off, h, &pd, &a, &ruleset); break; } default: - action = pf_test_state_other(&s, dir, ifp, &pd); + action = pf_test_state_other(&s, dir, kif, &pd); if (action == PF_PASS) { +#if NPFSYNC + pfsync_update_state(s); +#endif r = s->rule.ptr; a = s->anchor.ptr; log = s->log; } else if (s == NULL) - action = pf_test_other(&r, &s, dir, ifp, m, off, h, + action = pf_test_other(&r, &s, dir, kif, m, off, h, &pd, &a, &ruleset); break; } - if (ifp == status_ifp) { - pf_status.bcounters[0][dir == PF_OUT] += pd.tot_len; - pf_status.pcounters[0][dir == PF_OUT][action != PF_PASS]++; - } - done: - tr = r; - if (r == &pf_default_rule && s != NULL && s->nat_rule.ptr != NULL) - tr = s->nat_rule.ptr; - if (tr->src.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->src.addr.p.tbl, - (s == NULL || s->direction == dir) ? pd.src : pd.dst, pd.af, - pd.tot_len, dir == PF_OUT, r->action == PF_PASS, - tr->src.not); - if (tr->dst.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->dst.addr.p.tbl, - (s == NULL || s->direction == dir) ? pd.dst : pd.src, pd.af, - pd.tot_len, dir == PF_OUT, r->action == PF_PASS, - tr->dst.not); - if (action == PF_PASS && h->ip_hl > 5 && !((s && s->allow_opts) || r->allow_opts)) { action = PF_DROP; @@ -5810,8 +5999,87 @@ done: } #endif + /* + * connections redirected to loopback should not match sockets + * bound specifically to loopback due to security implications, + * see tcp_input() and in_pcblookup_listen(). + */ + if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || + pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && + (s->nat_rule.ptr->action == PF_RDR || + s->nat_rule.ptr->action == PF_BINAT) && + (ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET && + pf_add_mbuf_tag(m, PACKET_TAG_PF_TRANSLATE_LOCALHOST)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (log) - PFLOG_PACKET(ifp, h, m, AF_INET, dir, reason, r, a, ruleset); + PFLOG_PACKET(kif, h, m, AF_INET, dir, reason, r, a, ruleset); + + kif->pfik_bytes[0][dir == PF_OUT][action != PF_PASS] += pd.tot_len; + kif->pfik_packets[0][dir == PF_OUT][action != PF_PASS]++; + + if (action == PF_PASS || r->action == PF_DROP) { + r->packets++; + r->bytes += pd.tot_len; + if (a != NULL) { + a->packets++; + a->bytes += pd.tot_len; + } + if (s != NULL) { + dirndx = (dir == s->direction) ? 0 : 1; + s->packets[dirndx]++; + s->bytes[dirndx] += pd.tot_len; + if (s->nat_rule.ptr != NULL) { + 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; + if (nr != NULL) { + struct pf_addr *x; + /* + * XXX: we need to make sure that the addresses + * passed to pfr_update_stats() are the same than + * the addresses used during matching (pfr_match) + */ + if (r == &pf_default_rule) { + tr = nr; + x = (s == NULL || s->direction == dir) ? + &pd.baddr : &pd.naddr; + } else + x = (s == NULL || s->direction == dir) ? + &pd.naddr : &pd.baddr; + if (x == &pd.baddr || s == NULL) { + /* we need to change the address */ + if (dir == PF_OUT) + pd.src = x; + else + pd.dst = x; + } + } + if (tr->src.addr.type == PF_ADDR_TABLE) + pfr_update_stats(tr->src.addr.p.tbl, (s == NULL || + s->direction == dir) ? pd.src : pd.dst, pd.af, + pd.tot_len, dir == PF_OUT, r->action == PF_PASS, + tr->src.not); + if (tr->dst.addr.type == PF_ADDR_TABLE) + pfr_update_stats(tr->dst.addr.p.tbl, (s == NULL || + s->direction == dir) ? pd.dst : pd.src, pd.af, + pd.tot_len, dir == PF_OUT, r->action == PF_PASS, + tr->dst.not); + } + if (action == PF_SYNPROXY_DROP) { m_freem(*m0); @@ -5833,14 +6101,15 @@ done: int pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) { - u_short action, reason = 0, log = 0; - struct mbuf *m = *m0; - struct ip6_hdr *h = NULL; /* make the compiler happy */ - struct pf_rule *a = NULL, *r = &pf_default_rule, *tr; - struct pf_state *s = NULL; - struct pf_ruleset *ruleset = NULL; - struct pf_pdesc pd; - int off, terminal = 0; + struct pfi_kif *kif; + u_short action, reason = 0, log = 0; + struct mbuf *m = *m0; + struct ip6_hdr *h = NULL; /* make the compiler happy */ + struct pf_rule *a = NULL, *r = &pf_default_rule, *tr, *nr; + struct pf_state *s = NULL; + struct pf_ruleset *ruleset = NULL; + struct pf_pdesc pd; + int off, terminal = 0, dirndx; #ifdef __FreeBSD__ PF_LOCK(); @@ -5854,7 +6123,15 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) return (PF_PASS); } -#if defined(__FreeBSD__) && (__FreeBSD_version >= 501000) + kif = pfi_index2kif[ifp->if_index]; + if (kif == NULL) { +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + return (PF_DROP); + } + +#ifdef __FreeBSD__ M_ASSERTPKTHDR(m); #else #ifdef DIAGNOSTIC @@ -5863,6 +6140,7 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) #endif #endif + memset(&pd, 0, sizeof(pd)); if (m->m_pkthdr.len < (int)sizeof(*h)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); @@ -5871,16 +6149,16 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) } /* We do IP header normalization and packet reassembly here */ - if (pf_normalize_ip6(m0, dir, ifp, &reason) != PF_PASS) { + if (pf_normalize_ip6(m0, dir, kif, &reason) != PF_PASS) { action = PF_DROP; goto done; } m = *m0; h = mtod(m, struct ip6_hdr *); - memset(&pd, 0, sizeof(pd)); pd.src = (struct pf_addr *)&h->ip6_src; pd.dst = (struct pf_addr *)&h->ip6_dst; + PF_ACPY(&pd.baddr, dir == PF_OUT ? pd.src : pd.dst, AF_INET6); pd.ip_sum = NULL; pd.af = AF_INET6; pd.tos = 0; @@ -5891,7 +6169,7 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) do { switch (pd.proto) { case IPPROTO_FRAGMENT: - action = pf_test_fragment(&r, dir, ifp, m, h, + action = pf_test_fragment(&r, dir, kif, m, h, &pd, &a, &ruleset); if (action == PF_DROP) REASON_SET(&reason, PFRES_FRAG); @@ -5943,17 +6221,21 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) goto done; } pd.p_len = pd.tot_len - off - (th.th_off << 2); - action = pf_normalize_tcp(dir, ifp, m, 0, off, h, &pd); + action = pf_normalize_tcp(dir, kif, m, 0, off, h, &pd); if (action == PF_DROP) - break; - action = pf_test_state_tcp(&s, dir, ifp, m, 0, off, h, &pd, + goto done; + action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd, &reason); if (action == PF_PASS) { +#if NPFSYNC + pfsync_update_state(s); +#endif r = s->rule.ptr; + a = s->anchor.ptr; log = s->log; } else if (s == NULL) - action = pf_test_tcp(&r, &s, dir, ifp, - m, 0, off, h, &pd, &a, &ruleset); + action = pf_test_tcp(&r, &s, dir, kif, + m, off, h, &pd, &a, &ruleset); break; } @@ -5971,13 +6253,23 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) action = PF_DROP; goto done; } - action = pf_test_state_udp(&s, dir, ifp, m, 0, off, h, &pd); + if (uh.uh_dport == 0 || + ntohs(uh.uh_ulen) > m->m_pkthdr.len - off || + ntohs(uh.uh_ulen) < sizeof(struct udphdr)) { + action = PF_DROP; + goto done; + } + action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd); if (action == PF_PASS) { +#if NPFSYNC + pfsync_update_state(s); +#endif r = s->rule.ptr; + a = s->anchor.ptr; log = s->log; } else if (s == NULL) - action = pf_test_udp(&r, &s, dir, ifp, - m, 0, off, h, &pd, &a, &ruleset); + action = pf_test_udp(&r, &s, dir, kif, + m, off, h, &pd, &a, &ruleset); break; } @@ -5995,45 +6287,34 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) action = PF_DROP; goto done; } - action = pf_test_state_icmp(&s, dir, ifp, - m, 0, off, h, &pd); + action = pf_test_state_icmp(&s, dir, kif, + m, off, h, &pd); if (action == PF_PASS) { +#if NPFSYNC + pfsync_update_state(s); +#endif r = s->rule.ptr; - r->packets++; - r->bytes += h->ip6_plen; + a = s->anchor.ptr; log = s->log; } else if (s == NULL) - action = pf_test_icmp(&r, &s, dir, ifp, - m, 0, off, h, &pd, &a, &ruleset); + action = pf_test_icmp(&r, &s, dir, kif, + m, off, h, &pd, &a, &ruleset); break; } default: - action = pf_test_other(&r, &s, dir, ifp, m, off, h, - &pd, &a, &ruleset); + action = pf_test_state_other(&s, dir, kif, &pd); + if (action == PF_PASS) { + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_other(&r, &s, dir, kif, m, off, h, + &pd, &a, &ruleset); break; } - if (ifp == status_ifp) { - pf_status.bcounters[1][dir == PF_OUT] += pd.tot_len; - pf_status.pcounters[1][dir == PF_OUT][action != PF_PASS]++; - } - done: - tr = r; - if (r == &pf_default_rule && s != NULL && s->nat_rule.ptr != NULL) - tr = s->nat_rule.ptr; - if (tr->src.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->src.addr.p.tbl, - (s == NULL || s->direction == dir) ? pd.src : pd.dst, pd.af, - pd.tot_len, dir == PF_OUT, r->action == PF_PASS, - tr->src.not); - if (tr->dst.addr.type == PF_ADDR_TABLE) - pfr_update_stats(tr->dst.addr.p.tbl, - (s == NULL || s->direction == dir) ? pd.dst : pd.src, pd.af, - pd.tot_len, dir == PF_OUT, r->action == PF_PASS, - tr->dst.not); - /* XXX handle IPv6 options, if not allowed. not implemented. */ #ifdef ALTQ @@ -6056,8 +6337,82 @@ done: } #endif + if (dir == PF_IN && action == PF_PASS && (pd.proto == IPPROTO_TCP || + pd.proto == IPPROTO_UDP) && s != NULL && s->nat_rule.ptr != NULL && + (s->nat_rule.ptr->action == PF_RDR || + s->nat_rule.ptr->action == PF_BINAT) && + IN6_IS_ADDR_LOOPBACK(&pd.dst->v6) && + pf_add_mbuf_tag(m, PACKET_TAG_PF_TRANSLATE_LOCALHOST)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_MEMORY); + } + if (log) - PFLOG_PACKET(ifp, h, m, AF_INET6, dir, reason, r, a, ruleset); + PFLOG_PACKET(kif, h, m, AF_INET6, dir, reason, r, a, ruleset); + + kif->pfik_bytes[1][dir == PF_OUT][action != PF_PASS] += pd.tot_len; + kif->pfik_packets[1][dir == PF_OUT][action != PF_PASS]++; + + if (action == PF_PASS || r->action == PF_DROP) { + r->packets++; + r->bytes += pd.tot_len; + if (a != NULL) { + a->packets++; + a->bytes += pd.tot_len; + } + if (s != NULL) { + dirndx = (dir == s->direction) ? 0 : 1; + s->packets[dirndx]++; + s->bytes[dirndx] += pd.tot_len; + if (s->nat_rule.ptr != NULL) { + 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; + if (nr != NULL) { + struct pf_addr *x; + /* + * XXX: we need to make sure that the addresses + * passed to pfr_update_stats() are the same than + * the addresses used during matching (pfr_match) + */ + if (r == &pf_default_rule) { + tr = nr; + x = (s == NULL || s->direction == dir) ? + &pd.baddr : &pd.naddr; + } else { + x = (s == NULL || s->direction == dir) ? + &pd.naddr : &pd.baddr; + } + if (x == &pd.baddr || s == NULL) { + if (dir == PF_OUT) + pd.src = x; + else + pd.dst = x; + } + } + if (tr->src.addr.type == PF_ADDR_TABLE) + pfr_update_stats(tr->src.addr.p.tbl, (s == NULL || + s->direction == dir) ? pd.src : pd.dst, pd.af, + pd.tot_len, dir == PF_OUT, r->action == PF_PASS, + tr->src.not); + if (tr->dst.addr.type == PF_ADDR_TABLE) + pfr_update_stats(tr->dst.addr.p.tbl, (s == NULL || + s->direction == dir) ? pd.dst : pd.src, pd.af, + pd.tot_len, dir == PF_OUT, r->action == PF_PASS, + tr->dst.not); + } + if (action == PF_SYNPROXY_DROP) { m_freem(*m0); diff --git a/sys/contrib/pf/net/pf_if.c b/sys/contrib/pf/net/pf_if.c index cdc6e36..079b025 100644 --- a/sys/contrib/pf/net/pf_if.c +++ b/sys/contrib/pf/net/pf_if.c @@ -1,3 +1,4 @@ +/* $FreeBSD$ */ /* $OpenBSD: pf_if.c,v 1.11 2004/03/15 11:38:23 cedric Exp $ */ /* @@ -30,14 +31,24 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#if defined(__FreeBSD__) +#include "opt_inet.h" +#include "opt_inet6.h" +#endif + #include <sys/param.h> #include <sys/systm.h> +#ifdef __FreeBSD__ +#include <sys/malloc.h> +#endif #include <sys/mbuf.h> #include <sys/filio.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/kernel.h> +#ifndef __FreeBSD__ #include <sys/device.h> +#endif #include <sys/time.h> #include <net/if.h> @@ -65,12 +76,16 @@ #define senderr(e) do { rv = (e); goto _bad; } while (0) struct pfi_kif **pfi_index2kif; -struct pfi_kif *pfi_self; +struct pfi_kif *pfi_self, *pfi_dummy; int pfi_indexlim; struct pfi_ifhead pfi_ifs; struct pfi_statehead pfi_statehead; int pfi_ifcnt; +#ifdef __FreeBSD__ +uma_zone_t pfi_addr_pl; +#else struct pool pfi_addr_pl; +#endif long pfi_update = 1; struct pfr_addr *pfi_buffer; int pfi_buffer_cnt; @@ -79,6 +94,11 @@ char pfi_reserved_anchor[PF_ANCHOR_NAME_SIZE] = PF_RESERVED_ANCHOR; char pfi_interface_ruleset[PF_RULESET_NAME_SIZE] = PF_INTERFACE_RULESET; +#ifdef __FreeBSD__ +eventhandler_tag pfi_clone_cookie = NULL; +eventhandler_tag pfi_attach_cookie = NULL; +eventhandler_tag pfi_detach_cookie = NULL; +#endif void pfi_dynaddr_update(void *); void pfi_kifaddr_update(void *); @@ -94,29 +114,151 @@ void pfi_newgroup(const char *, int); int pfi_skip_if(const char *, struct pfi_kif *, int); int pfi_unmask(void *); void pfi_dohooks(struct pfi_kif *); +#ifdef __FreeBSD__ +void pfi_kifaddr_update_event(void *, struct ifnet *); +void pfi_attach_clone_event(void * __unused, struct if_clone *); +void pfi_attach_ifnet_event(void * __unused, struct ifnet *); +void pfi_detach_ifnet_event(void * __unused, struct ifnet *); +#endif RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); #define PFI_DYNAMIC_BUSES { "pcmcia", "cardbus", "uhub" } #define PFI_BUFFER_MAX 0x10000 +#ifdef __FreeBSD__ +MALLOC_DEFINE(PFI_MTYPE, "pf_if", "pf interface table"); +#else #define PFI_MTYPE M_IFADDR +#endif void pfi_initialize(void) { +#ifdef __FreeBSD__ + struct ifnet *ifp; +#endif + if (pfi_self != NULL) /* already initialized */ return; TAILQ_INIT(&pfi_statehead); +#ifndef __FreeBSD__ pool_init(&pfi_addr_pl, sizeof(struct pfi_dynaddr), 0, 0, 0, "pfiaddrpl", &pool_allocator_nointr); +#endif pfi_buffer_max = 64; pfi_buffer = malloc(pfi_buffer_max * sizeof(*pfi_buffer), PFI_MTYPE, M_WAITOK); pfi_self = pfi_if_create("self", NULL, PFI_IFLAG_GROUP); pfi_dynamic_drivers(); +#ifdef __FreeBSD__ + PF_LOCK(); + IFNET_RLOCK(); + TAILQ_FOREACH(ifp, &ifnet, if_link) + if (ifp->if_dunit != IF_DUNIT_NONE) { + IFNET_RUNLOCK(); + pfi_attach_ifnet(ifp); + IFNET_RLOCK(); + } + IFNET_RUNLOCK(); + PF_UNLOCK(); + pfi_dummy = pfi_if_create("notyet", pfi_self, + PFI_IFLAG_GROUP | PFI_IFLAG_DYNAMIC); + pfi_attach_cookie = EVENTHANDLER_REGISTER(ifnet_arrival_event, + pfi_attach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); + pfi_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event, + pfi_detach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY); + pfi_clone_cookie = EVENTHANDLER_REGISTER(if_clone_event, + pfi_attach_clone_event, NULL, EVENTHANDLER_PRI_ANY); +#endif +} + +#ifdef __FreeBSD__ +void +pfi_cleanup(void) +{ + struct pfi_kif *p, key; + struct ifnet *ifp; + + PF_ASSERT(MA_OWNED); + PF_UNLOCK(); + + EVENTHANDLER_DEREGISTER(ifnet_arrival_event, pfi_attach_cookie); + EVENTHANDLER_DEREGISTER(ifnet_departure_event, pfi_detach_cookie); + EVENTHANDLER_DEREGISTER(if_clone_event, pfi_clone_cookie); + + IFNET_RLOCK(); + /* release PFI_IFLAG_INSTANCE */ + TAILQ_FOREACH(ifp, &ifnet, if_link) { + strlcpy(key.pfik_name, ifp->if_xname, sizeof(key.pfik_name)); + p = RB_FIND(pfi_ifhead, &pfi_ifs, &key); + if (p != NULL) { + PF_LOCK(); + pfi_detach_ifnet(ifp); + PF_UNLOCK(); + } + } + IFNET_RUNLOCK(); + + PF_LOCK(); + /* XXX clear all other interface group */ + while ((p = RB_MIN(pfi_ifhead, &pfi_ifs))) { + RB_REMOVE(pfi_ifhead, &pfi_ifs, p); + + free(p->pfik_ah_head, PFI_MTYPE); + free(p, PFI_MTYPE); + } + free(pfi_index2kif, PFI_MTYPE); + free(pfi_buffer, PFI_MTYPE); + pfi_index2kif = NULL; + pfi_buffer = NULL; + pfi_self = NULL; +} + +/* + * Wrapper functions for FreeBSD eventhandler + */ +void +pfi_kifaddr_update_event(void *arg, struct ifnet *ifp) +{ + struct pfi_kif *p = arg; + + PF_LOCK(); + /* + * Check to see if it is 'our' interface as we do not have per + * interface hooks and thus get an update for every interface. + */ + if (p && p->pfik_ifp == ifp) + pfi_kifaddr_update(p); + PF_UNLOCK(); +} + +void +pfi_attach_clone_event(void *arg __unused, struct if_clone *ifc) +{ + PF_LOCK(); + pfi_attach_clone(ifc); + PF_UNLOCK(); +} + +void +pfi_attach_ifnet_event(void *arg __unused, struct ifnet *ifp) +{ + PF_LOCK(); + if (ifp->if_dunit != IF_DUNIT_NONE) + pfi_attach_ifnet(ifp); + PF_UNLOCK(); +} + +void +pfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp) +{ + PF_LOCK(); + pfi_detach_ifnet(ifp); + PF_UNLOCK(); } +#endif /* __FreeBSD__ */ void pfi_attach_clone(struct if_clone *ifc) @@ -130,6 +272,9 @@ pfi_attach_ifnet(struct ifnet *ifp) { struct pfi_kif *p, *q, key; int s; +#ifdef __FreeBSD__ + int realname; +#endif pfi_initialize(); s = splsoftnet(); @@ -150,7 +295,11 @@ pfi_attach_ifnet(struct ifnet *ifp) m = oldlim * sizeof(struct pfi_kif *); mp = pfi_index2kif; n = pfi_indexlim * sizeof(struct pfi_kif *); +#ifdef __FreeBSD__ + np = malloc(n, PFI_MTYPE, M_NOWAIT); +#else np = malloc(n, PFI_MTYPE, M_DONTWAIT); +#endif if (np == NULL) panic("pfi_attach_ifnet: " "cannot allocate translation table"); @@ -164,6 +313,54 @@ pfi_attach_ifnet(struct ifnet *ifp) strlcpy(key.pfik_name, ifp->if_xname, sizeof(key.pfik_name)); p = RB_FIND(pfi_ifhead, &pfi_ifs, &key); +#ifdef __FreeBSD__ + /* some additional trickery for placeholders */ + if ((p == NULL) || (p->pfik_parent == pfi_dummy)) { + /* are we looking at a renamed instance or not? */ + pfi_copy_group(key.pfik_name, ifp->if_xname, + sizeof(key.pfik_name)); + realname = (strncmp(key.pfik_name, ifp->if_dname, + sizeof(key.pfik_name)) == 0); + /* add group */ + /* we can change if_xname, hence use if_dname as group id */ + pfi_copy_group(key.pfik_name, ifp->if_dname, + sizeof(key.pfik_name)); + q = RB_FIND(pfi_ifhead, &pfi_ifs, &key); + if (q == NULL) + q = pfi_if_create(key.pfik_name, pfi_self, + PFI_IFLAG_GROUP|PFI_IFLAG_DYNAMIC); + else if (q->pfik_parent == pfi_dummy) { + q->pfik_parent = pfi_self; + q->pfik_flags = (PFI_IFLAG_GROUP | PFI_IFLAG_DYNAMIC); + } + if (q == NULL) + panic("pfi_attach_ifnet: " + "cannot allocate '%s' group", key.pfik_name); + + /* add/modify interface */ + if (p == NULL) + p = pfi_if_create(ifp->if_xname, q, + realname?PFI_IFLAG_INSTANCE:PFI_IFLAG_PLACEHOLDER); + else { + /* remove from the dummy group */ + /* XXX: copy stats? We should not have any!!! */ + pfi_dummy->pfik_delcnt++; + TAILQ_REMOVE(&pfi_dummy->pfik_grouphead, p, + pfik_instances); + /* move to the right group */ + p->pfik_parent = q; + q->pfik_addcnt++; + TAILQ_INSERT_TAIL(&q->pfik_grouphead, p, + pfik_instances); + if (realname) { + p->pfik_flags &= ~PFI_IFLAG_PLACEHOLDER; + p->pfik_flags |= PFI_IFLAG_INSTANCE; + } + } + if (p == NULL) + panic("pfi_attach_ifnet: " + "cannot allocate '%s' interface", ifp->if_xname); +#else if (p == NULL) { /* add group */ pfi_copy_group(key.pfik_name, ifp->if_xname, @@ -171,6 +368,10 @@ pfi_attach_ifnet(struct ifnet *ifp) q = RB_FIND(pfi_ifhead, &pfi_ifs, &key); if (q == NULL) q = pfi_if_create(key.pfik_name, pfi_self, PFI_IFLAG_GROUP); + else if (q->pfik_parent == pfi_dummy) { + q->pfik_parent = pfi_self; + q->pfik_flags = (PFI_IFLAG_GROUP | PFI_IFLAG_DYNAMIC); + } if (q == NULL) panic("pfi_attach_ifnet: " "cannot allocate '%s' group", key.pfik_name); @@ -180,12 +381,20 @@ pfi_attach_ifnet(struct ifnet *ifp) if (p == NULL) panic("pfi_attach_ifnet: " "cannot allocate '%s' interface", ifp->if_xname); +#endif } else q = p->pfik_parent; p->pfik_ifp = ifp; p->pfik_flags |= PFI_IFLAG_ATTACHED; +#ifdef __FreeBSD__ + PF_UNLOCK(); + p->pfik_ah_cookie = EVENTHANDLER_REGISTER(ifaddr_event, + pfi_kifaddr_update_event, p, EVENTHANDLER_PRI_ANY); + PF_LOCK(); +#else p->pfik_ah_cookie = hook_establish(ifp->if_addrhooks, 1, pfi_kifaddr_update, p); +#endif pfi_index2kif[ifp->if_index] = p; pfi_dohooks(p); splx(s); @@ -207,7 +416,13 @@ pfi_detach_ifnet(struct ifnet *ifp) splx(s); return; } +#ifdef __FreeBSD__ + PF_UNLOCK(); + EVENTHANDLER_DEREGISTER(ifaddr_event, p->pfik_ah_cookie); + PF_LOCK(); +#else hook_disestablish(p->pfik_ifp->if_addrhooks, p->pfik_ah_cookie); +#endif q = p->pfik_parent; p->pfik_ifp = NULL; p->pfik_flags &= ~PFI_IFLAG_ATTACHED; @@ -228,8 +443,19 @@ pfi_lookup_create(const char *name) if (p == NULL) { pfi_copy_group(key.pfik_name, name, sizeof(key.pfik_name)); q = pfi_lookup_if(key.pfik_name); +#ifdef __FreeBSD__ + if ((q != NULL) && (q->pfik_parent != pfi_dummy)) + p = pfi_if_create(name, q, PFI_IFLAG_INSTANCE); + else { + if (pfi_dummy == NULL) + panic("no 'notyet' dummy group"); + p = pfi_if_create(name, pfi_dummy, + PFI_IFLAG_PLACEHOLDER); + } +#else if (q != NULL) p = pfi_if_create(name, q, PFI_IFLAG_INSTANCE); +#endif } splx(s); return (p); @@ -467,8 +693,13 @@ pfi_address_add(struct sockaddr *sa, int af, int net) pfi_buffer_cnt, PFI_BUFFER_MAX); return; } +#ifdef __FreeBSD__ + p = malloc(new_max * sizeof(*pfi_buffer), PFI_MTYPE, + M_NOWAIT); +#else p = malloc(new_max * sizeof(*pfi_buffer), PFI_MTYPE, M_DONTWAIT); +#endif if (p == NULL) { printf("pfi_address_add: no memory to grow buffer " "(%d/%d)\n", pfi_buffer_cnt, PFI_BUFFER_MAX); @@ -552,12 +783,21 @@ pfi_if_create(const char *name, struct pfi_kif *q, int flags) { struct pfi_kif *p; +#ifdef __FreeBSD__ + p = malloc(sizeof(*p), PFI_MTYPE, M_NOWAIT); +#else p = malloc(sizeof(*p), PFI_MTYPE, M_DONTWAIT); +#endif if (p == NULL) return (NULL); bzero(p, sizeof(*p)); +#ifdef __FreeBSD__ + p->pfik_ah_head = malloc(sizeof(*p->pfik_ah_head), PFI_MTYPE, + M_NOWAIT); +#else p->pfik_ah_head = malloc(sizeof(*p->pfik_ah_head), PFI_MTYPE, M_DONTWAIT); +#endif if (p->pfik_ah_head == NULL) { free(p, PFI_MTYPE); return (NULL); @@ -570,7 +810,11 @@ pfi_if_create(const char *name, struct pfi_kif *q, int flags) RB_INIT(&p->pfik_ext_gwy); p->pfik_flags = flags; p->pfik_parent = q; +#ifdef __FreeBSD__ + p->pfik_tzero = time_second; +#else p->pfik_tzero = time.tv_sec; +#endif RB_INSERT(pfi_ifhead, &pfi_ifs, p); if (q != NULL) { @@ -589,6 +833,9 @@ pfi_maybe_destroy(struct pfi_kif *p) if ((p->pfik_flags & (PFI_IFLAG_ATTACHED | PFI_IFLAG_GROUP)) || p->pfik_rules > 0 || p->pfik_states > 0) +#ifdef __FreeBSD__ + if (!(p->pfik_flags & PFI_IFLAG_PLACEHOLDER)) +#endif return (0); s = splsoftnet(); @@ -600,10 +847,25 @@ pfi_maybe_destroy(struct pfi_kif *p) p->pfik_bytes[i][j][k]; q->pfik_packets[i][j][k] += p->pfik_packets[i][j][k]; +#ifdef __FreeBSD__ + /* clear stats in case we return to the dummy group */ + p->pfik_bytes[i][j][k] = 0; + p->pfik_packets[i][j][k] = 0; +#endif } q->pfik_delcnt++; TAILQ_REMOVE(&q->pfik_grouphead, p, pfik_instances); } +#ifdef __FreeBSD__ + if (p->pfik_rules > 0 || p->pfik_states > 0) { + /* move back to the dummy group */ + p->pfik_parent = pfi_dummy; + pfi_dummy->pfik_addcnt++; + TAILQ_INSERT_TAIL(&pfi_dummy->pfik_grouphead, p, + pfik_instances); + return (0); + } +#endif pfi_ifcnt--; RB_REMOVE(pfi_ifhead, &pfi_ifs, p); splx(s); @@ -627,6 +889,22 @@ pfi_copy_group(char *p, const char *q, int m) void pfi_dynamic_drivers(void) { +#ifdef __FreeBSD__ + struct ifnet *ifp; + +/* + * For FreeBSD basically every interface is "dynamic" as we can unload + * modules e.g. + */ + + IFNET_RLOCK(); + TAILQ_FOREACH(ifp, &ifnet, if_link) { + if (ifp->if_dunit == IF_DUNIT_NONE) + continue; + pfi_newgroup(ifp->if_dname, PFI_IFLAG_DYNAMIC); + } + IFNET_RUNLOCK(); +#else char *buses[] = PFI_DYNAMIC_BUSES; int nbuses = sizeof(buses)/sizeof(buses[0]); int enabled[sizeof(buses)/sizeof(buses[0])]; @@ -662,6 +940,7 @@ pfi_dynamic_drivers(void) } } } +#endif } void @@ -710,7 +989,11 @@ pfi_clr_istats(const char *name, int *nzero, int flags) { struct pfi_kif *p; int n = 0, s; +#ifdef __FreeBSD__ + long tzero = time_second; +#else long tzero = time.tv_sec; +#endif s = splsoftnet(); ACCEPT_FLAGS(PFI_FLAG_GROUP|PFI_FLAG_INSTANCE); @@ -733,6 +1016,9 @@ pfi_get_ifaces(const char *name, struct pfi_if *buf, int *size, int flags) { struct pfi_kif *p; int s, n = 0; +#ifdef __FreeBSD__ + int ec; +#endif ACCEPT_FLAGS(PFI_FLAG_GROUP|PFI_FLAG_INSTANCE); s = splsoftnet(); @@ -742,7 +1028,12 @@ pfi_get_ifaces(const char *name, struct pfi_if *buf, int *size, int flags) if (*size > n++) { if (!p->pfik_tzero) p->pfik_tzero = boottime.tv_sec; +#ifdef __FreeBSD__ + PF_COPYOUT(p, buf++, sizeof(*buf), ec); + if (ec) { +#else if (copyout(p, buf++, sizeof(*buf))) { +#endif splx(s); return (EFAULT); } diff --git a/sys/contrib/pf/net/pf_ioctl.c b/sys/contrib/pf/net/pf_ioctl.c index 3053de3..bd8cabb 100644 --- a/sys/contrib/pf/net/pf_ioctl.c +++ b/sys/contrib/pf/net/pf_ioctl.c @@ -1,8 +1,9 @@ /* $FreeBSD$ */ -/* $OpenBSD: pf_ioctl.c,v 1.81.2.2 2004/04/30 23:28:58 brad Exp $ */ +/* $OpenBSD: pf_ioctl.c,v 1.112 2004/03/22 04:54:18 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2002,2003 Henning Brauer * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,6 +41,18 @@ #include "opt_inet6.h" #endif +#ifdef __FreeBSD__ +#include "opt_bpf.h" +#include "opt_pf.h" +#define NBPFILTER DEV_BPF +#define NPFLOG DEV_PFLOG +#define NPFSYNC DEV_PFSYNC +#else +#include "bpfilter.h" +#include "pflog.h" +#include "pfsync.h" +#endif + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -69,14 +82,18 @@ #include <netinet/ip_var.h> #include <netinet/ip_icmp.h> +#ifndef __FreeBSD__ +#include <dev/rndvar.h> +#endif #include <net/pfvar.h> +#if NPFSYNC > 0 +#include <net/if_pfsync.h> +#endif /* NPFSYNC > 0 */ + #ifdef INET6 #include <netinet/ip6.h> #include <netinet/in_pcb.h> -#if defined(__FreeBSD__) && (__FreeBSD_version < 501108) -#include <netinet6/ip6protosw.h> -#endif #endif /* INET6 */ #ifdef ALTQ @@ -84,16 +101,9 @@ #endif #ifdef __FreeBSD__ -#if (__FreeBSD_version >= 500112) #include <sys/limits.h> -#else -#include <machine/limits.h> -#endif #include <sys/lock.h> #include <sys/mutex.h> -#if __FreeBSD_version < 501108 -#include <sys/protosw.h> -#endif #include <net/pfil.h> #endif /* __FreeBSD__ */ @@ -107,7 +117,7 @@ int pfopen(struct cdev *, int, int, struct proc *); int pfclose(struct cdev *, int, int, struct proc *); #endif struct pf_pool *pf_get_pool(char *, char *, u_int32_t, - u_int8_t, u_int32_t, u_int8_t, u_int8_t, u_int8_t); + u_int8_t, u_int8_t, u_int8_t, u_int8_t, u_int8_t); int pf_get_ruleset_number(u_int8_t); void pf_init_ruleset(struct pf_ruleset *); void pf_mv_pool(struct pf_palist *, struct pf_palist *); @@ -117,58 +127,53 @@ int pfioctl(struct cdev *, u_long, caddr_t, int, struct thread *); #else int pfioctl(struct cdev *, u_long, caddr_t, int, struct proc *); #endif +#ifdef ALTQ +int pf_begin_altq(u_int32_t *); +int pf_rollback_altq(u_int32_t); +int pf_commit_altq(u_int32_t); +#endif /* ALTQ */ +int pf_begin_rules(u_int32_t *, int, char *, char *); +int pf_rollback_rules(u_int32_t, int, char *, char *); +int pf_commit_rules(u_int32_t, int, char *, char *); #ifdef __FreeBSD__ extern struct callout pf_expire_to; -#if __FreeBSD_version < 501108 -extern struct protosw inetsw[]; -#endif #else extern struct timeout pf_expire_to; #endif struct pf_rule pf_default_rule; -#ifdef ALTQ -static int pfaltq_running; -#endif #define TAGID_MAX 50000 -TAILQ_HEAD(pf_tags, pf_tagname) pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags); +TAILQ_HEAD(pf_tags, pf_tagname) pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags), + pf_qids = TAILQ_HEAD_INITIALIZER(pf_qids); + +#if (PF_QNAME_SIZE != PF_TAG_NAME_SIZE) +#error PF_QNAME_SIZE must be equal to PF_TAG_NAME_SIZE +#endif +static u_int16_t tagname2tag(struct pf_tags *, char *); +static void tag2tagname(struct pf_tags *, u_int16_t, char *); +static void tag_unref(struct pf_tags *, u_int16_t); #define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x #ifdef __FreeBSD__ -static struct cdev *pf_dev; +static struct cdev *pf_dev; /* * XXX - These are new and need to be checked when moveing to a new version */ -static int pf_beginrules(void *addr); -static int pf_commitrules(void *addr); -#ifdef ALTQ -static int pf_beginaltqs(void *addr); -static int pf_commitaltqs(void *addr); -static int pf_stopaltq(void); -#endif -static void pf_clearstates(void); -static int pf_clear_tables(void *addr); +static void pf_clear_states(void); +static int pf_clear_tables(void); +static void pf_clear_srcnodes(void); /* * XXX - These are new and need to be checked when moveing to a new version */ -#if (__FreeBSD_version < 501108) -static int pf_check_in(void *ip, int hlen, struct ifnet *ifp, int dir, - struct mbuf **m); -static int pf_check_out(void *ip, int hlen, struct ifnet *ifp, int dir, - struct mbuf **m); -#ifdef INET6 -static int pf_check6_in(void *ip, int hlen, struct ifnet *ifp, int dir, - struct mbuf **m); -static int pf_check6_out(void *ip, int hlen, struct ifnet *ifp, int dir, - struct mbuf **m); -#endif -#else /* (__FreeBSD_version >= 501108) */ +/* + * Wrapper functions for pfil(9) hooks + */ static int pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir); static int pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, @@ -179,50 +184,17 @@ static int pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, static int pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir); #endif -#endif /* (__FreeBSD_version >= 501108) */ -static int hook_pf(void); -static int dehook_pf(void); -static int shutdown_pf(void); -static int pf_load(void); -static int pf_unload(void); - +static int hook_pf(void); +static int dehook_pf(void); +static int shutdown_pf(void); +static int pf_load(void); +static int pf_unload(void); static struct cdevsw pf_cdevsw = { -#if (__FreeBSD_version < 500105) - /* open */ noopen, - /* close */ noclose, - /* read */ noread, - /* write */ nowrite, - /* ioctl */ pfioctl, - /* poll */ nopoll, - /* mmap */ nommap, - /* strategy */ nostrategy, - /* name */ PF_NAME, - /* maj */ PF_CDEV_MAJOR, - /* dump */ nodump, - /* psize */ nopsize, - /* flags */ 0, - /* kqfilter */ nokqfilter, -#elif (__FreeBSD_version < 501110) - .d_open = noopen, - .d_close = noclose, - .d_read = noread, - .d_write = nowrite, - .d_ioctl = pfioctl, - .d_poll = nopoll, - .d_mmap = nommap, - .d_strategy = nostrategy, - .d_name = PF_NAME, - .d_maj = MAJOR_AUTO, /* PF_CDEV_MAJOR */ - .d_dump = nodump, - .d_flags = 0, - .d_kqfilter = nokqfilter, -#else .d_ioctl = pfioctl, .d_name = PF_NAME, .d_version = D_VERSION, -#endif }; static volatile int pf_pfil_hooked = 0; @@ -243,7 +215,7 @@ destroy_pf_mutex(void) void init_zone_var(void) { - pf_tree_pl = pf_rule_pl = pf_addr_pl = NULL; + pf_src_tree_pl = pf_rule_pl = NULL; pf_state_pl = pf_altq_pl = pf_pooladdr_pl = NULL; pf_frent_pl = pf_frag_pl = pf_cache_pl = pf_cent_pl = NULL; pf_state_scrub_pl = NULL; @@ -253,9 +225,8 @@ init_zone_var(void) void cleanup_pf_zone(void) { - UMA_DESTROY(pf_tree_pl); + UMA_DESTROY(pf_src_tree_pl); UMA_DESTROY(pf_rule_pl); - UMA_DESTROY(pf_addr_pl); UMA_DESTROY(pf_state_pl); UMA_DESTROY(pf_altq_pl); UMA_DESTROY(pf_pooladdr_pl); @@ -266,6 +237,7 @@ cleanup_pf_zone(void) UMA_DESTROY(pfr_ktable_pl); UMA_DESTROY(pfr_kentry_pl); UMA_DESTROY(pf_state_scrub_pl); + UMA_DESTROY(pfi_addr_pl); } int @@ -275,9 +247,8 @@ pfattach(void) int error = 1; do { - UMA_CREATE(pf_tree_pl, struct pf_tree_node, "pftrpl"); + UMA_CREATE(pf_src_tree_pl,struct pf_src_node, "pfsrctrpl"); UMA_CREATE(pf_rule_pl, struct pf_rule, "pfrulepl"); - UMA_CREATE(pf_addr_pl, struct pf_addr_dyn, "pfaddrpl"); UMA_CREATE(pf_state_pl, struct pf_state, "pfstatepl"); UMA_CREATE(pf_altq_pl, struct pf_altq, "pfaltqpl"); UMA_CREATE(pf_pooladdr_pl, struct pf_pooladdr, "pfpooladdrpl"); @@ -289,6 +260,7 @@ pfattach(void) UMA_CREATE(pf_cent_pl, struct pf_frcache, "pffrcent"); UMA_CREATE(pf_state_scrub_pl, struct pf_state_scrub, "pfstatescrub"); + UMA_CREATE(pfi_addr_pl, struct pfi_dynaddr, "pfiaddrpl"); error = 0; } while(0); if (error) { @@ -296,6 +268,7 @@ pfattach(void) return (error); } pfr_initialize(); + pfi_initialize(); if ( (error = pf_osfp_initialize()) ) { cleanup_pf_zone(); pf_osfp_cleanup(); @@ -309,15 +282,15 @@ pfattach(void) uma_zone_set_max(pf_pool_limits[PF_LIMIT_STATES].pp, pf_pool_limits[PF_LIMIT_STATES].limit); - 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); + pf_init_ruleset(&pf_main_ruleset); TAILQ_INIT(&pf_altqs[0]); TAILQ_INIT(&pf_altqs[1]); TAILQ_INIT(&pf_pabuf); pf_altqs_active = &pf_altqs[0]; pf_altqs_inactive = &pf_altqs[1]; + TAILQ_INIT(&state_updates); /* default rule should never be garbage collected */ pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next; @@ -354,6 +327,10 @@ pfattach(void) pf_normalize_init(); pf_status.debug = PF_DEBUG_URGENT; pf_pfil_hooked = 0; + + /* XXX do our best to avoid a conflict */ + pf_status.hostid = arc4random(); + return (error); } #else /* !__FreeBSD__ */ @@ -362,12 +339,10 @@ pfattach(int num) { u_int32_t *timeout = pf_default_rule.timeout; - pool_init(&pf_tree_pl, sizeof(struct pf_tree_node), 0, 0, 0, "pftrpl", - NULL); pool_init(&pf_rule_pl, sizeof(struct pf_rule), 0, 0, 0, "pfrulepl", &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", @@ -375,13 +350,13 @@ pfattach(int num) pool_init(&pf_pooladdr_pl, sizeof(struct pf_pooladdr), 0, 0, 0, "pfpooladdrpl", NULL); pfr_initialize(); + pfi_initialize(); pf_osfp_initialize(); - pool_sethardlimit(&pf_state_pl, pf_pool_limits[PF_LIMIT_STATES].limit, - NULL, 0); + pool_sethardlimit(pf_pool_limits[PF_LIMIT_STATES].pp, + pf_pool_limits[PF_LIMIT_STATES].limit, NULL, 0); - 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]); @@ -389,6 +364,7 @@ pfattach(int num) TAILQ_INIT(&pf_pabuf); pf_altqs_active = &pf_altqs[0]; pf_altqs_inactive = &pf_altqs[1]; + TAILQ_INIT(&state_updates); /* default rule should never be garbage collected */ pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next; @@ -397,7 +373,7 @@ pfattach(int num) /* initialize default timeouts */ timeout[PFTM_TCP_FIRST_PACKET] = 120; /* First TCP packet */ - timeout[PFTM_TCP_OPENING] = 30; /* No response yet */ + timeout[PFTM_TCP_OPENING] = 30; /* No response yet */ timeout[PFTM_TCP_ESTABLISHED] = 24*60*60; /* Established */ timeout[PFTM_TCP_CLOSING] = 15 * 60; /* Half closed */ timeout[PFTM_TCP_FIN_WAIT] = 45; /* Got both FINs */ @@ -412,12 +388,17 @@ 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; + + /* XXX do our best to avoid a conflict */ + pf_status.hostid = arc4random(); } int @@ -439,7 +420,7 @@ pfclose(struct cdev *dev, int flags, int fmt, struct proc *p) struct pf_pool * pf_get_pool(char *anchorname, char *rulesetname, u_int32_t ticket, - u_int8_t rule_action, u_int32_t rule_number, u_int8_t r_last, + u_int8_t rule_action, u_int8_t rule_number, u_int8_t r_last, u_int8_t active, u_int8_t check_ticket) { struct pf_ruleset *ruleset; @@ -564,7 +545,8 @@ pf_find_ruleset(char *anchorname, char *rulesetname) } struct pf_ruleset * -pf_find_or_create_ruleset(char *anchorname, char *rulesetname) +pf_find_or_create_ruleset(char anchorname[PF_ANCHOR_NAME_SIZE], + char rulesetname[PF_RULESET_NAME_SIZE]) { struct pf_anchor *anchor, *a; struct pf_ruleset *ruleset, *r; @@ -623,7 +605,8 @@ pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) return; for (i = 0; i < PF_RULESET_MAX; ++i) if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || - !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr)) + !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) || + ruleset->rules[i].inactive.open) return; anchor = ruleset->anchor; @@ -654,8 +637,9 @@ pf_empty_pool(struct pf_palist *poola) struct pf_pooladdr *empty_pool_pa; while ((empty_pool_pa = TAILQ_FIRST(poola)) != NULL) { - pf_dynaddr_remove(&empty_pool_pa->addr); + pfi_dynaddr_remove(&empty_pool_pa->addr); pf_tbladdr_remove(&empty_pool_pa->addr); + pfi_detach_rule(empty_pool_pa->kif); TAILQ_REMOVE(poola, empty_pool_pa, entries); pool_put(&pf_pooladdr_pl, empty_pool_pa); } @@ -678,27 +662,35 @@ 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); - pf_dynaddr_remove(&rule->src.addr); - pf_dynaddr_remove(&rule->dst.addr); +#ifdef ALTQ + if (rule->pqid != rule->qid) + pf_qid_unref(rule->pqid); + pf_qid_unref(rule->qid); +#endif + pfi_dynaddr_remove(&rule->src.addr); + pfi_dynaddr_remove(&rule->dst.addr); if (rulequeue == NULL) { pf_tbladdr_remove(&rule->src.addr); pf_tbladdr_remove(&rule->dst.addr); } + pfi_detach_rule(rule->kif); pf_empty_pool(&rule->rpool.list); pool_put(&pf_rule_pl, rule); } -u_int16_t -pf_tagname2tag(char *tagname) +static u_int16_t +tagname2tag(struct pf_tags *head, char *tagname) { struct pf_tagname *tag, *p = NULL; u_int16_t new_tagid = 1; - TAILQ_FOREACH(tag, &pf_tags, entries) + TAILQ_FOREACH(tag, head, entries) if (strcmp(tagname, tag->name) == 0) { tag->ref++; return (tag->tag); @@ -711,8 +703,8 @@ pf_tagname2tag(char *tagname) */ /* new entry */ - if (!TAILQ_EMPTY(&pf_tags)) - for (p = TAILQ_FIRST(&pf_tags); p != NULL && + if (!TAILQ_EMPTY(head)) + for (p = TAILQ_FIRST(head); p != NULL && p->tag == new_tagid; p = TAILQ_NEXT(p, entries)) new_tagid = p->tag + 1; @@ -732,36 +724,36 @@ pf_tagname2tag(char *tagname) if (p != NULL) /* insert new entry before p */ TAILQ_INSERT_BEFORE(p, tag, entries); else /* either list empty or no free slot in between */ - TAILQ_INSERT_TAIL(&pf_tags, tag, entries); + TAILQ_INSERT_TAIL(head, tag, entries); return (tag->tag); } -void -pf_tag2tagname(u_int16_t tagid, char *p) +static void +tag2tagname(struct pf_tags *head, u_int16_t tagid, char *p) { struct pf_tagname *tag; - TAILQ_FOREACH(tag, &pf_tags, entries) + TAILQ_FOREACH(tag, head, entries) if (tag->tag == tagid) { strlcpy(p, tag->name, PF_TAG_NAME_SIZE); return; } } -void -pf_tag_unref(u_int16_t tag) +static void +tag_unref(struct pf_tags *head, u_int16_t tag) { struct pf_tagname *p, *next; if (tag == 0) return; - for (p = TAILQ_FIRST(&pf_tags); p != NULL; p = next) { + for (p = TAILQ_FIRST(head); p != NULL; p = next) { next = TAILQ_NEXT(p, entries); if (tag == p->tag) { if (--p->ref == 0) { - TAILQ_REMOVE(&pf_tags, p, entries); + TAILQ_REMOVE(head, p, entries); free(p, M_TEMP); } break; @@ -769,6 +761,210 @@ pf_tag_unref(u_int16_t tag) } } +u_int16_t +pf_tagname2tag(char *tagname) +{ + return (tagname2tag(&pf_tags, tagname)); +} + +void +pf_tag2tagname(u_int16_t tagid, char *p) +{ + return (tag2tagname(&pf_tags, tagid, p)); +} + +void +pf_tag_unref(u_int16_t tag) +{ + return (tag_unref(&pf_tags, tag)); +} + +#ifdef ALTQ +u_int32_t +pf_qname2qid(char *qname) +{ + return ((u_int32_t)tagname2tag(&pf_qids, qname)); +} + +void +pf_qid2qname(u_int32_t qid, char *p) +{ + return (tag2tagname(&pf_qids, (u_int16_t)qid, p)); +} + +void +pf_qid_unref(u_int32_t qid) +{ + return (tag_unref(&pf_qids, (u_int16_t)qid)); +} + +int +pf_begin_altq(u_int32_t *ticket) +{ + struct pf_altq *altq; + int error = 0; + + /* Purge the old altq list */ + while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { + TAILQ_REMOVE(pf_altqs_inactive, altq, entries); + if (altq->qname[0] == 0) { + /* detach and destroy the discipline */ + error = altq_remove(altq); + } else + pf_qid_unref(altq->qid); + pool_put(&pf_altq_pl, altq); + } + if (error) + return (error); + *ticket = ++ticket_altqs_inactive; + altqs_inactive_open = 1; + return (0); +} + +int +pf_rollback_altq(u_int32_t ticket) +{ + struct pf_altq *altq; + int error = 0; + + if (!altqs_inactive_open || ticket != ticket_altqs_inactive) + return (0); + /* Purge the old altq list */ + while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { + TAILQ_REMOVE(pf_altqs_inactive, altq, entries); + if (altq->qname[0] == 0) { + /* detach and destroy the discipline */ + error = altq_remove(altq); + } else + pf_qid_unref(altq->qid); + pool_put(&pf_altq_pl, altq); + } + altqs_inactive_open = 0; + return (error); +} + +int +pf_commit_altq(u_int32_t ticket) +{ + struct pf_altqqueue *old_altqs; + struct pf_altq *altq; + int s, err, error = 0; + + if (!altqs_inactive_open || ticket != ticket_altqs_inactive) + return (EBUSY); + + /* swap altqs, keep the old. */ + s = splsoftnet(); + old_altqs = pf_altqs_active; + pf_altqs_active = pf_altqs_inactive; + pf_altqs_inactive = old_altqs; + ticket_altqs_active = ticket_altqs_inactive; + + /* Attach new disciplines */ + TAILQ_FOREACH(altq, pf_altqs_active, entries) { + if (altq->qname[0] == 0) { + /* attach the discipline */ + error = altq_pfattach(altq); + if (error) { + splx(s); + return (error); + } + } + } + + /* Purge the old altq list */ + while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { + TAILQ_REMOVE(pf_altqs_inactive, altq, entries); + if (altq->qname[0] == 0) { + /* detach and destroy the discipline */ + err = altq_pfdetach(altq); + if (err != 0 && error == 0) + error = err; + err = altq_remove(altq); + if (err != 0 && error == 0) + error = err; + } else + pf_qid_unref(altq->qid); + pool_put(&pf_altq_pl, altq); + } + splx(s); + + altqs_inactive_open = 0; + return (error); +} +#endif /* ALTQ */ + +int +pf_begin_rules(u_int32_t *ticket, int rs_num, char *anchor, char *ruleset) +{ + struct pf_ruleset *rs; + struct pf_rule *rule; + + if (rs_num < 0 || rs_num >= PF_RULESET_MAX) + return (EINVAL); + rs = pf_find_or_create_ruleset(anchor, ruleset); + if (rs == NULL) + return (EINVAL); + while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) + pf_rm_rule(rs->rules[rs_num].inactive.ptr, rule); + *ticket = ++rs->rules[rs_num].inactive.ticket; + rs->rules[rs_num].inactive.open = 1; + return (0); +} + +int +pf_rollback_rules(u_int32_t ticket, int rs_num, char *anchor, char *ruleset) +{ + struct pf_ruleset *rs; + struct pf_rule *rule; + + if (rs_num < 0 || rs_num >= PF_RULESET_MAX) + return (EINVAL); + rs = pf_find_ruleset(anchor, ruleset); + if (rs == NULL || !rs->rules[rs_num].inactive.open || + rs->rules[rs_num].inactive.ticket != ticket) + return (0); + while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) + pf_rm_rule(rs->rules[rs_num].inactive.ptr, rule); + rs->rules[rs_num].inactive.open = 0; + return (0); +} + +int +pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor, char *ruleset) +{ + struct pf_ruleset *rs; + struct pf_rule *rule; + struct pf_rulequeue *old_rules; + int s; + + if (rs_num < 0 || rs_num >= PF_RULESET_MAX) + return (EINVAL); + rs = pf_find_ruleset(anchor, ruleset); + if (rs == NULL || !rs->rules[rs_num].inactive.open || + ticket != rs->rules[rs_num].inactive.ticket) + return (EBUSY); + + /* Swap rules, keep the old. */ + s = splsoftnet(); + old_rules = rs->rules[rs_num].active.ptr; + rs->rules[rs_num].active.ptr = + rs->rules[rs_num].inactive.ptr; + rs->rules[rs_num].inactive.ptr = old_rules; + rs->rules[rs_num].active.ticket = + rs->rules[rs_num].inactive.ticket; + pf_calc_skip_steps(rs->rules[rs_num].active.ptr); + + /* Purge the old rule list. */ + while ((rule = TAILQ_FIRST(old_rules)) != NULL) + pf_rm_rule(old_rules, rule); + rs->rules[rs_num].inactive.open = 0; + pf_remove_if_empty_ruleset(rs); + pf_update_anchor_rules(); + splx(s); + return (0); +} + #ifdef __FreeBSD__ int pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) @@ -818,10 +1014,22 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCRCLRASTATS: case DIOCRTSTADDRS: case DIOCOSFPGET: + case DIOCGETSRCNODES: + case DIOCCLRSRCNODES: + case DIOCIGETIFACES: + case DIOCICLRISTATS: #ifdef __FreeBSD__ case DIOCGIFSPEED: #endif break; + case DIOCRCLRTABLES: + case DIOCRADDTABLES: + case DIOCRDELTABLES: + case DIOCRSETTFLAGS: + if (((struct pfioc_table *)addr)->pfrio_flags & + PFR_FLAG_DUMMY) + break; /* dummy operation ok */ + return (EPERM); default: return (EPERM); } @@ -850,10 +1058,25 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCRGETASTATS: case DIOCRTSTADDRS: case DIOCOSFPGET: + case DIOCGETSRCNODES: + case DIOCIGETIFACES: #ifdef __FreeBSD__ case DIOCGIFSPEED: #endif break; + case DIOCRCLRTABLES: + case DIOCRADDTABLES: + case DIOCRDELTABLES: + case DIOCRCLRTSTATS: + case DIOCRCLRADDRS: + case DIOCRADDADDRS: + case DIOCRDELADDRS: + case DIOCRSETADDRS: + case DIOCRSETTFLAGS: + if (((struct pfioc_table *)addr)->pfrio_flags & + PFR_FLAG_DUMMY) + break; /* dummy operation ok */ + return (EACCES); default: return (EACCES); } @@ -868,7 +1091,6 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (pf_status.running) error = EEXIST; else { - u_int32_t states = pf_status.states; #ifdef __FreeBSD__ PF_UNLOCK(); error = hook_pf(); @@ -879,22 +1101,20 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } #endif - bzero(&pf_status, sizeof(struct pf_status)); pf_status.running = 1; - pf_status.states = states; #ifdef __FreeBSD__ pf_status.since = time_second; #else pf_status.since = time.tv_sec; #endif - if (status_ifp != NULL) -#if defined(__FreeBSD__) && (__FreeBSD_version < 501113) - snprintf(pf_status.ifname, IFNAMSIZ, "%s%d", - status_ifp->if_name, status_ifp->if_unit); + if (pf_status.stateid == 0) { +#ifdef __FreeBSD__ + pf_status.stateid = time_second; #else - strlcpy(pf_status.ifname, - status_ifp->if_xname, IFNAMSIZ); + pf_status.stateid = time.tv_sec; #endif + pf_status.stateid = pf_status.stateid << 32; + } DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); } break; @@ -913,6 +1133,9 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) DPFPRINTF(PF_DEBUG_MISC, ("pf: pfil unregisteration failed\n")); } + pf_status.since = time_second; +#else + pf_status.since = time.tv_sec; #endif DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); } @@ -920,24 +1143,9 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCBEGINRULES: { struct pfioc_rule *pr = (struct pfioc_rule *)addr; - struct pf_ruleset *ruleset; - struct pf_rule *rule; - int rs_num; - ruleset = pf_find_or_create_ruleset(pr->anchor, pr->ruleset); - if (ruleset == NULL) { - error = EINVAL; - break; - } - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) { - error = EINVAL; - break; - } - while ((rule = - TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr)) != NULL) - pf_rm_rule(ruleset->rules[rs_num].inactive.ptr, rule); - pr->ticket = ++ruleset->rules[rs_num].inactive.ticket; + error = pf_begin_rules(&pr->ticket, pf_get_ruleset_number( + pr->rule.action), pr->anchor, pr->ruleset); break; } @@ -981,10 +1189,11 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } bcopy(&pr->rule, rule, sizeof(struct pf_rule)); rule->anchor = NULL; - rule->ifp = NULL; + rule->kif = NULL; 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) { @@ -1007,14 +1216,27 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) else rule->nr = 0; if (rule->ifname[0]) { - rule->ifp = ifunit(rule->ifname); - if (rule->ifp == NULL) { + rule->kif = pfi_attach_rule(rule->ifname); + if (rule->kif == NULL) { pool_put(&pf_rule_pl, rule); error = EINVAL; break; } } +#ifdef ALTQ + /* set queue IDs */ + if (rule->qname[0] != 0) { + if ((rule->qid = pf_qname2qid(rule->qname)) == 0) + error = EBUSY; + else if (rule->pqname[0] != 0) { + if ((rule->pqid = + pf_qname2qid(rule->pqname)) == 0) + error = EBUSY; + } else + rule->pqid = rule->qid; + } +#endif if (rule->tagname[0]) if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0) error = EBUSY; @@ -1024,9 +1246,9 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EBUSY; if (rule->rt && !rule->direction) error = EINVAL; - if (pf_dynaddr_setup(&rule->src.addr, rule->af)) + if (pfi_dynaddr_setup(&rule->src.addr, rule->af)) error = EINVAL; - if (pf_dynaddr_setup(&rule->dst.addr, rule->af)) + if (pfi_dynaddr_setup(&rule->dst.addr, rule->af)) error = EINVAL; if (pf_tbladdr_setup(ruleset, &rule->src.addr)) error = EINVAL; @@ -1056,48 +1278,9 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCCOMMITRULES: { struct pfioc_rule *pr = (struct pfioc_rule *)addr; - struct pf_ruleset *ruleset; - struct pf_rulequeue *old_rules; - struct pf_rule *rule; - int rs_num; - ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); - if (ruleset == NULL) { - error = EINVAL; - break; - } - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) { - error = EINVAL; - break; - } - if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { - error = EBUSY; - break; - } - -#ifdef ALTQ - /* set queue IDs */ - if (rs_num == PF_RULESET_FILTER) - pf_rule_set_qid(ruleset->rules[rs_num].inactive.ptr); -#endif - - /* Swap rules, keep the old. */ - s = splsoftnet(); - old_rules = ruleset->rules[rs_num].active.ptr; - ruleset->rules[rs_num].active.ptr = - ruleset->rules[rs_num].inactive.ptr; - ruleset->rules[rs_num].inactive.ptr = old_rules; - ruleset->rules[rs_num].active.ticket = - ruleset->rules[rs_num].inactive.ticket; - pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); - - /* Purge the old rule list. */ - while ((rule = TAILQ_FIRST(old_rules)) != NULL) - pf_rm_rule(old_rules, rule); - pf_remove_if_empty_ruleset(ruleset); - pf_update_anchor_rules(); - splx(s); + error = pf_commit_rules(pr->ticket, pf_get_ruleset_number( + pr->rule.action), pr->anchor, pr->ruleset); break; } @@ -1159,8 +1342,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } bcopy(rule, &pr->rule, sizeof(struct pf_rule)); - pf_dynaddr_copyout(&pr->rule.src.addr); - pf_dynaddr_copyout(&pr->rule.dst.addr); + pfi_dynaddr_copyout(&pr->rule.src.addr); + pfi_dynaddr_copyout(&pr->rule.dst.addr); pf_tbladdr_copyout(&pr->rule.src.addr); pf_tbladdr_copyout(&pr->rule.dst.addr); for (i = 0; i < PF_SKIP_COUNT; ++i) @@ -1244,23 +1427,26 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } #endif /* INET6 */ if (newrule->ifname[0]) { - newrule->ifp = ifunit(newrule->ifname); - if (newrule->ifp == NULL) { + newrule->kif = pfi_attach_rule(newrule->ifname); + if (newrule->kif == NULL) { pool_put(&pf_rule_pl, newrule); error = EINVAL; break; } } else - newrule->ifp = NULL; + newrule->kif = NULL; #ifdef ALTQ /* set queue IDs */ if (newrule->qname[0] != 0) { - newrule->qid = pf_qname_to_qid(newrule->qname); - if (newrule->pqname[0] != 0) - newrule->pqid = - pf_qname_to_qid(newrule->pqname); - else + if ((newrule->qid = + pf_qname2qid(newrule->qname)) == 0) + error = EBUSY; + else if (newrule->pqname[0] != 0) { + if ((newrule->pqid = + pf_qname2qid(newrule->pqname)) == 0) + error = EBUSY; + } else newrule->pqid = newrule->qid; } #endif @@ -1275,9 +1461,9 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (newrule->rt && !newrule->direction) error = EINVAL; - if (pf_dynaddr_setup(&newrule->src.addr, newrule->af)) + if (pfi_dynaddr_setup(&newrule->src.addr, newrule->af)) error = EINVAL; - if (pf_dynaddr_setup(&newrule->dst.addr, newrule->af)) + if (pfi_dynaddr_setup(&newrule->dst.addr, newrule->af)) error = EINVAL; if (pf_tbladdr_setup(ruleset, &newrule->src.addr)) error = EINVAL; @@ -1355,45 +1541,61 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } case DIOCCLRSTATES: { - struct pf_tree_node *n; + struct pf_state *state; + struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; + int killed = 0; s = splsoftnet(); - RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) - n->state->timeout = PFTM_PURGE; + RB_FOREACH(state, pf_state_tree_id, &tree_id) { + if (!psk->psk_ifname[0] || !strcmp(psk->psk_ifname, + state->u.s.kif->pfik_name)) { + state->timeout = PFTM_PURGE; +#if NPFSYNC + /* don't send out individual delete messages */ + state->sync_flags = PFSTATE_NOSYNC; +#endif + killed++; + } + } pf_purge_expired_states(); pf_status.states = 0; + psk->psk_af = killed; +#if NPFSYNC + pfsync_clear_states(pf_status.hostid, psk->psk_ifname); +#endif splx(s); break; } case DIOCKILLSTATES: { - struct pf_tree_node *n; - struct pf_state *st; + struct pf_state *state; struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; int killed = 0; s = splsoftnet(); - RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) { - st = n->state; - if ((!psk->psk_af || st->af == psk->psk_af) && - (!psk->psk_proto || psk->psk_proto == st->proto) && + RB_FOREACH(state, pf_state_tree_id, &tree_id) { + if ((!psk->psk_af || state->af == psk->psk_af) + && (!psk->psk_proto || psk->psk_proto == + state->proto) && PF_MATCHA(psk->psk_src.not, &psk->psk_src.addr.v.a.addr, - &psk->psk_src.addr.v.a.mask, &st->lan.addr, - st->af) && + &psk->psk_src.addr.v.a.mask, + &state->lan.addr, state->af) && PF_MATCHA(psk->psk_dst.not, &psk->psk_dst.addr.v.a.addr, - &psk->psk_dst.addr.v.a.mask, &st->ext.addr, - st->af) && + &psk->psk_dst.addr.v.a.mask, + &state->ext.addr, state->af) && (psk->psk_src.port_op == 0 || pf_match_port(psk->psk_src.port_op, psk->psk_src.port[0], psk->psk_src.port[1], - st->lan.port)) && + state->lan.port)) && (psk->psk_dst.port_op == 0 || pf_match_port(psk->psk_dst.port_op, psk->psk_dst.port[0], psk->psk_dst.port[1], - st->ext.port))) { - st->timeout = PFTM_PURGE; + state->ext.port)) && + (!psk->psk_ifname[0] || !strcmp(psk->psk_ifname, + state->u.s.kif->pfik_name))) { + state->timeout = PFTM_PURGE; killed++; } } @@ -1406,6 +1608,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCADDSTATE: { struct pfioc_state *ps = (struct pfioc_state *)addr; struct pf_state *state; + struct pfi_kif *kif; if (ps->state.timeout >= PFTM_MAX && ps->state.timeout != PFTM_UNTIL_PACKET) { @@ -1418,19 +1621,30 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } s = splsoftnet(); + kif = pfi_lookup_create(ps->state.u.ifname); + if (kif == NULL) { + pool_put(&pf_state_pl, state); + error = ENOENT; + splx(s); + break; + } bcopy(&ps->state, state, sizeof(struct pf_state)); - state->rule.ptr = NULL; + bzero(&state->u, sizeof(state->u)); + state->rule.ptr = &pf_default_rule; state->nat_rule.ptr = NULL; state->anchor.ptr = NULL; - state->rt_ifp = NULL; + state->rt_kif = NULL; #ifdef __FreeBSD__ state->creation = time_second; #else state->creation = time.tv_sec; #endif + state->pfsync_time = 0; state->packets[0] = state->packets[1] = 0; state->bytes[0] = state->bytes[1] = 0; - if (pf_insert_state(state)) { + + if (pf_insert_state(kif, state)) { + pfi_maybe_destroy(kif); pool_put(&pf_state_pl, state); error = ENOMEM; } @@ -1440,29 +1654,29 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCGETSTATE: { struct pfioc_state *ps = (struct pfioc_state *)addr; - struct pf_tree_node *n; + struct pf_state *state; u_int32_t nr; nr = 0; s = splsoftnet(); - RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) { + RB_FOREACH(state, pf_state_tree_id, &tree_id) { if (nr >= ps->nr) break; nr++; } - if (n == NULL) { + if (state == NULL) { error = EBUSY; splx(s); break; } - bcopy(n->state, &ps->state, sizeof(struct pf_state)); - ps->state.rule.nr = n->state->rule.ptr->nr; - ps->state.nat_rule.nr = (n->state->nat_rule.ptr == NULL) ? - -1 : n->state->nat_rule.ptr->nr; - ps->state.anchor.nr = (n->state->anchor.ptr == NULL) ? - -1 : n->state->anchor.ptr->nr; + bcopy(state, &ps->state, sizeof(struct pf_state)); + ps->state.rule.nr = state->rule.ptr->nr; + ps->state.nat_rule.nr = (state->nat_rule.ptr == NULL) ? + -1 : state->nat_rule.ptr->nr; + ps->state.anchor.nr = (state->anchor.ptr == NULL) ? + -1 : state->anchor.ptr->nr; splx(s); - ps->state.expire = pf_state_expires(n->state); + ps->state.expire = pf_state_expires(state); #ifdef __FreeBSD__ if (ps->state.expire > time_second) ps->state.expire -= time_second; @@ -1477,15 +1691,16 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCGETSTATES: { struct pfioc_states *ps = (struct pfioc_states *)addr; - struct pf_tree_node *n; + struct pf_state *state; struct pf_state *p, pstore; + struct pfi_kif *kif; u_int32_t nr = 0; int space = ps->ps_len; if (space == 0) { s = splsoftnet(); - RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) - nr++; + TAILQ_FOREACH(kif, &pfi_statehead, pfik_w_states) + nr += kif->pfik_states; splx(s); ps->ps_len = sizeof(struct pf_state) * nr; #ifdef __FreeBSD__ @@ -1496,40 +1711,44 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) s = splsoftnet(); p = ps->ps_states; - RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) { + TAILQ_FOREACH(kif, &pfi_statehead, pfik_w_states) + RB_FOREACH(state, pf_state_tree_ext_gwy, + &kif->pfik_ext_gwy) { #ifdef __FreeBSD__ - int secs = time_second; + int secs = time_second; #else - int secs = time.tv_sec; + int secs = time.tv_sec; #endif - if ((nr + 1) * sizeof(*p) > (unsigned)ps->ps_len) - break; + if ((nr+1) * sizeof(*p) > (unsigned)ps->ps_len) + break; - bcopy(n->state, &pstore, sizeof(pstore)); - pstore.rule.nr = n->state->rule.ptr->nr; - pstore.nat_rule.nr = (n->state->nat_rule.ptr == NULL) ? - -1 : n->state->nat_rule.ptr->nr; - pstore.anchor.nr = (n->state->anchor.ptr == NULL) ? - -1 : n->state->anchor.ptr->nr; - pstore.creation = secs - pstore.creation; - pstore.expire = pf_state_expires(n->state); - if (pstore.expire > secs) - pstore.expire -= secs; - else - pstore.expire = 0; + bcopy(state, &pstore, sizeof(pstore)); + strlcpy(pstore.u.ifname, kif->pfik_name, + sizeof(pstore.u.ifname)); + pstore.rule.nr = state->rule.ptr->nr; + pstore.nat_rule.nr = (state->nat_rule.ptr == + NULL) ? -1 : state->nat_rule.ptr->nr; + pstore.anchor.nr = (state->anchor.ptr == + NULL) ? -1 : state->anchor.ptr->nr; + pstore.creation = secs - pstore.creation; + pstore.expire = pf_state_expires(state); + if (pstore.expire > secs) + pstore.expire -= secs; + else + pstore.expire = 0; #ifdef __FreeBSD__ - PF_COPYOUT(&pstore, p, sizeof(*p), error); + PF_COPYOUT(&pstore, p, sizeof(*p), error); #else - error = copyout(&pstore, p, sizeof(*p)); + error = copyout(&pstore, p, sizeof(*p)); #endif - if (error) { - splx(s); - goto fail; + if (error) { + splx(s); + goto fail; + } + p++; + nr++; } - p++; - nr++; - } ps->ps_len = sizeof(struct pf_state) * nr; splx(s); break; @@ -1538,68 +1757,44 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCGETSTATUS: { struct pf_status *s = (struct pf_status *)addr; bcopy(&pf_status, s, sizeof(struct pf_status)); + pfi_fill_oldstatus(s); break; } case DIOCSETSTATUSIF: { struct pfioc_if *pi = (struct pfioc_if *)addr; - struct ifnet *ifp; if (pi->ifname[0] == 0) { - status_ifp = NULL; bzero(pf_status.ifname, IFNAMSIZ); break; } - if ((ifp = ifunit(pi->ifname)) == NULL) { + if (ifunit(pi->ifname) == NULL) { error = EINVAL; break; - } else if (ifp == status_ifp) - break; - status_ifp = ifp; - /* fallthrough into DIOCCLRSTATUS */ + } + strlcpy(pf_status.ifname, pi->ifname, IFNAMSIZ); + break; } case DIOCCLRSTATUS: { - u_int32_t running = pf_status.running; - u_int32_t states = pf_status.states; - 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.since = since; - pf_status.debug = debug; - if (status_ifp != NULL) -#if defined(__FreeBSD__) && (__FreeBSD_version < 501113) - snprintf(pf_status.ifname, IFNAMSIZ, "%s%d", - status_ifp->if_name, status_ifp->if_unit); -#else - strlcpy(pf_status.ifname, - status_ifp->if_xname, IFNAMSIZ); -#endif + bzero(pf_status.counters, sizeof(pf_status.counters)); + bzero(pf_status.fcounters, sizeof(pf_status.fcounters)); + bzero(pf_status.scounters, sizeof(pf_status.scounters)); + if (*pf_status.ifname) + pfi_clr_istats(pf_status.ifname, NULL, + PFI_FLAG_INSTANCE); break; } case DIOCNATLOOK: { struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr; - struct pf_state *st; - struct pf_tree_node key; - int direction = pnl->direction; + struct pf_state *state; + struct pf_state key; + int m = 0, direction = pnl->direction; key.af = pnl->af; key.proto = pnl->proto; - /* - * userland gives us source and dest of connection, reverse - * the lookup so we ask for what happens with the return - * traffic, enabling us to find it in the state tree. - */ - PF_ACPY(&key.addr[1], &pnl->saddr, pnl->af); - key.port[1] = pnl->sport; - PF_ACPY(&key.addr[0], &pnl->daddr, pnl->af); - key.port[0] = pnl->dport; - if (!pnl->proto || PF_AZERO(&pnl->saddr, pnl->af) || PF_AZERO(&pnl->daddr, pnl->af) || @@ -1607,22 +1802,40 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = EINVAL; else { s = splsoftnet(); - if (direction == PF_IN) - st = pf_find_state(&tree_ext_gwy, &key); - else - st = pf_find_state(&tree_lan_ext, &key); - if (st != NULL) { + + /* + * userland gives us source and dest of connection, + * reverse the lookup so we ask for what happens with + * the return traffic, enabling us to find it in the + * state tree. + */ + if (direction == PF_IN) { + PF_ACPY(&key.ext.addr, &pnl->daddr, pnl->af); + key.ext.port = pnl->dport; + PF_ACPY(&key.gwy.addr, &pnl->saddr, pnl->af); + key.gwy.port = pnl->sport; + state = pf_find_state_all(&key, PF_EXT_GWY, &m); + } else { + PF_ACPY(&key.lan.addr, &pnl->daddr, pnl->af); + key.lan.port = pnl->dport; + PF_ACPY(&key.ext.addr, &pnl->saddr, pnl->af); + key.ext.port = pnl->sport; + state = pf_find_state_all(&key, PF_LAN_EXT, &m); + } + if (m > 1) + error = E2BIG; /* more than one state */ + else if (state != NULL) { if (direction == PF_IN) { - PF_ACPY(&pnl->rsaddr, &st->lan.addr, - st->af); - pnl->rsport = st->lan.port; + PF_ACPY(&pnl->rsaddr, &state->lan.addr, + state->af); + pnl->rsport = state->lan.port; PF_ACPY(&pnl->rdaddr, &pnl->daddr, pnl->af); pnl->rdport = pnl->dport; } else { - PF_ACPY(&pnl->rdaddr, &st->gwy.addr, - st->af); - pnl->rdport = st->gwy.port; + PF_ACPY(&pnl->rdaddr, &state->gwy.addr, + state->af); + pnl->rdport = state->gwy.port; PF_ACPY(&pnl->rsaddr, &pnl->saddr, pnl->af); pnl->rsport = pnl->sport; @@ -1675,7 +1888,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) struct pfioc_limit *pl = (struct pfioc_limit *)addr; int old_limit; - if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) { + if (pl->index < 0 || pl->index >= PF_LIMIT_MAX || + pf_pool_limits[pl->index].pp == NULL) { error = EINVAL; goto fail; } @@ -1755,13 +1969,17 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) /* set tokenbucket regulator */ tb.rate = altq->ifbandwidth; tb.depth = altq->tbrsize; + PF_UNLOCK(); error = tbr_set(&ifp->if_snd, &tb); + PF_LOCK(); if (error != 0) break; } } +#ifndef __FreeBSD__ if (error == 0) pfaltq_running = 1; +#endif splx(s); DPFPRINTF(PF_DEBUG_MISC, ("altq: started\n")); break; @@ -1788,13 +2006,17 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } /* clear tokenbucket regulator */ tb.rate = 0; + PF_UNLOCK(); err = tbr_set(&ifp->if_snd, &tb); + PF_LOCK(); if (err != 0 && error == 0) error = err; } } +#ifndef __FreeBSD__ if (error == 0) pfaltq_running = 0; +#endif splx(s); DPFPRINTF(PF_DEBUG_MISC, ("altq: stopped\n")); break; @@ -1802,24 +2024,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCBEGINALTQS: { u_int32_t *ticket = (u_int32_t *)addr; - struct pf_altq *altq; - /* Purge the old altq list */ - while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { - /* detach and destroy the discipline */ -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - error = altq_remove(altq); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - } - pool_put(&pf_altq_pl, altq); - } - *ticket = ++ticket_altqs_inactive; + error = pf_begin_altq(ticket); break; } @@ -1843,6 +2049,11 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) * copy the necessary fields */ if (altq->qname[0] != 0) { + if ((altq->qid = pf_qname2qid(altq->qname)) == 0) { + error = EBUSY; + pool_put(&pf_altq_pl, altq); + break; + } TAILQ_FOREACH(a, pf_altqs_inactive, entries) { if (strncmp(a->ifname, altq->ifname, IFNAMSIZ) == 0 && a->qname[0] == 0) { @@ -1870,75 +2081,9 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } case DIOCCOMMITALTQS: { - u_int32_t *ticket = (u_int32_t *)addr; - struct pf_altqqueue *old_altqs; - struct pf_altq *altq; - struct pf_anchor *anchor; - struct pf_ruleset *ruleset; - int err; - - if (*ticket != ticket_altqs_inactive) { - error = EBUSY; - break; - } - - /* Swap altqs, keep the old. */ - s = splsoftnet(); - old_altqs = pf_altqs_active; - pf_altqs_active = pf_altqs_inactive; - pf_altqs_inactive = old_altqs; - ticket_altqs_active = ticket_altqs_inactive; + u_int32_t ticket = *(u_int32_t *)addr; - /* Attach new disciplines */ - TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { - /* attach the discipline */ -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - error = altq_pfattach(altq); -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - if (error) { - splx(s); - goto fail; - } - } - } - - /* Purge the old altq list */ - while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { - /* detach and destroy the discipline */ -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - err = altq_pfdetach(altq); - if (err != 0 && error == 0) - error = err; - err = altq_remove(altq); - if (err != 0 && error == 0) - error = err; -#ifdef __FreeBSD__ - PF_LOCK(); -#endif - } - pool_put(&pf_altq_pl, altq); - } - splx(s); - - /* update queue IDs */ - pf_rule_set_qid( - pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); - TAILQ_FOREACH(anchor, &pf_anchors, entries) { - TAILQ_FOREACH(ruleset, &anchor->rulesets, entries) { - pf_rule_set_qid( - ruleset->rules[PF_RULESET_FILTER].active.ptr - ); - } - } + error = pf_commit_altq(ticket); break; } @@ -2061,15 +2206,16 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } bcopy(&pp->addr, pa, sizeof(struct pf_pooladdr)); if (pa->ifname[0]) { - pa->ifp = ifunit(pa->ifname); - if (pa->ifp == NULL) { + pa->kif = pfi_attach_rule(pa->ifname); + if (pa->kif == NULL) { pool_put(&pf_pooladdr_pl, pa); error = EINVAL; break; } } - if (pf_dynaddr_setup(&pa->addr, pp->af)) { - pf_dynaddr_remove(&pa->addr); + if (pfi_dynaddr_setup(&pa->addr, pp->af)) { + pfi_dynaddr_remove(&pa->addr); + pfi_detach_rule(pa->kif); pool_put(&pf_pooladdr_pl, pa); error = EINVAL; break; @@ -2119,7 +2265,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } bcopy(pa, &pp->addr, sizeof(struct pf_pooladdr)); - pf_dynaddr_copyout(&pp->addr.addr); + pfi_dynaddr_copyout(&pp->addr.addr); pf_tbladdr_copyout(&pp->addr.addr); splx(s); break; @@ -2175,17 +2321,18 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } #endif /* INET6 */ if (newpa->ifname[0]) { - newpa->ifp = ifunit(newpa->ifname); - if (newpa->ifp == NULL) { + newpa->kif = pfi_attach_rule(newpa->ifname); + if (newpa->kif == NULL) { pool_put(&pf_pooladdr_pl, newpa); error = EINVAL; break; } } else - newpa->ifp = NULL; - if (pf_dynaddr_setup(&newpa->addr, pca->af) || + newpa->kif = NULL; + if (pfi_dynaddr_setup(&newpa->addr, pca->af) || pf_tbladdr_setup(ruleset, &newpa->addr)) { - pf_dynaddr_remove(&newpa->addr); + pfi_dynaddr_remove(&newpa->addr); + pfi_detach_rule(newpa->kif); pool_put(&pf_pooladdr_pl, newpa); error = EINVAL; break; @@ -2215,8 +2362,9 @@ pfioctl(struct cdev *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); + pfi_dynaddr_remove(&oldpa->addr); pf_tbladdr_remove(&oldpa->addr); + pfi_detach_rule(oldpa->kif); pool_put(&pf_pooladdr_pl, oldpa); } else { if (oldpa == NULL) @@ -2309,7 +2457,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel, - io->pfrio_flags); + io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2321,7 +2469,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_add_tables(io->pfrio_buffer, io->pfrio_size, - &io->pfrio_nadd, io->pfrio_flags); + &io->pfrio_nadd, io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2333,7 +2481,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_del_tables(io->pfrio_buffer, io->pfrio_size, - &io->pfrio_ndel, io->pfrio_flags); + &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2345,7 +2493,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_get_tables(&io->pfrio_table, io->pfrio_buffer, - &io->pfrio_size, io->pfrio_flags); + &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2357,7 +2505,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_get_tstats(&io->pfrio_table, io->pfrio_buffer, - &io->pfrio_size, io->pfrio_flags); + &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2369,7 +2517,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_clr_tstats(io->pfrio_buffer, io->pfrio_size, - &io->pfrio_nzero, io->pfrio_flags); + &io->pfrio_nzero, io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2382,7 +2530,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } error = pfr_set_tflags(io->pfrio_buffer, io->pfrio_size, io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange, - &io->pfrio_ndel, io->pfrio_flags); + &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2394,7 +2542,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_clr_addrs(&io->pfrio_table, &io->pfrio_ndel, - io->pfrio_flags); + io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2406,7 +2554,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_add_addrs(&io->pfrio_table, io->pfrio_buffer, - io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags); + io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags | + PFR_FLAG_USERIOCTL); break; } @@ -2418,7 +2567,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_del_addrs(&io->pfrio_table, io->pfrio_buffer, - io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags); + io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags | + PFR_FLAG_USERIOCTL); break; } @@ -2431,7 +2581,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } error = pfr_set_addrs(&io->pfrio_table, io->pfrio_buffer, io->pfrio_size, &io->pfrio_size2, &io->pfrio_nadd, - &io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags); + &io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags | + PFR_FLAG_USERIOCTL); break; } @@ -2443,7 +2594,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_get_addrs(&io->pfrio_table, io->pfrio_buffer, - &io->pfrio_size, io->pfrio_flags); + &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2455,7 +2606,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_get_astats(&io->pfrio_table, io->pfrio_buffer, - &io->pfrio_size, io->pfrio_flags); + &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2467,7 +2618,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_clr_astats(&io->pfrio_table, io->pfrio_buffer, - io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags); + io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags | + PFR_FLAG_USERIOCTL); break; } @@ -2479,7 +2631,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_tst_addrs(&io->pfrio_table, io->pfrio_buffer, - io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags); + io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags | + PFR_FLAG_USERIOCTL); break; } @@ -2491,7 +2644,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_ina_begin(&io->pfrio_table, &io->pfrio_ticket, - &io->pfrio_ndel, io->pfrio_flags); + &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); break; } @@ -2503,7 +2656,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } error = pfr_ina_commit(&io->pfrio_table, io->pfrio_ticket, - &io->pfrio_nadd, &io->pfrio_nchange, io->pfrio_flags); + &io->pfrio_nadd, &io->pfrio_nchange, io->pfrio_flags | + PFR_FLAG_USERIOCTL); break; } @@ -2516,16 +2670,10 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) } error = pfr_ina_define(&io->pfrio_table, io->pfrio_buffer, io->pfrio_size, &io->pfrio_nadd, &io->pfrio_naddr, - io->pfrio_ticket, io->pfrio_flags); + io->pfrio_ticket, io->pfrio_flags | PFR_FLAG_USERIOCTL); 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(); @@ -2542,400 +2690,472 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } - default: - error = ENODEV; - break; - } -fail: + case DIOCXBEGIN: { + struct pfioc_trans *io = (struct pfioc_trans *)addr; + struct pfioc_trans_e ioe; + struct pfr_table table; + int i; + + if (io->esize != sizeof(ioe)) { + error = ENODEV; + goto fail; + } + for (i = 0; i < io->size; i++) { #ifdef __FreeBSD__ - PF_UNLOCK(); + PF_COPYIN(io->array+i, &ioe, sizeof(ioe), error); + if (error) { +#else + if (copyin(io->array+i, &ioe, sizeof(ioe))) { #endif - return (error); -} - + error = EFAULT; + goto fail; + } + switch (ioe.rs_num) { +#ifdef ALTQ + case PF_RULESET_ALTQ: + if (ioe.anchor[0] || ioe.ruleset[0]) { + error = EINVAL; + goto fail; + } + if ((error = pf_begin_altq(&ioe.ticket))) + goto fail; + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, ioe.anchor, + sizeof(table.pfrt_anchor)); + strlcpy(table.pfrt_ruleset, ioe.ruleset, + sizeof(table.pfrt_ruleset)); + if ((error = pfr_ina_begin(&table, + &ioe.ticket, NULL, 0))) + goto fail; + break; + default: + if ((error = pf_begin_rules(&ioe.ticket, + ioe.rs_num, ioe.anchor, ioe.ruleset))) + goto fail; + break; + } #ifdef __FreeBSD__ -/* - * XXX - Check for version missmatch!!! - */ -static int -pf_beginrules(void *addr) -{ - struct pfioc_rule *pr = (struct pfioc_rule *)addr; - struct pf_ruleset *ruleset; - struct pf_rule *rule; - int rs_num; - int error = 0; - - do { - ruleset = pf_find_or_create_ruleset(pr->anchor, pr->ruleset); - if (ruleset == NULL) { - error = EINVAL; - break; - } - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) { - error = EINVAL; - break; + PF_COPYOUT(&ioe, io->array+i, sizeof(io->array[i]), + error); + if (error) { +#else + if (copyout(&ioe, io->array+i, sizeof(io->array[i]))) { +#endif + error = EFAULT; + goto fail; + } } - while ((rule = - TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr)) != NULL) - pf_rm_rule(ruleset->rules[rs_num].inactive.ptr, rule); - pr->ticket = ++ruleset->rules[rs_num].inactive.ticket; - } while(0); - - return (error); -} + break; + } -static int -pf_commitrules(void *addr) -{ - struct pfioc_rule *pr = (struct pfioc_rule *)addr; - struct pf_ruleset *ruleset; - struct pf_rulequeue *old_rules; - struct pf_rule *rule; - int rs_num, s; - int error = 0; + case DIOCXROLLBACK: { + struct pfioc_trans *io = (struct pfioc_trans *)addr; + struct pfioc_trans_e ioe; + struct pfr_table table; + int i; - do { - ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); - if (ruleset == NULL) { - error = EINVAL; - break; - } - rs_num = pf_get_ruleset_number(pr->rule.action); - if (rs_num >= PF_RULESET_MAX) { - error = EINVAL; - break; - } - if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { - error = EBUSY; - break; + if (io->esize != sizeof(ioe)) { + error = ENODEV; + goto fail; } - -#ifdef ALTQ - /* set queue IDs */ - if (rs_num == PF_RULESET_FILTER) - pf_rule_set_qid(ruleset->rules[rs_num].inactive.ptr); + for (i = 0; i < io->size; i++) { +#ifdef __FreeBSD__ + PF_COPYIN(io->array+i, &ioe, sizeof(ioe), error); + if (error) { +#else + if (copyin(io->array+i, &ioe, sizeof(ioe))) { #endif - - /* Swap rules, keep the old. */ - s = splsoftnet(); - old_rules = ruleset->rules[rs_num].active.ptr; - ruleset->rules[rs_num].active.ptr = - ruleset->rules[rs_num].inactive.ptr; - ruleset->rules[rs_num].inactive.ptr = old_rules; - ruleset->rules[rs_num].active.ticket = - ruleset->rules[rs_num].inactive.ticket; - pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); - - /* Purge the old rule list. */ - while ((rule = TAILQ_FIRST(old_rules)) != NULL) - pf_rm_rule(old_rules, rule); - pf_remove_if_empty_ruleset(ruleset); - pf_update_anchor_rules(); - splx(s); - } while (0); - - return (error); -} - + error = EFAULT; + goto fail; + } + switch (ioe.rs_num) { #ifdef ALTQ -static int -pf_beginaltqs(void *addr) -{ - u_int32_t *ticket = (u_int32_t *)addr; - struct pf_altq *altq; - int error = 0; + case PF_RULESET_ALTQ: + if (ioe.anchor[0] || ioe.ruleset[0]) { + error = EINVAL; + goto fail; + } + if ((error = pf_rollback_altq(ioe.ticket))) + goto fail; /* really bad */ + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, ioe.anchor, + sizeof(table.pfrt_anchor)); + strlcpy(table.pfrt_ruleset, ioe.ruleset, + sizeof(table.pfrt_ruleset)); + if ((error = pfr_ina_rollback(&table, + ioe.ticket, NULL, 0))) + goto fail; /* really bad */ + break; + default: + if ((error = pf_rollback_rules(ioe.ticket, + ioe.rs_num, ioe.anchor, ioe.ruleset))) + goto fail; /* really bad */ + break; + } + } + break; + } - /* Purge the old altq list */ - while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { + case DIOCXCOMMIT: { + struct pfioc_trans *io = (struct pfioc_trans *)addr; + struct pfioc_trans_e ioe; + struct pfr_table table; + struct pf_ruleset *rs; + int i; + + if (io->esize != sizeof(ioe)) { + error = ENODEV; + goto fail; + } + /* first makes sure everything will succeed */ + for (i = 0; i < io->size; i++) { #ifdef __FreeBSD__ - PF_UNLOCK(); + PF_COPYIN(io->array+i, &ioe, sizeof(ioe), error); + if (error) { +#else + if (copyin(io->array+i, &ioe, sizeof(ioe))) { #endif - /* detach and destroy the discipline */ - error = altq_remove(altq); + error = EFAULT; + goto fail; + } + switch (ioe.rs_num) { +#ifdef ALTQ + case PF_RULESET_ALTQ: + if (ioe.anchor[0] || ioe.ruleset[0]) { + error = EINVAL; + goto fail; + } + if (!altqs_inactive_open || ioe.ticket != + ticket_altqs_inactive) { + error = EBUSY; + goto fail; + } + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + rs = pf_find_ruleset(ioe.anchor, ioe.ruleset); + if (rs == NULL || !rs->topen || ioe.ticket != + rs->tticket) { + error = EBUSY; + goto fail; + } + break; + default: + if (ioe.rs_num < 0 || ioe.rs_num >= + PF_RULESET_MAX) { + error = EINVAL; + goto fail; + } + rs = pf_find_ruleset(ioe.anchor, ioe.ruleset); + if (rs == NULL || + !rs->rules[ioe.rs_num].inactive.open || + rs->rules[ioe.rs_num].inactive.ticket != + ioe.ticket) { + error = EBUSY; + goto fail; + } + break; + } + } + /* now do the commit - no errors should happen here */ + for (i = 0; i < io->size; i++) { #ifdef __FreeBSD__ - PF_LOCK(); + PF_COPYIN(io->array+i, &ioe, sizeof(ioe), error); + if (error) { +#else + if (copyin(io->array+i, &ioe, sizeof(ioe))) { #endif + error = EFAULT; + goto fail; + } + switch (ioe.rs_num) { +#ifdef ALTQ + case PF_RULESET_ALTQ: + if ((error = pf_commit_altq(ioe.ticket))) + goto fail; /* really bad */ + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, ioe.anchor, + sizeof(table.pfrt_anchor)); + strlcpy(table.pfrt_ruleset, ioe.ruleset, + sizeof(table.pfrt_ruleset)); + if ((error = pfr_ina_commit(&table, ioe.ticket, + NULL, NULL, 0))) + goto fail; /* really bad */ + break; + default: + if ((error = pf_commit_rules(ioe.ticket, + ioe.rs_num, ioe.anchor, ioe.ruleset))) + goto fail; /* really bad */ + break; + } } - uma_zfree(pf_altq_pl, altq); + break; } - *ticket = ++ticket_altqs_inactive; - - return (error); -} -static int -pf_commitaltqs(void *addr) -{ - u_int32_t *ticket = (u_int32_t *)addr; - struct pf_altqqueue *old_altqs; - struct pf_altq *altq; - struct pf_anchor *anchor; - struct pf_ruleset *ruleset; - int err; - int s; - int error = 0; - - do { - if (*ticket != ticket_altqs_inactive) { - error = EBUSY; - break; - } - - /* Swap altqs, keep the old. */ - s = splsoftnet(); - old_altqs = pf_altqs_active; - pf_altqs_active = pf_altqs_inactive; - pf_altqs_inactive = old_altqs; - ticket_altqs_active = ticket_altqs_inactive; + 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; - /* Attach new disciplines */ - TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { - /* attach the discipline */ -#ifdef __FreeBSD__ - PF_UNLOCK(); -#endif - error = altq_pfattach(altq); + 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; #ifdef __FreeBSD__ - PF_LOCK(); + PF_UNLOCK(); #endif - if (error) { - splx(s); - goto altq_fail; - } - } + return (0); } - /* Purge the old altq list */ - while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { - TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { - /* detach and destroy the discipline */ + s = splsoftnet(); + p = psn->psn_src_nodes; + RB_FOREACH(n, pf_src_tree, &tree_src_tracking) { #ifdef __FreeBSD__ - PF_UNLOCK(); + int secs = time_second; +#else + int secs = time.tv_sec; #endif - err = altq_pfdetach(altq); - if (err != 0 && error == 0) - error = err; - err = altq_remove(altq); - if (err != 0 && error == 0) - error = err; + + 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; #ifdef __FreeBSD__ - PF_LOCK(); + PF_COPYOUT(&pstore, p, sizeof(*p), error); +#else + error = copyout(&pstore, p, sizeof(*p)); #endif + if (error) { + splx(s); + goto fail; } - uma_zfree(pf_altq_pl, altq); + p++; + nr++; } + psn->psn_len = sizeof(struct pf_src_node) * nr; splx(s); + break; + } - /* update queue IDs */ - pf_rule_set_qid( - pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); - TAILQ_FOREACH(anchor, &pf_anchors, entries) { - TAILQ_FOREACH(ruleset, &anchor->rulesets, entries) { - pf_rule_set_qid( - ruleset->rules[PF_RULESET_FILTER].active.ptr - ); - } - } - } while (0); + case DIOCCLRSRCNODES: { + struct pf_src_node *n; + struct pf_state *state; -altq_fail: + s = splsoftnet(); + RB_FOREACH(state, pf_state_tree_id, &tree_id) { + 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; + } - return (error); -} + case DIOCSETHOSTID: { + u_int32_t *hostid = (u_int32_t *)addr; -static int -pf_stopaltq(void) -{ - struct pf_altq *altq; - struct ifnet *ifp; - struct tb_profile tb; - int err; - int s; - int error = 0; + if (*hostid == 0) { + error = EINVAL; + goto fail; + } + pf_status.hostid = *hostid; + break; + } - do { - /* disable all altq interfaces on active list */ + case DIOCOSFPFLUSH: s = splsoftnet(); - TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { - if ((ifp = ifunit(altq->ifname)) == NULL) { - error = EINVAL; - break; - } - if (ifp->if_snd.altq_type != ALTQT_NONE) { - err = altq_disable(&ifp->if_snd); - if (err != 0 && error == 0) - error = err; - } - /* clear tokenbucket regulator */ - tb.rate = 0; - err = tbr_set(&ifp->if_snd, &tb); - if (err != 0 && error == 0) - error = err; - } - } - if (error == 0) - pfaltq_running = 0; + pf_osfp_flush(); splx(s); - } while (0); + break; + + case DIOCIGETIFACES: { + struct pfioc_iface *io = (struct pfioc_iface *)addr; + + if (io->pfiio_esize != sizeof(struct pfi_if)) { + error = ENODEV; + break; + } + error = pfi_get_ifaces(io->pfiio_name, io->pfiio_buffer, + &io->pfiio_size, io->pfiio_flags); + break; + } + + case DIOCICLRISTATS: { + struct pfioc_iface *io = (struct pfioc_iface *)addr; + + error = pfi_clr_istats(io->pfiio_name, &io->pfiio_nzero, + io->pfiio_flags); + break; + } + default: + error = ENODEV; + break; + } +fail: +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif return (error); } -#endif +#ifdef __FreeBSD__ +/* + * XXX - Check for version missmatch!!! + */ static void -pf_clearstates(void) +pf_clear_states(void) { - struct pf_tree_node *n; - int s; + struct pf_state *state; - s = splsoftnet(); - RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) - n->state->timeout = PFTM_PURGE; + RB_FOREACH(state, pf_state_tree_id, &tree_id) { + state->timeout = PFTM_PURGE; +#if NPFSYNC + /* don't send out individual delete messages */ + state->sync_flags = PFSTATE_NOSYNC; +#endif + } pf_purge_expired_states(); pf_status.states = 0; - splx(s); +#if 0 /* NPFSYNC */ +/* + * XXX This is called on module unload, we do not want to sync that over? */ + */ + pfsync_clear_states(pf_status.hostid, psk->psk_ifname); +#endif } static int -pf_clear_tables(void *addr) +pf_clear_tables(void) { - struct pfioc_table *io = (struct pfioc_table *)addr; + struct pfioc_table io; int error; - error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel, - io->pfrio_flags); + bzero(&io, sizeof(io)); + + error = pfr_clr_tables(&io.pfrio_table, &io.pfrio_ndel, + io.pfrio_flags); return (error); } +static void +pf_clear_srcnodes(void) +{ + struct pf_src_node *n; + struct pf_state *state; + + RB_FOREACH(state, pf_state_tree_id, &tree_id) { + 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; +} +/* + * XXX - Check for version missmatch!!! + */ + +/* + * Duplicate pfctl -Fa operation to get rid of as much as we can. + */ static int shutdown_pf(void) { - struct pfioc_rule pr; -#ifdef ALTQ - struct pfioc_altq pa; -#endif - struct pfioc_table io; int error = 0; + u_int32_t t[5]; + char nn = '\0'; callout_stop(&pf_expire_to); - PF_LOCK(); pf_status.running = 0; do { -#ifdef ALTQ - if ((error = pf_stopaltq())) { - DPFPRINTF(PF_DEBUG_MISC, - ("ALTQ: stop(%i)\n", error)); + if ((error = pf_begin_rules(&t[0], PF_RULESET_SCRUB, &nn, + &nn)) != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: SCRUB\n")); break; } -#endif - bzero(&pr, sizeof(pr)); - pr.rule.action = PF_SCRUB; - if ((error = pf_beginrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_SCRUB: begin(%i)\n", error)); - break; + if ((error = pf_begin_rules(&t[1], PF_RULESET_FILTER, &nn, + &nn)) != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: FILTER\n")); + break; /* XXX: rollback? */ } - if ((error = pf_commitrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_SCRUB: commit(%i)\n", error)); - break; + if ((error = pf_begin_rules(&t[2], PF_RULESET_NAT, &nn, &nn)) + != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: NAT\n")); + break; /* XXX: rollback? */ } - - pr.rule.action = PF_PASS; - if ((error = pf_beginrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_PASS: begin(%i)\n", error)); - break; + if ((error = pf_begin_rules(&t[3], PF_RULESET_BINAT, &nn, &nn)) + != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: BINAT\n")); + break; /* XXX: rollback? */ } - if ((error = pf_commitrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_PASS: commit(%i)\n", error)); - break; + if ((error = pf_begin_rules(&t[4], PF_RULESET_RDR, &nn, &nn)) + != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: RDR\n")); + break; /* XXX: rollback? */ } -/* - * XXX not sure, but can't hurt: - */ - bzero(&pr, sizeof(pr)); - pr.rule.action = PF_NAT; - if ((error = pf_beginrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_NAT: begin(%i)\n", error)); - break; - } - if ((error = pf_commitrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_NAT: commit(%i)\n", error)); - break; - } - - pr.rule.action = PF_BINAT; - if ((error = pf_beginrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_BINAT: begin(%i)\n", error)); - break; - } - if ((error = pf_commitrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_BINAT: begin(%i)\n", error)); - break; - } + /* XXX: these should always succeed here */ + pf_commit_rules(t[0], PF_RULESET_SCRUB, &nn, &nn); + pf_commit_rules(t[1], PF_RULESET_FILTER, &nn, &nn); + pf_commit_rules(t[2], PF_RULESET_NAT, &nn, &nn); + pf_commit_rules(t[3], PF_RULESET_BINAT, &nn, &nn); + pf_commit_rules(t[4], PF_RULESET_RDR, &nn, &nn); - pr.rule.action = PF_RDR; - if ((error = pf_beginrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_RDR: begin(%i)\n", error)); + if ((error = pf_clear_tables()) != 0) break; - } - if ((error = pf_commitrules(&pr))) { - DPFPRINTF(PF_DEBUG_MISC, - ("PF_RDR: commit(%i)\n", error)); - break; - } #ifdef ALTQ - bzero(&pa, sizeof(pa)); - if ((error = pf_beginaltqs(&pa))) { - DPFPRINTF(PF_DEBUG_MISC, - ("ALTQ: begin(%i)\n", error)); - break; - } - if ((error = pf_commitaltqs(&pa))) { - DPFPRINTF(PF_DEBUG_MISC, - ("ALTQ: commit(%i)\n", error)); + if ((error = pf_begin_altq(&t[0])) != 0) { + DPFPRINTF(PF_DEBUG_MISC, ("shutdown_pf: ALTQ\n")); break; } + pf_commit_altq(t[0]); #endif - pf_clearstates(); - bzero(&io, sizeof(io)); - if ((error = pf_clear_tables(&io))) { - DPFPRINTF(PF_DEBUG_MISC, - ("TABLES: clear(%i)\n", error)); - break; - } - pf_osfp_flush(); + pf_clear_states(); + + pf_clear_srcnodes(); + + /* status does not use malloced mem so no need to cleanup */ + /* fingerprints and interfaces have thier own cleanup code */ } while(0); - PF_UNLOCK(); return (error); } static int -#if (__FreeBSD_version < 501108) -pf_check_in(void *ip, int hlen, struct ifnet *ifp, int dir, struct mbuf **m) -#else pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) -#endif { /* * XXX Wed Jul 9 22:03:16 2003 UTC @@ -2969,11 +3189,7 @@ pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) } static int -#if (__FreeBSD_version < 501108) -pf_check_out(void *ip, int hlen, struct ifnet *ifp, int dir, struct mbuf **m) -#else pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) -#endif { /* * XXX Wed Jul 9 22:03:16 2003 UTC @@ -3013,11 +3229,7 @@ pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) #ifdef INET6 static int -#if (__FreeBSD_version < 501108) -pf_check6_in(void *ip, int hlen, struct ifnet *ifp, int dir, struct mbuf **m) -#else pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) -#endif { /* * IPv6 does not affected ip_len/ip_off byte order changes. @@ -3033,11 +3245,7 @@ pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) } static int -#if (__FreeBSD_version < 501108) -pf_check6_out(void *ip, int hlen, struct ifnet *ifp, int dir, struct mbuf **m) -#else pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) -#endif { /* * IPv6 does not affected ip_len/ip_off byte order changes. @@ -3061,37 +3269,16 @@ pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) static int hook_pf(void) { -#if (__FreeBSD_version >= 501108) struct pfil_head *pfh_inet; #ifdef INET6 struct pfil_head *pfh_inet6; #endif -#endif PF_ASSERT(MA_NOTOWNED); if (pf_pfil_hooked) return (0); -#if (__FreeBSD_version < 501108) - /* - * XXX - * There is no easy way to get pfil header pointer with address - * family such as AF_INET, AF_INET6. - * Needs direct variable reference. - */ - - pfil_add_hook(pf_check_in, PFIL_IN, - &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); - pfil_add_hook(pf_check_out, PFIL_OUT, - &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); -#ifdef INET6 - pfil_add_hook(pf_check6_in, PFIL_IN, - &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); - pfil_add_hook(pf_check6_out, PFIL_OUT, - &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); -#endif -#else /* __FreeBSD_version >= 501108 */ pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (pfh_inet == NULL) return (ESRCH); /* XXX */ @@ -3109,7 +3296,6 @@ hook_pf(void) pfil_add_hook(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); pfil_add_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); #endif -#endif /* __FreeBSD_version >= 501108 */ pf_pfil_hooked = 1; return (0); @@ -3118,30 +3304,16 @@ hook_pf(void) static int dehook_pf(void) { -#if (__FreeBSD_version >= 501108) struct pfil_head *pfh_inet; #ifdef INET6 struct pfil_head *pfh_inet6; #endif -#endif PF_ASSERT(MA_NOTOWNED); if (pf_pfil_hooked == 0) return (0); -#if (__FreeBSD_version < 501108) - pfil_remove_hook(pf_check_in, PFIL_IN, - &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); - pfil_remove_hook(pf_check_out, PFIL_OUT, - &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); -#ifdef INET6 - pfil_remove_hook(pf_check6_in, PFIL_IN, - &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); - pfil_remove_hook(pf_check6_out, PFIL_OUT, - &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); -#endif -#else /* __FreeBSD_version >= 501108 */ pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (pfh_inet == NULL) return (ESRCH); /* XXX */ @@ -3158,7 +3330,6 @@ dehook_pf(void) pfil_remove_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); #endif -#endif /* __FreeBSD_version >= 501108 */ pf_pfil_hooked = 0; return (0); @@ -3196,9 +3367,13 @@ pf_unload(void) printf("%s : pfil unregisteration fail\n", __FUNCTION__); return error; } + PF_LOCK(); shutdown_pf(); - cleanup_pf_zone(); + pfi_cleanup(); + pf_osfp_flush(); pf_osfp_cleanup(); + cleanup_pf_zone(); + PF_UNLOCK(); destroy_dev(pf_dev); destroy_pf_mutex(); return error; @@ -3230,8 +3405,6 @@ static moduledata_t pf_mod = { 0 }; -DECLARE_MODULE(pf, pf_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); -MODULE_DEPEND(pf, pflog, PFLOG_MINVER, PFLOG_PREFVER, PFLOG_MAXVER); -MODULE_DEPEND(pf, pfsync, PFSYNC_MINVER, PFSYNC_PREFVER, PFSYNC_MAXVER); +DECLARE_MODULE(pf, pf_mod, SI_SUB_PSEUDO, SI_ORDER_FIRST); MODULE_VERSION(pf, PF_MODVER); #endif /* __FreeBSD__ */ diff --git a/sys/contrib/pf/net/pf_norm.c b/sys/contrib/pf/net/pf_norm.c index 10f066e..e00a11e 100644 --- a/sys/contrib/pf/net/pf_norm.c +++ b/sys/contrib/pf/net/pf_norm.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: pf_norm.c,v 1.75.2.1 2004/04/30 23:28:36 brad Exp $ */ +/* $OpenBSD: pf_norm.c,v 1.80 2004/03/09 21:44:41 mcbride Exp $ */ /* * Copyright 2001 Niels Provos <provos@citi.umich.edu> @@ -502,7 +502,7 @@ pf_reassemble(struct mbuf **m0, struct pf_fragment **frag, if (frep != NULL && FR_IP_OFF(frep) + ntohs(frep->fr_ip->ip_len) - frep->fr_ip->ip_hl * - 4 > off) + 4 > off) { u_int16_t precut; @@ -797,13 +797,16 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, h = mtod(m, struct ip *); #ifdef __FreeBSD__ - KASSERT(((int)m->m_len == ntohs(h->ip_len) - precut), + KASSERT(((int)m->m_len == + ntohs(h->ip_len) - precut), ("m->m_len != ntohs(h->ip_len) - precut: %s", __FUNCTION__)); #else - KASSERT((int)m->m_len == ntohs(h->ip_len) - precut); + KASSERT((int)m->m_len == + ntohs(h->ip_len) - precut); #endif - h->ip_off = htons(ntohs(h->ip_off) + (precut >> 3)); + h->ip_off = htons(ntohs(h->ip_off) + + (precut >> 3)); h->ip_len = htons(ntohs(h->ip_len) - precut); } else { hosed++; @@ -862,7 +865,8 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, ("m->m_len != ntohs(h->ip_len) - aftercut: %s", __FUNCTION__)); #else - KASSERT((int)m->m_len == ntohs(h->ip_len) - aftercut); + KASSERT((int)m->m_len == + ntohs(h->ip_len) - aftercut); #endif h->ip_len = htons(ntohs(h->ip_len) - aftercut); } else { @@ -982,7 +986,7 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, } int -pf_normalize_ip(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) +pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason) { struct mbuf *m = *m0; struct pf_rule *r; @@ -999,7 +1003,8 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); while (r != NULL) { r->evaluations++; - if (r->ifp != NULL && r->ifp != ifp) + if (r->kif != NULL && + (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != dir) r = r->skip[PF_SKIP_DIR].ptr; @@ -1162,13 +1167,13 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) no_mem: REASON_SET(reason, PFRES_MEMORY); if (r != NULL && r->log) - PFLOG_PACKET(ifp, h, m, AF_INET, dir, *reason, r, NULL, NULL); + PFLOG_PACKET(kif, h, m, AF_INET, dir, *reason, r, NULL, NULL); return (PF_DROP); drop: REASON_SET(reason, PFRES_NORM); if (r != NULL && r->log) - PFLOG_PACKET(ifp, h, m, AF_INET, dir, *reason, r, NULL, NULL); + PFLOG_PACKET(kif, h, m, AF_INET, dir, *reason, r, NULL, NULL); return (PF_DROP); bad: @@ -1180,14 +1185,15 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) REASON_SET(reason, PFRES_FRAG); if (r != NULL && r->log) - PFLOG_PACKET(ifp, h, m, AF_INET, dir, *reason, r, NULL, NULL); + PFLOG_PACKET(kif, h, m, AF_INET, dir, *reason, r, NULL, NULL); return (PF_DROP); } #ifdef INET6 int -pf_normalize_ip6(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) +pf_normalize_ip6(struct mbuf **m0, int dir, struct pfi_kif *kif, + u_short *reason) { struct mbuf *m = *m0; struct pf_rule *r; @@ -1207,7 +1213,8 @@ pf_normalize_ip6(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); while (r != NULL) { r->evaluations++; - if (r->ifp != NULL && r->ifp != ifp) + if (r->kif != NULL && + (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != dir) r = r->skip[PF_SKIP_DIR].ptr; @@ -1341,25 +1348,25 @@ pf_normalize_ip6(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) shortpkt: REASON_SET(reason, PFRES_SHORT); if (r != NULL && r->log) - PFLOG_PACKET(ifp, h, m, AF_INET6, dir, *reason, r, NULL, NULL); + PFLOG_PACKET(kif, h, m, AF_INET6, dir, *reason, r, NULL, NULL); return (PF_DROP); drop: REASON_SET(reason, PFRES_NORM); if (r != NULL && r->log) - PFLOG_PACKET(ifp, h, m, AF_INET6, dir, *reason, r, NULL, NULL); + PFLOG_PACKET(kif, h, m, AF_INET6, dir, *reason, r, NULL, NULL); return (PF_DROP); badfrag: REASON_SET(reason, PFRES_FRAG); if (r != NULL && r->log) - PFLOG_PACKET(ifp, h, m, AF_INET6, dir, *reason, r, NULL, NULL); + PFLOG_PACKET(kif, h, m, AF_INET6, dir, *reason, r, NULL, NULL); return (PF_DROP); } #endif int -pf_normalize_tcp(int dir, struct ifnet *ifp, struct mbuf *m, int ipoff, +pf_normalize_tcp(int dir, struct pfi_kif *kif, struct mbuf *m, int ipoff, int off, void *h, struct pf_pdesc *pd) { struct pf_rule *r, *rm = NULL; @@ -1372,7 +1379,8 @@ pf_normalize_tcp(int dir, struct ifnet *ifp, struct mbuf *m, int ipoff, r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); while (r != NULL) { r->evaluations++; - if (r->ifp != NULL && r->ifp != ifp) + if (r->kif != NULL && + (r->kif != kif && r->kif != kif->pfik_parent) == !r->ifnot) r = r->skip[PF_SKIP_IFP].ptr; else if (r->direction && r->direction != dir) r = r->skip[PF_SKIP_DIR].ptr; @@ -1465,7 +1473,7 @@ pf_normalize_tcp(int dir, struct ifnet *ifp, struct mbuf *m, int ipoff, tcp_drop: REASON_SET(&reason, PFRES_NORM); if (rm != NULL && r->log) - PFLOG_PACKET(ifp, h, m, AF_INET, dir, reason, r, NULL, NULL); + PFLOG_PACKET(kif, h, m, AF_INET, dir, reason, r, NULL, NULL); return (PF_DROP); } @@ -1511,7 +1519,7 @@ pf_normalize_tcp_init(struct mbuf *m, int off, struct pf_pdesc *pd, * the connections. They must all set an enabled bit in pfss_flags */ if ((th->th_flags & TH_SYN) == 0) - return 0; + return (0); if (th->th_off > (sizeof(struct tcphdr) >> 2) && src->scrub && @@ -1535,8 +1543,8 @@ pf_normalize_tcp_init(struct mbuf *m, int off, struct pf_pdesc *pd, } /* FALLTHROUGH */ default: - hlen -= MAX(opt[1], 2); - opt += MAX(opt[1], 2); + hlen -= opt[1]; + opt += opt[1]; break; } } @@ -1591,7 +1599,7 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, #endif /* INET */ #ifdef INET6 case AF_INET6: { - if (dst->scrub) { + if (src->scrub) { struct ip6_hdr *h = mtod(m, struct ip6_hdr *); if (h->ip6_hlim > src->scrub->pfss_ttl) src->scrub->pfss_ttl = h->ip6_hlim; @@ -1635,11 +1643,13 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, &th->th_sum, ts_value, 0); copyback = 1; } - if (dst->scrub && + + /* Modulate TS reply iff valid (!0) */ + memcpy(&ts_value, &opt[6], + sizeof(u_int32_t)); + if (ts_value && dst->scrub && (dst->scrub->pfss_flags & PFSS_TIMESTAMP)) { - memcpy(&ts_value, &opt[6], - sizeof(u_int32_t)); ts_value = htonl(ntohl(ts_value) - dst->scrub->pfss_ts_mod); pf_change_a(&opt[6], @@ -1649,8 +1659,8 @@ pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, } /* FALLTHROUGH */ default: - hlen -= MAX(opt[1], 2); - opt += MAX(opt[1], 2); + hlen -= opt[1]; + opt += opt[1]; break; } } diff --git a/sys/contrib/pf/net/pf_osfp.c b/sys/contrib/pf/net/pf_osfp.c index 8687cb0..af05330 100644 --- a/sys/contrib/pf/net/pf_osfp.c +++ b/sys/contrib/pf/net/pf_osfp.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: pf_osfp.c,v 1.3 2003/08/27 18:23:36 frantzen Exp $ */ +/* $OpenBSD: pf_osfp.c,v 1.9 2004/01/04 20:08:42 pvalchev Exp $ */ /* * Copyright (c) 2003 Mike Frantzen <frantzen@w4g.org> @@ -54,6 +54,7 @@ typedef struct pool pool_t; # include <errno.h> # include <stdio.h> # include <stdlib.h> +# include <string.h> # define pool_t int # define pool_get(pool, flags) malloc(*(pool)) # define pool_put(pool, item) free(item) @@ -64,7 +65,7 @@ typedef struct pool pool_t; # endif # ifdef PFDEBUG -# include <stdarg.h> +# include <sys/stdarg.h> # define DPFPRINTF(format, x...) fprintf(stderr, format , ##x) # else # define DPFPRINTF(format, x...) ((void)0) diff --git a/sys/contrib/pf/net/pf_subr.c b/sys/contrib/pf/net/pf_subr.c new file mode 100644 index 0000000..3de2924 --- /dev/null +++ b/sys/contrib/pf/net/pf_subr.c @@ -0,0 +1,127 @@ +/* $FreeBSD$ */ +/* from $OpenBSD: kern_subr.c,v 1.26 2003/10/31 11:10:41 markus Exp $ */ +/* $NetBSD: kern_subr.c,v 1.15 1996/04/09 17:21:56 ragge Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)kern_subr.c 8.3 (Berkeley) 1/21/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/kernel.h> +#include <sys/resourcevar.h> + +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> + +#include <net/pfvar.h> + +/* + * This implements additional functions used by pf which can not be ported + * easyly. At this point it boils down to mostly the Net/OpenBSD hook + * implementation. + * + * BEWARE: this is not locked! Required locking is done by the caller. + */ + +void * +hook_establish(struct hook_desc_head *head, int tail, void (*fn)(void *), + void *arg) +{ + struct hook_desc *hdp; + + hdp = (struct hook_desc *)malloc(sizeof (*hdp), M_DEVBUF, M_NOWAIT); + if (hdp == NULL) + return (NULL); + + hdp->hd_fn = fn; + hdp->hd_arg = arg; + if (tail) + TAILQ_INSERT_TAIL(head, hdp, hd_list); + else + TAILQ_INSERT_HEAD(head, hdp, hd_list); + + return (hdp); +} + +void +hook_disestablish(struct hook_desc_head *head, void *vhook) +{ + struct hook_desc *hdp; + +#ifdef DIAGNOSTIC + for (hdp = TAILQ_FIRST(head); hdp != NULL; + hdp = TAILQ_NEXT(hdp, hd_list)) + if (hdp == vhook) + break; + if (hdp == NULL) + panic("hook_disestablish: hook not established"); +#endif + hdp = vhook; + TAILQ_REMOVE(head, hdp, hd_list); + free(hdp, M_DEVBUF); +} + +/* + * Run hooks. Startup hooks are invoked right after scheduler_start but + * before root is mounted. Shutdown hooks are invoked immediately before the + * system is halted or rebooted, i.e. after file systems unmounted, + * after crash dump done, etc. + */ +void +dohooks(struct hook_desc_head *head, int flags) +{ + struct hook_desc *hdp; + + if ((flags & HOOK_REMOVE) == 0) { + TAILQ_FOREACH(hdp, head, hd_list) { + (*hdp->hd_fn)(hdp->hd_arg); + } + } else { + while ((hdp = TAILQ_FIRST(head)) != NULL) { + TAILQ_REMOVE(head, hdp, hd_list); + (*hdp->hd_fn)(hdp->hd_arg); + if ((flags & HOOK_FREE) != 0) + free(hdp, M_DEVBUF); + } + } +} diff --git a/sys/contrib/pf/net/pf_table.c b/sys/contrib/pf/net/pf_table.c index fb10915..78e37ed 100644 --- a/sys/contrib/pf/net/pf_table.c +++ b/sys/contrib/pf/net/pf_table.c @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: pf_table.c,v 1.41 2003/08/22 15:19:23 henning Exp $ */ +/* $OpenBSD: pf_table.c,v 1.47 2004/03/09 21:44:41 mcbride Exp $ */ /* * Copyright (c) 2002 Cedric Berger @@ -61,6 +61,55 @@ return (EINVAL); \ } while (0) +#ifdef __FreeBSD__ +static inline int +_copyin(const void *uaddr, void *kaddr, size_t len) +{ + int r; + + PF_UNLOCK(); + r = copyin(uaddr, kaddr, len); + PF_LOCK(); + + return (r); +} + +static inline int +_copyout(const void *uaddr, void *kaddr, size_t len) +{ + int r; + + PF_UNLOCK(); + r = copyout(uaddr, kaddr, len); + PF_LOCK(); + + return (r); +} + +#define COPYIN(from, to, size) \ + ((flags & PFR_FLAG_USERIOCTL) ? \ + _copyin((from), (to), (size)) : \ + (bcopy((from), (to), (size)), 0)) + +#define COPYOUT(from, to, size) \ + ((flags & PFR_FLAG_USERIOCTL) ? \ + _copyout((from), (to), (size)) : \ + (bcopy((from), (to), (size)), 0)) + +#else + +#define COPYIN(from, to, size) \ + ((flags & PFR_FLAG_USERIOCTL) ? \ + copyin((from), (to), (size)) : \ + (bcopy((from), (to), (size)), 0)) + +#define COPYOUT(from, to, size) \ + ((flags & PFR_FLAG_USERIOCTL) ? \ + copyout((from), (to), (size)) : \ + (bcopy((from), (to), (size)), 0)) + +#endif + #define FILLIN_SIN(sin, addr) \ do { \ (sin).sin_len = sizeof(sin); \ @@ -83,8 +132,8 @@ } while (0) #define SUNION2PF(su, af) (((af)==AF_INET) ? \ - (struct pf_addr *)&(su)->sin.sin_addr : \ - (struct pf_addr *)&(su)->sin6.sin6_addr) + (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)) @@ -103,20 +152,24 @@ struct pfr_walktree { PFRW_ENQUEUE, PFRW_GET_ADDRS, PFRW_GET_ASTATS, - PFRW_POOL_GET + PFRW_POOL_GET, + PFRW_DYNADDR_UPDATE } pfrw_op; union { struct pfr_addr *pfrw1_addr; struct pfr_astats *pfrw1_astats; struct pfr_kentryworkq *pfrw1_workq; struct pfr_kentry *pfrw1_kentry; + struct pfi_dynaddr *pfrw1_dyn; } pfrw_1; int pfrw_free; + int pfrw_flags; }; #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_dyn pfrw_1.pfrw1_dyn #define pfrw_cnt pfrw_free #define senderr(e) do { rv = (e); goto _bad; } while (0) @@ -130,7 +183,7 @@ struct pool pfr_kentry_pl; #endif struct sockaddr_in pfr_sin; struct sockaddr_in6 pfr_sin6; -union sockaddr_union pfr_mask; +union sockaddr_union pfr_mask; struct pf_addr pfr_ffaddr; void pfr_copyout_addr(struct pfr_addr *, @@ -150,14 +203,14 @@ void pfr_remove_kentries(struct pfr_ktable *, struct pfr_kentryworkq *); void pfr_clstats_kentries(struct pfr_kentryworkq *, long, int); -void pfr_reset_feedback(struct pfr_addr *, int); +void pfr_reset_feedback(struct pfr_addr *, int, int); void pfr_prepare_network(union sockaddr_union *, int, int); int pfr_route_kentry(struct pfr_ktable *, struct pfr_kentry *); int pfr_unroute_kentry(struct pfr_ktable *, struct pfr_kentry *); int pfr_walktree(struct radix_node *, void *); -int pfr_validate_table(struct pfr_table *, int); +int pfr_validate_table(struct pfr_table *, int, int); void pfr_commit_ktable(struct pfr_ktable *, long); void pfr_insert_ktables(struct pfr_ktableworkq *); void pfr_insert_ktable(struct pfr_ktable *); @@ -172,12 +225,12 @@ void pfr_destroy_ktable(struct pfr_ktable *, int); int pfr_ktable_compare(struct pfr_ktable *, struct pfr_ktable *); struct pfr_ktable *pfr_lookup_table(struct pfr_table *); -void pfr_clean_node_mask(struct pfr_ktable *, +void pfr_clean_node_mask(struct pfr_ktable *, struct pfr_kentryworkq *); 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); +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); @@ -212,7 +265,7 @@ pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) int s; ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY); - if (pfr_validate_table(tbl, 0)) + if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) @@ -246,7 +299,6 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_addr ad; int i, rv, s, xadd = 0; #ifdef __FreeBSD__ - int ec; /* * XXX Is it OK under LP64 environments? */ @@ -256,7 +308,7 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, #endif ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_FEEDBACK); - if (pfr_validate_table(tbl, 0)) + if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) @@ -268,14 +320,8 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, return (ENOMEM); SLIST_INIT(&workq); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(addr+i, &ad, sizeof(ad), ec); - if (ec) - senderr(EFAULT); -#else - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); -#endif if (pfr_validate_addr(&ad)) senderr(EINVAL); p = pfr_lookup_addr(kt, &ad, 1); @@ -302,17 +348,10 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, xadd++; } } -#ifdef __FreeBSD__ if (flags & PFR_FLAG_FEEDBACK) { - PF_COPYOUT(&ad, addr+i, sizeof(ad), ec); - if (ec) + if (COPYOUT(&ad, addr+i, sizeof(ad))) senderr(EFAULT); } -#else - if (flags & PFR_FLAG_FEEDBACK) - if (copyout(&ad, addr+i, sizeof(ad))) - senderr(EFAULT); -#endif } pfr_clean_node_mask(tmpkt, &workq); if (!(flags & PFR_FLAG_DUMMY)) { @@ -331,7 +370,7 @@ _bad: pfr_clean_node_mask(tmpkt, &workq); pfr_destroy_kentries(&workq); if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size); + pfr_reset_feedback(addr, size, flags); pfr_destroy_ktable(tmpkt, 0); return (rv); } @@ -345,12 +384,9 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_kentry *p; struct pfr_addr ad; int i, rv, s, xdel = 0; -#ifdef __FreeBSD__ - int ec; -#endif ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_FEEDBACK); - if (pfr_validate_table(tbl, 0)) + if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) @@ -360,14 +396,8 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, pfr_mark_addrs(kt); SLIST_INIT(&workq); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(addr+i, &ad, sizeof(ad), ec); - if (ec) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); -#else - if (copyin(addr+i, &ad, sizeof(ad))) - senderr(EFAULT); -#endif if (pfr_validate_addr(&ad)) senderr(EINVAL); p = pfr_lookup_addr(kt, &ad, 1); @@ -387,17 +417,9 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, SLIST_INSERT_HEAD(&workq, p, pfrke_workq); xdel++; } -#ifdef __FreeBSD__ - if (flags & PFR_FLAG_FEEDBACK) { - PF_COPYOUT(&ad, addr+i, sizeof(ad), ec); - if (ec) - senderr(EFAULT); - } -#else if (flags & PFR_FLAG_FEEDBACK) - if (copyout(&ad, addr+i, sizeof(ad))) + if (COPYOUT(&ad, addr+i, sizeof(ad))) senderr(EFAULT); -#endif } if (!(flags & PFR_FLAG_DUMMY)) { if (flags & PFR_FLAG_ATOMIC) @@ -411,7 +433,7 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, return (0); _bad: if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size); + pfr_reset_feedback(addr, size, flags); return (rv); } @@ -425,7 +447,6 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_addr ad; int i, rv, s, xadd = 0, xdel = 0, xchange = 0; #ifdef __FreeBSD__ - int ec; /* * XXX Is it OK under LP64 environments? */ @@ -435,7 +456,7 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, #endif ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_FEEDBACK); - if (pfr_validate_table(tbl, 0)) + if (pfr_validate_table(tbl, 0, flags & PFR_FLAG_USERIOCTL)) return (EINVAL); kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) @@ -450,14 +471,8 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, SLIST_INIT(&delq); SLIST_INIT(&changeq); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(addr+i, &ad, sizeof(ad), ec); - if (ec) - senderr(EFAULT); -#else - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); -#endif if (pfr_validate_addr(&ad)) senderr(EINVAL); ad.pfra_fback = PFR_FB_NONE; @@ -492,17 +507,9 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, } } _skip: -#ifdef __FreeBSD__ - if (flags & PFR_FLAG_FEEDBACK) { - PF_COPYOUT(&ad, addr+i, sizeof(ad), ec); - if (ec) - senderr(EFAULT); - } -#else if (flags & PFR_FLAG_FEEDBACK) - if (copyout(&ad, addr+i, sizeof(ad))) + if (COPYOUT(&ad, addr+i, sizeof(ad))) senderr(EFAULT); -#endif } pfr_enqueue_addrs(kt, &delq, &xdel, ENQUEUE_UNMARKED_ONLY); if ((flags & PFR_FLAG_FEEDBACK) && *size2) { @@ -514,14 +521,8 @@ _skip: SLIST_FOREACH(p, &delq, pfrke_workq) { pfr_copyout_addr(&ad, p); ad.pfra_fback = PFR_FB_DELETED; -#ifdef __FreeBSD__ - PF_COPYOUT(&ad, addr+size+i, sizeof(ad), ec); - if (ec) - senderr(EFAULT); -#else - if (copyout(&ad, addr+size+i, sizeof(ad))) + if (COPYOUT(&ad, addr+size+i, sizeof(ad))) senderr(EFAULT); -#endif i++; } } @@ -542,7 +543,7 @@ _skip: *ndel = xdel; if (nchange != NULL) *nchange = xchange; - if ((flags & PFR_FLAG_FEEDBACK) && *size2) + if ((flags & PFR_FLAG_FEEDBACK) && size2) *size2 = size+xdel; pfr_destroy_ktable(tmpkt, 0); return (0); @@ -550,7 +551,7 @@ _bad: pfr_clean_node_mask(tmpkt, &addq); pfr_destroy_kentries(&addq); if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size); + pfr_reset_feedback(addr, size, flags); pfr_destroy_ktable(tmpkt, 0); return (rv); } @@ -563,26 +564,17 @@ pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_kentry *p; struct pfr_addr ad; int i, xmatch = 0; -#ifdef __FreeBSD__ - int ec; -#endif ACCEPT_FLAGS(PFR_FLAG_REPLACE); - if (pfr_validate_table(tbl, 0)) + if (pfr_validate_table(tbl, 0, 0)) return (EINVAL); kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) return (ESRCH); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(addr+i, &ad, sizeof(ad), ec); - if (ec) - return (EFAULT); -#else - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) return (EFAULT); -#endif if (pfr_validate_addr(&ad)) return (EINVAL); if (ADDR_NETWORK(&ad)) @@ -594,14 +586,8 @@ pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, (p->pfrke_not ? PFR_FB_NOTMATCH : PFR_FB_MATCH); if (p != NULL && !p->pfrke_not) xmatch++; -#ifdef __FreeBSD__ - PF_COPYOUT(&ad, addr+i, sizeof(ad), ec); - if (ec) + if (COPYOUT(&ad, addr+i, sizeof(ad))) return (EFAULT); -#else - if (copyout(&ad, addr+i, sizeof(ad))) - return (EFAULT); -#endif } if (nmatch != NULL) *nmatch = xmatch; @@ -617,7 +603,7 @@ pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, int rv; ACCEPT_FLAGS(0); - if (pfr_validate_table(tbl, 0)) + if (pfr_validate_table(tbl, 0, 0)) return (EINVAL); kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) @@ -631,6 +617,7 @@ pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, w.pfrw_op = PFRW_GET_ADDRS; w.pfrw_addr = addr; w.pfrw_free = kt->pfrkt_cnt; + w.pfrw_flags = flags; #ifdef __FreeBSD__ rv = kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); #else @@ -673,7 +660,7 @@ pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, #endif ACCEPT_FLAGS(PFR_FLAG_ATOMIC); /* XXX PFR_FLAG_CLSTATS disabled */ - if (pfr_validate_table(tbl, 0)) + if (pfr_validate_table(tbl, 0, 0)) return (EINVAL); kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) @@ -687,6 +674,7 @@ pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, w.pfrw_op = PFRW_GET_ASTATS; w.pfrw_astats = addr; w.pfrw_free = kt->pfrkt_cnt; + w.pfrw_flags = flags; if (flags & PFR_FLAG_ATOMIC) s = splsoftnet(); #ifdef __FreeBSD__ @@ -728,40 +716,25 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_kentry *p; struct pfr_addr ad; int i, rv, s, xzero = 0; -#ifdef __FreeBSD__ - int ec; -#endif ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_FEEDBACK); - if (pfr_validate_table(tbl, 0)) + if (pfr_validate_table(tbl, 0, 0)) return (EINVAL); kt = pfr_lookup_table(tbl); if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) return (ESRCH); SLIST_INIT(&workq); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(addr+i, &ad, sizeof(ad), ec); - if (ec) - senderr(EFAULT); -#else - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); -#endif if (pfr_validate_addr(&ad)) senderr(EINVAL); p = pfr_lookup_addr(kt, &ad, 1); if (flags & PFR_FLAG_FEEDBACK) { ad.pfra_fback = (p != NULL) ? PFR_FB_CLEARED : PFR_FB_NONE; -#ifdef __FreeBSD__ - PF_COPYOUT(&ad, addr+i, sizeof(ad), ec); - if (ec) + if (COPYOUT(&ad, addr+i, sizeof(ad))) senderr(EFAULT); -#else - if (copyout(&ad, addr+i, sizeof(ad))) - senderr(EFAULT); -#endif } if (p != NULL) { SLIST_INSERT_HEAD(&workq, p, pfrke_workq); @@ -781,7 +754,7 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, return (0); _bad: if (flags & PFR_FLAG_FEEDBACK) - pfr_reset_feedback(addr, size); + pfr_reset_feedback(addr, size, flags); return (rv); } @@ -982,10 +955,10 @@ void pfr_clean_node_mask(struct pfr_ktable *kt, struct pfr_kentryworkq *workq) { - struct pfr_kentry *p; + struct pfr_kentry *p; - SLIST_FOREACH(p, workq, pfrke_workq) - pfr_unroute_kentry(kt, p); + SLIST_FOREACH(p, workq, pfrke_workq) + pfr_unroute_kentry(kt, p); } void @@ -1006,32 +979,17 @@ pfr_clstats_kentries(struct pfr_kentryworkq *workq, long tzero, int negchange) } void -pfr_reset_feedback(struct pfr_addr *addr, int size) +pfr_reset_feedback(struct pfr_addr *addr, int size, int flags) { struct pfr_addr ad; int i; -#ifdef __FreeBSD__ - int ec; -#endif for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(addr+i, &ad, sizeof(ad), ec); - if (ec) - break; -#else - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) break; -#endif ad.pfra_fback = PFR_FB_NONE; -#ifdef __FreeBSD__ - PF_COPYOUT(&ad, addr+i, sizeof(ad), ec); - if (ec) + if (COPYOUT(&ad, addr+i, sizeof(ad))) break; -#else - if (copyout(&ad, addr+i, sizeof(ad))) - break; -#endif } } @@ -1145,10 +1103,7 @@ pfr_walktree(struct radix_node *rn, void *arg) { struct pfr_kentry *ke = (struct pfr_kentry *)rn; struct pfr_walktree *w = arg; - int s; -#ifdef __FreeBSD__ - int ec; -#endif + int s, flags = w->pfrw_flags; switch (w->pfrw_op) { case PFRW_MARK: @@ -1157,7 +1112,7 @@ pfr_walktree(struct radix_node *rn, void *arg) case PFRW_SWEEP: if (ke->pfrke_mark) break; - /* fall trough */ + /* FALLTHROUGH */ case PFRW_ENQUEUE: SLIST_INSERT_HEAD(w->pfrw_workq, ke, pfrke_workq); w->pfrw_cnt++; @@ -1167,14 +1122,8 @@ pfr_walktree(struct radix_node *rn, void *arg) struct pfr_addr ad; pfr_copyout_addr(&ad, ke); -#ifdef __FreeBSD__ - PF_COPYOUT(&ad, w->pfrw_addr, sizeof(ad), ec); - if (ec) - return (EFAULT); -#else if (copyout(&ad, w->pfrw_addr, sizeof(ad))) return (EFAULT); -#endif w->pfrw_addr++; } break; @@ -1192,14 +1141,8 @@ pfr_walktree(struct radix_node *rn, void *arg) splx(s); as.pfras_tzero = ke->pfrke_tzero; -#ifdef __FreeBSD__ - PF_COPYOUT(&as, w->pfrw_astats, sizeof(as), ec); - if (ec) - return (EFAULT); -#else - if (copyout(&as, w->pfrw_astats, sizeof(as))) + if (COPYOUT(&as, w->pfrw_astats, sizeof(as))) return (EFAULT); -#endif w->pfrw_astats++; } break; @@ -1211,6 +1154,25 @@ pfr_walktree(struct radix_node *rn, void *arg) return (1); /* finish search */ } break; + case PFRW_DYNADDR_UPDATE: + if (ke->pfrke_af == AF_INET) { + if (w->pfrw_dyn->pfid_acnt4++ > 0) + break; + pfr_prepare_network(&pfr_mask, AF_INET, ke->pfrke_net); + w->pfrw_dyn->pfid_addr4 = *SUNION2PF( + &ke->pfrke_sa, AF_INET); + w->pfrw_dyn->pfid_mask4 = *SUNION2PF( + &pfr_mask, AF_INET); + } else { + if (w->pfrw_dyn->pfid_acnt6++ > 0) + break; + pfr_prepare_network(&pfr_mask, AF_INET6, ke->pfrke_net); + w->pfrw_dyn->pfid_addr6 = *SUNION2PF( + &ke->pfrke_sa, AF_INET6); + w->pfrw_dyn->pfid_mask6 = *SUNION2PF( + &pfr_mask, AF_INET6); + } + break; } return (0); } @@ -1230,6 +1192,8 @@ pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { if (pfr_skip_table(filter, p, flags)) continue; + if (!strcmp(p->pfrkt_anchor, PF_RESERVED_ANCHOR)) + continue; if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) continue; p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_ACTIVE; @@ -1255,7 +1219,6 @@ pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) struct pfr_ktable *p, *q, *r, key; int i, rv, s, xadd = 0; #ifdef __FreeBSD__ - int ec; /* * XXX Is it OK under LP64 environments? */ @@ -1268,15 +1231,10 @@ pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) SLIST_INIT(&addq); SLIST_INIT(&changeq); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), ec); - if (ec) + if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) senderr(EFAULT); -#else - if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) - senderr(EFAULT); -#endif - if (pfr_validate_table(&key.pfrkt_t, PFR_TFLAG_USRMASK)) + if (pfr_validate_table(&key.pfrkt_t, PFR_TFLAG_USRMASK, + flags & PFR_FLAG_USERIOCTL)) senderr(EINVAL); key.pfrkt_flags |= PFR_TFLAG_ACTIVE; p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); @@ -1348,22 +1306,14 @@ pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) struct pfr_ktableworkq workq; struct pfr_ktable *p, *q, key; int i, s, xdel = 0; -#ifdef __FreeBSD__ - int ec; -#endif ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY); SLIST_INIT(&workq); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), ec); - if (ec) + if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) return (EFAULT); -#else - if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) - return (EFAULT); -#endif - if (pfr_validate_table(&key.pfrkt_t, 0)) + if (pfr_validate_table(&key.pfrkt_t, 0, + flags & PFR_FLAG_USERIOCTL)) return (EINVAL); p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { @@ -1396,9 +1346,6 @@ pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, { struct pfr_ktable *p; int n, nn; -#ifdef __FreeBSD__ - int ec; -#endif ACCEPT_FLAGS(PFR_FLAG_ALLRSETS); n = nn = pfr_table_count(filter, flags); @@ -1413,14 +1360,8 @@ pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, continue; if (n-- <= 0) continue; -#ifdef __FreeBSD__ - PF_COPYOUT(&p->pfrkt_t, tbl++, sizeof(*tbl), ec); - if (ec) - return (EFAULT); -#else - if (copyout(&p->pfrkt_t, tbl++, sizeof(*tbl))) + if (COPYOUT(&p->pfrkt_t, tbl++, sizeof(*tbl))) return (EFAULT); -#endif } if (n) { printf("pfr_get_tables: corruption detected (%d).\n", n); @@ -1438,7 +1379,6 @@ pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, struct pfr_ktableworkq workq; int s, n, nn; #ifdef __FreeBSD__ - int ec; /* * XXX Is it OK under LP64 environments? */ @@ -1466,18 +1406,10 @@ pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, continue; if (!(flags & PFR_FLAG_ATOMIC)) s = splsoftnet(); -#ifdef __FreeBSD__ - PF_COPYOUT(&p->pfrkt_ts, tbl++, sizeof(*tbl), ec); - if (ec) { + if (COPYOUT(&p->pfrkt_ts, tbl++, sizeof(*tbl))) { splx(s); return (EFAULT); } -#else - if (copyout(&p->pfrkt_ts, tbl++, sizeof(*tbl))) { - splx(s); - return (EFAULT); - } -#endif if (!(flags & PFR_FLAG_ATOMIC)) splx(s); SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); @@ -1502,7 +1434,6 @@ pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) struct pfr_ktable *p, key; int i, s, xzero = 0; #ifdef __FreeBSD__ - int ec; /* * XXX Is it OK under LP64 environments? */ @@ -1514,15 +1445,9 @@ pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_ADDRSTOO); SLIST_INIT(&workq); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), ec); - if (ec) + if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) return (EFAULT); -#else - if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) - return (EFAULT); -#endif - if (pfr_validate_table(&key.pfrkt_t, 0)) + if (pfr_validate_table(&key.pfrkt_t, 0, 0)) return (EINVAL); p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); if (p != NULL) { @@ -1549,9 +1474,6 @@ pfr_set_tflags(struct pfr_table *tbl, int size, int setflag, int clrflag, struct pfr_ktableworkq workq; struct pfr_ktable *p, *q, key; int i, s, xchange = 0, xdel = 0; -#ifdef __FreeBSD__ - int ec; -#endif ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY); if ((setflag & ~PFR_TFLAG_USRMASK) || @@ -1560,15 +1482,10 @@ pfr_set_tflags(struct pfr_table *tbl, int size, int setflag, int clrflag, return (EINVAL); SLIST_INIT(&workq); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), ec); - if (ec) + if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) return (EFAULT); -#else - if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) - return (EFAULT); -#endif - if (pfr_validate_table(&key.pfrkt_t, 0)) + if (pfr_validate_table(&key.pfrkt_t, 0, + flags & PFR_FLAG_USERIOCTL)) return (EINVAL); p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { @@ -1648,14 +1565,12 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, struct pfr_addr ad; struct pf_ruleset *rs; int i, rv, xadd = 0, xaddr = 0; -#ifdef __FreeBSD__ - int ec; -#endif ACCEPT_FLAGS(PFR_FLAG_DUMMY|PFR_FLAG_ADDRSTOO); if (size && !(flags & PFR_FLAG_ADDRSTOO)) return (EINVAL); - if (pfr_validate_table(tbl, PFR_TFLAG_USRMASK)) + if (pfr_validate_table(tbl, PFR_TFLAG_USRMASK, + flags & PFR_FLAG_USERIOCTL)) return (EINVAL); rs = pf_find_ruleset(tbl->pfrt_anchor, tbl->pfrt_ruleset); if (rs == NULL || !rs->topen || ticket != rs->tticket) @@ -1697,14 +1612,8 @@ _skip: } SLIST_INIT(&addrq); for (i = 0; i < size; i++) { -#ifdef __FreeBSD__ - PF_COPYIN(addr+i, &ad, sizeof(ad), ec); - if (ec) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); -#else - if (copyin(addr+i, &ad, sizeof(ad))) - senderr(EFAULT); -#endif if (pfr_validate_addr(&ad)) senderr(EINVAL); if (pfr_lookup_addr(shadow, &ad, 1) != NULL) @@ -1746,6 +1655,37 @@ _bad: } int +pfr_ina_rollback(struct pfr_table *trs, u_int32_t ticket, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p; + struct pf_ruleset *rs; + int xdel = 0; + + ACCEPT_FLAGS(PFR_FLAG_DUMMY); + rs = pf_find_ruleset(trs->pfrt_anchor, trs->pfrt_ruleset); + if (rs == NULL || !rs->topen || ticket != rs->tticket) + return (0); + SLIST_INIT(&workq); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE) || + pfr_skip_table(trs, p, 0)) + continue; + p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_INACTIVE; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xdel++; + } + if (!(flags & PFR_FLAG_DUMMY)) { + pfr_setflags_ktables(&workq); + rs->topen = 0; + pf_remove_if_empty_ruleset(rs); + } + if (ndel != NULL) + *ndel = xdel; + return (0); +} + +int pfr_ina_commit(struct pfr_table *trs, u_int32_t ticket, int *nadd, int *nchange, int flags) { @@ -1857,12 +1797,14 @@ pfr_commit_ktable(struct pfr_ktable *kt, long tzero) } int -pfr_validate_table(struct pfr_table *tbl, int allowedflags) +pfr_validate_table(struct pfr_table *tbl, int allowedflags, int no_reserved) { int i; if (!tbl->pfrt_name[0]) return (-1); + if (no_reserved && !strcmp(tbl->pfrt_anchor, PF_RESERVED_ANCHOR)) + return (-1); if (tbl->pfrt_name[PF_TABLE_NAME_SIZE-1]) return (-1); for (i = strlen(tbl->pfrt_name); i < PF_TABLE_NAME_SIZE; i++) @@ -2091,15 +2033,16 @@ pfr_ktable_compare(struct pfr_ktable *p, struct pfr_ktable *q) if ((d = strncmp(p->pfrkt_anchor, q->pfrkt_anchor, PF_ANCHOR_NAME_SIZE))) return (d); - return strncmp(p->pfrkt_ruleset, q->pfrkt_ruleset, - PF_RULESET_NAME_SIZE); + return (strncmp(p->pfrkt_ruleset, q->pfrkt_ruleset, + PF_RULESET_NAME_SIZE)); } struct pfr_ktable * pfr_lookup_table(struct pfr_table *tbl) { /* struct pfr_ktable start like a struct pfr_table */ - return RB_FIND(pfr_ktablehead, &pfr_ktables, (struct pfr_ktable *)tbl); + return (RB_FIND(pfr_ktablehead, &pfr_ktables, + (struct pfr_ktable *)tbl)); } int @@ -2111,7 +2054,7 @@ pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af) if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) kt = kt->pfrkt_root; if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) - return 0; + return (0); switch (af) { case AF_INET: @@ -2216,7 +2159,7 @@ pfr_attach_table(struct pf_ruleset *rs, char *name) } if (!kt->pfrkt_refcnt[PFR_REFCNT_RULE]++) pfr_setflags_ktable(kt, kt->pfrkt_flags|PFR_TFLAG_REFERENCED); - return kt; + return (kt); } void @@ -2283,7 +2226,7 @@ _next_block: } for (;;) { /* we don't want to use a nested block */ - ke2 = (struct pfr_kentry *)(af == AF_INET ? + 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 */ @@ -2313,26 +2256,54 @@ 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; + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_POOL_GET; + w.pfrw_cnt = idx; - switch(af) { + switch (af) { case AF_INET: #ifdef __FreeBSD__ kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); #else rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); #endif - return w.pfrw_kentry; + return (w.pfrw_kentry); case AF_INET6: #ifdef __FreeBSD__ kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); #else rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); #endif - return w.pfrw_kentry; + return (w.pfrw_kentry); default: - return NULL; + return (NULL); } } + +void +pfr_dynaddr_update(struct pfr_ktable *kt, struct pfi_dynaddr *dyn) +{ + struct pfr_walktree w; + int s; + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_DYNADDR_UPDATE; + w.pfrw_dyn = dyn; + + s = splsoftnet(); + dyn->pfid_acnt4 = 0; + dyn->pfid_acnt6 = 0; + if (!dyn->pfid_af || dyn->pfid_af == AF_INET) +#ifdef __FreeBSD__ + kt->pfrkt_ip4->rnh_walktree(kt->pfrkt_ip4, pfr_walktree, &w); +#else + rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); +#endif + if (!dyn->pfid_af || dyn->pfid_af == AF_INET6) +#ifdef __FreeBSD__ + kt->pfrkt_ip6->rnh_walktree(kt->pfrkt_ip6, pfr_walktree, &w); +#else + rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); +#endif + splx(s); +} diff --git a/sys/contrib/pf/net/pfvar.h b/sys/contrib/pf/net/pfvar.h index 9012227..a6c739a 100644 --- a/sys/contrib/pf/net/pfvar.h +++ b/sys/contrib/pf/net/pfvar.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: pfvar.h,v 1.170 2003/08/22 21:50:34 david Exp $ */ +/* $OpenBSD: pfvar.h,v 1.187 2004/03/22 04:54:18 mcbride Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -67,6 +67,7 @@ struct ip; #define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1) enum { PF_INOUT, PF_IN, PF_OUT }; +enum { PF_LAN_EXT, PF_EXT_GWY, PF_ID }; enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NAT, PF_NONAT, PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP }; enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT, @@ -87,16 +88,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 @@ -117,6 +119,12 @@ struct pf_addr { #define PF_TABLE_NAME_SIZE 32 +#define PFI_AFLAG_NETWORK 0x01 +#define PFI_AFLAG_BROADCAST 0x02 +#define PFI_AFLAG_PEER 0x04 +#define PFI_AFLAG_MODEMASK 0x07 +#define PFI_AFLAG_NOALIAS 0x08 + struct pf_addr_wrap { union { struct { @@ -127,26 +135,30 @@ struct pf_addr_wrap { char tblname[PF_TABLE_NAME_SIZE]; } v; union { - struct pf_addr_dyn *dyn; + struct pfi_dynaddr *dyn; struct pfr_ktable *tbl; + int dyncnt; int tblcnt; } p; u_int8_t type; /* PF_ADDR_* */ + u_int8_t iflags; /* PFI_AFLAG_* */ }; #ifdef _KERNEL -struct pf_addr_dyn { - char ifname[IFNAMSIZ]; - struct ifnet *ifp; - struct pf_addr *addr; - sa_family_t af; -#ifdef __FreeBSD__ - eventhandler_tag hook_cookie; -#else - void *hook_cookie; -#endif - u_int8_t undefined; +struct pfi_dynaddr { + struct pf_addr pfid_addr4; + struct pf_addr pfid_mask4; + struct pf_addr pfid_addr6; + struct pf_addr pfid_mask6; + struct pfr_ktable *pfid_kt; + struct pfi_kif *pfid_kif; + void *pfid_hook_cookie; + int pfid_net; /* optional mask, or 128 */ + int pfid_acnt4; /* address count, IPv4 */ + int pfid_acnt6; /* address count, IPv6 */ + sa_family_t pfid_af; /* rule address family */ + u_int8_t pfid_iflags; /* PFI_AFLAG_* */ }; /* @@ -212,7 +224,22 @@ extern void destroy_pf_mutex(void); #define PFSYNC_MINVER 1 #define PFSYNC_PREFVER PFSYNC_MODVER #define PFSYNC_MAXVER 1 -#endif + +/* prototyped for pf_subr.c */ +struct hook_desc { + TAILQ_ENTRY(hook_desc) hd_list; + void (*hd_fn)(void *); + void *hd_arg; +}; +TAILQ_HEAD(hook_desc_head, hook_desc); + +void *hook_establish(struct hook_desc_head *, int, void (*)(void *), void *); +void hook_disestablish(struct hook_desc_head *, void *); +void dohooks(struct hook_desc_head *, int); + +#define HOOK_REMOVE 0x01 +#define HOOK_FREE 0x02 +#endif /* __FreeBSD__ */ #ifdef INET #ifndef INET6 @@ -350,10 +377,7 @@ extern void destroy_pf_mutex(void); ((aw)->type == PF_ADDR_TABLE && \ !pfr_match_addr((aw)->p.tbl, (x), (af))) || \ ((aw)->type == PF_ADDR_DYNIFTL && \ - ((aw)->p.dyn->undefined || \ - (!PF_AZERO(&(aw)->v.a.mask, (af)) && \ - !PF_MATCHA(0, &(aw)->v.a.addr, \ - &(aw)->v.a.mask, (x), (af))))) || \ + !pfi_match_addr((aw)->p.dyn, (x), (af))) || \ ((aw)->type == PF_ADDR_ADDRMASK && \ !PF_AZERO(&(aw)->v.a.mask, (af)) && \ !PF_MATCHA(0, &(aw)->v.a.addr, \ @@ -382,7 +406,7 @@ struct pf_pooladdr { struct pf_addr_wrap addr; TAILQ_ENTRY(pf_pooladdr) entries; char ifname[IFNAMSIZ]; - struct ifnet *ifp; + struct pfi_kif *kif; }; TAILQ_HEAD(pf_palist, pf_pooladdr); @@ -531,7 +555,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]; @@ -549,12 +572,17 @@ struct pf_rule { u_int64_t packets; u_int64_t bytes; - struct ifnet *ifp; + struct pfi_kif *kif; 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; @@ -601,6 +629,9 @@ struct pf_rule { #define PFRULE_FRAGMENT 0x0002 #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 @@ -609,8 +640,28 @@ struct pf_rule { #define PFRULE_RANDOMID 0x0800 #define PFRULE_REASSEMBLE_TCP 0x1000 +/* rule flags again */ +#define PFRULE_IFBOUND 0x00010000 /* if-bound */ +#define PFRULE_GRBOUND 0x00020000 /* group-bound */ + #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 pfi_kif *kif; + 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; +}; + +#define PFSNODE_HIWAT 10000 /* default source node table size */ struct pf_state_scrub { u_int16_t pfss_flags; @@ -637,7 +688,20 @@ struct pf_state_peer { struct pf_state_scrub *scrub; /* state is scrubbed */ }; +TAILQ_HEAD(pf_state_queue, pf_state); + struct pf_state { + u_int64_t id; + union { + struct { + RB_ENTRY(pf_state) entry_lan_ext; + RB_ENTRY(pf_state) entry_ext_gwy; + RB_ENTRY(pf_state) entry_id; + TAILQ_ENTRY(pf_state) entry_updates; + struct pfi_kif *kif; + } s; + char ifname[IFNAMSIZ]; + } u; struct pf_state_host lan; struct pf_state_host gwy; struct pf_state_host ext; @@ -647,27 +711,25 @@ struct pf_state { union pf_rule_ptr anchor; union pf_rule_ptr nat_rule; struct pf_addr rt_addr; - struct ifnet *rt_ifp; + struct pfi_kif *rt_kif; + struct pf_src_node *src_node; + struct pf_src_node *nat_src_node; u_int32_t creation; u_int32_t expire; + u_int32_t pfsync_time; u_int32_t packets[2]; u_int32_t bytes[2]; + u_int32_t creatorid; sa_family_t af; u_int8_t proto; u_int8_t direction; u_int8_t log; u_int8_t allow_opts; u_int8_t timeout; - u_int8_t pad[2]; -}; - -struct pf_tree_node { - RB_ENTRY(pf_tree_node) entry; - struct pf_state *state; - struct pf_addr addr[2]; - u_int16_t port[2]; - sa_family_t af; - u_int8_t proto; + u_int8_t sync_flags; +#define PFSTATE_NOSYNC 0x01 +#define PFSTATE_FROMSYNC 0x02 + u_int8_t pad; }; TAILQ_HEAD(pf_rulequeue, pf_rule); @@ -683,6 +745,7 @@ struct pf_ruleset { struct { struct pf_rulequeue *ptr; u_int32_t ticket; + int open; } active, inactive; } rules[PF_RULESET_MAX]; struct pf_anchor *anchor; @@ -702,6 +765,9 @@ struct pf_anchor { TAILQ_HEAD(pf_anchorqueue, pf_anchor); +#define PF_RESERVED_ANCHOR "_pf" +#define PF_INTERFACE_RULESET "_if" + #define PFR_TFLAG_PERSIST 0x00000001 #define PFR_TFLAG_CONST 0x00000002 #define PFR_TFLAG_ACTIVE 0x00000004 @@ -788,12 +854,13 @@ struct pfr_ktable { struct pfr_ktable *pfrkt_shadow; struct pfr_ktable *pfrkt_root; struct pf_ruleset *pfrkt_rs; + long pfrkt_larg; int pfrkt_nflags; }; #define pfrkt_t pfrkt_ts.pfrts_t #define pfrkt_name pfrkt_t.pfrt_name -#define pfrkt_anchor pfrkt_t.pfrt_anchor -#define pfrkt_ruleset pfrkt_t.pfrt_ruleset +#define pfrkt_anchor pfrkt_t.pfrt_anchor +#define pfrkt_ruleset pfrkt_t.pfrt_ruleset #define pfrkt_flags pfrkt_t.pfrt_flags #define pfrkt_cnt pfrkt_ts.pfrts_cnt #define pfrkt_refcnt pfrkt_ts.pfrts_refcnt @@ -803,6 +870,61 @@ struct pfr_ktable { #define pfrkt_nomatch pfrkt_ts.pfrts_nomatch #define pfrkt_tzero pfrkt_ts.pfrts_tzero +RB_HEAD(pf_state_tree_lan_ext, pf_state); +RB_PROTOTYPE(pf_state_tree_lan_ext, pf_state, + u.s.entry_lan_ext, pf_state_compare_lan_ext); + +RB_HEAD(pf_state_tree_ext_gwy, pf_state); +RB_PROTOTYPE(pf_state_tree_ext_gwy, pf_state, + u.s.entry_ext_gwy, pf_state_compare_ext_gwy); + +struct pfi_if { + char pfif_name[IFNAMSIZ]; + u_int64_t pfif_packets[2][2][2]; + u_int64_t pfif_bytes[2][2][2]; + u_int64_t pfif_addcnt; + u_int64_t pfif_delcnt; + long pfif_tzero; + int pfif_states; + int pfif_rules; + int pfif_flags; +}; + +TAILQ_HEAD(pfi_grouphead, pfi_kif); +TAILQ_HEAD(pfi_statehead, pfi_kif); +RB_HEAD(pfi_ifhead, pfi_kif); +struct pfi_kif { + struct pfi_if pfik_if; + RB_ENTRY(pfi_kif) pfik_tree; + struct pf_state_tree_lan_ext pfik_lan_ext; + struct pf_state_tree_ext_gwy pfik_ext_gwy; + struct pfi_grouphead pfik_grouphead; + TAILQ_ENTRY(pfi_kif) pfik_instances; + TAILQ_ENTRY(pfi_kif) pfik_w_states; + struct hook_desc_head *pfik_ah_head; + void *pfik_ah_cookie; + struct pfi_kif *pfik_parent; + struct ifnet *pfik_ifp; + int pfik_states; + int pfik_rules; +}; +#define pfik_name pfik_if.pfif_name +#define pfik_packets pfik_if.pfif_packets +#define pfik_bytes pfik_if.pfif_bytes +#define pfik_tzero pfik_if.pfif_tzero +#define pfik_flags pfik_if.pfif_flags +#define pfik_addcnt pfik_if.pfif_addcnt +#define pfik_delcnt pfik_if.pfif_delcnt +#define pfik_states pfik_if.pfif_states +#define pfik_rules pfik_if.pfif_rules + +#define PFI_IFLAG_GROUP 0x0001 /* group of interfaces */ +#define PFI_IFLAG_INSTANCE 0x0002 /* single instance */ +#define PFI_IFLAG_CLONABLE 0x0010 /* clonable group */ +#define PFI_IFLAG_DYNAMIC 0x0020 /* dynamic group */ +#define PFI_IFLAG_ATTACHED 0x0040 /* interface attached */ +#define PFI_IFLAG_PLACEHOLDER 0x8000 /* placeholder group/interface */ + struct pf_pdesc { u_int64_t tot_len; /* Make Mickey money */ union { @@ -814,6 +936,9 @@ struct pf_pdesc { #endif /* INET6 */ void *any; } hdr; + struct pf_addr baddr; /* address before translation */ + struct pf_addr naddr; /* address after translation */ + struct pf_rule *nat_rule; /* nat/rdr rule applied to packet */ struct pf_addr *src; struct pf_addr *dst; u_int16_t *ip_sum; @@ -882,6 +1007,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 { \ @@ -900,12 +1029,16 @@ 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_int64_t stateid; u_int32_t running; u_int32_t states; + u_int32_t src_nodes; u_int32_t since; u_int32_t debug; + u_int32_t hostid; char ifname[IFNAMSIZ]; }; @@ -1037,6 +1170,7 @@ struct pfioc_state_kill { int psk_proto; struct pf_rule_addr psk_src; struct pf_rule_addr psk_dst; + char psk_ifname[IFNAMSIZ]; }; struct pfioc_states { @@ -1049,6 +1183,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]; }; @@ -1089,6 +1233,19 @@ struct pfioc_ruleset { char name[PF_RULESET_NAME_SIZE]; }; +#define PF_RULESET_ALTQ (PF_RULESET_MAX) +#define PF_RULESET_TABLE (PF_RULESET_MAX+1) +struct pfioc_trans { + int size; /* number of elements */ + int esize; /* size of each element in bytes */ + struct pfioc_trans_e { + int rs_num; + char anchor[PF_ANCHOR_NAME_SIZE]; + char ruleset[PF_RULESET_NAME_SIZE]; + u_int32_t ticket; + } *array; +}; + #define PFR_FLAG_ATOMIC 0x00000001 #define PFR_FLAG_DUMMY 0x00000002 #define PFR_FLAG_FEEDBACK 0x00000004 @@ -1097,6 +1254,9 @@ struct pfioc_ruleset { #define PFR_FLAG_REPLACE 0x00000020 #define PFR_FLAG_ALLRSETS 0x00000040 #define PFR_FLAG_ALLMASK 0x0000007F +#ifdef _KERNEL +#define PFR_FLAG_USERIOCTL 0x10000000 +#endif struct pfioc_table { struct pfr_table pfrio_table; @@ -1118,6 +1278,20 @@ struct pfioc_table { #define pfrio_clrflag pfrio_nadd +#define PFI_FLAG_GROUP 0x0001 /* gets groups of interfaces */ +#define PFI_FLAG_INSTANCE 0x0002 /* gets single interfaces */ +#define PFI_FLAG_ALLMASK 0x0003 + +struct pfioc_iface { + char pfiio_name[IFNAMSIZ]; + void *pfiio_buffer; + int pfiio_esize; + int pfiio_size; + int pfiio_nzero; + int pfiio_flags; +}; + + /* * ioctl operations */ @@ -1130,7 +1304,7 @@ struct pfioc_table { #define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) #define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) /* XXX cut 8 - 17 */ -#define DIOCCLRSTATES _IO ('D', 18) +#define DIOCCLRSTATES _IOWR('D', 18, struct pfioc_state_kill) #define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) #define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if) #define DIOCGETSTATUS _IOWR('D', 21, struct pf_status) @@ -1186,30 +1360,45 @@ struct pfioc_table { #define DIOCOSFPFLUSH _IO('D', 78) #define DIOCOSFPADD _IOWR('D', 79, struct pf_osfp_ioctl) #define DIOCOSFPGET _IOWR('D', 80, struct pf_osfp_ioctl) +#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) +#define DIOCSETHOSTID _IOWR('D', 86, u_int32_t) +#define DIOCIGETIFACES _IOWR('D', 87, struct pfioc_iface) +#define DIOCICLRISTATS _IOWR('D', 88, struct pfioc_iface) #ifdef __FreeBSD__ struct pf_ifspeed { char ifname[IFNAMSIZ]; u_int32_t baudrate; }; -#define DIOCGIFSPEED _IOWR('D', 81, struct pf_ifspeed) +#define DIOCGIFSPEED _IOWR('D', 89, struct pf_ifspeed) #endif #ifdef _KERNEL -RB_HEAD(pf_state_tree, pf_tree_node); -RB_PROTOTYPE(pf_state_tree, pf_tree_node, entry, pf_state_compare); -extern struct pf_state_tree tree_lan_ext, tree_ext_gwy; - -extern struct pf_anchorqueue pf_anchors; -extern struct pf_ruleset pf_main_ruleset; +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_id, pf_state); +RB_PROTOTYPE(pf_state_tree_id, pf_state, + entry_id, pf_state_compare_id); +extern struct pf_state_tree_id tree_id; +extern struct pf_state_queue state_updates; + +extern struct pf_anchorqueue pf_anchors; +extern struct pf_ruleset pf_main_ruleset; TAILQ_HEAD(pf_poolqueue, pf_pool); -extern struct pf_poolqueue pf_pools[2]; +extern struct pf_poolqueue pf_pools[2]; TAILQ_HEAD(pf_altqqueue, pf_altq); -extern struct pf_altqqueue pf_altqs[2]; -extern struct pf_palist pf_pabuf; - +extern struct pf_altqqueue pf_altqs[2]; +extern struct pf_palist pf_pabuf; +extern struct pfi_kif **pfi_index2kif; extern u_int32_t ticket_altqs_active; extern u_int32_t ticket_altqs_inactive; +extern int altqs_inactive_open; extern u_int32_t ticket_pabuf; extern struct pf_altqqueue *pf_altqs_active; extern struct pf_altqqueue *pf_altqs_inactive; @@ -1219,37 +1408,41 @@ extern int pf_tbladdr_setup(struct pf_ruleset *, struct pf_addr_wrap *); extern void pf_tbladdr_remove(struct pf_addr_wrap *); extern void pf_tbladdr_copyout(struct pf_addr_wrap *); -extern int pf_dynaddr_setup(struct pf_addr_wrap *, - sa_family_t); -extern void pf_dynaddr_copyout(struct pf_addr_wrap *); -extern void pf_dynaddr_remove(struct pf_addr_wrap *); 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); #ifdef __FreeBSD__ -extern uma_zone_t pf_tree_pl, pf_rule_pl, pf_addr_pl; +extern uma_zone_t pf_src_tree_pl, pf_rule_pl; extern uma_zone_t pf_state_pl, pf_altq_pl, pf_pooladdr_pl; extern uma_zone_t pfr_ktable_pl, pfr_kentry_pl; extern uma_zone_t pf_cache_pl, pf_cent_pl; extern uma_zone_t pf_state_scrub_pl; +extern uma_zone_t pfi_addr_pl; #else -extern struct pool pf_tree_pl, pf_rule_pl, pf_addr_pl; +extern struct pool pf_src_tree_pl, pf_rule_pl; extern struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl; extern struct pool pf_state_scrub_pl; #endif 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 struct pf_state *pf_find_state(struct pf_state_tree *, - struct pf_tree_node *); +extern int pf_insert_state(struct pfi_kif *, + 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_byid(struct pf_state *); +extern struct pf_state *pf_find_state_all(struct pf_state *key, + u_int8_t tree, int *more); extern struct pf_anchor *pf_find_anchor(const char *); extern struct pf_ruleset *pf_find_ruleset(char *, char *); -extern struct pf_ruleset *pf_find_or_create_ruleset(char *, char *); +extern struct pf_ruleset *pf_find_or_create_ruleset( + char[PF_ANCHOR_NAME_SIZE], + char[PF_RULESET_NAME_SIZE]); extern void pf_remove_if_empty_ruleset( struct pf_ruleset *); -extern struct ifnet *status_ifp; +extern struct ifnet *sync_ifp; extern struct pf_rule pf_default_rule; extern void pf_addrcpy(struct pf_addr *, struct pf_addr *, u_int8_t); @@ -1270,7 +1463,7 @@ void pf_addr_inc(struct pf_addr *, sa_family_t); void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *, sa_family_t); void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); -int pflog_packet(struct ifnet *, struct mbuf *, sa_family_t, u_int8_t, +int pflog_packet(struct pfi_kif *, struct mbuf *, sa_family_t, u_int8_t, u_int8_t, struct pf_rule *, struct pf_rule *, struct pf_ruleset *); int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *, struct pf_addr *, sa_family_t); @@ -1280,9 +1473,9 @@ int pf_match_uid(u_int8_t, uid_t, uid_t, uid_t); int pf_match_gid(u_int8_t, gid_t, gid_t, gid_t); void pf_normalize_init(void); -int pf_normalize_ip(struct mbuf **, int, struct ifnet *, u_short *); -int pf_normalize_ip6(struct mbuf **, int, struct ifnet *, u_short *); -int pf_normalize_tcp(int, struct ifnet *, struct mbuf *, int, int, void *, +int pf_normalize_ip(struct mbuf **, int, struct pfi_kif *, u_short *); +int pf_normalize_ip6(struct mbuf **, int, struct pfi_kif *, u_short *); +int pf_normalize_tcp(int, struct pfi_kif *, struct mbuf *, int, int, void *, struct pf_pdesc *); void pf_normalize_tcp_cleanup(struct pf_state *); int pf_normalize_tcp_init(struct mbuf *, int, struct pf_pdesc *, @@ -1300,6 +1493,7 @@ 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); +void pfr_dynaddr_update(struct pfr_ktable *, struct pfi_dynaddr *); struct pfr_ktable * pfr_attach_table(struct pf_ruleset *, char *); void pfr_detach_table(struct pfr_ktable *); @@ -1324,14 +1518,43 @@ int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); int pfr_ina_begin(struct pfr_table *, u_int32_t *, int *, int); +int pfr_ina_rollback(struct pfr_table *, u_int32_t, int *, int); int pfr_ina_commit(struct pfr_table *, u_int32_t, int *, int *, int); int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *, int *, u_int32_t, int); +void pfi_initialize(void); +#ifdef __FreeBSD__ +void pfi_cleanup(void); +#endif +void pfi_attach_clone(struct if_clone *); +void pfi_attach_ifnet(struct ifnet *); +void pfi_detach_ifnet(struct ifnet *); +struct pfi_kif *pfi_lookup_create(const char *); +struct pfi_kif *pfi_lookup_if(const char *); +int pfi_maybe_destroy(struct pfi_kif *); +struct pfi_kif *pfi_attach_rule(const char *); +void pfi_detach_rule(struct pfi_kif *); +void pfi_attach_state(struct pfi_kif *); +void pfi_detach_state(struct pfi_kif *); +int pfi_dynaddr_setup(struct pf_addr_wrap *, sa_family_t); +void pfi_dynaddr_copyout(struct pf_addr_wrap *); +void pfi_dynaddr_remove(struct pf_addr_wrap *); +void pfi_fill_oldstatus(struct pf_status *); +int pfi_clr_istats(const char *, int *, int); +int pfi_get_ifaces(const char *, struct pfi_if *, int *, int); +int pfi_match_addr(struct pfi_dynaddr *, struct pf_addr *, + sa_family_t); + +extern struct pfi_statehead pfi_statehead; + u_int16_t pf_tagname2tag(char *); void pf_tag2tagname(u_int16_t, char *); void pf_tag_unref(u_int16_t); int pf_tag_packet(struct mbuf *, struct pf_tag *, int); +u_int32_t pf_qname2qid(char *); +void pf_qid2qname(u_int32_t, char *); +void pf_qid_unref(u_int32_t); extern struct pf_status pf_status; |