On Thu, Dec 24, 2020 at 10:54:59PM +0100, Alexander Bluhm wrote:
> It also makes v4 and v6 code look simmilar.  If you want, I can
> split this for easier review.

This is the part of the diff that creates a path MTU host route for
IPv6.  Basically the code is copied from IPv4 and adapted.  Some
things are changed in v4 to make it look similar.

- ip6_forward increases the noroute error counter, I think that
  should be done in ip_forward, too.
- Pass more specific sockaddr_in6 to icmp6_mtudisc_clone().
  Or should we use a sockaddr for both v4 and v6?
- IPv6 may also use reject routes for PMTU clones.
- To pass a route_in6 to ip6_output_ipsec_send() introduce one
  in ip6_forward().  That is the same what IPv4 does.  Note
  that dst and sin6 switch roles.
- Copy comments from ip_output_ipsec_send() to ip6_output_ipsec_send()
  to make code similar.
- Implement dynamic IPv6 IPsec PMTU routes.

ok?

bluhm

Index: netinet/icmp6.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/icmp6.h,v
retrieving revision 1.50
diff -u -p -r1.50 icmp6.h
--- netinet/icmp6.h     28 Oct 2020 17:27:35 -0000      1.50
+++ netinet/icmp6.h     27 Dec 2020 15:16:56 -0000
@@ -599,6 +599,7 @@ void                 icmp6_prepare(struct mbuf *);
 void            icmp6_redirect_input(struct mbuf *, int);
 void            icmp6_redirect_output(struct mbuf *, struct rtentry *);
 int             icmp6_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+struct rtentry *icmp6_mtudisc_clone(struct sockaddr_in6 *, u_int, int);
 
 struct ip6ctlparam;
 void   icmp6_mtudisc_update(struct ip6ctlparam *, int);
Index: netinet/ip_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.352
diff -u -p -r1.352 ip_input.c
--- netinet/ip_input.c  16 Nov 2020 06:44:38 -0000      1.352
+++ netinet/ip_input.c  27 Dec 2020 15:16:56 -0000
@@ -1418,8 +1418,8 @@ ip_forward(struct mbuf *m, struct ifnet 
                goto freecopy;
        }
 
+       memset(&ro, 0, sizeof(ro));
        sin = satosin(&ro.ro_dst);
-       memset(sin, 0, sizeof(*sin));
        sin->sin_family = AF_INET;
        sin->sin_len = sizeof(*sin);
        sin->sin_addr = ip->ip_dst;
@@ -1429,6 +1429,7 @@ ip_forward(struct mbuf *m, struct ifnet 
                rt = rtalloc_mpath(sintosa(sin), &ip->ip_src.s_addr,
                    m->m_pkthdr.ph_rtableid);
                if (rt == NULL) {
+                       ipstat_inc(ips_noroute);
                        icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, dest, 0);
                        return;
                }
