Skip to content

Commit

Permalink
Add httpboot tests (#162)
Browse files Browse the repository at this point in the history
* Add generic IPv6 tests

* Add generic IPv4 tests
  • Loading branch information
damyan authored Jun 25, 2024
1 parent e611122 commit 0369022
Show file tree
Hide file tree
Showing 2 changed files with 426 additions and 31 deletions.
87 changes: 56 additions & 31 deletions plugins/httpboot/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package httpboot

import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
Expand All @@ -30,6 +31,8 @@ var Plugin = plugins.Plugin{
Setup4: setup4,
}

const httpClient = "HTTPClient"

func parseArgs(args ...string) (*url.URL, bool, error) {
if len(args) != 1 {
return nil, false, fmt.Errorf("exactly one argument must be passed to the httpboot plugin, got %d", len(args))
Expand All @@ -43,31 +46,35 @@ func parseArgs(args ...string) (*url.URL, bool, error) {
if err != nil {
return nil, false, fmt.Errorf("invalid URL: %v", err)
}
if (parsedURL.Scheme != "http" && parsedURL.Scheme != "https") || parsedURL.Host == "" || parsedURL.Path == "" {
return nil, false, fmt.Errorf("malformed iPXE parameter, should be a valid URL")
}
return parsedURL, useBootService, nil
}

func setup6(args ...string) (handler.Handler6, error) {
u, usebootservice, err := parseArgs(args...)
u, ubs, err := parseArgs(args...)
if err != nil {
return nil, fmt.Errorf("invalid configuration: %v", err)
}
bootFile6 = u.String()
useBootService = usebootservice
useBootService = ubs
log.Printf("Configured httpboot plugin with URL: %s, useBootService: %t", bootFile6, useBootService)
return Handler6, nil
return handler6, nil
}

func setup4(args ...string) (handler.Handler4, error) {
u, _, err := parseArgs(args...)
u, ubs, err := parseArgs(args...)
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid configuration: %v", err)
}
bootFile4 = u.String()
log.Printf("loaded httpboot plugin for DHCPv4.")
return Handler4, nil
useBootService = ubs
log.Printf("Configured httpboot plugin with URL: %s, useBootService: %t", bootFile4, useBootService)
return handler4, nil
}

func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
func handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
log.Debugf("Received DHCPv6 request: %s", req.Summary())

var ukiURL string
Expand All @@ -93,51 +100,69 @@ func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
}

if decap.GetOneOption(dhcpv6.OptionVendorClass) != nil {
vc := decap.GetOneOption(dhcpv6.OptionVendorClass).String()
if strings.Contains(vc, "HTTPClient") {
bf := &dhcpv6.OptionGeneric{
OptionCode: dhcpv6.OptionBootfileURL,
OptionData: []byte(ukiURL),
}
optVendorClass := decap.GetOneOption(dhcpv6.OptionVendorClass)
log.Debugf("VendorClass: %s", optVendorClass.String())
vcc := optVendorClass.ToBytes()
if len(vcc) >= 16 && binary.BigEndian.Uint16(vcc[4:6]) >= 10 && string(vcc[6:16]) == httpClient {
bf := dhcpv6.OptBootFileURL(ukiURL)
resp.AddOption(bf)
vid := &dhcpv6.OptionGeneric{
OptionCode: dhcpv6.OptionVendorClass,
// 0000 (4 bytes) Enterprise Identifier
// 0a (2 bytes) length of "HTTPClient"
// - rest with HTTPClient
OptionData: []byte("00000aHTTPClient"),
log.Infof("Added option BootFileURL(%d): (%s)", dhcpv6.OptionBootfileURL, ukiURL)

buf := []byte(httpClient)
vc := &dhcpv6.OptVendorClass{
EnterpriseNumber: 0,
Data: [][]byte{buf},
}
resp.AddOption(vid)
resp.AddOption(vc)
log.Infof("Added option VendorClass %s", vc.String())
} else {
log.Errorf("non HTTPClient VendorClass %s", optVendorClass.String())
return resp, false
}
}

log.Debugf("Sent DHCPv6 response: %s", resp.Summary())
return resp, false
}

func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
func handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
log.Debugf("Received DHCPv4 request: %s", req.Summary())

ukiURL, err := fetchUKIURL(bootFile4, []string{req.ClientIPAddr.String()})
if err != nil {
log.Errorf("failed to fetch UKI URL: %v", err)
return resp, false
var ukiURL string
var err error
if !useBootService {
ukiURL = bootFile4
} else {
ukiURL, err = fetchUKIURL(bootFile4, []string{req.ClientIPAddr.String()})
if err != nil {
log.Errorf("failed to fetch UKI URL: %v", err)
return resp, false
}
}

if req.GetOneOption(dhcpv4.OptionClassIdentifier) != nil {
vc := req.GetOneOption(dhcpv4.OptionClassIdentifier)
if strings.Contains(string(vc), "HTTPClient") {
cic := req.GetOneOption(dhcpv4.OptionClassIdentifier)
log.Debugf("ClassIdentifier: %s (%s)", string(cic), cic)
if len(cic) >= 10 && string(cic[0:10]) == httpClient {
bf := &dhcpv4.Option{
Code: dhcpv4.OptionBootfileName,
Value: dhcpv4.String(ukiURL),
}
resp.Options.Update(*bf)
vid := &dhcpv4.Option{
log.Infof("Added option BooFileName %s", bf.String())

ci := &dhcpv4.Option{
Code: dhcpv4.OptionClassIdentifier,
Value: dhcpv4.String("HTTPClient"),
Value: dhcpv4.String(httpClient),
}
resp.Options.Update(*vid)
resp.Options.Update(*ci)
log.Infof("Added option ClassIdentifier %s", ci.String())
} else {
log.Errorf("non HTTPClient ClassIdentifier %s", string(cic))
return resp, false
}
}
log.Debugf("Sent DHCPv4 response: %s", resp.Summary())
return resp, false
}

Expand Down
Loading

0 comments on commit 0369022

Please sign in to comment.