changeset 6980:dbb0c854e308

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.
author Roman Arutyunyan <arut@nginx.com>
date Tue, 11 Apr 2017 16:41:53 +0300
parents ef935cd7ed8d
children 08dc60979133
files auto/unix src/os/unix/ngx_udp_sendmsg_chain.c
diffstat 2 files changed, 103 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/auto/unix
+++ b/auto/unix
@@ -394,6 +394,19 @@ ngx_feature_test="setsockopt(0, IPPROTO_
 . 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"
--- a/src/os/unix/ngx_udp_sendmsg_chain.c
+++ b/src/os/unix/ngx_udp_sendmsg_chain.c
@@ -203,6 +203,20 @@ ngx_sendmsg(ngx_connection_t *c, ngx_iov
     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_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_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);