Add IP_SENDSRCADDR cmsg for UDP sockets. As suggested by sthen@,
authorvgross <vgross@openbsd.org>
Tue, 16 Aug 2016 22:21:17 +0000 (22:21 +0000)
committervgross <vgross@openbsd.org>
Tue, 16 Aug 2016 22:21:17 +0000 (22:21 +0000)
IP_SENDSRCADDR == IP_RECVDSTADDR.

OK sthen@ jca@ bluhm@

share/man/man4/ip.4
sys/netinet/in.h
sys/netinet/udp_usrreq.c

index b8daeb4..85f3728 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ip.4,v 1.39 2016/06/28 17:32:58 jca Exp $
+.\"    $OpenBSD: ip.4,v 1.40 2016/08/16 22:21:17 vgross Exp $
 .\"    $NetBSD: ip.4,v 1.3 1994/11/30 16:22:19 jtc Exp $
 .\"
 .\" Copyright (c) 1983, 1991, 1993
@@ -30,7 +30,7 @@
 .\"
 .\"     @(#)ip.4       8.2 (Berkeley) 11/30/93
 .\"
-.Dd $Mdocdate: June 28 2016 $
+.Dd $Mdocdate: August 16 2016 $
 .Dt IP 4
 .Os
 .Sh NAME
@@ -289,6 +289,34 @@ cmsg_len = CMSG_LEN(sizeof(u_int))
 cmsg_level = IPPROTO_IP
 cmsg_type = IP_RECVRTABLE
 .Ed
+.Pp
+When sending on a
+.Dv SOCK_DGRAM
+socket with
+.Xr sendmsg 2
+, the source address to be used can be passed as ancillary data with a type code of
+.Dv IP_SENDSRCADDR .
+The
+.Va msg_control
+field in the
+.Vt msghdr
+structure should point to a buffer that contains a
+.Vt cmsghdr
+structure followed by the requested source address.
+The
+.Vt cmsghdr
+fields should have the following values:
+.Bd -literal -offset indent
+cmsg_len = CMSG_LEN(sizeof(struct in_addr))
+cmsg_level = IPPROTO_IP
+cmsg_type = IP_SENDSRCADDR
+.Ed
+.Pp
+The same checks and restrictions as for
+.Xr bind 2
+apply, unless the socket is bound to
+.Dv INADDR_ANY .
+In this case, there is no source address overlap check.
 .Ss "Multicast Options"
 .Tn IP
 multicasting is supported only on
index 31b2a26..a390574 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in.h,v 1.117 2016/06/28 17:18:24 chris Exp $  */
+/*     $OpenBSD: in.h,v 1.118 2016/08/16 22:21:17 vgross Exp $ */
 /*     $NetBSD: in.h,v 1.20 1996/02/13 23:41:47 christos Exp $ */
 
 /*
@@ -307,6 +307,8 @@ struct ip_opts {
 #define IP_RECVRTABLE          35   /* bool; receive rdomain w/dgram */
 #define IP_IPSECFLOWINFO       36   /* bool; IPsec flow info for dgram */
 #define IP_IPDEFTTL            37   /* int; IP TTL system default */
+#define IP_SENDSRCADDR         IP_RECVDSTADDR  /* struct in_addr; */
+                                               /* source address to use */
 
 #define IP_RTABLE              0x1021  /* int; routing table, see SO_RTABLE */
 #define IP_DIVERTFL            0x1022  /* int; divert direction flag opt */
index 6e2589e..01b6c5b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: udp_usrreq.c,v 1.217 2016/08/04 20:46:24 vgross Exp $ */
+/*     $OpenBSD: udp_usrreq.c,v 1.218 2016/08/16 22:21:17 vgross Exp $ */
 /*     $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
 
 /*
@@ -909,6 +909,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct mbuf *addr,
        struct sockaddr_in *sin = NULL;
        struct udpiphdr *ui;
        u_int32_t ipsecflowinfo = 0;
+       struct sockaddr_in src_sin;
        int len = m->m_pkthdr.len;
        struct in_addr *laddr;
        int error = 0;
@@ -927,6 +928,8 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct mbuf *addr,
                goto release;
        }
 
+       memset(&src_sin, 0, sizeof(src_sin));
+
        if (control) {
                u_int clen;
                struct cmsghdr *cm;
@@ -960,9 +963,20 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct mbuf *addr,
                            cm->cmsg_level == IPPROTO_IP &&
                            cm->cmsg_type == IP_IPSECFLOWINFO) {
                                ipsecflowinfo = *(u_int32_t *)CMSG_DATA(cm);
-                               break;
-                       }
+                       } else
 #endif
+                       if (cm->cmsg_len == CMSG_LEN(sizeof(struct in_addr)) &&
+                           cm->cmsg_level == IPPROTO_IP &&
+                           cm->cmsg_type == IP_SENDSRCADDR) {
+                               memcpy(&src_sin.sin_addr, CMSG_DATA(cm),
+                                   sizeof(struct in_addr));
+                               src_sin.sin_family = AF_INET;
+                               src_sin.sin_len = sizeof(src_sin);
+                               /* no check on reuse when sin->sin_port == 0 */
+                               if ((error = in_pcbaddrisavail(inp, &src_sin,
+                                   0, curproc)))
+                                       goto release;
+                       }
                        clen -= CMSG_ALIGN(cm->cmsg_len);
                        cmsgs += CMSG_ALIGN(cm->cmsg_len);
                } while (clen);
@@ -1000,6 +1014,17 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct mbuf *addr,
                        if (error)
                                goto release;
                }
+
+               if (src_sin.sin_len > 0 &&
+                   src_sin.sin_addr.s_addr != INADDR_ANY &&
+                   src_sin.sin_addr.s_addr != inp->inp_laddr.s_addr) {
+                       src_sin.sin_port = inp->inp_lport;
+                       if (inp->inp_laddr.s_addr != INADDR_ANY &&
+                           (error =
+                           in_pcbaddrisavail(inp, &src_sin, 0, curproc)))
+                               goto release;
+                       laddr = &src_sin.sin_addr;
+               }
        } else {
                if (inp->inp_faddr.s_addr == INADDR_ANY) {
                        error = ENOTCONN;