Index: netinet/ip_output.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.358
diff -u -p -r1.358 ip_output.c
--- netinet/ip_output.c 20 Dec 2020 21:15:47 -0000      1.358
+++ netinet/ip_output.c 27 Dec 2020 15:16:56 -0000
@@ -612,7 +612,7 @@ ip_output_ipsec_send(struct tdb *tdb, st
                    ntohl(tdb->tdb_spi), tdb->tdb_mtu, rt, rt_mtucloned));
                if (rt != NULL) {
                        rt->rt_mtu = tdb->tdb_mtu;
-                       if (ro && ro->ro_rt != NULL) {
+                       if (ro != NULL && ro->ro_rt != NULL) {
                                rtfree(ro->ro_rt);
                                ro->ro_rt = rtalloc(&ro->ro_dst, RT_RESOLVE,
                                    m->m_pkthdr.ph_rtableid);
Index: netinet6/icmp6.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/icmp6.c,v
retrieving revision 1.233
diff -u -p -r1.233 icmp6.c
--- netinet6/icmp6.c    28 Oct 2020 17:27:35 -0000      1.233
+++ netinet6/icmp6.c    27 Dec 2020 15:16:56 -0000
@@ -138,7 +138,6 @@ int icmp6_ratelimit(const struct in6_add
 const char *icmp6_redirect_diag(struct in6_addr *, struct in6_addr *,
            struct in6_addr *);
 int    icmp6_notify_error(struct mbuf *, int, int, int);
-struct rtentry *icmp6_mtudisc_clone(struct sockaddr *, u_int);
 void   icmp6_mtudisc_timeout(struct rtentry *, struct rttimer *);
 void   icmp6_redirect_timeout(struct rtentry *, struct rttimer *);
 
@@ -1015,7 +1014,7 @@ icmp6_mtudisc_update(struct ip6ctlparam 
        sin6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
            &sin6.sin6_addr);
 
-       rt = icmp6_mtudisc_clone(sin6tosa(&sin6), m->m_pkthdr.ph_rtableid);
+       rt = icmp6_mtudisc_clone(&sin6, m->m_pkthdr.ph_rtableid, 0);
 
        if (rt != NULL && ISSET(rt->rt_flags, RTF_HOST) &&
            !(rt->rt_locks & RTV_MTU) &&
@@ -1784,15 +1783,18 @@ icmp6_ratelimit(const struct in6_addr *d
 }
 
 struct rtentry *
-icmp6_mtudisc_clone(struct sockaddr *dst, u_int rtableid)
+icmp6_mtudisc_clone(struct sockaddr_in6 *dst, u_int rtableid, int ipsec)
 {
        struct rtentry *rt;
        int    error;
 
-       rt = rtalloc(dst, RT_RESOLVE, rtableid);
+       rt = rtalloc(sin6tosa(dst), RT_RESOLVE, rtableid);
 
        /* Check if the route is actually usable */
-       if (!rtisvalid(rt) || (rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)))
+       if (!rtisvalid(rt))
+               goto bad;
+       /* IPsec needs the route only for PMTU, it can use reject for that */
+       if (!ipsec && (rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)))
                goto bad;
 
        /*
@@ -1812,7 +1814,7 @@ icmp6_mtudisc_clone(struct sockaddr *dst
                memset(&info, 0, sizeof(info));
                info.rti_ifa = rt->rt_ifa;
                info.rti_flags = RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC;
-               info.rti_info[RTAX_DST] = dst;
+               info.rti_info[RTAX_DST] = sin6tosa(dst);
                info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
                info.rti_info[RTAX_LABEL] =
                    rtlabel_id2sa(rt->rt_labelid, &sa_rl);
Index: netinet6/ip6_forward.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_forward.c,v
retrieving revision 1.99
diff -u -p -r1.99 ip6_forward.c
--- netinet6/ip6_forward.c      26 Nov 2020 18:55:12 -0000      1.99
+++ netinet6/ip6_forward.c      27 Dec 2020 15:16:56 -0000
@@ -85,7 +85,8 @@ void
 ip6_forward(struct mbuf *m, struct rtentry *rt, int srcrt)
 {
        struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
-       struct sockaddr_in6 *dst, sin6;
+       struct sockaddr_in6 *sin6;
+       struct route_in6 ro;
        struct ifnet *ifp = NULL;
        int error = 0, type = 0, code = 0;
        struct mbuf *mcopy = NULL;
@@ -160,15 +161,15 @@ reroute:
        }
 #endif /* IPSEC */
 
-       dst = &sin6;
-       memset(dst, 0, sizeof(*dst));
-       dst->sin6_len = sizeof(struct sockaddr_in6);
-       dst->sin6_family = AF_INET6;
-       dst->sin6_addr = ip6->ip6_dst;
+       memset(&ro, 0, sizeof(ro));
+       sin6 = &ro.ro_dst;
+       sin6->sin6_family = AF_INET6;
+       sin6->sin6_len = sizeof(*sin6);
+       sin6->sin6_addr = ip6->ip6_dst;
 
        if (!rtisvalid(rt)) {
                rtfree(rt);
-               rt = rtalloc_mpath(sin6tosa(dst), &ip6->ip6_src.s6_addr32[0],
+               rt = rtalloc_mpath(sin6tosa(sin6), &ip6->ip6_src.s6_addr32[0],
                    m->m_pkthdr.ph_rtableid);
                if (rt == NULL) {
                        ip6stat_inc(ip6s_noroute);
@@ -215,12 +216,12 @@ reroute:
        /*
         * Check if the packet needs encapsulation.
         * ipsp_process_packet will never come back to here.
-        * XXX ipsp_process_packet() calls ip6_output(), and there'll be no
-        * PMTU notification.  is it okay?
         */
        if (tdb != NULL) {
                /* Callee frees mbuf */
-               error = ip6_output_ipsec_send(tdb, m, 0, 1);
+               ro.ro_rt = rt;
+               ro.ro_tableid = m->m_pkthdr.ph_rtableid;
+               error = ip6_output_ipsec_send(tdb, m, &ro, 0, 1);
                if (error)
                        goto senderr;
                goto freecopy;
@@ -228,7 +229,7 @@ reroute:
 #endif /* IPSEC */
 
        if (rt->rt_flags & RTF_GATEWAY)
-               dst = satosin6(rt->rt_gateway);
+               sin6 = satosin6(rt->rt_gateway);
 
        /*
         * If we are to forward the packet using the same interface
@@ -248,7 +249,7 @@ reroute:
            ip6_sendredirects &&
            (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) {
                if ((ifp->if_flags & IFF_POINTOPOINT) &&
-                   nd6_is_addr_neighbor(&sin6, ifp)) {
+                   nd6_is_addr_neighbor(&ro.ro_dst, ifp)) {
                        /*
                         * If the incoming interface is equal to the outgoing
                         * one, the link attached to the interface is
@@ -320,7 +321,7 @@ reroute:
                goto out;
        }
 
-       error = ifp->if_output(ifp, m, sin6tosa(dst), rt);
+       error = ifp->if_output(ifp, m, sin6tosa(sin6), rt);
        if (error) {
                ip6stat_inc(ip6s_cantforward);
        } else {
Index: netinet6/ip6_output.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_output.c,v
retrieving revision 1.248
diff -u -p -r1.248 ip6_output.c
--- netinet6/ip6_output.c       22 Dec 2020 13:37:48 -0000      1.248
+++ netinet6/ip6_output.c       27 Dec 2020 15:16:56 -0000
@@ -106,6 +106,12 @@
 #include <netinet/ip_ipsp.h>
 #include <netinet/ip_ah.h>
 #include <netinet/ip_esp.h>
+
+#ifdef ENCDEBUG
+#define DPRINTF(x)    do { if (encdebug) printf x ; } while (0)
+#else
+#define DPRINTF(x)
+#endif
 #endif /* IPSEC */
 
 struct ip6_exthdrs {
@@ -426,7 +432,7 @@ reroute:
                 * packet just because ip6_dst is different from what tdb has.
                 * XXX
                 */
-               error = ip6_output_ipsec_send(tdb, m,
+               error = ip6_output_ipsec_send(tdb, m, ro,
                    exthdrs.ip6e_rthdr ? 1 : 0, 0);
                goto done;
        }
@@ -2762,14 +2768,19 @@ ip6_output_ipsec_lookup(struct mbuf *m, 
 }
 
 int
-ip6_output_ipsec_send(struct tdb *tdb, struct mbuf *m, int tunalready, int fwd)
+ip6_output_ipsec_send(struct tdb *tdb, struct mbuf *m, struct route_in6 *ro,
+    int tunalready, int fwd)
 {
 #if NPF > 0
        struct ifnet *encif;
 #endif
+       struct ip6_hdr *ip6;
        int error;
 
 #if NPF > 0
+       /*
+        * Packet filter
+        */
        if ((encif = enc_getif(tdb->tdb_rdomain, tdb->tdb_tap)) == NULL ||
            pf_test(AF_INET6, fwd ? PF_FWD : PF_OUT, encif, &m) != PF_PASS) {
                m_freem(m);
@@ -2786,7 +2797,69 @@ ip6_output_ipsec_send(struct tdb *tdb, s
         */
        in6_proto_cksum_out(m, encif);
 #endif
-       m->m_flags &= ~(M_BCAST | M_MCAST);     /* just in case */
+
+        /* Check if we are allowed to fragment */
+        ip6 = mtod(m, struct ip6_hdr *);
+        if (ip_mtudisc && tdb->tdb_mtu &&
+           sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) > tdb->tdb_mtu &&
+           tdb->tdb_mtutimeout > gettime()) {
+               struct rtentry *rt = NULL;
+               int rt_mtucloned = 0;
+               int transportmode = 0;
+
+               transportmode = (tdb->tdb_dst.sa.sa_family == AF_INET6) &&
+                   (IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr,
+                   &ip6->ip6_dst));
+
+               /* Find a host route to store the mtu in */
+               if (ro != NULL)
+                       rt = ro->ro_rt;
+               /* but don't add a PMTU route for transport mode SAs */
+               if (transportmode)
+                       rt = NULL;
+               else if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0) {
+                       struct sockaddr_in6 sin6;
+
+                       memset(&sin6, 0, sizeof(sin6));
+                       sin6.sin6_family = AF_INET6;
+                       sin6.sin6_len = sizeof(sin6);
+                       sin6.sin6_addr = ip6->ip6_dst;
+                       sin6.sin6_scope_id =
+                           in6_addr2scopeid(m->m_pkthdr.ph_ifidx,
+                           &ip6->ip6_dst);
+                       error = in6_embedscope(&ip6->ip6_dst, &sin6, NULL);
+                       if (error) {
+                               /* should be impossible */
+                               ipsecstat_inc(ipsec_odrops);
+                               m_freem(m);
+                               return error;
+                       }
+                       rt = icmp6_mtudisc_clone(&sin6,
+                           m->m_pkthdr.ph_rtableid, 1);
+                       rt_mtucloned = 1;
+               }
+               DPRINTF(("%s: spi %08x mtu %d rt %p cloned %d\n", __func__,
+                   ntohl(tdb->tdb_spi), tdb->tdb_mtu, rt, rt_mtucloned));
+               if (rt != NULL) {
+                       rt->rt_mtu = tdb->tdb_mtu;
+                       if (ro != NULL && ro->ro_rt != NULL) {
+                               rtfree(ro->ro_rt);
+                               ro->ro_rt = rtalloc(sin6tosa(&ro->ro_dst),
+                                   RT_RESOLVE, m->m_pkthdr.ph_rtableid);
+                       }
+                       if (rt_mtucloned)
+                               rtfree(rt);
+               }
+               ipsec_adjust_mtu(m, tdb->tdb_mtu);
+               m_freem(m);
+               return EMSGSIZE;
+       }
+
+       /*
+        * Clear these -- they'll be set in the recursive invocation
+        * as needed.
+        */
+       m->m_flags &= ~(M_BCAST | M_MCAST);
 
        /* Callee frees mbuf */
        error = ipsp_process_packet(m, tdb, AF_INET6, tunalready);
Index: netinet6/ip6_var.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_var.h,v
retrieving revision 1.86
diff -u -p -r1.86 ip6_var.h
--- netinet6/ip6_var.h  8 Dec 2019 11:08:22 -0000       1.86
+++ netinet6/ip6_var.h  27 Dec 2020 15:16:56 -0000
@@ -368,7 +368,8 @@ u_int32_t ip6_randomflowlabel(void);
 struct tdb;
 struct tdb *
        ip6_output_ipsec_lookup(struct mbuf *, int *, struct inpcb *);
-int    ip6_output_ipsec_send(struct tdb *, struct mbuf *, int, int);
+int    ip6_output_ipsec_send(struct tdb *, struct mbuf *, struct route_in6 *,
+           int, int);
 #endif /* IPSEC */
 
 #endif /* _KERNEL */

Reply via email to