mirror of
https://github.com/danderson/netboot.git
synced 2025-10-16 10:01:20 +02:00
added support for setting server preference option
This commit is contained in:
parent
4d45c38c40
commit
f89f6af9a6
@ -11,15 +11,25 @@ import (
|
||||
|
||||
type BootConfiguration interface {
|
||||
GetBootUrl(id []byte, clientArchType uint16) ([]byte, error)
|
||||
GetPreference() []byte
|
||||
GetRecursiveDns() []byte
|
||||
}
|
||||
|
||||
type StaticBootConfiguration struct {
|
||||
HttpBootUrl []byte
|
||||
IPxeBootUrl []byte
|
||||
HttpBootUrl []byte
|
||||
IPxeBootUrl []byte
|
||||
RecursiveDns []byte
|
||||
Preference []byte
|
||||
UsePreference bool
|
||||
}
|
||||
|
||||
func MakeStaticBootConfiguration(httpBootUrl, ipxeBootUrl string) *StaticBootConfiguration {
|
||||
return &StaticBootConfiguration{HttpBootUrl: []byte(httpBootUrl), IPxeBootUrl: []byte(ipxeBootUrl)}
|
||||
func MakeStaticBootConfiguration(httpBootUrl, ipxeBootUrl string, preference uint8, usePreference bool) *StaticBootConfiguration {
|
||||
ret := &StaticBootConfiguration{HttpBootUrl: []byte(httpBootUrl), IPxeBootUrl: []byte(ipxeBootUrl), UsePreference: usePreference}
|
||||
if usePreference {
|
||||
ret.Preference = make([]byte, 1)
|
||||
ret.Preference[0] = byte(preference)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (bc *StaticBootConfiguration) GetBootUrl(id []byte, clientArchType uint16) ([]byte, error) {
|
||||
@ -29,19 +39,37 @@ func (bc *StaticBootConfiguration) GetBootUrl(id []byte, clientArchType uint16)
|
||||
return bc.IPxeBootUrl, nil
|
||||
}
|
||||
|
||||
type ApiBootConfiguration struct {
|
||||
client *http.Client
|
||||
urlPrefix string
|
||||
func (bc *StaticBootConfiguration) GetPreference() []byte {
|
||||
return bc.Preference
|
||||
}
|
||||
|
||||
func MakeApiBootConfiguration(url string, timeout time.Duration) *ApiBootConfiguration {
|
||||
func (bc *StaticBootConfiguration) GetRecursiveDns() []byte {
|
||||
return bc.RecursiveDns
|
||||
}
|
||||
|
||||
type ApiBootConfiguration struct {
|
||||
client *http.Client
|
||||
urlPrefix string
|
||||
RecursiveDns []byte
|
||||
Preference []byte
|
||||
UsePreference bool
|
||||
}
|
||||
|
||||
func MakeApiBootConfiguration(url string, timeout time.Duration, preference uint8, usePreference bool) *ApiBootConfiguration {
|
||||
if !strings.HasSuffix(url, "/") {
|
||||
url += "/"
|
||||
}
|
||||
return &ApiBootConfiguration{
|
||||
ret := &ApiBootConfiguration{
|
||||
client: &http.Client{Timeout: timeout},
|
||||
urlPrefix: url + "v1",
|
||||
UsePreference: usePreference,
|
||||
}
|
||||
if usePreference {
|
||||
ret.Preference = make([]byte, 1)
|
||||
ret.Preference[0] = byte(preference)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (bc *ApiBootConfiguration) GetBootUrl(id []byte, clientArchType uint16) ([]byte, error) {
|
||||
@ -77,3 +105,11 @@ func (bc *ApiBootConfiguration) makeURLAbsolute(urlStr string) (string, error) {
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (bc *ApiBootConfiguration) GetPreference() []byte {
|
||||
return bc.Preference
|
||||
}
|
||||
|
||||
func (bc *ApiBootConfiguration) GetRecursiveDns() []byte {
|
||||
return bc.RecursiveDns
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ type PacketBuilder struct {
|
||||
ServerDuid []byte
|
||||
PreferredLifetime uint32
|
||||
ValidLifetime uint32
|
||||
BootFileUrl BootConfiguration
|
||||
Configuration BootConfiguration
|
||||
Addresses AddressPool
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ func MakePacket(bs []byte, packetLength int) (*Packet, error) {
|
||||
func MakePacketBuilder(serverDuid []byte, preferredLifetime, validLifetime uint32, bootFileUrl BootConfiguration,
|
||||
addressPool AddressPool) *PacketBuilder {
|
||||
return &PacketBuilder{ServerDuid: serverDuid, PreferredLifetime: preferredLifetime, ValidLifetime: validLifetime,
|
||||
BootFileUrl: bootFileUrl, Addresses: addressPool}
|
||||
Configuration: bootFileUrl, Addresses: addressPool}
|
||||
}
|
||||
|
||||
func (p *Packet) Marshal() ([]byte, error) {
|
||||
@ -129,25 +129,26 @@ func ShouldDiscardInformationRequest(p *Packet, serverDuid []byte) error {
|
||||
}
|
||||
|
||||
func (b *PacketBuilder) BuildResponse(in *Packet) (*Packet, error) {
|
||||
|
||||
switch in.Type {
|
||||
case MsgSolicit:
|
||||
association := b.Addresses.ReserveAddress(in.Options.ClientId(), in.Options.IaNaId())
|
||||
bootFileUrl, err := b.BootFileUrl.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType())
|
||||
bootFileUrl, err := b.Configuration.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b.MakeMsgAdvertise(in.TransactionID, in.Options.ClientId(), in.Options.IaNaId(),
|
||||
in.Options.ClientArchType(), association.IpAddress, bootFileUrl), nil
|
||||
in.Options.ClientArchType(), association.IpAddress, bootFileUrl, b.Configuration.GetPreference()), nil
|
||||
case MsgRequest:
|
||||
association := b.Addresses.ReserveAddress(in.Options.ClientId(), in.Options.IaNaId())
|
||||
bootFileUrl, err := b.BootFileUrl.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType())
|
||||
bootFileUrl, err := b.Configuration.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b.MakeMsgReply(in.TransactionID, in.Options.ClientId(), in.Options.IaNaId(),
|
||||
in.Options.ClientArchType(), association.IpAddress, bootFileUrl), nil
|
||||
case MsgInformationRequest:
|
||||
bootFileUrl, err := b.BootFileUrl.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType())
|
||||
bootFileUrl, err := b.Configuration.GetBootUrl(b.ExtractLLAddressOrId(in.Options.ClientId()), in.Options.ClientArchType())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -161,8 +162,8 @@ func (b *PacketBuilder) BuildResponse(in *Packet) (*Packet, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId, iaId []byte, clientArchType uint16, ipAddress []byte,
|
||||
bootFileUrl []byte) *Packet {
|
||||
func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId, iaId []byte, clientArchType uint16, ipAddress,
|
||||
bootFileUrl, preference []byte) *Packet {
|
||||
ret_options := make(Options)
|
||||
ret_options.AddOption(MakeOption(OptClientId, clientId))
|
||||
ret_options.AddOption(MakeIaNaOption(iaId, b.calculateT1(), b.calculateT2(),
|
||||
@ -172,15 +173,15 @@ func (b *PacketBuilder) MakeMsgAdvertise(transactionId [3]byte, clientId, iaId [
|
||||
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://")
|
||||
//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,
|
||||
func (b *PacketBuilder) MakeMsgReply(transactionId [3]byte, clientId, iaId []byte, clientArchType uint16, ipAddress,
|
||||
bootFileUrl []byte) *Packet {
|
||||
ret_options := make(Options)
|
||||
ret_options.AddOption(MakeOption(OptClientId, clientId))
|
||||
|
@ -16,7 +16,8 @@ func TestMakeMsgAdvertise(t *testing.T) {
|
||||
builder := MakePacketBuilder(expectedServerId, 90, 100, nil,
|
||||
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, expectedBootFileUrl)
|
||||
msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x11, expectedIp,
|
||||
expectedBootFileUrl, nil)
|
||||
|
||||
if msg.Type != MsgAdvertise {
|
||||
t.Fatalf("Expected message type %d, got %d", MsgAdvertise, msg.Type)
|
||||
@ -59,6 +60,28 @@ func TestMakeMsgAdvertise(t *testing.T) {
|
||||
if iaNaOption == nil {
|
||||
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) {
|
||||
builder := MakePacketBuilder([]byte("serverid"), 90, 100, nil,
|
||||
NewRandomAddressPool(net.ParseIP("2001:db8:f00f:cafe::1"), net.ParseIP("2001:db8:f00f:cafe::1"), 100))
|
||||
|
||||
expectedPreference := []byte{128}
|
||||
msg := builder.MakeMsgAdvertise([3]byte{'t', 'i', 'd'}, []byte("clientid"), []byte("1234"), 0x11,
|
||||
net.ParseIP("2001:db8:f00f:cafe::1"), []byte("http://bootfileurl"), expectedPreference)
|
||||
|
||||
preferenceOption := msg.Options[OptPreference]
|
||||
if preferenceOption == nil {
|
||||
t.Fatalf("Preference option should be set")
|
||||
}
|
||||
if string(expectedPreference) != string(preferenceOption.Value) {
|
||||
t.Fatalf("Expected preference value %d, got %d", expectedPreference, preferenceOption.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeMsgAdvertiseWithHttpClientArch(t *testing.T) {
|
||||
@ -71,7 +94,8 @@ func TestMakeMsgAdvertiseWithHttpClientArch(t *testing.T) {
|
||||
builder := MakePacketBuilder(expectedServerId, 90, 100, nil,
|
||||
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, expectedBootFileUrl)
|
||||
msg := builder.MakeMsgAdvertise(transactionId, expectedClientId, []byte("1234"), 0x10, expectedIp,
|
||||
expectedBootFileUrl, nil)
|
||||
|
||||
vendorClassOption := msg.Options[OptVendorClass]
|
||||
if vendorClassOption == nil {
|
||||
@ -82,7 +106,7 @@ func TestMakeMsgAdvertiseWithHttpClientArch(t *testing.T) {
|
||||
t.Fatalf("Bootfile URL option should be present")
|
||||
}
|
||||
if string(expectedBootFileUrl) != string(bootfileUrlOption.Value) {
|
||||
t.Fatalf("Expected bootfile URL %v, got %v", expectedBootFileUrl, bootfileUrlOption)
|
||||
t.Fatalf("Expected bootfile URL %s, got %s", expectedBootFileUrl, bootfileUrlOption)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,11 @@ var bootIPv6Cmd = &cobra.Command{
|
||||
}
|
||||
|
||||
s.Address = addr
|
||||
s.BootUrls = dhcp6.MakeStaticBootConfiguration(httpBootUrl, ipxeUrl)
|
||||
preference, err := cmd.Flags().GetUint8("preference")
|
||||
if err != nil {
|
||||
fatalf("Error reading flag: %s", err)
|
||||
}
|
||||
s.BootConfig = dhcp6.MakeStaticBootConfiguration(httpBootUrl, ipxeUrl, preference, cmd.Flags().Changed("preference"))
|
||||
|
||||
fmt.Println(s.Serve())
|
||||
},
|
||||
@ -56,6 +60,7 @@ func serverv6ConfigFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().StringP("ipxe-url", "", "", "IPXE config file url, e.g. http://[2001:db8:f00f:cafe::4]/script.ipxe")
|
||||
cmd.Flags().StringP("httpboot-url", "", "", "HTTPBoot url, e.g. http://[2001:db8:f00f:cafe::4]/bootx64.efi")
|
||||
cmd.Flags().Bool("debug", false, "Enable debug-level logging")
|
||||
cmd.Flags().Uint8("preference", 255, "Set dhcp server preference value")
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -42,7 +42,12 @@ var ipv6ApiCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
s.Address = addr
|
||||
s.BootUrls = dhcp6.MakeApiBootConfiguration(apiUrl, apiTimeout)
|
||||
preference, err := cmd.Flags().GetUint8("preference")
|
||||
if err != nil {
|
||||
fatalf("Error reading flag: %s", err)
|
||||
}
|
||||
|
||||
s.BootConfig = dhcp6.MakeApiBootConfiguration(apiUrl, apiTimeout, preference, cmd.Flags().Changed("preference"))
|
||||
|
||||
fmt.Println(s.Serve())
|
||||
},
|
||||
@ -53,6 +58,7 @@ func serverv6ApiConfigFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().StringP("api-request-url", "", "", "Ipv6-specific API server url")
|
||||
cmd.Flags().Duration("api-request-timeout", 5*time.Second, "Timeout for request to the API server")
|
||||
cmd.Flags().Bool("debug", false, "Enable debug-level logging")
|
||||
cmd.Flags().Uint8("preference", 255, "Set dhcp server preference value")
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -21,11 +21,11 @@ func (s *ServerV6) serveDHCP(conn *dhcp6.Conn, packetBuilder *dhcp6.PacketBuilde
|
||||
|
||||
response, err := packetBuilder.BuildResponse(pkt)
|
||||
if err != nil {
|
||||
s.log("dhcpv6", fmt.Sprintf("Error creating response for transaction: %s: %s", pkt.TransactionID, err))
|
||||
s.log("dhcpv6", fmt.Sprintf("Error creating response for transaction: %d: %s", pkt.TransactionID, err))
|
||||
continue
|
||||
}
|
||||
if response == nil {
|
||||
s.log("dhcpv6", fmt.Sprintf("Don't know how to respond to packet type: %d (transaction id %s)", pkt.Type, pkt.TransactionID))
|
||||
s.log("dhcpv6", fmt.Sprintf("Don't know how to respond to packet type: %d (transaction id %d)", pkt.Type, pkt.TransactionID))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,10 @@ import (
|
||||
)
|
||||
|
||||
type ServerV6 struct {
|
||||
Address string
|
||||
Port string
|
||||
Duid []byte
|
||||
BootUrls dhcp6.BootConfiguration
|
||||
Address string
|
||||
Port string
|
||||
Duid []byte
|
||||
BootConfig dhcp6.BootConfiguration
|
||||
|
||||
errs chan error
|
||||
|
||||
@ -47,7 +47,7 @@ func (s *ServerV6) Serve() error {
|
||||
s.SetDUID(dhcp.SourceHardwareAddress())
|
||||
|
||||
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, s.BootUrls, addressPool)
|
||||
packetBuilder := dhcp6.MakePacketBuilder(s.Duid, 1800, 1850, s.BootConfig, addressPool)
|
||||
|
||||
go func() { s.errs <- s.serveDHCP(dhcp, packetBuilder) }()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user