From 0a4b95f3c98bfbcdd84aa64066fec146de7a9d76 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 00:57:34 +0000 Subject: [PATCH 01/36] refactor: move cmdtree to std --- cmd/ndnd/main.go | 10 +++++----- {cmd => std/utils}/cmdtree.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) rename {cmd => std/utils}/cmdtree.go (98%) diff --git a/cmd/ndnd/main.go b/cmd/ndnd/main.go index 7fc25789..d8cce0da 100644 --- a/cmd/ndnd/main.go +++ b/cmd/ndnd/main.go @@ -3,21 +3,21 @@ package main import ( "os" - "github.com/named-data/ndnd/cmd" dv "github.com/named-data/ndnd/dv/executor" fw "github.com/named-data/ndnd/fw/executor" + "github.com/named-data/ndnd/std/utils" tools "github.com/named-data/ndnd/tools" ) func main() { // create a command tree - tree := cmd.CmdTree{ + tree := utils.CmdTree{ Name: "ndnd", Help: "Named Data Networking Daemon", - Sub: []*cmd.CmdTree{{ + Sub: []*utils.CmdTree{{ Name: "fw", Help: "NDN Forwarding Daemon", - Sub: []*cmd.CmdTree{{ + Sub: []*utils.CmdTree{{ Name: "run", Help: "Start the NDN Forwarding Daemon", Fun: fw.Main, @@ -25,7 +25,7 @@ func main() { }, { Name: "dv", Help: "NDN Distance Vector Routing Daemon", - Sub: []*cmd.CmdTree{{ + Sub: []*utils.CmdTree{{ Name: "run", Help: "Start the NDN Distance Vector Routing Daemon", Fun: dv.Main, diff --git a/cmd/cmdtree.go b/std/utils/cmdtree.go similarity index 98% rename from cmd/cmdtree.go rename to std/utils/cmdtree.go index 48ef62f8..a9cf879d 100644 --- a/cmd/cmdtree.go +++ b/std/utils/cmdtree.go @@ -1,4 +1,4 @@ -package cmd +package utils import ( "fmt" From 9adc883ee33b8b0e94c8d5ad8e4ef755f753ec79 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 02:35:16 +0000 Subject: [PATCH 02/36] nfdc: implement route list --- cmd/ndnd/main.go | 18 +++-- dv/nfdc/nfdc.go | 2 +- fw/mgmt/helpers.go | 4 +- std/engine/basic/engine.go | 54 ++++++++------ std/ndn/engine.go | 3 +- std/utils/cmdtree.go | 29 ++++++-- tools/nfdc.go | 149 +++++++++++++++++++++++++++++++++++++ 7 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 tools/nfdc.go diff --git a/cmd/ndnd/main.go b/cmd/ndnd/main.go index d8cce0da..a78ba6bc 100644 --- a/cmd/ndnd/main.go +++ b/cmd/ndnd/main.go @@ -17,11 +17,7 @@ func main() { Sub: []*utils.CmdTree{{ Name: "fw", Help: "NDN Forwarding Daemon", - Sub: []*utils.CmdTree{{ - Name: "run", - Help: "Start the NDN Forwarding Daemon", - Fun: fw.Main, - }}, + Sub: fwSubtree(), }, { Name: "dv", Help: "NDN Distance Vector Routing Daemon", @@ -56,3 +52,15 @@ func main() { args[0] = tree.Name tree.Execute(args) } + +func fwSubtree() []*utils.CmdTree { + tree := []*utils.CmdTree{{ + Name: "run", + Help: "Start the NDN Forwarding Daemon", + Fun: fw.Main, + }, { + // separator for nfdc + }} + tree = append(tree, tools.GetNfdcCmdTree().Sub...) + return tree +} diff --git a/dv/nfdc/nfdc.go b/dv/nfdc/nfdc.go index 7f26d5f1..f7ac5a44 100644 --- a/dv/nfdc/nfdc.go +++ b/dv/nfdc/nfdc.go @@ -37,7 +37,7 @@ func (m *NfdMgmtThread) Start() { select { case cmd := <-m.channel: for i := 0; i < cmd.Retries || cmd.Retries < 0; i++ { - err := m.engine.ExecMgmtCmd(cmd.Module, cmd.Cmd, cmd.Args) + _, err := m.engine.ExecMgmtCmd(cmd.Module, cmd.Cmd, cmd.Args) if err != nil { log.Errorf("nfdc %s %s failed: %s %+v [%d]", cmd.Module, cmd.Cmd, cmd.Args.Name, err, i) time.Sleep(100 * time.Millisecond) diff --git a/fw/mgmt/helpers.go b/fw/mgmt/helpers.go index 11ccfdd5..4dc86309 100644 --- a/fw/mgmt/helpers.go +++ b/fw/mgmt/helpers.go @@ -57,8 +57,8 @@ func makeControlResponse(statusCode uint64, statusText string, args map[string]a // Note: The old mgmt.MakeStatusDataset is clearly wrong as it is against the single-Interest-single-Data // principle. Thus, we simply assume that the data packet should always fit in one segment. func makeStatusDataset(name enc.Name, version uint64, dataset enc.Wire) enc.Wire { - // Split into 8000 byte segments and publish - if len(dataset) > 8000 { + // TODO: Split into 8000 byte segments and publish + if dataset.Length() > 8000 { core.LogError("mgmt", "Status dataset is too large") return nil } diff --git a/std/engine/basic/engine.go b/std/engine/basic/engine.go index 53e78e20..74b25046 100644 --- a/std/engine/basic/engine.go +++ b/std/engine/basic/engine.go @@ -445,10 +445,10 @@ func (e *Engine) Express(interest *ndn.EncodedInterest, callback ndn.ExpressCall return err } -func (e *Engine) ExecMgmtCmd(module string, cmd string, args any) error { +func (e *Engine) ExecMgmtCmd(module string, cmd string, args any) (any, error) { cmdArgs, ok := args.(*mgmt.ControlArgs) if !ok { - return ndn.ErrInvalidValue{Item: "args", Value: args} + return nil, ndn.ErrInvalidValue{Item: "args", Value: args} } intCfg := &ndn.InterestConfig{ @@ -457,53 +457,63 @@ func (e *Engine) ExecMgmtCmd(module string, cmd string, args any) error { } interest, err := e.mgmtConf.MakeCmd(module, cmd, cmdArgs, intCfg) if err != nil { - return err + return err, nil + } + + type mgmtResp struct { + err error + val *mgmt.ControlResponse } - ch := make(chan error) + respCh := make(chan *mgmtResp) + err = e.Express(interest, func(args ndn.ExpressCallbackArgs) { + resp := &mgmtResp{} + defer func() { + respCh <- resp + close(respCh) + }() + if args.Result == ndn.InterestResultNack { - ch <- fmt.Errorf("nack received: %v", args.NackReason) + resp.err = fmt.Errorf("nack received: %v", args.NackReason) } else if args.Result == ndn.InterestResultTimeout { - ch <- ndn.ErrDeadlineExceed + resp.err = ndn.ErrDeadlineExceed } else if args.Result == ndn.InterestResultData { data := args.Data valid := e.cmdChecker(data.Name(), args.SigCovered, data.Signature()) if !valid { - ch <- fmt.Errorf("command signature is not valid") + resp.err = fmt.Errorf("command signature is not valid") } else { ret, err := mgmt.ParseControlResponse(enc.NewWireReader(data.Content()), true) if err != nil { - ch <- err + resp.err = err } else { + resp.val = ret if ret.Val != nil { if ret.Val.StatusCode == 200 { - ch <- nil + return } else { - errText := ret.Val.StatusText - ch <- fmt.Errorf("command failed due to error %d: %s", ret.Val.StatusCode, errText) + resp.err = fmt.Errorf("command failed due to error %d: %s", + ret.Val.StatusCode, ret.Val.StatusText) } } else { - ch <- fmt.Errorf("improper response") + resp.err = fmt.Errorf("improper response") } } } } else { - ch <- fmt.Errorf("unknown result: %v", args.Result) + resp.err = fmt.Errorf("unknown result: %v", args.Result) } - close(ch) }) if err != nil { - return err + return err, nil } - err = <-ch - if err != nil { - return err - } - return nil + + resp := <-respCh + return resp.val, resp.err } func (e *Engine) RegisterRoute(prefix enc.Name) error { - err := e.ExecMgmtCmd("rib", "register", &mgmt.ControlArgs{Name: prefix}) + _, err := e.ExecMgmtCmd("rib", "register", &mgmt.ControlArgs{Name: prefix}) if err != nil { e.log.WithField("name", prefix.String()). Errorf("Failed to register prefix: %v", err) @@ -516,7 +526,7 @@ func (e *Engine) RegisterRoute(prefix enc.Name) error { } func (e *Engine) UnregisterRoute(prefix enc.Name) error { - err := e.ExecMgmtCmd("rib", "unregister", &mgmt.ControlArgs{Name: prefix}) + _, err := e.ExecMgmtCmd("rib", "unregister", &mgmt.ControlArgs{Name: prefix}) if err != nil { e.log.WithField("name", prefix.String()). Errorf("Failed to register prefix: %v", err) diff --git a/std/ndn/engine.go b/std/ndn/engine.go index 6069ad20..7808da59 100644 --- a/std/ndn/engine.go +++ b/std/ndn/engine.go @@ -39,7 +39,8 @@ type Engine interface { UnregisterRoute(prefix enc.Name) error // ExecMgmtCmd executes a management command. // args is a pointer to mgmt.ControlArgs - ExecMgmtCmd(module string, cmd string, args any) error + // returns error and the response (error, *mgmt.ControlResponse) + ExecMgmtCmd(module string, cmd string, args any) (any, error) } type Timer interface { diff --git a/std/utils/cmdtree.go b/std/utils/cmdtree.go index a9cf879d..21e823b8 100644 --- a/std/utils/cmdtree.go +++ b/std/utils/cmdtree.go @@ -47,12 +47,31 @@ func (c *CmdTree) Execute(args []string) { // recursively search for subcommand for _, sub := range c.Sub { - if len(sub.Name) > 0 && args[1] == sub.Name { - name := args[0] + " " + args[1] - sargs := append([]string{name}, args[2:]...) - sub.Execute(sargs) - return + if len(sub.Name) == 0 { + continue // empty subcommand } + + subname := strings.Split(sub.Name, " ") + if len(args) < len(subname)+1 { + continue // not enough arguments + } + + matches := true + for i, s := range subname { + if args[i+1] != s { + matches = false + break + } + } + if !matches { + continue // no match + } + + // execute subcommand + name := args[0] + " " + sub.Name + sargs := append([]string{name}, args[1+len(subname):]...) + sub.Execute(sargs) + return } // command not found diff --git a/tools/nfdc.go b/tools/nfdc.go new file mode 100644 index 00000000..598e5f3b --- /dev/null +++ b/tools/nfdc.go @@ -0,0 +1,149 @@ +package tools + +import ( + "fmt" + "time" + + enc "github.com/named-data/ndnd/std/encoding" + "github.com/named-data/ndnd/std/engine" + "github.com/named-data/ndnd/std/log" + "github.com/named-data/ndnd/std/ndn" + mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" + "github.com/named-data/ndnd/std/utils" +) + +func GetNfdcCmdTree() utils.CmdTree { + nfdc := &Nfdc{} + execCmd := func(mod string, cmd string) func([]string) { + return func(args []string) { + nfdc.ExecCmd(mod, cmd, args) + } + } + + // all subcommands MUST be two words "module", "command" + return utils.CmdTree{ + Name: "nfdc", + Help: "NDNd Forwarder Control", + Sub: []*utils.CmdTree{{ + Name: "route list", + Help: "Print RIB routes", + Fun: nfdc.ExecRouteList, + }, { + Name: "route add", + Help: "Control RIB and FIB entries", + Fun: execCmd("rib", "add"), + }}, + } +} + +type Nfdc struct { + engine ndn.Engine +} + +func (n *Nfdc) Start() { + face := engine.NewUnixFace("/var/run/nfd/nfd.sock") + n.engine = engine.NewBasicEngine(face) + + err := n.engine.Start() + if err != nil { + log.Fatalf("Unable to start engine: %+v", err) + return + } +} + +func (n *Nfdc) Stop() { + n.engine.Stop() +} + +func (n *Nfdc) GetPrefix() enc.Name { + return enc.Name{ + enc.NewStringComponent(enc.TypeGenericNameComponent, "localhost"), + enc.NewStringComponent(enc.TypeGenericNameComponent, "nfd"), + } +} + +func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { + n.Start() + defer n.Stop() + + argmap := map[string]any{} + ctrlArgs, err := mgmt.DictToControlArgs(argmap) + if err != nil { + log.Fatalf("Invalid control args: %+v", err) + return + } + + resp, err := n.engine.ExecMgmtCmd(mod, cmd, ctrlArgs) + if err != nil { + log.Fatalf("Error executing command: %+v", err) + return + } + + log.Infof("Response: %+v", resp) +} + +func (n *Nfdc) ExecRouteList(args []string) { + suffix := enc.Name{ + enc.NewStringComponent(enc.TypeGenericNameComponent, "rib"), + enc.NewStringComponent(enc.TypeGenericNameComponent, "list"), + } + + data, err := n.fetchStatusDataset(suffix) + if err != nil { + log.Fatalf("Error fetching status dataset: %+v", err) + return + } + + status, err := mgmt.ParseRibStatus(enc.NewWireReader(data), true) + if err != nil { + log.Fatalf("Error parsing RIB status: %+v", err) + return + } + + for _, entry := range status.Entries { + for _, route := range entry.Routes { + expiry := "never" + if route.ExpirationPeriod != nil { + expiry = (time.Duration(*route.ExpirationPeriod) * time.Millisecond).String() + } + + // TODO: convert origin, flags to string + fmt.Printf("prefix=%s nexthop=%d origin=%d cost=%d flags=%d expires=%s\n", + entry.Name, route.FaceId, route.Origin, route.Cost, route.Flags, expiry) + } + } +} + +func (n *Nfdc) fetchStatusDataset(suffix enc.Name) (enc.Wire, error) { + n.Start() + defer n.Stop() + + // TODO: segmented fetch once supported by fw/mgmt + name := append(n.GetPrefix(), suffix...) + config := &ndn.InterestConfig{ + MustBeFresh: true, + CanBePrefix: true, + Lifetime: utils.IdPtr(time.Second), + Nonce: utils.ConvertNonce(n.engine.Timer().Nonce()), + } + interest, err := n.engine.Spec().MakeInterest(name, config, nil, nil) + if err != nil { + return nil, err + } + + ch := make(chan ndn.ExpressCallbackArgs) + err = n.engine.Express(interest, func(args ndn.ExpressCallbackArgs) { + ch <- args + close(ch) + }) + if err != nil { + return nil, err + } + + res := <-ch + if res.Result != ndn.InterestResultData { + return nil, fmt.Errorf("interest failed: %d", res.Result) + } + + return res.Data.Content(), nil +} From 9964b35c44aafbde1d90ca448e7f76efd88e079b Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 02:44:57 +0000 Subject: [PATCH 03/36] nfdc: add fib list --- tools/nfdc.go | 96 +++---------------------------------------- tools/nfdc_cmd.go | 26 ++++++++++++ tools/nfdc_dataset.go | 44 ++++++++++++++++++++ tools/nfdc_route.go | 71 ++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 91 deletions(-) create mode 100644 tools/nfdc_cmd.go create mode 100644 tools/nfdc_dataset.go create mode 100644 tools/nfdc_route.go diff --git a/tools/nfdc.go b/tools/nfdc.go index 598e5f3b..eefd55e9 100644 --- a/tools/nfdc.go +++ b/tools/nfdc.go @@ -1,14 +1,10 @@ package tools import ( - "fmt" - "time" - enc "github.com/named-data/ndnd/std/encoding" "github.com/named-data/ndnd/std/engine" "github.com/named-data/ndnd/std/log" "github.com/named-data/ndnd/std/ndn" - mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" "github.com/named-data/ndnd/std/utils" ) @@ -30,8 +26,12 @@ func GetNfdcCmdTree() utils.CmdTree { Fun: nfdc.ExecRouteList, }, { Name: "route add", - Help: "Control RIB and FIB entries", + Help: "Add a route to the RIB", Fun: execCmd("rib", "add"), + }, { + Name: "fib list", + Help: "Print FIB entries", + Fun: nfdc.ExecFibList, }}, } } @@ -61,89 +61,3 @@ func (n *Nfdc) GetPrefix() enc.Name { enc.NewStringComponent(enc.TypeGenericNameComponent, "nfd"), } } - -func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { - n.Start() - defer n.Stop() - - argmap := map[string]any{} - ctrlArgs, err := mgmt.DictToControlArgs(argmap) - if err != nil { - log.Fatalf("Invalid control args: %+v", err) - return - } - - resp, err := n.engine.ExecMgmtCmd(mod, cmd, ctrlArgs) - if err != nil { - log.Fatalf("Error executing command: %+v", err) - return - } - - log.Infof("Response: %+v", resp) -} - -func (n *Nfdc) ExecRouteList(args []string) { - suffix := enc.Name{ - enc.NewStringComponent(enc.TypeGenericNameComponent, "rib"), - enc.NewStringComponent(enc.TypeGenericNameComponent, "list"), - } - - data, err := n.fetchStatusDataset(suffix) - if err != nil { - log.Fatalf("Error fetching status dataset: %+v", err) - return - } - - status, err := mgmt.ParseRibStatus(enc.NewWireReader(data), true) - if err != nil { - log.Fatalf("Error parsing RIB status: %+v", err) - return - } - - for _, entry := range status.Entries { - for _, route := range entry.Routes { - expiry := "never" - if route.ExpirationPeriod != nil { - expiry = (time.Duration(*route.ExpirationPeriod) * time.Millisecond).String() - } - - // TODO: convert origin, flags to string - fmt.Printf("prefix=%s nexthop=%d origin=%d cost=%d flags=%d expires=%s\n", - entry.Name, route.FaceId, route.Origin, route.Cost, route.Flags, expiry) - } - } -} - -func (n *Nfdc) fetchStatusDataset(suffix enc.Name) (enc.Wire, error) { - n.Start() - defer n.Stop() - - // TODO: segmented fetch once supported by fw/mgmt - name := append(n.GetPrefix(), suffix...) - config := &ndn.InterestConfig{ - MustBeFresh: true, - CanBePrefix: true, - Lifetime: utils.IdPtr(time.Second), - Nonce: utils.ConvertNonce(n.engine.Timer().Nonce()), - } - interest, err := n.engine.Spec().MakeInterest(name, config, nil, nil) - if err != nil { - return nil, err - } - - ch := make(chan ndn.ExpressCallbackArgs) - err = n.engine.Express(interest, func(args ndn.ExpressCallbackArgs) { - ch <- args - close(ch) - }) - if err != nil { - return nil, err - } - - res := <-ch - if res.Result != ndn.InterestResultData { - return nil, fmt.Errorf("interest failed: %d", res.Result) - } - - return res.Data.Content(), nil -} diff --git a/tools/nfdc_cmd.go b/tools/nfdc_cmd.go new file mode 100644 index 00000000..9741ff5b --- /dev/null +++ b/tools/nfdc_cmd.go @@ -0,0 +1,26 @@ +package tools + +import ( + "github.com/named-data/ndnd/std/log" + mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" +) + +func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { + n.Start() + defer n.Stop() + + argmap := map[string]any{} + ctrlArgs, err := mgmt.DictToControlArgs(argmap) + if err != nil { + log.Fatalf("Invalid control args: %+v", err) + return + } + + resp, err := n.engine.ExecMgmtCmd(mod, cmd, ctrlArgs) + if err != nil { + log.Fatalf("Error executing command: %+v", err) + return + } + + log.Infof("Response: %+v", resp) +} diff --git a/tools/nfdc_dataset.go b/tools/nfdc_dataset.go new file mode 100644 index 00000000..c2976dc2 --- /dev/null +++ b/tools/nfdc_dataset.go @@ -0,0 +1,44 @@ +package tools + +import ( + "fmt" + "time" + + enc "github.com/named-data/ndnd/std/encoding" + "github.com/named-data/ndnd/std/ndn" + "github.com/named-data/ndnd/std/utils" +) + +func (n *Nfdc) fetchStatusDataset(suffix enc.Name) (enc.Wire, error) { + n.Start() + defer n.Stop() + + // TODO: segmented fetch once supported by fw/mgmt + name := append(n.GetPrefix(), suffix...) + config := &ndn.InterestConfig{ + MustBeFresh: true, + CanBePrefix: true, + Lifetime: utils.IdPtr(time.Second), + Nonce: utils.ConvertNonce(n.engine.Timer().Nonce()), + } + interest, err := n.engine.Spec().MakeInterest(name, config, nil, nil) + if err != nil { + return nil, err + } + + ch := make(chan ndn.ExpressCallbackArgs) + err = n.engine.Express(interest, func(args ndn.ExpressCallbackArgs) { + ch <- args + close(ch) + }) + if err != nil { + return nil, err + } + + res := <-ch + if res.Result != ndn.InterestResultData { + return nil, fmt.Errorf("interest failed: %d", res.Result) + } + + return res.Data.Content(), nil +} diff --git a/tools/nfdc_route.go b/tools/nfdc_route.go new file mode 100644 index 00000000..595eef56 --- /dev/null +++ b/tools/nfdc_route.go @@ -0,0 +1,71 @@ +package tools + +import ( + "fmt" + "strings" + "time" + + enc "github.com/named-data/ndnd/std/encoding" + "github.com/named-data/ndnd/std/log" + mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" +) + +func (n *Nfdc) ExecRouteList(args []string) { + suffix := enc.Name{ + enc.NewStringComponent(enc.TypeGenericNameComponent, "rib"), + enc.NewStringComponent(enc.TypeGenericNameComponent, "list"), + } + + data, err := n.fetchStatusDataset(suffix) + if err != nil { + log.Fatalf("Error fetching status dataset: %+v", err) + return + } + + status, err := mgmt.ParseRibStatus(enc.NewWireReader(data), true) + if err != nil { + log.Fatalf("Error parsing RIB status: %+v", err) + return + } + + for _, entry := range status.Entries { + for _, route := range entry.Routes { + expiry := "never" + if route.ExpirationPeriod != nil { + expiry = (time.Duration(*route.ExpirationPeriod) * time.Millisecond).String() + } + + // TODO: convert origin, flags to string + fmt.Printf("prefix=%s nexthop=%d origin=%d cost=%d flags=%d expires=%s\n", + entry.Name, route.FaceId, route.Origin, route.Cost, route.Flags, expiry) + } + } +} + +func (n *Nfdc) ExecFibList(args []string) { + suffix := enc.Name{ + enc.NewStringComponent(enc.TypeGenericNameComponent, "fib"), + enc.NewStringComponent(enc.TypeGenericNameComponent, "list"), + } + + data, err := n.fetchStatusDataset(suffix) + if err != nil { + log.Fatalf("Error fetching status dataset: %+v", err) + return + } + + status, err := mgmt.ParseFibStatus(enc.NewWireReader(data), true) + if err != nil { + log.Fatalf("Error parsing RIB status: %+v", err) + return + } + + fmt.Println("FIB:") + for _, entry := range status.Entries { + nexthops := make([]string, 0) + for _, record := range entry.NextHopRecords { + nexthops = append(nexthops, fmt.Sprintf("faceid=%d (cost=%d)", record.FaceId, record.Cost)) + } + fmt.Printf(" %s nexthops={%s}\n", entry.Name, strings.Join(nexthops, ", ")) + } +} From 42fa4075a0fb42e079e61fcaca5c8a9a37c6da42 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 03:13:10 +0000 Subject: [PATCH 04/36] nfdc: add more datasets --- tools/nfdc.go | 9 ++++++- tools/nfdc_face.go | 61 ++++++++++++++++++++++++++++++++++++++++++++ tools/nfdc_route.go | 2 +- tools/nfdc_status.go | 58 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 tools/nfdc_face.go create mode 100644 tools/nfdc_status.go diff --git a/tools/nfdc.go b/tools/nfdc.go index eefd55e9..8af4430f 100644 --- a/tools/nfdc.go +++ b/tools/nfdc.go @@ -16,11 +16,18 @@ func GetNfdcCmdTree() utils.CmdTree { } } - // all subcommands MUST be two words "module", "command" return utils.CmdTree{ Name: "nfdc", Help: "NDNd Forwarder Control", Sub: []*utils.CmdTree{{ + Name: "status", + Help: "Print general status", + Fun: nfdc.ExecStatusGeneral, + }, { + Name: "face list", + Help: "Print face table", + Fun: nfdc.ExecFaceList, + }, { Name: "route list", Help: "Print RIB routes", Fun: nfdc.ExecRouteList, diff --git a/tools/nfdc_face.go b/tools/nfdc_face.go new file mode 100644 index 00000000..e17c350b --- /dev/null +++ b/tools/nfdc_face.go @@ -0,0 +1,61 @@ +package tools + +import ( + "fmt" + "strings" + "time" + + enc "github.com/named-data/ndnd/std/encoding" + "github.com/named-data/ndnd/std/log" + mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" +) + +func (n *Nfdc) ExecFaceList(args []string) { + suffix := enc.Name{ + enc.NewStringComponent(enc.TypeGenericNameComponent, "faces"), + enc.NewStringComponent(enc.TypeGenericNameComponent, "list"), + } + + data, err := n.fetchStatusDataset(suffix) + if err != nil { + log.Fatalf("Error fetching status dataset: %+v", err) + return + } + + status, err := mgmt.ParseFaceStatusMsg(enc.NewWireReader(data), true) + if err != nil { + log.Fatalf("Error parsing face status: %+v", err) + return + } + + for _, entry := range status.Vals { + info := []string{} + + info = append(info, fmt.Sprintf("faceid=%d", entry.FaceId)) + info = append(info, fmt.Sprintf("remote=%s", entry.Uri)) + info = append(info, fmt.Sprintf("local=%s", entry.LocalUri)) + + congestion := []string{} + if entry.BaseCongestionMarkInterval != nil { + congestion = append(congestion, fmt.Sprintf("base-marking-interval=%s", time.Duration(*entry.BaseCongestionMarkInterval)*time.Nanosecond)) + } + if entry.DefaultCongestionThreshold != nil { + congestion = append(congestion, fmt.Sprintf("default-threshold=%dB", *entry.DefaultCongestionThreshold)) + } + if len(congestion) > 0 { + info = append(info, fmt.Sprintf("congestion={%s}", strings.Join(congestion, " "))) + } + + if entry.Mtu != nil { + info = append(info, fmt.Sprintf("mtu=%dB", *entry.Mtu)) + } + + info = append(info, fmt.Sprintf("counters={in={%di %dd %dn %dB} out={%di %dd %dn %dB}}", + entry.NInInterests, entry.NInData, entry.NInNacks, entry.NInBytes, + entry.NOutInterests, entry.NOutData, entry.NOutNacks, entry.NOutBytes)) + + info = append(info, fmt.Sprintf("flags=%d", entry.Flags)) + + fmt.Printf("%s\n", strings.Join(info, " ")) + } +} diff --git a/tools/nfdc_route.go b/tools/nfdc_route.go index 595eef56..d8ba54b6 100644 --- a/tools/nfdc_route.go +++ b/tools/nfdc_route.go @@ -56,7 +56,7 @@ func (n *Nfdc) ExecFibList(args []string) { status, err := mgmt.ParseFibStatus(enc.NewWireReader(data), true) if err != nil { - log.Fatalf("Error parsing RIB status: %+v", err) + log.Fatalf("Error parsing FIB status: %+v", err) return } diff --git a/tools/nfdc_status.go b/tools/nfdc_status.go new file mode 100644 index 00000000..cda2cc2c --- /dev/null +++ b/tools/nfdc_status.go @@ -0,0 +1,58 @@ +package tools + +import ( + "fmt" + "strings" + "time" + + enc "github.com/named-data/ndnd/std/encoding" + "github.com/named-data/ndnd/std/log" + mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" +) + +func (n *Nfdc) ExecStatusGeneral(args []string) { + suffix := enc.Name{ + enc.NewStringComponent(enc.TypeGenericNameComponent, "status"), + enc.NewStringComponent(enc.TypeGenericNameComponent, "general"), + } + + data, err := n.fetchStatusDataset(suffix) + if err != nil { + log.Fatalf("Error fetching status dataset: %+v", err) + return + } + + status, err := mgmt.ParseGeneralStatus(enc.NewWireReader(data), true) + if err != nil { + log.Fatalf("Error parsing general status: %+v", err) + return + } + + print := func(key string, value any) { + if _, ok := value.(uint64); ok { + value = fmt.Sprintf("%d", value) + } + + padding := 24 - len(key) + fmt.Printf("%s%s=%s\n", strings.Repeat(" ", padding), key, value) + } + + fmt.Println("General NFD status:") + print("version", status.NfdVersion) + print("startTime", time.UnixMilli(int64(status.StartTimestamp))) + print("currentTime", time.UnixMilli(int64(status.CurrentTimestamp))) + print("uptime", time.Duration(status.CurrentTimestamp-status.StartTimestamp)*time.Millisecond) + print("nNameTreeEntries", status.NNameTreeEntries) + print("nFibEntries", status.NFibEntries) + print("nPitEntries", status.NCsEntries) + print("nMeasurementsEntries", status.NMeasurementsEntries) + print("nCsEntries", status.NCsEntries) + print("nInInterests", status.NInInterests) + print("nOutInterests", status.NOutInterests) + print("nInData", status.NInData) + print("nOutData", status.NOutData) + print("nInNacks", status.NInNacks) + print("nOutNacks", status.NOutNacks) + print("nSatisfiedInterests", status.NSatisfiedInterests) + print("nUnsatisfiedInterests", status.NUnsatisfiedInterests) +} From e6db8e28a51a5a07f301267ee595ea69886f4669 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 03:24:29 +0000 Subject: [PATCH 05/36] nfdc: add more status datasets --- tools/nfdc.go | 7 ++++++- tools/nfdc_cs.go | 39 +++++++++++++++++++++++++++++++++++ tools/nfdc_status.go | 49 +++++++++++++++++++++----------------------- 3 files changed, 68 insertions(+), 27 deletions(-) create mode 100644 tools/nfdc_cs.go diff --git a/tools/nfdc.go b/tools/nfdc.go index 8af4430f..238e90ee 100644 --- a/tools/nfdc.go +++ b/tools/nfdc.go @@ -39,12 +39,17 @@ func GetNfdcCmdTree() utils.CmdTree { Name: "fib list", Help: "Print FIB entries", Fun: nfdc.ExecFibList, + }, { + Name: "cs info", + Help: "Print content store info", + Fun: nfdc.ExecCsInfo, }}, } } type Nfdc struct { - engine ndn.Engine + engine ndn.Engine + statusPadding int } func (n *Nfdc) Start() { diff --git a/tools/nfdc_cs.go b/tools/nfdc_cs.go new file mode 100644 index 00000000..de70cac1 --- /dev/null +++ b/tools/nfdc_cs.go @@ -0,0 +1,39 @@ +package tools + +import ( + "fmt" + + enc "github.com/named-data/ndnd/std/encoding" + "github.com/named-data/ndnd/std/log" + mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" +) + +func (n *Nfdc) ExecCsInfo(args []string) { + suffix := enc.Name{ + enc.NewStringComponent(enc.TypeGenericNameComponent, "cs"), + enc.NewStringComponent(enc.TypeGenericNameComponent, "info"), + } + + data, err := n.fetchStatusDataset(suffix) + if err != nil { + log.Fatalf("Error fetching status dataset: %+v", err) + return + } + + status, err := mgmt.ParseCsInfoMsg(enc.NewWireReader(data), true) + if err != nil || status.CsInfo == nil { + log.Fatalf("Error parsing CS info: %+v", err) + return + } + + info := status.CsInfo + + fmt.Println("CS information:") + n.statusPadding = 10 + n.printStatusLine("capacity", info.Capacity) + n.printStatusLine("admit", info.Flags&mgmt.CsEnableAdmit != 0) + n.printStatusLine("serve", info.Flags&mgmt.CsEnableServe != 0) + n.printStatusLine("nEntries", info.NCsEntries) + n.printStatusLine("nHits", info.NHits) + n.printStatusLine("nMisses", info.NMisses) +} diff --git a/tools/nfdc_status.go b/tools/nfdc_status.go index cda2cc2c..9f256b2b 100644 --- a/tools/nfdc_status.go +++ b/tools/nfdc_status.go @@ -28,31 +28,28 @@ func (n *Nfdc) ExecStatusGeneral(args []string) { return } - print := func(key string, value any) { - if _, ok := value.(uint64); ok { - value = fmt.Sprintf("%d", value) - } - - padding := 24 - len(key) - fmt.Printf("%s%s=%s\n", strings.Repeat(" ", padding), key, value) - } - fmt.Println("General NFD status:") - print("version", status.NfdVersion) - print("startTime", time.UnixMilli(int64(status.StartTimestamp))) - print("currentTime", time.UnixMilli(int64(status.CurrentTimestamp))) - print("uptime", time.Duration(status.CurrentTimestamp-status.StartTimestamp)*time.Millisecond) - print("nNameTreeEntries", status.NNameTreeEntries) - print("nFibEntries", status.NFibEntries) - print("nPitEntries", status.NCsEntries) - print("nMeasurementsEntries", status.NMeasurementsEntries) - print("nCsEntries", status.NCsEntries) - print("nInInterests", status.NInInterests) - print("nOutInterests", status.NOutInterests) - print("nInData", status.NInData) - print("nOutData", status.NOutData) - print("nInNacks", status.NInNacks) - print("nOutNacks", status.NOutNacks) - print("nSatisfiedInterests", status.NSatisfiedInterests) - print("nUnsatisfiedInterests", status.NUnsatisfiedInterests) + n.statusPadding = 24 + n.printStatusLine("version", status.NfdVersion) + n.printStatusLine("startTime", time.UnixMilli(int64(status.StartTimestamp))) + n.printStatusLine("currentTime", time.UnixMilli(int64(status.CurrentTimestamp))) + n.printStatusLine("uptime", time.Duration(status.CurrentTimestamp-status.StartTimestamp)*time.Millisecond) + n.printStatusLine("nNameTreeEntries", status.NNameTreeEntries) + n.printStatusLine("nFibEntries", status.NFibEntries) + n.printStatusLine("nPitEntries", status.NCsEntries) + n.printStatusLine("nMeasurementsEntries", status.NMeasurementsEntries) + n.printStatusLine("nCsEntries", status.NCsEntries) + n.printStatusLine("nInInterests", status.NInInterests) + n.printStatusLine("nOutInterests", status.NOutInterests) + n.printStatusLine("nInData", status.NInData) + n.printStatusLine("nOutData", status.NOutData) + n.printStatusLine("nInNacks", status.NInNacks) + n.printStatusLine("nOutNacks", status.NOutNacks) + n.printStatusLine("nSatisfiedInterests", status.NSatisfiedInterests) + n.printStatusLine("nUnsatisfiedInterests", status.NUnsatisfiedInterests) +} + +func (n *Nfdc) printStatusLine(key string, value any) { + padding := n.statusPadding - len(key) + fmt.Printf("%s%s=%v\n", strings.Repeat(" ", padding), key, value) } From 1bb789cefde8006b329ddd6cfc7a37df41bfa925 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 03:32:17 +0000 Subject: [PATCH 06/36] nfdc: refactor start --- tools/nfdc.go | 21 ++++++++++++++------- tools/nfdc_cmd.go | 3 --- tools/nfdc_dataset.go | 3 --- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/tools/nfdc.go b/tools/nfdc.go index 238e90ee..7a3df29e 100644 --- a/tools/nfdc.go +++ b/tools/nfdc.go @@ -10,11 +10,18 @@ import ( func GetNfdcCmdTree() utils.CmdTree { nfdc := &Nfdc{} - execCmd := func(mod string, cmd string) func([]string) { + cmd := func(mod string, cmd string) func([]string) { return func(args []string) { nfdc.ExecCmd(mod, cmd, args) } } + start := func(fun func([]string)) func([]string) { + return func(args []string) { + nfdc.Start() + defer nfdc.Stop() + fun(args) + } + } return utils.CmdTree{ Name: "nfdc", @@ -22,27 +29,27 @@ func GetNfdcCmdTree() utils.CmdTree { Sub: []*utils.CmdTree{{ Name: "status", Help: "Print general status", - Fun: nfdc.ExecStatusGeneral, + Fun: start(nfdc.ExecStatusGeneral), }, { Name: "face list", Help: "Print face table", - Fun: nfdc.ExecFaceList, + Fun: start(nfdc.ExecFaceList), }, { Name: "route list", Help: "Print RIB routes", - Fun: nfdc.ExecRouteList, + Fun: start(nfdc.ExecRouteList), }, { Name: "route add", Help: "Add a route to the RIB", - Fun: execCmd("rib", "add"), + Fun: start(cmd("rib", "add")), }, { Name: "fib list", Help: "Print FIB entries", - Fun: nfdc.ExecFibList, + Fun: start(nfdc.ExecFibList), }, { Name: "cs info", Help: "Print content store info", - Fun: nfdc.ExecCsInfo, + Fun: start(nfdc.ExecCsInfo), }}, } } diff --git a/tools/nfdc_cmd.go b/tools/nfdc_cmd.go index 9741ff5b..070ca532 100644 --- a/tools/nfdc_cmd.go +++ b/tools/nfdc_cmd.go @@ -6,9 +6,6 @@ import ( ) func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { - n.Start() - defer n.Stop() - argmap := map[string]any{} ctrlArgs, err := mgmt.DictToControlArgs(argmap) if err != nil { diff --git a/tools/nfdc_dataset.go b/tools/nfdc_dataset.go index c2976dc2..d722d394 100644 --- a/tools/nfdc_dataset.go +++ b/tools/nfdc_dataset.go @@ -10,9 +10,6 @@ import ( ) func (n *Nfdc) fetchStatusDataset(suffix enc.Name) (enc.Wire, error) { - n.Start() - defer n.Stop() - // TODO: segmented fetch once supported by fw/mgmt name := append(n.GetPrefix(), suffix...) config := &ndn.InterestConfig{ From c07c200a9c1258c48b4d19676750f046f0728841 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 04:08:53 +0000 Subject: [PATCH 07/36] nfdc: add face create --- tools/nfdc.go | 14 +++++++++-- tools/nfdc_cmd.go | 60 ++++++++++++++++++++++++++++++++++++++------ tools/nfdc_cs.go | 6 ++--- tools/nfdc_face.go | 6 ++--- tools/nfdc_route.go | 10 ++++---- tools/nfdc_status.go | 6 ++--- 6 files changed, 78 insertions(+), 24 deletions(-) diff --git a/tools/nfdc.go b/tools/nfdc.go index 7a3df29e..a96e9945 100644 --- a/tools/nfdc.go +++ b/tools/nfdc.go @@ -1,9 +1,11 @@ package tools import ( + "fmt" + "os" + enc "github.com/named-data/ndnd/std/encoding" "github.com/named-data/ndnd/std/engine" - "github.com/named-data/ndnd/std/log" "github.com/named-data/ndnd/std/ndn" "github.com/named-data/ndnd/std/utils" ) @@ -34,6 +36,14 @@ func GetNfdcCmdTree() utils.CmdTree { Name: "face list", Help: "Print face table", Fun: start(nfdc.ExecFaceList), + }, { + Name: "face create", + Help: "Create a face", + Fun: start(cmd("faces", "create")), + }, { + Name: "face destroy", + Help: "Destroy a face", + Fun: start(cmd("faces", "destroy")), }, { Name: "route list", Help: "Print RIB routes", @@ -65,7 +75,7 @@ func (n *Nfdc) Start() { err := n.engine.Start() if err != nil { - log.Fatalf("Unable to start engine: %+v", err) + fmt.Fprintf(os.Stderr, "Unable to start engine: %+v\n", err) return } } diff --git a/tools/nfdc_cmd.go b/tools/nfdc_cmd.go index 070ca532..44538b6a 100644 --- a/tools/nfdc_cmd.go +++ b/tools/nfdc_cmd.go @@ -1,23 +1,67 @@ package tools import ( - "github.com/named-data/ndnd/std/log" + "fmt" + "os" + "strconv" + "strings" + mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" + "github.com/named-data/ndnd/std/utils" ) func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { - argmap := map[string]any{} - ctrlArgs, err := mgmt.DictToControlArgs(argmap) + ctrlArgs := mgmt.ControlArgs{} + + for _, arg := range args[1:] { + kv := strings.SplitN(arg, "=", 2) + if len(kv) != 2 { + fmt.Fprintf(os.Stderr, "Invalid argument: %s (should be key=value)\n", arg) + return + } + n.convCmdArg(&ctrlArgs, kv[0], kv[1]) + } + + raw, err := n.engine.ExecMgmtCmd(mod, cmd, &ctrlArgs) if err != nil { - log.Fatalf("Invalid control args: %+v", err) + fmt.Fprintf(os.Stderr, "Error executing command: %+v\n", err) return } - resp, err := n.engine.ExecMgmtCmd(mod, cmd, ctrlArgs) - if err != nil { - log.Fatalf("Error executing command: %+v", err) + res, ok := raw.(*mgmt.ControlResponse) + if !ok { + fmt.Fprintf(os.Stderr, "Invalid response type: %T\n", raw) return } - log.Infof("Response: %+v", resp) + if res.Val == nil || res.Val.Params == nil { + fmt.Fprintf(os.Stderr, "Empty response: %+v\n", res) + return + } + + fmt.Printf("Status=%d (%s)\n", res.Val.StatusCode, res.Val.StatusText) + + params := res.Val.Params.ToDict() + for key, val := range params { + fmt.Printf(" %s=%v\n", key, val) + } +} + +func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { + switch key { + case "face": + if v, err := strconv.ParseUint(val, 10, 64); err == nil { + ctrlArgs.FaceId = utils.IdPtr(v) + } else { + fmt.Fprintf(os.Stderr, "Invalid face ID: %s\n", val) // TODO: support URI + os.Exit(1) + } + case "remote": + ctrlArgs.Uri = utils.IdPtr(val) + case "local": + ctrlArgs.LocalUri = utils.IdPtr(val) + default: + fmt.Fprintf(os.Stderr, "Unknown command argument key: %s\n", key) + os.Exit(1) + } } diff --git a/tools/nfdc_cs.go b/tools/nfdc_cs.go index de70cac1..64c06d6f 100644 --- a/tools/nfdc_cs.go +++ b/tools/nfdc_cs.go @@ -2,9 +2,9 @@ package tools import ( "fmt" + "os" enc "github.com/named-data/ndnd/std/encoding" - "github.com/named-data/ndnd/std/log" mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) @@ -16,13 +16,13 @@ func (n *Nfdc) ExecCsInfo(args []string) { data, err := n.fetchStatusDataset(suffix) if err != nil { - log.Fatalf("Error fetching status dataset: %+v", err) + fmt.Fprintf(os.Stderr, "Error fetching status dataset: %+v\n", err) return } status, err := mgmt.ParseCsInfoMsg(enc.NewWireReader(data), true) if err != nil || status.CsInfo == nil { - log.Fatalf("Error parsing CS info: %+v", err) + fmt.Fprintf(os.Stderr, "Error parsing CS info: %+v\n", err) return } diff --git a/tools/nfdc_face.go b/tools/nfdc_face.go index e17c350b..1d2dd68a 100644 --- a/tools/nfdc_face.go +++ b/tools/nfdc_face.go @@ -2,11 +2,11 @@ package tools import ( "fmt" + "os" "strings" "time" enc "github.com/named-data/ndnd/std/encoding" - "github.com/named-data/ndnd/std/log" mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) @@ -18,13 +18,13 @@ func (n *Nfdc) ExecFaceList(args []string) { data, err := n.fetchStatusDataset(suffix) if err != nil { - log.Fatalf("Error fetching status dataset: %+v", err) + fmt.Fprintf(os.Stderr, "Error fetching status dataset: %+v\n", err) return } status, err := mgmt.ParseFaceStatusMsg(enc.NewWireReader(data), true) if err != nil { - log.Fatalf("Error parsing face status: %+v", err) + fmt.Fprintf(os.Stderr, "Error parsing face status: %+v\n", err) return } diff --git a/tools/nfdc_route.go b/tools/nfdc_route.go index d8ba54b6..47ff0b54 100644 --- a/tools/nfdc_route.go +++ b/tools/nfdc_route.go @@ -2,11 +2,11 @@ package tools import ( "fmt" + "os" "strings" "time" enc "github.com/named-data/ndnd/std/encoding" - "github.com/named-data/ndnd/std/log" mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) @@ -18,13 +18,13 @@ func (n *Nfdc) ExecRouteList(args []string) { data, err := n.fetchStatusDataset(suffix) if err != nil { - log.Fatalf("Error fetching status dataset: %+v", err) + fmt.Fprintf(os.Stderr, "Error fetching status dataset: %+v\n", err) return } status, err := mgmt.ParseRibStatus(enc.NewWireReader(data), true) if err != nil { - log.Fatalf("Error parsing RIB status: %+v", err) + fmt.Fprintf(os.Stderr, "Error parsing RIB status: %+v\n", err) return } @@ -50,13 +50,13 @@ func (n *Nfdc) ExecFibList(args []string) { data, err := n.fetchStatusDataset(suffix) if err != nil { - log.Fatalf("Error fetching status dataset: %+v", err) + fmt.Fprintf(os.Stderr, "Error fetching status dataset: %+v\n", err) return } status, err := mgmt.ParseFibStatus(enc.NewWireReader(data), true) if err != nil { - log.Fatalf("Error parsing FIB status: %+v", err) + fmt.Fprintf(os.Stderr, "Error parsing FIB status: %+v\n", err) return } diff --git a/tools/nfdc_status.go b/tools/nfdc_status.go index 9f256b2b..ffbe57ad 100644 --- a/tools/nfdc_status.go +++ b/tools/nfdc_status.go @@ -2,11 +2,11 @@ package tools import ( "fmt" + "os" "strings" "time" enc "github.com/named-data/ndnd/std/encoding" - "github.com/named-data/ndnd/std/log" mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) @@ -18,13 +18,13 @@ func (n *Nfdc) ExecStatusGeneral(args []string) { data, err := n.fetchStatusDataset(suffix) if err != nil { - log.Fatalf("Error fetching status dataset: %+v", err) + fmt.Fprintf(os.Stderr, "Error fetching status dataset: %+v\n", err) return } status, err := mgmt.ParseGeneralStatus(enc.NewWireReader(data), true) if err != nil { - log.Fatalf("Error parsing general status: %+v", err) + fmt.Fprintf(os.Stderr, "Error parsing general status: %+v\n", err) return } From 18822a9a483134e19b9f4f373fc2e0a964874ce2 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 04:28:26 +0000 Subject: [PATCH 08/36] nfdc: improve face create --- fw/face/internal-transport.go | 7 +-- fw/face/link-service.go | 9 ++-- fw/face/multicast-udp-transport.go | 7 +-- fw/face/null-transport.go | 7 +-- fw/face/tcp-listener.go | 3 +- fw/face/transport.go | 13 ++--- fw/face/udp-listener.go | 3 +- fw/face/unicast-tcp-transport.go | 9 ++-- fw/face/unicast-udp-transport.go | 5 +- fw/face/unix-stream-transport.go | 7 +-- fw/face/web-socket-transport.go | 7 +-- fw/mgmt/face.go | 30 +++++------ .../ndn/mgmt_2022/const.go | 9 +--- tools/nfdc_cmd.go | 51 +++++++++++++++++-- 14 files changed, 107 insertions(+), 60 deletions(-) rename fw/face/persistency.go => std/ndn/mgmt_2022/const.go (71%) diff --git a/fw/face/internal-transport.go b/fw/face/internal-transport.go index 7c9ee4e6..3ce46838 100644 --- a/fw/face/internal-transport.go +++ b/fw/face/internal-transport.go @@ -13,6 +13,7 @@ import ( "github.com/named-data/ndnd/fw/core" defn "github.com/named-data/ndnd/fw/defn" enc "github.com/named-data/ndnd/std/encoding" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" spec "github.com/named-data/ndnd/std/ndn/spec_2022" "github.com/named-data/ndnd/std/utils" ) @@ -30,7 +31,7 @@ func MakeInternalTransport() *InternalTransport { t.makeTransportBase( defn.MakeInternalFaceURI(), defn.MakeInternalFaceURI(), - PersistencyPersistent, + spec_mgmt.PersistencyPersistent, defn.Local, defn.PointToPoint, defn.MaxNDNPacketSize) @@ -59,12 +60,12 @@ func (t *InternalTransport) String() string { } // SetPersistency changes the persistency of the face. -func (t *InternalTransport) SetPersistency(persistency Persistency) bool { +func (t *InternalTransport) SetPersistency(persistency spec_mgmt.Persistency) bool { if persistency == t.persistency { return true } - if persistency == PersistencyPersistent { + if persistency == spec_mgmt.PersistencyPersistent { t.persistency = persistency return true } diff --git a/fw/face/link-service.go b/fw/face/link-service.go index 579c9cd0..062e51ec 100644 --- a/fw/face/link-service.go +++ b/fw/face/link-service.go @@ -16,6 +16,7 @@ import ( defn "github.com/named-data/ndnd/fw/defn" "github.com/named-data/ndnd/fw/dispatch" "github.com/named-data/ndnd/fw/fw" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) // LinkService is an interface for link service implementations @@ -27,8 +28,8 @@ type LinkService interface { FaceID() uint64 LocalURI() *defn.URI RemoteURI() *defn.URI - Persistency() Persistency - SetPersistency(persistency Persistency) + Persistency() spec_mgmt.Persistency + SetPersistency(persistency spec_mgmt.Persistency) Scope() defn.Scope LinkType() defn.LinkType MTU() int @@ -121,12 +122,12 @@ func (l *linkServiceBase) RemoteURI() *defn.URI { } // Persistency returns the MTU of the underlying transport. -func (l *linkServiceBase) Persistency() Persistency { +func (l *linkServiceBase) Persistency() spec_mgmt.Persistency { return l.transport.Persistency() } // SetPersistency sets the MTU of the underlying transport. -func (l *linkServiceBase) SetPersistency(persistency Persistency) { +func (l *linkServiceBase) SetPersistency(persistency spec_mgmt.Persistency) { l.transport.SetPersistency(persistency) } diff --git a/fw/face/multicast-udp-transport.go b/fw/face/multicast-udp-transport.go index 7305f51d..dd3dd52a 100644 --- a/fw/face/multicast-udp-transport.go +++ b/fw/face/multicast-udp-transport.go @@ -16,6 +16,7 @@ import ( "github.com/named-data/ndnd/fw/core" defn "github.com/named-data/ndnd/fw/defn" "github.com/named-data/ndnd/fw/face/impl" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) // MulticastUDPTransport is a multicast UDP transport. @@ -48,7 +49,7 @@ func MakeMulticastUDPTransport(localURI *defn.URI) (*MulticastUDPTransport, erro t := &MulticastUDPTransport{} t.makeTransportBase( defn.DecodeURIString(remote), - localURI, PersistencyPermanent, + localURI, spec_mgmt.PersistencyPermanent, defn.NonLocal, defn.MultiAccess, defn.MaxNDNPacketSize) @@ -107,12 +108,12 @@ func (t *MulticastUDPTransport) String() string { return fmt.Sprintf("MulticastUDPTransport, FaceID=%d, RemoteURI=%s, LocalURI=%s", t.faceID, t.remoteURI, t.localURI) } -func (t *MulticastUDPTransport) SetPersistency(persistency Persistency) bool { +func (t *MulticastUDPTransport) SetPersistency(persistency spec_mgmt.Persistency) bool { if persistency == t.persistency { return true } - if persistency == PersistencyPermanent { + if persistency == spec_mgmt.PersistencyPermanent { t.persistency = persistency return true } diff --git a/fw/face/null-transport.go b/fw/face/null-transport.go index 8fb29b11..ca076c4a 100644 --- a/fw/face/null-transport.go +++ b/fw/face/null-transport.go @@ -11,6 +11,7 @@ import ( "strconv" defn "github.com/named-data/ndnd/fw/defn" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) // NullTransport is a transport that drops all packets. @@ -27,7 +28,7 @@ func MakeNullTransport() *NullTransport { t.makeTransportBase( defn.MakeNullFaceURI(), defn.MakeNullFaceURI(), - PersistencyPermanent, + spec_mgmt.PersistencyPermanent, defn.NonLocal, defn.PointToPoint, defn.MaxNDNPacketSize) @@ -39,12 +40,12 @@ func (t *NullTransport) String() string { } // SetPersistency changes the persistency of the face. -func (t *NullTransport) SetPersistency(persistency Persistency) bool { +func (t *NullTransport) SetPersistency(persistency spec_mgmt.Persistency) bool { if persistency == t.persistency { return true } - if persistency == PersistencyPermanent { + if persistency == spec_mgmt.PersistencyPermanent { t.persistency = persistency return true } diff --git a/fw/face/tcp-listener.go b/fw/face/tcp-listener.go index 48045396..629a1770 100644 --- a/fw/face/tcp-listener.go +++ b/fw/face/tcp-listener.go @@ -16,6 +16,7 @@ import ( "github.com/named-data/ndnd/fw/core" defn "github.com/named-data/ndnd/fw/defn" "github.com/named-data/ndnd/fw/face/impl" + "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) // TCPListener listens for incoming TCP unicast connections. @@ -75,7 +76,7 @@ func (l *TCPListener) Run() { continue } - newTransport, err := AcceptUnicastTCPTransport(remoteConn, l.localURI, PersistencyPersistent) + newTransport, err := AcceptUnicastTCPTransport(remoteConn, l.localURI, mgmt_2022.PersistencyPersistent) if err != nil { core.LogError(l, "Failed to create new unicast TCP transport: ", err) continue diff --git a/fw/face/transport.go b/fw/face/transport.go index 3077933c..5e4a1bf5 100644 --- a/fw/face/transport.go +++ b/fw/face/transport.go @@ -12,6 +12,7 @@ import ( "time" defn "github.com/named-data/ndnd/fw/defn" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) // transport provides an interface for transports for specific face types @@ -22,8 +23,8 @@ type transport interface { RemoteURI() *defn.URI LocalURI() *defn.URI - Persistency() Persistency - SetPersistency(persistency Persistency) bool + Persistency() spec_mgmt.Persistency + SetPersistency(persistency spec_mgmt.Persistency) bool Scope() defn.Scope LinkType() defn.LinkType MTU() int @@ -56,7 +57,7 @@ type transportBase struct { remoteURI *defn.URI localURI *defn.URI scope defn.Scope - persistency Persistency + persistency spec_mgmt.Persistency linkType defn.LinkType mtu int expirationTime *time.Time @@ -69,7 +70,7 @@ type transportBase struct { func (t *transportBase) makeTransportBase( remoteURI *defn.URI, localURI *defn.URI, - persistency Persistency, + persistency spec_mgmt.Persistency, scope defn.Scope, linkType defn.LinkType, mtu int, @@ -107,7 +108,7 @@ func (t *transportBase) RemoteURI() *defn.URI { return t.remoteURI } -func (t *transportBase) Persistency() Persistency { +func (t *transportBase) Persistency() spec_mgmt.Persistency { return t.persistency } @@ -130,7 +131,7 @@ func (t *transportBase) SetMTU(mtu int) { // ExpirationPeriod returns the time until this face expires. // If transport not on-demand, returns 0. func (t *transportBase) ExpirationPeriod() time.Duration { - if t.expirationTime == nil || t.persistency != PersistencyOnDemand { + if t.expirationTime == nil || t.persistency != spec_mgmt.PersistencyOnDemand { return 0 } return time.Until(*t.expirationTime) diff --git a/fw/face/udp-listener.go b/fw/face/udp-listener.go index 5a14a27d..32f5c6cd 100644 --- a/fw/face/udp-listener.go +++ b/fw/face/udp-listener.go @@ -17,6 +17,7 @@ import ( "github.com/named-data/ndnd/fw/core" defn "github.com/named-data/ndnd/fw/defn" "github.com/named-data/ndnd/fw/face/impl" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) // UDPListener listens for incoming UDP unicast connections. @@ -98,7 +99,7 @@ func (l *UDPListener) Run() { } // If frame received here, must be for new remote endpoint - newTransport, err := MakeUnicastUDPTransport(remoteURI, l.localURI, PersistencyOnDemand) + newTransport, err := MakeUnicastUDPTransport(remoteURI, l.localURI, spec_mgmt.PersistencyOnDemand) if err != nil { core.LogError(l, "Failed to create new unicast UDP transport: ", err) continue diff --git a/fw/face/unicast-tcp-transport.go b/fw/face/unicast-tcp-transport.go index 110b24bf..17fea07c 100644 --- a/fw/face/unicast-tcp-transport.go +++ b/fw/face/unicast-tcp-transport.go @@ -17,6 +17,7 @@ import ( "github.com/named-data/ndnd/fw/core" defn "github.com/named-data/ndnd/fw/defn" "github.com/named-data/ndnd/fw/face/impl" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" "github.com/named-data/ndnd/std/utils" ) @@ -38,7 +39,7 @@ type UnicastTCPTransport struct { func MakeUnicastTCPTransport( remoteURI *defn.URI, localURI *defn.URI, - persistency Persistency, + persistency spec_mgmt.Persistency, ) (*UnicastTCPTransport, error) { // Validate URIs. if !remoteURI.IsCanonical() || @@ -87,7 +88,7 @@ func MakeUnicastTCPTransport( func AcceptUnicastTCPTransport( remoteConn net.Conn, localURI *defn.URI, - persistency Persistency, + persistency spec_mgmt.Persistency, ) (*UnicastTCPTransport, error) { // Construct remote URI var remoteURI *defn.URI @@ -150,7 +151,7 @@ func (t *UnicastTCPTransport) String() string { return fmt.Sprintf("UnicastTCPTransport, FaceID=%d, RemoteURI=%s, LocalURI=%s", t.faceID, t.remoteURI, t.localURI) } -func (t *UnicastTCPTransport) SetPersistency(persistency Persistency) bool { +func (t *UnicastTCPTransport) SetPersistency(persistency spec_mgmt.Persistency) bool { t.persistency = persistency return true } @@ -191,7 +192,7 @@ func (t *UnicastTCPTransport) reconnect() { // However, make only one attempt to connect for non-permanent faces if !(t.conn == nil && attempt == 1) { // Do not continue if the transport is not permanent or closed - if t.Persistency() != PersistencyPermanent || t.closed { + if t.Persistency() != spec_mgmt.PersistencyPermanent || t.closed { t.rechan <- false // do not continue return } diff --git a/fw/face/unicast-udp-transport.go b/fw/face/unicast-udp-transport.go index 2865ad17..3e77b30a 100644 --- a/fw/face/unicast-udp-transport.go +++ b/fw/face/unicast-udp-transport.go @@ -18,6 +18,7 @@ import ( "github.com/named-data/ndnd/fw/core" defn "github.com/named-data/ndnd/fw/defn" "github.com/named-data/ndnd/fw/face/impl" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) // UnicastUDPTransport is a unicast UDP transport. @@ -33,7 +34,7 @@ type UnicastUDPTransport struct { func MakeUnicastUDPTransport( remoteURI *defn.URI, localURI *defn.URI, - persistency Persistency, + persistency spec_mgmt.Persistency, ) (*UnicastUDPTransport, error) { // Validate URIs if !remoteURI.IsCanonical() || (remoteURI.Scheme() != "udp4" && remoteURI.Scheme() != "udp6") || @@ -90,7 +91,7 @@ func (t *UnicastUDPTransport) String() string { return fmt.Sprintf("UnicastUDPTransport, FaceID=%d, RemoteURI=%s, LocalURI=%s", t.faceID, t.remoteURI, t.localURI) } -func (t *UnicastUDPTransport) SetPersistency(persistency Persistency) bool { +func (t *UnicastUDPTransport) SetPersistency(persistency spec_mgmt.Persistency) bool { t.persistency = persistency return true } diff --git a/fw/face/unix-stream-transport.go b/fw/face/unix-stream-transport.go index 14fdf843..d5f4967a 100644 --- a/fw/face/unix-stream-transport.go +++ b/fw/face/unix-stream-transport.go @@ -14,6 +14,7 @@ import ( "github.com/named-data/ndnd/fw/core" defn "github.com/named-data/ndnd/fw/defn" "github.com/named-data/ndnd/fw/face/impl" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) // UnixStreamTransport is a Unix stream transport for communicating with local applications. @@ -30,7 +31,7 @@ func MakeUnixStreamTransport(remoteURI *defn.URI, localURI *defn.URI, conn net.C } t := new(UnixStreamTransport) - t.makeTransportBase(remoteURI, localURI, PersistencyPersistent, defn.Local, defn.PointToPoint, defn.MaxNDNPacketSize) + t.makeTransportBase(remoteURI, localURI, spec_mgmt.PersistencyPersistent, defn.Local, defn.PointToPoint, defn.MaxNDNPacketSize) // Set connection t.conn = conn.(*net.UnixConn) @@ -44,12 +45,12 @@ func (t *UnixStreamTransport) String() string { } // SetPersistency changes the persistency of the face. -func (t *UnixStreamTransport) SetPersistency(persistency Persistency) bool { +func (t *UnixStreamTransport) SetPersistency(persistency spec_mgmt.Persistency) bool { if persistency == t.persistency { return true } - if persistency == PersistencyPersistent { + if persistency == spec_mgmt.PersistencyPersistent { t.persistency = persistency return true } diff --git a/fw/face/web-socket-transport.go b/fw/face/web-socket-transport.go index d48abdbf..abe53b02 100644 --- a/fw/face/web-socket-transport.go +++ b/fw/face/web-socket-transport.go @@ -14,6 +14,7 @@ import ( "github.com/gorilla/websocket" "github.com/named-data/ndnd/fw/core" defn "github.com/named-data/ndnd/fw/defn" + spec_mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" ) // WebSocketTransport communicates with web applications via WebSocket. @@ -32,7 +33,7 @@ func NewWebSocketTransport(localURI *defn.URI, c *websocket.Conn) (t *WebSocketT } t = &WebSocketTransport{c: c} - t.makeTransportBase(remoteURI, localURI, PersistencyOnDemand, scope, defn.PointToPoint, defn.MaxNDNPacketSize) + t.makeTransportBase(remoteURI, localURI, spec_mgmt.PersistencyOnDemand, scope, defn.PointToPoint, defn.MaxNDNPacketSize) t.running.Store(true) return t @@ -42,8 +43,8 @@ func (t *WebSocketTransport) String() string { return fmt.Sprintf("WebSocketTransport, FaceID=%d, RemoteURI=%s, LocalURI=%s", t.faceID, t.remoteURI, t.localURI) } -func (t *WebSocketTransport) SetPersistency(persistency Persistency) bool { - return persistency == PersistencyOnDemand +func (t *WebSocketTransport) SetPersistency(persistency spec_mgmt.Persistency) bool { + return persistency == spec_mgmt.PersistencyOnDemand } func (t *WebSocketTransport) GetSendQueueSize() uint64 { diff --git a/fw/mgmt/face.go b/fw/mgmt/face.go index 63800284..58b50dc4 100644 --- a/fw/mgmt/face.go +++ b/fw/mgmt/face.go @@ -141,12 +141,12 @@ func (f *FaceModule) create(interest *spec.Interest, pitToken []byte, inFace uin } // Check face persistency - persistency := face.PersistencyPersistent - if params.FacePersistency != nil && (*params.FacePersistency == uint64(face.PersistencyPersistent) || - *params.FacePersistency == uint64(face.PersistencyPermanent)) { - persistency = face.Persistency(*params.FacePersistency) + persistency := mgmt.PersistencyPersistent + if params.FacePersistency != nil && (*params.FacePersistency == uint64(mgmt.PersistencyPersistent) || + *params.FacePersistency == uint64(mgmt.PersistencyPermanent)) { + persistency = mgmt.Persistency(*params.FacePersistency) } else if params.FacePersistency != nil { - core.LogWarn(f, "Unacceptable persistency ", face.Persistency(*params.FacePersistency), + core.LogWarn(f, "Unacceptable persistency ", mgmt.Persistency(*params.FacePersistency), " for UDP face specified in ControlParameters for ", interest.Name()) response = makeControlResponse(406, "Unacceptable persistency", nil) f.manager.sendResponse(response, interest, pitToken, inFace) @@ -231,12 +231,12 @@ func (f *FaceModule) create(interest *spec.Interest, pitToken []byte, inFace uin } // Check face persistency - persistency := face.PersistencyPersistent - if params.FacePersistency != nil && (*params.FacePersistency == uint64(face.PersistencyPersistent) || - *params.FacePersistency == uint64(face.PersistencyPermanent)) { - persistency = face.Persistency(*params.FacePersistency) + persistency := mgmt.PersistencyPersistent + if params.FacePersistency != nil && (*params.FacePersistency == uint64(mgmt.PersistencyPersistent) || + *params.FacePersistency == uint64(mgmt.PersistencyPermanent)) { + persistency = mgmt.Persistency(*params.FacePersistency) } else if params.FacePersistency != nil { - core.LogWarn(f, "Unacceptable persistency ", face.Persistency(*params.FacePersistency), + core.LogWarn(f, "Unacceptable persistency ", mgmt.Persistency(*params.FacePersistency), " for UDP face specified in ControlParameters for ", interest.Name()) response = makeControlResponse(406, "Unacceptable persistency", nil) f.manager.sendResponse(response, interest, pitToken, inFace) @@ -372,16 +372,16 @@ func (f *FaceModule) update(interest *spec.Interest, pitToken []byte, inFace uin } if params.FacePersistency != nil { - if selectedFace.RemoteURI().Scheme() == "ether" && *params.FacePersistency != uint64(face.PersistencyPermanent) { + if selectedFace.RemoteURI().Scheme() == "ether" && *params.FacePersistency != uint64(mgmt.PersistencyPermanent) { responseParams["FacePersistency"] = uint64(*params.FacePersistency) areParamsValid = false } else if (selectedFace.RemoteURI().Scheme() == "udp4" || selectedFace.RemoteURI().Scheme() == "udp6") && - *params.FacePersistency != uint64(face.PersistencyPersistent) && - *params.FacePersistency != uint64(face.PersistencyPermanent) { + *params.FacePersistency != uint64(mgmt.PersistencyPersistent) && + *params.FacePersistency != uint64(mgmt.PersistencyPermanent) { responseParams["FacePersistency"] = uint64(*params.FacePersistency) areParamsValid = false } else if selectedFace.LocalURI().Scheme() == "unix" && - *params.FacePersistency != uint64(face.PersistencyPersistent) { + *params.FacePersistency != uint64(mgmt.PersistencyPersistent) { responseParams["FacePersistency"] = uint64(*params.FacePersistency) areParamsValid = false } @@ -408,7 +408,7 @@ func (f *FaceModule) update(interest *spec.Interest, pitToken []byte, inFace uin // Persistency if params.FacePersistency != nil { // Correctness of FacePersistency already validated - selectedFace.SetPersistency(face.Persistency(*params.FacePersistency)) + selectedFace.SetPersistency(mgmt.Persistency(*params.FacePersistency)) } options := selectedFace.(*face.NDNLPLinkService).Options() diff --git a/fw/face/persistency.go b/std/ndn/mgmt_2022/const.go similarity index 71% rename from fw/face/persistency.go rename to std/ndn/mgmt_2022/const.go index 294e1919..0460a1ed 100644 --- a/fw/face/persistency.go +++ b/std/ndn/mgmt_2022/const.go @@ -1,11 +1,4 @@ -/* YaNFD - Yet another NDN Forwarding Daemon - * - * Copyright (C) 2020-2021 Eric Newberry. - * - * This file is licensed under the terms of the MIT License, as found in LICENSE.md. - */ - -package face +package mgmt_2022 // Persistency represents the persistency of a face. type Persistency uint64 diff --git a/tools/nfdc_cmd.go b/tools/nfdc_cmd.go index 44538b6a..20aeb458 100644 --- a/tools/nfdc_cmd.go +++ b/tools/nfdc_cmd.go @@ -3,6 +3,7 @@ package tools import ( "fmt" "os" + "sort" "strconv" "strings" @@ -22,9 +23,9 @@ func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { n.convCmdArg(&ctrlArgs, kv[0], kv[1]) } - raw, err := n.engine.ExecMgmtCmd(mod, cmd, &ctrlArgs) - if err != nil { - fmt.Fprintf(os.Stderr, "Error executing command: %+v\n", err) + raw, execErr := n.engine.ExecMgmtCmd(mod, cmd, &ctrlArgs) + if raw == nil { + fmt.Fprintf(os.Stderr, "Error executing command: %+v\n", execErr) return } @@ -42,12 +43,42 @@ func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { fmt.Printf("Status=%d (%s)\n", res.Val.StatusCode, res.Val.StatusText) params := res.Val.Params.ToDict() - for key, val := range params { + + keys := make([]string, 0, len(params)) + for key := range params { + keys = append(keys, key) + } + sort.Strings(keys) + + for _, key := range keys { + val := params[key] + + // convert some values to human-readable form + switch key { + case "FacePersistency": + val = mgmt.Persistency(val.(uint64)).String() + } + fmt.Printf(" %s=%v\n", key, val) } + + if execErr != nil { + os.Exit(1) + } } func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { + // helper function to parse uint64 values + parseUint := func(val string) uint64 { + v, err := strconv.ParseUint(val, 10, 64) + if err != nil { + fmt.Fprintf(os.Stderr, "Invalid value for %s: %s\n", key, val) + os.Exit(1) + } + return v + } + + // convert key-value pairs to command arguments switch key { case "face": if v, err := strconv.ParseUint(val, 10, 64); err == nil { @@ -60,6 +91,18 @@ func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { ctrlArgs.Uri = utils.IdPtr(val) case "local": ctrlArgs.LocalUri = utils.IdPtr(val) + case "mtu": + ctrlArgs.Mtu = utils.IdPtr(parseUint(val)) + case "persistency": + switch val { + case "permanent": + ctrlArgs.FacePersistency = utils.IdPtr(uint64(mgmt.PersistencyPermanent)) + case "persistent": + ctrlArgs.FacePersistency = utils.IdPtr(uint64(mgmt.PersistencyPersistent)) + default: + fmt.Fprintf(os.Stderr, "Invalid persistency: %s\n", val) + os.Exit(1) + } default: fmt.Fprintf(os.Stderr, "Unknown command argument key: %s\n", key) os.Exit(1) From 7304e4665a68a5ebfc0596bc9aa1bb159c8f137a Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 04:49:29 +0000 Subject: [PATCH 09/36] nfdc: implement route create and destroy --- tools/nfdc.go | 20 +++++++++++++++----- tools/nfdc_cmd.go | 44 ++++++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/tools/nfdc.go b/tools/nfdc.go index a96e9945..57542a48 100644 --- a/tools/nfdc.go +++ b/tools/nfdc.go @@ -12,9 +12,9 @@ import ( func GetNfdcCmdTree() utils.CmdTree { nfdc := &Nfdc{} - cmd := func(mod string, cmd string) func([]string) { + cmd := func(mod string, cmd string, defaults []string) func([]string) { return func(args []string) { - nfdc.ExecCmd(mod, cmd, args) + nfdc.ExecCmd(mod, cmd, args, defaults) } } start := func(fun func([]string)) func([]string) { @@ -39,11 +39,13 @@ func GetNfdcCmdTree() utils.CmdTree { }, { Name: "face create", Help: "Create a face", - Fun: start(cmd("faces", "create")), + Fun: start(cmd("faces", "create", []string{ + "persistency=persistent", + })), }, { Name: "face destroy", Help: "Destroy a face", - Fun: start(cmd("faces", "destroy")), + Fun: start(cmd("faces", "destroy", []string{})), }, { Name: "route list", Help: "Print RIB routes", @@ -51,7 +53,15 @@ func GetNfdcCmdTree() utils.CmdTree { }, { Name: "route add", Help: "Add a route to the RIB", - Fun: start(cmd("rib", "add")), + Fun: start(cmd("rib", "register", []string{ + "cost=0", "origin=255", + })), + }, { + Name: "route remove", + Help: "Remove a route from the RIB", + Fun: start(cmd("rib", "unregister", []string{ + "origin=255", + })), }, { Name: "fib list", Help: "Print FIB entries", diff --git a/tools/nfdc_cmd.go b/tools/nfdc_cmd.go index 20aeb458..d30242ab 100644 --- a/tools/nfdc_cmd.go +++ b/tools/nfdc_cmd.go @@ -7,14 +7,17 @@ import ( "strconv" "strings" + enc "github.com/named-data/ndnd/std/encoding" mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" "github.com/named-data/ndnd/std/utils" ) -func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { +func (n *Nfdc) ExecCmd(mod string, cmd string, args []string, defaults []string) { + // parse command arguments ctrlArgs := mgmt.ControlArgs{} - for _, arg := range args[1:] { + // set default values first, then user-provided values + for _, arg := range append(defaults, args[1:]...) { kv := strings.SplitN(arg, "=", 2) if len(kv) != 2 { fmt.Fprintf(os.Stderr, "Invalid argument: %s (should be key=value)\n", arg) @@ -23,12 +26,14 @@ func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { n.convCmdArg(&ctrlArgs, kv[0], kv[1]) } + // execute command raw, execErr := n.engine.ExecMgmtCmd(mod, cmd, &ctrlArgs) if raw == nil { fmt.Fprintf(os.Stderr, "Error executing command: %+v\n", execErr) return } + // parse response res, ok := raw.(*mgmt.ControlResponse) if !ok { fmt.Fprintf(os.Stderr, "Invalid response type: %T\n", raw) @@ -40,16 +45,18 @@ func (n *Nfdc) ExecCmd(mod string, cmd string, args []string) { return } + // print status code and text fmt.Printf("Status=%d (%s)\n", res.Val.StatusCode, res.Val.StatusText) + // iterate over parameters in sorted order params := res.Val.Params.ToDict() - keys := make([]string, 0, len(params)) for key := range params { keys = append(keys, key) } sort.Strings(keys) + // print parameters for _, key := range keys { val := params[key] @@ -73,20 +80,16 @@ func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { v, err := strconv.ParseUint(val, 10, 64) if err != nil { fmt.Fprintf(os.Stderr, "Invalid value for %s: %s\n", key, val) - os.Exit(1) + os.Exit(9) } return v } // convert key-value pairs to command arguments switch key { + // face arguments case "face": - if v, err := strconv.ParseUint(val, 10, 64); err == nil { - ctrlArgs.FaceId = utils.IdPtr(v) - } else { - fmt.Fprintf(os.Stderr, "Invalid face ID: %s\n", val) // TODO: support URI - os.Exit(1) - } + ctrlArgs.FaceId = utils.IdPtr(parseUint(val)) case "remote": ctrlArgs.Uri = utils.IdPtr(val) case "local": @@ -101,10 +104,27 @@ func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { ctrlArgs.FacePersistency = utils.IdPtr(uint64(mgmt.PersistencyPersistent)) default: fmt.Fprintf(os.Stderr, "Invalid persistency: %s\n", val) - os.Exit(1) + os.Exit(9) + } + + // route arguments + case "name": + name, err := enc.NameFromStr(val) + if err != nil { + fmt.Fprintf(os.Stderr, "Invalid name for %s: %s\n", key, val) + os.Exit(9) } + ctrlArgs.Name = name + case "cost": + ctrlArgs.Cost = utils.IdPtr(parseUint(val)) + case "origin": + ctrlArgs.Origin = utils.IdPtr(parseUint(val)) + case "expires": + ctrlArgs.ExpirationPeriod = utils.IdPtr(parseUint(val)) + + // unknown argument default: fmt.Fprintf(os.Stderr, "Unknown command argument key: %s\n", key) - os.Exit(1) + os.Exit(9) } } From 451618a7b81a4311ccdfb6d29248e333ef47fde8 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 04:56:50 +0000 Subject: [PATCH 10/36] nfdc: implement strategy commands --- tools/nfdc.go | 12 ++++++++++++ tools/nfdc_cmd.go | 23 ++++++++++++++++------- tools/nfdc_strategy.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 tools/nfdc_strategy.go diff --git a/tools/nfdc.go b/tools/nfdc.go index 57542a48..68fc7020 100644 --- a/tools/nfdc.go +++ b/tools/nfdc.go @@ -70,6 +70,18 @@ func GetNfdcCmdTree() utils.CmdTree { Name: "cs info", Help: "Print content store info", Fun: start(nfdc.ExecCsInfo), + }, { + Name: "strategy list", + Help: "Print strategy choices", + Fun: start(nfdc.ExecStrategyList), + }, { + Name: "strategy set", + Help: "Set strategy choice", + Fun: start(cmd("strategy-choice", "set", []string{})), + }, { + Name: "strategy unset", + Help: "Unset strategy choice", + Fun: start(cmd("strategy-choice", "unset", []string{})), }}, } } diff --git a/tools/nfdc_cmd.go b/tools/nfdc_cmd.go index d30242ab..b70edcd1 100644 --- a/tools/nfdc_cmd.go +++ b/tools/nfdc_cmd.go @@ -85,6 +85,16 @@ func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { return v } + // helper function to parse name values + parseName := func(val string) enc.Name { + name, err := enc.NameFromStr(val) + if err != nil { + fmt.Fprintf(os.Stderr, "Invalid name for %s: %s\n", key, val) + os.Exit(9) + } + return name + } + // convert key-value pairs to command arguments switch key { // face arguments @@ -108,13 +118,8 @@ func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { } // route arguments - case "name": - name, err := enc.NameFromStr(val) - if err != nil { - fmt.Fprintf(os.Stderr, "Invalid name for %s: %s\n", key, val) - os.Exit(9) - } - ctrlArgs.Name = name + case "prefix": + ctrlArgs.Name = parseName(val) case "cost": ctrlArgs.Cost = utils.IdPtr(parseUint(val)) case "origin": @@ -122,6 +127,10 @@ func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { case "expires": ctrlArgs.ExpirationPeriod = utils.IdPtr(parseUint(val)) + // strategy arguments + case "strategy": + ctrlArgs.Strategy = &mgmt.Strategy{Name: parseName(val)} + // unknown argument default: fmt.Fprintf(os.Stderr, "Unknown command argument key: %s\n", key) diff --git a/tools/nfdc_strategy.go b/tools/nfdc_strategy.go new file mode 100644 index 00000000..dba514ae --- /dev/null +++ b/tools/nfdc_strategy.go @@ -0,0 +1,34 @@ +package tools + +import ( + "fmt" + "os" + + enc "github.com/named-data/ndnd/std/encoding" + mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022" +) + +func (n *Nfdc) ExecStrategyList(args []string) { + suffix := enc.Name{ + enc.NewStringComponent(enc.TypeGenericNameComponent, "strategy-choice"), + enc.NewStringComponent(enc.TypeGenericNameComponent, "list"), + } + + data, err := n.fetchStatusDataset(suffix) + if err != nil { + fmt.Fprintf(os.Stderr, "Error fetching status dataset: %+v\n", err) + return + } + + status, err := mgmt.ParseStrategyChoiceMsg(enc.NewWireReader(data), true) + if err != nil { + fmt.Fprintf(os.Stderr, "Error parsing strategy list: %+v\n", err) + return + } + + for _, entry := range status.StrategyChoices { + if entry.Strategy != nil { + fmt.Printf("prefix=%s strategy=%s\n", entry.Name, entry.Strategy.Name) + } + } +} From 5f76601cd5e475b57eb169d85ccbaf8f78533fc9 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 05:09:20 +0000 Subject: [PATCH 11/36] std: fix docs --- std/ndn/engine.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/ndn/engine.go b/std/ndn/engine.go index 7808da59..869cc3db 100644 --- a/std/ndn/engine.go +++ b/std/ndn/engine.go @@ -38,8 +38,8 @@ type Engine interface { // UnregisterRoute unregisters a route of prefix to the local forwarder. UnregisterRoute(prefix enc.Name) error // ExecMgmtCmd executes a management command. - // args is a pointer to mgmt.ControlArgs - // returns error and the response (error, *mgmt.ControlResponse) + // args are the control arguments (*mgmt.ControlArgs) + // returns response and error if any (*mgmt.ControlResponse, error) ExecMgmtCmd(module string, cmd string, args any) (any, error) } From bbf0233d0602726e3c701031c9eda03caba25e43 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 05:11:16 +0000 Subject: [PATCH 12/36] refactor: move nfdc to package --- cmd/ndnd/main.go | 3 ++- tools/{ => nfdc}/nfdc.go | 2 +- tools/{ => nfdc}/nfdc_cmd.go | 2 +- tools/{ => nfdc}/nfdc_cs.go | 2 +- tools/{ => nfdc}/nfdc_dataset.go | 2 +- tools/{ => nfdc}/nfdc_face.go | 2 +- tools/{ => nfdc}/nfdc_route.go | 2 +- tools/{ => nfdc}/nfdc_status.go | 2 +- tools/{ => nfdc}/nfdc_strategy.go | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) rename tools/{ => nfdc}/nfdc.go (99%) rename tools/{ => nfdc}/nfdc_cmd.go (99%) rename tools/{ => nfdc}/nfdc_cs.go (98%) rename tools/{ => nfdc}/nfdc_dataset.go (98%) rename tools/{ => nfdc}/nfdc_face.go (99%) rename tools/{ => nfdc}/nfdc_route.go (99%) rename tools/{ => nfdc}/nfdc_status.go (99%) rename tools/{ => nfdc}/nfdc_strategy.go (98%) diff --git a/cmd/ndnd/main.go b/cmd/ndnd/main.go index a78ba6bc..82222cdd 100644 --- a/cmd/ndnd/main.go +++ b/cmd/ndnd/main.go @@ -7,6 +7,7 @@ import ( fw "github.com/named-data/ndnd/fw/executor" "github.com/named-data/ndnd/std/utils" tools "github.com/named-data/ndnd/tools" + nfdc "github.com/named-data/ndnd/tools/nfdc" ) func main() { @@ -61,6 +62,6 @@ func fwSubtree() []*utils.CmdTree { }, { // separator for nfdc }} - tree = append(tree, tools.GetNfdcCmdTree().Sub...) + tree = append(tree, nfdc.GetNfdcCmdTree().Sub...) return tree } diff --git a/tools/nfdc.go b/tools/nfdc/nfdc.go similarity index 99% rename from tools/nfdc.go rename to tools/nfdc/nfdc.go index 68fc7020..b50c2613 100644 --- a/tools/nfdc.go +++ b/tools/nfdc/nfdc.go @@ -1,4 +1,4 @@ -package tools +package nfdc import ( "fmt" diff --git a/tools/nfdc_cmd.go b/tools/nfdc/nfdc_cmd.go similarity index 99% rename from tools/nfdc_cmd.go rename to tools/nfdc/nfdc_cmd.go index b70edcd1..2532264e 100644 --- a/tools/nfdc_cmd.go +++ b/tools/nfdc/nfdc_cmd.go @@ -1,4 +1,4 @@ -package tools +package nfdc import ( "fmt" diff --git a/tools/nfdc_cs.go b/tools/nfdc/nfdc_cs.go similarity index 98% rename from tools/nfdc_cs.go rename to tools/nfdc/nfdc_cs.go index 64c06d6f..a3e24a54 100644 --- a/tools/nfdc_cs.go +++ b/tools/nfdc/nfdc_cs.go @@ -1,4 +1,4 @@ -package tools +package nfdc import ( "fmt" diff --git a/tools/nfdc_dataset.go b/tools/nfdc/nfdc_dataset.go similarity index 98% rename from tools/nfdc_dataset.go rename to tools/nfdc/nfdc_dataset.go index d722d394..3bc825e5 100644 --- a/tools/nfdc_dataset.go +++ b/tools/nfdc/nfdc_dataset.go @@ -1,4 +1,4 @@ -package tools +package nfdc import ( "fmt" diff --git a/tools/nfdc_face.go b/tools/nfdc/nfdc_face.go similarity index 99% rename from tools/nfdc_face.go rename to tools/nfdc/nfdc_face.go index 1d2dd68a..c33ad919 100644 --- a/tools/nfdc_face.go +++ b/tools/nfdc/nfdc_face.go @@ -1,4 +1,4 @@ -package tools +package nfdc import ( "fmt" diff --git a/tools/nfdc_route.go b/tools/nfdc/nfdc_route.go similarity index 99% rename from tools/nfdc_route.go rename to tools/nfdc/nfdc_route.go index 47ff0b54..0e8e2848 100644 --- a/tools/nfdc_route.go +++ b/tools/nfdc/nfdc_route.go @@ -1,4 +1,4 @@ -package tools +package nfdc import ( "fmt" diff --git a/tools/nfdc_status.go b/tools/nfdc/nfdc_status.go similarity index 99% rename from tools/nfdc_status.go rename to tools/nfdc/nfdc_status.go index ffbe57ad..331eb213 100644 --- a/tools/nfdc_status.go +++ b/tools/nfdc/nfdc_status.go @@ -1,4 +1,4 @@ -package tools +package nfdc import ( "fmt" diff --git a/tools/nfdc_strategy.go b/tools/nfdc/nfdc_strategy.go similarity index 98% rename from tools/nfdc_strategy.go rename to tools/nfdc/nfdc_strategy.go index dba514ae..be802b70 100644 --- a/tools/nfdc_strategy.go +++ b/tools/nfdc/nfdc_strategy.go @@ -1,4 +1,4 @@ -package tools +package nfdc import ( "fmt" From 52cf0a7a5d5c1cd68a509b4077f52239a7502f63 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 16:40:02 +0000 Subject: [PATCH 13/36] fw: add missing nil check --- fw/mgmt/face.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fw/mgmt/face.go b/fw/mgmt/face.go index 58b50dc4..93ffbbe7 100644 --- a/fw/mgmt/face.go +++ b/fw/mgmt/face.go @@ -550,7 +550,7 @@ func (f *FaceModule) query(interest *spec.Interest, pitToken []byte, _ uint64) { return } filterV, err := mgmt.ParseFaceQueryFilter(enc.NewBufferReader(interest.NameV[f.manager.prefixLength()+2].Val), true) - if err != nil { + if err != nil || filterV == nil || filterV.Val == nil { return } filter := filterV.Val From 4a435967e28d3cb366db51fa46ee5c047fe0f12b Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 17:13:39 +0000 Subject: [PATCH 14/36] nfdc: add face creation --- fw/mgmt/face.go | 20 +++++++++- tools/nfdc/nfdc_cmd.go | 87 ++++++++++++++++++++++++++++++++++++++++- tools/nfdc/nfdc_face.go | 4 +- 3 files changed, 107 insertions(+), 4 deletions(-) diff --git a/fw/mgmt/face.go b/fw/mgmt/face.go index 93ffbbe7..3ddfa79d 100644 --- a/fw/mgmt/face.go +++ b/fw/mgmt/face.go @@ -555,6 +555,22 @@ func (f *FaceModule) query(interest *spec.Interest, pitToken []byte, _ uint64) { } filter := filterV.Val + // canonize URI if present in filter + var filterUri *defn.URI + if filter.Uri != nil { + filterUri = defn.DecodeURIString(*filter.Uri) + if filterUri == nil { + core.LogWarn(f, "Cannot decode URI in FaceQueryFilter ", filterUri) + return + } + err = filterUri.Canonize() + if err != nil { + core.LogWarn(f, "Cannot canonize URI in FaceQueryFilter ", filterUri) + return + } + } + + // filter all faces to match filter faces := face.FaceTable.GetAll() matchingFaces := make([]int, 0) for pos, face := range faces { @@ -568,7 +584,7 @@ func (f *FaceModule) query(interest *spec.Interest, pitToken []byte, _ uint64) { continue } - if filter.Uri != nil && *filter.Uri != face.RemoteURI().String() { + if filterUri != nil && filterUri.String() != face.RemoteURI().String() { continue } @@ -592,7 +608,7 @@ func (f *FaceModule) query(interest *spec.Interest, pitToken []byte, _ uint64) { } // We have to sort these or they appear in a strange order - //sort.Slice(matchingFaces, func(a int, b int) bool { return matchingFaces[a] < matchingFaces[b] }) + sort.Slice(matchingFaces, func(a int, b int) bool { return matchingFaces[a] < matchingFaces[b] }) dataset := &mgmt.FaceStatusMsg{} for _, pos := range matchingFaces { diff --git a/tools/nfdc/nfdc_cmd.go b/tools/nfdc/nfdc_cmd.go index 2532264e..76564a69 100644 --- a/tools/nfdc/nfdc_cmd.go +++ b/tools/nfdc/nfdc_cmd.go @@ -23,7 +23,9 @@ func (n *Nfdc) ExecCmd(mod string, cmd string, args []string, defaults []string) fmt.Fprintf(os.Stderr, "Invalid argument: %s (should be key=value)\n", arg) return } - n.convCmdArg(&ctrlArgs, kv[0], kv[1]) + + key, val := n.preprocessArg(&ctrlArgs, mod, cmd, kv[0], kv[1]) + n.convCmdArg(&ctrlArgs, key, val) } // execute command @@ -74,6 +76,89 @@ func (n *Nfdc) ExecCmd(mod string, cmd string, args []string, defaults []string) } } +func (n *Nfdc) preprocessArg( + ctrlArgs *mgmt.ControlArgs, + mod string, cmd string, + key string, val string, +) (string, string) { + // convert face from URI to face ID + if key == "face" && strings.Contains(val, "://") { + // query the existing face (without attempting to create a new one) + // for faces/create, we require specifying "remote" and/or "local" instead + if (mod == "faces" && cmd == "destroy") || + (mod == "rib" && cmd == "unregister") { + + filter := mgmt.FaceQueryFilter{ + Val: &mgmt.FaceQueryFilterValue{Uri: utils.IdPtr(val)}, + } + + dataset, err := n.fetchStatusDataset(enc.Name{ + enc.NewStringComponent(enc.TypeGenericNameComponent, "faces"), + enc.NewStringComponent(enc.TypeGenericNameComponent, "query"), + enc.NewBytesComponent(enc.TypeGenericNameComponent, filter.Encode().Join()), + }) + if dataset == nil { + fmt.Fprintf(os.Stderr, "Error fetching face status dataset: %+v\n", err) + os.Exit(1) + } + + status, err := mgmt.ParseFaceStatusMsg(enc.NewWireReader(dataset), true) + if err != nil { + fmt.Fprintf(os.Stderr, "Error parsing face status: %+v\n", err) + os.Exit(1) + } + + // face needs to exist, otherwise no point in continuing + if len(status.Vals) == 0 { + fmt.Fprintf(os.Stderr, "Face not found for URI: %s\n", val) + os.Exit(9) + } else if len(status.Vals) > 1 { + fmt.Fprintf(os.Stderr, "Multiple faces found for URI: %s\n", val) + os.Exit(9) + } + + // found the face we need + return key, fmt.Sprintf("%d", status.Vals[0].FaceId) + } + + // only for rib/register, create a new face if it doesn't exist + if mod == "rib" && cmd == "register" { + // copy over any face arguments that are already set + faceArgs := mgmt.ControlArgs{Uri: utils.IdPtr(val)} + if ctrlArgs.LocalUri != nil { + faceArgs.LocalUri = ctrlArgs.LocalUri + ctrlArgs.LocalUri = nil + } + if ctrlArgs.Mtu != nil { + faceArgs.Mtu = ctrlArgs.Mtu + ctrlArgs.Mtu = nil + } + if ctrlArgs.FacePersistency != nil { + faceArgs.FacePersistency = ctrlArgs.FacePersistency + ctrlArgs.FacePersistency = nil + } + + // create or use existing face + raw, execErr := n.engine.ExecMgmtCmd("faces", "create", &faceArgs) + if raw == nil { + fmt.Fprintf(os.Stderr, "Error creating face: %+v\n", execErr) + os.Exit(1) + } + + res, ok := raw.(*mgmt.ControlResponse) + if !ok || res.Val == nil || res.Val.Params == nil { + fmt.Fprintf(os.Stderr, "Invalid or empty response type: %T\n", raw) + os.Exit(1) + } + + fmt.Printf("Using FaceId=%d (%d)\n", *res.Val.Params.FaceId, res.Val.StatusCode) + return key, fmt.Sprintf("%d", *res.Val.Params.FaceId) + } + } + + return key, val +} + func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { // helper function to parse uint64 values parseUint := func(val string) uint64 { diff --git a/tools/nfdc/nfdc_face.go b/tools/nfdc/nfdc_face.go index c33ad919..159ad53f 100644 --- a/tools/nfdc/nfdc_face.go +++ b/tools/nfdc/nfdc_face.go @@ -54,7 +54,9 @@ func (n *Nfdc) ExecFaceList(args []string) { entry.NInInterests, entry.NInData, entry.NInNacks, entry.NInBytes, entry.NOutInterests, entry.NOutData, entry.NOutNacks, entry.NOutBytes)) - info = append(info, fmt.Sprintf("flags=%d", entry.Flags)) + flags := []string{} + flags = append(flags, strings.ToLower(mgmt.Persistency(entry.FacePersistency).String())) + info = append(info, fmt.Sprintf("flags={%s}", strings.Join(flags, " "))) fmt.Printf("%s\n", strings.Join(info, " ")) } From c90295d32515d2a1fc908fd1cfba19e0a1cde611 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 17:16:54 +0000 Subject: [PATCH 15/36] nfdc: fix output of route add --- tools/nfdc/nfdc_cmd.go | 62 ++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/tools/nfdc/nfdc_cmd.go b/tools/nfdc/nfdc_cmd.go index 76564a69..2b36f7dd 100644 --- a/tools/nfdc/nfdc_cmd.go +++ b/tools/nfdc/nfdc_cmd.go @@ -37,39 +37,11 @@ func (n *Nfdc) ExecCmd(mod string, cmd string, args []string, defaults []string) // parse response res, ok := raw.(*mgmt.ControlResponse) - if !ok { - fmt.Fprintf(os.Stderr, "Invalid response type: %T\n", raw) + if !ok || res.Val == nil || res.Val.Params == nil { + fmt.Fprintf(os.Stderr, "Invalid or empty response type: %T\n", raw) return } - - if res.Val == nil || res.Val.Params == nil { - fmt.Fprintf(os.Stderr, "Empty response: %+v\n", res) - return - } - - // print status code and text - fmt.Printf("Status=%d (%s)\n", res.Val.StatusCode, res.Val.StatusText) - - // iterate over parameters in sorted order - params := res.Val.Params.ToDict() - keys := make([]string, 0, len(params)) - for key := range params { - keys = append(keys, key) - } - sort.Strings(keys) - - // print parameters - for _, key := range keys { - val := params[key] - - // convert some values to human-readable form - switch key { - case "FacePersistency": - val = mgmt.Persistency(val.(uint64)).String() - } - - fmt.Printf(" %s=%v\n", key, val) - } + n.printCtrlResponse(res) if execErr != nil { os.Exit(1) @@ -150,8 +122,8 @@ func (n *Nfdc) preprocessArg( fmt.Fprintf(os.Stderr, "Invalid or empty response type: %T\n", raw) os.Exit(1) } + n.printCtrlResponse(res) - fmt.Printf("Using FaceId=%d (%d)\n", *res.Val.Params.FaceId, res.Val.StatusCode) return key, fmt.Sprintf("%d", *res.Val.Params.FaceId) } } @@ -222,3 +194,29 @@ func (n *Nfdc) convCmdArg(ctrlArgs *mgmt.ControlArgs, key string, val string) { os.Exit(9) } } + +func (n *Nfdc) printCtrlResponse(res *mgmt.ControlResponse) { + // print status code and text + fmt.Printf("Status=%d (%s)\n", res.Val.StatusCode, res.Val.StatusText) + + // iterate over parameters in sorted order + params := res.Val.Params.ToDict() + keys := make([]string, 0, len(params)) + for key := range params { + keys = append(keys, key) + } + sort.Strings(keys) + + // print parameters + for _, key := range keys { + val := params[key] + + // convert some values to human-readable form + switch key { + case "FacePersistency": + val = mgmt.Persistency(val.(uint64)).String() + } + + fmt.Printf(" %s=%v\n", key, val) + } +} From a8742b9ecd0cba6658e91b4f3df1ab319354a239 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 17:40:27 +0000 Subject: [PATCH 16/36] fw: fix strategy log --- fw/mgmt/strategy-choice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fw/mgmt/strategy-choice.go b/fw/mgmt/strategy-choice.go index 96d90274..991c800e 100644 --- a/fw/mgmt/strategy-choice.go +++ b/fw/mgmt/strategy-choice.go @@ -156,7 +156,7 @@ func (s *StrategyChoiceModule) set(interest *spec.Interest, pitToken []byte, inF } table.FibStrategyTable.SetStrategyEnc(params.Name, params.Strategy.Name) - core.LogInfo(s, "Set strategy for Name=", params.Name, " to Strategy=", params.Strategy) + core.LogInfo(s, "Set strategy for Name=", params.Name, " to Strategy=", params.Strategy.Name) responseParams := map[string]any{} responseParams["Name"] = params.Name responseParams["Strategy"] = params.Strategy From 096eab8d12e6b386f42ca67927cea5c1ab631ade Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 18:10:18 +0000 Subject: [PATCH 17/36] cmd: add dv link commands --- cmd/ndnd/main.go | 31 +++--- dv/config/config.go | 18 +++- dv/dv/router.go | 11 ++- dv/dv/status.go | 34 +++++++ dv/tlv/tlv.go | 7 ++ dv/tlv/zz_generated.go | 219 +++++++++++++++++++++++++++++++++++++++++ tools/dvc/dvc.go | 94 ++++++++++++++++++ 7 files changed, 397 insertions(+), 17 deletions(-) create mode 100644 dv/dv/status.go create mode 100644 tools/dvc/dvc.go diff --git a/cmd/ndnd/main.go b/cmd/ndnd/main.go index 82222cdd..38a7b4e5 100644 --- a/cmd/ndnd/main.go +++ b/cmd/ndnd/main.go @@ -7,10 +7,14 @@ import ( fw "github.com/named-data/ndnd/fw/executor" "github.com/named-data/ndnd/std/utils" tools "github.com/named-data/ndnd/tools" + dvc "github.com/named-data/ndnd/tools/dvc" nfdc "github.com/named-data/ndnd/tools/nfdc" ) func main() { + // subtrees from other packages + nfdcTree := nfdc.GetNfdcCmdTree() + // create a command tree tree := utils.CmdTree{ Name: "ndnd", @@ -18,7 +22,12 @@ func main() { Sub: []*utils.CmdTree{{ Name: "fw", Help: "NDN Forwarding Daemon", - Sub: fwSubtree(), + Sub: append([]*utils.CmdTree{{ + Name: "run", + Help: "Start the NDN Forwarding Daemon", + Fun: fw.Main, + }, {}, + }, nfdcTree.Sub...), }, { Name: "dv", Help: "NDN Distance Vector Routing Daemon", @@ -26,6 +35,14 @@ func main() { Name: "run", Help: "Start the NDN Distance Vector Routing Daemon", Fun: dv.Main, + }, {}, { + Name: "link create", + Help: "Create a new active neighbor link", + Fun: dvc.RunDvLinkCreate(&nfdcTree), + }, { + Name: "link destroy", + Help: "Destroy an active neighbor link", + Fun: dvc.RunDvLinkDestroy(&nfdcTree), }}, }, { // tools separator @@ -53,15 +70,3 @@ func main() { args[0] = tree.Name tree.Execute(args) } - -func fwSubtree() []*utils.CmdTree { - tree := []*utils.CmdTree{{ - Name: "run", - Help: "Start the NDN Forwarding Daemon", - Fun: fw.Main, - }, { - // separator for nfdc - }} - tree = append(tree, nfdc.GetNfdcCmdTree().Sub...) - return tree -} diff --git a/dv/config/config.go b/dv/config/config.go index 5cd51d1e..74159864 100644 --- a/dv/config/config.go +++ b/dv/config/config.go @@ -41,7 +41,7 @@ type Config struct { // Prefix Table Data Prefix pfxDataPfxN enc.Name // NLSR readvertise prefix - readvertisePfxN enc.Name + localPfxN enc.Name } func DefaultConfig() *Config { @@ -103,7 +103,7 @@ func (c *Config) Parse() (err error) { enc.NewStringComponent(enc.TypeKeywordNameComponent, "DV"), enc.NewStringComponent(enc.TypeKeywordNameComponent, "PFX"), ) - c.readvertisePfxN = append(Localhost, + c.localPfxN = append(Localhost, enc.NewStringComponent(enc.TypeGenericNameComponent, "nlsr"), ) @@ -142,8 +142,20 @@ func (c *Config) PrefixTableDataPrefix() enc.Name { return c.pfxDataPfxN } +func (c *Config) LocalPrefix() enc.Name { + return c.localPfxN +} + func (c *Config) ReadvertisePrefix() enc.Name { - return c.readvertisePfxN + return append(c.localPfxN, + enc.NewStringComponent(enc.TypeGenericNameComponent, "rib"), + ) +} + +func (c *Config) StatusPrefix() enc.Name { + return append(c.localPfxN, + enc.NewStringComponent(enc.TypeGenericNameComponent, "status"), + ) } func (c *Config) AdvertisementSyncInterval() time.Duration { diff --git a/dv/dv/router.go b/dv/dv/router.go index c610bd95..fb357769 100644 --- a/dv/dv/router.go +++ b/dv/dv/router.go @@ -197,13 +197,22 @@ func (dv *Router) register() (err error) { return err } + // Router status + err = dv.engine.AttachHandler(dv.config.StatusPrefix(), + func(args ndn.InterestHandlerArgs) { + go dv.statusOnInterest(args) + }) + if err != nil { + return err + } + // Register routes to forwarder pfxs := []enc.Name{ dv.config.AdvertisementSyncPrefix(), dv.config.AdvertisementDataPrefix(), dv.config.PrefixTableSyncPrefix(), dv.config.PrefixTableDataPrefix(), - dv.config.ReadvertisePrefix(), + dv.config.LocalPrefix(), } for _, prefix := range pfxs { dv.nfdc.Exec(nfdc.NfdMgmtCmd{ diff --git a/dv/dv/status.go b/dv/dv/status.go new file mode 100644 index 00000000..75395b52 --- /dev/null +++ b/dv/dv/status.go @@ -0,0 +1,34 @@ +package dv + +import ( + "time" + + "github.com/named-data/ndnd/dv/tlv" + "github.com/named-data/ndnd/std/log" + "github.com/named-data/ndnd/std/ndn" + "github.com/named-data/ndnd/std/security" + "github.com/named-data/ndnd/std/utils" +) + +// Received advertisement Interest +func (dv *Router) statusOnInterest(args ndn.InterestHandlerArgs) { + status := tlv.Status{ + NetworkName: &tlv.Destination{Name: dv.config.NetworkName()}, + RouterName: &tlv.Destination{Name: dv.config.RouterName()}, + } + + name := args.Interest.Name() + cfg := &ndn.DataConfig{ + ContentType: utils.IdPtr(ndn.ContentTypeBlob), + Freshness: utils.IdPtr(time.Second), + } + signer := security.NewSha256Signer() + + data, err := dv.engine.Spec().MakeData(name, cfg, status.Encode(), signer) + if err != nil { + log.Warnf("readvertise: failed to make response Data: %+v", err) + return + } + + args.Reply(data.Wire) +} diff --git a/dv/tlv/tlv.go b/dv/tlv/tlv.go index 98b59c45..b7d27ef5 100644 --- a/dv/tlv/tlv.go +++ b/dv/tlv/tlv.go @@ -53,3 +53,10 @@ type PrefixOpRemove struct { //+field:name Name enc.Name `tlv:"0x07"` } + +type Status struct { + //+field:struct:Destination + NetworkName *Destination `tlv:"0x191"` + //+field:struct:Destination + RouterName *Destination `tlv:"0x193"` +} diff --git a/dv/tlv/zz_generated.go b/dv/tlv/zz_generated.go index 3d8034cb..dd182777 100644 --- a/dv/tlv/zz_generated.go +++ b/dv/tlv/zz_generated.go @@ -1816,3 +1816,222 @@ func ParsePrefixOpRemove(reader enc.ParseReader, ignoreCritical bool) (*PrefixOp context.Init() return context.Parse(reader, ignoreCritical) } + +type StatusEncoder struct { + length uint + + NetworkName_encoder DestinationEncoder + RouterName_encoder DestinationEncoder +} + +type StatusParsingContext struct { + NetworkName_context DestinationParsingContext + RouterName_context DestinationParsingContext +} + +func (encoder *StatusEncoder) Init(value *Status) { + if value.NetworkName != nil { + encoder.NetworkName_encoder.Init(value.NetworkName) + } + if value.RouterName != nil { + encoder.RouterName_encoder.Init(value.RouterName) + } + + l := uint(0) + if value.NetworkName != nil { + l += 3 + switch x := encoder.NetworkName_encoder.length; { + case x <= 0xfc: + l += 1 + case x <= 0xffff: + l += 3 + case x <= 0xffffffff: + l += 5 + default: + l += 9 + } + l += encoder.NetworkName_encoder.length + } + if value.RouterName != nil { + l += 3 + switch x := encoder.RouterName_encoder.length; { + case x <= 0xfc: + l += 1 + case x <= 0xffff: + l += 3 + case x <= 0xffffffff: + l += 5 + default: + l += 9 + } + l += encoder.RouterName_encoder.length + } + encoder.length = l + +} + +func (context *StatusParsingContext) Init() { + context.NetworkName_context.Init() + context.RouterName_context.Init() +} + +func (encoder *StatusEncoder) EncodeInto(value *Status, buf []byte) { + + pos := uint(0) + + if value.NetworkName != nil { + buf[pos] = 253 + binary.BigEndian.PutUint16(buf[pos+1:], uint16(401)) + pos += 3 + switch x := encoder.NetworkName_encoder.length; { + case x <= 0xfc: + buf[pos] = byte(x) + pos += 1 + case x <= 0xffff: + buf[pos] = 0xfd + binary.BigEndian.PutUint16(buf[pos+1:], uint16(x)) + pos += 3 + case x <= 0xffffffff: + buf[pos] = 0xfe + binary.BigEndian.PutUint32(buf[pos+1:], uint32(x)) + pos += 5 + default: + buf[pos] = 0xff + binary.BigEndian.PutUint64(buf[pos+1:], uint64(x)) + pos += 9 + } + if encoder.NetworkName_encoder.length > 0 { + encoder.NetworkName_encoder.EncodeInto(value.NetworkName, buf[pos:]) + pos += encoder.NetworkName_encoder.length + } + } + if value.RouterName != nil { + buf[pos] = 253 + binary.BigEndian.PutUint16(buf[pos+1:], uint16(403)) + pos += 3 + switch x := encoder.RouterName_encoder.length; { + case x <= 0xfc: + buf[pos] = byte(x) + pos += 1 + case x <= 0xffff: + buf[pos] = 0xfd + binary.BigEndian.PutUint16(buf[pos+1:], uint16(x)) + pos += 3 + case x <= 0xffffffff: + buf[pos] = 0xfe + binary.BigEndian.PutUint32(buf[pos+1:], uint32(x)) + pos += 5 + default: + buf[pos] = 0xff + binary.BigEndian.PutUint64(buf[pos+1:], uint64(x)) + pos += 9 + } + if encoder.RouterName_encoder.length > 0 { + encoder.RouterName_encoder.EncodeInto(value.RouterName, buf[pos:]) + pos += encoder.RouterName_encoder.length + } + } +} + +func (encoder *StatusEncoder) Encode(value *Status) enc.Wire { + + wire := make(enc.Wire, 1) + wire[0] = make([]byte, encoder.length) + buf := wire[0] + encoder.EncodeInto(value, buf) + + return wire +} + +func (context *StatusParsingContext) Parse(reader enc.ParseReader, ignoreCritical bool) (*Status, error) { + if reader == nil { + return nil, enc.ErrBufferOverflow + } + + var handled_NetworkName bool = false + var handled_RouterName bool = false + + progress := -1 + _ = progress + + value := &Status{} + var err error + var startPos int + for { + startPos = reader.Pos() + if startPos >= reader.Length() { + break + } + typ := enc.TLNum(0) + l := enc.TLNum(0) + typ, err = enc.ReadTLNum(reader) + if err != nil { + return nil, enc.ErrFailToParse{TypeNum: 0, Err: err} + } + l, err = enc.ReadTLNum(reader) + if err != nil { + return nil, enc.ErrFailToParse{TypeNum: 0, Err: err} + } + + err = nil + if handled := false; true { + switch typ { + case 401: + if true { + handled = true + handled_NetworkName = true + value.NetworkName, err = context.NetworkName_context.Parse(reader.Delegate(int(l)), ignoreCritical) + } + case 403: + if true { + handled = true + handled_RouterName = true + value.RouterName, err = context.RouterName_context.Parse(reader.Delegate(int(l)), ignoreCritical) + } + default: + if !ignoreCritical && ((typ <= 31) || ((typ & 1) == 1)) { + return nil, enc.ErrUnrecognizedField{TypeNum: typ} + } + handled = true + err = reader.Skip(int(l)) + } + if err == nil && !handled { + } + if err != nil { + return nil, enc.ErrFailToParse{TypeNum: typ, Err: err} + } + } + } + + startPos = reader.Pos() + err = nil + + if !handled_NetworkName && err == nil { + value.NetworkName = nil + } + if !handled_RouterName && err == nil { + value.RouterName = nil + } + + if err != nil { + return nil, err + } + + return value, nil +} + +func (value *Status) Encode() enc.Wire { + encoder := StatusEncoder{} + encoder.Init(value) + return encoder.Encode(value) +} + +func (value *Status) Bytes() []byte { + return value.Encode().Join() +} + +func ParseStatus(reader enc.ParseReader, ignoreCritical bool) (*Status, error) { + context := StatusParsingContext{} + context.Init() + return context.Parse(reader, ignoreCritical) +} diff --git a/tools/dvc/dvc.go b/tools/dvc/dvc.go new file mode 100644 index 00000000..427a268a --- /dev/null +++ b/tools/dvc/dvc.go @@ -0,0 +1,94 @@ +package dvc + +import ( + "fmt" + "os" + "time" + + dvtlv "github.com/named-data/ndnd/dv/tlv" + enc "github.com/named-data/ndnd/std/encoding" + "github.com/named-data/ndnd/std/engine" + "github.com/named-data/ndnd/std/ndn" + "github.com/named-data/ndnd/std/utils" +) + +func dvGetStatus() *dvtlv.Status { + face := engine.NewUnixFace("/var/run/nfd/nfd.sock") + app := engine.NewBasicEngine(face) + err := app.Start() + if err != nil { + panic(err) + } + defer app.Stop() + + name, _ := enc.NameFromStr("/localhost/nlsr/status") + cfg := &ndn.InterestConfig{ + MustBeFresh: true, + Lifetime: utils.IdPtr(time.Second), + Nonce: utils.ConvertNonce(app.Timer().Nonce()), + } + + interest, err := app.Spec().MakeInterest(name, cfg, nil, nil) + if err != nil { + panic(err) + } + + ch := make(chan ndn.ExpressCallbackArgs) + err = app.Express(interest, func(args ndn.ExpressCallbackArgs) { ch <- args }) + if err != nil { + panic(err) + } + args := <-ch + + if args.Result != ndn.InterestResultData { + fmt.Fprintf(os.Stderr, "Failed to get router state. Is DV running?\n") + os.Exit(1) + } + + status, err := dvtlv.ParseStatus(enc.NewWireReader(args.Data.Content()), false) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to parse router state: %v\n", err) + os.Exit(1) + } + + return status +} + +func RunDvLinkCreate(nfdcTree *utils.CmdTree) func([]string) { + return func(args []string) { + if len(args) != 2 { + fmt.Fprintf(os.Stderr, "Usage: %s \n", args[0]) + return + } + + status := dvGetStatus() // will panic if fail + + // /localhop//32=DV/32=ADS/32=ACT + name := enc.Name{enc.NewStringComponent(enc.TypeGenericNameComponent, "localhop")} + name = append(name, status.NetworkName.Name...) + name = append(name, + enc.NewStringComponent(enc.TypeKeywordNameComponent, "DV"), + enc.NewStringComponent(enc.TypeKeywordNameComponent, "ADS"), + enc.NewStringComponent(enc.TypeKeywordNameComponent, "ACT"), + ) + + nfdcTree.Execute([]string{ + "nfdc", "route", "add", + "persistency=permanent", + "face=" + args[1], + "prefix=" + name.String(), + }) + } +} + +func RunDvLinkDestroy(nfdcTree *utils.CmdTree) func([]string) { + return func(args []string) { + if len(args) != 2 { + fmt.Fprintf(os.Stderr, "Usage: %s \n", args[0]) + return + } + + // just destroy the face assuming we created it + nfdcTree.Execute([]string{"nfdc", "face", "destroy", "face=" + args[1]}) + } +} From 167d83793bfd12aabc8395ebad224cd0f3ebb839 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 18:30:45 +0000 Subject: [PATCH 18/36] engine: add NewDefaultFace --- std/engine/factory.go | 4 ++++ std/examples/low-level/consumer/main.go | 3 +-- std/examples/low-level/producer/main.go | 3 +-- std/examples/low-level/svs/main.go | 3 +-- std/examples/schema-test/chat/main.go | 3 +-- std/examples/schema-test/encryption/consumer/main.go | 3 +-- std/examples/schema-test/encryption/producer/main.go | 3 +-- std/examples/schema-test/group-sig/consumer/main.go | 3 +-- std/examples/schema-test/group-sig/producer/main.go | 3 +-- std/examples/schema-test/rdr/catchunks/main.go | 3 +-- std/examples/schema-test/rdr/putchunks/main.go | 3 +-- std/examples/schema-test/signed-by/consumer/main.go | 3 +-- std/examples/schema-test/signed-by/producer/main.go | 3 +-- .../schema-test/single-packet/consumer/main.go | 3 +-- .../schema-test/single-packet/producer/main.go | 3 +-- std/examples/schema-test/storage/consumer/main.go | 3 +-- std/examples/schema-test/storage/producer/main.go | 3 +-- std/examples/schema-test/sync/main.go | 3 +-- tools/catchunks.go | 9 ++++----- tools/dvc/dvc.go | 3 +-- tools/nfdc/nfdc.go | 3 +-- tools/pingclient.go | 3 +-- tools/pingserver.go | 3 +-- tools/putchunks.go | 11 +++++------ 24 files changed, 34 insertions(+), 53 deletions(-) diff --git a/std/engine/factory.go b/std/engine/factory.go index cf92969d..24061fa6 100644 --- a/std/engine/factory.go +++ b/std/engine/factory.go @@ -21,3 +21,7 @@ func NewBasicEngine(face face.Face) ndn.Engine { func NewUnixFace(addr string) face.Face { return face.NewStreamFace("unix", addr, true) } + +func NewDefaultFace() face.Face { + return NewUnixFace("/var/run/nfd/nfd.sock") +} diff --git a/std/examples/low-level/consumer/main.go b/std/examples/low-level/consumer/main.go index ebd77a03..d27fd115 100644 --- a/std/examples/low-level/consumer/main.go +++ b/std/examples/low-level/consumer/main.go @@ -15,8 +15,7 @@ func main() { log.SetLevel(log.InfoLevel) logger := log.WithField("module", "main") - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/low-level/producer/main.go b/std/examples/low-level/producer/main.go index 75c997ee..4611d6d3 100644 --- a/std/examples/low-level/producer/main.go +++ b/std/examples/low-level/producer/main.go @@ -56,8 +56,7 @@ func main() { log.SetLevel(log.InfoLevel) logger := log.WithField("module", "main") - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app = engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/low-level/svs/main.go b/std/examples/low-level/svs/main.go index b9c1f844..25ea9005 100644 --- a/std/examples/low-level/svs/main.go +++ b/std/examples/low-level/svs/main.go @@ -25,8 +25,7 @@ func main() { } // Create a new engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err = app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/chat/main.go b/std/examples/schema-test/chat/main.go index 08393b8d..c765cbe9 100644 --- a/std/examples/schema-test/chat/main.go +++ b/std/examples/schema-test/chat/main.go @@ -197,8 +197,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err = app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/encryption/consumer/main.go b/std/examples/schema-test/encryption/consumer/main.go index 05e6cec3..7a525016 100644 --- a/std/examples/schema-test/encryption/consumer/main.go +++ b/std/examples/schema-test/encryption/consumer/main.go @@ -84,8 +84,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err = app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/encryption/producer/main.go b/std/examples/schema-test/encryption/producer/main.go index 60f84442..5b6a54dd 100644 --- a/std/examples/schema-test/encryption/producer/main.go +++ b/std/examples/schema-test/encryption/producer/main.go @@ -76,8 +76,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/group-sig/consumer/main.go b/std/examples/schema-test/group-sig/consumer/main.go index e3c5bd85..d9733e9a 100644 --- a/std/examples/schema-test/group-sig/consumer/main.go +++ b/std/examples/schema-test/group-sig/consumer/main.go @@ -89,8 +89,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err = app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/group-sig/producer/main.go b/std/examples/schema-test/group-sig/producer/main.go index ba516996..2023e045 100644 --- a/std/examples/schema-test/group-sig/producer/main.go +++ b/std/examples/schema-test/group-sig/producer/main.go @@ -87,8 +87,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/rdr/catchunks/main.go b/std/examples/schema-test/rdr/catchunks/main.go index b8834a5f..addfc3a6 100644 --- a/std/examples/schema-test/rdr/catchunks/main.go +++ b/std/examples/schema-test/rdr/catchunks/main.go @@ -66,8 +66,7 @@ func main() { }) // Setup engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/rdr/putchunks/main.go b/std/examples/schema-test/rdr/putchunks/main.go index f4379fac..be3ed10e 100644 --- a/std/examples/schema-test/rdr/putchunks/main.go +++ b/std/examples/schema-test/rdr/putchunks/main.go @@ -75,8 +75,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/signed-by/consumer/main.go b/std/examples/schema-test/signed-by/consumer/main.go index 2f08069f..dcb0da73 100644 --- a/std/examples/schema-test/signed-by/consumer/main.go +++ b/std/examples/schema-test/signed-by/consumer/main.go @@ -139,8 +139,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err = app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/signed-by/producer/main.go b/std/examples/schema-test/signed-by/producer/main.go index 500309ca..ffb70ba9 100644 --- a/std/examples/schema-test/signed-by/producer/main.go +++ b/std/examples/schema-test/signed-by/producer/main.go @@ -153,8 +153,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err = app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/single-packet/consumer/main.go b/std/examples/schema-test/single-packet/consumer/main.go index e6adac18..8a0a1923 100644 --- a/std/examples/schema-test/single-packet/consumer/main.go +++ b/std/examples/schema-test/single-packet/consumer/main.go @@ -53,8 +53,7 @@ func main() { }) // Setup engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/single-packet/producer/main.go b/std/examples/schema-test/single-packet/producer/main.go index a43ae229..e78ebbb4 100644 --- a/std/examples/schema-test/single-packet/producer/main.go +++ b/std/examples/schema-test/single-packet/producer/main.go @@ -70,8 +70,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/storage/consumer/main.go b/std/examples/schema-test/storage/consumer/main.go index 32e1f63b..c4d4f695 100644 --- a/std/examples/schema-test/storage/consumer/main.go +++ b/std/examples/schema-test/storage/consumer/main.go @@ -66,8 +66,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err = app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/storage/producer/main.go b/std/examples/schema-test/storage/producer/main.go index a572ff20..e5a4a707 100644 --- a/std/examples/schema-test/storage/producer/main.go +++ b/std/examples/schema-test/storage/producer/main.go @@ -58,8 +58,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/std/examples/schema-test/sync/main.go b/std/examples/schema-test/sync/main.go index 7e6146f3..3105963b 100644 --- a/std/examples/schema-test/sync/main.go +++ b/std/examples/schema-test/sync/main.go @@ -84,8 +84,7 @@ func main() { }) // Start engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { logger.Fatalf("Unable to start engine: %+v", err) diff --git a/tools/catchunks.go b/tools/catchunks.go index 8a6b40b9..3eef19f4 100644 --- a/tools/catchunks.go +++ b/tools/catchunks.go @@ -41,17 +41,16 @@ func (cc *CatChunks) run() { } // start face and engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - engine := engine.NewBasicEngine(face) - err = engine.Start() + app := engine.NewBasicEngine(engine.NewDefaultFace()) + err = app.Start() if err != nil { log.Errorf("Unable to start engine: %+v", err) return } - defer engine.Stop() + defer app.Stop() // start object client - cli := object.NewClient(engine, object.NewMemoryStore()) + cli := object.NewClient(app, object.NewMemoryStore()) err = cli.Start() if err != nil { log.Errorf("Unable to start object client: %+v", err) diff --git a/tools/dvc/dvc.go b/tools/dvc/dvc.go index 427a268a..66b58805 100644 --- a/tools/dvc/dvc.go +++ b/tools/dvc/dvc.go @@ -13,8 +13,7 @@ import ( ) func dvGetStatus() *dvtlv.Status { - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - app := engine.NewBasicEngine(face) + app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { panic(err) diff --git a/tools/nfdc/nfdc.go b/tools/nfdc/nfdc.go index b50c2613..467a3eb5 100644 --- a/tools/nfdc/nfdc.go +++ b/tools/nfdc/nfdc.go @@ -92,8 +92,7 @@ type Nfdc struct { } func (n *Nfdc) Start() { - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - n.engine = engine.NewBasicEngine(face) + n.engine = engine.NewBasicEngine(engine.NewDefaultFace()) err := n.engine.Start() if err != nil { diff --git a/tools/pingclient.go b/tools/pingclient.go index ce823b65..e0d4ae9c 100644 --- a/tools/pingclient.go +++ b/tools/pingclient.go @@ -157,8 +157,7 @@ func (pc *PingClient) run() { } // start the engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - pc.app = engine.NewBasicEngine(face) + pc.app = engine.NewBasicEngine(engine.NewDefaultFace()) err = pc.app.Start() if err != nil { log.Fatalf("Unable to start engine: %+v", err) diff --git a/tools/pingserver.go b/tools/pingserver.go index 38637c2d..eeb678ac 100644 --- a/tools/pingserver.go +++ b/tools/pingserver.go @@ -51,8 +51,7 @@ func (ps *PingServer) run() { ps.name = append(prefix, enc.NewStringComponent(enc.TypeGenericNameComponent, "ping")) - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - ps.app = engine.NewBasicEngine(face) + ps.app = engine.NewBasicEngine(engine.NewDefaultFace()) err = ps.app.Start() if err != nil { log.Fatalf("Unable to start engine: %+v", err) diff --git a/tools/putchunks.go b/tools/putchunks.go index 74d4f188..858b8b5f 100644 --- a/tools/putchunks.go +++ b/tools/putchunks.go @@ -41,17 +41,16 @@ func (pc *PutChunks) run() { } // start face and engine - face := engine.NewUnixFace("/var/run/nfd/nfd.sock") - engine := engine.NewBasicEngine(face) - err = engine.Start() + app := engine.NewBasicEngine(engine.NewDefaultFace()) + err = app.Start() if err != nil { log.Errorf("Unable to start engine: %+v", err) return } - defer engine.Stop() + defer app.Stop() // start object client - cli := object.NewClient(engine, object.NewMemoryStore()) + cli := object.NewClient(app, object.NewMemoryStore()) err = cli.Start() if err != nil { log.Errorf("Unable to start object client: %+v", err) @@ -86,7 +85,7 @@ func (pc *PutChunks) run() { log.Infof("Object produced: %s", vname) // register route to the object - err = engine.RegisterRoute(name) + err = app.RegisterRoute(name) if err != nil { log.Fatalf("Unable to register route: %+v", err) return From cf2410610994269985de3a8b231463d2076577cb Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 19:02:33 +0000 Subject: [PATCH 19/36] std: implement client configuration --- std/engine/client_conf.go | 57 +++++++++++++++++++++++++++++++++++++++ std/engine/factory.go | 26 +++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 std/engine/client_conf.go diff --git a/std/engine/client_conf.go b/std/engine/client_conf.go new file mode 100644 index 00000000..1a098b11 --- /dev/null +++ b/std/engine/client_conf.go @@ -0,0 +1,57 @@ +package engine + +import ( + "bufio" + "os" + "strings" +) + +type ClientConfig struct { + TransportUri string +} + +func GetClientConfig() ClientConfig { + // Default configuration + config := ClientConfig{ + TransportUri: "unix:///var/run/nfd/nfd.sock", + } + + // Order of increasing priority + configDirs := []string{ + "/etc/ndn", + "/usr/local/etc/ndn", + os.Getenv("HOME") + "/.ndn", + } + + // Read each config file that we can find + for _, dir := range configDirs { + filename := dir + "/client.conf" + + file, err := os.OpenFile(filename, os.O_RDONLY, 0) + if err != nil { + continue + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(line, ";") { // comment + continue + } + + transport := strings.TrimPrefix(line, "transport=") + if transport != line { + config.TransportUri = transport + } + } + } + + // Environment variable overrides config file + transportEnv := os.Getenv("NDN_CLIENT_TRANSPORT") + if transportEnv != "" { + config.TransportUri = transportEnv + } + + return config +} diff --git a/std/engine/factory.go b/std/engine/factory.go index 24061fa6..e2dfc594 100644 --- a/std/engine/factory.go +++ b/std/engine/factory.go @@ -1,6 +1,10 @@ package engine import ( + "fmt" + "net/url" + "os" + enc "github.com/named-data/ndnd/std/encoding" "github.com/named-data/ndnd/std/engine/basic" "github.com/named-data/ndnd/std/engine/face" @@ -23,5 +27,25 @@ func NewUnixFace(addr string) face.Face { } func NewDefaultFace() face.Face { - return NewUnixFace("/var/run/nfd/nfd.sock") + config := GetClientConfig() + + // Parse transport URI + uri, err := url.Parse(config.TransportUri) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to parse transport URI %s: %v (invalid client config)\n", uri, err) + os.Exit(1) + } + + if uri.Scheme == "unix" { + return NewUnixFace(uri.Path) + } + + if uri.Scheme == "tcp" || uri.Scheme == "tcp4" || uri.Scheme == "tcp6" { + return face.NewStreamFace(uri.Scheme, uri.Host, false) + } + + fmt.Fprintf(os.Stderr, "Unsupported transport URI: %s (invalid client config)\n", uri) + os.Exit(1) + + return nil } From f1bf133c7d0f510758e697fbf57ed2d12f5a3cf4 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 19:02:40 +0000 Subject: [PATCH 20/36] dvc: fix error message --- tools/dvc/dvc.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/dvc/dvc.go b/tools/dvc/dvc.go index 66b58805..c2a140a6 100644 --- a/tools/dvc/dvc.go +++ b/tools/dvc/dvc.go @@ -16,7 +16,8 @@ func dvGetStatus() *dvtlv.Status { app := engine.NewBasicEngine(engine.NewDefaultFace()) err := app.Start() if err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "Failed to start engine: %v\n", err) + os.Exit(1) } defer app.Stop() From 5e7196a3090b313a890abb5c05bbc3b8afcefbc5 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 19:12:36 +0000 Subject: [PATCH 21/36] dv: simplify config --- dv/dv.sample.yml | 6 +----- dv/executor/executor.go | 17 +++++------------ tools/nfdc/nfdc_cmd.go | 2 +- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/dv/dv.sample.yml b/dv/dv.sample.yml index 0a8fceda..9d60b112 100644 --- a/dv/dv.sample.yml +++ b/dv/dv.sample.yml @@ -1,8 +1,4 @@ -nfd: - # [optional] Unix socket used to connect to the local forwarder - unix: /var/run/nfd/nfd.sock - -config: +dv: # [required] Global prefix for all DV routers in the network network: /ndn # [required] Unique name for each router in the network diff --git a/dv/executor/executor.go b/dv/executor/executor.go index fe1748aa..c93acc03 100644 --- a/dv/executor/executor.go +++ b/dv/executor/executor.go @@ -10,20 +10,13 @@ import ( ) type DvConfig struct { - // NFD related options - Nfd struct { - Unix string `json:"unix"` - } `json:"nfd"` - - // Underlying configuration - Config *config.Config `json:"config"` + Config *config.Config `json:"dv"` } func DefaultConfig() DvConfig { - dc := DvConfig{} - dc.Nfd.Unix = "/var/run/nfd/nfd.sock" - dc.Config = config.DefaultConfig() - return dc + return DvConfig{ + Config: config.DefaultConfig(), + } } func (dc DvConfig) Parse() error { @@ -45,7 +38,7 @@ func NewDvExecutor(dc DvConfig) (*DvExecutor, error) { } // Start NDN engine - dve.engine = engine.NewBasicEngine(engine.NewUnixFace(dc.Nfd.Unix)) + dve.engine = engine.NewBasicEngine(engine.NewDefaultFace()) // Create the DV router dve.router, err = dv.NewRouter(dc.Config, dve.engine) diff --git a/tools/nfdc/nfdc_cmd.go b/tools/nfdc/nfdc_cmd.go index 2b36f7dd..44d6ebed 100644 --- a/tools/nfdc/nfdc_cmd.go +++ b/tools/nfdc/nfdc_cmd.go @@ -118,7 +118,7 @@ func (n *Nfdc) preprocessArg( } res, ok := raw.(*mgmt.ControlResponse) - if !ok || res.Val == nil || res.Val.Params == nil { + if !ok || res.Val == nil || res.Val.Params == nil || res.Val.Params.FaceId == nil { fmt.Fprintf(os.Stderr, "Invalid or empty response type: %T\n", raw) os.Exit(1) } From 921bf6e7b5a84927a4d88d85f7cec842020b2dbf Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 19:15:50 +0000 Subject: [PATCH 22/36] nfdc: fix face create error --- tools/nfdc/nfdc_cmd.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/nfdc/nfdc_cmd.go b/tools/nfdc/nfdc_cmd.go index 44d6ebed..0c350c42 100644 --- a/tools/nfdc/nfdc_cmd.go +++ b/tools/nfdc/nfdc_cmd.go @@ -118,11 +118,15 @@ func (n *Nfdc) preprocessArg( } res, ok := raw.(*mgmt.ControlResponse) - if !ok || res.Val == nil || res.Val.Params == nil || res.Val.Params.FaceId == nil { + if !ok { fmt.Fprintf(os.Stderr, "Invalid or empty response type: %T\n", raw) os.Exit(1) } n.printCtrlResponse(res) + if res.Val == nil || res.Val.Params == nil || res.Val.Params.FaceId == nil { + fmt.Fprintf(os.Stderr, "Failed to create face for route\n") + os.Exit(1) + } return key, fmt.Sprintf("%d", *res.Val.Params.FaceId) } From 95fc6bba54a81dd0abd2b787117cafdbcbdd20d8 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 20:18:56 +0000 Subject: [PATCH 23/36] fw: reimplement URI parsing --- fw/defn/uri.go | 185 ++++++++++++++------------------------------ fw/defn/uri_test.go | 116 +++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 126 deletions(-) create mode 100644 fw/defn/uri_test.go diff --git a/fw/defn/uri.go b/fw/defn/uri.go index 53d66da6..748b63d7 100644 --- a/fw/defn/uri.go +++ b/fw/defn/uri.go @@ -22,12 +22,8 @@ import ( // URIType represents the type of the URI. type URIType int -const devPattern = `^(?Pdev)://(?P[A-Za-z0-9\-]+)$` -const fdPattern = `^(?Pfd)://(?P[0-9]+)$` -const ipv4Pattern = `^((25[0-4]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]|[0-9])\.){3}(25[0-4]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]|[0-9])$` -const udpPattern = `^(?Pudp[46]?)://\[?(?P[0-9A-Za-z\:\.\-]+)(%(?P[A-Za-z0-9\-]+))?\]?:(?P[0-9]+)$` -const tcpPattern = `^(?Ptcp[46]?)://\[?(?P[0-9A-Za-z\:\.\-]+)(%(?P[A-Za-z0-9\-]+))?\]?:(?P[0-9]+)$` -const unixPattern = `^(?Punix)://(?P[/\\A-Za-z0-9\:\.\-_]+)$` +// Regex to extract zone from URI +var zoneRegex, _ = regexp.Compile(`:\/\/\[?(?:[0-9A-Za-z\:\.\-]+)(?:%(?P[A-Za-z0-9\-]+))?\]?`) const ( unknownURI URIType = iota @@ -153,141 +149,77 @@ func MakeWebSocketClientFaceURI(addr net.Addr) *URI { } } -// DecodeURIString decodes a URI from a string. func DecodeURIString(str string) *URI { - u := new(URI) - u.uriType = unknownURI - u.scheme = "unknown" - schemeSplit := strings.SplitN(str, ":", 2) - if len(schemeSplit) < 2 { - // No scheme - return u + ret := &URI{ + uriType: unknownURI, + scheme: "unknown", } - switch { - case strings.EqualFold("dev", schemeSplit[0]): - u.uriType = devURI - u.scheme = "dev" - - regex, err := regexp.Compile(devPattern) - if err != nil { - return u - } - - matches := regex.FindStringSubmatch(str) - if regex.SubexpIndex("ifname") < 0 || len(matches) <= regex.SubexpIndex("ifname") { - return u - } - - ifname := matches[regex.SubexpIndex("ifname")] - // Pure function is not allowed to have side effect - // _, err = net.InterfaceByName(ifname) - // if err != nil { - // return u - // } - u.path = ifname - case strings.EqualFold("fd", schemeSplit[0]): - u.uriType = fdURI - u.scheme = "fd" - - regex, err := regexp.Compile(fdPattern) - if err != nil { - return u - } + // extract zone if present first, since this is non-standard + var zone string = "" + zoneMatch := zoneRegex.FindStringSubmatch(str) + if len(zoneMatch) > zoneRegex.SubexpIndex("zone") { + zone = zoneMatch[zoneRegex.SubexpIndex("zone")] + str = strings.Replace(str, "%"+zone, "", 1) + } - matches := regex.FindStringSubmatch(str) - // fmt.Println(matches, len(matches), regex.SubexpIndex("fd")) - if regex.SubexpIndex("fd") < 0 || len(matches) <= regex.SubexpIndex("fd") { - return u - } - u.path = matches[regex.SubexpIndex("fd")] - case strings.EqualFold("internal", schemeSplit[0]): - u.uriType = internalURI - u.scheme = "internal" - case strings.EqualFold("null", schemeSplit[0]): - u.uriType = nullURI - u.scheme = "null" - case strings.EqualFold("udp", schemeSplit[0]), - strings.EqualFold("udp4", schemeSplit[0]), - strings.EqualFold("udp6", schemeSplit[0]): - u.uriType = udpURI - u.scheme = "udp" - - regex, err := regexp.Compile(udpPattern) - if err != nil { - return u - } + // parse common URI schemes + uri, err := url.Parse(str) + if err != nil { + return ret + } - matches := regex.FindStringSubmatch(str) - if regex.SubexpIndex("host") < 0 || len(matches) <= regex.SubexpIndex("host") || regex.SubexpIndex("port") < 0 || len(matches) <= regex.SubexpIndex("port") { - return u - } - u.path = matches[regex.SubexpIndex("host")] - if regex.SubexpIndex("zone") < 0 || len(matches) >= regex.SubexpIndex("zone") && matches[regex.SubexpIndex("zone")] != "" { - u.path += "%" + matches[regex.SubexpIndex("zone")] - } - port, err := strconv.ParseUint(matches[regex.SubexpIndex("port")], 10, 16) - if err != nil || port <= 0 || port > 65535 { - return u - } - u.port = uint16(port) - case strings.EqualFold("tcp", schemeSplit[0]), - strings.EqualFold("tcp4", schemeSplit[0]), - strings.EqualFold("tcp6", schemeSplit[0]): - u.uriType = tcpURI - u.scheme = "tcp" - - regex, err := regexp.Compile(tcpPattern) - if err != nil { - return u + switch uri.Scheme { + case "dev": + ret.uriType = devURI + ret.scheme = uri.Scheme + ret.path = uri.Host + case "fd": + ret.uriType = fdURI + ret.scheme = uri.Scheme + ret.path = uri.Host + case "internal": + ret.uriType = internalURI + ret.scheme = uri.Scheme + case "null": + ret.uriType = nullURI + ret.scheme = uri.Scheme + case "udp", "udp4", "udp6", "tcp", "tcp4", "tcp6": + if strings.HasPrefix(uri.Scheme, "udp") { + ret.uriType = udpURI + } else { + ret.uriType = tcpURI } - matches := regex.FindStringSubmatch(str) - if regex.SubexpIndex("host") < 0 || len(matches) <= regex.SubexpIndex("host") || regex.SubexpIndex("port") < 0 || len(matches) <= regex.SubexpIndex("port") { - return u - } - u.path = matches[regex.SubexpIndex("host")] - if regex.SubexpIndex("zone") < 0 || len(matches) >= regex.SubexpIndex("zone") && matches[regex.SubexpIndex("zone")] != "" { - u.path += "%" + matches[regex.SubexpIndex("zone")] - } - port, err := strconv.ParseUint(matches[regex.SubexpIndex("port")], 10, 16) - if err != nil || port <= 0 || port > 65535 { - return u - } - u.port = uint16(port) - case strings.EqualFold("unix", schemeSplit[0]): - u.uriType = unixURI - u.scheme = "unix" + ret.scheme = uri.Scheme + ret.path = uri.Hostname() + port, _ := strconv.ParseUint(uri.Port(), 10, 16) + ret.port = uint16(port) - regex, err := regexp.Compile(unixPattern) - if err != nil { - return u + if zone != "" { + ret.path += "%" + zone } - matches := regex.FindStringSubmatch(str) - if len(matches) != 3 { - return u - } - u.path = matches[2] - case strings.EqualFold("ws", schemeSplit[0]), - strings.EqualFold("wss", schemeSplit[0]): - uri, e := url.Parse(str) - if e != nil || uri.User != nil || strings.TrimLeft(uri.Path, "/") != "" || - uri.RawQuery != "" || uri.Fragment != "" { + case "unix": + ret.uriType = unixURI + ret.scheme = uri.Scheme + ret.path = uri.Path + case "ws", "wss": + if uri.User != nil || strings.TrimLeft(uri.Path, "/") != "" || uri.RawQuery != "" || uri.Fragment != "" { return nil } return MakeWebSocketServerFaceURI(uri) - case strings.EqualFold("wsclient", schemeSplit[0]): - addr, e := net.ResolveTCPAddr("tcp", strings.Trim(schemeSplit[1], "/")) - if e != nil { + case "wsclient": + addr, err := net.ResolveTCPAddr("tcp", uri.Host) + if err != nil { return nil } return MakeWebSocketClientFaceURI(addr) } - // Canonize, if possible - u.Canonize() - return u + ret.Canonize() + + return ret } // URIType returns the type of the face URI. @@ -346,14 +278,15 @@ func (u *URI) IsCanonical() bool { ip := net.ParseIP(u.PathHost()) // Port number is implicitly limited to <= 65535 by type uint16 // We have to test whether To16() && not IPv4 because the Go net library considers IPv4 addresses to be valid IPv6 addresses - isIPv4, _ := regexp.MatchString(ipv4Pattern, u.PathHost()) - return ip != nil && ((u.scheme == "udp4" && ip.To4() != nil) || (u.scheme == "udp6" && ip.To16() != nil && !isIPv4)) && u.port > 0 + isIPv4 := ip.To4() != nil + return ip != nil && ((u.scheme == "udp4" && isIPv4) || + (u.scheme == "udp6" && ip.To16() != nil && !isIPv4)) && u.port > 0 case tcpURI: // Split off zone, if any ip := net.ParseIP(u.PathHost()) // Port number is implicitly limited to <= 65535 by type uint16 // We have to test whether To16() && not IPv4 because the Go net library considers IPv4 addresses to be valid IPv6 addresses - isIPv4, _ := regexp.MatchString(ipv4Pattern, u.PathHost()) + isIPv4 := ip.To4() != nil return ip != nil && u.port > 0 && ((u.scheme == "tcp4" && ip.To4() != nil) || (u.scheme == "tcp6" && ip.To16() != nil && !isIPv4)) case unixURI: diff --git a/fw/defn/uri_test.go b/fw/defn/uri_test.go new file mode 100644 index 00000000..283f0f2a --- /dev/null +++ b/fw/defn/uri_test.go @@ -0,0 +1,116 @@ +package defn_test + +import ( + "strings" + "testing" + + "github.com/named-data/ndnd/fw/defn" + "github.com/stretchr/testify/assert" +) + +func TestDecodeUri(t *testing.T) { + var uri *defn.URI + + // Unknown URI + uri = defn.DecodeURIString("test://myhost:1234") + assert.False(t, uri.IsCanonical()) + assert.Equal(t, "unknown", uri.Scheme()) + + // Device URI + uri = defn.DecodeURIString("dev://eth0") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "dev", uri.Scheme()) + assert.Equal(t, "eth0", uri.PathHost()) + + // FD URI + uri = defn.DecodeURIString("fd://3") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "fd", uri.Scheme()) + assert.Equal(t, "3", uri.PathHost()) + + // Internal URI + uri = defn.DecodeURIString("internal://") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "internal", uri.Scheme()) + + // NULL URI + uri = defn.DecodeURIString("null://") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "null", uri.Scheme()) + + // Unix URI + uri = defn.DecodeURIString("unix:///tmp/test.sock") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "unix", uri.Scheme()) + assert.Equal(t, "/tmp/test.sock", uri.PathHost()) + assert.Equal(t, uint16(0), uri.Port()) + + // UDP URI + uri = defn.DecodeURIString("udp://127.0.0.1:5000") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "udp4", uri.Scheme()) + assert.Equal(t, "127.0.0.1", uri.PathHost()) + assert.Equal(t, uint16(5000), uri.Port()) + + uri = defn.DecodeURIString("udp://[2001:db8::1]:5000") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "udp6", uri.Scheme()) + assert.Equal(t, "2001:db8::1", uri.PathHost()) + assert.Equal(t, uint16(5000), uri.Port()) + + // TCP URI + uri = defn.DecodeURIString("tcp://127.0.0.1:4600") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "tcp4", uri.Scheme()) + assert.Equal(t, "127.0.0.1", uri.PathHost()) + assert.Equal(t, uint16(4600), uri.Port()) + + uri = defn.DecodeURIString("tcp://[2002:db8::1]:4600") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "tcp6", uri.Scheme()) + assert.Equal(t, "2002:db8::1", uri.PathHost()) + assert.Equal(t, uint16(4600), uri.Port()) + + // Ws URI (server) + uri = defn.DecodeURIString("ws://0.0.0.0:5000") + assert.False(t, uri.IsCanonical()) + assert.Equal(t, "ws", uri.Scheme()) + assert.Equal(t, uint16(5000), uri.Port()) + + // Ws URI (client) + uri = defn.DecodeURIString("wsclient://127.0.0.1:800") + assert.False(t, uri.IsCanonical()) + assert.Equal(t, "wsclient", uri.Scheme()) + assert.Equal(t, "127.0.0.1", uri.PathHost()) + assert.Equal(t, uint16(800), uri.Port()) + + // UDP4 with zone + uri = defn.DecodeURIString("udp://127.0.0.1%eth0:3000") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "udp4", uri.Scheme()) + assert.Equal(t, "127.0.0.1", uri.PathHost()) + assert.Equal(t, "127.0.0.1%eth0", uri.Path()) + assert.Equal(t, "eth0", uri.PathZone()) + assert.Equal(t, uint16(3000), uri.Port()) + + // UDP6 with zone + uri = defn.DecodeURIString("udp://[2001:db8::1%eth0]:3000") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "udp6", uri.Scheme()) + assert.Equal(t, "2001:db8::1", uri.PathHost()) + assert.Equal(t, "2001:db8::1%eth0", uri.Path()) + assert.Equal(t, "eth0", uri.PathZone()) + assert.Equal(t, uint16(3000), uri.Port()) + + // Test name resolution for IPv4 and IPv6 + uri = defn.DecodeURIString("udp://ipv4.google.com:5000") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "udp4", uri.Scheme()) + assert.Equal(t, 3, strings.Count(uri.PathHost(), ".")) + assert.Equal(t, uint16(5000), uri.Port()) + + uri = defn.DecodeURIString("udp://ipv6.google.com:5000") + assert.True(t, uri.IsCanonical()) + assert.Equal(t, "udp6", uri.Scheme()) + assert.Equal(t, uint16(5000), uri.Port()) +} From 284541e21d9d71c5c5fa7607f3aa9f2f24472de7 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 20:19:07 +0000 Subject: [PATCH 24/36] fw: fix log spacing --- fw/mgmt/face.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fw/mgmt/face.go b/fw/mgmt/face.go index 3ddfa79d..e218abf6 100644 --- a/fw/mgmt/face.go +++ b/fw/mgmt/face.go @@ -167,7 +167,7 @@ func (f *FaceModule) create(interest *spec.Interest, pitToken []byte, inFace uin // Create new UDP face transport, err := face.MakeUnicastUDPTransport(URI, nil, persistency) if err != nil { - core.LogWarn(f, "Unable to create unicast UDP face with URI ", URI, ":", err.Error()) + core.LogWarn(f, "Unable to create unicast UDP face with URI ", URI, ": ", err.Error()) response = makeControlResponse(406, "Transport error", nil) f.manager.sendResponse(response, interest, pitToken, inFace) return From 559c50d5269aca299d980aef27705cbffa370d52 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 20:22:04 +0000 Subject: [PATCH 25/36] fw: default port to 6363 --- fw/defn/uri.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fw/defn/uri.go b/fw/defn/uri.go index 748b63d7..825b1d69 100644 --- a/fw/defn/uri.go +++ b/fw/defn/uri.go @@ -193,8 +193,12 @@ func DecodeURIString(str string) *URI { ret.scheme = uri.Scheme ret.path = uri.Hostname() - port, _ := strconv.ParseUint(uri.Port(), 10, 16) - ret.port = uint16(port) + if uri.Port() != "" { + port, _ := strconv.ParseUint(uri.Port(), 10, 16) + ret.port = uint16(port) + } else { + ret.port = uint16(6363) // default NDN port + } if zone != "" { ret.path += "%" + zone From 9779c625b658882925e31e70bcda1e70c599b132 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 20:48:24 +0000 Subject: [PATCH 26/36] fw: various fixes --- fw/face/unicast-tcp-transport.go | 2 +- fw/mgmt/face.go | 4 ++-- tools/nfdc/nfdc_cmd.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fw/face/unicast-tcp-transport.go b/fw/face/unicast-tcp-transport.go index 17fea07c..471a0c0a 100644 --- a/fw/face/unicast-tcp-transport.go +++ b/fw/face/unicast-tcp-transport.go @@ -54,7 +54,7 @@ func MakeUnicastTCPTransport( t := new(UnicastTCPTransport) t.makeTransportBase(remoteURI, localURI, persistency, defn.NonLocal, defn.PointToPoint, defn.MaxNDNPacketSize) t.expirationTime = utils.IdPtr(time.Now().Add(tcpLifetime)) - t.rechan = make(chan bool, 1) + t.rechan = make(chan bool, 2) // Set scope ip := net.ParseIP(remoteURI.Path()) diff --git a/fw/mgmt/face.go b/fw/mgmt/face.go index e218abf6..f0503bcd 100644 --- a/fw/mgmt/face.go +++ b/fw/mgmt/face.go @@ -503,8 +503,8 @@ func (f *FaceModule) destroy(interest *spec.Interest, pitToken []byte, inFace ui return } - if face.FaceTable.Get(*params.FaceId) != nil { - face.FaceTable.Remove(*params.FaceId) + if link := face.FaceTable.Get(*params.FaceId); link != nil { + link.Close() core.LogInfo(f, "Destroyed face with FaceID=", *params.FaceId) } else { core.LogInfo(f, "Ignoring attempt to delete non-existent face with FaceID=", *params.FaceId) diff --git a/tools/nfdc/nfdc_cmd.go b/tools/nfdc/nfdc_cmd.go index 0c350c42..e0fa7c09 100644 --- a/tools/nfdc/nfdc_cmd.go +++ b/tools/nfdc/nfdc_cmd.go @@ -37,7 +37,7 @@ func (n *Nfdc) ExecCmd(mod string, cmd string, args []string, defaults []string) // parse response res, ok := raw.(*mgmt.ControlResponse) - if !ok || res.Val == nil || res.Val.Params == nil { + if !ok || res == nil || res.Val == nil || res.Val.Params == nil { fmt.Fprintf(os.Stderr, "Invalid or empty response type: %T\n", raw) return } From 5429e7b99fafe350d3d690ade4b4ab834e78d04a Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 21:01:29 +0000 Subject: [PATCH 27/36] dv: do not reset time when ping is not active --- dv/table/neighbor_table.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dv/table/neighbor_table.go b/dv/table/neighbor_table.go index 6706cd2c..96cecb76 100644 --- a/dv/table/neighbor_table.go +++ b/dv/table/neighbor_table.go @@ -96,16 +96,18 @@ func (ns *NeighborState) IsDead() bool { // and update the last seen time for the neighbor. // Return => true if the face ID has changed func (ns *NeighborState) RecvPing(faceId uint64, active bool) (error, bool) { + if ns.isFaceActive && !active { + // This ping is passive, but we already have an active ping. + return nil, false // ignore this ping. + } + // Update last seen time for neighbor + // Note that we skip this when the face is active and the ping is passive. + // This is because we want to detect if the active face is removed. ns.lastSeen = time.Now() // If face ID has changed, re-register face. if ns.faceId != faceId { - if ns.isFaceActive && !active { - // This ping is passive, but we already have an active ping. - return nil, false // ignore this ping. - } - ns.isFaceActive = active log.Infof("neighbor: %s face ID changed from %d to %d", ns.Name, ns.faceId, faceId) ns.routeUnregister() From 2f88002f6257c5d3a7560abedebe99f8e384f5ee Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 21:29:21 +0000 Subject: [PATCH 28/36] fw: refactor transport uri parsing --- fw/defn/uri.go | 4 ---- fw/face/udp-listener.go | 23 +++++++++-------------- fw/face/unicast-tcp-transport.go | 18 +----------------- fw/face/unicast-udp-transport.go | 10 +++++++--- 4 files changed, 17 insertions(+), 38 deletions(-) diff --git a/fw/defn/uri.go b/fw/defn/uri.go index 825b1d69..3fb472d9 100644 --- a/fw/defn/uri.go +++ b/fw/defn/uri.go @@ -91,10 +91,6 @@ func MakeNullFaceURI() *URI { // MakeUDPFaceURI constructs a URI for a UDP face. func MakeUDPFaceURI(ipVersion int, host string, port uint16) *URI { - /*path := host - if zone != "" { - path += "%" + zone - }*/ uri := new(URI) uri.uriType = udpURI uri.scheme = "udp" + strconv.Itoa(ipVersion) diff --git a/fw/face/udp-listener.go b/fw/face/udp-listener.go index 32f5c6cd..8b52422c 100644 --- a/fw/face/udp-listener.go +++ b/fw/face/udp-listener.go @@ -12,7 +12,6 @@ import ( "errors" "fmt" "net" - "strconv" "github.com/named-data/ndnd/fw/core" defn "github.com/named-data/ndnd/fw/defn" @@ -80,21 +79,17 @@ func (l *UDPListener) Run() { } // Construct remote URI - var remoteURI *defn.URI - host, port, err := net.SplitHostPort(remoteAddr.String()) - if err != nil { - core.LogWarn(l, "Unable to create face from ", remoteAddr, ": could not split host from port") - continue - } - portInt, err := strconv.ParseUint(port, 10, 16) - if err != nil { - core.LogWarn(l, "Unable to create face from ", remoteAddr, ": could not split host from port") + remoteURI := defn.DecodeURIString(fmt.Sprintf("udp://%s", remoteAddr)) + if remoteURI == nil || !remoteURI.IsCanonical() { + core.LogWarn(l, "Unable to create face from ", remoteURI, ": remote URI is not canonical") continue } - remoteURI = defn.MakeUDPFaceURI(4, host, uint16(portInt)) - remoteURI.Canonize() - if !remoteURI.IsCanonical() { - core.LogWarn(l, "Unable to create face from ", remoteURI, ": remote URI is not canonical") + + // Check if frame received here is for an existing face. + // This is probably because it was received too fast. + // For now just drop the frame, ideally we should pass it to face. + if face := FaceTable.GetByURI(remoteURI); face != nil { + core.LogTrace(l, "Received frame for existing face ", face) continue } diff --git a/fw/face/unicast-tcp-transport.go b/fw/face/unicast-tcp-transport.go index 471a0c0a..2f1f205c 100644 --- a/fw/face/unicast-tcp-transport.go +++ b/fw/face/unicast-tcp-transport.go @@ -91,24 +91,8 @@ func AcceptUnicastTCPTransport( persistency spec_mgmt.Persistency, ) (*UnicastTCPTransport, error) { // Construct remote URI - var remoteURI *defn.URI remoteAddr := remoteConn.RemoteAddr() - host, port, err := net.SplitHostPort(remoteAddr.String()) - if err != nil { - core.LogWarn("UnicastTCPTransport", "Unable to create face from ", remoteAddr, ": could not split host from port") - return nil, err - } - portInt, err := strconv.ParseUint(port, 10, 16) - if err != nil { - core.LogWarn("UnicastTCPTransport", "Unable to create face from ", remoteAddr, ": could not split host from port") - return nil, err - } - remoteURI = defn.MakeTCPFaceURI(4, host, uint16(portInt)) - remoteURI.Canonize() - if !remoteURI.IsCanonical() { - core.LogWarn("UnicastTCPTransport", "Unable to create face from ", remoteURI, ": remote URI is not canonical") - return nil, err - } + remoteURI := defn.DecodeURIString(fmt.Sprintf("tcp://%s", remoteAddr)) // Construct transport t := new(UnicastTCPTransport) diff --git a/fw/face/unicast-udp-transport.go b/fw/face/unicast-udp-transport.go index 3e77b30a..e48165cc 100644 --- a/fw/face/unicast-udp-transport.go +++ b/fw/face/unicast-udp-transport.go @@ -36,9 +36,13 @@ func MakeUnicastUDPTransport( localURI *defn.URI, persistency spec_mgmt.Persistency, ) (*UnicastUDPTransport, error) { - // Validate URIs - if !remoteURI.IsCanonical() || (remoteURI.Scheme() != "udp4" && remoteURI.Scheme() != "udp6") || - (localURI != nil && !localURI.IsCanonical()) || (localURI != nil && remoteURI.Scheme() != localURI.Scheme()) { + // Validate remote URI + if remoteURI == nil || !remoteURI.IsCanonical() || (remoteURI.Scheme() != "udp4" && remoteURI.Scheme() != "udp6") { + return nil, core.ErrNotCanonical + } + + // Validate local URI + if localURI != nil && (!localURI.IsCanonical() || remoteURI.Scheme() != localURI.Scheme()) { return nil, core.ErrNotCanonical } From 74e566411ca540721c2b61d36f0d2adb1cebd2bf Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 21:55:53 +0000 Subject: [PATCH 29/36] fw: refactor startup URI parsing --- fw/core/config.go | 6 ++++ fw/executor/yanfd.go | 67 +++++++++++++++++++++++++------------------- fw/yanfd.sample.yml | 4 +++ 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/fw/core/config.go b/fw/core/config.go index 138abcd7..5b7971a7 100644 --- a/fw/core/config.go +++ b/fw/core/config.go @@ -31,6 +31,10 @@ type Config struct { LockThreadsToCores bool `json:"lock_threads_to_cores"` Udp struct { + // Whether to enable unicast UDP listener + EnabledUnicast bool `json:"enabled_unicast"` + // Whether to enable multicast UDP listener + EnabledMulticast bool `json:"enabled_multicast"` // Port used for unicast UDP faces PortUnicast uint16 `json:"port_unicast"` // Port used for multicast UDP faces @@ -143,6 +147,8 @@ func DefaultConfig() *Config { c.Faces.CongestionMarking = true c.Faces.LockThreadsToCores = false + c.Faces.Udp.EnabledUnicast = true + c.Faces.Udp.EnabledMulticast = true c.Faces.Udp.PortUnicast = 6363 c.Faces.Udp.PortMulticast = 56363 c.Faces.Udp.MulticastAddressIpv4 = "224.0.23.170" diff --git a/fw/executor/yanfd.go b/fw/executor/yanfd.go index ecbbf982..b4a169e3 100644 --- a/fw/executor/yanfd.go +++ b/fw/executor/yanfd.go @@ -8,6 +8,7 @@ package executor import ( + "fmt" "net" "os" "time" @@ -108,40 +109,40 @@ func (y *YaNFD) Start() { // Perform setup operations for each network interface faceCnt := 0 + y.tcpListeners = make([]*face.TCPListener, 0) + + // Create multicast UDP face on each non-loopback interface ifaces, err := net.Interfaces() if err != nil { - core.LogFatal("Main", "Unable to access network interfaces: ", err) - os.Exit(2) + core.LogError("Main", "Unable to access network interfaces: ", err) } - tcpEnabled := core.GetConfig().Faces.Tcp.Enabled - tcpPort := face.TCPUnicastPort - y.tcpListeners = make([]*face.TCPListener, 0) for _, iface := range ifaces { if iface.Flags&net.FlagUp == 0 { core.LogInfo("Main", "Skipping interface ", iface.Name, " because not up") continue } - // Create UDP/TCP listener and multicast UDP interface for every address on interface addrs, err := iface.Addrs() if err != nil { - core.LogFatal("Main", "Unable to access addresses on network interface ", iface.Name, ": ", err) + core.LogError("Main", "Unable to access addresses on network interface ", iface.Name, ": ", err) + continue } + for _, addr := range addrs { ipAddr := addr.(*net.IPNet) - ipVersion := 4 - path := ipAddr.IP.String() - if ipAddr.IP.To4() == nil { - ipVersion = 6 - path += "%" + iface.Name + udpAddr := net.UDPAddr{ + IP: ipAddr.IP, + Zone: iface.Name, + // Port: set later } - if !addr.(*net.IPNet).IP.IsLoopback() { - multicastUDPTransport, err := face.MakeMulticastUDPTransport( - defn.MakeUDPFaceURI(ipVersion, path, face.UDPMulticastPort)) + if core.GetConfig().Faces.Udp.EnabledMulticast && !addr.(*net.IPNet).IP.IsLoopback() { + udpAddr.Port = int(face.UDPMulticastPort) + uri := fmt.Sprintf("udp://%s", &udpAddr) + multicastUDPTransport, err := face.MakeMulticastUDPTransport(defn.DecodeURIString(uri)) if err != nil { - core.LogError("Main", "Unable to create MulticastUDPTransport for ", path, " on ", iface.Name, ": ", err) + core.LogError("Main", "Unable to create MulticastUDPTransport for ", uri, ": ", err) continue } @@ -151,29 +152,37 @@ func (y *YaNFD) Start() { ).Run(nil) faceCnt += 1 - core.LogInfo("Main", "Created multicast UDP face for ", path, " on ", iface.Name) + core.LogInfo("Main", "Created multicast UDP face for ", uri) } - udpListener, err := face.MakeUDPListener(defn.MakeUDPFaceURI(ipVersion, path, face.UDPUnicastPort)) - if err != nil { - core.LogError("Main", "Unable to create UDP listener for ", path, " on ", iface.Name, ": ", err) - continue + if core.GetConfig().Faces.Udp.EnabledUnicast { + udpAddr.Port = int(face.UDPUnicastPort) + uri := fmt.Sprintf("udp://%s", &udpAddr) + udpListener, err := face.MakeUDPListener(defn.DecodeURIString(uri)) + if err != nil { + core.LogError("Main", "Unable to create UDP listener for ", uri, ": ", err) + continue + } + + faceCnt += 1 + go udpListener.Run() + y.udpListener = udpListener + core.LogInfo("Main", "Created UDP listener for ", uri) } - faceCnt += 1 - go udpListener.Run() - y.udpListener = udpListener - core.LogInfo("Main", "Created UDP listener for ", path, " on ", iface.Name) - if tcpEnabled { - tcpListener, err := face.MakeTCPListener(defn.MakeTCPFaceURI(ipVersion, path, tcpPort)) + if core.GetConfig().Faces.Tcp.Enabled { + udpAddr.Port = int(face.TCPUnicastPort) + uri := fmt.Sprintf("tcp://%s", &udpAddr) + + tcpListener, err := face.MakeTCPListener(defn.DecodeURIString(uri)) if err != nil { - core.LogError("Main", "Unable to create TCP listener for ", path, " on ", iface.Name, ": ", err) + core.LogError("Main", "Unable to create TCP listener for ", uri, ": ", err) continue } faceCnt += 1 go tcpListener.Run() y.tcpListeners = append(y.tcpListeners, tcpListener) - core.LogInfo("Main", "Created TCP listener for ", path, " on ", iface.Name) + core.LogInfo("Main", "Created TCP listener for ", uri) } } } diff --git a/fw/yanfd.sample.yml b/fw/yanfd.sample.yml index c1ffc3d8..5161005b 100644 --- a/fw/yanfd.sample.yml +++ b/fw/yanfd.sample.yml @@ -14,6 +14,10 @@ faces: lock_threads_to_cores: false udp: + # Whether to enable unicast UDP listener + enabled_unicast: true + # Whether to enable multicast UDP listener + enabled_multicast: true # Port used for unicast UDP faces port_unicast: 6363 # Port used for multicast UDP faces From aff18b04e97f430f1b594331a6e17724ddbbd306 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 22:17:16 +0000 Subject: [PATCH 30/36] fw: register global tcp and udp listeners --- fw/executor/yanfd.go | 169 ++++++++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 73 deletions(-) diff --git a/fw/executor/yanfd.go b/fw/executor/yanfd.go index b4a169e3..92deb212 100644 --- a/fw/executor/yanfd.go +++ b/fw/executor/yanfd.go @@ -45,7 +45,7 @@ type YaNFD struct { unixListener *face.UnixStreamListener wsListener *face.WebSocketListener tcpListeners []*face.TCPListener - udpListener *face.UDPListener + udpListeners []*face.UDPListener } // NewYaNFD creates a YaNFD. Don't call this function twice. @@ -96,7 +96,6 @@ func (y *YaNFD) Start() { core.LogFatal("Main", "Number of forwarding threads must be in range [1, ", fw.MaxFwThreads, "]") os.Exit(2) } - fw.Threads = make([]*fw.Thread, fw.NumFwThreads) var fwForDispatch []dispatch.FWThread for i := 0; i < fw.NumFwThreads; i++ { @@ -107,97 +106,118 @@ func (y *YaNFD) Start() { } dispatch.InitializeFWThreads(fwForDispatch) - // Perform setup operations for each network interface - faceCnt := 0 - y.tcpListeners = make([]*face.TCPListener, 0) - - // Create multicast UDP face on each non-loopback interface - ifaces, err := net.Interfaces() - if err != nil { - core.LogError("Main", "Unable to access network interfaces: ", err) + // Set up listeners for faces + listenerCount := 0 + + // Create unicast UDP face + if core.GetConfig().Faces.Udp.EnabledUnicast { + udpAddrs := []*net.UDPAddr{{ + IP: net.IPv4zero, + Port: int(face.UDPUnicastPort), + }, { + IP: net.IPv6zero, + Port: int(face.UDPUnicastPort), + }} + + for _, udpAddr := range udpAddrs { + uri := fmt.Sprintf("udp://%s", udpAddr) + udpListener, err := face.MakeUDPListener(defn.DecodeURIString(uri)) + if err != nil { + core.LogError("Main", "Unable to create UDP listener for ", uri, ": ", err) + } else { + listenerCount++ + go udpListener.Run() + y.udpListeners = append(y.udpListeners, udpListener) + core.LogInfo("Main", "Created unicast UDP listener for ", uri) + } + } } - for _, iface := range ifaces { - if iface.Flags&net.FlagUp == 0 { - core.LogInfo("Main", "Skipping interface ", iface.Name, " because not up") - continue + + // Create unicast TCP face + if core.GetConfig().Faces.Tcp.Enabled { + tcpAddrs := []*net.TCPAddr{{ + IP: net.IPv4zero, + Port: int(face.TCPUnicastPort), + }, { + IP: net.IPv6zero, + Port: int(face.TCPUnicastPort), + }} + + for _, tcpAddr := range tcpAddrs { + uri := fmt.Sprintf("tcp://%s", tcpAddr) + tcpListener, err := face.MakeTCPListener(defn.DecodeURIString(uri)) + if err != nil { + core.LogError("Main", "Unable to create TCP listener for ", uri, ": ", err) + } else { + listenerCount++ + go tcpListener.Run() + y.tcpListeners = append(y.tcpListeners, tcpListener) + core.LogInfo("Main", "Created unicast TCP listener for ", uri) + } } + } - addrs, err := iface.Addrs() + // Create multicast UDP face on each non-loopback interface + if core.GetConfig().Faces.Udp.EnabledMulticast { + ifaces, err := net.Interfaces() if err != nil { - core.LogError("Main", "Unable to access addresses on network interface ", iface.Name, ": ", err) - continue + core.LogError("Main", "Unable to access network interfaces: ", err) } - for _, addr := range addrs { - ipAddr := addr.(*net.IPNet) - - udpAddr := net.UDPAddr{ - IP: ipAddr.IP, - Zone: iface.Name, - // Port: set later + for _, iface := range ifaces { + if iface.Flags&net.FlagUp == 0 { + core.LogInfo("Main", "Skipping interface ", iface.Name, " because not up") + continue } - if core.GetConfig().Faces.Udp.EnabledMulticast && !addr.(*net.IPNet).IP.IsLoopback() { - udpAddr.Port = int(face.UDPMulticastPort) - uri := fmt.Sprintf("udp://%s", &udpAddr) - multicastUDPTransport, err := face.MakeMulticastUDPTransport(defn.DecodeURIString(uri)) - if err != nil { - core.LogError("Main", "Unable to create MulticastUDPTransport for ", uri, ": ", err) - continue - } - - face.MakeNDNLPLinkService( - multicastUDPTransport, - face.MakeNDNLPLinkServiceOptions(), - ).Run(nil) - - faceCnt += 1 - core.LogInfo("Main", "Created multicast UDP face for ", uri) + addrs, err := iface.Addrs() + if err != nil { + core.LogError("Main", "Unable to access addresses on network interface ", iface.Name, ": ", err) + continue } - if core.GetConfig().Faces.Udp.EnabledUnicast { - udpAddr.Port = int(face.UDPUnicastPort) - uri := fmt.Sprintf("udp://%s", &udpAddr) - udpListener, err := face.MakeUDPListener(defn.DecodeURIString(uri)) - if err != nil { - core.LogError("Main", "Unable to create UDP listener for ", uri, ": ", err) - continue + for _, addr := range addrs { + ipAddr := addr.(*net.IPNet) + udpAddr := net.UDPAddr{ + IP: ipAddr.IP, + Zone: iface.Name, + Port: int(face.UDPMulticastPort), } + uri := fmt.Sprintf("udp://%s", &udpAddr) - faceCnt += 1 - go udpListener.Run() - y.udpListener = udpListener - core.LogInfo("Main", "Created UDP listener for ", uri) - } + if !addr.(*net.IPNet).IP.IsLoopback() { + multicastUDPTransport, err := face.MakeMulticastUDPTransport(defn.DecodeURIString(uri)) + if err != nil { + core.LogError("Main", "Unable to create MulticastUDPTransport for ", uri, ": ", err) + continue + } - if core.GetConfig().Faces.Tcp.Enabled { - udpAddr.Port = int(face.TCPUnicastPort) - uri := fmt.Sprintf("tcp://%s", &udpAddr) + face.MakeNDNLPLinkService( + multicastUDPTransport, + face.MakeNDNLPLinkServiceOptions(), + ).Run(nil) - tcpListener, err := face.MakeTCPListener(defn.DecodeURIString(uri)) - if err != nil { - core.LogError("Main", "Unable to create TCP listener for ", uri, ": ", err) - continue + listenerCount++ + core.LogInfo("Main", "Created multicast UDP face for ", uri) } - faceCnt += 1 - go tcpListener.Run() - y.tcpListeners = append(y.tcpListeners, tcpListener) - core.LogInfo("Main", "Created TCP listener for ", uri) } } } + + // Set up Unix stream listener if core.GetConfig().Faces.Unix.Enabled { - // Set up Unix stream listener - y.unixListener, err = face.MakeUnixStreamListener(defn.MakeUnixFaceURI(face.UnixSocketPath)) + unixListener, err := face.MakeUnixStreamListener(defn.MakeUnixFaceURI(face.UnixSocketPath)) if err != nil { core.LogError("Main", "Unable to create Unix stream listener at ", face.UnixSocketPath, ": ", err) } else { - faceCnt += 1 - go y.unixListener.Run() + listenerCount++ + go unixListener.Run() + y.unixListener = unixListener core.LogInfo("Main", "Created Unix stream listener for ", face.UnixSocketPath) } } + // Set up WebSocket listener if core.GetConfig().Faces.WebSocket.Enabled { cfg := face.WebSocketListenerConfig{ Bind: core.GetConfig().Faces.WebSocket.Bind, @@ -206,17 +226,20 @@ func (y *YaNFD) Start() { TLSCert: core.ResolveConfigFileRelPath(core.GetConfig().Faces.WebSocket.TlsCert), TLSKey: core.ResolveConfigFileRelPath(core.GetConfig().Faces.WebSocket.TlsKey), } - y.wsListener, err = face.NewWebSocketListener(cfg) + + wsListener, err := face.NewWebSocketListener(cfg) if err != nil { core.LogError("Main", "Unable to create ", cfg, ": ", err) } else { - faceCnt++ - go y.wsListener.Run() + listenerCount++ + go wsListener.Run() + y.wsListener = wsListener core.LogInfo("Main", "Created ", cfg) } } - if faceCnt <= 0 { + // Check if any faces were created + if listenerCount <= 0 { core.LogFatal("Main", "No face or listener is successfully created. Quit.") os.Exit(2) } @@ -239,8 +262,8 @@ func (y *YaNFD) Stop() { } // Wait for UDP listener to quit - if y.udpListener != nil { - y.udpListener.Close() + for _, udpListener := range y.udpListeners { + udpListener.Close() } // Wait for TCP listeners to quit From 2275939aadf7af0f4c13cd5fad69b2ecac340aeb Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 22:24:29 +0000 Subject: [PATCH 31/36] fw: refactor some logging --- fw/executor/main.go | 2 +- fw/executor/profiler.go | 20 ++++++++++++-------- fw/executor/yanfd.go | 38 +++++++++++++++++++++----------------- fw/mgmt/thread.go | 4 ++-- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/fw/executor/main.go b/fw/executor/main.go index ba924241..ac9a6435 100644 --- a/fw/executor/main.go +++ b/fw/executor/main.go @@ -70,7 +70,7 @@ func Main(args []string) { sigChannel := make(chan os.Signal, 1) signal.Notify(sigChannel, os.Interrupt, syscall.SIGTERM) receivedSig := <-sigChannel - core.LogInfo("Main", "Received signal ", receivedSig, " - exiting") + core.LogInfo(yanfd, "Received signal ", receivedSig, " - exiting") yanfd.Stop() } diff --git a/fw/executor/profiler.go b/fw/executor/profiler.go index 061cee10..c5f9cd93 100644 --- a/fw/executor/profiler.go +++ b/fw/executor/profiler.go @@ -18,19 +18,23 @@ func NewProfiler(config *YaNFDConfig) *Profiler { return &Profiler{config: config} } +func (p *Profiler) String() string { + return "Profiler" +} + func (p *Profiler) Start() (err error) { if p.config.CpuProfile != "" { p.cpuFile, err = os.Create(p.config.CpuProfile) if err != nil { - core.LogFatal("Main", "Unable to open output file for CPU profile: ", err) + core.LogFatal(p, "Unable to open output file for CPU profile: ", err) } - core.LogInfo("Main", "Profiling CPU - outputting to ", p.config.CpuProfile) + core.LogInfo(p, "Profiling CPU - outputting to ", p.config.CpuProfile) pprof.StartCPUProfile(p.cpuFile) } if p.config.BlockProfile != "" { - core.LogInfo("Main", "Profiling blocking operations - outputting to ", p.config.BlockProfile) + core.LogInfo(p, "Profiling blocking operations - outputting to ", p.config.BlockProfile) runtime.SetBlockProfileRate(1) p.block = pprof.Lookup("block") } @@ -42,10 +46,10 @@ func (p *Profiler) Stop() { if p.block != nil { blockProfileFile, err := os.Create(p.config.BlockProfile) if err != nil { - core.LogFatal("Main", "Unable to open output file for block profile: ", err) + core.LogFatal(p, "Unable to open output file for block profile: ", err) } if err := p.block.WriteTo(blockProfileFile, 0); err != nil { - core.LogFatal("Main", "Unable to write block profile: ", err) + core.LogFatal(p, "Unable to write block profile: ", err) } blockProfileFile.Close() } @@ -53,14 +57,14 @@ func (p *Profiler) Stop() { if p.config.MemProfile != "" { memProfileFile, err := os.Create(p.config.MemProfile) if err != nil { - core.LogFatal("Main", "Unable to open output file for memory profile: ", err) + core.LogFatal(p, "Unable to open output file for memory profile: ", err) } defer memProfileFile.Close() - core.LogInfo("Main", "Profiling memory - outputting to ", p.config.MemProfile) + core.LogInfo(p, "Profiling memory - outputting to ", p.config.MemProfile) runtime.GC() if err := pprof.WriteHeapProfile(memProfileFile); err != nil { - core.LogFatal("Main", "Unable to write memory profile: ", err) + core.LogFatal(p, "Unable to write memory profile: ", err) } } diff --git a/fw/executor/yanfd.go b/fw/executor/yanfd.go index 92deb212..1d3f462b 100644 --- a/fw/executor/yanfd.go +++ b/fw/executor/yanfd.go @@ -48,6 +48,10 @@ type YaNFD struct { udpListeners []*face.UDPListener } +func (y *YaNFD) String() string { + return "YaNFD" +} + // NewYaNFD creates a YaNFD. Don't call this function twice. func NewYaNFD(config *YaNFDConfig) *YaNFD { // Provide metadata to other threads. @@ -76,7 +80,7 @@ func NewYaNFD(config *YaNFDConfig) *YaNFD { // Start runs YaNFD. Note: this function may exit the program when there is error. // This function is non-blocking. func (y *YaNFD) Start() { - core.LogInfo("Main", "Starting YaNFD") + core.LogInfo(y, "Starting YaNFD") // Start profiler y.profiler.Start() @@ -93,7 +97,7 @@ func (y *YaNFD) Start() { // Create forwarding threads if fw.NumFwThreads < 1 || fw.NumFwThreads > fw.MaxFwThreads { - core.LogFatal("Main", "Number of forwarding threads must be in range [1, ", fw.MaxFwThreads, "]") + core.LogFatal(y, "Number of forwarding threads must be in range [1, ", fw.MaxFwThreads, "]") os.Exit(2) } fw.Threads = make([]*fw.Thread, fw.NumFwThreads) @@ -123,12 +127,12 @@ func (y *YaNFD) Start() { uri := fmt.Sprintf("udp://%s", udpAddr) udpListener, err := face.MakeUDPListener(defn.DecodeURIString(uri)) if err != nil { - core.LogError("Main", "Unable to create UDP listener for ", uri, ": ", err) + core.LogError(y, "Unable to create UDP listener for ", uri, ": ", err) } else { listenerCount++ go udpListener.Run() y.udpListeners = append(y.udpListeners, udpListener) - core.LogInfo("Main", "Created unicast UDP listener for ", uri) + core.LogInfo(y, "Created unicast UDP listener for ", uri) } } } @@ -147,12 +151,12 @@ func (y *YaNFD) Start() { uri := fmt.Sprintf("tcp://%s", tcpAddr) tcpListener, err := face.MakeTCPListener(defn.DecodeURIString(uri)) if err != nil { - core.LogError("Main", "Unable to create TCP listener for ", uri, ": ", err) + core.LogError(y, "Unable to create TCP listener for ", uri, ": ", err) } else { listenerCount++ go tcpListener.Run() y.tcpListeners = append(y.tcpListeners, tcpListener) - core.LogInfo("Main", "Created unicast TCP listener for ", uri) + core.LogInfo(y, "Created unicast TCP listener for ", uri) } } } @@ -161,18 +165,18 @@ func (y *YaNFD) Start() { if core.GetConfig().Faces.Udp.EnabledMulticast { ifaces, err := net.Interfaces() if err != nil { - core.LogError("Main", "Unable to access network interfaces: ", err) + core.LogError(y, "Unable to access network interfaces: ", err) } for _, iface := range ifaces { if iface.Flags&net.FlagUp == 0 { - core.LogInfo("Main", "Skipping interface ", iface.Name, " because not up") + core.LogInfo(y, "Skipping interface ", iface.Name, " because not up") continue } addrs, err := iface.Addrs() if err != nil { - core.LogError("Main", "Unable to access addresses on network interface ", iface.Name, ": ", err) + core.LogError(y, "Unable to access addresses on network interface ", iface.Name, ": ", err) continue } @@ -188,7 +192,7 @@ func (y *YaNFD) Start() { if !addr.(*net.IPNet).IP.IsLoopback() { multicastUDPTransport, err := face.MakeMulticastUDPTransport(defn.DecodeURIString(uri)) if err != nil { - core.LogError("Main", "Unable to create MulticastUDPTransport for ", uri, ": ", err) + core.LogError(y, "Unable to create MulticastUDPTransport for ", uri, ": ", err) continue } @@ -198,7 +202,7 @@ func (y *YaNFD) Start() { ).Run(nil) listenerCount++ - core.LogInfo("Main", "Created multicast UDP face for ", uri) + core.LogInfo(y, "Created multicast UDP face for ", uri) } } } @@ -208,12 +212,12 @@ func (y *YaNFD) Start() { if core.GetConfig().Faces.Unix.Enabled { unixListener, err := face.MakeUnixStreamListener(defn.MakeUnixFaceURI(face.UnixSocketPath)) if err != nil { - core.LogError("Main", "Unable to create Unix stream listener at ", face.UnixSocketPath, ": ", err) + core.LogError(y, "Unable to create Unix stream listener at ", face.UnixSocketPath, ": ", err) } else { listenerCount++ go unixListener.Run() y.unixListener = unixListener - core.LogInfo("Main", "Created Unix stream listener for ", face.UnixSocketPath) + core.LogInfo(y, "Created Unix stream listener for ", face.UnixSocketPath) } } @@ -229,25 +233,25 @@ func (y *YaNFD) Start() { wsListener, err := face.NewWebSocketListener(cfg) if err != nil { - core.LogError("Main", "Unable to create ", cfg, ": ", err) + core.LogError(y, "Unable to create ", cfg, ": ", err) } else { listenerCount++ go wsListener.Run() y.wsListener = wsListener - core.LogInfo("Main", "Created ", cfg) + core.LogInfo(y, "Created ", cfg) } } // Check if any faces were created if listenerCount <= 0 { - core.LogFatal("Main", "No face or listener is successfully created. Quit.") + core.LogFatal(y, "No face or listener is successfully created. Quit.") os.Exit(2) } } // Stop shuts down YaNFD. func (y *YaNFD) Stop() { - core.LogInfo("Main", "Forwarder shutting down ...") + core.LogInfo(y, "Forwarder shutting down ...") core.ShouldQuit = true // Stop profiler diff --git a/fw/mgmt/thread.go b/fw/mgmt/thread.go index 3496816e..2953579f 100644 --- a/fw/mgmt/thread.go +++ b/fw/mgmt/thread.go @@ -66,7 +66,7 @@ func MakeMgmtThread() *Thread { } func (m *Thread) String() string { - return "Management" + return "Mgmt" } func (m *Thread) registerModule(name string, module Module) { @@ -115,7 +115,7 @@ func (m *Thread) sendResponse(response *mgmt.ControlResponse, interest *spec.Int // Run management thread func (m *Thread) Run() { - core.LogInfo(m, "Starting management") + core.LogInfo(m, "Starting management thread") // Create and register Internal transport m.face, m.transport = face.RegisterInternalTransport() From 43037761334d838009df67f5cbe370300f7c35d7 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 23:17:46 +0000 Subject: [PATCH 32/36] docs: update for nfdc and dvc --- README.md | 4 ++ fw/README.md | 1 - tools/dvc/README.md | 24 ++++++++ tools/nfdc/README.md | 129 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 tools/dvc/README.md create mode 100644 tools/nfdc/README.md diff --git a/README.md b/README.md index a32d6465..b8ac7b96 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ ndnd fw run yanfd.config.yml A full configuration example can be found in [fw/yanfd.sample.yml](fw/yanfd.sample.yml). Note that the default configuration may require root privileges to bind to multicast interfaces. +Once started, you can use the [Forwarder Control](tools/nfdc/README.md) tool to manage faces and routes. + ## 📡 Distance Vector Router The `ndnd/dv` package implements `ndn-dv`, an NDN Distance Vector routing daemon. @@ -76,6 +78,8 @@ ndnd dv run dv.config.yml A full configuration example can be found in [dv/dv.sample.yml](dv/dv.sample.yml). Make sure the network and router name are correctly configured and the forwarder is running. +Once started, you can use the [DV Control](tools/dvc/README.md) tool to create and destroy neighbor links. + ## 📚 Standard Library The `ndnd/std` package implements `go-ndn`, a standard library for NDN applications. diff --git a/fw/README.md b/fw/README.md index 9165379a..eb702800 100644 --- a/fw/README.md +++ b/fw/README.md @@ -37,7 +37,6 @@ yanfd /etc/ndn/yanfd.yml ``` *Runtime configuration* is performed via the [NFD Management Protocol](https://redmine.named-data.net/projects/nfd/wiki/Management). -At the moment, this requires the installation of the [NFD](https://github.com/named-data/NFD) package to obtain the `nfdc` configuration utility. ## Building from source diff --git a/tools/dvc/README.md b/tools/dvc/README.md new file mode 100644 index 00000000..a4c3fda2 --- /dev/null +++ b/tools/dvc/README.md @@ -0,0 +1,24 @@ +# DV Control Reference + +This is the detailed reference for the ndn-dv routing daemon control tool. + +## `ndnd dv link create` + +The link create command creates a new neighbor link. A new permanent face will be created for the neighbor if a matching face does not exist. + +```bash +# Create a UDP neighbor link +ndnd dv link create udp://suns.cs.ucla.edu + +# Create a TCP neighbor link +ndnd dv link create tcp4://hobo.cs.arizona.edu:6363 +``` + +## `ndnd dv link destroy` + +The link destroy command destroys a neighbor link. The face associated with the neighbor will be destroyed. + +```bash +# Destroy a neighbor link by URI +ndnd dv link destroy udp://suns.cs.ucla.edu +``` diff --git a/tools/nfdc/README.md b/tools/nfdc/README.md new file mode 100644 index 00000000..34cad28b --- /dev/null +++ b/tools/nfdc/README.md @@ -0,0 +1,129 @@ +# Forwarder Control Reference + +This is the detailed reference for the NDNd forwarder control tool, and implements the NFD Management Protocol. + +You can also use the [nfdc](https://docs.named-data.net/NFD/24.07/manpages/nfdc.html) tool from the NFD project to manage the NDNd forwarder. + +## `ndnd fw status` + +The status command shows general status of the forwarder, including its version, uptime, data structure counters, and global packet counters. + +## `ndnd fw face list` + +The face list command prints the face table, which contains information about faces. + +## `ndnd fw face create` + +The face create command creates a new face. The supported arguments are: + +- `remote=`: The remote URI of the face. +- `local=`: The local URI of the face. +- `cost=`: The cost of the face. +- `persistency=`: The persistency of the face (`persistent` or `permanent`). +- `mtu=`: The MTU of the face in bytes. + +```bash +# Create a UDP face with the default port +ndnd fw face create remote=udp://suns.cs.ucla.edu + +# Create a TCP face over IPv4 +ndnd fw face create remote=tcp4://suns.cs.ucla.edu:6363 + +# Create a peramanent TCP face with a cost of 10 +ndnd fw face create remote=tcp://suns.cs.ucla.edu cost=10 persistency=permanent +``` + +## `ndnd fw face destroy` + +The face destroy command destroys a face. The supported arguments are: + +- `face=|`: The face ID or remote URI of the face to destroy. + +```bash +# Destroy a face by ID +ndnd fw face destroy face=6 + +# Destroy a face by remote URI +ndnd fw face destroy face=tcp://suns.cs.ucla.edu +``` + +## `ndnd fw route list` + +The route list command prints the existing RIB routes. + +## `ndnd fw route add` + +The route add command adds a route to the RIB. The supported arguments are: + +- `prefix=`: The name prefix of the route. +- `face=|`: The next hop face ID to forward packets to. +- `cost=`: The cost of the route. +- `origin=`: The origin of the route (default=255). +- `expires=`: The expiration time of the route in milliseconds. + +If a face URI is specified and the face does not exist, it will be created. + +```bash +# Add a route to forward packets to a new or existing UDP face +ndnd fw route add prefix=/ndn face=udp://suns.cs.ucla.edu + +# Add a route with a permanent TCP face (face options must appear before "face=") +ndnd fw route add prefix=/ndn persistency=permanent face=tcp://suns.cs.ucla.edu + +# Add a route to forward packets to face 6 +ndnd fw route add prefix=/example face=6 + +# Add a route with a cost of 10 and origin of "client" +ndnd fw route add prefix=/example face=6 cost=10 origin=65 +``` + +## `ndnd fw route remove` + +The route remove command removes a route from the RIB. The supported arguments are: + +- `prefix=`: The name prefix of the route. +- `face=|`: The next hop face ID of the route. +- `origin=`: The origin of the route (default=255). + +```bash +# Remove a route by prefix, face and origin +ndnd fw route remove prefix=/example face=6 origin=65 +``` + +## `ndnd fw fib list` + +The fib list command prints the existing FIB entries. + +## `ndnd fw cs info` + +The cs info command prints information about the content store. + +## `ndnd fw strategy list` + +The strategy list command prints the currently selected forwarding strategies. + +## `ndnd fw strategy set` + +The strategy set command sets a forwarding strategy for a name prefix. The supported arguments are: + +- `prefix=`: The name prefix to set the strategy for. +- `strategy=`: The forwarding strategy to set. + +```bash +# Set the strategy for /example to "multicast" +ndnd fw strategy set prefix=/example strategy=/localhost/nfd/strategy/multicast/v=1 + +# Set the strategy for /example to "best-route" +ndnd fw strategy set prefix=/example strategy=/localhost/nfd/strategy/best-route/v=1 +``` + +## `ndnd fw strategy unset` + +The strategy unset command unsets a forwarding strategy for a name prefix. The supported arguments are: + +- `prefix=`: The name prefix to unset the strategy for. + +```bash +# Unset the strategy for /example +ndnd fw strategy unset prefix=/example +``` From 88de60cb80adb744629b4ba5f4192d67f6ce575d Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 23:20:09 +0000 Subject: [PATCH 33/36] engine: fix return value --- std/engine/basic/engine.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/engine/basic/engine.go b/std/engine/basic/engine.go index 74b25046..12c0ee8d 100644 --- a/std/engine/basic/engine.go +++ b/std/engine/basic/engine.go @@ -457,7 +457,7 @@ func (e *Engine) ExecMgmtCmd(module string, cmd string, args any) (any, error) { } interest, err := e.mgmtConf.MakeCmd(module, cmd, cmdArgs, intCfg) if err != nil { - return err, nil + return nil, err } type mgmtResp struct { @@ -505,7 +505,7 @@ func (e *Engine) ExecMgmtCmd(module string, cmd string, args any) (any, error) { } }) if err != nil { - return err, nil + return nil, err } resp := <-respCh From 36bbb41ce193282d1794085bfc46dcfc74f37867 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 2 Jan 2025 23:51:35 +0000 Subject: [PATCH 34/36] fw: add lock to RIB (fix #82) --- fw/table/rib.go | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/fw/table/rib.go b/fw/table/rib.go index 25f12a1c..7bce86a4 100644 --- a/fw/table/rib.go +++ b/fw/table/rib.go @@ -9,6 +9,7 @@ package table import ( "container/list" + "sync" "time" enc "github.com/named-data/ndnd/std/encoding" @@ -16,7 +17,8 @@ import ( // RibTable represents the Routing Information Base (RIB). type RibTable struct { - RibEntry + root RibEntry + mutex sync.RWMutex } // RibEntry represents an entry in the RIB table. @@ -59,12 +61,12 @@ const ( // Rib is the Routing Information Base. var Rib = RibTable{ - RibEntry: RibEntry{ + root: RibEntry{ children: map[*RibEntry]bool{}, }, } -func (r *RibTable) fillTreeToPrefixEnc(name enc.Name) *RibEntry { +func (r *RibEntry) fillTreeToPrefixEnc(name enc.Name) *RibEntry { entry := r.findLongestPrefixEntryEnc(name) for depth := entry.depth + 1; depth <= len(name); depth++ { child := &RibEntry{ @@ -149,8 +151,11 @@ func (r *RibEntry) updateNexthopsEnc() { // AddRoute adds or updates a RIB entry for the specified prefix. func (r *RibTable) AddEncRoute(name enc.Name, route *Route) { + r.mutex.Lock() + defer r.mutex.Unlock() + name = name.Clone() - node := r.fillTreeToPrefixEnc(name) + node := r.root.fillTreeToPrefixEnc(name) if node.Name == nil { node.Name = name } @@ -172,10 +177,13 @@ func (r *RibTable) AddEncRoute(name enc.Name, route *Route) { // GetAllEntries returns all routes in the RIB. func (r *RibTable) GetAllEntries() []*RibEntry { + r.mutex.RLock() + defer r.mutex.RUnlock() + entries := make([]*RibEntry, 0) // Walk tree in-order queue := list.New() - queue.PushBack(&r.RibEntry) + queue.PushBack(&r.root) for queue.Len() > 0 { ribEntry := queue.Front().Value.(*RibEntry) queue.Remove(queue.Front()) @@ -192,14 +200,12 @@ func (r *RibTable) GetAllEntries() []*RibEntry { return entries } -// GetRoutes returns all routes in the RIB entry. -func (r *RibEntry) GetRoutes() []*Route { - return r.routes -} - // RemoveRoute removes the specified route from the specified prefix. func (r *RibTable) RemoveRouteEnc(name enc.Name, faceID uint64, origin uint64) { - entry := r.findExactMatchEntryEnc(name) + r.mutex.Lock() + defer r.mutex.Unlock() + + entry := r.root.findExactMatchEntryEnc(name) if entry != nil { for i, route := range entry.routes { if route.FaceID == faceID && route.Origin == origin { @@ -216,11 +222,23 @@ func (r *RibTable) RemoveRouteEnc(name enc.Name, faceID uint64, origin uint64) { } } +func (r *RibTable) CleanUpFace(faceId uint64) { + r.mutex.Lock() + defer r.mutex.Unlock() + + r.root.cleanUpFace(faceId) +} + +// GetRoutes returns all routes in the RIB entry. +func (r *RibEntry) GetRoutes() []*Route { + return r.routes +} + // CleanUpFace removes the specified face from all entries. Used for clean-up after a face is destroyed. -func (r *RibEntry) CleanUpFace(faceId uint64) { +func (r *RibEntry) cleanUpFace(faceId uint64) { // Recursively clean children for child := range r.children { - child.CleanUpFace(faceId) + child.cleanUpFace(faceId) } if r.Name == nil { From b198a0a27886318aa39694033faecf738e06a197 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Fri, 3 Jan 2025 00:24:42 +0000 Subject: [PATCH 35/36] std: refactor mgmt/const -> persistency --- std/ndn/mgmt_2022/{const.go => persistency.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename std/ndn/mgmt_2022/{const.go => persistency.go} (100%) diff --git a/std/ndn/mgmt_2022/const.go b/std/ndn/mgmt_2022/persistency.go similarity index 100% rename from std/ndn/mgmt_2022/const.go rename to std/ndn/mgmt_2022/persistency.go From 8ee81881dcdbcfce3161ca7b46efde3dd81ee07e Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Fri, 3 Jan 2025 01:01:48 +0000 Subject: [PATCH 36/36] std: switch mgmt/nfd/status to time --- fw/mgmt/forwarder-status.go | 4 +-- std/ndn/mgmt_2022/definitions.go | 10 +++--- std/ndn/mgmt_2022/zz_generated.go | 53 ++++++++++++++++++------------- tools/nfdc/nfdc_status.go | 6 ++-- 4 files changed, 42 insertions(+), 31 deletions(-) diff --git a/fw/mgmt/forwarder-status.go b/fw/mgmt/forwarder-status.go index c49d545a..0df3df2e 100644 --- a/fw/mgmt/forwarder-status.go +++ b/fw/mgmt/forwarder-status.go @@ -66,8 +66,8 @@ func (f *ForwarderStatusModule) general(interest *spec.Interest, pitToken []byte // Generate new dataset status := &mgmt.GeneralStatus{ NfdVersion: core.Version, - StartTimestamp: uint64(core.StartTimestamp.UnixNano() / 1000 / 1000), - CurrentTimestamp: uint64(time.Now().UnixNano() / 1000 / 1000), + StartTimestamp: time.Duration(core.StartTimestamp.UnixNano()), + CurrentTimestamp: time.Duration(time.Now().UnixNano()), NFibEntries: uint64(len(table.FibStrategyTable.GetAllFIBEntries())), } // Don't set NNameTreeEntries because we don't use a NameTree diff --git a/std/ndn/mgmt_2022/definitions.go b/std/ndn/mgmt_2022/definitions.go index 2c6e8816..a390c2a2 100644 --- a/std/ndn/mgmt_2022/definitions.go +++ b/std/ndn/mgmt_2022/definitions.go @@ -2,6 +2,8 @@ package mgmt_2022 import ( + "time" + enc "github.com/named-data/ndnd/std/encoding" ) @@ -139,10 +141,10 @@ type FaceEventNotification struct { type GeneralStatus struct { //+field:string NfdVersion string `tlv:"0x80"` - //+field:natural - StartTimestamp uint64 `tlv:"0x81"` - //+field:natural - CurrentTimestamp uint64 `tlv:"0x82"` + //+field:time + StartTimestamp time.Duration `tlv:"0x81"` + //+field:time + CurrentTimestamp time.Duration `tlv:"0x82"` //+field:natural NNameTreeEntries uint64 `tlv:"0x83"` //+field:natural diff --git a/std/ndn/mgmt_2022/zz_generated.go b/std/ndn/mgmt_2022/zz_generated.go index 5e00a1d7..7a850667 100644 --- a/std/ndn/mgmt_2022/zz_generated.go +++ b/std/ndn/mgmt_2022/zz_generated.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "io" "strings" + "time" enc "github.com/named-data/ndnd/std/encoding" ) @@ -2917,7 +2918,7 @@ func (encoder *GeneralStatusEncoder) Init(value *GeneralStatus) { } l += uint(len(value.NfdVersion)) l += 1 - switch x := value.StartTimestamp; { + switch x := uint64(value.StartTimestamp / time.Millisecond); { case x <= 0xff: l += 2 case x <= 0xffff: @@ -2928,7 +2929,7 @@ func (encoder *GeneralStatusEncoder) Init(value *GeneralStatus) { l += 9 } l += 1 - switch x := value.CurrentTimestamp; { + switch x := uint64(value.CurrentTimestamp / time.Millisecond); { case x <= 0xff: l += 2 case x <= 0xffff: @@ -3233,7 +3234,7 @@ func (encoder *GeneralStatusEncoder) EncodeInto(value *GeneralStatus, buf []byte pos += uint(len(value.NfdVersion)) buf[pos] = byte(129) pos += 1 - switch x := value.StartTimestamp; { + switch x := uint64(value.StartTimestamp / time.Millisecond); { case x <= 0xff: buf[pos] = 1 buf[pos+1] = byte(x) @@ -3253,7 +3254,7 @@ func (encoder *GeneralStatusEncoder) EncodeInto(value *GeneralStatus, buf []byte } buf[pos] = byte(130) pos += 1 - switch x := value.CurrentTimestamp; { + switch x := uint64(value.CurrentTimestamp / time.Millisecond); { case x <= 0xff: buf[pos] = 1 buf[pos+1] = byte(x) @@ -3813,38 +3814,46 @@ func (context *GeneralStatusParsingContext) Parse(reader enc.ParseReader, ignore if true { handled = true handled_StartTimestamp = true - value.StartTimestamp = uint64(0) { - for i := 0; i < int(l); i++ { - x := byte(0) - x, err = reader.ReadByte() - if err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF + timeInt := uint64(0) + timeInt = uint64(0) + { + for i := 0; i < int(l); i++ { + x := byte(0) + x, err = reader.ReadByte() + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + break } - break + timeInt = uint64(timeInt<<8) | uint64(x) } - value.StartTimestamp = uint64(value.StartTimestamp<<8) | uint64(x) } + value.StartTimestamp = time.Duration(timeInt) * time.Millisecond } } case 130: if true { handled = true handled_CurrentTimestamp = true - value.CurrentTimestamp = uint64(0) { - for i := 0; i < int(l); i++ { - x := byte(0) - x, err = reader.ReadByte() - if err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF + timeInt := uint64(0) + timeInt = uint64(0) + { + for i := 0; i < int(l); i++ { + x := byte(0) + x, err = reader.ReadByte() + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + break } - break + timeInt = uint64(timeInt<<8) | uint64(x) } - value.CurrentTimestamp = uint64(value.CurrentTimestamp<<8) | uint64(x) } + value.CurrentTimestamp = time.Duration(timeInt) * time.Millisecond } } case 131: diff --git a/tools/nfdc/nfdc_status.go b/tools/nfdc/nfdc_status.go index 331eb213..c3bbbaea 100644 --- a/tools/nfdc/nfdc_status.go +++ b/tools/nfdc/nfdc_status.go @@ -31,9 +31,9 @@ func (n *Nfdc) ExecStatusGeneral(args []string) { fmt.Println("General NFD status:") n.statusPadding = 24 n.printStatusLine("version", status.NfdVersion) - n.printStatusLine("startTime", time.UnixMilli(int64(status.StartTimestamp))) - n.printStatusLine("currentTime", time.UnixMilli(int64(status.CurrentTimestamp))) - n.printStatusLine("uptime", time.Duration(status.CurrentTimestamp-status.StartTimestamp)*time.Millisecond) + n.printStatusLine("startTime", time.Unix(0, int64(status.StartTimestamp))) + n.printStatusLine("currentTime", time.Unix(0, int64(status.CurrentTimestamp))) + n.printStatusLine("uptime", (status.CurrentTimestamp - status.StartTimestamp)) n.printStatusLine("nNameTreeEntries", status.NNameTreeEntries) n.printStatusLine("nFibEntries", status.NFibEntries) n.printStatusLine("nPitEntries", status.NCsEntries)