diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index ab88925..c70dd7a 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -155,6 +155,9 @@
 # include "wsnwlink.h"
 #endif
 
+#if defined(HAVE_NETINET_IN_H) && !defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
+#    define USE_IP_PKTINFO_FILTER
+#endif
 
 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 # define sipx_network    sipx_addr.x_net
@@ -504,6 +507,32 @@ static unsigned int _get_sock_mask(SOCKET s)
     return ret;
 }
 
+/* Set and retrieve the interface for IP_PKTINFO filtered sockets */
+#ifdef USE_IP_PKTINFO_FILTER
+static void _set_sock_iface(SOCKET s, int index)
+{
+    SERVER_START_REQ( set_socket_iface )
+    {
+        req->handle = wine_server_obj_handle( SOCKET2HANDLE(s) );
+        req->iface = index;
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
+}
+static int _get_sock_iface(SOCKET s)
+{
+    int ret;
+    SERVER_START_REQ( get_socket_iface )
+    {
+        req->handle = wine_server_obj_handle( SOCKET2HANDLE(s) );
+        wine_server_call( req );
+        ret = reply->iface;
+    }
+    SERVER_END_REQ;
+    return ret;
+}
+#endif /* USE_IP_PKTINFO_FILTER */
+
 static void _sync_sock_state(SOCKET s)
 {
     /* do a dummy wineserver request in order to let
@@ -1173,12 +1202,88 @@ static void WINAPI ws2_async_apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserv
 }
 
 /***********************************************************************
+ *              WS2_get_pktinfo                (INTERNAL)
+ *
+ * check_iface is enabled, so this is a socket that might receive broadcast
+ * packets and it is bound to a particular network interface. Therefore, we
+ * need to check the IP_PKTINFO interface to make sure that the datagram
+ * is meant for this interface and throw it out if it is not.
+ */
+#ifdef USE_IP_PKTINFO_FILTER
+struct in_pktinfo *WS2_get_pktinfo(struct msghdr *hdr)
+{
+    struct cmsghdr *cmsg;
+
+    for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != NULL;
+         cmsg = CMSG_NXTHDR(hdr, cmsg))
+    {
+        if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
+            return (struct in_pktinfo *)CMSG_DATA(cmsg);
+    }
+    WARN("Failed to get IP_PKTINFO structure for a socket with a filter enabled.\n");
+    return NULL;
+}
+#endif
+
+/***********************************************************************
+ *              WS2_pktinfo_check_mismatch                (INTERNAL)
+ *
+ * Use a MSG_PEEK to check for packets that do not match the interface
+ * set in the IP_PKTINFO filter.  This check is intended to occur
+ * before a select or an asynchronous read so that the packet does not
+ * cause a spurious wakeup.  So, if there is a mismatch then throw the
+ * packet away.
+ */
+#ifdef USE_IP_PKTINFO_FILTER
+static int WS2_pktinfo_check_mismatch( SOCKET sock, int fd )
+{
+    unsigned int check_iface, optlen;
+    struct msghdr hdr;
+    char pktbuf[512];
+
+    memset(&hdr, 0, sizeof(struct msghdr));
+    hdr.msg_control = pktbuf;
+    hdr.msg_controllen = sizeof(pktbuf);
+    hdr.msg_flags = 0;
+
+    check_iface = 0;
+    optlen = sizeof(check_iface);
+    getsockopt(fd, IPPROTO_IP, IP_PKTINFO, (void *) &check_iface, &optlen);
+    if (check_iface)
+    {
+        if ( recvmsg(fd, &hdr, MSG_PEEK|MSG_DONTWAIT) != -1 )
+        {
+            struct in_pktinfo *pktinfo = WS2_get_pktinfo(&hdr);
+            int iface_index = _get_sock_iface(sock);
+
+            if (pktinfo && pktinfo->ipi_ifindex != iface_index)
+            {
+                if ( recvmsg(fd, &hdr, MSG_DONTWAIT) != -1 )
+                    return TRUE;
+                else
+                    WARN("Failed to throw away packet with interface mismatch! %m\n");
+            }
+        }
+        else
+            WARN("Failed to peek at message header! %m\n");
+    }
+    return FALSE;
+}
+#endif
+
+/***********************************************************************
  *              WS2_recv                (INTERNAL)
  *
  * Workhorse for both synchronous and asynchronous recv() operations.
  */
 static int WS2_recv( int fd, struct ws2_async *wsa )
 {
+#ifdef USE_IP_PKTINFO_FILTER
+    unsigned int check_iface = 0, optlen;
+    int correct_iface = FALSE;
+    int iface_index = 0;
+    char pktbuf[512];
+#endif
     struct msghdr hdr;
     union generic_unix_sockaddr unix_sockaddr;
     int n;
@@ -1195,17 +1300,47 @@ static int WS2_recv( int fd, struct ws2_async *wsa )
 
     hdr.msg_iov = wsa->iovec + wsa->first_iovec;
     hdr.msg_iovlen = wsa->n_iovecs - wsa->first_iovec;
-#ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
+#if defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
     hdr.msg_accrights = NULL;
     hdr.msg_accrightslen = 0;
+#elif defined(USE_IP_PKTINFO_FILTER)
+    hdr.msg_control = pktbuf;
+    hdr.msg_controllen = sizeof(pktbuf);
+    hdr.msg_flags = 0;
+
+    optlen = sizeof(check_iface);
+    getsockopt(fd, IPPROTO_IP, IP_PKTINFO, (void *) &check_iface, &optlen);
+    if (check_iface)
+        iface_index = _get_sock_iface(HANDLE2SOCKET(wsa->hSocket));
 #else
     hdr.msg_control = NULL;
     hdr.msg_controllen = 0;
     hdr.msg_flags = 0;
 #endif
 
+#ifndef USE_IP_PKTINFO_FILTER
     if ( (n = recvmsg(fd, &hdr, wsa->flags)) == -1 )
         return -1;
+#else
+    /* If IP_PKTINFO interface filtering is enabled then throw out any packet
+     * that is not meant for the interface associated with this socket.
+     */
+    while (!correct_iface)
+    {
+        if ( (n = recvmsg(fd, &hdr, wsa->flags)) == -1 )
+            return -1;
+        correct_iface = TRUE;
+        if (check_iface)
+        {
+            struct in_pktinfo *pktinfo = WS2_get_pktinfo(&hdr);
+
+            if(pktinfo && pktinfo->ipi_ifindex == iface_index)
+                correct_iface = TRUE;
+            else
+                WARN("Packet not destined for this interface (%d != %d), thrown away.\n", pktinfo->ipi_ifindex, iface_index);
+        }
+    }
+#endif
 
     /* if this socket is connected and lpFrom is not NULL, Linux doesn't give us
      * msg_name and msg_namelen from recvmsg, but it does set msg_namelen to zero.
@@ -1239,6 +1374,17 @@ static NTSTATUS WS2_async_recv( void* user, IO_STATUS_BLOCK* iosb, NTSTATUS stat
         if ((status = wine_server_handle_to_fd( wsa->hSocket, FILE_READ_DATA, &fd, NULL ) ))
             break;
 
+#ifdef USE_IP_PKTINFO_FILTER
+        /* Check for packets that do not match the IP_PKTINFO filter */
+        if (WS2_pktinfo_check_mismatch(HANDLE2SOCKET(wsa->hSocket), fd))
+        {
+            WARN("Packet not destined for this interface, thrown away.\n");
+            wine_server_release_fd( wsa->hSocket, fd );
+            status = STATUS_PENDING;
+            _enable_event( wsa->hSocket, FD_READ, 0, 0 );
+            break;
+        }
+#endif
         result = WS2_recv( fd, wsa );
         wine_server_release_fd( wsa->hSocket, fd );
         if (result >= 0)
@@ -1271,14 +1417,99 @@ static NTSTATUS WS2_async_recv( void* user, IO_STATUS_BLOCK* iosb, NTSTATUS stat
 }
 
 /***********************************************************************
+ *               WS2_fix_interface_bind          (INTERNAL)
+ *
+ * Replace the interface bind with an IP_PKTINFO filter.
+ */
+#ifdef USE_IP_PKTINFO_FILTER
+void WS2_fix_interface_bind (SOCKET s, union generic_unix_sockaddr *uaddr)
+{
+    struct sockaddr_in *in_sock = (struct sockaddr_in *) uaddr;
+    int fd = get_sock_fd( s, 0, NULL );
+    unsigned int sock_type = 0, optlen;
+    struct ifreq ifreqs[20], *ifr;
+    struct ifconf ifc;
+
+    if (in_sock->sin_addr.s_addr == htonl(WS_INADDR_ANY))
+    {
+        /* Not binding to specific interface, uses default route */
+        goto cleanup;
+    }
+    optlen = sizeof(sock_type);
+    if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &optlen) == -1
+        || sock_type != SOCK_DGRAM)
+    {
+        /* IP_PKTINFO filtering is only valid for UDP datagrams. */
+        goto cleanup;
+    }
+    memset(&ifc,0,sizeof(ifc));
+    ifc.ifc_len = sizeof(ifreqs);
+    ifc.ifc_buf = (char *) ifreqs;
+    if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
+    {
+        FIXME("Failed to get interface list, broadcast packets will not work on this socket.\n");
+        goto cleanup;
+    }
+    for (ifr = ifc.ifc_req; (ifr-ifc.ifc_req) < sizeof(ifreqs); ifr++)
+    {
+        /* Currently only coded to handle IPv4 */
+        if (uaddr->addr.sa_family == AF_INET && ifr->ifr_addr.sa_family == AF_INET)
+        {
+            struct sockaddr_in *in_iface = (struct sockaddr_in *) &(ifr->ifr_addr);
+
+            if (in_sock->sin_addr.s_addr == in_iface->sin_addr.s_addr)
+            {
+                int enable = 1;
+
+                if(ioctl(fd, SIOCGIFINDEX, ifr) == -1)
+                {
+                    /* We must re-request the interface index here specifically since
+                     * SIOCGIFCONF does NOT return the correct ifr_ifindex value. If
+                     * this request fails for whatever reason then we cannot proceed.
+                     */
+                    FIXME("obtaining unique interface ID for '%s' failed, broadcast packets will not work on this socket.\n", ifr->ifr_name);
+                }
+                else if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable)) < 0)
+                {
+                    /* If we are unable to enable IP_PKTINFO filtering then we cannot
+                     * switch over to INADDR_ANY or we will handle packets destined
+                     * for the wrong interfaces.
+                     */
+                    FIXME("setsockopt: setting IP_PKTINFO failed, broadcast packets will not work on this socket.\n");
+                }
+                else
+                {
+                    /* Success! Store the interface ID to the socket for proper
+                     * filtering later on and bind this socket to INADDR_ANY.
+                     */
+                    TRACE("Bind to local address %s corresponds to interface %d.\n", inet_ntoa(in_sock->sin_addr), ifr->ifr_ifindex);
+                    _set_sock_iface(s, ifr->ifr_ifindex);
+                    in_sock->sin_addr.s_addr = htonl(WS_INADDR_ANY);
+                }
+                break;
+            }
+        }
+    }
+
+cleanup:
+    release_sock_fd( s, fd );
+}
+#endif
+
+/***********************************************************************
  *              WS2_send                (INTERNAL)
  *
  * Workhorse for both synchronous and asynchronous send() operations.
  */
 static int WS2_send( int fd, struct ws2_async *wsa )
 {
-    struct msghdr hdr;
+#ifdef USE_IP_PKTINFO_FILTER
+    unsigned int force_iface = 0, optlen;
+    char *buf = NULL, pktbuf[512];
+    unsigned int buflen = 0;
+#endif
     union generic_unix_sockaddr unix_addr;
+    struct msghdr hdr;
 
     hdr.msg_name = NULL;
     hdr.msg_namelen = 0;
@@ -1313,9 +1544,40 @@ static int WS2_send( int fd, struct ws2_async *wsa )
 
     hdr.msg_iov = wsa->iovec + wsa->first_iovec;
     hdr.msg_iovlen = wsa->n_iovecs - wsa->first_iovec;
-#ifdef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
+#if defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
     hdr.msg_accrights = NULL;
     hdr.msg_accrightslen = 0;
+#elif defined(USE_IP_PKTINFO_FILTER)
+    hdr.msg_control = pktbuf;
+    hdr.msg_controllen = sizeof(pktbuf);
+
+    optlen = sizeof(force_iface);
+    getsockopt(fd, IPPROTO_IP, IP_PKTINFO, (void *) &force_iface, &optlen);
+    /* If force_iface is enabled then this is a socket that might send broadcast
+     * packets and is bound to a particular network interface. Therefore, we
+     * must setup the packet routing to use the correct interface.
+     */
+    if (force_iface)
+    {
+        struct in_pktinfo pktinfo;
+        struct cmsghdr *cmsg;
+
+        memset(&pktinfo, 0, sizeof(pktinfo));
+        pktinfo.ipi_ifindex = _get_sock_iface(HANDLE2SOCKET(wsa->hSocket));
+        cmsg = CMSG_FIRSTHDR(&hdr);
+        cmsg->cmsg_level = IPPROTO_IP;
+        cmsg->cmsg_type = IP_PKTINFO;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(pktinfo));
+        memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo));
+        buf = pktbuf;
+        buflen = cmsg->cmsg_len;
+    }
+    else
+        buf = NULL;
+
+    hdr.msg_control = buf;
+    hdr.msg_controllen = buflen;
+    hdr.msg_flags = 0;
 #else
     hdr.msg_control = NULL;
     hdr.msg_controllen = 0;
