Support IPV6_RECVPKTINFO on UDP sockets
PiperOrigin-RevId: 397631833
This commit is contained in:
parent
09477db2bc
commit
89a0011c10
|
@ -542,6 +542,15 @@ type ControlMessageIPPacketInfo struct {
|
|||
DestinationAddr InetAddr
|
||||
}
|
||||
|
||||
// ControlMessageIPv6PacketInfo represents struct in6_pktinfo from linux/ipv6.h.
|
||||
//
|
||||
// +marshal
|
||||
// +stateify savable
|
||||
type ControlMessageIPv6PacketInfo struct {
|
||||
Addr Inet6Addr
|
||||
NIC uint32
|
||||
}
|
||||
|
||||
// SizeOfControlMessageCredentials is the binary size of a
|
||||
// ControlMessageCredentials struct.
|
||||
var SizeOfControlMessageCredentials = (*ControlMessageCredentials)(nil).SizeBytes()
|
||||
|
@ -566,6 +575,10 @@ const SizeOfControlMessageTClass = 4
|
|||
// control message.
|
||||
const SizeOfControlMessageIPPacketInfo = 12
|
||||
|
||||
// SizeOfControlMessageIPv6PacketInfo is the size of a
|
||||
// ControlMessageIPv6PacketInfo.
|
||||
const SizeOfControlMessageIPv6PacketInfo = 20
|
||||
|
||||
// SCM_MAX_FD is the maximum number of FDs accepted in a single sendmsg call.
|
||||
// From net/scm.h.
|
||||
const SCM_MAX_FD = 253
|
||||
|
|
|
@ -355,6 +355,17 @@ func PackIPPacketInfo(t *kernel.Task, packetInfo *linux.ControlMessageIPPacketIn
|
|||
)
|
||||
}
|
||||
|
||||
// PackIPv6PacketInfo packs an IPV6_PKTINFO socket control message.
|
||||
func PackIPv6PacketInfo(t *kernel.Task, packetInfo *linux.ControlMessageIPv6PacketInfo, buf []byte) []byte {
|
||||
return putCmsgStruct(
|
||||
buf,
|
||||
linux.SOL_IPV6,
|
||||
linux.IPV6_PKTINFO,
|
||||
t.Arch().Width(),
|
||||
packetInfo,
|
||||
)
|
||||
}
|
||||
|
||||
// PackOriginalDstAddress packs an IP_RECVORIGINALDSTADDR socket control message.
|
||||
func PackOriginalDstAddress(t *kernel.Task, originalDstAddress linux.SockAddr, buf []byte) []byte {
|
||||
var level uint32
|
||||
|
@ -412,6 +423,10 @@ func PackControlMessages(t *kernel.Task, cmsgs socket.ControlMessages, buf []byt
|
|||
buf = PackIPPacketInfo(t, &cmsgs.IP.PacketInfo, buf)
|
||||
}
|
||||
|
||||
if cmsgs.IP.HasIPv6PacketInfo {
|
||||
buf = PackIPv6PacketInfo(t, &cmsgs.IP.IPv6PacketInfo, buf)
|
||||
}
|
||||
|
||||
if cmsgs.IP.OriginalDstAddress != nil {
|
||||
buf = PackOriginalDstAddress(t, cmsgs.IP.OriginalDstAddress, buf)
|
||||
}
|
||||
|
@ -453,6 +468,10 @@ func CmsgsSpace(t *kernel.Task, cmsgs socket.ControlMessages) int {
|
|||
space += cmsgSpace(t, linux.SizeOfControlMessageIPPacketInfo)
|
||||
}
|
||||
|
||||
if cmsgs.IP.HasIPv6PacketInfo {
|
||||
space += cmsgSpace(t, linux.SizeOfControlMessageIPv6PacketInfo)
|
||||
}
|
||||
|
||||
if cmsgs.IP.OriginalDstAddress != nil {
|
||||
space += cmsgSpace(t, cmsgs.IP.OriginalDstAddress.SizeBytes())
|
||||
}
|
||||
|
|
|
@ -1371,6 +1371,14 @@ func getSockOptIPv6(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name
|
|||
v := primitive.Int32(boolToInt32(ep.SocketOptions().GetReceiveOriginalDstAddress()))
|
||||
return &v, nil
|
||||
|
||||
case linux.IPV6_RECVPKTINFO:
|
||||
if outLen < sizeOfInt32 {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
v := primitive.Int32(boolToInt32(ep.SocketOptions().GetIPv6ReceivePacketInfo()))
|
||||
return &v, nil
|
||||
|
||||
case linux.IP6T_ORIGINAL_DST:
|
||||
if outLen < sockAddrInet6Size {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
|
@ -2127,6 +2135,15 @@ func setSockOptIPv6(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name
|
|||
ep.SocketOptions().SetReceiveOriginalDstAddress(v != 0)
|
||||
return nil
|
||||
|
||||
case linux.IPV6_RECVPKTINFO:
|
||||
if len(optVal) < sizeOfInt32 {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
v := int32(hostarch.ByteOrder.Uint32(optVal))
|
||||
|
||||
ep.SocketOptions().SetIPv6ReceivePacketInfo(v != 0)
|
||||
return nil
|
||||
|
||||
case linux.IPV6_TCLASS:
|
||||
if len(optVal) < sizeOfInt32 {
|
||||
return syserr.ErrInvalidArgument
|
||||
|
@ -2516,7 +2533,6 @@ func emitUnimplementedEventIPv6(t *kernel.Task, name int) {
|
|||
linux.IPV6_RECVHOPLIMIT,
|
||||
linux.IPV6_RECVHOPOPTS,
|
||||
linux.IPV6_RECVPATHMTU,
|
||||
linux.IPV6_RECVPKTINFO,
|
||||
linux.IPV6_RECVRTHDR,
|
||||
linux.IPV6_RTHDR,
|
||||
linux.IPV6_RTHDRDSTOPTS,
|
||||
|
@ -2742,6 +2758,8 @@ func (s *socketOpsCommon) controlMessages(cm tcpip.ControlMessages) socket.Contr
|
|||
TClass: readCM.TClass,
|
||||
HasIPPacketInfo: readCM.HasIPPacketInfo,
|
||||
PacketInfo: readCM.PacketInfo,
|
||||
HasIPv6PacketInfo: readCM.HasIPv6PacketInfo,
|
||||
IPv6PacketInfo: readCM.IPv6PacketInfo,
|
||||
OriginalDstAddress: readCM.OriginalDstAddress,
|
||||
SockErr: readCM.SockErr,
|
||||
},
|
||||
|
|
|
@ -56,6 +56,17 @@ func packetInfoToLinux(packetInfo tcpip.IPPacketInfo) linux.ControlMessageIPPack
|
|||
return p
|
||||
}
|
||||
|
||||
// ipv6PacketInfoToLinux converts IPv6PacketInfo from tcpip format to Linux
|
||||
// format.
|
||||
func ipv6PacketInfoToLinux(packetInfo tcpip.IPv6PacketInfo) linux.ControlMessageIPv6PacketInfo {
|
||||
var p linux.ControlMessageIPv6PacketInfo
|
||||
if n := copy(p.Addr[:], []byte(packetInfo.Addr)); n != len(p.Addr) {
|
||||
panic(fmt.Sprintf("got copy(%x, %x) = %d, want = %d", p.Addr, packetInfo.Addr, n, len(p.Addr)))
|
||||
}
|
||||
p.NIC = uint32(packetInfo.NIC)
|
||||
return p
|
||||
}
|
||||
|
||||
// errOriginToLinux maps tcpip socket origin to Linux socket origin constants.
|
||||
func errOriginToLinux(origin tcpip.SockErrOrigin) uint8 {
|
||||
switch origin {
|
||||
|
@ -114,7 +125,7 @@ func NewIPControlMessages(family int, cmgs tcpip.ControlMessages) IPControlMessa
|
|||
if cmgs.HasOriginalDstAddress {
|
||||
orgDstAddr, _ = ConvertAddress(family, cmgs.OriginalDstAddress)
|
||||
}
|
||||
return IPControlMessages{
|
||||
cm := IPControlMessages{
|
||||
HasTimestamp: cmgs.HasTimestamp,
|
||||
Timestamp: cmgs.Timestamp,
|
||||
HasInq: cmgs.HasInq,
|
||||
|
@ -125,9 +136,16 @@ func NewIPControlMessages(family int, cmgs tcpip.ControlMessages) IPControlMessa
|
|||
TClass: cmgs.TClass,
|
||||
HasIPPacketInfo: cmgs.HasIPPacketInfo,
|
||||
PacketInfo: packetInfoToLinux(cmgs.PacketInfo),
|
||||
HasIPv6PacketInfo: cmgs.HasIPv6PacketInfo,
|
||||
OriginalDstAddress: orgDstAddr,
|
||||
SockErr: sockErrCmsgToLinux(cmgs.SockErr),
|
||||
}
|
||||
|
||||
if cm.HasIPv6PacketInfo {
|
||||
cm.IPv6PacketInfo = ipv6PacketInfoToLinux(cmgs.IPv6PacketInfo)
|
||||
}
|
||||
|
||||
return cm
|
||||
}
|
||||
|
||||
// IPControlMessages contains socket control messages for IP sockets.
|
||||
|
@ -166,6 +184,12 @@ type IPControlMessages struct {
|
|||
// PacketInfo holds interface and address data on an incoming packet.
|
||||
PacketInfo linux.ControlMessageIPPacketInfo
|
||||
|
||||
// HasIPv6PacketInfo indicates whether IPv6PacketInfo is set.
|
||||
HasIPv6PacketInfo bool
|
||||
|
||||
// PacketInfo holds interface and address data on an incoming packet.
|
||||
IPv6PacketInfo linux.ControlMessageIPv6PacketInfo
|
||||
|
||||
// OriginalDestinationAddress holds the original destination address
|
||||
// and port of the incoming packet.
|
||||
OriginalDstAddress linux.SockAddr
|
||||
|
|
|
@ -324,6 +324,19 @@ func ReceiveIPPacketInfo(want tcpip.IPPacketInfo) ControlMessagesChecker {
|
|||
}
|
||||
}
|
||||
|
||||
// ReceiveIPv6PacketInfo creates a checker that checks the IPv6PacketInfo field
|
||||
// in ControlMessages.
|
||||
func ReceiveIPv6PacketInfo(want tcpip.IPv6PacketInfo) ControlMessagesChecker {
|
||||
return func(t *testing.T, cm tcpip.ControlMessages) {
|
||||
t.Helper()
|
||||
if !cm.HasIPv6PacketInfo {
|
||||
t.Errorf("got cm.HasIPv6PacketInfo = %t, want = true", cm.HasIPv6PacketInfo)
|
||||
} else if diff := cmp.Diff(want, cm.IPv6PacketInfo); diff != "" {
|
||||
t.Errorf("IPv6PacketInfo mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiveOriginalDstAddr creates a checker that checks the OriginalDstAddress
|
||||
// field in ControlMessages.
|
||||
func ReceiveOriginalDstAddr(want tcpip.FullAddress) ControlMessagesChecker {
|
||||
|
|
|
@ -170,10 +170,14 @@ type SocketOptions struct {
|
|||
// message is passed with incoming packets.
|
||||
receiveTClassEnabled uint32
|
||||
|
||||
// receivePacketInfoEnabled is used to specify if more inforamtion is
|
||||
// provided with incoming packets such as interface index and address.
|
||||
// receivePacketInfoEnabled is used to specify if more information is
|
||||
// provided with incoming IPv4 packets.
|
||||
receivePacketInfoEnabled uint32
|
||||
|
||||
// receivePacketInfoEnabled is used to specify if more information is
|
||||
// provided with incoming IPv6 packets.
|
||||
receiveIPv6PacketInfoEnabled uint32
|
||||
|
||||
// hdrIncludeEnabled is used to indicate for a raw endpoint that all packets
|
||||
// being written have an IP header and the endpoint should not attach an IP
|
||||
// header.
|
||||
|
@ -360,6 +364,16 @@ func (so *SocketOptions) SetReceivePacketInfo(v bool) {
|
|||
storeAtomicBool(&so.receivePacketInfoEnabled, v)
|
||||
}
|
||||
|
||||
// GetIPv6ReceivePacketInfo gets value for IPV6_RECVPKTINFO option.
|
||||
func (so *SocketOptions) GetIPv6ReceivePacketInfo() bool {
|
||||
return atomic.LoadUint32(&so.receiveIPv6PacketInfoEnabled) != 0
|
||||
}
|
||||
|
||||
// SetIPv6ReceivePacketInfo sets value for IPV6_RECVPKTINFO option.
|
||||
func (so *SocketOptions) SetIPv6ReceivePacketInfo(v bool) {
|
||||
storeAtomicBool(&so.receiveIPv6PacketInfoEnabled, v)
|
||||
}
|
||||
|
||||
// GetHeaderIncluded gets value for IP_HDRINCL option.
|
||||
func (so *SocketOptions) GetHeaderIncluded() bool {
|
||||
return atomic.LoadUint32(&so.hdrIncludedEnabled) != 0
|
||||
|
|
|
@ -451,6 +451,12 @@ type ControlMessages struct {
|
|||
// PacketInfo holds interface and address data on an incoming packet.
|
||||
PacketInfo IPPacketInfo
|
||||
|
||||
// HasIPv6PacketInfo indicates whether IPv6PacketInfo is set.
|
||||
HasIPv6PacketInfo bool
|
||||
|
||||
// IPv6PacketInfo holds interface and address data on an incoming packet.
|
||||
IPv6PacketInfo IPv6PacketInfo
|
||||
|
||||
// HasOriginalDestinationAddress indicates whether OriginalDstAddress is
|
||||
// set.
|
||||
HasOriginalDstAddress bool
|
||||
|
@ -1164,6 +1170,14 @@ type IPPacketInfo struct {
|
|||
DestinationAddr Address
|
||||
}
|
||||
|
||||
// IPv6PacketInfo is the message structure for IPV6_PKTINFO.
|
||||
//
|
||||
// +stateify savable
|
||||
type IPv6PacketInfo struct {
|
||||
Addr Address
|
||||
NIC NICID
|
||||
}
|
||||
|
||||
// SendBufferSizeOption is used by stack.(Stack*).Option/SetOption to
|
||||
// get/set the default, min and max send buffer sizes.
|
||||
type SendBufferSizeOption struct {
|
||||
|
|
|
@ -243,19 +243,29 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
|
|||
cm.HasTOS = true
|
||||
cm.TOS = p.tos
|
||||
}
|
||||
|
||||
if e.ops.GetReceivePacketInfo() {
|
||||
cm.HasIPPacketInfo = true
|
||||
cm.PacketInfo = p.packetInfo
|
||||
}
|
||||
case header.IPv6ProtocolNumber:
|
||||
if e.ops.GetReceiveTClass() {
|
||||
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 e.ops.GetIPv6ReceivePacketInfo() {
|
||||
cm.HasIPv6PacketInfo = true
|
||||
cm.IPv6PacketInfo = tcpip.IPv6PacketInfo{
|
||||
NIC: p.packetInfo.NIC,
|
||||
Addr: p.packetInfo.DestinationAddr,
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized network protocol = %d", p.netProto))
|
||||
}
|
||||
if e.ops.GetReceivePacketInfo() {
|
||||
cm.HasIPPacketInfo = true
|
||||
cm.PacketInfo = p.packetInfo
|
||||
}
|
||||
|
||||
if e.ops.GetReceiveOriginalDstAddress() {
|
||||
cm.HasOriginalDstAddress = true
|
||||
cm.OriginalDstAddress = p.destinationAddress
|
||||
|
|
|
@ -1369,64 +1369,70 @@ func TestReadIncrementsPacketsReceived(t *testing.T) {
|
|||
|
||||
func TestReadIPPacketInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
proto tcpip.NetworkProtocolNumber
|
||||
flow testFlow
|
||||
expectedLocalAddr tcpip.Address
|
||||
expectedDestAddr tcpip.Address
|
||||
name string
|
||||
proto tcpip.NetworkProtocolNumber
|
||||
flow testFlow
|
||||
checker func(tcpip.NICID) checker.ControlMessagesChecker
|
||||
}{
|
||||
{
|
||||
name: "IPv4 unicast",
|
||||
proto: header.IPv4ProtocolNumber,
|
||||
flow: unicastV4,
|
||||
expectedLocalAddr: stackAddr,
|
||||
expectedDestAddr: stackAddr,
|
||||
name: "IPv4 unicast",
|
||||
proto: header.IPv4ProtocolNumber,
|
||||
flow: unicastV4,
|
||||
checker: func(id tcpip.NICID) checker.ControlMessagesChecker {
|
||||
return checker.ReceiveIPPacketInfo(tcpip.IPPacketInfo{
|
||||
NIC: id,
|
||||
LocalAddr: stackAddr,
|
||||
DestinationAddr: stackAddr,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
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 :).
|
||||
expectedLocalAddr: multicastAddr,
|
||||
expectedDestAddr: multicastAddr,
|
||||
checker: func(id tcpip.NICID) checker.ControlMessagesChecker {
|
||||
return checker.ReceiveIPPacketInfo(tcpip.IPPacketInfo{
|
||||
NIC: id,
|
||||
// TODO(gvisor.dev/issue/3556): Check for a unicast address.
|
||||
LocalAddr: multicastAddr,
|
||||
DestinationAddr: multicastAddr,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
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 :).
|
||||
expectedLocalAddr: broadcastAddr,
|
||||
expectedDestAddr: broadcastAddr,
|
||||
checker: func(id tcpip.NICID) checker.ControlMessagesChecker {
|
||||
return checker.ReceiveIPPacketInfo(tcpip.IPPacketInfo{
|
||||
NIC: id,
|
||||
// TODO(gvisor.dev/issue/3556): Check for a unicast address.
|
||||
LocalAddr: broadcastAddr,
|
||||
DestinationAddr: broadcastAddr,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv6 unicast",
|
||||
proto: header.IPv6ProtocolNumber,
|
||||
flow: unicastV6,
|
||||
expectedLocalAddr: stackV6Addr,
|
||||
expectedDestAddr: stackV6Addr,
|
||||
name: "IPv6 unicast",
|
||||
proto: header.IPv6ProtocolNumber,
|
||||
flow: unicastV6,
|
||||
checker: func(id tcpip.NICID) checker.ControlMessagesChecker {
|
||||
return checker.ReceiveIPv6PacketInfo(tcpip.IPv6PacketInfo{
|
||||
NIC: id,
|
||||
Addr: stackV6Addr,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
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 :).
|
||||
expectedLocalAddr: multicastV6Addr,
|
||||
expectedDestAddr: multicastV6Addr,
|
||||
checker: func(id tcpip.NICID) checker.ControlMessagesChecker {
|
||||
return checker.ReceiveIPv6PacketInfo(tcpip.IPv6PacketInfo{
|
||||
NIC: id,
|
||||
Addr: multicastV6Addr,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1449,13 +1455,16 @@ func TestReadIPPacketInfo(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
c.ep.SocketOptions().SetReceivePacketInfo(true)
|
||||
switch f := test.flow.netProto(); f {
|
||||
case header.IPv4ProtocolNumber:
|
||||
c.ep.SocketOptions().SetReceivePacketInfo(true)
|
||||
case header.IPv6ProtocolNumber:
|
||||
c.ep.SocketOptions().SetIPv6ReceivePacketInfo(true)
|
||||
default:
|
||||
t.Fatalf("unhandled protocol number = %d", f)
|
||||
}
|
||||
|
||||
testRead(c, test.flow, checker.ReceiveIPPacketInfo(tcpip.IPPacketInfo{
|
||||
NIC: 1,
|
||||
LocalAddr: test.expectedLocalAddr,
|
||||
DestinationAddr: test.expectedDestAddr,
|
||||
}))
|
||||
testRead(c, test.flow, test.checker(c.nicID))
|
||||
|
||||
if got := c.s.Stats().UDP.PacketsReceived.Value(); got != 1 {
|
||||
t.Fatalf("Read did not increment PacketsReceived: got = %d, want = 1", got)
|
||||
|
|
|
@ -39,6 +39,86 @@
|
|||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
using ::testing::IsNull;
|
||||
using ::testing::NotNull;
|
||||
|
||||
TEST_P(IPv6UDPUnboundSocketTest, IPv6PacketInfo) {
|
||||
auto sender_socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
auto receiver_socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
|
||||
auto sender_addr = V6Loopback();
|
||||
ASSERT_THAT(bind(sender_socket->get(), AsSockAddr(&sender_addr.addr),
|
||||
sender_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
|
||||
auto receiver_addr = V6Loopback();
|
||||
ASSERT_THAT(bind(receiver_socket->get(), AsSockAddr(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
ASSERT_THAT(getsockname(receiver_socket->get(),
|
||||
AsSockAddr(&receiver_addr.addr), &receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
ASSERT_EQ(receiver_addr_len, receiver_addr.addr_len);
|
||||
|
||||
// Make sure we get IPv6 packet information as control messages.
|
||||
constexpr int one = 1;
|
||||
ASSERT_THAT(setsockopt(receiver_socket->get(), IPPROTO_IPV6, IPV6_RECVPKTINFO,
|
||||
&one, sizeof(one)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Send a packet - we don't care about the packet itself, only the returned
|
||||
// IPV6_PKTINFO control message.
|
||||
char send_buf[200];
|
||||
RandomizeBuffer(send_buf, sizeof(send_buf));
|
||||
ASSERT_THAT(RetryEINTR(sendto)(
|
||||
sender_socket->get(), send_buf, sizeof(send_buf), 0,
|
||||
AsSockAddr(&receiver_addr.addr), receiver_addr.addr_len),
|
||||
SyscallSucceedsWithValue(sizeof(send_buf)));
|
||||
|
||||
// Check that we received the packet with the packet information control
|
||||
// message.
|
||||
char recv_buf[sizeof(send_buf) + 1];
|
||||
in6_pktinfo received_pktinfo;
|
||||
char recv_cmsg_buf[CMSG_SPACE(sizeof(received_pktinfo))];
|
||||
iovec recv_iov = {
|
||||
.iov_base = recv_buf,
|
||||
.iov_len = sizeof(recv_buf),
|
||||
};
|
||||
msghdr recv_msg = {
|
||||
.msg_iov = &recv_iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = recv_cmsg_buf,
|
||||
.msg_controllen = sizeof(recv_cmsg_buf),
|
||||
};
|
||||
ASSERT_THAT(
|
||||
RetryEINTR(recvmsg)(receiver_socket->get(), &recv_msg, 0 /* flags */),
|
||||
SyscallSucceedsWithValue(sizeof(send_buf)));
|
||||
EXPECT_EQ(memcmp(send_buf, recv_buf, sizeof(send_buf)), 0);
|
||||
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&recv_msg);
|
||||
ASSERT_THAT(cmsg, NotNull());
|
||||
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(in6_pktinfo)));
|
||||
EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IPV6);
|
||||
EXPECT_EQ(cmsg->cmsg_type, IPV6_PKTINFO);
|
||||
// As per cmsg(3) (https://www.man7.org/linux/man-pages/man3/cmsg.3.html),
|
||||
//
|
||||
// CMSG_DATA() returns a pointer to the data portion of a cmsghdr. The
|
||||
// pointer returned cannot be assumed to be suitably aligned for accessing
|
||||
// arbitrary payload data types. Applications should not cast it to a
|
||||
// pointer type matching the payload, but should instead use memcpy(3) to
|
||||
// copy data to or from a suitably declared object.
|
||||
memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(received_pktinfo));
|
||||
EXPECT_EQ(
|
||||
memcmp(&received_pktinfo.ipi6_addr,
|
||||
&(reinterpret_cast<sockaddr_in6*>(&sender_addr.addr)->sin6_addr),
|
||||
sizeof(received_pktinfo.ipi6_addr)),
|
||||
0);
|
||||
EXPECT_EQ(received_pktinfo.ipi6_ifindex,
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
|
||||
EXPECT_THAT(CMSG_NXTHDR(&recv_msg, cmsg), IsNull());
|
||||
}
|
||||
|
||||
// Test that socket will receive IP_RECVORIGDSTADDR control message.
|
||||
TEST_P(IPv6UDPUnboundSocketTest, SetAndReceiveIPReceiveOrigDstAddr) {
|
||||
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
|
|
Loading…
Reference in New Issue