mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Enable IPV6_RECVTCLASS socket option for datagram sockets
Added the ability to get/set the IP_RECVTCLASS socket option on UDP endpoints. If enabled, traffic class from the incoming Network Header passed as ancillary data in the ControlMessages. Adding Get/SetSockOptBool to decrease the overhead of getting/setting simple options. (This was absorbed in a CL that will be landing before this one). Test: * Added unit test to udp_test.go that tests getting/setting as well as verifying that we receive expected TOS from incoming packet. * Added a syscall test for verifying getting/setting * Removed test skip for existing syscall test to enable end to end test. PiperOrigin-RevId: 295840218
This commit is contained in:
@@ -329,7 +329,7 @@ func PackTOS(t *kernel.Task, tos uint8, buf []byte) []byte {
|
||||
}
|
||||
|
||||
// PackTClass packs an IPV6_TCLASS socket control message.
|
||||
func PackTClass(t *kernel.Task, tClass int32, buf []byte) []byte {
|
||||
func PackTClass(t *kernel.Task, tClass uint32, buf []byte) []byte {
|
||||
return putCmsgStruct(
|
||||
buf,
|
||||
linux.SOL_IPV6,
|
||||
|
||||
@@ -1318,6 +1318,22 @@ func getSockOptIPv6(t *kernel.Task, ep commonEndpoint, name, outLen int) (interf
|
||||
}
|
||||
return ib, nil
|
||||
|
||||
case linux.IPV6_RECVTCLASS:
|
||||
if outLen < sizeOfInt32 {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
v, err := ep.GetSockOptBool(tcpip.ReceiveTClassOption)
|
||||
if err != nil {
|
||||
return nil, syserr.TranslateNetstackError(err)
|
||||
}
|
||||
|
||||
var o int32
|
||||
if v {
|
||||
o = 1
|
||||
}
|
||||
return o, nil
|
||||
|
||||
default:
|
||||
emitUnimplementedEventIPv6(t, name)
|
||||
}
|
||||
@@ -1803,6 +1819,14 @@ func setSockOptIPv6(t *kernel.Task, ep commonEndpoint, name int, optVal []byte)
|
||||
}
|
||||
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.IPv6TrafficClassOption(v)))
|
||||
|
||||
case linux.IPV6_RECVTCLASS:
|
||||
v, err := parseIntOrChar(optVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return syserr.TranslateNetstackError(ep.SetSockOptBool(tcpip.ReceiveTClassOption, v != 0))
|
||||
|
||||
default:
|
||||
emitUnimplementedEventIPv6(t, name)
|
||||
}
|
||||
@@ -2086,7 +2110,6 @@ func emitUnimplementedEventIPv6(t *kernel.Task, name int) {
|
||||
linux.IPV6_RECVPATHMTU,
|
||||
linux.IPV6_RECVPKTINFO,
|
||||
linux.IPV6_RECVRTHDR,
|
||||
linux.IPV6_RECVTCLASS,
|
||||
linux.IPV6_RTHDR,
|
||||
linux.IPV6_RTHDRDSTOPTS,
|
||||
linux.IPV6_TCLASS,
|
||||
@@ -2424,6 +2447,8 @@ func (s *SocketOperations) controlMessages() socket.ControlMessages {
|
||||
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,
|
||||
},
|
||||
|
||||
@@ -161,6 +161,20 @@ func FragmentFlags(flags uint8) NetworkChecker {
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiveTClass creates a checker that checks the TCLASS field in
|
||||
// ControlMessages.
|
||||
func ReceiveTClass(want uint32) ControlMessagesChecker {
|
||||
return func(t *testing.T, cm tcpip.ControlMessages) {
|
||||
t.Helper()
|
||||
if !cm.HasTClass {
|
||||
t.Fatalf("got cm.HasTClass = %t, want cm.TClass = %d", cm.HasTClass, want)
|
||||
}
|
||||
if got := cm.TClass; got != want {
|
||||
t.Fatalf("got cm.TClass = %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiveTOS creates a checker that checks the TOS field in ControlMessages.
|
||||
func ReceiveTOS(want uint8) ControlMessagesChecker {
|
||||
return func(t *testing.T, cm tcpip.ControlMessages) {
|
||||
|
||||
+11
-4
@@ -323,11 +323,11 @@ type ControlMessages struct {
|
||||
// TOS is the IPv4 type of service of the associated packet.
|
||||
TOS uint8
|
||||
|
||||
// HasTClass indicates whether Tclass is valid/set.
|
||||
// HasTClass indicates whether TClass is valid/set.
|
||||
HasTClass bool
|
||||
|
||||
// Tclass is the IPv6 traffic class of the associated packet.
|
||||
TClass int32
|
||||
// TClass is the IPv6 traffic class of the associated packet.
|
||||
TClass uint32
|
||||
|
||||
// HasIPPacketInfo indicates whether PacketInfo is set.
|
||||
HasIPPacketInfo bool
|
||||
@@ -502,9 +502,13 @@ type WriteOptions struct {
|
||||
type SockOptBool int
|
||||
|
||||
const (
|
||||
// ReceiveTClassOption is used by SetSockOpt/GetSockOpt to specify if the
|
||||
// IPV6_TCLASS ancillary message is passed with incoming packets.
|
||||
ReceiveTClassOption SockOptBool = iota
|
||||
|
||||
// ReceiveTOSOption is used by SetSockOpt/GetSockOpt to specify if the TOS
|
||||
// ancillary message is passed with incoming packets.
|
||||
ReceiveTOSOption SockOptBool = iota
|
||||
ReceiveTOSOption
|
||||
|
||||
// V6OnlyOption is used by {G,S}etSockOptBool to specify whether an IPv6
|
||||
// socket is to be restricted to sending and receiving IPv6 packets only.
|
||||
@@ -514,6 +518,9 @@ const (
|
||||
// if more inforamtion is provided with incoming packets such
|
||||
// as interface index and address.
|
||||
ReceiveIPPacketInfoOption
|
||||
|
||||
// TODO(b/146901447): convert existing bool socket options to be handled via
|
||||
// Get/SetSockOptBool
|
||||
)
|
||||
|
||||
// SockOptInt represents socket options which values have the int type.
|
||||
|
||||
@@ -32,7 +32,8 @@ type udpPacket struct {
|
||||
packetInfo tcpip.IPPacketInfo
|
||||
data buffer.VectorisedView `state:".(buffer.VectorisedView)"`
|
||||
timestamp int64
|
||||
tos uint8
|
||||
// tos stores either the receiveTOS or receiveTClass value.
|
||||
tos uint8
|
||||
}
|
||||
|
||||
// EndpointState represents the state of a UDP endpoint.
|
||||
@@ -119,6 +120,10 @@ type endpoint struct {
|
||||
// as ancillary data to ControlMessages on Read.
|
||||
receiveTOS bool
|
||||
|
||||
// receiveTClass determines if the incoming IPv6 TClass header field is
|
||||
// passed as ancillary data to ControlMessages on Read.
|
||||
receiveTClass bool
|
||||
|
||||
// receiveIPPacketInfo determines if the packet info is returned by Read.
|
||||
receiveIPPacketInfo bool
|
||||
|
||||
@@ -258,13 +263,18 @@ func (e *endpoint) Read(addr *tcpip.FullAddress) (buffer.View, tcpip.ControlMess
|
||||
}
|
||||
e.mu.RLock()
|
||||
receiveTOS := e.receiveTOS
|
||||
receiveTClass := e.receiveTClass
|
||||
receiveIPPacketInfo := e.receiveIPPacketInfo
|
||||
e.mu.RUnlock()
|
||||
if receiveTOS {
|
||||
cm.HasTOS = true
|
||||
cm.TOS = p.tos
|
||||
}
|
||||
|
||||
if receiveTClass {
|
||||
cm.HasTClass = true
|
||||
// Although TClass is an 8-bit value it's read in the CMsg as a uint32.
|
||||
cm.TClass = uint32(p.tos)
|
||||
}
|
||||
if receiveIPPacketInfo {
|
||||
cm.HasIPPacketInfo = true
|
||||
cm.PacketInfo = p.packetInfo
|
||||
@@ -490,6 +500,17 @@ func (e *endpoint) SetSockOptBool(opt tcpip.SockOptBool, v bool) *tcpip.Error {
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case tcpip.ReceiveTClassOption:
|
||||
// We only support this option on v6 endpoints.
|
||||
if e.NetProto != header.IPv6ProtocolNumber {
|
||||
return tcpip.ErrNotSupported
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
e.receiveTClass = v
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case tcpip.V6OnlyOption:
|
||||
// We only recognize this option on v6 endpoints.
|
||||
if e.NetProto != header.IPv6ProtocolNumber {
|
||||
@@ -709,6 +730,17 @@ func (e *endpoint) GetSockOptBool(opt tcpip.SockOptBool) (bool, *tcpip.Error) {
|
||||
e.mu.RUnlock()
|
||||
return v, nil
|
||||
|
||||
case tcpip.ReceiveTClassOption:
|
||||
// We only support this option on v6 endpoints.
|
||||
if e.NetProto != header.IPv6ProtocolNumber {
|
||||
return false, tcpip.ErrNotSupported
|
||||
}
|
||||
|
||||
e.mu.RLock()
|
||||
v := e.receiveTClass
|
||||
e.mu.RUnlock()
|
||||
return v, nil
|
||||
|
||||
case tcpip.V6OnlyOption:
|
||||
// We only recognize this option on v6 endpoints.
|
||||
if e.NetProto != header.IPv6ProtocolNumber {
|
||||
@@ -1273,6 +1305,8 @@ func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, pk
|
||||
packet.packetInfo.LocalAddr = r.LocalAddress
|
||||
packet.packetInfo.DestinationAddr = r.RemoteAddress
|
||||
packet.packetInfo.NIC = r.NICID()
|
||||
case header.IPv6ProtocolNumber:
|
||||
packet.tos, _ = header.IPv6(pkt.NetworkHeader).TOS()
|
||||
}
|
||||
|
||||
packet.timestamp = e.stack.NowNanoseconds()
|
||||
|
||||
@@ -409,6 +409,7 @@ func (c *testContext) injectV6Packet(payload []byte, h *header4Tuple, valid bool
|
||||
// Initialize the IP header.
|
||||
ip := header.IPv6(buf)
|
||||
ip.Encode(&header.IPv6Fields{
|
||||
TrafficClass: testTOS,
|
||||
PayloadLength: uint16(header.UDPMinimumSize + len(payload)),
|
||||
NextHeader: uint8(udp.ProtocolNumber),
|
||||
HopLimit: 65,
|
||||
@@ -1336,7 +1337,7 @@ func TestSetTTL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTOSV4(t *testing.T) {
|
||||
func TestSetTOS(t *testing.T) {
|
||||
for _, flow := range []testFlow{unicastV4, multicastV4, broadcast} {
|
||||
t.Run(fmt.Sprintf("flow:%s", flow), func(t *testing.T) {
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
@@ -1347,23 +1348,23 @@ func TestTOSV4(t *testing.T) {
|
||||
const tos = testTOS
|
||||
var v tcpip.IPv4TOSOption
|
||||
if err := c.ep.GetSockOpt(&v); err != nil {
|
||||
c.t.Errorf("GetSockopt failed: %s", err)
|
||||
c.t.Errorf("GetSockopt(%T) failed: %s", v, err)
|
||||
}
|
||||
// Test for expected default value.
|
||||
if v != 0 {
|
||||
c.t.Errorf("got GetSockOpt(...) = %#v, want = %#v", v, 0)
|
||||
c.t.Errorf("got GetSockOpt(%T) = 0x%x, want = 0x%x", v, v, 0)
|
||||
}
|
||||
|
||||
if err := c.ep.SetSockOpt(tcpip.IPv4TOSOption(tos)); err != nil {
|
||||
c.t.Errorf("SetSockOpt(%#v) failed: %s", tcpip.IPv4TOSOption(tos), err)
|
||||
c.t.Errorf("SetSockOpt(%T, 0x%x) failed: %s", v, tcpip.IPv4TOSOption(tos), err)
|
||||
}
|
||||
|
||||
if err := c.ep.GetSockOpt(&v); err != nil {
|
||||
c.t.Errorf("GetSockopt failed: %s", err)
|
||||
c.t.Errorf("GetSockopt(%T) failed: %s", v, err)
|
||||
}
|
||||
|
||||
if want := tcpip.IPv4TOSOption(tos); v != want {
|
||||
c.t.Errorf("got GetSockOpt(...) = %#v, want = %#v", v, want)
|
||||
c.t.Errorf("got GetSockOpt(%T) = 0x%x, want = 0x%x", v, v, want)
|
||||
}
|
||||
|
||||
testWrite(c, flow, checker.TOS(tos, 0))
|
||||
@@ -1371,7 +1372,7 @@ func TestTOSV4(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTOSV6(t *testing.T) {
|
||||
func TestSetTClass(t *testing.T) {
|
||||
for _, flow := range []testFlow{unicastV4in6, unicastV6, unicastV6Only, multicastV4in6, multicastV6, broadcastIn6} {
|
||||
t.Run(fmt.Sprintf("flow:%s", flow), func(t *testing.T) {
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
@@ -1379,71 +1380,92 @@ func TestTOSV6(t *testing.T) {
|
||||
|
||||
c.createEndpointForFlow(flow)
|
||||
|
||||
const tos = testTOS
|
||||
const tClass = testTOS
|
||||
var v tcpip.IPv6TrafficClassOption
|
||||
if err := c.ep.GetSockOpt(&v); err != nil {
|
||||
c.t.Errorf("GetSockopt failed: %s", err)
|
||||
c.t.Errorf("GetSockopt(%T) failed: %s", v, err)
|
||||
}
|
||||
// Test for expected default value.
|
||||
if v != 0 {
|
||||
c.t.Errorf("got GetSockOpt(...) = %#v, want = %#v", v, 0)
|
||||
c.t.Errorf("got GetSockOpt(%T) = 0x%x, want = 0x%x", v, v, 0)
|
||||
}
|
||||
|
||||
if err := c.ep.SetSockOpt(tcpip.IPv6TrafficClassOption(tos)); err != nil {
|
||||
c.t.Errorf("SetSockOpt failed: %s", err)
|
||||
if err := c.ep.SetSockOpt(tcpip.IPv6TrafficClassOption(tClass)); err != nil {
|
||||
c.t.Errorf("SetSockOpt(%T, 0x%x) failed: %s", v, tcpip.IPv6TrafficClassOption(tClass), err)
|
||||
}
|
||||
|
||||
if err := c.ep.GetSockOpt(&v); err != nil {
|
||||
c.t.Errorf("GetSockopt failed: %s", err)
|
||||
c.t.Errorf("GetSockopt(%T) failed: %s", v, err)
|
||||
}
|
||||
|
||||
if want := tcpip.IPv6TrafficClassOption(tos); v != want {
|
||||
c.t.Errorf("got GetSockOpt(...) = %#v, want = %#v", v, want)
|
||||
if want := tcpip.IPv6TrafficClassOption(tClass); v != want {
|
||||
c.t.Errorf("got GetSockOpt(%T) = 0x%x, want = 0x%x", v, v, want)
|
||||
}
|
||||
|
||||
testWrite(c, flow, checker.TOS(tos, 0))
|
||||
// The header getter for TClass is called TOS, so use that checker.
|
||||
testWrite(c, flow, checker.TOS(tClass, 0))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReceiveTOSV4(t *testing.T) {
|
||||
for _, flow := range []testFlow{unicastV4, broadcast} {
|
||||
t.Run(fmt.Sprintf("flow:%s", flow), func(t *testing.T) {
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
func TestReceiveTosTClass(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
getReceiveOption tcpip.SockOptBool
|
||||
tests []testFlow
|
||||
}{
|
||||
{"ReceiveTosOption", tcpip.ReceiveTOSOption, []testFlow{unicastV4, broadcast}},
|
||||
{"ReceiveTClassOption", tcpip.ReceiveTClassOption, []testFlow{unicastV4in6, unicastV6, unicastV6Only, broadcastIn6}},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
for _, flow := range testCase.tests {
|
||||
t.Run(fmt.Sprintf("%s:flow:%s", testCase.name, flow), func(t *testing.T) {
|
||||
c := newDualTestContext(t, defaultMTU)
|
||||
defer c.cleanup()
|
||||
|
||||
c.createEndpointForFlow(flow)
|
||||
c.createEndpointForFlow(flow)
|
||||
option := testCase.getReceiveOption
|
||||
name := testCase.name
|
||||
|
||||
// Verify that setting and reading the option works.
|
||||
v, err := c.ep.GetSockOptBool(tcpip.ReceiveTOSOption)
|
||||
if err != nil {
|
||||
c.t.Fatal("GetSockOptBool(tcpip.ReceiveTOSOption) failed:", err)
|
||||
}
|
||||
// Test for expected default value.
|
||||
if v != false {
|
||||
c.t.Errorf("got GetSockOptBool(tcpip.ReceiveTOSOption) = %t, want = %t", v, false)
|
||||
}
|
||||
// Verify that setting and reading the option works.
|
||||
v, err := c.ep.GetSockOptBool(option)
|
||||
if err != nil {
|
||||
c.t.Errorf("GetSockoptBool(%s) failed: %s", name, err)
|
||||
}
|
||||
// Test for expected default value.
|
||||
if v != false {
|
||||
c.t.Errorf("got GetSockOptBool(%s) = %t, want = %t", name, v, false)
|
||||
}
|
||||
|
||||
want := true
|
||||
if err := c.ep.SetSockOptBool(tcpip.ReceiveTOSOption, want); err != nil {
|
||||
c.t.Fatalf("SetSockOptBool(tcpip.ReceiveTOSOption, %t) failed: %s", want, err)
|
||||
}
|
||||
want := true
|
||||
if err := c.ep.SetSockOptBool(option, want); err != nil {
|
||||
c.t.Fatalf("SetSockOptBool(%s, %t) failed: %s", name, want, err)
|
||||
}
|
||||
|
||||
got, err := c.ep.GetSockOptBool(tcpip.ReceiveTOSOption)
|
||||
if err != nil {
|
||||
c.t.Fatal("GetSockOptBool(tcpip.ReceiveTOSOption) failed:", err)
|
||||
}
|
||||
if got != want {
|
||||
c.t.Fatalf("got GetSockOptBool(tcpip.ReceiveTOSOption) = %t, want = %t", got, want)
|
||||
}
|
||||
got, err := c.ep.GetSockOptBool(option)
|
||||
if err != nil {
|
||||
c.t.Errorf("GetSockoptBool(%s) failed: %s", name, err)
|
||||
}
|
||||
|
||||
// Verify that the correct received TOS is handed through as
|
||||
// ancillary data to the ControlMessages struct.
|
||||
if err := c.ep.Bind(tcpip.FullAddress{Port: stackPort}); err != nil {
|
||||
c.t.Fatal("Bind failed:", err)
|
||||
}
|
||||
testRead(c, flow, checker.ReceiveTOS(testTOS))
|
||||
})
|
||||
if got != want {
|
||||
c.t.Errorf("got GetSockOptBool(%s) = %t, want = %t", name, got, want)
|
||||
}
|
||||
|
||||
// Verify that the correct received TOS or TClass is handed through as
|
||||
// ancillary data to the ControlMessages struct.
|
||||
if err := c.ep.Bind(tcpip.FullAddress{Port: stackPort}); err != nil {
|
||||
c.t.Fatalf("Bind failed: %s", err)
|
||||
}
|
||||
switch option {
|
||||
case tcpip.ReceiveTClassOption:
|
||||
testRead(c, flow, checker.ReceiveTClass(testTOS))
|
||||
case tcpip.ReceiveTOSOption:
|
||||
testRead(c, flow, checker.ReceiveTOS(testTOS))
|
||||
default:
|
||||
t.Fatalf("unknown test variant: %s", name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,20 +84,20 @@ SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type);
|
||||
// SocketPairs created with AF_INET and the given type.
|
||||
SocketPairKind IPv4UDPUnboundSocketPair(int type);
|
||||
|
||||
// IPv4UDPUnboundSocketPair returns a SocketKind that represents
|
||||
// a SimpleSocket created with AF_INET, SOCK_DGRAM, and the given type.
|
||||
// IPv4UDPUnboundSocket returns a SocketKind that represents a SimpleSocket
|
||||
// created with AF_INET, SOCK_DGRAM, and the given type.
|
||||
SocketKind IPv4UDPUnboundSocket(int type);
|
||||
|
||||
// IPv6UDPUnboundSocketPair returns a SocketKind that represents
|
||||
// a SimpleSocket created with AF_INET6, SOCK_DGRAM, and the given type.
|
||||
// IPv6UDPUnboundSocket returns a SocketKind that represents a SimpleSocket
|
||||
// created with AF_INET6, SOCK_DGRAM, and the given type.
|
||||
SocketKind IPv6UDPUnboundSocket(int type);
|
||||
|
||||
// IPv4TCPUnboundSocketPair returns a SocketKind that represents
|
||||
// a SimpleSocket created with AF_INET, SOCK_STREAM and the given type.
|
||||
// IPv4TCPUnboundSocket returns a SocketKind that represents a SimpleSocket
|
||||
// created with AF_INET, SOCK_STREAM and the given type.
|
||||
SocketKind IPv4TCPUnboundSocket(int type);
|
||||
|
||||
// IPv6TCPUnboundSocketPair returns a SocketKind that represents
|
||||
// a SimpleSocket created with AF_INET6, SOCK_STREAM and the given type.
|
||||
// IPv6TCPUnboundSocket returns a SocketKind that represents a SimpleSocket
|
||||
// created with AF_INET6, SOCK_STREAM and the given type.
|
||||
SocketKind IPv6TCPUnboundSocket(int type);
|
||||
|
||||
// IfAddrHelper is a helper class that determines the local interfaces present
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "test/syscalls/linux/socket_ip_udp_generic.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
@@ -209,46 +210,6 @@ TEST_P(UDPSocketPairTest, SetMulticastLoopChar) {
|
||||
EXPECT_EQ(get, kSockOptOn);
|
||||
}
|
||||
|
||||
// Ensure that Receiving TOS is off by default.
|
||||
TEST_P(UDPSocketPairTest, RecvTosDefault) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_RECVTOS, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOff);
|
||||
}
|
||||
|
||||
// Test that setting and getting IP_RECVTOS works as expected.
|
||||
TEST_P(UDPSocketPairTest, SetRecvTos) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_RECVTOS,
|
||||
&kSockOptOff, sizeof(kSockOptOff)),
|
||||
SyscallSucceeds());
|
||||
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_RECVTOS, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOff);
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_RECVTOS,
|
||||
&kSockOptOn, sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_RECVTOS, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOn);
|
||||
}
|
||||
|
||||
TEST_P(UDPSocketPairTest, ReuseAddrDefault) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
@@ -401,5 +362,97 @@ TEST_P(UDPSocketPairTest, SetAndGetIPPKTINFO) {
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
}
|
||||
|
||||
// Holds TOS or TClass information for IPv4 or IPv6 respectively.
|
||||
struct RecvTosOption {
|
||||
int level;
|
||||
int option;
|
||||
};
|
||||
|
||||
RecvTosOption GetRecvTosOption(int domain) {
|
||||
TEST_CHECK(domain == AF_INET || domain == AF_INET6);
|
||||
RecvTosOption opt;
|
||||
switch (domain) {
|
||||
case AF_INET:
|
||||
opt.level = IPPROTO_IP;
|
||||
opt.option = IP_RECVTOS;
|
||||
break;
|
||||
case AF_INET6:
|
||||
opt.level = IPPROTO_IPV6;
|
||||
opt.option = IPV6_RECVTCLASS;
|
||||
break;
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
|
||||
// Ensure that Receiving TOS or TCLASS is off by default.
|
||||
TEST_P(UDPSocketPairTest, RecvTosDefault) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
RecvTosOption t = GetRecvTosOption(GetParam().domain);
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), t.level, t.option, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOff);
|
||||
}
|
||||
|
||||
// Test that setting and getting IP_RECVTOS or IPV6_RECVTCLASS works as
|
||||
// expected.
|
||||
TEST_P(UDPSocketPairTest, SetRecvTos) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
RecvTosOption t = GetRecvTosOption(GetParam().domain);
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), t.level, t.option, &kSockOptOff,
|
||||
sizeof(kSockOptOff)),
|
||||
SyscallSucceeds());
|
||||
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), t.level, t.option, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOff);
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), t.level, t.option, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), t.level, t.option, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOn);
|
||||
}
|
||||
|
||||
// Test that any socket (including IPv6 only) accepts the IPv4 TOS option: this
|
||||
// mirrors behavior in linux.
|
||||
TEST_P(UDPSocketPairTest, TOSRecvMismatch) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
RecvTosOption t = GetRecvTosOption(AF_INET);
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), t.level, t.option, &get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
}
|
||||
|
||||
// Test that an IPv4 socket does not support the IPv6 TClass option.
|
||||
TEST_P(UDPSocketPairTest, TClassRecvMismatch) {
|
||||
// This should only test AF_INET sockets for the mismatch behavior.
|
||||
SKIP_IF(GetParam().domain != AF_INET);
|
||||
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
|
||||
ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IPV6, IPV6_RECVTCLASS,
|
||||
&get, &get_len),
|
||||
SyscallFailsWithErrno(EOPNOTSUPP));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
@@ -1349,9 +1349,6 @@ TEST_P(UdpSocketTest, TimestampIoctlPersistence) {
|
||||
// outgoing packets, and that a receiving socket with IP_RECVTOS or
|
||||
// IPV6_RECVTCLASS will create the corresponding control message.
|
||||
TEST_P(UdpSocketTest, SetAndReceiveTOS) {
|
||||
// TODO(b/144868438): IPV6_RECVTCLASS not supported for netstack.
|
||||
SKIP_IF((GetParam() != AddressFamily::kIpv4) && IsRunningOnGvisor() &&
|
||||
!IsRunningWithHostinet());
|
||||
ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds());
|
||||
ASSERT_THAT(connect(t_, addr_[0], addrlen_), SyscallSucceeds());
|
||||
|
||||
@@ -1422,7 +1419,6 @@ TEST_P(UdpSocketTest, SetAndReceiveTOS) {
|
||||
// TOS byte on outgoing packets, and that a receiving socket with IP_RECVTOS or
|
||||
// IPV6_RECVTCLASS will create the corresponding control message.
|
||||
TEST_P(UdpSocketTest, SendAndReceiveTOS) {
|
||||
// TODO(b/144868438): IPV6_RECVTCLASS not supported for netstack.
|
||||
// TODO(b/146661005): Setting TOS via cmsg not supported for netstack.
|
||||
SKIP_IF(IsRunningOnGvisor() && !IsRunningWithHostinet());
|
||||
ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds());
|
||||
|
||||
Reference in New Issue
Block a user