/
switch.go
116 lines (91 loc) · 2.41 KB
/
switch.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
// SPDX-License-Identifier: Apache-2.0
package gont
import (
"fmt"
nl "github.com/vishvananda/netlink"
"go.uber.org/zap"
)
type SwitchOption interface {
ApplySwitch(sw *Switch)
}
type BridgeOption interface {
ApplyBridge(br *nl.Bridge)
}
// Switch is an abstraction for a Linux virtual bridge
type Switch struct {
*BaseNode
}
// Options
func (sw *Switch) ApplyInterface(i *Interface) {
i.Node = sw
}
// AddSwitch adds a new Linux virtual bridge in a dedicated namespace
func (n *Network) AddSwitch(name string, opts ...Option) (*Switch, error) {
node, err := n.AddNode(name, opts...)
if err != nil {
return nil, fmt.Errorf("failed to create node: %w", err)
}
sw := &Switch{
BaseNode: node,
}
n.Register(sw)
br := &nl.Bridge{
LinkAttrs: nl.LinkAttrs{
Name: bridgeInterfaceName,
Namespace: nl.NsFd(sw.NsHandle),
},
}
// Apply options
for _, opt := range opts {
switch opt := opt.(type) {
case SwitchOption:
opt.ApplySwitch(sw)
case BridgeOption:
opt.ApplyBridge(br)
case LinkOption:
opt.ApplyLink(&br.LinkAttrs)
}
}
if err := nl.LinkAdd(br); err != nil {
return nil, fmt.Errorf("failed to add bridge interface: %w", err)
}
n.logger.Info("Adding new Linux bridge",
zap.Any("node", sw),
zap.String("intf", br.LinkAttrs.Name),
)
if err := sw.nlHandle.LinkSetUp(br); err != nil {
return nil, fmt.Errorf("failed to bring bridge up: %w", err)
}
// Connect host to switch interfaces
for _, intf := range sw.Interfaces {
peerDev := fmt.Sprintf("veth-%s", name)
left := intf
left.Node = sw
right := &Interface{
Name: peerDev,
Node: intf.Node,
}
if err := n.AddLink(left, right); err != nil {
return nil, fmt.Errorf("failed to add link: %w", err)
}
}
return sw, nil
}
// ConfigureInterface attaches an existing interface to a bridge interface
func (sw *Switch) ConfigureInterface(i *Interface) error {
sw.logger.Info("Connecting interface to bridge master", zap.Any("intf", i))
br, err := sw.nlHandle.LinkByName(bridgeInterfaceName)
if err != nil {
return fmt.Errorf("failed to find bridge intf: %s", err)
}
l, err := sw.nlHandle.LinkByName(i.Name)
if err != nil {
return fmt.Errorf("failed to find new bridge interface: %s", err)
}
// Attach interface to bridge
if err := sw.nlHandle.LinkSetMaster(l, br); err != nil {
return err
}
return sw.BaseNode.ConfigureInterface(i)
}