mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Handle interfaceAddr and NIC options separately for IP_MULTICAST_IF
This tweaks the handling code for IP_MULTICAST_IF to ignore the InterfaceAddr if a NICID is given. PiperOrigin-RevId: 258982541
This commit is contained in:
@@ -252,7 +252,7 @@ func (e *endpoint) connectRoute(nicid tcpip.NICID, addr tcpip.FullAddress) (stac
|
||||
if nicid == 0 {
|
||||
nicid = e.multicastNICID
|
||||
}
|
||||
if localAddr == "" {
|
||||
if localAddr == "" && nicid == 0 {
|
||||
localAddr = e.multicastAddr
|
||||
}
|
||||
}
|
||||
@@ -675,6 +675,9 @@ func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort u
|
||||
|
||||
func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress, allowMismatch bool) (tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
netProto := e.netProto
|
||||
if len(addr.Addr) == 0 {
|
||||
return netProto, nil
|
||||
}
|
||||
if header.IsV4MappedAddress(addr.Addr) {
|
||||
// Fail if using a v4 mapped address on a v6only endpoint.
|
||||
if e.v6only {
|
||||
|
||||
+212
-129
@@ -847,9 +847,7 @@ func TestWriteIncrementsPacketsSent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTTL(t *testing.T) {
|
||||
payload := tcpip.SlicePayload(buffer.View(newPayload()))
|
||||
|
||||
func setSockOptVariants(t *testing.T, optFunc func(*testing.T, string, tcpip.NetworkProtocolNumber, string)) {
|
||||
for _, name := range []string{"v4", "v6", "dual"} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
var networkProtocolNumber tcpip.NetworkProtocolNumber
|
||||
@@ -874,134 +872,219 @@ func TestTTL(t *testing.T) {
|
||||
|
||||
for _, variant := range variants {
|
||||
t.Run(variant, func(t *testing.T) {
|
||||
for _, typ := range []string{"unicast", "multicast"} {
|
||||
t.Run(typ, func(t *testing.T) {
|
||||
var addr tcpip.Address
|
||||
var port uint16
|
||||
switch typ {
|
||||
case "unicast":
|
||||
port = testPort
|
||||
switch variant {
|
||||
case "v4":
|
||||
addr = testAddr
|
||||
case "mapped":
|
||||
addr = testV4MappedAddr
|
||||
case "v6":
|
||||
addr = testV6Addr
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
case "multicast":
|
||||
port = multicastPort
|
||||
switch variant {
|
||||
case "v4":
|
||||
addr = multicastAddr
|
||||
case "mapped":
|
||||
addr = multicastV4MappedAddr
|
||||
case "v6":
|
||||
addr = multicastV6Addr
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
|
||||
var err *tcpip.Error
|
||||
c.ep, err = c.s.NewEndpoint(udp.ProtocolNumber, networkProtocolNumber, &c.wq)
|
||||
if err != nil {
|
||||
c.t.Fatalf("NewEndpoint failed: %v", err)
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "v4":
|
||||
case "v6":
|
||||
if err := c.ep.SetSockOpt(tcpip.V6OnlyOption(1)); err != nil {
|
||||
c.t.Fatalf("SetSockOpt failed: %v", err)
|
||||
}
|
||||
case "dual":
|
||||
if err := c.ep.SetSockOpt(tcpip.V6OnlyOption(0)); err != nil {
|
||||
c.t.Fatalf("SetSockOpt failed: %v", err)
|
||||
}
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
const multicastTTL = 42
|
||||
if err := c.ep.SetSockOpt(tcpip.MulticastTTLOption(multicastTTL)); err != nil {
|
||||
c.t.Fatalf("SetSockOpt failed: %v", err)
|
||||
}
|
||||
|
||||
n, _, err := c.ep.Write(payload, tcpip.WriteOptions{To: &tcpip.FullAddress{Addr: addr, Port: port}})
|
||||
if err != nil {
|
||||
c.t.Fatalf("Write failed: %v", err)
|
||||
}
|
||||
if n != uintptr(len(payload)) {
|
||||
c.t.Fatalf("got c.ep.Write(...) = %d, want = %d", n, len(payload))
|
||||
}
|
||||
|
||||
checkerFn := checker.IPv4
|
||||
switch variant {
|
||||
case "v4", "mapped":
|
||||
case "v6":
|
||||
checkerFn = checker.IPv6
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
var wantTTL uint8
|
||||
var multicast bool
|
||||
switch typ {
|
||||
case "unicast":
|
||||
multicast = false
|
||||
switch variant {
|
||||
case "v4", "mapped":
|
||||
ep, err := ipv4.NewProtocol().NewEndpoint(0, "", nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantTTL = ep.DefaultTTL()
|
||||
ep.Close()
|
||||
case "v6":
|
||||
ep, err := ipv6.NewProtocol().NewEndpoint(0, "", nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantTTL = ep.DefaultTTL()
|
||||
ep.Close()
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
case "multicast":
|
||||
wantTTL = multicastTTL
|
||||
multicast = true
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
var networkProtocolNumber tcpip.NetworkProtocolNumber
|
||||
switch variant {
|
||||
case "v4", "mapped":
|
||||
networkProtocolNumber = ipv4.ProtocolNumber
|
||||
case "v6":
|
||||
networkProtocolNumber = ipv6.ProtocolNumber
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
b := c.getPacket(networkProtocolNumber, multicast)
|
||||
checkerFn(c.t, b,
|
||||
checker.TTL(wantTTL),
|
||||
checker.UDP(
|
||||
checker.DstPort(port),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
optFunc(t, name, networkProtocolNumber, variant)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTTL(t *testing.T) {
|
||||
payload := tcpip.SlicePayload(buffer.View(newPayload()))
|
||||
|
||||
setSockOptVariants(t, func(t *testing.T, name string, networkProtocolNumber tcpip.NetworkProtocolNumber, variant string) {
|
||||
for _, typ := range []string{"unicast", "multicast"} {
|
||||
t.Run(typ, func(t *testing.T) {
|
||||
var addr tcpip.Address
|
||||
var port uint16
|
||||
switch typ {
|
||||
case "unicast":
|
||||
port = testPort
|
||||
switch variant {
|
||||
case "v4":
|
||||
addr = testAddr
|
||||
case "mapped":
|
||||
addr = testV4MappedAddr
|
||||
case "v6":
|
||||
addr = testV6Addr
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
case "multicast":
|
||||
port = multicastPort
|
||||
switch variant {
|
||||
case "v4":
|
||||
addr = multicastAddr
|
||||
case "mapped":
|
||||
addr = multicastV4MappedAddr
|
||||
case "v6":
|
||||
addr = multicastV6Addr
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
|
||||
var err *tcpip.Error
|
||||
c.ep, err = c.s.NewEndpoint(udp.ProtocolNumber, networkProtocolNumber, &c.wq)
|
||||
if err != nil {
|
||||
c.t.Fatalf("NewEndpoint failed: %v", err)
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "v4":
|
||||
case "v6":
|
||||
if err := c.ep.SetSockOpt(tcpip.V6OnlyOption(1)); err != nil {
|
||||
c.t.Fatalf("SetSockOpt failed: %v", err)
|
||||
}
|
||||
case "dual":
|
||||
if err := c.ep.SetSockOpt(tcpip.V6OnlyOption(0)); err != nil {
|
||||
c.t.Fatalf("SetSockOpt failed: %v", err)
|
||||
}
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
const multicastTTL = 42
|
||||
if err := c.ep.SetSockOpt(tcpip.MulticastTTLOption(multicastTTL)); err != nil {
|
||||
c.t.Fatalf("SetSockOpt failed: %v", err)
|
||||
}
|
||||
|
||||
n, _, err := c.ep.Write(payload, tcpip.WriteOptions{To: &tcpip.FullAddress{Addr: addr, Port: port}})
|
||||
if err != nil {
|
||||
c.t.Fatalf("Write failed: %v", err)
|
||||
}
|
||||
if n != uintptr(len(payload)) {
|
||||
c.t.Fatalf("got c.ep.Write(...) = %d, want = %d", n, len(payload))
|
||||
}
|
||||
|
||||
checkerFn := checker.IPv4
|
||||
switch variant {
|
||||
case "v4", "mapped":
|
||||
case "v6":
|
||||
checkerFn = checker.IPv6
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
var wantTTL uint8
|
||||
var multicast bool
|
||||
switch typ {
|
||||
case "unicast":
|
||||
multicast = false
|
||||
switch variant {
|
||||
case "v4", "mapped":
|
||||
ep, err := ipv4.NewProtocol().NewEndpoint(0, "", nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantTTL = ep.DefaultTTL()
|
||||
ep.Close()
|
||||
case "v6":
|
||||
ep, err := ipv6.NewProtocol().NewEndpoint(0, "", nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantTTL = ep.DefaultTTL()
|
||||
ep.Close()
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
case "multicast":
|
||||
wantTTL = multicastTTL
|
||||
multicast = true
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
var networkProtocolNumber tcpip.NetworkProtocolNumber
|
||||
switch variant {
|
||||
case "v4", "mapped":
|
||||
networkProtocolNumber = ipv4.ProtocolNumber
|
||||
case "v6":
|
||||
networkProtocolNumber = ipv6.ProtocolNumber
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
b := c.getPacket(networkProtocolNumber, multicast)
|
||||
checkerFn(c.t, b,
|
||||
checker.TTL(wantTTL),
|
||||
checker.UDP(
|
||||
checker.DstPort(port),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMulticastInterfaceOption(t *testing.T) {
|
||||
setSockOptVariants(t, func(t *testing.T, name string, networkProtocolNumber tcpip.NetworkProtocolNumber, variant string) {
|
||||
for _, bindTyp := range []string{"bound", "unbound"} {
|
||||
t.Run(bindTyp, func(t *testing.T) {
|
||||
for _, optTyp := range []string{"use local-addr", "use NICID", "use local-addr and NIC"} {
|
||||
t.Run(optTyp, func(t *testing.T) {
|
||||
var mcastAddr, localIfAddr tcpip.Address
|
||||
switch variant {
|
||||
case "v4":
|
||||
mcastAddr = multicastAddr
|
||||
localIfAddr = stackAddr
|
||||
case "mapped":
|
||||
mcastAddr = multicastV4MappedAddr
|
||||
localIfAddr = stackAddr
|
||||
case "v6":
|
||||
mcastAddr = multicastV6Addr
|
||||
localIfAddr = stackV6Addr
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
var ifoptSet tcpip.MulticastInterfaceOption
|
||||
switch optTyp {
|
||||
case "use local-addr":
|
||||
ifoptSet.InterfaceAddr = localIfAddr
|
||||
case "use NICID":
|
||||
ifoptSet.NIC = 1
|
||||
case "use local-addr and NIC":
|
||||
ifoptSet.InterfaceAddr = localIfAddr
|
||||
ifoptSet.NIC = 1
|
||||
default:
|
||||
t.Fatal("unknown test variant")
|
||||
}
|
||||
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
|
||||
var err *tcpip.Error
|
||||
c.ep, err = c.s.NewEndpoint(udp.ProtocolNumber, networkProtocolNumber, &c.wq)
|
||||
if err != nil {
|
||||
c.t.Fatalf("NewEndpoint failed: %v", err)
|
||||
}
|
||||
|
||||
if bindTyp == "bound" {
|
||||
// Bind the socket by connecting to the multicast address.
|
||||
// This may have an influence on how the multicast interface
|
||||
// is set.
|
||||
addr := tcpip.FullAddress{
|
||||
Addr: mcastAddr,
|
||||
Port: multicastPort,
|
||||
}
|
||||
if err := c.ep.Connect(addr); err != nil {
|
||||
c.t.Fatalf("Connect failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.ep.SetSockOpt(ifoptSet); err != nil {
|
||||
c.t.Fatalf("SetSockOpt failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify multicast interface addr and NIC were set correctly.
|
||||
// Note that NIC must be 1 since this is our outgoing interface.
|
||||
ifoptWant := tcpip.MulticastInterfaceOption{NIC: 1, InterfaceAddr: ifoptSet.InterfaceAddr}
|
||||
var ifoptGot tcpip.MulticastInterfaceOption
|
||||
if err := c.ep.GetSockOpt(&ifoptGot); err != nil {
|
||||
c.t.Fatalf("GetSockOpt failed: %v", err)
|
||||
}
|
||||
if ifoptGot != ifoptWant {
|
||||
c.t.Errorf("got GetSockOpt() = %#v, want = %#v", ifoptGot, ifoptWant)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -120,5 +120,68 @@ SocketKind IPv4TCPUnboundSocket(int type) {
|
||||
UnboundSocketCreator(AF_INET, type | SOCK_STREAM, IPPROTO_TCP)};
|
||||
}
|
||||
|
||||
PosixError IfAddrHelper::Load() {
|
||||
Release();
|
||||
RETURN_ERROR_IF_SYSCALL_FAIL(getifaddrs(&ifaddr_));
|
||||
return PosixError(0);
|
||||
}
|
||||
|
||||
void IfAddrHelper::Release() {
|
||||
if (ifaddr_) {
|
||||
freeifaddrs(ifaddr_);
|
||||
}
|
||||
ifaddr_ = nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> IfAddrHelper::InterfaceList(int family) {
|
||||
std::vector<std::string> names;
|
||||
for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) {
|
||||
continue;
|
||||
}
|
||||
names.emplace(names.end(), ifa->ifa_name);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
sockaddr* IfAddrHelper::GetAddr(int family, std::string name) {
|
||||
for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) {
|
||||
continue;
|
||||
}
|
||||
if (name == ifa->ifa_name) {
|
||||
return ifa->ifa_addr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PosixErrorOr<int> IfAddrHelper::GetIndex(std::string name) {
|
||||
return InterfaceIndex(name);
|
||||
}
|
||||
|
||||
std::string GetAddr4Str(in_addr* a) {
|
||||
char str[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, a, str, sizeof(str));
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
std::string GetAddr6Str(in6_addr* a) {
|
||||
char str[INET6_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET6, a, str, sizeof(str));
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
std::string GetAddrStr(sockaddr* a) {
|
||||
if (a->sa_family == AF_INET) {
|
||||
auto src = &(reinterpret_cast<sockaddr_in*>(a)->sin_addr);
|
||||
return GetAddr4Str(src);
|
||||
} else if (a->sa_family == AF_INET6) {
|
||||
auto src = &(reinterpret_cast<sockaddr_in6*>(a)->sin6_addr);
|
||||
return GetAddr6Str(src);
|
||||
}
|
||||
return std::string("<invalid>");
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
#ifndef GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_
|
||||
#define GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "test/syscalls/linux/socket_test_util.h"
|
||||
|
||||
namespace gvisor {
|
||||
@@ -66,6 +71,35 @@ SocketKind IPv4UDPUnboundSocket(int type);
|
||||
// a SimpleSocket created with AF_INET, SOCK_STREAM and the given type.
|
||||
SocketKind IPv4TCPUnboundSocket(int type);
|
||||
|
||||
// IfAddrHelper is a helper class that determines the local interfaces present
|
||||
// and provides functions to obtain their names, index numbers, and IP address.
|
||||
class IfAddrHelper {
|
||||
public:
|
||||
IfAddrHelper() : ifaddr_(nullptr) {}
|
||||
~IfAddrHelper() { Release(); }
|
||||
|
||||
PosixError Load();
|
||||
void Release();
|
||||
|
||||
std::vector<std::string> InterfaceList(int family);
|
||||
|
||||
struct sockaddr* GetAddr(int family, std::string name);
|
||||
PosixErrorOr<int> GetIndex(std::string name);
|
||||
|
||||
private:
|
||||
struct ifaddrs* ifaddr_;
|
||||
};
|
||||
|
||||
// GetAddr4Str returns the given IPv4 network address structure as a string.
|
||||
std::string GetAddr4Str(in_addr* a);
|
||||
|
||||
// GetAddr6Str returns the given IPv6 network address structure as a string.
|
||||
std::string GetAddr6Str(in6_addr* a);
|
||||
|
||||
// GetAddrStr returns the given IPv4 or IPv6 network address structure as a
|
||||
// string.
|
||||
std::string GetAddrStr(sockaddr* a);
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ TestAddress V4Multicast() {
|
||||
return t;
|
||||
}
|
||||
|
||||
// Check that packets are not received without a group memebership. Default send
|
||||
// Check that packets are not received without a group membership. Default send
|
||||
// interface configured by bind.
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackNoGroup) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
#include "test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
@@ -32,6 +35,52 @@
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
TestAddress V4EmptyAddress() {
|
||||
TestAddress t("V4Empty");
|
||||
t.addr.ss_family = AF_INET;
|
||||
t.addr_len = sizeof(sockaddr_in);
|
||||
return t;
|
||||
}
|
||||
|
||||
void IPv4UDPUnboundExternalNetworkingSocketTest::SetUp() {
|
||||
got_if_infos_ = false;
|
||||
|
||||
// Get interface list.
|
||||
std::vector<std::string> if_names;
|
||||
ASSERT_NO_ERRNO(if_helper_.Load());
|
||||
if_names = if_helper_.InterfaceList(AF_INET);
|
||||
if (if_names.size() != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Figure out which interface is where.
|
||||
int lo = 0, eth = 1;
|
||||
if (if_names[lo] != "lo") {
|
||||
lo = 1;
|
||||
eth = 0;
|
||||
}
|
||||
|
||||
if (if_names[lo] != "lo") {
|
||||
return;
|
||||
}
|
||||
|
||||
lo_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(if_names[lo]));
|
||||
lo_if_addr_ = if_helper_.GetAddr(AF_INET, if_names[lo]);
|
||||
if (lo_if_addr_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
lo_if_sin_addr_ = reinterpret_cast<sockaddr_in*>(lo_if_addr_)->sin_addr;
|
||||
|
||||
eth_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(if_names[eth]));
|
||||
eth_if_addr_ = if_helper_.GetAddr(AF_INET, if_names[eth]);
|
||||
if (eth_if_addr_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
eth_if_sin_addr_ = reinterpret_cast<sockaddr_in*>(eth_if_addr_)->sin_addr;
|
||||
|
||||
got_if_infos_ = true;
|
||||
}
|
||||
|
||||
// Verifies that a newly instantiated UDP socket does not have the
|
||||
// broadcast socket option enabled.
|
||||
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, UDPBroadcastDefault) {
|
||||
@@ -658,7 +707,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
|
||||
sender->get(), reinterpret_cast<sockaddr*>(&sendto_addr.addr),
|
||||
sendto_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
TestAddress sender_addr("");
|
||||
auto sender_addr = V4EmptyAddress();
|
||||
ASSERT_THAT(
|
||||
getsockname(sender->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr),
|
||||
&sender_addr.addr_len),
|
||||
@@ -674,7 +723,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
|
||||
|
||||
// Receive a multicast packet.
|
||||
char recv_buf[sizeof(send_buf)] = {};
|
||||
TestAddress src_addr("");
|
||||
auto src_addr = V4EmptyAddress();
|
||||
ASSERT_THAT(
|
||||
RetryEINTR(recvfrom)(receiver->get(), recv_buf, sizeof(recv_buf), 0,
|
||||
reinterpret_cast<sockaddr*>(&src_addr.addr),
|
||||
@@ -688,5 +737,113 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
|
||||
EXPECT_EQ(sender_addr_in->sin_addr.s_addr, src_addr_in->sin_addr.s_addr);
|
||||
}
|
||||
|
||||
// Check that when setting the IP_MULTICAST_IF option to both an index pointing
|
||||
// to the loopback interface and an address pointing to the non-loopback
|
||||
// interface, a multicast packet sent out uses the latter as its source address.
|
||||
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
|
||||
IpMulticastLoopbackIfNicAndAddr) {
|
||||
// FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
|
||||
// IPv4 address on eth0.
|
||||
SKIP_IF(!got_if_infos_);
|
||||
|
||||
// Create receiver, bind to ANY and join the multicast group.
|
||||
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
auto receiver_addr = V4Any();
|
||||
ASSERT_THAT(
|
||||
bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
ASSERT_THAT(getsockname(receiver->get(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
&receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
|
||||
int receiver_port =
|
||||
reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port;
|
||||
ip_mreqn group = {};
|
||||
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
|
||||
group.imr_ifindex = lo_if_idx_;
|
||||
ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
|
||||
sizeof(group)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Set outgoing multicast interface config, with NIC and addr pointing to
|
||||
// different interfaces.
|
||||
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
ip_mreqn iface = {};
|
||||
iface.imr_ifindex = lo_if_idx_;
|
||||
iface.imr_address = eth_if_sin_addr_;
|
||||
ASSERT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
|
||||
sizeof(iface)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Send a multicast packet.
|
||||
auto sendto_addr = V4Multicast();
|
||||
reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = receiver_port;
|
||||
char send_buf[4] = {};
|
||||
ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0,
|
||||
reinterpret_cast<sockaddr*>(&sendto_addr.addr),
|
||||
sendto_addr.addr_len),
|
||||
SyscallSucceedsWithValue(sizeof(send_buf)));
|
||||
|
||||
// Receive a multicast packet.
|
||||
char recv_buf[sizeof(send_buf)] = {};
|
||||
auto src_addr = V4EmptyAddress();
|
||||
ASSERT_THAT(
|
||||
RetryEINTR(recvfrom)(receiver->get(), recv_buf, sizeof(recv_buf), 0,
|
||||
reinterpret_cast<sockaddr*>(&src_addr.addr),
|
||||
&src_addr.addr_len),
|
||||
SyscallSucceedsWithValue(sizeof(recv_buf)));
|
||||
ASSERT_EQ(sizeof(struct sockaddr_in), src_addr.addr_len);
|
||||
sockaddr_in* src_addr_in = reinterpret_cast<sockaddr_in*>(&src_addr.addr);
|
||||
|
||||
// FIXME (b/137781162): When sending a multicast packet use the proper logic
|
||||
// to determine the packet's src-IP.
|
||||
SKIP_IF(IsRunningOnGvisor());
|
||||
|
||||
// Verify the received source address.
|
||||
EXPECT_EQ(eth_if_sin_addr_.s_addr, src_addr_in->sin_addr.s_addr);
|
||||
}
|
||||
|
||||
// Check that when we are bound to one interface we can set IP_MULTICAST_IF to
|
||||
// another interface.
|
||||
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
|
||||
IpMulticastLoopbackBindToOneIfSetMcastIfToAnother) {
|
||||
// FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
|
||||
// IPv4 address on eth0.
|
||||
SKIP_IF(!got_if_infos_);
|
||||
|
||||
// FIXME (b/137790511): When bound to one interface it is not possible to set
|
||||
// IP_MULTICAST_IF to a different interface.
|
||||
SKIP_IF(IsRunningOnGvisor());
|
||||
|
||||
// Create sender and bind to eth interface.
|
||||
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
ASSERT_THAT(bind(sender->get(), eth_if_addr_, sizeof(sockaddr_in)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Run through all possible combinations of index and address for
|
||||
// IP_MULTICAST_IF that selects the loopback interface.
|
||||
struct {
|
||||
int imr_ifindex;
|
||||
struct in_addr imr_address;
|
||||
} test_data[] = {
|
||||
{lo_if_idx_, {}},
|
||||
{0, lo_if_sin_addr_},
|
||||
{lo_if_idx_, lo_if_sin_addr_},
|
||||
{lo_if_idx_, eth_if_sin_addr_},
|
||||
};
|
||||
for (auto t : test_data) {
|
||||
ip_mreqn iface = {};
|
||||
iface.imr_ifindex = t.imr_ifindex;
|
||||
iface.imr_address = t.imr_address;
|
||||
EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
|
||||
sizeof(iface)),
|
||||
SyscallSucceeds())
|
||||
<< "imr_index=" << iface.imr_ifindex
|
||||
<< " imr_address=" << GetAddr4Str(&iface.imr_address);
|
||||
}
|
||||
}
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_
|
||||
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_
|
||||
|
||||
#include "test/syscalls/linux/ip_socket_test_util.h"
|
||||
#include "test/syscalls/linux/socket_test_util.h"
|
||||
|
||||
namespace gvisor {
|
||||
@@ -22,7 +23,24 @@ namespace testing {
|
||||
|
||||
// Test fixture for tests that apply to unbound IPv4 UDP sockets in a sandbox
|
||||
// with external networking support.
|
||||
using IPv4UDPUnboundExternalNetworkingSocketTest = SimpleSocketTest;
|
||||
class IPv4UDPUnboundExternalNetworkingSocketTest : public SimpleSocketTest {
|
||||
protected:
|
||||
void SetUp();
|
||||
|
||||
IfAddrHelper if_helper_;
|
||||
|
||||
// got_if_infos_ is set to false if SetUp() could not obtain all interface
|
||||
// infos that we need.
|
||||
bool got_if_infos_;
|
||||
|
||||
// Interface infos.
|
||||
int lo_if_idx_;
|
||||
int eth_if_idx_;
|
||||
sockaddr* lo_if_addr_;
|
||||
sockaddr* eth_if_addr_;
|
||||
in_addr lo_if_sin_addr_;
|
||||
in_addr eth_if_sin_addr_;
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
Reference in New Issue
Block a user