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: