git: 54c62e3e5d8c - main - pf: work around icmp6 packet-too-big not being sent when binat-ing
Date: Mon, 22 Jan 2024 12:52:51 UTC
The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=54c62e3e5d8cd90c5571a1d4c8c5f062d580480e commit 54c62e3e5d8cd90c5571a1d4c8c5f062d580480e Author: Kristof Provost <kp@FreeBSD.org> AuthorDate: 2024-01-17 17:11:27 +0000 Commit: Kristof Provost <kp@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)