Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

weaveworks / weave / #6002

13 Apr 2016 - 6:49 coverage decreased (-0.1%) to 75.607%
#6002

Pull #2154

circleci

2f1e5f233f2b7283a9bf3277e75bf30a?size=18&default=identiconrade
Lock round TestRouter map accesses, and copy the set where we may block.
Pull Request #2154: Lock round TestRouter map accesses

6137 of 8117 relevant lines covered (75.61%)

129553.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.53
/router/fastdp.go
1
package router
2

3
import (
4
        "encoding/binary"
5
        "encoding/json"
6
        "fmt"
7
        "net"
8
        "sync"
9
        "syscall"
10
        "time"
11

12
        "github.com/weaveworks/go-odp/odp"
13
        "github.com/weaveworks/mesh"
14
)
15

16
// The virtual bridge accepts packets from ODP vports and the router
17
// port (i.e. InjectPacket).  We need a map key to index those
18
// possibilities:
19
type bridgePortID struct {
20
        vport  odp.VportID
21
        router bool
22
}
23

24
// A bridgeSender sends out a packet from the virtual bridge
25
type bridgeSender func(key PacketKey, lock *fastDatapathLock) FlowOp
26

27
// A missHandler handles an ODP miss
28
type missHandler func(fks odp.FlowKeys, lock *fastDatapathLock) FlowOp
29

30
type FastDatapath struct {
31
        lock             sync.Mutex // guards state and synchronises use of dpif
32
        iface            *net.Interface
33
        dpif             *odp.Dpif
34
        dp               odp.DatapathHandle
35
        deleteFlowsCount uint64
36
        missCount        uint64
37
        missHandlers     map[odp.VportID]missHandler
38
        localPeer        *mesh.Peer
39
        peers            *mesh.Peers
40
        overlayConsumer  OverlayConsumer
41

42
        // Bridge state: How to send to the given bridge port
43
        sendToPort map[bridgePortID]bridgeSender
44

45
        // How to send to a given destination MAC
46
        sendToMAC map[MAC]bridgeSender
47

48
        // MACs seen on the bridge recently
49
        seenMACs map[MAC]struct{}
50

51
        // vxlan vports associated with the given UDP ports
52
        vxlanVportIDs    map[int]odp.VportID
53
        mainVxlanVportID odp.VportID
54

55
        // A singleton pool for the occasions when we need to decode
56
        // the packet.
57
        dec *EthernetDecoder
58

59
        // forwarders by remote peer
60
        forwarders map[mesh.PeerName]*fastDatapathForwarder
61
}
62

63
func NewFastDatapath(iface *net.Interface, port int) (*FastDatapath, error) {
76×
64
        dpif, err := odp.NewDpif()
76×
65
        if err != nil {
76×
66
                return nil, err
!
67
        }
!
68

69
        success := false
76×
70
        defer func() {
152×
71
                if !success {
76×
72
                        dpif.Close()
!
73
                }
!
74
        }()
75

76
        dp, err := dpif.LookupDatapath(iface.Name)
76×
77
        if err != nil {
76×
78
                return nil, err
!
79
        }
!
80

81
        fastdp := &FastDatapath{
76×
82
                iface:         iface,
76×
83
                dpif:          dpif,
76×
84
                dp:            dp,
76×
85
                missHandlers:  make(map[odp.VportID]missHandler),
76×
86
                sendToPort:    nil,
76×
87
                sendToMAC:     make(map[MAC]bridgeSender),
76×
88
                seenMACs:      make(map[MAC]struct{}),
76×
89
                vxlanVportIDs: make(map[int]odp.VportID),
76×
90
                forwarders:    make(map[mesh.PeerName]*fastDatapathForwarder),
76×
91
        }
76×
92

76×
93
        // This delete happens asynchronously in the kernel, meaning that
76×
94
        // we can sometimes fail to recreate the vxlan vport with EADDRINUSE -
76×
95
        // consequently we retry a small number of times in
76×
96
        // getVxlanVportIDHarder() to compensate.
76×
97
        if err := fastdp.deleteVxlanVports(); err != nil {
76×
98
                return nil, err
!
99
        }
!
100

101
        if err := fastdp.deleteFlows(); err != nil {
76×
102
                return nil, err
!
103
        }
!
104

105
        // We use the weave port number plus 1 for vxlan.  Hard-coding
106
        // this relationship may seem dubious, but there is no moral
107
        // difference between this and requiring that the sleeve UDP
108
        // port number is the same as the TCP port number.  The hard
109
        // part would be not adding weaver flags to allow the port
110
        // numbers to be independent, but working out how to specify
111
        // them on the connecting side.  So we can wait to find out if
112
        // anyone wants that.
113
        fastdp.mainVxlanVportID, err = fastdp.getVxlanVportIDHarder(port+1, 5, time.Millisecond*10)
76×
114
        if err != nil {
76×
115
                return nil, err
!
116
        }
!
117

118
        // need to lock before we might receive events
119
        fastdp.lock.Lock()
76×
120
        defer fastdp.lock.Unlock()
76×
121

76×
122
        if _, err := dp.ConsumeMisses(fastdp); err != nil {
76×
123
                return nil, err
!
124
        }
!
125

126
        if _, err := dp.ConsumeVportEvents(fastdp); err != nil {
76×
127
                return nil, err
!
128
        }
!
129

130
        vports, err := dp.EnumerateVports()
76×
131
        if err != nil {
76×
132
                return nil, err
!
133
        }
!
134

135
        for _, vport := range vports {
304×
136
                fastdp.makeBridgeVport(vport)
228×
137
        }
228×
138

139
        success = true
76×
140
        go fastdp.run()
76×
141
        return fastdp, nil
76×
142
}
143

144
func (fastdp *FastDatapath) Close() error {
!
145
        fastdp.lock.Lock()
!
146
        defer fastdp.lock.Unlock()
!
147
        err := fastdp.dpif.Close()
!
148
        fastdp.dpif = nil
!
149
        return err
!
150
}
!
151

152
// While processing a packet, we can potentially acquire and drop the
153
// FastDatapath lock many times (acquiring it to acceess FastDatapath
154
// state, and invoke ODP operations; dropping it to invoke callbacks
155
// that may re-enter the FastDatapath).  A fastDatapathLock
156
// coordinates this process.
157
type fastDatapathLock struct {
158
        fastdp *FastDatapath
159
        locked bool
160

161
        // While the lock is dropped, deleteFlows could be called.  We
162
        // need to detect when this happens and avoid creating flows,
163
        // because they may be based on stale information.
164
        deleteFlowsCount uint64
165
}
166

