diff options
author | vitalybuka@chromium.org <vitalybuka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-05 11:19:42 +0000 |
---|---|---|
committer | vitalybuka@chromium.org <vitalybuka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-05 11:19:42 +0000 |
commit | 7b29dac97993644a2da5e49898671c4a51b9d4c4 (patch) | |
tree | 9c7e29700cd83403899c4f6be048d652b3152761 | |
parent | ef2992c7213fe547955c5b35418354cc1fc971e2 (diff) | |
download | chromium_src-7b29dac97993644a2da5e49898671c4a51b9d4c4.zip chromium_src-7b29dac97993644a2da5e49898671c4a51b9d4c4.tar.gz chromium_src-7b29dac97993644a2da5e49898671c4a51b9d4c4.tar.bz2 |
Add support of IP_MULTICAST_IF in UDP sockets.
BUG=319068
Review URL: https://codereview.chromium.org/99923004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@238939 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/renderer_host/p2p/socket_host_udp_unittest.cc | 5 | ||||
-rw-r--r-- | net/dns/mock_mdns_socket_factory.h | 3 | ||||
-rw-r--r-- | net/udp/datagram_server_socket.h | 6 | ||||
-rw-r--r-- | net/udp/udp_server_socket.cc | 4 | ||||
-rw-r--r-- | net/udp/udp_server_socket.h | 4 | ||||
-rw-r--r-- | net/udp/udp_socket_libevent.cc | 89 | ||||
-rw-r--r-- | net/udp/udp_socket_libevent.h | 9 | ||||
-rw-r--r-- | net/udp/udp_socket_unittest.cc | 5 | ||||
-rw-r--r-- | net/udp/udp_socket_win.cc | 49 | ||||
-rw-r--r-- | net/udp/udp_socket_win.h | 9 |
10 files changed, 165 insertions, 18 deletions
diff --git a/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc b/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc index 5657f47..3ccacfc 100644 --- a/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc +++ b/content/browser/renderer_host/p2p/socket_host_udp_unittest.cc @@ -140,6 +140,11 @@ class FakeDatagramServerSocket : public net::DatagramServerSocket { return net::ERR_NOT_IMPLEMENTED; } + virtual int SetMulticastInterface(uint32 interface_index) OVERRIDE { + NOTIMPLEMENTED(); + return net::ERR_NOT_IMPLEMENTED; + } + virtual int SetMulticastTimeToLive(int time_to_live) OVERRIDE { NOTIMPLEMENTED(); return net::ERR_NOT_IMPLEMENTED; diff --git a/net/dns/mock_mdns_socket_factory.h b/net/dns/mock_mdns_socket_factory.h index 4d1c190..7cc8fed 100644 --- a/net/dns/mock_mdns_socket_factory.h +++ b/net/dns/mock_mdns_socket_factory.h @@ -46,11 +46,10 @@ class MockMDnsDatagramServerSocket : public DatagramServerSocket { MOCK_METHOD0(AllowBroadcast, void()); MOCK_CONST_METHOD1(JoinGroup, int(const IPAddressNumber& group_address)); - MOCK_CONST_METHOD1(LeaveGroup, int(const IPAddressNumber& address)); + MOCK_METHOD1(SetMulticastInterface, int(uint32 interface_index)); MOCK_METHOD1(SetMulticastTimeToLive, int(int ttl)); - MOCK_METHOD1(SetMulticastLoopbackMode, int(bool loopback)); MOCK_METHOD1(SetDiffServCodePoint, int(DiffServCodePoint dscp)); diff --git a/net/udp/datagram_server_socket.h b/net/udp/datagram_server_socket.h index 0561d8a..ed5ab7a 100644 --- a/net/udp/datagram_server_socket.h +++ b/net/udp/datagram_server_socket.h @@ -80,6 +80,12 @@ class NET_EXPORT DatagramServerSocket : public DatagramSocket { // Returns a network error code. virtual int LeaveGroup(const IPAddressNumber& group_address) const = 0; + // Set interface to use for multicast. If |interface_index| set to 0, default + // interface is used. + // Should be called before Bind(). + // Returns a network error code. + virtual int SetMulticastInterface(uint32 interface_index) = 0; + // Set the time-to-live option for UDP packets sent to the multicast // group address. The default value of this option is 1. // Cannot be negative or more than 255. diff --git a/net/udp/udp_server_socket.cc b/net/udp/udp_server_socket.cc index 79086f8..99d741b 100644 --- a/net/udp/udp_server_socket.cc +++ b/net/udp/udp_server_socket.cc @@ -77,6 +77,10 @@ int UDPServerSocket::LeaveGroup(const IPAddressNumber& group_address) const { return socket_.LeaveGroup(group_address); } +int UDPServerSocket::SetMulticastInterface(uint32 interface_index) { + return socket_.SetMulticastInterface(interface_index); +} + int UDPServerSocket::SetMulticastTimeToLive(int time_to_live) { return socket_.SetMulticastTimeToLive(time_to_live); } diff --git a/net/udp/udp_server_socket.h b/net/udp/udp_server_socket.h index 6586f7e..c593413 100644 --- a/net/udp/udp_server_socket.h +++ b/net/udp/udp_server_socket.h @@ -18,8 +18,7 @@ class BoundNetLog; // A client socket that uses UDP as the transport layer. class NET_EXPORT UDPServerSocket : public DatagramServerSocket { public: - UDPServerSocket(net::NetLog* net_log, - const net::NetLog::Source& source); + UDPServerSocket(net::NetLog* net_log, const net::NetLog::Source& source); virtual ~UDPServerSocket(); // Implement DatagramServerSocket: @@ -42,6 +41,7 @@ class NET_EXPORT UDPServerSocket : public DatagramServerSocket { virtual void AllowBroadcast() OVERRIDE; virtual int JoinGroup(const IPAddressNumber& group_address) const OVERRIDE; virtual int LeaveGroup(const IPAddressNumber& group_address) const OVERRIDE; + virtual int SetMulticastInterface(uint32 interface_index) OVERRIDE; virtual int SetMulticastTimeToLive(int time_to_live) OVERRIDE; virtual int SetMulticastLoopbackMode(bool loopback) OVERRIDE; virtual int SetDiffServCodePoint(DiffServCodePoint dscp) OVERRIDE; diff --git a/net/udp/udp_socket_libevent.cc b/net/udp/udp_socket_libevent.cc index 7ef9c3c..2880fa13 100644 --- a/net/udp/udp_socket_libevent.cc +++ b/net/udp/udp_socket_libevent.cc @@ -7,8 +7,10 @@ #include <errno.h> #include <fcntl.h> #include <netdb.h> -#include <sys/socket.h> +#include <net/if.h> #include <netinet/in.h> +#include <sys/ioctl.h> +#include <sys/socket.h> #include "base/callback.h" #include "base/logging.h" @@ -24,15 +26,37 @@ #include "net/socket/socket_descriptor.h" #include "net/udp/udp_net_log_parameters.h" + +namespace net { + namespace { const int kBindRetries = 10; const int kPortStart = 1024; const int kPortEnd = 65535; -} // namespace +#if defined(OS_MACOSX) -namespace net { +// Returns IPv4 address in network order. +int GetIPv4AddressFromIndex(int socket, uint32 index, uint32* address){ + if (!index) { + *address = htonl(INADDR_ANY); + return OK; + } + ifreq ifr; + ifr.ifr_addr.sa_family = AF_INET; + if (!if_indextoname(index, ifr.ifr_name)) + return ERR_FAILED; + int rv = ioctl(socket, SIOCGIFADDR, &ifr); + if (!rv) + return MapSystemError(rv); + *address = reinterpret_cast<sockaddr_in*>(&ifr.ifr_addr)->sin_addr.s_addr; + return OK; +} + +#endif // OS_MACOSX + +} // namespace UDPSocketLibevent::UDPSocketLibevent( DatagramSocket::BindType bind_type, @@ -42,6 +66,7 @@ UDPSocketLibevent::UDPSocketLibevent( : socket_(kInvalidSocket), addr_family_(0), socket_options_(SOCKET_OPTION_MULTICAST_LOOP), + multicast_interface_(0), multicast_time_to_live_(1), bind_type_(bind_type), rand_int_cb_(rand_int_cb), @@ -524,7 +549,7 @@ int UDPSocketLibevent::SetSocketOptions() { rv = setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); } else { - // Signed interger. -1 to use route default. + // Signed integer. -1 to use route default. int ttl = multicast_time_to_live_; rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); @@ -532,6 +557,40 @@ int UDPSocketLibevent::SetSocketOptions() { if (rv < 0) return MapSystemError(errno); } + if (multicast_interface_ != 0) { + switch (addr_family_) { + case AF_INET: { +#if !defined(OS_MACOSX) + ip_mreqn mreq; + mreq.imr_ifindex = multicast_interface_; + mreq.imr_address.s_addr = htonl(INADDR_ANY); +#else + ip_mreq mreq; + int error = GetIPv4AddressFromIndex(socket_, multicast_interface_, + &mreq.imr_interface.s_addr); + if (error != OK) + return error; +#endif + int rv = setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_IF, + reinterpret_cast<const char*>(&mreq), sizeof(mreq)); + if (rv) + return MapSystemError(errno); + break; + } + case AF_INET6: { + uint32 interface_index = multicast_interface_; + int rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_MULTICAST_IF, + reinterpret_cast<const char*>(&interface_index), + sizeof(interface_index)); + if (rv) + return MapSystemError(errno); + break; + } + default: + NOTREACHED() << "Invalid address family"; + return ERR_ADDRESS_INVALID; + } + } return OK; } @@ -566,8 +625,18 @@ int UDPSocketLibevent::JoinGroup(const IPAddressNumber& group_address) const { case kIPv4AddressSize: { if (addr_family_ != AF_INET) return ERR_ADDRESS_INVALID; + +#if !defined(OS_MACOSX) + ip_mreqn mreq; + mreq.imr_ifindex = multicast_interface_; + mreq.imr_address.s_addr = htonl(INADDR_ANY); +#else ip_mreq mreq; - mreq.imr_interface.s_addr = INADDR_ANY; + int error = GetIPv4AddressFromIndex(socket_, multicast_interface_, + &mreq.imr_interface.s_addr); + if (error != OK) + return error; +#endif memcpy(&mreq.imr_multiaddr, &group_address[0], kIPv4AddressSize); int rv = setsockopt(socket_, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); @@ -579,7 +648,7 @@ int UDPSocketLibevent::JoinGroup(const IPAddressNumber& group_address) const { if (addr_family_ != AF_INET6) return ERR_ADDRESS_INVALID; ipv6_mreq mreq; - mreq.ipv6mr_interface = 0; // 0 indicates default multicast interface. + mreq.ipv6mr_interface = multicast_interface_; memcpy(&mreq.ipv6mr_multiaddr, &group_address[0], kIPv6AddressSize); int rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); @@ -630,6 +699,14 @@ int UDPSocketLibevent::LeaveGroup(const IPAddressNumber& group_address) const { } } +int UDPSocketLibevent::SetMulticastInterface(uint32 interface_index) { + DCHECK(CalledOnValidThread()); + if (is_connected()) + return ERR_SOCKET_IS_CONNECTED; + multicast_interface_ = interface_index; + return OK; +} + int UDPSocketLibevent::SetMulticastTimeToLive(int time_to_live) { DCHECK(CalledOnValidThread()); if (is_connected()) diff --git a/net/udp/udp_socket_libevent.h b/net/udp/udp_socket_libevent.h index 6e737de..9dfbc8c 100644 --- a/net/udp/udp_socket_libevent.h +++ b/net/udp/udp_socket_libevent.h @@ -129,6 +129,12 @@ class NET_EXPORT UDPSocketLibevent : public base::NonThreadSafe { // Return a network error code. int LeaveGroup(const IPAddressNumber& group_address) const; + // Set interface to use for multicast. If |interface_index| set to 0, default + // interface is used. + // Should be called before Bind(). + // Returns a network error code. + int SetMulticastInterface(uint32 interface_index); + // Set the time-to-live option for UDP packets sent to the multicast // group address. The default value of this option is 1. // Cannot be negative or more than 255. @@ -237,6 +243,9 @@ class NET_EXPORT UDPSocketLibevent : public base::NonThreadSafe { // options that should be applied to |socket_| before Bind(). int socket_options_; + // Multicast interface. + uint32 multicast_interface_; + // Multicast socket options cached for SetSocketOption. // Cannot be used after Bind(). int multicast_time_to_live_; diff --git a/net/udp/udp_socket_unittest.cc b/net/udp/udp_socket_unittest.cc index ffb56d9..20e38ee 100644 --- a/net/udp/udp_socket_unittest.cc +++ b/net/udp/udp_socket_unittest.cc @@ -574,9 +574,14 @@ TEST_F(UDPSocketTest, MulticastOptions) { EXPECT_EQ(OK, socket.SetMulticastTimeToLive(0)); EXPECT_EQ(OK, socket.SetMulticastTimeToLive(3)); EXPECT_NE(OK, socket.SetMulticastTimeToLive(-1)); + EXPECT_EQ(OK, socket.SetMulticastInterface(0)); EXPECT_EQ(OK, socket.Bind(bind_address)); + EXPECT_NE(OK, socket.SetMulticastLoopbackMode(false)); + EXPECT_NE(OK, socket.SetMulticastTimeToLive(0)); + EXPECT_NE(OK, socket.SetMulticastInterface(0)); + socket.Close(); } diff --git a/net/udp/udp_socket_win.cc b/net/udp/udp_socket_win.cc index d2745b9..b4e4a4b 100644 --- a/net/udp/udp_socket_win.cc +++ b/net/udp/udp_socket_win.cc @@ -165,6 +165,7 @@ UDPSocketWin::UDPSocketWin(DatagramSocket::BindType bind_type, : socket_(INVALID_SOCKET), addr_family_(0), socket_options_(SOCKET_OPTION_MULTICAST_LOOP), + multicast_interface_(0), multicast_time_to_live_(1), bind_type_(bind_type), rand_int_cb_(rand_int_cb), @@ -621,6 +622,32 @@ int UDPSocketWin::SetSocketOptions() { if (rv < 0) return MapSystemError(WSAGetLastError()); } + if (multicast_interface_ != 0) { + switch (addr_family_) { + case AF_INET: { + in_addr address; + address.s_addr = htonl(multicast_interface_); + int rv = setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_IF, + reinterpret_cast<const char*>(&address), + sizeof(address)); + if (rv) + return MapSystemError(WSAGetLastError()); + break; + } + case AF_INET6: { + uint32 interface_index = multicast_interface_; + int rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_MULTICAST_IF, + reinterpret_cast<const char*>(&interface_index), + sizeof(interface_index)); + if (rv) + return MapSystemError(WSAGetLastError()); + break; + } + default: + NOTREACHED() << "Invalid address family"; + return ERR_ADDRESS_INVALID; + } + } return OK; } @@ -662,7 +689,7 @@ int UDPSocketWin::JoinGroup( if (addr_family_ != AF_INET) return ERR_ADDRESS_INVALID; ip_mreq mreq; - mreq.imr_interface.s_addr = INADDR_ANY; + mreq.imr_interface.s_addr = htonl(multicast_interface_); memcpy(&mreq.imr_multiaddr, &group_address[0], kIPv4AddressSize); int rv = setsockopt(socket_, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<const char*>(&mreq), @@ -675,7 +702,7 @@ int UDPSocketWin::JoinGroup( if (addr_family_ != AF_INET6) return ERR_ADDRESS_INVALID; ipv6_mreq mreq; - mreq.ipv6mr_interface = 0; // 0 indicates default multicast interface. + mreq.ipv6mr_interface = multicast_interface_; memcpy(&mreq.ipv6mr_multiaddr, &group_address[0], kIPv6AddressSize); int rv = setsockopt(socket_, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, reinterpret_cast<const char*>(&mreq), @@ -701,11 +728,10 @@ int UDPSocketWin::LeaveGroup( if (addr_family_ != AF_INET) return ERR_ADDRESS_INVALID; ip_mreq mreq; - mreq.imr_interface.s_addr = INADDR_ANY; + mreq.imr_interface.s_addr = htonl(multicast_interface_); memcpy(&mreq.imr_multiaddr, &group_address[0], kIPv4AddressSize); int rv = setsockopt(socket_, IPPROTO_IP, IP_DROP_MEMBERSHIP, - reinterpret_cast<const char*>(&mreq), - sizeof(mreq)); + reinterpret_cast<const char*>(&mreq), sizeof(mreq)); if (rv) return MapSystemError(WSAGetLastError()); return OK; @@ -714,11 +740,10 @@ int UDPSocketWin::LeaveGroup( if (addr_family_ != AF_INET6) return ERR_ADDRESS_INVALID; ipv6_mreq mreq; - mreq.ipv6mr_interface = 0; // 0 indicates default multicast interface. + mreq.ipv6mr_interface = multicast_interface_; memcpy(&mreq.ipv6mr_multiaddr, &group_address[0], kIPv6AddressSize); int rv = setsockopt(socket_, IPPROTO_IPV6, IP_DROP_MEMBERSHIP, - reinterpret_cast<const char*>(&mreq), - sizeof(mreq)); + reinterpret_cast<const char*>(&mreq), sizeof(mreq)); if (rv) return MapSystemError(WSAGetLastError()); return OK; @@ -729,6 +754,14 @@ int UDPSocketWin::LeaveGroup( } } +int UDPSocketWin::SetMulticastInterface(uint32 interface_index) { + DCHECK(CalledOnValidThread()); + if (is_connected()) + return ERR_SOCKET_IS_CONNECTED; + multicast_interface_ = interface_index; + return OK; +} + int UDPSocketWin::SetMulticastTimeToLive(int time_to_live) { DCHECK(CalledOnValidThread()); if (is_connected()) diff --git a/net/udp/udp_socket_win.h b/net/udp/udp_socket_win.h index 142c692..a8712a7 100644 --- a/net/udp/udp_socket_win.h +++ b/net/udp/udp_socket_win.h @@ -130,6 +130,12 @@ class NET_EXPORT UDPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe) { // Return a network error code. int LeaveGroup(const IPAddressNumber& group_address) const; + // Set interface to use for multicast. If |interface_index| set to 0, default + // interface is used. + // Should be called before Bind(). + // Returns a network error code. + int SetMulticastInterface(uint32 interface_index); + // Set the time-to-live option for UDP packets sent to the multicast // group address. The default value of this option is 1. // Cannot be negative or more than 255. @@ -207,6 +213,9 @@ class NET_EXPORT UDPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe) { // options that should be applied to |socket_| before Bind(). int socket_options_; + // Multicast interface. + uint32 multicast_interface_; + // Multicast socket options cached for SetSocketOption. // Cannot be used after Bind(). int multicast_time_to_live_; |