Skip to content

Commit

Permalink
ndp: add RAFlagsExtension option
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Layher <mdlayher@gmail.com>
  • Loading branch information
mdlayher committed Jan 17, 2024
1 parent b794f5a commit 239470e
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 0 deletions.
2 changes: 2 additions & 0 deletions internal/ndpcmd/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ func optStr(o ndp.Option) string {
servers := strings.Join(ss, ", ")

return fmt.Sprintf("recursive DNS servers: lifetime: %s, servers: %s", o.Lifetime, servers)
case *ndp.RAFlagsExtension:
return fmt.Sprintf("RA flags extension: [%# 02x]", o.Flags)
case *ndp.DNSSearchList:
return fmt.Sprintf("DNS search list: lifetime: %s, domain names: %s", o.Lifetime, strings.Join(o.DomainNames, ", "))
case *ndp.CaptivePortal:
Expand Down
68 changes: 68 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
optNonce = 14
optRouteInformation = 24
optRDNSS = 25
optRAFlagsExtension = 26
optDNSSL = 31
optCaptivePortal = 37
)
Expand Down Expand Up @@ -766,6 +767,71 @@ func (cp *CaptivePortal) unmarshal(b []byte) error {
return nil
}

// A RAFlagsExtension is a Router Advertisement Flags Extension (or Expansion)
// option, as described in RFC 5175, Section 4.
type RAFlagsExtension struct {
Flags RAFlags
}

// RAFlags is a bitmask of Router Advertisement flags contained within an
// RAFlagsExtension.
type RAFlags []byte

// Code implements Option.
func (*RAFlagsExtension) Code() byte { return optRAFlagsExtension }

func (ra *RAFlagsExtension) marshal() ([]byte, error) {
// "MUST NOT be added to a Router Advertisement message if no flags in the
// option are set."
//
// TODO(mdlayher): replace with slices.IndexFunc when we raise the minimum
// Go version.
var found bool
for _, b := range ra.Flags {
if b != 0x00 {
found = true
break
}
}
if !found {
return nil, errors.New("ndp: RA flags extension requires one or more flags to be set")
}

// Enforce the option size matches the next unit of 8 bytes including 2
// bytes for code and length.
l := len(ra.Flags)
if r := (l + 2) % 8; r != 0 {
return nil, errors.New("ndp: RA flags extension length is invalid")
}

value := make([]byte, l)
copy(value, ra.Flags)

raw := &RawOption{
Type: ra.Code(),
Length: (uint8(l) + 2) / 8,
Value: value,
}

return raw.marshal()
}

func (ra *RAFlagsExtension) unmarshal(b []byte) error {
raw := new(RawOption)
if err := raw.unmarshal(b); err != nil {
return err
}

// Don't allow short bytes.
if len(raw.Value) < 6 {
return errors.New("ndp: RA Flags Extension too short")
}

// raw already made a copy.
ra.Flags = raw.Value
return nil
}

// A Nonce is a Nonce option, as described in RFC 3971, Section 5.3.2.
type Nonce struct {
b []byte
Expand Down Expand Up @@ -926,6 +992,8 @@ func parseOptions(b []byte) ([]Option, error) {
o = new(RouteInformation)
case optRDNSS:
o = new(RecursiveDNSServer)
case optRAFlagsExtension:
o = new(RAFlagsExtension)
case optDNSSL:
o = new(DNSSearchList)
case optCaptivePortal:
Expand Down
82 changes: 82 additions & 0 deletions option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func TestOptionMarshalUnmarshal(t *testing.T) {
name: "recursive DNS servers",
subs: rdnssTests(),
},
{
name: "RA flags extension",
subs: raFlagsExtensionTests(),
},
{
name: "DNS search list",
subs: dnsslTests(),
Expand Down Expand Up @@ -276,6 +280,20 @@ func TestOptionUnmarshalError(t *testing.T) {
},
},
},
{
name: "ra flags extension",
o: &RAFlagsExtension{},
subs: []sub{
{
name: "short flags",
bs: [][]byte{
{26, 1},
// Short flags.
ndptest.Zero(5),
},
},
},
},
{
name: "dnssl",
o: &DNSSearchList{},
Expand Down Expand Up @@ -776,6 +794,70 @@ func rdnssTests() []optionSub {
}
}

func raFlagsExtensionTests() []optionSub {
return []optionSub{
{
name: "bad, no flags",
os: []Option{
&RAFlagsExtension{},
},
},
{
name: "bad, zero flags",
os: []Option{
&RAFlagsExtension{
Flags: RAFlags(ndptest.Zero(6)),
},
},
},
{
name: "bad, short padding",
os: []Option{
&RAFlagsExtension{
Flags: RAFlags{
0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
},
},
},
},
{
name: "ok, length 1",
os: []Option{
&RAFlagsExtension{
Flags: RAFlags{0x80, 0x00, 0x00, 0x00, 0x00, 0x00},
},
},
bs: [][]byte{
{26, 1},
// Short values.
{128, 0, 0, 0, 0, 0},
},
ok: true,
},
{
name: "ok, length 2",
os: []Option{
&RAFlagsExtension{
Flags: RAFlags{
0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
},
},
bs: [][]byte{
{26, 2},
// Short values.
{
128, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
},
},
ok: true,
},
}
}

func dnsslTests() []optionSub {
return []optionSub{
{
Expand Down

0 comments on commit 239470e

Please sign in to comment.