blob: ecea16e8beb21c58948ea500c1c9b8faa3a716b4 [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"
"strings"
"app/context"
"fidl/bindings"
"netstack/link/eth"
"syscall/zx"
"syscall/zx/mxerror"
"garnet/public/lib/netstack/fidl/net_address"
nsfidl "garnet/public/lib/netstack/fidl/netstack"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/transport/tcp"
"github.com/google/netstack/tcpip/transport/udp"
)
type netstackImpl struct {
listener *nsfidl.NotificationListener_Proxy
stub *bindings.Stub
}
func toNetAddress(addr tcpip.Address) net_address.NetAddress {
out := net_address.NetAddress{Family: net_address.NetAddressFamily_Unspecified}
switch len(addr) {
case 4:
out.Family = net_address.NetAddressFamily_Ipv4
out.Ipv4 = &[4]uint8{}
copy(out.Ipv4[:], addr[:])
case 16:
out.Family = net_address.NetAddressFamily_Ipv6
out.Ipv6 = &[16]uint8{}
copy(out.Ipv6[:], addr[:])
}
return out
}
func toSubnets(addrs []tcpip.Address) []net_address.Subnet {
out := make([]net_address.Subnet, len(addrs))
for i := range addrs {
// TODO: prefix len?
out[i] = net_address.Subnet{Addr: toNetAddress(addrs[i]), PrefixLen: 64}
}
return out
}
func getInterfaces() (out []nsfidl.NetInterface) {
ns.mu.Lock()
defer ns.mu.Unlock()
index := uint32(0)
for nicid, ifs := range ns.ifStates {
// Long-hand for: broadaddr = ifs.nic.Addr | ^ifs.nic.Netmask
broadaddr := []byte(ifs.nic.Addr)
for i := range broadaddr {
broadaddr[i] |= ^ifs.nic.Netmask[i]
}
var flags uint32
if ifs.state == eth.StateStarted {
flags |= nsfidl.NetInterfaceFlagUp
}
outif := nsfidl.NetInterface{
Id: uint32(nicid),
Flags: flags,
Name: fmt.Sprintf("en%d", nicid),
Addr: toNetAddress(ifs.nic.Addr),
Netmask: toNetAddress(tcpip.Address(ifs.nic.Netmask)),
Broadaddr: toNetAddress(tcpip.Address(broadaddr)),
Hwaddr: []uint8(ifs.nic.Mac[:]),
Ipv6addrs: toSubnets(ifs.nic.Ipv6addrs),
}
out = append(out, outif)
index++
}
return out
}
func (ni *netstackImpl) RegisterListener(listener *nsfidl.NotificationListener_Pointer) (err error) {
if listener != nil {
lp := nsfidl.NewProxyForNotificationListener(*listener, bindings.GetAsyncWaiter())
ni.listener = lp
}
return nil
}
func (ni *netstackImpl) GetPortForService(service string, protocol nsfidl.Protocol) (port uint16, err error) {
switch protocol {
case nsfidl.Protocol_Udp:
port, err = serviceLookup(service, udp.ProtocolNumber)
case nsfidl.Protocol_Tcp:
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 []net_address.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.dispatcher.dnsClient.LookupIP(name)
if err == nil {
out = make([]net_address.SocketAddress, len(addrs))
netErr = nsfidl.NetErr{Status: nsfidl.Status_Ok}
for i, addr := range addrs {
switch len(addr) {
case 4, 16:
out[i].Addr = toNetAddress(addr)
out[i].Port = port
}
}
} else {
netErr = nsfidl.NetErr{Status: nsfidl.Status_DnsError, Message: err.Error()}
}
return out, netErr, nil
}
func (ni *netstackImpl) GetInterfaces() (out []nsfidl.NetInterface, err error) {
return getInterfaces(), nil
}
func (ni *netstackImpl) GetNodeName() (out string, err error) {
ns.mu.Lock()
nodename := ns.nodename
ns.mu.Unlock()
return nodename, 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: toNetAddress(dest),
Netmask: toNetAddress(mask),
Gateway: toNetAddress(gateway),
Nicid: uint32(route.NIC),
})
}
return out, nil
}
func (ni *netstackImpl) GetAggregateStats() (stats nsfidl.AggregateStats, err error) {
s := ns.stack.Stats()
return nsfidl.AggregateStats{
UnknownProtocolReceivedPackets: s.UnknownProtocolRcvdPackets,
UnknownNetworkEndpointReceivedPackets: s.UnknownNetworkEndpointRcvdPackets,
MalformedReceivedPackets: s.MalformedRcvdPackets,
DroppedPackets: s.DroppedPackets,
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,
},
}, 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 {
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(mpcomplete): This will close the FIDL channel. Should fail more gracefully.
return fmt.Errorf("no such interface id: %d", nicid)
}
if enabled {
ifState.eth.Start()
} else {
ifState.eth.Down()
}
return nil
}
func (ni *netstackImpl) onInterfacesChanged(interfaces []nsfidl.NetInterface) {
if ni.listener != nil {
ni.listener.OnInterfacesChanged(interfaces)
}
}
type netstackDelegate struct {
clients []*netstackImpl
}
func remove(clients []*netstackImpl, client *netstackImpl) []*netstackImpl {
for i, s := range clients {
if s == client {
clients[len(clients)-1], clients[i] = clients[i], clients[len(clients)-1]
break
}
}
return clients
}
func (delegate *netstackDelegate) Bind(request nsfidl.Netstack_Request) {
client := &netstackImpl{}
client.stub = request.NewStub(client, bindings.GetAsyncWaiter())
delegate.clients = append(delegate.clients, client)
go func() {
for {
if err := client.stub.ServeRequest(); err != nil {
if mxerror.Status(err) != zx.ErrPeerClosed {
log.Println(err)
}
break
}
}
delegate.clients = remove(delegate.clients, client)
}()
}
func (delegate *netstackDelegate) Quit() {
for _, client := range delegate.clients {
client.stub.Close()
}
}
var netstackService *netstackDelegate
// 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 = &netstackDelegate{}
ctx.OutgoingService.AddService(&nsfidl.Netstack_ServiceBinder{netstackService})
return nil
}
func OnInterfacesChanged() {
if netstackService != nil && netstackService.clients != nil {
interfaces := getInterfaces()
for _, client := range netstackService.clients {
client.onInterfacesChanged(interfaces)
}
}
}