summaryrefslogtreecommitdiffhomepage
path: root/dhcpv6/options
diff options
context:
space:
mode:
authorAndrea Barberio <insomniac@slackware.it>2017-11-29 21:25:48 +0000
committerAndrea Barberio <insomniac@slackware.it>2017-12-05 23:32:27 +0000
commit1403bbe04ce275148b601c32e9551b2281110347 (patch)
tree6eacd224c2ea6b6e3865cb774970726809363d46 /dhcpv6/options
Initial commit
Diffstat (limited to 'dhcpv6/options')
-rw-r--r--dhcpv6/options/clientid.go57
-rw-r--r--dhcpv6/options/dnsrecursivenameserver.go59
-rw-r--r--dhcpv6/options/domainsearchlist.go57
-rw-r--r--dhcpv6/options/duid.go100
-rw-r--r--dhcpv6/options/elapsedtime.go52
-rw-r--r--dhcpv6/options/elapsedtime_test.go44
-rw-r--r--dhcpv6/options/iaaddress.go86
-rw-r--r--dhcpv6/options/iaprefix.go98
-rw-r--r--dhcpv6/options/nontemporaryaddress.go86
-rw-r--r--dhcpv6/options/options.go125
-rw-r--r--dhcpv6/options/prefixdelegation.go86
-rw-r--r--dhcpv6/options/requestedoption.go72
-rw-r--r--dhcpv6/options/rfc1035label.go54
-rw-r--r--dhcpv6/options/rfc1035label_test.go66
-rw-r--r--dhcpv6/options/serverid.go57
-rw-r--r--dhcpv6/options/statuscode.go63
-rw-r--r--dhcpv6/options/types.go152
17 files changed, 1314 insertions, 0 deletions
diff --git a/dhcpv6/options/clientid.go b/dhcpv6/options/clientid.go
new file mode 100644
index 0000000..71422d7
--- /dev/null
+++ b/dhcpv6/options/clientid.go
@@ -0,0 +1,57 @@
+package options
+
+// This module defines the OptClientId and DUID structures.
+// https://www.ietf.org/rfc/rfc3315.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+type OptClientId struct {
+ cid Duid
+}
+
+func (op *OptClientId) Code() OptionCode {
+ return OPTION_CLIENTID
+}
+
+func (op *OptClientId) ToBytes() []byte {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_CLIENTID))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ buf = append(buf, op.cid.ToBytes()...)
+ return buf
+}
+
+func (op *OptClientId) ClientID() Duid {
+ return op.cid
+}
+
+func (op *OptClientId) SetClientID(cid Duid) {
+ op.cid = cid
+}
+
+func (op *OptClientId) Length() int {
+ return op.cid.Length()
+}
+
+func (op *OptClientId) String() string {
+ return fmt.Sprintf("OptClientId{cid=%v}", op.cid.String())
+}
+
+// build an OptClientId structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptClientId(data []byte) (*OptClientId, error) {
+ if len(data) < 2 {
+ // at least the DUID type is necessary to continue
+ return nil, fmt.Errorf("Invalid OptClientId data: shorter than 2 bytes")
+ }
+ opt := OptClientId{}
+ cid, err := DuidFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ opt.cid = *cid
+ return &opt, nil
+}
diff --git a/dhcpv6/options/dnsrecursivenameserver.go b/dhcpv6/options/dnsrecursivenameserver.go
new file mode 100644
index 0000000..a28fd1d
--- /dev/null
+++ b/dhcpv6/options/dnsrecursivenameserver.go
@@ -0,0 +1,59 @@
+package options
+
+// This module defines the OptDNSRecursiveNameServer structure.
+// https://www.ietf.org/rfc/rfc3646.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+ "net"
+)
+
+type OptDNSRecursiveNameServer struct {
+ nameServers []net.IP
+}
+
+func (op *OptDNSRecursiveNameServer) Code() OptionCode {
+ return DNS_RECURSIVE_NAME_SERVER
+}
+
+func (op *OptDNSRecursiveNameServer) ToBytes() []byte {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(DNS_RECURSIVE_NAME_SERVER))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ for _, ns := range op.nameServers {
+ buf = append(buf, ns...)
+ }
+ return buf
+}
+
+func (op *OptDNSRecursiveNameServer) NameServers() []net.IP {
+ return op.nameServers
+}
+
+func (op *OptDNSRecursiveNameServer) SetNameServers(ns []net.IP) {
+ op.nameServers = ns
+}
+
+func (op *OptDNSRecursiveNameServer) Length() int {
+ return len(op.nameServers) * net.IPv6len
+}
+
+func (op *OptDNSRecursiveNameServer) String() string {
+ return fmt.Sprintf("OptDNSRecursiveNameServer{nameservers=%v}", op.nameServers)
+}
+
+// build an OptDNSRecursiveNameServer structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptDNSRecursiveNameServer(data []byte) (*OptDNSRecursiveNameServer, error) {
+ if len(data)%2 != 0 {
+ return nil, fmt.Errorf("Invalid OptDNSRecursiveNameServer data: length is not a multiple of 2")
+ }
+ opt := OptDNSRecursiveNameServer{}
+ var nameServers []net.IP
+ for i := 0; i < len(data); i += net.IPv6len {
+ nameServers = append(nameServers, data[i:i+net.IPv6len])
+ }
+ opt.nameServers = nameServers
+ return &opt, nil
+}
diff --git a/dhcpv6/options/domainsearchlist.go b/dhcpv6/options/domainsearchlist.go
new file mode 100644
index 0000000..048384f
--- /dev/null
+++ b/dhcpv6/options/domainsearchlist.go
@@ -0,0 +1,57 @@
+package options
+
+// This module defines the OptDomainSearchList structure.
+// https://www.ietf.org/rfc/rfc3646.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+type OptDomainSearchList struct {
+ domainSearchList []string
+}
+
+func (op *OptDomainSearchList) Code() OptionCode {
+ return DOMAIN_SEARCH_LIST
+}
+
+func (op *OptDomainSearchList) ToBytes() []byte {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(DOMAIN_SEARCH_LIST))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ buf = append(buf, LabelsToBytes(op.domainSearchList)...)
+ return buf
+}
+
+func (op *OptDomainSearchList) DomainSearchList() []string {
+ return op.domainSearchList
+}
+
+func (op *OptDomainSearchList) SetDomainSearchList(dsList []string) {
+ op.domainSearchList = dsList
+}
+
+func (op *OptDomainSearchList) Length() int {
+ var length int
+ for _, label := range op.domainSearchList {
+ length += len(label) + 2 // add the first and the last length bytes
+ }
+ return length
+}
+
+func (op *OptDomainSearchList) String() string {
+ return fmt.Sprintf("OptDomainSearchList{searchlist=%v}", op.domainSearchList)
+}
+
+// build an OptDomainSearchList structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptDomainSearchList(data []byte) (*OptDomainSearchList, error) {
+ opt := OptDomainSearchList{}
+ var err error
+ opt.domainSearchList, err = LabelsFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ return &opt, nil
+}
diff --git a/dhcpv6/options/duid.go b/dhcpv6/options/duid.go
new file mode 100644
index 0000000..0fc3e33
--- /dev/null
+++ b/dhcpv6/options/duid.go
@@ -0,0 +1,100 @@
+package options
+
+import (
+ "encoding/binary"
+ "fmt"
+ "github.com/insomniacslk/dhcp/iana"
+)
+
+type DuidType uint16
+
+const (
+ DUID_LL DuidType = iota
+ DUID_LLT
+ DUID_EN
+)
+
+var DuidTypeToString = map[DuidType]string{
+ DUID_LL: "DUID-LL",
+ DUID_LLT: "DUID-LLT",
+ DUID_EN: "DUID-EN",
+}
+
+type Duid struct {
+ Type DuidType
+ HwType iana.HwTypeType // for DUID-LLT and DUID-LL. Ignored otherwise. RFC 826
+ Time uint32 // for DUID-LLT. Ignored otherwise
+ LinkLayerAddr []byte
+ EnterpriseNumber uint32 // for DUID-EN. Ignored otherwise
+ EnterpriseIdentifier []byte // for DUID-EN. Ignored otherwise
+}
+
+func (d *Duid) Length() int {
+ if d.Type == DUID_LLT || d.Type == DUID_LL {
+ return 8 + len(d.LinkLayerAddr)
+ }
+ if d.Type == DUID_EN {
+ return 6 + len(d.EnterpriseIdentifier)
+ }
+ return 0 // should never happen
+}
+
+func (d *Duid) ToBytes() []byte {
+ if d.Type == DUID_LLT || d.Type == DUID_LL {
+ buf := make([]byte, 8)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(d.Type))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(d.HwType))
+ binary.BigEndian.PutUint32(buf[4:8], d.Time)
+ return append(buf, d.LinkLayerAddr...)
+ } else if d.Type == DUID_EN {
+ buf := make([]byte, 6)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(d.Type))
+ binary.BigEndian.PutUint32(buf[2:6], d.EnterpriseNumber)
+ return append(buf, d.EnterpriseIdentifier...)
+ }
+ return []byte{} // should never happen
+}
+
+func (d *Duid) String() string {
+ dtype := DuidTypeToString[d.Type]
+ if dtype == "" {
+ dtype = "Unknown"
+ }
+ hwtype := iana.HwTypeToString[d.HwType]
+ if hwtype == "" {
+ hwtype = "Unknown"
+ }
+ var hwaddr string
+ if d.HwType == iana.HwTypeEthernet {
+ for _, b := range d.LinkLayerAddr {
+ hwaddr += fmt.Sprintf("%02x:", b)
+ }
+ if len(hwaddr) > 0 && hwaddr[len(hwaddr)-1] == ':' {
+ hwaddr = hwaddr[:len(hwaddr)-1]
+ }
+ }
+ return fmt.Sprintf("DUID{type=%v hwtype=%v hwaddr=%v}", dtype, hwtype, hwaddr)
+}
+
+func DuidFromBytes(data []byte) (*Duid, error) {
+ if len(data) < 2 {
+ return nil, fmt.Errorf("Invalid DUID: shorter than 2 bytes")
+ }
+ d := Duid{}
+ d.Type = DuidType(binary.BigEndian.Uint16(data[0:2]))
+ if d.Type == DUID_LLT || d.Type == DUID_LL {
+ if len(data) < 8 {
+ return nil, fmt.Errorf("Invalid DUID-LL/LLT: shorter than 8 bytes")
+ }
+ d.HwType = iana.HwTypeType(binary.BigEndian.Uint16(data[2:4]))
+ d.Time = binary.BigEndian.Uint32(data[4:8])
+ d.LinkLayerAddr = data[8:]
+ } else if d.Type == DUID_EN {
+ if len(data) < 6 {
+ return nil, fmt.Errorf("Invalid DUID-EN: shorter than 6 bytes")
+ }
+ d.EnterpriseNumber = binary.BigEndian.Uint32(data[2:6])
+ d.EnterpriseIdentifier = data[6:]
+ }
+ return &d, nil
+}
diff --git a/dhcpv6/options/elapsedtime.go b/dhcpv6/options/elapsedtime.go
new file mode 100644
index 0000000..e95185d
--- /dev/null
+++ b/dhcpv6/options/elapsedtime.go
@@ -0,0 +1,52 @@
+package options
+
+// This module defines the OptElapsedTime structure.
+// https://www.ietf.org/rfc/rfc3315.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+type OptElapsedTime struct {
+ elapsedTime uint16
+}
+
+func (op *OptElapsedTime) Code() OptionCode {
+ return OPTION_ELAPSED_TIME
+}
+
+func (op *OptElapsedTime) ToBytes() []byte {
+ buf := make([]byte, 6)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_ELAPSED_TIME))
+ binary.BigEndian.PutUint16(buf[2:4], 2)
+ binary.BigEndian.PutUint16(buf[4:6], uint16(op.elapsedTime))
+ return buf
+}
+
+func (op *OptElapsedTime) ElapsedTime() uint16 {
+ return op.elapsedTime
+}
+
+func (op *OptElapsedTime) SetElapsedTime(elapsedTime uint16) {
+ op.elapsedTime = elapsedTime
+}
+
+func (op *OptElapsedTime) Length() int {
+ return 2
+}
+
+func (op *OptElapsedTime) String() string {
+ return fmt.Sprintf("OptElapsedTime{elapsedtime=%v}", op.elapsedTime)
+}
+
+// build an OptElapsedTime structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptElapsedTime(data []byte) (*OptElapsedTime, error) {
+ opt := OptElapsedTime{}
+ if len(data) != 2 {
+ return nil, fmt.Errorf("Invalid elapsed time data length. Expected 2 bytes, got %v", len(data))
+ }
+ opt.elapsedTime = binary.BigEndian.Uint16(data)
+ return &opt, nil
+}
diff --git a/dhcpv6/options/elapsedtime_test.go b/dhcpv6/options/elapsedtime_test.go
new file mode 100644
index 0000000..402d62b
--- /dev/null
+++ b/dhcpv6/options/elapsedtime_test.go
@@ -0,0 +1,44 @@
+package options
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestOptElapsedTime(t *testing.T) {
+ opt, err := ParseOptElapsedTime([]byte{0xaa, 0xbb})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if optLen := opt.Length(); optLen != 2 {
+ t.Fatalf("Invalid length. Expected 2, got %v", optLen)
+ }
+ if elapsedTime := opt.ElapsedTime(); elapsedTime != 0xaabb {
+ t.Fatalf("Invalid elapsed time. Expected 0xaabb, got %v", elapsedTime)
+ }
+}
+
+func TestOptElapsedTimeToBytes(t *testing.T) {
+ opt := OptElapsedTime{}
+ expected := []byte{0, 8, 0, 2, 0, 0}
+ if toBytes := opt.ToBytes(); !bytes.Equal(expected, toBytes) {
+ t.Fatalf("Invalid ToBytes output. Expected %v, got %v", expected, toBytes)
+ }
+}
+
+func TestOptElapsedTimeSetGetElapsedTime(t *testing.T) {
+ opt := OptElapsedTime{}
+ opt.SetElapsedTime(10)
+ if elapsedTime := opt.ElapsedTime(); elapsedTime != 10 {
+ t.Fatalf("Invalid elapsed time. Expected 10, got %v", elapsedTime)
+ }
+}
+
+func TestOptElapsedTimeString(t *testing.T) {
+ opt := OptElapsedTime{}
+ opt.SetElapsedTime(10)
+ expected := "OptElapsedTime{elapsedtime=10}"
+ if optString := opt.String(); optString != expected {
+ t.Fatalf("Invalid elapsed time string. Expected %v, got %v", expected, optString)
+ }
+}
diff --git a/dhcpv6/options/iaaddress.go b/dhcpv6/options/iaaddress.go
new file mode 100644
index 0000000..24f28ad
--- /dev/null
+++ b/dhcpv6/options/iaaddress.go
@@ -0,0 +1,86 @@
+package options
+
+// This module defines the OptIAAddress structure.
+// https://www.ietf.org/rfc/rfc3633.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+ "net"
+)
+
+type OptIAAddress struct {
+ ipv6Addr [16]byte
+ preferredLifetime uint32
+ validLifetime uint32
+ options []byte
+}
+
+func (op *OptIAAddress) Code() OptionCode {
+ return OPTION_IAADDR
+}
+
+func (op *OptIAAddress) ToBytes() []byte {
+ buf := make([]byte, 28)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IAADDR))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ copy(buf[4:20], op.ipv6Addr[:])
+ binary.BigEndian.PutUint32(buf[20:24], op.preferredLifetime)
+ binary.BigEndian.PutUint32(buf[24:28], op.validLifetime)
+ buf = append(buf, op.options...)
+ return buf
+}
+
+func (op *OptIAAddress) IPv6Addr() []byte {
+ return op.ipv6Addr[:]
+}
+
+func (op *OptIAAddress) SetIPv6Addr(addr [16]byte) {
+ op.ipv6Addr = addr
+}
+
+func (op *OptIAAddress) PreferredLifetime() uint32 {
+ return op.preferredLifetime
+}
+
+func (op *OptIAAddress) SetPreferredLifetime(pl uint32) {
+ op.preferredLifetime = pl
+}
+
+func (op *OptIAAddress) ValidLifetime() uint32 {
+ return op.validLifetime
+}
+
+func (op *OptIAAddress) SetValidLifetime(vl uint32) {
+ op.validLifetime = vl
+}
+func (op *OptIAAddress) Options() []byte {
+ return op.options
+}
+
+func (op *OptIAAddress) SetOptions(options []byte) {
+ op.options = options
+}
+
+func (op *OptIAAddress) Length() int {
+ return 24 + len(op.options)
+}
+
+func (op *OptIAAddress) String() string {
+ return fmt.Sprintf("OptIAAddress{ipv6addr=%v, preferredlifetime=%v, validlifetime=%v, options=%v}",
+ net.IP(op.ipv6Addr[:]), op.preferredLifetime, op.validLifetime, op.options)
+}
+
+// build an OptIAAddress structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptIAAddress(data []byte) (*OptIAAddress, error) {
+ opt := OptIAAddress{}
+ if len(data) < 24 {
+ return nil, fmt.Errorf("Invalid IA Address data length. Expected at least 24 bytes, got %v", len(data))
+ }
+ copy(opt.ipv6Addr[:], data[:16])
+ opt.preferredLifetime = binary.BigEndian.Uint32(data[16:20])
+ opt.validLifetime = binary.BigEndian.Uint32(data[20:24])
+ copy(opt.options, data[24:])
+ return &opt, nil
+}
diff --git a/dhcpv6/options/iaprefix.go b/dhcpv6/options/iaprefix.go
new file mode 100644
index 0000000..d91df2a
--- /dev/null
+++ b/dhcpv6/options/iaprefix.go
@@ -0,0 +1,98 @@
+package options
+
+// This module defines the OptIAPrefix structure.
+// https://www.ietf.org/rfc/rfc3633.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+ "net"
+)
+
+type OptIAPrefix struct {
+ preferredLifetime uint32
+ validLifetime uint32
+ prefixLength byte
+ ipv6Prefix [16]byte
+ options []byte
+}
+
+func (op *OptIAPrefix) Code() OptionCode {
+ return OPTION_IAPREFIX
+}
+
+func (op *OptIAPrefix) ToBytes() []byte {
+ buf := make([]byte, 25)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IAPREFIX))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ binary.BigEndian.PutUint32(buf[4:8], op.preferredLifetime)
+ binary.BigEndian.PutUint32(buf[8:12], op.validLifetime)
+ buf = append(buf, op.prefixLength)
+ buf = append(buf, op.ipv6Prefix[:]...)
+ buf = append(buf, op.options...)
+ return buf
+}
+
+func (op *OptIAPrefix) PreferredLifetime() uint32 {
+ return op.preferredLifetime
+}
+
+func (op *OptIAPrefix) SetPreferredLifetime(pl uint32) {
+ op.preferredLifetime = pl
+}
+
+func (op *OptIAPrefix) ValidLifetime() uint32 {
+ return op.validLifetime
+}
+
+func (op *OptIAPrefix) SetValidLifetime(vl uint32) {
+ op.validLifetime = vl
+}
+
+func (op *OptIAPrefix) PrefixLength() byte {
+ return op.prefixLength
+}
+
+func (op *OptIAPrefix) SetPrefixLength(pl byte) {
+ op.prefixLength = pl
+}
+
+func (op *OptIAPrefix) IPv6Prefix() []byte {
+ return op.ipv6Prefix[:]
+}
+
+func (op *OptIAPrefix) SetIPv6Prefix(p [16]byte) {
+ op.ipv6Prefix = p
+}
+
+func (op *OptIAPrefix) Options() []byte {
+ return op.options
+}
+
+func (op *OptIAPrefix) SetOptions(options []byte) {
+ op.options = options
+}
+
+func (op *OptIAPrefix) Length() int {
+ return 25 + len(op.options)
+}
+
+func (op *OptIAPrefix) String() string {
+ return fmt.Sprintf("OptIAPrefix{preferredlifetime=%v, validlifetime=%v, prefixlength=%v, ipv6prefix=%v, options=%v}",
+ op.preferredLifetime, op.validLifetime, op.prefixLength, net.IP(op.ipv6Prefix[:]), op.options)
+}
+
+// build an OptIAPrefix structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptIAPrefix(data []byte) (*OptIAPrefix, error) {
+ opt := OptIAPrefix{}
+ if len(data) < 12 {
+ return nil, fmt.Errorf("Invalid IA for Prefix Delegation data length. Expected at least 12 bytes, got %v", len(data))
+ }
+ opt.preferredLifetime = binary.BigEndian.Uint32(data[:4])
+ opt.validLifetime = binary.BigEndian.Uint32(data[4:8])
+ opt.prefixLength = data[9]
+ copy(opt.ipv6Prefix[:], data[9:17])
+ copy(opt.options, data[17:])
+ return &opt, nil
+}
diff --git a/dhcpv6/options/nontemporaryaddress.go b/dhcpv6/options/nontemporaryaddress.go
new file mode 100644
index 0000000..703b8cc
--- /dev/null
+++ b/dhcpv6/options/nontemporaryaddress.go
@@ -0,0 +1,86 @@
+package options
+
+// This module defines the OptIANA structure.
+// https://www.ietf.org/rfc/rfc3633.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+type OptIANA struct {
+ iaId [4]byte
+ t1 uint32
+ t2 uint32
+ options []byte
+}
+
+func (op *OptIANA) Code() OptionCode {
+ return OPTION_IA_NA
+}
+
+func (op *OptIANA) ToBytes() []byte {
+ buf := make([]byte, 16)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IA_NA))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ copy(buf[4:8], op.iaId[:])
+ binary.BigEndian.PutUint32(buf[8:12], op.t1)
+ binary.BigEndian.PutUint32(buf[12:16], op.t2)
+ buf = append(buf, op.options...)
+ return buf
+}
+
+func (op *OptIANA) IAID() []byte {
+ return op.iaId[:]
+}
+
+func (op *OptIANA) SetIAID(iaId [4]byte) {
+ op.iaId = iaId
+}
+
+func (op *OptIANA) T1() uint32 {
+ return op.t1
+}
+
+func (op *OptIANA) SetT1(t1 uint32) {
+ op.t1 = t1
+}
+
+func (op *OptIANA) T2() uint32 {
+ return op.t2
+}
+
+func (op *OptIANA) SetT2(t2 uint32) {
+ op.t2 = t2
+}
+
+func (op *OptIANA) Options() []byte {
+ return op.options
+}
+
+func (op *OptIANA) SetOptions(options []byte) {
+ op.options = options
+}
+
+func (op *OptIANA) Length() int {
+ return 12 + len(op.options)
+}
+
+func (op *OptIANA) String() string {
+ return fmt.Sprintf("OptIANA{IAID=%v, t1=%v, t2=%v, options=%v}",
+ op.iaId, op.t1, op.t2, op.options)
+}
+
+// build an OptIANA structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptIANA(data []byte) (*OptIANA, error) {
+ opt := OptIANA{}
+ if len(data) < 12 {
+ return nil, fmt.Errorf("Invalid IA for Non-temporary Addresses data length. Expected at least 12 bytes, got %v", len(data))
+ }
+ copy(opt.iaId[:], data[:4])
+ opt.t1 = binary.BigEndian.Uint32(data[4:8])
+ opt.t2 = binary.BigEndian.Uint32(data[8:12])
+ copy(opt.options, data[12:])
+ return &opt, nil
+}
diff --git a/dhcpv6/options/options.go b/dhcpv6/options/options.go
new file mode 100644
index 0000000..6b587dd
--- /dev/null
+++ b/dhcpv6/options/options.go
@@ -0,0 +1,125 @@
+package options
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+type OptionCode uint16
+
+type Option interface {
+ Code() OptionCode
+ ToBytes() []byte
+ Length() int
+ String() string
+}
+
+type OptionGeneric struct {
+ OptionCode OptionCode
+ OptionData []byte
+}
+
+func (og *OptionGeneric) Code() OptionCode {
+ return og.OptionCode
+}
+
+func (og *OptionGeneric) ToBytes() []byte {
+ var ret []byte
+ codeBytes := make([]byte, 2)
+ binary.BigEndian.PutUint16(codeBytes, uint16(og.OptionCode))
+ ret = append(ret, codeBytes...)
+ lengthBytes := make([]byte, 2)
+ binary.BigEndian.PutUint16(lengthBytes, uint16(len(og.OptionData)))
+ ret = append(ret, lengthBytes...)
+ ret = append(ret, og.OptionData...)
+ return ret
+}
+
+func (og *OptionGeneric) String() string {
+ code, ok := OptionCodeToString[og.OptionCode]
+ if !ok {
+ code = "UnknownOption"
+ }
+ return fmt.Sprintf("%v -> %v", code, og.OptionData)
+}
+
+func (og *OptionGeneric) Length() int {
+ return len(og.OptionData)
+}
+
+func ParseOption(dataStart []byte) (Option, error) {
+ // Parse a sequence of bytes as a single DHCPv6 option.
+ // Returns the option structure, or an error if any.
+ if len(dataStart) < 4 {
+ return nil, fmt.Errorf("Invalid DHCPv6 option: less than 4 bytes")
+ }
+ code := OptionCode(binary.BigEndian.Uint16(dataStart[:2]))
+ length := int(binary.BigEndian.Uint16(dataStart[2:4]))
+ if len(dataStart) < length+4 {
+ return nil, fmt.Errorf("Invalid option length. Declared %v, actual %v",
+ length, len(dataStart)-4,
+ )
+ }
+ var (
+ err error
+ opt Option
+ )
+ optData := dataStart[4 : 4+length]
+ switch code {
+ case OPTION_CLIENTID:
+ opt, err = ParseOptClientId(optData)
+ case OPTION_SERVERID:
+ opt, err = ParseOptServerId(optData)
+ case OPTION_ELAPSED_TIME:
+ opt, err = ParseOptElapsedTime(optData)
+ case OPTION_ORO:
+ opt, err = ParseOptRequestedOption(optData)
+ case DNS_RECURSIVE_NAME_SERVER:
+ opt, err = ParseOptDNSRecursiveNameServer(optData)
+ case DOMAIN_SEARCH_LIST:
+ opt, err = ParseOptDomainSearchList(optData)
+ case OPTION_IA_NA:
+ opt, err = ParseOptIANA(optData)
+ case OPTION_IA_PD:
+ opt, err = ParseOptIAForPrefixDelegation(optData)
+ case OPTION_IAADDR:
+ opt, err = ParseOptIAAddress(optData)
+ case OPTION_IAPREFIX:
+ opt, err = ParseOptIAPrefix(optData)
+ case OPTION_STATUS_CODE:
+ opt, err = ParseOptStatusCode(optData)
+ default:
+ opt = &OptionGeneric{OptionCode: code, OptionData: optData}
+ }
+ if err != nil {
+ return nil, err
+ }
+ return opt, nil
+}
+
+func FromBytes(data []byte) ([]Option, error) {
+ // Parse a sequence of bytes until the end and build a list of options from
+ // it. Returns an error if any invalid option or length is found.
+ if len(data) < 4 {
+ // cannot be shorter than option code (2 bytes) + length (2 bytes)
+ return nil, fmt.Errorf("Invalid options: shorter than 4 bytes")
+ }
+ options := make([]Option, 0, 10)
+ idx := 0
+ for {
+ if idx == len(data) {
+ break
+ }
+ if idx > len(data) {
+ // this should never happen
+ return nil, fmt.Errorf("Error: reading past the end of options")
+ }
+ opt, err := ParseOption(data[idx:])
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, opt)
+ idx += opt.Length() + 4 // 4 bytes for type + length
+ }
+ return options, nil
+}
diff --git a/dhcpv6/options/prefixdelegation.go b/dhcpv6/options/prefixdelegation.go
new file mode 100644
index 0000000..1c02433
--- /dev/null
+++ b/dhcpv6/options/prefixdelegation.go
@@ -0,0 +1,86 @@
+package options
+
+// This module defines the OptIAForPrefixDelegation structure.
+// https://www.ietf.org/rfc/rfc3633.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+type OptIAForPrefixDelegation struct {
+ iaId [4]byte
+ t1 uint32
+ t2 uint32
+ options []byte
+}
+
+func (op *OptIAForPrefixDelegation) Code() OptionCode {
+ return OPTION_IA_PD
+}
+
+func (op *OptIAForPrefixDelegation) ToBytes() []byte {
+ buf := make([]byte, 16)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IA_PD))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ copy(buf[4:8], op.iaId[:])
+ binary.BigEndian.PutUint32(buf[8:12], op.t1)
+ binary.BigEndian.PutUint32(buf[12:16], op.t2)
+ buf = append(buf, op.options...)
+ return buf
+}
+
+func (op *OptIAForPrefixDelegation) IAID() []byte {
+ return op.iaId[:]
+}
+
+func (op *OptIAForPrefixDelegation) SetIAID(iaId [4]byte) {
+ op.iaId = iaId
+}
+
+func (op *OptIAForPrefixDelegation) T1() uint32 {
+ return op.t1
+}
+
+func (op *OptIAForPrefixDelegation) SetT1(t1 uint32) {
+ op.t1 = t1
+}
+
+func (op *OptIAForPrefixDelegation) T2() uint32 {
+ return op.t2
+}
+
+func (op *OptIAForPrefixDelegation) SetT2(t2 uint32) {
+ op.t2 = t2
+}
+
+func (op *OptIAForPrefixDelegation) Options() []byte {
+ return op.options
+}
+
+func (op *OptIAForPrefixDelegation) SetOptions(options []byte) {
+ op.options = options
+}
+
+func (op *OptIAForPrefixDelegation) Length() int {
+ return 12 + len(op.options)
+}
+
+func (op *OptIAForPrefixDelegation) String() string {
+ return fmt.Sprintf("OptIAForPrefixDelegation{IAID=%v, t1=%v, t2=%v, options=%v}",
+ op.iaId, op.t1, op.t2, op.options)
+}
+
+// build an OptIAForPrefixDelegation structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptIAForPrefixDelegation(data []byte) (*OptIAForPrefixDelegation, error) {
+ opt := OptIAForPrefixDelegation{}
+ if len(data) < 12 {
+ return nil, fmt.Errorf("Invalid IA for Prefix Delegation data length. Expected at least 12 bytes, got %v", len(data))
+ }
+ copy(opt.iaId[:], data[:4])
+ opt.t1 = binary.BigEndian.Uint32(data[4:8])
+ opt.t2 = binary.BigEndian.Uint32(data[8:12])
+ opt.options = append(opt.options, data[12:]...)
+ return &opt, nil
+}
diff --git a/dhcpv6/options/requestedoption.go b/dhcpv6/options/requestedoption.go
new file mode 100644
index 0000000..88e9ff3
--- /dev/null
+++ b/dhcpv6/options/requestedoption.go
@@ -0,0 +1,72 @@
+package options
+
+// This module defines the OptRequestedOption structure.
+// https://www.ietf.org/rfc/rfc3315.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+type OptRequestedOption struct {
+ requestedOptions []OptionCode
+}
+
+func (op *OptRequestedOption) Code() OptionCode {
+ return OPTION_ORO
+}
+
+func (op *OptRequestedOption) ToBytes() []byte {
+ buf := make([]byte, 4)
+ roBytes := make([]byte, 2)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_ORO))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ for _, ro := range op.requestedOptions {
+ binary.BigEndian.PutUint16(roBytes, uint16(ro))
+ buf = append(buf, roBytes...)
+ }
+ return buf
+}
+
+func (op *OptRequestedOption) RequestedOptions() []OptionCode {
+ return op.requestedOptions
+}
+
+func (op *OptRequestedOption) SetRequestedOptions(opts []OptionCode) {
+ op.requestedOptions = opts
+}
+
+func (op *OptRequestedOption) Length() int {
+ return len(op.requestedOptions) * 2
+}
+
+func (op *OptRequestedOption) String() string {
+ roString := "["
+ for idx, code := range op.requestedOptions {
+ if name, ok := OptionCodeToString[OptionCode(code)]; ok {
+ roString += name
+ } else {
+ roString += "Unknown"
+ }
+ if idx < len(op.requestedOptions)-1 {
+ roString += ", "
+ }
+ }
+ roString += "]"
+ return fmt.Sprintf("OptRequestedOption{options=%v}", roString)
+}
+
+// build an OptRequestedOption structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptRequestedOption(data []byte) (*OptRequestedOption, error) {
+ if len(data)%2 != 0 {
+ return nil, fmt.Errorf("Invalid OptRequestedOption data: length is not a multiple of 2")
+ }
+ opt := OptRequestedOption{}
+ var rOpts []OptionCode
+ for i := 0; i < len(data); i += 2 {
+ rOpts = append(rOpts, OptionCode(binary.BigEndian.Uint16(data[i:i+2])))
+ }
+ opt.requestedOptions = rOpts
+ return &opt, nil
+}
diff --git a/dhcpv6/options/rfc1035label.go b/dhcpv6/options/rfc1035label.go
new file mode 100644
index 0000000..0d2cff3
--- /dev/null
+++ b/dhcpv6/options/rfc1035label.go
@@ -0,0 +1,54 @@
+package options
+
+import (
+ "fmt"
+ "strings"
+)
+
+func LabelsFromBytes(buf []byte) ([]string, error) {
+ var (
+ pos = 0
+ domains = make([]string, 0)
+ label = ""
+ )
+ for {
+ if pos >= len(buf) {
+ return domains, nil
+ }
+ length := int(buf[pos])
+ pos++
+ if length == 0 {
+ domains = append(domains, label)
+ label = ""
+ }
+ if len(buf)-pos < length {
+ return nil, fmt.Errorf("DomainNamesFromBytes: invalid short label length")
+ }
+ if label != "" {
+ label += "."
+ }
+ label += string(buf[pos : pos+length])
+ pos += length
+ }
+ return domains, nil
+}
+
+func LabelToBytes(label string) []byte {
+ var encodedLabel []byte
+ if len(label) == 0 {
+ return []byte{0}
+ }
+ for _, part := range strings.Split(label, ".") {
+ encodedLabel = append(encodedLabel, byte(len(part)))
+ encodedLabel = append(encodedLabel, []byte(part)...)
+ }
+ return append(encodedLabel, 0)
+}
+
+func LabelsToBytes(labels []string) []byte {
+ var encodedLabels []byte
+ for _, label := range labels {
+ encodedLabels = append(encodedLabels, LabelToBytes(label)...)
+ }
+ return encodedLabels
+}
diff --git a/dhcpv6/options/rfc1035label_test.go b/dhcpv6/options/rfc1035label_test.go
new file mode 100644
index 0000000..4e1debb
--- /dev/null
+++ b/dhcpv6/options/rfc1035label_test.go
@@ -0,0 +1,66 @@
+package options
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestLabelsFromBytes(t *testing.T) {
+ labels, err := LabelsFromBytes([]byte{
+ 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e',
+ 0x2, 'i', 't',
+ 0x0,
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(labels) != 1 {
+ t.Fatalf("Invalid labels length. Expected: 1, got: %v", len(labels))
+ }
+ if labels[0] != "slackware.it" {
+ t.Fatalf("Invalid label. Expected: %v, got: %v'", "slackware.it", labels[0])
+ }
+}
+
+func TestLabelsFromBytesZeroLength(t *testing.T) {
+ labels, err := LabelsFromBytes([]byte{})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(labels) != 0 {
+ t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels))
+ }
+}
+
+func TestLabelsFromBytesInvalidLength(t *testing.T) {
+ labels, err := LabelsFromBytes([]byte{0x3, 0xaa, 0xbb}) // short length
+ if err == nil {
+ t.Fatal("Expected error, got nil")
+ }
+ if len(labels) != 0 {
+ t.Fatalf("Invalid labels length. Expected: 0, got: %v", len(labels))
+ }
+ if labels != nil {
+ t.Fatalf("Invalid label. Expected nil, got %v", labels)
+ }
+}
+
+func TestLabelToBytes(t *testing.T) {
+ encodedLabel := LabelToBytes("slackware.it")
+ expected := []byte{
+ 0x9, 's', 'l', 'a', 'c', 'k', 'w', 'a', 'r', 'e',
+ 0x2, 'i', 't',
+ 0x0,
+ }
+ if !bytes.Equal(encodedLabel, expected) {
+ t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel)
+ }
+}
+
+func TestLabelToBytesZeroLength(t *testing.T) {
+ encodedLabel := LabelToBytes("")
+ expected := []byte{0}
+ if !bytes.Equal(encodedLabel, expected) {
+ t.Fatalf("Invalid label. Expected: %v, got: %v", expected, encodedLabel)
+ }
+}
diff --git a/dhcpv6/options/serverid.go b/dhcpv6/options/serverid.go
new file mode 100644
index 0000000..a66f4b4
--- /dev/null
+++ b/dhcpv6/options/serverid.go
@@ -0,0 +1,57 @@
+package options
+
+// This module defines the OptServerId and DUID structures.
+// https://www.ietf.org/rfc/rfc3315.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+type OptServerId struct {
+ sid Duid
+}
+
+func (op *OptServerId) Code() OptionCode {
+ return OPTION_SERVERID
+}
+
+func (op *OptServerId) ToBytes() []byte {
+ buf := make([]byte, 4)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_SERVERID))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ buf = append(buf, op.sid.ToBytes()...)
+ return buf
+}
+
+func (op *OptServerId) ServerID() Duid {
+ return op.sid
+}
+
+func (op *OptServerId) SetServerID(sid Duid) {
+ op.sid = sid
+}
+
+func (op *OptServerId) Length() int {
+ return op.sid.Length()
+}
+
+func (op *OptServerId) String() string {
+ return fmt.Sprintf("OptServerId{sid=%v}", op.sid.String())
+}
+
+// build an OptServerId structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptServerId(data []byte) (*OptServerId, error) {
+ if len(data) < 2 {
+ // at least the DUID type is necessary to continue
+ return nil, fmt.Errorf("Invalid OptServerId data: shorter than 2 bytes")
+ }
+ opt := OptServerId{}
+ sid, err := DuidFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ opt.sid = *sid
+ return &opt, nil
+}
diff --git a/dhcpv6/options/statuscode.go b/dhcpv6/options/statuscode.go
new file mode 100644
index 0000000..60f6f71
--- /dev/null
+++ b/dhcpv6/options/statuscode.go
@@ -0,0 +1,63 @@
+package options
+
+// This module defines the OptStatusCode structure.
+// https://www.ietf.org/rfc/rfc3315.txt
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+type OptStatusCode struct {
+ statusCode uint16
+ statusMessage []byte
+}
+
+func (op *OptStatusCode) Code() OptionCode {
+ return OPTION_STATUS_CODE
+}
+
+func (op *OptStatusCode) ToBytes() []byte {
+ buf := make([]byte, 6)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_STATUS_CODE))
+ binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
+ binary.BigEndian.PutUint16(buf[4:6], op.statusCode)
+ buf = append(buf, op.statusMessage...)
+ return buf
+}
+
+func (op *OptStatusCode) StatusCode() uint16 {
+ return op.statusCode
+}
+
+func (op *OptStatusCode) SetStatusCode(code uint16) {
+ op.statusCode = code
+}
+
+func (op *OptStatusCode) StatusMessage() uint16 {
+ return op.statusCode
+}
+
+func (op *OptStatusCode) SetStatusMessage(message []byte) {
+ op.statusMessage = message
+}
+
+func (op *OptStatusCode) Length() int {
+ return 2 + len(op.statusMessage)
+}
+
+func (op *OptStatusCode) String() string {
+ return fmt.Sprintf("OptStatusCode{code=%v, message=%v}", op.statusCode, string(op.statusMessage))
+}
+
+// build an OptStatusCode structure from a sequence of bytes.
+// The input data does not include option code and length bytes.
+func ParseOptStatusCode(data []byte) (*OptStatusCode, error) {
+ if len(data) < 2 {
+ return nil, fmt.Errorf("Invalid OptStatusCode data: length is shorter than 2")
+ }
+ opt := OptStatusCode{}
+ opt.statusCode = binary.BigEndian.Uint16(data[0:2])
+ opt.statusMessage = append(opt.statusMessage, data[2:]...)
+ return &opt, nil
+}
diff --git a/dhcpv6/options/types.go b/dhcpv6/options/types.go
new file mode 100644
index 0000000..48f1003
--- /dev/null
+++ b/dhcpv6/options/types.go
@@ -0,0 +1,152 @@
+package options
+
+const (
+ _ OptionCode = iota // skip 0
+ OPTION_CLIENTID
+ OPTION_SERVERID
+ OPTION_IA_NA
+ OPTION_IA_TA
+ OPTION_IAADDR
+ OPTION_ORO
+ OPTION_PREFERENCE
+ OPTION_ELAPSED_TIME
+ OPTION_RELAY_MSG
+ _ // skip 10
+ OPTION_AUTH
+ OPTION_UNICAST
+ OPTION_STATUS_CODE
+ OPTION_RAPID_COMMIT
+ OPTION_USER_CLASS
+ OPTION_VENDOR_CLASS
+ OPTION_VENDOR_OPTS
+ OPTION_INTERFACE_ID
+ OPTION_RECONF_MSG
+ OPTION_RECONF_ACCEPT
+ SIP_SERVERS_DOMAIN_NAME_LIST
+ SIP_SERVERS_IPV6_ADDRESS_LIST
+ DNS_RECURSIVE_NAME_SERVER
+ DOMAIN_SEARCH_LIST
+ OPTION_IA_PD
+ OPTION_IAPREFIX
+ OPTION_NIS_SERVERS
+ OPTION_NISP_SERVERS
+ OPTION_NIS_DOMAIN_NAME
+ OPTION_NISP_DOMAIN_NAME
+ SNTP_SERVER_LIST
+ INFORMATION_REFRESH_TIME
+ BCMCS_CONTROLLER_DOMAIN_NAME_LIST
+ BCMCS_CONTROLLER_IPV6_ADDRESS_LIST
+ _ // skip 35
+ OPTION_GEOCONF_CIVIC
+ OPTION_REMOTE_ID
+ RELAY_AGENT_SUBSCRIBER_ID
+ FQDN
+ PANA_AUTHENTICATION_AGENT
+ OPTION_NEW_POSIX_TIMEZONE
+ OPTION_NEW_TZDB_TIMEZONE
+ ECHO_REQUEST
+ OPTION_LQ_QUERY
+ OPTION_CLIENT_DATA
+ OPTION_CLT_TIME
+ OPTION_LQ_RELAY_DATA
+ OPTION_LQ_CLIENT_LINK
+ MIPV6_HOME_NETWORK_ID_FQDN
+ MIPV6_VISITED_HOME_NETWORK_INFORMATION
+ LOST_SERVER
+ CAPWAP_ACCESS_CONTROLLER_ADDRESSES
+ RELAY_ID
+ OPTION_IPV6_ADDRESS_MOS
+ OPTION_IPV6_FQDN_MOS
+ OPTION_NTP_SERVER
+ OPTION_V6_ACCESS_DOMAIN
+ OPTION_SIP_UA_CS_LIST
+ OPT_BOOTFILE_URL
+ OPT_BOOTFILE_PARAM
+ OPTION_CLIENT_ARCH_TYPE
+ OPTION_NII
+ OPTION_GEOLOCATION
+ OPTION_AFTR_NAME
+ OPTION_ERP_LOCAL_DOMAIN_NAME
+ OPTION_RSOO
+ OPTION_PD_EXCLUDE
+ VIRTUAL_SUBNET_SELECTION
+ MIPV6_IDENTIFIED_HOME_NETWORK_INFORMATION
+ MIPV6_UNRESTRICTED_HOME_NETWORK_INFORMATION
+ MIPV6_HOME_NETWORK_PREFIX
+ MIPV6_HOME_AGENT_ADDRESS
+ MIPV6_HOME_AGENT_FQDN
+)
+
+var OptionCodeToString = map[OptionCode]string{
+ OPTION_CLIENTID: "OPTION_CLIENTID",
+ OPTION_SERVERID: "OPTION_SERVERID",
+ OPTION_IA_NA: "OPTION_IA_NA",
+ OPTION_IA_TA: "OPTION_IA_TA",
+ OPTION_IAADDR: "OPTION_IAADDR",
+ OPTION_ORO: "OPTION_ORO",
+ OPTION_PREFERENCE: "OPTION_PREFERENCE",
+ OPTION_ELAPSED_TIME: "OPTION_ELAPSED_TIME",
+ OPTION_RELAY_MSG: "OPTION_RELAY_MSG",
+ OPTION_AUTH: "OPTION_AUTH",
+ OPTION_UNICAST: "OPTION_UNICAST",
+ OPTION_STATUS_CODE: "OPTION_STATUS_CODE",
+ OPTION_RAPID_COMMIT: "OPTION_RAPID_COMMIT",
+ OPTION_USER_CLASS: "OPTION_USER_CLASS",
+ OPTION_VENDOR_CLASS: "OPTION_VENDOR_CLASS",
+ OPTION_VENDOR_OPTS: "OPTION_VENDOR_OPTS",
+ OPTION_INTERFACE_ID: "OPTION_INTERFACE_ID",
+ OPTION_RECONF_MSG: "OPTION_RECONF_MSG",
+ OPTION_RECONF_ACCEPT: "OPTION_RECONF_ACCEPT",
+ SIP_SERVERS_DOMAIN_NAME_LIST: "SIP Servers Domain Name List",
+ SIP_SERVERS_IPV6_ADDRESS_LIST: "SIP Servers IPv6 Address List",
+ DNS_RECURSIVE_NAME_SERVER: "DNS Recursive Name Server",
+ DOMAIN_SEARCH_LIST: "Domain Search List",
+ OPTION_IA_PD: "OPTION_IA_PD",
+ OPTION_IAPREFIX: "OPTION_IAPREFIX",
+ OPTION_NIS_SERVERS: "OPTION_NIS_SERVERS",
+ OPTION_NISP_SERVERS: "OPTION_NISP_SERVERS",
+ OPTION_NIS_DOMAIN_NAME: "OPTION_NIS_DOMAIN_NAME",
+ OPTION_NISP_DOMAIN_NAME: "OPTION_NISP_DOMAIN_NAME",
+ SNTP_SERVER_LIST: "SNTP Server List",
+ INFORMATION_REFRESH_TIME: "Information Refresh Time",
+ BCMCS_CONTROLLER_DOMAIN_NAME_LIST: "BCMCS Controller Domain Name List",
+ BCMCS_CONTROLLER_IPV6_ADDRESS_LIST: "BCMCS Controller IPv6 Address List",
+ OPTION_GEOCONF_CIVIC: "OPTION_GEOCONF",
+ OPTION_REMOTE_ID: "OPTION_REMOTE_ID",
+ RELAY_AGENT_SUBSCRIBER_ID: "Relay-Agent Subscriber ID",
+ FQDN: "FQDN",
+ PANA_AUTHENTICATION_AGENT: "PANA Authentication Agent",
+ OPTION_NEW_POSIX_TIMEZONE: "OPTION_NEW_POSIX_TIME_ZONE",
+ OPTION_NEW_TZDB_TIMEZONE: "OPTION_NEW_TZDB_TIMEZONE",
+ ECHO_REQUEST: "Echo Request",
+ OPTION_LQ_QUERY: "OPTION_LQ_QUERY",
+ OPTION_CLIENT_DATA: "OPTION_CLIENT_DATA",
+ OPTION_CLT_TIME: "OPTION_CLT_TIME",
+ OPTION_LQ_RELAY_DATA: "OPTION_LQ_RELAY_DATA",
+ OPTION_LQ_CLIENT_LINK: "OPTION_LQ_CLIENT_LINK",
+ MIPV6_HOME_NETWORK_ID_FQDN: "MIPv6 Home Network ID FQDN",
+ MIPV6_VISITED_HOME_NETWORK_INFORMATION: "MIPv6 Visited Home Network Information",
+ LOST_SERVER: "LoST Server",
+ CAPWAP_ACCESS_CONTROLLER_ADDRESSES: "CAPWAP Access Controller Addresses",
+ RELAY_ID: "RELAY_ID",
+ OPTION_IPV6_ADDRESS_MOS: "OPTION-IPv6_Address-MoS",
+ OPTION_IPV6_FQDN_MOS: "OPTION-IPv6-FQDN-MoS",
+ OPTION_NTP_SERVER: "OPTION_NTP_SERVER",
+ OPTION_V6_ACCESS_DOMAIN: "OPTION_V6_ACCESS_DOMAIN",
+ OPTION_SIP_UA_CS_LIST: "OPTION_SIP_UA_CS_LIST",
+ OPT_BOOTFILE_URL: "OPT_BOOTFILE_URL",
+ OPT_BOOTFILE_PARAM: "OPT_BOOTFILE_PARAM",
+ OPTION_CLIENT_ARCH_TYPE: "OPTION_CLIENT_ARCH_TYPE",
+ OPTION_NII: "OPTION_NII",
+ OPTION_GEOLOCATION: "OPTION_GEOLOCATION",
+ OPTION_AFTR_NAME: "OPTION_AFTR_NAME",
+ OPTION_ERP_LOCAL_DOMAIN_NAME: "OPTION_ERP_LOCAL_DOMAIN_NAME",
+ OPTION_RSOO: "OPTION_RSOO",
+ OPTION_PD_EXCLUDE: "OPTION_PD_EXCLUDE",
+ VIRTUAL_SUBNET_SELECTION: "Virtual Subnet Selection",
+ MIPV6_IDENTIFIED_HOME_NETWORK_INFORMATION: "MIPv6 Identified Home Network Information",
+ MIPV6_UNRESTRICTED_HOME_NETWORK_INFORMATION: "MIPv6 Unrestricted Home Network Information",
+ MIPV6_HOME_NETWORK_PREFIX: "MIPv6 Home Network Prefix",
+ MIPV6_HOME_AGENT_ADDRESS: "MIPv6 Home Agent Address",
+ MIPV6_HOME_AGENT_FQDN: "MIPv6 Home Agent FQDN",
+}