From fdc836786e82a355da43a67a64b447a9dced2df7 Mon Sep 17 00:00:00 2001 From: vgross Date: Tue, 16 Aug 2016 22:21:17 +0000 Subject: [PATCH] Add IP_SENDSRCADDR cmsg for UDP sockets. As suggested by sthen@, IP_SENDSRCADDR == IP_RECVDSTADDR. OK sthen@ jca@ bluhm@ --- share/man/man4/ip.4 | 32 ++++++++++++++++++++++++++++++-- sys/netinet/in.h | 4 +++- sys/netinet/udp_usrreq.c | 31 ++++++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/share/man/man4/ip.4 b/share/man/man4/ip.4 index b8daeb47ed8..85f3728064e 100644 --- a/share/man/man4/ip.4 +++ b/share/man/man4/ip.4 @@ -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 diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 31b2a26b453..a390574da3a 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -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 */ diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 6e2589ea607..01b6c5b64ef 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -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; -- 2.20.1