167
func (fastdp *FastDatapath) startLock() fastDatapathLock {
2,456×
168
        fastdp.lock.Lock()
2,456×
169
        return fastDatapathLock{
2,456×
170
                fastdp:           fastdp,
2,456×
171
                locked:           true,
2,456×
172
                deleteFlowsCount: fastdp.deleteFlowsCount,
2,456×
173
        }
2,456×
174
}
2,456×
175

176
func (lock *fastDatapathLock) unlock() {
4,173×
177
        if lock.locked {
8,346×
178
                lock.fastdp.lock.Unlock()
4,173×
179
                lock.locked = false
4,173×
180
        }
4,173×
181
}
182

183
func (lock *fastDatapathLock) relock() {
5,237×
184
        if !lock.locked {
6,954×
185
                lock.fastdp.lock.Lock()
1,717×
186
                lock.locked = true
1,717×
187
        }
1,717×
188
}
189

190
// Bridge bits
191

192
type fastDatapathBridge struct {
193
        *FastDatapath
194
}
195

196
func (fastdp *FastDatapath) Bridge() Bridge {
76×
197
        return fastDatapathBridge{fastdp}
76×
198
}
76×
199

200
func (fastdp fastDatapathBridge) Interface() *net.Interface {
76×
201
        return fastdp.iface
76×
202
}
76×
203

204
func (fastdp fastDatapathBridge) String() string {
231×
205
        return fmt.Sprint(fastdp.iface.Name, " (via ODP)")
231×
206
}
231×
207

208
func (fastdp fastDatapathBridge) Stats() map[string]int {
155×
209
        lock := fastdp.startLock()
155×
210
        defer lock.unlock()
155×
211

155×
212
        return map[string]int{
155×
213
                "FlowMisses": int(fastdp.missCount),
155×
214
        }
155×
215
}
155×
216

217
var routerBridgePortID = bridgePortID{router: true}
218

219
func (fastdp fastDatapathBridge) StartConsumingPackets(consumer BridgeConsumer) error {
76×
220
        fastdp.lock.Lock()
76×
221
        defer fastdp.lock.Unlock()
76×
222

76×
223
        if fastdp.sendToPort[routerBridgePortID] != nil {
76×
224
                return fmt.Errorf("FastDatapath already has a BridgeConsumer")
!
225
        }
!
226

227
        // set up delivery to the weave router port on the bridge
228
        fastdp.addSendToPort(routerBridgePortID,
76×
229
                func(key PacketKey, lock *fastDatapathLock) FlowOp {
962×
230
                        // drop the FastDatapath lock in order to call
886×
231
                        // the consumer
886×
232
                        lock.unlock()
886×
233
                        return consumer(key)
886×
234
                })
886×
235
        return nil
76×
236
}
237

238
func (fastdp fastDatapathBridge) InjectPacket(key PacketKey) FlowOp {
592×
239
        lock := fastdp.startLock()
592×
240
        defer lock.unlock()
592×
241
        return fastdp.bridge(routerBridgePortID, key, &lock)
592×
242
}
592×
243

244
// Ethernet bridge implementation
245

246
func (fastdp *FastDatapath) bridge(ingress bridgePortID, key PacketKey, lock *fastDatapathLock) FlowOp {
1,478×
247
        lock.relock()
1,478×
248
        if fastdp.sendToMAC[key.SrcMAC] == nil {
1,857×
249
                // Learn the source MAC
379×
250
                fastdp.sendToMAC[key.SrcMAC] = fastdp.sendToPort[ingress]
379×
251
                fastdp.seenMACs[key.SrcMAC] = struct{}{}
379×
252
        }
379×
253

254
        // If we know about the destination MAC, deliver it to the
255
        // associated port.
256
        if sender := fastdp.sendToMAC[key.DstMAC]; sender != nil {
1,642×
257
                return NewMultiFlowOp(false, odpEthernetFlowKey(key), sender(key, lock))
164×
258
        }
164×
259

260
        // Otherwise, it might be a real broadcast, or it might
261
        // be for a MAC we don't know about yet.  Either way, we'll
262
        // broadcast it.
263
        mfop := NewMultiFlowOp(false)
1,314×
264

1,314×
265
        if (key.DstMAC[0] & 1) == 0 {
1,315×
266
                // Not a real broadcast, so don't create a flow rule.
1×
267
                // If we did, we'd need to delete the flows every time
1×
268
                // we learned a new MAC address, or have a more
1×
269
                // complicated selective invalidation scheme.
1×
270
                mfop.Add(vetoFlowCreationFlowOp{})
1×
271
        } else {
1,314×
272
                // A real broadcast
1,313×
273
                mfop.Add(odpEthernetFlowKey(key))
1,313×
274
        }
1,313×
275

276
        // Send to all ports except the one it came in on. The
277
        // sendToPort map is immutable, so it is safe to iterate over
278
        // it even though the sender functions can drop the
279
        // fastDatapathLock
280
        for id, sender := range fastdp.sendToPort {
5,256×
281
                if id != ingress {
6,570×
282
                        mfop.Add(sender(key, lock))
2,628×
283
                }
2,628×
284
        }
285

286
        return mfop
1,314×
287
}
288

289
// Overlay bits
290

291
type fastDatapathOverlay struct {
292
        *FastDatapath
293
}
294

295
func (fastdp *FastDatapath) Overlay() NetworkOverlay {
76×
296
        return fastDatapathOverlay{fastdp}
76×
297
}
76×
298

299
func (fastdp fastDatapathOverlay) InvalidateRoutes() {
344×
300
        log.Debug("InvalidateRoutes")
344×
301
        fastdp.lock.Lock()
344×
302
        defer fastdp.lock.Unlock()
344×
303
        checkWarn(fastdp.deleteFlows())
344×
304
}
344×
305

306
func (fastdp fastDatapathOverlay) InvalidateShortIDs() {
2×
307
        log.Debug("InvalidateShortIDs")
2×
308
        fastdp.lock.Lock()
2×
309
        defer fastdp.lock.Unlock()
2×
310
        checkWarn(fastdp.deleteFlows())
2×
311
}
2×
312

