Set UDP datagram source address (ticket #1239).

Previously, the source IP address of a response UDP datagram could differ from
the original datagram destination address.  This could happen if the server UDP
socket is bound to a wildcard address and the network interface chosen to output
the response packet has a different default address than the destination address
of the original packet.  For example, if two addresses from the same network are
configured on an interface.

Now source address is set explicitly if a response is sent for a server UDP
socket bound to a wildcard address.
This commit is contained in:
Roman Arutyunyan 2017-04-11 16:41:53 +03:00
parent 62b20ce87a
commit 05841adfb2
2 changed files with 103 additions and 0 deletions

View File

@ -394,6 +394,19 @@ ngx_feature_test="setsockopt(0, IPPROTO_IP, IP_RECVDSTADDR, NULL, 0)"
. auto/feature
# BSD way to set IPv4 datagram source address
ngx_feature="IP_SENDSRCADDR"
ngx_feature_name="NGX_HAVE_IP_SENDSRCADDR"
ngx_feature_run=no
ngx_feature_incs="#include <sys/socket.h>
#include <netinet/in.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="setsockopt(0, IPPROTO_IP, IP_SENDSRCADDR, NULL, 0)"
. auto/feature
# Linux way to get IPv4 datagram destination address
ngx_feature="IP_PKTINFO"

View File

@ -203,6 +203,20 @@ ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec)
ngx_err_t err;
struct msghdr msg;
#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
#if (NGX_HAVE_IP_SENDSRCADDR)
u_char msg_control[CMSG_SPACE(sizeof(struct in_addr))];
#elif (NGX_HAVE_IP_PKTINFO)
u_char msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#endif
#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
u_char msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
#endif
ngx_memzero(&msg, sizeof(struct msghdr));
if (c->socklen) {
@ -213,6 +227,82 @@ ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec)
msg.msg_iov = vec->iovs;
msg.msg_iovlen = vec->count;
#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
if (c->listening && c->listening->wildcard && c->local_sockaddr) {
#if (NGX_HAVE_IP_SENDSRCADDR)
if (c->local_sockaddr->sa_family == AF_INET) {
struct cmsghdr *cmsg;
struct in_addr *addr;
struct sockaddr_in *sin;
msg.msg_control = &msg_control;
msg.msg_controllen = sizeof(msg_control);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
sin = (struct sockaddr_in *) c->local_sockaddr;
addr = (struct in_addr *) CMSG_DATA(cmsg);
*addr = sin->sin_addr;
}
#elif (NGX_HAVE_IP_PKTINFO)
if (c->local_sockaddr->sa_family == AF_INET) {
struct cmsghdr *cmsg;
struct in_pktinfo *pkt;
struct sockaddr_in *sin;
msg.msg_control = &msg_control;
msg.msg_controllen = sizeof(msg_control);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
sin = (struct sockaddr_in *) c->local_sockaddr;
pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
ngx_memzero(pkt, sizeof(struct in_pktinfo));
pkt->ipi_spec_dst = sin->sin_addr;
}
#endif
#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
if (c->local_sockaddr->sa_family == AF_INET6) {
struct cmsghdr *cmsg;
struct in6_pktinfo *pkt6;
struct sockaddr_in6 *sin6;
msg.msg_control = &msg_control6;
msg.msg_controllen = sizeof(msg_control6);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);
ngx_memzero(pkt6, sizeof(struct in6_pktinfo));
pkt6->ipi6_addr = sin6->sin6_addr;
}
#endif
}
#endif
eintr:
n = sendmsg(c->fd, &msg, 0);