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 *);