Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support serving pxelinux artifacts #6

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions examples/boot.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ func main() {
DHCPNoBind: true,
}

/*
Itxaka marked this conversation as resolved.
Show resolved Hide resolved
ret.PxeLinuxAssets = map[string]string{
"default": "pxelinux.0",
}

*/

ret.SetDefaultFirmwares()
b, _ := booters.StaticBooter(booterSpec)
ret.Booter = b
Expand Down
18 changes: 18 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ type Server struct {
// Ipxe lists the supported bootable Firmwares, and their
// associated ipxe binary.
Ipxe map[constants.Firmware][]byte
// PxeLinuxAssets lists the supported map of PxeLinux assets.
// It links the mac/default to the pxe config file.
// Usually the PxeLinux references are referred by several names, like:
// The client UUID, if provided by the PXE stack.
//
// Note that some BIOSes do not have a valid UUID, and it might end up reporting something like all 1's.
// This value is represented in the standard UUID format using lowercase hexadecimal digits, e.g. "b8945908-d6a6-41a9-611d-74a6ab80b83d".
//
// The hardware type (using its ARP "htype" code) and address, all in lowercase hexadecimal with dash separators
// For example, for an Ethernet (i.e. ARP hardware type "1") with address "88:99:AA:BB:CC:DD",
// it would search for the filename "01-88-99-aa-bb-cc-dd".
//
// The client's own IPv4 address in uppercase hexadecimal, followed by removing hex characters, one at a time, from the end.
// For example, "192.168.2.91" → "C0A8025B". Then "C0A8025", "C0A802", "C0A80", "C0A8", "C0A", "C0", "C"
//
// Lowercase "default" string
// This is useful to provide configs for just one client or a group of clients. And have a generic fallback for all other clients.
PxeLinuxAssets map[string]string

// Log receives logs on Pixiecore's operation. If nil, logging
// is suppressed.
Expand Down
78 changes: 60 additions & 18 deletions server/tftp.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"strconv"
"strings"

Expand Down Expand Up @@ -63,29 +63,71 @@
}

func (s *Server) logTFTPTransfer(clientAddr net.Addr, path string, err error) {
mac, _, pathErr := extractInfo(path)
if pathErr != nil {
s.log("TFTP", "unable to extract mac from request:%v", pathErr)
return
}
if err != nil {
s.log("TFTP", "Send of %q to %s failed: %s", path, clientAddr, err)
if strings.HasPrefix(path, "pxelinux.cfg/") {
if err != nil {
// debug logging because pxelinux is very noisy as it does a lot of requests
s.debug("TFTP", "Send of %q to %s failed: %s", path, clientAddr, err)
} else {
_, i, _ := extractInfoPxeLinux(path)
s.log("TFTP", "Sent %q to %s", i, clientAddr)
//s.machineEvent(mac, machineStateTFTP, "Sent Pxelinux asset to %s", clientAddr)
}
} else {
s.log("TFTP", "Sent %q to %s", mac.String(), clientAddr)
s.machineEvent(mac, machineStateTFTP, "Sent iPXE to %s", clientAddr)
mac, _, pathErr := extractInfo(path)
if pathErr != nil {
s.log("TFTP", "unable to extract mac from request:%v", pathErr)
return
}
if err != nil {
s.log("TFTP", "Send of %q to %s failed: %s", path, clientAddr, err)
} else {
s.log("TFTP", "Sent %q to %s", mac.String(), clientAddr)
s.machineEvent(mac, machineStateTFTP, "Sent iPXE to %s", clientAddr)
}
}
}

func (s *Server) handleTFTP(path string, clientAddr net.Addr) (io.ReadCloser, int64, error) {
_, i, err := extractInfo(path)
if err != nil {
return nil, 0, fmt.Errorf("unknown path %q", path)
}
if strings.HasPrefix(path, "pxelinux.cfg/") {
_, i, err := extractInfoPxeLinux(path)
if err != nil {
return nil, 0, fmt.Errorf("unknown path %q", path)
Copy link
Preview

Copilot AI Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message "unknown path %q" is used for both extractInfoPxeLinux and extractInfo. This could be confusing. Differentiate the error messages for clarity.

Suggested change
return nil, 0, fmt.Errorf("unknown path %q", path)
return nil, 0, fmt.Errorf("unknown pxelinux path %q", path)

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
}
f, ok := s.PxeLinuxAssets[i]
if !ok {
return nil, 0, fmt.Errorf("PxeLinux asset not found with key %s", i)
}
bs, err := os.ReadFile(f)
Dismissed Show dismissed Hide dismissed
if err != nil {
s.log("TFTP", "Failed to read file %q: %s", f, err)
return nil, 0, err
}
return io.NopCloser(bytes.NewBuffer(bs)), int64(len(bs)), nil
} else {
_, i, err := extractInfo(path)
if err != nil {
return nil, 0, fmt.Errorf("unknown path %q", path)
}

bs, ok := s.Ipxe[constants.Firmware(i)]
if !ok {
return nil, 0, fmt.Errorf("unknown firmware type %d", i)
bs, ok := s.Ipxe[constants.Firmware(i)]
if !ok {
return nil, 0, fmt.Errorf("unknown firmware type %d", i)
}

return io.NopCloser(bytes.NewBuffer(bs)), int64(len(bs)), nil
}
}

return ioutil.NopCloser(bytes.NewBuffer(bs)), int64(len(bs)), nil
func extractInfoPxeLinux(path string) (net.HardwareAddr, string, error) {
var mac net.HardwareAddr
pathElements := strings.Split(path, "/")
if len(pathElements) != 2 {
return nil, "", errors.New("not found")
}
cleanedMac := strings.Replace(pathElements[1], "01-", "", 1)
mac, _ = net.ParseMAC(cleanedMac)
Copy link
Preview

Copilot AI Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error from net.ParseMAC is ignored. It should be handled properly to avoid potential issues with invalid MAC addresses.

Suggested change
mac, _ = net.ParseMAC(cleanedMac)
mac, err := net.ParseMAC(cleanedMac); if err != nil { return nil, "", err }

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, because we may not get a mac in here, and we want to try but dont care if we fail.

// We return:
// parsed Mac address if possible
// the filename of the pxelinux config requested
return mac, pathElements[1], nil
}