313
func (fastDatapathOverlay) AddFeaturesTo(features map[string]string) {
!
314
        // Nothing needed.  Fast datapath support is indicated through
!
315
        // OverlaySwitch.
!
316
}
!
317

318
type FlowStatus odp.FlowInfo
319

320
func (flowStatus *FlowStatus) MarshalJSON() ([]byte, error) {
3×
321
        type jsonFlowStatus struct {
3×
322
                FlowKeys []string
3×
323
                Actions  []string
3×
324
                Packets  uint64
3×
325
                Bytes    uint64
3×
326
                Used     uint64
3×
327
        }
3×
328

3×
329
        flowKeys := make([]string, 0, len(flowStatus.FlowKeys))
3×
330
        for _, flowKey := range flowStatus.FlowKeys {
27×
331
                if !flowKey.Ignored() {
30×
332
                        flowKeys = append(flowKeys, fmt.Sprint(flowKey))
6×
333
                }
6×
334
        }
335

336
        actions := make([]string, 0, len(flowStatus.Actions))
3×
337
        for _, action := range flowStatus.Actions {
6×
338
                actions = append(actions, fmt.Sprint(action))
3×
339
        }
3×
340

341
        return json.Marshal(&jsonFlowStatus{flowKeys, actions, flowStatus.Packets, flowStatus.Bytes, flowStatus.Used})
3×
342
}
343

344
type VportStatus odp.Vport
345

346
func (vport *VportStatus) MarshalJSON() ([]byte, error) {
3×
347
        type jsonVportStatus struct {
3×
348
                ID       odp.VportID
3×
349
                Name     string
3×
350
                TypeName string
3×
351
        }
3×
352

3×
353
        return json.Marshal(&jsonVportStatus{vport.ID, vport.Spec.Name(), vport.Spec.TypeName()})
3×
354
}
3×
355

356
func (fastdp fastDatapathOverlay) Diagnostics() interface{} {
155×
357
        lock := fastdp.startLock()
155×
358
        defer lock.unlock()
155×
359

155×
360
        vports, err := fastdp.dp.EnumerateVports()
155×
361
        checkWarn(err)
155×
362
        vportStatuses := make([]VportStatus, 0, len(vports))
155×
363
        for _, vport := range vports {
620×
364
                vportStatuses = append(vportStatuses, VportStatus(vport))
465×
365
        }
465×
366

367
        flows, err := fastdp.dp.EnumerateFlows()
155×
368
        checkWarn(err)
155×
369
        flowStatuses := make([]FlowStatus, 0, len(flows))
155×
370
        for _, flow := range flows {
266×
371
                flowStatuses = append(flowStatuses, FlowStatus(flow))
111×
372
        }
111×
373

374
        return struct {
155×
375
                Vports []VportStatus
155×
376
                Flows  []FlowStatus
155×
377
        }{
155×
378
                vportStatuses,
155×
379
                flowStatuses,
155×
380
        }
155×
381
}
382

383
func (fastdp fastDatapathOverlay) StartConsumingPackets(localPeer *mesh.Peer, peers *mesh.Peers, consumer OverlayConsumer) error {
76×
384
        fastdp.lock.Lock()
76×
385
        defer fastdp.lock.Unlock()
76×
386

76×
387
        if fastdp.overlayConsumer != nil {
76×
388
                return fmt.Errorf("FastDatapath already has an OverlayConsumer")
!
389
        }
!
390

391
        fastdp.localPeer = localPeer
76×
392
        fastdp.peers = peers
76×
393
        fastdp.overlayConsumer = consumer
76×
394
        return nil
76×
395
}
396

397
func (fastdp *FastDatapath) getVxlanVportIDHarder(udpPort int, retries int, duration time.Duration) (odp.VportID, error) {
76×
398
        var vxlanVportID odp.VportID
76×
399
        var err error
76×
400
        for try := 0; try < retries; try++ {
152×
401
                vxlanVportID, err = fastdp.getVxlanVportID(udpPort)
76×
402
                if err == nil || err != odp.NetlinkError(syscall.EADDRINUSE) {
152×
403
                        return vxlanVportID, err
76×
404
                }
76×
405
                log.Warning("Address already in use creating vxlan vport ", udpPort, " - retrying")
!
406
                time.Sleep(duration)
!
407
        }
408
        return 0, err
!
409
}
410

411
func (fastdp *FastDatapath) getVxlanVportID(udpPort int) (odp.VportID, error) {
112×
412
        fastdp.lock.Lock()
112×
413
        defer fastdp.lock.Unlock()
112×
414

112×
415
        if vxlanVportID, present := fastdp.vxlanVportIDs[udpPort]; present {
148×
416
                return vxlanVportID, nil
36×
417
        }
36×
418

419
        vxlanVportID, err := fastdp.dp.CreateVport(
76×
420
                odp.NewVxlanVportSpec(fmt.Sprintf("vxlan-%d", udpPort), uint16(udpPort)))
76×
421
        if err != nil {
76×
422
                return 0, err
!
423
        }
!
424

425
        fastdp.vxlanVportIDs[udpPort] = vxlanVportID
76×
426
        fastdp.missHandlers[vxlanVportID] = func(fks odp.FlowKeys, lock *fastDatapathLock) FlowOp {
744×
427
                tunnel := fks[odp.OVS_KEY_ATTR_TUNNEL].(odp.TunnelFlowKey)
668×
428
                tunKey := tunnel.Key()
668×
429

668×
430
                lock.relock()
668×
431
                consumer := fastdp.overlayConsumer
668×
432
                if consumer == nil {
668×
433
                        return vetoFlowCreationFlowOp{}
!
434
                }
!
435

436
                srcPeer, dstPeer := fastdp.extractPeers(tunKey.TunnelId)
668×
437
                if srcPeer == nil || dstPeer == nil {
681×
438
                        return vetoFlowCreationFlowOp{}
13×
439
                }
13×
440

441
                lock.unlock()
655×
442
                pk := flowKeysToPacketKey(fks)
655×
443
                var zeroMAC MAC
655×
444
                if pk.SrcMAC == zeroMAC && pk.DstMAC == zeroMAC {
784×
445
                        return vxlanSpecialPacketFlowOp{
129×
446
                                fastdp:  fastdp,
129×
447
                                srcPeer: srcPeer,
129×
448
                                sender: &net.UDPAddr{
129×
449
                                        IP:   net.IP(tunKey.Ipv4Src[:]),
129×
450
                                        Port: udpPort,
129×
451
                                },
129×
452
                        }
129×
453
                }
129×
454

455
                key := ForwardPacketKey{
526×
456
                        SrcPeer:   srcPeer,
526×
457
                        DstPeer:   dstPeer,
526×
458
                        PacketKey: pk,
526×
459
                }
526×
460

526×
461
                var tunnelFlowKey odp.TunnelFlowKey
526×
462
                tunnelFlowKey.SetTunnelId(tunKey.TunnelId)
526×
463
                tunnelFlowKey.SetIpv4Src(tunKey.Ipv4Src)
526×
464
                tunnelFlowKey.SetIpv4Dst(tunKey.Ipv4Dst)
526×
465

526×
466
                return NewMultiFlowOp(false, odpFlowKey(tunnelFlowKey), consumer(key))
526×
467
        }
468

469
        return vxlanVportID, nil
76×
470
}
471

