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