git: 6b66194bcb7e - main - ipsec: Check PMTU before sending a frame.

Wojciech Macek wma at FreeBSD.org
Fri Aug 13 07:23:30 UTC 2021


The branch main has been updated by wma:

URL: https://cgit.FreeBSD.org/src/commit/?id=6b66194bcb7e43ef40b11005618544081c6e30ea

commit 6b66194bcb7e43ef40b11005618544081c6e30ea
Author:     Kornel Duleba <mindal at semihalf.com>
AuthorDate: 2021-08-13 07:20:46 +0000
Commit:     Wojciech Macek <wma at FreeBSD.org>
CommitDate: 2021-08-13 07:22:24 +0000

    ipsec: Check PMTU before sending a frame.
    
    If an encapsulated frame is going to have DF bit set check its desitnitions'
    PMTU and if it won't fit drop it and:
    
    Generate ICMP 3/4 message if the packet was to be forwarded.
    Return EMSGSIZE error otherwise.
    
    Obtained from:          Semihalf
    Sponsored by:           Stormshield
    Differential revision:  https://reviews.freebsd.org/D30993
---
 sys/netipsec/ipsec.c        |  2 +-
 sys/netipsec/ipsec.h        |  2 ++
 sys/netipsec/ipsec_output.c | 87 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/sys/netipsec/ipsec.c b/sys/netipsec/ipsec.c
index dcc607a5b617..07eb67641f7b 100644
--- a/sys/netipsec/ipsec.c
+++ b/sys/netipsec/ipsec.c
@@ -1093,7 +1093,7 @@ ipsec_in_reject(struct secpolicy *sp, struct inpcb *inp, const struct mbuf *m)
  * Compute the byte size to be occupied by IPsec header.
  * In case it is tunnelled, it includes the size of outer IP header.
  */
-static size_t
+size_t
 ipsec_hdrsiz_internal(struct secpolicy *sp)
 {
 	size_t size;
diff --git a/sys/netipsec/ipsec.h b/sys/netipsec/ipsec.h
index 1f5aa1a43edc..85360f9b737c 100644
--- a/sys/netipsec/ipsec.h
+++ b/sys/netipsec/ipsec.h
@@ -332,6 +332,7 @@ int ipsec_chkreplay(uint32_t, uint32_t *, struct secasvar *);
 int ipsec_updatereplay(uint32_t, struct secasvar *);
 int ipsec_updateid(struct secasvar *, crypto_session_t *, crypto_session_t *);
 int ipsec_initialized(void);
+size_t ipsec_hdrsiz_internal(struct secpolicy *);
 
 void ipsec_setspidx_inpcb(struct inpcb *, struct secpolicyindex *, u_int);
 
@@ -345,6 +346,7 @@ int ipsec4_output(struct mbuf *, struct inpcb *);
 int ipsec4_capability(struct mbuf *, u_int);
 int ipsec4_common_input_cb(struct mbuf *, struct secasvar *, int, int);
 int ipsec4_ctlinput(int, struct sockaddr *, void *);
+int ipsec4_check_pmtu(struct mbuf *, struct secpolicy *, int);
 int ipsec4_process_packet(struct mbuf *, struct secpolicy *, struct inpcb *);
 int ipsec_process_done(struct mbuf *, struct secpolicy *, struct secasvar *,
     u_int);
diff --git a/sys/netipsec/ipsec_output.c b/sys/netipsec/ipsec_output.c
index 86f06fd10947..a817b67fd93e 100644
--- a/sys/netipsec/ipsec_output.c
+++ b/sys/netipsec/ipsec_output.c
@@ -61,6 +61,8 @@
 #ifdef INET6
 #include <netinet6/ip6_ecn.h>
 #endif
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp_var.h>
 
 #include <netinet/ip6.h>
 #ifdef INET6
@@ -292,6 +294,83 @@ ipsec4_process_packet(struct mbuf *m, struct secpolicy *sp,
 	return (ipsec4_perform_request(m, sp, inp, 0));
 }
 
+int
+ipsec4_check_pmtu(struct mbuf *m, struct secpolicy *sp, int forwarding)
+{
+	union sockaddr_union *dst;
+	struct in_conninfo inc;
+	struct secasvar *sav;
+	struct ip *ip;
+	size_t hlen, pmtu;
+	uint32_t idx;
+	int error;
+
+
+	/* Don't check PMTU if the frame won't have DF bit set. */
+	if (!V_ip4_ipsec_dfbit)
+		return (0);
+	if (V_ip4_ipsec_dfbit == 1)
+		goto setdf;
+
+	/* V_ip4_ipsec_dfbit > 1 - we will copy it from inner header. */
+	ip = mtod(m, struct ip *);
+	if (!(ip->ip_off & htons(IP_DF)))
+		return (0);
+
+setdf:
+	idx = sp->tcount - 1;
+	sav = ipsec4_allocsa(m, sp, &idx, &error);
+	if (sav == NULL) {
+		key_freesp(&sp);
+		if (error != EJUSTRETURN)
+			m_freem(m);
+
+		return (error);
+	}
+
+	dst = &sav->sah->saidx.dst;
+
+	/* Final header is not ipv4. */
+	if (dst->sa.sa_family != AF_INET) {
+		key_freesav(&sav);
+		return (0);
+	}
+
+	memset(&inc, 0, sizeof(inc));
+	inc.inc_faddr = satosin(&dst->sa)->sin_addr;
+	key_freesav(&sav);
+	pmtu = tcp_hc_getmtu(&inc);
+	/* No entry in hostcache. */
+	if (pmtu == 0)
+		return (0);
+
+	hlen = ipsec_hdrsiz_internal(sp);
+	if (m_length(m, NULL) + hlen > pmtu) {
+		/*
+		 * If we're forwarding generate ICMP message here,
+		 * so that it contains pmtu and not link mtu.
+		 * Set error to EINPROGRESS, in order for the frame
+		 * to be dropped silently.
+		 */
+		if (forwarding) {
+			if (pmtu > hlen)
+				icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
+				    0, pmtu - hlen);
+			else
+				m_freem(m);
+
+			key_freesp(&sp);
+			return (EINPROGRESS); /* Pretend that we consumed it. */
+		} else {
+			m_freem(m);
+			key_freesp(&sp);
+			return (EMSGSIZE);
+		}
+	}
+
+	return (0);
+}
+
 static int
 ipsec4_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
 {
@@ -349,6 +428,14 @@ ipsec4_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
 #endif
 	}
 	/* NB: callee frees mbuf and releases reference to SP */
+	error = ipsec4_check_pmtu(m, sp, forwarding);
+	if (error != 0) {
+		if (error == EJUSTRETURN)
+			return (0);
+
+		return (error);
+	}
+
 	error = ipsec4_process_packet(m, sp, inp);
 	if (error == EJUSTRETURN) {
 		/*


More information about the dev-commits-src-main mailing list