Populate IPPacketInfo with destination address

IPPacketInfo.DestinationAddr should hold the destination of the IP
packet, not the source. This change fixes that bug.

PiperOrigin-RevId: 325910766
This commit is contained in:
Ghanan Gowripalan
2020-08-10 16:22:31 -07:00
committed by gVisor bot
parent a1af46c20a
commit 0a8ae4b32f
6 changed files with 226 additions and 11 deletions
+1
View File
@@ -12,5 +12,6 @@ go_library(
"//pkg/tcpip/buffer",
"//pkg/tcpip/header",
"//pkg/tcpip/seqnum",
"@com_github_google_go_cmp//cmp:go_default_library",
],
)
+19 -7
View File
@@ -21,6 +21,7 @@ import (
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -169,10 +170,9 @@ 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)
t.Errorf("got cm.HasTClass = %t, want = true", cm.HasTClass)
} else if got := cm.TClass; got != want {
t.Errorf("got cm.TClass = %d, want %d", got, want)
}
}
}
@@ -182,10 +182,22 @@ func ReceiveTOS(want uint8) ControlMessagesChecker {
return func(t *testing.T, cm tcpip.ControlMessages) {
t.Helper()
if !cm.HasTOS {
t.Fatalf("got cm.HasTOS = %t, want cm.TOS = %d", cm.HasTOS, want)
t.Errorf("got cm.HasTOS = %t, want = true", cm.HasTOS)
} else if got := cm.TOS; got != want {
t.Errorf("got cm.TOS = %d, want %d", got, want)
}
if got := cm.TOS; got != want {
t.Fatalf("got cm.TOS = %d, want %d", got, want)
}
}
// ReceiveIPPacketInfo creates a checker that checks the PacketInfo field in
// ControlMessages.
func ReceiveIPPacketInfo(want tcpip.IPPacketInfo) ControlMessagesChecker {
return func(t *testing.T, cm tcpip.ControlMessages) {
t.Helper()
if !cm.HasIPPacketInfo {
t.Errorf("got cm.HasIPPacketInfo = %t, want = true", cm.HasIPPacketInfo)
} else if diff := cmp.Diff(want, cm.PacketInfo); diff != "" {
t.Errorf("IPPacketInfo mismatch (-want +got):\n%s", diff)
}
}
}
+1 -1
View File
@@ -968,7 +968,7 @@ type IPPacketInfo struct {
// LocalAddr is the local address.
LocalAddr Address
// DestinationAddr is the destination address.
// DestinationAddr is the destination address found in the IP header.
DestinationAddr Address
}
+6 -3
View File
@@ -1444,13 +1444,16 @@ func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, pk
switch r.NetProto {
case header.IPv4ProtocolNumber:
packet.tos, _ = header.IPv4(pkt.NetworkHeader).TOS()
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()
}
// TODO(gvisor.dev/issue/3556): r.LocalAddress may be a multicast or broadcast
// address. packetInfo.LocalAddr should hold a unicast address that can be
// used to respond to the incoming packet.
packet.packetInfo.LocalAddr = r.LocalAddress
packet.packetInfo.DestinationAddr = r.LocalAddress
packet.packetInfo.NIC = r.NICID()
packet.timestamp = e.stack.Clock().NowNanoseconds()
e.rcvMu.Unlock()
+99
View File
@@ -1309,6 +1309,105 @@ 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: "IPv4 unicast",
proto: header.IPv4ProtocolNumber,
flow: unicastV4,
expectedLocalAddr: stackAddr,
expectedDestAddr: 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,
},
{
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,
},
{
name: "IPv6 unicast",
proto: header.IPv6ProtocolNumber,
flow: unicastV6,
expectedLocalAddr: stackV6Addr,
expectedDestAddr: 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,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := newDualTestContext(t, defaultMTU)
defer c.cleanup()
c.createEndpoint(test.proto)
bindAddr := tcpip.FullAddress{Port: stackPort}
if err := c.ep.Bind(bindAddr); err != nil {
t.Fatalf("Bind(%+v): %s", bindAddr, err)
}
if test.flow.isMulticast() {
ifoptSet := tcpip.AddMembershipOption{NIC: 1, MulticastAddr: test.flow.getMcastAddr()}
if err := c.ep.SetSockOpt(ifoptSet); err != nil {
c.t.Fatalf("SetSockOpt(%+v): %s:", ifoptSet, err)
}
}
if err := c.ep.SetSockOptBool(tcpip.ReceiveIPPacketInfoOption, true); err != nil {
t.Fatalf("c.ep.SetSockOptBool(tcpip.ReceiveIPPacketInfoOption, true): %s", err)
}
testRead(c, test.flow, checker.ReceiveIPPacketInfo(tcpip.IPPacketInfo{
NIC: 1,
LocalAddr: test.expectedLocalAddr,
DestinationAddr: test.expectedDestAddr,
}))
if got := c.s.Stats().UDP.PacketsReceived.Value(); got != 1 {
t.Fatalf("Read did not increment PacketsReceived: got = %d, want = 1", got)
}
})
}
}
func TestWriteIncrementsPacketsSent(t *testing.T) {
c := newDualTestContext(t, defaultMTU)
defer c.cleanup()
@@ -2452,5 +2452,105 @@ TEST_P(IPv4UDPUnboundSocketTest, SetSocketSendBuf) {
ASSERT_EQ(quarter_sz, val);
}
TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIPPacketInfo) {
auto sender_socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver_socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
// Bind the first FD to the loopback. This is an alternative to
// IP_MULTICAST_IF for setting the default send interface.
auto sender_addr = V4Loopback();
ASSERT_THAT(
bind(sender_socket->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr),
sender_addr.addr_len),
SyscallSucceeds());
// Bind the second FD to the v4 any address to ensure that we can receive the
// multicast packet.
auto receiver_addr = V4Any();
ASSERT_THAT(bind(receiver_socket->get(),
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
receiver_addr.addr_len),
SyscallSucceeds());
socklen_t receiver_addr_len = receiver_addr.addr_len;
ASSERT_THAT(getsockname(receiver_socket->get(),
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
&receiver_addr_len),
SyscallSucceeds());
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
ASSERT_THAT(setsockopt(receiver_socket->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
// Register to receive IP packet info.
const int one = 1;
ASSERT_THAT(setsockopt(receiver_socket->get(), IPPROTO_IP, IP_PKTINFO, &one,
sizeof(one)),
SyscallSucceeds());
// Send a multicast packet.
auto send_addr = V4Multicast();
reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port =
reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port;
char send_buf[200];
RandomizeBuffer(send_buf, sizeof(send_buf));
ASSERT_THAT(
RetryEINTR(sendto)(sender_socket->get(), send_buf, sizeof(send_buf), 0,
reinterpret_cast<sockaddr*>(&send_addr.addr),
send_addr.addr_len),
SyscallSucceedsWithValue(sizeof(send_buf)));
// Check that we received the multicast packet.
msghdr recv_msg = {};
iovec recv_iov = {};
char recv_buf[sizeof(send_buf)];
char recv_cmsg_buf[CMSG_SPACE(sizeof(in_pktinfo))] = {};
size_t cmsg_data_len = sizeof(in_pktinfo);
recv_iov.iov_base = recv_buf;
recv_iov.iov_len = sizeof(recv_buf);
recv_msg.msg_iov = &recv_iov;
recv_msg.msg_iovlen = 1;
recv_msg.msg_controllen = CMSG_LEN(cmsg_data_len);
recv_msg.msg_control = recv_cmsg_buf;
ASSERT_THAT(RetryEINTR(recvmsg)(receiver_socket->get(), &recv_msg, 0),
SyscallSucceedsWithValue(sizeof(send_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
// Check the IP_PKTINFO control message.
cmsghdr* cmsg = CMSG_FIRSTHDR(&recv_msg);
ASSERT_NE(cmsg, nullptr);
EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len));
EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IP);
EXPECT_EQ(cmsg->cmsg_type, IP_PKTINFO);
// Get loopback index.
ifreq ifr = {};
absl::SNPrintF(ifr.ifr_name, IFNAMSIZ, "lo");
ASSERT_THAT(ioctl(receiver_socket->get(), SIOCGIFINDEX, &ifr),
SyscallSucceeds());
ASSERT_NE(ifr.ifr_ifindex, 0);
in_pktinfo received_pktinfo = {};
memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(in_pktinfo));
EXPECT_EQ(received_pktinfo.ipi_ifindex, ifr.ifr_ifindex);
if (IsRunningOnGvisor()) {
// 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 :).
EXPECT_EQ(received_pktinfo.ipi_spec_dst.s_addr, group.imr_multiaddr.s_addr);
} else {
EXPECT_EQ(received_pktinfo.ipi_spec_dst.s_addr, htonl(INADDR_LOOPBACK));
}
EXPECT_EQ(received_pktinfo.ipi_addr.s_addr, group.imr_multiaddr.s_addr);
}
} // namespace testing
} // namespace gvisor