blob: e1335968ef23055045741b1811952c80931f29f7 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package netstack
import (
"errors"
"fmt"
"syscall/zx"
"syscall/zx/fidl"
"time"
"go.fuchsia.dev/fuchsia/src/connectivity/network/netstack/fidlconv"
"go.fuchsia.dev/fuchsia/src/lib/component"
syslog "go.fuchsia.dev/fuchsia/src/lib/syslog/go"
"fidl/fuchsia/net"
"fidl/fuchsia/net/neighbor"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const (
nudTag = "NUD"
)
type nudDispatcher struct {
ns *Netstack
}
var _ stack.NUDDispatcher = (*nudDispatcher)(nil)
// OnNeighborAdded implements stack.NUDDispatcher.
func (d *nudDispatcher) OnNeighborAdded(nicID tcpip.NICID, addr tcpip.Address, linkAddr tcpip.LinkAddress, state stack.NeighborState, _ time.Time) {
// TODO(fxbug.dev/62788): Change log level to Debug once the neighbor table
// is able to be inspected.
_ = syslog.InfoTf(nudTag, "ADD %s NIC=%s LinkAddress=%s %s", addr, d.ns.name(nicID), linkAddr, state)
}
// OnNeighborChanged implements stack.NUDDispatcher.
func (d *nudDispatcher) OnNeighborChanged(nicID tcpip.NICID, addr tcpip.Address, linkAddr tcpip.LinkAddress, state stack.NeighborState, _ time.Time) {
// TODO(fxbug.dev/62788): Change log level to Debug once the neighbor table
// is able to be inspected.
_ = syslog.InfoTf(nudTag, "MOD %s NIC=%s LinkAddress=%s %s", addr, d.ns.name(nicID), linkAddr, state)
}
// OnNeighborRemoved implements stack.NUDDispatcher.
func (d *nudDispatcher) OnNeighborRemoved(nicID tcpip.NICID, addr tcpip.Address, linkAddr tcpip.LinkAddress, state stack.NeighborState, _ time.Time) {
// TODO(fxbug.dev/62788): Change log level to Debug once the neighbor table
// is able to be inspected.
_ = syslog.InfoTf(nudTag, "DEL %s NIC=%s LinkAddress=%s %s", addr, d.ns.name(nicID), linkAddr, state)
}
type neighborImpl struct {
stack *stack.Stack
}
var _ neighbor.ViewWithCtx = (*neighborImpl)(nil)
func (n *neighborImpl) OpenEntryIterator(ctx fidl.Context, it neighbor.EntryIteratorWithCtxInterfaceRequest, options neighbor.EntryIteratorOptions) error {
// TODO(fxbug.dev/59425): Watch for changes.
var items []neighbor.EntryIteratorItem
for nicID := range n.stack.NICInfo() {
neighbors, err := n.stack.Neighbors(nicID)
switch err {
case nil:
case tcpip.ErrNotSupported:
// This NIC does not use a neighbor table.
continue
case tcpip.ErrUnknownNICID:
// This NIC was removed since stack.NICInfo() was called.
continue
default:
_ = syslog.ErrorTf(neighbor.ViewName, "EntryIterator received unexpected error from Neighbors(%d): %s", nicID, err)
_ = it.Close()
return WrapTcpIpError(err)
}
for _, n := range neighbors {
if netEntry, ok := toNeighborEntry(nicID, n); ok {
items = append(items, neighbor.EntryIteratorItemWithExisting(netEntry))
}
}
}
// End the list with a special item to indicate the end of existing entries.
items = append(items, neighbor.EntryIteratorItemWithIdle(neighbor.IdleEvent{}))
stub := neighbor.EntryIteratorWithCtxStub{
Impl: &neighborEntryIterator{
items: items,
},
}
go component.ServeExclusive(ctx, &stub, it.Channel, func(err error) {
_ = syslog.WarnTf(neighbor.ViewName, "EntryIterator: %s", err)
})
return nil
}
func (n *neighborImpl) GetUnreachabilityConfig(ctx fidl.Context, interfaceID uint64) (neighbor.ViewGetUnreachabilityConfigResult, error) {
// TODO(fxbug.dev/51776): Implement fuchsia.net.neighbor/View.GetUnreachabilityConfigs
return neighbor.ViewGetUnreachabilityConfigResult{}, &zx.Error{Status: zx.ErrNotSupported}
}
var _ neighbor.ControllerWithCtx = (*neighborImpl)(nil)
func (n *neighborImpl) AddEntry(ctx fidl.Context, interfaceID uint64, neighborIP net.IpAddress, mac net.MacAddress) (neighbor.ControllerAddEntryResult, error) {
// TODO(fxbug.dev/51777): Implement fuchsia.net.neighbor/Controller.AddEntry
resp := neighbor.ControllerAddEntryResponse{}
result := neighbor.ControllerAddEntryResultWithResponse(resp)
return result, &zx.Error{Status: zx.ErrNotSupported}
}
func (n *neighborImpl) RemoveEntry(ctx fidl.Context, interfaceID uint64, neighborIP net.IpAddress) (neighbor.ControllerRemoveEntryResult, error) {
// TODO(fxbug.dev/51778): Implement fuchsia.net.neighbor/Controller.RemoveEntry
resp := neighbor.ControllerRemoveEntryResponse{}
result := neighbor.ControllerRemoveEntryResultWithResponse(resp)
return result, &zx.Error{Status: zx.ErrNotSupported}
}
func (n *neighborImpl) ClearEntries(ctx fidl.Context, interfaceID uint64) (neighbor.ControllerClearEntriesResult, error) {
// TODO(fxbug.dev/51779): Implement fuchsia.net.neighbor/Controller.ClearEntries
resp := neighbor.ControllerClearEntriesResponse{}
result := neighbor.ControllerClearEntriesResultWithResponse(resp)
return result, &zx.Error{Status: zx.ErrNotSupported}
}
func (n *neighborImpl) UpdateUnreachabilityConfig(ctx fidl.Context, interfaceID uint64, config neighbor.UnreachabilityConfig) (neighbor.ControllerUpdateUnreachabilityConfigResult, error) {
// TODO(fxbug.dev/51780): Implement fuchsia.net.neighbor/Controller.UpdateUnreachabilityConfig
resp := neighbor.ControllerUpdateUnreachabilityConfigResponse{}
result := neighbor.ControllerUpdateUnreachabilityConfigResultWithResponse(resp)
return result, &zx.Error{Status: zx.ErrNotSupported}
}
// neighborEntryIterator queues events received from the neighbor table for
// consumption by a FIDL client.
type neighborEntryIterator struct {
// items contains neighbor entry notifications waiting for client consumption.
items []neighbor.EntryIteratorItem
}
var _ neighbor.EntryIteratorWithCtx = (*neighborEntryIterator)(nil)
// GetNext implements neighbor.EntryIteratorWithCtx.GetNext.
func (it *neighborEntryIterator) GetNext(ctx fidl.Context) ([]neighbor.EntryIteratorItem, error) {
if len(it.items) == 0 {
// TODO(fxbug.dev/59425): Watch for changes instead of closing the
// connection. This was deferred to unblock listing entries.
return nil, errors.New("watching for changes not supported")
}
items := it.items
if uint64(len(it.items)) > neighbor.MaxItemBatchSize {
// There are too many items to send; only send the max amount and leave the
// rest for subsequent calls.
items = items[:neighbor.MaxItemBatchSize]
}
it.items = it.items[len(items):]
// Avoid memory leak on always-appended slice.
if len(it.items) == 0 {
it.items = nil
}
return items, nil
}
// toNeighborEntry converts a stack.NeighborEntry to a
// fuchsia.net.neighbor/Entry. Returns the converted entry and true if the
// conversion was successful, false otherwise.
func toNeighborEntry(nicID tcpip.NICID, n stack.NeighborEntry) (neighbor.Entry, bool) {
e := neighbor.Entry{}
e.SetInterface(uint64(nicID))
e.SetUpdatedAt(n.UpdatedAt.UnixNano())
if len(n.Addr) != 0 {
e.SetNeighbor(fidlconv.ToNetIpAddress(n.Addr))
}
if len(n.LinkAddr) != 0 {
e.SetMac(fidlconv.ToNetMacAddress(n.LinkAddr))
}
switch n.State {
case stack.Unknown:
// Unknown is an internal state used by the netstack to represent a newly
// created or deleted entry. Clients do not need to be concerned with this
// in-between state; all transitions into and out of the Unknown state
// triggers an event.
return e, false
case stack.Incomplete:
e.SetState(neighbor.EntryStateWithIncomplete(neighbor.IncompleteState{}))
case stack.Reachable:
// TODO(fxbug.dev/59372): Populate expires_at.
e.SetState(neighbor.EntryStateWithReachable(neighbor.ReachableState{}))
case stack.Stale:
e.SetState(neighbor.EntryStateWithStale(neighbor.StaleState{}))
case stack.Delay:
e.SetState(neighbor.EntryStateWithDelay(neighbor.DelayState{}))
case stack.Probe:
e.SetState(neighbor.EntryStateWithProbe(neighbor.ProbeState{}))
case stack.Static:
e.SetState(neighbor.EntryStateWithStatic(neighbor.StaticState{}))
case stack.Failed:
// Failed is an internal state used by the netstack to inform transport
// endpoints of a failure to resolve a link-layer address. Clients should
// not be concerned with this error, thus is not representable by the
// fuchsia.net.neighbor FIDL. When an entry with this state is received, the
// entry should be skipped.
return e, false
default:
panic(fmt.Sprintf("invalid NeighborState = %d: %#v", n.State, n))
}
return e, true
}