Enable ARP resolution in TAP devices.

PiperOrigin-RevId: 301208471
This commit is contained in:
Ting-Yu Wang
2020-03-16 12:03:27 -07:00
committed by gVisor bot
parent 159a230b9b
commit 69da42885a
2 changed files with 86 additions and 29 deletions
+8 -2
View File
@@ -98,7 +98,12 @@ func (d *Device) SetIff(s *stack.Stack, name string, flags uint16) error {
prefix = "tap"
}
endpoint, err := attachOrCreateNIC(s, name, prefix)
linkCaps := stack.CapabilityNone
if isTap {
linkCaps |= stack.CapabilityResolutionRequired
}
endpoint, err := attachOrCreateNIC(s, name, prefix, linkCaps)
if err != nil {
return syserror.EINVAL
}
@@ -109,7 +114,7 @@ func (d *Device) SetIff(s *stack.Stack, name string, flags uint16) error {
return nil
}
func attachOrCreateNIC(s *stack.Stack, name, prefix string) (*tunEndpoint, error) {
func attachOrCreateNIC(s *stack.Stack, name, prefix string, linkCaps stack.LinkEndpointCapabilities) (*tunEndpoint, error) {
for {
// 1. Try to attach to an existing NIC.
if name != "" {
@@ -135,6 +140,7 @@ func attachOrCreateNIC(s *stack.Stack, name, prefix string) (*tunEndpoint, error
nicID: id,
name: name,
}
endpoint.Endpoint.LinkEPCapabilities = linkCaps
if endpoint.name == "" {
endpoint.name = fmt.Sprintf("%s%d", prefix, id)
}
+78 -27
View File
@@ -256,6 +256,40 @@ TEST_F(TuntapTest, WriteToDownDevice) {
EXPECT_THAT(write(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EIO));
}
PosixErrorOr<FileDescriptor> OpenAndAttachTap(
const std::string& dev_name, const std::string& dev_ipv4_addr) {
// Interface creation.
ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, Open(kDevNetTun, O_RDWR));
struct ifreq ifr_set = {};
ifr_set.ifr_flags = IFF_TAP;
strncpy(ifr_set.ifr_name, dev_name.c_str(), IFNAMSIZ);
if (ioctl(fd.get(), TUNSETIFF, &ifr_set) < 0) {
return PosixError(errno);
}
ASSIGN_OR_RETURN_ERRNO(absl::optional<Link> link, GetLinkByName(dev_name));
if (!link.has_value()) {
return PosixError(ENOENT, "no link");
}
// Interface setup.
struct in_addr addr;
inet_pton(AF_INET, dev_ipv4_addr.c_str(), &addr);
EXPECT_NO_ERRNO(LinkAddLocalAddr(link->index, AF_INET, /*prefixlen=*/24,
&addr, sizeof(addr)));
if (!IsRunningOnGvisor()) {
// FIXME: gVisor doesn't support setting MAC address on interfaces yet.
RETURN_IF_ERRNO(LinkSetMacAddr(link->index, kMacA, sizeof(kMacA)));
// FIXME: gVisor always creates enabled/up'd interfaces.
RETURN_IF_ERRNO(LinkChangeFlags(link->index, IFF_UP, IFF_UP));
}
return fd;
}
// This test sets up a TAP device and pings kernel by sending ICMP echo request.
//
// It works as the following:
@@ -273,33 +307,8 @@ TEST_F(TuntapTest, WriteToDownDevice) {
TEST_F(TuntapTest, PingKernel) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
// Interface creation.
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR));
struct ifreq ifr_set = {};
ifr_set.ifr_flags = IFF_TAP;
strncpy(ifr_set.ifr_name, kTapName, IFNAMSIZ);
EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr_set),
SyscallSucceedsWithValue(0));
absl::optional<Link> link =
ASSERT_NO_ERRNO_AND_VALUE(GetLinkByName(kTapName));
ASSERT_TRUE(link.has_value());
// Interface setup.
struct in_addr addr;
inet_pton(AF_INET, "10.0.0.1", &addr);
EXPECT_NO_ERRNO(LinkAddLocalAddr(link->index, AF_INET, /*prefixlen=*/24,
&addr, sizeof(addr)));
if (!IsRunningOnGvisor()) {
// FIXME: gVisor doesn't support setting MAC address on interfaces yet.
EXPECT_NO_ERRNO(LinkSetMacAddr(link->index, kMacA, sizeof(kMacA)));
// FIXME: gVisor always creates enabled/up'd interfaces.
EXPECT_NO_ERRNO(LinkChangeFlags(link->index, IFF_UP, IFF_UP));
}
FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, "10.0.0.1"));
ping_pkt ping_req = CreatePingPacket(kMacB, "10.0.0.2", kMacA, "10.0.0.1");
std::string arp_rep = CreateArpPacket(kMacB, "10.0.0.2", kMacA, "10.0.0.1");
@@ -349,5 +358,47 @@ TEST_F(TuntapTest, PingKernel) {
}
}
TEST_F(TuntapTest, SendUdpTriggersArpResolution) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, "10.0.0.1"));
// Send a UDP packet to remote.
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
ASSERT_THAT(sock, SyscallSucceeds());
struct sockaddr_in remote = {};
remote.sin_family = AF_INET;
remote.sin_port = htons(42);
inet_pton(AF_INET, "10.0.0.2", &remote.sin_addr);
int ret = sendto(sock, "hello", 5, 0, reinterpret_cast<sockaddr*>(&remote),
sizeof(remote));
ASSERT_THAT(ret, ::testing::AnyOf(SyscallSucceeds(),
SyscallFailsWithErrno(EHOSTDOWN)));
struct inpkt {
union {
pihdr pi;
arp_pkt arp;
};
};
while (1) {
inpkt r = {};
int n = read(fd.get(), &r, sizeof(r));
EXPECT_THAT(n, SyscallSucceeds());
if (n < sizeof(pihdr)) {
std::cerr << "Ignored packet, protocol: " << r.pi.pi_protocol
<< " len: " << n << std::endl;
continue;
}
if (n >= sizeof(arp_pkt) && r.pi.pi_protocol == htons(ETH_P_ARP)) {
break;
}
}
}
} // namespace testing
} // namespace gvisor