@@ -1534,6 +1796,10 @@ int WINAPI WS_bind(SOCKET s, const struct WS_sockaddr* name, int namelen)
                     }
                 }
 #endif
+#ifdef USE_IP_PKTINFO_FILTER
+                /* Replace interface-specific bind with a filter using IP_PKTINFO. */
+                WS2_fix_interface_bind(s, &uaddr);
+#endif
                 if (name->sa_family == WS_AF_INET)
                 {
                     struct sockaddr_in *in4 = (struct sockaddr_in*) &uaddr;
@@ -2720,6 +2986,69 @@ static struct pollfd *fd_sets_to_poll( const WS_fd_set *readfds, const WS_fd_set
     return fds;
 }
 
+/* Check to see if any of the read sockets have IP_PKTINFO enabled,
+ * if this option is enabled and "select" is called then it
+ * may cause the application to attempt to collect data on an
+ * interface for which the socket is not bound (which will fail).
+ */
+#ifdef USE_IP_PKTINFO_FILTER
+static int fd_sets_with_pktinfo( struct pollfd *fds, int count )
+{
+    unsigned int i, check_iface, optlen;
+    int sets = 0;
+
+    optlen = sizeof(check_iface);
+    for (i = 0; i < count; i++)
+    {
+        if (fds[i].events == POLLIN)
+        {
+            int fd = fds[i].fd;
+
+            check_iface = 0;
+            getsockopt(fd, IPPROTO_IP, IP_PKTINFO, (void *) &check_iface, &optlen);
+            if (check_iface)
+                sets++;
+        }
+    }
+    return sets;
+}
+#endif
+
+/* A poll succeeded, if any of the sockets have IP_PKTINFO enabled
+ * then peek the data from that socket and throw the packet away
+ * if there is an interface mismatch.
+ */
+#ifdef USE_IP_PKTINFO_FILTER
+static int check_pktinfo_fds( WS_fd_set *readfds, struct pollfd *fds, int count )
+{
+    unsigned int i;
+    int events = 0;
+
+    for (i = 0; i < count; i++)
+    {
+        if (fds[i].events == POLLIN && fds[i].revents)
+        {
+            SOCKET sock = readfds->fd_array[i];
+            int fd = fds[i].fd;
+
+            if (WS2_pktinfo_check_mismatch(sock, fd))
+            {
+                WARN("Packet not destined for this interface, thrown away.\n");
+                fds[i].revents = 0;
+            }
+            else
+                events++;
+        }
+        else
+        {
+            if (fds[i].revents)
+                events++;
+        }
+    }
+    return (events == 0);
+}
+#endif
+
 /* release the file descriptor obtained in fd_sets_to_poll */
 /* must be called with the original fd_set arrays, before calling get_poll_results */
 static void release_poll_fds( const WS_fd_set *readfds, const WS_fd_set *writefds,
@@ -2787,6 +3116,10 @@ int WINAPI WS_select(int nfds, WS_fd_set *ws_readfds,
                      WS_fd_set *ws_writefds, WS_fd_set *ws_exceptfds,
                      const struct WS_timeval* ws_timeout)
 {
+#ifdef USE_IP_PKTINFO_FILTER
+    unsigned int check_iface = 0;
+    unsigned int noevents = TRUE;
+#endif
     struct pollfd *pollfds;
     int count, ret, timeout = -1;
 
@@ -2799,9 +3132,28 @@ int WINAPI WS_select(int nfds, WS_fd_set *ws_readfds,
         return SOCKET_ERROR;
     }
 
+#ifdef USE_IP_PKTINFO_FILTER
+    check_iface = fd_sets_with_pktinfo( pollfds, count );
+#endif
+
     if (ws_timeout) timeout = (ws_timeout->tv_sec * 1000) + (ws_timeout->tv_usec + 999) / 1000;
 
+#ifndef USE_IP_PKTINFO_FILTER
     ret = poll( pollfds, count, timeout );
+#else
+    while (noevents)
+    {
+        ret = poll( pollfds, count, timeout );
+
+        noevents = FALSE;
+        /* If there is an IP_PKTINFO filter set on any of the sockets
+         * then ensure that the select only wakes up if the the packet
+         * is destined for the appropriate interface.
+         */
+        if (check_iface != 0 && ret > 0)
+            noevents = check_pktinfo_fds( ws_readfds, pollfds, count );
+    }
+#endif
     release_poll_fds( ws_readfds, ws_writefds, ws_exceptfds, pollfds );
 
     if (ret == -1) SetLastError(wsaErrno());
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 3d470c6..626d208 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -64,6 +64,26 @@ static PCSTR  (WINAPI  *pInetNtop)(INT,LPVOID,LPSTR,ULONG) = 0;
 
 /**************** Structs and typedefs ***************/
 
+/* UDP select thread information */
+struct udp_select {
+    SOCKET socket;
+    int responses;
+};
+
+/* UDP IOCP thread information */
+struct udp_iocp {
+    HANDLE iocp;
+    SOCKET socket;
+    int responses;
+};
+
+/* IOCP key for retrieving buffer information */
+struct iocp_key {
+    SOCKET socket;
+    int num_buffers;
+    WSABUF *buffers;
+};
+
 typedef struct thread_info
 {
     HANDLE thread;
@@ -1175,8 +1195,151 @@ static test_setup tests [NUM_TESTS] =
     }
 };
 
+/* Perform a timed "select" (100 ms) and read from a socket,
+ * does not block so if the "select" times out then this fails.
+ */
+int select_recv(SOCKET sock, char *buffer, int len)
+{
+    struct timeval tv = {0, 100000};
+    unsigned long enable = 1;
+    fd_set fdset;
+    int result;
+
+    FD_ZERO(&fdset);
+    FD_SET(sock, &fdset);
+    ioctlsocket(sock, FIONBIO, &enable);
+    result = select(sock+1, &fdset, NULL, NULL, &tv);
+    if (result < 0)
+        return -1;
+    else if (result > 0 && FD_ISSET(sock, &fdset))
+    {
+        result = recv(sock, buffer, len, 0);
+        return result;
+    }
+    return -2;
+}
+
+/* Perform a timed "IOCP" operation (100 ms) and read from a socket,
+ * does not block so if the "select" times out then this fails.
+ */
+int iocp_recv(HANDLE iocp, char *buffer, unsigned int len)
+{
+    struct iocp_key *key = NULL;
+    LPOVERLAPPED o = NULL;
+    unsigned int bytes, i;
+    int result;
+
+    result = GetQueuedCompletionStatus(iocp, &bytes, (PULONG_PTR) &key, &o, 100);
+    if (result && bytes > 0)
+    {
+        for (i=0; i < key->num_buffers; i++)
+        {
+            if (key->buffers[i].buf[0] != '\0')
+            {
+                memcpy(buffer, key->buffers[i].buf, bytes);
+                key->buffers[i].buf[0] = '\0';
+            }
+        }
+
+        return bytes;
+    }
+    return -1;
+}
+
+/* Loop through waiting for UDP datagrams (using "select")
+ * and count the number successfully received.
+ */
+static void WINAPI udp_select_thread( struct udp_select *udpsel )
+{
+    int tries = 0;
+
+    while (tries < 10)
+    {
+        char buffer[100];
+        int ret;
+
+        buffer[0] = '\0';
+        ret = select_recv(udpsel->socket, buffer, sizeof(buffer));
+        if (ret == sizeof(struct sockaddr_in))
+        {
+            struct sockaddr_in *addr = (struct sockaddr_in *) buffer;
+
+            if (ntohl(addr->sin_addr.s_addr) == INADDR_LOOPBACK)
+                (udpsel->responses)++;
+        }
+        tries++;
+    }
+}
+
+/* Loop through waiting for UDP datagrams (using IOCP)
+ * and count the number successfully received.
+ */
+static void WINAPI udp_iocp_thread( struct udp_iocp *udpiocp )
+{
+    int tries = 0;
+
+    while (tries < 10)
+    {
+        char buffer[100];
+        int ret;
+
+        buffer[0] = '\0';
+        ret = iocp_recv(udpiocp->iocp, buffer, sizeof(buffer));
+        if (ret == sizeof(struct sockaddr_in))
+        {
+            struct sockaddr_in *addr = (struct sockaddr_in *) buffer;
+
+            if (ntohl(addr->sin_addr.s_addr) == INADDR_LOOPBACK)
+                (udpiocp->responses)++;
+        }
+        tries++;
+    }
+}
+
+void start_iocp_recv( struct iocp_key *iocp_key, OVERLAPPED *o, int buffer_size, int count, int use_readfile )
+{
+    unsigned int flags = 0;
+    int i, ret;
+
+    iocp_key->num_buffers = count;
+    iocp_key->buffers = malloc(count*sizeof(WSABUF));
+    for(i = 0; i < count; i++)
+    {
+        iocp_key->buffers[i].len = buffer_size;
+        iocp_key->buffers[i].buf = malloc(buffer_size);
+        iocp_key->buffers[i].buf[0] = '\0';
+        if (use_readfile)
+        {
+            ret = ReadFile ((HANDLE)iocp_key->socket, iocp_key->buffers[i].buf, iocp_key->buffers[i].len, NULL ,o); 
+            ok ( ret == 0 && WSAGetLastError() == ERROR_IO_PENDING, "UDP: failed to start IOCP read!\n" );
+        }
+        else
+        {
+            ret = WSARecv(iocp_key->socket, &(iocp_key->buffers[i]), 1, NULL, &flags, o, NULL);
+            ok ( ret == SOCKET_ERROR && WSAGetLastError() == ERROR_IO_PENDING, "UDP: failed to start IOCP read!\n" );
+        }
+    }
+}
+
+void cleanup_iocp_recv( struct iocp_key *iocp_key )
+{
+    int i;
+
+    for(i = 0; i < iocp_key->num_buffers; i++)
+        free(iocp_key->buffers[i].buf);
+    free(iocp_key->buffers);
+}
+
 static void test_UDP(void)
 {
+    SOCKET broadcast_sock, write_sock, read_sock;
+    struct udp_iocp udpiocp = {0, 0, 0};
+    struct udp_select udpsel = {0, 0};
+    HANDLE thread_handle, iocp;
+    struct sockaddr_in addr;
+    int run_num;
+    DWORD id;
+
     /* This function tests UDP sendto() and recvfrom(). UDP is unreliable, so it is
        possible that this test fails due to dropped packets. */
 
@@ -1221,6 +1384,120 @@ static void test_UDP(void)
         ok ( n_recv == sizeof(buf), "UDP: recvfrom() received wrong amount of data or socket error: %d\n", n_recv );
         ok ( memcmp ( &peer[0].peer.sin_port, buf, sizeof(peer[0].addr.sin_port) ) == 0, "UDP: port numbers do not match\n" );
     }
+
+    for ( i = 1; i < NUM_UDP_PEERS; i++ )
+        closesocket ( peer[i].s );
+
+    read_sock = socket( AF_INET, SOCK_DGRAM, 0 );
+    write_sock = socket( AF_INET, SOCK_DGRAM, 0 );
+    broadcast_sock = socket( AF_INET, SOCK_DGRAM, 0 );
+    udpsel.responses = 0;
+    if (read_sock != INVALID_SOCKET && write_sock != INVALID_SOCKET
+        && broadcast_sock != INVALID_SOCKET)
+    {
+        unsigned int enable = 1;
+        int ret;
+
+        /* setup the necessary sockets */
+        addr.sin_family         = AF_INET;
+        addr.sin_addr.s_addr    = htonl ( INADDR_LOOPBACK );
+        addr.sin_port           = htons ( 2000 );
+        do_bind ( read_sock, (struct sockaddr *) &addr, sizeof(addr) );
+        ret = setsockopt ( read_sock, SOL_SOCKET, SO_BROADCAST, (LPVOID) &enable, sizeof (enable) );
+        ok ( ret == 0, "UDP: failed to set broadcast flag!\n" );
+        ret = setsockopt ( broadcast_sock, SOL_SOCKET, SO_BROADCAST, (LPVOID) &enable, sizeof (enable) );
+        ok ( ret == 0, "UDP: failed to set broadcast flag!\n" );
+
+        /* test the "select" operation on UDP sockets */
+        udpsel.socket = read_sock;
+        thread_handle = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) &udp_select_thread, &udpsel, 0, &id );
+
+        /* send test packets to the thread waiting on "select" */
+        memcpy ( buf, &addr, sizeof(addr) );
+        ret = sendto ( write_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+        ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+        addr.sin_addr.s_addr    = htonl ( INADDR_BROADCAST );
+        ret = sendto ( broadcast_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+        addr.sin_addr.s_addr    = htonl ( INADDR_LOOPBACK );
+        if (ret == -1 && WSAGetLastError() == WSAENETUNREACH)
+            skip ( "A connected network interface is required to run this test.\n" );
+        else
+            ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+        ret = sendto ( write_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+        ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+        /* send a test packet <out> on the "receive" interface (which is valid) */
+        ret = sendto ( read_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+        ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+
+        /* wait for the thread to exit and check the replies */
+        WaitForSingleObject ( thread_handle, TEST_TIMEOUT * 1000 );
+        ok ( udpsel.responses == 3, "UDP: expected three replies (received %d).\n", udpsel.responses);
+
+        /* test IOCP with both WSARecv and ReadFile */
+        for (run_num = 0; run_num <= 1; run_num++)
+        {
+            /* create an IOCP handle and associate it with our read socket */
+            iocp = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,0,3);
+            if (iocp)
+            {
+                struct iocp_key iocp_key;
+                OVERLAPPED o;
+
+                /* reset the read socket (make sure IOCP runs are separate) */
+                closesocket ( read_sock );
+                read_sock = socket( AF_INET, SOCK_DGRAM, 0 );
+                do_bind ( read_sock, (struct sockaddr *) &addr, sizeof(addr) );
+
+                /* initialize the IO Completion port */
+                iocp_key.socket = read_sock;
+                CreateIoCompletionPort ( (HANDLE)read_sock, iocp, (ULONG_PTR) &iocp_key, 1 );
+                memset ( &o, 0, sizeof(OVERLAPPED) );
+                o.hEvent = CreateEvent ( NULL, TRUE, TRUE, NULL );
+                udpiocp.responses = 0;
+                start_iocp_recv ( &iocp_key, &o, sizeof(addr), 10, run_num );
+
+                /* test the "select" operation on UDP sockets */
+                udpiocp.iocp = iocp;
+                udpiocp.socket = read_sock;
+                thread_handle = CreateThread ( NULL, 0, (LPTHREAD_START_ROUTINE) &udp_iocp_thread, &udpiocp, 0, &id );
+
+                /* send test packets to the thread waiting on the IO Completion */
+                memcpy ( buf, &addr, sizeof(addr) );
+                ret = sendto ( write_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+                ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+                addr.sin_addr.s_addr    = htonl ( INADDR_BROADCAST );
+                ret = sendto ( broadcast_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+                addr.sin_addr.s_addr    = htonl ( INADDR_LOOPBACK );
+                if (ret == -1 && WSAGetLastError() == WSAENETUNREACH)
+                    skip ( "A connected network interface is required to run this test.\n" );
+                else
+                    ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+                ret = sendto ( write_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+                ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+                /* send a test packet <out> on the "receive" interface (which is valid) */
+                ret = sendto ( read_sock, buf, sizeof(buf), 0, (struct sockaddr*) &addr, sizeof(addr) );
+                ok ( ret == sizeof(buf), "UDP: failed to send data!\n" );
+
+                /* wait for the thread to exit, check the replies, cleanup the buffers */
+                WaitForSingleObject ( thread_handle, TEST_TIMEOUT * 1000 );
+                ok ( udpiocp.responses == 3, "UDP: expected three replies (received %d).\n", udpiocp.responses);
+                cleanup_iocp_recv ( &iocp_key );
+
+                CloseHandle(iocp);
+            }
+            else
+                skip ( "Several tests skipped, failed to create IO completion port!\n" );
+        }
+    }
+    else
+        skip ( "Several tests skipped, failed to create necessary sockets!\n" );
+    /* cleanup socket select test */
+    if (read_sock != INVALID_SOCKET);
+        closesocket ( read_sock );
+    if (write_sock != INVALID_SOCKET);
+        closesocket ( write_sock );
+    if (broadcast_sock != INVALID_SOCKET);
+        closesocket ( broadcast_sock );
 }
 
 static void WINAPI do_getservbyname( HANDLE *starttest )
diff --git a/server/protocol.def b/server/protocol.def
index 9748931..1e09aa0 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1103,6 +1103,21 @@ enum server_fd_type
 @END
 
 
+/* Set socket interface ID */
+@REQ(set_socket_iface)
+    obj_handle_t handle;        /* handle to the socket */
+    int iface;                  /* interface ID */
+@END
+
+
+/* Get socket interface ID */
+@REQ(get_socket_iface)
+    obj_handle_t handle;        /* handle to the socket */
+@REPLY
+    int iface;                  /* interface ID */
+@END
+
+
 /* Reenable pending socket events */
 @REQ(enable_socket_event)
     obj_handle_t handle;        /* handle to the socket */
diff --git a/server/sock.c b/server/sock.c
index 71d8ccb..46c4665 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -38,6 +38,9 @@
 #ifdef HAVE_SYS_SOCKET_H
 # include <sys/socket.h>
 #endif
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
@@ -65,6 +68,10 @@
 #define USE_WS_PREFIX
 #include "winsock2.h"
 
+#if defined(HAVE_NETINET_IN_H) && !defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
+#    define USE_IP_PKTINFO_FILTER
+#endif
+
 struct sock
 {
     struct object       obj;         /* object header */
@@ -85,6 +92,7 @@ struct sock
     struct sock        *deferred;    /* socket that waits for a deferred accept */
     struct async_queue *read_q;      /* queue for asynchronous reads */
     struct async_queue *write_q;     /* queue for asynchronous writes */
+    int                 iface_index; /* interface ID for interface-specific sockets */
 };
 
 static void sock_dump( struct object *obj, int verbose );
@@ -296,6 +304,57 @@ static inline int sock_error( struct fd *fd )
     return optval ? sock_get_error(optval) : 0;
 }
 
+/* Test for IP_PKTINFO filtered packets that are requested using
+ * ReadFile instead of WSARecv.  If the packet does not match
+ * for this socket's interface then throw it away.
+ */
+static int accept_read_poll( struct fd *fd )
+{
+#ifdef USE_IP_PKTINFO_FILTER
+    struct sock *sock = get_fd_user( fd );
+    unsigned int check_iface, optlen;
+    int handle = get_unix_fd ( fd );
+    struct msghdr hdr;
+    char pktbuf[512];
+
+    memset(&hdr, 0, sizeof(struct msghdr));
+    hdr.msg_control = pktbuf;
+    hdr.msg_controllen = sizeof(pktbuf);
+    hdr.msg_flags = 0;
+
+    check_iface = 0;
+    optlen = sizeof(check_iface);
+    getsockopt(handle, IPPROTO_IP, IP_PKTINFO, (void *) &check_iface, &optlen);
+    if (check_iface)
+    {
+        /* Peek at the packet to check its interface */
+        if ( recvmsg(handle, &hdr, MSG_PEEK|MSG_DONTWAIT) != -1 )
+        {
+            int iface_index = sock->iface_index;
+            struct in_pktinfo *pktinfo = NULL;
+            struct cmsghdr *cmsg;
+
+            for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL;
+                 cmsg = CMSG_NXTHDR(&hdr, cmsg))
+            {
+                if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
+                    pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+            }
+            if (pktinfo && pktinfo->ipi_ifindex != iface_index)
+            {
+                if ( recvmsg(handle, &hdr, MSG_DONTWAIT) != -1 )
+                    return FALSE;
+                else
+                    fprintf(stderr, "Failed to throw away packet with interface mismatch! %m\n");
+            }
+        }
+        else
+            fprintf(stderr, "Failed to peek at message header! %m\n");
+    }
+#endif /* USE_IP_PKTINFO_FILTER */
+    return TRUE;
+}
+
 static void sock_poll_event( struct fd *fd, int event )
 {
     struct sock *sock = get_fd_user( fd );
@@ -388,9 +447,12 @@ static void sock_poll_event( struct fd *fd, int event )
         }
         else if ( event & POLLIN ) /* POLLIN for non-stream socket */
         {
-            sock->pmask |= FD_READ;
-            sock->hmask |= (FD_READ|FD_CLOSE);
-            sock->errors[FD_READ_BIT] = 0;
+            if (accept_read_poll(fd))
+            {
+                sock->pmask |= FD_READ;
+                sock->hmask |= (FD_READ|FD_CLOSE);
+                sock->errors[FD_READ_BIT] = 0;
+            }
             if (debug_level)
                 fprintf(stderr, "socket %p is readable\n", sock );
 
@@ -612,6 +674,7 @@ static struct object *create_socket( int family, int type, int protocol, unsigne
     sock->deferred = NULL;
     sock->read_q  = NULL;
     sock->write_q = NULL;
+    sock->iface_index = 0;
     if (!(sock->fd = create_anonymous_fd( &sock_fd_ops, sockfd, &sock->obj,
                             (flags & WSA_FLAG_OVERLAPPED) ? 0 : FILE_SYNCHRONOUS_IO_NONALERT )))
     {
@@ -682,6 +745,7 @@ static struct sock *accept_socket( obj_handle_t handle )
         acceptsock->deferred = NULL;
         acceptsock->read_q  = NULL;
         acceptsock->write_q = NULL;
+        acceptsock->iface_index = 0;
         if (!(acceptsock->fd = create_anonymous_fd( &sock_fd_ops, acceptfd, &acceptsock->obj,
                                                     get_fd_options( sock->fd ) )))
         {
@@ -799,6 +863,33 @@ DECL_HANDLER(accept_socket)
     }
 }
 
+/* set socket interface */
+DECL_HANDLER(set_socket_iface)
+{
+    struct sock *sock;
+
+    if (!(sock = (struct sock *)get_handle_obj( current->process, req->handle,
+                                                FILE_WRITE_ATTRIBUTES, &sock_ops))) return;
+    sock->iface_index = req->iface;
+    release_object( &sock->obj );
+}
+
+/* get socket interface */
+DECL_HANDLER(get_socket_iface)
+{
+    struct sock *sock;
+
+    sock = (struct sock *)get_handle_obj( current->process, req->handle, FILE_READ_ATTRIBUTES, &sock_ops );
+    if (!sock)
+    {
+        reply->iface = 0;
+        set_error( WSAENOTSOCK );
+        return;
+    }
+    reply->iface = sock->iface_index;
+    release_object( &sock->obj );
+}
+
 /* set socket event parameters */
 DECL_HANDLER(set_socket_event)
 {
