Skip to content
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9452847
initial skeleton of core logic
jaczhi Apr 22, 2025
b46d76a
add parsing and validation
jaczhi Apr 27, 2025
93f16c8
also add into local forwarder
jaczhi Apr 27, 2025
a40255e
Merge branch 'main' into jaczhi/prefix-inject
jaczhi Apr 28, 2025
21cc8e8
custom fetch
jaczhi Apr 28, 2025
c4de196
use stapled certificates
jaczhi Apr 29, 2025
ca0299e
fix command name to not use keyword component
jaczhi May 3, 2025
5974548
remember to reply to prefix injection
jaczhi May 3, 2025
7b3e6fc
fix usage of wrong api for prefix table
jaczhi May 3, 2025
ecb70b0
fix trust anchor typo
jaczhi May 3, 2025
6c1dc20
stapled certs are non-critical
jaczhi May 4, 2025
d9e04f9
regenerate
jaczhi May 4, 2025
f343014
change parse order
jaczhi May 4, 2025
1fbc450
revert
jaczhi May 4, 2025
5164c88
defer doesn't work with callbacks
jaczhi May 4, 2025
c62b4af
prefix injection remove
jaczhi May 4, 2025
40a36c6
avoid redundant parsing
jaczhi May 4, 2025
613c6b8
change default behavior for backwards compatibility
jaczhi May 4, 2025
fb597e9
Merge branch 'main' into jaczhi/prefix-inject
jaczhi May 4, 2025
417fb6c
remove negative cases for uint
jaczhi May 4, 2025
36ff0ec
fix import order for lint
jaczhi May 4, 2025
541d50c
refactor control command response, use binary fields, move defs to dv
jaczhi May 4, 2025
1333688
Merge branch 'main' into jaczhi/prefix-inject
zjkmxy May 5, 2025
e73f881
Merge branch 'main' into jaczhi/prefix-inject
jaczhi May 16, 2025
3d3dcf6
Merge branch 'jaczhi/prefix-inject' of https://github.com/jaczhi/ndnd…
jaczhi May 16, 2025
8240215
update constants and format to comply with new protocol format
jaczhi May 16, 2025
811015f
fix pa object name parsing
jaczhi May 16, 2025
984aeff
generate
jaczhi May 16, 2025
eaa1cd7
reject seen PA
jaczhi May 18, 2025
2aced71
check validityperiod
jaczhi May 18, 2025
2af277e
rename injection to insertion
jaczhi Jun 2, 2025
87f2840
use time format constant from spec
jaczhi Jun 2, 2025
803ef78
rename TLV inside PrefixInsertion to proper name
jaczhi Jun 17, 2025
042921d
do not construct prefix injection client if prefix injection is disabled
jaczhi Jun 17, 2025
28995d8
reduce collision by using binary string for hashing
jaczhi Jun 17, 2025
4b2447d
revert unncessary start logic
jaczhi Jun 17, 2025
c31911a
Merge branch 'main' into jaczhi/prefix-inject
zjkmxy Jun 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions dv/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const CostPfxInfinity = uint64(0xFFFFFFFF)

// NlsrOrigin is the origin to use for local registration.
const NlsrOrigin = uint64(mgmt.RouteOriginNLSR)
const PrefixInjOrigin = uint64(mgmt.RouteOriginPrefixInj)

