mirror of
https://github.com/danderson/netboot.git
synced 2025-08-07 23:27:16 +02:00
extracted packet generation into a dedicated class
This commit is contained in:
parent
ea2d223a25
commit
02db3ab76d
@ -10,13 +10,11 @@ type IdentityAssociation struct {
|
|||||||
clientId []byte
|
clientId []byte
|
||||||
interfaceId []byte
|
interfaceId []byte
|
||||||
createdAt time.Time
|
createdAt time.Time
|
||||||
t1 uint32
|
|
||||||
t2 uint32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddressPool interface {
|
type AddressPool interface {
|
||||||
ReserveAddress(clientId, interfaceId []byte) *IdentityAssociation
|
ReserveAddress(clientId, interfaceId []byte) *IdentityAssociation
|
||||||
ReleaseAddress(clientId, interfaceId []byte, addr net.IP)
|
ReleaseAddress(clientId, interfaceId []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -210,3 +210,33 @@ func (o Options) HasIaTa() bool {
|
|||||||
_, present := o[OptIaTa]
|
_, present := o[OptIaTa]
|
||||||
return present
|
return present
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o Options) HasClientArchType() bool {
|
||||||
|
_, present := o[OptClientArchType]
|
||||||
|
return present
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Options) ClientId() []byte {
|
||||||
|
opt, exists := o[OptClientId]
|
||||||
|
if exists {
|
||||||
|
return opt.Value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Options) IaNaId() []byte {
|
||||||
|
opt, exists := o[OptIaNa]
|
||||||
|
if exists {
|
||||||
|
return opt.Value[0:4]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Options) ClientArchType() uint16 {
|
||||||
|
opt, exists := o[OptClientArchType]
|
||||||
|
if exists {
|
||||||
|
return binary.BigEndian.Uint16(opt.Value)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
201
dhcp6/packet.go
201
dhcp6/packet.go
@ -2,7 +2,6 @@ package dhcp6
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"encoding/binary"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,6 +29,15 @@ type Packet struct {
|
|||||||
Options Options
|
Options Options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PacketBuilder struct {
|
||||||
|
ServerDuid []byte
|
||||||
|
PreferredLifetime uint32
|
||||||
|
ValidLifetime uint32
|
||||||
|
BootFileUrlForHttpBoot string
|
||||||
|
BootFileUrlForIpxe string
|
||||||
|
Addresses AddressPool
|
||||||
|
}
|
||||||
|
|
||||||
func MakePacket(bs []byte, packetLength int) (*Packet, error) {
|
func MakePacket(bs []byte, packetLength int) (*Packet, error) {
|
||||||
options, err := MakeOptions(bs[4:packetLength])
|
options, err := MakeOptions(bs[4:packetLength])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -40,6 +48,12 @@ func MakePacket(bs []byte, packetLength int) (*Packet, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MakePacketBuilder(serverDuid []byte, preferredLifetime, validLifetime uint32, httpBootFileUrl, ipxeBootFileUrl string,
|
||||||
|
addressPool AddressPool) *PacketBuilder {
|
||||||
|
return &PacketBuilder{ServerDuid: serverDuid, PreferredLifetime: preferredLifetime, ValidLifetime: validLifetime,
|
||||||
|
BootFileUrlForHttpBoot: httpBootFileUrl, BootFileUrlForIpxe: ipxeBootFileUrl, Addresses: addressPool}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Packet) Marshal() ([]byte, error) {
|
func (p *Packet) Marshal() ([]byte, error) {
|
||||||
marshalled_options, err := p.Options.Marshal()
|
marshalled_options, err := p.Options.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,97 +68,6 @@ func (p *Packet) Marshal() ([]byte, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Packet) BuildResponse(serverDuid []byte, addressPool AddressPool) *Packet {
|
|
||||||
transactionId := p.TransactionID
|
|
||||||
clientId := p.Options[OptClientId].Value
|
|
||||||
iaNaId := p.Options[OptIaNa].Value[0:4]
|
|
||||||
var clientArchType []byte
|
|
||||||
o, exists := p.Options[OptClientArchType]; if exists {
|
|
||||||
clientArchType = o.Value
|
|
||||||
}
|
|
||||||
switch p.Type {
|
|
||||||
case MsgSolicit:
|
|
||||||
return MakeMsgAdvertise(transactionId, serverDuid, clientId, iaNaId, clientArchType, addressPool)
|
|
||||||
case MsgRequest:
|
|
||||||
return MakeMsgReply(transactionId, serverDuid, clientId, iaNaId, clientArchType, addressPool)
|
|
||||||
case MsgInformationRequest:
|
|
||||||
return MakeMsgInformationRequestReply(transactionId, serverDuid, clientId, clientArchType)
|
|
||||||
case MsgRelease:
|
|
||||||
return MakeMsgReleaseReply(transactionId, serverDuid, clientId)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeMsgAdvertise(transactionId [3]byte, serverDuid, clientId, iaId, clientArchType []byte, addressPool AddressPool) *Packet {
|
|
||||||
ret_options := make(Options)
|
|
||||||
ret_options.AddOption(MakeOption(OptClientId, clientId))
|
|
||||||
association := addressPool.ReserveAddress(clientId, iaId)
|
|
||||||
ret_options.AddOption(MakeIaNaOption(iaId, association.t1, association.t2,
|
|
||||||
MakeIaAddrOption(association.ipAddress, 27000, 43200)))
|
|
||||||
ret_options.AddOption(MakeOption(OptServerId, serverDuid))
|
|
||||||
|
|
||||||
if 0x10 == binary.BigEndian.Uint16(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, []byte("http://[2001:db8:f00f:cafe::4]/bootx64.efi")))
|
|
||||||
} else {
|
|
||||||
ret_options.AddOption(MakeOption(OptBootfileUrl, []byte("http://[2001:db8:f00f:cafe::4]/script.ipxe")))
|
|
||||||
}
|
|
||||||
// ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1"))
|
|
||||||
//ret_options.AddOption(OptBootfileParam, []byte("http://")
|
|
||||||
//ret.Options[OptPreference] = [][]byte("http://")
|
|
||||||
|
|
||||||
return &Packet{Type: MsgAdvertise, TransactionID: transactionId, Options: ret_options}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: OptClientArchType may not be present
|
|
||||||
|
|
||||||
func MakeMsgReply(transactionId [3]byte, serverDuid, clientId, iaId, clientArchType []byte, addressPool AddressPool) *Packet {
|
|
||||||
ret_options := make(Options)
|
|
||||||
|
|
||||||
ret_options.AddOption(MakeOption(OptClientId, clientId))
|
|
||||||
association := addressPool.ReserveAddress(clientId, iaId)
|
|
||||||
ret_options.AddOption(MakeIaNaOption(iaId, association.t1, association.t2,
|
|
||||||
MakeIaAddrOption(association.ipAddress, 27000, 43200)))
|
|
||||||
ret_options.AddOption(MakeOption(OptServerId, serverDuid))
|
|
||||||
// ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1"))
|
|
||||||
if 0x10 == binary.BigEndian.Uint16(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, []byte("http://[2001:db8:f00f:cafe::4]/bootx64.efi")))
|
|
||||||
} else {
|
|
||||||
ret_options.AddOption(MakeOption(OptBootfileUrl, []byte("http://[2001:db8:f00f:cafe::4]/script.ipxe")))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeMsgInformationRequestReply(transactionId [3]byte, serverDuid, clientId, clientArchType []byte) *Packet {
|
|
||||||
ret_options := make(Options)
|
|
||||||
ret_options.AddOption(MakeOption(OptClientId, clientId))
|
|
||||||
ret_options.AddOption(MakeOption(OptServerId, serverDuid))
|
|
||||||
// ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1"))
|
|
||||||
if 0x10 == binary.BigEndian.Uint16(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, []byte("http://[2001:db8:f00f:cafe::4]/bootx64.efi")))
|
|
||||||
} else {
|
|
||||||
ret_options.AddOption(MakeOption(OptBootfileUrl, []byte("http://[2001:db8:f00f:cafe::4]/script.ipxe")))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeMsgReleaseReply(transactionId [3]byte, serverDuid, clientId []byte) *Packet {
|
|
||||||
ret_options := make(Options)
|
|
||||||
|
|
||||||
ret_options.AddOption(MakeOption(OptClientId, clientId))
|
|
||||||
ret_options.AddOption(MakeOption(OptServerId, 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 (p *Packet) ShouldDiscard(serverDuid []byte) error {
|
func (p *Packet) ShouldDiscard(serverDuid []byte) error {
|
||||||
switch p.Type {
|
switch p.Type {
|
||||||
case MsgSolicit:
|
case MsgSolicit:
|
||||||
@ -204,3 +127,97 @@ func ShouldDiscardInformationRequest(p *Packet, serverDuid []byte) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *PacketBuilder) BuildResponse(in *Packet) *Packet {
|
||||||
|
switch in.Type {
|
||||||
|
case MsgSolicit:
|
||||||
|
association := b.Addresses.ReserveAddress(in.Options.ClientId(), in.Options.IaNaId())
|
||||||
|
return b.MakeMsgAdvertise(in.TransactionID, in.Options.ClientId(), in.Options.IaNaId(),
|
||||||
|
in.Options.ClientArchType(), association.ipAddress)
|
||||||
|
case MsgRequest:
|
||||||
|
association := b.Addresses.ReserveAddress(in.Options.ClientId(), in.Options.IaNaId())
|
||||||
|
return b.MakeMsgReply(in.TransactionID, in.Options.ClientId(), in.Options.IaNaId(),
|
||||||
|
in.Options.ClientArchType(), association.ipAddress)
|
||||||
|
case MsgInformationRequest:
|
||||||
|
return b.MakeMsgInformationRequestReply(in.TransactionID, in.Options.ClientId(),
|
||||||
|
in.Options.ClientArchType())
|
||||||
|
case MsgRelease:
|
||||||
|
b.Addresses.ReleaseAddress(in.Options.ClientId(), in.Options.IaNaId())
|
||||||
|
return b.MakeMsgReleaseReply(in.TransactionID, in.Options.ClientId())
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId, iaId []byte, clientArchType uint16, ipAddress []byte) *Packet {
|
||||||
|
ret_options := make(Options)
|
||||||
|
ret_options.AddOption(MakeOption(OptClientId, clientId))
|
||||||
|
ret_options.AddOption(MakeIaNaOption(iaId, b.calculateT1(), b.calculateT2(),
|
||||||
|
MakeIaAddrOption(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, []byte(b.BootFileUrlForHttpBoot)))
|
||||||
|
} else {
|
||||||
|
ret_options.AddOption(MakeOption(OptBootfileUrl, []byte(b.BootFileUrlForIpxe)))
|
||||||
|
}
|
||||||
|
// ret_options.AddOption(OptRecursiveDns, net.ParseIP("2001:db8:f00f:cafe::1"))
|
||||||
|
//ret_options.AddOption(OptBootfileParam, []byte("http://")
|
||||||
|
//ret.Options[OptPreference] = [][]byte("http://")
|
||||||
|
|
||||||
|
return &Packet{Type: MsgAdvertise, TransactionID: transactionId, Options: ret_options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PacketBuilder) MakeMsgReply(transactionId [3]byte, clientId, iaId []byte, clientArchType uint16, ipAddress []byte) *Packet {
|
||||||
|
ret_options := make(Options)
|
||||||
|
ret_options.AddOption(MakeOption(OptClientId, clientId))
|
||||||
|
ret_options.AddOption(MakeIaNaOption(iaId, b.calculateT1(), b.calculateT2(),
|
||||||
|
MakeIaAddrOption(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, []byte(b.BootFileUrlForHttpBoot)))
|
||||||
|
} else {
|
||||||
|
ret_options.AddOption(MakeOption(OptBootfileUrl, []byte(b.BootFileUrlForIpxe)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Packet{Type: MsgReply, TransactionID: transactionId, Options: ret_options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PacketBuilder) MakeMsgInformationRequestReply(transactionId [3]byte, clientId []byte, clientArchType uint16) *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, []byte(b.BootFileUrlForHttpBoot)))
|
||||||
|
} else {
|
||||||
|
ret_options.AddOption(MakeOption(OptBootfileUrl, []byte(b.BootFileUrlForIpxe)))
|
||||||
|
}
|
||||||
|
|
||||||
|
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) calculateT1() uint32 {
|
||||||
|
return b.PreferredLifetime / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PacketBuilder) calculateT2() uint32 {
|
||||||
|
return (b.PreferredLifetime * 4)/5
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -10,10 +10,13 @@ func TestMakeMsgAdvertise(t *testing.T) {
|
|||||||
expectedClientId := []byte("clientid")
|
expectedClientId := []byte("clientid")
|
||||||
expectedServerId := []byte("serverid")
|
expectedServerId := []byte("serverid")
|
||||||
transactionId := [3]byte{'1', '2', '3'}
|
transactionId := [3]byte{'1', '2', '3'}
|
||||||
|
expectedIp := net.ParseIP("2001:db8:f00f:cafe::1")
|
||||||
|
|
||||||
msg := MakeMsgAdvertise(transactionId, expectedServerId, expectedClientId, []byte("1234"), []byte("11"),
|
builder := MakePacketBuilder(expectedServerId, 90, 100, "httpbootfileurl", "ipxebootfileurl",
|
||||||
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
||||||
|
|
||||||
|
msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x11, expectedIp)
|
||||||
|
|
||||||
if msg.Type != MsgAdvertise {
|
if msg.Type != MsgAdvertise {
|
||||||
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
|
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
|
||||||
}
|
}
|
||||||
@ -58,10 +61,13 @@ func TestMakeMsgAdvertiseWithHttpClientArch(t *testing.T) {
|
|||||||
expectedClientId := []byte("clientid")
|
expectedClientId := []byte("clientid")
|
||||||
expectedServerId := []byte("serverid")
|
expectedServerId := []byte("serverid")
|
||||||
transactionId := [3]byte{'1', '2', '3'}
|
transactionId := [3]byte{'1', '2', '3'}
|
||||||
|
expectedIp := net.ParseIP("2001:db8:f00f:cafe::1")
|
||||||
|
|
||||||
msg := MakeMsgAdvertise(transactionId, expectedServerId, expectedClientId, []byte("1234"), []byte{0x0, 0x10},
|
builder := MakePacketBuilder(expectedServerId, 90, 100, "httpbootfileurl", "ipxebootfileurl",
|
||||||
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
||||||
|
|
||||||
|
msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x10, expectedIp)
|
||||||
|
|
||||||
vendorClassOption := msg.Options[OptVendorClass]
|
vendorClassOption := msg.Options[OptVendorClass]
|
||||||
if vendorClassOption == nil {
|
if vendorClassOption == nil {
|
||||||
t.Fatalf("Vendor class option should be present")
|
t.Fatalf("Vendor class option should be present")
|
||||||
@ -77,10 +83,13 @@ func TestMakeMsgReply(t *testing.T) {
|
|||||||
expectedClientId := []byte("clientid")
|
expectedClientId := []byte("clientid")
|
||||||
expectedServerId := []byte("serverid")
|
expectedServerId := []byte("serverid")
|
||||||
transactionId := [3]byte{'1', '2', '3'}
|
transactionId := [3]byte{'1', '2', '3'}
|
||||||
|
expectedIp := net.ParseIP("2001:db8:f00f:cafe::1")
|
||||||
|
|
||||||
msg := MakeMsgReply(transactionId, expectedServerId, expectedClientId, []byte("1234"), []byte("11"),
|
builder := MakePacketBuilder(expectedServerId, 90, 100, "httpbootfileurl", "ipxebootfileurl",
|
||||||
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
||||||
|
|
||||||
|
msg := builder.MakeMsgReply(transactionId, expectedClientId, []byte("1234"), 0x11, expectedIp)
|
||||||
|
|
||||||
if msg.Type != MsgReply {
|
if msg.Type != MsgReply {
|
||||||
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
|
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
|
||||||
}
|
}
|
||||||
@ -125,10 +134,13 @@ func TestMakeMsgReplyWithHttpClientArch(t *testing.T) {
|
|||||||
expectedClientId := []byte("clientid")
|
expectedClientId := []byte("clientid")
|
||||||
expectedServerId := []byte("serverid")
|
expectedServerId := []byte("serverid")
|
||||||
transactionId := [3]byte{'1', '2', '3'}
|
transactionId := [3]byte{'1', '2', '3'}
|
||||||
|
expectedIp := net.ParseIP("2001:db8:f00f:cafe::1")
|
||||||
|
|
||||||
msg := MakeMsgReply(transactionId, expectedServerId, expectedClientId, []byte("1234"), []byte{0x0, 0x10},
|
builder := MakePacketBuilder(expectedServerId, 90, 100, "httpbootfileurl", "ipxebootfileurl",
|
||||||
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
||||||
|
|
||||||
|
msg := builder.MakeMsgReply(transactionId, expectedClientId, []byte("1234"), 0x10, expectedIp)
|
||||||
|
|
||||||
vendorClassOption := msg.Options[OptVendorClass]
|
vendorClassOption := msg.Options[OptVendorClass]
|
||||||
if vendorClassOption == nil {
|
if vendorClassOption == nil {
|
||||||
t.Fatalf("Vendor class option should be present")
|
t.Fatalf("Vendor class option should be present")
|
||||||
@ -145,8 +157,10 @@ func TestMakeMsgInformationRequestReply(t *testing.T) {
|
|||||||
expectedServerId := []byte("serverid")
|
expectedServerId := []byte("serverid")
|
||||||
transactionId := [3]byte{'1', '2', '3'}
|
transactionId := [3]byte{'1', '2', '3'}
|
||||||
|
|
||||||
msg := MakeMsgInformationRequestReply(transactionId, expectedServerId, expectedClientId, []byte("11"))
|
builder := MakePacketBuilder(expectedServerId, 90, 100, "httpbootfileurl", "ipxebootfileurl",
|
||||||
|
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
||||||
|
|
||||||
|
msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x11)
|
||||||
|
|
||||||
if msg.Type != MsgReply {
|
if msg.Type != MsgReply {
|
||||||
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
|
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
|
||||||
@ -188,7 +202,10 @@ func TestMakeMsgInformationRequestReplyWithHttpClientArch(t *testing.T) {
|
|||||||
expectedServerId := []byte("serverid")
|
expectedServerId := []byte("serverid")
|
||||||
transactionId := [3]byte{'1', '2', '3'}
|
transactionId := [3]byte{'1', '2', '3'}
|
||||||
|
|
||||||
msg := MakeMsgInformationRequestReply(transactionId, expectedServerId, expectedClientId, []byte{0x0, 0x10})
|
builder := MakePacketBuilder(expectedServerId, 90, 100, "httpbootfileurl", "ipxebootfileurl",
|
||||||
|
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
||||||
|
|
||||||
|
msg := builder.MakeMsgInformationRequestReply(transactionId, expectedClientId, 0x10)
|
||||||
|
|
||||||
vendorClassOption := msg.Options[OptVendorClass]
|
vendorClassOption := msg.Options[OptVendorClass]
|
||||||
if vendorClassOption == nil {
|
if vendorClassOption == nil {
|
||||||
@ -206,7 +223,10 @@ func TestMakeMsgReleaseReply(t *testing.T) {
|
|||||||
expectedServerId := []byte("serverid")
|
expectedServerId := []byte("serverid")
|
||||||
transactionId := [3]byte{'1', '2', '3'}
|
transactionId := [3]byte{'1', '2', '3'}
|
||||||
|
|
||||||
msg := MakeMsgReleaseReply(transactionId, expectedServerId, expectedClientId)
|
builder := MakePacketBuilder(expectedServerId, 90, 100, "httpbootfileurl", "ipxebootfileurl",
|
||||||
|
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
||||||
|
|
||||||
|
msg := builder.MakeMsgReleaseReply(transactionId, expectedClientId)
|
||||||
|
|
||||||
if msg.Type != MsgReply {
|
if msg.Type != MsgReply {
|
||||||
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
|
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
|
||||||
|
@ -46,14 +46,14 @@ type RandomAddressPool struct {
|
|||||||
identityAssociations map[uint64]*IdentityAssociation
|
identityAssociations map[uint64]*IdentityAssociation
|
||||||
usedIps map[uint64]struct{}
|
usedIps map[uint64]struct{}
|
||||||
identityAssociationExpirations Fifo
|
identityAssociationExpirations Fifo
|
||||||
preferredLifetime uint32 // in seconds
|
validLifetime uint32 // in seconds
|
||||||
timeNow func() time.Time
|
timeNow func() time.Time
|
||||||
lock chan int
|
lock chan int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRandomAddressPool(poolStartAddress, poolEndAddress net.IP, preferredLifetime uint32) *RandomAddressPool {
|
func NewRandomAddressPool(poolStartAddress, poolEndAddress net.IP, validLifetime uint32) *RandomAddressPool {
|
||||||
to_ret := &RandomAddressPool{}
|
to_ret := &RandomAddressPool{}
|
||||||
to_ret.preferredLifetime = preferredLifetime
|
to_ret.validLifetime = validLifetime
|
||||||
to_ret.poolStartAddress = big.NewInt(0)
|
to_ret.poolStartAddress = big.NewInt(0)
|
||||||
to_ret.poolStartAddress.SetBytes(poolStartAddress)
|
to_ret.poolStartAddress.SetBytes(poolStartAddress)
|
||||||
to_ret.poolEndAddress = big.NewInt(0)
|
to_ret.poolEndAddress = big.NewInt(0)
|
||||||
@ -94,12 +94,10 @@ func (p *RandomAddressPool) ReserveAddress(clientId, interfaceId []byte) *Identi
|
|||||||
to_ret := &IdentityAssociation{clientId: clientId,
|
to_ret := &IdentityAssociation{clientId: clientId,
|
||||||
interfaceId: interfaceId,
|
interfaceId: interfaceId,
|
||||||
ipAddress: newIp.Bytes(),
|
ipAddress: newIp.Bytes(),
|
||||||
createdAt: timeNow,
|
createdAt: timeNow }
|
||||||
t1: p.calculateT1(p.preferredLifetime),
|
|
||||||
t2: p.calculateT2(p.preferredLifetime) }
|
|
||||||
p.identityAssociations[clientIdHash] = to_ret
|
p.identityAssociations[clientIdHash] = to_ret
|
||||||
p.usedIps[newIp.Uint64()] = struct{}{}
|
p.usedIps[newIp.Uint64()] = struct{}{}
|
||||||
p.identityAssociationExpirations.Push(&AssociationExpiration{expiresAt: p.calculateAssociationExpiration(timeNow, p.preferredLifetime), ia: to_ret})
|
p.identityAssociationExpirations.Push(&AssociationExpiration{expiresAt: p.calculateAssociationExpiration(timeNow), ia: to_ret})
|
||||||
p.lock <- 1
|
p.lock <- 1
|
||||||
return to_ret
|
return to_ret
|
||||||
}
|
}
|
||||||
@ -108,10 +106,15 @@ func (p *RandomAddressPool) ReserveAddress(clientId, interfaceId []byte) *Identi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *RandomAddressPool) ReleaseAddress(clientId, interfaceId []byte, addr net.IP) {
|
func (p *RandomAddressPool) ReleaseAddress(clientId, interfaceId []byte) {
|
||||||
<-p.lock
|
<-p.lock
|
||||||
|
association, exists := p.identityAssociations[p.calculateIaIdHash(clientId, interfaceId)]
|
||||||
|
if !exists {
|
||||||
|
p.lock <- 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(p.usedIps, big.NewInt(0).SetBytes(association.ipAddress).Uint64())
|
||||||
delete(p.identityAssociations, p.calculateIaIdHash(clientId, interfaceId))
|
delete(p.identityAssociations, p.calculateIaIdHash(clientId, interfaceId))
|
||||||
delete(p.usedIps, big.NewInt(0).SetBytes(addr).Uint64())
|
|
||||||
p.lock <- 1
|
p.lock <- 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,16 +131,8 @@ func (p *RandomAddressPool) ExpireIdentityAssociations() {
|
|||||||
p.lock <- 1
|
p.lock <- 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *RandomAddressPool) calculateT1(preferredLifetime uint32) uint32 {
|
func (p *RandomAddressPool) calculateAssociationExpiration(now time.Time) time.Time {
|
||||||
return preferredLifetime / 2
|
return now.Add(time.Duration(p.validLifetime)*time.Second)
|
||||||
}
|
|
||||||
|
|
||||||
func (p *RandomAddressPool) calculateT2(preferredLifetime uint32) uint32 {
|
|
||||||
return (preferredLifetime * 4)/5
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *RandomAddressPool) calculateAssociationExpiration(now time.Time, preferredLifetime uint32) time.Time {
|
|
||||||
return now.Add(time.Duration(p.preferredLifetime)*time.Second)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *RandomAddressPool) calculateIaIdHash(clientId, interfaceId []byte) uint64 {
|
func (p *RandomAddressPool) calculateIaIdHash(clientId, interfaceId []byte) uint64 {
|
||||||
|
@ -35,12 +35,6 @@ func TestReserveAddress(t *testing.T) {
|
|||||||
if ia.createdAt != expectedTime {
|
if ia.createdAt != expectedTime {
|
||||||
t.Fatalf("Expected creation time: %v, but got: %v", expectedTime, ia.createdAt)
|
t.Fatalf("Expected creation time: %v, but got: %v", expectedTime, ia.createdAt)
|
||||||
}
|
}
|
||||||
expectedT1 := pool.calculateT1(expectedMaxLifetime); if ia.t1 != expectedT1 {
|
|
||||||
t.Fatalf("Expected creation t1: %v, but got: %v", expectedT1, ia.t1)
|
|
||||||
}
|
|
||||||
expectedT2 := pool.calculateT2(expectedMaxLifetime); if ia.t2 != expectedT2 {
|
|
||||||
t.Fatalf("Expected creation t2: %v, but got: %v", expectedT2, ia.t2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReserveAddressUpdatesAddressPool(t *testing.T) {
|
func TestReserveAddressUpdatesAddressPool(t *testing.T) {
|
||||||
@ -93,9 +87,9 @@ func TestReserveAddressKeepsTrackOfAssociationExpiration(t *testing.T) {
|
|||||||
if expiration == nil {
|
if expiration == nil {
|
||||||
t.Fatal("Expected an identity association expiration, but got nil")
|
t.Fatal("Expected an identity association expiration, but got nil")
|
||||||
}
|
}
|
||||||
if expiration.expiresAt != pool.calculateAssociationExpiration(expectedTime, expectedMaxLifetime) {
|
if expiration.expiresAt != pool.calculateAssociationExpiration(expectedTime) {
|
||||||
t.Fatalf("Expected association to expire at %v, but got %v",
|
t.Fatalf("Expected association to expire at %v, but got %v",
|
||||||
pool.calculateAssociationExpiration(expectedTime, expectedMaxLifetime), expiration.expiresAt)
|
pool.calculateAssociationExpiration(expectedTime), expiration.expiresAt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +119,7 @@ func TestReleaseAddress(t *testing.T) {
|
|||||||
pool.timeNow = func() time.Time { return expectedTime }
|
pool.timeNow = func() time.Time { return expectedTime }
|
||||||
a := pool.ReserveAddress(expectedClientId, expectedIaId)
|
a := pool.ReserveAddress(expectedClientId, expectedIaId)
|
||||||
|
|
||||||
pool.ReleaseAddress(expectedClientId, expectedIaId, a.ipAddress)
|
pool.ReleaseAddress(expectedClientId, expectedIaId)
|
||||||
|
|
||||||
_, exists := pool.identityAssociations[pool.calculateIaIdHash(expectedClientId, expectedIaId)]; if exists {
|
_, exists := pool.identityAssociations[pool.calculateIaIdHash(expectedClientId, expectedIaId)]; if exists {
|
||||||
t.Fatalf("identity association for %v should've been removed, but is still available", a.ipAddress)
|
t.Fatalf("identity association for %v should've been removed, but is still available", a.ipAddress)
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *ServerV6) serveDHCP(conn *dhcp6.Conn, addressPool dhcp6.AddressPool) error {
|
func (s *ServerV6) serveDHCP(conn *dhcp6.Conn, packetBuilder *dhcp6.PacketBuilder) error {
|
||||||
s.log("dhcpv6", "Waiting for packets...\n")
|
s.log("dhcpv6", "Waiting for packets...\n")
|
||||||
for {
|
for {
|
||||||
pkt, src, err := conn.RecvDHCP()
|
pkt, src, err := conn.RecvDHCP()
|
||||||
@ -19,7 +19,8 @@ func (s *ServerV6) serveDHCP(conn *dhcp6.Conn, addressPool dhcp6.AddressPool) er
|
|||||||
|
|
||||||
s.log("dhcpv6", fmt.Sprintf("Received (%d) packet (%d): %s\n", pkt.Type, pkt.TransactionID, pkt.Options.HumanReadable()))
|
s.log("dhcpv6", fmt.Sprintf("Received (%d) packet (%d): %s\n", pkt.Type, pkt.TransactionID, pkt.Options.HumanReadable()))
|
||||||
|
|
||||||
response := pkt.BuildResponse(s.Duid, addressPool)
|
response := packetBuilder.BuildResponse(pkt)
|
||||||
|
|
||||||
marshalled_response, err := response.Marshal()
|
marshalled_response, err := response.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log("dhcpv6", fmt.Sprintf("Error marshalling response: %s", response.Type, response.TransactionID, err))
|
s.log("dhcpv6", fmt.Sprintf("Error marshalling response: %s", response.Type, response.TransactionID, err))
|
||||||
|
@ -54,10 +54,14 @@ func (s *ServerV6) Serve() error {
|
|||||||
s.errs = make(chan error, 6)
|
s.errs = make(chan error, 6)
|
||||||
|
|
||||||
//s.debug("Init", "Starting Pixiecore goroutines")
|
//s.debug("Init", "Starting Pixiecore goroutines")
|
||||||
addressPool := dhcp6.NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::10"), net.ParseIP("2001:db8:f00f:cafe::100"), 1800)
|
|
||||||
|
|
||||||
s.SetDUID(dhcp.SourceHardwareAddress())
|
s.SetDUID(dhcp.SourceHardwareAddress())
|
||||||
go func() { s.errs <- s.serveDHCP(dhcp, addressPool) }()
|
|
||||||
|
addressPool := dhcp6.NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::10"), net.ParseIP("2001:db8:f00f:cafe::100"), 1850)
|
||||||
|
packetBuilder := dhcp6.MakePacketBuilder(s.Duid, 1800, 1850,
|
||||||
|
"http://[2001:db8:f00f:cafe::4]/bootx64.efi",
|
||||||
|
"http://[2001:db8:f00f:cafe::4]/script.ipxe", addressPool)
|
||||||
|
|
||||||
|
go func() { s.errs <- s.serveDHCP(dhcp, packetBuilder) }()
|
||||||
|
|
||||||
// Wait for either a fatal error, or Shutdown().
|
// Wait for either a fatal error, or Shutdown().
|
||||||
err = <-s.errs
|
err = <-s.errs
|
||||||
|
Loading…
Reference in New Issue
Block a user