mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add support for IP_RECVORIGDSTADDR IP option.
Fixes #5004 PiperOrigin-RevId: 346643745
This commit is contained in:
committed by
Shentubot
parent
b4af9d4572
commit
92ca72ecb7
@@ -359,13 +359,26 @@ func PackIPPacketInfo(t *kernel.Task, packetInfo tcpip.IPPacketInfo, buf []byte)
|
||||
)
|
||||
}
|
||||
|
||||
// PackOriginalDstAddress packs an IP_RECVORIGINALDSTADDR socket control message.
|
||||
func PackOriginalDstAddress(t *kernel.Task, family int, originalDstAddress tcpip.FullAddress, buf []byte) []byte {
|
||||
p, _ := socket.ConvertAddress(family, originalDstAddress)
|
||||
level := uint32(linux.SOL_IP)
|
||||
optType := uint32(linux.IP_RECVORIGDSTADDR)
|
||||
if family == linux.AF_INET6 {
|
||||
level = linux.SOL_IPV6
|
||||
optType = linux.IPV6_RECVORIGDSTADDR
|
||||
}
|
||||
return putCmsgStruct(
|
||||
buf, level, optType, t.Arch().Width(), p)
|
||||
}
|
||||
|
||||
// PackControlMessages packs control messages into the given buffer.
|
||||
//
|
||||
// We skip control messages specific to Unix domain sockets.
|
||||
//
|
||||
// Note that some control messages may be truncated if they do not fit under
|
||||
// the capacity of buf.
|
||||
func PackControlMessages(t *kernel.Task, cmsgs socket.ControlMessages, buf []byte) []byte {
|
||||
func PackControlMessages(t *kernel.Task, family int, cmsgs socket.ControlMessages, buf []byte) []byte {
|
||||
if cmsgs.IP.HasTimestamp {
|
||||
buf = PackTimestamp(t, cmsgs.IP.Timestamp, buf)
|
||||
}
|
||||
@@ -387,6 +400,10 @@ func PackControlMessages(t *kernel.Task, cmsgs socket.ControlMessages, buf []byt
|
||||
buf = PackIPPacketInfo(t, cmsgs.IP.PacketInfo, buf)
|
||||
}
|
||||
|
||||
if cmsgs.IP.HasOriginalDstAddress {
|
||||
buf = PackOriginalDstAddress(t, family, cmsgs.IP.OriginalDstAddress, buf)
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@ func (s *socketOpsCommon) SendMsg(t *kernel.Task, src usermem.IOSequence, to []b
|
||||
}
|
||||
controlBuf := make([]byte, 0, space)
|
||||
// PackControlMessages will append up to space bytes to controlBuf.
|
||||
controlBuf = control.PackControlMessages(t, controlMessages, controlBuf)
|
||||
controlBuf = control.PackControlMessages(t, s.family, controlMessages, controlBuf)
|
||||
|
||||
sendmsgFromBlocks := safemem.WriterFunc(func(srcs safemem.BlockSeq) (uint64, error) {
|
||||
// Refuse to do anything if any part of src.Addrs was unusable.
|
||||
|
||||
@@ -1418,6 +1418,14 @@ func getSockOptIPv6(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name
|
||||
v := primitive.Int32(boolToInt32(ep.SocketOptions().GetReceiveTClass()))
|
||||
return &v, nil
|
||||
|
||||
case linux.IPV6_RECVORIGDSTADDR:
|
||||
if outLen < sizeOfInt32 {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
v := primitive.Int32(boolToInt32(ep.SocketOptions().GetReceiveOriginalDstAddress()))
|
||||
return &v, nil
|
||||
|
||||
case linux.IP6T_ORIGINAL_DST:
|
||||
if outLen < int(binary.Size(linux.SockAddrInet6{})) {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
@@ -1599,6 +1607,14 @@ func getSockOptIP(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name in
|
||||
v := primitive.Int32(boolToInt32(ep.SocketOptions().GetHeaderIncluded()))
|
||||
return &v, nil
|
||||
|
||||
case linux.IP_RECVORIGDSTADDR:
|
||||
if outLen < sizeOfInt32 {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
v := primitive.Int32(boolToInt32(ep.SocketOptions().GetReceiveOriginalDstAddress()))
|
||||
return &v, nil
|
||||
|
||||
case linux.SO_ORIGINAL_DST:
|
||||
if outLen < int(binary.Size(linux.SockAddrInet{})) {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
@@ -2094,6 +2110,15 @@ func setSockOptIPv6(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name
|
||||
|
||||
t.Kernel().EmitUnimplementedEvent(t)
|
||||
|
||||
case linux.IPV6_RECVORIGDSTADDR:
|
||||
if len(optVal) < sizeOfInt32 {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
v := int32(usermem.ByteOrder.Uint32(optVal))
|
||||
|
||||
ep.SocketOptions().SetReceiveOriginalDstAddress(v != 0)
|
||||
return nil
|
||||
|
||||
case linux.IPV6_TCLASS:
|
||||
if len(optVal) < sizeOfInt32 {
|
||||
return syserr.ErrInvalidArgument
|
||||
@@ -2325,6 +2350,18 @@ func setSockOptIP(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name in
|
||||
ep.SocketOptions().SetHeaderIncluded(v != 0)
|
||||
return nil
|
||||
|
||||
case linux.IP_RECVORIGDSTADDR:
|
||||
if len(optVal) == 0 {
|
||||
return nil
|
||||
}
|
||||
v, err := parseIntOrChar(optVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ep.SocketOptions().SetReceiveOriginalDstAddress(v != 0)
|
||||
return nil
|
||||
|
||||
case linux.IPT_SO_SET_REPLACE:
|
||||
if len(optVal) < linux.SizeOfIPTReplace {
|
||||
return syserr.ErrInvalidArgument
|
||||
@@ -2363,7 +2400,6 @@ func setSockOptIP(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name in
|
||||
linux.IP_RECVERR,
|
||||
linux.IP_RECVFRAGSIZE,
|
||||
linux.IP_RECVOPTS,
|
||||
linux.IP_RECVORIGDSTADDR,
|
||||
linux.IP_RECVTTL,
|
||||
linux.IP_RETOPTS,
|
||||
linux.IP_TRANSPARENT,
|
||||
@@ -2441,7 +2477,6 @@ func emitUnimplementedEventIPv6(t *kernel.Task, name int) {
|
||||
linux.IPV6_RECVFRAGSIZE,
|
||||
linux.IPV6_RECVHOPLIMIT,
|
||||
linux.IPV6_RECVHOPOPTS,
|
||||
linux.IPV6_RECVORIGDSTADDR,
|
||||
linux.IPV6_RECVPATHMTU,
|
||||
linux.IPV6_RECVPKTINFO,
|
||||
linux.IPV6_RECVRTHDR,
|
||||
@@ -2746,14 +2781,16 @@ func (s *socketOpsCommon) nonBlockingRead(ctx context.Context, dst usermem.IOSeq
|
||||
func (s *socketOpsCommon) controlMessages() socket.ControlMessages {
|
||||
return socket.ControlMessages{
|
||||
IP: tcpip.ControlMessages{
|
||||
HasTimestamp: s.readCM.HasTimestamp && s.sockOptTimestamp,
|
||||
Timestamp: s.readCM.Timestamp,
|
||||
HasTOS: s.readCM.HasTOS,
|
||||
TOS: s.readCM.TOS,
|
||||
HasTClass: s.readCM.HasTClass,
|
||||
TClass: s.readCM.TClass,
|
||||
HasIPPacketInfo: s.readCM.HasIPPacketInfo,
|
||||
PacketInfo: s.readCM.PacketInfo,
|
||||
HasTimestamp: s.readCM.HasTimestamp && s.sockOptTimestamp,
|
||||
Timestamp: s.readCM.Timestamp,
|
||||
HasTOS: s.readCM.HasTOS,
|
||||
TOS: s.readCM.TOS,
|
||||
HasTClass: s.readCM.HasTClass,
|
||||
TClass: s.readCM.TClass,
|
||||
HasIPPacketInfo: s.readCM.HasIPPacketInfo,
|
||||
PacketInfo: s.readCM.PacketInfo,
|
||||
HasOriginalDstAddress: s.readCM.HasOriginalDstAddress,
|
||||
OriginalDstAddress: s.readCM.OriginalDstAddress,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,8 +784,9 @@ func recvSingleMsg(t *kernel.Task, s socket.Socket, msgPtr usermem.Addr, flags i
|
||||
}
|
||||
defer cms.Release(t)
|
||||
|
||||
family, _, _ := s.Type()
|
||||
controlData := make([]byte, 0, msg.ControlLen)
|
||||
controlData = control.PackControlMessages(t, cms, controlData)
|
||||
controlData = control.PackControlMessages(t, family, cms, controlData)
|
||||
|
||||
if cr, ok := s.(transport.Credentialer); ok && cr.Passcred() {
|
||||
creds, _ := cms.Unix.Credentials.(control.SCMCredentials)
|
||||
|
||||
@@ -787,8 +787,9 @@ func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr usermem.Addr, fla
|
||||
}
|
||||
defer cms.Release(t)
|
||||
|
||||
family, _, _ := s.Type()
|
||||
controlData := make([]byte, 0, msg.ControlLen)
|
||||
controlData = control.PackControlMessages(t, cms, controlData)
|
||||
controlData = control.PackControlMessages(t, family, cms, controlData)
|
||||
|
||||
if cr, ok := s.(transport.Credentialer); ok && cr.Passcred() {
|
||||
creds, _ := cms.Unix.Credentials.(control.SCMCredentials)
|
||||
|
||||
@@ -321,6 +321,19 @@ func ReceiveIPPacketInfo(want tcpip.IPPacketInfo) ControlMessagesChecker {
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiveOriginalDstAddr creates a checker that checks the OriginalDstAddress
|
||||
// field in ControlMessages.
|
||||
func ReceiveOriginalDstAddr(want tcpip.FullAddress) ControlMessagesChecker {
|
||||
return func(t *testing.T, cm tcpip.ControlMessages) {
|
||||
t.Helper()
|
||||
if !cm.HasOriginalDstAddress {
|
||||
t.Errorf("got cm.HasOriginalDstAddress = %t, want = true", cm.HasOriginalDstAddress)
|
||||
} else if diff := cmp.Diff(want, cm.OriginalDstAddress); diff != "" {
|
||||
t.Errorf("OriginalDstAddress mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TOS creates a checker that checks the TOS field.
|
||||
func TOS(tos uint8, label uint32) NetworkChecker {
|
||||
return func(t *testing.T, h []header.Network) {
|
||||
|
||||
@@ -130,6 +130,10 @@ type SocketOptions struct {
|
||||
// corkOptionEnabled is used to specify if data should be held until segments
|
||||
// are full by the TCP transport protocol.
|
||||
corkOptionEnabled uint32
|
||||
|
||||
// receiveOriginalDstAddress is used to specify if the original destination of
|
||||
// the incoming packet should be returned as an ancillary message.
|
||||
receiveOriginalDstAddress uint32
|
||||
}
|
||||
|
||||
// InitHandler initializes the handler. This must be called before using the
|
||||
@@ -302,3 +306,13 @@ func (so *SocketOptions) SetCorkOption(v bool) {
|
||||
storeAtomicBool(&so.corkOptionEnabled, v)
|
||||
so.handler.OnCorkOptionSet(v)
|
||||
}
|
||||
|
||||
// GetReceiveOriginalDstAddress gets value for IP(V6)_RECVORIGDSTADDR option.
|
||||
func (so *SocketOptions) GetReceiveOriginalDstAddress() bool {
|
||||
return atomic.LoadUint32(&so.receiveOriginalDstAddress) != 0
|
||||
}
|
||||
|
||||
// SetReceiveOriginalDstAddress sets value for IP(V6)_RECVORIGDSTADDR option.
|
||||
func (so *SocketOptions) SetReceiveOriginalDstAddress(v bool) {
|
||||
storeAtomicBool(&so.receiveOriginalDstAddress, v)
|
||||
}
|
||||
|
||||
@@ -492,6 +492,14 @@ type ControlMessages struct {
|
||||
|
||||
// PacketInfo holds interface and address data on an incoming packet.
|
||||
PacketInfo IPPacketInfo
|
||||
|
||||
// HasOriginalDestinationAddress indicates whether OriginalDstAddress is
|
||||
// set.
|
||||
HasOriginalDstAddress bool
|
||||
|
||||
// OriginalDestinationAddress holds the original destination address
|
||||
// and port of the incoming packet.
|
||||
OriginalDstAddress FullAddress
|
||||
}
|
||||
|
||||
// PacketOwner is used to get UID and GID of the packet.
|
||||
|
||||
@@ -30,10 +30,11 @@ import (
|
||||
// +stateify savable
|
||||
type udpPacket struct {
|
||||
udpPacketEntry
|
||||
senderAddress tcpip.FullAddress
|
||||
packetInfo tcpip.IPPacketInfo
|
||||
data buffer.VectorisedView `state:".(buffer.VectorisedView)"`
|
||||
timestamp int64
|
||||
senderAddress tcpip.FullAddress
|
||||
destinationAddress tcpip.FullAddress
|
||||
packetInfo tcpip.IPPacketInfo
|
||||
data buffer.VectorisedView `state:".(buffer.VectorisedView)"`
|
||||
timestamp int64
|
||||
// tos stores either the receiveTOS or receiveTClass value.
|
||||
tos uint8
|
||||
}
|
||||
@@ -323,6 +324,10 @@ func (e *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMess
|
||||
cm.HasIPPacketInfo = true
|
||||
cm.PacketInfo = p.packetInfo
|
||||
}
|
||||
if e.ops.GetReceiveOriginalDstAddress() {
|
||||
cm.HasOriginalDstAddress = true
|
||||
cm.OriginalDstAddress = p.destinationAddress
|
||||
}
|
||||
return p.data.ToView(), cm, nil
|
||||
}
|
||||
|
||||
@@ -1314,6 +1319,11 @@ func (e *endpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketB
|
||||
Addr: id.RemoteAddress,
|
||||
Port: hdr.SourcePort(),
|
||||
},
|
||||
destinationAddress: tcpip.FullAddress{
|
||||
NIC: pkt.NICID,
|
||||
Addr: id.LocalAddress,
|
||||
Port: header.UDP(hdr).DestinationPort(),
|
||||
},
|
||||
}
|
||||
packet.data = pkt.Data
|
||||
e.rcvList.PushBack(packet)
|
||||
|
||||
@@ -1428,6 +1428,93 @@ func TestReadIPPacketInfo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadRecvOriginalDstAddr(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
proto tcpip.NetworkProtocolNumber
|
||||
flow testFlow
|
||||
expectedOriginalDstAddr tcpip.FullAddress
|
||||
}{
|
||||
{
|
||||
name: "IPv4 unicast",
|
||||
proto: header.IPv4ProtocolNumber,
|
||||
flow: unicastV4,
|
||||
expectedOriginalDstAddr: tcpip.FullAddress{1, stackAddr, stackPort},
|
||||
},
|
||||
{
|
||||
name: "IPv4 multicast",
|
||||
proto: header.IPv4ProtocolNumber,
|
||||
flow: multicastV4,
|
||||
// This should actually be a unicast address assigned to the interface.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/3556): This check is validating incorrect
|
||||
// behaviour. We still include the test so that once the bug is
|
||||
// resolved, this test will start to fail and the individual tasked
|
||||
// with fixing this bug knows to also fix this test :).
|
||||
expectedOriginalDstAddr: tcpip.FullAddress{1, multicastAddr, stackPort},
|
||||
},
|
||||
{
|
||||
name: "IPv4 broadcast",
|
||||
proto: header.IPv4ProtocolNumber,
|
||||
flow: broadcast,
|
||||
// This should actually be a unicast address assigned to the interface.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/3556): This check is validating incorrect
|
||||
// behaviour. We still include the test so that once the bug is
|
||||
// resolved, this test will start to fail and the individual tasked
|
||||
// with fixing this bug knows to also fix this test :).
|
||||
expectedOriginalDstAddr: tcpip.FullAddress{1, broadcastAddr, stackPort},
|
||||
},
|
||||
{
|
||||
name: "IPv6 unicast",
|
||||
proto: header.IPv6ProtocolNumber,
|
||||
flow: unicastV6,
|
||||
expectedOriginalDstAddr: tcpip.FullAddress{1, stackV6Addr, stackPort},
|
||||
},
|
||||
{
|
||||
name: "IPv6 multicast",
|
||||
proto: header.IPv6ProtocolNumber,
|
||||
flow: multicastV6,
|
||||
// This should actually be a unicast address assigned to the interface.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/3556): This check is validating incorrect
|
||||
// behaviour. We still include the test so that once the bug is
|
||||
// resolved, this test will start to fail and the individual tasked
|
||||
// with fixing this bug knows to also fix this test :).
|
||||
expectedOriginalDstAddr: tcpip.FullAddress{1, multicastV6Addr, stackPort},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
|
||||
c.createEndpoint(test.proto)
|
||||
|
||||
bindAddr := tcpip.FullAddress{Port: stackPort}
|
||||
if err := c.ep.Bind(bindAddr); err != nil {
|
||||
t.Fatalf("Bind(%+v): %s", bindAddr, err)
|
||||
}
|
||||
|
||||
if test.flow.isMulticast() {
|
||||
ifoptSet := tcpip.AddMembershipOption{NIC: 1, MulticastAddr: test.flow.getMcastAddr()}
|
||||
if err := c.ep.SetSockOpt(&ifoptSet); err != nil {
|
||||
c.t.Fatalf("SetSockOpt(&%#v): %s:", ifoptSet, err)
|
||||
}
|
||||
}
|
||||
|
||||
c.ep.SocketOptions().SetReceiveOriginalDstAddress(true)
|
||||
|
||||
testRead(c, test.flow, checker.ReceiveOriginalDstAddr(test.expectedOriginalDstAddr))
|
||||
|
||||
if got := c.s.Stats().UDP.PacketsReceived.Value(); got != 1 {
|
||||
t.Fatalf("Read did not increment PacketsReceived: got = %d, want = 1", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteIncrementsPacketsSent(t *testing.T) {
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
|
||||
@@ -2450,6 +2450,27 @@ cc_library(
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "socket_ipv6_udp_unbound_test_cases",
|
||||
testonly = 1,
|
||||
srcs = [
|
||||
"socket_ipv6_udp_unbound.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"socket_ipv6_udp_unbound.h",
|
||||
],
|
||||
deps = [
|
||||
":ip_socket_test_util",
|
||||
":socket_test_util",
|
||||
"@com_google_absl//absl/memory",
|
||||
gtest,
|
||||
"//test/util:posix_error",
|
||||
"//test/util:save_util",
|
||||
"//test/util:test_util",
|
||||
],
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "socket_ipv4_udp_unbound_netlink_test_cases",
|
||||
testonly = 1,
|
||||
@@ -2789,6 +2810,22 @@ cc_binary(
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "socket_ipv6_udp_unbound_loopback_test",
|
||||
testonly = 1,
|
||||
srcs = [
|
||||
"socket_ipv6_udp_unbound_loopback.cc",
|
||||
],
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
":ip_socket_test_util",
|
||||
":socket_ipv6_udp_unbound_test_cases",
|
||||
":socket_test_util",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "socket_ipv4_udp_unbound_loopback_nogotsan_test",
|
||||
testonly = 1,
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
#include "test/syscalls/linux/socket_ip_udp_generic.h"
|
||||
|
||||
#include <errno.h>
|
||||
#ifdef __linux__
|
||||
#include <linux/in6.h>
|
||||
#endif // __linux__
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
@@ -356,6 +359,58 @@ TEST_P(UDPSocketPairTest, SetAndGetIPPKTINFO) {
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
}
|
||||
|
||||
// Test getsockopt for a socket which is not set with IP_RECVORIGDSTADDR option.
|
||||
TEST_P(UDPSocketPairTest, ReceiveOrigDstAddrDefault) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
int level = SOL_IP;
|
||||
int type = IP_RECVORIGDSTADDR;
|
||||
if (sockets->first_addr()->sa_family == AF_INET6) {
|
||||
level = SOL_IPV6;
|
||||
type = IPV6_RECVORIGDSTADDR;
|
||||
}
|
||||
ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOff);
|
||||
}
|
||||
|
||||
// Test setsockopt and getsockopt for a socket with IP_RECVORIGDSTADDR option.
|
||||
TEST_P(UDPSocketPairTest, SetAndGetReceiveOrigDstAddr) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
int level = SOL_IP;
|
||||
int type = IP_RECVORIGDSTADDR;
|
||||
if (sockets->first_addr()->sa_family == AF_INET6) {
|
||||
level = SOL_IPV6;
|
||||
type = IPV6_RECVORIGDSTADDR;
|
||||
}
|
||||
|
||||
// Check getsockopt before IP_PKTINFO is set.
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceedsWithValue(0));
|
||||
|
||||
ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get, kSockOptOn);
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOff,
|
||||
sizeof(kSockOptOff)),
|
||||
SyscallSucceedsWithValue(0));
|
||||
|
||||
ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get, kSockOptOff);
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
}
|
||||
|
||||
// Holds TOS or TClass information for IPv4 or IPv6 respectively.
|
||||
struct RecvTosOption {
|
||||
int level;
|
||||
|
||||
@@ -2222,6 +2222,90 @@ TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPPKTINFO) {
|
||||
EXPECT_EQ(received_pktinfo.ipi_addr.s_addr, htonl(INADDR_LOOPBACK));
|
||||
}
|
||||
|
||||
// Test that socket will receive IP_RECVORIGDSTADDR control message.
|
||||
TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPReceiveOrigDstAddr) {
|
||||
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
auto receiver_addr = V4Loopback();
|
||||
int level = SOL_IP;
|
||||
int type = IP_RECVORIGDSTADDR;
|
||||
|
||||
ASSERT_THAT(
|
||||
bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Retrieve the port bound by the receiver.
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
ASSERT_THAT(getsockname(receiver->get(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
&receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
|
||||
|
||||
ASSERT_THAT(
|
||||
connect(sender->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Get address and port bound by the sender.
|
||||
sockaddr_storage sender_addr_storage;
|
||||
socklen_t sender_addr_len = sizeof(sender_addr_storage);
|
||||
ASSERT_THAT(getsockname(sender->get(),
|
||||
reinterpret_cast<sockaddr*>(&sender_addr_storage),
|
||||
&sender_addr_len),
|
||||
SyscallSucceeds());
|
||||
ASSERT_EQ(sender_addr_len, sizeof(struct sockaddr_in));
|
||||
|
||||
// Enable IP_RECVORIGDSTADDR on socket so that we get the original destination
|
||||
// address of the datagram as auxiliary information in the control message.
|
||||
ASSERT_THAT(
|
||||
setsockopt(receiver->get(), level, type, &kSockOptOn, sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Prepare message to send.
|
||||
constexpr size_t kDataLength = 1024;
|
||||
msghdr sent_msg = {};
|
||||
iovec sent_iov = {};
|
||||
char sent_data[kDataLength];
|
||||
sent_iov.iov_base = sent_data;
|
||||
sent_iov.iov_len = kDataLength;
|
||||
sent_msg.msg_iov = &sent_iov;
|
||||
sent_msg.msg_iovlen = 1;
|
||||
sent_msg.msg_flags = 0;
|
||||
|
||||
ASSERT_THAT(RetryEINTR(sendmsg)(sender->get(), &sent_msg, 0),
|
||||
SyscallSucceedsWithValue(kDataLength));
|
||||
|
||||
msghdr received_msg = {};
|
||||
iovec received_iov = {};
|
||||
char received_data[kDataLength];
|
||||
char received_cmsg_buf[CMSG_SPACE(sizeof(sockaddr_in))] = {};
|
||||
size_t cmsg_data_len = sizeof(sockaddr_in);
|
||||
received_iov.iov_base = received_data;
|
||||
received_iov.iov_len = kDataLength;
|
||||
received_msg.msg_iov = &received_iov;
|
||||
received_msg.msg_iovlen = 1;
|
||||
received_msg.msg_controllen = CMSG_LEN(cmsg_data_len);
|
||||
received_msg.msg_control = received_cmsg_buf;
|
||||
|
||||
ASSERT_THAT(RecvMsgTimeout(receiver->get(), &received_msg, 1 /*timeout*/),
|
||||
IsPosixErrorOkAndHolds(kDataLength));
|
||||
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg);
|
||||
ASSERT_NE(cmsg, nullptr);
|
||||
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len));
|
||||
EXPECT_EQ(cmsg->cmsg_level, level);
|
||||
EXPECT_EQ(cmsg->cmsg_type, type);
|
||||
|
||||
// Check the data
|
||||
sockaddr_in received_addr = {};
|
||||
memcpy(&received_addr, CMSG_DATA(cmsg), sizeof(received_addr));
|
||||
auto orig_receiver_addr = reinterpret_cast<sockaddr_in*>(&receiver_addr.addr);
|
||||
EXPECT_EQ(received_addr.sin_addr.s_addr, orig_receiver_addr->sin_addr.s_addr);
|
||||
EXPECT_EQ(received_addr.sin_port, orig_receiver_addr->sin_port);
|
||||
}
|
||||
|
||||
// Check that setting SO_RCVBUF below min is clamped to the minimum
|
||||
// receive buffer size.
|
||||
TEST_P(IPv4UDPUnboundSocketTest, SetSocketRecvBufBelowMin) {
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "test/syscalls/linux/socket_ipv6_udp_unbound.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#ifdef __linux__
|
||||
#include <linux/in6.h>
|
||||
#endif // __linux__
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "test/syscalls/linux/ip_socket_test_util.h"
|
||||
#include "test/syscalls/linux/socket_test_util.h"
|
||||
#include "test/util/posix_error.h"
|
||||
#include "test/util/save_util.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
// Test that socket will receive IP_RECVORIGDSTADDR control message.
|
||||
TEST_P(IPv6UDPUnboundSocketTest, SetAndReceiveIPReceiveOrigDstAddr) {
|
||||
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
auto receiver_addr = V6Loopback();
|
||||
int level = SOL_IPV6;
|
||||
int type = IPV6_RECVORIGDSTADDR;
|
||||
|
||||
ASSERT_THAT(
|
||||
bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Retrieve the port bound by the receiver.
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
ASSERT_THAT(getsockname(receiver->get(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
&receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
|
||||
|
||||
ASSERT_THAT(
|
||||
connect(sender->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Get address and port bound by the sender.
|
||||
sockaddr_storage sender_addr_storage;
|
||||
socklen_t sender_addr_len = sizeof(sender_addr_storage);
|
||||
ASSERT_THAT(getsockname(sender->get(),
|
||||
reinterpret_cast<sockaddr*>(&sender_addr_storage),
|
||||
&sender_addr_len),
|
||||
SyscallSucceeds());
|
||||
ASSERT_EQ(sender_addr_len, sizeof(struct sockaddr_in6));
|
||||
|
||||
// Enable IP_RECVORIGDSTADDR on socket so that we get the original destination
|
||||
// address of the datagram as auxiliary information in the control message.
|
||||
ASSERT_THAT(
|
||||
setsockopt(receiver->get(), level, type, &kSockOptOn, sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Prepare message to send.
|
||||
constexpr size_t kDataLength = 1024;
|
||||
msghdr sent_msg = {};
|
||||
iovec sent_iov = {};
|
||||
char sent_data[kDataLength];
|
||||
sent_iov.iov_base = sent_data;
|
||||
sent_iov.iov_len = kDataLength;
|
||||
sent_msg.msg_iov = &sent_iov;
|
||||
sent_msg.msg_iovlen = 1;
|
||||
sent_msg.msg_flags = 0;
|
||||
|
||||
ASSERT_THAT(RetryEINTR(sendmsg)(sender->get(), &sent_msg, 0),
|
||||
SyscallSucceedsWithValue(kDataLength));
|
||||
|
||||
msghdr received_msg = {};
|
||||
iovec received_iov = {};
|
||||
char received_data[kDataLength];
|
||||
char received_cmsg_buf[CMSG_SPACE(sizeof(sockaddr_in6))] = {};
|
||||
size_t cmsg_data_len = sizeof(sockaddr_in6);
|
||||
received_iov.iov_base = received_data;
|
||||
received_iov.iov_len = kDataLength;
|
||||
received_msg.msg_iov = &received_iov;
|
||||
received_msg.msg_iovlen = 1;
|
||||
received_msg.msg_controllen = CMSG_LEN(cmsg_data_len);
|
||||
received_msg.msg_control = received_cmsg_buf;
|
||||
|
||||
ASSERT_THAT(RecvMsgTimeout(receiver->get(), &received_msg, 1 /*timeout*/),
|
||||
IsPosixErrorOkAndHolds(kDataLength));
|
||||
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg);
|
||||
ASSERT_NE(cmsg, nullptr);
|
||||
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len));
|
||||
EXPECT_EQ(cmsg->cmsg_level, level);
|
||||
EXPECT_EQ(cmsg->cmsg_type, type);
|
||||
|
||||
// Check that the received address in the control message matches the expected
|
||||
// receiver's address.
|
||||
sockaddr_in6 received_addr = {};
|
||||
memcpy(&received_addr, CMSG_DATA(cmsg), sizeof(received_addr));
|
||||
auto orig_receiver_addr =
|
||||
reinterpret_cast<sockaddr_in6*>(&receiver_addr.addr);
|
||||
EXPECT_EQ(memcmp(&received_addr.sin6_addr, &orig_receiver_addr->sin6_addr,
|
||||
sizeof(in6_addr)),
|
||||
0);
|
||||
EXPECT_EQ(received_addr.sin6_port, orig_receiver_addr->sin6_port);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_
|
||||
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_
|
||||
|
||||
#include "test/syscalls/linux/socket_test_util.h"
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
// Test fixture for tests that apply to IPv6 UDP sockets.
|
||||
using IPv6UDPUnboundSocketTest = SimpleSocketTest;
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "test/syscalls/linux/ip_socket_test_util.h"
|
||||
#include "test/syscalls/linux/socket_ipv6_udp_unbound.h"
|
||||
#include "test/syscalls/linux/socket_test_util.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
IPv6UDPSockets, IPv6UDPUnboundSocketTest,
|
||||
::testing::ValuesIn(ApplyVec<SocketKind>(IPv6UDPUnboundSocket,
|
||||
AllBitwiseCombinations(List<int>{
|
||||
0, SOCK_NONBLOCK}))));
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
Reference in New Issue
Block a user