diff options
author | mlaier <mlaier@FreeBSD.org> | 2004-06-16 23:03:14 +0000 |
---|---|---|
committer | mlaier <mlaier@FreeBSD.org> | 2004-06-16 23:03:14 +0000 |
commit | e31d6b9346e110db8eee07eb9643b6b7235bc7f7 (patch) | |
tree | 7f15b9d365aceccf9b46e93be14c9682058bdc5f /sys | |
parent | f3b4bd55ea7e6ff73fe7d21383da543b59f5a435 (diff) | |
download | FreeBSD-src-e31d6b9346e110db8eee07eb9643b6b7235bc7f7.zip FreeBSD-src-e31d6b9346e110db8eee07eb9643b6b7235bc7f7.tar.gz |
Import pf from OpenBSD 3.5 (OPENBSD_3_5_BASE)
Diffstat (limited to 'sys')
-rw-r--r-- | sys/contrib/pf/net/if_pflog.c | 14 | ||||
-rw-r--r-- | sys/contrib/pf/net/if_pflog.h | 4 | ||||
-rw-r--r-- | sys/contrib/pf/net/if_pfsync.c | 1038 | ||||
-rw-r--r-- | sys/contrib/pf/net/if_pfsync.h | 234 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf.c | 2225 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_if.c | 840 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_ioctl.c | 1128 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_norm.c | 63 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_osfp.c | 9 | ||||
-rw-r--r-- | sys/contrib/pf/net/pf_table.c | 228 | ||||
-rw-r--r-- | sys/contrib/pf/net/pfvar.h | 329 |
11 files changed, 4695 insertions, 1417 deletions
diff --git a/sys/contrib/pf/net/if_pflog.c b/sys/contrib/pf/net/if_pflog.c index e856f2c..3b93226 100644 --- a/sys/contrib/pf/net/if_pflog.c +++ b/sys/contrib/pf/net/if_pflog.c @@ -1,4 +1,4 @@ -/* $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 @@ -172,7 +172,7 @@ 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) { @@ -181,25 +181,23 @@ pflog_packet(struct ifnet *ifp, struct mbuf *m, sa_family_t af, u_int8_t dir, struct pfloghdr hdr; struct mbuf m1; - 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; - memcpy(hdr.ifname, ifp->if_xname, sizeof(hdr.ifname)); + 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)); diff --git a/sys/contrib/pf/net/if_pflog.h b/sys/contrib/pf/net/if_pflog.h index 9333f48..e4e603e 100644 --- a/sys/contrib/pf/net/if_pflog.h +++ b/sys/contrib/pf/net/if_pflog.h @@ -1,4 +1,4 @@ -/* $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. @@ -51,7 +51,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 e29a06e..e4840ff 100644 --- a/sys/contrib/pf/net/if_pfsync.c +++ b/sys/contrib/pf/net/if_pfsync.c @@ -1,4 +1,4 @@ -/* $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 @@ #include "pfsync.h" #include <sys/param.h> +#include <sys/proc.h> #include <sys/systm.h> #include <sys/time.h> #include <sys/mbuf.h> @@ -44,7 +45,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 @@ -67,29 +71,46 @@ int pfsyncdebug; #define DPRINTF(x) #endif -struct pfsync_softc pfsyncif; +struct pfsync_softc pfsyncif; +int pfsync_sync_ok; +struct pfsyncstats pfsyncstats; void pfsyncattach(int); -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 *); extern int ifqmaxlen; +extern struct timeval time; +extern struct timeval mono_time; +extern int hz; void 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; @@ -99,9 +120,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); @@ -133,6 +155,451 @@ pfsyncstart(struct ifnet *ifp) } int +pfsync_insert_net_state(struct pfsync_state *sp) +{ + struct pf_state *st = NULL; + struct pf_rule *r = NULL; + struct pfi_kif *kif; + + 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)); + st->creation = ntohl(sp->creation) + time.tv_sec; + st->expire = ntohl(sp->expire) + time.tv_sec; + + 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 +pfsync_input(struct mbuf *m, ...) +{ + struct ip *ip = mtod(m, struct ip *); + struct pfsync_header *ph; + struct pfsync_softc *sc = &pfsyncif; + 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) + 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(); + 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); + 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(); + 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(); + 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); + goto done; + } + continue; + } + } + 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(); + 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); + st->expire = ntohl(sp->expire) + time.tv_sec; + st->timeout = sp->timeout; + + } + 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(); + 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(); + 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(); + 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); + st->expire = ntohl(up->expire) + time.tv_sec; + st->timeout = up->timeout; + } + if (update_requested) + pfsync_sendout(sc); + 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(); + 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(); + 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. */ + 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) { + sc->sc_ureq_received = mono_time.tv_sec; + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received " + "bulk update request\n"); + pfsync_send_bus(sc, PFSYNC_BUS_START); + timeout_add(&sc->sc_bulk_tmo, 1 * hz); + } 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); + 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: + timeout_add(&sc->sc_bulkfail_tmo, + pf_pool_limits[PF_LIMIT_STATES].limit / + (PFSYNC_BULKPACKETS * sc->sc_maxcount)); + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: received bulk " + "update start\n"); + break; + case PFSYNC_BUS_END: + if (mono_time.tv_sec - ntohl(bus->endtime) >= + sc->sc_ureq_sent) { + /* that's it, we're happy */ + sc->sc_ureq_sent = 0; + sc->sc_bulk_tries = 0; + timeout_del(&sc->sc_bulkfail_tmo); + 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 pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt) { @@ -144,9 +611,13 @@ pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, int pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { + struct proc *p = curproc; 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: @@ -169,6 +640,84 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) pfsync_setmtu(sc, ifr->ifr_mtu); splx(s); break; + case SIOCGETPFSYNC: + 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: + if ((error = suser(p, p->p_acflag)) != 0) + return (error); + if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr)))) + return (error); + + if (pfsyncr.pfsyncr_maxupdates > 255) + return (EINVAL); + 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); + } + break; + } + if ((sifp = ifunit(pfsyncr.pfsyncr_syncif)) == NULL) + return (EINVAL); + else if (sifp == sc->sc_sync_ifp) + 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; + + addr.s_addr = INADDR_PFSYNC_GROUP; + 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. */ + sc->sc_ureq_sent = mono_time.tv_sec; + pfsync_sync_ok = 0; + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: requesting bulk update\n"); + timeout_add(&sc->sc_bulkfail_tmo, 5 * hz); + pfsync_request_update(NULL, NULL); + pfsync_sendout(sc); + } + splx(s); + + break; + default: return (ENOTTY); } @@ -177,22 +726,26 @@ 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) { - extern int hz; struct pfsync_header *h; struct mbuf *m; int len; @@ -203,7 +756,33 @@ pfsync_get_mbuf(sc, action) 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) { @@ -211,115 +790,286 @@ 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); timeout_add(&sc->sc_tmo, hz); - return (m); } 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) { - extern struct timeval time; struct ifnet *ifp = &pfsyncif.sc_if; struct pfsync_softc *sc = ifp->if_softc; - struct pfsync_header *h; - struct pf_state *sp; - struct pf_rule *r = st->rule.ptr; - struct mbuf *m; + 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; + int s, ret = 0; + u_int8_t i = 255, newaction = 0; + + /* + * If a packet falls in the forest and there's nobody around to + * hear, does it make a sound? + */ + 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)); + secs = time.tv_sec; + + st->pfsync_time = mono_time.tv_sec; + 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; - bcopy(&st->lan, &sp->lan, sizeof(sp->lan)); - bcopy(&st->gwy, &sp->gwy, sizeof(sp->gwy)); - bcopy(&st->ext, &sp->ext, sizeof(sp->ext)); + 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); - bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); - secs = time.tv_sec; - sp->creation = htonl(secs - st->creation); 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) { struct ifnet *ifp = &pfsyncif.sc_if; + struct pfsync_header *h; struct pfsync_softc *sc = ifp->if_softc; - struct mbuf *m = sc->sc_mbuf; + struct pfsync_state_upd_req *rup; + int s, ret; + + 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) +{ + struct ifnet *ifp = &pfsyncif.sc_if; + 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) { + 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); @@ -337,23 +1087,157 @@ pfsync_timeout(void *v) splx(s); } +void +pfsync_send_bus(struct pfsync_softc *sc, u_int8_t status) +{ + struct pfsync_state_bus *bus; + + 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; + bus->endtime = htonl(mono_time.tv_sec - sc->sc_ureq_received); + pfsync_sendout(sc); + } +} + +void +pfsync_bulk_update(void *v) +{ + struct pfsync_softc *sc = v; + int s, i = 0; + struct pf_state *state; + + 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; + timeout_del(&sc->sc_bulk_tmo); + 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); + state->pfsync_time = mono_time.tv_sec; + 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 */ + timeout_add(&sc->sc_bulk_tmo, 1); + } + } + if (sc->sc_mbuf != NULL) + pfsync_sendout(sc); + splx(s); +} + +void +pfsync_bulkfail(void *v) +{ + struct pfsync_softc *sc = v; + + if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) { + /* Try again in a bit */ + timeout_add(&sc->sc_bulkfail_tmo, 5 * hz); + 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"); + timeout_del(&sc->sc_bulkfail_tmo); + } +} + int pfsync_sendout(sc) struct pfsync_softc *sc; { struct ifnet *ifp = &sc->sc_if; - struct mbuf *m = sc->sc_mbuf; + struct mbuf *m; timeout_del(&sc->sc_tmo); + + if (sc->sc_mbuf == NULL) + return (0); + m = sc->sc_mbuf; sc->sc_mbuf = NULL; - sc->sc_ptr = NULL; + sc->sc_statep.s = NULL; #if NBPFILTER > 0 if (ifp->if_bpf) 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; + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_id = htons(ip_randomid()); + ip->ip_off = htons(IP_DF); + 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; + + if (sc->sc_sendaddr.s_addr == INADDR_PFSYNC_GROUP) + m->m_flags |= M_MCAST; + ip->ip_dst = sc->sc_sendaddr; + sc->sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP; + + pfsyncstats.pfsyncs_opackets++; + + if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) + pfsyncstats.pfsyncs_oerrors++; + } 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 9fff97f..b3705c8 100644 --- a/sys/contrib/pf/net/if_pfsync.h +++ b/sys/contrib/pf/net/if_pfsync.h @@ -1,4 +1,4 @@ -/* $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 @@ -29,40 +29,208 @@ #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 timeout sc_tmo; - 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 ip_moptions sc_imo; + struct timeout sc_tmo; + struct timeout sc_bulk_tmo; + struct timeout sc_bulkfail_tmo; + 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 */ }; #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 { \ @@ -70,15 +238,43 @@ 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)) +void pfsync_input(struct mbuf *, ...); +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 0472c27..82113ec 100644 --- a/sys/contrib/pf/net/pf.c +++ b/sys/contrib/pf/net/pf.c @@ -1,7 +1,8 @@ -/* $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 @@ -71,7 +72,10 @@ #include <dev/rndvar.h> #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> @@ -80,13 +84,8 @@ #include <netinet6/nd6.h> #endif /* INET6 */ -#ifdef ALTQ -#include <altq/if_altq.h> -#endif - #define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x -struct pf_state_tree; /* * Global variables @@ -99,18 +98,17 @@ 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; struct timeout pf_expire_to; /* expire timeout */ -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; -void pf_dynaddr_update(void *); 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); @@ -135,62 +133,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); @@ -204,17 +202,24 @@ 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); - -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 } +}; #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 && \ @@ -222,8 +227,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) @@ -235,14 +240,145 @@ 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; + +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); + +static __inline int +pf_src_compare(struct pf_src_node *a, struct pf_src_node *b) +{ + int diff; + + if (a->rule.ptr > b->rule.ptr) + return (1); + if (a->rule.ptr < b->rule.ptr) + return (-1); + if ((diff = a->af - b->af) != 0) + return (diff); + switch (a->af) { +#ifdef INET + case AF_INET: + if (a->addr.addr32[0] > b->addr.addr32[0]) + return (1); + if (a->addr.addr32[0] < b->addr.addr32[0]) + return (-1); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (a->addr.addr32[3] > b->addr.addr32[3]) + return (1); + if (a->addr.addr32[3] < b->addr.addr32[3]) + return (-1); + if (a->addr.addr32[2] > b->addr.addr32[2]) + return (1); + if (a->addr.addr32[2] < b->addr.addr32[2]) + return (-1); + if (a->addr.addr32[1] > b->addr.addr32[1]) + return (1); + if (a->addr.addr32[1] < b->addr.addr32[1]) + return (-1); + if (a->addr.addr32[0] > b->addr.addr32[0]) + return (1); + if (a->addr.addr32[0] < b->addr.addr32[0]) + return (-1); + break; +#endif /* INET6 */ + } + return (0); +} + +static __inline int +pf_state_compare_lan_ext(struct pf_state *a, struct pf_state *b) +{ + int diff; + + 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->lan.addr.addr32[0] > b->lan.addr.addr32[0]) + return (1); + if (a->lan.addr.addr32[0] < b->lan.addr.addr32[0]) + 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); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (a->lan.addr.addr32[3] > b->lan.addr.addr32[3]) + return (1); + if (a->lan.addr.addr32[3] < b->lan.addr.addr32[3]) + return (-1); + 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->lan.addr.addr32[2] > b->lan.addr.addr32[2]) + return (1); + if (a->lan.addr.addr32[2] < b->lan.addr.addr32[2]) + 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->lan.addr.addr32[1] > b->lan.addr.addr32[1]) + return (1); + if (a->lan.addr.addr32[1] < b->lan.addr.addr32[1]) + 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->lan.addr.addr32[0] > b->lan.addr.addr32[0]) + return (1); + if (a->lan.addr.addr32[0] < b->lan.addr.addr32[0]) + 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); + break; +#endif /* INET6 */ + } + + if ((diff = a->lan.port - b->lan.port) != 0) + return (diff); + if ((diff = a->ext.port - b->ext.port) != 0) + return (diff); -struct pf_state_tree tree_lan_ext, tree_ext_gwy; -RB_GENERATE(pf_state_tree, pf_tree_node, entry, pf_state_compare); + return (0); +} static __inline int -pf_state_compare(struct pf_tree_node *a, struct pf_tree_node *b) +pf_state_compare_ext_gwy(struct pf_state *a, struct pf_state *b) { int diff; @@ -253,62 +389,77 @@ 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->ext.addr.addr32[0] > b->ext.addr.addr32[0]) return (1); - if (a->addr[0].addr32[0] < b->addr[0].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->gwy.addr.addr32[0] > b->gwy.addr.addr32[0]) return (1); - if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + if (a->gwy.addr.addr32[0] < b->gwy.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->ext.addr.addr32[3] > b->ext.addr.addr32[3]) return (1); - if (a->addr[0].addr32[3] < b->addr[0].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->gwy.addr.addr32[3] > b->gwy.addr.addr32[3]) return (1); - if (a->addr[1].addr32[3] < b->addr[1].addr32[3]) + if (a->gwy.addr.addr32[3] < b->gwy.addr.addr32[3]) return (-1); - if (a->addr[0].addr32[2] > b->addr[0].addr32[2]) + if (a->ext.addr.addr32[2] > b->ext.addr.addr32[2]) return (1); - if (a->addr[0].addr32[2] < b->addr[0].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->gwy.addr.addr32[2] > b->gwy.addr.addr32[2]) return (1); - if (a->addr[1].addr32[2] < b->addr[1].addr32[2]) + if (a->gwy.addr.addr32[2] < b->gwy.addr.addr32[2]) return (-1); - if (a->addr[0].addr32[1] > b->addr[0].addr32[1]) + if (a->ext.addr.addr32[1] > b->ext.addr.addr32[1]) return (1); - if (a->addr[0].addr32[1] < b->addr[0].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->gwy.addr.addr32[1] > b->gwy.addr.addr32[1]) return (1); - if (a->addr[1].addr32[1] < b->addr[1].addr32[1]) + if (a->gwy.addr.addr32[1] < b->gwy.addr.addr32[1]) return (-1); - if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) + if (a->ext.addr.addr32[0] > b->ext.addr.addr32[0]) return (1); - if (a->addr[0].addr32[0] < b->addr[0].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->gwy.addr.addr32[0] > b->gwy.addr.addr32[0]) return (1); - if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + if (a->gwy.addr.addr32[0] < b->gwy.addr.addr32[0]) return (-1); break; #endif /* INET6 */ } - if ((diff = a->port[0] - b->port[0]) != 0) + if ((diff = a->ext.port - b->ext.port) != 0) return (diff); - if ((diff = a->port[1] - b->port[1]) != 0) + if ((diff = a->gwy.port - b->gwy.port) != 0) return (diff); return (0); } +static __inline int +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) @@ -330,36 +481,140 @@ 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); + } + (*sn)->creation = time.tv_sec; + (*sn)->ruletype = rule->action; + if ((*sn)->rule.ptr != NULL) + (*sn)->rule.ptr->src_nodes++; + pf_status.scounters[SCNT_SRC_NODE_INSERT]++; + pf_status.src_nodes++; + } else { + if (rule->max_src_states && + (*sn)->states >= rule->max_src_states) + return (-1); + } + return (0); +} +int +pf_insert_state(struct 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: "); @@ -371,28 +626,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: "); @@ -404,16 +645,36 @@ 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) { + printf("pf: state insert failed: " + "id: %016llx creatorid: %08x", + betoh64(state->id), ntohl(state->creatorid)); + 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 @@ -429,6 +690,7 @@ pf_purge_timeout(void *arg) s = splsoftnet(); pf_purge_expired_states(); pf_purge_expired_fragments(); + pf_purge_expired_src_nodes(); splx(s); timeout_add(to, pf_default_rule.timeout[PFTM_INTERVAL] * hz); @@ -471,61 +733,93 @@ pf_state_expires(const struct pf_state *state) } void +pf_purge_expired_src_nodes(void) +{ + struct pf_src_node *cur, *next; + + for (cur = RB_MIN(pf_src_tree, &tree_src_tracking); cur; cur = next) { + next = RB_NEXT(pf_src_tree, &tree_src_tracking, cur); + + if (cur->states <= 0 && cur->expire <= time.tv_sec) { + if (cur->rule.ptr != NULL) { + cur->rule.ptr->src_nodes--; + if (cur->rule.ptr->states <= 0 && + cur->rule.ptr->max_src_nodes <= 0) + pf_rm_rule(NULL, cur->rule.ptr); + } + RB_REMOVE(pf_src_tree, &tree_src_tracking, cur); + pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++; + pf_status.src_nodes--; + pool_put(&pf_src_tree_pl, cur); + } + } +} + +void +pf_src_tree_remove_state(struct pf_state *s) +{ + u_int32_t timeout; + + if (s->src_node != NULL) { + if (--s->src_node->states <= 0) { + timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; + if (!timeout) + timeout = + pf_default_rule.timeout[PFTM_SRC_NODE]; + s->src_node->expire = time.tv_sec + timeout; + } + } + if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) { + if (--s->nat_src_node->states <= 0) { + timeout = s->rule.ptr->timeout[PFTM_SRC_NODE]; + if (!timeout) + timeout = + pf_default_rule.timeout[PFTM_SRC_NODE]; + s->nat_src_node->expire = time.tv_sec + timeout; + } + } + s->src_node = s->nat_src_node = NULL; +} + +void pf_purge_expired_states(void) { - struct pf_tree_node *cur, *peer, *next; - struct pf_tree_node key; - - for (cur = RB_MIN(pf_state_tree, &tree_ext_gwy); cur; cur = next) { - next = RB_NEXT(pf_state_tree, &tree_ext_gwy, cur); - - if (pf_state_expires(cur->state) <= time.tv_sec) { - 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, + 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); + + if (pf_state_expires(cur) <= time.tv_sec) { + 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, &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); - KASSERT(peer); - KASSERT(peer->state == cur->state); - RB_REMOVE(pf_state_tree, &tree_lan_ext, peer); - + 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--; } @@ -565,101 +859,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; - 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); - } - pf_dynaddr_update(aw->p.dyn); - return (0); -} - -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; - hook_disestablish(aw->p.dyn->ifp->if_addrhooks, - aw->p.dyn->hook_cookie); - 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) { @@ -805,7 +1004,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); @@ -848,11 +1047,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: @@ -864,33 +1059,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; @@ -1265,7 +1433,7 @@ pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, switch (af) { #ifdef INET case AF_INET: - icmp_error(m0, type, code, 0, 0); + icmp_error(m0, type, code, 0, (void *)NULL); break; #endif /* INET */ #ifdef INET6 @@ -1385,8 +1553,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); @@ -1394,10 +1562,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) || @@ -1481,7 +1647,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 @@ -1570,20 +1736,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 { @@ -1643,6 +1848,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; @@ -1654,7 +1864,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); @@ -1670,10 +1890,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"); } @@ -1682,41 +1904,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); } @@ -1732,16 +1954,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); @@ -1749,10 +1971,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: @@ -1760,7 +1982,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) ); @@ -1769,7 +1990,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; @@ -1790,8 +2011,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; @@ -1837,7 +2058,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) @@ -1845,16 +2066,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); } @@ -1864,11 +2085,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", @@ -1880,21 +2100,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, @@ -1903,7 +2160,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]) { @@ -1925,7 +2182,6 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, } default: return (NULL); - break; } } @@ -1933,8 +2189,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; @@ -1943,7 +2198,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; @@ -1969,12 +2224,11 @@ 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: 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); } @@ -1984,8 +2238,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); } @@ -2027,13 +2280,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); @@ -2063,13 +2317,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); @@ -2132,22 +2387,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 */ } @@ -2155,12 +2410,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; @@ -2169,6 +2423,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; @@ -2180,35 +2435,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; @@ -2233,19 +2490,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); @@ -2274,18 +2528,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), 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) && @@ -2293,14 +2541,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)) && @@ -2331,16 +2581,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); } @@ -2349,10 +2628,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; @@ -2366,8 +2642,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); @@ -2378,8 +2654,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); @@ -2413,22 +2689,27 @@ 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; s->creation = time.tv_sec; s->expire = time.tv_sec; 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); } @@ -2436,12 +2717,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 @@ -2449,14 +2732,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); @@ -2464,8 +2750,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); } } @@ -2479,12 +2765,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; @@ -2493,6 +2778,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; @@ -2503,35 +2789,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; @@ -2554,19 +2842,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); @@ -2594,32 +2879,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), 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, @@ -2637,13 +2918,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); } @@ -2652,10 +2961,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; @@ -2669,8 +2975,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); @@ -2681,32 +2987,32 @@ 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; s->creation = time.tv_sec; s->expire = time.tv_sec; 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 @@ -2722,14 +3028,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; sa_family_t af = pd->af; @@ -2775,56 +3081,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; @@ -2844,8 +3152,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); @@ -2873,12 +3180,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) { @@ -2887,7 +3188,7 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, m_copyback(m, off, sizeof(struct icmp6_hdr), 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) @@ -2898,14 +3199,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); } @@ -2914,10 +3242,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; @@ -2931,8 +3256,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; @@ -2941,30 +3266,28 @@ 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; s->creation = time.tv_sec; s->expire = time.tv_sec; 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 @@ -2983,14 +3306,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; @@ -3000,52 +3323,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; @@ -3061,8 +3386,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); @@ -3090,36 +3414,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 */ } @@ -3140,13 +3461,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); } @@ -3155,10 +3504,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; @@ -3169,46 +3515,37 @@ 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; s->creation = time.tv_sec; s->expire = time.tv_sec; 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 @@ -3219,7 +3556,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) { @@ -3233,8 +3570,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; @@ -3252,8 +3589,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); @@ -3277,15 +3613,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); @@ -3299,36 +3630,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) { @@ -3369,7 +3705,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)) || @@ -3526,9 +3863,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; @@ -3613,9 +3947,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; @@ -3681,8 +4012,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); } @@ -3704,50 +4035,41 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, m_copyback(m, off, sizeof(*th), 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; @@ -3774,27 +4096,17 @@ pf_test_state_udp(struct pf_state **state, int direction, struct ifnet *ifp, m_copyback(m, off, sizeof(*uh), 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, *icmpsum; u_int8_t icmptype; - int state_icmp = 0, dirndx; + int state_icmp = 0; switch (pd->proto) { #ifdef INET @@ -3832,20 +4144,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; (*state)->expire = time.tv_sec; (*state)->timeout = PFTM_ICMP_ERROR_REPLY; @@ -4001,7 +4317,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; @@ -4020,10 +4336,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(); @@ -4035,7 +4358,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; @@ -4108,7 +4432,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)) { @@ -4120,10 +4444,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(); @@ -4168,7 +4499,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)) { @@ -4180,10 +4511,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(); @@ -4213,7 +4551,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)) { @@ -4225,10 +4563,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(); @@ -4258,14 +4603,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(); @@ -4311,35 +4663,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; @@ -4387,16 +4740,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); } @@ -4423,7 +4766,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); @@ -4485,6 +4829,7 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, struct ifnet *ifp = NULL; struct m_tag *mtag; struct pf_addr naddr; + struct pf_src_node *sn = NULL; int error = 0; if (m == NULL || *m == NULL || r == NULL || @@ -4536,31 +4881,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) { if (pf_test(PF_OUT, ifp, &m0) != PF_PASS) goto bad; else if (m0 == NULL) @@ -4571,6 +4919,34 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, } /* 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) { @@ -4649,6 +5025,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 || @@ -4697,17 +5074,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) @@ -4762,7 +5139,8 @@ bad: * 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; @@ -4843,29 +5221,48 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t a return (0); } +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; - 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; + 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; if (!pf_status.running || (m_tag_find(m, PACKET_TAG_PF_GENERATED, NULL) != NULL)) return (PF_PASS); + kif = pfi_index2kif[ifp->if_index]; + if (kif == NULL) + return (PF_DROP); + #ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) panic("non-M_PKTHDR is passed to pf_test"); #endif + memset(&pd, 0, sizeof(pd)); if (m->m_pkthdr.len < (int)sizeof(*h)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); @@ -4874,7 +5271,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; } @@ -4889,9 +5286,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; @@ -4900,7 +5297,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; } @@ -4924,17 +5321,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; } @@ -4952,14 +5353,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; } @@ -4977,55 +5387,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; @@ -5055,8 +5446,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); @@ -5074,24 +5544,30 @@ 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; - 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; + 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; if (!pf_status.running || (m_tag_find(m, PACKET_TAG_PF_GENERATED, NULL) != NULL)) return (PF_PASS); + kif = pfi_index2kif[ifp->if_index]; + if (kif == NULL) + return (PF_DROP); + #ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) panic("non-M_PKTHDR is passed to pf_test"); #endif + memset(&pd, 0, sizeof(pd)); if (m->m_pkthdr.len < (int)sizeof(*h)) { action = PF_DROP; REASON_SET(&reason, PFRES_SHORT); @@ -5100,16 +5576,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; @@ -5120,7 +5596,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); @@ -5172,17 +5648,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; } @@ -5200,13 +5680,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; } @@ -5224,45 +5714,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 @@ -5285,8 +5764,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 new file mode 100644 index 0000000..cdc6e36 --- /dev/null +++ b/sys/contrib/pf/net/pf_if.c @@ -0,0 +1,840 @@ +/* $OpenBSD: pf_if.c,v 1.11 2004/03/15 11:38:23 cedric Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * Copyright (c) 2003 Cedric Berger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/filio.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/if_types.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> + +#include <net/pfvar.h> + +#ifdef INET6 +#include <netinet/ip6.h> +#endif /* INET6 */ + +#define ACCEPT_FLAGS(oklist) \ + do { \ + if ((flags & ~(oklist)) & \ + PFI_FLAG_ALLMASK) \ + return (EINVAL); \ + } while (0) + +#define senderr(e) do { rv = (e); goto _bad; } while (0) + +struct pfi_kif **pfi_index2kif; +struct pfi_kif *pfi_self; +int pfi_indexlim; +struct pfi_ifhead pfi_ifs; +struct pfi_statehead pfi_statehead; +int pfi_ifcnt; +struct pool pfi_addr_pl; +long pfi_update = 1; +struct pfr_addr *pfi_buffer; +int pfi_buffer_cnt; +int pfi_buffer_max; +char pfi_reserved_anchor[PF_ANCHOR_NAME_SIZE] = + PF_RESERVED_ANCHOR; +char pfi_interface_ruleset[PF_RULESET_NAME_SIZE] = + PF_INTERFACE_RULESET; + +void pfi_dynaddr_update(void *); +void pfi_kifaddr_update(void *); +void pfi_table_update(struct pfr_ktable *, struct pfi_kif *, + int, int); +void pfi_instance_add(struct ifnet *, int, int); +void pfi_address_add(struct sockaddr *, int, int); +int pfi_if_compare(struct pfi_kif *, struct pfi_kif *); +struct pfi_kif *pfi_if_create(const char *, struct pfi_kif *, int); +void pfi_copy_group(char *, const char *, int); +void pfi_dynamic_drivers(void); +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 *); + +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 +#define PFI_MTYPE M_IFADDR + +void +pfi_initialize(void) +{ + if (pfi_self != NULL) /* already initialized */ + return; + + TAILQ_INIT(&pfi_statehead); + pool_init(&pfi_addr_pl, sizeof(struct pfi_dynaddr), 0, 0, 0, + "pfiaddrpl", &pool_allocator_nointr); + 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(); +} + +void +pfi_attach_clone(struct if_clone *ifc) +{ + pfi_initialize(); + pfi_newgroup(ifc->ifc_name, PFI_IFLAG_CLONABLE); +} + +void +pfi_attach_ifnet(struct ifnet *ifp) +{ + struct pfi_kif *p, *q, key; + int s; + + pfi_initialize(); + s = splsoftnet(); + pfi_update++; + if (ifp->if_index >= pfi_indexlim) { + /* + * grow pfi_index2kif, similar to ifindex2ifnet code in if.c + */ + size_t m, n, oldlim; + struct pfi_kif **mp, **np; + + oldlim = pfi_indexlim; + if (pfi_indexlim == 0) + pfi_indexlim = 64; + while (ifp->if_index >= pfi_indexlim) + pfi_indexlim <<= 1; + + m = oldlim * sizeof(struct pfi_kif *); + mp = pfi_index2kif; + n = pfi_indexlim * sizeof(struct pfi_kif *); + np = malloc(n, PFI_MTYPE, M_DONTWAIT); + if (np == NULL) + panic("pfi_attach_ifnet: " + "cannot allocate translation table"); + bzero(np, n); + if (mp != NULL) + bcopy(mp, np, m); + pfi_index2kif = np; + if (mp != NULL) + free(mp, PFI_MTYPE); + } + + strlcpy(key.pfik_name, ifp->if_xname, sizeof(key.pfik_name)); + p = RB_FIND(pfi_ifhead, &pfi_ifs, &key); + if (p == NULL) { + /* add group */ + pfi_copy_group(key.pfik_name, ifp->if_xname, + 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); + if (q == NULL) + panic("pfi_attach_ifnet: " + "cannot allocate '%s' group", key.pfik_name); + + /* add interface */ + p = pfi_if_create(ifp->if_xname, q, PFI_IFLAG_INSTANCE); + if (p == NULL) + panic("pfi_attach_ifnet: " + "cannot allocate '%s' interface", ifp->if_xname); + } else + q = p->pfik_parent; + p->pfik_ifp = ifp; + p->pfik_flags |= PFI_IFLAG_ATTACHED; + p->pfik_ah_cookie = + hook_establish(ifp->if_addrhooks, 1, pfi_kifaddr_update, p); + pfi_index2kif[ifp->if_index] = p; + pfi_dohooks(p); + splx(s); +} + +void +pfi_detach_ifnet(struct ifnet *ifp) +{ + struct pfi_kif *p, *q, key; + int s; + + strlcpy(key.pfik_name, ifp->if_xname, sizeof(key.pfik_name)); + + s = splsoftnet(); + pfi_update++; + p = RB_FIND(pfi_ifhead, &pfi_ifs, &key); + if (p == NULL) { + printf("pfi_detach_ifnet: cannot find %s", ifp->if_xname); + splx(s); + return; + } + hook_disestablish(p->pfik_ifp->if_addrhooks, p->pfik_ah_cookie); + q = p->pfik_parent; + p->pfik_ifp = NULL; + p->pfik_flags &= ~PFI_IFLAG_ATTACHED; + pfi_index2kif[ifp->if_index] = NULL; + pfi_dohooks(p); + pfi_maybe_destroy(p); + splx(s); +} + +struct pfi_kif * +pfi_lookup_create(const char *name) +{ + struct pfi_kif *p, *q, key; + int s; + + s = splsoftnet(); + p = pfi_lookup_if(name); + if (p == NULL) { + pfi_copy_group(key.pfik_name, name, sizeof(key.pfik_name)); + q = pfi_lookup_if(key.pfik_name); + if (q != NULL) + p = pfi_if_create(name, q, PFI_IFLAG_INSTANCE); + } + splx(s); + return (p); +} + +struct pfi_kif * +pfi_attach_rule(const char *name) +{ + struct pfi_kif *p; + + p = pfi_lookup_create(name); + if (p != NULL) + p->pfik_rules++; + return (p); +} + +void +pfi_detach_rule(struct pfi_kif *p) +{ + if (p == NULL) + return; + if (p->pfik_rules > 0) + p->pfik_rules--; + else + printf("pfi_detach_rule: reference count at 0\n"); + pfi_maybe_destroy(p); +} + +void +pfi_attach_state(struct pfi_kif *p) +{ + if (!p->pfik_states++) + TAILQ_INSERT_TAIL(&pfi_statehead, p, pfik_w_states); +} + +void +pfi_detach_state(struct pfi_kif *p) +{ + if (p == NULL) + return; + if (p->pfik_states <= 0) { + printf("pfi_detach_state: reference count <= 0\n"); + return; + } + if (!--p->pfik_states) + TAILQ_REMOVE(&pfi_statehead, p, pfik_w_states); + pfi_maybe_destroy(p); +} + +int +pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af) +{ + struct pfi_dynaddr *dyn; + char tblname[PF_TABLE_NAME_SIZE]; + struct pf_ruleset *ruleset = NULL; + int s, rv = 0; + + if (aw->type != PF_ADDR_DYNIFTL) + return (0); + dyn = pool_get(&pfi_addr_pl, PR_NOWAIT); + if (dyn == NULL) + return (1); + bzero(dyn, sizeof(*dyn)); + + s = splsoftnet(); + dyn->pfid_kif = pfi_attach_rule(aw->v.ifname); + if (dyn->pfid_kif == NULL) + senderr(1); + + dyn->pfid_net = pfi_unmask(&aw->v.a.mask); + if (af == AF_INET && dyn->pfid_net == 32) + dyn->pfid_net = 128; + strlcpy(tblname, aw->v.ifname, sizeof(tblname)); + if (aw->iflags & PFI_AFLAG_NETWORK) + strlcat(tblname, ":network", sizeof(tblname)); + if (aw->iflags & PFI_AFLAG_BROADCAST) + strlcat(tblname, ":broadcast", sizeof(tblname)); + if (aw->iflags & PFI_AFLAG_PEER) + strlcat(tblname, ":peer", sizeof(tblname)); + if (aw->iflags & PFI_AFLAG_NOALIAS) + strlcat(tblname, ":0", sizeof(tblname)); + if (dyn->pfid_net != 128) + snprintf(tblname + strlen(tblname), + sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net); + ruleset = pf_find_or_create_ruleset(pfi_reserved_anchor, + pfi_interface_ruleset); + if (ruleset == NULL) + senderr(1); + + dyn->pfid_kt = pfr_attach_table(ruleset, tblname); + if (dyn->pfid_kt == NULL) + senderr(1); + + dyn->pfid_kt->pfrkt_flags |= PFR_TFLAG_ACTIVE; + dyn->pfid_iflags = aw->iflags; + dyn->pfid_af = af; + dyn->pfid_hook_cookie = hook_establish(dyn->pfid_kif->pfik_ah_head, 1, + pfi_dynaddr_update, dyn); + if (dyn->pfid_hook_cookie == NULL) + senderr(1); + + aw->p.dyn = dyn; + pfi_dynaddr_update(aw->p.dyn); + splx(s); + return (0); + +_bad: + if (dyn->pfid_kt != NULL) + pfr_detach_table(dyn->pfid_kt); + if (ruleset != NULL) + pf_remove_if_empty_ruleset(ruleset); + if (dyn->pfid_kif != NULL) + pfi_detach_rule(dyn->pfid_kif); + pool_put(&pfi_addr_pl, dyn); + splx(s); + return (rv); +} + +void +pfi_dynaddr_update(void *p) +{ + struct pfi_dynaddr *dyn = (struct pfi_dynaddr *)p; + struct pfi_kif *kif = dyn->pfid_kif; + struct pfr_ktable *kt = dyn->pfid_kt; + + if (dyn == NULL || kif == NULL || kt == NULL) + panic("pfi_dynaddr_update"); + if (kt->pfrkt_larg != pfi_update) { + /* this table needs to be brought up-to-date */ + pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags); + kt->pfrkt_larg = pfi_update; + } + pfr_dynaddr_update(kt, dyn); +} + +void +pfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, int net, int flags) +{ + int e, size2 = 0; + struct pfi_kif *p; + struct pfr_table t; + + if ((kif->pfik_flags & PFI_IFLAG_INSTANCE) && kif->pfik_ifp == NULL) { + pfr_clr_addrs(&kt->pfrkt_t, NULL, 0); + return; + } + pfi_buffer_cnt = 0; + if ((kif->pfik_flags & PFI_IFLAG_INSTANCE)) + pfi_instance_add(kif->pfik_ifp, net, flags); + else if (strcmp(kif->pfik_name, "self")) { + TAILQ_FOREACH(p, &kif->pfik_grouphead, pfik_instances) + pfi_instance_add(p->pfik_ifp, net, flags); + } else { + RB_FOREACH(p, pfi_ifhead, &pfi_ifs) + if (p->pfik_flags & PFI_IFLAG_INSTANCE) + pfi_instance_add(p->pfik_ifp, net, flags); + } + t = kt->pfrkt_t; + t.pfrt_flags = 0; + if ((e = pfr_set_addrs(&t, pfi_buffer, pfi_buffer_cnt, &size2, + NULL, NULL, NULL, 0))) + printf("pfi_table_update: cannot set %d new addresses " + "into table %s: %d\n", pfi_buffer_cnt, kt->pfrkt_name, e); +} + +void +pfi_instance_add(struct ifnet *ifp, int net, int flags) +{ + struct ifaddr *ia; + int got4 = 0, got6 = 0; + int net2, af; + + if (ifp == NULL) + return; + TAILQ_FOREACH(ia, &ifp->if_addrlist, ifa_list) { + if (ia->ifa_addr == NULL) + continue; + af = ia->ifa_addr->sa_family; + if (af != AF_INET && af != AF_INET6) + continue; + if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6) + continue; + if ((flags & PFI_AFLAG_BROADCAST) && + !(ifp->if_flags & IFF_BROADCAST)) + continue; + if ((flags & PFI_AFLAG_PEER) && + !(ifp->if_flags & IFF_POINTOPOINT)) + continue; + if ((flags & PFI_AFLAG_NETWORK) && af == AF_INET6 && + IN6_IS_ADDR_LINKLOCAL( + &((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr)) + continue; + if (flags & PFI_AFLAG_NOALIAS) { + if (af == AF_INET && got4) + continue; + if (af == AF_INET6 && got6) + continue; + } + if (af == AF_INET) + got4 = 1; + else + got6 = 1; + net2 = net; + if (net2 == 128 && (flags & PFI_AFLAG_NETWORK)) { + if (af == AF_INET) { + net2 = pfi_unmask(&((struct sockaddr_in *) + ia->ifa_netmask)->sin_addr); + } else { + net2 = pfi_unmask(&((struct sockaddr_in6 *) + ia->ifa_netmask)->sin6_addr); + } + } + if (af == AF_INET && net2 > 32) + net2 = 32; + if (flags & PFI_AFLAG_BROADCAST) + pfi_address_add(ia->ifa_broadaddr, af, net2); + else if (flags & PFI_AFLAG_PEER) + pfi_address_add(ia->ifa_dstaddr, af, net2); + else + pfi_address_add(ia->ifa_addr, af, net2); + } +} + +void +pfi_address_add(struct sockaddr *sa, int af, int net) +{ + struct pfr_addr *p; + int i; + + if (pfi_buffer_cnt >= pfi_buffer_max) { + int new_max = pfi_buffer_max * 2; + + if (new_max > PFI_BUFFER_MAX) { + printf("pfi_address_add: address buffer full (%d/%d)\n", + pfi_buffer_cnt, PFI_BUFFER_MAX); + return; + } + p = malloc(new_max * sizeof(*pfi_buffer), PFI_MTYPE, + M_DONTWAIT); + if (p == NULL) { + printf("pfi_address_add: no memory to grow buffer " + "(%d/%d)\n", pfi_buffer_cnt, PFI_BUFFER_MAX); + return; + } + memcpy(pfi_buffer, p, pfi_buffer_cnt * sizeof(*pfi_buffer)); + /* no need to zero buffer */ + free(pfi_buffer, PFI_MTYPE); + pfi_buffer = p; + pfi_buffer_max = new_max; + } + if (af == AF_INET && net > 32) + net = 128; + p = pfi_buffer + pfi_buffer_cnt++; + bzero(p, sizeof(*p)); + p->pfra_af = af; + p->pfra_net = net; + if (af == AF_INET) + p->pfra_ip4addr = ((struct sockaddr_in *)sa)->sin_addr; + if (af == AF_INET6) { + p->pfra_ip6addr = ((struct sockaddr_in6 *)sa)->sin6_addr; + if (IN6_IS_ADDR_LINKLOCAL(&p->pfra_ip6addr)) + p->pfra_ip6addr.s6_addr16[1] = 0; + } + /* mask network address bits */ + if (net < 128) + ((caddr_t)p)[p->pfra_net/8] &= ~(0xFF >> (p->pfra_net%8)); + for (i = (p->pfra_net+7)/8; i < sizeof(p->pfra_u); i++) + ((caddr_t)p)[i] = 0; +} + +void +pfi_dynaddr_remove(struct pf_addr_wrap *aw) +{ + int s; + + if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL || + aw->p.dyn->pfid_kif == NULL || aw->p.dyn->pfid_kt == NULL) + return; + + s = splsoftnet(); + hook_disestablish(aw->p.dyn->pfid_kif->pfik_ah_head, + aw->p.dyn->pfid_hook_cookie); + pfi_detach_rule(aw->p.dyn->pfid_kif); + aw->p.dyn->pfid_kif = NULL; + pfr_detach_table(aw->p.dyn->pfid_kt); + aw->p.dyn->pfid_kt = NULL; + pool_put(&pfi_addr_pl, aw->p.dyn); + aw->p.dyn = NULL; + splx(s); +} + +void +pfi_dynaddr_copyout(struct pf_addr_wrap *aw) +{ + if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL || + aw->p.dyn->pfid_kif == NULL) + return; + aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6; +} + +void +pfi_kifaddr_update(void *v) +{ + int s; + + s = splsoftnet(); + pfi_update++; + pfi_dohooks(v); + splx(s); +} + +int +pfi_if_compare(struct pfi_kif *p, struct pfi_kif *q) +{ + return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ)); +} + +struct pfi_kif * +pfi_if_create(const char *name, struct pfi_kif *q, int flags) +{ + struct pfi_kif *p; + + p = malloc(sizeof(*p), PFI_MTYPE, M_DONTWAIT); + if (p == NULL) + return (NULL); + bzero(p, sizeof(*p)); + p->pfik_ah_head = malloc(sizeof(*p->pfik_ah_head), PFI_MTYPE, + M_DONTWAIT); + if (p->pfik_ah_head == NULL) { + free(p, PFI_MTYPE); + return (NULL); + } + bzero(p->pfik_ah_head, sizeof(*p->pfik_ah_head)); + TAILQ_INIT(p->pfik_ah_head); + TAILQ_INIT(&p->pfik_grouphead); + strlcpy(p->pfik_name, name, sizeof(p->pfik_name)); + RB_INIT(&p->pfik_lan_ext); + RB_INIT(&p->pfik_ext_gwy); + p->pfik_flags = flags; + p->pfik_parent = q; + p->pfik_tzero = time.tv_sec; + + RB_INSERT(pfi_ifhead, &pfi_ifs, p); + if (q != NULL) { + q->pfik_addcnt++; + TAILQ_INSERT_TAIL(&q->pfik_grouphead, p, pfik_instances); + } + pfi_ifcnt++; + return (p); +} + +int +pfi_maybe_destroy(struct pfi_kif *p) +{ + int i, j, k, s; + struct pfi_kif *q = p->pfik_parent; + + if ((p->pfik_flags & (PFI_IFLAG_ATTACHED | PFI_IFLAG_GROUP)) || + p->pfik_rules > 0 || p->pfik_states > 0) + return (0); + + s = splsoftnet(); + if (q != NULL) { + for (i = 0; i < 2; i++) + for (j = 0; j < 2; j++) + for (k = 0; k < 2; k++) { + q->pfik_bytes[i][j][k] += + p->pfik_bytes[i][j][k]; + q->pfik_packets[i][j][k] += + p->pfik_packets[i][j][k]; + } + q->pfik_delcnt++; + TAILQ_REMOVE(&q->pfik_grouphead, p, pfik_instances); + } + pfi_ifcnt--; + RB_REMOVE(pfi_ifhead, &pfi_ifs, p); + splx(s); + + free(p->pfik_ah_head, PFI_MTYPE); + free(p, PFI_MTYPE); + return (1); +} + +void +pfi_copy_group(char *p, const char *q, int m) +{ + while (m > 1 && *q && !(*q >= '0' && *q <= '9')) { + *p++ = *q++; + m--; + } + if (m > 0) + *p++ = '\0'; +} + +void +pfi_dynamic_drivers(void) +{ + char *buses[] = PFI_DYNAMIC_BUSES; + int nbuses = sizeof(buses)/sizeof(buses[0]); + int enabled[sizeof(buses)/sizeof(buses[0])]; + struct device *dev; + struct cfdata *cf; + struct cfdriver *drv; + short *p; + int i; + + bzero(enabled, sizeof(enabled)); + TAILQ_FOREACH(dev, &alldevs, dv_list) { + if (!(dev->dv_flags & DVF_ACTIVE)) + continue; + for (i = 0; i < nbuses; i++) + if (!enabled[i] && !strcmp(buses[i], + dev->dv_cfdata->cf_driver->cd_name)) + enabled[i] = 1; + } + for (cf = cfdata; cf->cf_driver; cf++) { + if (cf->cf_driver->cd_class != DV_IFNET) + continue; + for (p = cf->cf_parents; p && *p >= 0; p++) { + if ((drv = cfdata[*p].cf_driver) == NULL) + continue; + for (i = 0; i < nbuses; i++) + if (enabled[i] && + !strcmp(drv->cd_name, buses[i])) + break; + if (i < nbuses) { + pfi_newgroup(cf->cf_driver->cd_name, + PFI_IFLAG_DYNAMIC); + break; + } + } + } +} + +void +pfi_newgroup(const char *name, int flags) +{ + struct pfi_kif *p; + + p = pfi_lookup_if(name); + if (p == NULL) + p = pfi_if_create(name, pfi_self, PFI_IFLAG_GROUP); + if (p == NULL) { + printf("pfi_newgroup: cannot allocate '%s' group", name); + return; + } + p->pfik_flags |= flags; +} + +void +pfi_fill_oldstatus(struct pf_status *pfs) +{ + struct pfi_kif *p, key; + int i, j, k, s; + + strlcpy(key.pfik_name, pfs->ifname, sizeof(key.pfik_name)); + s = splsoftnet(); + p = RB_FIND(pfi_ifhead, &pfi_ifs, &key); + if (p == NULL) { + splx(s); + return; + } + bzero(pfs->pcounters, sizeof(pfs->pcounters)); + bzero(pfs->bcounters, sizeof(pfs->bcounters)); + for (i = 0; i < 2; i++) + for (j = 0; j < 2; j++) + for (k = 0; k < 2; k++) { + pfs->pcounters[i][j][k] = + p->pfik_packets[i][j][k]; + pfs->bcounters[i][j] += + p->pfik_bytes[i][j][k]; + } + splx(s); +} + +int +pfi_clr_istats(const char *name, int *nzero, int flags) +{ + struct pfi_kif *p; + int n = 0, s; + long tzero = time.tv_sec; + + s = splsoftnet(); + ACCEPT_FLAGS(PFI_FLAG_GROUP|PFI_FLAG_INSTANCE); + RB_FOREACH(p, pfi_ifhead, &pfi_ifs) { + if (pfi_skip_if(name, p, flags)) + continue; + bzero(p->pfik_packets, sizeof(p->pfik_packets)); + bzero(p->pfik_bytes, sizeof(p->pfik_bytes)); + p->pfik_tzero = tzero; + n++; + } + splx(s); + if (nzero != NULL) + *nzero = n; + return (0); +} + +int +pfi_get_ifaces(const char *name, struct pfi_if *buf, int *size, int flags) +{ + struct pfi_kif *p; + int s, n = 0; + + ACCEPT_FLAGS(PFI_FLAG_GROUP|PFI_FLAG_INSTANCE); + s = splsoftnet(); + RB_FOREACH(p, pfi_ifhead, &pfi_ifs) { + if (pfi_skip_if(name, p, flags)) + continue; + if (*size > n++) { + if (!p->pfik_tzero) + p->pfik_tzero = boottime.tv_sec; + if (copyout(p, buf++, sizeof(*buf))) { + splx(s); + return (EFAULT); + } + } + } + splx(s); + *size = n; + return (0); +} + +struct pfi_kif * +pfi_lookup_if(const char *name) +{ + struct pfi_kif *p, key; + + strlcpy(key.pfik_name, name, sizeof(key.pfik_name)); + p = RB_FIND(pfi_ifhead, &pfi_ifs, &key); + return (p); +} + +int +pfi_skip_if(const char *filter, struct pfi_kif *p, int f) +{ + int n; + + if ((p->pfik_flags & PFI_IFLAG_GROUP) && !(f & PFI_FLAG_GROUP)) + return (1); + if ((p->pfik_flags & PFI_IFLAG_INSTANCE) && !(f & PFI_FLAG_INSTANCE)) + return (1); + if (filter == NULL || !*filter) + return (0); + if (!strcmp(p->pfik_name, filter)) + return (0); /* exact match */ + n = strlen(filter); + if (n < 1 || n >= IFNAMSIZ) + return (1); /* sanity check */ + if (filter[n-1] >= '0' && filter[n-1] <= '9') + return (1); /* only do exact match in that case */ + if (strncmp(p->pfik_name, filter, n)) + return (1); /* prefix doesn't match */ + return (p->pfik_name[n] < '0' || p->pfik_name[n] > '9'); +} + +/* from pf_print_state.c */ +int +pfi_unmask(void *addr) +{ + struct pf_addr *m = addr; + int i = 31, j = 0, b = 0; + u_int32_t tmp; + + while (j < 4 && m->addr32[j] == 0xffffffff) { + b += 32; + j++; + } + if (j < 4) { + tmp = ntohl(m->addr32[j]); + for (i = 31; tmp & (1 << i); --i) + b++; + } + return (b); +} + +void +pfi_dohooks(struct pfi_kif *p) +{ + for (; p != NULL; p = p->pfik_parent) + dohooks(p->pfik_ah_head, 0); +} + +int +pfi_match_addr(struct pfi_dynaddr *dyn, struct pf_addr *a, sa_family_t af) +{ + if (af == AF_INET) { + switch (dyn->pfid_acnt4) { + case 0: + return (0); + case 1: + return (PF_MATCHA(0, &dyn->pfid_addr4, + &dyn->pfid_mask4, a, AF_INET)); + default: + return (pfr_match_addr(dyn->pfid_kt, a, AF_INET)); + } + } else { + switch (dyn->pfid_acnt6) { + case 0: + return (0); + case 1: + return (PF_MATCHA(0, &dyn->pfid_addr6, + &dyn->pfid_mask6, a, AF_INET6)); + default: + return (pfr_match_addr(dyn->pfid_kt, a, AF_INET6)); + } + } +} diff --git a/sys/contrib/pf/net/pf_ioctl.c b/sys/contrib/pf/net/pf_ioctl.c index 8ac0983..dd25ce2 100644 --- a/sys/contrib/pf/net/pf_ioctl.c +++ b/sys/contrib/pf/net/pf_ioctl.c @@ -1,7 +1,8 @@ -/* $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 @@ -34,6 +35,8 @@ * */ +#include "pfsync.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> @@ -58,8 +61,13 @@ #include <netinet/ip_var.h> #include <netinet/ip_icmp.h> +#include <dev/rndvar.h> #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> @@ -73,19 +81,35 @@ void pfattach(int); int pfopen(dev_t, int, int, struct proc *); int pfclose(dev_t, int, int, struct proc *); 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 *); void pf_empty_pool(struct pf_palist *); int pfioctl(dev_t, u_long, caddr_t, int, struct proc *); +#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 *); extern struct timeout pf_expire_to; struct pf_rule pf_default_rule; #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 @@ -94,12 +118,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", @@ -107,13 +129,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]); @@ -121,6 +143,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; @@ -144,12 +167,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 @@ -170,7 +198,7 @@ pfclose(dev_t 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; @@ -295,7 +323,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; @@ -349,11 +378,13 @@ pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) struct pf_anchor *anchor; int i; - if (ruleset == NULL || ruleset->anchor == NULL || ruleset->tables > 0 || ruleset->topen) + if (ruleset == NULL || ruleset->anchor == NULL || ruleset->tables > 0 || + ruleset->topen) 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; @@ -384,8 +415,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); } @@ -408,27 +440,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); @@ -441,8 +481,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; @@ -462,36 +502,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; @@ -499,6 +539,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); +} + int pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) { @@ -543,7 +787,19 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCRCLRASTATS: case DIOCRTSTADDRS: case DIOCOSFPGET: - break; + case DIOCGETSRCNODES: + case DIOCCLRSRCNODES: + case DIOCIGETIFACES: + case DIOCICLRISTATS: + 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); } @@ -572,7 +828,22 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) case DIOCRGETASTATS: case DIOCRTSTADDRS: case DIOCOSFPGET: + case DIOCGETSRCNODES: + case DIOCIGETIFACES: 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); } @@ -583,14 +854,12 @@ pfioctl(dev_t 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; - bzero(&pf_status, sizeof(struct pf_status)); pf_status.running = 1; - pf_status.states = states; pf_status.since = time.tv_sec; - if (status_ifp != NULL) - strlcpy(pf_status.ifname, - status_ifp->if_xname, IFNAMSIZ); + if (pf_status.stateid == 0) { + pf_status.stateid = time.tv_sec; + pf_status.stateid = pf_status.stateid << 32; + } DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); } break; @@ -600,30 +869,16 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) error = ENOENT; else { pf_status.running = 0; + pf_status.since = time.tv_sec; DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); } break; 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; } @@ -667,10 +922,11 @@ pfioctl(dev_t 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) { @@ -693,14 +949,27 @@ pfioctl(dev_t 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; @@ -710,9 +979,9 @@ pfioctl(dev_t 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; @@ -742,48 +1011,9 @@ pfioctl(dev_t 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; } @@ -845,8 +1075,8 @@ pfioctl(dev_t 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) @@ -930,23 +1160,26 @@ pfioctl(dev_t 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 @@ -961,9 +1194,9 @@ pfioctl(dev_t 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; @@ -1041,45 +1274,61 @@ pfioctl(dev_t 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++; } } @@ -1092,6 +1341,7 @@ pfioctl(dev_t 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) { @@ -1104,15 +1354,26 @@ pfioctl(dev_t 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; state->creation = time.tv_sec; + 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; } @@ -1122,29 +1383,29 @@ pfioctl(dev_t 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); if (ps->state.expire > time.tv_sec) ps->state.expire -= time.tv_sec; else @@ -1154,15 +1415,16 @@ pfioctl(dev_t 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; return (0); @@ -1170,32 +1432,36 @@ pfioctl(dev_t 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) { - int secs = time.tv_sec; + TAILQ_FOREACH(kif, &pfi_statehead, pfik_w_states) + RB_FOREACH(state, pf_state_tree_ext_gwy, + &kif->pfik_ext_gwy) { + int secs = time.tv_sec; - 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; - error = copyout(&pstore, p, sizeof(*p)); - if (error) { - splx(s); - goto fail; + 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; + error = copyout(&pstore, p, sizeof(*p)); + if (error) { + splx(s); + goto fail; + } + p++; + nr++; } - p++; - nr++; - } ps->ps_len = sizeof(struct pf_state) * nr; splx(s); break; @@ -1204,63 +1470,44 @@ pfioctl(dev_t 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) - strlcpy(pf_status.ifname, - status_ifp->if_xname, IFNAMSIZ); + 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) || @@ -1268,22 +1515,40 @@ pfioctl(dev_t 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; @@ -1336,7 +1601,8 @@ pfioctl(dev_t 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; } @@ -1439,18 +1705,8 @@ pfioctl(dev_t 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 */ - error = altq_remove(altq); - } - pool_put(&pf_altq_pl, altq); - } - *ticket = ++ticket_altqs_inactive; + error = pf_begin_altq(ticket); break; } @@ -1474,6 +1730,11 @@ pfioctl(dev_t 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) { @@ -1495,63 +1756,9 @@ pfioctl(dev_t 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 */ - error = altq_pfattach(altq); - 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 */ - err = altq_pfdetach(altq); - if (err != 0 && error == 0) - error = err; - err = altq_remove(altq); - if (err != 0 && error == 0) - error = err; - } - 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; } @@ -1668,15 +1875,16 @@ pfioctl(dev_t 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; @@ -1726,7 +1934,7 @@ pfioctl(dev_t 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; @@ -1782,17 +1990,18 @@ pfioctl(dev_t 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; @@ -1822,8 +2031,9 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) if (pca->action == PF_CHANGE_REMOVE) { TAILQ_REMOVE(&pool->list, oldpa, entries); - pf_dynaddr_remove(&oldpa->addr); + 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) @@ -1916,7 +2126,7 @@ pfioctl(dev_t 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; } @@ -1928,7 +2138,7 @@ pfioctl(dev_t 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; } @@ -1940,7 +2150,7 @@ pfioctl(dev_t 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; } @@ -1952,7 +2162,7 @@ pfioctl(dev_t 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; } @@ -1964,7 +2174,7 @@ pfioctl(dev_t 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; } @@ -1976,7 +2186,7 @@ pfioctl(dev_t 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; } @@ -1989,7 +2199,7 @@ pfioctl(dev_t 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; } @@ -2001,7 +2211,7 @@ pfioctl(dev_t 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; } @@ -2013,7 +2223,8 @@ pfioctl(dev_t 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; } @@ -2025,7 +2236,8 @@ pfioctl(dev_t 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; } @@ -2038,7 +2250,8 @@ pfioctl(dev_t 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; } @@ -2050,7 +2263,7 @@ pfioctl(dev_t 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; } @@ -2062,7 +2275,7 @@ pfioctl(dev_t 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; } @@ -2074,7 +2287,8 @@ pfioctl(dev_t 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; } @@ -2086,7 +2300,8 @@ pfioctl(dev_t 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; } @@ -2098,7 +2313,7 @@ pfioctl(dev_t 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; } @@ -2110,7 +2325,8 @@ pfioctl(dev_t 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; } @@ -2123,16 +2339,10 @@ pfioctl(dev_t 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(); @@ -2149,6 +2359,292 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) break; } + 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++) { + if (copyin(io->array+i, &ioe, sizeof(ioe))) { + 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; + } + if (copyout(&ioe, io->array+i, sizeof(io->array[i]))) { + error = EFAULT; + goto fail; + } + } + break; + } + + case DIOCXROLLBACK: { + 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++) { + if (copyin(io->array+i, &ioe, sizeof(ioe))) { + 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_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; + } + + 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++) { + if (copyin(io->array+i, &ioe, sizeof(ioe))) { + 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++) { + if (copyin(io->array+i, &ioe, sizeof(ioe))) { + 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; + } + } + break; + } + + case DIOCGETSRCNODES: { + struct pfioc_src_nodes *psn = (struct pfioc_src_nodes *)addr; + struct pf_src_node *n; + struct pf_src_node *p, pstore; + u_int32_t nr = 0; + int space = psn->psn_len; + + if (space == 0) { + s = splsoftnet(); + RB_FOREACH(n, pf_src_tree, &tree_src_tracking) + nr++; + splx(s); + psn->psn_len = sizeof(struct pf_src_node) * nr; + return (0); + } + + s = splsoftnet(); + p = psn->psn_src_nodes; + RB_FOREACH(n, pf_src_tree, &tree_src_tracking) { + int secs = time.tv_sec; + + if ((nr + 1) * sizeof(*p) > (unsigned)psn->psn_len) + break; + + bcopy(n, &pstore, sizeof(pstore)); + if (n->rule.ptr != NULL) + pstore.rule.nr = n->rule.ptr->nr; + pstore.creation = secs - pstore.creation; + if (pstore.expire > secs) + pstore.expire -= secs; + else + pstore.expire = 0; + error = copyout(&pstore, p, sizeof(*p)); + if (error) { + splx(s); + goto fail; + } + p++; + nr++; + } + psn->psn_len = sizeof(struct pf_src_node) * nr; + splx(s); + break; + } + + case DIOCCLRSRCNODES: { + struct pf_src_node *n; + struct pf_state *state; + + s = splsoftnet(); + RB_FOREACH(state, pf_state_tree_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; + } + + case DIOCSETHOSTID: { + u_int32_t *hostid = (u_int32_t *)addr; + + if (*hostid == 0) { + error = EINVAL; + goto fail; + } + pf_status.hostid = *hostid; + break; + } + + case DIOCOSFPFLUSH: + s = splsoftnet(); + pf_osfp_flush(); + splx(s); + 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; diff --git a/sys/contrib/pf/net/pf_norm.c b/sys/contrib/pf/net/pf_norm.c index 19c5326..d2c6456 100644 --- a/sys/contrib/pf/net/pf_norm.c +++ b/sys/contrib/pf/net/pf_norm.c @@ -1,4 +1,4 @@ -/* $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> @@ -374,7 +374,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; @@ -638,8 +638,10 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, h = mtod(m, struct ip *); - KASSERT((int)m->m_len == ntohs(h->ip_len) - precut); - h->ip_off = htons(ntohs(h->ip_off) + (precut >> 3)); + KASSERT((int)m->m_len == + ntohs(h->ip_len) - precut); + h->ip_off = htons(ntohs(h->ip_off) + + (precut >> 3)); h->ip_len = htons(ntohs(h->ip_len) - precut); } else { hosed++; @@ -693,7 +695,8 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff, m->m_pkthdr.len = plen; } h = mtod(m, struct ip *); - KASSERT((int)m->m_len == ntohs(h->ip_len) - aftercut); + KASSERT((int)m->m_len == + ntohs(h->ip_len) - aftercut); h->ip_len = htons(ntohs(h->ip_len) - aftercut); } else { hosed++; @@ -807,7 +810,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; @@ -824,7 +827,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; @@ -987,13 +991,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: @@ -1005,14 +1009,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; @@ -1032,7 +1037,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; @@ -1166,25 +1172,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; @@ -1197,7 +1203,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; @@ -1290,7 +1297,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); } @@ -1331,7 +1338,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 && @@ -1355,8 +1362,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; } } @@ -1406,7 +1413,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; @@ -1450,11 +1457,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], @@ -1464,8 +1473,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 c01d6de..98cc01a 100644 --- a/sys/contrib/pf/net/pf_osfp.c +++ b/sys/contrib/pf/net/pf_osfp.c @@ -1,4 +1,4 @@ -/* $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> @@ -50,13 +50,14 @@ 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) # define pool_init(pool, size, a, ao, f, m, p) (*(pool)) = (size) # ifdef PFDEBUG -# include <stdarg.h> +# include <sys/stdarg.h> # define DPFPRINTF(format, x...) fprintf(stderr, format , ##x) # else # define DPFPRINTF(format, x...) ((void)0) @@ -106,7 +107,7 @@ pf_osfp_fingerprint_hdr(const struct ip *ip, const struct tcphdr *tcp) { struct pf_os_fingerprint fp, *fpresult; int cnt, optlen = 0; - u_int8_t *optp; + const u_int8_t *optp; if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN || (ip->ip_off & htons(IP_OFFMASK))) @@ -122,7 +123,7 @@ pf_osfp_fingerprint_hdr(const struct ip *ip, const struct tcphdr *tcp) cnt = (tcp->th_off << 2) - sizeof(*tcp); - optp = (caddr_t)tcp + sizeof(*tcp); + optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp)); for (; cnt > 0; cnt -= optlen, optp += optlen) { if (*optp == TCPOPT_EOL) break; diff --git a/sys/contrib/pf/net/pf_table.c b/sys/contrib/pf/net/pf_table.c index e6ce25f..42d4cee 100644 --- a/sys/contrib/pf/net/pf_table.c +++ b/sys/contrib/pf/net/pf_table.c @@ -1,4 +1,4 @@ -/* $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 @@ -49,6 +49,16 @@ return (EINVAL); \ } while (0) +#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)) + #define FILLIN_SIN(sin, addr) \ do { \ (sin).sin_len = sizeof(sin); \ @@ -71,8 +81,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)) @@ -91,20 +101,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) @@ -113,7 +127,7 @@ struct pool pfr_ktable_pl; struct pool pfr_kentry_pl; struct sockaddr_in pfr_sin; struct sockaddr_in6 pfr_sin6; -union sockaddr_union pfr_mask; +union sockaddr_union pfr_mask; struct pf_addr pfr_ffaddr; void pfr_copyout_addr(struct pfr_addr *, @@ -133,14 +147,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 *); @@ -160,7 +174,7 @@ void pfr_clean_node_mask(struct pfr_ktable *, int pfr_table_count(struct pfr_table *, int); int pfr_skip_table(struct pfr_table *, struct pfr_ktable *, int); -struct pfr_kentry *pfr_kentry_byidx(struct pfr_ktable *, int, int); +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); @@ -193,7 +207,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)) @@ -229,7 +243,7 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, long tzero = time.tv_sec; 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)) @@ -241,7 +255,7 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, return (ENOMEM); SLIST_INIT(&workq); for (i = 0; i < size; i++) { - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); if (pfr_validate_addr(&ad)) senderr(EINVAL); @@ -270,7 +284,7 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, } } if (flags & PFR_FLAG_FEEDBACK) - if (copyout(&ad, addr+i, sizeof(ad))) + if (COPYOUT(&ad, addr+i, sizeof(ad))) senderr(EFAULT); } pfr_clean_node_mask(tmpkt, &workq); @@ -290,7 +304,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); } @@ -306,7 +320,7 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, int i, rv, s, xdel = 0; 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)) @@ -316,7 +330,7 @@ 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++) { - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); if (pfr_validate_addr(&ad)) senderr(EINVAL); @@ -338,7 +352,7 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, xdel++; } if (flags & PFR_FLAG_FEEDBACK) - if (copyout(&ad, addr+i, sizeof(ad))) + if (COPYOUT(&ad, addr+i, sizeof(ad))) senderr(EFAULT); } if (!(flags & PFR_FLAG_DUMMY)) { @@ -353,7 +367,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); } @@ -369,7 +383,7 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, long tzero = time.tv_sec; 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)) @@ -384,7 +398,7 @@ 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++) { - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); if (pfr_validate_addr(&ad)) senderr(EINVAL); @@ -421,7 +435,7 @@ pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, } _skip: if (flags & PFR_FLAG_FEEDBACK) - if (copyout(&ad, addr+i, sizeof(ad))) + if (COPYOUT(&ad, addr+i, sizeof(ad))) senderr(EFAULT); } pfr_enqueue_addrs(kt, &delq, &xdel, ENQUEUE_UNMARKED_ONLY); @@ -434,7 +448,7 @@ _skip: SLIST_FOREACH(p, &delq, pfrke_workq) { pfr_copyout_addr(&ad, p); ad.pfra_fback = PFR_FB_DELETED; - if (copyout(&ad, addr+size+i, sizeof(ad))) + if (COPYOUT(&ad, addr+size+i, sizeof(ad))) senderr(EFAULT); i++; } @@ -456,7 +470,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); @@ -464,7 +478,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); } @@ -479,14 +493,14 @@ pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, int i, xmatch = 0; 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++) { - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) return (EFAULT); if (pfr_validate_addr(&ad)) return (EINVAL); @@ -499,7 +513,7 @@ 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++; - if (copyout(&ad, addr+i, sizeof(ad))) + if (COPYOUT(&ad, addr+i, sizeof(ad))) return (EFAULT); } if (nmatch != NULL) @@ -516,7 +530,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)) @@ -530,6 +544,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; rv = rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); if (!rv) rv = rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); @@ -556,7 +571,7 @@ pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, long tzero = time.tv_sec; 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)) @@ -570,6 +585,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(); rv = rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); @@ -604,14 +620,14 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, int i, rv, s, xzero = 0; 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++) { - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); if (pfr_validate_addr(&ad)) senderr(EINVAL); @@ -619,7 +635,7 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, if (flags & PFR_FLAG_FEEDBACK) { ad.pfra_fback = (p != NULL) ? PFR_FB_CLEARED : PFR_FB_NONE; - if (copyout(&ad, addr+i, sizeof(ad))) + if (COPYOUT(&ad, addr+i, sizeof(ad))) senderr(EFAULT); } if (p != NULL) { @@ -640,7 +656,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); } @@ -817,10 +833,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 @@ -841,16 +857,16 @@ 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; for (i = 0; i < size; i++) { - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) break; ad.pfra_fback = PFR_FB_NONE; - if (copyout(&ad, addr+i, sizeof(ad))) + if (COPYOUT(&ad, addr+i, sizeof(ad))) break; } } @@ -953,7 +969,7 @@ pfr_walktree(struct radix_node *rn, void *arg) { struct pfr_kentry *ke = (struct pfr_kentry *)rn; struct pfr_walktree *w = arg; - int s; + int s, flags = w->pfrw_flags; switch (w->pfrw_op) { case PFRW_MARK: @@ -962,7 +978,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++; @@ -991,7 +1007,7 @@ pfr_walktree(struct radix_node *rn, void *arg) splx(s); as.pfras_tzero = ke->pfrke_tzero; - if (copyout(&as, w->pfrw_astats, sizeof(as))) + if (COPYOUT(&as, w->pfrw_astats, sizeof(as))) return (EFAULT); w->pfrw_astats++; } @@ -1004,6 +1020,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); } @@ -1023,6 +1058,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; @@ -1053,9 +1090,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++) { - if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) + if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) senderr(EFAULT); - 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); @@ -1131,9 +1169,10 @@ pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY); SLIST_INIT(&workq); for (i = 0; i < size; i++) { - if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) + if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) return (EFAULT); - 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)) { @@ -1180,7 +1219,7 @@ pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, continue; if (n-- <= 0) continue; - if (copyout(&p->pfrkt_t, tbl++, sizeof(*tbl))) + if (COPYOUT(&p->pfrkt_t, tbl++, sizeof(*tbl))) return (EFAULT); } if (n) { @@ -1219,7 +1258,7 @@ pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, continue; if (!(flags & PFR_FLAG_ATOMIC)) s = splsoftnet(); - if (copyout(&p->pfrkt_ts, tbl++, sizeof(*tbl))) { + if (COPYOUT(&p->pfrkt_ts, tbl++, sizeof(*tbl))) { splx(s); return (EFAULT); } @@ -1251,9 +1290,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++) { - if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) + if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) return (EFAULT); - 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) { @@ -1288,9 +1327,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++) { - if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) + if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) return (EFAULT); - 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)) { @@ -1374,7 +1414,8 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, 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) @@ -1416,7 +1457,7 @@ _skip: } SLIST_INIT(&addrq); for (i = 0; i < size; i++) { - if (copyin(addr+i, &ad, sizeof(ad))) + if (COPYIN(addr+i, &ad, sizeof(ad))) senderr(EFAULT); if (pfr_validate_addr(&ad)) senderr(EINVAL); @@ -1459,6 +1500,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) { @@ -1563,12 +1635,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++) @@ -1786,15 +1860,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 @@ -1806,7 +1881,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: @@ -1904,7 +1979,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 @@ -1970,7 +2045,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 */ @@ -2000,19 +2075,38 @@ 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: rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); - return w.pfrw_kentry; + return (w.pfrw_kentry); case AF_INET6: rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); - 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) + rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); + if (!dyn->pfid_af || dyn->pfid_af == AF_INET6) + rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); + splx(s); +} diff --git a/sys/contrib/pf/net/pfvar.h b/sys/contrib/pf/net/pfvar.h index 058e425..d3dda46 100644 --- a/sys/contrib/pf/net/pfvar.h +++ b/sys/contrib/pf/net/pfvar.h @@ -1,4 +1,4 @@ -/* $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 @@ -47,6 +47,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, @@ -67,16 +68,17 @@ enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED, PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY, PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE, PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL, - PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_MAX, - PFTM_PURGE, PFTM_UNTIL_PACKET }; + PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_SRC_NODE, + PFTM_MAX, PFTM_PURGE, PFTM_UNTIL_PACKET }; enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; -enum { PF_LIMIT_STATES, PF_LIMIT_FRAGS, PF_LIMIT_MAX }; +enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, PF_LIMIT_MAX }; #define PF_POOL_IDMASK 0x0f enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, PF_ADDR_TABLE }; #define PF_POOL_TYPEMASK 0x0f +#define PF_POOL_STICKYADDR 0x20 #define PF_WSCALE_FLAG 0x80 #define PF_WSCALE_MASK 0x0f @@ -97,6 +99,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 { @@ -107,28 +115,36 @@ 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_* */ }; -struct pf_addr_dyn { - char ifname[IFNAMSIZ]; - struct ifnet *ifp; - struct pf_addr *addr; - sa_family_t af; - void *hook_cookie; - u_int8_t undefined; +#ifdef _KERNEL + +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_* */ }; /* * Address manipulation macros */ -#ifdef _KERNEL - #ifdef INET #ifndef INET6 #define PF_INET_ONLY @@ -265,10 +281,7 @@ struct pf_addr_dyn { ((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, \ @@ -297,7 +310,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); @@ -446,7 +459,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]; @@ -464,12 +476,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; @@ -516,6 +533,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 @@ -524,8 +544,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; @@ -552,7 +592,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; @@ -562,27 +615,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); @@ -598,6 +649,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; @@ -617,6 +669,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 @@ -703,12 +758,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 @@ -718,6 +774,60 @@ 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 */ + struct pf_pdesc { u_int64_t tot_len; /* Make Mickey money */ union { @@ -729,6 +839,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; @@ -797,6 +910,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 { \ @@ -815,12 +932,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]; }; @@ -952,6 +1073,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 { @@ -964,6 +1086,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]; }; @@ -1004,6 +1136,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 @@ -1012,6 +1157,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; @@ -1033,6 +1181,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 */ @@ -1045,7 +1207,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) @@ -1101,23 +1263,38 @@ 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 _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; @@ -1127,29 +1304,32 @@ 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); -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; 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); @@ -1170,7 +1350,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); @@ -1180,9 +1360,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 *, @@ -1200,6 +1380,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 *); @@ -1224,14 +1405,40 @@ 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); +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; extern struct pool pf_frent_pl, pf_frag_pl; |