Author: glebius
Date: Mon Feb 16 07:01:02 2015
New Revision: 278843
URL: https://svnweb.freebsd.org/changeset/base/278843

Log:
  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.
  
  Submitted by:         Kristof Provost
  Differential Revision:        D1767

Modified:
  head/sys/netpfil/pf/pf.c
  head/sys/netpfil/pf/pf.h
  head/sys/netpfil/pf/pf_mtag.h
  head/sys/netpfil/pf/pf_norm.c

Modified: head/sys/netpfil/pf/pf.c
==============================================================================
--- head/sys/netpfil/pf/pf.c    Mon Feb 16 06:30:27 2015        (r278842)
+++ head/sys/netpfil/pf/pf.c    Mon Feb 16 07:01:02 2015        (r278843)
@@ -5499,7 +5499,7 @@ pf_route6(struct mbuf **m, struct pf_rul
                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;
@@ -6057,15 +6057,20 @@ pf_test6(int dir, struct ifnet *ifp, str
        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);
 
@@ -6427,6 +6432,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 */

Modified: head/sys/netpfil/pf/pf.h
==============================================================================
--- head/sys/netpfil/pf/pf.h    Mon Feb 16 06:30:27 2015        (r278842)
+++ head/sys/netpfil/pf/pf.h    Mon Feb 16 07:01:02 2015        (r278843)
@@ -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,

Modified: head/sys/netpfil/pf/pf_mtag.h
==============================================================================
--- head/sys/netpfil/pf/pf_mtag.h       Mon Feb 16 06:30:27 2015        
(r278842)
+++ head/sys/netpfil/pf/pf_mtag.h       Mon Feb 16 07:01:02 2015        
(r278843)
@@ -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 */

Modified: head/sys/netpfil/pf/pf_norm.c
==============================================================================
--- head/sys/netpfil/pf/pf_norm.c       Mon Feb 16 06:30:27 2015        
(r278842)
+++ head/sys/netpfil/pf/pf_norm.c       Mon Feb 16 07:01:02 2015        
(r278843)
@@ -678,6 +678,8 @@ pf_reassemble6(struct mbuf **m0, struct 
        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 
                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
 }
 
 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)
 {
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to