Hi,

Path MTU discovery for IPv6 in IPv6 tunnel over IPsec does not work.
Sending ICMP6 too big is not implemented.  Copying the code from
ip_forward() fixes it.

While there, do some cleanup.
- #ifdef IPSEC makes no sense.  While IPsec needs this code, only
  the route and interface are used and all protocols are affected.
- Sort the cases in v4 and v6 the same way.
- Initialize the error variable in both functions.  Is is not
  needed in ip_forward() but the gotos don't make it obvious.  The
  gotos in ip6_forward() are differnet and error = 0 is neccessary.
- Do not send an icmp packet if destmtu could not be set.
- The pf(4) EACCES case also makes sense for IPv4.

ok?

bluhm

Index: netinet/ip_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.363
diff -u -p -r1.363 ip_input.c
--- netinet/ip_input.c  21 Jun 2021 22:09:14 -0000      1.363
+++ netinet/ip_input.c  21 Nov 2021 22:04:20 -0000
@@ -1436,7 +1436,7 @@ ip_forward(struct mbuf *m, struct ifnet 
        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 
                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 
                                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 
                 * 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 
                 * 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: netinet6/ip6_forward.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_forward.c,v
retrieving revision 1.101
diff -u -p -r1.101 ip6_forward.c
--- netinet6/ip6_forward.c      14 Oct 2021 17:39:42 -0000      1.101
+++ netinet6/ip6_forward.c      21 Nov 2021 22:04:22 -0000
@@ -88,7 +88,7 @@ ip6_forward(struct mbuf *m, struct rtent
        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:

Reply via email to