blob: c9785bd7c2285069405505d133e8f065db1da876 [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 netstack
import (
"fmt"
"log"
"sort"
"strings"
"syscall/zx"
"syscall/zx/zxwait"
"netstack/fidlconv"
"netstack/link"
"fidl/fuchsia/hardware/ethernet"
"fidl/fuchsia/net"
"fidl/fuchsia/netstack"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/transport/tcp"
"github.com/google/netstack/tcpip/transport/udp"
)
type netstackImpl struct {
ns *Netstack
}
func toSubnets(addrs []tcpip.Address) []net.Subnet {
out := make([]net.Subnet, len(addrs))
for i := range addrs {
// TODO: prefix len?
out[i] = net.Subnet{Addr: fidlconv.ToNetIpAddress(addrs[i]), PrefixLen: 64}
}
return out
}
func (ns *Netstack) getNetInterfacesLocked() []netstack.NetInterface {
ifStates := ns.mu.ifStates
interfaces := make([]netstack.NetInterface, 0, len(ifStates))
for _, ifs := range ifStates {
ifs.mu.Lock()
netinterface, err := ifs.toNetInterfaceLocked()
ifs.mu.Unlock()
if err != nil {
log.Print(err)
}
interfaces = append(interfaces, netinterface)
}
return interfaces
}
func (ifs *ifState) toNetInterfaceLocked() (netstack.NetInterface, error) {
// Long-hand for: broadaddr = ifs.mu.nic.Addr | ^ifs.mu.nic.Netmask
broadaddr := []byte(ifs.mu.nic.Addr)
if len(ifs.mu.nic.Netmask) != len(ifs.mu.nic.Addr) {
return netstack.NetInterface{}, fmt.Errorf("address length doesn't match netmask: %+v\n", ifs.mu.nic)
}
for i := range broadaddr {
broadaddr[i] |= ^ifs.mu.nic.Netmask[i]
}
var flags uint32
if ifs.mu.state == link.StateStarted {
flags |= netstack.NetInterfaceFlagUp
}
if ifs.mu.dhcpState.enabled {
flags |= netstack.NetInterfaceFlagDhcp
}
return netstack.NetInterface{
Id: uint32(ifs.mu.nic.ID),
Flags: flags,
Features: ifs.mu.nic.Features,
Name: ifs.mu.nic.Name,
Addr: fidlconv.ToNetIpAddress(ifs.mu.nic.Addr),
Netmask: fidlconv.ToNetIpAddress(tcpip.Address(ifs.mu.nic.Netmask)),
Broadaddr: fidlconv.ToNetIpAddress(tcpip.Address(broadaddr)),
Hwaddr: []uint8(ifs.statsEP.LinkAddress()[:]),
Ipv6addrs: toSubnets(ifs.mu.nic.Ipv6addrs),
}, nil
}
func (ns *Netstack) getInterfacesLocked() []netstack.NetInterface {
out := ns.getNetInterfacesLocked()
sort.Slice(out, func(i, j int) bool {
return out[i].Id < out[j].Id
})
return out
}
func (ni *netstackImpl) GetPortForService(service string, protocol netstack.Protocol) (port uint16, err error) {
switch protocol {
case netstack.ProtocolUdp:
port, err = serviceLookup(service, udp.ProtocolNumber)
case netstack.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) ([]netstack.SocketAddress, netstack.NetErr, error) {
// TODO: This should handle IP address strings, empty strings, "localhost", etc. Pull the logic from
// fdio's getaddrinfo into here.
addrs, err := ni.ns.dnsClient.LookupIP(name)
if err != nil {
return nil, netstack.NetErr{Status: netstack.StatusDnsError, Message: err.Error()}, nil
}
out := make([]netstack.SocketAddress, 0, len(addrs))
for _, addr := range addrs {
out = append(out, netstack.SocketAddress{
Addr: fidlconv.ToNetIpAddress(addr),
Port: port,
})
}
return out, netstack.NetErr{Status: netstack.StatusOk}, nil
}
func (ni *netstackImpl) GetInterfaces() ([]netstack.NetInterface, error) {
ni.ns.mu.Lock()
defer ni.ns.mu.Unlock()
return ni.ns.getInterfacesLocked(), nil
}
func (ni *netstackImpl) GetRouteTable() ([]netstack.RouteTableEntry, error) {
ni.ns.mu.Lock()
defer ni.ns.mu.Unlock()
table := ni.ns.mu.stack.GetRouteTable()
return nsToRouteTable(table)
}
func nsToRouteTable(table []tcpip.Route) ([]netstack.RouteTableEntry, error) {
out := []netstack.RouteTableEntry{}
for _, route := range table {
// Ensure that if any of the returned addresses are "empty",
// they still have the appropriate length.
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.AddressMask(strings.Repeat("\x00", l))
}
if len(gateway) == 0 {
gateway = tcpip.Address(strings.Repeat("\x00", l))
}
out = append(out, netstack.RouteTableEntry{
Destination: fidlconv.ToNetIpAddress(dest),
Netmask: fidlconv.ToNetIpAddress(tcpip.Address(mask)),
Gateway: fidlconv.ToNetIpAddress(gateway),
Nicid: uint32(route.NIC),
})
}
return out, nil
}
func routeTableToNs(rt []netstack.RouteTableEntry) []tcpip.Route {
routes := make([]tcpip.Route, 0, len(rt))
for _, r := range rt {
routes = append(routes, tcpip.Route{
Destination: fidlconv.ToTCPIPAddress(r.Destination),
Mask: tcpip.AddressMask(fidlconv.ToTCPIPAddress(r.Netmask)),
Gateway: fidlconv.ToTCPIPAddress(r.Gateway),
NIC: tcpip.NICID(r.Nicid),
})
}
return routes
}
type routeTableTransactionImpl struct {
ni *netstackImpl
routeTableCache []tcpip.Route
}
func (i *routeTableTransactionImpl) GetRouteTable() ([]netstack.RouteTableEntry, error) {
return nsToRouteTable(i.routeTableCache)
}
func (i *routeTableTransactionImpl) SetRouteTable(rt []netstack.RouteTableEntry) error {
routes := routeTableToNs(rt)
i.routeTableCache = routes
return nil
}
func (i *routeTableTransactionImpl) Commit() (int32, error) {
i.ni.ns.mu.Lock()
defer i.ni.ns.mu.Unlock()
i.ni.ns.mu.stack.SetRouteTable(i.routeTableCache)
return int32(zx.ErrOk), nil
}
func (ni *netstackImpl) StartRouteTableTransaction(req netstack.RouteTableTransactionInterfaceRequest) (int32, error) {
{
ni.ns.mu.Lock()
defer ni.ns.mu.Unlock()
if ni.ns.mu.transactionRequest != nil {
oldChannel := ni.ns.mu.transactionRequest.ToChannel()
observed, _ := zxwait.Wait(*oldChannel.Handle(), 0, 0)
// If the channel is neither readable nor writable, there is no
// data left to be processed (not readable) and we can't return
// any more results (not writable). It's not enough to only
// look at peerclosed because the peer can close the channel
// while it still has data in its buffers.
if observed&(zx.SignalChannelReadable|zx.SignalChannelWritable) == 0 {
ni.ns.mu.transactionRequest = nil
}
}
if ni.ns.mu.transactionRequest != nil {
return int32(zx.ErrShouldWait), nil
}
ni.ns.mu.transactionRequest = &req
}
var routeTableService netstack.RouteTableTransactionService
transaction := routeTableTransactionImpl{
ni: ni,
routeTableCache: ni.ns.mu.stack.GetRouteTable(),
}
// We don't use the error handler to free the channel because it's
// possible that the peer closes the channel before our service has
// finished processing.
c := req.ToChannel()
_, err := routeTableService.Add(&transaction, c, nil)
if err != nil {
return int32(zx.ErrShouldWait), err
}
return int32(zx.ErrOk), err
}
// Add address to the given network interface.
func (ni *netstackImpl) SetInterfaceAddress(nicid uint32, address net.IpAddress, prefixLen uint8) (netstack.NetErr, error) {
log.Printf("net address %+v", address)
nic := tcpip.NICID(nicid)
protocol, addr, neterr := ni.ns.validateInterfaceAddress(address, prefixLen)
if neterr.Status != netstack.StatusOk {
return neterr, nil
}
if err := ni.ns.setInterfaceAddress(nic, protocol, addr, prefixLen); err != nil {
return netstack.NetErr{Status: netstack.StatusUnknownError, Message: err.Error()}, nil
}
return netstack.NetErr{Status: netstack.StatusOk, Message: ""}, nil
}
func (ni *netstackImpl) RemoveInterfaceAddress(nicid uint32, address net.IpAddress, prefixLen uint8) (netstack.NetErr, error) {
nic := tcpip.NICID(nicid)
protocol, addr, neterr := ni.ns.validateInterfaceAddress(address, prefixLen)
if neterr.Status != netstack.StatusOk {
return neterr, nil
}
if err := ni.ns.removeInterfaceAddress(nic, protocol, addr, prefixLen); err != nil {
return netstack.NetErr{Status: netstack.StatusUnknownError, Message: err.Error()}, nil
}
return netstack.NetErr{Status: netstack.StatusOk, Message: ""}, nil
}
func (ni *netstackImpl) BridgeInterfaces(nicids []uint32) (netstack.NetErr, error) {
nics := make([]tcpip.NICID, len(nicids))
for i, n := range nicids {
nics[i] = tcpip.NICID(n)
}
_, err := ni.ns.Bridge(nics)
if err != nil {
return netstack.NetErr{Status: netstack.StatusUnknownError}, nil
}
return netstack.NetErr{Status: netstack.StatusOk}, nil
}
func (ni *netstackImpl) GetAggregateStats() (stats netstack.AggregateStats, err error) {
s := ni.ns.mu.stack.Stats()
return netstack.AggregateStats{
UnknownProtocolReceivedPackets: s.UnknownProtocolRcvdPackets.Value(),
MalformedReceivedPackets: s.MalformedRcvdPackets.Value(),
DroppedPackets: s.DroppedPackets.Value(),
IpStats: netstack.IpStats{
PacketsReceived: s.IP.PacketsReceived.Value(),
InvalidAddressesReceived: s.IP.InvalidAddressesReceived.Value(),
PacketsDelivered: s.IP.PacketsDelivered.Value(),
PacketsSent: s.IP.PacketsSent.Value(),
OutgoingPacketErrors: s.IP.OutgoingPacketErrors.Value(),
},
TcpStats: netstack.TcpStats{
ActiveConnectionOpenings: s.TCP.ActiveConnectionOpenings.Value(),
PassiveConnectionOpenings: s.TCP.PassiveConnectionOpenings.Value(),
FailedConnectionAttempts: s.TCP.FailedConnectionAttempts.Value(),
ValidSegmentsReceived: s.TCP.ValidSegmentsReceived.Value(),
InvalidSegmentsReceived: s.TCP.InvalidSegmentsReceived.Value(),
SegmentsSent: s.TCP.SegmentsSent.Value(),
ResetsSent: s.TCP.ResetsSent.Value(),
},
UdpStats: netstack.UdpStats{
PacketsReceived: s.UDP.PacketsReceived.Value(),
UnknownPortErrors: s.UDP.UnknownPortErrors.Value(),
ReceiveBufferErrors: s.UDP.ReceiveBufferErrors.Value(),
MalformedPacketsReceived: s.UDP.MalformedPacketsReceived.Value(),
PacketsSent: s.UDP.PacketsSent.Value(),
},
}, nil
}
func (ni *netstackImpl) GetStats(nicid uint32) (stats netstack.NetInterfaceStats, err error) {
ni.ns.mu.Lock()
ifState, ok := ni.ns.mu.ifStates[tcpip.NICID(nicid)]
ni.ns.mu.Unlock()
if !ok {
// TODO(stijlist): refactor to return NetErr and use StatusUnknownInterface
return netstack.NetInterfaceStats{}, fmt.Errorf("no such interface id: %d", nicid)
}
return ifState.statsEP.Stats, nil
}
func (ni *netstackImpl) SetInterfaceStatus(nicid uint32, enabled bool) error {
ni.ns.mu.Lock()
ifState, ok := ni.ns.mu.ifStates[tcpip.NICID(nicid)]
ni.ns.mu.Unlock()
if !ok {
// TODO(stijlist): refactor to return NetErr and use StatusUnknownInterface
return fmt.Errorf("no such interface id: %d", nicid)
}
if enabled {
return ifState.eth.Up()
}
return ifState.eth.Down()
}
func (ni *netstackImpl) SetDhcpClientStatus(nicid uint32, enabled bool) (netstack.NetErr, error) {
ni.ns.mu.Lock()
ifState, ok := ni.ns.mu.ifStates[tcpip.NICID(nicid)]
ni.ns.mu.Unlock()
if !ok {
return netstack.NetErr{Status: netstack.StatusUnknownInterface, Message: "unknown interface"}, nil
}
ifState.mu.Lock()
ifState.setDHCPStatusLocked(enabled)
ifState.mu.Unlock()
return netstack.NetErr{Status: netstack.StatusOk, Message: ""}, nil
}
// TODO(NET-1263): Remove once clients registering with the ResolverAdmin interface
// does not crash netstack.
func (ni *netstackImpl) SetNameServers(servers []net.IpAddress) error {
d := dnsImpl{ns: ni.ns}
return d.SetNameServers(servers)
}
type dnsImpl struct {
ns *Netstack
}
func (dns *dnsImpl) SetNameServers(servers []net.IpAddress) error {
ss := make([]tcpip.Address, len(servers))
for i, s := range servers {
ss[i] = fidlconv.ToTCPIPAddress(s)
}
dns.ns.dnsClient.SetDefaultServers(ss)
return nil
}
func (dns *dnsImpl) GetNameServers() ([]net.IpAddress, error) {
servers := dns.ns.getDNSServers()
out := make([]net.IpAddress, len(servers))
for i, s := range servers {
out[i] = fidlconv.ToNetIpAddress(s)
}
return out, nil
}
func (ns *netstackImpl) AddEthernetDevice(topological_path string, interfaceConfig netstack.InterfaceConfig, device ethernet.DeviceInterface) (uint32, error) {
ifs, err := ns.ns.addEth(topological_path, interfaceConfig, &device)
if err != nil {
return 0, err
}
return uint32(ifs.mu.nic.ID), err
}