mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support receiving PKTINFO on icmp endpoints
PiperOrigin-RevId: 428599075
This commit is contained in:
@@ -600,6 +600,16 @@ func Parse(t *kernel.Task, socketOrEndpoint interface{}, buf []byte, width uint)
|
||||
tclass.UnmarshalUnsafe(buf)
|
||||
cmsgs.IP.TClass = uint32(tclass)
|
||||
|
||||
case linux.IPV6_PKTINFO:
|
||||
if length < linux.SizeOfControlMessageIPv6PacketInfo {
|
||||
return socket.ControlMessages{}, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
cmsgs.IP.HasIPv6PacketInfo = true
|
||||
var packetInfo linux.ControlMessageIPv6PacketInfo
|
||||
packetInfo.UnmarshalUnsafe(buf)
|
||||
cmsgs.IP.IPv6PacketInfo = packetInfo
|
||||
|
||||
case linux.IPV6_RECVORIGDSTADDR:
|
||||
var addr linux.SockAddrInet6
|
||||
if length < addr.SizeBytes() {
|
||||
|
||||
@@ -392,7 +392,7 @@ func (s *socketOpsCommon) GetSockOpt(t *kernel.Task, level int, name int, optVal
|
||||
}
|
||||
case linux.SOL_IPV6:
|
||||
switch name {
|
||||
case linux.IPV6_TCLASS, linux.IPV6_RECVTCLASS, linux.IPV6_RECVERR, linux.IPV6_V6ONLY, linux.IPV6_RECVORIGDSTADDR:
|
||||
case linux.IPV6_TCLASS, linux.IPV6_RECVTCLASS, linux.IPV6_RECVPKTINFO, linux.IPV6_RECVERR, linux.IPV6_V6ONLY, linux.IPV6_RECVORIGDSTADDR:
|
||||
optlen = sizeofInt32
|
||||
}
|
||||
case linux.SOL_SOCKET:
|
||||
@@ -449,7 +449,7 @@ func (s *socketOpsCommon) SetSockOpt(t *kernel.Task, level int, name int, opt []
|
||||
}
|
||||
case linux.SOL_IPV6:
|
||||
switch name {
|
||||
case linux.IPV6_TCLASS, linux.IPV6_RECVTCLASS, linux.IPV6_RECVERR, linux.IPV6_V6ONLY, linux.IPV6_RECVORIGDSTADDR:
|
||||
case linux.IPV6_TCLASS, linux.IPV6_RECVTCLASS, linux.IPV6_RECVPKTINFO, linux.IPV6_RECVERR, linux.IPV6_V6ONLY, linux.IPV6_RECVORIGDSTADDR:
|
||||
optlen = sizeofInt32
|
||||
}
|
||||
case linux.SOL_SOCKET:
|
||||
@@ -632,6 +632,12 @@ func parseUnixControlMessages(unixControlMessages []unix.SocketControlMessage) s
|
||||
tclass.UnmarshalUnsafe(unixCmsg.Data)
|
||||
controlMessages.IP.TClass = uint32(tclass)
|
||||
|
||||
case linux.IPV6_PKTINFO:
|
||||
controlMessages.IP.HasIPv6PacketInfo = true
|
||||
var packetInfo linux.ControlMessageIPv6PacketInfo
|
||||
packetInfo.UnmarshalUnsafe(unixCmsg.Data)
|
||||
controlMessages.IP.IPv6PacketInfo = packetInfo
|
||||
|
||||
case linux.IPV6_RECVORIGDSTADDR:
|
||||
var addr linux.SockAddrInet6
|
||||
addr.UnmarshalUnsafe(unixCmsg.Data)
|
||||
|
||||
@@ -346,6 +346,17 @@ func ReceiveIPPacketInfo(want tcpip.IPPacketInfo) ControlMessagesChecker {
|
||||
}
|
||||
}
|
||||
|
||||
// NoIPPacketInfoReceived creates a checker that checks the PacketInfo field in
|
||||
// ControlMessages.
|
||||
func NoIPPacketInfoReceived() ControlMessagesChecker {
|
||||
return func(t *testing.T, cm tcpip.ControlMessages) {
|
||||
t.Helper()
|
||||
if cm.HasIPPacketInfo {
|
||||
t.Error("got cm.HasIPPacketInfo = true, want = false")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiveIPv6PacketInfo creates a checker that checks the IPv6PacketInfo field
|
||||
// in ControlMessages.
|
||||
func ReceiveIPv6PacketInfo(want tcpip.IPv6PacketInfo) ControlMessagesChecker {
|
||||
@@ -359,6 +370,17 @@ func ReceiveIPv6PacketInfo(want tcpip.IPv6PacketInfo) ControlMessagesChecker {
|
||||
}
|
||||
}
|
||||
|
||||
// NoIPv6PacketInfoReceived creates a checker that checks the PacketInfo field
|
||||
// in ControlMessages.
|
||||
func NoIPv6PacketInfoReceived() ControlMessagesChecker {
|
||||
return func(t *testing.T, cm tcpip.ControlMessages) {
|
||||
t.Helper()
|
||||
if cm.HasIPv6PacketInfo {
|
||||
t.Error("got cm.HasIPv6PacketInfo = true, want = false")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiveOriginalDstAddr creates a checker that checks the OriginalDstAddress
|
||||
// field in ControlMessages.
|
||||
func ReceiveOriginalDstAddr(want tcpip.FullAddress) ControlMessagesChecker {
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
type icmpPacket struct {
|
||||
icmpPacketEntry
|
||||
senderAddress tcpip.FullAddress
|
||||
packetInfo tcpip.IPPacketInfo
|
||||
data buffer.VectorisedView `state:".(buffer.VectorisedView)"`
|
||||
receivedAt time.Time `state:".(int64)"`
|
||||
|
||||
@@ -194,12 +195,23 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
|
||||
cm.HasTOS = true
|
||||
cm.TOS = p.tosOrTClass
|
||||
}
|
||||
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.tosOrTClass)
|
||||
}
|
||||
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", netProto))
|
||||
}
|
||||
@@ -696,16 +708,28 @@ func (e *endpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketB
|
||||
|
||||
wasEmpty := e.rcvBufSize == 0
|
||||
|
||||
net := pkt.Network()
|
||||
dstAddr := net.DestinationAddress()
|
||||
// Push new packet into receive list and increment the buffer size.
|
||||
packet := &icmpPacket{
|
||||
senderAddress: tcpip.FullAddress{
|
||||
NIC: pkt.NICID,
|
||||
Addr: id.RemoteAddress,
|
||||
},
|
||||
packetInfo: tcpip.IPPacketInfo{
|
||||
// Linux does not 'prepare' [1] in_pktinfo on socket buffers destined to
|
||||
// ping sockets (unlike UDP/RAW sockets). However the interface index [2]
|
||||
// and the Header Destination Address [3] are always filled.
|
||||
// [1] https://github.com/torvalds/linux/blob/dcb85f85fa6/net/ipv4/ip_sockglue.c#L1392
|
||||
// [2] https://github.com/torvalds/linux/blob/dcb85f85fa6/net/ipv4/ip_input.c#L510
|
||||
// [3] https://github.com/torvalds/linux/blob/dcb85f85fa6/net/ipv4/ip_sockglue.c#L60
|
||||
NIC: pkt.NICID,
|
||||
DestinationAddr: dstAddr,
|
||||
},
|
||||
}
|
||||
|
||||
// Save any useful information from the network header to the packet.
|
||||
packet.tosOrTClass, _ = pkt.Network().TOS()
|
||||
packet.tosOrTClass, _ = net.TOS()
|
||||
|
||||
// ICMP socket's data includes ICMP header.
|
||||
packet.data = pkt.TransportHeader().View().ToVectorisedView()
|
||||
|
||||
@@ -320,34 +320,65 @@ func buildEchoReplyPacket(payload []byte, flow context.TestFlow) (buffer.View, b
|
||||
func TestReceiveControlMessages(t *testing.T) {
|
||||
var payload = [...]byte{0, 1, 2, 3, 4, 5}
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
optionProtocol tcpip.NetworkProtocolNumber
|
||||
getReceiveOption func(tcpip.Endpoint) bool
|
||||
setReceiveOption func(tcpip.Endpoint, bool)
|
||||
presenceChecker checker.ControlMessagesChecker
|
||||
absenceChecker checker.ControlMessagesChecker
|
||||
}{
|
||||
{
|
||||
name: "TOS",
|
||||
optionProtocol: header.IPv4ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTOS() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTOS(value) },
|
||||
presenceChecker: checker.ReceiveTOS(testTOS),
|
||||
absenceChecker: checker.NoTOSReceived(),
|
||||
},
|
||||
{
|
||||
name: "TClass",
|
||||
optionProtocol: header.IPv6ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTClass() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTClass(value) },
|
||||
presenceChecker: checker.ReceiveTClass(testTOS),
|
||||
absenceChecker: checker.NoTClassReceived(),
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, flow := range []context.TestFlow{context.UnicastV4, context.UnicastV6, context.UnicastV6Only, context.MulticastV4, context.MulticastV6, context.MulticastV6Only, context.Broadcast} {
|
||||
t.Run(flow.String(), func(t *testing.T) {
|
||||
for _, flow := range []context.TestFlow{context.UnicastV4, context.UnicastV6, context.UnicastV6Only, context.MulticastV4, context.MulticastV6, context.MulticastV6Only, context.Broadcast} {
|
||||
t.Run(flow.String(), func(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
optionProtocol tcpip.NetworkProtocolNumber
|
||||
getReceiveOption func(tcpip.Endpoint) bool
|
||||
setReceiveOption func(tcpip.Endpoint, bool)
|
||||
presenceChecker checker.ControlMessagesChecker
|
||||
absenceChecker checker.ControlMessagesChecker
|
||||
}{
|
||||
{
|
||||
name: "TOS",
|
||||
optionProtocol: header.IPv4ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTOS() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTOS(value) },
|
||||
presenceChecker: checker.ReceiveTOS(testTOS),
|
||||
absenceChecker: checker.NoTOSReceived(),
|
||||
},
|
||||
{
|
||||
name: "TClass",
|
||||
optionProtocol: header.IPv6ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTClass() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTClass(value) },
|
||||
presenceChecker: checker.ReceiveTClass(testTOS),
|
||||
absenceChecker: checker.NoTClassReceived(),
|
||||
},
|
||||
{
|
||||
name: "IPPacketInfo",
|
||||
optionProtocol: header.IPv4ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceivePacketInfo() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceivePacketInfo(value) },
|
||||
presenceChecker: func() checker.ControlMessagesChecker {
|
||||
h := flow.MakeHeader4Tuple(context.Incoming)
|
||||
return checker.ReceiveIPPacketInfo(tcpip.IPPacketInfo{
|
||||
NIC: context.NICID,
|
||||
// TODO(https://gvisor.dev/issue/3556): Expect the NIC's address
|
||||
// instead of the header destination address for the LocalAddr
|
||||
// field.
|
||||
DestinationAddr: h.Dst.Addr,
|
||||
})
|
||||
}(),
|
||||
absenceChecker: checker.NoIPPacketInfoReceived(),
|
||||
},
|
||||
{
|
||||
name: "IPv6PacketInfo",
|
||||
optionProtocol: header.IPv6ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetIPv6ReceivePacketInfo() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetIPv6ReceivePacketInfo(value) },
|
||||
presenceChecker: func() checker.ControlMessagesChecker {
|
||||
h := flow.MakeHeader4Tuple(context.Incoming)
|
||||
return checker.ReceiveIPv6PacketInfo(tcpip.IPv6PacketInfo{
|
||||
NIC: context.NICID,
|
||||
Addr: h.Dst.Addr,
|
||||
})
|
||||
}(),
|
||||
absenceChecker: checker.NoIPv6PacketInfoReceived(),
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := context.New(t, []stack.TransportProtocolFactory{icmp.NewProtocol4, icmp.NewProtocol6})
|
||||
defer c.Cleanup()
|
||||
|
||||
|
||||
@@ -36,35 +36,66 @@ const (
|
||||
func TestReceiveControlMessage(t *testing.T) {
|
||||
var payload = [...]byte{0, 1, 2, 3, 4, 5}
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
optionProtocol tcpip.NetworkProtocolNumber
|
||||
getReceiveOption func(tcpip.Endpoint) bool
|
||||
setReceiveOption func(tcpip.Endpoint, bool)
|
||||
presenceChecker checker.ControlMessagesChecker
|
||||
absenceChecker checker.ControlMessagesChecker
|
||||
}{
|
||||
{
|
||||
name: "TOS",
|
||||
optionProtocol: header.IPv4ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTOS() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTOS(value) },
|
||||
presenceChecker: checker.ReceiveTOS(testTOS),
|
||||
absenceChecker: checker.NoTOSReceived(),
|
||||
},
|
||||
{
|
||||
name: "TClass",
|
||||
optionProtocol: header.IPv6ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTClass() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTClass(value) },
|
||||
presenceChecker: checker.ReceiveTClass(testTOS),
|
||||
absenceChecker: checker.NoTClassReceived(),
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, flow := range []context.TestFlow{context.UnicastV4, context.UnicastV6, context.UnicastV6Only, context.MulticastV4, context.MulticastV6, context.MulticastV6Only, context.Broadcast} {
|
||||
t.Run(flow.String(), func(t *testing.T) {
|
||||
|
||||
for _, flow := range []context.TestFlow{context.UnicastV4, context.UnicastV6, context.UnicastV6Only, context.MulticastV4, context.MulticastV6, context.MulticastV6Only, context.Broadcast} {
|
||||
t.Run(flow.String(), func(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
optionProtocol tcpip.NetworkProtocolNumber
|
||||
getReceiveOption func(tcpip.Endpoint) bool
|
||||
setReceiveOption func(tcpip.Endpoint, bool)
|
||||
presenceChecker checker.ControlMessagesChecker
|
||||
absenceChecker checker.ControlMessagesChecker
|
||||
}{
|
||||
{
|
||||
name: "TOS",
|
||||
optionProtocol: header.IPv4ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTOS() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTOS(value) },
|
||||
presenceChecker: checker.ReceiveTOS(testTOS),
|
||||
absenceChecker: checker.NoTOSReceived(),
|
||||
},
|
||||
{
|
||||
name: "TClass",
|
||||
optionProtocol: header.IPv6ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTClass() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTClass(value) },
|
||||
presenceChecker: checker.ReceiveTClass(testTOS),
|
||||
absenceChecker: checker.NoTClassReceived(),
|
||||
},
|
||||
{
|
||||
name: "IPPacketInfo",
|
||||
optionProtocol: header.IPv4ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceivePacketInfo() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceivePacketInfo(value) },
|
||||
presenceChecker: func() checker.ControlMessagesChecker {
|
||||
h := flow.MakeHeader4Tuple(context.Incoming)
|
||||
return checker.ReceiveIPPacketInfo(tcpip.IPPacketInfo{
|
||||
NIC: context.NICID,
|
||||
// TODO(https://gvisor.dev/issue/3556): Expect the NIC's address
|
||||
// instead of the header destination address for the LocalAddr
|
||||
// field.
|
||||
LocalAddr: h.Dst.Addr,
|
||||
DestinationAddr: h.Dst.Addr,
|
||||
})
|
||||
}(),
|
||||
absenceChecker: checker.NoIPPacketInfoReceived(),
|
||||
},
|
||||
{
|
||||
name: "IPv6PacketInfo",
|
||||
optionProtocol: header.IPv6ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetIPv6ReceivePacketInfo() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetIPv6ReceivePacketInfo(value) },
|
||||
presenceChecker: func() checker.ControlMessagesChecker {
|
||||
h := flow.MakeHeader4Tuple(context.Incoming)
|
||||
return checker.ReceiveIPv6PacketInfo(tcpip.IPv6PacketInfo{
|
||||
NIC: context.NICID,
|
||||
Addr: h.Dst.Addr,
|
||||
})
|
||||
}(),
|
||||
absenceChecker: checker.NoIPv6PacketInfoReceived(),
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := context.New(t, []stack.TransportProtocolFactory{udp.NewProtocol})
|
||||
defer c.Cleanup()
|
||||
|
||||
|
||||
@@ -844,112 +844,6 @@ func TestReadIncrementsPacketsReceived(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadIPPacketInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
proto tcpip.NetworkProtocolNumber
|
||||
flow context.TestFlow
|
||||
checker func(tcpip.NICID) checker.ControlMessagesChecker
|
||||
}{
|
||||
{
|
||||
name: "IPv4 unicast",
|
||||
proto: header.IPv4ProtocolNumber,
|
||||
flow: context.UnicastV4,
|
||||
checker: func(id tcpip.NICID) checker.ControlMessagesChecker {
|
||||
return checker.ReceiveIPPacketInfo(tcpip.IPPacketInfo{
|
||||
NIC: id,
|
||||
LocalAddr: context.StackAddr,
|
||||
DestinationAddr: context.StackAddr,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv4 multicast",
|
||||
proto: header.IPv4ProtocolNumber,
|
||||
flow: context.MulticastV4,
|
||||
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: context.MulticastAddr,
|
||||
DestinationAddr: context.MulticastAddr,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv4 broadcast",
|
||||
proto: header.IPv4ProtocolNumber,
|
||||
flow: context.Broadcast,
|
||||
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: context.BroadcastAddr,
|
||||
DestinationAddr: context.BroadcastAddr,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv6 unicast",
|
||||
proto: header.IPv6ProtocolNumber,
|
||||
flow: context.UnicastV6,
|
||||
checker: func(id tcpip.NICID) checker.ControlMessagesChecker {
|
||||
return checker.ReceiveIPv6PacketInfo(tcpip.IPv6PacketInfo{
|
||||
NIC: id,
|
||||
Addr: context.StackV6Addr,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv6 multicast",
|
||||
proto: header.IPv6ProtocolNumber,
|
||||
flow: context.MulticastV6,
|
||||
checker: func(id tcpip.NICID) checker.ControlMessagesChecker {
|
||||
return checker.ReceiveIPv6PacketInfo(tcpip.IPv6PacketInfo{
|
||||
NIC: id,
|
||||
Addr: context.MulticastV6Addr,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := context.New(t, []stack.TransportProtocolFactory{udp.NewProtocol, icmp.NewProtocol6, icmp.NewProtocol4})
|
||||
defer c.Cleanup()
|
||||
|
||||
c.CreateEndpoint(test.proto, udp.ProtocolNumber)
|
||||
|
||||
bindAddr := tcpip.FullAddress{Port: context.StackPort}
|
||||
if err := c.EP.Bind(bindAddr); err != nil {
|
||||
t.Fatalf("Bind(%+v): %s", bindAddr, err)
|
||||
}
|
||||
|
||||
if test.flow.IsMulticast() {
|
||||
ifoptSet := tcpip.AddMembershipOption{NIC: context.NICID, MulticastAddr: test.flow.GetMulticastAddr()}
|
||||
if err := c.EP.SetSockOpt(&ifoptSet); err != nil {
|
||||
c.T.Fatalf("SetSockOpt(&%#v): %s:", ifoptSet, err)
|
||||
}
|
||||
}
|
||||
|
||||
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, test.checker(context.NICID))
|
||||
|
||||
if got := c.Stack.Stats().UDP.PacketsReceived.Value(); got != 1 {
|
||||
t.Fatalf("Read did not increment PacketsReceived: got = %d, want = 1", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadRecvOriginalDstAddr(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -1246,34 +1140,66 @@ func TestSetTClass(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReceiveControlMessage(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
optionProtocol tcpip.NetworkProtocolNumber
|
||||
getReceiveOption func(tcpip.Endpoint) bool
|
||||
setReceiveOption func(tcpip.Endpoint, bool)
|
||||
presenceChecker checker.ControlMessagesChecker
|
||||
absenceChecker checker.ControlMessagesChecker
|
||||
}{
|
||||
{
|
||||
name: "TOS",
|
||||
optionProtocol: header.IPv4ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTOS() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTOS(value) },
|
||||
presenceChecker: checker.ReceiveTOS(testTOS),
|
||||
absenceChecker: checker.NoTOSReceived(),
|
||||
},
|
||||
{
|
||||
name: "TClass",
|
||||
optionProtocol: header.IPv6ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTClass() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTClass(value) },
|
||||
presenceChecker: checker.ReceiveTClass(testTOS),
|
||||
absenceChecker: checker.NoTClassReceived(),
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, flow := range []context.TestFlow{context.UnicastV4, context.UnicastV6, context.UnicastV6Only, context.MulticastV4, context.MulticastV6, context.MulticastV6Only, context.Broadcast} {
|
||||
t.Run(flow.String(), func(t *testing.T) {
|
||||
for _, flow := range []context.TestFlow{context.UnicastV4, context.UnicastV6, context.UnicastV6Only, context.MulticastV4, context.MulticastV6, context.MulticastV6Only, context.Broadcast} {
|
||||
t.Run(flow.String(), func(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
optionProtocol tcpip.NetworkProtocolNumber
|
||||
getReceiveOption func(tcpip.Endpoint) bool
|
||||
setReceiveOption func(tcpip.Endpoint, bool)
|
||||
presenceChecker checker.ControlMessagesChecker
|
||||
absenceChecker checker.ControlMessagesChecker
|
||||
}{
|
||||
{
|
||||
name: "TOS",
|
||||
optionProtocol: header.IPv4ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTOS() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTOS(value) },
|
||||
presenceChecker: checker.ReceiveTOS(testTOS),
|
||||
absenceChecker: checker.NoTOSReceived(),
|
||||
},
|
||||
{
|
||||
name: "TClass",
|
||||
optionProtocol: header.IPv6ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceiveTClass() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceiveTClass(value) },
|
||||
presenceChecker: checker.ReceiveTClass(testTOS),
|
||||
absenceChecker: checker.NoTClassReceived(),
|
||||
},
|
||||
{
|
||||
name: "PacketInfo",
|
||||
optionProtocol: header.IPv4ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetReceivePacketInfo() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetReceivePacketInfo(value) },
|
||||
presenceChecker: func() checker.ControlMessagesChecker {
|
||||
h := flow.MakeHeader4Tuple(context.Incoming)
|
||||
return checker.ReceiveIPPacketInfo(tcpip.IPPacketInfo{
|
||||
NIC: context.NICID,
|
||||
// TODO(https://gvisor.dev/issue/3556): Expect the NIC's address
|
||||
// instead of the header destination address for the LocalAddr
|
||||
// field.
|
||||
LocalAddr: h.Dst.Addr,
|
||||
DestinationAddr: h.Dst.Addr,
|
||||
})
|
||||
}(),
|
||||
absenceChecker: checker.NoIPPacketInfoReceived(),
|
||||
},
|
||||
{
|
||||
name: "IPv6PacketInfo",
|
||||
optionProtocol: header.IPv6ProtocolNumber,
|
||||
getReceiveOption: func(ep tcpip.Endpoint) bool { return ep.SocketOptions().GetIPv6ReceivePacketInfo() },
|
||||
setReceiveOption: func(ep tcpip.Endpoint, value bool) { ep.SocketOptions().SetIPv6ReceivePacketInfo(value) },
|
||||
presenceChecker: func() checker.ControlMessagesChecker {
|
||||
h := flow.MakeHeader4Tuple(context.Incoming)
|
||||
return checker.ReceiveIPv6PacketInfo(tcpip.IPv6PacketInfo{
|
||||
NIC: context.NICID,
|
||||
Addr: h.Dst.Addr,
|
||||
})
|
||||
}(),
|
||||
absenceChecker: checker.NoIPv6PacketInfoReceived(),
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := context.New(t, []stack.TransportProtocolFactory{udp.NewProtocol})
|
||||
defer c.Cleanup()
|
||||
|
||||
|
||||
@@ -389,6 +389,11 @@ func hostInetFilters() seccomp.SyscallRules {
|
||||
seccomp.EqualTo(unix.SOL_IPV6),
|
||||
seccomp.EqualTo(unix.IPV6_RECVTCLASS),
|
||||
},
|
||||
{
|
||||
seccomp.MatchAny{},
|
||||
seccomp.EqualTo(unix.SOL_IPV6),
|
||||
seccomp.EqualTo(unix.IPV6_RECVPKTINFO),
|
||||
},
|
||||
{
|
||||
seccomp.MatchAny{},
|
||||
seccomp.EqualTo(unix.SOL_IPV6),
|
||||
@@ -548,6 +553,13 @@ func hostInetFilters() seccomp.SyscallRules {
|
||||
seccomp.MatchAny{},
|
||||
seccomp.EqualTo(4),
|
||||
},
|
||||
{
|
||||
seccomp.MatchAny{},
|
||||
seccomp.EqualTo(unix.SOL_IPV6),
|
||||
seccomp.EqualTo(unix.IPV6_RECVPKTINFO),
|
||||
seccomp.MatchAny{},
|
||||
seccomp.EqualTo(4),
|
||||
},
|
||||
{
|
||||
seccomp.MatchAny{},
|
||||
seccomp.EqualTo(unix.SOL_IP),
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
using ::testing::IsNull;
|
||||
using ::testing::NotNull;
|
||||
|
||||
uint32_t IPFromInetSockaddr(const struct sockaddr* addr) {
|
||||
@@ -270,10 +271,11 @@ void RecvCmsg(int sock, int cmsg_level, int cmsg_type, char buf[],
|
||||
ASSERT_EQ(msg.msg_controllen, CMSG_SPACE(sizeof(*out_cmsg_value)));
|
||||
|
||||
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||
ASSERT_NE(cmsg, nullptr);
|
||||
ASSERT_THAT(cmsg, NotNull());
|
||||
ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(*out_cmsg_value)));
|
||||
ASSERT_EQ(cmsg->cmsg_level, cmsg_level);
|
||||
ASSERT_EQ(cmsg->cmsg_type, cmsg_type);
|
||||
ASSERT_THAT(CMSG_NXTHDR(&msg, cmsg), IsNull());
|
||||
|
||||
std::copy_n(CMSG_DATA(cmsg), sizeof(*out_cmsg_value),
|
||||
reinterpret_cast<uint8_t*>(out_cmsg_value));
|
||||
@@ -325,5 +327,15 @@ void SendTClass(int sock, char buf[], size_t buf_size, int tclass) {
|
||||
SendCmsg(sock, SOL_IPV6, IPV6_TCLASS, buf, buf_size, tclass);
|
||||
}
|
||||
|
||||
void RecvPktInfo(int sock, char buf[], size_t* buf_size,
|
||||
in_pktinfo* out_pktinfo) {
|
||||
RecvCmsg(sock, SOL_IP, IP_PKTINFO, buf, buf_size, out_pktinfo);
|
||||
}
|
||||
|
||||
void RecvIPv6PktInfo(int sock, char buf[], size_t* buf_size,
|
||||
in6_pktinfo* out_pktinfo) {
|
||||
RecvCmsg(sock, SOL_IPV6, IPV6_PKTINFO, buf, buf_size, out_pktinfo);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
@@ -144,6 +144,20 @@ void RecvTClass(int sock, char buf[], size_t* buf_size, int* out_tclass);
|
||||
// message.
|
||||
void SendTClass(int sock, char buf[], size_t buf_size, int tclass);
|
||||
|
||||
// RecvPktInfo attempts to read buf_size bytes into buf, and then update
|
||||
// buf_size with the numbers of bytes actually read. It expects the
|
||||
// IP_PKTINFO cmsg to be received. The buffer must already be allocated with
|
||||
// at least buf_size size.
|
||||
void RecvPktInfo(int sock, char buf[], size_t* buf_size,
|
||||
in_pktinfo* out_pktinfo);
|
||||
|
||||
// RecvIPv6PktInfo attempts to read buf_size bytes into buf, and then update
|
||||
// buf_size with the numbers of bytes actually read. It expects the
|
||||
// IPV6_PKTINFO cmsg to be received. The buffer must already be allocated with
|
||||
// at least buf_size size.
|
||||
void RecvIPv6PktInfo(int sock, char buf[], size_t* buf_size,
|
||||
in6_pktinfo* out_pktinfo);
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
|
||||
@@ -112,10 +112,9 @@ TEST(PingSocket, ReceiveTOS) {
|
||||
SyscallSucceedsWithValue(sizeof(kSendIcmp)));
|
||||
|
||||
// Register to receive TOS.
|
||||
constexpr int kOne = 1;
|
||||
ASSERT_THAT(
|
||||
setsockopt(ping.get(), IPPROTO_IP, IP_RECVTOS, &kOne, sizeof(kOne)),
|
||||
SyscallSucceeds());
|
||||
ASSERT_THAT(setsockopt(ping.get(), IPPROTO_IP, IP_RECVTOS, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
struct {
|
||||
icmphdr icmp;
|
||||
@@ -167,9 +166,8 @@ TEST(PingSocket, ReceiveTClass) {
|
||||
SyscallSucceedsWithValue(sizeof(kSendIcmp)));
|
||||
|
||||
// Register to receive TCLASS.
|
||||
constexpr int kOne = 1;
|
||||
ASSERT_THAT(setsockopt(ping.get(), IPPROTO_IPV6, IPV6_RECVTCLASS, &kOne,
|
||||
sizeof(kOne)),
|
||||
ASSERT_THAT(setsockopt(ping.get(), IPPROTO_IPV6, IPV6_RECVTCLASS, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
struct {
|
||||
@@ -191,6 +189,111 @@ TEST(PingSocket, ReceiveTClass) {
|
||||
EXPECT_EQ(received_tclass, kArbitraryTClass);
|
||||
}
|
||||
|
||||
TEST(PingSocket, ReceiveIPPacketInfo) {
|
||||
PosixErrorOr<FileDescriptor> result =
|
||||
Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||
if (!result.ok()) {
|
||||
int errno_value = result.error().errno_value();
|
||||
ASSERT_EQ(errno_value, EACCES) << strerror(errno_value);
|
||||
GTEST_SKIP() << "ping socket not supported";
|
||||
}
|
||||
FileDescriptor& ping = result.ValueOrDie();
|
||||
|
||||
const sockaddr_in kAddr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)},
|
||||
};
|
||||
ASSERT_THAT(bind(ping.get(), reinterpret_cast<const sockaddr*>(&kAddr),
|
||||
sizeof(kAddr)),
|
||||
SyscallSucceeds());
|
||||
|
||||
constexpr icmphdr kSendIcmp = {
|
||||
.type = ICMP_ECHO,
|
||||
};
|
||||
ASSERT_THAT(sendto(ping.get(), &kSendIcmp, sizeof(kSendIcmp), 0,
|
||||
reinterpret_cast<const sockaddr*>(&kAddr), sizeof(kAddr)),
|
||||
SyscallSucceedsWithValue(sizeof(kSendIcmp)));
|
||||
|
||||
// Register to receive PKTINFO.
|
||||
ASSERT_THAT(setsockopt(ping.get(), IPPROTO_IP, IP_PKTINFO, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
struct {
|
||||
icmphdr icmp;
|
||||
|
||||
// Add an extra byte to confirm we did not read unexpected bytes.
|
||||
char unused;
|
||||
} ABSL_ATTRIBUTE_PACKED recv_buf;
|
||||
size_t recv_buf_len = sizeof(recv_buf);
|
||||
in_pktinfo received_pktinfo;
|
||||
ASSERT_NO_FATAL_FAILURE(RecvPktInfo(ping.get(),
|
||||
reinterpret_cast<char*>(&recv_buf),
|
||||
&recv_buf_len, &received_pktinfo));
|
||||
ASSERT_EQ(recv_buf_len, sizeof(icmphdr));
|
||||
|
||||
EXPECT_EQ(recv_buf.icmp.type, ICMP_ECHOREPLY);
|
||||
EXPECT_EQ(recv_buf.icmp.code, 0);
|
||||
|
||||
EXPECT_EQ(received_pktinfo.ipi_ifindex,
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
|
||||
EXPECT_EQ(ntohl(received_pktinfo.ipi_spec_dst.s_addr), INADDR_ANY);
|
||||
EXPECT_EQ(ntohl(received_pktinfo.ipi_addr.s_addr), INADDR_LOOPBACK);
|
||||
}
|
||||
|
||||
TEST(PingSocket, ReceiveIPv6PktInfo) {
|
||||
PosixErrorOr<FileDescriptor> result =
|
||||
Socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
|
||||
if (!result.ok()) {
|
||||
int errno_value = result.error().errno_value();
|
||||
ASSERT_EQ(errno_value, EACCES) << strerror(errno_value);
|
||||
GTEST_SKIP() << "ping socket not supported";
|
||||
}
|
||||
FileDescriptor& ping = result.ValueOrDie();
|
||||
|
||||
const sockaddr_in6 kAddr = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = in6addr_loopback,
|
||||
};
|
||||
ASSERT_THAT(bind(ping.get(), reinterpret_cast<const sockaddr*>(&kAddr),
|
||||
sizeof(kAddr)),
|
||||
SyscallSucceeds());
|
||||
|
||||
constexpr icmp6_hdr kSendIcmp = {
|
||||
.icmp6_type = ICMP6_ECHO_REQUEST,
|
||||
};
|
||||
ASSERT_THAT(sendto(ping.get(), &kSendIcmp, sizeof(kSendIcmp), 0,
|
||||
reinterpret_cast<const sockaddr*>(&kAddr), sizeof(kAddr)),
|
||||
SyscallSucceedsWithValue(sizeof(kSendIcmp)));
|
||||
|
||||
// Register to receive PKTINFO.
|
||||
ASSERT_THAT(setsockopt(ping.get(), IPPROTO_IPV6, IPV6_RECVPKTINFO,
|
||||
&kSockOptOn, sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
struct {
|
||||
icmp6_hdr icmpv6;
|
||||
|
||||
// Add an extra byte to confirm we did not read unexpected bytes.
|
||||
char unused;
|
||||
} ABSL_ATTRIBUTE_PACKED recv_buf;
|
||||
size_t recv_buf_len = sizeof(recv_buf);
|
||||
in6_pktinfo received_pktinfo;
|
||||
ASSERT_NO_FATAL_FAILURE(RecvIPv6PktInfo(ping.get(),
|
||||
reinterpret_cast<char*>(&recv_buf),
|
||||
&recv_buf_len, &received_pktinfo));
|
||||
ASSERT_EQ(recv_buf_len, sizeof(kSendIcmp));
|
||||
|
||||
EXPECT_EQ(recv_buf.icmpv6.icmp6_type, ICMP6_ECHO_REPLY);
|
||||
EXPECT_EQ(recv_buf.icmpv6.icmp6_code, 0);
|
||||
|
||||
EXPECT_EQ(received_pktinfo.ipi6_ifindex,
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
|
||||
ASSERT_EQ(memcmp(&received_pktinfo.ipi6_addr, &in6addr_loopback,
|
||||
sizeof(in6addr_loopback)),
|
||||
0);
|
||||
}
|
||||
|
||||
struct BindTestCase {
|
||||
TestAddress bind_to;
|
||||
int want = 0;
|
||||
|
||||
@@ -1091,12 +1091,12 @@ TEST(RawSocketTest, ReceiveIPPacketInfo) {
|
||||
SyscallSucceeds());
|
||||
|
||||
// Register to receive IP packet info.
|
||||
constexpr int one = 1;
|
||||
ASSERT_THAT(setsockopt(raw.get(), IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)),
|
||||
ASSERT_THAT(setsockopt(raw.get(), IPPROTO_IP, IP_PKTINFO, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
constexpr char send_buf[] = "malformed UDP";
|
||||
ASSERT_THAT(sendto(raw.get(), send_buf, sizeof(send_buf), 0 /* flags */,
|
||||
ASSERT_THAT(sendto(raw.get(), send_buf, sizeof(send_buf), /*flags=*/0,
|
||||
reinterpret_cast<const sockaddr*>(&addr_), sizeof(addr_)),
|
||||
SyscallSucceedsWithValue(sizeof(send_buf)));
|
||||
|
||||
@@ -1107,20 +1107,14 @@ TEST(RawSocketTest, ReceiveIPPacketInfo) {
|
||||
// Extra space in the receive buffer should be unused.
|
||||
char unused_space;
|
||||
} ABSL_ATTRIBUTE_PACKED recv_buf;
|
||||
iovec recv_iov = {
|
||||
.iov_base = &recv_buf,
|
||||
.iov_len = sizeof(recv_buf),
|
||||
};
|
||||
|
||||
size_t recv_buf_len = sizeof(recv_buf);
|
||||
in_pktinfo received_pktinfo;
|
||||
char recv_cmsg_buf[CMSG_SPACE(sizeof(received_pktinfo))];
|
||||
msghdr recv_msg = {
|
||||
.msg_iov = &recv_iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = recv_cmsg_buf,
|
||||
.msg_controllen = CMSG_LEN(sizeof(received_pktinfo)),
|
||||
};
|
||||
ASSERT_THAT(RetryEINTR(recvmsg)(raw.get(), &recv_msg, 0),
|
||||
SyscallSucceedsWithValue(sizeof(iphdr) + sizeof(send_buf)));
|
||||
ASSERT_NO_FATAL_FAILURE(RecvPktInfo(raw.get(),
|
||||
reinterpret_cast<char*>(&recv_buf),
|
||||
&recv_buf_len, &received_pktinfo));
|
||||
|
||||
EXPECT_EQ(recv_buf_len, sizeof(iphdr) + sizeof(send_buf));
|
||||
EXPECT_EQ(memcmp(send_buf, &recv_buf.data, sizeof(send_buf)), 0);
|
||||
EXPECT_EQ(recv_buf.ip.version, static_cast<unsigned int>(IPVERSION));
|
||||
// IHL holds the number of header bytes in 4 byte units.
|
||||
@@ -1130,18 +1124,10 @@ TEST(RawSocketTest, ReceiveIPPacketInfo) {
|
||||
EXPECT_EQ(ntohl(recv_buf.ip.saddr), INADDR_LOOPBACK);
|
||||
EXPECT_EQ(ntohl(recv_buf.ip.daddr), INADDR_LOOPBACK);
|
||||
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&recv_msg);
|
||||
ASSERT_THAT(cmsg, NotNull());
|
||||
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(received_pktinfo)));
|
||||
EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IP);
|
||||
EXPECT_EQ(cmsg->cmsg_type, IP_PKTINFO);
|
||||
memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(received_pktinfo));
|
||||
EXPECT_EQ(received_pktinfo.ipi_ifindex,
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
|
||||
EXPECT_EQ(ntohl(received_pktinfo.ipi_spec_dst.s_addr), INADDR_LOOPBACK);
|
||||
EXPECT_EQ(ntohl(received_pktinfo.ipi_addr.s_addr), INADDR_LOOPBACK);
|
||||
|
||||
EXPECT_THAT(CMSG_NXTHDR(&recv_msg, cmsg), IsNull());
|
||||
}
|
||||
|
||||
TEST(RawSocketTest, ReceiveIPv6PacketInfo) {
|
||||
@@ -1159,46 +1145,28 @@ TEST(RawSocketTest, ReceiveIPv6PacketInfo) {
|
||||
SyscallSucceeds());
|
||||
|
||||
// Register to receive IPv6 packet info.
|
||||
constexpr int one = 1;
|
||||
ASSERT_THAT(
|
||||
setsockopt(raw.get(), IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)),
|
||||
SyscallSucceeds());
|
||||
ASSERT_THAT(setsockopt(raw.get(), IPPROTO_IPV6, IPV6_RECVPKTINFO, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
constexpr char send_buf[] = "malformed UDP";
|
||||
ASSERT_THAT(sendto(raw.get(), send_buf, sizeof(send_buf), 0 /* flags */,
|
||||
ASSERT_THAT(sendto(raw.get(), send_buf, sizeof(send_buf), /*flags=*/0,
|
||||
reinterpret_cast<const sockaddr*>(&addr_), sizeof(addr_)),
|
||||
SyscallSucceedsWithValue(sizeof(send_buf)));
|
||||
|
||||
char recv_buf[sizeof(send_buf) + 1];
|
||||
iovec recv_iov = {
|
||||
.iov_base = recv_buf,
|
||||
.iov_len = sizeof(recv_buf),
|
||||
};
|
||||
size_t recv_buf_len = sizeof(recv_buf);
|
||||
in6_pktinfo received_pktinfo;
|
||||
char recv_cmsg_buf[CMSG_SPACE(sizeof(received_pktinfo))];
|
||||
msghdr recv_msg = {
|
||||
.msg_iov = &recv_iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = recv_cmsg_buf,
|
||||
.msg_controllen = CMSG_LEN(sizeof(received_pktinfo)),
|
||||
};
|
||||
ASSERT_THAT(RetryEINTR(recvmsg)(raw.get(), &recv_msg, 0),
|
||||
SyscallSucceedsWithValue(sizeof(send_buf)));
|
||||
ASSERT_NO_FATAL_FAILURE(RecvIPv6PktInfo(raw.get(),
|
||||
reinterpret_cast<char*>(&recv_buf),
|
||||
&recv_buf_len, &received_pktinfo));
|
||||
EXPECT_EQ(recv_buf_len, 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(received_pktinfo)));
|
||||
EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IPV6);
|
||||
EXPECT_EQ(cmsg->cmsg_type, IPV6_PKTINFO);
|
||||
memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(received_pktinfo));
|
||||
EXPECT_EQ(received_pktinfo.ipi6_ifindex,
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
|
||||
ASSERT_EQ(memcmp(&received_pktinfo.ipi6_addr, &in6addr_loopback,
|
||||
sizeof(in6addr_loopback)),
|
||||
0);
|
||||
|
||||
EXPECT_THAT(CMSG_NXTHDR(&recv_msg, cmsg), IsNull());
|
||||
}
|
||||
|
||||
TEST(RawSocketTest, ReceiveTOS) {
|
||||
|
||||
@@ -2142,6 +2142,126 @@ TEST_P(UdpSocketControlMessagesTest, SendAndReceiveTOSorTClass) {
|
||||
EXPECT_EQ(recv_data_len, sizeof(sent_data));
|
||||
}
|
||||
|
||||
TEST_P(UdpSocketControlMessagesTest, SetAndReceivePktInfo) {
|
||||
// Enable receiving IP_PKTINFO and maybe IPV6_PKTINFO on the receiver.
|
||||
ASSERT_THAT(setsockopt(server_.get(), SOL_IP, IP_PKTINFO, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
if (ServerAddressFamily() == AF_INET6) {
|
||||
ASSERT_THAT(setsockopt(server_.get(), SOL_IPV6, IPV6_RECVPKTINFO,
|
||||
&kSockOptOn, sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
}
|
||||
|
||||
constexpr size_t kArbitrarySendSize = 1042;
|
||||
constexpr char sent_data[kArbitrarySendSize] = {};
|
||||
ASSERT_THAT(RetryEINTR(send)(client_.get(), sent_data, sizeof(sent_data), 0),
|
||||
SyscallSucceedsWithValue(sizeof(sent_data)));
|
||||
|
||||
char recv_data[sizeof(sent_data) + 1];
|
||||
size_t recv_data_len = sizeof(recv_data);
|
||||
switch (GetParam()) {
|
||||
case AddressFamily::kIpv4: {
|
||||
in_pktinfo received_pktinfo;
|
||||
ASSERT_NO_FATAL_FAILURE(RecvPktInfo(server_.get(), recv_data,
|
||||
&recv_data_len, &received_pktinfo));
|
||||
EXPECT_EQ(recv_data_len, sizeof(sent_data));
|
||||
EXPECT_EQ(received_pktinfo.ipi_ifindex,
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
|
||||
EXPECT_EQ(ntohl(received_pktinfo.ipi_spec_dst.s_addr), INADDR_LOOPBACK);
|
||||
EXPECT_EQ(ntohl(received_pktinfo.ipi_addr.s_addr), INADDR_LOOPBACK);
|
||||
break;
|
||||
}
|
||||
|
||||
case AddressFamily::kIpv6: {
|
||||
in6_pktinfo received_pktinfo;
|
||||
ASSERT_NO_FATAL_FAILURE(RecvIPv6PktInfo(
|
||||
server_.get(), recv_data, &recv_data_len, &received_pktinfo));
|
||||
EXPECT_EQ(recv_data_len, sizeof(sent_data));
|
||||
EXPECT_EQ(received_pktinfo.ipi6_ifindex,
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
|
||||
ASSERT_EQ(memcmp(&received_pktinfo.ipi6_addr, &in6addr_loopback,
|
||||
sizeof(in6addr_loopback)),
|
||||
0);
|
||||
break;
|
||||
}
|
||||
|
||||
case AddressFamily::kDualStack: {
|
||||
// TODO(https://gvisor.dev/issue/7144): On dual stack sockets, Linux can
|
||||
// receive both the IPv4 and IPv6 packet info. gVisor should do the same.
|
||||
iovec iov = {
|
||||
iov.iov_base = recv_data,
|
||||
iov.iov_len = recv_data_len,
|
||||
};
|
||||
// Add an extra byte to confirm we only read what we expected.
|
||||
char control[CMSG_SPACE(sizeof(in_pktinfo)) +
|
||||
CMSG_SPACE(sizeof(in6_pktinfo)) + 1];
|
||||
msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = control,
|
||||
.msg_controllen = sizeof(control),
|
||||
};
|
||||
|
||||
ASSERT_THAT(
|
||||
recv_data_len = RetryEINTR(recvmsg)(server_.get(), &msg, /*flags=*/0),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(recv_data_len, sizeof(sent_data));
|
||||
size_t expected_controllen = CMSG_SPACE(sizeof(in_pktinfo));
|
||||
if (!IsRunningOnGvisor() || IsRunningWithHostinet()) {
|
||||
expected_controllen += CMSG_SPACE(sizeof(in6_pktinfo));
|
||||
}
|
||||
EXPECT_EQ(msg.msg_controllen, expected_controllen);
|
||||
|
||||
std::pair<in_pktinfo, bool> received_pktinfo;
|
||||
std::pair<in6_pktinfo, bool> received_pktinfo6;
|
||||
|
||||
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||
while (cmsg != nullptr) {
|
||||
ASSERT_TRUE(cmsg->cmsg_level == SOL_IP || cmsg->cmsg_level == SOL_IPV6);
|
||||
if (cmsg->cmsg_level == SOL_IP) {
|
||||
ASSERT_FALSE(received_pktinfo.second);
|
||||
ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(in_pktinfo)));
|
||||
ASSERT_EQ(cmsg->cmsg_type, IP_PKTINFO);
|
||||
received_pktinfo.second = true;
|
||||
std::copy_n(CMSG_DATA(cmsg), sizeof(received_pktinfo.first),
|
||||
reinterpret_cast<uint8_t*>(&received_pktinfo.first));
|
||||
} else { // SOL_IPV6
|
||||
ASSERT_FALSE(received_pktinfo6.second);
|
||||
ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(in6_pktinfo)));
|
||||
ASSERT_EQ(cmsg->cmsg_type, IPV6_PKTINFO);
|
||||
received_pktinfo6.second = true;
|
||||
std::copy_n(CMSG_DATA(cmsg), sizeof(received_pktinfo6.first),
|
||||
reinterpret_cast<uint8_t*>(&received_pktinfo6.first));
|
||||
}
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(received_pktinfo.second);
|
||||
EXPECT_EQ(received_pktinfo.first.ipi_ifindex,
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
|
||||
EXPECT_EQ(ntohl(received_pktinfo.first.ipi_spec_dst.s_addr),
|
||||
INADDR_LOOPBACK);
|
||||
EXPECT_EQ(ntohl(received_pktinfo.first.ipi_addr.s_addr), INADDR_LOOPBACK);
|
||||
|
||||
if (!IsRunningOnGvisor() || IsRunningWithHostinet()) {
|
||||
ASSERT_TRUE(received_pktinfo6.second);
|
||||
EXPECT_EQ(received_pktinfo6.first.ipi6_ifindex,
|
||||
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
|
||||
struct in6_addr expected;
|
||||
inet_pton(AF_INET6, "::ffff:127.0.0.1", &expected);
|
||||
EXPECT_EQ(memcmp(&received_pktinfo6.first.ipi6_addr, &expected,
|
||||
sizeof(expected)),
|
||||
0);
|
||||
} else {
|
||||
ASSERT_FALSE(received_pktinfo6.second);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(AllInetTests, UdpSocketControlMessagesTest,
|
||||
::testing::Values(AddressFamily::kIpv4,
|
||||
AddressFamily::kIpv6,
|
||||
|
||||
Reference in New Issue
Block a user