summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--dhcpv4/option_domain_name_server.go2
-rw-r--r--dhcpv4/option_ip_address_lease_time.go57
-rw-r--r--dhcpv4/option_ip_address_lease_time_test.go41
-rw-r--r--dhcpv4/option_maximum_dhcp_message_size.go4
-rw-r--r--dhcpv4/option_maximum_dhcp_message_size_test.go2
-rw-r--r--dhcpv4/option_router.go70
-rw-r--r--dhcpv4/option_router_test.go65
-rw-r--r--dhcpv4/options.go8
-rw-r--r--dhcpv4/options_test.go16
9 files changed, 251 insertions, 14 deletions
diff --git a/dhcpv4/option_domain_name_server.go b/dhcpv4/option_domain_name_server.go
index 78aaf90..470eaa0 100644
--- a/dhcpv4/option_domain_name_server.go
+++ b/dhcpv4/option_domain_name_server.go
@@ -48,7 +48,7 @@ func (o *OptDomainNameServer) Code() OptionCode {
func (o *OptDomainNameServer) ToBytes() []byte {
ret := []byte{byte(o.Code()), byte(o.Length())}
for _, ns := range o.NameServers {
- ret = append(ret, ns...)
+ ret = append(ret, ns.To4()...)
}
return ret
}
diff --git a/dhcpv4/option_ip_address_lease_time.go b/dhcpv4/option_ip_address_lease_time.go
new file mode 100644
index 0000000..7562c58
--- /dev/null
+++ b/dhcpv4/option_ip_address_lease_time.go
@@ -0,0 +1,57 @@
+package dhcpv4
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+// This option implements the IP Address Lease Time option
+// https://tools.ietf.org/html/rfc2132
+
+// OptIPAddressLeaseTime represents the IP Address Lease Time option.
+type OptIPAddressLeaseTime struct {
+ LeaseTime uint32
+}
+
+// ParseOptIPAddressLeaseTime constructs an OptIPAddressLeaseTime struct from a
+// sequence of bytes and returns it, or an error.
+func ParseOptIPAddressLeaseTime(data []byte) (*OptIPAddressLeaseTime, error) {
+ // Should at least have code, length, and lease time.
+ if len(data) < 6 {
+ return nil, ErrShortByteStream
+ }
+ code := OptionCode(data[0])
+ if code != OptionIPAddressLeaseTime {
+ return nil, fmt.Errorf("expected option %v, got %v instead", OptionIPAddressLeaseTime, code)
+ }
+ length := int(data[1])
+ if length != 4 {
+ return nil, fmt.Errorf("expected length 4, got %v instead", length)
+ }
+ leaseTime := binary.BigEndian.Uint32(data[2:6])
+ return &OptIPAddressLeaseTime{LeaseTime: leaseTime}, nil
+}
+
+// Code returns the option code.
+func (o *OptIPAddressLeaseTime) Code() OptionCode {
+ return OptionIPAddressLeaseTime
+}
+
+// ToBytes returns a serialized stream of bytes for this option.
+func (o *OptIPAddressLeaseTime) ToBytes() []byte {
+ serializedTime := make([]byte, 4)
+ binary.BigEndian.PutUint32(serializedTime, o.LeaseTime)
+ serializedOpt := []byte{byte(o.Code()), byte(o.Length())}
+ return append(serializedOpt, serializedTime...)
+}
+
+// String returns a human-readable string for this option.
+func (o *OptIPAddressLeaseTime) String() string {
+ return fmt.Sprintf("IP Addresses Lease Time -> %v", o.LeaseTime)
+}
+
+// Length returns the length of the data portion (excluding option code and byte
+// for length, if any).
+func (o *OptIPAddressLeaseTime) Length() int {
+ return 4
+}
diff --git a/dhcpv4/option_ip_address_lease_time_test.go b/dhcpv4/option_ip_address_lease_time_test.go
new file mode 100644
index 0000000..7d507bf
--- /dev/null
+++ b/dhcpv4/option_ip_address_lease_time_test.go
@@ -0,0 +1,41 @@
+package dhcpv4
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestOptIPAddressLeaseTimeInterfaceMethods(t *testing.T) {
+ o := OptIPAddressLeaseTime{LeaseTime: 43200}
+ require.Equal(t, OptionIPAddressLeaseTime, o.Code(), "Code")
+ require.Equal(t, 4, o.Length(), "Length")
+ require.Equal(t, []byte{51, 4, 0, 0, 168, 192}, o.ToBytes(), "ToBytes")
+}
+
+func TestParseOptIPAddressLeaseTime(t *testing.T) {
+ data := []byte{51, 4, 0, 0, 168, 192}
+ o, err := ParseOptIPAddressLeaseTime(data)
+ require.NoError(t, err)
+ require.Equal(t, &OptIPAddressLeaseTime{LeaseTime: 43200}, o)
+
+ // Short byte stream
+ data = []byte{51, 4, 168, 192}
+ _, err = ParseOptIPAddressLeaseTime(data)
+ require.Error(t, err, "should get error from short byte stream")
+
+ // Wrong code
+ data = []byte{54, 4, 0, 0, 168, 192}
+ _, err = ParseOptIPAddressLeaseTime(data)
+ require.Error(t, err, "should get error from wrong code")
+
+ // Bad length
+ data = []byte{51, 5, 1, 1, 1, 1, 1}
+ _, err = ParseOptIPAddressLeaseTime(data)
+ require.Error(t, err, "should get error from bad length")
+}
+
+func TestOptIPAddressLeaseTimeString(t *testing.T) {
+ o := OptIPAddressLeaseTime{LeaseTime: 43200}
+ require.Equal(t, "IP Addresses Lease Time -> 43200", o.String())
+}
diff --git a/dhcpv4/option_maximum_dhcp_message_size.go b/dhcpv4/option_maximum_dhcp_message_size.go
index 05186f5..e5fedc6 100644
--- a/dhcpv4/option_maximum_dhcp_message_size.go
+++ b/dhcpv4/option_maximum_dhcp_message_size.go
@@ -8,7 +8,7 @@ import (
// This option implements the Maximum DHCP Message size option
// https://tools.ietf.org/html/rfc2132
-// OptMaximumDHCPMessageSize represents the DHCP message type option.
+// OptMaximumDHCPMessageSize represents the Maximum DHCP Message size option.
type OptMaximumDHCPMessageSize struct {
Size uint16
}
@@ -16,7 +16,7 @@ type OptMaximumDHCPMessageSize struct {
// ParseOptMaximumDHCPMessageSize constructs an OptMaximumDHCPMessageSize struct from a sequence of
// bytes and returns it, or an error.
func ParseOptMaximumDHCPMessageSize(data []byte) (*OptMaximumDHCPMessageSize, error) {
- // Should at least have code, length, and message type.
+ // Should at least have code, length, and message size.
if len(data) < 4 {
return nil, ErrShortByteStream
}
diff --git a/dhcpv4/option_maximum_dhcp_message_size_test.go b/dhcpv4/option_maximum_dhcp_message_size_test.go
index 65a26fc..f24b499 100644
--- a/dhcpv4/option_maximum_dhcp_message_size_test.go
+++ b/dhcpv4/option_maximum_dhcp_message_size_test.go
@@ -14,7 +14,7 @@ func TestOptMaximumDHCPMessageSizeInterfaceMethods(t *testing.T) {
}
func TestParseOptMaximumDHCPMessageSize(t *testing.T) {
- data := []byte{57, 2, 5, 220} // DISCOVER
+ data := []byte{57, 2, 5, 220}
o, err := ParseOptMaximumDHCPMessageSize(data)
require.NoError(t, err)
require.Equal(t, &OptMaximumDHCPMessageSize{Size: 1500}, o)
diff --git a/dhcpv4/option_router.go b/dhcpv4/option_router.go
new file mode 100644
index 0000000..b9bdff1
--- /dev/null
+++ b/dhcpv4/option_router.go
@@ -0,0 +1,70 @@
+package dhcpv4
+
+import (
+ "fmt"
+ "net"
+)
+
+// This option implements the router option
+// https://tools.ietf.org/html/rfc2132
+
+// OptRouter represents an option encapsulating the routers.
+type OptRouter struct {
+ Routers []net.IP
+}
+
+// ParseOptRouter returns a new OptRouter from a byte stream, or error if any.
+func ParseOptRouter(data []byte) (*OptRouter, error) {
+ if len(data) < 2 {
+ return nil, ErrShortByteStream
+ }
+ code := OptionCode(data[0])
+ if code != OptionRouter {
+ return nil, fmt.Errorf("expected code %v, got %v", OptionRouter, code)
+ }
+ length := int(data[1])
+ if length == 0 || length%4 != 0 {
+ return nil, fmt.Errorf("Invalid length: expected multiple of 4 larger than 4, got %v", length)
+ }
+ if len(data) < 2+length {
+ return nil, ErrShortByteStream
+ }
+ routers := make([]net.IP, 0, length%4)
+ for idx := 0; idx < length; idx += 4 {
+ b := data[2+idx : 2+idx+4]
+ routers = append(routers, net.IPv4(b[0], b[1], b[2], b[3]))
+ }
+ return &OptRouter{Routers: routers}, nil
+}
+
+// Code returns the option code.
+func (o *OptRouter) Code() OptionCode {
+ return OptionRouter
+}
+
+// ToBytes returns a serialized stream of bytes for this option.
+func (o *OptRouter) ToBytes() []byte {
+ ret := []byte{byte(o.Code()), byte(o.Length())}
+ for _, router := range o.Routers {
+ ret = append(ret, router.To4()...)
+ }
+ return ret
+}
+
+// String returns a human-readable string.
+func (o *OptRouter) String() string {
+ var routers string
+ for idx, router := range o.Routers {
+ routers += router.String()
+ if idx < len(o.Routers)-1 {
+ routers += ", "
+ }
+ }
+ return fmt.Sprintf("Routers -> %v", routers)
+}
+
+// Length returns the length of the data portion (excluding option code an byte
+// length).
+func (o *OptRouter) Length() int {
+ return len(o.Routers) * 4
+}
diff --git a/dhcpv4/option_router_test.go b/dhcpv4/option_router_test.go
new file mode 100644
index 0000000..f492c22
--- /dev/null
+++ b/dhcpv4/option_router_test.go
@@ -0,0 +1,65 @@
+package dhcpv4
+
+import (
+ "net"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestOptRoutersInterfaceMethods(t *testing.T) {
+ routers := []net.IP{
+ net.IPv4(192, 168, 0, 10),
+ net.IPv4(192, 168, 0, 20),
+ }
+ o := OptRouter{Routers: routers}
+ require.Equal(t, OptionRouter, o.Code(), "Code")
+ require.Equal(t, net.IPv4len*len(routers), o.Length(), "Length")
+ require.Equal(t, routers, o.Routers, "Routers")
+}
+
+func TestParseOptRouter(t *testing.T) {
+ data := []byte{
+ byte(OptionRouter),
+ 8, // Length
+ 192, 168, 0, 10, // Router #1
+ 192, 168, 0, 20, // Router #2
+ }
+ o, err := ParseOptRouter(data)
+ require.NoError(t, err)
+ routers := []net.IP{
+ net.IPv4(192, 168, 0, 10),
+ net.IPv4(192, 168, 0, 20),
+ }
+ require.Equal(t, &OptRouter{Routers: routers}, o)
+
+ // Short byte stream
+ data = []byte{byte(OptionRouter)}
+ _, err = ParseOptRouter(data)
+ require.Error(t, err, "should get error from short byte stream")
+
+ // Wrong code
+ data = []byte{54, 2, 1, 1}
+ _, err = ParseOptRouter(data)
+ require.Error(t, err, "should get error from wrong code")
+
+ // Bad length
+ data = []byte{byte(OptionRouter), 6, 1, 1, 1}
+ _, err = ParseOptRouter(data)
+ require.Error(t, err, "should get error from bad length")
+}
+
+func TestParseOptRouterNoRouters(t *testing.T) {
+ // RFC2132 requires that at least one Router IP is specified
+ data := []byte{
+ byte(OptionRouter),
+ 0, // Length
+ }
+ _, err := ParseOptRouter(data)
+ require.Error(t, err)
+}
+
+func TestOptRouterString(t *testing.T) {
+ o := OptRouter{Routers: []net.IP{net.IPv4(192, 168, 0, 1), net.IPv4(192, 168, 0, 10)}}
+ require.Equal(t, "Routers -> 192.168.0.1, 192.168.0.10", o.String())
+}
diff --git a/dhcpv4/options.go b/dhcpv4/options.go
index 198fbb0..760bc98 100644
--- a/dhcpv4/options.go
+++ b/dhcpv4/options.go
@@ -42,12 +42,16 @@ func ParseOption(data []byte) (Option, error) {
switch OptionCode(data[0]) {
case OptionSubnetMask:
opt, err = ParseOptSubnetMask(data)
+ case OptionRouter:
+ opt, err = ParseOptRouter(data)
+ case OptionRequestedIPAddress:
+ opt, err = ParseOptRequestedIPAddress(data)
+ case OptionIPAddressLeaseTime:
+ opt, err = ParseOptIPAddressLeaseTime(data)
case OptionDHCPMessageType:
opt, err = ParseOptMessageType(data)
case OptionParameterRequestList:
opt, err = ParseOptParameterRequestList(data)
- case OptionRequestedIPAddress:
- opt, err = ParseOptRequestedIPAddress(data)
case OptionServerIdentifier:
opt, err = ParseOptServerIdentifier(data)
case OptionBroadcastAddress:
diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go
index 01d7427..b3d7605 100644
--- a/dhcpv4/options_test.go
+++ b/dhcpv4/options_test.go
@@ -25,6 +25,14 @@ func TestParseOption(t *testing.T) {
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)
+ require.NoError(t, err)
+ require.Equal(t, OptionRequestedIPAddress, opt.Code(), "Code")
+ require.Equal(t, 4, opt.Length(), "Length")
+ require.Equal(t, option, opt.ToBytes(), "ToBytes")
+
// Message type
option = []byte{53, 1, 1}
opt, err = ParseOption(option)
@@ -41,14 +49,6 @@ func TestParseOption(t *testing.T) {
require.Equal(t, 3, 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)
- require.NoError(t, err)
- require.Equal(t, OptionRequestedIPAddress, opt.Code(), "Code")
- require.Equal(t, 4, 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)