blob: 503e023e64a136c852ec2409eac025f12cd21ad4 [file] [log] [blame]
// Copyright 2017 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"
"strings"
"app/context"
"netstack/connectivity"
"netstack/fidlconv"
"netstack/link/eth"
"syscall/zx"
"fidl/fuchsia/net"
nsfidl "fidl/fuchsia/netstack"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/network/ipv4"
"github.com/google/netstack/tcpip/transport/tcp"
"github.com/google/netstack/tcpip/transport/udp"
)
type netstackImpl struct {
}
func toSubnets(addrs []tcpip.Address) []nsfidl.Subnet {
out := make([]nsfidl.Subnet, len(addrs))
for i := range addrs {
// TODO: prefix len?
out[i] = nsfidl.Subnet{Addr: fidlconv.ToNetAddress(addrs[i]), PrefixLen: 64}
}
return out
}
func getInterfaces() (out []nsfidl.NetInterface) {
ns.mu.Lock()
defer ns.mu.Unlock()
for nicid, ifs := range ns.ifStates {
// 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\n", ifs.nic)
continue
}
for i := range broadaddr {
broadaddr[i] |= ^ifs.nic.Netmask[i]
}
var flags uint32
if ifs.state == eth.StateStarted {
flags |= nsfidl.NetInterfaceFlagUp
}
if ifs.dhcpState.enabled {
flags |= nsfidl.NetInterfaceFlagDhcp
}
var mac []uint8
if eth := ifs.eth; eth != nil {
mac = eth.MAC[:]
}
outif := nsfidl.NetInterface{
Id: uint32(nicid),
Flags: flags,
Features: ifs.nic.Features,
Name: ifs.nic.Name,
Addr: fidlconv.ToNetAddress(ifs.nic.Addr),
Netmask: fidlconv.ToNetAddress(tcpip.Address(ifs.nic.Netmask)),
Broadaddr: fidlconv.ToNetAddress(tcpip.Address(broadaddr)),
Hwaddr: mac,
Ipv6addrs: toSubnets(ifs.nic.Ipv6addrs),
}
out = append(out, outif)
}
sort.Slice(out, func(i, j int) bool {
return out[i].Id < out[j].Id
})
return out
}
func (ni *netstackImpl) GetPortForService(service string, protocol nsfidl.Protocol) (port uint16, err error) {
switch protocol {
case nsfidl.ProtocolUdp:
port, err = serviceLookup(service, udp.ProtocolNumber)
case nsfidl.ProtocolTcp:
port, err = serviceLookup(service, tcp.ProtocolNumber)
default:
port, err = serviceLookup(service, tcp.ProtocolNumber)
if err != nil {
port, err = serviceLookup(service, udp.ProtocolNumber)
}
}
return port, err
}
func (ni *netstackImpl) GetAddress(name string, port uint16) (out []nsfidl.SocketAddress, netErr nsfidl.NetErr, retErr error) {
// TODO: This should handle IP address strings, empty strings, "localhost", etc. Pull the logic from
// fdio's getaddrinfo into here.
addrs, err := ns.dnsClient.LookupIP(name)
if err == nil {
out = make([]nsfidl.SocketAddress, len(addrs))
netErr = nsfidl.NetErr{Status: nsfidl.StatusOk}
for i, addr := range addrs {
switch len(addr) {
case 4, 16:
out[i].Addr = fidlconv.ToNetAddress(addr)
out[i].Port = port
}
}
} else {
netErr = nsfidl.NetErr{Status: nsfidl.StatusDnsError, Message: err.Error()}
}
return out, netErr, nil
}
func (ni *netstackImpl) GetInterfaces() (out []nsfidl.NetInterface, err error) {
return getInterfaces(), nil
}
func (ni *netstackImpl) GetRouteTable() (out []nsfidl.RouteTableEntry, err error) {
ns.mu.Lock()
table := ns.stack.GetRouteTable()
ns.mu.Unlock()
for _, route := range table {
// Ensure that if any of the returned addresss are "empty",
// they still have the appropriate NetAddressFamily.
l := 0
if len(route.Destination) > 0 {
l = len(route.Destination)
} else if len(route.Mask) > 0 {
l = len(route.Destination)
} else if len(route.Gateway) > 0 {
l = len(route.Gateway)
}
dest := route.Destination
mask := route.Mask
gateway := route.Gateway
if len(dest) == 0 {
dest = tcpip.Address(strings.Repeat("\x00", l))
}
if len(mask) == 0 {
mask = tcpip.Address(strings.Repeat("\x00", l))
}
if len(gateway) == 0 {
gateway = tcpip.Address(strings.Repeat("\x00", l))
}
out = append(out, nsfidl.RouteTableEntry{
Destination: fidlconv.ToNetAddress(dest),
Netmask: fidlconv.ToNetAddress(mask),
Gateway: fidlconv.ToNetAddress(gateway),
Nicid: uint32(route.NIC),
})
}
return out, nil
}
func (ni *netstackImpl) SetRouteTable(rt []nsfidl.RouteTableEntry) error {
routes := []tcpip.Route{}
for _, r := range rt {
route := tcpip.Route{
Destination: fidlconv.NetAddressToTCPIPAddress(r.Destination),
Mask: fidlconv.NetAddressToTCPIPAddress(r.Netmask),
Gateway: fidlconv.NetAddressToTCPIPAddress(r.Gateway),
NIC: tcpip.NICID(r.Nicid),
}
routes = append(routes, route)
}
ns.mu.Lock()
defer ns.mu.Unlock()
ns.stack.SetRouteTable(routes)
return nil
}
func validateInterfaceAddress(nicid uint32, address nsfidl.NetAddress, prefixLen uint8) (nic tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, retval nsfidl.NetErr) {
switch address.Family {
case nsfidl.NetAddressFamilyIpv4:
protocol = ipv4.ProtocolNumber
case nsfidl.NetAddressFamilyIpv6:
retval = nsfidl.NetErr{nsfidl.StatusIpv4Only, "IPv6 not yet supported"}
return
}
nic = tcpip.NICID(nicid)
addr = fidlconv.NetAddressToTCPIPAddress(address)
if (8 * len(addr)) < int(prefixLen) {
retval = nsfidl.NetErr{nsfidl.StatusParseError, "Prefix length does not match address"}
return
}
retval = nsfidl.NetErr{nsfidl.StatusOk, ""}
return
}
// Add address to the given network interface.
func (ni *netstackImpl) SetInterfaceAddress(nicid uint32, address nsfidl.NetAddress, prefixLen uint8) (result nsfidl.NetErr, endService error) {
log.Printf("net address %+v", address)
nic, protocol, addr, neterr := validateInterfaceAddress(nicid, address, prefixLen)
if neterr.Status != nsfidl.StatusOk {
return neterr, nil
}
if err := ns.setInterfaceAddress(nic, protocol, addr, prefixLen); err != nil {
return nsfidl.NetErr{nsfidl.StatusUnknownError, err.Error()}, nil
}
return nsfidl.NetErr{nsfidl.StatusOk, ""}, nil
}
func (ni *netstackImpl) RemoveInterfaceAddress(nicid uint32, address nsfidl.NetAddress, prefixLen uint8) (result nsfidl.NetErr, endService error) {
nic, protocol, addr, neterr := validateInterfaceAddress(nicid, address, prefixLen)
if neterr.Status != nsfidl.StatusOk {
return neterr, nil
}
if err := ns.removeInterfaceAddress(nic, protocol, addr, prefixLen); err != nil {
return nsfidl.NetErr{nsfidl.StatusUnknownError, err.Error()}, nil
}
return nsfidl.NetErr{nsfidl.StatusOk, ""}, nil
}
func (ni *netstackImpl) BridgeInterfaces(nicids []uint32) (nsfidl.NetErr, error) {
nics := make([]tcpip.NICID, len(nicids))
for i, n := range nicids {
nics[i] = tcpip.NICID(n)
}
err := ns.Bridge(nics)
if err != nil {
return nsfidl.NetErr{Status: nsfidl.StatusUnknownError}, nil
}
return nsfidl.NetErr{Status: nsfidl.StatusOk}, nil
}
func (ni *netstackImpl) SetFilterStatus(enabled bool) (result nsfidl.NetErr, err error) {
ns.filter.Enable(enabled)
return nsfidl.NetErr{nsfidl.StatusOk, ""}, nil
}
func (ni *netstackImpl) GetFilterStatus() (enabled bool, err error) {
return ns.filter.IsEnabled(), nil
}
func (ni *netstackImpl) GetAggregateStats() (stats nsfidl.AggregateStats, err error) {
s := ns.stack.Stats()
return nsfidl.AggregateStats{
UnknownProtocolReceivedPackets: s.UnknownProtocolRcvdPackets,
MalformedReceivedPackets: s.MalformedRcvdPackets,
DroppedPackets: s.DroppedPackets,
IpStats: nsfidl.IpStats{
PacketsReceived: s.IP.PacketsReceived,
InvalidAddressesReceived: s.IP.InvalidAddressesReceived,
PacketsDiscarded: s.IP.PacketsDiscarded,
PacketsDelivered: s.IP.PacketsDelivered,
PacketsSent: s.IP.PacketsSent,
OutgoingPacketErrors: s.IP.OutgoingPacketErrors,
},
TcpStats: nsfidl.TcpStats{
ActiveConnectionOpenings: s.TCP.ActiveConnectionOpenings,
PassiveConnectionOpenings: s.TCP.PassiveConnectionOpenings,
FailedConnectionAttempts: s.TCP.FailedConnectionAttempts,
ValidSegmentsReceived: s.TCP.ValidSegmentsReceived,
InvalidSegmentsReceived: s.TCP.InvalidSegmentsReceived,
SegmentsSent: s.TCP.SegmentsSent,
ResetsSent: s.TCP.ResetsSent,
},
UdpStats: nsfidl.UdpStats{
PacketsReceived: s.UDP.PacketsReceived,
UnknownPortErrors: s.UDP.UnknownPortErrors,
ReceiveBufferErrors: s.UDP.ReceiveBufferErrors,
MalformedPacketsReceived: s.UDP.MalformedPacketsReceived,
PacketsSent: s.UDP.PacketsSent,
},
}, nil
}
func (ni *netstackImpl) GetStats(nicid uint32) (stats nsfidl.NetInterfaceStats, err error) {
// Pure reading of statistics. No critical section. No lock is needed.
ifState, ok := ns.ifStates[tcpip.NICID(nicid)]
if !ok {
// TODO(stijlist): refactor to return NetErr and use StatusUnknownInterface
return nsfidl.NetInterfaceStats{}, fmt.Errorf("no such interface id: %d", nicid)
}
return ifState.statsEP.Stats, nil
}
func (ni *netstackImpl) SetInterfaceStatus(nicid uint32, enabled bool) (err error) {
ifState, ok := ns.ifStates[tcpip.NICID(nicid)]
if !ok {
// TODO(stijlist): refactor to return NetErr and use StatusUnknownInterface
return fmt.Errorf("no such interface id: %d", nicid)
}
if enabled {
ifState.eth.Up()
} else {
ifState.eth.Down()
}
return nil
}
func (ni *netstackImpl) SetDhcpClientStatus(nicid uint32, enabled bool) (result nsfidl.NetErr, err error) {
ifState, ok := ns.ifStates[tcpip.NICID(nicid)]
if !ok {
return nsfidl.NetErr{nsfidl.StatusUnknownInterface, "unknown interface"}, nil
}
ifState.setDHCPStatus(enabled)
return nsfidl.NetErr{nsfidl.StatusOk, ""}, nil
}
// TODO(NET-1263): Remove once clients registering with the ResolverAdmin interface
// does not crash netstack.
func (ni *netstackImpl) SetNameServers(servers []nsfidl.NetAddress) error {
d := dnsImpl{}
return d.SetNameServers(servers)
}
type dnsImpl struct{}
func (*dnsImpl) SetNameServers(servers []nsfidl.NetAddress) error {
ss := make([]tcpip.Address, len(servers))
for i, s := range servers {
ss[i] = fidlconv.NetAddressToTCPIPAddress(s)
}
ns.dnsClient.SetDefaultServers(ss)
return nil
}
func (*dnsImpl) GetNameServers() ([]nsfidl.NetAddress, error) {
servers := ns.getDNSServers()
out := make([]nsfidl.NetAddress, len(servers))
for i, s := range servers {
out[i] = fidlconv.ToNetAddress(s)
}
return out, nil
}
var netstackService *nsfidl.NetstackService
// TODO(NET-1263): register resolver admin service once clients don't crash netstack
// var dnsService *nsfidl.ResolverAdminService
// AddNetstackService registers the NetstackService with the application context,
// allowing it to respond to FIDL queries.
func AddNetstackService(ctx *context.Context) error {
if netstackService != nil {
return fmt.Errorf("AddNetworkService must be called only once")
}
netstackService = &nsfidl.NetstackService{}
ctx.OutgoingService.AddService(nsfidl.NetstackName, func(c zx.Channel) error {
k, err := netstackService.Add(&netstackImpl{}, c, nil)
if err != nil {
return err
}
// Send a synthetic InterfacesChanged event to each client when they join
// Prevents clients from having to race GetInterfaces / InterfacesChanged.
if p, ok := netstackService.EventProxyFor(k); ok {
p.OnInterfacesChanged(getInterfaces())
}
return nil
})
// TODO(NET-1263): register resolver admin service once clients don't crash netstack
// when registering.
// ctx.OutgoingService.AddService(nsfidl.ResolverAdminName, func(c zx.Channel) error {
// _, err := dnsService.Add(&dnsImpl{}, c, nil)
// return err
// })
ctx.OutgoingService.AddService(net.ConnectivityName, func(c zx.Channel) error {
k, err := connectivity.Service.Add(struct{}{}, c, nil)
// Let clients know the status of the network when they get added.
if p, ok := connectivity.Service.EventProxyFor(k); ok {
p.OnNetworkReachable(connectivity.CurrentlyReachable())
}
return err
})
return nil
}
func OnInterfacesChanged() {
if netstackService != nil {
interfaces := getInterfaces()
connectivity.InferAndNotify(interfaces)
for key := range netstackService.Bindings {
if p, ok := netstackService.EventProxyFor(key); ok {
p.OnInterfacesChanged(interfaces)
}
}
}
}