• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openconfig / gribigo / 11421736951

29 Aug 2024 11:16PM UTC coverage: 73.682% (-0.02%) from 73.697%
11421736951

push

github

web-flow
Re-anchor comments in server/server.go. (#243)

* Re-anchor comments in server/server.go.

* Fix staticcheck errors regarding Print vs. Printf.

* Add `RequiresIPv6` to tests per #244.

1 of 1 new or added line in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

6389 of 8671 relevant lines covered (73.68%)

0.79 hits per line

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

82.9
/rib/rib.go
1
// Copyright 2021 Google LLC
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 rib implements a basic RIB for a gRIBI server.
16
package rib
17

18
import (
19
        "bytes"
20
        "errors"
21
        "fmt"
22
        "reflect"
23
        "sort"
24
        "sync"
25
        "time"
26

27
        log "github.com/golang/glog"
28
        "github.com/openconfig/gnmi/value"
29
        "github.com/openconfig/goyang/pkg/yang"
30
        "github.com/openconfig/gribigo/aft"
31
        "github.com/openconfig/gribigo/constants"
32
        "github.com/openconfig/ygot/protomap"
33
        "github.com/openconfig/ygot/ygot"
34
        "github.com/openconfig/ygot/ytypes"
35
        "google.golang.org/grpc/codes"
36
        "google.golang.org/grpc/status"
37
        "google.golang.org/protobuf/encoding/prototext"
38
        "google.golang.org/protobuf/proto"
39

40
        gpb "github.com/openconfig/gnmi/proto/gnmi"
41
        aftpb "github.com/openconfig/gribi/v1/proto/gribi_aft"
42
        spb "github.com/openconfig/gribi/v1/proto/service"
43
        wpb "github.com/openconfig/ygot/proto/ywrapper"
44
)
45

46
// Schema for the AFT model used by the RIB -- we set a global to ensure that
47
// we only deserialise it once.
48
var aftSchema *yang.Entry
49

50
func init() {
1✔
51
        // Unmarshal the YANG schema on startup.
1✔
52
        r, err := aft.Schema()
1✔
53
        if err != nil {
1✔
54
                log.Exitf("cannot unmarshal YANG schema, %v", err)
×
55
        }
×
56
        aftSchema = r.RootSchema()
1✔
57
}
58

59
// unixTS is used to determine the current unix timestamp in nanoseconds since the
60
// epoch. It is defined such that it can be overloaded by unit tests.
61
var unixTS = time.Now().UnixNano
62

63
// RIBHookFn is a function that is used as a hook following a change. It takes:
64
//   - an OpType determining whether an add, remove, or modify operation was sent.
65
//   - the timestamp in nanoseconds since the unix epoch that a function was performed.
66
//   - a string indicating the name of the network instance
67
//   - a ygot.ValidatedGoStruct containing the entry that has been changed.
68
type RIBHookFn func(constants.OpType, int64, string, ygot.ValidatedGoStruct)
69

70
// ResolvedEntryFn is a function that is called for all entries that can be fully
71
// resolved within the RIB. Fully resolved in this case is defined as an input
72
// packet match criteria set of next-hops.
73
//
74
// It takes arguments of:
75
//   - the set of RIBs that were stored in the RIB as a map keyed by the name of
76
//     a network instance, with a RIB represented as a ygot-generated AFT struct.
77
//   - the prefix that was impacted.
78
//   - the OpType that the entry was subject to (add/replace/delete).
79
//   - a string indicating the network instance that the operation was within
80
//   - an enumerated value indicating the AFT the operation was within.
81
//   - an any that indicates the impacted AFT entry's key. The function must cast
82
//     the any to the relevant type.
83
//   - a set of details that the handler function may utilise.
84
type ResolvedEntryFn func(ribs map[string]*aft.RIB, optype constants.OpType, netinst string, aft constants.AFT, key any, dets ...ResolvedDetails)
85

86
// ResolvedDetails is an interface implemented by any type that is returned as
87
// part of the AFT details.
88
type ResolvedDetails interface {
89
        isResolvedDetail()
90
}
91

92
// RIBHolderCheckFunc is a function that is used as a check to determine whether
93
// a RIB entry is eligible for a particular operation. It takes arguments of:
94
//
95
//   - the operation type that is being performed.
96
//
97
//   - the network instance within which the operation should be considered.
98
//
99
//   - the RIB that describes the candidate changes. In the case that the operation
100
//     is an ADD or REPLACE the candidate must contain the entry that would be added
101
//     or replaced. In the case that it is a DELETE, the candidate contains the entry
102
//     that is to be deleted.
103
//
104
//     The candidate contains a single entry.
105
//
106
// The check function must return:
107
//   - a bool indicating whether the RIB operation should go ahead (true = proceed).
108
//   - an error that is considered fatal for the entry (i.e., this entry should never
109
//     be tried again).
110
type RIBHolderCheckFunc func(constants.OpType, string, *aft.RIB) (bool, error)
111

112
// RIB is a struct that stores a representation of a RIB for a network device.
113
type RIB struct {
114
        // nrMu protects the niRIB map.
115
        nrMu sync.RWMutex
116
        // niRIB is a map of OpenConfig AFTs that are used to represent the RIBs of a network element.
117
        // The key of the map is the name of the network instance to which the RIBs belong.
118
        niRIB map[string]*RIBHolder
119

120
        // defaultName is the name assigned to the default network instance.
121
        defaultName string
122
        // ribCheck indicates whether this RIB is running the RIB check function.
123
        ribCheck bool
124
        // disableForwardReferences indicates whether this RIB and the VRF RIBs it
125
        // contains allow for entries that are pending since they have unsatifisied dependencies.
126
        disableForwardReferences bool
127

128
        // pendMu protects the pendingEntries map.
129
        pendMu sync.RWMutex
130
        // pendingEntries is the set of entries that have been requested by
131
        // the AddXXX methods that cannot yet be installed in the RIB because they do
132
        // not resolve. Resolve is defined as canResolve returning true - which means that:
133
        //  - entries (ipv4, ipv6 etc.) reference valid NHGs
134
        //  - NHGs reference valid NHs
135
        //  - NHs are accepted by default (since they can be resolved with other protocols)
136
        //
137
        // After every successful AddXXX operation the list of candidates is walked to
138
        // determine whether they are now resolvable.
139
        //
140
        // The candidates are stored as the operation that was submitted in order that the
141
        // same AddXXX methods can be used along with network instance the operation
142
        // referred to. The map is keyed by the operation ID.
143
        pendingEntries map[uint64]*pendingEntry
144

145
        // resolvedEntryHook is a function that is called for all entries that
146
        // can be fully resolved in the RIB. In the current implementation it
147
        // is called only for IPv4 entries.
148
        resolvedEntryHook ResolvedEntryFn
149
}
150

151
// RIBHolder is a container for a set of RIBs.
152
type RIBHolder struct {
153
        // name is the name that is used for this network instance by the system.
154
        name string
155

156
        // mu protects the aft.RIB datastructure. This is a coarse lock, but is
157
        // the simplest implementation -- we can create a more fine-grained lock
158
        // if performance requires it.
159
        mu sync.RWMutex
160
        // r is the RIB within the network instance as the OpenConfig AFT model.
161
        r *aft.RIB
162

163
        // TODO(robjs): flag as to whether we should run any semantic validations
164
        // as we add to the RIB. We probably want to allow invalid entries to be
165
        // implemented.
166

167
        // checkFn is a function that is called for all entries before they are
168
        // considered valid candidates to have the operation op performed for them.
169
        //  It can be used to check that an entry is resolvable, or whether an
170
        // entry is referenced before deleting it.
171
        // The argument handed to it is a candidate RIB as described by an aft.RIB
172
        // structure. It returns a boolean indicating
173
        // whether the entry should be installed, or an error indicating that the
174
        // entry is not valid for installation.
175
        //
176
        // When checkFn returns false, but no error is returned, it is expected
177
        // that the client of the RIB can retry to process this entry at a later
178
        // point in time. If an error is returned, the checkFn is asserting that
179
        // there is no way that this entry can ever be installed in the RIB,
180
        // regardless of whether there are state changes.
181
        checkFn func(op constants.OpType, a *aft.RIB) (bool, error)
182

183
        // postChangeHook is a function that is called after each of the operations
184
        // within the RIB completes, it takes arguments of the
185
        //   - name of the network instance
186
        //          - operation type (as an constants.OpType enumerated value)
187
        //         - the changed entry as a ygot.ValidatedGoStruct.
188
        postChangeHook RIBHookFn
189

190
        // refCounts is used to store counters for the number of references to next-hop
191
        // groups and next-hops within the RIB. It is used to ensure that referenced NHs
192
        // and NHGs cannot be removed from the RIB.
193
        refCounts *niRefCounter
194

195
        // disableForwardRef indicates that this RIB should not allow
196
        // references to entities that do not yet exist.
197
        disableForwardRef bool
198
}
199

200
// niRefCounter stores reference counters for a particular network instance.
201
type niRefCounter struct {
202
        // mu protects the contents of niRefCounter
203
        mu sync.RWMutex
204
        // NextHop is the reference counter for NextHops - and is keyed by the
205
        // index of the next-hop within the network instance.
206
        NextHop map[uint64]uint64
207
        // NextHopGroup is the referenced counter for NextHopGroups within a
208
        // network instance, and is keyed by the ID of the next-hop-group
209
        // within the network instance.
210
        NextHopGroup map[uint64]uint64
211
}
212

213
// String returns a string representation of the RIBHolder.
214
func (r *RIBHolder) String() string {
×
215
        r.mu.RLock()
×
216
        defer r.mu.RUnlock()
×
217
        js, err := ygot.Marshal7951(r.r, ygot.JSONIndent("  "))
×
218
        if err != nil {
×
219
                return "invalid RIB"
×
220
        }
×
221
        return string(js)
×
222
}
223

224
// RIBOpt is an interface that is implemented for options to the RIB.
225
type RIBOpt interface {
226
        isRIBOpt()
227
}
228

229
// DisableRIBCheckFn specifies that the consistency checking functions should
230
// be disabled for the RIB. It is useful for a testing RIB that does not need
231
// to have working references.
232
func DisableRIBCheckFn() *disableCheckFn { return &disableCheckFn{} }
1✔
233

234
// disableCheckFn is the internal implementation of DisableRIBCheckFn.
235
type disableCheckFn struct{}
236

237
// isRIBOpt implements the RIBOpt interface.
238
func (*disableCheckFn) isRIBOpt() {}
×
239

240
// hasDisableCheckFn checks whether the RIBOpt slice supplied contains the
241
// disableCheckFn option.
242
func hasDisableCheckFn(opt []RIBOpt) bool {
1✔
243
        for _, o := range opt {
2✔
244
                if _, ok := o.(*disableCheckFn); ok {
2✔
245
                        return true
1✔
246
                }
1✔
247
        }
248
        return false
1✔
249
}
250

251
// DisableForwardReferences specifies that forward references should be
252
// disallowed in this RIB -- for example, an NHG cannot reference a NH
253
// that does not exist.
254
//
255
// If set for a RIB then it is applied across all network instances within the
256
// RIB. If set for a RIB holder, it is set only for that network instance.
257
func DisableForwardReferences() *disableForwardRef { return &disableForwardRef{} }
1✔
258

259
// disableForwardRef is the internal implementation of DisableForwardReferences.
260
type disableForwardRef struct{}
261

262
// isRIBOpt implements the RIBOpt interface.
263
func (*disableForwardRef) isRIBOpt() {}
×
264

265
// isRHOpt indicates that this option can be used for RIBHolders (individual VRF)
266
// RIBs.
267
func (*disableForwardRef) isRHOpt() {}
×
268

269
// hasDisableForwardRef checks whether the RIBOpt slice supplied contains the
270
// disableForwardRef option.
271
func hasDisableForwardRef(opt []RIBOpt) bool {
1✔
272
        for _, o := range opt {
2✔
273
                if _, ok := o.(*disableForwardRef); ok {
2✔
274
                        return true
1✔
275
                }
1✔
276
        }
277
        return false
1✔
278
}
279

280
// hasRHDisableForwardRef checks whether the RHOpt slice supplied contains
281
// the disableForwardRef option.
282
func hasRHDisableForwardRef(opt []ribHolderOpt) bool {
1✔
283
        for _, o := range opt {
2✔
284
                if _, ok := o.(*disableForwardRef); ok {
2✔
285
                        return true
1✔
286
                }
1✔
287
        }
288
        return false
1✔
289
}
290

291
// New returns a new RIB with the default network instance created with name dn.
292
func New(dn string, opt ...RIBOpt) *RIB {
1✔
293
        r := &RIB{
1✔
294
                niRIB:          map[string]*RIBHolder{},
1✔
295
                defaultName:    dn,
1✔
296
                pendingEntries: map[uint64]*pendingEntry{},
1✔
297
        }
1✔
298

1✔
299
        rhOpt := []ribHolderOpt{}
1✔
300

1✔
301
        checkRIB := !hasDisableCheckFn(opt)
1✔
302
        if checkRIB {
2✔
303
                rhOpt = append(rhOpt, RIBHolderCheckFn(r.checkFn))
1✔
304
        }
1✔
305
        r.ribCheck = checkRIB
1✔
306

1✔
307
        if hasDisableForwardRef(opt) {
2✔
308
                rhOpt = append(rhOpt, DisableForwardReferences())
1✔
309
                r.disableForwardReferences = true
1✔
310
        }
1✔
311

312
        r.niRIB[dn] = NewRIBHolder(dn, rhOpt...)
1✔
313

1✔
314
        return r
1✔
315
}
316

317
// checkFn wraps canResolve and canDelete to implement a RIBHolderCheckFn
318
func (r *RIB) checkFn(t constants.OpType, ni string, candidate *aft.RIB) (bool, error) {
1✔
319
        switch t {
1✔
320
        case constants.Add:
1✔
321
                // Replace has exactly the same validation as Add, we always just get called
1✔
322
                // with Add (since Add can be an implicit replace anyway).
1✔
323
                return r.canResolve(ni, candidate)
1✔
324
        case constants.Delete:
1✔
325
                return r.canDelete(ni, candidate)
1✔
326
        }
327
        return false, fmt.Errorf("invalid unknown operation type, %s", t)
×
328
}
329

330
// pendingEntry describes an operation that is pending on the gRIBI server. Generally,
331
// this is due to RIB recursion lookup failures.
332
type pendingEntry struct {
333
        // ni is the network instance the operation is operating on.
334
        ni string
335
        // op is the AFTOperation that is being performed.
336
        op *spb.AFTOperation
337
}
338

339
// SetPostChangeHook assigns the supplied hook to all network instance RIBs within
340
// the RIB structure.
341
func (r *RIB) SetPostChangeHook(fn RIBHookFn) {
1✔
342
        for _, nir := range r.niRIB {
2✔
343
                nir.mu.Lock()
1✔
344
                nir.postChangeHook = fn
1✔
345
                nir.mu.Unlock()
1✔
346
        }
1✔
347
}
348

349
// SetResolvedEntryHook assigns the supplied hook to all network instance RIBs within
350
// the RIB structure.
351
func (r *RIB) SetResolvedEntryHook(fn ResolvedEntryFn) {
1✔
352
        r.resolvedEntryHook = fn
1✔
353
}
1✔
354

355
// NetworkInstanceRIB returns the RIB for the network instance with name s.
356
func (r *RIB) NetworkInstanceRIB(s string) (*RIBHolder, bool) {
1✔
357
        r.nrMu.RLock()
1✔
358
        defer r.nrMu.RUnlock()
1✔
359
        rh, ok := r.niRIB[s]
1✔
360
        return rh, ok
1✔
361
}
1✔
362

363
// AddNetworkInstance adds a new network instance with the specified name
364
// to the RIB.
365
func (r *RIB) AddNetworkInstance(name string) error {
1✔
366
        r.nrMu.Lock()
1✔
367
        defer r.nrMu.Unlock()
1✔
368

1✔
369
        if r.niRIB[name] != nil {
2✔
370
                return fmt.Errorf("RIB %s already exists", name)
1✔
371
        }
1✔
372

373
        rhOpt := []ribHolderOpt{}
1✔
374
        if r.ribCheck {
2✔
375
                rhOpt = append(rhOpt, RIBHolderCheckFn(r.checkFn))
1✔
376
        }
1✔
377
        if r.disableForwardReferences {
1✔
378
                rhOpt = append(rhOpt, DisableForwardReferences())
×
379
        }
×
380

381
        r.niRIB[name] = NewRIBHolder(name, rhOpt...)
1✔
382
        return nil
1✔
383
}
384

385
// KnownNetworkInstances returns the name of all known network instances
386
// within the RIB.
387
func (r *RIB) KnownNetworkInstances() []string {
1✔
388
        r.nrMu.RLock()
1✔
389
        defer r.nrMu.RUnlock()
1✔
390
        names := []string{}
1✔
391
        for n := range r.niRIB {
2✔
392
                names = append(names, n)
1✔
393
        }
1✔
394
        // return the RIB names in a stable order.
395
        sort.Strings(names)
1✔
396
        return names
1✔
397
}
398

399
// RIBContents returns the contents of the RIB in a manner that an external
400
// caller can interact with. It returns a map, keyed by network instance name,
401
// with a deep copy of the RIB contents. Since copying large RIBs may be expensive
402
// care should be taken with when it is used. A copy is used since the RIB continues
403
// to handle concurrent changes to the contents from multiple sources.
404
func (r *RIB) RIBContents() (map[string]*aft.RIB, error) {
1✔
405
        return r.copyRIBs()
1✔
406
}
1✔
407

408
// String returns a string representation of the RIB.
409
func (r *RIB) String() string {
×
410
        r.nrMu.RLock()
×
411
        defer r.nrMu.RUnlock()
×
412
        buf := &bytes.Buffer{}
×
413
        for ni, niR := range r.niRIB {
×
414
                buf.WriteString(fmt.Sprintf("%s:\n-----\n%s\n", ni, niR))
×
415
        }
×
416
        return buf.String()
×
417
}
418

419
// OpResult contains the result of an operation (Add, Modify, Delete).
420
type OpResult struct {
421
        // ID is the ID of the operation as specified in the input request.
422
        ID uint64
423
        // Op is the operation that was performed.
424
        Op *spb.AFTOperation
425
        // Error is an error string detailing any error that occurred.
426
        Error string
427
}
428

429
// String returns the OpResult as a human-readable string.
430
func (o *OpResult) String() string {
×
431
        return fmt.Sprintf("ID: %d, Type: %s, Error: %v", o.ID, prototext.Format(o.Op), o.Error)
×
432
}
×
433

434
// AddEntry adds the entry described in op to the network instance with name ni. It returns
435
// two slices of OpResults:
436
//   - the first ("oks") describes the set of entries that were installed successfully based on
437
//     this operation.
438
//   - the second ("fails") describes the set of entries that were NOT installed, and encountered
439
//     fatal errors during the process of installing the entry.
440
//
441
// It returns an error if there is a fatal error encountered for the function during operation.
442
//
443
// If the input AFT operation is a REPLACE operation, AddEntry ensures that the entry exists within
444
// the RIB before replacing it.
445
//
446
// The oks slice may have length > 1 (i.e., not just be the input operation) in the case an entry
447
// becomes resolvable (per canResolve) *after* this operation has been installed. It will recursively
448
// call the internal implementation in order to install all entries that are now resolvable based
449
// on the operation provided.
450
func (r *RIB) AddEntry(ni string, op *spb.AFTOperation) ([]*OpResult, []*OpResult, error) {
1✔
451
        if ni == "" {
1✔
452
                return nil, nil, fmt.Errorf("invalid network instance, %s", ni)
×
453
        }
×
454

455
        oks, fails := []*OpResult{}, []*OpResult{}
1✔
456
        checked := map[uint64]bool{}
1✔
457
        if err := r.addEntryInternal(ni, op, &oks, &fails, checked); err != nil {
2✔
458
                return nil, nil, err
1✔
459
        }
1✔
460

461
        return oks, fails, nil
1✔
462
}
463

464
// addEntryInternal is the internal implementation of AddEntry. It takes arguments of:
465
//   - the name of the network instance being operated on (ni) by the operation op.
466
//   - a slice of installed results, which is appended to.
467
//   - a slice of failed results, which is appended to.
468
//   - a map, keyed by operation ID, describing the stack of calls that we have currently
469
//     done during this recursion so that we do not repeat an install operation.
470
func (r *RIB) addEntryInternal(ni string, op *spb.AFTOperation, oks, fails *[]*OpResult, installStack map[uint64]bool) error {
1✔
471
        if installStack[op.GetId()] {
2✔
472
                return nil
1✔
473
        }
1✔
474
        niR, ok := r.NetworkInstanceRIB(ni)
1✔
475
        if !ok || !niR.IsValid() {
1✔
476
                return fmt.Errorf("invalid network instance, %s", ni)
×
477
        }
×
478

479
        explicitReplace := false
1✔
480
        if op.GetOp() == spb.AFTOperation_REPLACE {
2✔
481
                explicitReplace = true
1✔
482
        }
1✔
483

484
        var (
1✔
485
                installed bool
1✔
486
                opErr     error
1✔
487
                // Used to store information about the transaction that was
1✔
488
                // completed in case it completes successfully.
1✔
489
                v4Prefix, v6Prefix string
1✔
490
                mplsLabel          uint64
1✔
491
        )
1✔
492

1✔
493
        switch t := op.Entry.(type) {
1✔
494
        case *spb.AFTOperation_Ipv4:
1✔
495
                log.V(2).Infof("[op %d] attempting to add IPv4 prefix %s", op.GetId(), t.Ipv4.GetPrefix())
1✔
496
                done, orig, err := niR.AddIPv4(t.Ipv4, explicitReplace)
1✔
497
                switch {
1✔
498
                case err != nil:
1✔
499
                        opErr = err
1✔
500
                case done:
1✔
501
                        installed = done
1✔
502
                        v4Prefix = t.Ipv4.GetPrefix()
1✔
503
                        handleReferences(r, niR, orig, t.Ipv4.GetIpv4Entry())
1✔
504
                }
505
        case *spb.AFTOperation_Ipv6:
1✔
506
                v6Prefix = t.Ipv6.GetPrefix()
1✔
507
                log.V(2).Infof("[op %d] attempting to add IPv6 prefix %s", op.GetId(), t.Ipv6.GetPrefix())
1✔
508
                done, orig, err := niR.AddIPv6(t.Ipv6, explicitReplace)
1✔
509
                switch {
1✔
510
                case err != nil:
1✔
511
                        opErr = err
1✔
512
                case done:
1✔
513
                        installed = done
1✔
514
                        handleReferences(r, niR, orig, t.Ipv6.GetIpv6Entry())
1✔
515
                }
516
        case *spb.AFTOperation_Mpls:
1✔
517
                mplsLabel = t.Mpls.GetLabelUint64()
1✔
518
                log.V(2).Infof("[op %d] attempting to add MPLS label entry %d", op.GetId(), mplsLabel)
1✔
519
                done, orig, err := niR.AddMPLS(t.Mpls, explicitReplace)
1✔
520
                switch {
1✔
521
                case err != nil:
1✔
522
                        opErr = err
1✔
523
                case done:
1✔
524
                        installed = done
1✔
525
                        handleReferences(r, niR, orig, t.Mpls.GetLabelEntry())
1✔
526
                }
527
        case *spb.AFTOperation_NextHopGroup:
1✔
528
                log.V(2).Infof("[op %d] attempting to add NHG ID %d", op.GetId(), t.NextHopGroup.GetId())
1✔
529
                done, orig, err := niR.AddNextHopGroup(t.NextHopGroup, explicitReplace)
1✔
530
                switch {
1✔
531
                case err != nil:
1✔
532
                        opErr = err
1✔
533
                case done:
1✔
534
                        r.handleNHGReferences(niR, orig, t.NextHopGroup.GetNextHopGroup())
1✔
535
                        installed = done
1✔
536
                }
537
        case *spb.AFTOperation_NextHop:
1✔
538
                log.V(2).Infof("[op %d] attempting to add NH Index %d", op.GetId(), t.NextHop.GetIndex())
1✔
539
                done, _, err := niR.AddNextHop(t.NextHop, explicitReplace)
1✔
540
                switch {
1✔
541
                case err != nil:
1✔
542
                        opErr = err
1✔
543
                case done:
1✔
544
                        installed = done
1✔
545
                }
546
        default:
1✔
547
                return status.Newf(codes.Unimplemented, "unsupported AFT operation type %T", t).Err()
1✔
548
        }
549

550
        switch {
1✔
551
        case opErr != nil:
1✔
552
                *fails = append(*fails, &OpResult{
1✔
553
                        ID:    op.GetId(),
1✔
554
                        Op:    op,
1✔
555
                        Error: opErr.Error(),
1✔
556
                })
1✔
557
        case installed:
1✔
558
                // Mark that within this stack we have installed this entry successfully, so
1✔
559
                // we don't retry if it was somewhere further up the stack.
1✔
560
                installStack[op.GetId()] = true
1✔
561
                log.V(2).Infof("operation %d installed in RIB successfully", op.GetId())
1✔
562

1✔
563
                r.rmPending(op.GetId())
1✔
564

1✔
565
                *oks = append(*oks, &OpResult{
1✔
566
                        ID: op.GetId(),
1✔
567
                        Op: op,
1✔
568
                })
1✔
569

1✔
570
                var (
1✔
571
                        call bool
1✔
572
                        aft  constants.AFT
1✔
573
                        key  any
1✔
574
                )
1✔
575
                switch {
1✔
576
                case v4Prefix != "":
1✔
577
                        call = true
1✔
578
                        aft = constants.IPv4
1✔
579
                        key = v4Prefix
1✔
580
                case mplsLabel != 0:
1✔
581
                        call = true
1✔
582
                        aft = constants.MPLS
1✔
583
                        key = mplsLabel
1✔
584
                case v6Prefix != "":
1✔
585
                        call = true
1✔
586
                        aft = constants.IPv6
1✔
587
                        key = v6Prefix
1✔
588
                }
589
                if call {
2✔
590
                        if err := r.callResolvedEntryHook(constants.Add, ni, aft, key); err != nil {
1✔
591
                                return fmt.Errorf("cannot run resolvedEntryHook, %v", err)
×
592
                        }
×
593
                }
594

595
                // we may now have made some other pending entry be possible to install,
596
                // so try them all out.
597
                for _, e := range r.getPending() {
2✔
598
                        err := r.addEntryInternal(e.ni, e.op, oks, fails, installStack)
1✔
599
                        if err != nil {
1✔
600
                                return err
×
601
                        }
×
602
                }
603
        default:
1✔
604
                switch r.disableForwardReferences {
1✔
605
                case false:
1✔
606
                        r.addPending(op.GetId(), &pendingEntry{
1✔
607
                                ni: ni,
1✔
608
                                op: op,
1✔
609
                        })
1✔
610
                default:
1✔
611
                        *fails = append(*fails, &OpResult{
1✔
612
                                ID:    op.GetId(),
1✔
613
                                Op:    op,
1✔
614
                                Error: fmt.Sprintf("operation %d has unresolved dependencies", op.GetId()),
1✔
615
                        })
1✔
616
                }
617
        }
618

619
        return nil
1✔
620
}
621

622
// topLevelEntryProto is an interface implemented by protobuf messages that represent
623
// an IPv4, MPLS, or IPv6 protobuf.
624
type topLevelEntryProto interface {
625
        GetNextHopGroupNetworkInstance() *wpb.StringValue
626
        GetNextHopGroup() *wpb.UintValue
627
}
628

629
// topLevelEntryStruct is an interface implemented by ygot Go structs tha represent
630
// an IPv4, IPv6 or MPLS YANG container.
631
type topLevelEntryStruct interface {
632
        GetNextHopGroupNetworkInstance() string
633
        GetNextHopGroup() uint64
634
}
635

636
// isNil safely allows a topLevelEntryStruct to be compared to nil.
637
func isNil[T any](t T) bool {
1✔
638
        v := reflect.ValueOf(t)
1✔
639
        switch v.Kind() {
1✔
640
        case reflect.Ptr, reflect.Interface, reflect.Map, reflect.Chan, reflect.Func:
1✔
641
                return v.IsNil()
1✔
642
        default:
×
643
                return false
×
644
        }
645
}
646

647
// handleReferences handles the reference counts for the specified new entry "new" in the RIB r. The
648
// context niRIB is used as the default VRF RIB for lookup, and the original struct, "orig" is used
649
// to update any replaced entries. If original is nil, then references are only incremented, otherwise
650
// replaced references are not adjusted, new references are incremented, and deleted references
651
// are decremented.
652
func handleReferences[P topLevelEntryProto, S topLevelEntryStruct](r *RIB, niRIB *RIBHolder, original S, new P) {
1✔
653
        incRefCounts := true
1✔
654
        newNHGNI := new.GetNextHopGroupNetworkInstance().GetValue()
1✔
655
        newNHG := new.GetNextHopGroup().GetValue()
1✔
656
        if !isNil(original) {
2✔
657
                // This is an entry that was replaced, and hence we need to handle two sets of
1✔
658
                // reference counts, decrementing anything that was deleted, and incrementing
1✔
659
                // anything that was newly referenced.
1✔
660
                origNHGNI := original.GetNextHopGroupNetworkInstance()
1✔
661
                origNHG := original.GetNextHopGroup()
1✔
662

1✔
663
                switch {
1✔
664
                case newNHGNI == origNHGNI && newNHG == origNHG:
1✔
665
                        // We are referencing the same entries, so this is a NOOP.
1✔
666
                        incRefCounts = false
1✔
667
                case newNHGNI != origNHGNI || newNHG != origNHG:
1✔
668
                        // We are no longer referencing the original NHG, so we need to decrement
1✔
669
                        // the old references.
1✔
670
                        rr, err := r.refdRIB(niRIB, origNHGNI)
1✔
671
                        switch err {
1✔
672
                        case nil:
1✔
673
                                rr.decNHGRefCount(origNHG)
1✔
674
                        default:
×
675
                                log.Errorf("cannot find NHG network instance %s", origNHGNI)
×
676
                        }
677
                default:
×
678
                        // We are referencing new network instances.
679
                }
680
        }
681

682
        if incRefCounts {
2✔
683
                referencingRIB, err := r.refdRIB(niRIB, newNHGNI)
1✔
684
                switch err {
1✔
685
                case nil:
1✔
686
                        referencingRIB.incNHGRefCount(newNHG)
1✔
687
                default:
×
688
                        log.Errorf("cannot find network instance %s", newNHGNI)
×
689
                }
690
        }
691
}
692

693
func (r *RIB) handleNHGReferences(niRIB *RIBHolder, original *aft.Afts_NextHopGroup, new *aftpb.Afts_NextHopGroup) {
1✔
694
        // Increment all the new references.
1✔
695
        for _, nh := range new.NextHop {
2✔
696
                niRIB.incNHRefCount(nh.GetIndex())
1✔
697
        }
1✔
698

699
        // And decrement all the old references.
700
        if original != nil {
1✔
701
                for _, nh := range original.NextHop {
×
702
                        niRIB.decNHRefCount(nh.GetIndex())
×
703
                }
×
704
        }
705
}
706

707
// callResolvedEntryHook calls the resolvedEntryHook supplying information about the triggering
708
// operation. Particularky:
709
//   - the operation is of type optype
710
//   - it corresponds to the network instance netinst
711
//   - it is within the aft AFT
712
//   - it affects the AFT table entry with key value key.
713
//
714
// It returns an error if the hook cannot be called. Any error from the hook must be handled externally.
715
func (r *RIB) callResolvedEntryHook(optype constants.OpType, netinst string, aft constants.AFT, key any) error {
1✔
716
        if r.resolvedEntryHook == nil {
2✔
717
                return nil
1✔
718
        }
1✔
719

720
        ribs, err := r.copyRIBs()
1✔
721
        if err != nil {
1✔
722
                return err
×
723
        }
×
724
        go r.resolvedEntryHook(ribs, optype, netinst, aft, key)
1✔
725
        return nil
1✔
726
}
727

728
// copyRIBs returns a map, keyed by network instance name, with the value of the ygot-generated
729
// AFT struct, of the set of RIBs stored by the instance r. A DeepCopy of the RIBs is returned,
730
// along with an error that indicates whether the entries could be copied.
731
func (r *RIB) copyRIBs() (map[string]*aft.RIB, error) {
1✔
732
        r.nrMu.RLock()
1✔
733
        defer r.nrMu.RUnlock()
1✔
734

1✔
735
        rib := map[string]*aft.RIB{}
1✔
736
        for name, niR := range r.niRIB {
2✔
737
                niR.mu.RLock()
1✔
738
                // this is likely expensive on very large RIBs, but with today's implementation
1✔
739
                // it seems acceptable, since we then allow the caller not to have to figure out
1✔
740
                // any locking since they have their own RIB to work on.
1✔
741
                dupRIB, err := ygot.DeepCopy(niR.r)
1✔
742
                if err != nil {
1✔
743
                        return nil, fmt.Errorf("cannot copy RIB for NI %s, %v", name, err)
×
744
                }
×
745
                rib[name] = dupRIB.(*aft.RIB)
1✔
746
                niR.mu.RUnlock()
1✔
747
        }
748
        return rib, nil
1✔
749
}
750

751
// refdRIB returns the RIB for the specified ref -- which may be the current RIB
752
// if ref is empty, otherwise it is a different RIB on the server. It returns an
753
// error if it does not exist.
754
func (r *RIB) refdRIB(ni *RIBHolder, ref string) (*RIBHolder, error) {
1✔
755
        referencingRIB := ni
1✔
756
        if ref != "" {
2✔
757
                rr, ok := r.NetworkInstanceRIB(ref)
1✔
758
                if !ok {
1✔
759
                        return nil, status.Newf(codes.InvalidArgument, "invalid network-instance %s specified in entry", ref).Err()
×
760
                }
×
761
                referencingRIB = rr
1✔
762
        }
763
        return referencingRIB, nil
1✔
764
}
765

766
// DeleteEntry removes the entry specified by op from the network instance ni.
767
func (r *RIB) DeleteEntry(ni string, op *spb.AFTOperation) ([]*OpResult, []*OpResult, error) {
1✔
768
        niR, ok := r.NetworkInstanceRIB(ni)
1✔
769
        if !ok || !niR.IsValid() {
2✔
770
                return nil, nil, fmt.Errorf("invalid network instance, %s", ni)
1✔
771
        }
1✔
772

773
        var (
1✔
774
                oks, fails   []*OpResult
1✔
775
                removed      bool
1✔
776
                err          error
1✔
777
                originalv4   *aft.Afts_Ipv4Entry
1✔
778
                originalv6   *aft.Afts_Ipv6Entry
1✔
779
                originalNHG  *aft.Afts_NextHopGroup
1✔
780
                originalMPLS *aft.Afts_LabelEntry
1✔
781
        )
1✔
782

1✔
783
        if op == nil || op.Entry == nil {
2✔
784
                return nil, nil, status.Newf(codes.InvalidArgument, "invalid nil AFT operation, %v", op).Err()
1✔
785
        }
1✔
786
        switch t := op.Entry.(type) {
1✔
787
        case *spb.AFTOperation_Ipv4:
1✔
788
                log.V(2).Infof("deleting IPv4 prefix %s", t.Ipv4.GetPrefix())
1✔
789
                removed, originalv4, err = niR.DeleteIPv4(t.Ipv4)
1✔
790
        case *spb.AFTOperation_Ipv6:
1✔
791
                log.V(2).Infof("deleting IPv6 prefix %s", t.Ipv6.GetPrefix())
1✔
792
                removed, originalv6, err = niR.DeleteIPv6(t.Ipv6)
1✔
793
        case *spb.AFTOperation_NextHop:
1✔
794
                log.V(2).Infof("deleting NH Index %d", t.NextHop.GetIndex())
1✔
795
                removed, _, err = niR.DeleteNextHop(t.NextHop)
1✔
796
        case *spb.AFTOperation_NextHopGroup:
1✔
797
                log.V(2).Infof("deleting NHG ID %d", t.NextHopGroup.GetId())
1✔
798
                removed, originalNHG, err = niR.DeleteNextHopGroup(t.NextHopGroup)
1✔
799
        case *spb.AFTOperation_Mpls:
1✔
800
                log.V(2).Infof("deleting MPLS entry %s", t.Mpls.GetLabel())
1✔
801
                removed, originalMPLS, err = niR.DeleteMPLS(t.Mpls)
1✔
802
        default:
1✔
803
                return nil, nil, status.Newf(codes.Unimplemented, "unsupported AFT operation type %T", t).Err()
1✔
804
        }
805

806
        var (
1✔
807
                callHook bool
1✔
808
                aft      constants.AFT
1✔
809
                key      any
1✔
810
        )
1✔
811

1✔
812
        switch {
1✔
813
        case err != nil:
1✔
814
                fails = append(fails, &OpResult{
1✔
815
                        ID:    op.GetId(),
1✔
816
                        Op:    op,
1✔
817
                        Error: err.Error(),
1✔
818
                })
1✔
819
        case removed:
1✔
820
                // Decrement the reference counts.
1✔
821
                switch {
1✔
822
                case originalv4 != nil:
1✔
823
                        referencingRIB, err := r.refdRIB(niR, originalv4.GetNextHopGroupNetworkInstance())
1✔
824
                        if err != nil {
1✔
825
                                return nil, nil, err
×
826
                        }
×
827
                        referencingRIB.decNHGRefCount(originalv4.GetNextHopGroup())
1✔
828
                        callHook = true
1✔
829
                        aft = constants.IPv4
1✔
830
                        key = originalv4.GetPrefix()
1✔
831
                case originalv6 != nil:
1✔
832
                        referencingRIB, err := r.refdRIB(niR, originalv6.GetNextHopGroupNetworkInstance())
1✔
833
                        if err != nil {
1✔
834
                                return nil, nil, err
×
835
                        }
×
836
                        referencingRIB.decNHGRefCount(originalv6.GetNextHopGroup())
1✔
837
                        callHook = true
1✔
838
                        aft = constants.IPv6
1✔
839
                        key = originalv6.GetPrefix()
1✔
840
                case originalNHG != nil:
1✔
841
                        for id := range originalNHG.NextHop {
2✔
842
                                niR.decNHRefCount(id)
1✔
843
                        }
1✔
844
                case originalMPLS != nil:
1✔
845
                        referencingRIB, err := r.refdRIB(niR, originalMPLS.GetNextHopGroupNetworkInstance())
1✔
846
                        if err != nil {
1✔
847
                                return nil, nil, err
×
848
                        }
×
849
                        referencingRIB.decNHGRefCount(originalMPLS.GetNextHopGroup())
1✔
850
                        callHook = true
1✔
851
                        aft = constants.MPLS
1✔
852
                        key = originalMPLS.GetLabel()
1✔
853
                }
854

855
                log.V(2).Infof("operation %d deleted from RIB successfully", op.GetId())
1✔
856
                oks = append(oks, &OpResult{
1✔
857
                        ID: op.GetId(),
1✔
858
                        Op: op,
1✔
859
                })
1✔
860
        default:
1✔
861
                fails = append(fails, &OpResult{
1✔
862
                        ID: op.GetId(),
1✔
863
                        Op: op,
1✔
864
                })
1✔
865
        }
866

867
        if callHook {
2✔
868
                if err := r.callResolvedEntryHook(constants.Delete, ni, aft, key); err != nil {
1✔
869
                        return oks, fails, fmt.Errorf("cannot run resolvedEntryHook, %v", err)
×
870
                }
×
871
        }
872
        return oks, fails, nil
1✔
873
}
874

875
// getPending returns the current set of pending entry operations for the
876
// RIB receiver.
877
func (r *RIB) getPending() []*pendingEntry {
1✔
878
        r.pendMu.RLock()
1✔
879
        defer r.pendMu.RUnlock()
1✔
880
        p := []*pendingEntry{}
1✔
881
        for _, e := range r.pendingEntries {
2✔
882
                p = append(p, e)
1✔
883
        }
1✔
884
        return p
1✔
885
}
886

887
// addPending adds a pendingEntry with operation ID id to the pending entries
888
// within the RIB.
889
func (r *RIB) addPending(id uint64, e *pendingEntry) {
1✔
890
        r.pendMu.Lock()
1✔
891
        defer r.pendMu.Unlock()
1✔
892
        r.pendingEntries[id] = e
1✔
893
}
1✔
894

895
// rmPending removes the operation with ID id from the RIB's pendingEntries.
896
func (r *RIB) rmPending(id uint64) {
1✔
897
        r.pendMu.Lock()
1✔
898
        defer r.pendMu.Unlock()
1✔
899
        delete(r.pendingEntries, id)
1✔
900
}
1✔
901

902
// canResolve takes an input candidate RIB, which contains only the new entry
903
// being added and determines whether it can be resolved against the existing set
904
// of RIBs that are stored in r. The specified netInst string is used to
905
// determine the current network instance within which this entry is being
906
// considered, such that where the assumption is that a reference is resolved within
907
// the same network-instance this NI can be used.
908
//
909
// canResolve returns a boolean indicating whether the entry
910
// can be resolved or not.
911
//
912
// An entry is defined to be resolved if all its external references within the gRIBI
913
// RIB can be resolved - particularly (starting from the most specific):
914
//
915
//  1. for a next-hop
916
//     - always consider this valid, since all elements can be resolved outside of
917
//     gRIBI.
918
//  2. for a next-hop-group
919
//     - all the next-hops within the NHG can be resolved
920
//  3. for an ipv4-entry
921
//     - the next-hop-group can be resolved
922
//
923
// An error is returned if the candidate RIB contains more than one new type.
924
func (r *RIB) canResolve(netInst string, candidate *aft.RIB) (bool, error) {
1✔
925
        caft := candidate.GetAfts()
1✔
926
        if caft == nil {
2✔
927
                return false, errors.New("invalid nil candidate AFT")
1✔
928
        }
1✔
929

930
        if err := checkCandidate(caft); err != nil {
2✔
931
                return false, err
1✔
932
        }
1✔
933

934
        for _, n := range caft.NextHop {
2✔
935
                if n.GetIndex() == 0 {
2✔
936
                        return false, fmt.Errorf("invalid index zero for next-hop in NI %s", netInst)
1✔
937
                }
1✔
938
                // we always resolve next-hop entries because they can be resolved outside of gRIBI.
939
                return true, nil
1✔
940
        }
941

942
        // resolve in the default NI if we didn't get asked for a specific NI.
943
        if netInst == "" {
2✔
944
                netInst = r.defaultName
1✔
945
        }
1✔
946
        niRIB, ok := r.NetworkInstanceRIB(netInst)
1✔
947
        if !ok {
2✔
948
                return false, fmt.Errorf("invalid network-instance %s", netInst)
1✔
949
        }
1✔
950

951
        for _, g := range caft.NextHopGroup {
2✔
952
                if g.GetId() == 0 {
2✔
953
                        return false, fmt.Errorf("invalid zero-index NHG")
1✔
954
                }
1✔
955
                if len(g.NextHop) == 0 {
2✔
956
                        return false, fmt.Errorf("empty next-hop-group")
1✔
957
                }
1✔
958
                for _, n := range g.NextHop {
2✔
959
                        // Zero is an invalid value for a next-hop index. GetIndex() will also return 0
1✔
960
                        // if the NH index is nil, which is also invalid - so handle them together.
1✔
961
                        if n.GetIndex() == 0 {
2✔
962
                                return false, fmt.Errorf("invalid zero index NH in NHG %d, NI %s", g.GetId(), netInst)
1✔
963
                        }
1✔
964
                        // nexthops are resolved in the same NI as the next-hop-group
965
                        if _, ok := niRIB.GetNextHop(n.GetIndex()); !ok {
2✔
966
                                // this is not an error - it's just that we can't resolve this seemingly
1✔
967
                                // valid looking NHG at this point.
1✔
968
                                return false, nil
1✔
969
                        }
1✔
970
                }
971
                return true, nil
1✔
972
        }
973

974
        nhgResolvable := func(resolveRIB *RIBHolder, otherNI string, nhg uint64) (bool, error) {
2✔
975
                if otherNI != "" {
2✔
976
                        resolveRIB, ok = r.NetworkInstanceRIB(otherNI)
1✔
977
                        if !ok {
2✔
978
                                return false, fmt.Errorf("invalid unknown network-instance for entry, %s", otherNI)
1✔
979
                        }
1✔
980
                }
981
                if _, ok := resolveRIB.GetNextHopGroup(nhg); !ok {
2✔
982
                        // again, not an error - we just can't resolve this IPv4 entry due to missing NHG right now.
1✔
983
                        return false, nil
1✔
984
                }
1✔
985
                return true, nil
1✔
986
        }
987

988
        for _, i := range caft.Ipv4Entry {
2✔
989
                if i.GetNextHopGroup() == 0 {
2✔
990
                        // handle zero index again.
1✔
991
                        return false, fmt.Errorf("invalid zero-index NHG in IPv4Entry %s, NI %s", i.GetPrefix(), netInst)
1✔
992
                }
1✔
993
                return nhgResolvable(niRIB, i.GetNextHopGroupNetworkInstance(), i.GetNextHopGroup())
1✔
994

995
        }
996

997
        for _, i := range caft.Ipv6Entry {
2✔
998
                if i.GetNextHopGroup() == 0 {
2✔
999
                        return false, fmt.Errorf("invalid zero-index NHG in IPv6Entry %s, NI %s", i.GetPrefix(), netInst)
1✔
1000
                }
1✔
1001
                return nhgResolvable(niRIB, i.GetNextHopGroupNetworkInstance(), i.GetNextHopGroup())
1✔
1002
        }
1003

1004
        for _, i := range caft.LabelEntry {
2✔
1005
                if i.GetNextHopGroup() == 0 {
2✔
1006
                        return false, fmt.Errorf("invalid zero index NHG in LabelEntry %v, NI %s", i.GetLabel(), netInst)
1✔
1007
                }
1✔
1008
                return nhgResolvable(niRIB, i.GetNextHopGroupNetworkInstance(), i.GetNextHopGroup())
1✔
1009
        }
1010

1011
        // We should never reach here since we checked that at least one of the things that we are looping over has
1012
        // length >1, but return here too.
1013
        return false, errors.New("no entries in specified candidate")
×
1014
}
1015

1016
// canDelete takes an input deletionCandidate RIB, which contains only the entry that
1017
// is to be removed from the RIB and determines whether it is safe to remove
1018
// it from the existing set of RIBs that are stored in r. The specified netInst string is
1019
// used to determine the current network instance within which this entry is being
1020
// considered.
1021
//
1022
// canDelete returns a boolean indicating whether the entry can be removed or not
1023
// or an error if the candidate is found to be invalid.
1024
func (r *RIB) canDelete(netInst string, deletionCandidate *aft.RIB) (bool, error) {
1✔
1025
        caft := deletionCandidate.GetAfts()
1✔
1026
        if caft == nil {
2✔
1027
                return false, errors.New("invalid nil candidate AFT")
1✔
1028
        }
1✔
1029

1030
        if err := checkCandidate(caft); err != nil {
2✔
1031
                return false, err
1✔
1032
        }
1✔
1033

1034
        // Throughout the following code, we know there is a single entry within the
1035
        // candidate RIB, since checkCandidate performs this check.
1036
        //
1037
        // We always check references in the local network instance and resolve in the
1038
        // default NI if we didn't get asked for a specific NI. We check for this before
1039
        // doing the delete to make sure we're working in a valid NI.
1040
        if netInst == "" {
2✔
1041
                netInst = r.defaultName
1✔
1042
        }
1✔
1043
        niRIB, ok := r.NetworkInstanceRIB(netInst)
1✔
1044
        if !ok {
2✔
1045
                return false, fmt.Errorf("invalid network-instance %s", netInst)
1✔
1046
        }
1✔
1047

1048
        // IPv4 entries can always be removed, since we allow recursion to happen
1049
        // inside and outside of gRIBI - this is true for MPLS and IPv6.
1050
        if len(caft.Ipv4Entry) != 0 || len(caft.LabelEntry) != 0 || len(caft.Ipv6Entry) != 0 {
2✔
1051
                return true, nil
1✔
1052
        }
1✔
1053

1054
        // Now, we need to check that nothing references a NHG. We could do this naîvely,
1055
        // by walking all RIBs, but this is expensive, so rather we check the refCounter
1056
        // within the RIB instance.
1057
        for id := range caft.NextHopGroup {
2✔
1058
                switch {
1✔
1059
                case id == 0:
1✔
1060
                        return false, fmt.Errorf("bad NextHopGroup ID 0")
1✔
1061
                case !niRIB.nhgExists(id):
1✔
1062
                        return true, nil
1✔
1063
                }
1064
                // if the NHG is not referenced, then we can delete it.
1065
                return !niRIB.nhgReferenced(id), nil
1✔
1066
        }
1067

1068
        for idx := range caft.NextHop {
2✔
1069
                switch {
1✔
1070
                case idx == 0:
1✔
1071
                        return false, fmt.Errorf("bad NextHop ID 0")
1✔
1072
                case !niRIB.nhExists(idx):
1✔
1073
                        return true, nil
1✔
1074
                }
1075
                // again if the NH is not referenced, then we can delete it.
1076
                return !niRIB.nhReferenced(idx), nil
1✔
1077
        }
1078

1079
        // We checked that there was 1 entry in the RIB, so we should never reach here,
1080
        // but return an error and keep the compiler happy.
1081
        return false, errors.New("no entries in specified candidate")
×
1082

1083
}
1084

1085
// checkCandidate checks whether the candidate RIB 'caft' can be processed
1086
// by the RIB implementation. It returns an error if it cannot.
1087
func checkCandidate(caft *aft.Afts) error {
1✔
1088
        switch {
1✔
1089
        case len(caft.MacEntry) != 0:
1✔
1090
                return fmt.Errorf("ethernet MAC entries are unsupported, got: %v", caft.MacEntry)
1✔
1091
        case len(caft.PolicyForwardingEntry) != 0:
1✔
1092
                return fmt.Errorf("PBR entries are unsupported, got: %v", caft.PolicyForwardingEntry)
1✔
1093
        case (len(caft.Ipv6Entry) + len(caft.LabelEntry) + len(caft.Ipv4Entry) + len(caft.NextHopGroup) + len(caft.NextHop)) == 0:
1✔
1094
                return errors.New("no entries in specified candidate")
1✔
1095
        case (len(caft.Ipv6Entry) + len(caft.LabelEntry) + len(caft.Ipv4Entry) + len(caft.NextHopGroup) + len(caft.NextHop)) > 1:
1✔
1096
                return fmt.Errorf("multiple entries are unsupported, got mpls: %v, ipv4: %v, next-hop-group: %v, next-hop: %v", caft.LabelEntry, caft.Ipv4Entry, caft.NextHopGroup, caft.NextHop)
1✔
1097
        }
1098
        return nil
1✔
1099
}
1100

1101
// ribHolderOpt is an interface implemented by all options that can be provided to the RIBHolder's NewRIBHolder
1102
// function.
1103
type ribHolderOpt interface {
1104
        isRHOpt()
1105
}
1106

1107
// ribHolderCheckFn is a ribHolderOpt that provides a function that can be run for each operation to
1108
// determine whether it should be installed in the RIB.
1109
type ribHolderCheckFn struct {
1110
        fn RIBHolderCheckFunc
1111
}
1112

1113
// isRHOpt implements the ribHolderOpt function
1114
func (r *ribHolderCheckFn) isRHOpt() {}
×
1115

1116
// RIBHolderCheckFn is an option that provides a function f to be run for each RIB
1117
// change.
1118
func RIBHolderCheckFn(f RIBHolderCheckFunc) *ribHolderCheckFn {
1✔
1119
        return &ribHolderCheckFn{fn: f}
1✔
1120
}
1✔
1121

1122
// hasCheckFn checks whether there is a ribHolderCheckFn option within the supplied
1123
// options.
1124
func hasCheckFn(opts []ribHolderOpt) *ribHolderCheckFn {
1✔
1125
        for _, o := range opts {
2✔
1126
                if f, ok := o.(*ribHolderCheckFn); ok {
2✔
1127
                        return f
1✔
1128
                }
1✔
1129
        }
1130
        return nil
1✔
1131
}
1132

1133
// NewRIBHolder returns a new RIB holder for a single network instance.
1134
func NewRIBHolder(name string, opts ...ribHolderOpt) *RIBHolder {
1✔
1135
        r := &RIBHolder{
1✔
1136
                name: name,
1✔
1137
                r: &aft.RIB{
1✔
1138
                        Afts: &aft.Afts{},
1✔
1139
                },
1✔
1140
                refCounts: &niRefCounter{
1✔
1141
                        NextHop:      map[uint64]uint64{},
1✔
1142
                        NextHopGroup: map[uint64]uint64{},
1✔
1143
                },
1✔
1144
        }
1✔
1145

1✔
1146
        fn := hasCheckFn(opts)
1✔
1147
        // If there is a check function - regenerate it so that it
1✔
1148
        // always operates on the local name.
1✔
1149
        if fn != nil {
2✔
1150
                checkFn := func(op constants.OpType, r *aft.RIB) (bool, error) {
2✔
1151
                        return fn.fn(op, name, r)
1✔
1152
                }
1✔
1153
                r.checkFn = checkFn
1✔
1154
        }
1155

1156
        if hasRHDisableForwardRef(opts) {
2✔
1157
                r.disableForwardRef = true
1✔
1158
        }
1✔
1159

1160
        return r
1✔
1161
}
1162

1163
// IsValid determines whether the specified RIBHolder is valid to be
1164
// programmed.
1165
func (r *RIBHolder) IsValid() bool {
1✔
1166
        // This shows why we need to make the locking on the RIB more granular,
1✔
1167
        // since now we're taking a lock just to check whether things are not nil.
1✔
1168
        r.mu.RLock()
1✔
1169
        defer r.mu.RUnlock()
1✔
1170
        if r.name == "" || r.r == nil || r.r.Afts == nil {
1✔
1171
                return false
×
1172
        }
×
1173
        return true
1✔
1174
}
1175

1176
// GetNextHop gets the next-hop with the specified index from the RIB
1177
// and returns it. It returns a bool indicating whether the value was
1178
// found.
1179
func (r *RIBHolder) GetNextHop(index uint64) (*aft.Afts_NextHop, bool) {
1✔
1180
        r.mu.RLock()
1✔
1181
        defer r.mu.RUnlock()
1✔
1182
        n := r.r.GetAfts().GetNextHop(index)
1✔
1183
        if n == nil {
2✔
1184
                return nil, false
1✔
1185
        }
1✔
1186
        return n, true
1✔
1187
}
1188

1189
// GetNextHopGroup gets the next-hop-group with the specified ID from the RIB
1190
// and returns it. It returns a bool indicating whether the value was found.
1191
func (r *RIBHolder) GetNextHopGroup(id uint64) (*aft.Afts_NextHopGroup, bool) {
1✔
1192
        r.mu.RLock()
1✔
1193
        defer r.mu.RUnlock()
1✔
1194
        n := r.r.GetAfts().GetNextHopGroup(id)
1✔
1195
        if n == nil {
2✔
1196
                return nil, false
1✔
1197
        }
1✔
1198
        return n, true
1✔
1199
}
1200

1201
// candidateRIB takes the input set of Afts and returns them as a aft.RIB pointer
1202
// that can be merged into an existing RIB.
1203
func candidateRIB(a *aftpb.Afts) (*aft.RIB, error) {
1✔
1204
        paths, err := protomap.PathsFromProto(a)
1✔
1205
        if err != nil {
2✔
1206
                return nil, err
1✔
1207
        }
1✔
1208

1209
        nr := &aft.RIB{}
1✔
1210
        for p, v := range paths {
2✔
1211
                sv, err := value.FromScalar(v)
1✔
1212

1✔
1213
                if err != nil {
1✔
1214
                        ps := p.String()
×
1215
                        if yps, err := ygot.PathToString(p); err == nil {
×
1216
                                ps = yps
×
1217
                        }
×
1218
                        return nil, fmt.Errorf("cannot convert field %s to scalar, %v", ps, sv)
×
1219
                }
1220
                // Use the global aftSchema to avoid needing to unmarshal again.
1221
                if err := ytypes.SetNode(aftSchema, nr, p, sv, &ytypes.InitMissingElements{}); err != nil {
1✔
1222
                        return nil, fmt.Errorf("invalid RIB %s, %v", a, err)
×
1223
                }
×
1224
        }
1225

1226
        // We validate against the schema, but not semantically within gRIBI.
1227
        if err := nr.Afts.Validate(&ytypes.LeafrefOptions{
1✔
1228
                IgnoreMissingData: true,
1✔
1229
                Log:               false,
1✔
1230
        }); err != nil {
2✔
1231
                return nil, fmt.Errorf("invalid entry provided, %v", err)
1✔
1232
        }
1✔
1233

1234
        return nr, nil
1✔
1235
}
1236

1237
// AddIPv4 adds the IPv4 entry described by e to the RIB. If the explicitReplace
1238
// argument is set to true, the entry is checked for existence before it is replaced
1239
// otherwise, replaces are implicit. It returns a bool that indicates whether the
1240
// entry was installed, a IPv4 entry that represents the replaced entry, and an
1241
// error which can be considered fatal (i.e., there is no future possibility of
1242
// this entry becoming valid).)
1243
func (r *RIBHolder) AddIPv4(e *aftpb.Afts_Ipv4EntryKey, explicitReplace bool) (bool, *aft.Afts_Ipv4Entry, error) {
1✔
1244
        if r.r == nil {
2✔
1245
                return false, nil, errors.New("invalid RIB structure, nil")
1✔
1246
        }
1✔
1247

1248
        if e == nil {
2✔
1249
                return false, nil, errors.New("nil IPv4 Entry provided")
1✔
1250
        }
1✔
1251

1252
        // This is a hack, since ygot does not know that the field that we
1253
        // have provided is a list entry, then it doesn't do the right thing. So
1254
        // we just give it the root so that it knows.
1255
        nr, err := candidateRIB(&aftpb.Afts{
1✔
1256
                Ipv4Entry: []*aftpb.Afts_Ipv4EntryKey{e},
1✔
1257
        })
1✔
1258
        if err != nil {
2✔
1259
                return false, nil, fmt.Errorf("invalid IPv4Entry, %v", err)
1✔
1260
        }
1✔
1261

1262
        if explicitReplace && !r.ipv4Exists(e.GetPrefix()) {
1✔
1263
                return false, nil, fmt.Errorf("cannot replace IPv4 Entry %s, does not exist", e.GetPrefix())
×
1264
        }
×
1265

1266
        var orig *aft.Afts_Ipv4Entry
1✔
1267
        // If we are replacing this entry, return the original to allow the caller to handle any
1✔
1268
        // refcounting that is required.
1✔
1269
        if explicitReplace || r.ipv4Exists(e.GetPrefix()) {
2✔
1270
                orig = r.retrieveIPv4(e.GetPrefix())
1✔
1271
        }
1✔
1272

1273
        if r.checkFn != nil {
2✔
1274
                ok, err := r.checkFn(constants.Add, nr)
1✔
1275
                if err != nil {
2✔
1276
                        // This entry can never be installed, so return the error
1✔
1277
                        // to the caller directly -- signalling to them not to retry.
1✔
1278
                        return false, nil, err
1✔
1279
                }
1✔
1280
                if !ok {
2✔
1281
                        // The checkFn validated the entry and found it to be OK, but
1✔
1282
                        // indicated that we should not merge it into the RIB because
1✔
1283
                        // some prerequisite was not satisfied. Based on this, we
1✔
1284
                        // return false (we didn't install it), but indicate with err == nil
1✔
1285
                        // that the caller can retry this entry at some later point, and we'll
1✔
1286
                        // run the checkFn again to see whether it can now be installed.
1✔
1287
                        return false, nil, nil
1✔
1288
                }
1✔
1289
        }
1290

1291
        if _, err := r.doAddIPv4(e.GetPrefix(), nr); err != nil {
1✔
1292
                return false, nil, err
×
1293
        }
×
1294

1295
        // We expect that there is just a single entry here since we are
1296
        // being called based on a single entry, but we loop since we don't
1297
        // know the key.
1298
        if r.postChangeHook != nil {
2✔
1299
                for _, ip4 := range nr.Afts.Ipv4Entry {
2✔
1300
                        r.postChangeHook(constants.Add, unixTS(), r.name, ip4)
1✔
1301
                }
1✔
1302
        }
1303

1304
        return true, orig, nil
1✔
1305
}
1306

1307
// ipv4Exists returns true if the IPv4 prefix exists within the RIBHolder.
1308
func (r *RIBHolder) ipv4Exists(prefix string) bool {
1✔
1309
        r.mu.RLock()
1✔
1310
        defer r.mu.RUnlock()
1✔
1311
        _, ok := r.r.GetAfts().Ipv4Entry[prefix]
1✔
1312
        return ok
1✔
1313
}
1✔
1314

1315
// doAddIPv4 adds an IPv4Entry holding the shortest possible lock on the RIB.
1316
// It returns a bool indicating whether this was an implicit replace.
1317
func (r *RIBHolder) doAddIPv4(pfx string, newRIB *aft.RIB) (bool, error) {
1✔
1318
        r.mu.Lock()
1✔
1319
        defer r.mu.Unlock()
1✔
1320

1✔
1321
        // Sanity check.
1✔
1322
        if nhg, nh := len(newRIB.Afts.NextHopGroup), len(newRIB.Afts.NextHop); nhg != 0 || nh != 0 {
1✔
1323
                return false, fmt.Errorf("candidate RIB specifies entries other than NextHopGroups, got: %d nhg, %d nh", nhg, nh)
×
1324
        }
×
1325

1326
        // Check whether this is an implicit replace.
1327
        _, implicit := r.r.GetAfts().Ipv4Entry[pfx]
1✔
1328

1✔
1329
        // MergeStructInto doesn't completely replace a list entry if it finds a missing key,
1✔
1330
        // so will append the two entries together.
1✔
1331
        // We don't use Delete itself because it will deadlock (we already hold the lock).
1✔
1332
        delete(r.r.GetAfts().Ipv4Entry, pfx)
1✔
1333

1✔
1334
        // TODO(robjs): consider what happens if this fails -- we may leave the RIB in
1✔
1335
        // an inconsistent state.
1✔
1336
        if err := ygot.MergeStructInto(r.r, newRIB); err != nil {
1✔
1337
                return false, fmt.Errorf("cannot merge candidate RIB into existing RIB, %v", err)
×
1338
        }
×
1339
        return implicit, nil
1✔
1340
}
1341

1342
// DeleteIPv4 removes the IPv4 entry e from the RIB. It returns a boolean
1343
// indicating whether the entry has been removed, a copy of the entry that was
1344
// removed  and an error if the message cannot be parsed. Per the gRIBI specification,
1345
// the payload of the entry is not compared.
1346
func (r *RIBHolder) DeleteIPv4(e *aftpb.Afts_Ipv4EntryKey) (bool, *aft.Afts_Ipv4Entry, error) {
1✔
1347
        if e == nil {
2✔
1348
                return false, nil, errors.New("nil entry provided")
1✔
1349
        }
1✔
1350

1351
        if r.r == nil {
1✔
1352
                return false, nil, errors.New("invalid RIB structure, nil")
×
1353
        }
×
1354

1355
        de := r.retrieveIPv4(e.GetPrefix())
1✔
1356

1✔
1357
        rr := &aft.RIB{}
1✔
1358
        rr.GetOrCreateAfts().GetOrCreateIpv4Entry(e.GetPrefix())
1✔
1359
        if r.checkFn != nil {
2✔
1360
                ok, err := r.checkFn(constants.Delete, rr)
1✔
1361
                switch {
1✔
1362
                case err != nil:
×
1363
                        // the check told us this was fatal for this entry -> we should return.
×
1364
                        return false, nil, err
×
1365
                case !ok:
×
1366
                        // otherwise, we just didn't do this operation.
×
1367
                        return false, nil, nil
×
1368
                }
1369
        }
1370

1371
        r.doDeleteIPv4(e.GetPrefix())
1✔
1372

1✔
1373
        if r.postChangeHook != nil {
2✔
1374
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
1✔
1375
        }
1✔
1376

1377
        return true, de, nil
1✔
1378
}
1379

1380
// retrieveIPv4 returns the specified IPv4Entry, holding a lock
1381
// on the RIBHolder as it does so.
1382
func (r *RIBHolder) retrieveIPv4(prefix string) *aft.Afts_Ipv4Entry {
1✔
1383
        r.mu.RLock()
1✔
1384
        defer r.mu.RUnlock()
1✔
1385
        return r.r.Afts.Ipv4Entry[prefix]
1✔
1386
}
1✔
1387

1388
// doDeleteIPv4 deletes pfx from the IPv4Entry RIB holding the shortest possible lock.
1389
func (r *RIBHolder) doDeleteIPv4(pfx string) {
1✔
1390
        r.mu.Lock()
1✔
1391
        defer r.mu.Unlock()
1✔
1392
        delete(r.r.Afts.Ipv4Entry, pfx)
1✔
1393
}
1✔
1394

1395
// locklessDeleteIPv4 removes the next-hop with the specified prefix without
1396
// holding a lock on the RIB. The caller MUST hold the relevant lock. It returns
1397
// an error if the entry cannot be found.
1398
func (r *RIBHolder) locklessDeleteIPv4(prefix string) error {
1✔
1399
        de := r.r.Afts.Ipv4Entry[prefix]
1✔
1400
        if de == nil {
1✔
1401
                return fmt.Errorf("cannot find prefix %s", prefix)
×
1402
        }
×
1403

1404
        delete(r.r.Afts.Ipv4Entry, prefix)
1✔
1405
        if r.postChangeHook != nil {
1✔
1406
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
×
1407
        }
×
1408
        return nil
1✔
1409
}
1410

1411
// AddIPv6 adds the IPv6 entry specified by e to the RIB, explicitReplace indicates whether
1412
// the "add" operation that is being performed is actually an explicit replace of a specific
1413
// prefix such that an error can be returned.
1414
func (r *RIBHolder) AddIPv6(e *aftpb.Afts_Ipv6EntryKey, explicitReplace bool) (bool, *aft.Afts_Ipv6Entry, error) {
1✔
1415
        if r.r == nil {
1✔
1416
                return false, nil, errors.New("invalid RIB structure, nil")
×
1417
        }
×
1418

1419
        if e == nil {
1✔
1420
                return false, nil, errors.New("nil IPv6 Entry provided")
×
1421
        }
×
1422

1423
        nr, err := candidateRIB(&aftpb.Afts{
1✔
1424
                Ipv6Entry: []*aftpb.Afts_Ipv6EntryKey{e},
1✔
1425
        })
1✔
1426
        if err != nil {
2✔
1427
                return false, nil, fmt.Errorf("invalid IPv6Entry, %v", err)
1✔
1428
        }
1✔
1429

1430
        if explicitReplace && !r.ipv6Exists(e.GetPrefix()) {
2✔
1431
                return false, nil, fmt.Errorf("cannot replace IPv6 Entry %s, does not exist", e.GetPrefix())
1✔
1432
        }
1✔
1433

1434
        var orig *aft.Afts_Ipv6Entry
1✔
1435
        if r.ipv6Exists(e.GetPrefix()) {
2✔
1436
                orig = r.retrieveIPv6(e.GetPrefix())
1✔
1437
        }
1✔
1438

1439
        if r.checkFn != nil {
2✔
1440
                ok, err := r.checkFn(constants.Add, nr)
1✔
1441
                if err != nil {
2✔
1442
                        return false, nil, err
1✔
1443
                }
1✔
1444
                if !ok {
1✔
UNCOV
1445
                        return false, nil, nil
×
UNCOV
1446
                }
×
1447
        }
1448

1449
        if _, err := r.doAddIPv6(e.GetPrefix(), nr); err != nil {
1✔
1450
                return false, nil, err
×
1451
        }
×
1452

1453
        if r.postChangeHook != nil {
2✔
1454
                for _, ip4 := range nr.Afts.Ipv6Entry {
2✔
1455
                        r.postChangeHook(constants.Add, unixTS(), r.name, ip4)
1✔
1456
                }
1✔
1457
        }
1458

1459
        return true, orig, nil
1✔
1460
}
1461

1462
// ipv6Exists determines whether the specified prefix exists within the RIB.
1463
func (r *RIBHolder) ipv6Exists(prefix string) bool {
1✔
1464
        r.mu.RLock()
1✔
1465
        defer r.mu.RUnlock()
1✔
1466
        _, ok := r.r.GetAfts().Ipv6Entry[prefix]
1✔
1467
        return ok
1✔
1468
}
1✔
1469

1470
// doAddIPv6 implements the addition of the prefix pfx to the RIB using the supplied
1471
// newRIB as the entries that should be merged into this RIB.
1472
func (r *RIBHolder) doAddIPv6(pfx string, newRIB *aft.RIB) (bool, error) {
1✔
1473
        r.mu.Lock()
1✔
1474
        defer r.mu.Unlock()
1✔
1475

1✔
1476
        if nhg, nh := len(newRIB.Afts.NextHopGroup), len(newRIB.Afts.NextHop); nhg != 0 || nh != 0 {
1✔
1477
                return false, fmt.Errorf("candidate RIB specifies entries other than NextHopGroups, got: %d nhg, %d nh", nhg, nh)
×
1478
        }
×
1479

1480
        _, implicit := r.r.GetAfts().Ipv6Entry[pfx]
1✔
1481

1✔
1482
        delete(r.r.GetAfts().Ipv6Entry, pfx)
1✔
1483

1✔
1484
        // TODO(robjs): consider what happens if this fails -- we may leave the RIB in
1✔
1485
        // an inconsistent state.
1✔
1486
        if err := ygot.MergeStructInto(r.r, newRIB); err != nil {
1✔
1487
                return false, fmt.Errorf("cannot merge candidate RIB into existing RIB, %v", err)
×
1488
        }
×
1489
        return implicit, nil
1✔
1490
}
1491

1492
// DeleteIPv6 deletes the entry specified by e from the RIB, returning the entry that was removed.
1493
func (r *RIBHolder) DeleteIPv6(e *aftpb.Afts_Ipv6EntryKey) (bool, *aft.Afts_Ipv6Entry, error) {
1✔
1494
        if e == nil {
1✔
1495
                return false, nil, errors.New("nil entry provided")
×
1496
        }
×
1497

1498
        if r.r == nil {
1✔
1499
                return false, nil, errors.New("invalid RIB structure, nil")
×
1500
        }
×
1501

1502
        de := r.retrieveIPv6(e.GetPrefix())
1✔
1503

1✔
1504
        rr := &aft.RIB{}
1✔
1505
        rr.GetOrCreateAfts().GetOrCreateIpv6Entry(e.GetPrefix())
1✔
1506
        if r.checkFn != nil {
2✔
1507
                ok, err := r.checkFn(constants.Delete, rr)
1✔
1508
                switch {
1✔
1509
                case err != nil:
×
1510
                        // the check told us this was fatal for this entry -> we should return.
×
1511
                        return false, nil, err
×
1512
                case !ok:
×
1513
                        // otherwise, we just didn't do this operation.
×
1514
                        return false, nil, nil
×
1515
                }
1516
        }
1517

1518
        r.doDeleteIPv6(e.GetPrefix())
1✔
1519

1✔
1520
        if r.postChangeHook != nil {
1✔
1521
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
×
1522
        }
×
1523

1524
        return true, de, nil
1✔
1525
}
1526

1527
// retrieveIPv6 retrieves the contents of the entry for prefix from the RIB, holding
1528
// the shortest possible lock.
1529
func (r *RIBHolder) retrieveIPv6(prefix string) *aft.Afts_Ipv6Entry {
1✔
1530
        r.mu.RLock()
1✔
1531
        defer r.mu.RUnlock()
1✔
1532
        return r.r.Afts.Ipv6Entry[prefix]
1✔
1533
}
1✔
1534

1535
// doDeleteIPv6 deletes the prefix pfx from the RIB, holding the shortest possible lock.
1536
func (r *RIBHolder) doDeleteIPv6(pfx string) {
1✔
1537
        r.mu.Lock()
1✔
1538
        defer r.mu.Unlock()
1✔
1539
        delete(r.r.Afts.Ipv6Entry, pfx)
1✔
1540
}
1✔
1541

1542
// locklessDeleteIPv6 deletes the entry for prefix from the RIB, without holding the lock
1543
// caution must be exercised and the lock MUST be held to call this function.
1544
func (r *RIBHolder) locklessDeleteIPv6(prefix string) error {
1✔
1545
        de := r.r.Afts.Ipv6Entry[prefix]
1✔
1546
        if de == nil {
1✔
1547
                return fmt.Errorf("cannot find prefix %s", prefix)
×
1548
        }
×
1549

1550
        delete(r.r.Afts.Ipv6Entry, prefix)
1✔
1551
        if r.postChangeHook != nil {
1✔
1552
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
×
1553
        }
×
1554
        return nil
1✔
1555
}
1556

1557
// AddMPLS adds the specified label entry described by e to the RIB. If the
1558
// explicitReplace argument is set to true, it checks whether the entry exists
1559
// before it is replaced, otherwise replaces are implicit. It returns a bool
1560
// which indicates whether the entry was added, a second bool that indicates
1561
// whether the programming was an implicit replace and an error that should be
1562
// considered fatal by the caller (i.e., there is no possibility that this
1563
// entry can become valid and be installed in the future).
1564
func (r *RIBHolder) AddMPLS(e *aftpb.Afts_LabelEntryKey, explicitReplace bool) (bool, *aft.Afts_LabelEntry, error) {
1✔
1565
        if r.r == nil {
1✔
1566
                return false, nil, errors.New("invalid RIB structure, nil")
×
1567
        }
×
1568

1569
        if e == nil {
1✔
1570
                return false, nil, errors.New("nil MPLS Entry provided")
×
1571
        }
×
1572

1573
        nr, err := candidateRIB(&aftpb.Afts{
1✔
1574
                LabelEntry: []*aftpb.Afts_LabelEntryKey{e},
1✔
1575
        })
1✔
1576
        if err != nil {
2✔
1577
                return false, nil, fmt.Errorf("invalid LabelEntry, %v", err)
1✔
1578
        }
1✔
1579

1580
        if explicitReplace && !r.mplsExists(uint32(e.GetLabelUint64())) {
2✔
1581
                return false, nil, fmt.Errorf("cannot replace MPLS Entry %d, does not exist", e.GetLabelUint64())
1✔
1582
        }
1✔
1583

1584
        var orig *aft.Afts_LabelEntry
1✔
1585
        if r.mplsExists(uint32(e.GetLabelUint64())) {
2✔
1586
                orig = r.retrieveMPLS(uint32(e.GetLabelUint64()))
1✔
1587
        }
1✔
1588

1589
        if r.checkFn != nil {
2✔
1590
                ok, err := r.checkFn(constants.Add, nr)
1✔
1591
                if err != nil {
2✔
1592
                        // This entry can never be installed, so return the error
1✔
1593
                        // to the caller directly -- signalling to them not to retry.
1✔
1594
                        return false, nil, err
1✔
1595
                }
1✔
1596
                if !ok {
1✔
1597
                        // The checkFn validated the entry and found it to be OK, but
×
1598
                        // indicated that we should not merge it into the RIB because
×
1599
                        // some prerequisite was not satisfied. Based on this, we
×
1600
                        // return false (we didn't install it), but indicate with err == nil
×
1601
                        // that the caller can retry this entry at some later point, and we'll
×
1602
                        // run the checkFn again to see whether it can now be installed.
×
1603
                        return false, nil, nil
×
1604
                }
×
1605
        }
1606

1607
        if _, err := r.doAddMPLS(uint32(e.GetLabelUint64()), nr); err != nil {
1✔
1608
                return false, nil, err
×
1609
        }
×
1610

1611
        // We expect that there is just a single entry here since we are
1612
        // being called based on a single entry, but we loop since we don't
1613
        // know the key.
1614
        if r.postChangeHook != nil {
2✔
1615
                for _, mpls := range nr.Afts.LabelEntry {
2✔
1616
                        r.postChangeHook(constants.Add, unixTS(), r.name, mpls)
1✔
1617
                }
1✔
1618
        }
1619

1620
        return true, orig, nil
1✔
1621
}
1622

1623
// mplsExists validates whether an entry exists in r for the specified MPLS
1624
// label entry. It returns true if such an entry exists.
1625
func (r *RIBHolder) mplsExists(label uint32) bool {
1✔
1626
        r.mu.RLock()
1✔
1627
        defer r.mu.RUnlock()
1✔
1628
        _, ok := r.r.GetAfts().LabelEntry[aft.UnionUint32(label)]
1✔
1629
        return ok
1✔
1630
}
1✔
1631

1632
// doAddMPLS implements the addition of the label entry specified by label
1633
// with the contents of the newRIB specified. It holds the shortest possible
1634
// lock on the RIB. doMPLS returns a bool indicating whether the update was
1635
// an implicit replace.
1636
func (r *RIBHolder) doAddMPLS(label uint32, newRIB *aft.RIB) (bool, error) {
1✔
1637
        r.mu.Lock()
1✔
1638
        defer r.mu.Unlock()
1✔
1639

1✔
1640
        // Sanity check.
1✔
1641
        if nhg, nh := len(newRIB.Afts.NextHopGroup), len(newRIB.Afts.NextHop); nhg != 0 || nh != 0 {
1✔
1642
                return false, fmt.Errorf("candidate RIB specifies entries other than NextHopGroups, got: %d nhg, %d nh", nhg, nh)
×
1643
        }
×
1644

1645
        // Check whether this is an implicit replace.
1646
        _, implicit := r.r.GetAfts().LabelEntry[aft.UnionUint32(label)]
1✔
1647

1✔
1648
        // MergeStructInto doesn't completely replace a list entry if it finds a missing key,
1✔
1649
        // so will append the two entries together.
1✔
1650
        // We don't use Delete itself because it will deadlock (we already hold the lock).
1✔
1651
        delete(r.r.GetAfts().LabelEntry, aft.UnionUint32(label))
1✔
1652

1✔
1653
        // TODO(robjs): consider what happens if this fails -- we may leave the RIB in
1✔
1654
        // an inconsistent state.
1✔
1655
        if err := ygot.MergeStructInto(r.r, newRIB); err != nil {
1✔
1656
                return false, fmt.Errorf("cannot merge candidate RIB into existing RIB, %v", err)
×
1657
        }
×
1658
        return implicit, nil
1✔
1659
}
1660

1661
// DeleteMPLS removes the MPLS label entry e from the RIB. It returns a
1662
// boolean indicating whether the entry has been removed, a copy of the entry
1663
// that was removed, and an error if the message cannot be parsed. Per the gRIBI
1664
// specification the payload of the entry is not compared the existing entry
1665
// before deleting it.
1666
func (r *RIBHolder) DeleteMPLS(e *aftpb.Afts_LabelEntryKey) (bool, *aft.Afts_LabelEntry, error) {
1✔
1667
        if e == nil {
1✔
1668
                return false, nil, errors.New("nil Label entry provided")
×
1669
        }
×
1670

1671
        if r.r == nil {
1✔
1672
                return false, nil, errors.New("invalid RIB structure, nil")
×
1673
        }
×
1674

1675
        if _, ok := e.GetLabel().(*aftpb.Afts_LabelEntryKey_LabelUint64); !ok {
2✔
1676
                return false, nil, fmt.Errorf("unsupported label type %T, only uint64 labels are supported, %v", e, e)
1✔
1677
        }
1✔
1678

1679
        lbl := uint32(e.GetLabelUint64())
1✔
1680

1✔
1681
        de := r.retrieveMPLS(lbl)
1✔
1682

1✔
1683
        rr := &aft.RIB{}
1✔
1684
        rr.GetOrCreateAfts().GetOrCreateLabelEntry(aft.UnionUint32(lbl))
1✔
1685

1✔
1686
        if r.checkFn != nil {
2✔
1687
                ok, err := r.checkFn(constants.Delete, rr)
1✔
1688
                switch {
1✔
1689
                case err != nil:
×
1690
                        // the check told us this was a fatal error that cannot be
×
1691
                        // recovered from.
×
1692
                        return false, nil, err
×
1693
                case !ok:
×
1694
                        // we did not complete this operation, but it can be retried.
×
1695
                        return false, nil, nil
×
1696
                }
1697
        }
1698

1699
        r.doDeleteMPLS(lbl)
1✔
1700

1✔
1701
        if r.postChangeHook != nil {
2✔
1702
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
1✔
1703
        }
1✔
1704

1705
        return true, de, nil
1✔
1706
}
1707

1708
// retrieveMPLS returns the MPLS entry specified by label, holding a lock
1709
// on the RIBHolder as it does so.
1710
func (r *RIBHolder) retrieveMPLS(label uint32) *aft.Afts_LabelEntry {
1✔
1711
        r.mu.RLock()
1✔
1712
        defer r.mu.RUnlock()
1✔
1713
        return r.r.Afts.LabelEntry[aft.UnionUint32(label)]
1✔
1714
}
1✔
1715

1716
// doDeleteMPLS deletes label from the LabelEntry RIB holding the shortest
1717
// possible lock.
1718
func (r *RIBHolder) doDeleteMPLS(label uint32) {
1✔
1719
        r.mu.Lock()
1✔
1720
        defer r.mu.Unlock()
1✔
1721
        delete(r.r.Afts.LabelEntry, aft.UnionUint32(label))
1✔
1722
}
1✔
1723

1724
// locklessDeleteMPLS removes the label forwarding entry with the specified label
1725
// from the RIB, without holding the lock on the AFT. The calling routine
1726
// MUST ensure that it holds the lock to ensure thread-safe operation.
1727
func (r *RIBHolder) locklessDeleteMPLS(label aft.Afts_LabelEntry_Label_Union) error {
1✔
1728
        de := r.r.Afts.LabelEntry[label]
1✔
1729
        if de == nil {
1✔
1730
                return fmt.Errorf("cannot find label %d", label)
×
1731
        }
×
1732

1733
        delete(r.r.Afts.LabelEntry, label)
1✔
1734
        if r.postChangeHook != nil {
1✔
1735
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
×
1736
        }
×
1737
        return nil
1✔
1738
}
1739

1740
// DeleteNextHopGroup removes the NextHopGroup entry e from the RIB. It returns a boolean
1741
// indicating whether the entry has been removed, a copy of the next-hop-group that was
1742
// removed and an error if the message cannot be parsed. Per the gRIBI specification, the
1743
// payload of the entry is not compared.
1744
func (r *RIBHolder) DeleteNextHopGroup(e *aftpb.Afts_NextHopGroupKey) (bool, *aft.Afts_NextHopGroup, error) {
1✔
1745
        if e == nil {
1✔
1746
                return false, nil, errors.New("nil entry provided")
×
1747
        }
×
1748

1749
        if r.r == nil {
1✔
1750
                return false, nil, errors.New("invalid RIB structure, nil")
×
1751
        }
×
1752

1753
        if e.GetId() == 0 {
2✔
1754
                return false, nil, errors.New("invalid NHG ID 0")
1✔
1755
        }
1✔
1756

1757
        de := r.retrieveNHG(e.GetId())
1✔
1758

1✔
1759
        rr := &aft.RIB{}
1✔
1760
        rr.GetOrCreateAfts().GetOrCreateNextHopGroup(e.GetId())
1✔
1761
        if r.checkFn != nil {
2✔
1762
                ok, err := r.checkFn(constants.Delete, rr)
1✔
1763
                switch {
1✔
1764
                case err != nil:
×
1765
                        // the check told us this was fatal for this entry -> we should return.
×
1766
                        return false, nil, err
×
1767
                case !ok:
1✔
1768
                        // otherwise, we just didn't do this operation.
1✔
1769
                        return false, nil, nil
1✔
1770
                }
1771
        }
1772

1773
        r.doDeleteNHG(e.GetId())
1✔
1774

1✔
1775
        if r.postChangeHook != nil {
2✔
1776
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
1✔
1777
        }
1✔
1778

1779
        return true, de, nil
1✔
1780
}
1781

1782
// retrieveNHG returns the specified NextHopGroup, holding a lock
1783
// on the RIBHolder as it does so.
1784
func (r *RIBHolder) retrieveNHG(id uint64) *aft.Afts_NextHopGroup {
1✔
1785
        r.mu.RLock()
1✔
1786
        defer r.mu.RUnlock()
1✔
1787
        return r.r.Afts.NextHopGroup[id]
1✔
1788
}
1✔
1789

1790
// locklessDeleteNHG removes the next-hop-group with the specified ID without
1791
// holding a lock on the RIB. The caller MUST hold the relevant lock. It returns
1792
// an error if the entry cannot be found.
1793
func (r *RIBHolder) locklessDeleteNHG(id uint64) error {
1✔
1794
        de := r.r.Afts.NextHopGroup[id]
1✔
1795
        if de == nil {
1✔
1796
                return fmt.Errorf("cannot find NHG %d", id)
×
1797
        }
×
1798

1799
        // NextHops must be in the same network instance and NHGs.
1800
        for idx := range de.NextHop {
2✔
1801
                r.decNHRefCount(idx)
1✔
1802
        }
1✔
1803

1804
        delete(r.r.Afts.NextHopGroup, id)
1✔
1805
        if r.postChangeHook != nil {
1✔
1806
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
×
1807
        }
×
1808
        return nil
1✔
1809
}
1810

1811
// DeleteNextHop removes the NextHop entry e from the RIB. It returns a boolean
1812
// indicating whether the entry has been removed, a copy of the group that was
1813
// removed and an error if the message cannot be parsed. Per the gRIBI specification,
1814
// the payload of the entry is not compared.
1815
func (r *RIBHolder) DeleteNextHop(e *aftpb.Afts_NextHopKey) (bool, *aft.Afts_NextHop, error) {
1✔
1816
        if e == nil {
1✔
1817
                return false, nil, errors.New("nil entry provided")
×
1818
        }
×
1819

1820
        if r.r == nil {
1✔
1821
                return false, nil, errors.New("invalid RIB structure, nil")
×
1822
        }
×
1823

1824
        if e.GetIndex() == 0 {
1✔
1825
                return false, nil, fmt.Errorf("invalid NH index 0")
×
1826
        }
×
1827

1828
        de := r.retrieveNH(e.GetIndex())
1✔
1829

1✔
1830
        rr := &aft.RIB{}
1✔
1831
        rr.GetOrCreateAfts().GetOrCreateNextHop(e.GetIndex())
1✔
1832
        if r.checkFn != nil {
2✔
1833
                ok, err := r.checkFn(constants.Delete, rr)
1✔
1834
                switch {
1✔
1835
                case err != nil:
×
1836
                        // the check told us this was fatal for this entry -> we should return.
×
1837
                        return false, nil, err
×
1838
                case !ok:
1✔
1839
                        // otherwise, we just didn't do this operation.
1✔
1840
                        return false, nil, nil
1✔
1841
                }
1842
        }
1843
        r.doDeleteNH(e.GetIndex())
1✔
1844

1✔
1845
        if r.postChangeHook != nil {
2✔
1846
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
1✔
1847
        }
1✔
1848

1849
        return true, de, nil
1✔
1850
}
1851

1852
// retrieveNH returns the specified NextHop, holding a lock
1853
// on the RIBHolder as it does so.
1854
func (r *RIBHolder) retrieveNH(index uint64) *aft.Afts_NextHop {
1✔
1855
        r.mu.RLock()
1✔
1856
        defer r.mu.RUnlock()
1✔
1857
        return r.r.Afts.NextHop[index]
1✔
1858
}
1✔
1859

1860
// locklessDeleteNH removes the next-hop with the specified index without
1861
// holding a lock on the RIB. The caller MUST hold the relevant lock. It returns
1862
// an error if the entry cannot be found.
1863
func (r *RIBHolder) locklessDeleteNH(index uint64) error {
1✔
1864
        de := r.r.Afts.NextHop[index]
1✔
1865
        if de == nil {
1✔
1866
                return fmt.Errorf("cannot find NH %d", index)
×
1867
        }
×
1868

1869
        delete(r.r.Afts.NextHop, index)
1✔
1870
        if r.postChangeHook != nil {
1✔
1871
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
×
1872
        }
×
1873
        return nil
1✔
1874
}
1875

1876
// doDeleteNHG deletes the NHG with index idx from the NHG AFTm holding the shortest
1877
// possible lock.
1878
func (r *RIBHolder) doDeleteNHG(idx uint64) {
1✔
1879
        r.mu.Lock()
1✔
1880
        defer r.mu.Unlock()
1✔
1881
        delete(r.r.Afts.NextHopGroup, idx)
1✔
1882
}
1✔
1883

1884
// doDeleteNH deletes the NH with ID id from the NH AFT, holding the shortest possible
1885
// lock.
1886
func (r *RIBHolder) doDeleteNH(id uint64) {
1✔
1887
        r.mu.Lock()
1✔
1888
        defer r.mu.Unlock()
1✔
1889
        delete(r.r.Afts.NextHop, id)
1✔
1890
}
1✔
1891

1892
// AddNextHopGroup adds a NextHopGroup e to the RIBHolder receiver. The explicitReplace argument
1893
// determines whether the operation was an explicit replace, in which case an error is returned
1894
// if the entry does not exist. It returns a boolean
1895
// indicating whether the NHG was installed, a second bool indicating whether this was
1896
// a replace. If encounted it returns an error if the group is invalid.
1897
func (r *RIBHolder) AddNextHopGroup(e *aftpb.Afts_NextHopGroupKey, explicitReplace bool) (bool, *aft.Afts_NextHopGroup, error) {
1✔
1898
        if r.r == nil {
1✔
1899
                return false, nil, errors.New("invalid RIB structure, nil")
×
1900
        }
×
1901

1902
        if e == nil {
1✔
1903
                return false, nil, errors.New("nil NextHopGroup provided")
×
1904
        }
×
1905

1906
        nr, err := candidateRIB(&aftpb.Afts{
1✔
1907
                NextHopGroup: []*aftpb.Afts_NextHopGroupKey{e},
1✔
1908
        })
1✔
1909
        if err != nil {
1✔
1910
                return false, nil, fmt.Errorf("invalid NextHopGroup, %v", err)
×
1911
        }
×
1912

1913
        if explicitReplace && !r.nhgExists(e.GetId()) {
1✔
1914
                return false, nil, fmt.Errorf("cannot replace NextHopGroup %d, does not exist", e.GetId())
×
1915
        }
×
1916

1917
        var orig *aft.Afts_NextHopGroup
1✔
1918
        if r.nhgExists(e.GetId()) {
2✔
1919
                orig = r.retrieveNHG(e.GetId())
1✔
1920
        }
1✔
1921

1922
        if r.checkFn != nil {
2✔
1923
                ok, err := r.checkFn(constants.Add, nr)
1✔
1924
                if err != nil {
2✔
1925
                        // Entry can never be installed (see the documentation in
1✔
1926
                        // the AddIPv4 function for additional details).
1✔
1927
                        return false, nil, err
1✔
1928
                }
1✔
1929
                if !ok {
2✔
1930
                        log.Infof("NextHopGroup %d added to pending queue - not installed", e.GetId())
1✔
1931
                        // Entry is not valid for installation right now.
1✔
1932
                        return false, nil, nil
1✔
1933
                }
1✔
1934
        }
1935

1936
        if _, err := r.doAddNHG(e.GetId(), nr); err != nil {
1✔
1937
                return false, nil, err
×
1938
        }
×
1939

1940
        if r.postChangeHook != nil {
2✔
1941
                for _, nhg := range nr.Afts.NextHopGroup {
2✔
1942
                        r.postChangeHook(constants.Add, unixTS(), r.name, nhg)
1✔
1943
                }
1✔
1944
        }
1945

1946
        return true, orig, nil
1✔
1947
}
1948

1949
// nhgExists returns true if the NHG with ID id exists in the RIBHolder.
1950
func (r *RIBHolder) nhgExists(id uint64) bool {
1✔
1951
        r.mu.RLock()
1✔
1952
        defer r.mu.RUnlock()
1✔
1953
        _, ok := r.r.GetAfts().NextHopGroup[id]
1✔
1954
        return ok
1✔
1955
}
1✔
1956

1957
// doAddNHG adds a NHG holding the shortest possible lock on the RIB to avoid
1958
// deadlocking. It returns a boolean indicating whether this was a replace.
1959
func (r *RIBHolder) doAddNHG(ID uint64, newRIB *aft.RIB) (bool, error) {
1✔
1960
        r.mu.Lock()
1✔
1961
        defer r.mu.Unlock()
1✔
1962

1✔
1963
        // Sanity check.
1✔
1964
        if ip4, nh := len(newRIB.Afts.Ipv4Entry), len(newRIB.Afts.NextHop); ip4 != 0 || nh != 0 {
1✔
1965
                return false, fmt.Errorf("candidate RIB specifies entries other than NextHopGroups, got: %d ipv4, %d nh", ip4, nh)
×
1966
        }
×
1967

1968
        _, wasReplace := r.r.GetAfts().NextHopGroup[ID]
1✔
1969

1✔
1970
        // Handle implicit replace.
1✔
1971
        delete(r.r.GetAfts().NextHopGroup, ID)
1✔
1972

1✔
1973
        if err := ygot.MergeStructInto(r.r, newRIB); err != nil {
1✔
1974
                return false, fmt.Errorf("cannot merge candidate RIB into existing RIB, %v", err)
×
1975
        }
×
1976
        return wasReplace, nil
1✔
1977
}
1978

1979
// incNHGRefCount increments the reference count for the specified next-hop-group.
1980
func (r *RIBHolder) incNHGRefCount(i uint64) {
1✔
1981
        r.refCounts.mu.Lock()
1✔
1982
        defer r.refCounts.mu.Unlock()
1✔
1983
        r.refCounts.NextHopGroup[i]++
1✔
1984
}
1✔
1985

1986
// decNHGRefCount decrements the reference count for the specified next-hop-group.
1987
func (r *RIBHolder) decNHGRefCount(i uint64) {
1✔
1988
        r.refCounts.mu.Lock()
1✔
1989
        defer r.refCounts.mu.Unlock()
1✔
1990
        if r.refCounts.NextHopGroup[i] == 0 {
2✔
1991
                // prevent the refcount from rolling back - this is an error, since it
1✔
1992
                // means the implementation did not add references correctly.
1✔
1993
                return
1✔
1994
        }
1✔
1995
        r.refCounts.NextHopGroup[i]--
1✔
1996
}
1997

1998
// nhgReferenced indicates whether the next-hop-group has a refCount > 0.
1999
func (r *RIBHolder) nhgReferenced(i uint64) bool {
1✔
2000
        r.refCounts.mu.RLock()
1✔
2001
        defer r.refCounts.mu.RUnlock()
1✔
2002
        return r.refCounts.NextHopGroup[i] > 0
1✔
2003
}
1✔
2004

2005
// AddNextHop adds a new NextHop e to the RIBHolder receiver. If the explicitReplace
2006
// argument is set to true, AddNextHop verifies that the entry exists within the
2007
// RIB before replacing it, otherwise replaces are implicit. It returns a boolean
2008
// indicating whether the NextHop was installed, along with a second boolean that
2009
// indicates whether this was an implicit replace. If encountered, it returns an error
2010
// if the group is invalid.
2011
func (r *RIBHolder) AddNextHop(e *aftpb.Afts_NextHopKey, explicitReplace bool) (bool, *aft.Afts_NextHop, error) {
1✔
2012
        if r.r == nil {
1✔
2013
                return false, nil, errors.New("invalid RIB structure, nil")
×
2014
        }
×
2015

2016
        if e == nil {
1✔
2017
                return false, nil, errors.New("nil NextHop provided")
×
2018
        }
×
2019

2020
        nr, err := candidateRIB(&aftpb.Afts{
1✔
2021
                NextHop: []*aftpb.Afts_NextHopKey{e},
1✔
2022
        })
1✔
2023
        if err != nil {
2✔
2024
                return false, nil, fmt.Errorf("invalid NextHop, %v", err)
1✔
2025
        }
1✔
2026

2027
        if explicitReplace && !r.nhExists(e.GetIndex()) {
1✔
2028
                return false, nil, fmt.Errorf("cannot replace NextHop %d, does not exist", e.GetIndex())
×
2029
        }
×
2030

2031
        var replaced *aft.Afts_NextHop
1✔
2032
        if r.nhExists(e.GetIndex()) {
2✔
2033
                replaced = r.retrieveNH(e.GetIndex())
1✔
2034
        }
1✔
2035

2036
        if r.checkFn != nil {
2✔
2037
                ok, err := r.checkFn(constants.Add, nr)
1✔
2038
                if err != nil {
1✔
2039
                        // Entry can never be installed (see the documentation in
×
2040
                        // the AddIPv4 function for additional details).
×
2041
                        return false, nil, err
×
2042
                }
×
2043
                if !ok {
1✔
2044
                        // Entry is not valid for installation right now.
×
2045
                        return false, nil, nil
×
2046
                }
×
2047
        }
2048

2049
        if _, err := r.doAddNH(e.GetIndex(), nr); err != nil {
1✔
2050
                return false, nil, err
×
2051
        }
×
2052

2053
        if r.postChangeHook != nil {
2✔
2054
                for _, nh := range nr.Afts.NextHop {
2✔
2055
                        r.postChangeHook(constants.Add, unixTS(), r.name, nh)
1✔
2056
                }
1✔
2057
        }
2058

2059
        return true, replaced, nil
1✔
2060
}
2061

2062
// nhExists returns true if the next-hop with index exists within the RIBHolder.
2063
func (r *RIBHolder) nhExists(index uint64) bool {
1✔
2064
        r.mu.RLock()
1✔
2065
        defer r.mu.RUnlock()
1✔
2066
        _, ok := r.r.GetAfts().NextHop[index]
1✔
2067
        return ok
1✔
2068
}
1✔
2069

2070
// doAddNH adds a NH holding the shortest possible lock on the RIB to avoid
2071
// deadlocking. It returns a boolean indicating whether the add was an implicit
2072
// replace.
2073
func (r *RIBHolder) doAddNH(index uint64, newRIB *aft.RIB) (bool, error) {
1✔
2074
        r.mu.Lock()
1✔
2075
        defer r.mu.Unlock()
1✔
2076

1✔
2077
        // Sanity check.
1✔
2078
        if ip4, nhg := len(newRIB.Afts.Ipv4Entry), len(newRIB.Afts.NextHopGroup); ip4 != 0 || nhg != 0 {
1✔
2079
                return false, fmt.Errorf("candidate RIB specifies entries other than NextHopGroups, got: %d ipv4, %d nhg", ip4, nhg)
×
2080
        }
×
2081

2082
        _, implicit := r.r.GetAfts().NextHop[index]
1✔
2083

1✔
2084
        // Handle implicit replace.
1✔
2085
        delete(r.r.GetAfts().NextHop, index)
1✔
2086

1✔
2087
        if err := ygot.MergeStructInto(r.r, newRIB); err != nil {
1✔
2088
                return false, fmt.Errorf("cannot merge candidate RIB into existing RIB, %v", err)
×
2089
        }
×
2090
        return implicit, nil
1✔
2091
}
2092

2093
// incNHRefCount increments the reference count for the specified next-hop-group.
2094
func (r *RIBHolder) incNHRefCount(i uint64) {
1✔
2095
        r.refCounts.mu.Lock()
1✔
2096
        defer r.refCounts.mu.Unlock()
1✔
2097
        r.refCounts.NextHop[i]++
1✔
2098
}
1✔
2099

2100
// decNHRefCount decrements the reference count for the specified next-hop-group.
2101
func (r *RIBHolder) decNHRefCount(i uint64) {
1✔
2102
        r.refCounts.mu.Lock()
1✔
2103
        defer r.refCounts.mu.Unlock()
1✔
2104
        if r.refCounts.NextHop[i] == 0 {
2✔
2105
                // prevent the refcount from rolling back - this is an error, since it
1✔
2106
                // means the implementation did not add references correctly.
1✔
2107
                return
1✔
2108
        }
1✔
2109
        r.refCounts.NextHop[i]--
1✔
2110
}
2111

2112
// nhReferenced indicates whether the next-hop-group has a refCount > 0.
2113
func (r *RIBHolder) nhReferenced(i uint64) bool {
1✔
2114
        r.refCounts.mu.RLock()
1✔
2115
        defer r.refCounts.mu.RUnlock()
1✔
2116
        return r.refCounts.NextHop[i] > 0
1✔
2117
}
1✔
2118

2119
// ConcreteIPv4Proto takes the input Ipv4Entry GoStruct and returns it as a gRIBI
2120
// Ipv4EntryKey protobuf. It returns an error if the protobuf cannot be marshalled.
2121
func ConcreteIPv4Proto(e *aft.Afts_Ipv4Entry) (*aftpb.Afts_Ipv4EntryKey, error) {
1✔
2122
        ip4proto := &aftpb.Afts_Ipv4Entry{}
1✔
2123
        if err := protoFromGoStruct(e, &gpb.Path{
1✔
2124
                Elem: []*gpb.PathElem{{
1✔
2125
                        Name: "afts",
1✔
2126
                }, {
1✔
2127
                        Name: "ipv4-unicast",
1✔
2128
                }, {
1✔
2129
                        Name: "ipv4-entry",
1✔
2130
                }},
1✔
2131
        }, ip4proto); err != nil {
1✔
2132
                return nil, fmt.Errorf("cannot marshal IPv4 prefix %s, %v", e.GetPrefix(), err)
×
2133
        }
×
2134
        return &aftpb.Afts_Ipv4EntryKey{
1✔
2135
                Prefix:    *e.Prefix,
1✔
2136
                Ipv4Entry: ip4proto,
1✔
2137
        }, nil
1✔
2138
}
2139

2140
// ConcreteIPv6Proto takes the input Ipv6Entry GoStruct and returns it as a gRIBI
2141
// Ipv6EntryKey protobuf. It returns an error if the protobuf cannot be marshalled.
2142
func ConcreteIPv6Proto(e *aft.Afts_Ipv6Entry) (*aftpb.Afts_Ipv6EntryKey, error) {
1✔
2143
        ip6proto := &aftpb.Afts_Ipv6Entry{}
1✔
2144
        if err := protoFromGoStruct(e, &gpb.Path{
1✔
2145
                Elem: []*gpb.PathElem{{
1✔
2146
                        Name: "afts",
1✔
2147
                }, {
1✔
2148
                        Name: "ipv6-unicast",
1✔
2149
                }, {
1✔
2150
                        Name: "ipv6-entry",
1✔
2151
                }},
1✔
2152
        }, ip6proto); err != nil {
1✔
2153
                return nil, fmt.Errorf("cannot marshal IPv6 prefix %s, %v", e.GetPrefix(), err)
×
2154
        }
×
2155
        return &aftpb.Afts_Ipv6EntryKey{
1✔
2156
                Prefix:    *e.Prefix,
1✔
2157
                Ipv6Entry: ip6proto,
1✔
2158
        }, nil
1✔
2159
}
2160

2161
// ConcreteMPLSProto takes the input LabelEntry GoStruct and returns it as a gRIBI
2162
// LabelEntryKey protobuf. It returns an error if the protobuf cannot be marshalled.
2163
func ConcreteMPLSProto(e *aft.Afts_LabelEntry) (*aftpb.Afts_LabelEntryKey, error) {
1✔
2164
        mplsProto := &aftpb.Afts_LabelEntry{}
1✔
2165
        if err := protoFromGoStruct(e, &gpb.Path{
1✔
2166
                Elem: []*gpb.PathElem{{
1✔
2167
                        Name: "afts",
1✔
2168
                }, {
1✔
2169
                        Name: "mpls",
1✔
2170
                }, {
1✔
2171
                        Name: "label-entry",
1✔
2172
                }},
1✔
2173
        }, mplsProto); err != nil {
1✔
2174
                return nil, fmt.Errorf("cannot marshal MPLS label %v, %v", e.GetLabel(), err)
×
2175
        }
×
2176

2177
        l, ok := e.GetLabel().(aft.UnionUint32)
1✔
2178
        if !ok {
2✔
2179
                return nil, fmt.Errorf("cannot marshal MPLS label %v, incorrect type %T", e.GetLabel(), e.GetLabel())
1✔
2180
        }
1✔
2181

2182
        return &aftpb.Afts_LabelEntryKey{
1✔
2183
                Label: &aftpb.Afts_LabelEntryKey_LabelUint64{
1✔
2184
                        LabelUint64: uint64(l),
1✔
2185
                },
1✔
2186
                LabelEntry: mplsProto,
1✔
2187
        }, nil
1✔
2188
}
2189

2190
// ConcreteNextHopProto takes the input NextHop GoStruct and returns it as a gRIBI
2191
// NextHopEntryKey protobuf. It returns an error if the protobuf cannot be marshalled.
2192
func ConcreteNextHopProto(e *aft.Afts_NextHop) (*aftpb.Afts_NextHopKey, error) {
1✔
2193
        nhproto := &aftpb.Afts_NextHop{}
1✔
2194
        if err := protoFromGoStruct(e, &gpb.Path{
1✔
2195
                Elem: []*gpb.PathElem{{
1✔
2196
                        Name: "afts",
1✔
2197
                }, {
1✔
2198
                        Name: "next-hops",
1✔
2199
                }, {
1✔
2200
                        Name: "next-hop",
1✔
2201
                }},
1✔
2202
        }, nhproto); err != nil {
1✔
2203
                return nil, fmt.Errorf("cannot marshal next-hop index %d, %v", e.GetIndex(), err)
×
2204
        }
×
2205
        return &aftpb.Afts_NextHopKey{
1✔
2206
                Index:   *e.Index,
1✔
2207
                NextHop: nhproto,
1✔
2208
        }, nil
1✔
2209
}
2210

2211
// ConcreteNextHopGroupProto takes the input NextHopGroup GoStruct and returns it as a gRIBI
2212
// NextHopGroupEntryKey protobuf. It returns an error if the protobuf cannot be marshalled.
2213
func ConcreteNextHopGroupProto(e *aft.Afts_NextHopGroup) (*aftpb.Afts_NextHopGroupKey, error) {
1✔
2214
        nhgproto := &aftpb.Afts_NextHopGroup{}
1✔
2215
        if err := protoFromGoStruct(e, &gpb.Path{
1✔
2216
                Elem: []*gpb.PathElem{{
1✔
2217
                        Name: "afts",
1✔
2218
                }, {
1✔
2219
                        Name: "next-hop-groups",
1✔
2220
                }, {
1✔
2221
                        Name: "next-hop-group",
1✔
2222
                }},
1✔
2223
        }, nhgproto); err != nil {
1✔
2224
                return nil, fmt.Errorf("cannot marshal next-hop index %d, %v", e.GetId(), err)
×
2225
        }
×
2226
        return &aftpb.Afts_NextHopGroupKey{
1✔
2227
                Id:           *e.Id,
1✔
2228
                NextHopGroup: nhgproto,
1✔
2229
        }, nil
1✔
2230
}
2231

2232
// protoFromGoStruct takes the input GoStruct and marshals into the supplied pb
2233
// protobuf message, trimming the prefix specified from the annotated paths within
2234
// the protobuf.
2235
func protoFromGoStruct(s ygot.ValidatedGoStruct, prefix *gpb.Path, pb proto.Message) error {
1✔
2236
        ns, err := ygot.TogNMINotifications(s, 0, ygot.GNMINotificationsConfig{
1✔
2237
                UsePathElem: true,
1✔
2238
        })
1✔
2239
        if err != nil {
1✔
2240
                return fmt.Errorf("cannot marshal existing entry key %s, %v", s, err)
×
2241
        }
×
2242

2243
        vals := map[*gpb.Path]any{}
1✔
2244
        for _, n := range ns {
2✔
2245
                for _, u := range n.GetUpdate() {
2✔
2246
                        vals[u.Path] = u.Val
1✔
2247
                }
1✔
2248
        }
2249

2250
        if err := protomap.ProtoFromPaths(pb, vals,
1✔
2251
                protomap.ProtobufMessagePrefix(prefix),
1✔
2252
                protomap.ValuePathPrefix(prefix),
1✔
2253
                protomap.IgnoreExtraPaths(),
1✔
2254
        ); err != nil {
1✔
2255
                return fmt.Errorf("cannot unmarshal gNMI paths, %v", err)
×
2256
        }
×
2257

2258
        return nil
1✔
2259
}
2260

2261
// GetRIB writes the contents of the RIBs specified in the filter to msgCh. filter is a map,
2262
// keyed by the gRIBI AFTType enumeration, if the value is set to true, the AFT is written
2263
// to msgCh, otherwise it is skipped. The contents of the RIB are returned as gRIBI
2264
// GetResponse messages which are written to the supplied msgCh. stopCh is a channel that
2265
// indicates that the GetRIB method should stop its work and return immediately.
2266
//
2267
// An error is returned if the RIB cannot be returned.
2268
func (r *RIBHolder) GetRIB(filter map[spb.AFTType]bool, msgCh chan *spb.GetResponse, stopCh chan struct{}) error {
1✔
2269
        // TODO(robjs): since we are wanting to ensure that we tell the client
1✔
2270
        // exactly what is installed, this leads to a decision to make about locking
1✔
2271
        // of the RIB -- either we can go and lock the entire network instance RIB,
1✔
2272
        // or be more granular than that.
1✔
2273
        //
1✔
2274
        //  * we take the NI-level lock: in the incoming master case, the client can
1✔
2275
        //    ensure that they wait for the Get to complete before writing ==> there
1✔
2276
        //    is no convergence impact. In the multi-master case (or even a consistency)
1✔
2277
        //    check case, we impact convergence.
1✔
2278
        //  * we take a more granular lock, in this case we do not impact convergence
1✔
2279
        //    for any other entity than that individual entry.
1✔
2280
        //
1✔
2281
        // The latter is a better choice for a high-performance implementation, but
1✔
2282
        // it's not clear that we need to worry about this for this implementation *yet*.
1✔
2283
        // In the future we should consider a fine-grained per-entry lock.
1✔
2284
        r.mu.RLock()
1✔
2285
        defer r.mu.RUnlock()
1✔
2286

1✔
2287
        // rewrite ALL to the values that we support.
1✔
2288
        if filter[spb.AFTType_ALL] {
2✔
2289
                filter = map[spb.AFTType]bool{
1✔
2290
                        spb.AFTType_IPV4:          true,
1✔
2291
                        spb.AFTType_MPLS:          true,
1✔
2292
                        spb.AFTType_NEXTHOP:       true,
1✔
2293
                        spb.AFTType_NEXTHOP_GROUP: true,
1✔
2294
                        spb.AFTType_IPV6:          true,
1✔
2295
                }
1✔
2296
        }
1✔
2297

2298
        if filter[spb.AFTType_IPV4] {
2✔
2299
                for pfx, e := range r.r.Afts.Ipv4Entry {
2✔
2300
                        select {
1✔
2301
                        case <-stopCh:
×
2302
                                return nil
×
2303
                        default:
1✔
2304
                                p, err := ConcreteIPv4Proto(e)
1✔
2305
                                if err != nil {
1✔
2306
                                        return status.Errorf(codes.Internal, "cannot marshal IPv4Entry for %s into GetResponse, %v", pfx, err)
×
2307
                                }
×
2308
                                msgCh <- &spb.GetResponse{
1✔
2309
                                        Entry: []*spb.AFTEntry{{
1✔
2310
                                                NetworkInstance: r.name,
1✔
2311
                                                Entry: &spb.AFTEntry_Ipv4{
1✔
2312
                                                        Ipv4: p,
1✔
2313
                                                },
1✔
2314
                                        }},
1✔
2315
                                }
1✔
2316
                        }
2317
                }
2318
        }
2319

2320
        if filter[spb.AFTType_IPV6] {
2✔
2321
                for pfx, e := range r.r.Afts.Ipv6Entry {
2✔
2322
                        select {
1✔
2323
                        case <-stopCh:
×
2324
                                return nil
×
2325
                        default:
1✔
2326
                                p, err := ConcreteIPv6Proto(e)
1✔
2327
                                if err != nil {
1✔
2328
                                        return status.Errorf(codes.Internal, "cannot marshal IPv6Entry for %s into GetResponse, %v", pfx, err)
×
2329
                                }
×
2330
                                msgCh <- &spb.GetResponse{
1✔
2331
                                        Entry: []*spb.AFTEntry{{
1✔
2332
                                                NetworkInstance: r.name,
1✔
2333
                                                Entry: &spb.AFTEntry_Ipv6{
1✔
2334
                                                        Ipv6: p,
1✔
2335
                                                },
1✔
2336
                                        }},
1✔
2337
                                }
1✔
2338
                        }
2339
                }
2340
        }
2341

2342
        if filter[spb.AFTType_MPLS] {
2✔
2343
                for lbl, e := range r.r.Afts.LabelEntry {
2✔
2344
                        select {
1✔
2345
                        case <-stopCh:
×
2346
                                return nil
×
2347
                        default:
1✔
2348
                                p, err := ConcreteMPLSProto(e)
1✔
2349
                                if err != nil {
1✔
2350
                                        return status.Errorf(codes.Internal, "cannot marshal MPLS entry for label %d into GetResponse, %v", lbl, err)
×
2351
                                }
×
2352
                                msgCh <- &spb.GetResponse{
1✔
2353
                                        Entry: []*spb.AFTEntry{{
1✔
2354
                                                NetworkInstance: r.name,
1✔
2355
                                                Entry: &spb.AFTEntry_Mpls{
1✔
2356
                                                        Mpls: p,
1✔
2357
                                                },
1✔
2358
                                        }},
1✔
2359
                                }
1✔
2360
                        }
2361
                }
2362
        }
2363

2364
        if filter[spb.AFTType_NEXTHOP_GROUP] {
2✔
2365
                for index, e := range r.r.Afts.NextHopGroup {
2✔
2366
                        select {
1✔
2367
                        case <-stopCh:
×
2368
                                return nil
×
2369
                        default:
1✔
2370
                                p, err := ConcreteNextHopGroupProto(e)
1✔
2371
                                if err != nil {
1✔
2372
                                        return status.Errorf(codes.Internal, "cannot marshal NextHopGroupEntry for index %d into GetResponse, %v", index, err)
×
2373
                                }
×
2374
                                msgCh <- &spb.GetResponse{
1✔
2375
                                        Entry: []*spb.AFTEntry{{
1✔
2376
                                                NetworkInstance: r.name,
1✔
2377
                                                Entry: &spb.AFTEntry_NextHopGroup{
1✔
2378
                                                        NextHopGroup: p,
1✔
2379
                                                },
1✔
2380
                                        }},
1✔
2381
                                }
1✔
2382
                        }
2383
                }
2384
        }
2385

2386
        if filter[spb.AFTType_NEXTHOP] {
2✔
2387
                for id, e := range r.r.Afts.NextHop {
2✔
2388
                        select {
1✔
2389
                        case <-stopCh:
×
2390
                                return nil
×
2391
                        default:
1✔
2392
                                p, err := ConcreteNextHopProto(e)
1✔
2393
                                if err != nil {
1✔
2394
                                        return status.Errorf(codes.Internal, "cannot marshal NextHopEntry for ID %d into GetResponse, %v", id, err)
×
2395
                                }
×
2396
                                msgCh <- &spb.GetResponse{
1✔
2397
                                        Entry: []*spb.AFTEntry{{
1✔
2398
                                                NetworkInstance: r.name,
1✔
2399
                                                Entry: &spb.AFTEntry_NextHop{
1✔
2400
                                                        NextHop: p,
1✔
2401
                                                },
1✔
2402
                                        }},
1✔
2403
                                }
1✔
2404
                        }
2405
                }
2406
        }
2407

2408
        return nil
1✔
2409
}
2410

2411
type FlushErr struct {
2412
        Errs []error
2413
}
2414

2415
func (f *FlushErr) Error() string {
×
2416
        b := &bytes.Buffer{}
×
2417
        for _, err := range f.Errs {
×
2418
                b.WriteString(fmt.Sprintf("%s\n", err))
×
2419
        }
×
2420
        return b.String()
×
2421
}
2422

2423
// Flush cleanly removes all entries from the specified RIB. A lock on the RIB
2424
// is held throughout the flush so that no entries can be added during this
2425
// time.
2426
//
2427
// The order of operations for deletes considers the dependency tree:
2428
//   - we remove IPv4 entries first, since these are never referenced counted,
2429
//     and the order of removing them never matters.
2430
//   - we check for any backup NHGs, and remove these first - since otherwise we
2431
//     may end up with referenced NHGs. note, we need to consider circular references
2432
//     of backup NHGs, which we may allow today. we remove the backup NHGs.
2433
//   - we remove the remaining NHGs.
2434
//   - we remove the NHs.
2435
//
2436
// Flush handles updating the reference counts within the RIB.
2437
func (r *RIB) Flush(networkInstances []string) error {
1✔
2438
        errs := []error{}
1✔
2439

1✔
2440
        for _, netInst := range networkInstances {
2✔
2441
                niR, ok := r.NetworkInstanceRIB(netInst)
1✔
2442
                if !ok {
1✔
2443
                        log.Errorf("cannot find network instance RIB for %s", netInst)
×
2444
                }
×
2445

2446
                // We hold a long lock during the Flush operation since we need to ensure that
2447
                // no entries are added to it whilst we remove all entries. This also means
2448
                // that we use the locklessDeleteXXX functions below to avoid deadlocking.
2449
                niR.mu.Lock()
1✔
2450
                defer niR.mu.Unlock()
1✔
2451

1✔
2452
                for p, entry := range niR.r.Afts.Ipv4Entry {
2✔
2453
                        referencedRIB, err := r.refdRIB(niR, entry.GetNextHopGroupNetworkInstance())
1✔
2454
                        switch {
1✔
2455
                        case err != nil:
×
2456
                                log.Errorf("cannot find network instance RIB %s during Flush for IPv4 prefix %s", entry.GetNextHopGroupNetworkInstance(), p)
×
2457
                        default:
1✔
2458
                                referencedRIB.decNHGRefCount(entry.GetNextHopGroup())
1✔
2459
                        }
2460
                        if err := niR.locklessDeleteIPv4(p); err != nil {
1✔
2461
                                errs = append(errs, err)
×
2462
                        }
×
2463
                }
2464

2465
                for p, entry := range niR.r.Afts.Ipv6Entry {
2✔
2466
                        referencedRIB, err := r.refdRIB(niR, entry.GetNextHopGroupNetworkInstance())
1✔
2467
                        switch {
1✔
2468
                        case err != nil:
×
2469
                                log.Errorf("cannot find network instance RIB %s during Flush for IPv6 prefix %s", entry.GetNextHopGroupNetworkInstance(), p)
×
2470
                        default:
1✔
2471
                                referencedRIB.decNHGRefCount(entry.GetNextHopGroup())
1✔
2472
                        }
2473
                        if err := niR.locklessDeleteIPv6(p); err != nil {
1✔
2474
                                errs = append(errs, err)
×
2475
                        }
×
2476
                }
2477

2478
                for label, entry := range niR.r.Afts.LabelEntry {
2✔
2479
                        referencedRIB, err := r.refdRIB(niR, entry.GetNextHopGroupNetworkInstance())
1✔
2480
                        switch {
1✔
2481
                        case err != nil:
×
2482
                                log.Errorf("cannot find network instance RIB %s during Flush for MPLS label %d", entry.GetNextHopGroupNetworkInstance(), label)
×
2483
                        default:
1✔
2484
                                referencedRIB.decNHGRefCount(entry.GetNextHopGroup())
1✔
2485
                        }
2486
                        if err := niR.locklessDeleteMPLS(label); err != nil {
1✔
2487
                                errs = append(errs, err)
×
2488
                        }
×
2489
                }
2490

2491
                backupNHGs := []uint64{}
1✔
2492
                for _, nhg := range niR.r.Afts.NextHopGroup {
2✔
2493
                        if nhg.BackupNextHopGroup != nil {
2✔
2494
                                backupNHGs = append(backupNHGs, *nhg.BackupNextHopGroup)
1✔
2495
                        }
1✔
2496
                }
2497

2498
                delNHG := func(id uint64) {
2✔
2499
                        if err := niR.locklessDeleteNHG(id); err != nil {
1✔
2500
                                errs = append(errs, err)
×
2501
                        }
×
2502
                }
2503

2504
                for _, id := range backupNHGs {
2✔
2505
                        delNHG(id)
1✔
2506
                }
1✔
2507

2508
                for n := range niR.r.Afts.NextHopGroup {
2✔
2509
                        delNHG(n)
1✔
2510
                }
1✔
2511

2512
                for n := range niR.r.Afts.NextHop {
2✔
2513
                        if err := niR.locklessDeleteNH(n); err != nil {
1✔
2514
                                errs = append(errs, err)
×
2515
                        }
×
2516
                }
2517

2518
        }
2519

2520
        if len(errs) != 0 {
1✔
2521
                return &FlushErr{Errs: errs}
×
2522
        }
×
2523

2524
        return nil
1✔
2525
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc