summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netpfil
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-11-06 15:42:44 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-11-15 10:56:14 +0100
commite0b4edbdcc3558d3f38af8398f995c2e9f019f07 (patch)
treeea91a5fcfb9b6a66a8c0b74cf68ff8d450ce17e0 /freebsd/sys/netpfil
parentDisable or make static kern_* functions (diff)
downloadrtems-libbsd-e0b4edbdcc3558d3f38af8398f995c2e9f019f07.tar.bz2
Update to FreeBSD head 2018-11-15
Git mirror commit a18b0830c4be01b39489a891b63d6023ada6358a. Update #3472.
Diffstat (limited to 'freebsd/sys/netpfil')
-rw-r--r--freebsd/sys/netpfil/pf/if_pfsync.c76
-rw-r--r--freebsd/sys/netpfil/pf/pf.c44
-rw-r--r--freebsd/sys/netpfil/pf/pf_if.c20
-rw-r--r--freebsd/sys/netpfil/pf/pf_ioctl.c22
-rw-r--r--freebsd/sys/netpfil/pf/pf_norm.c294
-rw-r--r--freebsd/sys/netpfil/pf/pf_table.c1
6 files changed, 331 insertions, 126 deletions
diff --git a/freebsd/sys/netpfil/pf/if_pfsync.c b/freebsd/sys/netpfil/pf/if_pfsync.c
index dae091db..9612ac99 100644
--- a/freebsd/sys/netpfil/pf/if_pfsync.c
+++ b/freebsd/sys/netpfil/pf/if_pfsync.c
@@ -283,6 +283,7 @@ static void pfsync_bulk_status(u_int8_t);
static void pfsync_bulk_update(void *);
static void pfsync_bulk_fail(void *);
+static void pfsync_detach_ifnet(struct ifnet *);
#ifdef IPSEC
static void pfsync_update_net_tdb(struct pfsync_tdb *);
#endif
@@ -1364,10 +1365,10 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates;
if (pfsyncr.pfsyncr_defer) {
sc->sc_flags |= PFSYNCF_DEFER;
- pfsync_defer_ptr = pfsync_defer;
+ V_pfsync_defer_ptr = pfsync_defer;
} else {
sc->sc_flags &= ~PFSYNCF_DEFER;
- pfsync_defer_ptr = NULL;
+ V_pfsync_defer_ptr = NULL;
}
if (sifp == NULL) {
@@ -1395,6 +1396,7 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if (error) {
if_rele(sifp);
free(mship, M_PFSYNC);
+ PFSYNC_UNLOCK(sc);
return (error);
}
}
@@ -2294,6 +2296,29 @@ pfsync_multicast_cleanup(struct pfsync_softc *sc)
imo->imo_multicast_ifp = NULL;
}
+void
+pfsync_detach_ifnet(struct ifnet *ifp)
+{
+ struct pfsync_softc *sc = V_pfsyncif;
+
+ if (sc == NULL)
+ return;
+
+ PFSYNC_LOCK(sc);
+
+ if (sc->sc_sync_if == ifp) {
+ /* We don't need mutlicast cleanup here, because the interface
+ * is going away. We do need to ensure we don't try to do
+ * cleanup later.
+ */
+ sc->sc_imo.imo_membership = NULL;
+ sc->sc_imo.imo_multicast_ifp = NULL;
+ sc->sc_sync_if = NULL;
+ }
+
+ PFSYNC_UNLOCK(sc);
+}
+
#ifdef INET
extern struct domain inetdomain;
static struct protosw in_pfsync_protosw = {
@@ -2313,12 +2338,12 @@ pfsync_pointers_init()
{
PF_RULES_WLOCK();
- pfsync_state_import_ptr = pfsync_state_import;
- pfsync_insert_state_ptr = pfsync_insert_state;
- pfsync_update_state_ptr = pfsync_update_state;
- pfsync_delete_state_ptr = pfsync_delete_state;
- pfsync_clear_states_ptr = pfsync_clear_states;
- pfsync_defer_ptr = pfsync_defer;
+ V_pfsync_state_import_ptr = pfsync_state_import;
+ V_pfsync_insert_state_ptr = pfsync_insert_state;
+ V_pfsync_update_state_ptr = pfsync_update_state;
+ V_pfsync_delete_state_ptr = pfsync_delete_state;
+ V_pfsync_clear_states_ptr = pfsync_clear_states;
+ V_pfsync_defer_ptr = pfsync_defer;
PF_RULES_WUNLOCK();
}
@@ -2327,12 +2352,12 @@ pfsync_pointers_uninit()
{
PF_RULES_WLOCK();
- pfsync_state_import_ptr = NULL;
- pfsync_insert_state_ptr = NULL;
- pfsync_update_state_ptr = NULL;
- pfsync_delete_state_ptr = NULL;
- pfsync_clear_states_ptr = NULL;
- pfsync_defer_ptr = NULL;
+ V_pfsync_state_import_ptr = NULL;
+ V_pfsync_insert_state_ptr = NULL;
+ V_pfsync_update_state_ptr = NULL;
+ V_pfsync_delete_state_ptr = NULL;
+ V_pfsync_clear_states_ptr = NULL;
+ V_pfsync_defer_ptr = NULL;
PF_RULES_WUNLOCK();
}
@@ -2349,6 +2374,8 @@ vnet_pfsync_init(const void *unused __unused)
if_clone_detach(V_pfsync_cloner);
log(LOG_INFO, "swi_add() failed in %s\n", __func__);
}
+
+ pfsync_pointers_init();
}
VNET_SYSINIT(vnet_pfsync_init, SI_SUB_PROTO_FIREWALL, SI_ORDER_ANY,
vnet_pfsync_init, NULL);
@@ -2357,14 +2384,13 @@ static void
vnet_pfsync_uninit(const void *unused __unused)
{
+ pfsync_pointers_uninit();
+
if_clone_detach(V_pfsync_cloner);
swi_remove(V_pfsync_swi_cookie);
}
-/*
- * Detach after pf is gone; otherwise we might touch pfsync memory
- * from within pf after freeing pfsync.
- */
-VNET_SYSUNINIT(vnet_pfsync_uninit, SI_SUB_INIT_IF, SI_ORDER_SECOND,
+
+VNET_SYSUNINIT(vnet_pfsync_uninit, SI_SUB_PROTO_FIREWALL, SI_ORDER_FOURTH,
vnet_pfsync_uninit, NULL);
static int
@@ -2373,6 +2399,8 @@ pfsync_init()
#ifdef INET
int error;
+ pfsync_detach_ifnet_ptr = pfsync_detach_ifnet;
+
error = pf_proto_register(PF_INET, &in_pfsync_protosw);
if (error)
return (error);
@@ -2382,7 +2410,6 @@ pfsync_init()
return (error);
}
#endif
- pfsync_pointers_init();
return (0);
}
@@ -2390,8 +2417,7 @@ pfsync_init()
static void
pfsync_uninit()
{
-
- pfsync_pointers_uninit();
+ pfsync_detach_ifnet_ptr = NULL;
#ifdef INET
ipproto_unregister(IPPROTO_PFSYNC);
@@ -2408,12 +2434,6 @@ pfsync_modevent(module_t mod, int type, void *data)
case MOD_LOAD:
error = pfsync_init();
break;
- case MOD_QUIESCE:
- /*
- * Module should not be unloaded due to race conditions.
- */
- error = EBUSY;
- break;
case MOD_UNLOAD:
pfsync_uninit();
break;
diff --git a/freebsd/sys/netpfil/pf/pf.c b/freebsd/sys/netpfil/pf/pf.c
index 5fa7a8fe..e115061a 100644
--- a/freebsd/sys/netpfil/pf/pf.c
+++ b/freebsd/sys/netpfil/pf/pf.c
@@ -1270,8 +1270,8 @@ pf_state_insert(struct pfi_kif *kif, struct pf_state_key *skw,
refcount_init(&s->refs, 2);
counter_u64_add(V_pf_status.fcounters[FCNT_STATE_INSERT], 1);
- if (pfsync_insert_state_ptr != NULL)
- pfsync_insert_state_ptr(s);
+ if (V_pfsync_insert_state_ptr != NULL)
+ V_pfsync_insert_state_ptr(s);
/* Returns locked. */
return (0);
@@ -1674,8 +1674,8 @@ pf_unlink_state(struct pf_state *s, u_int flags)
LIST_REMOVE(s, entry);
pf_src_tree_remove_state(s);
- if (pfsync_delete_state_ptr != NULL)
- pfsync_delete_state_ptr(s);
+ if (V_pfsync_delete_state_ptr != NULL)
+ V_pfsync_delete_state_ptr(s);
STATE_DEC_COUNTERS(s);
@@ -3441,7 +3441,7 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction,
if (PF_ANEQ(daddr,
&nk->addr[pd->didx], AF_INET6))
- PF_ACPY(saddr, &nk->addr[pd->didx], af);
+ PF_ACPY(daddr, &nk->addr[pd->didx], af);
break;
#endif /* INET */
}
@@ -3594,7 +3594,7 @@ pf_test_rule(struct pf_rule **rm, struct pf_state **sm, int direction,
if (*sm != NULL && !((*sm)->state_flags & PFSTATE_NOSYNC) &&
direction == PF_OUT &&
- pfsync_defer_ptr != NULL && pfsync_defer_ptr(*sm, m))
+ V_pfsync_defer_ptr != NULL && V_pfsync_defer_ptr(*sm, m))
/*
* We want the state created, but we dont
* want to send this in case a partner
@@ -6008,8 +6008,8 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb *
action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd,
&reason);
if (action == PF_PASS) {
- if (pfsync_update_state_ptr != NULL)
- pfsync_update_state_ptr(s);
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
r = s->rule.ptr;
a = s->anchor.ptr;
log = s->log;
@@ -6037,8 +6037,8 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb *
}
action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd);
if (action == PF_PASS) {
- if (pfsync_update_state_ptr != NULL)
- pfsync_update_state_ptr(s);
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
r = s->rule.ptr;
a = s->anchor.ptr;
log = s->log;
@@ -6060,8 +6060,8 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb *
action = pf_test_state_icmp(&s, dir, kif, m, off, h, &pd,
&reason);
if (action == PF_PASS) {
- if (pfsync_update_state_ptr != NULL)
- pfsync_update_state_ptr(s);
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
r = s->rule.ptr;
a = s->anchor.ptr;
log = s->log;
@@ -6083,8 +6083,8 @@ pf_test(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb *
default:
action = pf_test_state_other(&s, dir, kif, m, &pd);
if (action == PF_PASS) {
- if (pfsync_update_state_ptr != NULL)
- pfsync_update_state_ptr(s);
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
r = s->rule.ptr;
a = s->anchor.ptr;
log = s->log;
@@ -6447,8 +6447,8 @@ pf_test6(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb
action = pf_test_state_tcp(&s, dir, kif, m, off, h, &pd,
&reason);
if (action == PF_PASS) {
- if (pfsync_update_state_ptr != NULL)
- pfsync_update_state_ptr(s);
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
r = s->rule.ptr;
a = s->anchor.ptr;
log = s->log;
@@ -6476,8 +6476,8 @@ pf_test6(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb
}
action = pf_test_state_udp(&s, dir, kif, m, off, h, &pd);
if (action == PF_PASS) {
- if (pfsync_update_state_ptr != NULL)
- pfsync_update_state_ptr(s);
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
r = s->rule.ptr;
a = s->anchor.ptr;
log = s->log;
@@ -6506,8 +6506,8 @@ pf_test6(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb
action = pf_test_state_icmp(&s, dir, kif,
m, off, h, &pd, &reason);
if (action == PF_PASS) {
- if (pfsync_update_state_ptr != NULL)
- pfsync_update_state_ptr(s);
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
r = s->rule.ptr;
a = s->anchor.ptr;
log = s->log;
@@ -6520,8 +6520,8 @@ pf_test6(int dir, int pflags, struct ifnet *ifp, struct mbuf **m0, struct inpcb
default:
action = pf_test_state_other(&s, dir, kif, m, &pd);
if (action == PF_PASS) {
- if (pfsync_update_state_ptr != NULL)
- pfsync_update_state_ptr(s);
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
r = s->rule.ptr;
a = s->anchor.ptr;
log = s->log;
diff --git a/freebsd/sys/netpfil/pf/pf_if.c b/freebsd/sys/netpfil/pf/pf_if.c
index 2c321118..ed69acad 100644
--- a/freebsd/sys/netpfil/pf/pf_if.c
+++ b/freebsd/sys/netpfil/pf/pf_if.c
@@ -167,8 +167,10 @@ pfi_cleanup_vnet(void)
RB_REMOVE(pfi_ifhead, &V_pfi_ifs, kif);
if (kif->pfik_group)
kif->pfik_group->ifg_pf_kif = NULL;
- if (kif->pfik_ifp)
+ if (kif->pfik_ifp) {
+ if_rele(kif->pfik_ifp);
kif->pfik_ifp->if_pf_kif = NULL;
+ }
free(kif, PFI_MTYPE);
}
@@ -324,6 +326,8 @@ pfi_attach_ifnet(struct ifnet *ifp)
V_pfi_update++;
kif = pfi_kif_attach(kif, ifp->if_xname);
+ if_ref(ifp);
+
kif->pfik_ifp = ifp;
ifp->if_pf_kif = kif;
@@ -555,7 +559,8 @@ pfi_instance_add(struct ifnet *ifp, int net, int flags)
if ((flags & PFI_AFLAG_PEER) &&
!(ifp->if_flags & IFF_POINTOPOINT))
continue;
- if ((flags & PFI_AFLAG_NETWORK) && af == AF_INET6 &&
+ if ((flags & (PFI_AFLAG_NETWORK | PFI_AFLAG_NOALIAS)) &&
+ af == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(
&((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr))
continue;
@@ -835,6 +840,9 @@ pfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp)
{
struct pfi_kif *kif = (struct pfi_kif *)ifp->if_pf_kif;
+ if (pfsync_detach_ifnet_ptr)
+ pfsync_detach_ifnet_ptr(ifp);
+
if (kif == NULL)
return;
@@ -842,10 +850,13 @@ pfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp)
/* Avoid teardown race in the least expensive way. */
return;
}
+
PF_RULES_WLOCK();
V_pfi_update++;
pfi_kif_update(kif);
+ if_rele(kif->pfik_ifp);
+
kif->pfik_ifp = NULL;
ifp->if_pf_kif = NULL;
#ifdef ALTQ
@@ -906,6 +917,9 @@ pfi_detach_group_event(void *arg __unused, struct ifg_group *ifg)
static void
pfi_ifaddr_event(void *arg __unused, struct ifnet *ifp)
{
+
+ KASSERT(ifp, ("ifp == NULL"));
+
if (ifp->if_pf_kif == NULL)
return;
@@ -914,7 +928,7 @@ pfi_ifaddr_event(void *arg __unused, struct ifnet *ifp)
return;
}
PF_RULES_WLOCK();
- if (ifp && ifp->if_pf_kif) {
+ if (ifp->if_pf_kif) {
V_pfi_update++;
pfi_kif_update(ifp->if_pf_kif);
}
diff --git a/freebsd/sys/netpfil/pf/pf_ioctl.c b/freebsd/sys/netpfil/pf/pf_ioctl.c
index 9ca15a41..3f732d62 100644
--- a/freebsd/sys/netpfil/pf/pf_ioctl.c
+++ b/freebsd/sys/netpfil/pf/pf_ioctl.c
@@ -214,12 +214,14 @@ struct sx pf_ioctl_lock;
struct sx pf_end_lock;
/* pfsync */
-pfsync_state_import_t *pfsync_state_import_ptr = NULL;
-pfsync_insert_state_t *pfsync_insert_state_ptr = NULL;
-pfsync_update_state_t *pfsync_update_state_ptr = NULL;
-pfsync_delete_state_t *pfsync_delete_state_ptr = NULL;
-pfsync_clear_states_t *pfsync_clear_states_ptr = NULL;
-pfsync_defer_t *pfsync_defer_ptr = NULL;
+VNET_DEFINE(pfsync_state_import_t *, pfsync_state_import_ptr);
+VNET_DEFINE(pfsync_insert_state_t *, pfsync_insert_state_ptr);
+VNET_DEFINE(pfsync_update_state_t *, pfsync_update_state_ptr);
+VNET_DEFINE(pfsync_delete_state_t *, pfsync_delete_state_ptr);
+VNET_DEFINE(pfsync_clear_states_t *, pfsync_clear_states_ptr);
+VNET_DEFINE(pfsync_defer_t *, pfsync_defer_ptr);
+pfsync_detach_ifnet_t *pfsync_detach_ifnet_ptr;
+
/* pflog */
pflog_packet_t *pflog_packet_ptr = NULL;
@@ -1885,8 +1887,8 @@ relock_DIOCCLRSTATES:
PF_HASHROW_UNLOCK(ih);
}
psk->psk_killed = killed;
- if (pfsync_clear_states_ptr != NULL)
- pfsync_clear_states_ptr(V_pf_status.hostid, psk->psk_ifname);
+ if (V_pfsync_clear_states_ptr != NULL)
+ V_pfsync_clear_states_ptr(V_pf_status.hostid, psk->psk_ifname);
break;
}
@@ -1973,9 +1975,9 @@ relock_DIOCKILLSTATES:
error = EINVAL;
break;
}
- if (pfsync_state_import_ptr != NULL) {
+ if (V_pfsync_state_import_ptr != NULL) {
PF_RULES_RLOCK();
- error = pfsync_state_import_ptr(sp, PFSYNC_SI_IOCTL);
+ error = V_pfsync_state_import_ptr(sp, PFSYNC_SI_IOCTL);
PF_RULES_RUNLOCK();
} else
error = EOPNOTSUPP;
diff --git a/freebsd/sys/netpfil/pf/pf_norm.c b/freebsd/sys/netpfil/pf/pf_norm.c
index 0f98c669..9538e97c 100644
--- a/freebsd/sys/netpfil/pf/pf_norm.c
+++ b/freebsd/sys/netpfil/pf/pf_norm.c
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright 2001 Niels Provos <provos@citi.umich.edu>
- * Copyright 2011 Alexander Bluhm <bluhm@openbsd.org>
+ * Copyright 2011-2018 Alexander Bluhm <bluhm@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -89,14 +89,17 @@ struct pf_fragment {
#define fr_af fr_key.frc_af
#define fr_proto fr_key.frc_proto
+ /* pointers to queue element */
+ struct pf_frent *fr_firstoff[PF_FRAG_ENTRY_POINTS];
+ /* count entries between pointers */
+ uint8_t fr_entries[PF_FRAG_ENTRY_POINTS];
RB_ENTRY(pf_fragment) fr_entry;
TAILQ_ENTRY(pf_fragment) frag_next;
uint32_t fr_timeout;
uint16_t fr_maxlen; /* maximum length of single fragment */
- uint16_t fr_entries; /* Total number of pf_fragment entries */
+ u_int16_t fr_holes; /* number of holes in the queue */
TAILQ_HEAD(pf_fragq, pf_frent) fr_queue;
};
-#define PF_MAX_FRENT_PER_FRAGMENT 64
struct pf_fragment_tag {
uint16_t ft_hdrlen; /* header length of reassembled pkt */
@@ -136,11 +139,18 @@ static void pf_remove_fragment(struct pf_fragment *);
static int pf_normalize_tcpopt(struct pf_rule *, struct mbuf *,
struct tcphdr *, int, sa_family_t);
static struct pf_frent *pf_create_fragment(u_short *);
+static int pf_frent_holes(struct pf_frent *frent);
static struct pf_fragment *pf_find_fragment(struct pf_fragment_cmp *key,
struct pf_frag_tree *tree);
+static inline int pf_frent_index(struct pf_frent *);
+static int pf_frent_insert(struct pf_fragment *,
+ struct pf_frent *, struct pf_frent *);
+void pf_frent_remove(struct pf_fragment *,
+ struct pf_frent *);
+struct pf_frent *pf_frent_previous(struct pf_fragment *,
+ struct pf_frent *);
static struct pf_fragment *pf_fillup_fragment(struct pf_fragment_cmp *,
struct pf_frent *, u_short *);
-static int pf_isfull_fragment(struct pf_fragment *);
static struct mbuf *pf_join_fragment(struct pf_fragment *);
#ifdef INET
static void pf_scrub_ip(struct mbuf **, uint32_t, uint8_t, uint8_t);
@@ -311,6 +321,7 @@ pf_remove_fragment(struct pf_fragment *frag)
{
PF_FRAG_ASSERT();
+ KASSERT(frag, ("frag != NULL"));
RB_REMOVE(pf_frag_tree, &V_pf_frag_tree, frag);
TAILQ_REMOVE(&V_pf_fragqueue, frag, frag_next);
@@ -337,9 +348,201 @@ pf_create_fragment(u_short *reason)
return (frent);
}
+/*
+ * Calculate the additional holes that were created in the fragment
+ * queue by inserting this fragment. A fragment in the middle
+ * creates one more hole by splitting. For each connected side,
+ * it loses one hole.
+ * Fragment entry must be in the queue when calling this function.
+ */
+static int
+pf_frent_holes(struct pf_frent *frent)
+{
+ struct pf_frent *prev = TAILQ_PREV(frent, pf_fragq, fr_next);
+ struct pf_frent *next = TAILQ_NEXT(frent, fr_next);
+ int holes = 1;
+
+ if (prev == NULL) {
+ if (frent->fe_off == 0)
+ holes--;
+ } else {
+ KASSERT(frent->fe_off != 0, ("frent->fe_off != 0"));
+ if (frent->fe_off == prev->fe_off + prev->fe_len)
+ holes--;
+ }
+ if (next == NULL) {
+ if (!frent->fe_mff)
+ holes--;
+ } else {
+ KASSERT(frent->fe_mff, ("frent->fe_mff"));
+ if (next->fe_off == frent->fe_off + frent->fe_len)
+ holes--;
+ }
+ return holes;
+}
+
+static inline int
+pf_frent_index(struct pf_frent *frent)
+{
+ /*
+ * We have an array of 16 entry points to the queue. A full size
+ * 65535 octet IP packet can have 8192 fragments. So the queue
+ * traversal length is at most 512 and at most 16 entry points are
+ * checked. We need 128 additional bytes on a 64 bit architecture.
+ */
+ CTASSERT(((u_int16_t)0xffff &~ 7) / (0x10000 / PF_FRAG_ENTRY_POINTS) ==
+ 16 - 1);
+ CTASSERT(((u_int16_t)0xffff >> 3) / PF_FRAG_ENTRY_POINTS == 512 - 1);
+
+ return frent->fe_off / (0x10000 / PF_FRAG_ENTRY_POINTS);
+}
+
+static int
+pf_frent_insert(struct pf_fragment *frag, struct pf_frent *frent,
+ struct pf_frent *prev)
+{
+ int index;
+
+ CTASSERT(PF_FRAG_ENTRY_LIMIT <= 0xff);
+
+ /*
+ * A packet has at most 65536 octets. With 16 entry points, each one
+ * spawns 4096 octets. We limit these to 64 fragments each, which
+ * means on average every fragment must have at least 64 octets.
+ */
+ index = pf_frent_index(frent);
+ if (frag->fr_entries[index] >= PF_FRAG_ENTRY_LIMIT)
+ return ENOBUFS;
+ frag->fr_entries[index]++;
+
+ if (prev == NULL) {
+ TAILQ_INSERT_HEAD(&frag->fr_queue, frent, fr_next);
+ } else {
+ KASSERT(prev->fe_off + prev->fe_len <= frent->fe_off,
+ ("overlapping fragment"));
+ TAILQ_INSERT_AFTER(&frag->fr_queue, prev, frent, fr_next);
+ }
+
+ if (frag->fr_firstoff[index] == NULL) {
+ KASSERT(prev == NULL || pf_frent_index(prev) < index,
+ ("prev == NULL || pf_frent_index(pref) < index"));
+ frag->fr_firstoff[index] = frent;
+ } else {
+ if (frent->fe_off < frag->fr_firstoff[index]->fe_off) {
+ KASSERT(prev == NULL || pf_frent_index(prev) < index,
+ ("prev == NULL || pf_frent_index(pref) < index"));
+ frag->fr_firstoff[index] = frent;
+ } else {
+ KASSERT(prev != NULL, ("prev != NULL"));
+ KASSERT(pf_frent_index(prev) == index,
+ ("pf_frent_index(prev) == index"));
+ }
+ }
+
+ frag->fr_holes += pf_frent_holes(frent);
+
+ return 0;
+}
+
+void
+pf_frent_remove(struct pf_fragment *frag, struct pf_frent *frent)
+{
+#ifdef INVARIANTS
+ struct pf_frent *prev = TAILQ_PREV(frent, pf_fragq, fr_next);
+#endif
+ struct pf_frent *next = TAILQ_NEXT(frent, fr_next);
+ int index;
+
+ frag->fr_holes -= pf_frent_holes(frent);
+
+ index = pf_frent_index(frent);
+ KASSERT(frag->fr_firstoff[index] != NULL, ("frent not found"));
+ if (frag->fr_firstoff[index]->fe_off == frent->fe_off) {
+ if (next == NULL) {
+ frag->fr_firstoff[index] = NULL;
+ } else {
+ KASSERT(frent->fe_off + frent->fe_len <= next->fe_off,
+ ("overlapping fragment"));
+ if (pf_frent_index(next) == index) {
+ frag->fr_firstoff[index] = next;
+ } else {
+ frag->fr_firstoff[index] = NULL;
+ }
+ }
+ } else {
+ KASSERT(frag->fr_firstoff[index]->fe_off < frent->fe_off,
+ ("frag->fr_firstoff[index]->fe_off < frent->fe_off"));
+ KASSERT(prev != NULL, ("prev != NULL"));
+ KASSERT(prev->fe_off + prev->fe_len <= frent->fe_off,
+ ("overlapping fragment"));
+ KASSERT(pf_frent_index(prev) == index,
+ ("pf_frent_index(prev) == index"));
+ }
+
+ TAILQ_REMOVE(&frag->fr_queue, frent, fr_next);
+
+ KASSERT(frag->fr_entries[index] > 0, ("No fragments remaining"));
+ frag->fr_entries[index]--;
+}
+
+struct pf_frent *
+pf_frent_previous(struct pf_fragment *frag, struct pf_frent *frent)
+{
+ struct pf_frent *prev, *next;
+ int index;
+
+ /*
+ * If there are no fragments after frag, take the final one. Assume
+ * that the global queue is not empty.
+ */
+ prev = TAILQ_LAST(&frag->fr_queue, pf_fragq);
+ KASSERT(prev != NULL, ("prev != NULL"));
+ if (prev->fe_off <= frent->fe_off)
+ return prev;
+ /*
+ * We want to find a fragment entry that is before frag, but still
+ * close to it. Find the first fragment entry that is in the same
+ * entry point or in the first entry point after that. As we have
+ * already checked that there are entries behind frag, this will
+ * succeed.
+ */
+ for (index = pf_frent_index(frent); index < PF_FRAG_ENTRY_POINTS;
+ index++) {
+ prev = frag->fr_firstoff[index];
+ if (prev != NULL)
+ break;
+ }
+ KASSERT(prev != NULL, ("prev != NULL"));
+ /*
+ * In prev we may have a fragment from the same entry point that is
+ * before frent, or one that is just one position behind frent.
+ * In the latter case, we go back one step and have the predecessor.
+ * There may be none if the new fragment will be the first one.
+ */
+ if (prev->fe_off > frent->fe_off) {
+ prev = TAILQ_PREV(prev, pf_fragq, fr_next);
+ if (prev == NULL)
+ return NULL;
+ KASSERT(prev->fe_off <= frent->fe_off,
+ ("prev->fe_off <= frent->fe_off"));
+ return prev;
+ }
+ /*
+ * In prev is the first fragment of the entry point. The offset
+ * of frag is behind it. Find the closest previous fragment.
+ */
+ for (next = TAILQ_NEXT(prev, fr_next); next != NULL;
+ next = TAILQ_NEXT(next, fr_next)) {
+ if (next->fe_off > frent->fe_off)
+ break;
+ prev = next;
+ }
+ return prev;
+}
+
static struct pf_fragment *
pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
- u_short *reason)
+ u_short *reason)
{
struct pf_frent *after, *next, *prev;
struct pf_fragment *frag;
@@ -386,23 +589,22 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
}
*(struct pf_fragment_cmp *)frag = *key;
+ memset(frag->fr_firstoff, 0, sizeof(frag->fr_firstoff));
+ memset(frag->fr_entries, 0, sizeof(frag->fr_entries));
frag->fr_timeout = time_uptime;
frag->fr_maxlen = frent->fe_len;
- frag->fr_entries = 0;
+ frag->fr_holes = 1;
TAILQ_INIT(&frag->fr_queue);
RB_INSERT(pf_frag_tree, &V_pf_frag_tree, frag);
TAILQ_INSERT_HEAD(&V_pf_fragqueue, frag, frag_next);
- /* We do not have a previous fragment. */
- TAILQ_INSERT_HEAD(&frag->fr_queue, frent, fr_next);
+ /* We do not have a previous fragment, cannot fail. */
+ pf_frent_insert(frag, frent, NULL);
return (frag);
}
- if (frag->fr_entries >= PF_MAX_FRENT_PER_FRAGMENT)
- goto bad_fragment;
-
KASSERT(!TAILQ_EMPTY(&frag->fr_queue), ("!TAILQ_EMPTY()->fr_queue"));
/* Remember maximum fragment len for refragmentation. */
@@ -427,17 +629,15 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
goto bad_fragment;
}
- /* Find a fragment after the current one. */
- prev = NULL;
- TAILQ_FOREACH(after, &frag->fr_queue, fr_next) {
- if (after->fe_off > frent->fe_off)
- break;
- prev = after;
+ /* Find neighbors for newly inserted fragment */
+ prev = pf_frent_previous(frag, frent);
+ if (prev == NULL) {
+ after = TAILQ_FIRST(&frag->fr_queue);
+ KASSERT(after != NULL, ("after != NULL"));
+ } else {
+ after = TAILQ_NEXT(prev, fr_next);
}
- KASSERT(prev != NULL || after != NULL,
- ("prev != NULL || after != NULL"));
-
if (prev != NULL && prev->fe_off + prev->fe_len > frent->fe_off) {
uint16_t precut;
@@ -465,17 +665,16 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
/* This fragment is completely overlapped, lose it. */
next = TAILQ_NEXT(after, fr_next);
+ pf_frent_remove(frag, after);
m_freem(after->fe_m);
- TAILQ_REMOVE(&frag->fr_queue, after, fr_next);
uma_zfree(V_pf_frent_z, after);
}
- if (prev == NULL)
- TAILQ_INSERT_HEAD(&frag->fr_queue, frent, fr_next);
- else
- TAILQ_INSERT_AFTER(&frag->fr_queue, prev, frent, fr_next);
-
- frag->fr_entries++;
+ /* If part of the queue gets too long, there is not way to recover. */
+ if (pf_frent_insert(frag, frent, prev)) {
+ DPFPRINTF(("fragment queue limit exceeded"));
+ goto bad_fragment;
+ }
return (frag);
@@ -486,40 +685,6 @@ drop_fragment:
return (NULL);
}
-static int
-pf_isfull_fragment(struct pf_fragment *frag)
-{
- struct pf_frent *frent, *next;
- uint16_t off, total;
-
- /* Check if we are completely reassembled */
- if (TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_mff)
- return (0);
-
- /* Maximum data we have seen already */
- total = TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_off +
- TAILQ_LAST(&frag->fr_queue, pf_fragq)->fe_len;
-
- /* Check if we have all the data */
- off = 0;
- for (frent = TAILQ_FIRST(&frag->fr_queue); frent; frent = next) {
- next = TAILQ_NEXT(frent, fr_next);
-
- off += frent->fe_len;
- if (off < total && (next == NULL || next->fe_off != off)) {
- DPFPRINTF(("missing fragment at %d, next %d, total %d",
- off, next == NULL ? -1 : next->fe_off, total));
- return (0);
- }
- }
- DPFPRINTF(("%d < %d?", off, total));
- if (off < total)
- return (0);
- KASSERT(off == total, ("off == total"));
-
- return (1);
-}
-
static struct mbuf *
pf_join_fragment(struct pf_fragment *frag)
{
@@ -580,8 +745,10 @@ pf_reassemble(struct mbuf **m0, struct ip *ip, int dir, u_short *reason)
/* The mbuf is part of the fragment entry, no direct free or access */
m = *m0 = NULL;
- if (!pf_isfull_fragment(frag))
+ if (frag->fr_holes) {
+ DPFPRINTF(("frag %d, holes %d", frag->fr_id, frag->fr_holes));
return (PF_PASS); /* drop because *m0 is NULL, no error */
+ }
/* We have all the data */
frent = TAILQ_FIRST(&frag->fr_queue);
@@ -664,7 +831,8 @@ pf_reassemble6(struct mbuf **m0, struct ip6_hdr *ip6, struct ip6_frag *fraghdr,
/* The mbuf is part of the fragment entry, no direct free or access. */
m = *m0 = NULL;
- if (!pf_isfull_fragment(frag)) {
+ if (frag->fr_holes) {
+ DPFPRINTF(("frag %d, holes %d", frag->fr_id, frag->fr_holes));
PF_FRAG_UNLOCK();
return (PF_PASS); /* Drop because *m0 is NULL, no error. */
}
diff --git a/freebsd/sys/netpfil/pf/pf_table.c b/freebsd/sys/netpfil/pf/pf_table.c
index 1fadd38c..3f15fb0e 100644
--- a/freebsd/sys/netpfil/pf/pf_table.c
+++ b/freebsd/sys/netpfil/pf/pf_table.c
@@ -1756,6 +1756,7 @@ pfr_setflags_ktable(struct pfr_ktable *kt, int newf)
PF_RULES_WASSERT();
if (!(newf & PFR_TFLAG_REFERENCED) &&
+ !(newf & PFR_TFLAG_REFDANCHOR) &&
!(newf & PFR_TFLAG_PERSIST))
newf &= ~PFR_TFLAG_ACTIVE;
if (!(newf & PFR_TFLAG_ACTIVE))