var MulticastStrategy = enc.LOCALHOST.
Append(enc.NewGenericComponent("nfd")).
Expand All @@ -39,6 +40,12 @@ type Config struct {
KeyChainUri string `json:"keychain"`
// List of trust anchor full names.
TrustAnchors []string `json:"trust_anchors"`
// Path to trust schema for prefix injection.
PrefixInjectionSchemaPath string `json:"prefix_injection_schema"`
// URI specifying KeyChain location for prefix injection verifier.
PrefixInjectionKeychainUri string `json:"prefix_injection_keychain"`
// List of trust anchor full names for prefix injection.
PrefixInjectionTrustAnchors []string `json:"prefix_injection_trust_anchors"`
// List of permanent neighbors.
Neighbors []Neighbor `json:"neighbors"`

Expand All @@ -60,6 +67,8 @@ type Config struct {
mgmtPrefix enc.Name
// Trust anchor names
trustAnchorsN []enc.Name
// Prefix Injection trust anchor names
prefixInjectionTrustAnchorsN []enc.Name
}

type Neighbor struct {
Expand All @@ -81,6 +90,8 @@ func DefaultConfig() *Config {
AdvertisementSyncInterval_ms: 5000,
RouterDeadInterval_ms: 30000,
KeyChainUri: "undefined",
PrefixInjectionSchemaPath: "deny",
PrefixInjectionKeychainUri: "undefined",
}
}

Expand Down Expand Up @@ -136,6 +147,15 @@ func (c *Config) Parse() (err error) {
c.trustAnchorsN = append(c.trustAnchorsN, name)
}

c.prefixInjectionTrustAnchorsN = make([]enc.Name, 0, len(c.PrefixInjectionTrustAnchors))
for _, anchor := range c.PrefixInjectionTrustAnchors {
name, err := enc.NameFromStr(anchor)
if err != nil {
return err
}
c.prefixInjectionTrustAnchorsN = append(c.prefixInjectionTrustAnchorsN, name)
}

// Advertisement sync and data prefixes
c.advSyncPfxN = enc.LOCALHOP.
Append(c.networkNameN...).
Expand Down Expand Up @@ -206,6 +226,10 @@ func (c *Config) TrustAnchorNames() []enc.Name {
return c.trustAnchorsN
}

func (c *Config) PrefixInjectionTrustAnchorNames() []enc.Name {
return c.prefixInjectionTrustAnchorsN
}

func (c *Config) SchemaBytes() []byte {
return SchemaBytes
}
11 changes: 11 additions & 0 deletions dv/dv.sample.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ dv:
trust_anchors:
- "/ndn/KEY/%27%C4%B2%2A%9F%7B%81%27/ndn/v=1651246789556"

# [optional] Trust schema for prefix injection
# - If "insecure" is specified, security is disabled
# - If "deny" is specified, the prefix injection handler will be turned off
# - Example: /absolute/path/to/schema.tlv
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A concrete example of the trust schema should be included as part of the repository.

prefix_injection_schema: "insecure"
# [optional] Keychain URI for prefix injection
prefix_injection_keychain: "insecure"
# [optional] List of full names of all prefix injection trust anchors
# - There should be at least one trust anchor if the schema is not "insecure" or "deny"
prefix_injection_trust_anchors: []

# [optional] List of permanent neighbors
# Example with all options:
# - uri: udp4://suns.cs.ucla.edu:6363 # required
Expand Down
231 changes: 231 additions & 0 deletions dv/dv/injection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package dv

import (
"time"

"github.com/named-data/ndnd/dv/config"
"github.com/named-data/ndnd/dv/nfdc"
"github.com/named-data/ndnd/dv/tlv"
enc "github.com/named-data/ndnd/std/encoding"
"github.com/named-data/ndnd/std/log"
"github.com/named-data/ndnd/std/ndn"
mgmt "github.com/named-data/ndnd/std/ndn/mgmt_2022"
spec "github.com/named-data/ndnd/std/ndn/spec_2022"
sig "github.com/named-data/ndnd/std/security/signer"
"github.com/named-data/ndnd/std/types/optional"
)

func (dv *Router) onInjection(args ndn.InterestHandlerArgs) {
resError := &mgmt.ControlResponse{
Val: &mgmt.ControlResponseVal{
StatusCode: 400,
StatusText: "Failed to execute prefix injection",
Params: nil,
},
}

reply := func(res *mgmt.ControlResponse) {
signer := sig.NewSha256Signer()
data, err := dv.engine.Spec().MakeData(
args.Interest.Name(),
&ndn.DataConfig{
ContentType: optional.Some(ndn.ContentTypeBlob),
Freshness: optional.Some(1 * time.Second),
},
res.Encode(),
signer)
if err != nil {
log.Warn(dv, "Failed to make inject response Data", "err", err)
return
}
args.Reply(data.Wire)
}

// If there is no incoming face ID, we can't use this
if !args.IncomingFaceId.IsSet() {
log.Warn(dv, "Received Prefix Injection with no incoming face ID, ignoring")
reply(resError)
return
}

// Check if app param is present
if args.Interest.AppParam() == nil {
log.Warn(dv, "Received Prefix Injection with no AppParam, ignoring")
reply(resError)
return
}

paParams, err := tlv.ParsePrefixInjection(enc.NewWireView(args.Interest.AppParam()), true)
if err != nil {
log.Warn(dv, "Failed to parse Prefix Injection AppParam", "err", err)
reply(resError)
return
}

// Decode Prefix Injection Object
dCtx := spec.DataParsingContext{}
dCtx.Init()
data, err := dCtx.Parse(enc.NewBufferView(paParams.ObjectWire), true)
if err != nil {
log.Warn(dv, "Failed to parse Prefix Injection inner data", "err", err)
reply(resError)
return
}
sigCov := dCtx.SigCovered()

var stapledCertCallbacks []ndn.ExpressCallbackArgs
for _, certWire := range paParams.StapledCertificates {
data, sigCov, err := spec.Spec{}.ReadData(enc.NewBufferView(certWire))
if err != nil {
log.Warn(dv, "Stapled malformed certificate", "err", err)
reply(resError)
return
}

stapledCertCallbacks = append(stapledCertCallbacks,
ndn.ExpressCallbackArgs{
Result: ndn.InterestResultData,
Data: data,
RawData: enc.Wire{certWire},
SigCovered: sigCov,
IsLocal: true,
})
}

// Validate signature
dv.prefixInjectionClient.ValidateExt(ndn.ValidateExtArgs{
Data: data,
SigCovered: sigCov,
Fetch: optional.Some(func(name enc.Name, config *ndn.InterestConfig, callback ndn.ExpressCallbackFunc) {
for _, certCallback := range stapledCertCallbacks {
if certCallback.Data.Name().Equal(name) {
dv.prefixInjectionClient.Engine().Post(func() {
callback(certCallback)
})
return
}
}

config.NextHopId = optional.None[uint64]()
dv.prefixInjectionClient.ExpressR(ndn.ExpressRArgs{
Name: name,
Config: config,
Retries: 3,
Callback: callback,
TryStore: dv.prefixInjectionClient.Store(),
})
}),
Callback: func(valid bool, err error) {
if !valid || err != nil {
log.Warn(dv, "Failed to validate signature", "name", data.Name(), "valid", valid, "err", err)
reply(resError)
return
}

res := dv.onPrefixInjectionObject(data, args.IncomingFaceId.Unwrap())
reply(res)
},
})
}

func (dv *Router) onPrefixInjectionObject(object ndn.Data, faceId uint64) *mgmt.ControlResponse {
resError := &mgmt.ControlResponse{
Val: &mgmt.ControlResponseVal{
StatusCode: 400,
StatusText: "Failed to execute prefix injection",
Params: nil,
},
}

if contentType, set := object.ContentType().Get(); !set || contentType != ndn.ContentTypePrefixInjection {
log.Warn(dv, "Prefix Injection Object does not have the correct content type",
"contentType", object.ContentType())
return resError
}

// TODO: reject already seen injections (using version)
var prefix enc.Name
found := false

for i, c := range object.Name() {
if c.IsKeyword("inject") {
prefix = object.Name().Prefix(i)
found = true
break
}
}

if !found {
log.Warn(dv, "Prefix Injection Object name not in correct format", "name", object.Name())
return resError
}

piWire := object.Content()
params, err := tlv.ParsePrefixInjectionInnerContent(enc.NewWireView(piWire), true)
if err != nil {
log.Warn(dv, "Failed to parse prefix injection object", "err", err)
return resError
}

var shouldRemove bool
var cost uint64
if params.ExpirationPeriod == 0 {
// Remove the RIB entry
shouldRemove = true
cost = config.CostInfinity
} else {
// Add or update RIB entry
shouldRemove = false
cost = params.Cost.GetOr(0)
if cost > config.CostInfinity {
log.Warn(dv, "Invalid Cost value", "Cost", cost)
return resError
}
}

if shouldRemove {
dv.nfdc.Exec(nfdc.NfdMgmtCmd{
Module: "rib",
Cmd: "unregister",
Args: &mgmt.ControlArgs{
Name: prefix,
FaceId: optional.Some(faceId),
},
Retries: 3,
})
} else {
dv.nfdc.Exec(nfdc.NfdMgmtCmd{
Module: "rib",
Cmd: "register",
Args: &mgmt.ControlArgs{
Name: prefix,
FaceId: optional.Some(faceId),
Origin: optional.Some(config.PrefixInjOrigin),
Cost: optional.Some(cost),
},
Retries: 3,
})
}

dv.mutex.Lock()
defer dv.mutex.Unlock()

if shouldRemove {
dv.pfx.Withdraw(prefix, faceId)
} else {
dv.pfx.Announce(prefix, faceId, cost)
}

return &mgmt.ControlResponse{
Val: &mgmt.ControlResponseVal{
StatusCode: 200,
StatusText: "Prefix Injection command successful",
Params: &mgmt.ControlArgs{
Name: prefix,
FaceId: optional.Some(faceId),
Origin: optional.Some(config.PrefixInjOrigin),
Cost: optional.Some(cost),
},
},
}
}
Loading
Loading