blob: 61fb2ea93725e6c42286af3d3343dea587a75b50 [file] [log] [blame]
// Copyright 2020 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.
// +build !build_with_native_toolchain
package netstack
import (
"context"
"strings"
"syscall/zx"
"syscall/zx/fidl"
"go.fuchsia.dev/fuchsia/src/connectivity/network/netstack/fidlconv"
syslog "go.fuchsia.dev/fuchsia/src/lib/syslog/go"
"fidl/fuchsia/net"
"fidl/fuchsia/net/routes"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
var _ routes.StateWithCtx = (*routesImpl)(nil)
// routesImpl provides implementation for protocols in fuchsia.net.routes.
type routesImpl struct {
stack *stack.Stack
}
func (r *routesImpl) Resolve(ctx fidl.Context, destination net.IpAddress) (routes.StateResolveResult, error) {
const unspecifiedNIC = tcpip.NICID(0)
const unspecifiedLocalAddress = tcpip.Address("")
remote, proto := fidlconv.ToTCPIPAddressAndProtocolNumber(destination)
netProtoName := strings.TrimPrefix(networkProtocolToString(proto), "IP")
var flags string
syslogFn := syslog.DebugTf
if remote.Unspecified() {
flags = "U"
syslogFn = syslog.InfoTf
}
logFn := func(suffix string, err tcpip.Error) {
_ = syslogFn(
"fuchsia.net.routes", "stack.FindRoute(%s (%s|%s))%s = (_, %s)",
remote,
netProtoName,
flags,
suffix,
err,
)
}
route, err := r.stack.FindRoute(unspecifiedNIC, unspecifiedLocalAddress, remote, proto, false /* multicastLoop */)
if err != nil {
logFn("", err)
return routes.StateResolveResultWithErr(int32(WrapTcpIpError(err).ToZxStatus())), nil
}
defer route.Release()
return func() routes.StateResolveResult {
ch := make(chan stack.ResolvedFieldsResult, 1)
err := route.ResolvedFields(func(result stack.ResolvedFieldsResult) {
ch <- result
})
switch err.(type) {
case nil, *tcpip.ErrWouldBlock:
select {
case result := <-ch:
if result.Err == nil {
// Build our response with the resolved route.
nicID := route.NICID()
route := result.RouteInfo
var node routes.Destination
node.SetSourceAddress(fidlconv.ToNetIpAddress(route.LocalAddress))
// If the remote link address is unspecified, then the outgoing link
// does not support MAC addressing.
if linkAddr := route.RemoteLinkAddress; len(linkAddr) != 0 {
node.SetMac(fidlconv.ToNetMacAddress(linkAddr))
}
node.SetInterfaceId(uint64(nicID))
var response routes.StateResolveResponse
if len(route.NextHop) != 0 {
node.SetAddress(fidlconv.ToNetIpAddress(route.NextHop))
response.Result.SetGateway(node)
} else {
node.SetAddress(fidlconv.ToNetIpAddress(route.RemoteAddress))
response.Result.SetDirect(node)
}
return routes.StateResolveResultWithResponse(response)
}
err = result.Err
case <-ctx.Done():
switch ctx.Err() {
case context.Canceled:
return routes.StateResolveResultWithErr(int32(zx.ErrCanceled))
case context.DeadlineExceeded:
return routes.StateResolveResultWithErr(int32(zx.ErrTimedOut))
}
}
}
logFn(".ResolvedFields(...)", err)
return routes.StateResolveResultWithErr(int32(zx.ErrAddressUnreachable))
}(), nil
}