| // 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 |
| } |
| } |