472
func (fastdp *FastDatapath) extractPeers(tunnelID [8]byte) (*mesh.Peer, *mesh.Peer) {
668×
473
        vni := binary.BigEndian.Uint64(tunnelID[:])
668×
474
        srcPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID(vni & 0xfff))
668×
475
        dstPeer := fastdp.peers.FetchByShortID(mesh.PeerShortID((vni >> 12) & 0xfff))
668×
476
        return srcPeer, dstPeer
668×
477
}
668×
478

479
type vxlanSpecialPacketFlowOp struct {
480
        NonDiscardingFlowOp
481
        fastdp  *FastDatapath
482
        srcPeer *mesh.Peer
483
        sender  *net.UDPAddr
484
}
485

486
func (op vxlanSpecialPacketFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
129×
487
        op.fastdp.lock.Lock()
129×
488
        fwd := op.fastdp.forwarders[op.srcPeer.Name]
129×
489
        op.fastdp.lock.Unlock()
129×
490

129×
491
        if fwd != nil && dec.IsSpecial() {
258×
492
                fwd.handleVxlanSpecialPacket(frame, op.sender)
129×
493
        }
129×
494
}
495

496
type fastDatapathForwarder struct {
497
        fastdp         *FastDatapath
498
        remotePeer     *mesh.Peer
499
        localIP        [4]byte
500
        sendControlMsg func(byte, []byte) error
501
        connUID        uint64
502
        vxlanVportID   odp.VportID
503

504
        lock              sync.RWMutex
505
        confirmed         bool
506
        remoteAddr        *net.UDPAddr
507
        heartbeatInterval time.Duration
508
        heartbeatTimer    *time.Timer
509
        heartbeatTimeout  *time.Timer
510
        ackedHeartbeat    bool
511
        stopChan          chan struct{}
512
        stopped           bool
513

514
        establishedChan chan struct{}
515
        errorChan       chan error
516
}
517

518
func (fastdp fastDatapathOverlay) PrepareConnection(params mesh.OverlayConnectionParams) (mesh.OverlayConnection, error) {
69×
519
        if params.SessionKey != nil {
70×
520
                // No encryption support in fastdp
1×
521
                return nil, fmt.Errorf("encryption not supported")
1×
522
        }
1×
523

524
        vxlanVportID := fastdp.mainVxlanVportID
68×
525
        var remoteAddr *net.UDPAddr
68×
526

68×
527
        if params.Outbound {
104×
528
                remoteAddr = makeUDPAddr(params.RemoteAddr)
36×
529
                // The provided address contains the main weave port
36×
530
                // number to connect to.  We need to derive the vxlan
36×
531
                // port number from that.
36×
532
                vxlanRemoteAddr := *remoteAddr
36×
533
                vxlanRemoteAddr.Port++
36×
534
                remoteAddr = &vxlanRemoteAddr
36×
535
                var err error
36×
536
                vxlanVportID, err = fastdp.getVxlanVportID(remoteAddr.Port)
36×
537
                if err != nil {
36×
538
                        return nil, err
!
539
                }
!
540
        }
541

542
        localIP, err := ipv4Bytes(params.LocalAddr.IP)
68×
543
        if err != nil {
68×
544
                return nil, err
!
545
        }
!
546

547
        fwd := &fastDatapathForwarder{
68×
548
                fastdp:         fastdp.FastDatapath,
68×
549
                remotePeer:     params.RemotePeer,
68×
550
                localIP:        localIP,
68×
551
                sendControlMsg: params.SendControlMessage,
68×
552
                connUID:        params.ConnUID,
68×
553
                vxlanVportID:   vxlanVportID,
68×
554

68×
555
                remoteAddr:        remoteAddr,
68×
556
                heartbeatInterval: FastHeartbeat,
68×
557
                stopChan:          make(chan struct{}),
68×
558

68×
559
                establishedChan: make(chan struct{}),
68×
560
                errorChan:       make(chan error, 1),
68×
561
        }
68×
562

68×
563
        return fwd, err
68×
564
}
565

566
func ipv4Bytes(ip net.IP) (res [4]byte, err error) {
793×
567
        ipv4 := ip.To4()
793×
568
        if ipv4 != nil {
1,586×
569
                copy(res[:], ipv4)
793×
570
        } else {
793×
571
                err = fmt.Errorf("IP address %s is not IPv4", ip)
!
572
        }
!
573
        return
793×
574
}
575

576
func (fwd *fastDatapathForwarder) logPrefix() string {
457×
577
        return fmt.Sprintf("fastdp ->[%s|%s]: ", fwd.remoteAddr, fwd.remotePeer)
457×
578
}
457×
579

580
func (fwd *fastDatapathForwarder) Confirm() {
63×
581
        fwd.lock.Lock()
63×
582
        defer fwd.lock.Unlock()
63×
583

63×
584
        if fwd.confirmed {
63×
585
                log.Fatal(fwd.logPrefix(), "already confirmed")
!
586
        }
!
587

588
        log.Debug(fwd.logPrefix(), "confirmed")
63×
589
        fwd.fastdp.addForwarder(fwd.remotePeer.Name, fwd)
63×
590
        fwd.confirmed = true
63×
591

63×
592
        if fwd.remoteAddr != nil {
96×
593
                // have the goroutine send a heartbeat straight away
33×
594
                fwd.heartbeatTimer = time.NewTimer(0)
33×
595
        } else {
63×
596
                // we'll reset the timer when we learn the remote ip
30×
597
                fwd.heartbeatTimer = time.NewTimer(MaxDuration)
30×
598
        }
30×
599

600
        fwd.heartbeatTimeout = time.NewTimer(HeartbeatTimeout)
63×
601
        go fwd.doHeartbeats()
63×
602
}
603

