Skip to content

Commit 161bbac

Browse files
shailend-ggvisor-bot
authored andcommitted
Netstack: Allow IP_(ADD|REMOVE)_MEMBERSHIP on AF_INET6 sockets
Just like Linux does. The new test `AddV4MembershipToV6Socket` passes only if there is a routing table entry to resolve the ipv4 multicast address, and that is easiest to setup via a "external networking" test. Now it turns out that the ipv6 "external networking" test did not really have a second non-loopback (external) interface, so I've fixed that. Also, the existing "external networking" test `TestJoinLeaveMulticast` needs only the loopback interface, so I've moved it to the loopback test. PiperOrigin-RevId: 807959283
1 parent 2c49d9f commit 161bbac

File tree

10 files changed

+202
-124
lines changed

10 files changed

+202
-124
lines changed

pkg/tcpip/tcpip.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,8 @@ import (
4949

5050
// Using the header package here would cause an import cycle.
5151
const (
52-
ipv4AddressSize = 4
53-
ipv4ProtocolNumber = 0x0800
54-
ipv6AddressSize = 16
55-
ipv6ProtocolNumber = 0x86dd
52+
ipv4AddressSize = 4
53+
ipv6AddressSize = 16
5654
)
5755

5856
const (

pkg/tcpip/tests/integration/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ go_test(
128128
"//pkg/tcpip/tests/utils",
129129
"//pkg/tcpip/testutil",
130130
"//pkg/tcpip/transport/icmp",
131-
"//pkg/tcpip/transport/raw",
132131
"//pkg/tcpip/transport/udp",
133132
"//pkg/waiter",
134133
"@com_github_google_go_cmp//cmp:go_default_library",

pkg/tcpip/tests/integration/multicast_broadcast_test.go

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import (
3333
"gvisor.dev/gvisor/pkg/tcpip/tests/utils"
3434
"gvisor.dev/gvisor/pkg/tcpip/testutil"
3535
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
36-
"gvisor.dev/gvisor/pkg/tcpip/transport/raw"
3736
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
3837
"gvisor.dev/gvisor/pkg/waiter"
3938
)
@@ -779,54 +778,3 @@ func TestAddMembershipInterfacePrecedence(t *testing.T) {
779778
t.Fatalf("ep.SetSockOpt(&%#v): %s", addOpt, err)
780779
}
781780
}
782-
783-
func TestMismatchedMulticastAddressAndProtocol(t *testing.T) {
784-
const nicID = 1
785-
// MulticastAddr is IPv4, but proto is IPv6.
786-
multicastAddr := tcpip.AddrFromSlice([]byte("\xe0\x01\x02\x03"))
787-
s := stack.New(stack.Options{
788-
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
789-
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol6},
790-
RawFactory: raw.EndpointFactory{},
791-
})
792-
e := channel.New(0, defaultMTU, "")
793-
defer e.Close()
794-
if err := s.CreateNIC(nicID, e); err != nil {
795-
t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
796-
}
797-
protoAddr := tcpip.ProtocolAddress{Protocol: header.IPv4ProtocolNumber, AddressWithPrefix: utils.Ipv4Addr}
798-
if err := s.AddProtocolAddress(nicID, protoAddr, stack.AddressProperties{}); err != nil {
799-
t.Fatalf("AddProtocolAddress(%d, %+v, {}): %s", nicID, protoAddr, err)
800-
}
801-
802-
var wq waiter.Queue
803-
ep, err := s.NewRawEndpoint(header.ICMPv6ProtocolNumber, header.IPv6ProtocolNumber, &wq, false)
804-
if err != nil {
805-
t.Fatalf("NewEndpoint(%d, %d, _): %s", udp.ProtocolNumber, header.IPv6ProtocolNumber, err)
806-
}
807-
defer ep.Close()
808-
809-
bindAddr := tcpip.FullAddress{Port: utils.LocalPort}
810-
if err := ep.Bind(bindAddr); err != nil {
811-
t.Fatalf("ep.Bind(%#v): %s", bindAddr, err)
812-
}
813-
814-
memOpt := tcpip.MembershipOption{
815-
MulticastAddr: multicastAddr,
816-
NIC: 0,
817-
InterfaceAddr: utils.Ipv4Addr.Address,
818-
}
819-
820-
// Add/remove membership should succeed when the interface index is specified,
821-
// even if a bad interface address is specified.
822-
addOpt := tcpip.AddMembershipOption(memOpt)
823-
expErr := &tcpip.ErrInvalidOptionValue{}
824-
if err := ep.SetSockOpt(&addOpt); err != expErr {
825-
t.Fatalf("ep.SetSockOpt(&%#v): want %q, got %q", addOpt, expErr, err)
826-
}
827-
828-
removeOpt := tcpip.RemoveMembershipOption(memOpt)
829-
if err := ep.SetSockOpt(&removeOpt); err != expErr {
830-
t.Fatalf("ep.SetSockOpt(&%#v): want %q, got %q", addOpt, expErr, err)
831-
}
832-
}

pkg/tcpip/transport/internal/network/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ go_library(
2222
"//pkg/sync",
2323
"//pkg/tcpip",
2424
"//pkg/tcpip/header",
25+
"//pkg/tcpip/network/ipv4",
26+
"//pkg/tcpip/network/ipv6",
2527
"//pkg/tcpip/stack",
2628
"//pkg/tcpip/transport",
2729
"//pkg/waiter",

pkg/tcpip/transport/internal/network/endpoint.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424
"gvisor.dev/gvisor/pkg/sync"
2525
"gvisor.dev/gvisor/pkg/tcpip"
2626
"gvisor.dev/gvisor/pkg/tcpip/header"
27+
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
28+
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
2729
"gvisor.dev/gvisor/pkg/tcpip/stack"
2830
"gvisor.dev/gvisor/pkg/tcpip/transport"
2931
"gvisor.dev/gvisor/pkg/waiter"
@@ -952,21 +954,26 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
952954
e.multicastAddr = addr
953955

954956
case *tcpip.AddMembershipOption:
955-
if !(header.IsV4MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv4ProtocolNumber) && !(header.IsV6MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv6ProtocolNumber) {
957+
var proto tcpip.NetworkProtocolNumber
958+
switch {
959+
case header.IsV4MulticastAddress(v.MulticastAddr):
960+
proto = ipv4.ProtocolNumber
961+
case header.IsV6MulticastAddress(v.MulticastAddr):
962+
proto = ipv6.ProtocolNumber
963+
default:
956964
return &tcpip.ErrInvalidOptionValue{}
957965
}
958966

959967
nicID := v.NIC
960-
961968
if v.InterfaceAddr.Unspecified() {
962969
if nicID == 0 {
963-
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil {
970+
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, proto, false /* multicastLoop */); err == nil {
964971
nicID = r.NICID()
965972
r.Release()
966973
}
967974
}
968975
} else {
969-
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
976+
nicID = e.stack.CheckLocalAddress(nicID, proto, v.InterfaceAddr)
970977
}
971978
if nicID == 0 {
972979
return &tcpip.ErrUnknownDevice{}
@@ -981,27 +988,33 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
981988
return &tcpip.ErrPortInUse{}
982989
}
983990

984-
if err := e.stack.JoinGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
991+
if err := e.stack.JoinGroup(proto, nicID, v.MulticastAddr); err != nil {
985992
return err
986993
}
987994

988995
e.multicastMemberships[memToInsert] = struct{}{}
989996

990997
case *tcpip.RemoveMembershipOption:
991-
if !(header.IsV4MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv4ProtocolNumber) && !(header.IsV6MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv6ProtocolNumber) {
998+
var proto tcpip.NetworkProtocolNumber
999+
switch {
1000+
case header.IsV4MulticastAddress(v.MulticastAddr):
1001+
proto = ipv4.ProtocolNumber
1002+
case header.IsV6MulticastAddress(v.MulticastAddr):
1003+
proto = ipv6.ProtocolNumber
1004+
default:
9921005
return &tcpip.ErrInvalidOptionValue{}
9931006
}
9941007

9951008
nicID := v.NIC
9961009
if v.InterfaceAddr.Unspecified() {
9971010
if nicID == 0 {
998-
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil {
1011+
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, proto, false /* multicastLoop */); err == nil {
9991012
nicID = r.NICID()
10001013
r.Release()
10011014
}
10021015
}
10031016
} else {
1004-
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
1017+
nicID = e.stack.CheckLocalAddress(nicID, proto, v.InterfaceAddr)
10051018
}
10061019
if nicID == 0 {
10071020
return &tcpip.ErrUnknownDevice{}
@@ -1016,7 +1029,7 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
10161029
return &tcpip.ErrBadLocalAddress{}
10171030
}
10181031

1019-
if err := e.stack.LeaveGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
1032+
if err := e.stack.LeaveGroup(proto, nicID, v.MulticastAddr); err != nil {
10201033
return err
10211034
}
10221035

test/syscalls/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,12 @@ syscall_test(
810810
test = "//test/syscalls/linux:socket_ipv4_udp_unbound_external_networking_test",
811811
)
812812

813+
syscall_test(
814+
add_hostinet = True,
815+
netstack_sr = True,
816+
test = "//test/syscalls/linux:socket_ipv6_udp_unbound_external_networking_test",
817+
)
818+
813819
syscall_test(
814820
size = "large",
815821
add_hostinet = True,

test/syscalls/linux/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2896,7 +2896,12 @@ cc_library(
28962896
"socket_ipv6_udp_unbound_external_networking.h",
28972897
],
28982898
deps = select_gtest() + [
2899+
":ip_socket_test_util",
28992900
":socket_ip_udp_unbound_external_networking",
2901+
"//test/util:posix_error",
2902+
"//test/util:socket_util",
2903+
"//test/util:test_util",
2904+
"@com_google_absl//absl/cleanup",
29002905
],
29012906
alwayslink = 1,
29022907
)

test/syscalls/linux/socket_ipv6_udp_unbound.cc

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#include <arpa/inet.h>
1818
#include <netinet/in.h>
19+
20+
#include <cerrno>
1921
#ifdef __linux__
2022
#include <linux/in6.h>
2123
#endif // __linux__
@@ -204,5 +206,70 @@ TEST_P(IPv6UDPUnboundSocketTest, SetAndReceiveIPReceiveOrigDstAddr) {
204206
EXPECT_EQ(received_addr.sin6_port, orig_receiver_addr->sin6_port);
205207
}
206208

209+
TEST_P(IPv6UDPUnboundSocketTest, TestJoinLeaveMulticast) {
210+
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
211+
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
212+
213+
auto receiver_addr = V6Any();
214+
ASSERT_THAT(bind(receiver->get(), AsSockAddr(&receiver_addr.addr),
215+
receiver_addr.addr_len),
216+
SyscallSucceeds());
217+
socklen_t receiver_addr_len = receiver_addr.addr_len;
218+
ASSERT_THAT(getsockname(receiver->get(), AsSockAddr(&receiver_addr.addr),
219+
&receiver_addr_len),
220+
SyscallSucceeds());
221+
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
222+
223+
// Register to receive multicast packets.
224+
auto multicast_addr = V6Multicast();
225+
ipv6_mreq group_req = {
226+
.ipv6mr_multiaddr =
227+
reinterpret_cast<sockaddr_in6*>(&multicast_addr.addr)->sin6_addr,
228+
.ipv6mr_interface = static_cast<decltype(ipv6_mreq::ipv6mr_interface)>(
229+
ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex())),
230+
};
231+
ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
232+
&group_req, sizeof(group_req)),
233+
SyscallSucceeds());
234+
235+
// Set the sender to the loopback interface.
236+
auto sender_addr = V6Loopback();
237+
ASSERT_THAT(
238+
bind(sender->get(), AsSockAddr(&sender_addr.addr), sender_addr.addr_len),
239+
SyscallSucceeds());
240+
241+
// Send a multicast packet.
242+
auto send_addr = multicast_addr;
243+
reinterpret_cast<sockaddr_in6*>(&send_addr.addr)->sin6_port =
244+
reinterpret_cast<sockaddr_in6*>(&receiver_addr.addr)->sin6_port;
245+
char send_buf[200];
246+
RandomizeBuffer(send_buf, sizeof(send_buf));
247+
ASSERT_THAT(
248+
RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0,
249+
AsSockAddr(&send_addr.addr), send_addr.addr_len),
250+
SyscallSucceedsWithValue(sizeof(send_buf)));
251+
252+
// Check that we received the multicast packet.
253+
char recv_buf[sizeof(send_buf)] = {};
254+
ASSERT_THAT(
255+
RecvTimeout(receiver->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
256+
IsPosixErrorOkAndHolds(sizeof(recv_buf)));
257+
258+
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
259+
260+
// Leave the group and make sure we don't receive its multicast traffic.
261+
ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP,
262+
&group_req, sizeof(group_req)),
263+
SyscallSucceeds());
264+
RandomizeBuffer(send_buf, sizeof(send_buf));
265+
ASSERT_THAT(
266+
RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0,
267+
AsSockAddr(&send_addr.addr), send_addr.addr_len),
268+
SyscallSucceedsWithValue(sizeof(send_buf)));
269+
ASSERT_THAT(RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf),
270+
MSG_DONTWAIT),
271+
SyscallFailsWithErrno(EAGAIN));
272+
}
273+
207274
} // namespace testing
208275
} // namespace gvisor

0 commit comments

Comments
 (0)