Support sending TTL and HopLimit

PiperOrigin-RevId: 430554985
This commit is contained in:
Arthur Sfez
2022-02-23 15:40:37 -08:00
committed by gVisor bot
parent 45835a60fd
commit 21dffa8f4c
6 changed files with 142 additions and 57 deletions
+13 -2
View File
@@ -17,6 +17,7 @@
package control
import (
"math"
"time"
"gvisor.dev/gvisor/pkg/abi/linux"
@@ -522,6 +523,10 @@ func CmsgsSpace(t *kernel.Task, cmsgs socket.ControlMessages) int {
}
// Parse parses a raw socket control message into portable objects.
// TODO(https://gvisor.dev/issue/7188): Parse is only called on raw cmsg that
// are used when sending a messages. We should fail with EINVAL when we find a
// non-sendable control messages (such as IP_RECVERR). And the function should
// be renamed to reflect that.
func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint) (socket.ControlMessages, error) {
var (
cmsgs socket.ControlMessages
@@ -601,10 +606,13 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
if length < linux.SizeOfControlMessageTTL {
return socket.ControlMessages{}, linuxerr.EINVAL
}
cmsgs.IP.HasTTL = true
var ttl primitive.Uint32
ttl.UnmarshalUnsafe(buf)
if ttl == 0 || ttl > math.MaxUint8 {
return socket.ControlMessages{}, linuxerr.EINVAL
}
cmsgs.IP.TTL = uint32(ttl)
cmsgs.IP.HasTTL = true
case linux.IP_PKTINFO:
if length < linux.SizeOfControlMessageIPPacketInfo {
@@ -661,9 +669,12 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
if length < linux.SizeOfControlMessageHopLimit {
return socket.ControlMessages{}, linuxerr.EINVAL
}
cmsgs.IP.HasHopLimit = true
var hoplimit primitive.Uint32
hoplimit.UnmarshalUnsafe(buf)
if hoplimit > math.MaxUint8 {
return socket.ControlMessages{}, linuxerr.EINVAL
}
cmsgs.IP.HasHopLimit = true
cmsgs.IP.HopLimit = uint32(hoplimit)
case linux.IPV6_RECVORIGDSTADDR:
+16 -6
View File
@@ -2869,7 +2869,7 @@ func (s *socketOpsCommon) nonBlockingRead(ctx context.Context, dst usermem.IOSeq
flags |= linux.MSG_TRUNC
}
return msgLen, flags, addr, addrLen, s.controlMessages(res.ControlMessages), nil
return msgLen, flags, addr, addrLen, s.netstackToLinuxControlMessages(res.ControlMessages), nil
}
if peek {
@@ -2892,12 +2892,12 @@ func (s *socketOpsCommon) nonBlockingRead(ctx context.Context, dst usermem.IOSeq
s.Endpoint.ModerateRecvBuf(n)
}
cmsg := s.controlMessages(res.ControlMessages)
cmsg := s.netstackToLinuxControlMessages(res.ControlMessages)
s.fillCmsgInq(&cmsg)
return res.Count, 0, nil, 0, cmsg, syserr.TranslateNetstackError(err)
}
func (s *socketOpsCommon) controlMessages(cm tcpip.ReceivableControlMessages) socket.ControlMessages {
func (s *socketOpsCommon) netstackToLinuxControlMessages(cm tcpip.ReceivableControlMessages) socket.ControlMessages {
readCM := socket.NewIPControlMessages(s.family, cm)
return socket.ControlMessages{
IP: socket.IPControlMessages{
@@ -2923,6 +2923,15 @@ func (s *socketOpsCommon) controlMessages(cm tcpip.ReceivableControlMessages) so
}
}
func (s *socketOpsCommon) linuxToNetstackControlMessages(cm socket.ControlMessages) tcpip.SendableControlMessages {
return tcpip.SendableControlMessages{
HasTTL: cm.IP.HasTTL,
TTL: uint8(cm.IP.TTL),
HasHopLimit: cm.IP.HasHopLimit,
HopLimit: uint8(cm.IP.HopLimit),
}
}
// updateTimestamp sets the timestamp for SIOCGSTAMP. It should be called after
// successfully writing packet data out to userspace.
//
@@ -3083,9 +3092,10 @@ func (s *socketOpsCommon) SendMsg(t *kernel.Task, src usermem.IOSequence, to []b
}
opts := tcpip.WriteOptions{
To: addr,
More: flags&linux.MSG_MORE != 0,
EndOfRecord: flags&linux.MSG_EOR != 0,
To: addr,
More: flags&linux.MSG_MORE != 0,
EndOfRecord: flags&linux.MSG_EOR != 0,
ControlMessages: s.linuxToNetstackControlMessages(controlMessages),
}
r := src.Reader(t)
+25 -4
View File
@@ -416,13 +416,28 @@ func (l *LimitedWriter) Write(p []byte) (int, error) {
return n, err
}
// ReceivableControlMessages holds control messages that can be received.
// SendableControlMessages contains socket control messages that can be written.
//
// +stateify savable
type SendableControlMessages struct {
// HasTTL indicates whether TTL is valid/set.
HasTTL bool
// TTL is the IPv4 Time To Live of the associated packet.
TTL uint8
// HasHopLimit indicates whether HopLimit is valid/set.
HasHopLimit bool
// HopLimit is the IPv6 Hop Limit of the associated packet.
HopLimit uint8
}
// ReceivableControlMessages contains socket control messages that can be
// received.
//
// +stateify savable
type ReceivableControlMessages struct {
// HasTimestamp indicates whether Timestamp is valid/set.
HasTimestamp bool
// Timestamp is the time that the last packet used to create the read data
// was received.
Timestamp time.Time `state:".(int64)"`
@@ -451,6 +466,9 @@ type ReceivableControlMessages struct {
// HopLimit is the IPv6 Hop Limit of the associated packet.
HopLimit uint8
// HasTimestamp indicates whether Timestamp is valid/set.
HasTimestamp bool
// HasTClass indicates whether TClass is valid/set.
HasTClass bool
@@ -699,6 +717,9 @@ type WriteOptions struct {
// endpoint. If Atomic is false, then data fetched from the Payloader may be
// discarded if available endpoint buffer space is unsufficient.
Atomic bool
// ControlMessages contains optional overrides used when writing a packet.
ControlMessages SendableControlMessages
}
// SockOptInt represents socket options which values have the int type.
@@ -329,11 +329,22 @@ func (e *Endpoint) AcquireContextForWrite(opts tcpip.WriteOptions) (WriteContext
}
var tos uint8
var ttl uint8
switch netProto := route.NetProto(); netProto {
case header.IPv4ProtocolNumber:
tos = e.ipv4TOS
if opts.ControlMessages.HasTTL {
ttl = opts.ControlMessages.TTL
} else {
ttl = e.calculateTTL(route)
}
case header.IPv6ProtocolNumber:
tos = e.ipv6TClass
if opts.ControlMessages.HasHopLimit {
ttl = opts.ControlMessages.HopLimit
} else {
ttl = e.calculateTTL(route)
}
default:
panic(fmt.Sprintf("invalid protocol number = %d", netProto))
}
@@ -341,7 +352,7 @@ func (e *Endpoint) AcquireContextForWrite(opts tcpip.WriteOptions) (WriteContext
return WriteContext{
transProto: e.transProto,
route: route,
ttl: e.calculateTTL(route),
ttl: ttl,
tos: tos,
owner: e.owner,
}, nil
+76 -40
View File
@@ -1300,44 +1300,63 @@ TEST(RawSocketTest, ReceiveTTL) {
ASSERT_THAT(
bind(raw.get(), reinterpret_cast<const sockaddr*>(&kAddr), sizeof(kAddr)),
SyscallSucceeds());
ASSERT_THAT(connect(raw.get(), reinterpret_cast<const sockaddr*>(&kAddr),
sizeof(kAddr)),
SyscallSucceeds());
constexpr int kArbitraryTTL = 42;
ASSERT_THAT(setsockopt(raw.get(), IPPROTO_IP, IP_TTL, &kArbitraryTTL,
sizeof(kArbitraryTTL)),
SyscallSucceeds());
constexpr char kSendBuf[] = "malformed UDP";
ASSERT_THAT(sendto(raw.get(), kSendBuf, sizeof(kSendBuf), 0 /* flags */,
reinterpret_cast<const sockaddr*>(&kAddr), sizeof(kAddr)),
SyscallSucceedsWithValue(sizeof(kSendBuf)));
char send_buf[] = "malformed UDP";
auto test_recv_ttl = [&](int expected_ttl) {
// Register to receive TTL.
constexpr int kOne = 1;
ASSERT_THAT(
setsockopt(raw.get(), IPPROTO_IP, IP_RECVTTL, &kOne, sizeof(kOne)),
SyscallSucceeds());
// Register to receive TTL.
constexpr int kOne = 1;
ASSERT_THAT(
setsockopt(raw.get(), IPPROTO_IP, IP_RECVTTL, &kOne, sizeof(kOne)),
SyscallSucceeds());
struct {
iphdr ip;
char data[sizeof(send_buf)];
} ABSL_ATTRIBUTE_PACKED recv_buf;
struct {
iphdr ip;
char data[sizeof(kSendBuf)];
} ABSL_ATTRIBUTE_PACKED recv_buf;
int recv_ttl;
size_t recv_buf_len = sizeof(recv_buf);
ASSERT_NO_FATAL_FAILURE(RecvTTL(raw.get(), reinterpret_cast<char*>(&recv_buf),
&recv_buf_len, &recv_ttl));
ASSERT_EQ(recv_buf_len, sizeof(iphdr) + sizeof(kSendBuf));
int recv_ttl;
size_t recv_buf_len = sizeof(recv_buf);
ASSERT_NO_FATAL_FAILURE(RecvTTL(raw.get(),
reinterpret_cast<char*>(&recv_buf),
&recv_buf_len, &recv_ttl));
ASSERT_EQ(recv_buf_len, sizeof(iphdr) + sizeof(send_buf));
EXPECT_EQ(recv_buf.ip.version, static_cast<unsigned int>(IPVERSION));
// IHL holds the number of header bytes in 4 byte units.
EXPECT_EQ(recv_buf.ip.ihl, sizeof(iphdr) / 4);
EXPECT_EQ(ntohs(recv_buf.ip.tot_len), sizeof(iphdr) + sizeof(kSendBuf));
EXPECT_EQ(recv_buf.ip.protocol, IPPROTO_UDP);
EXPECT_EQ(ntohl(recv_buf.ip.saddr), INADDR_LOOPBACK);
EXPECT_EQ(ntohl(recv_buf.ip.daddr), INADDR_LOOPBACK);
EXPECT_EQ(recv_buf.ip.version, static_cast<unsigned int>(IPVERSION));
// IHL holds the number of header bytes in 4 byte units.
EXPECT_EQ(recv_buf.ip.ihl, sizeof(iphdr) / 4);
EXPECT_EQ(ntohs(recv_buf.ip.tot_len), sizeof(iphdr) + sizeof(send_buf));
EXPECT_EQ(recv_buf.ip.protocol, IPPROTO_UDP);
EXPECT_EQ(ntohl(recv_buf.ip.saddr), INADDR_LOOPBACK);
EXPECT_EQ(ntohl(recv_buf.ip.daddr), INADDR_LOOPBACK);
EXPECT_EQ(recv_buf.ip.ttl, static_cast<uint8_t>(expected_ttl));
EXPECT_EQ(memcmp(kSendBuf, &recv_buf.data, sizeof(kSendBuf)), 0);
EXPECT_EQ(memcmp(send_buf, &recv_buf.data, sizeof(send_buf)), 0);
EXPECT_EQ(recv_ttl, kArbitraryTTL);
EXPECT_EQ(recv_ttl, expected_ttl);
};
ASSERT_THAT(send(raw.get(), send_buf, sizeof(send_buf), /*flags=*/0),
SyscallSucceedsWithValue(sizeof(send_buf)));
{
SCOPED_TRACE("receive ttl set by option");
ASSERT_NO_FATAL_FAILURE(test_recv_ttl(kArbitraryTTL));
}
constexpr int kArbitrarySendmsgTTL = kArbitraryTTL + 1;
ASSERT_NO_FATAL_FAILURE(SendTTL(raw.get(), send_buf, size_t(sizeof(send_buf)),
kArbitrarySendmsgTTL));
{
SCOPED_TRACE("receive ttl set by cmsg");
ASSERT_NO_FATAL_FAILURE(test_recv_ttl(kArbitrarySendmsgTTL));
}
}
TEST(RawSocketTest, ReceiveHopLimit) {
@@ -1353,32 +1372,49 @@ TEST(RawSocketTest, ReceiveHopLimit) {
ASSERT_THAT(
bind(raw.get(), reinterpret_cast<const sockaddr*>(&kAddr), sizeof(kAddr)),
SyscallSucceeds());
ASSERT_THAT(connect(raw.get(), reinterpret_cast<const sockaddr*>(&kAddr),
sizeof(kAddr)),
SyscallSucceeds());
constexpr int kArbitraryHopLimit = 42;
ASSERT_THAT(setsockopt(raw.get(), IPPROTO_IPV6, IPV6_UNICAST_HOPS,
&kArbitraryHopLimit, sizeof(kArbitraryHopLimit)),
SyscallSucceeds());
constexpr char send_buf[] = "malformed UDP";
ASSERT_THAT(sendto(raw.get(), send_buf, sizeof(send_buf), 0 /* flags */,
reinterpret_cast<const sockaddr*>(&kAddr), sizeof(kAddr)),
SyscallSucceedsWithValue(sizeof(send_buf)));
// Register to receive HOPLIMIT.
constexpr int kOne = 1;
ASSERT_THAT(setsockopt(raw.get(), IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &kOne,
sizeof(kOne)),
SyscallSucceeds());
char recv_buf[sizeof(send_buf) + 1];
size_t recv_buf_len = sizeof(recv_buf);
int recv_hoplimit;
ASSERT_NO_FATAL_FAILURE(
RecvHopLimit(raw.get(), recv_buf, &recv_buf_len, &recv_hoplimit));
ASSERT_EQ(recv_buf_len, sizeof(send_buf));
char send_buf[] = "malformed UDP";
auto test_recv_hoplimit = [&](int expected_hoplimit) {
char recv_buf[sizeof(send_buf)];
size_t recv_buf_len = sizeof(recv_buf);
int recv_hoplimit;
ASSERT_NO_FATAL_FAILURE(
RecvHopLimit(raw.get(), recv_buf, &recv_buf_len, &recv_hoplimit));
ASSERT_EQ(recv_buf_len, sizeof(send_buf));
EXPECT_EQ(memcmp(send_buf, recv_buf, sizeof(send_buf)), 0);
EXPECT_EQ(recv_hoplimit, kArbitraryHopLimit);
EXPECT_EQ(memcmp(send_buf, recv_buf, sizeof(send_buf)), 0);
EXPECT_EQ(recv_hoplimit, expected_hoplimit);
};
ASSERT_THAT(send(raw.get(), send_buf, sizeof(send_buf), /*flags=*/0),
SyscallSucceedsWithValue(sizeof(send_buf)));
{
SCOPED_TRACE("receive hoplimit set by option");
ASSERT_NO_FATAL_FAILURE(test_recv_hoplimit(kArbitraryHopLimit));
}
constexpr int kArbitrarySendmsgHopLimit = kArbitraryHopLimit + 1;
ASSERT_NO_FATAL_FAILURE(SendHopLimit(raw.get(), send_buf,
size_t(sizeof(send_buf)),
kArbitrarySendmsgHopLimit));
{
SCOPED_TRACE("receive hoplimit set by cmsg");
ASSERT_NO_FATAL_FAILURE(test_recv_hoplimit(kArbitrarySendmsgHopLimit));
}
}
TEST(RawSocketTest, SetIPv6ChecksumError_MultipleOf2) {
-4
View File
@@ -2189,10 +2189,6 @@ TEST_P(UdpSocketControlMessagesTest, SetAndReceiveTTLOrHopLimit) {
}
TEST_P(UdpSocketControlMessagesTest, SendAndReceiveTTLOrHopLimit) {
// TODO(b/146661005): Setting TTL/HopLimit via sendmsg is not supported by
// netstack.
SKIP_IF(IsRunningOnGvisor() && !IsRunningWithHostinet());
// Enable receiving TTL and maybe HOPLIMIT on the receiver.
ASSERT_THAT(setsockopt(server_.get(), SOL_IP, IP_RECVTTL, &kSockOptOn,
sizeof(kSockOptOn)),