Copy code from ip_forward() to ip6_forward() to fix Path MTU discovery
authorbluhm <bluhm@openbsd.org>
Mon, 22 Nov 2021 13:47:10 +0000 (13:47 +0000)
committerbluhm <bluhm@openbsd.org>
Mon, 22 Nov 2021 13:47:10 +0000 (13:47 +0000)
in IPsec IPv6 tunnel.  Implement sending ICMP6 packet too big
messages.  Also implement the pf error case in ip6_forward().  While
there, do some cleanup and make the IPv4 and IPv6 code look similar.
OK tobhe@

sys/netinet/ip_input.c
sys/netinet6/ip6_forward.c

index 3915225..9e7e20a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_input.c,v 1.363 2021/06/21 22:09:14 jca Exp $      */
+/*     $OpenBSD: ip_input.c,v 1.364 2021/11/22 13:47:10 bluhm Exp $    */
 /*     $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $   */
 
 /*
@@ -1436,7 +1436,7 @@ ip_forward(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt, int srcrt)
        struct ip *ip = mtod(m, struct ip *);
        struct sockaddr_in *sin;
        struct route ro;
-       int error, type = 0, code = 0, destmtu = 0, fake = 0, len;
+       int error = 0, type = 0, code = 0, destmtu = 0, fake = 0, len;
        u_int32_t dest;
 
        dest = 0;
@@ -1535,29 +1535,17 @@ ip_forward(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt, int srcrt)
                goto freecopy;
 
        switch (error) {
-
        case 0:                         /* forwarded, but need redirect */
                /* type, code set above */
                break;
 
-       case ENETUNREACH:               /* shouldn't happen, checked above */
-       case EHOSTUNREACH:
-       case ENETDOWN:
-       case EHOSTDOWN:
-       default:
-               type = ICMP_UNREACH;
-               code = ICMP_UNREACH_HOST;
-               break;
-
        case EMSGSIZE:
                type = ICMP_UNREACH;
                code = ICMP_UNREACH_NEEDFRAG;
-
-#ifdef IPSEC
                if (rt != NULL) {
-                       if (rt->rt_mtu)
+                       if (rt->rt_mtu) {
                                destmtu = rt->rt_mtu;
-                       else {
+                       else {
                                struct ifnet *destifp;
 
                                destifp = if_get(rt->rt_ifidx);
@@ -1566,8 +1554,9 @@ ip_forward(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt, int srcrt)
                                if_put(destifp);
                        }
                }
-#endif /*IPSEC*/
                ipstat_inc(ips_cantfrag);
+               if (destmtu == 0)
+                       goto freecopy;
                break;
 
        case EACCES:
@@ -1576,6 +1565,7 @@ ip_forward(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt, int srcrt)
                 * packet back since pf(4) takes care of it.
                 */
                goto freecopy;
+
        case ENOBUFS:
                /*
                 * a router should not generate ICMP_SOURCEQUENCH as
@@ -1584,8 +1574,16 @@ ip_forward(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt, int srcrt)
                 * or the underlying interface is rate-limited.
                 */
                goto freecopy;
-       }
 
+       case ENETUNREACH:               /* shouldn't happen, checked above */
+       case EHOSTUNREACH:
+       case ENETDOWN:
+       case EHOSTDOWN:
+       default:
+               type = ICMP_UNREACH;
+               code = ICMP_UNREACH_HOST;
+               break;
+       }
        mcopy = m_copym(&mfake, 0, len, M_DONTWAIT);
        if (mcopy)
                icmp_error(mcopy, type, code, dest, destmtu);
index 12b74cb..ba9c25e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip6_forward.c,v 1.101 2021/10/14 17:39:42 bluhm Exp $ */
+/*     $OpenBSD: ip6_forward.c,v 1.102 2021/11/22 13:47:10 bluhm Exp $ */
 /*     $KAME: ip6_forward.c,v 1.75 2001/06/29 12:42:13 jinmei Exp $    */
 
 /*
@@ -88,7 +88,7 @@ ip6_forward(struct mbuf *m, struct rtentry *rt, int srcrt)
        struct sockaddr_in6 *sin6;
        struct route_in6 ro;
        struct ifnet *ifp = NULL;
-       int error = 0, type = 0, code = 0;
+       int error = 0, type = 0, code = 0, destmtu = 0;
        struct mbuf *mcopy = NULL;
 #ifdef IPSEC
        struct tdb *tdb = NULL;
@@ -340,6 +340,7 @@ senderr:
 #endif
        if (mcopy == NULL)
                goto out;
+
        switch (error) {
        case 0:
                if (type == ND_REDIRECT) {
@@ -349,7 +350,30 @@ senderr:
                goto freecopy;
 
        case EMSGSIZE:
-               /* xxx MTU is constant in PPP? */
+               type = ICMP6_PACKET_TOO_BIG;
+               code = 0;
+               if (rt != NULL) {
+                       if (rt->rt_mtu) {
+                               destmtu = rt->rt_mtu;
+                       } else {
+                               struct ifnet *destifp;
+
+                               destifp = if_get(rt->rt_ifidx);
+                               if (destifp != NULL)
+                                       destmtu = destifp->if_mtu;
+                               if_put(destifp);
+                       }
+               }
+               ip6stat_inc(ip6s_cantfrag);
+               if (destmtu == 0)
+                       goto freecopy;
+               break;
+
+       case EACCES:
+               /*
+                * pf(4) blocked the packet. There is no need to send an ICMP
+                * packet back since pf(4) takes care of it.
+                */
                goto freecopy;
 
        case ENOBUFS:
@@ -365,7 +389,7 @@ senderr:
                code = ICMP6_DST_UNREACH_ADDR;
                break;
        }
-       icmp6_error(mcopy, type, code, 0);
+       icmp6_error(mcopy, type, code, destmtu);
        goto out;
 
 freecopy: