blob: 608cfb72134a685ecf98c7a25746f87b2a4f7518 [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.
//go:build !build_with_native_toolchain
// +build !build_with_native_toolchain
// link/bridge implements a bridging LinkEndpoint
// It can be writable.
package bridge
import (
"fmt"
"hash/fnv"
"math"
"sort"
"strings"
"sync"
"fidl/fuchsia/hardware/network"
"go.fuchsia.dev/fuchsia/src/connectivity/network/netstack/link"
syslog "go.fuchsia.dev/fuchsia/src/lib/syslog/go"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
var _ stack.LinkEndpoint = (*Endpoint)(nil)
var _ link.Controller = (*Endpoint)(nil)
type Endpoint struct {
links map[tcpip.LinkAddress]*BridgeableEndpoint
mtu uint32
capabilities stack.LinkEndpointCapabilities
maxHeaderLength uint16
linkAddress tcpip.LinkAddress
mu struct {
sync.RWMutex
dispatcher stack.NetworkDispatcher
}
}
// New creates a new link from a list of BridgeableEndpoints that bridges
// packets written to it and received from any of its constituent links.
//
// `links` must be non-empty, as properties of the new link are derived from
// the constituent links: it will have the minimum of the MTUs, the maximum
// of the max header lengths, and the minimum set of capabilities.
func New(links []*BridgeableEndpoint) (*Endpoint, error) {
if len(links) == 0 {
return nil, fmt.Errorf("creating bridge with no attached endpoints is invalid")
}
{
// TODO(https://fxbug.dev/57022): Make sure links are all using the same kind of link.
links := append([]*BridgeableEndpoint(nil), links...)
sort.Slice(links, func(i, j int) bool {
return strings.Compare(string(links[i].LinkAddress()), string(links[j].LinkAddress())) > 0
})
ep := &Endpoint{
links: make(map[tcpip.LinkAddress]*BridgeableEndpoint),
mtu: math.MaxUint32,
}
h := fnv.New64()
for _, l := range links {
linkAddress := l.LinkAddress()
ep.links[linkAddress] = l
// mtu is the maximum write size, which is the minimum of any link's mtu.
if mtu := l.MTU(); mtu < ep.mtu {
ep.mtu = mtu
}
// Resolution is required if any link requires it.
ep.capabilities |= l.Capabilities() & stack.CapabilityResolutionRequired
// maxHeaderLength is the space to reserve for possible addition
// headers. We want to reserve enough to suffice for all links.
if maxHeaderLength := l.MaxHeaderLength(); maxHeaderLength > ep.maxHeaderLength {
ep.maxHeaderLength = maxHeaderLength
}
if _, err := h.Write([]byte(linkAddress)); err != nil {
panic(err)
}
}
b := h.Sum(nil)[:6]
// Set the second-least-significant bit of the first byte so the address is locally-administered.
b[0] |= 1 << 1
// Clear the least-significant bit of the first byte so the address is unicast.
b[0] &^= 1
ep.linkAddress = tcpip.LinkAddress(b)
return ep, nil
}
}
func (*Endpoint) Up() error {
return nil
}
// TODO(https://fxbug.dev/86388): Implement disabling the bridge.
func (*Endpoint) Down() error {
_ = syslog.Warnf("disabling bridges is unimplemented, the bridge will still be usable")
return nil
}
// SetPromiscuousMode on a bridge is a no-op, since all of the constituent
// links on a bridge need to already be in promiscuous mode for bridging to
// work.
func (*Endpoint) SetPromiscuousMode(bool) error {
return nil
}
func (*Endpoint) DeviceClass() network.DeviceClass {
return network.DeviceClassBridge
}
func (ep *Endpoint) MTU() uint32 {
return ep.mtu
}
func (ep *Endpoint) Capabilities() stack.LinkEndpointCapabilities {
return ep.capabilities
}
func (ep *Endpoint) MaxHeaderLength() uint16 {
return ep.maxHeaderLength
}
func (ep *Endpoint) LinkAddress() tcpip.LinkAddress {
return ep.linkAddress
}
func (ep *Endpoint) WritePacket(r stack.RouteInfo, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
ep.AddHeader(ep.LinkAddress(), r.RemoteLinkAddress, protocol, pkt)
return ep.WriteRawPacket(pkt)
}
// WritePackets returns the number of packets in hdrs that were successfully
// written to all links.
func (ep *Endpoint) WritePackets(r stack.RouteInfo, pkts stack.PacketBufferList, protocol tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
cnt := 0
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
switch err := ep.WritePacket(r, protocol, pkt); err.(type) {
case nil:
cnt++
default:
return cnt, err
}
}
return cnt, nil
}
func (ep *Endpoint) WriteRawPacket(pkt *stack.PacketBuffer) tcpip.Error {
i := 0
for _, l := range ep.links {
i++
// Need to clone when writing to all but the last endpoint, since callee
// takes ownership.
pkt := pkt
if i != len(ep.links) {
pkt = pkt.Clone()
}
switch err := l.WriteRawPacket(pkt); err.(type) {
case nil:
case *tcpip.ErrClosedForSend:
// TODO(https://fxbug.dev/86959): Handle bridged interface removal.
_ = syslog.Warnf("WriteRawPacket on bridged endpoint returned ClosedForSend")
default:
return err
}
}
return nil
}
func (ep *Endpoint) Attach(d stack.NetworkDispatcher) {
ep.mu.Lock()
defer ep.mu.Unlock()
ep.mu.dispatcher = d
if d == nil {
for _, l := range ep.links {
l.SetBridge(nil)
}
}
}
func (ep *Endpoint) IsAttached() bool {
ep.mu.RLock()
defer ep.mu.RUnlock()
return ep.mu.dispatcher != nil
}
// DeliverNetworkPacketToBridge delivers a network packet to the bridged network.
//
// Endpoint does not implement stack.NetworkEndpoint.DeliverNetworkPacket because we need
// to know which BridgeableEndpoint the packet was delivered from to prevent packet loops.
func (ep *Endpoint) DeliverNetworkPacketToBridge(rxEP *BridgeableEndpoint, srcLinkAddr, dstLinkAddr tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
ep.mu.RLock()
dispatcher := ep.mu.dispatcher
ep.mu.RUnlock()
if dstLinkAddr == ep.linkAddress {
if dispatcher != nil {
dispatcher.DeliverNetworkPacket(srcLinkAddr, dstLinkAddr, protocol, pkt)
}
return
}
if len(dstLinkAddr) != header.EthernetAddressSize {
panic(fmt.Sprintf("DeliverNetworkPacket(%p, %s, %s, %d, _) called with non-MAC dst link addr", rxEP, srcLinkAddr, dstLinkAddr, protocol))
}
if header.IsMulticastEthernetAddress(dstLinkAddr) {
// The bridge `ep` isn't included in ep.links below.
//
// Need to clone as callee takes ownership and the packet still needs to be
// written to constituent links.
if dispatcher != nil {
dispatcher.DeliverNetworkPacket(srcLinkAddr, dstLinkAddr, protocol, pkt.Clone())
}
}
// TODO(https://fxbug.dev/20778): Learn which destinations are on
// which links and restrict transmission, like a bridge.
i := 0
rxFound := false
for _, l := range ep.links {
i++
// Don't write back out the interface from which the frame arrived
// because that causes interoperability issues with a router.
if l == rxEP {
rxFound = true
continue
}
// Shadow pkt so that changes the link makes to the packet buffer
// are not visible to links we write the packet to after.
pkt := pkt
switch i {
case len(ep.links):
// The last call never needs cloning.
case len(ep.links) - 1:
// The second-to-last call needs cloning iff the last endpoint is not rxEP.
if !rxFound {
break
}
fallthrough
default:
pkt = pkt.Clone()
}
switch err := l.WriteRawPacket(pkt); err.(type) {
case nil:
case *tcpip.ErrClosedForSend:
// TODO(https://fxbug.dev/86959): Handle bridged interface removal.
default:
_ = syslog.Warnf("failed to write to bridged endpoint %p: %s", l, err)
}
}
}
// Wait implements stack.LinkEndpoint.
func (*Endpoint) Wait() {}
// ARPHardwareType implements stack.LinkEndpoint.
func (e *Endpoint) ARPHardwareType() header.ARPHardwareType {
// Use the first bridged endpoint.
for _, link := range e.links {
return link.ARPHardwareType()
}
return header.ARPHardwareNone
}
// AddHeader implements stack.LinkEndpoint.
func (e *Endpoint) AddHeader(local, remote tcpip.LinkAddress, protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
// Use the first bridged endpoint.
for _, link := range e.links {
link.AddHeader(local, remote, protocol, pkt)
return
}
}