Skip to content

Commit

Permalink
Add PXE/TFTP IPv6 tests
Browse files Browse the repository at this point in the history
  • Loading branch information
damyan committed Apr 18, 2024
1 parent 5fa46dc commit cdf061f
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 2 deletions.
6 changes: 4 additions & 2 deletions plugins/pxeboot/pxeboot.go → plugins/pxeboot/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ package pxeboot

import (
"fmt"
"github.com/insomniacslk/dhcp/dhcpv4"
"net/url"

"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/iana"

"github.com/coredhcp/coredhcp/handler"
"github.com/coredhcp/coredhcp/logger"
"github.com/coredhcp/coredhcp/plugins"
Expand Down Expand Up @@ -153,7 +155,7 @@ func pxeBootHandler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
if decap.GetOneOption(dhcpv6.OptionClientArchType) != nil {
optBytes := decap.GetOneOption(dhcpv6.OptionClientArchType).ToBytes()
log.Debugf("ClientArchType: %s (%x)", string(optBytes), optBytes)
if len(optBytes) == 2 && optBytes[0] == 0 && optBytes[1] == 0x07 {
if len(optBytes) == 2 && optBytes[0] == 0 && optBytes[1] == byte(iana.EFI_X86_64) { // 0x07
opt = &tftpOption
}
}
Expand Down
238 changes: 238 additions & 0 deletions plugins/pxeboot/plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
// Copyright 2018-present the CoreDHCP Authors. All rights reserved
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

package pxeboot

import (
"testing"

"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/insomniacslk/dhcp/iana"
)

const (
ipxePath = "http://[2001:db8::1]/boot.ipxe"
tftpPath = "tftp://[2001:db8::1]/boot.efi"
)

var (
numberOptsBootFileURL int
)

func Init(numOptBoot int) {
numberOptsBootFileURL = numOptBoot

_, err := setup6(tftpPath, ipxePath)
if err != nil {
log.Fatal(err)
}
}

func TestPXERequested6(t *testing.T) {
Init(1)

req, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
req.MessageType = dhcpv6.MessageTypeRequest
req.AddOption(dhcpv6.OptRequestedOption(dhcpv6.OptionBootfileURL))
optUserClass := dhcpv6.OptUserClass{}
buf := []byte{
0, 4,
'i', 'P', 'X', 'E',
}
_ = optUserClass.FromBytes(buf)
req.UpdateOption(&optUserClass)

stub, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
stub.MessageType = dhcpv6.MessageTypeReply

resp, stop := pxeBootHandler6(req, stub)
if resp == nil {
t.Fatal("plugin did not return a message")
}

if !stop {
t.Error("plugin does not interrupt processing, but it should have")
}
opts := resp.GetOption(dhcpv6.OptionBootfileURL)
if len(opts) != numberOptsBootFileURL {
t.Fatalf("Expected %d BootFileUrl option, got %d: %v", numberOptsBootFileURL, len(opts), opts)
}

bootFileURL := resp.(*dhcpv6.Message).Options.BootFileURL()
if bootFileURL != ipxePath {
t.Errorf("Found BootFileURL %s, expected %s", bootFileURL, ipxePath)
}
}

func TestTFTPRequested6(t *testing.T) {
Init(1)

req, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
req.MessageType = dhcpv6.MessageTypeRequest
req.AddOption(dhcpv6.OptRequestedOption(dhcpv6.OptionBootfileURL))
optClientArchType := dhcpv6.OptClientArchType(iana.EFI_X86_64)
req.UpdateOption(optClientArchType)

stub, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
stub.MessageType = dhcpv6.MessageTypeReply

resp, stop := pxeBootHandler6(req, stub)
if resp == nil {
t.Fatal("plugin did not return a message")
}

if !stop {
t.Error("plugin does not interrupt processing, but it should have")
}
opts := resp.GetOption(dhcpv6.OptionBootfileURL)
if len(opts) != numberOptsBootFileURL {
t.Fatalf("Expected %d BootFileUrl option, got %d: %v", numberOptsBootFileURL, len(opts), opts)
}

bootFileURL := resp.(*dhcpv6.Message).Options.BootFileURL()
if bootFileURL != tftpPath {
t.Errorf("Found BootFileURL %s, expected %s", bootFileURL, tftpPath)
}
}

func TestWrongPXERequested6(t *testing.T) {
Init(0)

req, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
req.MessageType = dhcpv6.MessageTypeRequest
req.AddOption(dhcpv6.OptRequestedOption(dhcpv6.OptionBootfileURL))
optUserClass := dhcpv6.OptUserClass{}
buf := []byte{
0, 6,
'f', '0', '0', 'b', 'a', 'r', // nonsense
}
_ = optUserClass.FromBytes(buf)
req.UpdateOption(&optUserClass)

stub, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
stub.MessageType = dhcpv6.MessageTypeReply

resp, stop := pxeBootHandler6(req, stub)
if resp == nil {
t.Fatal("plugin did not return a message")
}

if !stop {
t.Error("plugin does not interrupt processing, but it should have")
}
opts := resp.GetOption(dhcpv6.OptionBootfileURL)
if len(opts) != numberOptsBootFileURL {
t.Fatalf("Expected %d BootFileUrl option, got %d: %v", numberOptsBootFileURL, len(opts), opts)
}
}

func TestWrongTFTPRequested6(t *testing.T) {
Init(0)

req, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
req.MessageType = dhcpv6.MessageTypeRequest
req.AddOption(dhcpv6.OptRequestedOption(dhcpv6.OptionBootfileURL))
optClientArchType := dhcpv6.OptClientArchType(iana.Arch(4711)) // nonsense
req.UpdateOption(optClientArchType)

stub, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
stub.MessageType = dhcpv6.MessageTypeReply

resp, stop := pxeBootHandler6(req, stub)
if resp == nil {
t.Fatal("plugin did not return a message")
}

if !stop {
t.Error("plugin does not interrupt processing, but it should have")
}
opts := resp.GetOption(dhcpv6.OptionBootfileURL)
if len(opts) != numberOptsBootFileURL {
t.Fatalf("Expected %d BootFileUrl option, got %d: %v", numberOptsBootFileURL, len(opts), opts)
}
}

func TestPXENotRequested6(t *testing.T) {
Init(0)

req, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
req.MessageType = dhcpv6.MessageTypeRequest
req.AddOption(dhcpv6.OptRequestedOption(dhcpv6.OptionBootfileURL))

stub, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
stub.MessageType = dhcpv6.MessageTypeReply

resp, stop := pxeBootHandler6(req, stub)
if resp == nil {
t.Fatal("plugin did not return a message")
}

if !stop {
t.Error("plugin does not interrupt processing, but it should have")
}
opts := resp.GetOption(dhcpv6.OptionBootfileURL)
if len(opts) != numberOptsBootFileURL {
t.Fatalf("Expected %d BootFileUrl option, got %d: %v", numberOptsBootFileURL, len(opts), opts)
}
}

func TestTFTPNotRequested6(t *testing.T) {
Init(0)

req, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
req.MessageType = dhcpv6.MessageTypeRequest
req.AddOption(dhcpv6.OptRequestedOption(dhcpv6.OptionBootfileURL))

stub, err := dhcpv6.NewMessage()
if err != nil {
t.Fatal(err)
}
stub.MessageType = dhcpv6.MessageTypeReply

resp, stop := pxeBootHandler6(req, stub)
if resp == nil {
t.Fatal("plugin did not return a message")
}

if !stop {
t.Error("plugin does not interrupt processing, but it should have")
}
opts := resp.GetOption(dhcpv6.OptionBootfileURL)
if len(opts) != numberOptsBootFileURL {
t.Fatalf("Expected %d BootFileUrl option, got %d: %v", numberOptsBootFileURL, len(opts), opts)
}
}

0 comments on commit cdf061f

Please sign in to comment.