diff options
author | Sean Karlage <skarlage@fb.com> | 2018-08-11 14:30:48 -0700 |
---|---|---|
committer | Sean Karlage <skarlage@fb.com> | 2018-08-11 14:32:26 -0700 |
commit | 8ea2525c898436a2a935580de67727bbe7035c85 (patch) | |
tree | 69d35d17c238feabb07ef07a907aae5520104911 | |
parent | 2b05c7d03724d31529886d98f738499ac06ead7e (diff) | |
parent | a6212f1f72e94821a29894fb66656a981bd035d0 (diff) |
Merge branch 'master' into dhcpv4-moar-tests
76 files changed, 1593 insertions, 442 deletions
diff --git a/dhcpv4/client.go b/dhcpv4/client.go index 4b10240..8a44338 100644 --- a/dhcpv4/client.go +++ b/dhcpv4/client.go @@ -124,8 +124,8 @@ func MakeListeningSocket(ifname string) (int, error) { // ordered as Discovery, Offer, Request and Acknowledge. In case of errors, an // error is returned, and the list of DHCPv4 objects will be shorted than 4, // containing all the sent and received DHCPv4 messages. -func (c *Client) Exchange(ifname string, discover *DHCPv4) ([]DHCPv4, error) { - conversation := make([]DHCPv4, 1) +func (c *Client) Exchange(ifname string, discover *DHCPv4, modifiers ...Modifier) ([]*DHCPv4, error) { + conversation := make([]*DHCPv4, 1) var err error // Get our file descriptor for the broadcast socket. @@ -149,28 +149,31 @@ func (c *Client) Exchange(ifname string, discover *DHCPv4) ([]DHCPv4, error) { return conversation, err } } - conversation[0] = *discover + for _, mod := range modifiers { + discover = mod(discover) + } + conversation[0] = discover // Offer offer, err := BroadcastSendReceive(sfd, rfd, discover, c.ReadTimeout, c.WriteTimeout, MessageTypeOffer) if err != nil { return conversation, err } - conversation = append(conversation, *offer) + conversation = append(conversation, offer) // Request - request, err := RequestFromOffer(*offer) + request, err := NewRequestFromOffer(offer, modifiers...) if err != nil { return conversation, err } - conversation = append(conversation, *request) + conversation = append(conversation, request) // Ack ack, err := BroadcastSendReceive(sfd, rfd, request, c.ReadTimeout, c.WriteTimeout, MessageTypeAck) if err != nil { return conversation, err } - conversation = append(conversation, *ack) + conversation = append(conversation, ack) return conversation, nil } diff --git a/dhcpv4/dhcpv4.go b/dhcpv4/dhcpv4.go index eb0f467..d452f6e 100644 --- a/dhcpv4/dhcpv4.go +++ b/dhcpv4/dhcpv4.go @@ -38,6 +38,10 @@ type DHCPv4 struct { options []Option } +// Modifier defines the signature for functions that can modify DHCPv4 +// structures. This is used to simplify packet manipulation +type Modifier func(d *DHCPv4) *DHCPv4 + // GetExternalIPv4Addrs obtains the currently-configured, non-loopback IPv4 // addresses from `addrs` coming from a particular interface (e.g. // net.Interface.Addrs). @@ -157,8 +161,8 @@ func NewInform(hwaddr net.HardwareAddr, localIP net.IP) (*DHCPv4, error) { return d, nil } -// RequestFromOffer builds a DHCPv4 request from an offer. -func RequestFromOffer(offer DHCPv4) (*DHCPv4, error) { +// NewRequestFromOffer builds a DHCPv4 request from an offer. +func NewRequestFromOffer(offer *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) { d, err := New() if err != nil { return nil, err @@ -188,11 +192,14 @@ func RequestFromOffer(offer DHCPv4) (*DHCPv4, error) { d.AddOption(&OptMessageType{MessageType: MessageTypeRequest}) d.AddOption(&OptRequestedIPAddress{RequestedAddr: offer.YourIPAddr()}) d.AddOption(&OptServerIdentifier{ServerID: serverIP}) + for _, mod := range modifiers { + d = mod(d) + } return d, nil } // NewReplyFromRequest builds a DHCPv4 reply from a request. -func NewReplyFromRequest(request *DHCPv4) (*DHCPv4, error) { +func NewReplyFromRequest(request *DHCPv4, modifiers ...Modifier) (*DHCPv4, error) { reply, err := New() if err != nil { return nil, err @@ -205,6 +212,9 @@ func NewReplyFromRequest(request *DHCPv4) (*DHCPv4, error) { reply.SetTransactionID(request.TransactionID()) reply.SetFlags(request.Flags()) reply.SetGatewayIPAddr(request.GatewayIPAddr()) + for _, mod := range modifiers { + reply = mod(reply) + } return reply, nil } @@ -664,10 +674,10 @@ func (d *DHCPv4) ToBytes() []byte { ret = append(ret, u16...) binary.BigEndian.PutUint16(u16, d.flags) ret = append(ret, u16...) - ret = append(ret, d.clientIPAddr[:4]...) - ret = append(ret, d.yourIPAddr[:4]...) - ret = append(ret, d.serverIPAddr[:4]...) - ret = append(ret, d.gatewayIPAddr[:4]...) + ret = append(ret, d.clientIPAddr.To4()...) + ret = append(ret, d.yourIPAddr.To4()...) + ret = append(ret, d.serverIPAddr.To4()...) + ret = append(ret, d.gatewayIPAddr.To4()...) ret = append(ret, d.clientHwAddr[:16]...) ret = append(ret, d.serverHostName[:64]...) ret = append(ret, d.bootFileName[:128]...) diff --git a/dhcpv4/dhcpv4_test.go b/dhcpv4/dhcpv4_test.go index e0afaba..fe9fe0d 100644 --- a/dhcpv4/dhcpv4_test.go +++ b/dhcpv4/dhcpv4_test.go @@ -389,19 +389,19 @@ func TestStrippedOptions(t *testing.T) { } } -func TestRequestFromOffer(t *testing.T) { +func TestDHCPv4NewRequestFromOffer(t *testing.T) { offer, err := New() require.NoError(t, err) offer.SetBroadcast() offer.AddOption(&OptMessageType{MessageType: MessageTypeOffer}) - req, err := RequestFromOffer(*offer) + req, err := NewRequestFromOffer(offer) require.Error(t, err) // Now add the option so it doesn't error out. offer.AddOption(&OptServerIdentifier{ServerID: net.IPv4(192, 168, 0, 1)}) // Broadcast request - req, err = RequestFromOffer(*offer) + req, err = NewRequestFromOffer(offer) require.NoError(t, err) require.NotNil(t, req.MessageType()) require.Equal(t, MessageTypeRequest, *req.MessageType()) @@ -410,12 +410,25 @@ func TestRequestFromOffer(t *testing.T) { // Unicast request offer.SetUnicast() - req, err = RequestFromOffer(*offer) + req, err = NewRequestFromOffer(offer) require.NoError(t, err) require.True(t, req.IsUnicast()) require.False(t, req.IsBroadcast()) } +func TestDHCPv4NewRequestFromOfferWithModifier(t *testing.T) { + offer, err := New() + require.NoError(t, err) + offer.AddOption(&OptMessageType{MessageType: MessageTypeOffer}) + offer.AddOption(&OptServerIdentifier{ServerID: net.IPv4(192, 168, 0, 1)}) + userClass := WithUserClass([]byte("linuxboot"), false) + req, err := NewRequestFromOffer(offer, userClass) + require.NoError(t, err) + require.NotEqual(t, (*MessageType)(nil), *req.MessageType()) + require.Equal(t, MessageTypeRequest, *req.MessageType()) + require.Equal(t, "User Class Information -> linuxboot", req.options[3].String()) +} + func TestNewReplyFromRequest(t *testing.T) { discover, err := New() require.NoError(t, err) @@ -426,7 +439,19 @@ func TestNewReplyFromRequest(t *testing.T) { require.Equal(t, discover.GatewayIPAddr(), reply.GatewayIPAddr()) } -func TestMessageTypeNil(t *testing.T) { +func TestNewReplyFromRequestWithModifier(t *testing.T) { + discover, err := New() + require.NoError(t, err) + discover.SetGatewayIPAddr(net.IPv4(192, 168, 0, 1)) + userClass := WithUserClass([]byte("linuxboot"), false) + reply, err := NewReplyFromRequest(discover, userClass) + require.NoError(t, err) + require.Equal(t, discover.TransactionID(), reply.TransactionID()) + require.Equal(t, discover.GatewayIPAddr(), reply.GatewayIPAddr()) + require.Equal(t, "User Class Information -> linuxboot", reply.options[0].String()) +} + +func TestDHCPv4MessageTypeNil(t *testing.T) { m, err := New() require.NoError(t, err) require.Nil(t, m.MessageType()) diff --git a/dhcpv4/modifiers.go b/dhcpv4/modifiers.go new file mode 100644 index 0000000..3b6ce70 --- /dev/null +++ b/dhcpv4/modifiers.go @@ -0,0 +1,49 @@ +package dhcpv4 + +// WithUserClass adds a user class option to the packet. +// The rfc parameter allows you to specify if the userclass should be +// rfc compliant or not. More details in issue #113 +func WithUserClass(uc []byte, rfc bool) Modifier { + // TODO let the user specify multiple user classes + return func(d *DHCPv4) *DHCPv4 { + ouc := OptUserClass{ + UserClasses: [][]byte{uc}, + Rfc3004: rfc, + } + d.AddOption(&ouc) + return d + } +} + +// WithNetboot adds bootfile URL and bootfile param options to a DHCPv4 packet. +func WithNetboot(d *DHCPv4) *DHCPv4 { + params := d.GetOneOption(OptionParameterRequestList) + + var ( + OptParams *OptParameterRequestList + foundOptionTFTPServerName bool + foundOptionBootfileName bool + ) + if params != nil { + OptParams = params.(*OptParameterRequestList) + for _, option := range OptParams.RequestedOpts { + if option == OptionTFTPServerName { + foundOptionTFTPServerName = true + } else if option == OptionBootfileName { + foundOptionBootfileName = true + } + } + if !foundOptionTFTPServerName { + OptParams.RequestedOpts = append(OptParams.RequestedOpts, OptionTFTPServerName) + } + if !foundOptionBootfileName { + OptParams.RequestedOpts = append(OptParams.RequestedOpts, OptionBootfileName) + } + } else { + OptParams = &OptParameterRequestList{ + RequestedOpts: []OptionCode{OptionTFTPServerName, OptionBootfileName}, + } + d.AddOption(OptParams) + } + return d +} diff --git a/dhcpv4/modifiers_test.go b/dhcpv4/modifiers_test.go new file mode 100644 index 0000000..2f0e1ed --- /dev/null +++ b/dhcpv4/modifiers_test.go @@ -0,0 +1,69 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUserClassModifier(t *testing.T) { + d, _ := New() + userClass := WithUserClass([]byte("linuxboot"), false) + d = userClass(d) + expected := []byte{ + 77, // OptionUserClass + 9, // length + 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } + require.Equal(t, "User Class Information -> linuxboot", d.options[0].String()) + require.Equal(t, expected, d.options[0].ToBytes()) +} + +func TestUserClassModifierRFC(t *testing.T) { + d, _ := New() + userClass := WithUserClass([]byte("linuxboot"), true) + d = userClass(d) + expected := []byte{ + 77, // OptionUserClass + 10, // length + 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } + require.Equal(t, "User Class Information -> linuxboot", d.options[0].String()) + require.Equal(t, expected, d.options[0].ToBytes()) +} + +func TestWithNetboot(t *testing.T) { + d, _ := New() + d = WithNetboot(d) + require.Equal(t, "Parameter Request List -> [TFTP Server Name, Bootfile Name]", d.options[0].String()) +} + +func TestWithNetbootExistingTFTP(t *testing.T) { + d, _ := New() + OptParams := &OptParameterRequestList{ + RequestedOpts: []OptionCode{OptionTFTPServerName}, + } + d.AddOption(OptParams) + d = WithNetboot(d) + require.Equal(t, "Parameter Request List -> [TFTP Server Name, Bootfile Name]", d.options[0].String()) +} + +func TestWithNetbootExistingBootfileName(t *testing.T) { + d, _ := New() + OptParams := &OptParameterRequestList{ + RequestedOpts: []OptionCode{OptionBootfileName}, + } + d.AddOption(OptParams) + d = WithNetboot(d) + require.Equal(t, "Parameter Request List -> [Bootfile Name, TFTP Server Name]", d.options[0].String()) +} + +func TestWithNetbootExistingBoth(t *testing.T) { + d, _ := New() + OptParams := &OptParameterRequestList{ + RequestedOpts: []OptionCode{OptionBootfileName, OptionTFTPServerName}, + } + d.AddOption(OptParams) + d = WithNetboot(d) + require.Equal(t, "Parameter Request List -> [Bootfile Name, TFTP Server Name]", d.options[0].String()) +} 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/option_bootfile_name.go b/dhcpv4/option_bootfile_name.go index 73ea625..ca9317b 100644 --- a/dhcpv4/option_bootfile_name.go +++ b/dhcpv4/option_bootfile_name.go @@ -28,7 +28,7 @@ func (op *OptBootfileName) Length() int { } func (op *OptBootfileName) String() string { - return fmt.Sprintf("OptBootfileName{BootfileName=%s}", op.BootfileName) + return fmt.Sprintf("Bootfile Name -> %s", op.BootfileName) } diff --git a/dhcpv4/option_bootfile_name_test.go b/dhcpv4/option_bootfile_name_test.go index 1f66807..0c7c200 100644 --- a/dhcpv4/option_bootfile_name_test.go +++ b/dhcpv4/option_bootfile_name_test.go @@ -58,3 +58,8 @@ func TestParseOptBootfileNameShortLength(t *testing.T) { require.NoError(t, err) require.Equal(t, []byte("linu"), opt.BootfileName) } + +func TestOptBootfileNameString(t *testing.T) { + o := OptBootfileName{BootfileName: []byte("testy test")} + require.Equal(t, "Bootfile Name -> testy test", o.String()) +} diff --git a/dhcpv4/option_domain_name.go b/dhcpv4/option_domain_name.go index 71d7ef1..e876b05 100644 --- a/dhcpv4/option_domain_name.go +++ b/dhcpv4/option_domain_name.go @@ -2,10 +2,10 @@ package dhcpv4 import "fmt" -// This option implements the server domani name option +// This option implements the domain name option // https://tools.ietf.org/html/rfc2132 -// OptDomainName represents an option encapsulating the server identifier. +// OptDomainName represents an option encapsulating the domain name. type OptDomainName struct { DomainName string } @@ -13,7 +13,7 @@ type OptDomainName struct { // ParseOptDomainName returns a new OptDomainName from a byte // stream, or error if any. func ParseOptDomainName(data []byte) (*OptDomainName, error) { - if len(data) < 2 { + if len(data) < 3 { return nil, ErrShortByteStream } code := OptionCode(data[0]) 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_domain_search.go b/dhcpv4/option_domain_search.go new file mode 100644 index 0000000..daade0a --- /dev/null +++ b/dhcpv4/option_domain_search.go @@ -0,0 +1,63 @@ +package dhcpv4 + +// This module defines the OptDomainSearch structure. +// https://tools.ietf.org/html/rfc3397 + +import ( + "fmt" + + "github.com/insomniacslk/dhcp/rfc1035label" +) + +// OptDomainSearch represents an option encapsulating a domain search list. +type OptDomainSearch struct { + DomainSearch []string +} + +// Code returns the option code. +func (op *OptDomainSearch) Code() OptionCode { + return OptionDNSDomainSearchList +} + +// ToBytes returns a serialized stream of bytes for this option. +func (op *OptDomainSearch) ToBytes() []byte { + buf := []byte{byte(op.Code()), byte(op.Length())} + buf = append(buf, rfc1035label.LabelsToBytes(op.DomainSearch)...) + return buf +} + +// Length returns the length of the data portion (excluding option code an byte +// length). +func (op *OptDomainSearch) Length() int { + var length int + for _, label := range op.DomainSearch { + length += len(label) + 2 // add the first and the last length bytes + } + return length +} + +// String returns a human-readable string. +func (op *OptDomainSearch) String() string { + return fmt.Sprintf("DNS Domain Search List -> %v", op.DomainSearch) +} + +// ParseOptDomainSearch returns a new OptDomainSearch from a byte stream, or +// error if any. +func ParseOptDomainSearch(data []byte) (*OptDomainSearch, error) { + if len(data) < 2 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionDNSDomainSearchList { + return nil, fmt.Errorf("expected code %v, got %v", OptionDNSDomainSearchList, code) + } + length := int(data[1]) + if len(data) < 2+length { + return nil, ErrShortByteStream + } + domainSearch, err := rfc1035label.LabelsFromBytes(data[2:length+2]) + if err != nil { + return nil, err + } + return &OptDomainSearch{DomainSearch: domainSearch}, nil +} diff --git a/dhcpv4/option_domain_search_test.go b/dhcpv4/option_domain_search_test.go new file mode 100644 index 0000000..4848a83 --- /dev/null +++ b/dhcpv4/option_domain_search_test.go @@ -0,0 +1,37 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseOptDomainSearch(t *testing.T) { + data := []byte{ + 119, // OptionDNSDomainSearchList + 33, // length + 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, + 6, 's', 'u', 'b', 'n', 'e', 't', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'o', 'r', 'g', 0, + } + opt, err := ParseOptDomainSearch(data) + require.NoError(t, err) + require.Equal(t, len(opt.DomainSearch), 2) + require.Equal(t, opt.DomainSearch[0], "example.com") + require.Equal(t, opt.DomainSearch[1], "subnet.example.org") +} + +func TestOptDomainSearchToBytes(t *testing.T) { + expected := []byte{ + 119, // OptionDNSDomainSearchList + 33, // length + 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, + 6, 's', 'u', 'b', 'n', 'e', 't', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'o', 'r', 'g', 0, + } + opt := OptDomainSearch{ + DomainSearch: []string{ + "example.com", + "subnet.example.org", + }, + } + require.Equal(t, opt.ToBytes(), expected) +} diff --git a/dhcpv4/option_host_name.go b/dhcpv4/option_host_name.go new file mode 100644 index 0000000..a922a2b --- /dev/null +++ b/dhcpv4/option_host_name.go @@ -0,0 +1,49 @@ +package dhcpv4 + +import "fmt" + +// This option implements the host name option +// https://tools.ietf.org/html/rfc2132 + +// OptHostName represents an option encapsulating the host name. +type OptHostName struct { + HostName string +} + +// ParseOptHostName returns a new OptHostName from a byte stream, or error if +// any. +func ParseOptHostName(data []byte) (*OptHostName, error) { + if len(data) < 3 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionHostName { + return nil, fmt.Errorf("expected code %v, got %v", OptionHostName, code) + } + length := int(data[1]) + if len(data) < 2+length { + return nil, ErrShortByteStream + } + return &OptHostName{HostName: string(data[2 : 2+length])}, nil +} + +// Code returns the option code. +func (o *OptHostName) Code() OptionCode { + return OptionHostName +} + +// ToBytes returns a serialized stream of bytes for this option. +func (o *OptHostName) ToBytes() []byte { + return append([]byte{byte(o.Code()), byte(o.Length())}, []byte(o.HostName)...) +} + +// String returns a human-readable string. +func (o *OptHostName) String() string { + return fmt.Sprintf("Host Name -> %v", o.HostName) +} + +// Length returns the length of the data portion (excluding option code an byte +// length). +func (o *OptHostName) Length() int { + return len(o.HostName) +} diff --git a/dhcpv4/option_host_name_test.go b/dhcpv4/option_host_name_test.go new file mode 100644 index 0000000..7f99100 --- /dev/null +++ b/dhcpv4/option_host_name_test.go @@ -0,0 +1,41 @@ +package dhcpv4 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptHostNameInterfaceMethods(t *testing.T) { + o := OptHostName{HostName: "foo"} + require.Equal(t, OptionHostName, o.Code(), "Code") + require.Equal(t, 3, o.Length(), "Length") + require.Equal(t, []byte{byte(OptionHostName), 3, 'f', 'o', 'o'}, o.ToBytes(), "ToBytes") +} + +func TestParseOptHostName(t *testing.T) { + data := []byte{byte(OptionHostName), 4, 't', 'e', 's', 't'} + o, err := ParseOptHostName(data) + require.NoError(t, err) + require.Equal(t, &OptHostName{HostName: "test"}, o) + + // Short byte stream + data = []byte{byte(OptionHostName)} + _, err = ParseOptHostName(data) + require.Error(t, err, "should get error from short byte stream") + + // Wrong code + data = []byte{54, 2, 1, 1} + _, err = ParseOptHostName(data) + require.Error(t, err, "should get error from wrong code") + + // Bad length + data = []byte{byte(OptionHostName), 6, 1, 1, 1} + _, err = ParseOptHostName(data) + require.Error(t, err, "should get error from bad length") +} + +func TestOptHostNameString(t *testing.T) { + o := OptHostName{HostName: "testy test"} + require.Equal(t, "Host Name -> testy test", o.String()) +} 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_ntp_servers.go b/dhcpv4/option_ntp_servers.go new file mode 100644 index 0000000..39881d6 --- /dev/null +++ b/dhcpv4/option_ntp_servers.go @@ -0,0 +1,70 @@ +package dhcpv4 + +import ( + "fmt" + "net" +) + +// This option implements the network time protocol servers option +// https://tools.ietf.org/html/rfc2132 + +// OptNTPServers represents an option encapsulating the NTP servers. +type OptNTPServers struct { + NTPServers []net.IP +} + +// ParseOptNTPServers returns a new OptNTPServers from a byte stream, or error if any. +func ParseOptNTPServers(data []byte) (*OptNTPServers, error) { + if len(data) < 2 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionNTPServers { + return nil, fmt.Errorf("expected code %v, got %v", OptionNTPServers, 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 + } + ntpServers := make([]net.IP, 0, length%4) + for idx := 0; idx < length; idx += 4 { + b := data[2+idx : 2+idx+4] + ntpServers = append(ntpServers, net.IPv4(b[0], b[1], b[2], b[3])) + } + return &OptNTPServers{NTPServers: ntpServers}, nil +} + +// Code returns the option code. +func (o *OptNTPServers) Code() OptionCode { + return OptionNTPServers +} + +// ToBytes returns a serialized stream of bytes for this option. +func (o *OptNTPServers) ToBytes() []byte { + ret := []byte{byte(o.Code()), byte(o.Length())} + for _, ntp := range o.NTPServers { + ret = append(ret, ntp.To4()...) + } + return ret +} + +// String returns a human-readable string. +func (o *OptNTPServers) String() string { + var ntpServers string + for idx, ntp := range o.NTPServers { + ntpServers += ntp.String() + if idx < len(o.NTPServers)-1 { + ntpServers += ", " + } + } + return fmt.Sprintf("NTP Servers -> %v", ntpServers) +} + +// Length returns the length of the data portion (excluding option code an byte +// length). +func (o *OptNTPServers) Length() int { + return len(o.NTPServers) * 4 +} diff --git a/dhcpv4/option_ntp_servers_test.go b/dhcpv4/option_ntp_servers_test.go new file mode 100644 index 0000000..e7bcefd --- /dev/null +++ b/dhcpv4/option_ntp_servers_test.go @@ -0,0 +1,65 @@ +package dhcpv4 + +import ( + "net" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptNTPServersInterfaceMethods(t *testing.T) { + ntpServers := []net.IP{ + net.IPv4(192, 168, 0, 10), + net.IPv4(192, 168, 0, 20), + } + o := OptNTPServers{NTPServers: ntpServers} + require.Equal(t, OptionNTPServers, o.Code(), "Code") + require.Equal(t, net.IPv4len*len(ntpServers), o.Length(), "Length") + require.Equal(t, ntpServers, o.NTPServers, "NTPServers") +} + +func TestParseOptNTPServers(t *testing.T) { + data := []byte{ + byte(OptionNTPServers), + 8, // Length + 192, 168, 0, 10, // NTP server #1 + 192, 168, 0, 20, // NTP server #2 + } + o, err := ParseOptNTPServers(data) + require.NoError(t, err) + ntpServers := []net.IP{ + net.IPv4(192, 168, 0, 10), + net.IPv4(192, 168, 0, 20), + } + require.Equal(t, &OptNTPServers{NTPServers: ntpServers}, o) + + // Short byte stream + data = []byte{byte(OptionNTPServers)} + _, err = ParseOptNTPServers(data) + require.Error(t, err, "should get error from short byte stream") + + // Wrong code + data = []byte{54, 2, 1, 1} + _, err = ParseOptNTPServers(data) + require.Error(t, err, "should get error from wrong code") + + // Bad length + data = []byte{byte(OptionNTPServers), 6, 1, 1, 1} + _, err = ParseOptNTPServers(data) + require.Error(t, err, "should get error from bad length") +} + +func TestParseOptNTPserversNoNTPServers(t *testing.T) { + // RFC2132 requires that at least one NTP server IP is specified + data := []byte{ + byte(OptionNTPServers), + 0, // Length + } + _, err := ParseOptNTPServers(data) + require.Error(t, err) +} + +func TestOptNTPServersString(t *testing.T) { + o := OptNTPServers{NTPServers: []net.IP{net.IPv4(192, 168, 0, 1), net.IPv4(192, 168, 0, 10)}} + require.Equal(t, "NTP Servers -> 192.168.0.1, 192.168.0.10", o.String()) +} diff --git a/dhcpv4/option_router.go b/dhcpv4/option_router.go new file mode 100644 index 0000000..3154edd --- /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/option_subnet_mask.go b/dhcpv4/option_subnet_mask.go new file mode 100644 index 0000000..f1ff4a4 --- /dev/null +++ b/dhcpv4/option_subnet_mask.go @@ -0,0 +1,56 @@ +package dhcpv4 + +import ( + "fmt" + "net" +) + +// This option implements the subnet mask option +// https://tools.ietf.org/html/rfc2132 + +// OptSubnetMask represents an option encapsulating the subnet mask. +type OptSubnetMask struct { + SubnetMask net.IPMask +} + +// ParseOptSubnetMask returns a new OptSubnetMask from a byte +// stream, or error if any. +func ParseOptSubnetMask(data []byte) (*OptSubnetMask, error) { + if len(data) < 2 { + return nil, ErrShortByteStream + } + code := OptionCode(data[0]) + if code != OptionSubnetMask { + return nil, fmt.Errorf("expected code %v, got %v", OptionSubnetMask, code) + } + length := int(data[1]) + if length != 4 { + return nil, fmt.Errorf("unexepcted length: expected 4, got %v", length) + } + if len(data) < 6 { + return nil, ErrShortByteStream + } + return &OptSubnetMask{SubnetMask: net.IPMask(data[2 : 2+length])}, nil +} + +// Code returns the option code. +func (o *OptSubnetMask) Code() OptionCode { + return OptionSubnetMask +} + +// ToBytes returns a serialized stream of bytes for this option. +func (o *OptSubnetMask) ToBytes() []byte { + ret := []byte{byte(o.Code()), byte(o.Length())} + return append(ret, o.SubnetMask[:4]...) +} + +// String returns a human-readable string. +func (o *OptSubnetMask) String() string { + return fmt.Sprintf("Subnet Mask -> %v", o.SubnetMask.String()) +} + +// Length returns the length of the data portion (excluding option code an byte +// length). +func (o *OptSubnetMask) Length() int { + return 4 +} diff --git a/dhcpv4/option_subnet_mask_test.go b/dhcpv4/option_subnet_mask_test.go new file mode 100644 index 0000000..4cb8819 --- /dev/null +++ b/dhcpv4/option_subnet_mask_test.go @@ -0,0 +1,44 @@ +package dhcpv4 + +import ( + "net" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestOptSubnetMaskInterfaceMethods(t *testing.T) { + mask := net.IPMask{255, 255, 255, 0} + o := OptSubnetMask{SubnetMask: mask} + + require.Equal(t, OptionSubnetMask, o.Code(), "Code") + + expectedBytes := []byte{1, 4, 255, 255, 255, 0} + require.Equal(t, expectedBytes, o.ToBytes(), "ToBytes") + + require.Equal(t, 4, o.Length(), "Length") + + require.Equal(t, "Subnet Mask -> ffffff00", o.String(), "String") +} + +func TestParseOptSubnetMask(t *testing.T) { + var ( + o *OptSubnetMask + err error + ) + o, err = ParseOptSubnetMask([]byte{}) + require.Error(t, err, "empty byte stream") + + o, err = ParseOptSubnetMask([]byte{1, 4, 255}) + require.Error(t, err, "short byte stream") + + o, err = ParseOptSubnetMask([]byte{1, 3, 255, 255, 255, 0}) + require.Error(t, err, "wrong IP length") + + o, err = ParseOptSubnetMask([]byte{2, 4, 255, 255, 255}) + require.Error(t, err, "wrong option code") + + o, err = ParseOptSubnetMask([]byte{1, 4, 255, 255, 255, 0}) + require.NoError(t, err) + require.Equal(t, net.IPMask{255, 255, 255, 0}, o.SubnetMask) +} diff --git a/dhcpv4/option_tftp_server_name.go b/dhcpv4/option_tftp_server_name.go index b4029e1..19dde21 100644 --- a/dhcpv4/option_tftp_server_name.go +++ b/dhcpv4/option_tftp_server_name.go @@ -28,7 +28,7 @@ func (op *OptTFTPServerName) Length() int { } func (op *OptTFTPServerName) String() string { - return fmt.Sprintf("OptTFTPServerName{TFTPServerName=%s}", op.TFTPServerName) + return fmt.Sprintf("TFTP Server Name -> %s", op.TFTPServerName) } // ParseOptTFTPServerName returns a new OptTFTPServerName fomr a byte stream or error if any diff --git a/dhcpv4/option_tftp_server_name_test.go b/dhcpv4/option_tftp_server_name_test.go index caddf95..812210f 100644 --- a/dhcpv4/option_tftp_server_name_test.go +++ b/dhcpv4/option_tftp_server_name_test.go @@ -58,3 +58,8 @@ func TestParseOptTFTPServerNameShortLength(t *testing.T) { require.NoError(t, err) require.Equal(t, []byte("linu"), opt.TFTPServerName) } + +func TestOptTFTPServerNameString(t *testing.T) { + o := OptTFTPServerName{TFTPServerName: []byte("testy test")} + require.Equal(t, "TFTP Server Name -> testy test", o.String()) +} diff --git a/dhcpv4/option_userclass.go b/dhcpv4/option_userclass.go index 1505dbb..d6ddabc 100644 --- a/dhcpv4/option_userclass.go +++ b/dhcpv4/option_userclass.go @@ -12,6 +12,7 @@ import ( // OptUserClass represents an option encapsulating User Classes. type OptUserClass struct { UserClasses [][]byte + Rfc3004 bool } // Code returns the option code @@ -22,6 +23,9 @@ func (op *OptUserClass) Code() OptionCode { // ToBytes serializes the option and returns it as a sequence of bytes func (op *OptUserClass) ToBytes() []byte { buf := []byte{byte(op.Code()), byte(op.Length())} + if !op.Rfc3004 { + return append(buf, op.UserClasses[0]...) + } for _, uc := range op.UserClasses { buf = append(buf, byte(len(uc))) buf = append(buf, uc...) @@ -32,6 +36,9 @@ func (op *OptUserClass) ToBytes() []byte { // Length returns the option length func (op *OptUserClass) Length() int { ret := 0 + if !op.Rfc3004 { + return len(op.UserClasses[0]) + } for _, uc := range op.UserClasses { ret += 1 + len(uc) } @@ -39,11 +46,15 @@ func (op *OptUserClass) Length() int { } func (op *OptUserClass) String() string { - ucStrings := make([]string, len(op.UserClasses)) - for _, uc := range op.UserClasses { - ucStrings = append(ucStrings, string(uc)) + ucStrings := make([]string, 0, len(op.UserClasses)) + if !op.Rfc3004 { + ucStrings = append(ucStrings, string(op.UserClasses[0])) + } else { + for _, uc := range op.UserClasses { + ucStrings = append(ucStrings, string(uc)) + } } - return fmt.Sprintf("OptUserClass{userclass=[%s]}", strings.Join(ucStrings, ", ")) + return fmt.Sprintf("User Class Information -> %v", strings.Join(ucStrings, ", ")) } // ParseOptUserClass returns a new OptUserClass from a byte stream or @@ -51,7 +62,7 @@ func (op *OptUserClass) String() string { func ParseOptUserClass(data []byte) (*OptUserClass, error) { opt := OptUserClass{} - if len(data) < 4 { + if len(data) < 3 { return nil, ErrShortByteStream } code := OptionCode(data[0]) @@ -66,6 +77,23 @@ func ParseOptUserClass(data []byte) (*OptUserClass, error) { totalLength, len(data)) } + // Check if option is Microsoft style instead of RFC compliant, issue #113 + + // User-class options are, according to RFC3004, supposed to contain a set + // of strings each with length UC_Len_i. Here we check that this is so, + // by seeing if all the UC_Len_i lengths are consistent with the overall + // option length. If the lengths don't add up, we assume that the option + // is a single string and non RFC3004 compliant + var counting int + for counting < totalLength { + // UC_Len_i does not include itself so add 1 + counting += int(data[counting]) + 1 + } + if counting != totalLength { + opt.UserClasses = append(opt.UserClasses, data[:totalLength]) + return &opt, nil + } + opt.Rfc3004 = true for i := 0; i < totalLength; { ucLen := int(data[i]) if ucLen == 0 { diff --git a/dhcpv4/option_userclass_test.go b/dhcpv4/option_userclass_test.go index 5b71ea5..f6039df 100644 --- a/dhcpv4/option_userclass_test.go +++ b/dhcpv4/option_userclass_test.go @@ -9,16 +9,30 @@ import ( func TestOptUserClassToBytes(t *testing.T) { opt := OptUserClass{ UserClasses: [][]byte{[]byte("linuxboot")}, + Rfc3004: true, } data := opt.ToBytes() expected := []byte{ - 77, // OPTION_USER_CLASS + 77, // OptionUserClass 10, // length 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', } require.Equal(t, expected, data) } +func TestOptUserClassMicrosoftToBytes(t *testing.T) { + opt := OptUserClass{ + UserClasses: [][]byte{[]byte("linuxboot")}, + } + data := opt.ToBytes() + expected := []byte{ + 77, // OptionUserClass + 9, // length + 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } + require.Equal(t, expected, data) +} + func TestParseOptUserClassMultiple(t *testing.T) { expected := []byte{ 77, 15, @@ -38,6 +52,36 @@ func TestParseOptUserClassNone(t *testing.T) { require.Error(t, err) } +func TestParseOptUserClassMicrosoft(t *testing.T) { + expected := []byte{ + 77, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', + } + opt, err := ParseOptUserClass(expected) + require.NoError(t, err) + require.Equal(t, 1, len(opt.UserClasses)) + require.Equal(t, []byte("linuxboot"), opt.UserClasses[0]) +} + +func TestParseOptUserClassMicrosoftShort(t *testing.T) { + expected := []byte{ + 77, 1, 'l', + } + opt, err := ParseOptUserClass(expected) + require.NoError(t, err) + require.Equal(t, 1, len(opt.UserClasses)) + require.Equal(t, []byte("l"), opt.UserClasses[0]) +} + +func TestParseOptUserClassMicrosoftLongerThanLength(t *testing.T) { + expected := []byte{ + 77, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', 'X', + } + opt, err := ParseOptUserClass(expected) + require.NoError(t, err) + require.Equal(t, 1, len(opt.UserClasses)) + require.Equal(t, []byte("linuxboot"), opt.UserClasses[0]) +} + func TestParseOptUserClass(t *testing.T) { expected := []byte{ 77, 10, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', @@ -54,10 +98,11 @@ func TestOptUserClassToBytesMultiple(t *testing.T) { []byte("linuxboot"), []byte("test"), }, + Rfc3004: true, } data := opt.ToBytes() expected := []byte{ - 77, // OPTION_USER_CLASS + 77, // OptionUserClass 15, // length 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', 4, 't', 'e', 's', 't', @@ -75,14 +120,6 @@ func TestParseOptUserClassLongerThanLength(t *testing.T) { require.Equal(t, []byte("linuxboot"), opt.UserClasses[0]) } -func TestParseOptUserClassShorterThanLength(t *testing.T) { - expected := []byte{ - 77, 10, 10, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', - } - _, err := ParseOptUserClass(expected) - require.Error(t, err) -} - func TestParseOptUserClassShorterTotalLength(t *testing.T) { expected := []byte{ 77, 11, 10, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', diff --git a/dhcpv4/options.go b/dhcpv4/options.go index 5e6da80..d869b7d 100644 --- a/dhcpv4/options.go +++ b/dhcpv4/options.go @@ -40,32 +40,46 @@ func ParseOption(data []byte) (Option, error) { err error ) switch OptionCode(data[0]) { - case OptionDHCPMessageType: - opt, err = ParseOptMessageType(data) - case OptionParameterRequestList: - opt, err = ParseOptParameterRequestList(data) + case OptionSubnetMask: + 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: opt, err = ParseOptRequestedIPAddress(data) + case OptionIPAddressLeaseTime: + opt, err = ParseOptIPAddressLeaseTime(data) + case OptionDHCPMessageType: + opt, err = ParseOptMessageType(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: opt, err = ParseOptionGeneric(data) } diff --git a/dhcpv4/options_test.go b/dhcpv4/options_test.go index 41ef415..0268483 100644 --- a/dhcpv4/options_test.go +++ b/dhcpv4/options_test.go @@ -17,20 +17,52 @@ func TestParseOption(t *testing.T) { require.Equal(t, 4, generic.Length()) require.Equal(t, "Name Server -> [192 168 1 254]", generic.String()) - // Message type - option = []byte{53, 1, 1} + // Option subnet mask + option = []byte{1, 4, 255, 255, 255, 0} opt, err = ParseOption(option) require.NoError(t, err) - require.Equal(t, OptionDHCPMessageType, opt.Code(), "Code") - require.Equal(t, 1, opt.Length(), "Length") + require.Equal(t, OptionSubnetMask, opt.Code(), "Code") + require.Equal(t, 4, opt.Length(), "Length") require.Equal(t, option, opt.ToBytes(), "ToBytes") - // Parameter request list - option = []byte{55, 3, 5, 53, 61} + // Option router + option = []byte{3, 4, 192, 168, 1, 1} 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, 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 @@ -41,6 +73,14 @@ func TestParseOption(t *testing.T) { 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) + require.NoError(t, err) + require.Equal(t, OptionDHCPMessageType, opt.Code(), "Code") + require.Equal(t, 1, 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) @@ -49,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) @@ -88,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) { diff --git a/dhcpv6/client.go b/dhcpv6/client.go index 8431482..3492a47 100644 --- a/dhcpv6/client.go +++ b/dhcpv6/client.go @@ -81,16 +81,16 @@ func (c *Client) sendReceive(ifname string, packet DHCPv6, expectedType MessageT if packet == nil { return nil, fmt.Errorf("Packet to send cannot be nil") } - if expectedType == MSGTYPE_NONE { + if expectedType == MessageTypeNone { // infer the expected type from the packet being sent - if packet.Type() == SOLICIT { - expectedType = ADVERTISE - } else if packet.Type() == REQUEST { - expectedType = REPLY - } else if packet.Type() == RELAY_FORW { - expectedType = RELAY_REPL - } else if packet.Type() == LEASEQUERY { - expectedType = LEASEQUERY_REPLY + if packet.Type() == MessageTypeSolicit { + expectedType = MessageTypeAdvertise + } else if packet.Type() == MessageTypeRequest { + expectedType = MessageTypeReply + } else if packet.Type() == MessageTypeRelayForward { + expectedType = MessageTypeRelayReply + } else if packet.Type() == MessageTypeLeaseQuery { + expectedType = MessageTypeLeaseQueryReply } // and probably more } // if no LocalAddr is specified, get the interface's link-local address @@ -167,7 +167,7 @@ func (c *Client) sendReceive(ifname string, packet DHCPv6, expectedType MessageT continue } } - if expectedType == MSGTYPE_NONE { + if expectedType == MessageTypeNone { // just take whatever arrived break } else if adv.Type() == expectedType { @@ -190,7 +190,7 @@ func (c *Client) Solicit(ifname string, solicit DHCPv6, modifiers ...Modifier) ( for _, mod := range modifiers { solicit = mod(solicit) } - advertise, err := c.sendReceive(ifname, solicit, MSGTYPE_NONE) + advertise, err := c.sendReceive(ifname, solicit, MessageTypeNone) return solicit, advertise, err } @@ -207,6 +207,6 @@ func (c *Client) Request(ifname string, advertise, request DHCPv6, modifiers ... for _, mod := range modifiers { request = mod(request) } - reply, err := c.sendReceive(ifname, request, MSGTYPE_NONE) + reply, err := c.sendReceive(ifname, request, MessageTypeNone) return request, reply, err } diff --git a/dhcpv6/dhcpv6.go b/dhcpv6/dhcpv6.go index b424101..0dabca5 100644 --- a/dhcpv6/dhcpv6.go +++ b/dhcpv6/dhcpv6.go @@ -30,7 +30,7 @@ func FromBytes(data []byte) (DHCPv6, error) { headerSize int messageType = MessageType(data[0]) ) - if messageType == RELAY_FORW || messageType == RELAY_REPL { + if messageType == MessageTypeRelayForward || messageType == MessageTypeRelayReply { isRelay = true } if isRelay { @@ -85,7 +85,7 @@ func NewMessage(modifiers ...Modifier) (DHCPv6, error) { return nil, err } msg := DHCPv6Message{ - messageType: SOLICIT, + messageType: MessageTypeSolicit, transactionID: *tid, } // apply modifiers @@ -134,7 +134,7 @@ func DecapsulateRelay(l DHCPv6) (DHCPv6, error) { if !l.IsRelay() { return l, nil } - opt := l.GetOneOption(OPTION_RELAY_MSG) + opt := l.GetOneOption(OptionRelayMsg) if opt == nil { return nil, fmt.Errorf("No OptRelayMsg found") } @@ -181,7 +181,7 @@ func DecapsulateRelayIndex(l DHCPv6, index int) (DHCPv6, error) { // message as payload. The passed message type must be either RELAY_FORW or // RELAY_REPL func EncapsulateRelay(d DHCPv6, mType MessageType, linkAddr, peerAddr net.IP) (DHCPv6, error) { - if mType != RELAY_FORW && mType != RELAY_REPL { + if mType != MessageTypeRelayForward && mType != MessageTypeRelayReply { return nil, fmt.Errorf("Message type must be either RELAY_FORW or RELAY_REPL") } outer := DHCPv6Relay{ diff --git a/dhcpv6/dhcpv6_test.go b/dhcpv6/dhcpv6_test.go index 3e2b6bc..4a44e0e 100644 --- a/dhcpv6/dhcpv6_test.go +++ b/dhcpv6/dhcpv6_test.go @@ -33,18 +33,18 @@ func TestNewMessage(t *testing.T) { d, err := NewMessage() require.NoError(t, err) require.NotNil(t, d) - require.Equal(t, SOLICIT, d.Type()) + require.Equal(t, MessageTypeSolicit, d.Type()) require.NotEqual(t, 0, d.(*DHCPv6Message).transactionID) require.Empty(t, d.(*DHCPv6Message).options) } func TestDecapsulateRelayIndex(t *testing.T) { m := DHCPv6Message{} - r1, err := EncapsulateRelay(&m, RELAY_FORW, net.IPv6linklocalallnodes, net.IPv6interfacelocalallnodes) + r1, err := EncapsulateRelay(&m, MessageTypeRelayForward, net.IPv6linklocalallnodes, net.IPv6interfacelocalallnodes) require.NoError(t, err) - r2, err := EncapsulateRelay(r1, RELAY_FORW, net.IPv6loopback, net.IPv6linklocalallnodes) + r2, err := EncapsulateRelay(r1, MessageTypeRelayForward, net.IPv6loopback, net.IPv6linklocalallnodes) require.NoError(t, err) - r3, err := EncapsulateRelay(r2, RELAY_FORW, net.IPv6unspecified, net.IPv6linklocalallrouters) + r3, err := EncapsulateRelay(r2, MessageTypeRelayForward, net.IPv6unspecified, net.IPv6linklocalallrouters) require.NoError(t, err) first, err := DecapsulateRelayIndex(r3, 0) @@ -83,10 +83,10 @@ func TestDecapsulateRelayIndex(t *testing.T) { func TestSettersAndGetters(t *testing.T) { d := DHCPv6Message{} // Message - d.SetMessage(SOLICIT) - require.Equal(t, SOLICIT, d.Type()) - d.SetMessage(ADVERTISE) - require.Equal(t, ADVERTISE, d.Type()) + d.SetMessage(MessageTypeSolicit) + require.Equal(t, MessageTypeSolicit, d.Type()) + d.SetMessage(MessageTypeAdvertise) + require.Equal(t, MessageTypeAdvertise, d.Type()) // TransactionID d.SetTransactionID(12345) @@ -109,7 +109,7 @@ func TestAddOption(t *testing.T) { func TestToBytes(t *testing.T) { d := DHCPv6Message{} - d.SetMessage(SOLICIT) + d.SetMessage(MessageTypeSolicit) d.SetTransactionID(0xabcdef) opt := OptionGeneric{OptionCode: 0, OptionData: []byte{}} d.AddOption(&opt) @@ -128,7 +128,7 @@ func TestFromAndToBytes(t *testing.T) { func TestNewAdvertiseFromSolicit(t *testing.T) { s := DHCPv6Message{} - s.SetMessage(SOLICIT) + s.SetMessage(MessageTypeSolicit) s.SetTransactionID(0xabcdef) cid := OptClientId{} s.AddOption(&cid) @@ -137,7 +137,7 @@ func TestNewAdvertiseFromSolicit(t *testing.T) { a, err := NewAdvertiseFromSolicit(&s, WithServerID(duid)) require.NoError(t, err) require.Equal(t, a.(*DHCPv6Message).TransactionID(), s.TransactionID()) - require.Equal(t, a.Type(), ADVERTISE) + require.Equal(t, a.Type(), MessageTypeAdvertise) } func TestNewReplyFromDHCPv6Message(t *testing.T) { @@ -150,31 +150,31 @@ func TestNewReplyFromDHCPv6Message(t *testing.T) { sid.Sid = duid msg.AddOption(&sid) - msg.SetMessage(CONFIRM) + msg.SetMessage(MessageTypeConfirm) rep, err := NewReplyFromDHCPv6Message(&msg, WithServerID(duid)) require.NoError(t, err) require.Equal(t, rep.(*DHCPv6Message).TransactionID(), msg.TransactionID()) - require.Equal(t, rep.Type(), REPLY) + require.Equal(t, rep.Type(), MessageTypeReply) - msg.SetMessage(RENEW) + msg.SetMessage(MessageTypeRenew) rep, err = NewReplyFromDHCPv6Message(&msg, WithServerID(duid)) require.NoError(t, err) require.Equal(t, rep.(*DHCPv6Message).TransactionID(), msg.TransactionID()) - require.Equal(t, rep.Type(), REPLY) + require.Equal(t, rep.Type(), MessageTypeReply) - msg.SetMessage(REBIND) + msg.SetMessage(MessageTypeRebind) rep, err = NewReplyFromDHCPv6Message(&msg, WithServerID(duid)) require.NoError(t, err) require.Equal(t, rep.(*DHCPv6Message).TransactionID(), msg.TransactionID()) - require.Equal(t, rep.Type(), REPLY) + require.Equal(t, rep.Type(), MessageTypeReply) - msg.SetMessage(RELEASE) + msg.SetMessage(MessageTypeRelease) rep, err = NewReplyFromDHCPv6Message(&msg, WithServerID(duid)) require.NoError(t, err) require.Equal(t, rep.(*DHCPv6Message).TransactionID(), msg.TransactionID()) - require.Equal(t, rep.Type(), REPLY) + require.Equal(t, rep.Type(), MessageTypeReply) - msg.SetMessage(SOLICIT) + msg.SetMessage(MessageTypeSolicit) rep, err = NewReplyFromDHCPv6Message(&msg) require.Error(t, err) @@ -183,7 +183,7 @@ func TestNewReplyFromDHCPv6Message(t *testing.T) { require.Error(t, err) } -func TestNewSolicitWithCID(t *testing.T) { +func TestNewMessageTypeSolicitWithCID(t *testing.T) { hwAddr, err := net.ParseMAC("24:0A:9E:9F:EB:2B") require.NoError(t, err) @@ -196,24 +196,24 @@ func TestNewSolicitWithCID(t *testing.T) { s, err := NewSolicitWithCID(duid) require.NoError(t, err) - require.Equal(t, s.Type(), SOLICIT) + require.Equal(t, s.Type(), MessageTypeSolicit) // Check CID - cidOption := s.GetOneOption(OPTION_CLIENTID) + cidOption := s.GetOneOption(OptionClientID) require.NotNil(t, cidOption) cid, ok := cidOption.(*OptClientId) require.True(t, ok) require.Equal(t, cid.Cid, duid) // Check ORO - oroOption := s.GetOneOption(OPTION_ORO) + oroOption := s.GetOneOption(OptionORO) require.NotNil(t, oroOption) oro, ok := oroOption.(*OptRequestedOption) require.True(t, ok) opts := oro.RequestedOptions() - require.Contains(t, opts, DNS_RECURSIVE_NAME_SERVER) - require.Contains(t, opts, DOMAIN_SEARCH_LIST) + require.Contains(t, opts, OptionDNSRecursiveNameServer) + require.Contains(t, opts, OptionDomainSearchList) require.Equal(t, len(opts), 2) } -// TODO test NewSolicit +// TODO test NewMessageTypeSolicit // test String and Summary diff --git a/dhcpv6/dhcpv6message.go b/dhcpv6/dhcpv6message.go index 0b84dae..e601932 100644 --- a/dhcpv6/dhcpv6message.go +++ b/dhcpv6/dhcpv6message.go @@ -71,12 +71,12 @@ func NewSolicitWithCID(duid Duid, modifiers ...Modifier) (DHCPv6, error) { if err != nil { return nil, err } - d.(*DHCPv6Message).SetMessage(SOLICIT) + d.(*DHCPv6Message).SetMessage(MessageTypeSolicit) d.AddOption(&OptClientId{Cid: duid}) oro := new(OptRequestedOption) oro.SetRequestedOptions([]OptionCode{ - DNS_RECURSIVE_NAME_SERVER, - DOMAIN_SEARCH_LIST, + OptionDNSRecursiveNameServer, + OptionDomainSearchList, }) d.AddOption(oro) d.AddOption(&OptElapsedTime{}) @@ -114,7 +114,7 @@ func NewAdvertiseFromSolicit(solicit DHCPv6, modifiers ...Modifier) (DHCPv6, err if solicit == nil { return nil, errors.New("SOLICIT cannot be nil") } - if solicit.Type() != SOLICIT { + if solicit.Type() != MessageTypeSolicit { return nil, errors.New("The passed SOLICIT must have SOLICIT type set") } sol, ok := solicit.(*DHCPv6Message) @@ -123,10 +123,10 @@ func NewAdvertiseFromSolicit(solicit DHCPv6, modifiers ...Modifier) (DHCPv6, err } // build ADVERTISE from SOLICIT adv := DHCPv6Message{} - adv.SetMessage(ADVERTISE) + adv.SetMessage(MessageTypeAdvertise) adv.SetTransactionID(sol.TransactionID()) // add Client ID - cid := sol.GetOneOption(OPTION_CLIENTID) + cid := sol.GetOneOption(OptionClientID) if cid == nil { return nil, errors.New("Client ID cannot be nil in SOLICIT when building ADVERTISE") } @@ -146,7 +146,7 @@ func NewRequestFromAdvertise(advertise DHCPv6, modifiers ...Modifier) (DHCPv6, e if advertise == nil { return nil, fmt.Errorf("ADVERTISE cannot be nil") } - if advertise.Type() != ADVERTISE { + if advertise.Type() != MessageTypeAdvertise { return nil, fmt.Errorf("The passed ADVERTISE must have ADVERTISE type set") } adv, ok := advertise.(*DHCPv6Message) @@ -155,16 +155,16 @@ func NewRequestFromAdvertise(advertise DHCPv6, modifiers ...Modifier) (DHCPv6, e } // build REQUEST from ADVERTISE req := DHCPv6Message{} - req.SetMessage(REQUEST) + req.SetMessage(MessageTypeRequest) req.SetTransactionID(adv.TransactionID()) // add Client ID - cid := adv.GetOneOption(OPTION_CLIENTID) + cid := adv.GetOneOption(OptionClientID) if cid == nil { return nil, fmt.Errorf("Client ID cannot be nil in ADVERTISE when building REQUEST") } req.AddOption(cid) // add Server ID - sid := adv.GetOneOption(OPTION_SERVERID) + sid := adv.GetOneOption(OptionServerID) if sid == nil { return nil, fmt.Errorf("Server ID cannot be nil in ADVERTISE when building REQUEST") } @@ -172,7 +172,7 @@ func NewRequestFromAdvertise(advertise DHCPv6, modifiers ...Modifier) (DHCPv6, e // add Elapsed Time req.AddOption(&OptElapsedTime{}) // add IA_NA - iaNa := adv.GetOneOption(OPTION_IA_NA) + iaNa := adv.GetOneOption(OptionIANA) if iaNa == nil { return nil, fmt.Errorf("IA_NA cannot be nil in ADVERTISE when building REQUEST") } @@ -180,13 +180,13 @@ func NewRequestFromAdvertise(advertise DHCPv6, modifiers ...Modifier) (DHCPv6, e // add OptRequestedOption oro := OptRequestedOption{} oro.SetRequestedOptions([]OptionCode{ - DNS_RECURSIVE_NAME_SERVER, - DOMAIN_SEARCH_LIST, + OptionDNSRecursiveNameServer, + OptionDomainSearchList, }) req.AddOption(&oro) // add OPTION_VENDOR_CLASS, only if present in the original request // TODO implement OptionVendorClass - vClass := adv.GetOneOption(OPTION_VENDOR_CLASS) + vClass := adv.GetOneOption(OptionVendorClass) if vClass != nil { req.AddOption(vClass) } @@ -207,9 +207,9 @@ func NewReplyFromDHCPv6Message(message DHCPv6, modifiers ...Modifier) (DHCPv6, e return nil, errors.New("DHCPv6Message cannot be nil") } switch message.Type() { - case REQUEST, CONFIRM, RENEW, REBIND, RELEASE: - default: - return nil, errors.New("Cannot create REPLY from the passed message type set") + case MessageTypeRequest, MessageTypeConfirm, MessageTypeRenew, MessageTypeRebind, MessageTypeRelease: + default: + return nil, errors.New("Cannot create REPLY from the passed message type set") } msg, ok := message.(*DHCPv6Message) if !ok { @@ -217,10 +217,10 @@ func NewReplyFromDHCPv6Message(message DHCPv6, modifiers ...Modifier) (DHCPv6, e } // build REPLY from MESSAGE rep := DHCPv6Message{} - rep.SetMessage(REPLY) + rep.SetMessage(MessageTypeReply) rep.SetTransactionID(msg.TransactionID()) // add Client ID - cid := message.GetOneOption(OPTION_CLIENTID) + cid := message.GetOneOption(OptionClientID) if cid == nil { return nil, errors.New("Client ID cannot be nil when building REPLY") } @@ -243,7 +243,7 @@ func (d *DHCPv6Message) SetMessage(messageType MessageType) { if msgString == "" { log.Printf("Warning: unknown DHCPv6 message type: %v", messageType) } - if messageType == RELAY_FORW || messageType == RELAY_REPL { + if messageType == MessageTypeRelayForward || messageType == MessageTypeRelayReply { log.Printf("Warning: using a RELAY message type with a non-relay message: %v (%v)", msgString, messageType) } diff --git a/dhcpv6/dhcpv6relay.go b/dhcpv6/dhcpv6relay.go index 1bde354..d9555cb 100644 --- a/dhcpv6/dhcpv6relay.go +++ b/dhcpv6/dhcpv6relay.go @@ -173,7 +173,7 @@ func NewRelayReplFromRelayForw(relayForw, msg DHCPv6) (DHCPv6, error) { if !ok { return nil, errors.New("Not a DHCPv6Relay") } - if relay.Type() != RELAY_FORW { + if relay.Type() != MessageTypeRelayForward { return nil, errors.New("The passed packet is not of type RELAY_FORW") } if msg == nil { @@ -185,7 +185,7 @@ func NewRelayReplFromRelayForw(relayForw, msg DHCPv6) (DHCPv6, error) { for { linkAddr = append(linkAddr, relay.LinkAddr()) peerAddr = append(peerAddr, relay.PeerAddr()) - optiids = append(optiids, relay.GetOneOption(OPTION_INTERFACE_ID)) + optiids = append(optiids, relay.GetOneOption(OptionInterfaceID)) decap, err := DecapsulateRelay(relay) if err != nil { return nil, err @@ -197,7 +197,7 @@ func NewRelayReplFromRelayForw(relayForw, msg DHCPv6) (DHCPv6, error) { } } for i := len(linkAddr) - 1; i >= 0; i-- { - msg, err = EncapsulateRelay(msg, RELAY_REPL, linkAddr[i], peerAddr[i]) + msg, err = EncapsulateRelay(msg, MessageTypeRelayReply, linkAddr[i], peerAddr[i]) if opt := optiids[i]; opt != nil { msg.AddOption(opt) } diff --git a/dhcpv6/dhcpv6relay_test.go b/dhcpv6/dhcpv6relay_test.go index e08e6a4..afb4086 100644 --- a/dhcpv6/dhcpv6relay_test.go +++ b/dhcpv6/dhcpv6relay_test.go @@ -12,15 +12,15 @@ func TestDHCPv6Relay(t *testing.T) { ll := net.IP{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xff, 0xfe, 0xdd, 0xee, 0xff} ma := net.IP{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} r := DHCPv6Relay{ - messageType: RELAY_FORW, + messageType: MessageTypeRelayForward, hopCount: 10, linkAddr: ll, peerAddr: ma, // options is left empty here for testing purposes, even if it's // mandatory to have at least a relay message option } - if mt := r.MessageType(); mt != RELAY_FORW { - t.Fatalf("Invalid message type. Expected %v, got %v", RELAY_FORW, mt) + if mt := r.MessageType(); mt != MessageTypeRelayForward { + t.Fatalf("Invalid message type. Expected %v, got %v", MessageTypeRelayForward, mt) } if hc := r.HopCount(); hc != 10 { t.Fatalf("Invalid hop count. Expected 10, got %v", hc) @@ -39,9 +39,9 @@ func TestDHCPv6Relay(t *testing.T) { func TestDHCPv6RelaySettersAndGetters(t *testing.T) { r := DHCPv6Relay{} // set and get message type - r.SetMessageType(RELAY_REPL) - if mt := r.MessageType(); mt != RELAY_REPL { - t.Fatalf("Invalid message type. Expected %v, got %v", RELAY_REPL, mt) + r.SetMessageType(MessageTypeRelayReply) + if mt := r.MessageType(); mt != MessageTypeRelayReply { + t.Fatalf("Invalid message type. Expected %v, got %v", MessageTypeRelayReply, mt) } // set and get hop count r.SetHopCount(5) @@ -68,7 +68,7 @@ func TestDHCPv6RelaySettersAndGetters(t *testing.T) { func TestDHCPv6RelayToBytes(t *testing.T) { expected := []byte{ - 12, // RELAY_FORW + 12, // MessageTypeRelayForward 1, // hop count 0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, // link addr 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, // peer addr @@ -76,7 +76,7 @@ func TestDHCPv6RelayToBytes(t *testing.T) { 0, 9, // relay msg 0, 10, // option length // inner dhcp solicit - 1, // SOLICIT + 1, // MessageTypeSolicit 0xaa, 0xbb, 0xcc, // transaction ID // inner option - elapsed time 0, 8, // elapsed time @@ -84,14 +84,14 @@ func TestDHCPv6RelayToBytes(t *testing.T) { 0, 0, } r := DHCPv6Relay{ - messageType: RELAY_FORW, + messageType: MessageTypeRelayForward, hopCount: 1, linkAddr: net.IPv6interfacelocalallnodes, peerAddr: net.IPv6linklocalallrouters, } opt := OptRelayMsg{ relayMessage: &DHCPv6Message{ - messageType: SOLICIT, + messageType: MessageTypeSolicit, transactionID: 0xaabbcc, options: []Option{ &OptElapsedTime{ @@ -109,12 +109,12 @@ func TestDHCPv6RelayToBytes(t *testing.T) { func TestNewRelayRepFromRelayForw(t *testing.T) { rf := DHCPv6Relay{} - rf.SetMessageType(RELAY_FORW) + rf.SetMessageType(MessageTypeRelayForward) rf.SetPeerAddr(net.IPv6linklocalallrouters) rf.SetLinkAddr(net.IPv6interfacelocalallnodes) oro := OptRelayMsg{} s := DHCPv6Message{} - s.SetMessage(SOLICIT) + s.SetMessage(MessageTypeSolicit) cid := OptClientId{} s.AddOption(&cid) oro.SetRelayMessage(&s) @@ -125,7 +125,7 @@ func TestNewRelayRepFromRelayForw(t *testing.T) { rr, err := NewRelayReplFromRelayForw(&rf, a) require.NoError(t, err) relay := rr.(*DHCPv6Relay) - require.Equal(t, rr.Type(), RELAY_REPL) + require.Equal(t, rr.Type(), MessageTypeRelayReply) require.Equal(t, relay.HopCount(), rf.HopCount()) require.Equal(t, relay.PeerAddr(), rf.PeerAddr()) require.Equal(t, relay.LinkAddr(), rf.LinkAddr()) diff --git a/dhcpv6/modifiers.go b/dhcpv6/modifiers.go index 54285cd..32d11ac 100644 --- a/dhcpv6/modifiers.go +++ b/dhcpv6/modifiers.go @@ -29,15 +29,15 @@ func WithNetboot(d DHCPv6) DHCPv6 { log.Printf("WithNetboot: not a DHCPv6Message") return d } - // add OPT_BOOTFILE_URL and OPT_BOOTFILE_PARAM - opt := msg.GetOneOption(OPTION_ORO) + // add OptionBootfileURL and OptionBootfileParam + opt := msg.GetOneOption(OptionORO) if opt == nil { opt = &OptRequestedOption{} } // TODO only add options if they are not there already oro := opt.(*OptRequestedOption) - oro.AddRequestedOption(OPT_BOOTFILE_URL) - oro.AddRequestedOption(OPT_BOOTFILE_PARAM) + oro.AddRequestedOption(OptionBootfileURL) + oro.AddRequestedOption(OptionBootfileParam) msg.UpdateOption(oro) return d } diff --git a/dhcpv6/modifiers_test.go b/dhcpv6/modifiers_test.go index da1ef56..ab21367 100644 --- a/dhcpv6/modifiers_test.go +++ b/dhcpv6/modifiers_test.go @@ -16,7 +16,7 @@ func TestWithClientID(t *testing.T) { } m, err := NewMessage(WithClientID(duid)) require.NoError(t, err) - opt := m.GetOneOption(OPTION_CLIENTID) + opt := m.GetOneOption(OptionClientID) require.NotNil(t, opt) cid := opt.(*OptClientId) require.Equal(t, cid.Cid, duid) @@ -30,7 +30,7 @@ func TestWithServerID(t *testing.T) { } m, err := NewMessage(WithServerID(duid)) require.NoError(t, err) - opt := m.GetOneOption(OPTION_SERVERID) + opt := m.GetOneOption(OptionServerID) require.NotNil(t, opt) sid := opt.(*OptServerId) require.Equal(t, sid.Sid, duid) diff --git a/dhcpv6/option_archtype.go b/dhcpv6/option_archtype.go index 802e334..a1b4a9b 100644 --- a/dhcpv6/option_archtype.go +++ b/dhcpv6/option_archtype.go @@ -45,12 +45,12 @@ type OptClientArchType struct { } func (op *OptClientArchType) Code() OptionCode { - return OPTION_CLIENT_ARCH_TYPE + return OptionClientArchType } func (op *OptClientArchType) ToBytes() []byte { buf := make([]byte, 6) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_CLIENT_ARCH_TYPE)) + 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)) return buf diff --git a/dhcpv6/option_archtype_test.go b/dhcpv6/option_archtype_test.go index b446661..748c8c5 100644 --- a/dhcpv6/option_archtype_test.go +++ b/dhcpv6/option_archtype_test.go @@ -26,7 +26,7 @@ func TestOptClientArchTypeParseAndToBytes(t *testing.T) { 0, 8, // EFI_XSCALE } expected := []byte{ - 0, 61, // OPTION_CLIENT_ARCH_TYPE + 0, 61, // OptionClientArchType 0, 2, // length 0, 8, // EFI_XSCALE } @@ -40,5 +40,5 @@ func TestOptClientArchType(t *testing.T) { ArchType: EFI_ITANIUM, } require.Equal(t, opt.Length(), 2) - require.Equal(t, opt.Code(), OPTION_CLIENT_ARCH_TYPE) + require.Equal(t, opt.Code(), OptionClientArchType) } diff --git a/dhcpv6/option_bootfileurl.go b/dhcpv6/option_bootfileurl.go index e4817cb..d2da0c3 100644 --- a/dhcpv6/option_bootfileurl.go +++ b/dhcpv6/option_bootfileurl.go @@ -8,20 +8,20 @@ import ( "fmt" ) -// OptBootFileURL implements the OPT_BOOTFILE_URL option +// OptBootFileURL implements the OptionBootfileURL option type OptBootFileURL struct { BootFileURL []byte } // Code returns the option code func (op *OptBootFileURL) Code() OptionCode { - return OPT_BOOTFILE_URL + return OptionBootfileURL } // ToBytes serializes the option and returns it as a sequence of bytes func (op *OptBootFileURL) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPT_BOOTFILE_URL)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionBootfileURL)) binary.BigEndian.PutUint16(buf[2:4], uint16(len(op.BootFileURL))) buf = append(buf, op.BootFileURL...) return buf diff --git a/dhcpv6/option_clientid.go b/dhcpv6/option_clientid.go index 3c01d8c..fc68ae7 100644 --- a/dhcpv6/option_clientid.go +++ b/dhcpv6/option_clientid.go @@ -14,12 +14,12 @@ type OptClientId struct { } func (op *OptClientId) Code() OptionCode { - return OPTION_CLIENTID + return OptionClientID } func (op *OptClientId) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_CLIENTID)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionClientID)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) buf = append(buf, op.Cid.ToBytes()...) return buf diff --git a/dhcpv6/option_clientid_test.go b/dhcpv6/option_clientid_test.go index dcaffca..77839f3 100644 --- a/dhcpv6/option_clientid_test.go +++ b/dhcpv6/option_clientid_test.go @@ -30,7 +30,7 @@ func TestOptClientIdToBytes(t *testing.T) { }, } expected := []byte{ - 0, 1, // OPTION_CLIENTID + 0, 1, // OptionClientID 0, 10, // length 0, 3, // DUID_LL 0, 1, // hwtype ethernet @@ -46,7 +46,7 @@ func TestOptClientIdDecodeEncode(t *testing.T) { 5, 4, 3, 2, 1, 0, // hw addr } expected := append([]byte{ - 0, 1, // OPTION_CLIENTID + 0, 1, // OptionClientID 0, 10, // length }, data...) opt, err := ParseOptClientId(data) @@ -63,5 +63,5 @@ func TestOptionClientId(t *testing.T) { }, } require.Equal(t, opt.Length(), 10) - require.Equal(t, opt.Code(), OPTION_CLIENTID) + require.Equal(t, opt.Code(), OptionClientID) } diff --git a/dhcpv6/option_dnsrecursivenameserver.go b/dhcpv6/option_dnsrecursivenameserver.go index 737a2d3..72b0767 100644 --- a/dhcpv6/option_dnsrecursivenameserver.go +++ b/dhcpv6/option_dnsrecursivenameserver.go @@ -9,21 +9,21 @@ import ( "net" ) -// OptDNSRecursiveNameServer represents a DNS_RECURSIVE_NAME_SERVER option +// OptDNSRecursiveNameServer represents a OptionDNSRecursiveNameServer option type OptDNSRecursiveNameServer struct { NameServers []net.IP } // Code returns the option code func (op *OptDNSRecursiveNameServer) Code() OptionCode { - return DNS_RECURSIVE_NAME_SERVER + return OptionDNSRecursiveNameServer } // ToBytes returns the option serialized to bytes, including option code and // length func (op *OptDNSRecursiveNameServer) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(DNS_RECURSIVE_NAME_SERVER)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionDNSRecursiveNameServer)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) for _, ns := range op.NameServers { buf = append(buf, ns...) diff --git a/dhcpv6/option_dnsrecursivenameserver_test.go b/dhcpv6/option_dnsrecursivenameserver_test.go index 85bb982..71fe6d3 100644 --- a/dhcpv6/option_dnsrecursivenameserver_test.go +++ b/dhcpv6/option_dnsrecursivenameserver_test.go @@ -25,7 +25,7 @@ func TestOptDNSRecursiveNameServerToBytes(t *testing.T) { ns2 := net.ParseIP("2001:4860:4860::8888") nameservers := []net.IP{ns1, ns2} expected := []byte{ - 0, 23, // DNS_RECURSIVE_NAME_SERVER + 0, 23, // OptionDNSRecursiveNameServer 0, 32, // length } expected = append(expected, []byte(ns1)...) diff --git a/dhcpv6/option_domainsearchlist.go b/dhcpv6/option_domainsearchlist.go index 402c68e..a3c6f28 100644 --- a/dhcpv6/option_domainsearchlist.go +++ b/dhcpv6/option_domainsearchlist.go @@ -6,22 +6,24 @@ package dhcpv6 import ( "encoding/binary" "fmt" + + "github.com/insomniacslk/dhcp/rfc1035label" ) -// OptDomainSearchList list implements a DOMAIN_SEARCH_LIST option +// OptDomainSearchList list implements a OptionDomainSearchList option type OptDomainSearchList struct { DomainSearchList []string } func (op *OptDomainSearchList) Code() OptionCode { - return DOMAIN_SEARCH_LIST + return OptionDomainSearchList } func (op *OptDomainSearchList) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(DOMAIN_SEARCH_LIST)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionDomainSearchList)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) - buf = append(buf, LabelsToBytes(op.DomainSearchList)...) + buf = append(buf, rfc1035label.LabelsToBytes(op.DomainSearchList)...) return buf } @@ -42,7 +44,7 @@ func (op *OptDomainSearchList) String() string { func ParseOptDomainSearchList(data []byte) (*OptDomainSearchList, error) { opt := OptDomainSearchList{} var err error - opt.DomainSearchList, err = LabelsFromBytes(data) + opt.DomainSearchList, err = rfc1035label.LabelsFromBytes(data) if err != nil { return nil, err } diff --git a/dhcpv6/option_domainsearchlist_test.go b/dhcpv6/option_domainsearchlist_test.go index d5b8bf7..68f93ba 100644 --- a/dhcpv6/option_domainsearchlist_test.go +++ b/dhcpv6/option_domainsearchlist_test.go @@ -20,7 +20,7 @@ func TestParseOptDomainSearchList(t *testing.T) { func TestOptDomainSearchListToBytes(t *testing.T) { expected := []byte{ - 0, 24, // DOMAIN_SEARCH_LIST + 0, 24, // OptionDomainSearchList 0, 33, // length 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 6, 's', 'u', 'b', 'n', 'e', 't', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'o', 'r', 'g', 0, diff --git a/dhcpv6/option_elapsedtime.go b/dhcpv6/option_elapsedtime.go index e0a1094..6009bf8 100644 --- a/dhcpv6/option_elapsedtime.go +++ b/dhcpv6/option_elapsedtime.go @@ -13,12 +13,12 @@ type OptElapsedTime struct { } func (op *OptElapsedTime) Code() OptionCode { - return OPTION_ELAPSED_TIME + return OptionElapsedTime } func (op *OptElapsedTime) ToBytes() []byte { buf := make([]byte, 6) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_ELAPSED_TIME)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionElapsedTime)) binary.BigEndian.PutUint16(buf[2:4], 2) binary.BigEndian.PutUint16(buf[4:6], uint16(op.ElapsedTime)) return buf diff --git a/dhcpv6/option_iaaddress.go b/dhcpv6/option_iaaddress.go index 1a89a25..0500d83 100644 --- a/dhcpv6/option_iaaddress.go +++ b/dhcpv6/option_iaaddress.go @@ -9,7 +9,7 @@ import ( "net" ) -// OptIAAddress represents an OPTION_IAADDR +// OptIAAddress represents an OptionIAAddr type OptIAAddress struct { IPv6Addr net.IP PreferredLifetime uint32 @@ -19,13 +19,13 @@ type OptIAAddress struct { // Code returns the option's code func (op *OptIAAddress) Code() OptionCode { - return OPTION_IAADDR + return OptionIAAddr } // ToBytes serializes the option and returns it as a sequence of bytes func (op *OptIAAddress) ToBytes() []byte { buf := make([]byte, 28) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IAADDR)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionIAAddr)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) copy(buf[4:20], op.IPv6Addr[:]) binary.BigEndian.PutUint32(buf[20:24], op.PreferredLifetime) diff --git a/dhcpv6/option_iaaddress_test.go b/dhcpv6/option_iaaddress_test.go index ad4f69c..0240949 100644 --- a/dhcpv6/option_iaaddress_test.go +++ b/dhcpv6/option_iaaddress_test.go @@ -45,7 +45,7 @@ func TestOptIAAddressParseInvalidBrokenOptions(t *testing.T) { func TestOptIAAddressToBytes(t *testing.T) { expected := []byte{ - 0, 5, // OPTION_IAADDR + 0, 5, // OptionIAAddr 0, 30, // length } ipBytes := []byte{0x24, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} diff --git a/dhcpv6/option_iaprefix.go b/dhcpv6/option_iaprefix.go index 57810b1..3f6227b 100644 --- a/dhcpv6/option_iaprefix.go +++ b/dhcpv6/option_iaprefix.go @@ -18,12 +18,12 @@ type OptIAPrefix struct { } func (op *OptIAPrefix) Code() OptionCode { - return OPTION_IAPREFIX + return OptionIAPrefix } func (op *OptIAPrefix) ToBytes() []byte { buf := make([]byte, 12) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IAPREFIX)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionIAPrefix)) 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) diff --git a/dhcpv6/option_interfaceid.go b/dhcpv6/option_interfaceid.go index 2634d9f..2bee744 100644 --- a/dhcpv6/option_interfaceid.go +++ b/dhcpv6/option_interfaceid.go @@ -13,12 +13,12 @@ type OptInterfaceId struct { } func (op *OptInterfaceId) Code() OptionCode { - return OPTION_INTERFACE_ID + return OptionInterfaceID } func (op *OptInterfaceId) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_INTERFACE_ID)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionInterfaceID)) binary.BigEndian.PutUint16(buf[2:4], uint16(len(op.interfaceId))) buf = append(buf, op.interfaceId...) return buf diff --git a/dhcpv6/option_nii.go b/dhcpv6/option_nii.go index 3a838ee..68e315e 100644 --- a/dhcpv6/option_nii.go +++ b/dhcpv6/option_nii.go @@ -33,12 +33,12 @@ type OptNetworkInterfaceId struct { } func (op *OptNetworkInterfaceId) Code() OptionCode { - return OPTION_NII + return OptionNII } func (op *OptNetworkInterfaceId) ToBytes() []byte { buf := make([]byte, 7) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_NII)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionNII)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) buf[4] = op.type_ buf[5] = op.major diff --git a/dhcpv6/option_nontemporaryaddress.go b/dhcpv6/option_nontemporaryaddress.go index 5debeec..62442c1 100644 --- a/dhcpv6/option_nontemporaryaddress.go +++ b/dhcpv6/option_nontemporaryaddress.go @@ -16,12 +16,12 @@ type OptIANA struct { } func (op *OptIANA) Code() OptionCode { - return OPTION_IA_NA + return OptionIANA } func (op *OptIANA) ToBytes() []byte { buf := make([]byte, 16) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IA_NA)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionIANA)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) copy(buf[4:8], op.IaId[:]) binary.BigEndian.PutUint32(buf[8:12], op.T1) diff --git a/dhcpv6/option_nontemporaryaddress_test.go b/dhcpv6/option_nontemporaryaddress_test.go index 54df8f0..39c3c36 100644 --- a/dhcpv6/option_nontemporaryaddress_test.go +++ b/dhcpv6/option_nontemporaryaddress_test.go @@ -47,7 +47,7 @@ func TestOptIANAGetOneOption(t *testing.T) { opt := OptIANA{ Options: []Option{&OptElapsedTime{}, oaddr}, } - require.Equal(t, oaddr, opt.GetOneOption(OPTION_IAADDR)) + require.Equal(t, oaddr, opt.GetOneOption(OptionIAAddr)) } func TestOptIANAGetOneOptionMissingOpt(t *testing.T) { @@ -57,7 +57,7 @@ func TestOptIANAGetOneOptionMissingOpt(t *testing.T) { opt := OptIANA{ Options: []Option{&OptElapsedTime{}, oaddr}, } - require.Equal(t, nil, opt.GetOneOption(DNS_RECURSIVE_NAME_SERVER)) + require.Equal(t, nil, opt.GetOneOption(OptionDNSRecursiveNameServer)) } func TestOptIANADelOption(t *testing.T) { @@ -69,13 +69,13 @@ func TestOptIANADelOption(t *testing.T) { optiana1.Options = append(optiana1.Options, &optsc) optiana1.Options = append(optiana1.Options, &optiaaddr) optiana1.Options = append(optiana1.Options, &optiaaddr) - optiana1.DelOption(OPTION_IAADDR) + optiana1.DelOption(OptionIAAddr) require.Equal(t, optiana1.Options, []Option{&optsc}) optiana2.Options = append(optiana2.Options, &optiaaddr) optiana2.Options = append(optiana2.Options, &optsc) optiana2.Options = append(optiana2.Options, &optiaaddr) - optiana2.DelOption(OPTION_IAADDR) + optiana2.DelOption(OptionIAAddr) require.Equal(t, optiana2.Options, []Option{&optsc}) } @@ -91,7 +91,7 @@ func TestOptIANAToBytes(t *testing.T) { }, } expected := []byte{ - 0, 3, // OPTION_IA_NA + 0, 3, // OptionIANA 0, 18, // length 1, 2, 3, 4, // IA ID 0, 0, 0x30, 0x39, // T1 = 12345 diff --git a/dhcpv6/option_prefixdelegation.go b/dhcpv6/option_prefixdelegation.go index 2a211c0..ecc7990 100644 --- a/dhcpv6/option_prefixdelegation.go +++ b/dhcpv6/option_prefixdelegation.go @@ -16,12 +16,12 @@ type OptIAForPrefixDelegation struct { } func (op *OptIAForPrefixDelegation) Code() OptionCode { - return OPTION_IA_PD + return OptionIAPD } func (op *OptIAForPrefixDelegation) ToBytes() []byte { buf := make([]byte, 16) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_IA_PD)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionIAPD)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) copy(buf[4:8], op.iaId[:]) binary.BigEndian.PutUint32(buf[8:12], op.t1) diff --git a/dhcpv6/option_relaymsg.go b/dhcpv6/option_relaymsg.go index 98b9e84..89621ec 100644 --- a/dhcpv6/option_relaymsg.go +++ b/dhcpv6/option_relaymsg.go @@ -13,12 +13,12 @@ type OptRelayMsg struct { } func (op *OptRelayMsg) Code() OptionCode { - return OPTION_RELAY_MSG + return OptionRelayMsg } func (op *OptRelayMsg) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_RELAY_MSG)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionRelayMsg)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) buf = append(buf, op.relayMessage.ToBytes()...) return buf diff --git a/dhcpv6/option_relaymsg_test.go b/dhcpv6/option_relaymsg_test.go index 6d9d6a6..df2442c 100644 --- a/dhcpv6/option_relaymsg_test.go +++ b/dhcpv6/option_relaymsg_test.go @@ -7,7 +7,7 @@ import ( func TestRelayMsgParseOptRelayMsg(t *testing.T) { opt, err := ParseOptRelayMsg([]byte{ - 1, // SOLICIT + 1, // MessageTypeSolicit 0xaa, 0xbb, 0xcc, // transaction ID 0, 8, // option: elapsed time 0, 2, // option length @@ -16,9 +16,9 @@ func TestRelayMsgParseOptRelayMsg(t *testing.T) { if err != nil { t.Fatal(err) } - if code := opt.Code(); code != OPTION_RELAY_MSG { - t.Fatalf("Invalid option code. Expected OPTION_RELAY_MSG (%v), got %v", - OPTION_RELAY_MSG, code, + if code := opt.Code(); code != OptionRelayMsg { + t.Fatalf("Invalid option code. Expected OptionRelayMsg (%v), got %v", + OptionRelayMsg, code, ) } } @@ -27,7 +27,7 @@ func TestRelayMsgOptionsFromBytes(t *testing.T) { opts, err := OptionsFromBytes([]byte{ 0, 9, // option: relay message 0, 10, // relayed message length - 1, // SOLICIT + 1, // MessageTypeSolicit 0xaa, 0xbb, 0xcc, // transaction ID 0, 8, // option: elapsed time 0, 2, // option length @@ -40,9 +40,9 @@ func TestRelayMsgOptionsFromBytes(t *testing.T) { t.Fatalf("Invalid number of options. Expected 1, got %v", len(opts)) } opt := opts[0] - if code := opt.Code(); code != OPTION_RELAY_MSG { - t.Fatalf("Invalid option code. Expected OPTION_RELAY_MSG (%v), got %v", - OPTION_RELAY_MSG, code, + if code := opt.Code(); code != OptionRelayMsg { + t.Fatalf("Invalid option code. Expected OptionRelayMsg (%v), got %v", + OptionRelayMsg, code, ) } } @@ -55,7 +55,7 @@ func TestRelayMsgParseOptRelayMsgSingleEncapsulation(t *testing.T) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, // peerAddr 0, 9, // option: relay message 0, 10, // relayed message length - 1, // SOLICIT + 1, // MessageTypeSolicit 0xaa, 0xbb, 0xcc, // transaction ID 0, 8, // option: elapsed time 0, 2, // option length @@ -70,15 +70,15 @@ func TestRelayMsgParseOptRelayMsgSingleEncapsulation(t *testing.T) { reflect.TypeOf(d), ) } - if mType := r.Type(); mType != RELAY_FORW { - t.Fatalf("Invalid messge type for relay. Expected %v, got %v", RELAY_FORW, mType) + if mType := r.Type(); mType != MessageTypeRelayForward { + t.Fatalf("Invalid messge type for relay. Expected %v, got %v", MessageTypeRelayForward, mType) } if len(r.options) != 1 { t.Fatalf("Invalid number of options. Expected 1, got %v", len(r.options)) } - if code := r.options[0].Code(); code != OPTION_RELAY_MSG { - t.Fatalf("Invalid option code. Expected OPTION_RELAY_MSG (%v), got %v", - OPTION_RELAY_MSG, code, + if code := r.options[0].Code(); code != OptionRelayMsg { + t.Fatalf("Invalid option code. Expected OptionRelayMsg (%v), got %v", + OptionRelayMsg, code, ) } opt := r.options[0] @@ -94,9 +94,9 @@ func TestRelayMsgParseOptRelayMsgSingleEncapsulation(t *testing.T) { reflect.TypeOf(innerDHCP), ) } - if dType := innerDHCP.Type(); dType != SOLICIT { - t.Fatalf("Invalid inner DHCP type. Expected SOLICIT (%v), got %v", - SOLICIT, dType, + if dType := innerDHCP.Type(); dType != MessageTypeSolicit { + t.Fatalf("Invalid inner DHCP type. Expected MessageTypeSolicit (%v), got %v", + MessageTypeSolicit, dType, ) } if tID := innerDHCP.TransactionID(); tID != 0xaabbcc { diff --git a/dhcpv6/option_remoteid.go b/dhcpv6/option_remoteid.go index 210363d..9d249a7 100644 --- a/dhcpv6/option_remoteid.go +++ b/dhcpv6/option_remoteid.go @@ -14,12 +14,12 @@ type OptRemoteId struct { } func (op *OptRemoteId) Code() OptionCode { - return OPTION_REMOTE_ID + return OptionRemoteID } func (op *OptRemoteId) ToBytes() []byte { buf := make([]byte, 8) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_REMOTE_ID)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionRemoteID)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) binary.BigEndian.PutUint32(buf[4:8], uint32(op.enterpriseNumber)) buf = append(buf, op.remoteId...) diff --git a/dhcpv6/option_requestedoption.go b/dhcpv6/option_requestedoption.go index f6327f9..a55c533 100644 --- a/dhcpv6/option_requestedoption.go +++ b/dhcpv6/option_requestedoption.go @@ -13,13 +13,13 @@ type OptRequestedOption struct { } func (op *OptRequestedOption) Code() OptionCode { - return OPTION_ORO + return OptionORO } 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[0:2], uint16(OptionORO)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) for _, ro := range op.requestedOptions { binary.BigEndian.PutUint16(roBytes, uint16(ro)) diff --git a/dhcpv6/option_serverid.go b/dhcpv6/option_serverid.go index ffc1b69..dbc17f5 100644 --- a/dhcpv6/option_serverid.go +++ b/dhcpv6/option_serverid.go @@ -14,12 +14,12 @@ type OptServerId struct { } func (op *OptServerId) Code() OptionCode { - return OPTION_SERVERID + return OptionServerID } func (op *OptServerId) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_SERVERID)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionServerID)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) buf = append(buf, op.Sid.ToBytes()...) return buf diff --git a/dhcpv6/option_serverid_test.go b/dhcpv6/option_serverid_test.go index f420b78..e330fa8 100644 --- a/dhcpv6/option_serverid_test.go +++ b/dhcpv6/option_serverid_test.go @@ -30,7 +30,7 @@ func TestOptServerIdToBytes(t *testing.T) { }, } expected := []byte{ - 0, 2, // OPTION_SERVERID + 0, 2, // OptionServerID 0, 10, // length 0, 3, // DUID_LL 0, 1, // hwtype ethernet @@ -46,7 +46,7 @@ func TestOptServerIdDecodeEncode(t *testing.T) { 5, 4, 3, 2, 1, 0, // hw addr } expected := append([]byte{ - 0, 2, // OPTION_SERVERID + 0, 2, // OptionServerID 0, 10, // length }, data...) opt, err := ParseOptServerId(data) @@ -63,5 +63,5 @@ func TestOptionServerId(t *testing.T) { }, } require.Equal(t, opt.Length(), 10) - require.Equal(t, opt.Code(), OPTION_SERVERID) + require.Equal(t, opt.Code(), OptionServerID) } diff --git a/dhcpv6/option_statuscode.go b/dhcpv6/option_statuscode.go index e6bd8ae..11f1cbf 100644 --- a/dhcpv6/option_statuscode.go +++ b/dhcpv6/option_statuscode.go @@ -18,13 +18,13 @@ type OptStatusCode struct { // Code returns the option code func (op *OptStatusCode) Code() OptionCode { - return OPTION_STATUS_CODE + return OptionStatusCode } // ToBytes serializes the option and returns it as a sequence of bytes func (op *OptStatusCode) ToBytes() []byte { buf := make([]byte, 6) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_STATUS_CODE)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionStatusCode)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) binary.BigEndian.PutUint16(buf[4:6], uint16(op.StatusCode)) buf = append(buf, op.StatusMessage...) diff --git a/dhcpv6/option_statuscode_test.go b/dhcpv6/option_statuscode_test.go index 95c785e..6d2ba94 100644 --- a/dhcpv6/option_statuscode_test.go +++ b/dhcpv6/option_statuscode_test.go @@ -20,7 +20,7 @@ func TestParseOptStatusCode(t *testing.T) { func TestOptStatusCodeToBytes(t *testing.T) { expected := []byte{ - 0, 13, // OPTION_STATUS_CODE + 0, 13, // OptionStatusCode 0, 9, // length 0, 0, // StatusSuccess 's', 'u', 'c', 'c', 'e', 's', 's', diff --git a/dhcpv6/option_types.go b/dhcpv6/option_types.go index 946dcd0..6a852ad 100644 --- a/dhcpv6/option_types.go +++ b/dhcpv6/option_types.go @@ -1,152 +1,153 @@ package dhcpv6 -// FIXME: rename all the options to have a consistent name, e.g. OPT_<NAME> +// All DHCPv6 options. const ( - OPTION_CLIENTID OptionCode = 1 - OPTION_SERVERID OptionCode = 2 - OPTION_IA_NA OptionCode = 3 - OPTION_IA_TA OptionCode = 4 - OPTION_IAADDR OptionCode = 5 - OPTION_ORO OptionCode = 6 - OPTION_PREFERENCE OptionCode = 7 - OPTION_ELAPSED_TIME OptionCode = 8 - OPTION_RELAY_MSG OptionCode = 9 + OptionClientID OptionCode = 1 + OptionServerID OptionCode = 2 + OptionIANA OptionCode = 3 + OptionIATA OptionCode = 4 + OptionIAAddr OptionCode = 5 + OptionORO OptionCode = 6 + OptionPreference OptionCode = 7 + OptionElapsedTime OptionCode = 8 + OptionRelayMsg OptionCode = 9 // skip 10 - OPTION_AUTH OptionCode = 11 - OPTION_UNICAST OptionCode = 12 - OPTION_STATUS_CODE OptionCode = 13 - OPTION_RAPID_COMMIT OptionCode = 14 - OPTION_USER_CLASS OptionCode = 15 - OPTION_VENDOR_CLASS OptionCode = 16 - OPTION_VENDOR_OPTS OptionCode = 17 - OPTION_INTERFACE_ID OptionCode = 18 - OPTION_RECONF_MSG OptionCode = 19 - OPTION_RECONF_ACCEPT OptionCode = 20 - SIP_SERVERS_DOMAIN_NAME_LIST OptionCode = 21 - SIP_SERVERS_IPV6_ADDRESS_LIST OptionCode = 22 - DNS_RECURSIVE_NAME_SERVER OptionCode = 23 - DOMAIN_SEARCH_LIST OptionCode = 24 - OPTION_IA_PD OptionCode = 25 - OPTION_IAPREFIX OptionCode = 26 - OPTION_NIS_SERVERS OptionCode = 27 - OPTION_NISP_SERVERS OptionCode = 28 - OPTION_NIS_DOMAIN_NAME OptionCode = 29 - OPTION_NISP_DOMAIN_NAME OptionCode = 30 - SNTP_SERVER_LIST OptionCode = 31 - INFORMATION_REFRESH_TIME OptionCode = 32 - BCMCS_CONTROLLER_DOMAIN_NAME_LIST OptionCode = 33 - BCMCS_CONTROLLER_IPV6_ADDRESS_LIST OptionCode = 34 + OptionAuth OptionCode = 11 + OptionUnicast OptionCode = 12 + OptionStatusCode OptionCode = 13 + OptionRapidCommit OptionCode = 14 + OptionUserClass OptionCode = 15 + OptionVendorClass OptionCode = 16 + OptionVendorOpts OptionCode = 17 + OptionInterfaceID OptionCode = 18 + OptionReconfMessage OptionCode = 19 + OptionReconfAccept OptionCode = 20 + OptionSIPServersDomainNameList OptionCode = 21 + OptionSIPServersIPv6AddressList OptionCode = 22 + OptionDNSRecursiveNameServer OptionCode = 23 + OptionDomainSearchList OptionCode = 24 + OptionIAPD OptionCode = 25 + OptionIAPrefix OptionCode = 26 + OptionNISServers OptionCode = 27 + OptionNISPServers OptionCode = 28 + OptionNISDomainName OptionCode = 29 + OptionNISPDomainName OptionCode = 30 + OptionSNTPServerList OptionCode = 31 + OptionInformationRefreshTime OptionCode = 32 + OptionBCMCSControllerDomainNameList OptionCode = 33 + OptionBCMCSControllerIPv6AddressList OptionCode = 34 // skip 35 - OPTION_GEOCONF_CIVIC OptionCode = 36 - OPTION_REMOTE_ID OptionCode = 37 - RELAY_AGENT_SUBSCRIBER_ID OptionCode = 38 - FQDN OptionCode = 39 - PANA_AUTHENTICATION_AGENT OptionCode = 40 - OPTION_NEW_POSIX_TIMEZONE OptionCode = 41 - OPTION_NEW_TZDB_TIMEZONE OptionCode = 42 - ECHO_REQUEST OptionCode = 43 - OPTION_LQ_QUERY OptionCode = 44 - OPTION_CLIENT_DATA OptionCode = 45 - OPTION_CLT_TIME OptionCode = 46 - OPTION_LQ_RELAY_DATA OptionCode = 47 - OPTION_LQ_CLIENT_LINK OptionCode = 48 - MIPV6_HOME_NETWORK_ID_FQDN OptionCode = 49 - MIPV6_VISITED_HOME_NETWORK_INFORMATION OptionCode = 50 - LOST_SERVER OptionCode = 51 - CAPWAP_ACCESS_CONTROLLER_ADDRESSES OptionCode = 52 - RELAY_ID OptionCode = 53 - OPTION_IPV6_ADDRESS_MOS OptionCode = 54 - OPTION_IPV6_FQDN_MOS OptionCode = 55 - OPTION_NTP_SERVER OptionCode = 56 - OPTION_V6_ACCESS_DOMAIN OptionCode = 57 - OPTION_SIP_UA_CS_LIST OptionCode = 58 - OPT_BOOTFILE_URL OptionCode = 59 - OPT_BOOTFILE_PARAM OptionCode = 60 - OPTION_CLIENT_ARCH_TYPE OptionCode = 61 - OPTION_NII OptionCode = 62 - OPTION_GEOLOCATION OptionCode = 63 - OPTION_AFTR_NAME OptionCode = 64 - OPTION_ERP_LOCAL_DOMAIN_NAME OptionCode = 65 - OPTION_RSOO OptionCode = 66 - OPTION_PD_EXCLUDE OptionCode = 67 - VIRTUAL_SUBNET_SELECTION OptionCode = 68 - MIPV6_IDENTIFIED_HOME_NETWORK_INFORMATION OptionCode = 69 - MIPV6_UNRESTRICTED_HOME_NETWORK_INFORMATION OptionCode = 70 - MIPV6_HOME_NETWORK_PREFIX OptionCode = 71 - MIPV6_HOME_AGENT_ADDRESS OptionCode = 72 - MIPV6_HOME_AGENT_FQDN OptionCode = 73 + OptionGeoConfCivic OptionCode = 36 + OptionRemoteID OptionCode = 37 + OptionRelayAgentSubscriberID OptionCode = 38 + OptionFQDN OptionCode = 39 + OptionPANAAuthenticationAgent OptionCode = 40 + OptionNewPOSIXTimezone OptionCode = 41 + OptionNewTZDBTimezone OptionCode = 42 + OptionEchoRequest OptionCode = 43 + OptionLQQuery OptionCode = 44 + OptionClientData OptionCode = 45 + OptionCLTTime OptionCode = 46 + OptionLQRelayData OptionCode = 47 + OptionLQClientLink OptionCode = 48 + OptionMIPv6HomeNetworkIDFQDN OptionCode = 49 + OptionMIPv6VisitedHomeNetworkInformation OptionCode = 50 + OptionLoSTServer OptionCode = 51 + OptionCAPWAPAccessControllerAddresses OptionCode = 52 + OptionRelayID OptionCode = 53 + OptionIPv6AddressMOS OptionCode = 54 + OptionIPv6FQDNMOS OptionCode = 55 + OptionNTPServer OptionCode = 56 + OptionV6AccessDomain OptionCode = 57 + OptionSIPUACSList OptionCode = 58 + OptionBootfileURL OptionCode = 59 + OptionBootfileParam OptionCode = 60 + OptionClientArchType OptionCode = 61 + OptionNII OptionCode = 62 + OptionGeolocation OptionCode = 63 + OptionAFTRName OptionCode = 64 + OptionERPLocalDomainName OptionCode = 65 + OptionRSOO OptionCode = 66 + OptionPDExclude OptionCode = 67 + OptionVirtualSubnetSelection OptionCode = 68 + OptionMIPv6IdentifiedHomeNetworkInformation OptionCode = 69 + OptionMIPv6UnrestrictedHomeNetworkInformation OptionCode = 70 + OptionMIPv6HomeNetworkPrefix OptionCode = 71 + OptionMIPv6HomeAgentAddress OptionCode = 72 + OptionMIPv6HomeAgentFQDN OptionCode = 73 ) +// OptionCodeToString maps DHCPv6 OptionCodes to human-readable strings. 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", + OptionClientID: "OPTION_CLIENTID", + OptionServerID: "OPTION_SERVERID", + OptionIANA: "OPTION_IA_NA", + OptionIATA: "OPTION_IA_TA", + OptionIAAddr: "OPTION_IAADDR", + OptionORO: "OPTION_ORO", + OptionPreference: "OPTION_PREFERENCE", + OptionElapsedTime: "OPTION_ELAPSED_TIME", + OptionRelayMsg: "OPTION_RELAY_MSG", + OptionAuth: "OPTION_AUTH", + OptionUnicast: "OPTION_UNICAST", + OptionStatusCode: "OPTION_STATUS_CODE", + OptionRapidCommit: "OPTION_RAPID_COMMIT", + OptionUserClass: "OPTION_USER_CLASS", + OptionVendorClass: "OPTION_VENDOR_CLASS", + OptionVendorOpts: "OPTION_VENDOR_OPTS", + OptionInterfaceID: "OPTION_INTERFACE_ID", + OptionReconfMessage: "OPTION_RECONF_MSG", + OptionReconfAccept: "OPTION_RECONF_ACCEPT", + OptionSIPServersDomainNameList: "SIP Servers Domain Name List", + OptionSIPServersIPv6AddressList: "SIP Servers IPv6 Address List", + OptionDNSRecursiveNameServer: "DNS Recursive Name Server", + OptionDomainSearchList: "Domain Search List", + OptionIAPD: "OPTION_IA_PD", + OptionIAPrefix: "OPTION_IAPREFIX", + OptionNISServers: "OPTION_NIS_SERVERS", + OptionNISPServers: "OPTION_NISP_SERVERS", + OptionNISDomainName: "OPTION_NIS_DOMAIN_NAME", + OptionNISPDomainName: "OPTION_NISP_DOMAIN_NAME", + OptionSNTPServerList: "SNTP Server List", + OptionInformationRefreshTime: "Information Refresh Time", + OptionBCMCSControllerDomainNameList: "BCMCS Controller Domain Name List", + OptionBCMCSControllerIPv6AddressList: "BCMCS Controller IPv6 Address List", + OptionGeoConfCivic: "OPTION_GEOCONF", + OptionRemoteID: "OPTION_REMOTE_ID", + OptionRelayAgentSubscriberID: "Relay-Agent Subscriber ID", + OptionFQDN: "FQDN", + OptionPANAAuthenticationAgent: "PANA Authentication Agent", + OptionNewPOSIXTimezone: "OPTION_NEW_POSIX_TIME_ZONE", + OptionNewTZDBTimezone: "OPTION_NEW_TZDB_TIMEZONE", + OptionEchoRequest: "Echo Request", + OptionLQQuery: "OPTION_LQ_QUERY", + OptionClientData: "OPTION_CLIENT_DATA", + OptionCLTTime: "OPTION_CLT_TIME", + OptionLQRelayData: "OPTION_LQ_RELAY_DATA", + OptionLQClientLink: "OPTION_LQ_CLIENT_LINK", + OptionMIPv6HomeNetworkIDFQDN: "MIPv6 Home Network ID FQDN", + OptionMIPv6VisitedHomeNetworkInformation: "MIPv6 Visited Home Network Information", + OptionLoSTServer: "LoST Server", + OptionCAPWAPAccessControllerAddresses: "CAPWAP Access Controller Addresses", + OptionRelayID: "RELAY_ID", + OptionIPv6AddressMOS: "OPTION-IPv6_Address-MoS", + OptionIPv6FQDNMOS: "OPTION-IPv6-FQDN-MoS", + OptionNTPServer: "OPTION_NTP_SERVER", + OptionV6AccessDomain: "OPTION_V6_ACCESS_DOMAIN", + OptionSIPUACSList: "OPTION_SIP_UA_CS_LIST", + OptionBootfileURL: "OPT_BOOTFILE_URL", + OptionBootfileParam: "OPT_BOOTFILE_PARAM", + OptionClientArchType: "OPTION_CLIENT_ARCH_TYPE", + OptionNII: "OPTION_NII", + OptionGeolocation: "OPTION_GEOLOCATION", + OptionAFTRName: "OPTION_AFTR_NAME", + OptionERPLocalDomainName: "OPTION_ERP_LOCAL_DOMAIN_NAME", + OptionRSOO: "OPTION_RSOO", + OptionPDExclude: "OPTION_PD_EXCLUDE", + OptionVirtualSubnetSelection: "Virtual Subnet Selection", + OptionMIPv6IdentifiedHomeNetworkInformation: "MIPv6 Identified Home Network Information", + OptionMIPv6UnrestrictedHomeNetworkInformation: "MIPv6 Unrestricted Home Network Information", + OptionMIPv6HomeNetworkPrefix: "MIPv6 Home Network Prefix", + OptionMIPv6HomeAgentAddress: "MIPv6 Home Agent Address", + OptionMIPv6HomeAgentFQDN: "MIPv6 Home Agent FQDN", } diff --git a/dhcpv6/option_userclass.go b/dhcpv6/option_userclass.go index c5ac4ca..652c216 100644 --- a/dhcpv6/option_userclass.go +++ b/dhcpv6/option_userclass.go @@ -17,13 +17,13 @@ type OptUserClass struct { // Code returns the option code func (op *OptUserClass) Code() OptionCode { - return OPTION_USER_CLASS + return OptionUserClass } // ToBytes serializes the option and returns it as a sequence of bytes func (op *OptUserClass) ToBytes() []byte { buf := make([]byte, 4) - binary.BigEndian.PutUint16(buf[0:2], uint16(OPTION_USER_CLASS)) + binary.BigEndian.PutUint16(buf[0:2], uint16(OptionUserClass)) binary.BigEndian.PutUint16(buf[2:4], uint16(op.Length())) u16 := make([]byte, 2) for _, uc := range op.UserClasses { diff --git a/dhcpv6/option_userclass_test.go b/dhcpv6/option_userclass_test.go index 92c3848..3ba65cf 100644 --- a/dhcpv6/option_userclass_test.go +++ b/dhcpv6/option_userclass_test.go @@ -40,7 +40,7 @@ func TestOptUserClassToBytes(t *testing.T) { } data := opt.ToBytes() expected := []byte{ - 0, 15, // OPTION_USER_CLASS + 0, 15, // OptionUserClass 0, 11, // length 0, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', } @@ -56,7 +56,7 @@ func TestOptUserClassToBytesMultiple(t *testing.T) { } data := opt.ToBytes() expected := []byte{ - 0, 15, // OPTION_USER_CLASS + 0, 15, // OptionUserClass 0, 17, // length 0, 9, 'l', 'i', 'n', 'u', 'x', 'b', 'o', 'o', 't', 0, 4, 't', 'e', 's', 't', diff --git a/dhcpv6/options.go b/dhcpv6/options.go index d64d1d9..0a0bdc1 100644 --- a/dhcpv6/options.go +++ b/dhcpv6/options.go @@ -68,41 +68,41 @@ func ParseOption(dataStart []byte) (Option, error) { ) optData := dataStart[4 : 4+length] switch code { - case OPTION_CLIENTID: + case OptionClientID: opt, err = ParseOptClientId(optData) - case OPTION_SERVERID: + case OptionServerID: opt, err = ParseOptServerId(optData) - case OPTION_ELAPSED_TIME: + case OptionElapsedTime: opt, err = ParseOptElapsedTime(optData) - case OPTION_ORO: + case OptionORO: opt, err = ParseOptRequestedOption(optData) - case DNS_RECURSIVE_NAME_SERVER: + case OptionDNSRecursiveNameServer: opt, err = ParseOptDNSRecursiveNameServer(optData) - case DOMAIN_SEARCH_LIST: + case OptionDomainSearchList: opt, err = ParseOptDomainSearchList(optData) - case OPTION_IA_NA: + case OptionIANA: opt, err = ParseOptIANA(optData) - case OPTION_IA_PD: + case OptionIAPD: opt, err = ParseOptIAForPrefixDelegation(optData) - case OPTION_IAADDR: + case OptionIAAddr: opt, err = ParseOptIAAddress(optData) - case OPTION_IAPREFIX: + case OptionIAPrefix: opt, err = ParseOptIAPrefix(optData) - case OPTION_STATUS_CODE: + case OptionStatusCode: opt, err = ParseOptStatusCode(optData) - case OPTION_RELAY_MSG: + case OptionRelayMsg: opt, err = ParseOptRelayMsg(optData) - case OPTION_REMOTE_ID: + case OptionRemoteID: opt, err = ParseOptRemoteId(optData) - case OPTION_INTERFACE_ID: + case OptionInterfaceID: opt, err = ParseOptInterfaceId(optData) - case OPTION_CLIENT_ARCH_TYPE: + case OptionClientArchType: opt, err = ParseOptClientArchType(optData) - case OPTION_NII: + case OptionNII: opt, err = ParseOptNetworkInterfaceId(optData) - case OPT_BOOTFILE_URL: + case OptionBootfileURL: opt, err = ParseOptBootFileURL(optData) - case OPTION_USER_CLASS: + case OptionUserClass: opt, err = ParseOptUserClass(optData) default: opt = &OptionGeneric{OptionCode: code, OptionData: optData} diff --git a/dhcpv6/types.go b/dhcpv6/types.go index 795a284..1ef5938 100644 --- a/dhcpv6/types.go +++ b/dhcpv6/types.go @@ -2,53 +2,59 @@ package dhcpv6 // from http://www.networksorcery.com/enp/protocol/dhcpv6.htm +// MessageType represents the kind of DHCPv6 message. type MessageType uint8 +// The different kinds of DHCPv6 message types. const ( - // MSGTYPE_NONE is used internally and is not part of the RFC - MSGTYPE_NONE MessageType = 0 - SOLICIT MessageType = 1 - ADVERTISE MessageType = 2 - REQUEST MessageType = 3 - CONFIRM MessageType = 4 - RENEW MessageType = 5 - REBIND MessageType = 6 - REPLY MessageType = 7 - RELEASE MessageType = 8 - DECLINE MessageType = 9 - RECONFIGURE MessageType = 10 - INFORMATION_REQUEST MessageType = 11 - RELAY_FORW MessageType = 12 - RELAY_REPL MessageType = 13 - LEASEQUERY MessageType = 14 - LEASEQUERY_REPLY MessageType = 15 - LEASEQUERY_DONE MessageType = 16 - LEASEQUERY_DATA MessageType = 17 + // MessageTypeNone is used internally and is not part of the RFC + MessageTypeNone MessageType = 0 + MessageTypeSolicit MessageType = 1 + MessageTypeAdvertise MessageType = 2 + MessageTypeRequest MessageType = 3 + MessageTypeConfirm MessageType = 4 + MessageTypeRenew MessageType = 5 + MessageTypeRebind MessageType = 6 + MessageTypeReply MessageType = 7 + MessageTypeRelease MessageType = 8 + MessageTypeDecline MessageType = 9 + MessageTypeReconfigure MessageType = 10 + MessageTypeInformationRequest MessageType = 11 + MessageTypeRelayForward MessageType = 12 + MessageTypeRelayReply MessageType = 13 + MessageTypeLeaseQuery MessageType = 14 + MessageTypeLeaseQueryReply MessageType = 15 + MessageTypeLeaseQueryDone MessageType = 16 + MessageTypeLeaseQueryData MessageType = 17 ) +// MessageTypeToString converts a MessageType to a human-readable string +// representation. func MessageTypeToString(t MessageType) string { - if m := MessageTypeToStringMap[t]; m != "" { + if m, ok := MessageTypeToStringMap[t]; ok { return m } return "Unknown" } +// MessageTypeToStringMap contains the mapping of MessageTypes to human-readable +// strings. var MessageTypeToStringMap = map[MessageType]string{ - SOLICIT: "SOLICIT", - ADVERTISE: "ADVERTISE", - REQUEST: "REQUEST", - CONFIRM: "CONFIRM", - RENEW: "RENEW", - REBIND: "REBIND", - REPLY: "REPLY", - RELEASE: "RELEASE", - DECLINE: "DECLINE", - RECONFIGURE: "RECONFIGURE", - INFORMATION_REQUEST: "INFORMATION-REQUEST", - RELAY_FORW: "RELAY-FORW", - RELAY_REPL: "RELAY-REPL", - LEASEQUERY: "LEASEQUERY", - LEASEQUERY_REPLY: "LEASEQUERY-REPLY", - LEASEQUERY_DONE: "LEASEQUERY-DONE", - LEASEQUERY_DATA: "LEASEQUERY-DATA", + MessageTypeSolicit: "SOLICIT", + MessageTypeAdvertise: "ADVERTISE", + MessageTypeRequest: "REQUEST", + MessageTypeConfirm: "CONFIRM", + MessageTypeRenew: "RENEW", + MessageTypeRebind: "REBIND", + MessageTypeReply: "REPLY", + MessageTypeRelease: "RELEASE", + MessageTypeDecline: "DECLINE", + MessageTypeReconfigure: "RECONFIGURE", + MessageTypeInformationRequest: "INFORMATION-REQUEST", + MessageTypeRelayForward: "RELAY-FORW", + MessageTypeRelayReply: "RELAY-REPL", + MessageTypeLeaseQuery: "LEASEQUERY", + MessageTypeLeaseQueryReply: "LEASEQUERY-REPLY", + MessageTypeLeaseQueryDone: "LEASEQUERY-DONE", + MessageTypeLeaseQueryData: "LEASEQUERY-DATA", } diff --git a/dhcpv6/utils.go b/dhcpv6/utils.go index b1c0b93..1681661 100644 --- a/dhcpv6/utils.go +++ b/dhcpv6/utils.go @@ -11,14 +11,14 @@ import ( // 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(OPTION_ORO) { + for _, optoro := range msg.GetOption(OptionORO) { for _, o := range optoro.(*OptRequestedOption).RequestedOptions() { - if o == OPT_BOOTFILE_URL { + if o == OptionBootfileURL { return true } } } - if optbf := msg.GetOneOption(OPT_BOOTFILE_URL); optbf != nil { + if optbf := msg.GetOneOption(OptionBootfileURL); optbf != nil { return true } return false @@ -42,14 +42,14 @@ func IsUsingUEFI(msg DHCPv6) bool { // 7 EFI BC // 8 EFI Xscale // 9 EFI x86-64 - if opt := msg.GetOneOption(OPTION_CLIENT_ARCH_TYPE); opt != nil { + 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(OPTION_USER_CLASS); opt != nil { + if opt := msg.GetOneOption(OptionUserClass); opt != nil { optuc := opt.(*OptUserClass) for _, uc := range optuc.UserClasses { if strings.Contains(string(uc), "EFI") { diff --git a/dhcpv6/utils_test.go b/dhcpv6/utils_test.go index 77205b4..f3b53f0 100644 --- a/dhcpv6/utils_test.go +++ b/dhcpv6/utils_test.go @@ -12,7 +12,7 @@ func TestIsNetboot(t *testing.T) { msg2 := DHCPv6Message{} optro := OptRequestedOption{} - optro.AddRequestedOption(OPT_BOOTFILE_URL) + optro.AddRequestedOption(OptionBootfileURL) msg2.AddOption(&optro) require.True(t, IsNetboot(&msg2)) @@ -61,7 +61,7 @@ func TestGetTransactionIDMessage(t *testing.T) { func TestGetTransactionIDRelay(t *testing.T) { message, err := NewMessage() require.NoError(t, err) - relay, err := EncapsulateRelay(message, RELAY_FORW, nil, nil) + relay, err := EncapsulateRelay(message, MessageTypeRelayForward, nil, nil) require.NoError(t, err) transactionID, err := GetTransactionID(relay) require.NoError(t, err) diff --git a/netboot/netboot.go b/netboot/netboot.go index 97a3240..81ba144 100644 --- a/netboot/netboot.go +++ b/netboot/netboot.go @@ -50,7 +50,7 @@ func ConversationToNetconf(conversation []dhcpv6.DHCPv6) (*NetConf, string, erro var reply dhcpv6.DHCPv6 for _, m := range conversation { // look for a REPLY - if m.Type() == dhcpv6.REPLY { + if m.Type() == dhcpv6.MessageTypeReply { reply = m break } @@ -67,14 +67,14 @@ func ConversationToNetconf(conversation []dhcpv6.DHCPv6) (*NetConf, string, erro opt dhcpv6.Option bootfile string ) - opt = reply.GetOneOption(dhcpv6.OPT_BOOTFILE_URL) + opt = reply.GetOneOption(dhcpv6.OptionBootfileURL) if opt == nil { log.Printf("no bootfile URL option found in REPLY, looking for it in ADVERTISE") // as a fallback, look for bootfile URL in the advertise var advertise dhcpv6.DHCPv6 for _, m := range conversation { // look for an ADVERTISE - if m.Type() == dhcpv6.ADVERTISE { + if m.Type() == dhcpv6.MessageTypeAdvertise { advertise = m break } @@ -82,7 +82,7 @@ func ConversationToNetconf(conversation []dhcpv6.DHCPv6) (*NetConf, string, erro if advertise == nil { return nil, "", errors.New("no ADVERTISE found") } - opt = advertise.GetOneOption(dhcpv6.OPT_BOOTFILE_URL) + opt = advertise.GetOneOption(dhcpv6.OptionBootfileURL) if opt == nil { return nil, "", errors.New("no bootfile URL option found in ADVERTISE") } diff --git a/netboot/netconf.go b/netboot/netconf.go index f7a117c..84fb263 100644 --- a/netboot/netconf.go +++ b/netboot/netconf.go @@ -30,7 +30,7 @@ type NetConf struct { // GetNetConfFromPacketv6 extracts network configuration information from a DHCPv6 // Reply packet and returns a populated NetConf structure func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) { - opt := d.GetOneOption(dhcpv6.OPTION_IA_NA) + opt := d.GetOneOption(dhcpv6.OptionIANA) if opt == nil { return nil, errors.New("No option IA NA found") } @@ -39,7 +39,7 @@ func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) { oiana := opt.(*dhcpv6.OptIANA) iaaddrs := make([]*dhcpv6.OptIAAddress, 0) for _, o := range oiana.Options { - if o.Code() == dhcpv6.OPTION_IAADDR { + if o.Code() == dhcpv6.OptionIAAddr { iaaddrs = append(iaaddrs, o.(*dhcpv6.OptIAAddress)) } } @@ -55,7 +55,7 @@ func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) { }) } // get DNS configuration - opt = d.GetOneOption(dhcpv6.DNS_RECURSIVE_NAME_SERVER) + opt = d.GetOneOption(dhcpv6.OptionDNSRecursiveNameServer) if opt == nil { return nil, errors.New("No option DNS Recursive Name Servers found ") } @@ -63,7 +63,7 @@ func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) { // TODO should this be copied? netconf.DNSServers = odnsserv.NameServers - opt = d.GetOneOption(dhcpv6.DOMAIN_SEARCH_LIST) + opt = d.GetOneOption(dhcpv6.OptionDomainSearchList) if opt == nil { return nil, errors.New("No option DNS Domain Search List found") } diff --git a/dhcpv6/option_rfc1035label.go b/rfc1035label/label.go index 06870b5..c63b4af 100644 --- a/dhcpv6/option_rfc1035label.go +++ b/rfc1035label/label.go @@ -1,10 +1,14 @@ -package dhcpv6 +package rfc1035label import ( "fmt" "strings" ) +// This implements the compression from RFC 1035, section 4.1.4 +// https://tools.ietf.org/html/rfc1035 + +// LabelsFromBytes decodes a serialized stream and returns a list of labels func LabelsFromBytes(buf []byte) ([]string, error) { var ( pos = 0 @@ -30,9 +34,9 @@ func LabelsFromBytes(buf []byte) ([]string, error) { label += string(buf[pos : pos+length]) pos += length } - return domains, nil } +// LabelToBytes encodes a label and returns a serialized stream of bytes func LabelToBytes(label string) []byte { var encodedLabel []byte if len(label) == 0 { @@ -45,6 +49,8 @@ func LabelToBytes(label string) []byte { return append(encodedLabel, 0) } +// LabelsToBytes encodes a list of labels and returns a serialized stream of +// bytes func LabelsToBytes(labels []string) []byte { var encodedLabels []byte for _, label := range labels { diff --git a/dhcpv6/option_rfc1035label_test.go b/rfc1035label/label_test.go index f99c209..f91110b 100644 --- a/dhcpv6/option_rfc1035label_test.go +++ b/rfc1035label/label_test.go @@ -1,4 +1,4 @@ -package dhcpv6 +package rfc1035label import ( "bytes" |