Le Mon, 13 Jun 2016 07:35:16 +0200, [email protected] (Jérémie Courrèges-Anglas) a écrit :
> [email protected] (Jeremie Courreges-Anglas) writes: > > > cc'ing sthen since he also has interest in IP_SENDSRCADDR > > > > Jeremie Courreges-Anglas <[email protected]> writes: > > > >> Vincent Gross <[email protected]> writes: > >> > >>> This diff adds support for IP_SENDSRCADDR cmsg on UDP sockets. As > >>> for udp6_output(), we check that the source address+port is > >>> available only if inp_laddr != * > >> > >> Your last IP_SENDSRCADDR diff didn't have that check, I think it is > >> harmful. If the socket is not bound then there is effectively no > >> check performed by in_pcbaddrisavail(), thus I can use any random > >> address. Other than this additional bypass check, your diff looks > >> good to me. > >> [...] > >> > >> I haven't checked yet whether udp6_output is also affected. If you > >> folks already know that it isn't, please let me know. > > The answer is "no", a few tests can't trigger the same problem. IIUC > in6_selectsrc is responsible for rejection of non-local systems. > Maybe we should take the same approach in netinet/, and extend > in_selectsrc()? > > -- While validating source address inside selection functions is the right direction, I don't think it would be a good thing to extend further in_selectsrc() prototype. However it is easy to add a check while processing cmsg. rev2 below. Ok ? diff --git a/share/man/man4/ip.4 b/share/man/man4/ip.4 index 111432b..154b0d1 100644 --- a/share/man/man4/ip.4 +++ b/share/man/man4/ip.4 @@ -290,6 +290,27 @@ cmsg_len = CMSG_LEN(sizeof(u_int)) cmsg_level = IPPROTO_IP cmsg_type = IP_RECVRTABLE .Ed +.Pp +If the +.Dv IP_SENDSRCADDR +option is passed to a +.Xr sendmsg 2 +call on a +.Dv SOCK_DGRAM +socket, the address passed along the +.Vt cmsghdr +structure will be used as the source of the outgoing +.Tn UDP +datagram. The +.Vt cmsghdr +fields for +.Xr sendmsg 2 +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 .Ss "Multicast Options" .Tn IP multicasting is supported only on diff --git a/sys/netinet/in.h b/sys/netinet/in.h index adb1b30..bf8c95d 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -307,6 +307,7 @@ 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 38 /* 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 1feea11..401ed7a 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -888,6 +888,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; @@ -906,6 +907,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; @@ -939,9 +942,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) { + bzero(&src_sin, sizeof(src_sin)); + 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 done 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); @@ -980,6 +994,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;
