From: "Erich E. Hoover" Subject: [PATCH 2/2] ws2_32: Permit broadcast packets on interface-bound sockets for systems with IP_UNICAST_IF and SO_ATTACH_FILTER. Message-Id: Date: Mon, 16 Jul 2012 14:52:51 -0600 This patch is a continuation of the previous patch that adds support for Linux systems (kernel 3.4-rc1 and newer). Since the Linux networking maintainer has been unwilling to create a socket option identical to SO_BINDTODEVICE that does not require administrative permissions, I went to the effort of getting IP_UNICAST_IF added to the kernel. In addition to giving us the ability to support IP_UNICAST_IF, the addition of this option gives us a new way to solve Bug #7929. This new approach involves restricting outgoing packets through the use of IP_UNICAST_IF and filtering incoming packets using Linux Socket Filters. It is important to note that neither of these features require administrative privileges, so I hope that this patch provides a good solution to finally close Bug #7929. From 8c2fcfbf3a3aeef5bc938a9e7f41d62a3acb769b Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Mon, 16 Jul 2012 14:48:51 -0600 Subject: ws2_32: Permit broadcast packets on interface-bound sockets for systems with IP_UNICAST_IF and SO_ATTACH_FILTER. --- configure.ac | 1 + dlls/ws2_32/socket.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 0 deletions(-) diff --git a/configure.ac b/configure.ac index 52ef054..11dbea3 100644 --- a/configure.ac +++ b/configure.ac @@ -441,6 +441,7 @@ AC_CHECK_HEADERS(\ link.h \ linux/cdrom.h \ linux/compiler.h \ + linux/filter.h \ linux/hdreg.h \ linux/input.h \ linux/ioctl.h \ diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 9783852..50af28f 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -92,6 +92,9 @@ #ifdef HAVE_NET_IF_H # include #endif +#ifdef HAVE_LINUX_FILTER_H +# include +#endif #ifdef HAVE_NETIPX_IPX_H # include @@ -164,6 +167,10 @@ #define INADDR_NONE ~0UL #endif +#if defined(IP_UNICAST_IF) && defined(SO_ATTACH_FILTER) +# define LINUX_BOUND_IF +#endif + WINE_DEFAULT_DEBUG_CHANNEL(winsock); WINE_DECLARE_DEBUG_CHANNEL(winediag); @@ -2106,6 +2113,73 @@ static int WINAPI WS2_WSARecvMsg( SOCKET s, LPWSAMSG msg, LPDWORD lpNumberOfByte lpOverlapped, lpCompletionRoutine, &msg->Control ); } +#ifdef LINUX_BOUND_IF +struct interface_filter { + struct sock_filter iface_memaddr; + struct sock_filter iface_rule; + struct sock_filter return_keep; + struct sock_filter return_dump; +}; +#define FILTER_JUMP_DUMP(here) (u_char)(offsetof(struct interface_filter, return_dump) \ + -offsetof(struct interface_filter, here)-sizeof(struct sock_filter)) \ + /sizeof(struct sock_filter) +#define FILTER_JUMP_KEEP(here) (u_char)(offsetof(struct interface_filter, return_keep) \ + -offsetof(struct interface_filter, here)-sizeof(struct sock_filter)) \ + /sizeof(struct sock_filter) +static struct interface_filter generic_interface_filter = { + /* Only keep packets received on the specified interface */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, SKF_AD_OFF+SKF_AD_IFINDEX), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xdeadbeef, FILTER_JUMP_KEEP(iface_rule), FILTER_JUMP_DUMP(iface_rule)), + /* Rule return values */ + BPF_STMT(BPF_RET+BPF_K, (u_int)-1), /* keep packet */ + BPF_STMT(BPF_RET+BPF_K, 0) /* dump packet */ +}; + +/*********************************************************************** + * incoming_interface_bind (INTERNAL) + * + * Restrict the given socket to only allow incoming packets from the given interface index. + */ +static BOOL incoming_interface_bind( SOCKET s, int fd, int iface ) +{ + struct interface_filter specific_interface_filter; + struct sock_fprog filter_prog; + + memcpy(&specific_interface_filter, &generic_interface_filter, sizeof(generic_interface_filter)); + specific_interface_filter.iface_rule.k = iface; + filter_prog.len = sizeof(generic_interface_filter)/sizeof(struct sock_filter); + filter_prog.filter = (struct sock_filter *) &specific_interface_filter; + + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) != 0) + { + ERR("Enabling incoming filter failed.\n"); + return FALSE; + } + + TRACE("Socket %04lx incoming packets bound to interface index %d\n", s, iface); + return TRUE; +} + +/*********************************************************************** + * outgoing_interface_bind (INTERNAL) + * + * Restrict the given socket to only send packets from the given interface index. + */ +static BOOL outgoing_interface_bind( SOCKET s, int fd, int iface ) +{ + in_addr_t ifindex = (in_addr_t) htonl(iface); + + if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)) != 0) + { + ERR("Enabling outgoing filter failed.\n"); + return FALSE; + } + + TRACE("Socket %04lx outgoing packets bound to interface index %d\n", s, iface); + return TRUE; +} +#endif /* LINUX_BOUND_IF */ + /*********************************************************************** * interface_bind (INTERNAL) * @@ -2180,6 +2254,23 @@ static BOOL interface_bind( SOCKET s, int fd, struct sockaddr *addr ) TRACE("Socket %04lx bound to interface index %d\n", s, adapter->Index); ret = TRUE; +#elif defined(LINUX_BOUND_IF) + /* Activate the outgoing packet restriction */ + if (!outgoing_interface_bind(s, fd, adapter->Index)) + { + ERR("Receiving broadcast packets will not work on socket %04lx.\n", s); + goto cleanup; + } + + /* Activate the incoming packet restriction */ + if (!incoming_interface_bind(s, fd, adapter->Index)) + { + ERR("Receiving broadcast packets will not work on socket %04lx.\n", s); + goto cleanup; + } + + TRACE("Socket %04lx bound to interface index %d\n", s, adapter->Index); + ret = TRUE; #else /* No known way to bind to an interface on this platform */ FIXME("Broadcast packets on interface-bound sockets is not currently supported on this platform, " -- 1.7.5.4