604
func (fwd *fastDatapathForwarder) EstablishedChannel() <-chan struct{} {
68×
605
        return fwd.establishedChan
68×
606
}
68×
607

608
func (fwd *fastDatapathForwarder) ErrorChannel() <-chan error {
130×
609
        return fwd.errorChan
130×
610
}
130×
611

612
func (fwd *fastDatapathForwarder) doHeartbeats() {
63×
613
        var err error
63×
614

63×
615
        for err == nil {
329×
616
                select {
266×
617
                case <-fwd.heartbeatTimer.C:
203×
618
                        if fwd.confirmed {
406×
619
                                fwd.sendHeartbeat()
203×
620
                        }
203×
621
                        fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
203×
622

623
                case <-fwd.heartbeatTimeout.C:
!
624
                        err = fmt.Errorf("timed out waiting for vxlan heartbeat")
!
625

626
                case <-fwd.stopChan:
33×
627
                        return
33×
628
                }
629
        }
630

631
        fwd.lock.Lock()
!
632
        defer fwd.lock.Unlock()
!
633
        fwd.handleError(err)
!
634
}
635

636
// Handle an error which leads to notifying the listener and
637
// termination of the forwarder
638
func (fwd *fastDatapathForwarder) handleError(err error) {
62×
639
        if err == nil {
124×
640
                return
62×
641
        }
62×
642

UNCOV
643
        select {
!
UNCOV
644
        case fwd.errorChan <- err:
!
645
        default:
!
646
        }
647

648
        // stop the heartbeat goroutine
UNCOV
649
        if !fwd.stopped {
!
UNCOV
650
                fwd.stopped = true
!
UNCOV
651
                close(fwd.stopChan)
!
UNCOV
652
        }
!
653
}
654

655
func (fwd *fastDatapathForwarder) sendHeartbeat() {
203×
656
        fwd.lock.RLock()
203×
657
        log.Debug(fwd.logPrefix(), "sendHeartbeat")
203×
658

203×
659
        // the heartbeat payload consists of the 64-bit connection uid
203×
660
        // followed by the 16-bit packet size.
203×
661
        buf := make([]byte, EthernetOverhead+fwd.fastdp.iface.MTU)
203×
662
        binary.BigEndian.PutUint64(buf[EthernetOverhead:], fwd.connUID)
203×
663
        binary.BigEndian.PutUint16(buf[EthernetOverhead+8:], uint16(len(buf)))
203×
664

203×
665
        dec := NewEthernetDecoder()
203×
666
        dec.DecodeLayers(buf)
203×
667
        pk := ForwardPacketKey{
203×
668
                PacketKey: dec.PacketKey(),
203×
669
                SrcPeer:   fwd.fastdp.localPeer,
203×
670
                DstPeer:   fwd.remotePeer,
203×
671
        }
203×
672
        fwd.lock.RUnlock()
203×
673

203×
674
        if fop := fwd.Forward(pk); fop != nil {
406×
675
                fop.Process(buf, dec, false)
203×
676
        }
203×
677
}
678

679
const (
680
        FastDatapathHeartbeatAck = iota
681
)
682

683
func (fwd *fastDatapathForwarder) handleVxlanSpecialPacket(frame []byte, sender *net.UDPAddr) {
129×
684
        fwd.lock.Lock()
129×
685
        defer fwd.lock.Unlock()
129×
686

129×
687
        log.Debug(fwd.logPrefix(), "handleVxlanSpecialPacket")
129×
688

129×
689
        // the only special packet type is a heartbeat
129×
690
        if len(frame) < EthernetOverhead+10 {
129×
691
                log.Warning(fwd.logPrefix(), "short vxlan special packet: ", len(frame), " bytes")
!
692
                return
!
693
        }
!
694

695
        if binary.BigEndian.Uint64(frame[EthernetOverhead:]) != fwd.connUID ||
129×
696
                uint16(len(frame)) != binary.BigEndian.Uint16(frame[EthernetOverhead+8:]) {
129×
UNCOV
697
                return
!
UNCOV
698
        }
!
699

700
        if fwd.remoteAddr == nil {
158×
701
                fwd.remoteAddr = sender
29×
702

29×
703
                if fwd.confirmed {
58×
704
                        fwd.heartbeatTimer.Reset(0)
29×
705
                }
29×
706
        } else if !udpAddrsEqual(fwd.remoteAddr, sender) {
100×
707
                log.Info(fwd.logPrefix(), "Peer IP address changed to ", sender)
!
708
                fwd.remoteAddr = sender
!
709
        }
!
710

711
        if !fwd.ackedHeartbeat {
191×
712
                fwd.ackedHeartbeat = true
62×
713
                fwd.handleError(fwd.sendControlMsg(FastDatapathHeartbeatAck, nil))
62×
714
        }
62×
715

716
        // we can receive a heartbeat before Confirm() has set up
717
        // heartbeatTimeout
718
        if fwd.heartbeatTimeout != nil {
258×
719
                fwd.heartbeatTimeout.Reset(HeartbeatTimeout)
129×
720
        }
129×
721
}
722

723
func (fwd *fastDatapathForwarder) ControlMessage(tag byte, msg []byte) {
62×
724
        fwd.lock.Lock()
62×
725
        defer fwd.lock.Unlock()
62×
726

62×
727
        switch tag {
62×
728
        case FastDatapathHeartbeatAck:
62×
729
                fwd.handleHeartbeatAck()
62×
730

731
        default:
!
732
                log.Info(fwd.logPrefix(), "Ignoring unknown control message: ", tag)
!
733
        }
734
}
735

736
func (fwd *fastDatapathForwarder) DisplayName() string {
55×
737
        return "fastdp"
55×
738
}
55×
739

740
func (fwd *fastDatapathForwarder) handleHeartbeatAck() {
62×
741
        log.Debug(fwd.logPrefix(), "handleHeartbeatAck")
62×
742

62×
743
        if fwd.heartbeatInterval != SlowHeartbeat {
124×
744
                close(fwd.establishedChan)
62×
745
                fwd.heartbeatInterval = SlowHeartbeat
62×
746
                if fwd.heartbeatTimer != nil {
124×
747
                        fwd.heartbeatTimer.Reset(fwd.heartbeatInterval)
62×
748
                }
62×
749
        }
750
}
751

