mirror of
https://github.com/danderson/netboot.git
synced 2025-08-09 16:17:11 +02:00
extracted packet builder into a dedicated file
This commit is contained in:
parent
a3a8cf5188
commit
c0ce892172
191
dhcp6/packet.go
191
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()
|
||||
}
|
||||
|
194
dhcp6/packet_builder.go
Normal file
194
dhcp6/packet_builder.go
Normal file
@ -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()
|
||||
}
|
438
dhcp6/packet_builder_test.go
Normal file
438
dhcp6/packet_builder_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user