Internal change.

PiperOrigin-RevId: 297638665
This commit is contained in:
Nayana Bidari
2020-02-27 11:00:41 -08:00
committed by gVisor bot
parent 8e2b14fecf
commit abf7ebcd38
3 changed files with 167 additions and 18 deletions
+33 -7
View File
@@ -712,14 +712,40 @@ func (s *SocketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking boo
// Bind implements the linux syscall bind(2) for sockets backed by
// tcpip.Endpoint.
func (s *SocketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error {
addr, family, err := AddressAndFamily(sockaddr)
if err != nil {
return err
family := usermem.ByteOrder.Uint16(sockaddr)
var addr tcpip.FullAddress
// Bind for AF_PACKET requires only family, protocol and ifindex.
// In function AddressAndFamily, we check the address length which is
// not needed for AF_PACKET bind.
if family == linux.AF_PACKET {
var a linux.SockAddrLink
if len(sockaddr) < sockAddrLinkSize {
return syserr.ErrInvalidArgument
}
binary.Unmarshal(sockaddr[:sockAddrLinkSize], usermem.ByteOrder, &a)
if a.Protocol != uint16(s.protocol) {
return syserr.ErrInvalidArgument
}
addr = tcpip.FullAddress{
NIC: tcpip.NICID(a.InterfaceIndex),
Addr: tcpip.Address(a.HardwareAddr[:header.EthernetAddressSize]),
}
} else {
var err *syserr.Error
addr, family, err = AddressAndFamily(sockaddr)
if err != nil {
return err
}
if err = s.checkFamily(family, true /* exact */); err != nil {
return err
}
addr = s.mapFamily(addr, family)
}
if err := s.checkFamily(family, true /* exact */); err != nil {
return err
}
addr = s.mapFamily(addr, family)
// Issue the bind request to the endpoint.
return syserr.TranslateNetstackError(s.Endpoint.Bind(addr))
+20 -1
View File
@@ -76,6 +76,7 @@ type endpoint struct {
sndBufSize int
closed bool
stats tcpip.TransportEndpointStats `state:"nosave"`
bound bool
}
// NewEndpoint returns a new packet endpoint.
@@ -125,6 +126,7 @@ func (ep *endpoint) Close() {
}
ep.closed = true
ep.bound = false
ep.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.EventIn | waiter.EventOut)
}
@@ -216,7 +218,24 @@ func (ep *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
// sll_family (should be AF_PACKET), sll_protocol, and sll_ifindex."
// - packet(7).
return tcpip.ErrNotSupported
ep.mu.Lock()
defer ep.mu.Unlock()
if ep.bound {
return tcpip.ErrAlreadyBound
}
// Unregister endpoint with all the nics.
ep.stack.UnregisterPacketEndpoint(0, ep.netProto, ep)
// Bind endpoint to receive packets from specific interface.
if err := ep.stack.RegisterPacketEndpoint(addr.NIC, ep.netProto, ep); err != nil {
return err
}
ep.bound = true
return nil
}
// GetLocalAddress implements tcpip.Endpoint.GetLocalAddress.
+114 -10
View File
@@ -13,6 +13,7 @@
// limitations under the License.
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <linux/capability.h>
#include <linux/if_arp.h>
#include <linux/if_packet.h>
@@ -163,16 +164,11 @@ int CookedPacketTest::GetLoopbackIndex() {
return ifr.ifr_ifindex;
}
// Receive via a packet socket.
TEST_P(CookedPacketTest, Receive) {
// Let's use a simple IP payload: a UDP datagram.
FileDescriptor udp_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
SendUDPMessage(udp_sock.get());
// Receive and verify the message via packet socket on interface.
void ReceiveMessage(int sock, int ifindex) {
// Wait for the socket to become readable.
struct pollfd pfd = {};
pfd.fd = socket_;
pfd.fd = sock;
pfd.events = POLLIN;
EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 2000), SyscallSucceedsWithValue(1));
@@ -182,9 +178,10 @@ TEST_P(CookedPacketTest, Receive) {
char buf[64];
struct sockaddr_ll src = {};
socklen_t src_len = sizeof(src);
ASSERT_THAT(recvfrom(socket_, buf, sizeof(buf), 0,
ASSERT_THAT(recvfrom(sock, buf, sizeof(buf), 0,
reinterpret_cast<struct sockaddr*>(&src), &src_len),
SyscallSucceedsWithValue(packet_size));
// sockaddr_ll ends with an 8 byte physical address field, but ethernet
// addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
// here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns
@@ -194,7 +191,7 @@ TEST_P(CookedPacketTest, Receive) {
// TODO(b/129292371): Verify protocol once we return it.
// Verify the source address.
EXPECT_EQ(src.sll_family, AF_PACKET);
EXPECT_EQ(src.sll_ifindex, GetLoopbackIndex());
EXPECT_EQ(src.sll_ifindex, ifindex);
EXPECT_EQ(src.sll_halen, ETH_ALEN);
// This came from the loopback device, so the address is all 0s.
for (int i = 0; i < src.sll_halen; i++) {
@@ -222,6 +219,18 @@ TEST_P(CookedPacketTest, Receive) {
EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0);
}
// Receive via a packet socket.
TEST_P(CookedPacketTest, Receive) {
// Let's use a simple IP payload: a UDP datagram.
FileDescriptor udp_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
SendUDPMessage(udp_sock.get());
// Receive and verify the data.
int loopback_index = GetLoopbackIndex();
ReceiveMessage(socket_, loopback_index);
}
// Send via a packet socket.
TEST_P(CookedPacketTest, Send) {
// TODO(b/129292371): Remove once we support packet socket writing.
@@ -313,6 +322,101 @@ TEST_P(CookedPacketTest, Send) {
EXPECT_EQ(src.sin_addr.s_addr, htonl(INADDR_LOOPBACK));
}
// Bind and receive via packet socket.
TEST_P(CookedPacketTest, BindReceive) {
struct sockaddr_ll bind_addr = {};
bind_addr.sll_family = AF_PACKET;
bind_addr.sll_protocol = htons(GetParam());
bind_addr.sll_ifindex = GetLoopbackIndex();
ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
// Let's use a simple IP payload: a UDP datagram.
FileDescriptor udp_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
SendUDPMessage(udp_sock.get());
// Receive and verify the data.
ReceiveMessage(socket_, bind_addr.sll_ifindex);
}
// Double Bind socket.
TEST_P(CookedPacketTest, DoubleBind) {
struct sockaddr_ll bind_addr = {};
bind_addr.sll_family = AF_PACKET;
bind_addr.sll_protocol = htons(GetParam());
bind_addr.sll_ifindex = GetLoopbackIndex();
ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
// Binding socket again should fail.
ASSERT_THAT(
bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
sizeof(bind_addr)),
// Linux 4.09 returns EINVAL here, but some time before 4.19 it switched
// to EADDRINUSE.
AnyOf(SyscallFailsWithErrno(EADDRINUSE), SyscallFailsWithErrno(EINVAL)));
}
// Bind and verify we do not receive data on interface which is not bound
TEST_P(CookedPacketTest, BindDrop) {
// Let's use a simple IP payload: a UDP datagram.
FileDescriptor udp_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
struct ifaddrs* if_addr_list = nullptr;
auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
// Get interface other than loopback.
struct ifreq ifr = {};
for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
if (strcmp(i->ifa_name, "lo") != 0) {
strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name));
break;
}
}
// Skip if no interface is available other than loopback.
if (strlen(ifr.ifr_name) == 0) {
GTEST_SKIP();
}
// Get interface index.
EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
EXPECT_NE(ifr.ifr_ifindex, 0);
// Bind to packet socket requires only family, protocol and ifindex.
struct sockaddr_ll bind_addr = {};
bind_addr.sll_family = AF_PACKET;
bind_addr.sll_protocol = htons(GetParam());
bind_addr.sll_ifindex = ifr.ifr_ifindex;
ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
// Send to loopback interface.
struct sockaddr_in dest = {};
dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
dest.sin_family = AF_INET;
dest.sin_port = kPort;
EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0,
reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
SyscallSucceedsWithValue(sizeof(kMessage)));
// Wait and make sure the socket never receives any data.
struct pollfd pfd = {};
pfd.fd = socket_;
pfd.events = POLLIN;
EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0));
}
INSTANTIATE_TEST_SUITE_P(AllInetTests, CookedPacketTest,
::testing::Values(ETH_P_IP, ETH_P_ALL));