summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--dhcpv4/option_archtype.go109
-rw-r--r--dhcpv4/option_archtype_test.go68
-rw-r--r--dhcpv4/options.go22
-rw-r--r--dhcpv4/options_test.go64
4 files changed, 245 insertions, 18 deletions
diff --git a/dhcpv4/option_archtype.go b/dhcpv4/option_archtype.go
new file mode 100644
index 0000000..16ca98d
--- /dev/null
+++ b/dhcpv4/option_archtype.go
@@ -0,0 +1,109 @@
+package dhcpv4
+
+// This option implements the Client System Architecture Type option
+// https://tools.ietf.org/html/rfc4578
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+//ArchType encodes an architecture type in an uint16
+type ArchType uint16
+
+// 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
+)
+
+// 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 encapsulating the Client System
+// Architecture Type option Definition.
+type OptClientArchType struct {
+ ArchTypes []ArchType
+}
+
+// Code returns the option code.
+func (o *OptClientArchType) Code() OptionCode {
+ return OptionClientSystemArchitectureType
+}
+
+// ToBytes returns a serialized stream of bytes for this option.
+func (o *OptClientArchType) ToBytes() []byte {
+ ret := []byte{byte(o.Code()), byte(o.Length())}
+ for _, at := range o.ArchTypes {
+ buf := make([]byte, 2)
+ binary.BigEndian.PutUint16(buf[0:2], uint16(at))
+ ret = append(ret, buf...)
+ }
+ return ret
+}
+
+// Length returns the length of the data portion (excluding option code an byte
+// length).
+func (o *OptClientArchType) Length() int {
+ return 2*len(o.ArchTypes)
+}
+
+// String returns a human-readable string.
+func (o *OptClientArchType) String() string {
+ var archTypes string
+ for idx, at := range o.ArchTypes {
+ name, ok := ArchTypeToStringMap[at]
+ if !ok {
+ name = "Unknown"
+ }
+ archTypes += name
+ if idx < len(o.ArchTypes)-1 {
+ archTypes += ", "
+ }
+ }
+ return fmt.Sprintf("Client System Architecture Type -> %v", archTypes)
+}
+
+// ParseOptClientArchType returns a new OptClientArchType from a byte stream,
+// or error if any.
+func ParseOptClientArchType(data []byte) (*OptClientArchType, error) {
+ if len(data) < 2 {
+ return nil, ErrShortByteStream
+ }
+ code := OptionCode(data[0])
+ if code != OptionClientSystemArchitectureType {
+ return nil, fmt.Errorf("expected code %v, got %v", OptionClientSystemArchitectureType, code)
+ }
+ length := int(data[1])
+ if length == 0 || length%2 != 0 {
+ return nil, fmt.Errorf("Invalid length: expected multiple of 2 larger than 2, got %v", length)
+ }
+ if len(data) < 2+length {
+ return nil, ErrShortByteStream
+ }
+ archTypes := make([]ArchType, 0, length%2)
+ for idx := 0; idx < length; idx += 2 {
+ b := data[2+idx : 2+idx+2]
+ archTypes = append(archTypes, ArchType(binary.BigEndian.Uint16(b)))
+ }
+ return &OptClientArchType{ArchTypes: archTypes}, nil
+}
diff --git a/dhcpv4/option_archtype_test.go b/dhcpv4/option_archtype_test.go
new file mode 100644
index 0000000..cb9bc3f
--- /dev/null
+++ b/dhcpv4/option_archtype_test.go
@@ -0,0 +1,68 @@
+package dhcpv4
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestParseOptClientArchType(t *testing.T) {
+ data := []byte{
+ 93, // OptionClientSystemArchitectureType
+ 2, // Length
+ 0, 6, // EFI_IA32
+ }
+ opt, err := ParseOptClientArchType(data)
+ require.NoError(t, err)
+ require.Equal(t, opt.ArchTypes[0], EFI_IA32)
+}
+
+func TestParseOptClientArchTypeMultiple(t *testing.T) {
+ data := []byte{
+ 93, // OptionClientSystemArchitectureType
+ 4, // Length
+ 0, 6, // EFI_IA32
+ 0, 2, // EFI_ITANIUM
+ }
+ opt, err := ParseOptClientArchType(data)
+ require.NoError(t, err)
+ require.Equal(t, opt.ArchTypes[0], EFI_IA32)
+ require.Equal(t, opt.ArchTypes[1], EFI_ITANIUM)
+}
+
+func TestParseOptClientArchTypeInvalid(t *testing.T) {
+ data := []byte{42}
+ _, err := ParseOptClientArchType(data)
+ require.Error(t, err)
+}
+
+func TestOptClientArchTypeParseAndToBytes(t *testing.T) {
+ data := []byte{
+ 93, // OptionClientSystemArchitectureType
+ 2, // Length
+ 0, 8, // EFI_XSCALE
+ }
+ opt, err := ParseOptClientArchType(data)
+ require.NoError(t, err)
+ require.Equal(t, opt.ToBytes(), data)
+}
+
+func TestOptClientArchTypeParseAndToBytesMultiple(t *testing.T) {
+ data := []byte{
+ 93, // OptionClientSystemArchitectureType
+ 4, // Length
+ 0, 8, // EFI_XSCALE
+ 0, 6, // EFI_IA32
+ }
+ opt, err := ParseOptClientArchType(data)
+ require.NoError(t, err)
+ require.Equal(t, opt.ToBytes(), data)
+}
+
+func TestOptClientArchType(t *testing.T) {
+ opt := OptClientArchType{
+ ArchTypes: []ArchType{EFI_ITANIUM},
+ }
+ require.Equal(t, opt.Length(), 2)
+ require.Equal(t, opt.Code(), OptionClientSystemArchitectureType)
+}
diff --git a/dhcpv4/options.go b/dhcpv4/options.go
index 0be494f..d869b7d 100644
--- a/dhcpv4/options.go
+++ b/dhcpv4/options.go
@@ -44,8 +44,14 @@ func ParseOption(data []byte) (Option, error) {
opt, err = ParseOptSubnetMask(data)
case OptionRouter:
opt, err = ParseOptRouter(data)
+ case OptionDomainNameServer:
+ opt, err = ParseOptDomainNameServer(data)
case OptionHostName:
opt, err = ParseOptHostName(data)
+ case OptionDomainName:
+ opt, err = ParseOptDomainName(data)
+ case OptionBroadcastAddress:
+ opt, err = ParseOptBroadcastAddress(data)
case OptionNTPServers:
opt, err = ParseOptNTPServers(data)
case OptionRequestedIPAddress:
@@ -54,28 +60,24 @@ func ParseOption(data []byte) (Option, error) {
opt, err = ParseOptIPAddressLeaseTime(data)
case OptionDHCPMessageType:
opt, err = ParseOptMessageType(data)
- case OptionParameterRequestList:
- opt, err = ParseOptParameterRequestList(data)
case OptionServerIdentifier:
opt, err = ParseOptServerIdentifier(data)
- case OptionBroadcastAddress:
- opt, err = ParseOptBroadcastAddress(data)
+ case OptionParameterRequestList:
+ opt, err = ParseOptParameterRequestList(data)
case OptionMaximumDHCPMessageSize:
opt, err = ParseOptMaximumDHCPMessageSize(data)
case OptionClassIdentifier:
opt, err = ParseOptClassIdentifier(data)
- case OptionDomainName:
- opt, err = ParseOptDomainName(data)
- case OptionDomainNameServer:
- opt, err = ParseOptDomainNameServer(data)
- case OptionVendorIdentifyingVendorClass:
- opt, err = ParseOptVIVC(data)
case OptionTFTPServerName:
opt, err = ParseOptTFTPServerName(data)
case OptionBootfileName:
opt, err = ParseOptBootfileName(data)
case OptionUserClassInformation:
opt, err = ParseOptUserClass(data)
+ case OptionClientSystemArchitectureType:
+ opt, err = ParseOptClientArchType(data)
+ case OptionVendorIdentifyingVendorClass:
+ opt, err = ParseOptVIVC(data)
case OptionDNSDomainSearchList:
opt, err = ParseOptDomainSearch(data)
default:
diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go
index b3d7605..0268483 100644
--- a/dhcpv4/options_test.go
+++ b/dhcpv4/options_test.go
@@ -25,6 +25,46 @@ func TestParseOption(t *testing.T) {
require.Equal(t, 4, opt.Length(), "Length")
require.Equal(t, option, opt.ToBytes(), "ToBytes")
+ // Option router
+ option = []byte{3, 4, 192, 168, 1, 1}
+ opt, err = ParseOption(option)
+ require.NoError(t, err)
+ require.Equal(t, OptionRouter, opt.Code(), "Code")
+ require.Equal(t, 4, opt.Length(), "Length")
+ require.Equal(t, option, opt.ToBytes(), "ToBytes")
+
+ // Option domain name server
+ option = []byte{6, 4, 192, 168, 1, 1}
+ opt, err = ParseOption(option)
+ require.NoError(t, err)
+ require.Equal(t, OptionDomainNameServer, opt.Code(), "Code")
+ require.Equal(t, 4, opt.Length(), "Length")
+ require.Equal(t, option, opt.ToBytes(), "ToBytes")
+
+ // Option host name
+ option = []byte{12, 4, 't', 'e', 's', 't'}
+ opt, err = ParseOption(option)
+ require.NoError(t, err)
+ require.Equal(t, OptionHostName, opt.Code(), "Code")
+ require.Equal(t, 4, opt.Length(), "Length")
+ require.Equal(t, option, opt.ToBytes(), "ToBytes")
+
+ // Option domain name
+ option = []byte{15, 4, 't', 'e', 's', 't'}
+ opt, err = ParseOption(option)
+ require.NoError(t, err)
+ require.Equal(t, OptionDomainName, opt.Code(), "Code")
+ require.Equal(t, 4, opt.Length(), "Length")
+ require.Equal(t, option, opt.ToBytes(), "ToBytes")
+
+ // Option NTP servers
+ option = []byte{42, 4, 10, 10, 10, 10}
+ opt, err = ParseOption(option)
+ require.NoError(t, err)
+ require.Equal(t, OptionNTPServers, opt.Code(), "Code")
+ require.Equal(t, 4, opt.Length(), "Length")
+ require.Equal(t, option, opt.ToBytes(), "ToBytes")
+
// Requested IP address
option = []byte{50, 4, 1, 2, 3, 4}
opt, err = ParseOption(option)
@@ -41,14 +81,6 @@ func TestParseOption(t *testing.T) {
require.Equal(t, 1, opt.Length(), "Length")
require.Equal(t, option, opt.ToBytes(), "ToBytes")
- // Parameter request list
- option = []byte{55, 3, 5, 53, 61}
- opt, err = ParseOption(option)
- require.NoError(t, err)
- require.Equal(t, OptionParameterRequestList, opt.Code(), "Code")
- require.Equal(t, 3, opt.Length(), "Length")
- require.Equal(t, option, opt.ToBytes(), "ToBytes")
-
// Option server ID
option = []byte{54, 4, 1, 2, 3, 4}
opt, err = ParseOption(option)
@@ -57,6 +89,14 @@ func TestParseOption(t *testing.T) {
require.Equal(t, 4, opt.Length(), "Length")
require.Equal(t, option, opt.ToBytes(), "ToBytes")
+ // Parameter request list
+ option = []byte{55, 3, 5, 53, 61}
+ opt, err = ParseOption(option)
+ require.NoError(t, err)
+ require.Equal(t, OptionParameterRequestList, opt.Code(), "Code")
+ require.Equal(t, 3, opt.Length(), "Length")
+ require.Equal(t, option, opt.ToBytes(), "ToBytes")
+
// Option max message size
option = []byte{57, 2, 1, 2}
opt, err = ParseOption(option)
@@ -96,6 +136,14 @@ func TestParseOption(t *testing.T) {
require.Equal(t, OptionUserClassInformation, opt.Code(), "Code")
require.Equal(t, 5, opt.Length(), "Length")
require.Equal(t, option, opt.ToBytes(), "ToBytes")
+
+ // Option client system architecture type option
+ option = []byte{93, 4, 't', 'e', 's', 't'}
+ opt, err = ParseOption(option)
+ require.NoError(t, err)
+ require.Equal(t, OptionClientSystemArchitectureType, opt.Code(), "Code")
+ require.Equal(t, 4, opt.Length(), "Length")
+ require.Equal(t, option, opt.ToBytes(), "ToBytes")
}
func TestParseOptionZeroLength(t *testing.T) {