Implement IP_MULTICAST_LOOP.

IP_MULTICAST_LOOP controls whether or not multicast packets sent on the default
route are looped back. In order to implement this switch, support for sending
and looping back multicast packets on the default route had to be implemented.

For now we only support IPv4 multicast.

PiperOrigin-RevId: 237534603
Change-Id: I490ac7ff8e8ebef417c7eb049a919c29d156ac1c
This commit is contained in:
Ian Gudger
2019-03-08 15:49:17 -08:00
committed by Shentubot
parent 832589cb07
commit 56a6128295
24 changed files with 966 additions and 124 deletions
+36 -1
View File
@@ -911,6 +911,21 @@ func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interfac
}
return rv.InetMulticastRequest, nil
case linux.IP_MULTICAST_LOOP:
if outLen < sizeOfInt32 {
return nil, syserr.ErrInvalidArgument
}
var v tcpip.MulticastLoopOption
if err := ep.GetSockOpt(&v); err != nil {
return nil, syserr.TranslateNetstackError(err)
}
if v {
return int32(1), nil
}
return int32(0), nil
default:
emitUnimplementedEventIP(t, name)
}
@@ -1178,6 +1193,15 @@ func copyInMulticastRequest(optVal []byte) (linux.InetMulticastRequestWithNIC, *
return req, nil
}
// reduceToByte ORs all of the bytes in the input.
func reduceToByte(buf []byte) byte {
var out byte
for _, b := range buf {
out |= b
}
return out
}
// setSockOptIP implements SetSockOpt when level is SOL_IP.
func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *syserr.Error {
switch name {
@@ -1235,6 +1259,18 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
InterfaceAddr: bytesToIPAddress(req.InterfaceAddr[:]),
}))
case linux.IP_MULTICAST_LOOP:
if len(optVal) < 1 {
return syserr.ErrInvalidArgument
}
if len(optVal) > sizeOfInt32 {
optVal = optVal[:sizeOfInt32]
}
return syserr.TranslateNetstackError(ep.SetSockOpt(
tcpip.MulticastLoopOption(reduceToByte(optVal) != 0),
))
case linux.MCAST_JOIN_GROUP:
// FIXME: Implement MCAST_JOIN_GROUP.
t.Kernel().EmitUnimplementedEvent(t)
@@ -1252,7 +1288,6 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
linux.IP_MSFILTER,
linux.IP_MTU_DISCOVER,
linux.IP_MULTICAST_ALL,
linux.IP_MULTICAST_LOOP,
linux.IP_NODEFRAG,
linux.IP_OPTIONS,
linux.IP_PASSSEC,
+2
View File
@@ -23,6 +23,7 @@ import (
var (
ErrUnknownProtocol = New(tcpip.ErrUnknownProtocol.String(), linux.EINVAL)
ErrUnknownNICID = New(tcpip.ErrUnknownNICID.String(), linux.EINVAL)
ErrUnknownDevice = New(tcpip.ErrUnknownDevice.String(), linux.ENODEV)
ErrUnknownProtocolOption = New(tcpip.ErrUnknownProtocolOption.String(), linux.ENOPROTOOPT)
ErrDuplicateNICID = New(tcpip.ErrDuplicateNICID.String(), linux.EEXIST)
ErrDuplicateAddress = New(tcpip.ErrDuplicateAddress.String(), linux.EEXIST)
@@ -49,6 +50,7 @@ var (
var netstackErrorTranslations = map[*tcpip.Error]*Error{
tcpip.ErrUnknownProtocol: ErrUnknownProtocol,
tcpip.ErrUnknownNICID: ErrUnknownNICID,
tcpip.ErrUnknownDevice: ErrUnknownDevice,
tcpip.ErrUnknownProtocolOption: ErrUnknownProtocolOption,
tcpip.ErrDuplicateNICID: ErrDuplicateNICID,
tcpip.ErrDuplicateAddress: ErrDuplicateAddress,
+1 -1
View File
@@ -79,7 +79,7 @@ func (e *endpoint) MaxHeaderLength() uint16 {
func (e *endpoint) Close() {}
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error {
func (e *endpoint) WritePacket(*stack.Route, buffer.Prependable, buffer.VectorisedView, tcpip.TransportProtocolNumber, uint8, stack.PacketLooping) *tcpip.Error {
return tcpip.ErrNotSupported
}
+4 -4
View File
@@ -177,7 +177,7 @@ func buildIPv4Route(local, remote tcpip.Address) (stack.Route, *tcpip.Error) {
NIC: 1,
}})
return s.FindRoute(1, local, remote, ipv4.ProtocolNumber)
return s.FindRoute(1, local, remote, ipv4.ProtocolNumber, false /* multicastLoop */)
}
func buildIPv6Route(local, remote tcpip.Address) (stack.Route, *tcpip.Error) {
@@ -191,7 +191,7 @@ func buildIPv6Route(local, remote tcpip.Address) (stack.Route, *tcpip.Error) {
NIC: 1,
}})
return s.FindRoute(1, local, remote, ipv6.ProtocolNumber)
return s.FindRoute(1, local, remote, ipv6.ProtocolNumber, false /* multicastLoop */)
}
func TestIPv4Send(t *testing.T) {
@@ -221,7 +221,7 @@ func TestIPv4Send(t *testing.T) {
if err != nil {
t.Fatalf("could not find route: %v", err)
}
if err := ep.WritePacket(&r, hdr, payload.ToVectorisedView(), 123, 123); err != nil {
if err := ep.WritePacket(&r, hdr, payload.ToVectorisedView(), 123, 123, stack.PacketOut); err != nil {
t.Fatalf("WritePacket failed: %v", err)
}
}
@@ -450,7 +450,7 @@ func TestIPv6Send(t *testing.T) {
if err != nil {
t.Fatalf("could not find route: %v", err)
}
if err := ep.WritePacket(&r, hdr, payload.ToVectorisedView(), 123, 123); err != nil {
if err := ep.WritePacket(&r, hdr, payload.ToVectorisedView(), 123, 123, stack.PacketOut); err != nil {
t.Fatalf("WritePacket failed: %v", err)
}
}
+13 -2
View File
@@ -104,7 +104,7 @@ func (e *endpoint) MaxHeaderLength() uint16 {
}
// WritePacket writes a packet to the given destination address and protocol.
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error {
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, ttl uint8, loop stack.PacketLooping) *tcpip.Error {
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
length := uint16(hdr.UsedLength() + payload.Size())
id := uint32(0)
@@ -123,8 +123,19 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload b
DstAddr: r.RemoteAddress,
})
ip.SetChecksum(^ip.CalculateChecksum())
r.Stats().IP.PacketsSent.Increment()
if loop&stack.PacketLoop != 0 {
views := make([]buffer.View, 1, 1+len(payload.Views()))
views[0] = hdr.View()
views = append(views, payload.Views()...)
vv := buffer.NewVectorisedView(len(views[0])+payload.Size(), views)
e.HandlePacket(r, vv)
}
if loop&stack.PacketOut == 0 {
return nil
}
r.Stats().IP.PacketsSent.Increment()
return e.linkEP.WritePacket(r, hdr, payload, ProtocolNumber)
}
+1 -1
View File
@@ -161,7 +161,7 @@ func (c *testContext) cleanup() {
func TestLinkResolution(t *testing.T) {
c := newTestContext(t)
defer c.cleanup()
r, err := c.s0.FindRoute(1, lladdr0, lladdr1, ProtocolNumber)
r, err := c.s0.FindRoute(1, lladdr0, lladdr1, ProtocolNumber, false /* multicastLoop */)
if err != nil {
t.Fatal(err)
}
+13 -2
View File
@@ -84,7 +84,7 @@ func (e *endpoint) MaxHeaderLength() uint16 {
}
// WritePacket writes a packet to the given destination address and protocol.
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error {
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, ttl uint8, loop stack.PacketLooping) *tcpip.Error {
length := uint16(hdr.UsedLength() + payload.Size())
ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
ip.Encode(&header.IPv6Fields{
@@ -94,8 +94,19 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload b
SrcAddr: r.LocalAddress,
DstAddr: r.RemoteAddress,
})
r.Stats().IP.PacketsSent.Increment()
if loop&stack.PacketLoop != 0 {
views := make([]buffer.View, 1, 1+len(payload.Views()))
views[0] = hdr.View()
views = append(views, payload.Views()...)
vv := buffer.NewVectorisedView(len(views[0])+payload.Size(), views)
e.HandlePacket(r, vv)
}
if loop&stack.PacketOut == 0 {
return nil
}
r.Stats().IP.PacketsSent.Increment()
return e.linkEP.WritePacket(r, hdr, payload, ProtocolNumber)
}
+10 -8
View File
@@ -28,10 +28,11 @@ import (
// NIC represents a "network interface card" to which the networking stack is
// attached.
type NIC struct {
stack *Stack
id tcpip.NICID
name string
linkEP LinkEndpoint
stack *Stack
id tcpip.NICID
name string
linkEP LinkEndpoint
loopback bool
demux *transportDemuxer
@@ -62,12 +63,13 @@ const (
NeverPrimaryEndpoint
)
func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint) *NIC {
func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, loopback bool) *NIC {
return &NIC{
stack: stack,
id: id,
name: name,
linkEP: ep,
loopback: loopback,
demux: newTransportDemuxer(stack),
primary: make(map[tcpip.NetworkProtocolNumber]*ilist.List),
endpoints: make(map[NetworkEndpointID]*referencedNetworkEndpoint),
@@ -407,7 +409,7 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, _ tcpip.LinkAddr
n.mu.RLock()
for _, ref := range n.endpoints {
if ref.protocol == header.IPv4ProtocolNumber && ref.tryIncRef() {
r := makeRoute(protocol, dst, src, linkEP.LinkAddress(), ref)
r := makeRoute(protocol, dst, src, linkEP.LinkAddress(), ref, false /* multicastLoop */)
r.RemoteLinkAddress = remote
ref.ep.HandlePacket(&r, vv)
ref.decRef()
@@ -418,7 +420,7 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, _ tcpip.LinkAddr
}
if ref := n.getRef(protocol, dst); ref != nil {
r := makeRoute(protocol, dst, src, linkEP.LinkAddress(), ref)
r := makeRoute(protocol, dst, src, linkEP.LinkAddress(), ref, false /* multicastLoop */)
r.RemoteLinkAddress = remote
ref.ep.HandlePacket(&r, vv)
ref.decRef()
@@ -430,7 +432,7 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remote, _ tcpip.LinkAddr
//
// TODO: Should we be forwarding the packet even if promiscuous?
if n.stack.Forwarding() {
r, err := n.stack.FindRoute(0, "", dst, protocol)
r, err := n.stack.FindRoute(0, "", dst, protocol, false /* multicastLoop */)
if err != nil {
n.stack.stats.IP.InvalidAddressesReceived.Increment()
return
+13 -1
View File
@@ -125,6 +125,18 @@ type TransportDispatcher interface {
DeliverTransportControlPacket(local, remote tcpip.Address, net tcpip.NetworkProtocolNumber, trans tcpip.TransportProtocolNumber, typ ControlType, extra uint32, vv buffer.VectorisedView)
}
// PacketLooping specifies where an outbound packet should be sent.
type PacketLooping byte
const (
// PacketOut indicates that the packet should be passed to the link
// endpoint.
PacketOut PacketLooping = 1 << iota
// PacketLoop indicates that the packet should be handled locally.
PacketLoop
)
// NetworkEndpoint is the interface that needs to be implemented by endpoints
// of network layer protocols (e.g., ipv4, ipv6).
type NetworkEndpoint interface {
@@ -149,7 +161,7 @@ type NetworkEndpoint interface {
// WritePacket writes a packet to the given destination address and
// protocol.
WritePacket(r *Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error
WritePacket(r *Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, ttl uint8, loop PacketLooping) *tcpip.Error
// ID returns the network protocol endpoint ID.
ID() *NetworkEndpointID
+10 -2
View File
@@ -46,17 +46,20 @@ type Route struct {
// ref a reference to the network endpoint through which the route
// starts.
ref *referencedNetworkEndpoint
multicastLoop bool
}
// makeRoute initializes a new route. It takes ownership of the provided
// reference to a network endpoint.
func makeRoute(netProto tcpip.NetworkProtocolNumber, localAddr, remoteAddr tcpip.Address, localLinkAddr tcpip.LinkAddress, ref *referencedNetworkEndpoint) Route {
func makeRoute(netProto tcpip.NetworkProtocolNumber, localAddr, remoteAddr tcpip.Address, localLinkAddr tcpip.LinkAddress, ref *referencedNetworkEndpoint, multicastLoop bool) Route {
return Route{
NetProto: netProto,
LocalAddress: localAddr,
LocalLinkAddress: localLinkAddr,
RemoteAddress: remoteAddr,
ref: ref,
multicastLoop: multicastLoop,
}
}
@@ -134,7 +137,12 @@ func (r *Route) IsResolutionRequired() bool {
// WritePacket writes the packet through the given route.
func (r *Route) WritePacket(hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error {
err := r.ref.ep.WritePacket(r, hdr, payload, protocol, ttl)
loop := PacketOut
if r.multicastLoop && (header.IsV4MulticastAddress(r.RemoteAddress) || header.IsV6MulticastAddress(r.RemoteAddress)) {
loop |= PacketLoop
}
err := r.ref.ep.WritePacket(r, hdr, payload, protocol, ttl, loop)
if err == tcpip.ErrNoRoute {
r.Stats().IP.OutgoingPacketErrors.Increment()
}
+15 -9
View File
@@ -513,7 +513,7 @@ func (s *Stack) NewRawEndpoint(transport tcpip.TransportProtocolNumber, network
// createNIC creates a NIC with the provided id and link-layer endpoint, and
// optionally enable it.
func (s *Stack) createNIC(id tcpip.NICID, name string, linkEP tcpip.LinkEndpointID, enabled bool) *tcpip.Error {
func (s *Stack) createNIC(id tcpip.NICID, name string, linkEP tcpip.LinkEndpointID, enabled, loopback bool) *tcpip.Error {
ep := FindLinkEndpoint(linkEP)
if ep == nil {
return tcpip.ErrBadLinkEndpoint
@@ -527,7 +527,7 @@ func (s *Stack) createNIC(id tcpip.NICID, name string, linkEP tcpip.LinkEndpoint
return tcpip.ErrDuplicateNICID
}
n := newNIC(s, id, name, ep)
n := newNIC(s, id, name, ep, loopback)
s.nics[id] = n
if enabled {
@@ -539,26 +539,32 @@ func (s *Stack) createNIC(id tcpip.NICID, name string, linkEP tcpip.LinkEndpoint
// CreateNIC creates a NIC with the provided id and link-layer endpoint.
func (s *Stack) CreateNIC(id tcpip.NICID, linkEP tcpip.LinkEndpointID) *tcpip.Error {
return s.createNIC(id, "", linkEP, true)
return s.createNIC(id, "", linkEP, true, false)
}
// CreateNamedNIC creates a NIC with the provided id and link-layer endpoint,
// and a human-readable name.
func (s *Stack) CreateNamedNIC(id tcpip.NICID, name string, linkEP tcpip.LinkEndpointID) *tcpip.Error {
return s.createNIC(id, name, linkEP, true)
return s.createNIC(id, name, linkEP, true, false)
}
// CreateNamedLoopbackNIC creates a NIC with the provided id and link-layer
// endpoint, and a human-readable name.
func (s *Stack) CreateNamedLoopbackNIC(id tcpip.NICID, name string, linkEP tcpip.LinkEndpointID) *tcpip.Error {
return s.createNIC(id, name, linkEP, true, true)
}
// CreateDisabledNIC creates a NIC with the provided id and link-layer endpoint,
// but leave it disable. Stack.EnableNIC must be called before the link-layer
// endpoint starts delivering packets to it.
func (s *Stack) CreateDisabledNIC(id tcpip.NICID, linkEP tcpip.LinkEndpointID) *tcpip.Error {
return s.createNIC(id, "", linkEP, false)
return s.createNIC(id, "", linkEP, false, false)
}
// CreateDisabledNamedNIC is a combination of CreateNamedNIC and
// CreateDisabledNIC.
func (s *Stack) CreateDisabledNamedNIC(id tcpip.NICID, name string, linkEP tcpip.LinkEndpointID) *tcpip.Error {
return s.createNIC(id, name, linkEP, false)
return s.createNIC(id, name, linkEP, false, false)
}
// EnableNIC enables the given NIC so that the link-layer endpoint can start
@@ -748,7 +754,7 @@ func (s *Stack) getRefEP(nic *NIC, localAddr tcpip.Address, netProto tcpip.Netwo
// FindRoute creates a route to the given destination address, leaving through
// the given nic and local address (if provided).
func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, netProto tcpip.NetworkProtocolNumber) (Route, *tcpip.Error) {
func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, netProto tcpip.NetworkProtocolNumber, multicastLoop bool) (Route, *tcpip.Error) {
s.mu.RLock()
defer s.mu.RUnlock()
@@ -758,7 +764,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
if id != 0 && !needRoute {
if nic, ok := s.nics[id]; ok {
if ref := s.getRefEP(nic, localAddr, netProto); ref != nil {
return makeRoute(netProto, ref.ep.ID().LocalAddress, remoteAddr, nic.linkEP.LinkAddress(), ref), nil
return makeRoute(netProto, ref.ep.ID().LocalAddress, remoteAddr, nic.linkEP.LinkAddress(), ref, multicastLoop && !nic.loopback), nil
}
}
} else {
@@ -774,7 +780,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
remoteAddr = ref.ep.ID().LocalAddress
}
r := makeRoute(netProto, ref.ep.ID().LocalAddress, remoteAddr, nic.linkEP.LinkAddress(), ref)
r := makeRoute(netProto, ref.ep.ID().LocalAddress, remoteAddr, nic.linkEP.LinkAddress(), ref, multicastLoop && !nic.loopback)
if needRoute {
r.NextHop = route.Gateway
}
+26 -14
View File
@@ -112,7 +112,7 @@ func (f *fakeNetworkEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return f.linkEP.Capabilities()
}
func (f *fakeNetworkEndpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, _ uint8) *tcpip.Error {
func (f *fakeNetworkEndpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, protocol tcpip.TransportProtocolNumber, _ uint8, loop stack.PacketLooping) *tcpip.Error {
// Increment the sent packet count in the protocol descriptor.
f.proto.sendPacketCount[int(r.RemoteAddress[0])%len(f.proto.sendPacketCount)]++
@@ -122,6 +122,18 @@ func (f *fakeNetworkEndpoint) WritePacket(r *stack.Route, hdr buffer.Prependable
b[0] = r.RemoteAddress[0]
b[1] = f.id.LocalAddress[0]
b[2] = byte(protocol)
if loop&stack.PacketLoop != 0 {
views := make([]buffer.View, 1, 1+len(payload.Views()))
views[0] = hdr.View()
views = append(views, payload.Views()...)
vv := buffer.NewVectorisedView(len(views[0])+payload.Size(), views)
f.HandlePacket(r, vv)
}
if loop&stack.PacketOut == 0 {
return nil
}
return f.linkEP.WritePacket(r, hdr, payload, fakeNetNumber)
}
@@ -262,7 +274,7 @@ func TestNetworkReceive(t *testing.T) {
}
func sendTo(t *testing.T, s *stack.Stack, addr tcpip.Address) {
r, err := s.FindRoute(0, "", addr, fakeNetNumber)
r, err := s.FindRoute(0, "", addr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute failed: %v", err)
}
@@ -354,7 +366,7 @@ func TestNetworkSendMultiRoute(t *testing.T) {
}
func testRoute(t *testing.T, s *stack.Stack, nic tcpip.NICID, srcAddr, dstAddr, expectedSrcAddr tcpip.Address) {
r, err := s.FindRoute(nic, srcAddr, dstAddr, fakeNetNumber)
r, err := s.FindRoute(nic, srcAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute failed: %v", err)
}
@@ -371,7 +383,7 @@ func testRoute(t *testing.T, s *stack.Stack, nic tcpip.NICID, srcAddr, dstAddr,
}
func testNoRoute(t *testing.T, s *stack.Stack, nic tcpip.NICID, srcAddr, dstAddr tcpip.Address) {
_, err := s.FindRoute(nic, srcAddr, dstAddr, fakeNetNumber)
_, err := s.FindRoute(nic, srcAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err != tcpip.ErrNoRoute {
t.Fatalf("FindRoute returned unexpected error, expected tcpip.ErrNoRoute, got %v", err)
}
@@ -514,7 +526,7 @@ func TestDelayedRemovalDueToRoute(t *testing.T) {
}
// Get a route, check that packet is still deliverable.
r, err := s.FindRoute(0, "", "\x02", fakeNetNumber)
r, err := s.FindRoute(0, "", "\x02", fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute failed: %v", err)
}
@@ -584,7 +596,7 @@ func TestPromiscuousMode(t *testing.T) {
}
// Check that we can't get a route as there is no local address.
_, err := s.FindRoute(0, "", "\x02", fakeNetNumber)
_, err := s.FindRoute(0, "", "\x02", fakeNetNumber, false /* multicastLoop */)
if err != tcpip.ErrNoRoute {
t.Fatalf("FindRoute returned unexpected status: expected %v, got %v", tcpip.ErrNoRoute, err)
}
@@ -622,7 +634,7 @@ func TestAddressSpoofing(t *testing.T) {
// With address spoofing disabled, FindRoute does not permit an address
// that was not added to the NIC to be used as the source.
r, err := s.FindRoute(0, srcAddr, dstAddr, fakeNetNumber)
r, err := s.FindRoute(0, srcAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err == nil {
t.Errorf("FindRoute succeeded with route %+v when it should have failed", r)
}
@@ -632,7 +644,7 @@ func TestAddressSpoofing(t *testing.T) {
if err := s.SetSpoofing(1, true); err != nil {
t.Fatalf("SetSpoofing failed: %v", err)
}
r, err = s.FindRoute(0, srcAddr, dstAddr, fakeNetNumber)
r, err = s.FindRoute(0, srcAddr, dstAddr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute failed: %v", err)
}
@@ -654,14 +666,14 @@ func TestBroadcastNeedsNoRoute(t *testing.T) {
s.SetRouteTable([]tcpip.Route{})
// If there is no endpoint, it won't work.
if _, err := s.FindRoute(1, header.IPv4Any, header.IPv4Broadcast, fakeNetNumber); err != tcpip.ErrNetworkUnreachable {
if _, err := s.FindRoute(1, header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, false /* multicastLoop */); err != tcpip.ErrNetworkUnreachable {
t.Fatalf("got FindRoute(1, %v, %v, %v) = %v, want = %v", header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, err, tcpip.ErrNetworkUnreachable)
}
if err := s.AddAddress(1, fakeNetNumber, header.IPv4Any); err != nil {
t.Fatalf("AddAddress(%v, %v) failed: %v", fakeNetNumber, header.IPv4Any, err)
}
r, err := s.FindRoute(1, header.IPv4Any, header.IPv4Broadcast, fakeNetNumber)
r, err := s.FindRoute(1, header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute(1, %v, %v, %v) failed: %v", header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, err)
}
@@ -675,7 +687,7 @@ func TestBroadcastNeedsNoRoute(t *testing.T) {
}
// If the NIC doesn't exist, it won't work.
if _, err := s.FindRoute(2, header.IPv4Any, header.IPv4Broadcast, fakeNetNumber); err != tcpip.ErrNetworkUnreachable {
if _, err := s.FindRoute(2, header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, false /* multicastLoop */); err != tcpip.ErrNetworkUnreachable {
t.Fatalf("got FindRoute(2, %v, %v, %v) = %v want = %v", header.IPv4Any, header.IPv4Broadcast, fakeNetNumber, err, tcpip.ErrNetworkUnreachable)
}
}
@@ -738,7 +750,7 @@ func TestMulticastOrIPv6LinkLocalNeedsNoRoute(t *testing.T) {
}
// If there is no endpoint, it won't work.
if _, err := s.FindRoute(1, anyAddr, tc.address, fakeNetNumber); err != want {
if _, err := s.FindRoute(1, anyAddr, tc.address, fakeNetNumber, false /* multicastLoop */); err != want {
t.Fatalf("got FindRoute(1, %v, %v, %v) = %v, want = %v", anyAddr, tc.address, fakeNetNumber, err, want)
}
@@ -746,7 +758,7 @@ func TestMulticastOrIPv6LinkLocalNeedsNoRoute(t *testing.T) {
t.Fatalf("AddAddress(%v, %v) failed: %v", fakeNetNumber, anyAddr, err)
}
if r, err := s.FindRoute(1, anyAddr, tc.address, fakeNetNumber); tc.routeNeeded {
if r, err := s.FindRoute(1, anyAddr, tc.address, fakeNetNumber, false /* multicastLoop */); tc.routeNeeded {
// Route table is empty but we need a route, this should cause an error.
if err != tcpip.ErrNoRoute {
t.Fatalf("got FindRoute(1, %v, %v, %v) = %v, want = %v", anyAddr, tc.address, fakeNetNumber, err, tcpip.ErrNoRoute)
@@ -763,7 +775,7 @@ func TestMulticastOrIPv6LinkLocalNeedsNoRoute(t *testing.T) {
}
}
// If the NIC doesn't exist, it won't work.
if _, err := s.FindRoute(2, anyAddr, tc.address, fakeNetNumber); err != want {
if _, err := s.FindRoute(2, anyAddr, tc.address, fakeNetNumber, false /* multicastLoop */); err != want {
t.Fatalf("got FindRoute(2, %v, %v, %v) = %v want = %v", anyAddr, tc.address, fakeNetNumber, err, want)
}
})
+1 -1
View File
@@ -103,7 +103,7 @@ func (f *fakeTransportEndpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
f.peerAddr = addr.Addr
// Find the route.
r, err := f.stack.FindRoute(addr.NIC, "", addr.Addr, fakeNetNumber)
r, err := f.stack.FindRoute(addr.NIC, "", addr.Addr, fakeNetNumber, false /* multicastLoop */)
if err != nil {
return tcpip.ErrNoRoute
}
+5
View File
@@ -68,6 +68,7 @@ func (e *Error) IgnoreStats() bool {
var (
ErrUnknownProtocol = &Error{msg: "unknown protocol"}
ErrUnknownNICID = &Error{msg: "unknown nic id"}
ErrUnknownDevice = &Error{msg: "unknown device"}
ErrUnknownProtocolOption = &Error{msg: "unknown option for protocol"}
ErrDuplicateNICID = &Error{msg: "duplicate nic id"}
ErrDuplicateAddress = &Error{msg: "duplicate address"}
@@ -477,6 +478,10 @@ type MulticastInterfaceOption struct {
InterfaceAddr Address
}
// MulticastLoopOption is used by SetSockOpt/GetSockOpt to specify whether
// multicast packets sent over a non-loopback interface will be looped back.
type MulticastLoopOption bool
// MembershipOption is used by SetSockOpt/GetSockOpt as an argument to
// AddMembershipOption and RemoveMembershipOption.
type MembershipOption struct {
+2 -2
View File
@@ -277,7 +277,7 @@ func (e *endpoint) Write(p tcpip.Payload, opts tcpip.WriteOptions) (uintptr, <-c
}
// Find the enpoint.
r, err := e.stack.FindRoute(nicid, e.bindAddr, to.Addr, netProto)
r, err := e.stack.FindRoute(nicid, e.bindAddr, to.Addr, netProto, false /* multicastLoop */)
if err != nil {
return 0, nil, err
}
@@ -471,7 +471,7 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
}
// Find a route to the desired destination.
r, err := e.stack.FindRoute(nicid, e.bindAddr, addr.Addr, netProto)
r, err := e.stack.FindRoute(nicid, e.bindAddr, addr.Addr, netProto, false /* multicastLoop */)
if err != nil {
return err
}
+1 -1
View File
@@ -71,7 +71,7 @@ func (e *endpoint) afterLoad() {
var err *tcpip.Error
if e.state == stateConnected {
e.route, err = e.stack.FindRoute(e.regNICID, e.bindAddr, e.id.RemoteAddress, e.netProto)
e.route, err = e.stack.FindRoute(e.regNICID, e.bindAddr, e.id.RemoteAddress, e.netProto, false /* multicastLoop */)
if err != nil {
panic(*err)
}
+1 -1
View File
@@ -1091,7 +1091,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) (er
}
// Find a route to the desired destination.
r, err := e.stack.FindRoute(nicid, e.id.LocalAddress, addr.Addr, netProto)
r, err := e.stack.FindRoute(nicid, e.id.LocalAddress, addr.Addr, netProto, false /* multicastLoop */)
if err != nil {
return err
}
@@ -307,6 +307,7 @@ func loadError(s string) *tcpip.Error {
var errors = []*tcpip.Error{
tcpip.ErrUnknownProtocol,
tcpip.ErrUnknownNICID,
tcpip.ErrUnknownDevice,
tcpip.ErrUnknownProtocolOption,
tcpip.ErrDuplicateNICID,
tcpip.ErrDuplicateAddress,
+1
View File
@@ -27,6 +27,7 @@ go_library(
imports = ["gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"],
visibility = ["//visibility:public"],
deps = [
"//pkg/log",
"//pkg/sleep",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
+36 -7
View File
@@ -81,6 +81,7 @@ type endpoint struct {
multicastTTL uint8
multicastAddr tcpip.Address
multicastNICID tcpip.NICID
multicastLoop bool
reusePort bool
broadcast bool
@@ -124,6 +125,7 @@ func newEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, waite
//
// Linux defaults to TTL=1.
multicastTTL: 1,
multicastLoop: true,
rcvBufSizeMax: 32 * 1024,
sndBufSize: 32 * 1024,
}
@@ -274,7 +276,7 @@ func (e *endpoint) connectRoute(nicid tcpip.NICID, addr tcpip.FullAddress) (stac
}
// Find a route to the desired destination.
r, err := e.stack.FindRoute(nicid, localAddr, addr.Addr, netProto)
r, err := e.stack.FindRoute(nicid, localAddr, addr.Addr, netProto, e.multicastLoop)
if err != nil {
return stack.Route{}, 0, 0, err
}
@@ -458,13 +460,19 @@ func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
case tcpip.AddMembershipOption:
nicID := v.NIC
if v.InterfaceAddr != header.IPv4Any {
if v.InterfaceAddr == header.IPv4Any {
if nicID == 0 {
r, err := e.stack.FindRoute(0, "", v.MulticastAddr, header.IPv4ProtocolNumber, false /* multicastLoop */)
if err == nil {
nicID = r.NICID()
r.Release()
}
}
} else {
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
}
if nicID == 0 {
// TODO: Allow adding memberships without
// specifing an interface.
return tcpip.ErrNoRoute
return tcpip.ErrUnknownDevice
}
// TODO: check that v.MulticastAddr is a multicast address.
@@ -479,11 +487,19 @@ func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
case tcpip.RemoveMembershipOption:
nicID := v.NIC
if v.InterfaceAddr != header.IPv4Any {
if v.InterfaceAddr == header.IPv4Any {
if nicID == 0 {
r, err := e.stack.FindRoute(0, "", v.MulticastAddr, header.IPv4ProtocolNumber, false /* multicastLoop */)
if err == nil {
nicID = r.NICID()
r.Release()
}
}
} else {
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
}
if nicID == 0 {
return tcpip.ErrNoRoute
return tcpip.ErrUnknownDevice
}
// TODO: check that v.MulticastAddr is a multicast address.
@@ -503,6 +519,11 @@ func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
}
}
case tcpip.MulticastLoopOption:
e.mu.Lock()
e.multicastLoop = bool(v)
e.mu.Unlock()
case tcpip.ReusePortOption:
e.mu.Lock()
e.reusePort = v != 0
@@ -578,6 +599,14 @@ func (e *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
e.mu.Unlock()
return nil
case *tcpip.MulticastLoopOption:
e.mu.RLock()
v := e.multicastLoop
e.mu.RUnlock()
*o = tcpip.MulticastLoopOption(v)
return nil
case *tcpip.ReusePortOption:
e.mu.RLock()
v := e.reusePort

Some files were not shown because too many files have changed in this diff Show More