752
func (fwd *fastDatapathForwarder) Forward(key ForwardPacketKey) FlowOp {
725×
753
        if !key.SrcPeer.HasShortID || !key.DstPeer.HasShortID {
725×
754
                return nil
!
755
        }
!
756

757
        fwd.lock.RLock()
725×
758
        defer fwd.lock.RUnlock()
725×
759

725×
760
        if fwd.remoteAddr == nil {
725×
761
                // Returning a DiscardingFlowOp would discard the
!
762
                // packet, but also result in a flow rule, which we
!
763
                // would have to invalidate when we learn the remote
!
764
                // IP.  So for now, just prevent flows.
!
765
                return vetoFlowCreationFlowOp{}
!
766
        }
!
767

768
        remoteIP, err := ipv4Bytes(fwd.remoteAddr.IP)
725×
769
        if err != nil {
725×
770
                log.Error(err)
!
771
                return DiscardingFlowOp{}
!
772
        }
!
773

774
        var sta odp.SetTunnelAction
725×
775
        sta.SetTunnelId(tunnelIDFor(key))
725×
776
        sta.SetIpv4Src(fwd.localIP)
725×
777
        sta.SetIpv4Dst(remoteIP)
725×
778
        sta.SetTos(0)
725×
779
        sta.SetTtl(64)
725×
780
        sta.SetDf(true)
725×
781
        sta.SetCsum(false)
725×
782
        return fwd.fastdp.odpActions(sta, odp.NewOutputAction(fwd.vxlanVportID))
725×
783
}
784

785
func tunnelIDFor(key ForwardPacketKey) (tunnelID [8]byte) {
725×
786
        src := uint64(key.SrcPeer.ShortID)
725×
787
        dst := uint64(key.DstPeer.ShortID)
725×
788
        binary.BigEndian.PutUint64(tunnelID[:], src|dst<<12)
725×
789
        return
725×
790
}
725×
791

792
func (fwd *fastDatapathForwarder) Stop() {
38×
793
        // Might be nice to delete all the relevant flows here, but we
38×
794
        // can just let them expire.
38×
795
        fwd.fastdp.removeForwarder(fwd.remotePeer.Name, fwd)
38×
796

38×
797
        fwd.lock.Lock()
38×
798
        defer fwd.lock.Unlock()
38×
799
        fwd.sendControlMsg = func(byte, []byte) error { return nil }
38×
800

801
        // stop the heartbeat goroutine
802
        if !fwd.stopped {
76×
803
                fwd.stopped = true
38×
804
                close(fwd.stopChan)
38×
805
        }
38×
806
}
807

808
func (fastdp *FastDatapath) addForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
63×
809
        fastdp.lock.Lock()
63×
810
        defer fastdp.lock.Unlock()
63×
811

63×
812
        // We shouldn't have two confirmed forwarders to the same
63×
813
        // remotePeer, due to the checks in LocalPeer AddConnection.
63×
814
        fastdp.forwarders[peer] = fwd
63×
815
}
63×
816

817
func (fastdp *FastDatapath) removeForwarder(peer mesh.PeerName, fwd *fastDatapathForwarder) {
38×
818
        fastdp.lock.Lock()
38×
819
        defer fastdp.lock.Unlock()
38×
820
        if fastdp.forwarders[peer] == fwd {
70×
821
                delete(fastdp.forwarders, peer)
32×
822
        }
32×
823
}
824

825
func (fastdp *FastDatapath) deleteFlows() error {
574×
826
        fastdp.deleteFlowsCount++
574×
827

574×
828
        flows, err := fastdp.dp.EnumerateFlows()
574×
829
        if err != nil {
574×
830
                return err
!
831
        }
!
832

833
        for _, flow := range flows {
1,190×
834
                err = fastdp.dp.DeleteFlow(flow.FlowKeys)
616×
835
                if err != nil && !odp.IsNoSuchFlowError(err) {
616×
836
                        return err
!
837
                }
!
838
        }
839

840
        return nil
574×
841
}
842

843
func (fastdp *FastDatapath) deleteVxlanVports() error {
76×
844
        vports, err := fastdp.dp.EnumerateVports()
76×
845
        if err != nil {
76×
846
                return err
!
847
        }
!
848

849
        for _, vport := range vports {
243×
850
                if vport.Spec.TypeName() != "vxlan" {
319×
851
                        continue
152×
852
                }
853

854
                err = fastdp.dp.DeleteVport(vport.ID)
15×
855
                if err != nil && !odp.IsNoSuchVportError(err) {
15×
856
                        return err
!
857
                }
!
858
        }
859

860
        return nil
76×
861
}
862

863
func (fastdp *FastDatapath) run() {
76×
864
        expireMACsCh := time.Tick(10 * time.Minute)
76×
865
        expireFlowsCh := time.Tick(5 * time.Minute)
76×
866

76×
867
        for {
152×
868
                select {
76×
869
                case <-expireMACsCh:
!
870
                        fastdp.expireMACs()
!
871

872
                case <-expireFlowsCh:
!
873
                        fastdp.expireFlows()
!
874
                }
875
        }
876
}
877

878
func (fastdp *FastDatapath) expireMACs() {
!
879
        lock := fastdp.startLock()
!
880
        defer lock.unlock()
!
881

!
882
        for mac := range fastdp.sendToMAC {
!
883
                if _, present := fastdp.seenMACs[mac]; !present {
!
884
                        delete(fastdp.sendToMAC, mac)
!
885
                }
!
886
        }
887

888
        fastdp.seenMACs = make(map[MAC]struct{})
!
889
}
890

891
func (fastdp *FastDatapath) expireFlows() {
!
892
        lock := fastdp.startLock()
!
893
        defer lock.unlock()
!
894

!
895
        flows, err := fastdp.dp.EnumerateFlows()
!
896
        checkWarn(err)
!
897

!
898
        for _, flow := range flows {
!
899
                if flow.Used == 0 {
!
900
                        log.Debug("Expiring flow ", flow.FlowSpec)
!
901
                        err = fastdp.dp.DeleteFlow(flow.FlowKeys)
!
902
                } else {
!
903
                        fastdp.touchFlow(flow.FlowKeys, &lock)
!
904
                        err = fastdp.dp.ClearFlow(flow.FlowSpec)
!
905
                }
!
906

907
                if err != nil && !odp.IsNoSuchFlowError(err) {
!
908
                        log.Warn(err)
!
909
                }
!
910
        }
911
}
912

