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