mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
netstack: implement bridge devices
Implement the core part. All packets are broadcast-ed to all ports. The next step will be to implement forwarding and multicast group databases. PiperOrigin-RevId: 644471256
This commit is contained in:
@@ -165,6 +165,21 @@ func (s *Stack) SetInterface(ctx context.Context, msg *nlmsg.Message) *syserr.Er
|
||||
// Netstack interfaces are always up.
|
||||
}
|
||||
|
||||
return s.setLink(tcpip.NICID(ifinfomsg.Index), attrs)
|
||||
}
|
||||
|
||||
func (s *Stack) setLink(id tcpip.NICID, linkAttrs map[uint16]nlmsg.BytesView) *syserr.Error {
|
||||
if v, ok := linkAttrs[linux.IFLA_MASTER]; ok {
|
||||
master, ok := v.Uint32()
|
||||
if !ok {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
if master != 0 {
|
||||
if err := s.Stack.SetNICCoordinator(id, tcpip.NICID(master)); err != nil {
|
||||
return syserr.TranslateNetstackError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -232,6 +247,10 @@ func (s *Stack) newVeth(ctx context.Context, linkAttrs map[uint16]nlmsg.BytesVie
|
||||
return syserr.TranslateNetstackError(err)
|
||||
}
|
||||
ep.SetStack(s.Stack, id)
|
||||
if err := s.setLink(id, linkAttrs); err != nil {
|
||||
peerEP.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if peerName == "" {
|
||||
peerName = fmt.Sprintf("veth%d", peerID)
|
||||
@@ -244,6 +263,34 @@ func (s *Stack) newVeth(ctx context.Context, linkAttrs map[uint16]nlmsg.BytesVie
|
||||
return syserr.TranslateNetstackError(err)
|
||||
}
|
||||
peerEP.SetStack(peerStack.Stack, id)
|
||||
if peerLinkAttrs != nil {
|
||||
if err := s.setLink(peerID, peerLinkAttrs); err != nil {
|
||||
peerStack.Stack.RemoveNIC(peerID)
|
||||
peerEP.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stack) newBridge(ctx context.Context, linkAttrs map[uint16]nlmsg.BytesView, linkInfoAttrs map[uint16]nlmsg.BytesView) *syserr.Error {
|
||||
ifname := ""
|
||||
|
||||
if v, ok := linkAttrs[linux.IFLA_IFNAME]; ok {
|
||||
ifname = v.String()
|
||||
}
|
||||
ep := stack.NewBridgeEndpoint(defaultMTU)
|
||||
id := tcpip.NICID(s.Stack.UniqueID())
|
||||
err := s.Stack.CreateNICWithOptions(id, ep, stack.NICOptions{
|
||||
Name: ifname,
|
||||
})
|
||||
if err != nil {
|
||||
return syserr.TranslateNetstackError(err)
|
||||
}
|
||||
if err := s.setLink(id, linkAttrs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -275,6 +322,8 @@ func (s *Stack) newInterface(ctx context.Context, msg *nlmsg.Message, linkAttrs
|
||||
switch kind {
|
||||
case "":
|
||||
return syserr.ErrInvalidArgument
|
||||
case "bridge":
|
||||
return s.newBridge(ctx, linkAttrs, linkInfoAttrs)
|
||||
case "veth":
|
||||
return s.newVeth(ctx, linkAttrs, linkInfoAttrs)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,13 @@ declare_rwmutex(
|
||||
prefix = "route",
|
||||
)
|
||||
|
||||
declare_rwmutex(
|
||||
name = "bridge_mutex",
|
||||
out = "bridge_mutex.go",
|
||||
package = "stack",
|
||||
prefix = "bridge",
|
||||
)
|
||||
|
||||
declare_rwmutex(
|
||||
name = "route_stack_mutex",
|
||||
out = "route_stack_mutex.go",
|
||||
@@ -201,6 +208,8 @@ go_library(
|
||||
"address_state_refs.go",
|
||||
"addressable_endpoint_state.go",
|
||||
"addressable_endpoint_state_mutex.go",
|
||||
"bridge.go",
|
||||
"bridge_mutex.go",
|
||||
"bucket_mutex.go",
|
||||
"cleanup_endpoints_mutex.go",
|
||||
"conn_mutex.go",
|
||||
@@ -336,3 +345,21 @@ go_test(
|
||||
"@com_github_google_go_cmp//cmp/cmpopts:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "bridge_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"bridge_test.go",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/buffer",
|
||||
"//pkg/refs",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/link/channel",
|
||||
"//pkg/tcpip/link/ethernet",
|
||||
"//pkg/tcpip/link/veth",
|
||||
"//pkg/tcpip/stack",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
// Copyright 2024 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package stack
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/atomicbitops"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
var _ NetworkLinkEndpoint = (*BridgeEndpoint)(nil)
|
||||
|
||||
type bridgePort struct {
|
||||
bridge *BridgeEndpoint
|
||||
nic *nic
|
||||
}
|
||||
|
||||
// ParseHeader implements stack.LinkEndpoint.
|
||||
func (p *bridgePort) ParseHeader(pkt *PacketBuffer) bool {
|
||||
_, ok := pkt.LinkHeader().Consume(header.EthernetMinimumSize)
|
||||
return ok
|
||||
}
|
||||
|
||||
// DeliverNetworkPacket implements stack.NetworkDispatcher.
|
||||
func (p *bridgePort) DeliverNetworkPacket(protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) {
|
||||
bridge := p.bridge
|
||||
bridge.mu.Lock()
|
||||
defer bridge.mu.Unlock()
|
||||
|
||||
// Send the packet to all other ports.
|
||||
for _, port := range bridge.ports {
|
||||
if p == port {
|
||||
continue
|
||||
}
|
||||
newPkt := NewPacketBuffer(PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(port.nic.MaxHeaderLength()),
|
||||
Payload: pkt.ToBuffer(),
|
||||
})
|
||||
port.nic.writeRawPacket(newPkt)
|
||||
newPkt.DecRef()
|
||||
}
|
||||
|
||||
bridge.injectInboundLocked(protocol, pkt)
|
||||
}
|
||||
|
||||
func (p *bridgePort) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) {
|
||||
}
|
||||
|
||||
func getRandMacAddr() tcpip.LinkAddress {
|
||||
mac := make(net.HardwareAddr, 6)
|
||||
rand.Read(mac) // Fill with random data.
|
||||
mac[0] &^= 0x1 // Clear multicast bit.
|
||||
mac[0] |= 0x2 // Set local assignment bit (IEEE802).
|
||||
return tcpip.LinkAddress(mac)
|
||||
}
|
||||
|
||||
// NewBridgeEndpoint creates a new bridge endpoint.
|
||||
func NewBridgeEndpoint(mtu uint32) *BridgeEndpoint {
|
||||
b := &BridgeEndpoint{
|
||||
mtu: mtu,
|
||||
addr: getRandMacAddr(),
|
||||
}
|
||||
b.ports = make(map[tcpip.NICID]*bridgePort)
|
||||
return b
|
||||
}
|
||||
|
||||
// BridgeEndpoint is a bridge endpoint.
|
||||
type BridgeEndpoint struct {
|
||||
mu bridgeRWMutex
|
||||
// +checklocks:mu
|
||||
ports map[tcpip.NICID]*bridgePort
|
||||
// +checklocks:mu
|
||||
dispatcher NetworkDispatcher
|
||||
// +checklocks:mu
|
||||
addr tcpip.LinkAddress
|
||||
// +checklocks:mu
|
||||
attached bool
|
||||
mtu uint32
|
||||
maxHeaderLength atomicbitops.Uint32
|
||||
}
|
||||
|
||||
// WritePackets implements stack.LinkEndpoint.WritePackets.
|
||||
func (b *BridgeEndpoint) WritePackets(pkts PacketBufferList) (int, tcpip.Error) {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
|
||||
pktsSlice := pkts.AsSlice()
|
||||
n := len(pktsSlice)
|
||||
for _, p := range b.ports {
|
||||
for _, pkt := range pktsSlice {
|
||||
// In order to properly loop back to the inbound side we must create a
|
||||
// fresh packet that only contains the underlying payload with no headers
|
||||
// or struct fields set.
|
||||
newPkt := NewPacketBuffer(PacketBufferOptions{
|
||||
Payload: pkt.ToBuffer(),
|
||||
ReserveHeaderBytes: int(p.nic.MaxHeaderLength()),
|
||||
})
|
||||
newPkt.EgressRoute = pkt.EgressRoute
|
||||
newPkt.NetworkProtocolNumber = pkt.NetworkProtocolNumber
|
||||
p.nic.writePacket(newPkt)
|
||||
newPkt.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// AddNIC adds the specified NIC to the bridge.
|
||||
func (b *BridgeEndpoint) AddNIC(n *nic) tcpip.Error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
port := &bridgePort{
|
||||
nic: n,
|
||||
bridge: b,
|
||||
}
|
||||
n.NetworkLinkEndpoint.Attach(port)
|
||||
b.ports[n.id] = port
|
||||
|
||||
if b.maxHeaderLength.Load() < uint32(n.MaxHeaderLength()) {
|
||||
b.maxHeaderLength.Store(uint32(n.MaxHeaderLength()))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelNIC remove the specified NIC from the bridge.
|
||||
func (b *BridgeEndpoint) DelNIC(nic *nic) tcpip.Error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
delete(b.ports, nic.id)
|
||||
nic.NetworkLinkEndpoint.Attach(nic)
|
||||
return nil
|
||||
}
|
||||
|
||||
// +checklocks:b.mu
|
||||
func (b *BridgeEndpoint) injectInboundLocked(protocol tcpip.NetworkProtocolNumber, pkt *PacketBuffer) {
|
||||
d := b.dispatcher
|
||||
if d != nil {
|
||||
d.DeliverNetworkPacket(protocol, pkt)
|
||||
}
|
||||
}
|
||||
|
||||
// MTU implements stack.LinkEndpoint.MTU.
|
||||
func (b *BridgeEndpoint) MTU() uint32 {
|
||||
if b.mtu > header.EthernetMinimumSize {
|
||||
return b.mtu - header.EthernetMinimumSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// MaxHeaderLength implements stack.LinkEndpoint.
|
||||
func (b *BridgeEndpoint) MaxHeaderLength() uint16 {
|
||||
return uint16(b.maxHeaderLength.Load())
|
||||
}
|
||||
|
||||
// LinkAddress implements stack.LinkEndpoint.LinkAddress.
|
||||
func (b *BridgeEndpoint) LinkAddress() tcpip.LinkAddress {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.addr
|
||||
}
|
||||
|
||||
// SetLinkAddress implements stack.LinkEndpoint.SetLinkAddress.
|
||||
func (b *BridgeEndpoint) SetLinkAddress(addr tcpip.LinkAddress) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
b.addr = addr
|
||||
}
|
||||
|
||||
// Capabilities implements stack.LinkEndpoint.Capabilities.
|
||||
func (b *BridgeEndpoint) Capabilities() LinkEndpointCapabilities {
|
||||
return CapabilityRXChecksumOffload | CapabilitySaveRestore | CapabilityResolutionRequired
|
||||
}
|
||||
|
||||
// Attach implements stack.LinkEndpoint.Attach.
|
||||
func (b *BridgeEndpoint) Attach(dispatcher NetworkDispatcher) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
for _, p := range b.ports {
|
||||
p.nic.Primary = nil
|
||||
}
|
||||
b.dispatcher = dispatcher
|
||||
b.ports = make(map[tcpip.NICID]*bridgePort)
|
||||
}
|
||||
|
||||
// IsAttached implements stack.LinkEndpoint.IsAttached.
|
||||
func (b *BridgeEndpoint) IsAttached() bool {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
return b.dispatcher != nil
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (b *BridgeEndpoint) Wait() {
|
||||
}
|
||||
|
||||
// ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType.
|
||||
func (b *BridgeEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
return header.ARPHardwareEther
|
||||
}
|
||||
|
||||
// AddHeader implements stack.LinkEndpoint.AddHeader.
|
||||
func (b *BridgeEndpoint) AddHeader(pkt *PacketBuffer) {
|
||||
}
|
||||
|
||||
// ParseHeader implements stack.LinkEndpoint.ParseHeader.
|
||||
func (b *BridgeEndpoint) ParseHeader(*PacketBuffer) bool {
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package bridge_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/refs"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/ethernet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/veth"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
func TestWritePacketFromBridge(t *testing.T) {
|
||||
const (
|
||||
localLinkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06")
|
||||
remoteLinkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x07")
|
||||
bridgeLinkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x08")
|
||||
|
||||
netProto = 55
|
||||
nicID = 5
|
||||
bridgeID = 6
|
||||
)
|
||||
|
||||
c := channel.New(1, header.EthernetMinimumSize, localLinkAddr)
|
||||
|
||||
s := stack.New(stack.Options{})
|
||||
if err := s.CreateNIC(nicID, ethernet.New(c)); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err)
|
||||
}
|
||||
bridgeEndpoint := stack.NewBridgeEndpoint(1500)
|
||||
bridgeEndpoint.SetLinkAddress(bridgeLinkAddr)
|
||||
if err := s.CreateNIC(bridgeID, bridgeEndpoint); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err)
|
||||
}
|
||||
if err := s.SetNICCoordinator(nicID, bridgeID); err != nil {
|
||||
t.Fatalf("s.SetNICCoordinator")
|
||||
}
|
||||
|
||||
if err := s.WritePacketToRemote(bridgeID, remoteLinkAddr, netProto, buffer.Buffer{}); err != nil {
|
||||
t.Fatalf("s.WritePacketToRemote(%d, %s, _): %s", bridgeID, remoteLinkAddr, err)
|
||||
}
|
||||
pkt := c.Read()
|
||||
if pkt == nil {
|
||||
t.Fatal("expected to read a packet")
|
||||
}
|
||||
|
||||
eth := header.Ethernet(pkt.LinkHeader().Slice())
|
||||
pkt.DecRef()
|
||||
if got := eth.SourceAddress(); got != bridgeLinkAddr {
|
||||
t.Errorf("got eth.SourceAddress() = %s, want = %s", got, bridgeLinkAddr)
|
||||
}
|
||||
if got := eth.DestinationAddress(); got != remoteLinkAddr {
|
||||
t.Errorf("got eth.DestinationAddress() = %s, want = %s", got, remoteLinkAddr)
|
||||
}
|
||||
if got := eth.Type(); got != netProto {
|
||||
t.Errorf("got eth.Type() = %d, want = %d", got, netProto)
|
||||
}
|
||||
}
|
||||
|
||||
type testNotification struct {
|
||||
ch chan bool
|
||||
}
|
||||
|
||||
func (n *testNotification) WriteNotify() {
|
||||
n.ch <- true
|
||||
}
|
||||
|
||||
func TestWritePacketBetweenDevices(t *testing.T) {
|
||||
const (
|
||||
localLinkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x06")
|
||||
remoteLinkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x07")
|
||||
bridgeLinkAddr = tcpip.LinkAddress("\x02\x02\x03\x04\x05\x08")
|
||||
|
||||
netProto = 55
|
||||
nicID = 5
|
||||
vethID = 7
|
||||
bridgeID = 6
|
||||
)
|
||||
|
||||
veth1, veth2 := veth.NewPair(1500)
|
||||
secondStack := stack.New(stack.Options{})
|
||||
if err := secondStack.CreateNIC(vethID, ethernet.New(veth2)); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _): %s", vethID, err)
|
||||
}
|
||||
veth2.SetStack(secondStack, vethID)
|
||||
veth2.SetLinkAddress(localLinkAddr)
|
||||
|
||||
s := stack.New(stack.Options{})
|
||||
bridgeEndpoint := stack.NewBridgeEndpoint(1500)
|
||||
bridgeEndpoint.SetLinkAddress(bridgeLinkAddr)
|
||||
if err := s.CreateNIC(bridgeID, bridgeEndpoint); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err)
|
||||
}
|
||||
|
||||
c := channel.New(1, header.EthernetMinimumSize, localLinkAddr)
|
||||
c.SetLinkAddress(remoteLinkAddr)
|
||||
if err := s.CreateNIC(nicID, ethernet.New(c)); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err)
|
||||
}
|
||||
if err := s.SetNICCoordinator(nicID, bridgeID); err != nil {
|
||||
t.Fatalf("s.SetNICCoordinator")
|
||||
}
|
||||
|
||||
if err := s.CreateNIC(vethID, ethernet.New(veth1)); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _): %s", vethID, err)
|
||||
}
|
||||
veth1.SetStack(s, vethID)
|
||||
if err := s.SetNICCoordinator(vethID, bridgeID); err != nil {
|
||||
t.Fatalf("s.SetNICCoordinator")
|
||||
}
|
||||
|
||||
n := &testNotification{ch: make(chan bool, 1)}
|
||||
c.AddNotify(n)
|
||||
if err := secondStack.WritePacketToRemote(vethID, remoteLinkAddr, netProto, buffer.Buffer{}); err != nil {
|
||||
t.Fatalf("s.WritePacketToRemote(%d, %s, _): %s", bridgeID, remoteLinkAddr, err)
|
||||
}
|
||||
<-n.ch
|
||||
pkt := c.Read()
|
||||
if pkt == nil {
|
||||
t.Fatal("expected to read a packet")
|
||||
}
|
||||
|
||||
pkt.LinkHeader().Consume(header.EthernetMinimumSize)
|
||||
eth := header.Ethernet(pkt.LinkHeader().Slice())
|
||||
pkt.DecRef()
|
||||
if got := eth.SourceAddress(); got != localLinkAddr {
|
||||
t.Errorf("got eth.SourceAddress() = %s, want = %s", got, localLinkAddr)
|
||||
}
|
||||
if got := eth.DestinationAddress(); got != remoteLinkAddr {
|
||||
t.Errorf("got eth.DestinationAddress() = %s, want = %s", got, remoteLinkAddr)
|
||||
}
|
||||
if got := eth.Type(); got != netProto {
|
||||
t.Errorf("got eth.Type() = %d, want = %d", got, netProto)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
refs.SetLeakMode(refs.LeaksPanic)
|
||||
code := m.Run()
|
||||
refs.DoLeakCheck()
|
||||
os.Exit(code)
|
||||
}
|
||||
@@ -87,6 +87,9 @@ type nic struct {
|
||||
// deliverLinkPackets is off by default because some users already
|
||||
// deliver link packets by explicitly calling nic.DeliverLinkPackets.
|
||||
deliverLinkPackets bool
|
||||
|
||||
// Primary is the main controlling interface in a bonded setup.
|
||||
Primary *nic
|
||||
}
|
||||
|
||||
// makeNICStats initializes the NIC statistics and associates them to the global
|
||||
@@ -1079,3 +1082,11 @@ func (n *nic) multicastForwarding(protocol tcpip.NetworkProtocolNumber) (bool, t
|
||||
|
||||
return ep.MulticastForwarding(), nil
|
||||
}
|
||||
|
||||
// CoordinatorNIC represents NetworkLinkEndpoint that can join multiple network devices.
|
||||
type CoordinatorNIC interface {
|
||||
// AddNIC adds the specified NIC device.
|
||||
AddNIC(n *nic) tcpip.Error
|
||||
// DelNIC deletes the specified NIC device.
|
||||
DelNIC(n *nic) tcpip.Error
|
||||
}
|
||||
|
||||
@@ -843,6 +843,18 @@ type NICOptions struct {
|
||||
DeliverLinkPackets bool
|
||||
}
|
||||
|
||||
// GetNICByID return a network device associated with the specified ID.
|
||||
func (s *Stack) GetNICByID(id tcpip.NICID) (*nic, tcpip.Error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
n, ok := s.nics[id]
|
||||
if !ok {
|
||||
return nil, &tcpip.ErrNoSuchFile{}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// CreateNICWithOptions creates a NIC with the provided id, LinkEndpoint, and
|
||||
// NICOptions. See the documentation on type NICOptions for details on how
|
||||
// NICs can be configured.
|
||||
@@ -964,6 +976,13 @@ func (s *Stack) removeNICLocked(id tcpip.NICID) tcpip.Error {
|
||||
}
|
||||
delete(s.nics, id)
|
||||
|
||||
if nic.Primary != nil {
|
||||
b := nic.Primary.NetworkLinkEndpoint.(CoordinatorNIC)
|
||||
if err := b.DelNIC(nic); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Remove routes in-place. n tracks the number of routes written.
|
||||
s.routeMu.Lock()
|
||||
n := 0
|
||||
@@ -981,6 +1000,31 @@ func (s *Stack) removeNICLocked(id tcpip.NICID) tcpip.Error {
|
||||
return nic.remove()
|
||||
}
|
||||
|
||||
// SetNICCoordinator sets a coordinator device.
|
||||
func (s *Stack) SetNICCoordinator(id tcpip.NICID, mid tcpip.NICID) tcpip.Error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
nic, ok := s.nics[id]
|
||||
if !ok {
|
||||
return &tcpip.ErrUnknownNICID{}
|
||||
}
|
||||
|
||||
m, ok := s.nics[mid]
|
||||
if !ok {
|
||||
return &tcpip.ErrUnknownNICID{}
|
||||
}
|
||||
b, ok := m.NetworkLinkEndpoint.(CoordinatorNIC)
|
||||
if !ok {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
if err := b.AddNIC(nic); err != nil {
|
||||
return err
|
||||
}
|
||||
nic.Primary = m
|
||||
return nil
|
||||
}
|
||||
|
||||
// NICInfo captures the name and addresses assigned to a NIC.
|
||||
type NICInfo struct {
|
||||
Name string
|
||||
|
||||
Reference in New Issue
Block a user