913
// The router needs to know which flows are active in order to
914
// maintain its MAC->peer table.  We do this by querying the router
915
// without an actual packet being involved.  Maybe it's
916
// worth devising a more unified approach in the future.
917
func (fastdp *FastDatapath) touchFlow(fks odp.FlowKeys, lock *fastDatapathLock) {
!
918
        // All the flows we create should have an ingress key, but we
!
919
        // check here just in case we encounter one from somewhere
!
920
        // else.
!
921
        ingressKey, present := fks[odp.OVS_KEY_ATTR_IN_PORT]
!
922
        if present {
!
923
                ingress := ingressKey.(odp.InPortFlowKey).VportID()
!
924
                handler := fastdp.getMissHandler(ingress)
!
925
                if handler != nil {
!
926
                        handler(fks, lock)
!
927
                        lock.relock()
!
928
                }
!
929
        }
930
}
931

932
func (fastdp *FastDatapath) Error(err error, stopped bool) {
!
933
        if stopped {
!
934
                log.Fatal("Error while listeniing on ODP datapath: ", err)
!
935
        }
!
936

937
        log.Error("Error while listening on ODP datapath: ", err)
!
938
}
939

940
func (fastdp *FastDatapath) Miss(packet []byte, fks odp.FlowKeys) error {
1,554×
941
        ingress := fks[odp.OVS_KEY_ATTR_IN_PORT].(odp.InPortFlowKey).VportID()
1,554×
942
        log.Debug("ODP miss ", fks, " on port ", ingress)
1,554×
943

1,554×
944
        lock := fastdp.startLock()
1,554×
945
        defer lock.unlock()
1,554×
946

1,554×
947
        fastdp.missCount++
1,554×
948

1,554×
949
        handler := fastdp.getMissHandler(ingress)
1,554×
950
        if handler == nil {
1,554×
951
                return nil
!
952
        }
!
953

954
        // Always include the ingress vport in the flow key.  While
955
        // this is not strictly necessary in some cases (e.g. for
956
        // delivery to a local netdev based on the dest MAC),
957
        // including the ingress in every flow makes things simpler
958
        // in touchFlow.
959
        mfop := NewMultiFlowOp(false, handler(fks, &lock), odpFlowKey(odp.NewInPortFlowKey(ingress)))
1,554×
960
        fastdp.send(mfop, packet, &lock)
1,554×
961
        return nil
1,554×
962
}
963

964
func (fastdp *FastDatapath) getMissHandler(ingress odp.VportID) missHandler {
1,554×
965
        handler := fastdp.missHandlers[ingress]
1,554×
966
        if handler == nil {
1,554×
967
                vport, err := fastdp.dp.LookupVport(ingress)
!
968
                if err != nil {
!
969
                        log.Error(err)
!
970
                        return nil
!
971
                }
!
972

973
                fastdp.makeBridgeVport(vport)
!
974
        }
975

976
        return handler
1,554×
977
}
978

979
func (fastdp *FastDatapath) VportCreated(dpid odp.DatapathID, vport odp.Vport) error {
!
980
        fastdp.lock.Lock()
!
981
        defer fastdp.lock.Unlock()
!
982

!
983
        if _, present := fastdp.missHandlers[vport.ID]; !present {
!
984
                fastdp.makeBridgeVport(vport)
!
985
        }
!
986

987
        return nil
!
988
}
989

990
func (fastdp *FastDatapath) VportDeleted(dpid odp.DatapathID, vport odp.Vport) error {
!
991
        fastdp.lock.Lock()
!
992
        defer fastdp.lock.Unlock()
!
993

!
994
        // there might be flow rules that still refer to the id of
!
995
        // this vport.  But we just allow them to expire.  Unless we
!
996
        // want prompt migration of MAC addresses, that should be
!
997
        // fine.
!
998
        delete(fastdp.missHandlers, vport.ID)
!
999
        fastdp.deleteSendToPort(bridgePortID{vport: vport.ID})
!
1000
        return nil
!
1001
}
!
1002

1003
func (fastdp *FastDatapath) makeBridgeVport(vport odp.Vport) {
228×
1004
        // Set up a bridge port for netdev and internal vports.  vxlan
228×
1005
        // vports are handled separately, as they do not correspond to
228×
1006
        // bridge ports (we set up the miss handler for them in
228×
1007
        // getVxlanVportID).
228×
1008
        typ := vport.Spec.TypeName()
228×
1009
        if typ != "netdev" && typ != "internal" {
304×
1010
                return
76×
1011
        }
76×
1012

1013
        vportID := vport.ID
152×
1014

152×
1015
        // Sending to the bridge port outputs on the vport:
152×
1016
        fastdp.addSendToPort(bridgePortID{vport: vportID},
152×
1017
                func(_ PacketKey, _ *fastDatapathLock) FlowOp {
2,058×
1018
                        return fastdp.odpActions(odp.NewOutputAction(vportID))
1,906×
1019
                })
1,906×
1020

1021
        // Delete flows, in order to recalculate flows for broadcasts
1022
        // on the bridge.
1023
        checkWarn(fastdp.deleteFlows())
152×
1024

152×
1025
        // Packets coming from the netdev are processed by the bridge
152×
1026
        fastdp.missHandlers[vportID] = func(flowKeys odp.FlowKeys, lock *fastDatapathLock) FlowOp {
1,038×
1027
                return fastdp.bridge(bridgePortID{vport: vportID}, flowKeysToPacketKey(flowKeys), lock)
886×
1028
        }
886×
1029
}
1030

1031
func flowKeysToPacketKey(fks odp.FlowKeys) PacketKey {
1,541×
1032
        eth := fks[odp.OVS_KEY_ATTR_ETHERNET].(odp.EthernetFlowKey).Key()
1,541×
1033
        return PacketKey{SrcMAC: eth.EthSrc, DstMAC: eth.EthDst}
1,541×
1034
}
1,541×
1035

1036
// The sendToPort map is read-only, so this method does the copy in
1037
// order to add an entry.
1038
func (fastdp *FastDatapath) addSendToPort(portID bridgePortID, sender bridgeSender) {
228×
1039
        sendToPort := map[bridgePortID]bridgeSender{portID: sender}
228×
1040
        for id, sender := range fastdp.sendToPort {
456×
1041
                sendToPort[id] = sender
228×
1042
        }
228×
1043
        fastdp.sendToPort = sendToPort
228×
1044
}
1045

