Hello,

It looks like IPv6 fragments are not forwarded properly, when PBR
rules are used. My testing rules looks as follow:

    pass in quick on vnet1 from any to self
    pass in quick on vnet2 from any to self
    pass in on vnet1 from any to any route-to 2006::2@vnet2 no state
    pass in on vnet2 from any to any route-to 2005::2@vnet1 no state

(I've just stolen 2006::/64 @vnet2  2005::/64 @vnet1 prefixes since my test
network lives in LDOMs, which don't talk to outside world...)

The attached patch makes sure pf_route6() uses pf_refragment6() before it puts
reassembled packet to destination NIC. It looks like the proposed
fix works for me. I have not tried it with NAT-64 yet.

I've also did not shrink MTU on vnet2 (destination NIC) to see what happens.

        MTU(vnet1) > MTU(vnet2)

it's quite possible the patch is not complete yet.

regards
sasha
Index: pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.915
diff -u -p -u -r1.915 pf.c
--- pf.c        22 May 2015 14:18:55 -0000      1.915
+++ pf.c        25 May 2015 16:12:23 -0000
@@ -5598,6 +5598,7 @@ pf_route6(struct mbuf **m, struct pf_rul
        struct ifnet            *ifp = NULL;
        struct pf_addr           naddr;
        struct pf_src_node      *sns[PF_SN_MAX];
+       struct m_tag            *mtag;
 
        if (m == NULL || *m == NULL || r == NULL ||
            (dir != PF_IN && dir != PF_OUT) || oifp == NULL)
@@ -5632,6 +5633,9 @@ pf_route6(struct mbuf **m, struct pf_rul
        dst->sin6_addr = ip6->ip6_dst;
 
        if (!r->rt) {
+               /*
+                * XXX how we can be so sure m0 fits to wire?
+                */
                m0->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
                ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL);
                return;
@@ -5672,13 +5676,16 @@ pf_route6(struct mbuf **m, struct pf_rul
 
        in6_proto_cksum_out(m0, ifp);
 
-       /*
-        * If the packet is too large for the outgoing interface,
-        * send back an icmp6 error.
-        */
        if (IN6_IS_SCOPE_EMBED(&dst->sin6_addr))
                dst->sin6_addr.s6_addr16[1] = htons(ifp->if_index);
-       if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) {
+
+       /*
+        * If packet has been reassembled by PF earlier, we have to
+        * use pf_refragment6() here to turn it back to fragments.
+        */
+       if ((mtag = m_tag_find(m0, PACKET_TAG_PF_REASSEMBLED, NULL))) {
+               (void) pf_refragment6(&m0, mtag, PF_OUT, dst, ifp);
+       } else if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) {
                nd6_output(ifp, m0, dst, NULL);
        } else {
                in6_ifstat_inc(ifp, ifs6_in_toobig);
@@ -6593,7 +6600,7 @@ done:
                struct m_tag    *mtag;
 
                if ((mtag = m_tag_find(*m0, PACKET_TAG_PF_REASSEMBLED, NULL)))
-                       action = pf_refragment6(m0, mtag, fwdir);
+                       action = pf_refragment6(m0, mtag, fwdir, NULL, NULL);
        }
 #endif
        if (s && action != PF_DROP) {
Index: pf_norm.c
===================================================================
RCS file: /cvs/src/sys/net/pf_norm.c,v
retrieving revision 1.178
diff -u -p -u -r1.178 pf_norm.c
--- pf_norm.c   5 May 2015 23:27:47 -0000       1.178
+++ pf_norm.c   25 May 2015 16:12:23 -0000
@@ -57,6 +57,8 @@
 #ifdef INET6
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
 #endif /* INET6 */
 
 #include <net/pfvar.h>
@@ -680,7 +682,8 @@ fail:
 }
 
 int
-pf_refragment6(struct mbuf **m0, struct m_tag *mtag, int dir)
+pf_refragment6(struct mbuf **m0, struct m_tag *mtag, int dir,
+    struct sockaddr_in6 *dst, struct ifnet *ifp)
 {
        struct mbuf             *m = *m0, *t;
        struct pf_fragment_tag  *ftag = (struct pf_fragment_tag *)(mtag + 1);
@@ -743,10 +746,14 @@ pf_refragment6(struct mbuf **m0, struct 
                t = m->m_nextpkt;
                m->m_nextpkt = NULL;
                m->m_pkthdr.pf.flags |= PF_TAG_REFRAGMENTED;
-               if (error == 0)
-                       ip6_forward(m, 0);
-               else
+               if (error == 0) {
+                       if (ifp == NULL)
+                               ip6_forward(m, 0);
+                       else
+                               nd6_output(ifp, m, dst, NULL);
+               } else {
                        m_freem(m);
+               }
        }
 
        return (action);
Index: pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.414
diff -u -p -u -r1.414 pfvar.h
--- pfvar.h     11 Apr 2015 13:00:12 -0000      1.414
+++ pfvar.h     25 May 2015 16:12:23 -0000
@@ -1826,7 +1826,8 @@ int       pf_match_port(u_int8_t, u_int16_t, u
 int    pf_match_uid(u_int8_t, uid_t, uid_t, uid_t);
 int    pf_match_gid(u_int8_t, gid_t, gid_t, gid_t);
 
-int    pf_refragment6(struct mbuf **, struct m_tag *mtag, int);
+int    pf_refragment6(struct mbuf **, struct m_tag *mtag, int,
+           struct sockaddr_in6 *, struct ifnet *);
 void   pf_normalize_init(void);
 int    pf_normalize_ip(struct pf_pdesc *, u_short *);
 int    pf_normalize_ip6(struct pf_pdesc *, u_short *);

Reply via email to