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 ?
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;