aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/netpfil/pf/pf_norm.c46
-rw-r--r--tests/sys/netpfil/pf/frag6.py44
2 files changed, 82 insertions, 8 deletions
diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c
index 5b34e825203b..45e56edc34fe 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -98,9 +98,9 @@ struct pf_fragment {
RB_ENTRY(pf_fragment) fr_entry;
TAILQ_ENTRY(pf_fragment) frag_next;
uint32_t fr_timeout;
+ TAILQ_HEAD(pf_fragq, pf_frent) fr_queue;
uint16_t fr_maxlen; /* maximum length of single fragment */
u_int16_t fr_holes; /* number of holes in the queue */
- TAILQ_HEAD(pf_fragq, pf_frent) fr_queue;
};
VNET_DEFINE_STATIC(struct mtx, pf_frag_mtx);
@@ -593,9 +593,9 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
memset(frag->fr_firstoff, 0, sizeof(frag->fr_firstoff));
memset(frag->fr_entries, 0, sizeof(frag->fr_entries));
frag->fr_timeout = time_uptime;
+ TAILQ_INIT(&frag->fr_queue);
frag->fr_maxlen = frent->fe_len;
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);
@@ -606,7 +606,15 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
return (frag);
}
- KASSERT(!TAILQ_EMPTY(&frag->fr_queue), ("!TAILQ_EMPTY()->fr_queue"));
+ if (TAILQ_EMPTY(&frag->fr_queue)) {
+ /*
+ * Overlapping IPv6 fragments have been detected. Do not
+ * reassemble packet but also drop future fragments.
+ * This will be done for this ident/src/dst combination
+ * until fragment queue timeout.
+ */
+ goto drop_fragment;
+ }
/* Remember maximum fragment len for refragmentation. */
if (frent->fe_len > frag->fr_maxlen)
@@ -642,10 +650,15 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
if (prev != NULL && prev->fe_off + prev->fe_len > frent->fe_off) {
uint16_t precut;
+ if (frag->fr_af == AF_INET6)
+ goto flush_fragentries;
+
precut = prev->fe_off + prev->fe_len - frent->fe_off;
- if (precut >= frent->fe_len)
- goto bad_fragment;
- DPFPRINTF(("overlap -%d\n", precut));
+ if (precut >= frent->fe_len) {
+ DPFPRINTF(("new frag overlapped\n"));
+ goto drop_fragment;
+ }
+ DPFPRINTF(("frag head overlap %d\n", precut));
m_adj(frent->fe_m, precut);
frent->fe_off += precut;
frent->fe_len -= precut;
@@ -664,7 +677,7 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
after->fe_len -= aftercut;
new_index = pf_frent_index(after);
if (old_index != new_index) {
- DPFPRINTF(("frag index %d, new %d",
+ DPFPRINTF(("frag index %d, new %d\n",
old_index, new_index));
/* Fragment switched queue as fe_off changed */
after->fe_off -= aftercut;
@@ -676,7 +689,7 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
/* Insert into correct queue */
if (pf_frent_insert(frag, after, prev)) {
DPFPRINTF(
- ("fragment requeue limit exceeded"));
+ ("fragment requeue limit exceeded\n"));
m_freem(after->fe_m);
uma_zfree(V_pf_frent_z, after);
/* There is not way to recover */
@@ -687,6 +700,7 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
}
/* This fragment is completely overlapped, lose it. */
+ DPFPRINTF(("old frag overlapped\n"));
next = TAILQ_NEXT(after, fr_next);
pf_frent_remove(frag, after);
m_freem(after->fe_m);
@@ -701,6 +715,22 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent,
return (frag);
+flush_fragentries:
+ /*
+ * RFC5722: When reassembling an IPv6 datagram, if one or
+ * more its constituent fragments is determined to be an
+ * overlapping fragment, the entire datagram (and any constituent
+ * fragments, including those not yet received) MUST be
+ * silently discarded.
+ */
+ DPFPRINTF(("flush overlapping fragments\n"));
+ while ((prev = TAILQ_FIRST(&frag->fr_queue)) != NULL) {
+ TAILQ_REMOVE(&frag->fr_queue, prev, fr_next);
+
+ m_freem(prev->fe_m);
+ uma_zfree(V_pf_frent_z, prev);
+ }
+
bad_fragment:
REASON_SET(reason, PFRES_FRAG);
drop_fragment:
diff --git a/tests/sys/netpfil/pf/frag6.py b/tests/sys/netpfil/pf/frag6.py
index 28b1829d418c..f54381fba8cb 100644
--- a/tests/sys/netpfil/pf/frag6.py
+++ b/tests/sys/netpfil/pf/frag6.py
@@ -58,3 +58,47 @@ class TestFrag6(VnetTestTemplate):
timeout=3)
for p in packets:
assert not p.getlayer(sp.ICMPv6EchoReply)
+
+class TestFrag6_Overlap(VnetTestTemplate):
+ REQUIRED_MODULES = ["pf"]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes6": [("2001:db8::1/64", "2001:db8::2/64")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ ToolsHelper.pf_rules([
+ "scrub fragment reassemble",
+ "pass",
+ ])
+
+ @pytest.mark.require_user("root")
+ def test_overlap(self):
+ "Ensure we discard packets with overlapping fragments"
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ packet = sp.IPv6(src="2001:db8::1", dst="2001:db8::2") \
+ / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f00f') * 90))
+ frags = sp.fragment6(packet, 128)
+ assert len(frags) == 3
+
+ f = frags[0].getlayer(sp.IPv6ExtHdrFragment)
+ # Fragment with overlap
+ overlap = sp.IPv6(src="2001:db8::1", dst="2001:db8::2") \
+ / sp.IPv6ExtHdrFragment(offset = 4, m = 1, id = f.id, nh = f.nh) \
+ / sp.raw(bytes.fromhex('f00f') * 4)
+ frags = [ frags[0], frags[1], overlap, frags[2] ]
+
+ # Delay the send so the sniffer is running when we transmit.
+ s = DelayedSend(frags)
+
+ packets = sp.sniff(iface=self.vnet.iface_alias_map["if1"].name,
+ timeout=3)
+ for p in packets:
+ p.show()
+ assert not p.getlayer(sp.ICMPv6EchoReply)