blob: dfa938f98579b939eba9a0a9df019b175733ded3 [file] [log] [blame]
// Copyright 2018 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.
//go:build !build_with_native_toolchain
// +build !build_with_native_toolchain
package netstack
import (
"errors"
"fmt"
"syscall/zx"
"syscall/zx/fidl"
syslog "go.fuchsia.dev/fuchsia/src/lib/syslog/go"
"go.fuchsia.dev/fuchsia/src/connectivity/network/netstack/fidlconv"
"go.fuchsia.dev/fuchsia/src/connectivity/network/netstack/routes"
fidlethernet "fidl/fuchsia/hardware/ethernet"
"fidl/fuchsia/net"
"fidl/fuchsia/net/interfaces/admin"
"fidl/fuchsia/net/name"
"fidl/fuchsia/net/stack"
"fidl/fuchsia/netstack"
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/tcpip"
tcpipstack "gvisor.dev/gvisor/pkg/tcpip/stack"
)
var _ stack.StackWithCtx = (*stackImpl)(nil)
type stackImpl struct {
ns *Netstack
dnsWatchers *dnsServerWatcherCollection
}
func (ns *Netstack) delInterface(id uint64) stack.StackDelEthernetInterfaceResult {
var result stack.StackDelEthernetInterfaceResult
if nicInfo, ok := ns.stack.NICInfo()[tcpip.NICID(id)]; ok {
if nicInfo.Flags.Loopback {
result.SetErr(stack.ErrorNotSupported)
} else {
nicInfo.Context.(*ifState).RemoveByUser()
result.SetResponse(stack.StackDelEthernetInterfaceResponse{})
}
} else {
result.SetErr(stack.ErrorNotFound)
}
return result
}
func (ns *Netstack) enableInterface(id uint64) stack.StackEnableInterfaceDeprecatedResult {
var result stack.StackEnableInterfaceDeprecatedResult
nicInfo, ok := ns.stack.NICInfo()[tcpip.NICID(id)]
if !ok {
result.SetErr(stack.ErrorNotFound)
return result
}
if err := nicInfo.Context.(*ifState).Up(); err != nil {
_ = syslog.Errorf("ifs.Up() failed (NIC %d): %s", id, err)
result.SetErr(stack.ErrorInternal)
return result
}
result.SetResponse(stack.StackEnableInterfaceDeprecatedResponse{})
return result
}
func (ns *Netstack) disableInterface(id uint64) stack.StackDisableInterfaceDeprecatedResult {
var result stack.StackDisableInterfaceDeprecatedResult
nicInfo, ok := ns.stack.NICInfo()[tcpip.NICID(id)]
if !ok {
result.SetErr(stack.ErrorNotFound)
return result
}
if err := nicInfo.Context.(*ifState).Down(); err != nil {
_ = syslog.Errorf("ifs.Down() failed (NIC %d): %s", id, err)
result.SetErr(stack.ErrorInternal)
return result
}
result.SetResponse(stack.StackDisableInterfaceDeprecatedResponse{})
return result
}
func (ns *Netstack) addInterfaceAddr(id uint64, ifAddr net.Subnet) stack.StackAddInterfaceAddressDeprecatedResult {
protocolAddr := fidlconv.ToTCPIPProtocolAddress(ifAddr)
if protocolAddr.AddressWithPrefix.PrefixLen > 8*len(protocolAddr.AddressWithPrefix.Address) {
return stack.StackAddInterfaceAddressDeprecatedResultWithErr(stack.ErrorInvalidArgs)
}
_ = syslog.Infof("NIC %d: adding IP %s with subnet route", id, protocolAddr.AddressWithPrefix)
nicid := tcpip.NICID(id)
info, ok := ns.stack.NICInfo()[nicid]
if !ok {
return stack.StackAddInterfaceAddressDeprecatedResultWithErr(stack.ErrorNotFound)
}
ifs := info.Context.(*ifState)
for _, candidate := range info.ProtocolAddresses {
if protocolAddr.AddressWithPrefix.Address == candidate.AddressWithPrefix.Address {
if protocolAddr.AddressWithPrefix.PrefixLen == candidate.AddressWithPrefix.PrefixLen {
return stack.StackAddInterfaceAddressDeprecatedResultWithErr(stack.ErrorAlreadyExists)
}
// Same address but different prefix. Remove the address and re-add it
// with the new prefix (below).
switch err := ifs.removeAddress(protocolAddr); err {
case zx.ErrOk:
case zx.ErrBadState:
return stack.StackAddInterfaceAddressDeprecatedResultWithErr(stack.ErrorNotFound)
case zx.ErrNotFound:
// We lost a race, the address was already removed.
default:
panic(fmt.Sprintf("NIC %d: failed to remove address %s: %s", nicid, protocolAddr.AddressWithPrefix, err))
}
break
}
}
if ok, reason := ifs.addAddress(protocolAddr, tcpipstack.AddressProperties{}); !ok {
switch reason {
case admin.AddressRemovalReasonAlreadyAssigned:
return stack.StackAddInterfaceAddressDeprecatedResultWithErr(stack.ErrorAlreadyExists)
case admin.AddressRemovalReasonInterfaceRemoved:
return stack.StackAddInterfaceAddressDeprecatedResultWithErr(stack.ErrorNotFound)
default:
panic(fmt.Sprintf("NIC %d: ifs.addAddress(%s, {}) unexpected removal reason: %s", nicid, protocolAddr.AddressWithPrefix, reason))
}
}
route := addressWithPrefixRoute(nicid, protocolAddr.AddressWithPrefix)
_ = syslog.Infof("creating subnet route %s with metric=<not-set>, dynamic=false", route)
if err := ns.AddRoute(route, metricNotSet, false /* dynamic */); err != nil {
if !errors.Is(err, routes.ErrNoSuchNIC) {
panic(fmt.Sprintf("NIC %d: failed to add subnet route %s: %s", nicid, route, err))
}
return stack.StackAddInterfaceAddressDeprecatedResultWithErr(stack.ErrorNotFound)
}
return stack.StackAddInterfaceAddressDeprecatedResultWithResponse(stack.StackAddInterfaceAddressDeprecatedResponse{})
}
func (ns *Netstack) delInterfaceAddr(id uint64, ifAddr net.Subnet) stack.StackDelInterfaceAddressDeprecatedResult {
protocolAddr := fidlconv.ToTCPIPProtocolAddress(ifAddr)
if protocolAddr.AddressWithPrefix.PrefixLen > 8*len(protocolAddr.AddressWithPrefix.Address) {
return stack.StackDelInterfaceAddressDeprecatedResultWithErr(stack.ErrorInvalidArgs)
}
nicid := tcpip.NICID(id)
route := addressWithPrefixRoute(nicid, protocolAddr.AddressWithPrefix)
_ = syslog.Infof("removing subnet route %s", route)
if routesDeleted := ns.DelRoute(route); len(routesDeleted) == 0 {
// The route might have been removed by user action. Continue.
}
info, ok := ns.stack.NICInfo()[nicid]
if !ok {
return stack.StackDelInterfaceAddressDeprecatedResultWithErr(stack.ErrorNotFound)
}
ifs := info.Context.(*ifState)
switch status := ifs.removeAddress(protocolAddr); status {
case zx.ErrOk:
return stack.StackDelInterfaceAddressDeprecatedResultWithResponse(stack.StackDelInterfaceAddressDeprecatedResponse{})
case zx.ErrBadState, zx.ErrNotFound:
return stack.StackDelInterfaceAddressDeprecatedResultWithErr(stack.ErrorNotFound)
default:
_ = syslog.Errorf("(*Netstack).delInterfaceAddr(%s) failed (NIC %d): %s", protocolAddr.AddressWithPrefix, id, status)
return stack.StackDelInterfaceAddressDeprecatedResultWithErr(stack.ErrorInternal)
}
}
func (ns *Netstack) getForwardingTable() []stack.ForwardingEntry {
ert := ns.GetExtendedRouteTable()
entries := make([]stack.ForwardingEntry, 0, len(ert))
for _, er := range ert {
entry := fidlconv.TCPIPRouteToForwardingEntry(er.Route)
entry.Metric = uint32(er.Metric)
entries = append(entries, entry)
}
return entries
}
// validateSubnet returns true if the prefix length is valid and no
// address bits are set beyond the prefix length.
func validateSubnet(subnet net.Subnet) bool {
var ipBytes []uint8
switch typ := subnet.Addr.Which(); typ {
case net.IpAddressIpv4:
ipBytes = subnet.Addr.Ipv4.Addr[:]
case net.IpAddressIpv6:
ipBytes = subnet.Addr.Ipv6.Addr[:]
default:
panic(fmt.Sprintf("unknown IpAddress type %d", typ))
}
if int(subnet.PrefixLen) > len(ipBytes)*8 {
return false
}
prefixBytes := subnet.PrefixLen / 8
ipBytes = ipBytes[prefixBytes:]
if prefixBits := subnet.PrefixLen - (prefixBytes * 8); prefixBits > 0 {
// prefixBits is only greater than zero when ipBytes is non-empty.
mask := uint8((1 << (8 - prefixBits)) - 1)
ipBytes[0] &= mask
}
for _, byte := range ipBytes {
if byte != 0 {
return false
}
}
return true
}
func (ns *Netstack) addForwardingEntry(entry stack.ForwardingEntry) stack.StackAddForwardingEntryResult {
var result stack.StackAddForwardingEntryResult
if !validateSubnet(entry.Subnet) {
result.SetErr(stack.ErrorInvalidArgs)
return result
}
route := fidlconv.ForwardingEntryToTCPIPRoute(entry)
if err := ns.AddRoute(route, routes.Metric(entry.Metric), false /* not dynamic */); err != nil {
if errors.Is(err, routes.ErrNoSuchNIC) {
result.SetErr(stack.ErrorInvalidArgs)
} else {
_ = syslog.Errorf("adding route %s to route table failed: %s", route, err)
result.SetErr(stack.ErrorInternal)
}
return result
}
result.SetResponse(stack.StackAddForwardingEntryResponse{})
return result
}
func (ns *Netstack) delForwardingEntry(entry stack.ForwardingEntry) stack.StackDelForwardingEntryResult {
if !validateSubnet(entry.Subnet) {
return stack.StackDelForwardingEntryResultWithErr(stack.ErrorInvalidArgs)
}
route := fidlconv.ForwardingEntryToTCPIPRoute(entry)
if routesDeleted := ns.DelRoute(route); len(routesDeleted) == 0 {
return stack.StackDelForwardingEntryResultWithErr(stack.ErrorNotFound)
}
return stack.StackDelForwardingEntryResultWithResponse(stack.StackDelForwardingEntryResponse{})
}
func (ni *stackImpl) AddEthernetInterface(_ fidl.Context, topologicalPath string, device fidlethernet.DeviceWithCtxInterface) (stack.StackAddEthernetInterfaceResult, error) {
var result stack.StackAddEthernetInterfaceResult
if ifs, err := ni.ns.addEth(topologicalPath, netstack.InterfaceConfig{}, &device); err != nil {
var tcpipErr *TcpIpError
if errors.As(err, &tcpipErr) {
result.SetErr(tcpipErr.ToStackError())
} else {
result.SetErr(stack.ErrorInternal)
}
} else {
result.SetResponse(stack.StackAddEthernetInterfaceResponse{
Id: uint64(ifs.nicid),
})
}
return result, nil
}
func (ni *stackImpl) DelEthernetInterface(_ fidl.Context, id uint64) (stack.StackDelEthernetInterfaceResult, error) {
return ni.ns.delInterface(id), nil
}
func (ni *stackImpl) EnableInterfaceDeprecated(_ fidl.Context, id uint64) (stack.StackEnableInterfaceDeprecatedResult, error) {
return ni.ns.enableInterface(id), nil
}
func (ni *stackImpl) DisableInterfaceDeprecated(_ fidl.Context, id uint64) (stack.StackDisableInterfaceDeprecatedResult, error) {
return ni.ns.disableInterface(id), nil
}
func (ni *stackImpl) AddInterfaceAddressDeprecated(_ fidl.Context, id uint64, addr net.Subnet) (stack.StackAddInterfaceAddressDeprecatedResult, error) {
return ni.ns.addInterfaceAddr(id, addr), nil
}
func (ni *stackImpl) DelInterfaceAddressDeprecated(_ fidl.Context, id uint64, addr net.Subnet) (stack.StackDelInterfaceAddressDeprecatedResult, error) {
return ni.ns.delInterfaceAddr(id, addr), nil
}
func (ni *stackImpl) GetForwardingTable(fidl.Context) ([]stack.ForwardingEntry, error) {
return ni.ns.getForwardingTable(), nil
}
func (ni *stackImpl) AddForwardingEntry(_ fidl.Context, entry stack.ForwardingEntry) (stack.StackAddForwardingEntryResult, error) {
return ni.ns.addForwardingEntry(entry), nil
}
func (ni *stackImpl) DelForwardingEntry(_ fidl.Context, entry stack.ForwardingEntry) (stack.StackDelForwardingEntryResult, error) {
return ni.ns.delForwardingEntry(entry), nil
}
// TODO(https://fxbug.dev/94475): Remove this.
func (ni *stackImpl) SetInterfaceIpForwardingDeprecated(_ fidl.Context, id uint64, ip net.IpVersion, enabled bool) (stack.StackSetInterfaceIpForwardingDeprecatedResult, error) {
netProto, ok := fidlconv.ToTCPIPNetProto(ip)
if !ok {
return stack.StackSetInterfaceIpForwardingDeprecatedResultWithErr(stack.ErrorInvalidArgs), nil
}
nicInfo, ok := ni.ns.stack.NICInfo()[tcpip.NICID(id)]
if !ok {
return stack.StackSetInterfaceIpForwardingDeprecatedResultWithErr(stack.ErrorNotFound), nil
}
// Lock the interface to synchronize changes with
// fuchsia.net.interfaces.admin/Control.{Set,Get}Configuration.
ifs := nicInfo.Context.(*ifState)
ifs.mu.Lock()
defer ifs.mu.Unlock()
// We ignore the returned previous forwarding configuration as this FIDL
// method has no use for it.
switch _, err := ni.ns.stack.SetNICForwarding(tcpip.NICID(id), netProto, enabled); err.(type) {
case nil:
return stack.StackSetInterfaceIpForwardingDeprecatedResultWithResponse(stack.StackSetInterfaceIpForwardingDeprecatedResponse{}), nil
case *tcpip.ErrUnknownNICID:
return stack.StackSetInterfaceIpForwardingDeprecatedResultWithErr(stack.ErrorNotFound), nil
default:
panic(fmt.Sprintf("ni.ns.stack.SetNICForwarding(tcpip.NICID(%d), %d, %t): %s", id, netProto, enabled, err))
}
}
func (ni *stackImpl) GetDnsServerWatcher(ctx_ fidl.Context, watcher name.DnsServerWatcherWithCtxInterfaceRequest) error {
return ni.dnsWatchers.Bind(watcher)
}
var _ stack.LogWithCtx = (*logImpl)(nil)
type logImpl struct {
logPackets *atomicbitops.Uint32
}
func (li *logImpl) SetLogPackets(_ fidl.Context, enabled bool) error {
var val uint32
if enabled {
val = 1
}
li.logPackets.Store(val)
syslog.VLogTf(syslog.DebugVerbosity, "fuchsia_net_stack", "SetLogPackets: %t", enabled)
return nil
}