aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartlomiej Grzesik <bag@semihalf.com>2021-09-24 08:27:21 +0000
committerWojciech Macek <wma@FreeBSD.org>2021-09-24 08:27:21 +0000
commit9dfc8606eb80a49a0b071ff17dbbfa307d7248be (patch)
treebba2db8d5707a62945368570faee8b39121c7e3b
parentb4220bf387e62f59d73308f122f5eea887a59d58 (diff)
downloadsrc-9dfc8606eb80a49a0b071ff17dbbfa307d7248be.tar.gz
src-9dfc8606eb80a49a0b071ff17dbbfa307d7248be.zip
ipsec: Add support for PMTUD for IPv6 tunnels
Discard and send ICMPv6 Packet Too Big to sender when we try to encapsulate and forward a packet which total length exceeds the PMTU. Logic is based on the IPv4 implementation. Common code was moved to a separate function. Differential revision: https://reviews.freebsd.org/D31771 Obtained from: Semihalf Sponsored by: Stormshield
-rw-r--r--sys/netipsec/ipsec6.h1
-rw-r--r--sys/netipsec/ipsec_output.c166
2 files changed, 133 insertions, 34 deletions
diff --git a/sys/netipsec/ipsec6.h b/sys/netipsec/ipsec6.h
index aaba405ce0db..f690102fae2b 100644
--- a/sys/netipsec/ipsec6.h
+++ b/sys/netipsec/ipsec6.h
@@ -74,6 +74,7 @@ int ipsec6_output(struct mbuf *, struct inpcb *);
int ipsec6_capability(struct mbuf *, u_int);
int ipsec6_common_input_cb(struct mbuf *, struct secasvar *, int, int);
int ipsec6_ctlinput(int, struct sockaddr *, void *);
+int ipsec6_check_pmtu(struct mbuf *, struct secpolicy *, int);
int ipsec6_process_packet(struct mbuf *, struct secpolicy *, struct inpcb *);
int ip6_ipsec_filtertunnel(struct mbuf *);
diff --git a/sys/netipsec/ipsec_output.c b/sys/netipsec/ipsec_output.c
index c4e34665b8f5..9beb5c47444f 100644
--- a/sys/netipsec/ipsec_output.c
+++ b/sys/netipsec/ipsec_output.c
@@ -106,6 +106,7 @@
} while (0)
static int ipsec_encap(struct mbuf **mp, struct secasindex *saidx);
+static size_t ipsec_get_pmtu(struct secasvar *sav);
#ifdef INET
static struct secasvar *
@@ -297,8 +298,6 @@ ipsec4_process_packet(struct mbuf *m, struct secpolicy *sp,
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;
@@ -333,44 +332,15 @@ setdf:
return (error);
}
- dst = &sav->sah->saidx.dst;
- memset(&inc, 0, sizeof(inc));
- switch (dst->sa.sa_family) {
- case AF_INET:
- inc.inc_faddr = satosin(&dst->sa)->sin_addr;
- break;
-#ifdef INET6
- case AF_INET6:
- inc.inc6_faddr = satosin6(&dst->sa)->sin6_addr;
- inc.inc_flags |= INC_ISIPV6;
- break;
-#endif
- default:
+ pmtu = ipsec_get_pmtu(sav);
+ if (pmtu == 0) {
key_freesav(&sav);
return (0);
}
+ hlen = ipsec_hdrsiz_internal(sp);
key_freesav(&sav);
- pmtu = tcp_hc_getmtu(&inc);
- /* No entry in hostcache. Use link MTU instead. */
- if (pmtu == 0) {
- switch (dst->sa.sa_family) {
- case AF_INET:
- pmtu = tcp_maxmtu(&inc, NULL);
- break;
-#ifdef INET6
- case AF_INET6:
- pmtu = tcp_maxmtu6(&inc, NULL);
- break;
-#endif
- }
- if (pmtu == 0)
- return (0);
-
- tcp_hc_updatemtu(&inc, pmtu);
- }
- hlen = ipsec_hdrsiz_internal(sp);
if (m_length(m, NULL) + hlen > pmtu) {
/*
* If we're forwarding generate ICMP message here,
@@ -720,6 +690,72 @@ ipsec6_process_packet(struct mbuf *m, struct secpolicy *sp,
return (ipsec6_perform_request(m, sp, inp, 0));
}
+/*
+ * IPv6 implementation is based on IPv4 implementation.
+ */
+int
+ipsec6_check_pmtu(struct mbuf *m, struct secpolicy *sp, int forwarding)
+{
+ struct secasvar *sav;
+ size_t hlen, pmtu;
+ uint32_t idx;
+ int error;
+
+ /*
+ * According to RFC8200 L3 fragmentation is supposed to be done only on
+ * locally generated packets. During L3 forwarding packets that are too
+ * big are always supposed to be dropped, with an ICMPv6 packet being
+ * sent back.
+ */
+ if (!forwarding)
+ return (0);
+
+ idx = sp->tcount - 1;
+ sav = ipsec6_allocsa(m, sp, &idx, &error);
+ if (sav == NULL) {
+ key_freesp(&sp);
+ /*
+ * No matching SA was found and SADB_ACQUIRE message was generated.
+ * Since we have matched a SP to this packet drop it silently.
+ */
+ if (error == 0)
+ error = EINPROGRESS;
+ if (error != EJUSTRETURN)
+ m_freem(m);
+
+ return (error);
+ }
+
+ pmtu = ipsec_get_pmtu(sav);
+ if (pmtu == 0) {
+ key_freesav(&sav);
+ return (0);
+ }
+
+ hlen = ipsec_hdrsiz_internal(sp);
+ key_freesav(&sav);
+
+ if (m_length(m, NULL) + hlen > pmtu) {
+ /*
+ * If we're forwarding generate ICMPv6 message here,
+ * so that it contains pmtu substracted by header size.
+ * Set error to EINPROGRESS, in order for the frame
+ * to be dropped silently.
+ */
+ if (forwarding) {
+ if (pmtu > hlen)
+ icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, pmtu - hlen);
+ else
+ m_freem(m);
+
+ key_freesp(&sp);
+ return (EINPROGRESS); /* Pretend that we consumed it. */
+ }
+ }
+
+ return (0);
+}
+
static int
ipsec6_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
{
@@ -766,6 +802,15 @@ ipsec6_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
}
#endif
}
+
+ error = ipsec6_check_pmtu(m, sp, forwarding);
+ if (error != 0) {
+ if (error == EJUSTRETURN)
+ return (0);
+
+ return (error);
+ }
+
/* NB: callee frees mbuf and releases reference to SP */
error = ipsec6_process_packet(m, sp, inp);
if (error == EJUSTRETURN) {
@@ -1001,6 +1046,59 @@ ipsec_prepend(struct mbuf *m, int len, int how)
return (n);
}
+static size_t
+ipsec_get_pmtu(struct secasvar *sav)
+{
+ union sockaddr_union *dst;
+ struct in_conninfo inc;
+ size_t pmtu;
+
+ dst = &sav->sah->saidx.dst;
+ memset(&inc, 0, sizeof(inc));
+
+ switch (dst->sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ inc.inc_faddr = satosin(&dst->sa)->sin_addr;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ inc.inc6_faddr = satosin6(&dst->sa)->sin6_addr;
+ inc.inc_flags |= INC_ISIPV6;
+ break;
+#endif
+ default:
+ return (0);
+ }
+
+ pmtu = tcp_hc_getmtu(&inc);
+ if (pmtu != 0)
+ return (pmtu);
+
+ /* No entry in hostcache. Assume that PMTU is equal to link's MTU */
+ switch (dst->sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ pmtu = tcp_maxmtu(&inc, NULL);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ pmtu = tcp_maxmtu6(&inc, NULL);
+ break;
+#endif
+ default:
+ return (0);
+ }
+ if (pmtu == 0)
+ return (0);
+
+ tcp_hc_updatemtu(&inc, pmtu);
+
+ return (pmtu);
+}
+
static int
ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
{