> Date: Sun, 12 Jun 2016 14:59:55 +0200
> From: Vincent Gross <[email protected]>
>
> 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 != *
>
> Ok ?
Why do we need this? cmsg stuff is fragile, so we want the to keep it
as simple as possible.
> 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..35675b4 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,16 @@ 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);
> + }
> clen -= CMSG_ALIGN(cm->cmsg_len);
> cmsgs += CMSG_ALIGN(cm->cmsg_len);
> } while (clen);
> @@ -980,6 +990,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;
>
>