diff options
author | Pablo Mazzini <pmazzini@gmail.com> | 2018-07-29 22:24:53 +0200 |
---|---|---|
committer | Pablo Mazzini <pmazzini@gmail.com> | 2018-07-29 22:24:53 +0200 |
commit | 5d3c53961b899757aef35269e39c11c21e4087fe (patch) | |
tree | 98c5fe50aff204c3edb7004a1003623c29cdad2c | |
parent | 23c03c8fa262c77c68741b079e0e1d2c91397823 (diff) |
add OptRouter
-rw-r--r-- | dhcpv4/option_domain_name_server.go | 2 | ||||
-rw-r--r-- | dhcpv4/option_router.go | 70 | ||||
-rw-r--r-- | dhcpv4/option_router_test.go | 65 | ||||
-rw-r--r-- | dhcpv4/options.go | 6 | ||||
-rw-r--r-- | dhcpv4/options_test.go | 16 |
5 files changed, 148 insertions, 11 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_router.go b/dhcpv4/option_router.go new file mode 100644 index 0000000..6991f89 --- /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 + +// OptDomainRouter 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..4e08699 --- /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(OptionDomainNameServer)} + _, err = ParseOptDomainNameServer(data) + require.Error(t, err, "should get error from short byte stream") + + // Wrong code + data = []byte{54, 2, 1, 1} + _, err = ParseOptDomainNameServer(data) + require.Error(t, err, "should get error from wrong code") + + // Bad length + data = []byte{byte(OptionDomainNameServer), 6, 1, 1, 1} + _, err = ParseOptDomainNameServer(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..4224fa0 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -42,12 +42,14 @@ 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 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) |