blob: 5f085be3ee66914881b26e3b5cb9209734efbfd8 [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.
package main
import (
"fmt"
"log"
"sort"
"app/context"
"netstack/fidlconv"
"netstack/link/eth"
"syscall/zx"
netfidl "fidl/fuchsia/net"
"fidl/fuchsia/net/stack"
nsfidl "fidl/fuchsia/netstack"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/network/ipv4"
)
type stackImpl struct{}
func getInterfaceInfo(nicid tcpip.NICID, ifs *ifState) *stack.InterfaceInfo {
// Long-hand for: broadaddr = ifs.nic.Addr | ^ifs.nic.Netmask
broadaddr := []byte(ifs.nic.Addr)
if len(ifs.nic.Netmask) != len(ifs.nic.Addr) {
log.Printf("warning: mismatched netmask and address length for nic: %+v", ifs.nic)
return nil
}
for i := range broadaddr {
broadaddr[i] |= ^ifs.nic.Netmask[i]
}
// TODO(tkilbourn): distinguish between enabled and link up
var status uint32 = 0
if ifs.state == eth.StateStarted {
status |= stack.InterfaceStatusEnabled
}
features := []stack.InterfaceFeature{}
if ifs.nic.Features&nsfidl.InterfaceFeatureWlan != 0 {
features = append(features, stack.InterfaceFeatureWlan)
}
if ifs.nic.Features&nsfidl.InterfaceFeatureSynth != 0 {
features = append(features, stack.InterfaceFeatureSynthetic)
}
if ifs.nic.Features&nsfidl.InterfaceFeatureLoopback != 0 {
features = append(features, stack.InterfaceFeatureLoopback)
}
// TODO(tkilbourn): implement interface addresses
addrs := []stack.InterfaceAddress{}
if len(ifs.nic.Addr) > 0 {
addrs = append(addrs, stack.InterfaceAddress{
IpAddress: fidlconv.ToNetIpAddress(ifs.nic.Addr),
PrefixLen: fidlconv.GetPrefixLen(tcpip.Address(ifs.nic.Netmask)),
})
}
var mac netfidl.MacAddress
var path string
if eth := ifs.eth; eth != nil {
mac.Addr = eth.MAC
path = eth.Path
}
return &stack.InterfaceInfo{
Id: uint64(nicid),
Path: path,
Mac: &mac,
Mtu: uint32(ifs.statsEP.MTU()),
Status: status,
Features: features,
Addresses: addrs,
}
}
func getNetInterfaces() (out []stack.InterfaceInfo) {
ns.mu.Lock()
defer ns.mu.Unlock()
for nicid, ifs := range ns.ifStates {
out = append(out, *getInterfaceInfo(nicid, ifs))
}
sort.Slice(out[:], func(i, j int) bool {
return out[i].Id < out[j].Id
})
return out
}
func getInterface(id uint64) (*stack.InterfaceInfo, *stack.Error) {
ns.mu.Lock()
defer ns.mu.Unlock()
for nicid, ifs := range ns.ifStates {
if uint64(nicid) == id {
return getInterfaceInfo(nicid, ifs), nil
}
}
return nil, &stack.Error{Type: stack.ErrorTypeNotFound}
}
func setInterfaceState(id uint64, enabled bool) *stack.Error {
ns.mu.Lock()
defer ns.mu.Unlock()
var ifs *ifState = nil
for i, ifState := range ns.ifStates {
if uint64(i) == id {
ifs = ifState
break
}
}
if ifs == nil {
return &stack.Error{Type: stack.ErrorTypeNotFound}
}
if enabled {
ifs.eth.Up()
} else {
ifs.eth.Down()
}
return nil
}
func addInterfaceAddr(id uint64, ifAddr stack.InterfaceAddress) *stack.Error {
// The ns mutex is held in the setInterfaceAddress call below so release it
// after we find the right ifState.
ns.mu.Lock()
var ifs *ifState = nil
for i, ifState := range ns.ifStates {
if uint64(i) == id {
ifs = ifState
break
}
}
ns.mu.Unlock()
if ifs == nil {
return &stack.Error{Type: stack.ErrorTypeNotFound}
}
var protocol tcpip.NetworkProtocolNumber
switch ifAddr.IpAddress.Which() {
case netfidl.IpAddressIpv4:
if len(ifs.nic.Addr) > 0 {
return &stack.Error{Type: stack.ErrorTypeAlreadyExists}
}
protocol = ipv4.ProtocolNumber
case netfidl.IpAddressIpv6:
// TODO(tkilbourn): support IPv6 addresses (NET-1181)
return &stack.Error{Type: stack.ErrorTypeNotSupported}
}
nic := ifs.nic.ID
addr := fidlconv.ToTCPIPAddress(ifAddr.IpAddress)
if err := ns.setInterfaceAddress(nic, protocol, addr, ifAddr.PrefixLen); err != nil {
return &stack.Error{Type: stack.ErrorTypeBadState}
}
return nil
}
func getForwardingTable() []stack.ForwardingEntry {
ns.mu.Lock()
table := ns.stack.GetRouteTable()
ns.mu.Unlock()
entries := make([]stack.ForwardingEntry, 0)
for _, route := range table {
dest := stack.ForwardingDestination{}
// There are two types of destinations: link-local and next-hop.
// If a route has a gateway, use that as the next-hop, and ignore the NIC.
// Otherwise, it is considered link-local, and use the NIC.
if route.Gateway == tcpip.Address("") {
dest.SetDeviceId(uint64(route.NIC))
} else {
dest.SetNextHop(fidlconv.ToNetIpAddress(route.Gateway))
}
entry := stack.ForwardingEntry{
Subnet: netfidl.Subnet{
Addr: fidlconv.ToNetIpAddress(route.Destination),
PrefixLen: fidlconv.GetPrefixLen(route.Mask),
},
Destination: dest,
}
entries = append(entries, entry)
}
return entries
}
func (ni *stackImpl) ListInterfaces() ([]stack.InterfaceInfo, error) {
return getNetInterfaces(), nil
}
func (ni *stackImpl) GetInterfaceInfo(id uint64) (*stack.InterfaceInfo, *stack.Error, error) {
info, err := getInterface(id)
return info, err, nil
}
func (ni *stackImpl) EnableInterface(id uint64) (*stack.Error, error) {
return setInterfaceState(id, true), nil
}
func (ni *stackImpl) DisableInterface(id uint64) (*stack.Error, error) {
return setInterfaceState(id, false), nil
}
func (ni *stackImpl) AddInterfaceAddress(id uint64, addr stack.InterfaceAddress) (*stack.Error, error) {
return addInterfaceAddr(id, addr), nil
}
func (ni *stackImpl) DelInterfaceAddress(id uint64, addr netfidl.IpAddress) (*stack.Error, error) {
panic("not implemented")
}
func (ni *stackImpl) GetForwardingTable() ([]stack.ForwardingEntry, error) {
return getForwardingTable(), nil
}
func (ni *stackImpl) AddForwardingEntry(entry stack.ForwardingEntry) (*stack.Error, error) {
panic("not implemented")
}
func (ni *stackImpl) DelForwardingEntry(subset netfidl.Subnet) (*stack.Error, error) {
panic("not implemented")
}
var stackService *stack.StackService
// AddStackService registers the StackService with the application context,
// allowing it to respond to FIDL queries.
func AddStackService(ctx *context.Context) error {
if stackService != nil {
return fmt.Errorf("AddNetworkService must be called only once")
}
stackService = &stack.StackService{}
ctx.OutgoingService.AddService(stack.StackName, func(c zx.Channel) error {
_, err := stackService.Add(&stackImpl{}, c, nil)
return err
})
return nil
}