Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: test
on:
push:
branches: ["main"]
branches: ["main", "dv2"]
pull_request:

jobs:
Expand All @@ -20,7 +20,8 @@ jobs:
- name: Build
run: |
make
make examples
mkdir -p .bin
CGO_ENABLED=0 go build -o .bin/alo-latest std/examples/svs/alo-latest/main.go

- name: Test
run: make test
Expand Down
2 changes: 1 addition & 1 deletion dv/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func DefaultConfig() *Config {
PrefixInsertionKeychainUri: "",
TrustSchemaPath: "",
PrefixInsertionTrustSchemaPath: "",
PrefixStateReplicate: true,
PrefixStateReplicate: true,
}
}

Expand Down
55 changes: 35 additions & 20 deletions dv/dv/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ type Router struct {

// prefix state daemon
pfx *PrefixModule
// installed PSD sync/data PET egresses keyed by router TLV.
psdSyncInstalled map[string]enc.Name
// neighbor table
neighbors *table.NeighborTable
// routing information base
Expand Down Expand Up @@ -129,6 +131,8 @@ func NewRouter(config *config.Config, engine ndn.Engine) (*Router, error) {
client: object.NewClient(engine, store, trust),
nfdc: nfdc.NewNfdMgmtThread(engine),
mutex: sync.Mutex{},

psdSyncInstalled: make(map[string]enc.Name),
}

// Initialize advertisement module
Expand Down Expand Up @@ -290,19 +294,13 @@ func (dv *Router) register() (err error) {
Name: prefix,
})
}
// // Allow outgoing local-prefix-sync Interests to use two-phase forwarding.
// // Incoming Interests still terminate locally on the same prefix.
// dv.execMgmtRetry("pet", "add-egress", &mgmt.ControlArgs{
// Name: dv.pfx.SyncPrefix(),
// Egress: &mgmt.EgressRecord{Name: neighborsPrefix.Clone()},
// Multicast: true,
// })
// Set Advertisement Sync to localhop neighbors
dv.execMgmtRetry("pet", "add-egress", &mgmt.ControlArgs{
Name: dv.config.AdvertisementSyncPrefix(),
Egress: &mgmt.EgressRecord{Name: neighborsPrefix.Clone()},
})
// Set broadcast strategy for Advertisement Sync prefix

