diff --git a/dhcp6/address_pool.go b/dhcp6/address_pool.go index 7328753..21dc56c 100644 --- a/dhcp6/address_pool.go +++ b/dhcp6/address_pool.go @@ -10,13 +10,11 @@ type IdentityAssociation struct { clientId []byte interfaceId []byte createdAt time.Time - t1 uint32 - t2 uint32 } type AddressPool interface { ReserveAddress(clientId, interfaceId []byte) *IdentityAssociation - ReleaseAddress(clientId, interfaceId []byte, addr net.IP) + ReleaseAddress(clientId, interfaceId []byte) } diff --git a/dhcp6/options.go b/dhcp6/options.go index 2bd891a..eecaf46 100644 --- a/dhcp6/options.go +++ b/dhcp6/options.go @@ -209,4 +209,34 @@ func (o Options) HasIaNa() bool { func (o Options) HasIaTa() bool { _, present := o[OptIaTa] return present -} \ No newline at end of file +} + +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 +} + diff --git a/dhcp6/packet.go b/dhcp6/packet.go index 0a7a3c8..5b072d1 100644 --- a/dhcp6/packet.go +++ b/dhcp6/packet.go @@ -2,7 +2,6 @@ package dhcp6 import ( "fmt" - "encoding/binary" "bytes" ) @@ -30,6 +29,15 @@ type Packet struct { 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) { options, err := MakeOptions(bs[4:packetLength]) if err != nil { @@ -40,6 +48,12 @@ func MakePacket(bs []byte, packetLength int) (*Packet, error) { 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) { marshalled_options, err := p.Options.Marshal() if err != nil { @@ -54,97 +68,6 @@ func (p *Packet) Marshal() ([]byte, error) { 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 { switch p.Type { case MsgSolicit: @@ -203,4 +126,98 @@ func ShouldDiscardInformationRequest(p *Packet, serverDuid []byte) error { return fmt.Errorf("'Information-request' packet's server id option (%d) is different from ours (%d)", options[OptServerId].Value, serverDuid) } return nil -} \ No newline at end of file +} + +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 +} + diff --git a/dhcp6/packet_test.go b/dhcp6/packet_test.go index 7885aa2..76ce62f 100644 --- a/dhcp6/packet_test.go +++ b/dhcp6/packet_test.go @@ -10,10 +10,13 @@ func TestMakeMsgAdvertise(t *testing.T) { expectedClientId := []byte("clientid") expectedServerId := []byte("serverid") 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)) + msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x11, expectedIp) + if msg.Type != MsgAdvertise { t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) } @@ -58,10 +61,13 @@ 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") - 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)) + msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x10, expectedIp) + vendorClassOption := msg.Options[OptVendorClass] if vendorClassOption == nil { t.Fatalf("Vendor class option should be present") @@ -77,10 +83,13 @@ 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") - 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)) + msg := builder.MakeMsgReply(transactionId, expectedClientId, []byte("1234"), 0x11, expectedIp) + if msg.Type != MsgReply { t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) } @@ -125,10 +134,13 @@ 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") - 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)) + msg := builder.MakeMsgReply(transactionId, expectedClientId, []byte("1234"), 0x10, expectedIp) + vendorClassOption := msg.Options[OptVendorClass] if vendorClassOption == nil { t.Fatalf("Vendor class option should be present") @@ -145,8 +157,10 @@ func TestMakeMsgInformationRequestReply(t *testing.T) { expectedServerId := []byte("serverid") 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 { t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) @@ -188,7 +202,10 @@ func TestMakeMsgInformationRequestReplyWithHttpClientArch(t *testing.T) { expectedServerId := []byte("serverid") 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] if vendorClassOption == nil { @@ -206,7 +223,10 @@ func TestMakeMsgReleaseReply(t *testing.T) { expectedServerId := []byte("serverid") 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 { t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type) diff --git a/dhcp6/random_address_pool.go b/dhcp6/random_address_pool.go index db0e0b2..cef9c59 100644 --- a/dhcp6/random_address_pool.go +++ b/dhcp6/random_address_pool.go @@ -46,14 +46,14 @@ type RandomAddressPool struct { identityAssociations map[uint64]*IdentityAssociation usedIps map[uint64]struct{} identityAssociationExpirations Fifo - preferredLifetime uint32 // in seconds + validLifetime uint32 // in seconds timeNow func() time.Time 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.preferredLifetime = preferredLifetime + to_ret.validLifetime = validLifetime to_ret.poolStartAddress = big.NewInt(0) to_ret.poolStartAddress.SetBytes(poolStartAddress) to_ret.poolEndAddress = big.NewInt(0) @@ -94,12 +94,10 @@ func (p *RandomAddressPool) ReserveAddress(clientId, interfaceId []byte) *Identi to_ret := &IdentityAssociation{clientId: clientId, interfaceId: interfaceId, ipAddress: newIp.Bytes(), - createdAt: timeNow, - t1: p.calculateT1(p.preferredLifetime), - t2: p.calculateT2(p.preferredLifetime) } + createdAt: timeNow } p.identityAssociations[clientIdHash] = to_ret 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 return to_ret } @@ -108,10 +106,15 @@ func (p *RandomAddressPool) ReserveAddress(clientId, interfaceId []byte) *Identi return nil } -func (p *RandomAddressPool) ReleaseAddress(clientId, interfaceId []byte, addr net.IP) { +func (p *RandomAddressPool) ReleaseAddress(clientId, interfaceId []byte) { <-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.usedIps, big.NewInt(0).SetBytes(addr).Uint64()) p.lock <- 1 } @@ -128,16 +131,8 @@ func (p *RandomAddressPool) ExpireIdentityAssociations() { p.lock <- 1 } -func (p *RandomAddressPool) calculateT1(preferredLifetime uint32) uint32 { - return preferredLifetime / 2 -} - -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) calculateAssociationExpiration(now time.Time) time.Time { + return now.Add(time.Duration(p.validLifetime)*time.Second) } func (p *RandomAddressPool) calculateIaIdHash(clientId, interfaceId []byte) uint64 { diff --git a/dhcp6/random_address_pool_test.go b/dhcp6/random_address_pool_test.go index 4b7f080..5584694 100644 --- a/dhcp6/random_address_pool_test.go +++ b/dhcp6/random_address_pool_test.go @@ -35,12 +35,6 @@ func TestReserveAddress(t *testing.T) { if ia.createdAt != expectedTime { 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) { @@ -93,9 +87,9 @@ func TestReserveAddressKeepsTrackOfAssociationExpiration(t *testing.T) { if expiration == 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", - 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 } a := pool.ReserveAddress(expectedClientId, expectedIaId) - pool.ReleaseAddress(expectedClientId, expectedIaId, a.ipAddress) + pool.ReleaseAddress(expectedClientId, expectedIaId) _, 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) diff --git a/pixiecore/dhcpv6.go b/pixiecore/dhcpv6.go index afcce3a..cf522fd 100644 --- a/pixiecore/dhcpv6.go +++ b/pixiecore/dhcpv6.go @@ -5,7 +5,7 @@ import ( "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") for { 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())) - response := pkt.BuildResponse(s.Duid, addressPool) + response := packetBuilder.BuildResponse(pkt) + marshalled_response, err := response.Marshal() if err != nil { s.log("dhcpv6", fmt.Sprintf("Error marshalling response: %s", response.Type, response.TransactionID, err)) diff --git a/pixiecore/pixicorev6.go b/pixiecore/pixicorev6.go index 2e77ea2..093744b 100644 --- a/pixiecore/pixicorev6.go +++ b/pixiecore/pixicorev6.go @@ -54,10 +54,14 @@ func (s *ServerV6) Serve() error { s.errs = make(chan error, 6) //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()) - 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(). err = <-s.errs