diff --git a/pkg/LinuxGeneralModule/lgm.go b/pkg/LinuxGeneralModule/lgm.go index 5140bd05..97a91cd7 100644 --- a/pkg/LinuxGeneralModule/lgm.go +++ b/pkg/LinuxGeneralModule/lgm.go @@ -31,12 +31,6 @@ import ( // ModulelgmHandler enmpty interface type ModulelgmHandler struct{} -// RoutingTableMax max value of routing table -const RoutingTableMax = 4000 - -// RoutingTableMin min value of routing table -const RoutingTableMin = 1000 - // lgmComp string constant const lgmComp string = "lgm" @@ -46,10 +40,11 @@ const brStr string = "br-" // vxlanStr string constant const vxlanStr string = "vxlan-" -// GenerateRouteTable range specification, note that min <= max -func GenerateRouteTable() uint32 { - return uint32(rand.Intn(RoutingTableMax-RoutingTableMin+1) + RoutingTableMin) //nolint:gosec -} +// routingTableMax max value of routing table +const routingTableMax = 4000 + +// routingTableMin min value of routing table +const routingTableMin = 1000 // run runs the commands func run(cmd []string, flag bool) (string, int) { @@ -350,6 +345,9 @@ var ctx context.Context // nlink variable wrapper var nlink utils.Netlink +// RouteTableGen table id generate variable +var RouteTableGen utils.IDPool + // Initialize initializes the config, logger and subscribers func Initialize() { eb := eventbus.EBus @@ -363,7 +361,8 @@ func Initialize() { brTenant = "br-tenant" ipMtu = config.GlobalConfig.LinuxFrr.IPMtu ctx = context.Background() - nlink = utils.NewNetlinkWrapperWithArgs(config.GlobalConfig.Tracer) + RouteTableGen = utils.IDPoolInit("RTtable", routingTableMin, routingTableMax) + nlink = utils.NewNetlinkWrapperWithArgs(false) // Set up the static configuration parts _, err := nlink.LinkByName(ctx, brTenant) if err != nil { @@ -401,8 +400,8 @@ func setUpTenantBridge() { } } -// routingTableBusy checks if the route is in filterred list -func routingTableBusy(table uint32) (bool, error) { +// routingtableBusy checks if the route is in filterred list +func routingtableBusy(table uint32) (bool, error) { routeList, err := nlink.RouteListFiltered(ctx, netlink.FAMILY_V4, &netlink.Route{Table: int(table)}, netlink.RT_FILTER_TABLE) if err != nil { return false, err @@ -454,6 +453,7 @@ func setUpBridge(lb *infradb.LogicalBridge) bool { //nolint:funlen,gocognit func setUpVrf(vrf *infradb.Vrf) (string, bool) { IPMtu := fmt.Sprintf("%+v", ipMtu) + var addKey int if path.Base(vrf.Name) == "GRD" { vrf.Metadata.RoutingTable = make([]*uint32, 2) vrf.Metadata.RoutingTable[0] = new(uint32) @@ -464,19 +464,23 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { } vrf.Metadata.RoutingTable = make([]*uint32, 1) vrf.Metadata.RoutingTable[0] = new(uint32) - var routingTable uint32 + var routingtable uint32 + name := vrf.Name for { - routingTable = GenerateRouteTable() - isBusy, err := routingTableBusy(routingTable) + routingtable, _ = RouteTableGen.GetID(name, 0) + log.Printf("LGM assigned id %+v for vrf name %s\n", routingtable, vrf.Name) + isbusy, err := routingtableBusy(routingtable) if err != nil { - log.Printf("LGM : Error occurred when checking if routing table %d is busy: %+v\n", routingTable, err) + log.Printf("LGM : Error occurred when checking if routing table %d is busy: %+v\n", routingtable, err) return "", false } - if !isBusy { - log.Printf("LGM: Routing Table %d is not busy\n", routingTable) + if !isbusy { + log.Printf("LGM: Routing Table %d is not busy\n", routingtable) break } - log.Printf("LGM: Routing Table %d is busy\n", routingTable) + log.Printf("LGM: Routing Table %d is busy\n", routingtable) + addKey++ + name = fmt.Sprintf("%s%d", name, addKey) } var vtip string if !reflect.ValueOf(vrf.Spec.VtepIP).IsZero() { @@ -489,19 +493,19 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { return "", false } } - log.Printf("setUpVrf: %s %d\n", vtip, routingTable) + log.Printf("setUpVrf: %s %d\n", vtip, routingtable) // Create the vrf interface for the specified routing table and add loopback address linkAdderr := nlink.LinkAdd(ctx, &netlink.Vrf{ LinkAttrs: netlink.LinkAttrs{Name: path.Base(vrf.Name)}, - Table: routingTable, + Table: routingtable, }) if linkAdderr != nil { - log.Printf("LGM: Error in Adding vrf link table %d\n", routingTable) + log.Printf("LGM: Error in Adding vrf link table %d\n", routingtable) return "", false } - log.Printf("LGM: vrf link %s Added with table id %d\n", vrf.Name, routingTable) + log.Printf("LGM: vrf link %s Added with table id %d\n", vrf.Name, routingtable) link, linkErr := nlink.LinkByName(ctx, path.Base(vrf.Name)) if linkErr != nil { @@ -536,7 +540,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { Src1 := net.IPv4(0, 0, 0, 0) route := netlink.Route{ - Table: int(routingTable), + Table: int(routingtable), Type: unix.RTN_THROW, Protocol: 255, Priority: 9999, @@ -548,7 +552,7 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { return "", false } - log.Printf("LGM : Added route throw default table %d proto opi_evpn_br metric 9999\n", routingTable) + log.Printf("LGM : Added route throw default table %d proto opi_evpn_br metric 9999\n", routingtable) // Disable reverse-path filtering to accept ingress traffic punted by the pipeline // disable_rp_filter("rep-"+vrf.Name) // Configuration specific for VRFs associated with L3 EVPN @@ -638,8 +642,8 @@ func setUpVrf(vrf *infradb.Vrf) (string, bool) { return "", false } } - details := fmt.Sprintf("{\"routingTable\":\"%d\"}", routingTable) - *vrf.Metadata.RoutingTable[0] = routingTable + details := fmt.Sprintf("{\"routingtable\":\"%d\"}", routingtable) + *vrf.Metadata.RoutingTable[0] = routingtable return details, true } @@ -775,7 +779,7 @@ func tearDownVrf(vrf *infradb.Vrf) bool { if path.Base(vrf.Name) == "GRD" { return true } - routingTable := *vrf.Metadata.RoutingTable[0] + routingtable := *vrf.Metadata.RoutingTable[0] // Delete the Linux networking artefacts in reverse order if !reflect.ValueOf(vrf.Spec.Vni).IsZero() { linkVxlan, linkErr := nlink.LinkByName(ctx, vxlanStr+path.Base(vrf.Name)) @@ -802,7 +806,7 @@ func tearDownVrf(vrf *infradb.Vrf) bool { } log.Printf("LGM : Delete br-%s\n", vrf.Name) } - routeTable := fmt.Sprintf("%+v", routingTable) + routeTable := fmt.Sprintf("%+v", routingtable) flusherr := nlink.RouteFlushTable(ctx, routeTable) if flusherr != nil { log.Printf("LGM: Error in flush table %+v\n", routeTable) diff --git a/pkg/utils/idpool.go b/pkg/utils/idpool.go new file mode 100644 index 00000000..54d4b6b4 --- /dev/null +++ b/pkg/utils/idpool.go @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2022-2023 Intel Corporation, or its subsidiaries. +// Copyright (C) 2023 Nordix Foundation. + +// Package linuxgeneralmodule is the main package of the application + +package utils + +import ( + "log" + "reflect" +) + +// IDPool structure +/* Helper class for uniquely assigning IDs from a specified integer set (e.g. a +# range) to keys. IDs are assigned (or read) with GetID(key) and returned back +# into the pool with ReleaseID(key). The IDPool remembers a once-assigned ID +# for keys so that the same ID is assigned for a key. Only when the pool runs +# out of unassigned keys, it will recycle released ids and assign them to new +# keys. +# Optionally, the IDPool supports reference tracking for key/ID pairs. Clients +# can provide a unique reference when fetching and releasing an ID for a key +# to support multiple independent clients. +# The pool will only release the ID for the key, when the last client has the +# released the ID with its reference. When a reference is specified in GetID() +# and ReleaseID() the IDPool returns the current number of reference for the +# ID so that a caller knows when an ID was newly assigned (ref_count 1) or +# finally released (ref_count 0). +*/ +type IDPool struct { + name string // Name of pool + _unusedids []uint32 // Yet unused IDs in pool Available ids + _idsinuse map[interface{}]uint32 // Mapping key: id for currently assigned ids + _idsforreuse map[interface{}]uint32 // Mapping key: id for previously assigned ids + _refs map[uint32][]interface{} + _size int // Size of the pool +} + +// IDPoolInit initialize mod ptr pool +func IDPoolInit(name string, min uint32, max uint32) IDPool { + var pool IDPool + pool.name = name + pool._unusedids = make([]uint32, 0) + for j := min; j <= (max + 1); j++ { + pool._unusedids = append(pool._unusedids, j) + } + pool._size = len(pool._unusedids) + pool._idsinuse = make(map[interface{}]uint32) + pool._idsforreuse = make(map[interface{}]uint32) + pool._refs = make(map[uint32][]interface{}) + return pool +} + +func (ip *IDPool) _assignID(key interface{}) uint32 { + // Check if there was an id assigned for that key earlier + id := ip._idsforreuse[key] + if id != 0 { + // Re-use the old id + delete(ip._idsforreuse, key) + } else { + if len(ip._unusedids) != 0 { + // Pick an unused id + id = ip._unusedids[0] + ip._unusedids = ip._unusedids[1:len(ip._unusedids)] + } else { + if len(ip._idsforreuse) != 0 { + // Pick one of the ids earlier used for another key + for oldKey := range ip._idsforreuse { + delete(ip._idsforreuse, oldKey) + break + } + } else { + log.Printf("IDPool: Failed to allocate id for %+v. No free ids in pool.", key) + return 0 + } + } + } + // Store the assigned id, if any + if id != 0 { + ip._idsinuse[key] = id + } + return id +} + +// GetID get the mod ptr id from pool +func (ip *IDPool) GetID(key interface{}, ref interface{}) (uint32, uint32) { + id := ip._idsinuse[key] + if id == 0 { + id = ip._assignID(key) + if id == 0 { + return 0, 0 + } + } + if ref != nil { + log.Printf("IDPool: GetID Assigning key : %+v , id %+v for ref %v", id, key, ref) + if reflect.ValueOf(ip._refs[id]).IsZero() { + ip._refs[id] = make([]interface{}, 0) + } + ip._refs[id] = append(ip._refs[id], ref) + return id, uint32(len(ip._refs[id])) + } + log.Printf("IDPool: GetID Assigning id %v for key %v and ref %v", id, key, ref) + return id, uint32(0) +} + +func deleteRef(refSet []interface{}, ref interface{}) []interface{} { + var i uint32 + for index, value := range refSet { + if value == ref { + i = uint32(index) + break + } + } + return append(refSet[:i], refSet[i+1:]...) +} + +// ReleaseID get the reference id +func (ip *IDPool) ReleaseID(key interface{}, ref interface{}) (uint32, uint32) { + log.Printf("IDPool:ReleaseID Releasing id for key %v", key) + id := ip._idsinuse[key] + if ref == nil { + log.Printf("No id to release for key %v", key) + return 0, 0 + } + refSet := ip._refs[id] + if !reflect.ValueOf(refSet).IsZero() && !reflect.ValueOf(ref).IsZero() { + refSet = deleteRef(refSet, ref) + } + if refSet != nil { + log.Printf("IDPool:ReleaseID Id %v has been released", id) + delete(ip._idsinuse, key) + if refSet != nil { + delete(ip._refs, id) + } + // Store released id for future reassignment + ip._idsforreuse[key] = id + } else { + log.Printf("IDPool:ReleaseID Keep id:%+v remaining references %+v", id, len(refSet)) + } + if ref != nil { + return id, uint32(len(refSet)) + } + return id, uint32(0) +}