Author: bz
Date: Tue Sep  1 19:54:43 2020
New Revision: 365059
URL: https://svnweb.freebsd.org/changeset/base/365059

Log:
  MFC r364018:
  
    IPV6_PKTINFO support for v4-mapped IPv6 sockets
  
    When using v4-mapped IPv6 sockets with IPV6_PKTINFO we do not
    respect the given v4-mapped src address on the IPv4 socket.
    Implement the needed functionality. This allows single-socket
    UDP applications (such as OpenVPN) to work better on FreeBSD.
  
    Requested by:       Gert Doering (gert greenie.net), pfsense
    Tested by:  Gert Doering (gert greenie.net)

Modified:
  stable/12/sys/netinet/udp_usrreq.c
  stable/12/sys/netinet6/udp6_usrreq.c
  stable/12/sys/sys/protosw.h
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/netinet/udp_usrreq.c
==============================================================================
--- stable/12/sys/netinet/udp_usrreq.c  Tue Sep  1 19:06:08 2020        
(r365058)
+++ stable/12/sys/netinet/udp_usrreq.c  Tue Sep  1 19:54:43 2020        
(r365059)
@@ -162,7 +162,7 @@ VNET_PCPUSTAT_SYSUNINIT(udpstat);
 #ifdef INET
 static void    udp_detach(struct socket *so);
 static int     udp_output(struct inpcb *, struct mbuf *, struct sockaddr *,
-                   struct mbuf *, struct thread *);
+                   struct mbuf *, struct thread *, int);
 #endif
 
 static void
@@ -1114,13 +1114,69 @@ udp_ctloutput(struct socket *so, struct sockopt *sopt)
        return (error);
 }
 
+#ifdef INET6
+/* The logic here is derived from ip6_setpktopt(). See comments there. */
+static int
+udp_v4mapped_pktinfo(struct cmsghdr *cm, struct sockaddr_in * src,
+    struct inpcb *inp, int flags)
+{
+       struct ifnet *ifp;
+       struct in6_pktinfo *pktinfo;
+       struct in_addr ia;
+
+       if ((flags & PRUS_IPV6) == 0)
+               return (0);
+
+       if (cm->cmsg_level != IPPROTO_IPV6)
+               return (0);
+
+       if  (cm->cmsg_type != IPV6_2292PKTINFO &&
+           cm->cmsg_type != IPV6_PKTINFO)
+               return (0);
+
+       if (cm->cmsg_len !=
+           CMSG_LEN(sizeof(struct in6_pktinfo)))
+               return (EINVAL);
+
+       pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm);
+       if (!IN6_IS_ADDR_V4MAPPED(&pktinfo->ipi6_addr) &&
+           !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr))
+               return (EINVAL);
+
+       /* Validate the interface index if specified. */
+       if (pktinfo->ipi6_ifindex > V_if_index)
+               return (ENXIO);
+
+       ifp = NULL;
+       if (pktinfo->ipi6_ifindex) {
+               ifp = ifnet_byindex(pktinfo->ipi6_ifindex);
+               if (ifp == NULL)
+                       return (ENXIO);
+       }
+       if (ifp != NULL && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
+
+               ia.s_addr = pktinfo->ipi6_addr.s6_addr32[3];
+               if (in_ifhasaddr(ifp, ia) == 0)
+                       return (EADDRNOTAVAIL);
+       }
+
+       bzero(src, sizeof(*src));
+       src->sin_family = AF_INET;
+       src->sin_len = sizeof(*src);
+       src->sin_port = inp->inp_lport;
+       src->sin_addr.s_addr = pktinfo->ipi6_addr.s6_addr32[3];
+
+       return (0);
+}
+#endif
+
 #ifdef INET
 #define        UH_WLOCKED      2
 #define        UH_RLOCKED      1
 #define        UH_UNLOCKED     0
 static int
 udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr,
-    struct mbuf *control, struct thread *td)
+    struct mbuf *control, struct thread *td, int flags)
 {
        struct udpiphdr *ui;
        int len = m->m_pkthdr.len;
@@ -1201,6 +1257,11 @@ retry:
                                error = EINVAL;
                                break;
                        }
+#ifdef INET6
+                       error = udp_v4mapped_pktinfo(cm, &src, inp, flags);
+                       if (error != 0)
+                               break;
+#endif
                        if (cm->cmsg_level != IPPROTO_IP)
                                continue;
 
@@ -1816,7 +1877,7 @@ udp_send(struct socket *so, int flags, struct mbuf *m,
 
        inp = sotoinpcb(so);
        KASSERT(inp != NULL, ("udp_send: inp == NULL"));
-       return (udp_output(inp, m, addr, control, td));
+       return (udp_output(inp, m, addr, control, td, flags));
 }
 #endif /* INET */
 

Modified: stable/12/sys/netinet6/udp6_usrreq.c
==============================================================================
--- stable/12/sys/netinet6/udp6_usrreq.c        Tue Sep  1 19:06:08 2020        
(r365058)
+++ stable/12/sys/netinet6/udp6_usrreq.c        Tue Sep  1 19:54:43 2020        
(r365059)
@@ -804,7 +804,7 @@ retry:
                                in6_sin6_2_sin_in_sock((struct sockaddr *)sin6);
                        pru = inetsw[ip_protox[nxt]].pr_usrreqs;
                        /* addr will just be freed in sendit(). */
-                       return ((*pru->pru_send)(so, flags_arg, m,
+                       return ((*pru->pru_send)(so, flags_arg | PRUS_IPV6, m,
                            (struct sockaddr *)sin6, control, td));
                }
        } else

Modified: stable/12/sys/sys/protosw.h
==============================================================================
--- stable/12/sys/sys/protosw.h Tue Sep  1 19:06:08 2020        (r365058)
+++ stable/12/sys/sys/protosw.h Tue Sep  1 19:54:43 2020        (r365059)
@@ -210,6 +210,7 @@ struct pr_usrreqs {
 #define        PRUS_EOF        0x2
 #define        PRUS_MORETOCOME 0x4
 #define        PRUS_NOTREADY   0x8
+#define        PRUS_IPV6       0x10
        int     (*pru_ready)(struct socket *so, struct mbuf *m, int count);
        int     (*pru_sense)(struct socket *so, struct stat *sb);
        int     (*pru_shutdown)(struct socket *so);
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to