bridge.go raw

   1  // Copyright 2024 The gVisor Authors.
   2  //
   3  // Licensed under the Apache License, Version 2.0 (the "License");
   4  // you may not use this file except in compliance with the License.
   5  // You may obtain a copy of the License at
   6  //
   7  //     http://www.apache.org/licenses/LICENSE-2.0
   8  //
   9  // Unless required by applicable law or agreed to in writing, software
  10  // distributed under the License is distributed on an "AS IS" BASIS,
  11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12  // See the License for the specific language governing permissions and
  13  // limitations under the License.
  14  
  15  package stack
  16  
  17  import (
  18  	"gvisor.dev/gvisor/pkg/atomicbitops"
  19  	"gvisor.dev/gvisor/pkg/tcpip"
  20  	"gvisor.dev/gvisor/pkg/tcpip/header"
  21  )
  22  
  23  var _ NetworkLinkEndpoint = (*BridgeEndpoint)(nil)
  24  
  25  // +stateify savable
  26  type bridgePort struct {
  27  	bridge *BridgeEndpoint
  28  	nic    *nic
  29  }
  30  
  31  // BridgeFDBKey is the MAC address of a device which a bridge port is associated with.
  32  type BridgeFDBKey tcpip.LinkAddress
  33  
  34  // BridgeFDBEntry consists of all metadata for a FDB record.
  35  type BridgeFDBEntry struct {
  36  	port *bridgePort
  37  }
  38  
  39  // PortLinkAddress returns the mac address of the device that is bound to the bridge port.
  40  func (e BridgeFDBEntry) PortLinkAddress() tcpip.LinkAddress {
  41  	if e.port == nil {
  42  		return ""
  43  	}
  44  	return e.port.nic.LinkAddress()
  45  }
  46  
  47  // ParseHeader implements stack.LinkEndpoint.
  48  func (p *bridgePort) ParseHeader(pkt *PacketBuffer) bool {
  49  	_, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize)
  50  	return ok
  51  }
  52  
  53  // DeliverNetworkPacket implements stack.NetworkDispatcher.
  54  func (p *bridgePort) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) {
  55  	bridge := p.bridge
  56  	eth := header.Ethernet(pkt.LinkHeader().Slice())
  57  	updateFDB := false
  58  	bridge.mu.RLock()
  59  	// Add an entry at the bridge FDB, it maps a MAC address
  60  	// to a bridge port where the traffic is received when
  61  	// the MAC address is not multicast.
  62  	// Network packets that are sent to the learned MAC address
  63  	// will be forwarded to the bridge port that is stored in
  64  	// the FDB table.
  65  	sourceAddress := eth.SourceAddress()
  66  	if _, hasSourceFDB := bridge.fdbTable[BridgeFDBKey(sourceAddress)]; !header.IsMulticastEthernetAddress(sourceAddress) && !hasSourceFDB {
  67  		updateFDB = true
  68  	}
  69  	if entry, exist := bridge.fdbTable[BridgeFDBKey(eth.DestinationAddress())]; !exist {
  70  		// When no FDB entry is found, send the packet to all ports.
  71  		for _, port := range bridge.ports {
  72  			if p == port {
  73  				continue
  74  			}
  75  			newPkt := NewPacketBuffer(PacketBufferOptions{
  76  				ReserveHeaderBytes: int(port.nic.MaxHeaderLength()),
  77  				Payload:            pkt.ToBuffer(),
  78  			})
  79  			port.nic.writeRawPacket(newPkt)
  80  			newPkt.DecRef()
  81  		}
  82  	} else if entry.port != p {
  83  		destPort := entry.port
  84  		newPkt := NewPacketBuffer(PacketBufferOptions{
  85  			ReserveHeaderBytes: int(destPort.nic.MaxHeaderLength()),
  86  			Payload:            pkt.ToBuffer(),
  87  		})
  88  		destPort.nic.writeRawPacket(newPkt)
  89  		newPkt.DecRef()
  90  	}
  91  
  92  	d := bridge.dispatcher
  93  	bridge.mu.RUnlock()
  94  	if updateFDB {
  95  		bridge.mu.Lock()
  96  		bridge.addFDBEntryLocked(eth.SourceAddress(), p, 0)
  97  		bridge.mu.Unlock()
  98  	}
  99  	if d != nil {
 100  		// The dispatcher may acquire Stack.mu in DeliverNetworkPacket(), which is
 101  		// ordered above bridge.mu. So call DeliverNetworkPacket() without holding
 102  		// bridge.mu to avoid circular locking.
 103  		d.DeliverNetworkPacket(protocol, pkt)
 104  	}
 105  }
 106  
 107  func (p *bridgePort) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) {
 108  }
 109  
 110  // NewBridgeEndpoint creates a new bridge endpoint.
 111  func NewBridgeEndpoint(mtu uint32) *BridgeEndpoint {
 112  	b := &BridgeEndpoint{
 113  		mtu:  mtu,
 114  		addr: tcpip.GetRandMacAddr(),
 115  	}
 116  	b.ports = make(map[tcpip.NICID]*bridgePort)
 117  	b.fdbTable = make(map[BridgeFDBKey]BridgeFDBEntry)
 118  	return b
 119  }
 120  
 121  // BridgeEndpoint is a bridge endpoint.
 122  //
 123  // +stateify savable
 124  type BridgeEndpoint struct {
 125  	mu bridgeRWMutex `state:"nosave"`
 126  	// +checklocks:mu
 127  	ports map[tcpip.NICID]*bridgePort
 128  	// +checklocks:mu
 129  	dispatcher NetworkDispatcher
 130  	// +checklocks:mu
 131  	addr tcpip.LinkAddress
 132  	// +checklocks:mu
 133  	attached bool
 134  	// +checklocks:mu
 135  	mtu uint32
 136  	// +checklocks:mu
 137  	fdbTable        map[BridgeFDBKey]BridgeFDBEntry
 138  	maxHeaderLength atomicbitops.Uint32
 139  }
 140  
 141  // WritePackets implements stack.LinkEndpoint.WritePackets.
 142  func (b *BridgeEndpoint) WritePackets(pkts PacketBufferList) (int, tcpip.Error) {
 143  	b.mu.RLock()
 144  	defer b.mu.RUnlock()
 145  
 146  	pktsSlice := pkts.AsSlice()
 147  	n := len(pktsSlice)
 148  	for _, p := range b.ports {
 149  		for _, pkt := range pktsSlice {
 150  			// In order to properly loop back to the inbound side we must create a
 151  			// fresh packet that only contains the underlying payload with no headers
 152  			// or struct fields set.
 153  			newPkt := NewPacketBuffer(PacketBufferOptions{
 154  				Payload:            pkt.ToBuffer(),
 155  				ReserveHeaderBytes: int(p.nic.MaxHeaderLength()),
 156  			})
 157  			newPkt.EgressRoute = pkt.EgressRoute
 158  			newPkt.NetworkProtocolNumber = pkt.NetworkProtocolNumber
 159  			p.nic.writePacket(newPkt)
 160  			newPkt.DecRef()
 161  		}
 162  	}
 163  
 164  	return n, nil
 165  }
 166  
 167  // AddNIC adds the specified NIC to the bridge.
 168  func (b *BridgeEndpoint) AddNIC(n *nic) tcpip.Error {
 169  	b.mu.Lock()
 170  	defer b.mu.Unlock()
 171  
 172  	port := &bridgePort{
 173  		nic:    n,
 174  		bridge: b,
 175  	}
 176  	n.NetworkLinkEndpoint.Attach(port)
 177  	b.ports[n.id] = port
 178  
 179  	if b.maxHeaderLength.Load() < uint32(n.MaxHeaderLength()) {
 180  		b.maxHeaderLength.Store(uint32(n.MaxHeaderLength()))
 181  	}
 182  
 183  	return nil
 184  }
 185  
 186  // DelNIC remove the specified NIC from the bridge.
 187  func (b *BridgeEndpoint) DelNIC(nic *nic) tcpip.Error {
 188  	b.mu.Lock()
 189  	defer b.mu.Unlock()
 190  
 191  	port := b.ports[nic.id]
 192  	for k, e := range b.fdbTable {
 193  		if e.port == port {
 194  			delete(b.fdbTable, k)
 195  		}
 196  	}
 197  	delete(b.ports, nic.id)
 198  	nic.NetworkLinkEndpoint.Attach(nic)
 199  	return nil
 200  }
 201  
 202  // MTU implements stack.LinkEndpoint.MTU.
 203  func (b *BridgeEndpoint) MTU() uint32 {
 204  	b.mu.RLock()
 205  	defer b.mu.RUnlock()
 206  	if b.mtu > header.EthernetMinimumSize {
 207  		return b.mtu - header.EthernetMinimumSize
 208  	}
 209  	return 0
 210  }
 211  
 212  // SetMTU implements stack.LinkEndpoint.SetMTU.
 213  func (b *BridgeEndpoint) SetMTU(mtu uint32) {
 214  	b.mu.Lock()
 215  	defer b.mu.Unlock()
 216  	b.mtu = mtu
 217  }
 218  
 219  // MaxHeaderLength implements stack.LinkEndpoint.
 220  func (b *BridgeEndpoint) MaxHeaderLength() uint16 {
 221  	return uint16(b.maxHeaderLength.Load())
 222  }
 223  
 224  // LinkAddress implements stack.LinkEndpoint.LinkAddress.
 225  func (b *BridgeEndpoint) LinkAddress() tcpip.LinkAddress {
 226  	b.mu.RLock()
 227  	defer b.mu.RUnlock()
 228  	return b.addr
 229  }
 230  
 231  // SetLinkAddress implements stack.LinkEndpoint.SetLinkAddress.
 232  func (b *BridgeEndpoint) SetLinkAddress(addr tcpip.LinkAddress) {
 233  	b.mu.Lock()
 234  	defer b.mu.Unlock()
 235  	b.addr = addr
 236  }
 237  
 238  // Capabilities implements stack.LinkEndpoint.Capabilities.
 239  func (b *BridgeEndpoint) Capabilities() LinkEndpointCapabilities {
 240  	return CapabilityRXChecksumOffload | CapabilitySaveRestore | CapabilityResolutionRequired
 241  }
 242  
 243  // Attach implements stack.LinkEndpoint.Attach.
 244  func (b *BridgeEndpoint) Attach(dispatcher NetworkDispatcher) {
 245  	b.mu.Lock()
 246  	defer b.mu.Unlock()
 247  	for _, p := range b.ports {
 248  		p.nic.Primary = nil
 249  	}
 250  	b.dispatcher = dispatcher
 251  	b.ports = make(map[tcpip.NICID]*bridgePort)
 252  	b.fdbTable = make(map[BridgeFDBKey]BridgeFDBEntry)
 253  }
 254  
 255  // IsAttached implements stack.LinkEndpoint.IsAttached.
 256  func (b *BridgeEndpoint) IsAttached() bool {
 257  	b.mu.RLock()
 258  	defer b.mu.RUnlock()
 259  	return b.dispatcher != nil
 260  }
 261  
 262  // Wait implements stack.LinkEndpoint.Wait.
 263  func (b *BridgeEndpoint) Wait() {
 264  }
 265  
 266  // ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType.
 267  func (b *BridgeEndpoint) ARPHardwareType() header.ARPHardwareType {
 268  	return header.ARPHardwareEther
 269  }
 270  
 271  // AddHeader implements stack.LinkEndpoint.AddHeader.
 272  func (b *BridgeEndpoint) AddHeader(pkt *PacketBuffer) {
 273  }
 274  
 275  // ParseHeader implements stack.LinkEndpoint.ParseHeader.
 276  func (b *BridgeEndpoint) ParseHeader(*PacketBuffer) bool {
 277  	return true
 278  }
 279  
 280  // Close implements stack.LinkEndpoint.Close.
 281  func (b *BridgeEndpoint) Close() {}
 282  
 283  // SetOnCloseAction implements stack.LinkEndpoint.Close.
 284  func (b *BridgeEndpoint) SetOnCloseAction(func()) {}
 285  
 286  // Add a new FDBEntry by learning. The learning happens when a packet
 287  // is received by a bridge port, the bridge will use the port for the future
 288  // deliveries to the NIC device.
 289  // The addr is the key when it looks for the entry.
 290  //
 291  // +checklocks:b.mu
 292  func (b *BridgeEndpoint) addFDBEntryLocked(addr tcpip.LinkAddress, source *bridgePort, flags uint64) bool {
 293  	// TODO(b/376924093): limit bridge FDB size.
 294  	b.fdbTable[BridgeFDBKey(addr)] = BridgeFDBEntry{
 295  		port: source,
 296  	}
 297  	return true
 298  }
 299  
 300  // FindFDBEntry find the FDB entry for the given address. If it doesn't exist,
 301  // it will return an empty entry.
 302  func (b *BridgeEndpoint) FindFDBEntry(addr tcpip.LinkAddress) BridgeFDBEntry {
 303  	b.mu.RLock()
 304  	defer b.mu.RUnlock()
 305  	return b.fdbTable[BridgeFDBKey(addr)]
 306  }
 307