Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions pkg/sentry/socket/netstack/netstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -3638,9 +3638,17 @@ func interfaceIoctl(ctx context.Context, _ usermem.IO, arg int, ifr *linux.IFReq
if addr.Family != linux.AF_INET {
continue
}
// Populate ifr.ifr_addr (type sockaddr_in).
hostarch.ByteOrder.PutUint16(ifr.Data[0:], uint16(linux.AF_INET))
hostarch.ByteOrder.PutUint16(ifr.Data[2:], 0)
copy(ifr.Data[4:8], addr.Addr)
break
return nil

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why return here instead of breaking? The other "success" cases don't return, they allow execution after the switch statement. It doesn't matter today since nothing happens outside the switch, but that might change in the future. Same for line 3692

}
// Linux returns EADDRNOTAVAIL when the device has no IPv4
// address (Linux: net/ipv4/devinet.c:devinet_ioctl()), rather
// than succeeding without writing ifr, which would let the
// caller read back stale ifreq union data.
return syserr.ErrAddressNotAvailable

case linux.SIOCGIFMETRIC:
// Gets the metric of the device. As per netdevice(7), this
Expand Down Expand Up @@ -3681,8 +3689,11 @@ func interfaceIoctl(ctx context.Context, _ usermem.IO, arg int, ifr *linux.IFReq
// Netmask is expected to be returned as a big endian
// value.
binary.BigEndian.PutUint32(ifr.Data[4:8], mask)
break
return nil
}
// Linux returns EADDRNOTAVAIL when the device has no IPv4
// address; see the SIOCGIFADDR case above.
return syserr.ErrAddressNotAvailable

case linux.SIOCETHTOOL:
// Stubbed out for now, Ideally we should implement the required
Expand Down
1 change: 1 addition & 0 deletions test/syscalls/linux/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4117,6 +4117,7 @@ cc_binary(
malloc = "//test/util:errno_safe_allocator",
deps = select_gtest() + [
":socket_netlink_util",
"//test/util:capability_util",
"//test/util:file_descriptor",
"//test/util:socket_util",
"//test/util:test_main",
Expand Down
46 changes: 46 additions & 0 deletions test/syscalls/linux/socket_netdevice.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include <fcntl.h>
#include <linux/ethtool.h>
#include <linux/if_tun.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <cstring>

#include "gtest/gtest.h"
#include "test/syscalls/linux/socket_netlink_util.h"
#include "test/util/capability_util.h"
#include "test/util/linux_capability_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/socket_util.h"
#include "test/util/test_util.h"
Expand Down Expand Up @@ -58,6 +65,45 @@ TEST(NetdeviceTest, Loopback) {
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[5], 0);
}

TEST(NetdeviceTest, InterfaceAddr) {
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));

// Loopback always has 127.0.0.1 assigned, and Linux returns it as a
// sockaddr_in with the family filled in.
struct ifreq ifr = {};
snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
ASSERT_THAT(ioctl(sock.get(), SIOCGIFADDR, &ifr), SyscallSucceeds());
struct sockaddr_in* sin =
reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
EXPECT_EQ(sin->sin_family, AF_INET);
EXPECT_EQ(ntohl(sin->sin_addr.s_addr), INADDR_LOOPBACK);
}

TEST(NetdeviceTest, InterfaceAddrIoctlsNoIPv4Addr) {
// SIOCGIFADDR and SIOCGIFNETMASK fail with EADDRNOTAVAIL on an interface
// that has no IPv4 address assigned, rather than returning success without
// writing ifr, which would let callers read back stale ifreq data.
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));

// Create a tun interface, which starts out with no addresses assigned.
FileDescriptor tun =
ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/net/tun", O_RDWR));
struct ifreq tun_ifr = {};
tun_ifr.ifr_flags = IFF_TUN;
strncpy(tun_ifr.ifr_name, "tun_noaddr", IFNAMSIZ);
ASSERT_THAT(ioctl(tun.get(), TUNSETIFF, &tun_ifr), SyscallSucceeds());

FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
struct ifreq ifr = {};
strncpy(ifr.ifr_name, "tun_noaddr", IFNAMSIZ);
EXPECT_THAT(ioctl(sock.get(), SIOCGIFADDR, &ifr),
SyscallFailsWithErrno(EADDRNOTAVAIL));
EXPECT_THAT(ioctl(sock.get(), SIOCGIFNETMASK, &ifr),
SyscallFailsWithErrno(EADDRNOTAVAIL));
}

TEST(NetdeviceTest, Netmask) {
// We need an interface index to identify the loopback device.
FileDescriptor sock =
Expand Down