The branch main has been updated by kp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=54c62e3e5d8cd90c5571a1d4c8c5f062d580480e

commit 54c62e3e5d8cd90c5571a1d4c8c5f062d580480e
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2024-01-17 17:11:27 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2024-01-22 11:52:14 +0000

    pf: work around icmp6 packet-too-big not being sent when binat-ing
    
    If we're applying NPTv6 we pass a packet with a modified source and/or
    destination address to the network stack.
    
    If that packet then turns out to be larger than the MTU of the sending
    interface the stack will attempt to generate an icmp6 packet-too-big
    error, but may fail to look up the appropriate source address for that
    error message. Even if it does, pf would still have to undo the binat
    operation inside the icmp6 packet so the sending host can make sense of
    the error.
    
    We can avoid both problems entirely by having pf also perform the MTU
    check (taking the potential refragmentation into account), and
    generating the icmp6 error directly in pf.
    
    See also:       https://redmine.pfsense.org/issues/14290
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D43499
---
 sys/net/pfvar.h          |  1 +
 sys/netpfil/pf/pf.c      | 12 ++++++++++++
 sys/netpfil/pf/pf_norm.c | 15 +++++++++++++++
 3 files changed, 28 insertions(+)

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index f0742c99a4a8..ff3370bc105e 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2297,6 +2297,7 @@ int       pf_normalize_ip6(struct mbuf **, struct 
pfi_kkif *, u_short *,
 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
index 9bd9828a99d9..38a5a45d7991 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -8510,6 +8510,18 @@ pf_test6(int dir, int pflags, struct ifnet *ifp, struct 
mbuf **m0, struct inpcb
                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
index f5d1a66f6467..295377bef3e8 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -939,6 +939,21 @@ fail:
 #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)

Reply via email to