From f89f6af9a630870d65ffc9bb620fa034c0c4f8a5 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Tue, 24 Oct 2017 16:18:43 -0700 Subject: [PATCH] added support for setting server preference option --- dhcp6/boot_configuration.go | 54 ++++++++++++++++++++++++++++++------ dhcp6/packet.go | 21 +++++++------- dhcp6/packet_test.go | 30 ++++++++++++++++++-- pixiecore/cli/bootipv6cmd.go | 7 ++++- pixiecore/cli/ipv6apicmd.go | 8 +++++- pixiecorev6/dhcpv6.go | 4 +-- pixiecorev6/pixicorev6.go | 10 +++---- 7 files changed, 103 insertions(+), 31 deletions(-) diff --git a/dhcp6/boot_configuration.go b/dhcp6/boot_configuration.go index 3cd2b8b..78abcca 100644 --- a/dhcp6/boot_configuration.go +++ b/dhcp6/boot_configuration.go @@ -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 +} diff --git a/dhcp6/packet.go b/dhcp6/packet.go index c9d9f72..0af6580 100644 --- a/dhcp6/packet.go +++ b/dhcp6/packet.go @@ -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)) diff --git a/dhcp6/packet_test.go b/dhcp6/packet_test.go index 065092b..47933b6 100644 --- a/dhcp6/packet_test.go +++ b/dhcp6/packet_test.go @@ -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) } } diff --git a/pixiecore/cli/bootipv6cmd.go b/pixiecore/cli/bootipv6cmd.go index adf4380..11a56fe 100644 --- a/pixiecore/cli/bootipv6cmd.go +++ b/pixiecore/cli/bootipv6cmd.go @@ -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() { diff --git a/pixiecore/cli/ipv6apicmd.go b/pixiecore/cli/ipv6apicmd.go index 58333b4..41d7753 100644 --- a/pixiecore/cli/ipv6apicmd.go +++ b/pixiecore/cli/ipv6apicmd.go @@ -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() { diff --git a/pixiecorev6/dhcpv6.go b/pixiecorev6/dhcpv6.go index 0dd8545..61b3319 100644 --- a/pixiecorev6/dhcpv6.go +++ b/pixiecorev6/dhcpv6.go @@ -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 } diff --git a/pixiecorev6/pixicorev6.go b/pixiecorev6/pixicorev6.go index e62e981..f1dce22 100644 --- a/pixiecorev6/pixicorev6.go +++ b/pixiecorev6/pixicorev6.go @@ -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) }()