summaryrefslogtreecommitdiffhomepage
path: root/dhcpv6
diff options
context:
space:
mode:
Diffstat (limited to 'dhcpv6')
-rw-r--r--dhcpv6/dhcpv6.go58
-rw-r--r--dhcpv6/dhcpv6_test.go47
-rw-r--r--dhcpv6/dhcpv6message.go27
-rw-r--r--dhcpv6/dhcpv6message_test.go34
-rw-r--r--dhcpv6/modifiers.go6
-rw-r--r--dhcpv6/option_archtype.go62
-rw-r--r--dhcpv6/option_archtype_test.go5
-rw-r--r--dhcpv6/utils.go77
-rw-r--r--dhcpv6/utils_test.go69
9 files changed, 194 insertions, 191 deletions
diff --git a/dhcpv6/dhcpv6.go b/dhcpv6/dhcpv6.go
index 0dabca5..9382334 100644
--- a/dhcpv6/dhcpv6.go
+++ b/dhcpv6/dhcpv6.go
@@ -1,8 +1,12 @@
package dhcpv6
import (
+ "errors"
"fmt"
"net"
+ "strings"
+
+ "github.com/insomniacslk/dhcp/iana"
)
type DHCPv6 interface {
@@ -199,3 +203,57 @@ func EncapsulateRelay(d DHCPv6, mType MessageType, linkAddr, peerAddr net.IP) (D
outer.AddOption(&orm)
return &outer, nil
}
+
+// IsUsingUEFI function takes a DHCPv6 message and returns true if
+// the machine trying to netboot is using UEFI of false if it is not.
+func IsUsingUEFI(msg DHCPv6) bool {
+ // RFC 4578 says:
+ // As of the writing of this document, the following pre-boot
+ // architecture types have been requested.
+ // Type Architecture Name
+ // ---- -----------------
+ // 0 Intel x86PC
+ // 1 NEC/PC98
+ // 2 EFI Itanium
+ // 3 DEC Alpha
+ // 4 Arc x86
+ // 5 Intel Lean Client
+ // 6 EFI IA32
+ // 7 EFI BC
+ // 8 EFI Xscale
+ // 9 EFI x86-64
+ if opt := msg.GetOneOption(OptionClientArchType); opt != nil {
+ optat := opt.(*OptClientArchType)
+ for _, at := range optat.ArchTypes {
+ // TODO investigate if other types are appropriate
+ if at == iana.EFI_BC || at == iana.EFI_X86_64 {
+ return true
+ }
+ }
+ }
+ if opt := msg.GetOneOption(OptionUserClass); opt != nil {
+ optuc := opt.(*OptUserClass)
+ for _, uc := range optuc.UserClasses {
+ if strings.Contains(string(uc), "EFI") {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// GetTransactionID returns a transactionID of a message or its inner message
+// in case of relay
+func GetTransactionID(packet DHCPv6) (uint32, error) {
+ if message, ok := packet.(*DHCPv6Message); ok {
+ return message.TransactionID(), nil
+ }
+ if relay, ok := packet.(*DHCPv6Relay); ok {
+ message, err := relay.GetInnerMessage()
+ if err != nil {
+ return 0, err
+ }
+ return GetTransactionID(message)
+ }
+ return 0, errors.New("Invalid DHCPv6 packet")
+}
diff --git a/dhcpv6/dhcpv6_test.go b/dhcpv6/dhcpv6_test.go
index 4a44e0e..6840252 100644
--- a/dhcpv6/dhcpv6_test.go
+++ b/dhcpv6/dhcpv6_test.go
@@ -215,5 +215,52 @@ func TestNewMessageTypeSolicitWithCID(t *testing.T) {
require.Equal(t, len(opts), 2)
}
+
+func TestIsUsingUEFIArchTypeTrue(t *testing.T) {
+ msg := DHCPv6Message{}
+ opt := OptClientArchType{ArchTypes: []iana.ArchType{iana.EFI_BC}}
+ msg.AddOption(&opt)
+ require.True(t, IsUsingUEFI(&msg))
+}
+
+func TestIsUsingUEFIArchTypeFalse(t *testing.T) {
+ msg := DHCPv6Message{}
+ opt := OptClientArchType{ArchTypes: []iana.ArchType{iana.INTEL_X86PC}}
+ msg.AddOption(&opt)
+ require.False(t, IsUsingUEFI(&msg))
+}
+
+func TestIsUsingUEFIUserClassTrue(t *testing.T) {
+ msg := DHCPv6Message{}
+ opt := OptUserClass{UserClasses: [][]byte{[]byte("ipxeUEFI")}}
+ msg.AddOption(&opt)
+ require.True(t, IsUsingUEFI(&msg))
+}
+
+func TestIsUsingUEFIUserClassFalse(t *testing.T) {
+ msg := DHCPv6Message{}
+ opt := OptUserClass{UserClasses: [][]byte{[]byte("ipxeLegacy")}}
+ msg.AddOption(&opt)
+ require.False(t, IsUsingUEFI(&msg))
+}
+
+func TestGetTransactionIDMessage(t *testing.T) {
+ message, err := NewMessage()
+ require.NoError(t, err)
+ transactionID, err := GetTransactionID(message)
+ require.NoError(t, err)
+ require.Equal(t, transactionID, message.(*DHCPv6Message).TransactionID())
+}
+
+func TestGetTransactionIDRelay(t *testing.T) {
+ message, err := NewMessage()
+ require.NoError(t, err)
+ relay, err := EncapsulateRelay(message, MessageTypeRelayForward, nil, nil)
+ require.NoError(t, err)
+ transactionID, err := GetTransactionID(relay)
+ require.NoError(t, err)
+ require.Equal(t, transactionID, message.(*DHCPv6Message).TransactionID())
+}
+
// TODO test NewMessageTypeSolicit
// test String and Summary
diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go
index e601932..7ee00ad 100644
--- a/dhcpv6/dhcpv6message.go
+++ b/dhcpv6/dhcpv6message.go
@@ -286,6 +286,33 @@ func (d *DHCPv6Message) UpdateOption(option Option) {
d.AddOption(option)
}
+// IsNetboot returns true if the machine is trying to netboot. It checks if
+// "boot file" is one of the requested options, which is useful for
+// SOLICIT/REQUEST packet types, it also checks if the "boot file" option is
+// included in the packet, which is useful for ADVERTISE/REPLY packet.
+func (d *DHCPv6Message) IsNetboot() bool {
+ if d.IsOptionRequested(OptionBootfileURL) {
+ return true
+ }
+ if optbf := d.GetOneOption(OptionBootfileURL); optbf != nil {
+ return true
+ }
+ return false
+}
+
+// IsOptionRequested takes an OptionCode and returns true if that option is
+// within the requested options of the DHCPv6 message.
+func (d *DHCPv6Message) IsOptionRequested(requested OptionCode) bool {
+ for _, optoro := range d.GetOption(OptionORO) {
+ for _, o := range optoro.(*OptRequestedOption).RequestedOptions() {
+ if o == requested {
+ return true
+ }
+ }
+ }
+ return false
+}
+
func (d *DHCPv6Message) String() string {
return fmt.Sprintf("DHCPv6Message(messageType=%v transactionID=0x%06x, %d options)",
d.MessageTypeToString(), d.TransactionID(), len(d.options),
diff --git a/dhcpv6/dhcpv6message_test.go b/dhcpv6/dhcpv6message_test.go
new file mode 100644
index 0000000..5c92a7b
--- /dev/null
+++ b/dhcpv6/dhcpv6message_test.go
@@ -0,0 +1,34 @@
+package dhcpv6
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestIsNetboot(t *testing.T) {
+ msg1 := DHCPv6Message{}
+ require.False(t, msg1.IsNetboot())
+
+ msg2 := DHCPv6Message{}
+ optro := OptRequestedOption{}
+ optro.AddRequestedOption(OptionBootfileURL)
+ msg2.AddOption(&optro)
+ require.True(t, msg2.IsNetboot())
+
+ msg3 := DHCPv6Message{}
+ optbf := OptBootFileURL{}
+ msg3.AddOption(&optbf)
+ require.True(t, msg3.IsNetboot())
+}
+
+func TestIsOptionRequested(t *testing.T) {
+ msg1 := DHCPv6Message{}
+ require.False(t, msg1.IsOptionRequested(OptionDNSRecursiveNameServer))
+
+ msg2 := DHCPv6Message{}
+ optro := OptRequestedOption{}
+ optro.AddRequestedOption(OptionDNSRecursiveNameServer)
+ msg2.AddOption(&optro)
+ require.True(t, msg2.IsOptionRequested(OptionDNSRecursiveNameServer))
+}
diff --git a/dhcpv6/modifiers.go b/dhcpv6/modifiers.go
index 32d11ac..6cd66db 100644
--- a/dhcpv6/modifiers.go
+++ b/dhcpv6/modifiers.go
@@ -2,6 +2,8 @@ package dhcpv6
import (
"log"
+
+ "github.com/insomniacslk/dhcp/iana"
)
// WithClientID adds a client ID option to a DHCPv6 packet
@@ -53,9 +55,9 @@ func WithUserClass(uc []byte) Modifier {
}
// WithArchType adds an arch type option to the packet
-func WithArchType(at ArchType) Modifier {
+func WithArchType(at iana.ArchType) Modifier {
return func(d DHCPv6) DHCPv6 {
- ao := OptClientArchType{ArchType: at}
+ ao := OptClientArchType{ArchTypes: []iana.ArchType{at}}
d.AddOption(&ao)
return d
}
diff --git a/dhcpv6/option_archtype.go b/dhcpv6/option_archtype.go
index a1b4a9b..231eddd 100644
--- a/dhcpv6/option_archtype.go
+++ b/dhcpv6/option_archtype.go
@@ -6,42 +6,14 @@ package dhcpv6
import (
"encoding/binary"
"fmt"
-)
-
-//ArchType encodes an architecture type in an uint16
-type ArchType uint16
+ "strings"
-// see rfc4578
-const (
- INTEL_X86PC ArchType = 0
- NEC_PC98 ArchType = 1
- EFI_ITANIUM ArchType = 2
- DEC_ALPHA ArchType = 3
- ARC_X86 ArchType = 4
- INTEL_LEAN_CLIENT ArchType = 5
- EFI_IA32 ArchType = 6
- EFI_BC ArchType = 7
- EFI_XSCALE ArchType = 8
- EFI_X86_64 ArchType = 9
+ "github.com/insomniacslk/dhcp/iana"
)
-// ArchTypeToStringMap maps an ArchType to a mnemonic name
-var ArchTypeToStringMap = map[ArchType]string{
- INTEL_X86PC: "Intel x86PC",
- NEC_PC98: "NEC/PC98",
- EFI_ITANIUM: "EFI Itanium",
- DEC_ALPHA: "DEC Alpha",
- ARC_X86: "Arc x86",
- INTEL_LEAN_CLIENT: "Intel Lean Client",
- EFI_IA32: "EFI IA32",
- EFI_BC: "EFI BC",
- EFI_XSCALE: "EFI Xscale",
- EFI_X86_64: "EFI x86-64",
-}
-
// OptClientArchType represents an option CLIENT_ARCH_TYPE
type OptClientArchType struct {
- ArchType ArchType
+ ArchTypes []iana.ArchType
}
func (op *OptClientArchType) Code() OptionCode {
@@ -49,23 +21,28 @@ func (op *OptClientArchType) Code() OptionCode {
}
func (op *OptClientArchType) ToBytes() []byte {
- buf := make([]byte, 6)
+ buf := make([]byte, 4)
binary.BigEndian.PutUint16(buf[0:2], uint16(OptionClientArchType))
binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length()))
- binary.BigEndian.PutUint16(buf[4:6], uint16(op.ArchType))
+ u16 := make([]byte, 2)
+ for _, at := range op.ArchTypes {
+ binary.BigEndian.PutUint16(u16, uint16(at))
+ buf = append(buf, u16...)
+ }
return buf
}
func (op *OptClientArchType) Length() int {
- return 2
+ return 2*len(op.ArchTypes)
}
func (op *OptClientArchType) String() string {
- name, ok := ArchTypeToStringMap[op.ArchType]
- if !ok {
- name = "Unknown"
+ atStrings := make([]string, 0)
+ for _, at := range op.ArchTypes {
+ name := iana.ArchTypeToString(at)
+ atStrings = append(atStrings, name)
}
- return fmt.Sprintf("OptClientArchType{archtype=%v}", name)
+ return fmt.Sprintf("OptClientArchType{archtype=%v}", strings.Join(atStrings, ", "))
}
// ParseOptClientArchType builds an OptClientArchType structure from
@@ -73,9 +50,12 @@ func (op *OptClientArchType) String() string {
// length bytes.
func ParseOptClientArchType(data []byte) (*OptClientArchType, error) {
opt := OptClientArchType{}
- if len(data) != 2 {
- return nil, fmt.Errorf("Invalid arch type data length. Expected 2 bytes, got %v", len(data))
+ if len(data) == 0 || len(data)%2 != 0 {
+ return nil, fmt.Errorf("Invalid arch type data length. Expected multiple of 2 larger than 2, got %v", len(data))
+ }
+ for idx := 0; idx < len(data); idx += 2 {
+ b := data[idx : idx+2]
+ opt.ArchTypes = append(opt.ArchTypes, iana.ArchType(binary.BigEndian.Uint16(b)))
}
- opt.ArchType = ArchType(binary.BigEndian.Uint16(data))
return &opt, nil
}
diff --git a/dhcpv6/option_archtype_test.go b/dhcpv6/option_archtype_test.go
index 748c8c5..1848e55 100644
--- a/dhcpv6/option_archtype_test.go
+++ b/dhcpv6/option_archtype_test.go
@@ -3,6 +3,7 @@ package dhcpv6
import (
"testing"
+ "github.com/insomniacslk/dhcp/iana"
"github.com/stretchr/testify/require"
)
@@ -12,7 +13,7 @@ func TestParseOptClientArchType(t *testing.T) {
}
opt, err := ParseOptClientArchType(data)
require.NoError(t, err)
- require.Equal(t, opt.ArchType, EFI_IA32)
+ require.Equal(t, opt.ArchTypes[0], iana.EFI_IA32)
}
func TestParseOptClientArchTypeInvalid(t *testing.T) {
@@ -37,7 +38,7 @@ func TestOptClientArchTypeParseAndToBytes(t *testing.T) {
func TestOptClientArchType(t *testing.T) {
opt := OptClientArchType{
- ArchType: EFI_ITANIUM,
+ ArchTypes: []iana.ArchType{iana.EFI_ITANIUM},
}
require.Equal(t, opt.Length(), 2)
require.Equal(t, opt.Code(), OptionClientArchType)
diff --git a/dhcpv6/utils.go b/dhcpv6/utils.go
deleted file mode 100644
index 1681661..0000000
--- a/dhcpv6/utils.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package dhcpv6
-
-import (
- "errors"
- "strings"
-)
-
-// IsNetboot function takes a DHCPv6 message and returns true if the machine
-// is trying to netboot. It checks if "boot file" is one of the requested
-// options, which is useful for SOLICIT/REQUEST packet types, it also checks
-// if the "boot file" option is included in the packet, which is useful for
-// ADVERTISE/REPLY packet.
-func IsNetboot(msg DHCPv6) bool {
- for _, optoro := range msg.GetOption(OptionORO) {
- for _, o := range optoro.(*OptRequestedOption).RequestedOptions() {
- if o == OptionBootfileURL {
- return true
- }
- }
- }
- if optbf := msg.GetOneOption(OptionBootfileURL); optbf != nil {
- return true
- }
- return false
-}
-
-// IsUsingUEFI function takes a DHCPv6 message and returns true if
-// the machine trying to netboot is using UEFI of false if it is not.
-func IsUsingUEFI(msg DHCPv6) bool {
- // RFC 4578 says:
- // As of the writing of this document, the following pre-boot
- // architecture types have been requested.
- // Type Architecture Name
- // ---- -----------------
- // 0 Intel x86PC
- // 1 NEC/PC98
- // 2 EFI Itanium
- // 3 DEC Alpha
- // 4 Arc x86
- // 5 Intel Lean Client
- // 6 EFI IA32
- // 7 EFI BC
- // 8 EFI Xscale
- // 9 EFI x86-64
- if opt := msg.GetOneOption(OptionClientArchType); opt != nil {
- optat := opt.(*OptClientArchType)
- // TODO investigate if other types are appropriate
- if optat.ArchType == EFI_BC || optat.ArchType == EFI_X86_64 {
- return true
- }
- }
- if opt := msg.GetOneOption(OptionUserClass); opt != nil {
- optuc := opt.(*OptUserClass)
- for _, uc := range optuc.UserClasses {
- if strings.Contains(string(uc), "EFI") {
- return true
- }
- }
- }
- return false
-}
-
-// GetTransactionID returns a transactionID of a message or its inner message
-// in case of relay
-func GetTransactionID(packet DHCPv6) (uint32, error) {
- if message, ok := packet.(*DHCPv6Message); ok {
- return message.TransactionID(), nil
- }
- if relay, ok := packet.(*DHCPv6Relay); ok {
- message, err := relay.GetInnerMessage()
- if err != nil {
- return 0, err
- }
- return GetTransactionID(message)
- }
- return 0, errors.New("Invalid DHCPv6 packet")
-}
diff --git a/dhcpv6/utils_test.go b/dhcpv6/utils_test.go
deleted file mode 100644
index f3b53f0..0000000
--- a/dhcpv6/utils_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package dhcpv6
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestIsNetboot(t *testing.T) {
- msg1 := DHCPv6Message{}
- require.False(t, IsNetboot(&msg1))
-
- msg2 := DHCPv6Message{}
- optro := OptRequestedOption{}
- optro.AddRequestedOption(OptionBootfileURL)
- msg2.AddOption(&optro)
- require.True(t, IsNetboot(&msg2))
-
- msg3 := DHCPv6Message{}
- optbf := OptBootFileURL{}
- msg3.AddOption(&optbf)
- require.True(t, IsNetboot(&msg3))
-}
-
-func TestIsUsingUEFIArchTypeTrue(t *testing.T) {
- msg := DHCPv6Message{}
- opt := OptClientArchType{ArchType: EFI_BC}
- msg.AddOption(&opt)
- require.True(t, IsUsingUEFI(&msg))
-}
-
-func TestIsUsingUEFIArchTypeFalse(t *testing.T) {
- msg := DHCPv6Message{}
- opt := OptClientArchType{ArchType: INTEL_X86PC}
- msg.AddOption(&opt)
- require.False(t, IsUsingUEFI(&msg))
-}
-
-func TestIsUsingUEFIUserClassTrue(t *testing.T) {
- msg := DHCPv6Message{}
- opt := OptUserClass{UserClasses: [][]byte{[]byte("ipxeUEFI")}}
- msg.AddOption(&opt)
- require.True(t, IsUsingUEFI(&msg))
-}
-
-func TestIsUsingUEFIUserClassFalse(t *testing.T) {
- msg := DHCPv6Message{}
- opt := OptUserClass{UserClasses: [][]byte{[]byte("ipxeLegacy")}}
- msg.AddOption(&opt)
- require.False(t, IsUsingUEFI(&msg))
-}
-
-func TestGetTransactionIDMessage(t *testing.T) {
- message, err := NewMessage()
- require.NoError(t, err)
- transactionID, err := GetTransactionID(message)
- require.NoError(t, err)
- require.Equal(t, transactionID, message.(*DHCPv6Message).TransactionID())
-}
-
-func TestGetTransactionIDRelay(t *testing.T) {
- message, err := NewMessage()
- require.NoError(t, err)
- relay, err := EncapsulateRelay(message, MessageTypeRelayForward, nil, nil)
- require.NoError(t, err)
- transactionID, err := GetTransactionID(relay)
- require.NoError(t, err)
- require.Equal(t, transactionID, message.(*DHCPv6Message).TransactionID())
-}