Hi,

This part of the IPv6 IPsec path MTU discovery is for the case where
the router is between the tunnel endpoints.  Basically it handles
ICMP6 packets for ESP.  Originally this diff came from markus@.

ok?

bluhm

Index: netinet/ip_ipsp.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_ipsp.h,v
retrieving revision 1.196
diff -u -p -r1.196 ip_ipsp.h
--- netinet/ip_ipsp.h   5 Nov 2020 19:28:28 -0000       1.196
+++ netinet/ip_ipsp.h   20 Jan 2021 16:47:58 -0000
@@ -610,6 +610,7 @@ void        esp4_ctlinput(int, struct sockaddr 
 
 #ifdef INET6
 int    esp6_input(struct mbuf **, int *, int, int);
+void   esp6_ctlinput(int, struct sockaddr *, u_int, void *);
 #endif /* INET6 */
 
 /* XF_IPCOMP */
Index: netinet/ipsec_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ipsec_input.c,v
retrieving revision 1.173
diff -u -p -r1.173 ipsec_input.c
--- netinet/ipsec_input.c       1 Sep 2020 01:53:34 -0000       1.173
+++ netinet/ipsec_input.c       20 Jan 2021 18:04:00 -0000
@@ -66,6 +66,7 @@
 #ifdef INET6
 #include <netinet6/in6_var.h>
 #include <netinet/ip6.h>
+#include <netinet/icmp6.h>
 #include <netinet6/ip6_var.h>
 #include <netinet6/ip6protosw.h>
 #endif /* INET6 */
@@ -82,8 +83,6 @@
 
 #include "bpfilter.h"
 
-void ipsec_common_ctlinput(u_int, int, struct sockaddr *, void *, int);
-
 #ifdef ENCDEBUG
 #define DPRINTF(x)     if (encdebug) printf x
 #else
@@ -148,6 +147,9 @@ int esp_sysctl_espstat(void *, size_t *,
 int ah_sysctl_ahstat(void *, size_t *, void *);
 int ipcomp_sysctl_ipcompstat(void *, size_t *, void *);
 int ipsec_sysctl_ipsecstat(void *, size_t *, void *);
+void ipsec_set_mtu(struct tdb *, u_int32_t, const char *);
+void ipsec_common_ctlinput(u_int, int, struct sockaddr *, void *, int);
+void ipsec6_common_ctlinput(u_int, int, struct sockaddr *, void *, int);
 
 void
 ipsec_init(void)
@@ -946,18 +948,42 @@ ipcomp4_input(struct mbuf **mp, int *off
 }
 
 void
+ipsec_set_mtu(struct tdb *tdbp, u_int32_t mtu, const char *msg)
+{
+       ssize_t adjust;
+       uint64_t timeout;
+
+       timeout = gettime() + ip_mtudisc_timeout;
+       /* Walk the chain backwards to the first tdb */
+       NET_ASSERT_LOCKED();
+       for (; tdbp; tdbp = tdbp->tdb_inext) {
+               if (tdbp->tdb_flags & TDBF_INVALID ||
+                   (adjust = ipsec_hdrsz(tdbp)) == -1)
+                       return;
+
+               mtu -= adjust;
+
+               /* Store adjusted MTU in tdb */
+               tdbp->tdb_mtu = mtu;
+               tdbp->tdb_mtutimeout = timeout;
+               DPRINTF(("%s: %s: spi %08x mtu %d adjust %ld timeout %llu\n",
+                   __func__, msg, ntohl(tdbp->tdb_spi), tdbp->tdb_mtu, adjust,
+                   timeout));
+       }
+}
+
+void
 ipsec_common_ctlinput(u_int rdomain, int cmd, struct sockaddr *sa,
     void *v, int proto)
 {
        struct ip *ip = v;
 
-       if (cmd == PRC_MSGSIZE && ip && ip_mtudisc && ip->ip_v == 4) {
+       if (cmd == PRC_MSGSIZE && ip_mtudisc && ip && ip->ip_v == 4) {
                struct tdb *tdbp;
                struct sockaddr_in dst;
                struct icmp *icp;
                int hlen = ip->ip_hl << 2;
                u_int32_t spi, mtu;
-               ssize_t adjust;
 
                /* Find the right MTU. */
                icp = (struct icmp *)((caddr_t) ip -
@@ -971,7 +997,7 @@ ipsec_common_ctlinput(u_int rdomain, int
                if (mtu < 296)
                        return;
 
-               memset(&dst, 0, sizeof(struct sockaddr_in));
+               memset(&dst, 0, sizeof(dst));
                dst.sin_family = AF_INET;
                dst.sin_len = sizeof(struct sockaddr_in);
                dst.sin_addr.s_addr = ip->ip_dst.s_addr;
@@ -980,28 +1006,72 @@ ipsec_common_ctlinput(u_int rdomain, int
 
                tdbp = gettdb_rev(rdomain, spi, (union sockaddr_union *)&dst,
                    proto);
-               if (tdbp == NULL || tdbp->tdb_flags & TDBF_INVALID)
-                       return;
+               if (tdbp != NULL && (tdbp->tdb_flags & TDBF_INVALID) == 0)
+                       ipsec_set_mtu(tdbp, mtu, __func__);
+       }
+}
 
-               /* Walk the chain backwards to the first tdb */
-               NET_ASSERT_LOCKED();
-               for (; tdbp; tdbp = tdbp->tdb_inext) {
-                       if (tdbp->tdb_flags & TDBF_INVALID ||
-                           (adjust = ipsec_hdrsz(tdbp)) == -1)
-                               return;
+#ifdef INET6
+void
+ipsec6_common_ctlinput(u_int rdomain, int cmd, struct sockaddr *sa,
+    void *v, int proto)
+{
+       struct ip6ctlparam *ip6cp = v;
+
+       if (cmd == PRC_MSGSIZE && ip_mtudisc && ip6cp && ip6cp->ip6c_icmp6) {
+               struct tdb *tdbp;
+               struct sockaddr_in6 dst;
+               struct icmp6_hdr *icmp6;
+               struct mbuf *m;
+               u_int32_t spi, mtu;
+               int off;
+
+               /* Find the right MTU. */
+               icmp6 = ip6cp->ip6c_icmp6;
+               mtu = ntohl(icmp6->icmp6_mtu);
 
-                       mtu -= adjust;
+               /*
+                * Ignore the packet, if we do not receive a MTU
+                * or the MTU is too small to be acceptable.
+                */
+               if (mtu < IPV6_MMTU)
+                       return;
+               m = ip6cp->ip6c_m;
+               off = ip6cp->ip6c_off;
+               if (m->m_pkthdr.len < off + sizeof(spi))
+                       return;
 
-                       /* Store adjusted MTU in tdb */
-                       tdbp->tdb_mtu = mtu;
-                       tdbp->tdb_mtutimeout = gettime() +
-                           ip_mtudisc_timeout;
-                       DPRINTF(("%s: spi %08x mtu %d adjust %ld\n", __func__,
-                           ntohl(tdbp->tdb_spi), tdbp->tdb_mtu,
-                           adjust));
+               m_copydata(m, off, sizeof(spi), (caddr_t)&spi);
+
+               if (ip6cp->ip6c_finaldst) {
+                       memset(&dst, 0, sizeof(dst));
+                       dst.sin6_family = AF_INET6;
+                       dst.sin6_len = sizeof(dst);
+                       dst.sin6_addr = *ip6cp->ip6c_finaldst;
+                       /* XXX: assuming M is valid in this case */
+                       dst.sin6_scope_id =
+                           in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
+                           ip6cp->ip6c_finaldst);
+                       if (in6_embedscope(ip6cp->ip6c_finaldst, &dst, NULL)) {
+                               /* should be impossible */
+                               return;
+                       }
+               } else {
+                       /* XXX: translate addresses into internal form */
+                       dst = *satosin6(sa);
+                       if (in6_embedscope(&dst.sin6_addr, &dst, NULL)) {
+                               /* should be impossible */
+                               return;
+                       }
                }
+
+               tdbp = gettdb_rev(rdomain, spi, (union sockaddr_union *)&dst,
+                   proto);
+               if (tdbp != NULL && (tdbp->tdb_flags & TDBF_INVALID) == 0)
+                       ipsec_set_mtu(tdbp, mtu, __func__);
        }
 }
+#endif
 
 void
 udpencap_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
@@ -1010,7 +1080,6 @@ udpencap_ctlinput(int cmd, struct sockad
        struct tdb *tdbp;
        struct icmp *icp;
        u_int32_t mtu;
-       ssize_t adjust;
        struct sockaddr_in dst, src;
        union sockaddr_union *su_dst, *su_src;
 
@@ -1046,16 +1115,7 @@ udpencap_ctlinput(int cmd, struct sockad
                    TDBF_UDPENCAP) &&
                    !memcmp(&tdbp->tdb_dst, &dst, su_dst->sa.sa_len) &&
                    !memcmp(&tdbp->tdb_src, &src, su_src->sa.sa_len)) {
-                       if ((adjust = ipsec_hdrsz(tdbp)) != -1) {
-                               /* Store adjusted MTU in tdb */
-                               tdbp->tdb_mtu = mtu - adjust;
-                               tdbp->tdb_mtutimeout = gettime() +
-                                   ip_mtudisc_timeout;
-                               DPRINTF(("%s: spi %08x mtu %d adjust %ld\n",
-                                   __func__,
-                                   ntohl(tdbp->tdb_spi), tdbp->tdb_mtu,
-                                   adjust));
-                       }
+                       ipsec_set_mtu(tdbp, mtu, __func__);
                }
        }
 }
@@ -1069,6 +1129,18 @@ esp4_ctlinput(int cmd, struct sockaddr *
 
        ipsec_common_ctlinput(rdomain, cmd, sa, v, IPPROTO_ESP);
 }
+
+#ifdef INET6
+void
+esp6_ctlinput(int cmd, struct sockaddr *sa, u_int rdomain, void *v)
+{
+       if (sa->sa_family != AF_INET6 ||
+           sa->sa_len != sizeof(struct sockaddr_in6))
+               return;
+
+       ipsec6_common_ctlinput(rdomain, cmd, sa, v, IPPROTO_ESP);
+}
+#endif
 
 #ifdef INET6
 /* IPv6 AH wrapper. */
Index: netinet6/in6_proto.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6_proto.c,v
retrieving revision 1.104
diff -u -p -r1.104 in6_proto.c
--- netinet6/in6_proto.c        13 Jun 2019 08:12:11 -0000      1.104
+++ netinet6/in6_proto.c        20 Jan 2021 16:47:58 -0000
@@ -227,6 +227,7 @@ const struct protosw inet6sw[] = {
   .pr_protocol = IPPROTO_ESP,
   .pr_flags    = PR_ATOMIC|PR_ADDR,
   .pr_input    = esp6_input,
+  .pr_ctlinput = esp6_ctlinput,
   .pr_ctloutput        = rip6_ctloutput,
   .pr_usrreq   = rip6_usrreq,
   .pr_attach   = rip6_attach,

Reply via email to