mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Use multicast Ethernet address for multicast NDP
As per RFC 2464 section 7, an IPv6 packet with a multicast destination address is transmitted to the mapped Ethernet multicast address. Test: - ipv6.TestLinkResolution - stack_test.TestDADResolve - stack_test.TestRouterSolicitation PiperOrigin-RevId: 292610529
This commit is contained in:
committed by
gVisor bot
parent
528dd1ec72
commit
77bf586db7
@@ -17,6 +17,7 @@ package header_test
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -300,3 +301,31 @@ func TestScopeForIPv6Address(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSolicitedNodeAddr(t *testing.T) {
|
||||
tests := []struct {
|
||||
addr tcpip.Address
|
||||
want tcpip.Address
|
||||
}{
|
||||
{
|
||||
addr: "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\xa0",
|
||||
want: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x0e\x0f\xa0",
|
||||
},
|
||||
{
|
||||
addr: "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\xdd\x0e\x0f\xa0",
|
||||
want: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x0e\x0f\xa0",
|
||||
},
|
||||
{
|
||||
addr: "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\xdd\x01\x02\x03",
|
||||
want: "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x01\x02\x03",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s", test.addr), func(t *testing.T) {
|
||||
if got := header.SolicitedNodeAddr(test.addr); got != test.want {
|
||||
t.Fatalf("got header.SolicitedNodeAddr(%s) = %s, want = %s", test.addr, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,15 +30,16 @@ type PacketInfo struct {
|
||||
Pkt tcpip.PacketBuffer
|
||||
Proto tcpip.NetworkProtocolNumber
|
||||
GSO *stack.GSO
|
||||
Route stack.Route
|
||||
}
|
||||
|
||||
// Endpoint is link layer endpoint that stores outbound packets in a channel
|
||||
// and allows injection of inbound packets.
|
||||
type Endpoint struct {
|
||||
dispatcher stack.NetworkDispatcher
|
||||
mtu uint32
|
||||
linkAddr tcpip.LinkAddress
|
||||
GSO bool
|
||||
dispatcher stack.NetworkDispatcher
|
||||
mtu uint32
|
||||
linkAddr tcpip.LinkAddress
|
||||
LinkEPCapabilities stack.LinkEndpointCapabilities
|
||||
|
||||
// c is where outbound packets are queued.
|
||||
c chan PacketInfo
|
||||
@@ -122,11 +123,7 @@ func (e *Endpoint) MTU() uint32 {
|
||||
|
||||
// Capabilities implements stack.LinkEndpoint.Capabilities.
|
||||
func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||
caps := stack.LinkEndpointCapabilities(0)
|
||||
if e.GSO {
|
||||
caps |= stack.CapabilityHardwareGSO
|
||||
}
|
||||
return caps
|
||||
return e.LinkEPCapabilities
|
||||
}
|
||||
|
||||
// GSOMaxSize returns the maximum GSO packet size.
|
||||
@@ -146,11 +143,16 @@ func (e *Endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
}
|
||||
|
||||
// WritePacket stores outbound packets into the channel.
|
||||
func (e *Endpoint) WritePacket(_ *stack.Route, gso *stack.GSO, protocol tcpip.NetworkProtocolNumber, pkt tcpip.PacketBuffer) *tcpip.Error {
|
||||
func (e *Endpoint) WritePacket(r *stack.Route, gso *stack.GSO, protocol tcpip.NetworkProtocolNumber, pkt tcpip.PacketBuffer) *tcpip.Error {
|
||||
// Clone r then release its resource so we only get the relevant fields from
|
||||
// stack.Route without holding a reference to a NIC's endpoint.
|
||||
route := r.Clone()
|
||||
route.Release()
|
||||
p := PacketInfo{
|
||||
Pkt: pkt,
|
||||
Proto: protocol,
|
||||
GSO: gso,
|
||||
Route: route,
|
||||
}
|
||||
|
||||
select {
|
||||
@@ -162,7 +164,11 @@ func (e *Endpoint) WritePacket(_ *stack.Route, gso *stack.GSO, protocol tcpip.Ne
|
||||
}
|
||||
|
||||
// WritePackets stores outbound packets into the channel.
|
||||
func (e *Endpoint) WritePackets(_ *stack.Route, gso *stack.GSO, pkts []tcpip.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
func (e *Endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []tcpip.PacketBuffer, protocol tcpip.NetworkProtocolNumber) (int, *tcpip.Error) {
|
||||
// Clone r then release its resource so we only get the relevant fields from
|
||||
// stack.Route without holding a reference to a NIC's endpoint.
|
||||
route := r.Clone()
|
||||
route.Release()
|
||||
payloadView := pkts[0].Data.ToView()
|
||||
n := 0
|
||||
packetLoop:
|
||||
@@ -176,6 +182,7 @@ packetLoop:
|
||||
},
|
||||
Proto: protocol,
|
||||
GSO: gso,
|
||||
Route: route,
|
||||
}
|
||||
|
||||
select {
|
||||
|
||||
@@ -408,10 +408,14 @@ func (*protocol) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
|
||||
// LinkAddressRequest implements stack.LinkAddressResolver.
|
||||
func (*protocol) LinkAddressRequest(addr, localAddr tcpip.Address, linkEP stack.LinkEndpoint) *tcpip.Error {
|
||||
snaddr := header.SolicitedNodeAddr(addr)
|
||||
|
||||
// TODO(b/148672031): Use stack.FindRoute instead of manually creating the
|
||||
// route here. Note, we would need the nicID to do this properly so the right
|
||||
// NIC (associated to linkEP) is used to send the NDP NS message.
|
||||
r := &stack.Route{
|
||||
LocalAddress: localAddr,
|
||||
RemoteAddress: snaddr,
|
||||
RemoteLinkAddress: broadcastMAC,
|
||||
RemoteLinkAddress: header.EthernetAddressFromMulticastIPv6Address(snaddr),
|
||||
}
|
||||
hdr := buffer.NewPrependable(int(linkEP.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize)
|
||||
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize))
|
||||
|
||||
@@ -270,8 +270,9 @@ func (c *testContext) cleanup() {
|
||||
}
|
||||
|
||||
type routeArgs struct {
|
||||
src, dst *channel.Endpoint
|
||||
typ header.ICMPv6Type
|
||||
src, dst *channel.Endpoint
|
||||
typ header.ICMPv6Type
|
||||
remoteLinkAddr tcpip.LinkAddress
|
||||
}
|
||||
|
||||
func routeICMPv6Packet(t *testing.T, args routeArgs, fn func(*testing.T, header.ICMPv6)) {
|
||||
@@ -292,6 +293,11 @@ func routeICMPv6Packet(t *testing.T, args routeArgs, fn func(*testing.T, header.
|
||||
t.Errorf("unexpected protocol number %d", pi.Proto)
|
||||
return
|
||||
}
|
||||
|
||||
if len(args.remoteLinkAddr) != 0 && args.remoteLinkAddr != pi.Route.RemoteLinkAddress {
|
||||
t.Errorf("got remote link address = %s, want = %s", pi.Route.RemoteLinkAddress, args.remoteLinkAddr)
|
||||
}
|
||||
|
||||
ipv6 := header.IPv6(pi.Pkt.Header.View())
|
||||
transProto := tcpip.TransportProtocolNumber(ipv6.NextHeader())
|
||||
if transProto != header.ICMPv6ProtocolNumber {
|
||||
@@ -339,7 +345,7 @@ func TestLinkResolution(t *testing.T) {
|
||||
t.Fatalf("ep.Write(_) = _, <non-nil>, %s, want = _, <non-nil>, tcpip.ErrNoLinkAddress", err)
|
||||
}
|
||||
for _, args := range []routeArgs{
|
||||
{src: c.linkEP0, dst: c.linkEP1, typ: header.ICMPv6NeighborSolicit},
|
||||
{src: c.linkEP0, dst: c.linkEP1, typ: header.ICMPv6NeighborSolicit, remoteLinkAddr: header.EthernetAddressFromMulticastIPv6Address(header.SolicitedNodeAddr(lladdr1))},
|
||||
{src: c.linkEP1, dst: c.linkEP0, typ: header.ICMPv6NeighborAdvert},
|
||||
} {
|
||||
routeICMPv6Packet(t, args, func(t *testing.T, icmpv6 header.ICMPv6) {
|
||||
|
||||
@@ -538,6 +538,14 @@ func (ndp *ndpState) sendDADPacket(addr tcpip.Address) *tcpip.Error {
|
||||
r := makeRoute(header.IPv6ProtocolNumber, header.IPv6Any, snmc, ndp.nic.linkEP.LinkAddress(), ref, false, false)
|
||||
defer r.Release()
|
||||
|
||||
// Route should resolve immediately since snmc is a multicast address so a
|
||||
// remote link address can be calculated without a resolution process.
|
||||
if c, err := r.Resolve(nil); err != nil {
|
||||
log.Fatalf("ndp: error when resolving route to send NDP NS for DAD (%s -> %s on NIC(%d)): %s", header.IPv6Any, snmc, ndp.nic.ID(), err)
|
||||
} else if c != nil {
|
||||
log.Fatalf("ndp: route resolution not immediate for route to send NDP NS for DAD (%s -> %s on NIC(%d))", header.IPv6Any, snmc, ndp.nic.ID())
|
||||
}
|
||||
|
||||
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv6NeighborSolicitMinimumSize)
|
||||
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborSolicitMinimumSize))
|
||||
pkt.SetType(header.ICMPv6NeighborSolicit)
|
||||
@@ -1197,6 +1205,15 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
r := makeRoute(header.IPv6ProtocolNumber, header.IPv6Any, header.IPv6AllRoutersMulticastAddress, ndp.nic.linkEP.LinkAddress(), ref, false, false)
|
||||
defer r.Release()
|
||||
|
||||
// Route should resolve immediately since
|
||||
// header.IPv6AllRoutersMulticastAddress is a multicast address so a
|
||||
// remote link address can be calculated without a resolution process.
|
||||
if c, err := r.Resolve(nil); err != nil {
|
||||
log.Fatalf("ndp: error when resolving route to send NDP RS (%s -> %s on NIC(%d)): %s", header.IPv6Any, header.IPv6AllRoutersMulticastAddress, ndp.nic.ID(), err)
|
||||
} else if c != nil {
|
||||
log.Fatalf("ndp: route resolution not immediate for route to send NDP RS (%s -> %s on NIC(%d))", header.IPv6Any, header.IPv6AllRoutersMulticastAddress, ndp.nic.ID())
|
||||
}
|
||||
|
||||
payloadSize := header.ICMPv6HeaderSize + header.NDPRSMinimumSize
|
||||
hdr := buffer.NewPrependable(header.IPv6MinimumSize + payloadSize)
|
||||
pkt := header.ICMPv6(hdr.Prepend(payloadSize))
|
||||
|
||||
@@ -336,6 +336,7 @@ func TestDADResolve(t *testing.T) {
|
||||
opts.NDPConfigs.DupAddrDetectTransmits = test.dupAddrDetectTransmits
|
||||
|
||||
e := channel.New(int(test.dupAddrDetectTransmits), 1280, linkAddr1)
|
||||
e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
|
||||
s := stack.New(opts)
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
@@ -413,6 +414,12 @@ func TestDADResolve(t *testing.T) {
|
||||
t.Fatalf("got Proto = %d, want = %d", p.Proto, header.IPv6ProtocolNumber)
|
||||
}
|
||||
|
||||
// Make sure the right remote link address is used.
|
||||
snmc := header.SolicitedNodeAddr(addr1)
|
||||
if want := header.EthernetAddressFromMulticastIPv6Address(snmc); p.Route.RemoteLinkAddress != want {
|
||||
t.Errorf("got remote link address = %s, want = %s", p.Route.RemoteLinkAddress, want)
|
||||
}
|
||||
|
||||
// Check NDP NS packet.
|
||||
//
|
||||
// As per RFC 4861 section 4.3, a possible option is the Source Link
|
||||
@@ -420,7 +427,7 @@ func TestDADResolve(t *testing.T) {
|
||||
// address of the packet is the unspecified address.
|
||||
checker.IPv6(t, p.Pkt.Header.View().ToVectorisedView().First(),
|
||||
checker.SrcAddr(header.IPv6Any),
|
||||
checker.DstAddr(header.SolicitedNodeAddr(addr1)),
|
||||
checker.DstAddr(snmc),
|
||||
checker.TTL(header.NDPHopLimit),
|
||||
checker.NDPNS(
|
||||
checker.NDPNSTargetAddress(addr1),
|
||||
@@ -3292,6 +3299,7 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
e := channel.New(int(test.maxRtrSolicit), 1280, linkAddr1)
|
||||
e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
|
||||
waitForPkt := func(timeout time.Duration) {
|
||||
t.Helper()
|
||||
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||
@@ -3304,6 +3312,12 @@ func TestRouterSolicitation(t *testing.T) {
|
||||
if p.Proto != header.IPv6ProtocolNumber {
|
||||
t.Fatalf("got Proto = %d, want = %d", p.Proto, header.IPv6ProtocolNumber)
|
||||
}
|
||||
|
||||
// Make sure the right remote link address is used.
|
||||
if want := header.EthernetAddressFromMulticastIPv6Address(header.IPv6AllRoutersMulticastAddress); p.Route.RemoteLinkAddress != want {
|
||||
t.Errorf("got remote link address = %s, want = %s", p.Route.RemoteLinkAddress, want)
|
||||
}
|
||||
|
||||
checker.IPv6(t,
|
||||
p.Pkt.Header.View(),
|
||||
checker.SrcAddr(header.IPv6Any),
|
||||
|
||||
@@ -225,7 +225,9 @@ func (r *Route) Release() {
|
||||
// Clone Clone a route such that the original one can be released and the new
|
||||
// one will remain valid.
|
||||
func (r *Route) Clone() Route {
|
||||
r.ref.incRef()
|
||||
if r.ref != nil {
|
||||
r.ref.incRef()
|
||||
}
|
||||
return *r
|
||||
}
|
||||
|
||||
|
||||
@@ -1082,7 +1082,11 @@ func (c *Context) SACKEnabled() bool {
|
||||
|
||||
// SetGSOEnabled enables or disables generic segmentation offload.
|
||||
func (c *Context) SetGSOEnabled(enable bool) {
|
||||
c.linkEP.GSO = enable
|
||||
if enable {
|
||||
c.linkEP.LinkEPCapabilities |= stack.CapabilityHardwareGSO
|
||||
} else {
|
||||
c.linkEP.LinkEPCapabilities &^= stack.CapabilityHardwareGSO
|
||||
}
|
||||
}
|
||||
|
||||
// MSSWithoutOptions returns the value for the MSS used by the stack when no
|
||||
|
||||
Reference in New Issue
Block a user