From c0ce892172ff2841cd64fad596d43f9b4ff9c1cd Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Fri, 27 Oct 2017 16:42:11 -0700 Subject: [PATCH] extracted packet builder into a dedicated file --- dhcp6/packet.go | 191 --------------- dhcp6/packet_builder.go | 194 ++++++++++++++++ dhcp6/packet_builder_test.go | 438 +++++++++++++++++++++++++++++++++++ dhcp6/packet_test.go | 432 ---------------------------------- 4 files changed, 632 insertions(+), 623 deletions(-) create mode 100644 dhcp6/packet_builder.go create mode 100644 dhcp6/packet_builder_test.go diff --git a/dhcp6/packet.go b/dhcp6/packet.go index 39f582f..6da5c0a 100644 --- a/dhcp6/packet.go +++ b/dhcp6/packet.go @@ -3,8 +3,6 @@ package dhcp6 import ( "fmt" "bytes" - "encoding/binary" - "hash/fnv" ) type MessageType uint8 @@ -31,14 +29,6 @@ type Packet struct { Options Options } -type PacketBuilder struct { - ServerDuid []byte - PreferredLifetime uint32 - ValidLifetime uint32 - Configuration BootConfiguration - Addresses AddressPool -} - func MakePacket(bs []byte, packetLength int) (*Packet, error) { options, err := MakeOptions(bs[4:packetLength]) if err != nil { @@ -49,12 +39,6 @@ func MakePacket(bs []byte, packetLength int) (*Packet, error) { return ret, nil } -func MakePacketBuilder(serverDuid []byte, preferredLifetime, validLifetime uint32, bootFileUrl BootConfiguration, - addressPool AddressPool) *PacketBuilder { - return &PacketBuilder{ServerDuid: serverDuid, PreferredLifetime: preferredLifetime, ValidLifetime: validLifetime, - Configuration: bootFileUrl, Addresses: addressPool} -} - func (p *Packet) Marshal() ([]byte, error) { marshalled_options, err := p.Options.Marshal() if err != nil { @@ -128,178 +112,3 @@ func ShouldDiscardInformationRequest(p *Packet, serverDuid []byte) error { } return nil } - -func (b *PacketBuilder) BuildResponse(in *Packet) (*Packet, error) { - - switch in.Type { - case MsgSolicit: - bootFileUrl, err := b.Configuration.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType()) - if err != nil { - return nil, err - } - associations, err := b.Addresses.ReserveAddresses(in.Options.ClientId(), in.Options.IaNaIds()) - if err != nil { - return b.MakeMsgAdvertiseWithNoAddrsAvailable(in.TransactionID, in.Options.ClientId(), err), err - } - return b.MakeMsgAdvertise(in.TransactionID, in.Options.ClientId(), - in.Options.ClientArchType(), associations, bootFileUrl, b.Configuration.GetPreference()), nil - case MsgRequest: - bootFileUrl, err := b.Configuration.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType()) - if err != nil { - return nil, err - } - associations, err := b.Addresses.ReserveAddresses(in.Options.ClientId(), in.Options.IaNaIds()) - if err != nil { - return b.MakeMsgReplyWithNoAddrsAvailable(in.TransactionID, in.Options.ClientId(), - in.Options.ClientArchType(), associations, iasWithoutAddesses(associations, in.Options.IaNaIds()), bootFileUrl, err), err - } - return b.MakeMsgReply(in.TransactionID, in.Options.ClientId(), - in.Options.ClientArchType(), associations, bootFileUrl), nil - case MsgInformationRequest: - bootFileUrl, err := b.Configuration.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType()) - if err != nil { - return nil, err - } - return b.MakeMsgInformationRequestReply(in.TransactionID, in.Options.ClientId(), - in.Options.ClientArchType(), bootFileUrl), nil - case MsgRelease: - b.Addresses.ReleaseAddresses(in.Options.ClientId(), in.Options.IaNaIds()) - return b.MakeMsgReleaseReply(in.TransactionID, in.Options.ClientId()), nil - default: - return nil, nil - } -} - -func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId []byte, clientArchType uint16, - associations []*IdentityAssociation, bootFileUrl, preference []byte) *Packet { - ret_options := make(Options) - ret_options.AddOption(MakeOption(OptClientId, clientId)) - for _, association := range(associations) { - ret_options.AddOption(MakeIaNaOption(association.InterfaceId, b.calculateT1(), b.calculateT2(), - MakeIaAddrOption(association.IpAddress, b.PreferredLifetime, b.ValidLifetime))) - } - ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) - if 0x10 == clientArchType { // HTTPClient - ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient - } - ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl)) - if preference != nil {ret_options.AddOption(MakeOption(OptPreference, preference))} - - //ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1")) - //ret_options.AddOption(OptBootfileParam, []byte("http://") - - return &Packet{Type: MsgAdvertise, TransactionID: transactionId, Options: ret_options} -} - -func (b *PacketBuilder) MakeMsgReply(transactionId [3]byte, clientId []byte, clientArchType uint16, - associations []*IdentityAssociation, bootFileUrl []byte) *Packet { - ret_options := make(Options) - ret_options.AddOption(MakeOption(OptClientId, clientId)) - for _, association := range(associations) { - ret_options.AddOption(MakeIaNaOption(association.InterfaceId, b.calculateT1(), b.calculateT2(), - MakeIaAddrOption(association.IpAddress, b.PreferredLifetime, b.ValidLifetime))) - } - ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) - if 0x10 == clientArchType { // HTTPClient - ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient - } - ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl)) - - return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options} -} - -func (b *PacketBuilder) MakeMsgInformationRequestReply(transactionId [3]byte, clientId []byte, clientArchType uint16, - bootFileUrl []byte) *Packet { - ret_options := make(Options) - ret_options.AddOption(MakeOption(OptClientId, clientId)) - ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) - if 0x10 == clientArchType { // HTTPClient - ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient - } - ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl)) - - return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options} -} - -func (b *PacketBuilder) MakeMsgReleaseReply(transactionId [3]byte, clientId []byte) *Packet { - ret_options := make(Options) - - ret_options.AddOption(MakeOption(OptClientId, clientId)) - ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) - v := make([]byte, 19, 19) - copy(v[2:], []byte("Release received.")) - ret_options.AddOption(MakeOption(OptStatusCode, v)) - - return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options} -} - -func (b *PacketBuilder) MakeMsgAdvertiseWithNoAddrsAvailable(transactionId [3]byte, clientId []byte, err error) *Packet { - ret_options := make(Options) - ret_options.AddOption(MakeOption(OptClientId, clientId)) - ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) - ret_options.AddOption(MakeStatusOption(2, err.Error())) // NoAddrAvailable - return &Packet{Type: MsgAdvertise, TransactionID: transactionId, Options: ret_options} -} - -func (b *PacketBuilder) MakeMsgReplyWithNoAddrsAvailable(transactionId [3]byte, clientId []byte, clientArchType uint16, - associations []*IdentityAssociation, iasWithoutAddresses [][]byte, bootFileUrl []byte, err error) *Packet { - ret_options := make(Options) - ret_options.AddOption(MakeOption(OptClientId, clientId)) - for _, association := range(associations) { - ret_options.AddOption(MakeIaNaOption(association.InterfaceId, b.calculateT1(), b.calculateT2(), - MakeIaAddrOption(association.IpAddress, b.PreferredLifetime, b.ValidLifetime))) - } - for _, ia := range(iasWithoutAddresses) { - ret_options.AddOption(MakeIaNaOption(ia, b.calculateT1(), b.calculateT2(), - MakeStatusOption(2, err.Error()))) - } - ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) - if 0x10 == clientArchType { // HTTPClient - ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient - } - ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl)) - - return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options} -} - -func (b *PacketBuilder) calculateT1() uint32 { - return b.PreferredLifetime / 2 -} - -func (b *PacketBuilder) calculateT2() uint32 { - return (b.PreferredLifetime * 4)/5 -} - -func (b *PacketBuilder) ExtractLLAddressOrId(optClientId []byte) []byte { - idType := binary.BigEndian.Uint16(optClientId[0:2]) - switch idType { - case 1: - return optClientId[8:] - case 3: - return optClientId[4:] - default: - return optClientId[2:] - } -} - -func iasWithoutAddesses(availableAssociations []*IdentityAssociation, allIas [][]byte) [][]byte { - ret := make([][]byte, 0) - iasWithAddresses := make(map[uint64]bool) - - for _, association := range(availableAssociations) { - iasWithAddresses[calculateIaIdHash(association.InterfaceId)] = true - } - - for _, ia := range(allIas) { - _, exists := iasWithAddresses[calculateIaIdHash(ia)]; if !exists { - ret = append(ret, ia) - } - } - return ret -} - -func calculateIaIdHash(interfaceId []byte) uint64 { - h := fnv.New64a() - h.Write(interfaceId) - return h.Sum64() -} diff --git a/dhcp6/packet_builder.go b/dhcp6/packet_builder.go new file mode 100644 index 0000000..f01d8f6 --- /dev/null +++ b/dhcp6/packet_builder.go @@ -0,0 +1,194 @@ +package dhcp6 + +import ( + "hash/fnv" + "encoding/binary" +) + +type PacketBuilder struct { + ServerDuid []byte + PreferredLifetime uint32 + ValidLifetime uint32 + Configuration BootConfiguration + Addresses AddressPool +} + +func MakePacketBuilder(serverDuid []byte, preferredLifetime, validLifetime uint32, bootFileUrl BootConfiguration, + addressPool AddressPool) *PacketBuilder { + return &PacketBuilder{ServerDuid: serverDuid, PreferredLifetime: preferredLifetime, ValidLifetime: validLifetime, + Configuration: bootFileUrl, Addresses: addressPool} +} + +func (b *PacketBuilder) BuildResponse(in *Packet) (*Packet, error) { + switch in.Type { + case MsgSolicit: + bootFileUrl, err := b.Configuration.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType()) + if err != nil { + return nil, err + } + associations, err := b.Addresses.ReserveAddresses(in.Options.ClientId(), in.Options.IaNaIds()) + if err != nil { + return b.MakeMsgAdvertiseWithNoAddrsAvailable(in.TransactionID, in.Options.ClientId(), err), err + } + return b.MakeMsgAdvertise(in.TransactionID, in.Options.ClientId(), + in.Options.ClientArchType(), associations, bootFileUrl, b.Configuration.GetPreference()), nil + case MsgRequest: + bootFileUrl, err := b.Configuration.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType()) + if err != nil { + return nil, err + } + associations, err := b.Addresses.ReserveAddresses(in.Options.ClientId(), in.Options.IaNaIds()) + if err != nil { + return b.MakeMsgReplyWithNoAddrsAvailable(in.TransactionID, in.Options.ClientId(), + in.Options.ClientArchType(), associations, iasWithoutAddesses(associations, in.Options.IaNaIds()), bootFileUrl, err), err + } + return b.MakeMsgReply(in.TransactionID, in.Options.ClientId(), + in.Options.ClientArchType(), associations, bootFileUrl), nil + case MsgInformationRequest: + bootFileUrl, err := b.Configuration.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType()) + if err != nil { + return nil, err + } + return b.MakeMsgInformationRequestReply(in.TransactionID, in.Options.ClientId(), + in.Options.ClientArchType(), bootFileUrl), nil + case MsgRelease: + b.Addresses.ReleaseAddresses(in.Options.ClientId(), in.Options.IaNaIds()) + return b.MakeMsgReleaseReply(in.TransactionID, in.Options.ClientId()), nil + default: + return nil, nil + } +} + +func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId []byte, clientArchType uint16, + associations []*IdentityAssociation, bootFileUrl, preference []byte) *Packet { + ret_options := make(Options) + ret_options.AddOption(MakeOption(OptClientId, clientId)) + for _, association := range(associations) { + ret_options.AddOption(MakeIaNaOption(association.InterfaceId, b.calculateT1(), b.calculateT2(), + MakeIaAddrOption(association.IpAddress, b.PreferredLifetime, b.ValidLifetime))) + } + ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) + if 0x10 == clientArchType { // HTTPClient + ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient + } + ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl)) + if preference != nil {ret_options.AddOption(MakeOption(OptPreference, preference))} + + //ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1")) + //ret_options.AddOption(OptBootfileParam, []byte("http://") + + return &Packet{Type: MsgAdvertise, TransactionID: transactionId, Options: ret_options} +} + +func (b *PacketBuilder) MakeMsgReply(transactionId [3]byte, clientId []byte, clientArchType uint16, + associations []*IdentityAssociation, bootFileUrl []byte) *Packet { + ret_options := make(Options) + ret_options.AddOption(MakeOption(OptClientId, clientId)) + for _, association := range(associations) { + ret_options.AddOption(MakeIaNaOption(association.InterfaceId, b.calculateT1(), b.calculateT2(), + MakeIaAddrOption(association.IpAddress, b.PreferredLifetime, b.ValidLifetime))) + } + ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) + if 0x10 == clientArchType { // HTTPClient + ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient + } + ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl)) + + return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options} +} + +func (b *PacketBuilder) MakeMsgInformationRequestReply(transactionId [3]byte, clientId []byte, clientArchType uint16, + bootFileUrl []byte) *Packet { + ret_options := make(Options) + ret_options.AddOption(MakeOption(OptClientId, clientId)) + ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) + if 0x10 == clientArchType { // HTTPClient + ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient + } + ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl)) + + return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options} +} + +func (b *PacketBuilder) MakeMsgReleaseReply(transactionId [3]byte, clientId []byte) *Packet { + ret_options := make(Options) + + ret_options.AddOption(MakeOption(OptClientId, clientId)) + ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) + v := make([]byte, 19, 19) + copy(v[2:], []byte("Release received.")) + ret_options.AddOption(MakeOption(OptStatusCode, v)) + + return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options} +} + +func (b *PacketBuilder) MakeMsgAdvertiseWithNoAddrsAvailable(transactionId [3]byte, clientId []byte, err error) *Packet { + ret_options := make(Options) + ret_options.AddOption(MakeOption(OptClientId, clientId)) + ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) + ret_options.AddOption(MakeStatusOption(2, err.Error())) // NoAddrAvailable + return &Packet{Type: MsgAdvertise, TransactionID: transactionId, Options: ret_options} +} + +func (b *PacketBuilder) MakeMsgReplyWithNoAddrsAvailable(transactionId [3]byte, clientId []byte, clientArchType uint16, + associations []*IdentityAssociation, iasWithoutAddresses [][]byte, bootFileUrl []byte, err error) *Packet { + ret_options := make(Options) + ret_options.AddOption(MakeOption(OptClientId, clientId)) + for _, association := range(associations) { + ret_options.AddOption(MakeIaNaOption(association.InterfaceId, b.calculateT1(), b.calculateT2(), + MakeIaAddrOption(association.IpAddress, b.PreferredLifetime, b.ValidLifetime))) + } + for _, ia := range(iasWithoutAddresses) { + ret_options.AddOption(MakeIaNaOption(ia, b.calculateT1(), b.calculateT2(), + MakeStatusOption(2, err.Error()))) + } + ret_options.AddOption(MakeOption(OptServerId, b.ServerDuid)) + if 0x10 == clientArchType { // HTTPClient + ret_options.AddOption(MakeOption(OptVendorClass, []byte {0, 0, 0, 0, 0, 10, 72, 84, 84, 80, 67, 108, 105, 101, 110, 116})) // HTTPClient + } + ret_options.AddOption(MakeOption(OptBootfileUrl, bootFileUrl)) + + return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options} +} + +func (b *PacketBuilder) calculateT1() uint32 { + return b.PreferredLifetime / 2 +} + +func (b *PacketBuilder) calculateT2() uint32 { + return (b.PreferredLifetime * 4)/5 +} + +func (b *PacketBuilder) ExtractLLAddressOrId(optClientId []byte) []byte { + idType := binary.BigEndian.Uint16(optClientId[0:2]) + switch idType { + case 1: + return optClientId[8:] + case 3: + return optClientId[4:] + default: + return optClientId[2:] + } +} + +func iasWithoutAddesses(availableAssociations []*IdentityAssociation, allIas [][]byte) [][]byte { + ret := make([][]byte, 0) + iasWithAddresses := make(map[uint64]bool) + + for _, association := range(availableAssociations) { + iasWithAddresses[calculateIaIdHash(association.InterfaceId)] = true + } + + for _, ia := range(allIas) { + _, exists := iasWithAddresses[calculateIaIdHash(ia)]; if !exists { + ret = append(ret, ia) + } + } + return ret +} + +func calculateIaIdHash(interfaceId []byte) uint64 { + h := fnv.New64a() + h.Write(interfaceId) + return h.Sum64() +} diff --git a/dhcp6/packet_builder_test.go b/dhcp6/packet_builder_test.go new file mode 100644 index 0000000..b37083e --- /dev/null +++ b/dhcp6/packet_builder_test.go @@ -0,0 +1,438 @@ +package dhcp6 + +import ( + "testing" + "encoding/binary" + "net" + "fmt" +) + +func TestMakeMsgAdvertise(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + expectedInterfaceId := []byte("id-1") + transactionId := [3]byte{'1', '2', '3'} + expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") + expectedBootFileUrl := []byte("http://bootfileurl") + identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: expectedInterfaceId} + + builder := MakePacketBuilder(expectedServerId, 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, 0x11, []*IdentityAssociation{identityAssociation}, + expectedBootFileUrl, nil) + + if msg.Type != MsgAdvertise { + t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) + } + if transactionId != msg.TransactionID { + t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) + } + + clientIdOption := msg.Options.ClientId() + if clientIdOption == nil { + t.Fatalf("Client Id option should be present") + } + if string(expectedClientId) != string(clientIdOption) { + t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) + } + + serverIdOption := msg.Options.ServerId() + if serverIdOption == nil { + t.Fatalf("Server Id option should be present") + } + if string(expectedServerId) != string(serverIdOption) { + t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) + } + + bootfileUrlOption := msg.Options[OptBootfileUrl][0] + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } + if string(expectedBootFileUrl) != string(bootfileUrlOption.Value) { + t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) + } + + iaNaOption := msg.Options.IaNaIds() + if len(iaNaOption) == 0 { + t.Fatalf("interface non-temporary association option should be present") + } + + preferenceOption := msg.Options[OptPreference] + if preferenceOption != nil { + t.Fatalf("Preference option shouldn't be set") + } +} + +func TestShouldSetPreferenceOptionWhenSpecified(t *testing.T) { + identityAssociation := &IdentityAssociation{IpAddress: net.ParseIP("2001:db8:f00f:cafe::1"), InterfaceId: []byte("id-1")} + + builder := MakePacketBuilder([]byte("serverid"), 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + expectedPreference := []byte{128} + msg := builder.MakeMsgAdvertise([3]byte{'t', 'i', 'd'}, []byte("clientid"), 0x11, + []*IdentityAssociation{identityAssociation}, []byte("http://bootfileurl"), expectedPreference) + + preferenceOption := msg.Options[OptPreference] + if preferenceOption == nil { + t.Fatalf("Preference option should be set") + } + if string(expectedPreference) != string(preferenceOption[0].Value) { + t.Fatalf("Expected preference value %d, got %d", expectedPreference, preferenceOption[0].Value) + } +} + +func TestMakeMsgAdvertiseWithHttpClientArch(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") + expectedBootFileUrl := []byte("http://bootfileurl") + identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: []byte("id-1")} + + builder := MakePacketBuilder(expectedServerId, 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, 0x10, []*IdentityAssociation{identityAssociation}, + expectedBootFileUrl, nil) + + vendorClassOption := msg.Options[OptVendorClass] + if vendorClassOption == nil { + t.Fatalf("Vendor class option should be present") + } + bootfileUrlOption := msg.Options.BootfileUrl() + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } + if string(expectedBootFileUrl) != string(bootfileUrlOption) { + t.Fatalf("Expected bootfile URL %s, got %s", expectedBootFileUrl, bootfileUrlOption) + } +} + +func TestMakeNoAddrsAvailable(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + expectedMessage := "Boom!" + + builder := MakePacketBuilder(expectedServerId, 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + msg := builder.MakeMsgAdvertiseWithNoAddrsAvailable(transactionId, expectedClientId, fmt.Errorf(expectedMessage)) + + if msg.Type != MsgAdvertise { + t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) + } + if transactionId != msg.TransactionID { + t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) + } + + clientIdOption := msg.Options.ClientId() + if clientIdOption == nil { + t.Fatalf("Client Id option should be present") + } + if string(expectedClientId) != string(clientIdOption) { + t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) + } + + serverIdOption := msg.Options.ServerId() + if serverIdOption == nil { + t.Fatalf("Server Id option should be present") + } + if string(expectedServerId) != string(serverIdOption) { + t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) + } + + _, exists := msg.Options[OptStatusCode]; if !exists { + t.Fatalf("Expected status code option to be present") + } + statusCodeOption := msg.Options[OptStatusCode][0].Value + if binary.BigEndian.Uint16(statusCodeOption[0:2]) != uint16(2) { + t.Fatalf("Expected status code 2, got %d", binary.BigEndian.Uint16(statusCodeOption[0:2])) + } + if string(statusCodeOption[2:]) != expectedMessage { + t.Fatalf("Expected message %s, got %s", expectedMessage, string(statusCodeOption[2:])) + } +} + +func TestMakeMsgReply(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") + expectedBootFileUrl := []byte("http://bootfileurl") + identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: []byte("id-1")} + + builder := MakePacketBuilder(expectedServerId, 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + msg := builder.MakeMsgReply(transactionId, expectedClientId, 0x11, []*IdentityAssociation{identityAssociation}, + expectedBootFileUrl) + + if msg.Type != MsgReply { + t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) + } + if transactionId != msg.TransactionID { + t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) + } + + clientIdOption := msg.Options.ClientId() + if clientIdOption == nil { + t.Fatalf("Client Id option should be present") + } + if string(expectedClientId) != string(clientIdOption) { + t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) + } + if len(expectedClientId) != len(clientIdOption) { + t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption)) + } + + serverIdOption := msg.Options.ServerId() + if serverIdOption == nil { + t.Fatalf("Server Id option should be present") + } + if string(expectedServerId) != string(serverIdOption) { + t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) + } + if len(expectedServerId) != len(serverIdOption) { + t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption)) + } + + bootfileUrlOption := msg.Options.BootfileUrl() + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } + if string(expectedBootFileUrl) != string(bootfileUrlOption) { + t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) + } + + iaNaOption := msg.Options[OptIaNa] + if iaNaOption == nil { + t.Fatalf("interface non-temporary association option should be present") + } +} + +func TestMakeMsgReplyWithHttpClientArch(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") + expectedBootFileUrl := []byte("http://bootfileurl") + identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: []byte("id-1")} + + builder := MakePacketBuilder(expectedServerId, 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + msg := builder.MakeMsgReply(transactionId, expectedClientId, 0x10, []*IdentityAssociation{identityAssociation}, + expectedBootFileUrl) + + vendorClassOption := msg.Options[OptVendorClass] + if vendorClassOption == nil { + t.Fatalf("Vendor class option should be present") + } + + bootfileUrlOption := msg.Options.BootfileUrl() + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } + if string(expectedBootFileUrl) != string(bootfileUrlOption) { + t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) + } +} + +func TestMakeMsgReplyWithNoAddrsAvailable(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") + expectedBootFileUrl := []byte("http://bootfileurl") + identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: []byte("id-1")} + expectedErrorMessage := "Boom!" + + builder := MakePacketBuilder(expectedServerId, 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + msg := builder.MakeMsgReplyWithNoAddrsAvailable(transactionId, expectedClientId, 0x10, + []*IdentityAssociation{identityAssociation}, [][]byte{[]byte("id-2")}, expectedBootFileUrl, + fmt.Errorf(expectedErrorMessage)) + + iaNaOption := msg.Options[OptIaNa] + if iaNaOption == nil { + t.Fatalf("interface non-temporary association options should be present") + } + if (len(iaNaOption)) != 2 { + t.Fatalf("Expected 2 identity associations, got %d", len(iaNaOption)) + } + var okIaNaOption, failedIaNaOption []byte + if string(iaNaOption[0].Value[0:4]) == string("id-1") { + okIaNaOption = iaNaOption[0].Value + failedIaNaOption = iaNaOption[1].Value + } else { + okIaNaOption = iaNaOption[1].Value + failedIaNaOption = iaNaOption[0].Value + } + + possiblyIaAddrOption, err := UnmarshalOption(okIaNaOption[12:]) + if err != nil { + t.Fatalf("Failed to unmarshal IaNa options: %s", err) + } + if possiblyIaAddrOption.Id != OptIaAddr { + t.Fatalf("Expected option 5 (ia address), got %d", possiblyIaAddrOption.Id) + } + + possiblyStatusOption, err := UnmarshalOption(failedIaNaOption[12:]) + if err != nil { + t.Fatalf("Failed to unmarshal IaNa options: %s", err) + } + if possiblyStatusOption.Id != OptStatusCode { + t.Fatalf("Expected option 13 (status code), got %d", possiblyStatusOption.Id) + } + if binary.BigEndian.Uint16(possiblyStatusOption.Value[0:2]) != uint16(2) { + t.Fatalf("Expected status code 2, got %d", binary.BigEndian.Uint16(possiblyStatusOption.Value[0:2])) + } + if string(possiblyStatusOption.Value[2:]) != expectedErrorMessage { + t.Fatalf("Expected message %s, got %s", expectedErrorMessage, string(possiblyStatusOption.Value[2:])) + } +} + +func TestMakeMsgInformationRequestReply(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + expectedBootFileUrl := []byte("http://bootfileurl") + + builder := MakePacketBuilder(expectedServerId, 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x11, expectedBootFileUrl) + + if msg.Type != MsgReply { + t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) + } + if transactionId != msg.TransactionID { + t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) + } + + clientIdOption := msg.Options.ClientId() + if clientIdOption == nil { + t.Fatalf("Client Id option should be present") + } + if string(expectedClientId) != string(clientIdOption) { + t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) + } + if len(expectedClientId) != len(clientIdOption) { + t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption)) + } + + serverIdOption := msg.Options.ServerId() + if serverIdOption == nil { + t.Fatalf("Server Id option should be present") + } + if string(expectedServerId) != string(serverIdOption) { + t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) + } + if len(expectedServerId) != len(serverIdOption) { + t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption)) + } + + bootfileUrlOption := msg.Options.BootfileUrl() + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } + if string(expectedBootFileUrl) != string(bootfileUrlOption) { + t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) + } +} + +func TestMakeMsgInformationRequestReplyWithHttpClientArch(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + expectedBootFileUrl := []byte("http://bootfileurl") + + builder := MakePacketBuilder(expectedServerId, 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x10, expectedBootFileUrl) + + vendorClassOption := msg.Options[OptVendorClass] + if vendorClassOption == nil { + t.Fatalf("Vendor class option should be present") + } + + bootfileUrlOption := msg.Options.BootfileUrl() + if bootfileUrlOption == nil { + t.Fatalf("Bootfile URL option should be present") + } + if string(expectedBootFileUrl) != string(bootfileUrlOption) { + t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) + } +} + +func TestMakeMsgReleaseReply(t *testing.T) { + expectedClientId := []byte("clientid") + expectedServerId := []byte("serverid") + transactionId := [3]byte{'1', '2', '3'} + + builder := MakePacketBuilder(expectedServerId, 90, 100, nil, + NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) + + msg := builder.MakeMsgReleaseReply(transactionId, expectedClientId) + + if msg.Type != MsgReply { + t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) + } + if transactionId != msg.TransactionID { + t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) + } + + clientIdOption := msg.Options.ClientId() + if clientIdOption == nil { + t.Fatalf("Client Id option should be present") + } + if string(expectedClientId) != string(clientIdOption) { + t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) + } + if len(expectedClientId) != len(clientIdOption) { + t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption)) + } + + serverIdOption := msg.Options.ServerId() + if serverIdOption == nil { + t.Fatalf("Server Id option should be present") + } + if string(expectedServerId) != string(serverIdOption) { + t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) + } + if len(expectedServerId) != len(serverIdOption) { + t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption)) + } +} + +func TestExtractLLAddressOrIdWithDUIDLLT(t *testing.T) { + builder := &PacketBuilder{} + expectedLLAddress := []byte{0xac, 0xbc, 0x32, 0xae, 0x86, 0x37} + llAddress := builder.ExtractLLAddressOrId([]byte{0x0, 0x1, 0x0, 0x1, 0x1, 0x2, 0x3, 0x4, 0xac, 0xbc, 0x32, 0xae, 0x86, 0x37}) + if string(expectedLLAddress) != string(llAddress) { + t.Fatalf("Expected ll address %x, got: %x", expectedLLAddress, llAddress) + } +} + +func TestExtractLLAddressOrIdWithDUIDEN(t *testing.T) { + builder := &PacketBuilder{} + expectedId := []byte{0x0, 0x1, 0x2, 0x3, 0xac, 0xbc, 0x32, 0xae, 0x86, 0x37} + id := builder.ExtractLLAddressOrId([]byte{0x0, 0x2, 0x0, 0x1, 0x2, 0x3, 0xac, 0xbc, 0x32, 0xae, 0x86, 0x37}) + if string(expectedId) != string(id) { + t.Fatalf("Expected id %x, got: %x", expectedId, id) + } +} + +func TestExtractLLAddressOrIdWithDUIDLL(t *testing.T) { + builder := &PacketBuilder{} + expectedLLAddress := []byte{0xac, 0xbc, 0x32, 0xae, 0x86, 0x37} + llAddress := builder.ExtractLLAddressOrId([]byte{0x0, 0x3, 0x0, 0x1, 0xac, 0xbc, 0x32, 0xae, 0x86, 0x37}) + if string(expectedLLAddress) != string(llAddress) { + t.Fatalf("Expected ll address %x, got: %x", expectedLLAddress, llAddress) + } +} diff --git a/dhcp6/packet_test.go b/dhcp6/packet_test.go index 48a91df..e21c471 100644 --- a/dhcp6/packet_test.go +++ b/dhcp6/packet_test.go @@ -3,440 +3,8 @@ package dhcp6 import ( "testing" "encoding/binary" - "net" - "fmt" ) -func TestMakeMsgAdvertise(t *testing.T) { - expectedClientId := []byte("clientid") - expectedServerId := []byte("serverid") - expectedInterfaceId := []byte("id-1") - transactionId := [3]byte{'1', '2', '3'} - expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") - expectedBootFileUrl := []byte("http://bootfileurl") - identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: expectedInterfaceId} - - builder := MakePacketBuilder(expectedServerId, 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, 0x11, []*IdentityAssociation{identityAssociation}, - expectedBootFileUrl, nil) - - if msg.Type != MsgAdvertise { - t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) - } - if transactionId != msg.TransactionID { - t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) - } - - clientIdOption := msg.Options.ClientId() - if clientIdOption == nil { - t.Fatalf("Client Id option should be present") - } - if string(expectedClientId) != string(clientIdOption) { - t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) - } - - serverIdOption := msg.Options.ServerId() - if serverIdOption == nil { - t.Fatalf("Server Id option should be present") - } - if string(expectedServerId) != string(serverIdOption) { - t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) - } - - bootfileUrlOption := msg.Options[OptBootfileUrl][0] - if bootfileUrlOption == nil { - t.Fatalf("Bootfile URL option should be present") - } - if string(expectedBootFileUrl) != string(bootfileUrlOption.Value) { - t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) - } - - iaNaOption := msg.Options.IaNaIds() - if len(iaNaOption) == 0 { - t.Fatalf("interface non-temporary association option should be present") - } - - preferenceOption := msg.Options[OptPreference] - if preferenceOption != nil { - t.Fatalf("Preference option shouldn't be set") - } -} - -func TestShouldSetPreferenceOptionWhenSpecified(t *testing.T) { - identityAssociation := &IdentityAssociation{IpAddress: net.ParseIP("2001:db8:f00f:cafe::1"), InterfaceId: []byte("id-1")} - - builder := MakePacketBuilder([]byte("serverid"), 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - expectedPreference := []byte{128} - msg := builder.MakeMsgAdvertise([3]byte{'t', 'i', 'd'}, []byte("clientid"), 0x11, - []*IdentityAssociation{identityAssociation}, []byte("http://bootfileurl"), expectedPreference) - - preferenceOption := msg.Options[OptPreference] - if preferenceOption == nil { - t.Fatalf("Preference option should be set") - } - if string(expectedPreference) != string(preferenceOption[0].Value) { - t.Fatalf("Expected preference value %d, got %d", expectedPreference, preferenceOption[0].Value) - } -} - -func TestMakeMsgAdvertiseWithHttpClientArch(t *testing.T) { - expectedClientId := []byte("clientid") - expectedServerId := []byte("serverid") - transactionId := [3]byte{'1', '2', '3'} - expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") - expectedBootFileUrl := []byte("http://bootfileurl") - identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: []byte("id-1")} - - builder := MakePacketBuilder(expectedServerId, 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, 0x10, []*IdentityAssociation{identityAssociation}, - expectedBootFileUrl, nil) - - vendorClassOption := msg.Options[OptVendorClass] - if vendorClassOption == nil { - t.Fatalf("Vendor class option should be present") - } - bootfileUrlOption := msg.Options.BootfileUrl() - if bootfileUrlOption == nil { - t.Fatalf("Bootfile URL option should be present") - } - if string(expectedBootFileUrl) != string(bootfileUrlOption) { - t.Fatalf("Expected bootfile URL %s, got %s", expectedBootFileUrl, bootfileUrlOption) - } -} - -func TestMakeNoAddrsAvailable(t *testing.T) { - expectedClientId := []byte("clientid") - expectedServerId := []byte("serverid") - transactionId := [3]byte{'1', '2', '3'} - expectedMessage := "Boom!" - - builder := MakePacketBuilder(expectedServerId, 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - msg := builder.MakeMsgAdvertiseWithNoAddrsAvailable(transactionId, expectedClientId, fmt.Errorf(expectedMessage)) - - if msg.Type != MsgAdvertise { - t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) - } - if transactionId != msg.TransactionID { - t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) - } - - clientIdOption := msg.Options.ClientId() - if clientIdOption == nil { - t.Fatalf("Client Id option should be present") - } - if string(expectedClientId) != string(clientIdOption) { - t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) - } - - serverIdOption := msg.Options.ServerId() - if serverIdOption == nil { - t.Fatalf("Server Id option should be present") - } - if string(expectedServerId) != string(serverIdOption) { - t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) - } - - _, exists := msg.Options[OptStatusCode]; if !exists { - t.Fatalf("Expected status code option to be present") - } - statusCodeOption := msg.Options[OptStatusCode][0].Value - if binary.BigEndian.Uint16(statusCodeOption[0:2]) != uint16(2) { - t.Fatalf("Expected status code 2, got %d", binary.BigEndian.Uint16(statusCodeOption[0:2])) - } - if string(statusCodeOption[2:]) != expectedMessage { - t.Fatalf("Expected message %s, got %s", expectedMessage, string(statusCodeOption[2:])) - } -} - -func TestMakeMsgReply(t *testing.T) { - expectedClientId := []byte("clientid") - expectedServerId := []byte("serverid") - transactionId := [3]byte{'1', '2', '3'} - expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") - expectedBootFileUrl := []byte("http://bootfileurl") - identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: []byte("id-1")} - - builder := MakePacketBuilder(expectedServerId, 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - msg := builder.MakeMsgReply(transactionId, expectedClientId, 0x11, []*IdentityAssociation{identityAssociation}, - expectedBootFileUrl) - - if msg.Type != MsgReply { - t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) - } - if transactionId != msg.TransactionID { - t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) - } - - clientIdOption := msg.Options.ClientId() - if clientIdOption == nil { - t.Fatalf("Client Id option should be present") - } - if string(expectedClientId) != string(clientIdOption) { - t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) - } - if len(expectedClientId) != len(clientIdOption) { - t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption)) - } - - serverIdOption := msg.Options.ServerId() - if serverIdOption == nil { - t.Fatalf("Server Id option should be present") - } - if string(expectedServerId) != string(serverIdOption) { - t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) - } - if len(expectedServerId) != len(serverIdOption) { - t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption)) - } - - bootfileUrlOption := msg.Options.BootfileUrl() - if bootfileUrlOption == nil { - t.Fatalf("Bootfile URL option should be present") - } - if string(expectedBootFileUrl) != string(bootfileUrlOption) { - t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) - } - - iaNaOption := msg.Options[OptIaNa] - if iaNaOption == nil { - t.Fatalf("interface non-temporary association option should be present") - } -} - -func TestMakeMsgReplyWithHttpClientArch(t *testing.T) { - expectedClientId := []byte("clientid") - expectedServerId := []byte("serverid") - transactionId := [3]byte{'1', '2', '3'} - expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") - expectedBootFileUrl := []byte("http://bootfileurl") - identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: []byte("id-1")} - - builder := MakePacketBuilder(expectedServerId, 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - msg := builder.MakeMsgReply(transactionId, expectedClientId, 0x10, []*IdentityAssociation{identityAssociation}, - expectedBootFileUrl) - - vendorClassOption := msg.Options[OptVendorClass] - if vendorClassOption == nil { - t.Fatalf("Vendor class option should be present") - } - - bootfileUrlOption := msg.Options.BootfileUrl() - if bootfileUrlOption == nil { - t.Fatalf("Bootfile URL option should be present") - } - if string(expectedBootFileUrl) != string(bootfileUrlOption) { - t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) - } -} - -func TestMakeMsgReplyWithNoAddrsAvailable(t *testing.T) { - expectedClientId := []byte("clientid") - expectedServerId := []byte("serverid") - transactionId := [3]byte{'1', '2', '3'} - expectedIp := net.ParseIP("2001:db8:f00f:cafe::1") - expectedBootFileUrl := []byte("http://bootfileurl") - identityAssociation := &IdentityAssociation{IpAddress: expectedIp, InterfaceId: []byte("id-1")} - expectedErrorMessage := "Boom!" - - builder := MakePacketBuilder(expectedServerId, 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - msg := builder.MakeMsgReplyWithNoAddrsAvailable(transactionId, expectedClientId, 0x10, - []*IdentityAssociation{identityAssociation}, [][]byte{[]byte("id-2")}, expectedBootFileUrl, - fmt.Errorf(expectedErrorMessage)) - - iaNaOption := msg.Options[OptIaNa] - if iaNaOption == nil { - t.Fatalf("interface non-temporary association options should be present") - } - if (len(iaNaOption)) != 2 { - t.Fatalf("Expected 2 identity associations, got %d", len(iaNaOption)) - } - var okIaNaOption, failedIaNaOption []byte - if string(iaNaOption[0].Value[0:4]) == string("id-1") { - okIaNaOption = iaNaOption[0].Value - failedIaNaOption = iaNaOption[1].Value - } else { - okIaNaOption = iaNaOption[1].Value - failedIaNaOption = iaNaOption[0].Value - } - - possiblyIaAddrOption, err := UnmarshalOption(okIaNaOption[12:]) - if err != nil { - t.Fatalf("Failed to unmarshal IaNa options: %s", err) - } - if possiblyIaAddrOption.Id != OptIaAddr { - t.Fatalf("Expected option 5 (ia address), got %d", possiblyIaAddrOption.Id) - } - - possiblyStatusOption, err := UnmarshalOption(failedIaNaOption[12:]) - if err != nil { - t.Fatalf("Failed to unmarshal IaNa options: %s", err) - } - if possiblyStatusOption.Id != OptStatusCode { - t.Fatalf("Expected option 13 (status code), got %d", possiblyStatusOption.Id) - } - if binary.BigEndian.Uint16(possiblyStatusOption.Value[0:2]) != uint16(2) { - t.Fatalf("Expected status code 2, got %d", binary.BigEndian.Uint16(possiblyStatusOption.Value[0:2])) - } - if string(possiblyStatusOption.Value[2:]) != expectedErrorMessage { - t.Fatalf("Expected message %s, got %s", expectedErrorMessage, string(possiblyStatusOption.Value[2:])) - } -} - -func TestMakeMsgInformationRequestReply(t *testing.T) { - expectedClientId := []byte("clientid") - expectedServerId := []byte("serverid") - transactionId := [3]byte{'1', '2', '3'} - expectedBootFileUrl := []byte("http://bootfileurl") - - builder := MakePacketBuilder(expectedServerId, 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x11, expectedBootFileUrl) - - if msg.Type != MsgReply { - t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) - } - if transactionId != msg.TransactionID { - t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) - } - - clientIdOption := msg.Options.ClientId() - if clientIdOption == nil { - t.Fatalf("Client Id option should be present") - } - if string(expectedClientId) != string(clientIdOption) { - t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) - } - if len(expectedClientId) != len(clientIdOption) { - t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption)) - } - - serverIdOption := msg.Options.ServerId() - if serverIdOption == nil { - t.Fatalf("Server Id option should be present") - } - if string(expectedServerId) != string(serverIdOption) { - t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) - } - if len(expectedServerId) != len(serverIdOption) { - t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption)) - } - - bootfileUrlOption := msg.Options.BootfileUrl() - if bootfileUrlOption == nil { - t.Fatalf("Bootfile URL option should be present") - } - if string(expectedBootFileUrl) != string(bootfileUrlOption) { - t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) - } -} - -func TestMakeMsgInformationRequestReplyWithHttpClientArch(t *testing.T) { - expectedClientId := []byte("clientid") - expectedServerId := []byte("serverid") - transactionId := [3]byte{'1', '2', '3'} - expectedBootFileUrl := []byte("http://bootfileurl") - - builder := MakePacketBuilder(expectedServerId, 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x10, expectedBootFileUrl) - - vendorClassOption := msg.Options[OptVendorClass] - if vendorClassOption == nil { - t.Fatalf("Vendor class option should be present") - } - - bootfileUrlOption := msg.Options.BootfileUrl() - if bootfileUrlOption == nil { - t.Fatalf("Bootfile URL option should be present") - } - if string(expectedBootFileUrl) != string(bootfileUrlOption) { - t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption) - } -} - -func TestMakeMsgReleaseReply(t *testing.T) { - expectedClientId := []byte("clientid") - expectedServerId := []byte("serverid") - transactionId := [3]byte{'1', '2', '3'} - - builder := MakePacketBuilder(expectedServerId, 90, 100, nil, - NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), 1, 100)) - - msg := builder.MakeMsgReleaseReply(transactionId, expectedClientId) - - if msg.Type != MsgReply { - t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) - } - if transactionId != msg.TransactionID { - t.Fatalf("Expected transaction Id %v, got %v", transactionId, msg.TransactionID) - } - - clientIdOption := msg.Options.ClientId() - if clientIdOption == nil { - t.Fatalf("Client Id option should be present") - } - if string(expectedClientId) != string(clientIdOption) { - t.Fatalf("Expected client id %v, got %v", expectedClientId, clientIdOption) - } - if len(expectedClientId) != len(clientIdOption) { - t.Fatalf("Expected client id length of %d, got %d", len(expectedClientId), len(clientIdOption)) - } - - serverIdOption := msg.Options.ServerId() - if serverIdOption == nil { - t.Fatalf("Server Id option should be present") - } - if string(expectedServerId) != string(serverIdOption) { - t.Fatalf("Expected server id %v, got %v", expectedClientId, serverIdOption) - } - if len(expectedServerId) != len(serverIdOption) { - t.Fatalf("Expected server id length of %d, got %d", len(expectedClientId), len(serverIdOption)) - } -} - -func TestExtractLLAddressOrIdWithDUIDLLT(t *testing.T) { - builder := &PacketBuilder{} - expectedLLAddress := []byte{0xac, 0xbc, 0x32, 0xae, 0x86, 0x37} - llAddress := builder.ExtractLLAddressOrId([]byte{0x0, 0x1, 0x0, 0x1, 0x1, 0x2, 0x3, 0x4, 0xac, 0xbc, 0x32, 0xae, 0x86, 0x37}) - if string(expectedLLAddress) != string(llAddress) { - t.Fatalf("Expected ll address %x, got: %x", expectedLLAddress, llAddress) - } -} - -func TestExtractLLAddressOrIdWithDUIDEN(t *testing.T) { - builder := &PacketBuilder{} - expectedId := []byte{0x0, 0x1, 0x2, 0x3, 0xac, 0xbc, 0x32, 0xae, 0x86, 0x37} - id := builder.ExtractLLAddressOrId([]byte{0x0, 0x2, 0x0, 0x1, 0x2, 0x3, 0xac, 0xbc, 0x32, 0xae, 0x86, 0x37}) - if string(expectedId) != string(id) { - t.Fatalf("Expected id %x, got: %x", expectedId, id) - } -} - -func TestExtractLLAddressOrIdWithDUIDLL(t *testing.T) { - builder := &PacketBuilder{} - expectedLLAddress := []byte{0xac, 0xbc, 0x32, 0xae, 0x86, 0x37} - llAddress := builder.ExtractLLAddressOrId([]byte{0x0, 0x3, 0x0, 0x1, 0xac, 0xbc, 0x32, 0xae, 0x86, 0x37}) - if string(expectedLLAddress) != string(llAddress) { - t.Fatalf("Expected ll address %x, got: %x", expectedLLAddress, llAddress) - } -} - func TestShouldDiscardSolicitWithoutBootfileUrlOption(t *testing.T) { clientId := []byte("clientid") options := make(Options)