forked from sandia-minimega/minimega
/
bridge.go
232 lines (182 loc) · 5.35 KB
/
bridge.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// Copyright 2016-2021 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain
// rights in this software.
package bridge
import (
"fmt"
"gonetflow"
log "minilog"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/google/gopacket/pcap"
)
// Global lock for all bridge operations
var bridgeLock sync.Mutex
// Bridge stores state about an openvswitch bridge including the taps, tunnels,
// trunks, and netflow.
type Bridge struct {
Name string
preExist bool
// mirrors records the mirror tap names used by captures
mirrors map[string]bool
// captures records the "stop" flags that are set to non-zero values when
// we want to stop a capture.
captures map[int]capture
trunks map[string]bool
tunnels map[string]bool
taps map[string]*Tap
nf *gonetflow.Netflow
// nameChan is a reference to the nameChan from the Bridges struct that
// this Bridge was created on.
nameChan chan string
handle *pcap.Handle
// config values that have been set on this bridge
config map[string]string
// set to non-zero value by Bridge.destroy
isdestroyed uint64
}
// BridgeInfo is a summary of fields from a Bridge.
type BridgeInfo struct {
Name string
PreExist bool
VLANs []int
Trunks []string
Tunnels []string
Mirrors []string
Config map[string]string
}
// Tap represents an interface that is attached to an openvswitch bridge.
type Tap struct {
Name string // Name of the tap
Bridge string // Bridge that the tap is connected to
VLAN int // VLAN ID for the tap
MAC string // MAC address
Host bool // Set when created as a host tap (and, thus, promiscuous)
Container bool // Set when created via CreateContainerTap
Defunct bool // Set when Tap should be reaped
IP4 string // Snooped IPv4 address
IP6 string // Snooped IPv6 address
*qos // Quality-of-service constraints
stats []tapStat
}
type capture struct {
tap string
// isstopped is set to non-zero when stopped
isstopped *uint64
// ack is closed when the goroutine doing the capture closes
ack chan bool
// pcap handle, needed so that we can close it in stopCapture
handle *pcap.Handle
}
type tapStat struct {
t time.Time
RxBytes int
TxBytes int
}
func (b *Bridge) destroy() error {
log.Info("destroying bridge: %v", b.Name)
if b.destroyed() {
// bridge has already been destroyed
return nil
}
b.setDestroyed()
if b.handle != nil {
b.handle.Close()
}
// first get all of the taps off of this bridge and destroy them
for _, tap := range b.taps {
if tap.Defunct {
continue
}
log.Debug("destroying tap %v", tap.Name)
if err := b.destroyTap(tap.Name); err != nil {
log.Info("could not destroy tap: %v", err)
}
}
for v := range b.trunks {
if err := b.removeTrunk(v); err != nil {
return err
}
}
for v := range b.tunnels {
if err := b.removeTunnel(v); err != nil {
return err
}
}
for v := range b.captures {
b.stopCapture(v)
}
if b.nf != nil {
if err := b.destroyNetflow(); err != nil {
return err
}
}
// make sure we actually reap the taps before we return
if err := b.reapTaps(); err != nil {
return err
}
// don't destroy the bridge if it existed before we started
if b.preExist {
return nil
}
return ovsDelBridge(b.Name)
}
// ReapTap should be called periodically to remove defunct taps.
func (b *Bridge) ReapTaps() error {
bridgeLock.Lock()
defer bridgeLock.Unlock()
return b.reapTaps()
}
// reapTaps deletes all defunct taps from a bridge using a single openvswitch
// del-port command. We do this to speed up the time it takes to remove
// openvswitch taps when a large number of taps are present on a bridge. See
// https://github.com/sandia-minimega/minimega/issues/296 for more discussion.
//
// A single del-port command in openvswitch is typically between 30-40
// characters, plus the 'ovs-vsctl' command. A command line buffer on a modern
// linux machine is something like 2MB (wow), so if we round the per-del-port
// up to 50 characters, we should be able to stack 40000 del-ports on a single
// command line. To that end we won't bother with setting a maximum number of
// taps to remove in a single operation. If we eventually get to 40k taps
// needing removal in a single pass of the reaper, then we have other problems.
//
// You can check yourself with `getconf ARG_MAX` or `xargs --show-limits`
func (b *Bridge) reapTaps() error {
log.Debug("reaping taps on bridge: %v", b.Name)
var args []string
for _, tap := range b.taps {
// build up the arg string directly for defunct taps
if tap.Defunct {
args = append(args, "--", "del-port", b.Name, tap.Name)
}
}
if len(args) == 0 {
return nil
}
log.Debug("reapTaps args: %v", strings.Join(args, " "))
if _, err := ovsCmdWrapper(args); err != nil {
return fmt.Errorf("reap taps failed: %v", err)
}
// clean up state
for _, tap := range b.taps {
if tap.Defunct {
delete(b.taps, tap.Name)
}
}
return nil
}
func (b *Bridge) setDestroyed() {
atomic.StoreUint64(&b.isdestroyed, 1)
}
func (b *Bridge) destroyed() bool {
return atomic.LoadUint64(&b.isdestroyed) > 0
}
// DestroyBridge deletes an `unmanaged` bridge. This can be used when cleaning
// up from a crash. See `Bride.Destroy` for managed bridges.
func DestroyBridge(name string) error {
bridgeLock.Lock()
defer bridgeLock.Unlock()
return ovsDelBridge(name)
}