mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support sending TTL and HopLimit
PiperOrigin-RevId: 430554985
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user