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)