summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAnatole Denis <natolumin@unverle.fr>2020-08-05 20:15:27 +0200
committerAnatole Denis <Natolumin@users.noreply.github.com>2020-08-06 23:07:22 +0200
commit3f14f7f8bd9cd69eb47abcc82768498a03d1e74a (patch)
tree41d1af21c245d428dd62f53b8c10417521ba332a
parent5197d6147699e3f568d2f1dca695a9aec3cb9f23 (diff)
dhcpv6: Handle IA_TA options
This creates support for IA_TA options, based on and reusing the blocks from IA_NA, to which it is extremely similar Signed-off-by: Anatole Denis <natolumin@unverle.fr>
-rw-r--r--dhcpv6/dhcpv6message.go19
-rw-r--r--dhcpv6/modifiers.go17
-rw-r--r--dhcpv6/option_temporaryaddress.go48
-rw-r--r--dhcpv6/option_temporaryaddress_test.go123
-rw-r--r--dhcpv6/options.go2
5 files changed, 209 insertions, 0 deletions
diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go
index a4da0a4..f3ed4ef 100644
--- a/dhcpv6/dhcpv6message.go
+++ b/dhcpv6/dhcpv6message.go
@@ -67,6 +67,25 @@ func (mo MessageOptions) OneIANA() *OptIANA {
return ianas[0]
}
+// IATA returns all Identity Association for Temporary Address options.
+func (mo MessageOptions) IATA() []*OptIATA {
+ opts := mo.Get(OptionIANA)
+ var iatas []*OptIATA
+ for _, o := range opts {
+ iatas = append(iatas, o.(*OptIATA))
+ }
+ return iatas
+}
+
+// OneIATA returns the first IATA option.
+func (mo MessageOptions) OneIATA() *OptIATA {
+ iatas := mo.IATA()
+ if len(iatas) == 0 {
+ return nil
+ }
+ return iatas[0]
+}
+
// IAPD returns all Identity Association for Prefix Delegation options.
func (mo MessageOptions) IAPD() []*OptIAPD {
opts := mo.Get(OptionIAPD)
diff --git a/dhcpv6/modifiers.go b/dhcpv6/modifiers.go
index 8099795..fbbad23 100644
--- a/dhcpv6/modifiers.go
+++ b/dhcpv6/modifiers.go
@@ -90,6 +90,23 @@ func WithIAID(iaid [4]byte) Modifier {
}
}
+// WithIATA adds or updates an OptIANA option with the provided IAAddress
+// options
+func WithIATA(addrs ...OptIAAddress) Modifier {
+ return func(d DHCPv6) {
+ if msg, ok := d.(*Message); ok {
+ iata := msg.Options.OneIATA()
+ if iata == nil {
+ iata = &OptIATA{}
+ }
+ for _, addr := range addrs {
+ iata.Options.Add(&addr)
+ }
+ msg.UpdateOption(iata)
+ }
+ }
+}
+
// WithDNS adds or updates an OptDNSRecursiveNameServer
func WithDNS(dnses ...net.IP) Modifier {
return WithOption(OptDNS(dnses...))
diff --git a/dhcpv6/option_temporaryaddress.go b/dhcpv6/option_temporaryaddress.go
new file mode 100644
index 0000000..3aff1d4
--- /dev/null
+++ b/dhcpv6/option_temporaryaddress.go
@@ -0,0 +1,48 @@
+package dhcpv6
+
+import (
+ "fmt"
+
+ "github.com/u-root/u-root/pkg/uio"
+)
+
+// OptIATA implements the identity association for non-temporary addresses
+// option.
+//
+// This module defines the OptIATA structure.
+// https://www.ietf.org/rfc/rfc8415.txt
+type OptIATA struct {
+ IaId [4]byte
+ Options IdentityOptions
+}
+
+// Code returns the option code for an IA_TA
+func (op *OptIATA) Code() OptionCode {
+ return OptionIATA
+}
+
+// ToBytes serializes IATA to DHCPv6 bytes.
+func (op *OptIATA) ToBytes() []byte {
+ buf := uio.NewBigEndianBuffer(nil)
+ buf.WriteBytes(op.IaId[:])
+ buf.WriteBytes(op.Options.ToBytes())
+ return buf.Data()
+}
+
+func (op *OptIATA) String() string {
+ return fmt.Sprintf("IATA: {IAID=%v, options=%v}",
+ op.IaId, op.Options)
+}
+
+// ParseOptIATA builds an OptIATA structure from a sequence of bytes. The
+// input data does not include option code and length bytes.
+func ParseOptIATA(data []byte) (*OptIATA, error) {
+ var opt OptIATA
+ buf := uio.NewBigEndianBuffer(data)
+ buf.ReadBytes(opt.IaId[:])
+
+ if err := opt.Options.FromBytes(buf.ReadAll()); err != nil {
+ return nil, err
+ }
+ return &opt, buf.FinError()
+}
diff --git a/dhcpv6/option_temporaryaddress_test.go b/dhcpv6/option_temporaryaddress_test.go
new file mode 100644
index 0000000..de7fe39
--- /dev/null
+++ b/dhcpv6/option_temporaryaddress_test.go
@@ -0,0 +1,123 @@
+package dhcpv6
+
+import (
+ "net"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestOptIATAParseOptIATA(t *testing.T) {
+ data := []byte{
+ 1, 0, 0, 0, // IAID
+ 0, 5, 0, 0x18, 0x24, 1, 0xdb, 0, 0x30, 0x10, 0xc0, 0x8f, 0xfa, 0xce, 0, 0, 0, 0x44, 0, 0, 0, 0, 0xb2, 0x7a, 0, 0, 0xc0, 0x8a, // options
+ }
+ opt, err := ParseOptIATA(data)
+ require.NoError(t, err)
+ require.Equal(t, OptionIATA, opt.Code())
+}
+
+func TestOptIATAParseOptIATAInvalidLength(t *testing.T) {
+ data := []byte{
+ 1, 0, 0, // truncated IAID
+ }
+ _, err := ParseOptIATA(data)
+ require.Error(t, err)
+}
+
+func TestOptIATAParseOptIATAInvalidOptions(t *testing.T) {
+ data := []byte{
+ 1, 0, 0, 0, // IAID
+ 0, 5, 0, 0x18, 0x24, 1, 0xdb, 0, 0x30, 0x10, 0xc0, 0x8f, 0xfa, 0xce, 0, 0, 0, 0x44, 0, 0, 0, 0, 0xb2, 0x7a, // truncated options
+ }
+ _, err := ParseOptIATA(data)
+ require.Error(t, err)
+}
+
+func TestOptIATAGetOneOption(t *testing.T) {
+ oaddr := &OptIAAddress{
+ IPv6Addr: net.ParseIP("::1"),
+ }
+ opt := OptIATA{
+ Options: IdentityOptions{[]Option{&OptStatusCode{}, oaddr}},
+ }
+ require.Equal(t, oaddr, opt.Options.OneAddress())
+}
+
+func TestOptIATAAddOption(t *testing.T) {
+ opt := OptIATA{}
+ opt.Options.Add(OptElapsedTime(0))
+ require.Equal(t, 1, len(opt.Options.Options))
+ require.Equal(t, OptionElapsedTime, opt.Options.Options[0].Code())
+}
+
+func TestOptIATAGetOneOptionMissingOpt(t *testing.T) {
+ oaddr := &OptIAAddress{
+ IPv6Addr: net.ParseIP("::1"),
+ }
+ opt := OptIATA{
+ Options: IdentityOptions{[]Option{&OptStatusCode{}, oaddr}},
+ }
+ require.Equal(t, nil, opt.Options.GetOne(OptionDNSRecursiveNameServer))
+}
+
+func TestOptIATADelOption(t *testing.T) {
+ optiaaddr := OptIAAddress{}
+ optsc := OptStatusCode{}
+
+ iana1 := OptIATA{
+ Options: IdentityOptions{[]Option{
+ &optsc,
+ &optiaaddr,
+ &optiaaddr,
+ }},
+ }
+ iana1.Options.Del(OptionIAAddr)
+ require.Equal(t, iana1.Options.Options, Options{&optsc})
+
+ iana2 := OptIATA{
+ Options: IdentityOptions{[]Option{
+ &optiaaddr,
+ &optsc,
+ &optiaaddr,
+ }},
+ }
+ iana2.Options.Del(OptionIAAddr)
+ require.Equal(t, iana2.Options.Options, Options{&optsc})
+}
+
+func TestOptIATAToBytes(t *testing.T) {
+ opt := OptIATA{
+ IaId: [4]byte{1, 2, 3, 4},
+ Options: IdentityOptions{[]Option{
+ OptElapsedTime(10 * time.Millisecond),
+ }},
+ }
+ expected := []byte{
+ 1, 2, 3, 4, // IA ID
+ 0, 8, 0, 2, 0x00, 0x01,
+ }
+ require.Equal(t, expected, opt.ToBytes())
+}
+
+func TestOptIATAString(t *testing.T) {
+ data := []byte{
+ 1, 0, 0, 0, // IAID
+ 0, 5, 0, 0x18, 0x24, 1, 0xdb, 0, 0x30, 0x10, 0xc0, 0x8f, 0xfa, 0xce, 0, 0, 0, 0x44, 0, 0, 0, 0, 0xb2, 0x7a, 0, 0, 0xc0, 0x8a, // options
+ }
+ opt, err := ParseOptIATA(data)
+ require.NoError(t, err)
+
+ str := opt.String()
+ require.Contains(
+ t, str,
+ "IAID=[1 0 0 0]",
+ "String() should return the IAID",
+ )
+ require.Contains(
+ t, str,
+ "options={",
+ "String() should return a list of options",
+ )
+}
diff --git a/dhcpv6/options.go b/dhcpv6/options.go
index a3afde0..16bc8d7 100644
--- a/dhcpv6/options.go
+++ b/dhcpv6/options.go
@@ -45,6 +45,8 @@ func ParseOption(code OptionCode, optData []byte) (Option, error) {
opt, err = parseOptServerID(optData)
case OptionIANA:
opt, err = ParseOptIANA(optData)
+ case OptionIATA:
+ opt, err = ParseOptIATA(optData)
case OptionIAAddr:
opt, err = ParseOptIAAddress(optData)
case OptionORO: