|  | // Copyright (c) 2019 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "quic/qbone/bonnet/tun_device.h" | 
|  |  | 
|  | #include <linux/if.h> | 
|  | #include <linux/if_tun.h> | 
|  | #include <sys/ioctl.h> | 
|  |  | 
|  | #include "quic/platform/api/quic_test.h" | 
|  | #include "quic/qbone/platform/mock_kernel.h" | 
|  |  | 
|  | namespace quic { | 
|  | namespace { | 
|  |  | 
|  | using ::testing::_; | 
|  | using ::testing::AnyNumber; | 
|  | using ::testing::Invoke; | 
|  | using ::testing::Return; | 
|  | using ::testing::StrEq; | 
|  | using ::testing::Unused; | 
|  |  | 
|  | const char kDeviceName[] = "tun0"; | 
|  | const int kSupportedFeatures = | 
|  | IFF_TUN | IFF_TAP | IFF_MULTI_QUEUE | IFF_ONE_QUEUE | IFF_NO_PI; | 
|  |  | 
|  | // Quite a bit of EXPECT_CALL().Times(AnyNumber()).WillRepeatedly() are used to | 
|  | // make sure we can correctly set common expectations and override the | 
|  | // expectation with later call to EXPECT_CALL(). ON_CALL cannot be used here | 
|  | // since when EPXECT_CALL overrides ON_CALL, it ignores the parameter matcher | 
|  | // which results in unexpected call even if ON_CALL exists. | 
|  | class TunDeviceTest : public QuicTest { | 
|  | protected: | 
|  | void SetUp() override { | 
|  | EXPECT_CALL(mock_kernel_, socket(AF_INET6, _, _)) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Invoke([this](Unused, Unused, Unused) { | 
|  | EXPECT_CALL(mock_kernel_, close(next_fd_)).WillOnce(Return(0)); | 
|  | return next_fd_++; | 
|  | })); | 
|  | } | 
|  |  | 
|  | // Set the expectations for calling Init(). | 
|  | void SetInitExpectations(int mtu, bool persist) { | 
|  | EXPECT_CALL(mock_kernel_, open(StrEq("/dev/net/tun"), _)) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Invoke([this](Unused, Unused) { | 
|  | EXPECT_CALL(mock_kernel_, close(next_fd_)).WillOnce(Return(0)); | 
|  | return next_fd_++; | 
|  | })); | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, TUNGETFEATURES, _)) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Invoke([](Unused, Unused, void* argp) { | 
|  | auto* actual_flags = reinterpret_cast<int*>(argp); | 
|  | *actual_flags = kSupportedFeatures; | 
|  | return 0; | 
|  | })); | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETIFF, _)) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Invoke([](Unused, Unused, void* argp) { | 
|  | auto* ifr = reinterpret_cast<struct ifreq*>(argp); | 
|  | EXPECT_EQ(IFF_TUN | IFF_MULTI_QUEUE | IFF_NO_PI, ifr->ifr_flags); | 
|  | EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); | 
|  | return 0; | 
|  | })); | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETPERSIST, _)) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Invoke([persist](Unused, Unused, void* argp) { | 
|  | auto* ifr = reinterpret_cast<struct ifreq*>(argp); | 
|  | if (persist) { | 
|  | EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); | 
|  | } else { | 
|  | EXPECT_EQ(nullptr, ifr); | 
|  | } | 
|  | return 0; | 
|  | })); | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, SIOCSIFMTU, _)) | 
|  | .Times(AnyNumber()) | 
|  | .WillRepeatedly(Invoke([mtu](Unused, Unused, void* argp) { | 
|  | auto* ifr = reinterpret_cast<struct ifreq*>(argp); | 
|  | EXPECT_EQ(mtu, ifr->ifr_mtu); | 
|  | EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); | 
|  | return 0; | 
|  | })); | 
|  | } | 
|  |  | 
|  | // Expect that Up() will be called. Force the call to fail when fail == true. | 
|  | void ExpectUp(bool fail) { | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, SIOCSIFFLAGS, _)) | 
|  | .WillOnce(Invoke([fail](Unused, Unused, void* argp) { | 
|  | auto* ifr = reinterpret_cast<struct ifreq*>(argp); | 
|  | EXPECT_TRUE(ifr->ifr_flags & IFF_UP); | 
|  | EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); | 
|  | if (fail) { | 
|  | return -1; | 
|  | } else { | 
|  | return 0; | 
|  | } | 
|  | })); | 
|  | } | 
|  |  | 
|  | // Expect that Down() will be called *after* the interface is up. Force the | 
|  | // call to fail when fail == true. | 
|  | void ExpectDown(bool fail) { | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, SIOCSIFFLAGS, _)) | 
|  | .WillOnce(Invoke([fail](Unused, Unused, void* argp) { | 
|  | auto* ifr = reinterpret_cast<struct ifreq*>(argp); | 
|  | EXPECT_FALSE(ifr->ifr_flags & IFF_UP); | 
|  | EXPECT_THAT(ifr->ifr_name, StrEq(kDeviceName)); | 
|  | if (fail) { | 
|  | return -1; | 
|  | } else { | 
|  | return 0; | 
|  | } | 
|  | })); | 
|  | } | 
|  |  | 
|  | MockKernel mock_kernel_; | 
|  | int next_fd_ = 100; | 
|  | }; | 
|  |  | 
|  | // A TunTapDevice can be initialized and up | 
|  | TEST_F(TunDeviceTest, BasicWorkFlow) { | 
|  | SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); | 
|  | TunTapDevice tun_device(kDeviceName, 1500, false, true, false, &mock_kernel_); | 
|  | EXPECT_TRUE(tun_device.Init()); | 
|  | EXPECT_GT(tun_device.GetFileDescriptor(), -1); | 
|  |  | 
|  | ExpectUp(/* fail = */ false); | 
|  | EXPECT_TRUE(tun_device.Up()); | 
|  | ExpectDown(/* fail = */ false); | 
|  | } | 
|  |  | 
|  | TEST_F(TunDeviceTest, FailToOpenTunDevice) { | 
|  | SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); | 
|  | EXPECT_CALL(mock_kernel_, open(StrEq("/dev/net/tun"), _)) | 
|  | .WillOnce(Return(-1)); | 
|  | TunTapDevice tun_device(kDeviceName, 1500, false, true, false, &mock_kernel_); | 
|  | EXPECT_FALSE(tun_device.Init()); | 
|  | EXPECT_EQ(tun_device.GetFileDescriptor(), -1); | 
|  | ExpectDown(false); | 
|  | } | 
|  |  | 
|  | TEST_F(TunDeviceTest, FailToCheckFeature) { | 
|  | SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, TUNGETFEATURES, _)).WillOnce(Return(-1)); | 
|  | TunTapDevice tun_device(kDeviceName, 1500, false, true, false, &mock_kernel_); | 
|  | EXPECT_FALSE(tun_device.Init()); | 
|  | EXPECT_EQ(tun_device.GetFileDescriptor(), -1); | 
|  | ExpectDown(false); | 
|  | } | 
|  |  | 
|  | TEST_F(TunDeviceTest, TooFewFeature) { | 
|  | SetInitExpectations(/* mtu = */ 1500, /* persist = */ false); | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, TUNGETFEATURES, _)) | 
|  | .WillOnce(Invoke([](Unused, Unused, void* argp) { | 
|  | int* actual_features = reinterpret_cast<int*>(argp); | 
|  | *actual_features = IFF_TUN | IFF_ONE_QUEUE; | 
|  | return 0; | 
|  | })); | 
|  | TunTapDevice tun_device(kDeviceName, 1500, false, true, false, &mock_kernel_); | 
|  | EXPECT_FALSE(tun_device.Init()); | 
|  | EXPECT_EQ(tun_device.GetFileDescriptor(), -1); | 
|  | ExpectDown(false); | 
|  | } | 
|  |  | 
|  | TEST_F(TunDeviceTest, FailToSetFlag) { | 
|  | SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETIFF, _)).WillOnce(Return(-1)); | 
|  | TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); | 
|  | EXPECT_FALSE(tun_device.Init()); | 
|  | EXPECT_EQ(tun_device.GetFileDescriptor(), -1); | 
|  | } | 
|  |  | 
|  | TEST_F(TunDeviceTest, FailToPersistDevice) { | 
|  | SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, TUNSETPERSIST, _)).WillOnce(Return(-1)); | 
|  | TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); | 
|  | EXPECT_FALSE(tun_device.Init()); | 
|  | EXPECT_EQ(tun_device.GetFileDescriptor(), -1); | 
|  | } | 
|  |  | 
|  | TEST_F(TunDeviceTest, FailToOpenSocket) { | 
|  | SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); | 
|  | EXPECT_CALL(mock_kernel_, socket(AF_INET6, _, _)).WillOnce(Return(-1)); | 
|  | TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); | 
|  | EXPECT_FALSE(tun_device.Init()); | 
|  | EXPECT_EQ(tun_device.GetFileDescriptor(), -1); | 
|  | } | 
|  |  | 
|  | TEST_F(TunDeviceTest, FailToSetMtu) { | 
|  | SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); | 
|  | EXPECT_CALL(mock_kernel_, ioctl(_, SIOCSIFMTU, _)).WillOnce(Return(-1)); | 
|  | TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); | 
|  | EXPECT_FALSE(tun_device.Init()); | 
|  | EXPECT_EQ(tun_device.GetFileDescriptor(), -1); | 
|  | } | 
|  |  | 
|  | TEST_F(TunDeviceTest, FailToUp) { | 
|  | SetInitExpectations(/* mtu = */ 1500, /* persist = */ true); | 
|  | TunTapDevice tun_device(kDeviceName, 1500, true, true, false, &mock_kernel_); | 
|  | EXPECT_TRUE(tun_device.Init()); | 
|  | EXPECT_GT(tun_device.GetFileDescriptor(), -1); | 
|  |  | 
|  | ExpectUp(/* fail = */ true); | 
|  | EXPECT_FALSE(tun_device.Up()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace quic |