1046
func (fastdp *FastDatapath) deleteSendToPort(portID bridgePortID) {
!
1047
        sendToPort := make(map[bridgePortID]bridgeSender)
!
1048
        for id, sender := range fastdp.sendToPort {
!
1049
                if id != portID {
!
1050
                        sendToPort[id] = sender
!
1051
                }
!
1052
        }
1053
        fastdp.sendToPort = sendToPort
!
1054
}
1055

1056
// Send a packet, creating a corresponding ODP flow rule if possible
1057
func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, lock *fastDatapathLock) {
1,554×
1058
        // Gather the actions from actionFlowOps, execute any others
1,554×
1059
        var dec *EthernetDecoder
1,554×
1060
        flow := odp.NewFlowSpec()
1,554×
1061
        createFlow := true
1,554×
1062

1,554×
1063
        for _, xfop := range FlattenFlowOp(fops) {
8,562×
1064
                switch fop := xfop.(type) {
7,008×
1065
                case interface {
5,826×
1066
                        updateFlowSpec(*odp.FlowSpec)
5,826×
1067
                }:
5,826×
1068
                        fop.updateFlowSpec(&flow)
5,826×
1069
                case vetoFlowCreationFlowOp:
14×
1070
                        createFlow = false
14×
1071
                default:
1,168×
1072
                        if xfop.Discards() {
2,160×
1073
                                continue
992×
1074
                        }
1075

1076
                        // A foreign FlowOp (e.g. a sleeve forwarding
1077
                        // FlowOp), so send the packet through the
1078
                        // FlowOp interface, decoding the packet
1079
                        // lazily.
1080
                        if dec == nil {
352×
1081
                                dec = fastdp.takeDecoder(lock)
176×
1082
                                dec.DecodeLayers(frame)
176×
1083

176×
1084
                                // If we are sending the packet
176×
1085
                                // through the FlowOp interface, we
176×
1086
                                // mustn't create a flow, as that
176×
1087
                                // could prevent the proper handling
176×
1088
                                // of similar packets in the future.
176×
1089
                                createFlow = false
176×
1090
                        }
176×
1091

1092
                        if len(dec.decoded) != 0 {
352×
1093
                                lock.unlock()
176×
1094
                                fop.Process(frame, dec, false)
176×
1095
                        }
176×
1096
                }
1097
        }
1098

1099
        if dec != nil {
1,730×
1100
                // put the decoder back
176×
1101
                lock.relock()
176×
1102
                fastdp.dec = dec
176×
1103
        }
176×
1104

1105
        if len(flow.Actions) != 0 {
2,929×
1106
                lock.relock()
1,375×
1107
                checkWarn(fastdp.dp.Execute(frame, nil, flow.Actions))
1,375×
1108
        }
1,375×
1109

1110
        if createFlow {
2,918×
1111
                lock.relock()
1,364×
1112
                // if the fastdp's deleteFlowsCount changed since we
1,364×
1113
                // initially locked it, then we might have created a
1,364×
1114
                // flow on the basis of stale information.  It's fine
1,364×
1115
                // to handle one packet like that, but it would be bad
1,364×
1116
                // to introduce a stale flow.
1,364×
1117
                if lock.deleteFlowsCount == fastdp.deleteFlowsCount {
2,728×
1118
                        log.Debug("Creating ODP flow ", flow)
1,364×
1119
                        checkWarn(fastdp.dp.CreateFlow(flow))
1,364×
1120
                }
1,364×
1121
        }
1122
}
1123

1124
// Get the EthernetDecoder from the singleton pool
1125
func (fastdp *FastDatapath) takeDecoder(lock *fastDatapathLock) *EthernetDecoder {
176×
1126
        lock.relock()
176×
1127
        dec := fastdp.dec
176×
1128
        if dec == nil {
218×
1129
                dec = NewEthernetDecoder()
42×
1130
        } else {
176×
1131
                fastdp.dec = nil
134×
1132
        }
134×
1133
        return dec
176×
1134
}
1135

1136
type odpActionsFlowOp struct {
1137
        NonDiscardingFlowOp
1138
        fastdp  *FastDatapath
1139
        actions []odp.Action
1140
}
1141

1142
func (fastdp *FastDatapath) odpActions(actions ...odp.Action) FlowOp {
2,631×
1143
        return odpActionsFlowOp{
2,631×
1144
                fastdp:  fastdp,
2,631×
1145
                actions: actions,
2,631×
1146
        }
2,631×
1147
}
2,631×
1148

1149
func (fop odpActionsFlowOp) updateFlowSpec(flow *odp.FlowSpec) {
2,343×
1150
        flow.AddActions(fop.actions)
2,343×
1151
}
2,343×
1152

1153
func (fop odpActionsFlowOp) Process(frame []byte, dec *EthernetDecoder, broadcast bool) {
288×
1154
        fastdp := fop.fastdp
288×
1155
        fastdp.lock.Lock()
288×
1156
        defer fastdp.lock.Unlock()
288×
1157
        checkWarn(fastdp.dp.Execute(frame, nil, fop.actions))
288×
1158
}
288×
1159

1160
// A vetoFlowCreationFlowOp flags that no flow should be created
1161
type vetoFlowCreationFlowOp struct {
1162
        DiscardingFlowOp
1163
}
1164

1165
// A odpFlowKeyFlowOp adds a FlowKey to the resulting flow
1166
type odpFlowKeyFlowOp struct {
1167
        DiscardingFlowOp
1168
        key odp.FlowKey
1169
}
1170

1171
func odpFlowKey(key odp.FlowKey) FlowOp {
2,080×
1172
        return odpFlowKeyFlowOp{key: key}
2,080×
1173
}
2,080×
1174

1175
func (fop odpFlowKeyFlowOp) updateFlowSpec(flow *odp.FlowSpec) {
3,483×
1176
        flow.AddKey(fop.key)
3,483×
1177
}
3,483×
1178

1179
func odpEthernetFlowKey(key PacketKey) FlowOp {
1,477×
1180
        fk := odp.NewEthernetFlowKey()
1,477×
1181
        fk.SetEthSrc(key.SrcMAC)
1,477×
1182
        fk.SetEthDst(key.DstMAC)
1,477×
1183
        return odpFlowKeyFlowOp{key: fk}
1,477×
1184
}
1,477×
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc