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

openconfig / gribigo / 10085762241

18 Jul 2024 10:36PM UTC coverage: 73.509% (+0.2%) from 73.287%
10085762241

push

github

web-flow
Merge pull request #238 from nflath/duplicate-nh

Add IPv6 Get() test and add metadata to the Get() tests.

75 of 77 new or added lines in 1 file covered. (97.4%)

8 existing lines in 1 file now uncovered.

6288 of 8554 relevant lines covered (73.51%)

0.79 hits per line

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

82.48
/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 deterining 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 pendingEntires 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 cnanot 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 asssigns 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).Info("[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:
×
532
                        opErr = err
×
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 implementatiom
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
                for _, n := range g.NextHop {
2✔
956
                        // Zero is an invalid value for a next-hop index. GetIndex() will also return 0
1✔
957
                        // if the NH index is nil, which is also invalid - so handle them together.
1✔
958
                        if n.GetIndex() == 0 {
2✔
959
                                return false, fmt.Errorf("invalid zero index NH in NHG %d, NI %s", g.GetId(), netInst)
1✔
960
                        }
1✔
961
                        // nexthops are resolved in the same NI as the next-hop-group
962
                        if _, ok := niRIB.GetNextHop(n.GetIndex()); !ok {
2✔
963
                                // this is not an error - it's just that we can't resolve this seemingly
1✔
964
                                // valid looking NHG at this point.
1✔
965
                                return false, nil
1✔
966
                        }
1✔
967
                }
968
                return true, nil
1✔
969
        }
970

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

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

992
        }
993

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

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

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

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

1027
        if err := checkCandidate(caft); err != nil {
2✔
1028
                return false, err
1✔
1029
        }
1✔
1030

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

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

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

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

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

1080
}
1081

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

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

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

1110
// isRHOpt implements the ribHolderOpt function
1111
func (r *ribHolderCheckFn) isRHOpt() {}
×
1112

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

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

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

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

1153
        if hasRHDisableForwardRef(opts) {
2✔
1154
                r.disableForwardRef = true
1✔
1155
        }
1✔
1156

1157
        return r
1✔
1158
}
1159

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

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

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

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

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

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

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

1231
        return nr, nil
1✔
1232
}
1233

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

1245
        if e == nil {
2✔
1246
                return false, nil, errors.New("nil IPv4 Entry provided")
1✔
1247
        }
1✔
1248

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

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

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

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

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

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

1301
        return true, orig, nil
1✔
1302
}
1303

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

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

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

1323
        // Check whether this is an implicit replace.
1324
        _, implicit := r.r.GetAfts().Ipv4Entry[pfx]
1✔
1325

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

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

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

1348
        if r.r == nil {
1✔
1349
                return false, nil, errors.New("invalid RIB structure, nil")
×
1350
        }
×
1351

1352
        de := r.retrieveIPv4(e.GetPrefix())
1✔
1353

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

1368
        r.doDeleteIPv4(e.GetPrefix())
1✔
1369

1✔
1370
        if r.postChangeHook != nil {
2✔
1371
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
1✔
1372
        }
1✔
1373

1374
        return true, de, nil
1✔
1375
}
1376

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

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

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

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

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

1416
        if e == nil {
1✔
1417
                return false, nil, errors.New("nil IPv6 Entry provided")
×
1418
        }
×
1419

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

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

1431
        var orig *aft.Afts_Ipv6Entry
1✔
1432
        if r.ipv6Exists(e.GetPrefix()) {
2✔
1433
                orig = r.retrieveIPv6(e.GetPrefix())
1✔
1434
        }
1✔
1435

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

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

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

1456
        return true, orig, nil
1✔
1457
}
1458

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

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

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

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

1✔
1479
        delete(r.r.GetAfts().Ipv6Entry, pfx)
1✔
1480

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

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

1495
        if r.r == nil {
1✔
1496
                return false, nil, errors.New("invalid RIB structure, nil")
×
1497
        }
×
1498

1499
        de := r.retrieveIPv6(e.GetPrefix())
1✔
1500

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

1515
        r.doDeleteIPv6(e.GetPrefix())
1✔
1516

1✔
1517
        if r.postChangeHook != nil {
1✔
1518
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
×
1519
        }
×
1520

1521
        return true, de, nil
1✔
1522
}
1523

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

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

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

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

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

1566
        if e == nil {
1✔
1567
                return false, nil, errors.New("nil MPLS Entry provided")
×
1568
        }
×
1569

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

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

1581
        var orig *aft.Afts_LabelEntry
1✔
1582
        if r.mplsExists(uint32(e.GetLabelUint64())) {
2✔
1583
                orig = r.retrieveMPLS(uint32(e.GetLabelUint64()))
1✔
1584
        }
1✔
1585

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

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

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

1617
        return true, orig, nil
1✔
1618
}
1619

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

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

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

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

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

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

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

1668
        if r.r == nil {
1✔
1669
                return false, nil, errors.New("invalid RIB structure, nil")
×
1670
        }
×
1671

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

1676
        lbl := uint32(e.GetLabelUint64())
1✔
1677

1✔
1678
        de := r.retrieveMPLS(lbl)
1✔
1679

1✔
1680
        rr := &aft.RIB{}
1✔
1681
        rr.GetOrCreateAfts().GetOrCreateLabelEntry(aft.UnionUint32(lbl))
1✔
1682

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

1696
        r.doDeleteMPLS(lbl)
1✔
1697

1✔
1698
        if r.postChangeHook != nil {
2✔
1699
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
1✔
1700
        }
1✔
1701

1702
        return true, de, nil
1✔
1703
}
1704

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

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

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

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

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

1746
        if r.r == nil {
1✔
1747
                return false, nil, errors.New("invalid RIB structure, nil")
×
1748
        }
×
1749

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

1754
        de := r.retrieveNHG(e.GetId())
1✔
1755

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

1770
        r.doDeleteNHG(e.GetId())
1✔
1771

1✔
1772
        if r.postChangeHook != nil {
2✔
1773
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
1✔
1774
        }
1✔
1775

1776
        return true, de, nil
1✔
1777
}
1778

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

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

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

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

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

1817
        if r.r == nil {
1✔
1818
                return false, nil, errors.New("invalid RIB structure, nil")
×
1819
        }
×
1820

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

1825
        de := r.retrieveNH(e.GetIndex())
1✔
1826

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

1✔
1842
        if r.postChangeHook != nil {
2✔
1843
                r.postChangeHook(constants.Delete, unixTS(), r.name, de)
1✔
1844
        }
1✔
1845

1846
        return true, de, nil
1✔
1847
}
1848

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

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

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

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

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

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

1899
        if e == nil {
1✔
1900
                return false, nil, errors.New("nil NextHopGroup provided")
×
1901
        }
×
1902

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

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

1914
        var orig *aft.Afts_NextHopGroup
1✔
1915
        if r.nhgExists(e.GetId()) {
2✔
1916
                orig = r.retrieveNHG(e.GetId())
1✔
1917
        }
1✔
1918

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

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

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

1943
        return true, orig, nil
1✔
1944
}
1945

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

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

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

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

1✔
1967
        // Handle implicit replace.
1✔
1968
        delete(r.r.GetAfts().NextHopGroup, ID)
1✔
1969

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

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

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

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

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

2013
        if e == nil {
1✔
2014
                return false, nil, errors.New("nil NextHop provided")
×
2015
        }
×
2016

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

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

2028
        var replaced *aft.Afts_NextHop
1✔
2029
        if r.nhExists(e.GetIndex()) {
2✔
2030
                replaced = r.retrieveNH(e.GetIndex())
1✔
2031
        }
1✔
2032

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

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

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

2056
        return true, replaced, nil
1✔
2057
}
2058

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

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

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

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

1✔
2081
        // Handle implicit replace.
1✔
2082
        delete(r.r.GetAfts().NextHop, index)
1✔
2083

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2255
        return nil
1✔
2256
}
2257

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

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

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

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

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

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

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

2405
        return nil
1✔
2406
}
2407

2408
type FlushErr struct {
2409
        Errs []error
2410
}
2411

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

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

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

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

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

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

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

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

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

2501
                for _, id := range backupNHGs {
2✔
2502
                        delNHG(id)
1✔
2503
                }
1✔
2504

2505
                for n := range niR.r.Afts.NextHopGroup {
2✔
2506
                        delNHG(n)
1✔
2507
                }
1✔
2508

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

2515
        }
2516

2517
        if len(errs) != 0 {
1✔
2518
                return &FlushErr{Errs: errs}
×
2519
        }
×
2520

2521
        return nil
1✔
2522
}
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