diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -2297,6 +2297,7 @@ void pf_poolmask(struct pf_addr *, struct pf_addr*, struct pf_addr *, struct pf_addr *, sa_family_t); void pf_addr_inc(struct pf_addr *, sa_family_t); +int pf_max_frag_size(struct mbuf *); int pf_refragment6(struct ifnet *, struct mbuf **, struct m_tag *, bool); #endif /* INET6 */ diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -8510,6 +8510,18 @@ return (PF_PASS); } + /* + * If we end up changing IP addresses (e.g. binat) the stack may get + * confused and fail to send the icmp6 packet too big error. Just send + * it here, before we do any NAT. + */ + if (dir == PF_OUT && IN6_LINKMTU(ifp) < pf_max_frag_size(m)) { + PF_RULES_RUNLOCK(); + *m0 = NULL; + icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, IN6_LINKMTU(ifp)); + return (PF_DROP); + } + memset(&pd, 0, sizeof(pd)); TAILQ_INIT(&pd.sctp_multihome_jobs); if (default_actions != NULL) diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -939,6 +939,21 @@ #endif /* INET6 */ #ifdef INET6 +int +pf_max_frag_size(struct mbuf *m) +{ + struct m_tag *tag; + struct pf_fragment_tag *ftag; + + tag = m_tag_find(m, PACKET_TAG_PF_REASSEMBLED, NULL); + if (tag == NULL) + return (m->m_pkthdr.len); + + ftag = (struct pf_fragment_tag *)(tag + 1); + + return (ftag->ft_maxlen); +} + int pf_refragment6(struct ifnet *ifp, struct mbuf **m0, struct m_tag *mtag, bool forward)