diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-11-06 15:42:44 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-11-15 10:56:14 +0100 |
commit | e0b4edbdcc3558d3f38af8398f995c2e9f019f07 (patch) | |
tree | ea91a5fcfb9b6a66a8c0b74cf68ff8d450ce17e0 /freebsd/sys/netpfil | |
parent | Disable or make static kern_* functions (diff) | |
download | rtems-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.c | 76 | ||||
-rw-r--r-- | freebsd/sys/netpfil/pf/pf.c | 44 | ||||
-rw-r--r-- | freebsd/sys/netpfil/pf/pf_if.c | 20 | ||||
-rw-r--r-- | freebsd/sys/netpfil/pf/pf_ioctl.c | 22 | ||||
-rw-r--r-- | freebsd/sys/netpfil/pf/pf_norm.c | 294 | ||||
-rw-r--r-- | freebsd/sys/netpfil/pf/pf_table.c | 1 |
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)) |