diff options
-rw-r--r-- | pkg/tcpip/link/tun/device.go | 16 | ||||
-rw-r--r-- | test/syscalls/linux/tuntap.cc | 44 |
2 files changed, 53 insertions, 7 deletions
diff --git a/pkg/tcpip/link/tun/device.go b/pkg/tcpip/link/tun/device.go index 575a084e2..59ac5cb8c 100644 --- a/pkg/tcpip/link/tun/device.go +++ b/pkg/tcpip/link/tun/device.go @@ -208,6 +208,15 @@ func (d *Device) Write(data []byte) (int64, error) { protocol = pktInfoHdr.Protocol() case ethHdr != nil: protocol = ethHdr.Type() + case d.flags.TUN: + // TUN interface with IFF_NO_PI enabled, thus + // we need to determine protocol from version field + version := data[0] >> 4 + if version == 4 { + protocol = header.IPv4ProtocolNumber + } else if version == 6 { + protocol = header.IPv6ProtocolNumber + } } // Try to determine remote link address, default zero. @@ -265,13 +274,6 @@ func (d *Device) encodePkt(info *channel.PacketInfo) (buffer.View, bool) { vv.AppendView(buffer.View(hdr)) } - // If the packet does not already have link layer header, and the route - // does not exist, we can't compute it. This is possibly a raw packet, tun - // device doesn't support this at the moment. - if info.Pkt.LinkHeader().View().IsEmpty() && len(info.Route.RemoteLinkAddress) == 0 { - return nil, false - } - // Ethernet header (TAP only). if d.flags.TAP { // Add ethernet header if not provided. diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc index 279fe342c..1c74b9724 100644 --- a/test/syscalls/linux/tuntap.cc +++ b/test/syscalls/linux/tuntap.cc @@ -24,6 +24,8 @@ #include <sys/socket.h> #include <sys/types.h> +#include <cstddef> + #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/strings/ascii.h" @@ -44,6 +46,7 @@ constexpr int kIPLen = 4; constexpr const char kDevNetTun[] = "/dev/net/tun"; constexpr const char kTapName[] = "tap0"; +constexpr const char kTunName[] = "tun0"; #define kTapIPAddr htonl(0x0a000001) /* Inet 10.0.0.1 */ #define kTapPeerIPAddr htonl(0x0a000002) /* Inet 10.0.0.2 */ @@ -413,6 +416,47 @@ TEST_F(TuntapTest, SendUdpTriggersArpResolution) { } } +TEST_F(TuntapTest, TUNNoPacketInfo) { + 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_TUN | IFF_NO_PI; + strncpy(ifr_set.ifr_name, kTunName, IFNAMSIZ); + EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr_set), SyscallSucceeds()); + + // Interface setup. + auto link = ASSERT_NO_ERRNO_AND_VALUE(GetLinkByName(kTunName)); + const struct in_addr dev_ipv4_addr = {.s_addr = kTapIPAddr}; + EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, 24, &dev_ipv4_addr, + sizeof(dev_ipv4_addr))); + + ping_pkt ping_req = + CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr); + size_t packet_size = sizeof(ping_req) - offsetof(ping_pkt, ip); + + // Send ICMP query + EXPECT_THAT(write(fd.get(), &ping_req.ip, packet_size), + SyscallSucceedsWithValue(packet_size)); + + // Receive loop to process inbound packets. + while (1) { + ping_pkt ping_resp = {}; + EXPECT_THAT(read(fd.get(), &ping_resp.ip, packet_size), + SyscallSucceedsWithValue(packet_size)); + + // Process ping response packet. + if (!memcmp(&ping_resp.ip.saddr, &ping_req.ip.daddr, kIPLen) && + !memcmp(&ping_resp.ip.daddr, &ping_req.ip.saddr, kIPLen) && + ping_resp.icmp.type == 0 && ping_resp.icmp.code == 0) { + // Ends and passes the test. + break; + } + } +} + // TCPBlockingConnectFailsArpResolution tests for TCP connect to fail on link // address resolution failure to a routable, but non existent peer. TEST_F(TuntapTest, TCPBlockingConnectFailsArpResolution) { |