# HG changeset patch
# User Roman Arutyunyan <arut@nginx.com>
# Date 1491569164 -10800
#      Fri Apr 07 15:46:04 2017 +0300
# Node ID 213701890e6fefc21f8fd34191006ac79bbd3512
# Parent  3ff293cfdab85082a9560ab56ea9f16b02a8a8c8
Set UDP source address.

When multiple IP addresses from the same subnet are configured in a system, and
nginx is listening on a wildcard address, the response UDP packet might be sent
from the wrong source address.  Now source address is set explicitly if a
response is sent for a wildcard listener.

diff --git a/src/os/unix/ngx_udp_sendmsg_chain.c b/src/os/unix/ngx_udp_sendmsg_chain.c
--- a/src/os/unix/ngx_udp_sendmsg_chain.c
+++ b/src/os/unix/ngx_udp_sendmsg_chain.c
@@ -203,6 +203,21 @@ ngx_sendmsg(ngx_connection_t *c, ngx_iov
     ngx_err_t      err;
     struct msghdr  msg;
 
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+    union {
+#if (NGX_HAVE_IP_RECVDSTADDR)
+        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
+        struct cmsghdr     align;
+    } u;
+#endif
+
     ngx_memzero(&msg, sizeof(struct msghdr));
 
     if (c->socklen) {
@@ -213,6 +228,82 @@ ngx_sendmsg(ngx_connection_t *c, ngx_iov
     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_RECVDSTADDR)
+
+        if (c->local_sockaddr->sa_family == AF_INET) {
+            struct cmsghdr      *cmsg;
+            struct in_addr      *addr;
+            struct sockaddr_in  *sin;
+
+            msg.msg_control = &u.msg_control;
+            msg.msg_controllen = sizeof(u.msg_control);
+
+            cmsg = CMSG_FIRSTHDR(&msg);
+            cmsg->cmsg_level = IPPROTO_IP;
+            cmsg->cmsg_type = IP_RECVDSTADDR;
+            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 = &u.msg_control;
+            msg.msg_controllen = sizeof(u.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 = &u.msg_control6;
+            msg.msg_controllen = sizeof(u.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);
