summaryrefslogtreecommitdiffstats
path: root/sys/netpfil
diff options
context:
space:
mode:
authorkp <kp@FreeBSD.org>2015-06-18 20:34:39 +0000
committerkp <kp@FreeBSD.org>2015-06-18 20:34:39 +0000
commit83b6287db453b282c2b0abafdf15ef08ad0f9c98 (patch)
treefa12c19b8f8e25ded89a2ecc18395142752ef43c /sys/netpfil
parent80d7588d81ffca8e895717266bef43ea2a55bac1 (diff)
downloadFreeBSD-src-83b6287db453b282c2b0abafdf15ef08ad0f9c98.zip
FreeBSD-src-83b6287db453b282c2b0abafdf15ef08ad0f9c98.tar.gz
Merge r278843, r278858
In the forwarding case refragment the reassembled packets with the same size as they arrived in. This allows the sender to determine the optimal fragment size by Path MTU Discovery. Roughly based on the OpenBSD work by Alexander Bluhm. Differential Revision: https://reviews.freebsd.org/D2816 Reviewed by: gnn
Diffstat (limited to 'sys/netpfil')
-rw-r--r--sys/netpfil/pf/pf.c12
-rw-r--r--sys/netpfil/pf/pf.h2
-rw-r--r--sys/netpfil/pf/pf_mtag.h1
-rw-r--r--sys/netpfil/pf/pf_norm.c80
4 files changed, 93 insertions, 2 deletions
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 44b102e..856bffb 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -5509,7 +5509,7 @@ pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp,
goto bad;
if (oifp != ifp) {
- if (pf_test6(PF_OUT, ifp, &m0, NULL) != PF_PASS)
+ if (pf_test6(PF_FWD, ifp, &m0, NULL) != PF_PASS)
goto bad;
else if (m0 == NULL)
goto done;
@@ -6067,15 +6067,20 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp)
struct pfi_kif *kif;
u_short action, reason = 0, log = 0;
struct mbuf *m = *m0, *n = NULL;
+ struct m_tag *mtag;
struct ip6_hdr *h = NULL;
struct pf_rule *a = NULL, *r = &V_pf_default_rule, *tr, *nr;
struct pf_state *s = NULL;
struct pf_ruleset *ruleset = NULL;
struct pf_pdesc pd;
int off, terminal = 0, dirndx, rh_cnt = 0;
+ int fwdir = dir;
M_ASSERTPKTHDR(m);
+ if (ifp != m->m_pkthdr.rcvif)
+ fwdir = PF_FWD;
+
if (!V_pf_status.running)
return (PF_PASS);
@@ -6437,6 +6442,11 @@ done:
if (s)
PF_STATE_UNLOCK(s);
+ /* If reassembled packet passed, create new fragments. */
+ if (action == PF_PASS && *m0 && fwdir == PF_FWD &&
+ (mtag = m_tag_find(m, PF_REASSEMBLED, NULL)) != NULL)
+ action = pf_refragment6(ifp, m0, mtag);
+
return (action);
}
#endif /* INET6 */
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index 3ea66d3..3a5d2aa 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -43,7 +43,7 @@
#endif
#endif
-enum { PF_INOUT, PF_IN, PF_OUT };
+enum { PF_INOUT, PF_IN, PF_OUT, PF_FWD };
enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT,
PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, PF_DEFER };
enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT,
diff --git a/sys/netpfil/pf/pf_mtag.h b/sys/netpfil/pf/pf_mtag.h
index baff00a..3aacb2e 100644
--- a/sys/netpfil/pf/pf_mtag.h
+++ b/sys/netpfil/pf/pf_mtag.h
@@ -39,6 +39,7 @@
#define PF_TAG_TRANSLATE_LOCALHOST 0x04
#define PF_PACKET_LOOPED 0x08
#define PF_FASTFWD_OURS_PRESENT 0x10
+#define PF_REASSEMBLED 0x20
struct pf_mtag {
void *hdr; /* saved hdr pos in mbuf, for ECN */
diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c
index d9216e3..262a0d4 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -678,6 +678,8 @@ pf_reassemble6(struct mbuf **m0, struct ip6_hdr *ip6, struct ip6_frag *fraghdr,
struct pf_frent *frent;
struct pf_fragment *frag;
struct pf_fragment_cmp key;
+ struct m_tag *mtag;
+ struct pf_fragment_tag *ftag;
int off;
uint16_t total, maxlen;
uint8_t proto;
@@ -750,6 +752,15 @@ pf_reassemble6(struct mbuf **m0, struct ip6_hdr *ip6, struct ip6_frag *fraghdr,
m->m_pkthdr.len = plen;
}
+ if ((mtag = m_tag_get(PF_REASSEMBLED, sizeof(struct pf_fragment_tag),
+ M_NOWAIT)) == NULL)
+ goto fail;
+ ftag = (struct pf_fragment_tag *)(mtag + 1);
+ ftag->ft_hdrlen = hdrlen;
+ ftag->ft_extoff = extoff;
+ ftag->ft_maxlen = maxlen;
+ m_tag_prepend(m, mtag);
+
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(hdrlen - sizeof(struct ip6_hdr) + total);
if (extoff) {
@@ -1084,6 +1095,75 @@ pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment **frag, int mff,
}
int
+pf_refragment6(struct ifnet *ifp, struct mbuf **m0, struct m_tag *mtag)
+{
+ struct mbuf *m = *m0, *t;
+ struct pf_fragment_tag *ftag = (struct pf_fragment_tag *)(mtag + 1);
+ struct pf_pdesc pd;
+ uint16_t hdrlen, extoff, maxlen;
+ uint8_t proto;
+ int error, action;
+
+ hdrlen = ftag->ft_hdrlen;
+ extoff = ftag->ft_extoff;
+ maxlen = ftag->ft_maxlen;
+ m_tag_delete(m, mtag);
+ mtag = NULL;
+ ftag = NULL;
+
+ if (extoff) {
+ int off;
+
+ /* Use protocol from next field of last extension header */
+ m = m_getptr(m, extoff + offsetof(struct ip6_ext, ip6e_nxt),
+ &off);
+ KASSERT((m != NULL), ("pf_refragment6: short mbuf chain"));
+ proto = *(mtod(m, caddr_t) + off);
+ *(mtod(m, char *) + off) = IPPROTO_FRAGMENT;
+ m = *m0;
+ } else {
+ struct ip6_hdr *hdr;
+
+ hdr = mtod(m, struct ip6_hdr *);
+ proto = hdr->ip6_nxt;
+ hdr->ip6_nxt = IPPROTO_FRAGMENT;
+ }
+
+ /*
+ * Maxlen may be less than 8 if there was only a single
+ * fragment. As it was fragmented before, add a fragment
+ * header also for a single fragment. If total or maxlen
+ * is less than 8, ip6_fragment() will return EMSGSIZE and
+ * we drop the packet.
+ */
+ error = ip6_fragment(ifp, m, hdrlen, proto, maxlen);
+ m = (*m0)->m_nextpkt;
+ (*m0)->m_nextpkt = NULL;
+ if (error == 0) {
+ /* The first mbuf contains the unfragmented packet. */
+ m_freem(*m0);
+ *m0 = NULL;
+ action = PF_PASS;
+ } else {
+ /* Drop expects an mbuf to free. */
+ DPFPRINTF(("refragment error %d", error));
+ action = PF_DROP;
+ }
+ for (t = m; m; m = t) {
+ t = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ memset(&pd, 0, sizeof(pd));
+ pd.pf_mtag = pf_find_mtag(m);
+ if (error == 0)
+ ip6_forward(m, 0);
+ else
+ m_freem(m);
+ }
+
+ return (action);
+}
+
+int
pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kif *kif, u_short *reason,
struct pf_pdesc *pd)
{
OpenPOWER on IntegriCloud