// Keep advertisement sync on the current working setup.
dv.execMgmtRetry("strategy-choice", "set", &mgmt.ControlArgs{
Name: dv.config.AdvertisementSyncPrefix(),
Strategy: &mgmt.Strategy{Name: defn.BROADCAST_STRATEGY},
Expand All @@ -323,33 +321,50 @@ func (dv *Router) execMgmtRetry(module, cmd string, args *mgmt.ControlArgs) {
}
}

// updatePsdSyncPrefix updates the PSD sync prefix PET entry with all routers as egress for BIER delivery.
// updatePsdPrefix incrementally updates PSD sync/data PET egresses.
func (dv *Router) updatePsdPrefix() {
synPfx := dv.pfx.SyncPrefix()
grpPfx := dv.pfx.GroupPrefix()
// First, remove existing egress entries for this prefix

dv.mutex.Lock()
desired := make(map[string]enc.Name)
for _, router := range dv.rib.Entries() {
name := router.Name().Clone()
desired[name.TlvStr()] = name
}
installed := make(map[string]enc.Name, len(dv.psdSyncInstalled))
for key, name := range dv.psdSyncInstalled {
installed[key] = name.Clone()
}
dv.psdSyncInstalled = desired
dv.mutex.Unlock()

for key, router := range installed {
if _, ok := desired[key]; ok {
continue
}
dv.execMgmtRetry("pet", "remove-egress", &mgmt.ControlArgs{
Name: synPfx,
Egress: &mgmt.EgressRecord{Name: router.Name().Clone()},
Egress: &mgmt.EgressRecord{Name: router.Clone()},
})
// Protocol naming convention
dv.execMgmtRetry("pet", "remove-egress", &mgmt.ControlArgs{
Name: grpPfx.Clone().Append(router.Name().Clone()...),
Egress: &mgmt.EgressRecord{Name: router.Name().Clone()},
Name: grpPfx.Clone().Append(router.Clone()...),
Egress: &mgmt.EgressRecord{Name: router.Clone()},
})
}
// Then add all routers as egress
for _, router := range dv.rib.Entries() {

for key, router := range desired {
if _, ok := installed[key]; ok {
continue
}
dv.execMgmtRetry("pet", "add-egress", &mgmt.ControlArgs{
Name: synPfx,
Egress: &mgmt.EgressRecord{Name: router.Name().Clone()},
Egress: &mgmt.EgressRecord{Name: router.Clone()},
Multicast: true,
})
// Protocol naming convention
dv.execMgmtRetry("pet", "add-egress", &mgmt.ControlArgs{
Name: grpPfx.Clone().Append(router.Name().Clone()...),
Egress: &mgmt.EgressRecord{Name: router.Name().Clone()},
Name: grpPfx.Clone().Append(router.Clone()...),
Egress: &mgmt.EgressRecord{Name: router.Clone()},
})
}
}
Expand Down
1 change: 0 additions & 1 deletion dv/dv/table_algo.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
func (dv *Router) postUpdateRib() {
dv.updateFib()
dv.advert.generate()
// Update PSD sync prefix with all routers as egress for BIER delivery.
dv.updatePsdPrefix()
}

Expand Down
Binary file modified e2e/client_lvs_minindn.tlv
Binary file not shown.
37 changes: 32 additions & 5 deletions fw/bier/bier.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,22 +109,44 @@ func BierClearBit(bs []byte, pos int) {
bs[byteIdx] &^= (1 << bitIdx)
}

// BierAnd returns bitwise AND of two bitstrings. Result length = min(len(a), len(b)).
// BierAnd returns bitwise AND of two bitstrings. Result length = max(len(a), len(b)).
func BierAnd(a, b []byte) []byte {
maxLen := len(a)
if len(b) > maxLen {
maxLen = len(b)
}
result := make([]byte, maxLen)
minLen := len(a)
if len(b) < minLen {
minLen = len(b)
}
result := make([]byte, minLen)
for i := 0; i < minLen; i++ {
result[i] = a[i] & b[i]
}
return result
}

// BierAndNot returns a &^ b (a AND NOT b). Clears bits in a that are set in b.
// BierOr returns bitwise OR of two bitstrings. Result length = max(len(a), len(b)).
func BierOr(a, b []byte) []byte {
maxLen := len(a)
if len(b) > maxLen {
maxLen = len(b)
}
result := make([]byte, maxLen)
copy(result, a)
for i := 0; i < len(b); i++ {
result[i] |= b[i]
}
return result
}

// BierAndNot returns a &^ b (a AND NOT b). Result length = max(len(a), len(b)).
func BierAndNot(a, b []byte) []byte {
result := make([]byte, len(a))
maxLen := len(a)
if len(b) > maxLen {
maxLen = len(b)
}
result := make([]byte, maxLen)
copy(result, a)
minLen := len(a)
if len(b) < minLen {
Expand Down Expand Up @@ -368,7 +390,9 @@ func (b *BiftState) GetNeighborEntries() []BiftNeighborEntry {
if entry.NextHop == 0 || entry.Fbm == nil {
continue
}
if _, ok := faceMap[entry.NextHop]; !ok {
if fbm, ok := faceMap[entry.NextHop]; ok {
faceMap[entry.NextHop] = BierOr(fbm, entry.Fbm)
} else {
faceMap[entry.NextHop] = BierClone(entry.Fbm)
}
}
Expand All @@ -377,5 +401,8 @@ func (b *BiftState) GetNeighborEntries() []BiftNeighborEntry {
for faceID, fbm := range faceMap {
neighbors = append(neighbors, BiftNeighborEntry{FaceID: faceID, Fbm: fbm})
}
sort.Slice(neighbors, func(i, j int) bool {
return neighbors[i].FaceID < neighbors[j].FaceID
})
return neighbors
}
32 changes: 29 additions & 3 deletions fw/bier/bier_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,10 @@ func TestBierBitManipulationEdgeCases(t *testing.T) {
a := []byte{0xFF, 0xFF, 0xFF} // 3 bytes
b := []byte{0x0F, 0xF0} // 2 bytes — shorter
res := bier.BierAnd(a, b)
if len(res) != 2 {
t.Errorf("result length should be min(3,2)=2, got %d", len(res))
if len(res) != 3 {
t.Errorf("result length should be max(3,2)=3, got %d", len(res))
}
if res[0] != 0x0F || res[1] != 0xF0 {
if res[0] != 0x0F || res[1] != 0xF0 || res[2] != 0x00 {
t.Errorf("unexpected result %v", res)
}
})
Expand Down Expand Up @@ -230,6 +230,32 @@ func TestBiftEdgeCases(t *testing.T) {
}
})

t.Run("GetNeighborEntries aggregates all bits for same face", func(t *testing.T) {
b := &bier.BiftState{}
r0 := enc.Name{enc.NewGenericComponent("r0")}
r1 := enc.Name{enc.NewGenericComponent("r1")}
r9 := enc.Name{enc.NewGenericComponent("r9")}

b.RegisterRouter(r0, 0)
b.RegisterRouter(r1, 1)
b.RegisterRouter(r9, 9)

b.UpdateNextHop(0, 77)
b.UpdateNextHop(1, 77)
b.UpdateNextHop(9, 77)
b.RebuildFbm()

neighbors := b.GetNeighborEntries()
if len(neighbors) != 1 {
t.Fatalf("expected 1 neighbor entry, got %d", len(neighbors))
}

fbm := neighbors[0].Fbm
if !bier.BierGetBit(fbm, 0) || !bier.BierGetBit(fbm, 1) || !bier.BierGetBit(fbm, 9) {
t.Fatalf("expected aggregated F-BM to contain bits 0, 1, and 9; got %08b %08b", fbm[0], fbm[1])
}
})

t.Run("RebuildFbm on empty BIFT does not panic", func(t *testing.T) {
b := &bier.BiftState{}
b.RebuildFbm() // must not panic
Expand Down
53 changes: 36 additions & 17 deletions fw/fw/thread.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/named-data/ndnd/fw/dispatch"
"github.com/named-data/ndnd/fw/table"
enc "github.com/named-data/ndnd/std/encoding"
"github.com/named-data/ndnd/std/types/optional"
"github.com/named-data/ndnd/std/utils"
)

Expand Down Expand Up @@ -58,7 +59,7 @@ type pipelineContext struct {
Pipeline forwardPipeline
PetLocalHops []*table.PetNextHop
PetEntry table.PetEntry
PetFound bool
PetFound optional.Optional[bool]
LookupName enc.Name
LocalFacesOnly bool
}
Expand All @@ -79,7 +80,7 @@ type petLocalHopsContext struct {
LookupName enc.Name
PitEntry table.PitEntry
PetEntry table.PetEntry
PetFound bool
PetFound optional.Optional[bool]
}

// forwardInContext holds arguments for tryContentStoreHit
Expand Down Expand Up @@ -417,10 +418,29 @@ func (t *Thread) validateNonce(interest *defn.FwInterest, packet *defn.Pkt) bool
return true
}

func (t *Thread) determinePipeline(packet *defn.Pkt, lookupName enc.Name) (forwardPipeline, table.PetEntry, bool) {
func petLookupState(found optional.Optional[bool]) string {
if !found.IsSet() {
return "none"
}
if found.Unwrap() {
return "true"
}
return "false"
}

func (t *Thread) ensurePetLookup(petEntry *table.PetEntry, petFound *optional.Optional[bool], lookupName enc.Name) {
if petFound.IsSet() {
return
}
entry, found := table.Pet.FindLongestPrefixEnc(lookupName)
*petEntry = entry
*petFound = optional.Some(found)
}

func (t *Thread) determinePipeline(packet *defn.Pkt, lookupName enc.Name) (forwardPipeline, table.PetEntry, optional.Optional[bool]) {
var pipeline forwardPipeline
var petEntry table.PetEntry
var petFound bool
petFound := optional.None[bool]()

routerName, routerNameSet := CfgRouterName()

Expand All @@ -438,8 +458,9 @@ func (t *Thread) determinePipeline(packet *defn.Pkt, lookupName enc.Name) (forwa
pipeline = fwUnicastTransit
}
} else {
petEntry, petFound = table.Pet.FindLongestPrefixEnc(lookupName)
if petFound && petEntry.Multicast {
petEntry, found := table.Pet.FindLongestPrefixEnc(lookupName)
petFound = optional.Some(found)
if found && petEntry.Multicast {
pipeline = fwMulticastIngress
} else {
pipeline = fwUnicastIngress
Expand All @@ -451,7 +472,7 @@ func (t *Thread) determinePipeline(packet *defn.Pkt, lookupName enc.Name) (forwa
"name", packet.Name,
"lookup", lookupName,
"pipeline", pipeline,
"petFound", petFound,
"petFound", petLookupState(petFound),
"isLocalHop", isLocalHop,
"egressRouter", len(packet.EgressRouter) > 0,
"bier", len(packet.Bier) > 0,
Expand Down Expand Up @@ -516,11 +537,8 @@ func (t *Thread) collectPetLocalHops(ctx petLocalHopsContext) []*table.PetNextHo
return nil
}

if !ctx.PetFound {
ctx.PetEntry, ctx.PetFound = table.Pet.FindLongestPrefixEnc(ctx.LookupName)
}

if !ctx.PetFound {
t.ensurePetLookup(&ctx.PetEntry, &ctx.PetFound, ctx.LookupName)
if !ctx.PetFound.GetOr(false) {
return nil
}

Expand All @@ -543,7 +561,7 @@ func (t *Thread) handleUnicastPipeline(ctx pipelineContext) {
core.Log.Trace(t, "Unicast pipeline",
"name", ctx.Pkt.Name,
"lookup", ctx.LookupName,
"petFound", ctx.PetFound,
"petFound", petLookupState(ctx.PetFound),
"localHop", isLocalHop,
"localFacesOnly", ctx.LocalFacesOnly,
)
Expand Down Expand Up @@ -586,7 +604,7 @@ func (t *Thread) handleUnicastPipeline(ctx pipelineContext) {
}
}

func (t *Thread) collectNetworkNextHops(packet *defn.Pkt, petEntry table.PetEntry, petFound bool) []StrategyCandidateHop {
func (t *Thread) collectNetworkNextHops(packet *defn.Pkt, petEntry table.PetEntry, petFound optional.Optional[bool]) []StrategyCandidateHop {
var nextNet []StrategyCandidateHop

if len(packet.EgressRouter) > 0 {
Expand All @@ -596,7 +614,7 @@ func (t *Thread) collectNetworkNextHops(packet *defn.Pkt, petEntry table.PetEntr
EgressRouter: packet.EgressRouter,
})
}
} else if petFound {
} else if petFound.GetOr(false) {
for _, er := range petEntry.EgressRouters {
for _, nextHop := range table.FibStrategyTable.FindNextHopsEnc(er) {
nextNet = append(nextNet, StrategyCandidateHop{
Expand Down Expand Up @@ -639,7 +657,7 @@ func (t *Thread) handleMulticastPipeline(ctx pipelineContext) {
core.Log.Trace(t, "Multicast pipeline",
"name", ctx.Pkt.Name,
"lookup", ctx.LookupName,
"petFound", ctx.PetFound,
"petFound", petLookupState(ctx.PetFound),
"localHop", isLocalHop,
"localFacesOnly", ctx.LocalFacesOnly,
"bier", len(ctx.Pkt.Bier),
Expand All @@ -664,7 +682,8 @@ func (t *Thread) handleMulticastPipeline(ctx pipelineContext) {
}

// BIER forwarding for multicast
if !ctx.PetFound || len(ctx.PetEntry.EgressRouters) == 0 {
t.ensurePetLookup(&ctx.PetEntry, &ctx.PetFound, ctx.LookupName)
if !ctx.PetFound.GetOr(false) || len(ctx.PetEntry.EgressRouters) == 0 {
return
}

Expand Down
Loading
Loading