Skip to content
Closed
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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.git
*.md
LICENSE
ipxe/ipxe/bin
5 changes: 3 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ jobs:
go-version: "1.23"
cache: false

- name: make ipxe
- name: fake ipxe/bin for linter
run: |
make ipxe
mkdir -p ipxe/ipxe/bin
touch ipxe/ipxe/bin/empty

- name: Lint
uses: golangci/golangci-lint-action@v6
Expand Down
1 change: 0 additions & 1 deletion ipxe/ipxe.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
)

// Files to embed get created during make ipxe
// nolint:typecheck
//
//go:embed ipxe/bin
var payload embed.FS
Expand Down
23 changes: 22 additions & 1 deletion pixiecore/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ package cli // import "github.com/metal-stack/pixie/cli"
import (
"fmt"
"log/slog"
"net"
"os"

"github.com/metal-stack/pixie/pixiecore"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/metal-stack/pixie/pixiecore"
)

// Ipxe is the set of ipxe binaries for supported firmware.
Expand Down Expand Up @@ -63,6 +65,7 @@ func fatalf(msg string, args ...any) {
func serverConfigFlags(cmd *cobra.Command) {
cmd.Flags().BoolP("debug", "d", false, "Log more things that aren't directly related to booting a recognized client")
cmd.Flags().StringP("listen-addr", "l", "0.0.0.0", "IPv4 address to listen on")
cmd.Flags().String("server-ip", "", "Source IP of the DHCP responses")
cmd.Flags().IntP("port", "p", 80, "Port to listen on for HTTP")
cmd.Flags().String("metrics-listen-addr", "0.0.0.0", "IPv4 address of the metrics server to listen on")
cmd.Flags().Int("metrics-port", 2113, "Metrics server port")
Expand Down Expand Up @@ -92,6 +95,10 @@ func serverFromFlags(cmd *cobra.Command) *pixiecore.Server {
if err != nil {
fatalf("Error reading flag: %s", err)
}
serverIP, err := cmd.Flags().GetString("server-ip")
if err != nil {
fatalf("Error reading flag: %s", err)
}
httpPort, err := cmd.Flags().GetInt("port")
if err != nil {
fatalf("Error reading flag: %s", err)
Expand Down Expand Up @@ -142,6 +149,20 @@ func serverFromFlags(cmd *cobra.Command) *pixiecore.Server {
MetricsAddress: metricsAddr,
DHCPNoBind: dhcpNoBind,
}
if serverIP != "" {
ip := net.ParseIP(serverIP)
if ip == nil {
fatalf("Error: serverIP %q is not a valid IP address", serverIP)
}

// Check specifically for IPv4 address
ipv4 := ip.To4()
if ipv4 == nil {
fatalf("Error: serverIP %q is not a valid IPv4 address", serverIP)
}

ret.ServerIP = &ipv4
}
for fwtype, bs := range Ipxe {
ret.Ipxe[fwtype] = bs
}
Expand Down
29 changes: 17 additions & 12 deletions pixiecore/dhcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/metal-stack/pixie/dhcp4"
)

func (s *Server) serveDHCP(conn *dhcp4.Conn) error {
func (s *Server) serveDHCP(conn *dhcp4.Conn, fixedServerIP *net.IP) error {
for {
pkt, intf, err := conn.RecvDHCP()
if err != nil {
Expand All @@ -33,24 +33,24 @@ func (s *Server) serveDHCP(conn *dhcp4.Conn) error {
}

if err = s.isBootDHCP(pkt); err != nil {
s.Log.Debug("Ignoring packet", "mac", pkt.HardwareAddr, "error", err)
s.Log.Debug("Ignoring packet", "mac", pkt.HardwareAddr.String(), "error", err)
continue
}
mach, fwtype, err := s.validateDHCP(pkt)
if err != nil {
s.Log.Info("Unusable packet", "mac", pkt.HardwareAddr, "error", err)
s.Log.Info("Unusable packet", "mac", pkt.HardwareAddr.String(), "error", err)
continue
}

s.Log.Debug("Got valid request to boot", "mac", mach.MAC, "guid", mach.GUID, "arch", mach.Arch)
s.Log.Debug("Got valid request to boot", "mac", mach.MAC.String(), "guid", mach.GUID, "arch", mach.Arch)

spec, err := s.Booter.BootSpec(mach)
if err != nil {
s.Log.Info("Couldn't get bootspec", "mac", pkt.HardwareAddr, "error", err)
s.Log.Info("Couldn't get bootspec", "mac", pkt.HardwareAddr.String(), "error", err)
continue
}
if spec == nil {
s.Log.Debug("No boot spec, ignoring boot request", "mac", pkt.HardwareAddr)
s.Log.Debug("No boot spec, ignoring boot request", "mac", pkt.HardwareAddr.String())
s.machineEvent(pkt.HardwareAddr, machineStateIgnored, "Machine should not netboot")
continue
}
Expand All @@ -63,20 +63,25 @@ func (s *Server) serveDHCP(conn *dhcp4.Conn) error {
}

// Machine should be booted.
serverIP, err := interfaceIP(intf)
if err != nil {
s.Log.Info("Want to boot, but couldn't get a source address", "mac", pkt.HardwareAddr, "interface", intf.Name, "error", err)
continue
var serverIP net.IP
if fixedServerIP != nil {
serverIP = *fixedServerIP
} else {
serverIP, err = interfaceIP(intf)
if err != nil {
s.Log.Info("Want to boot, but couldn't get a source address", "mac", pkt.HardwareAddr.String(), "interface", intf.Name, "error", err)
continue
}
}

resp, err := s.offerDHCP(pkt, mach, serverIP, fwtype)
if err != nil {
s.Log.Info("Failed to construct ProxyDHCP offer", "mac", pkt.HardwareAddr, "error", err)
s.Log.Info("Failed to construct ProxyDHCP offer", "mac", pkt.HardwareAddr.String(), "error", err)
continue
}

if err = conn.SendDHCP(resp, intf); err != nil {
s.Log.Info("Failed to send ProxyDHCP offer", "mac", pkt.HardwareAddr, "error", err)
s.Log.Info("Failed to send ProxyDHCP offer", "mac", pkt.HardwareAddr.String(), "error", err)
continue
}
}
Expand Down
10 changes: 7 additions & 3 deletions pixiecore/pixiecore.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ import (
"sync"
"text/template"

"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/metal-stack/pixie/api"
"github.com/metal-stack/pixie/dhcp4"
"github.com/metal-stack/v"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
Expand Down Expand Up @@ -174,6 +175,9 @@ type Server struct {
// HTTPPort.
HTTPStatusPort int

// Source IP of DHCP responses.
ServerIP *net.IP

// MetricsPort is the port of the metrics server.
MetricsPort int
// MetricsAddress is the bind address that the metrics server listens on.
Expand Down Expand Up @@ -264,8 +268,8 @@ func (s *Server) Serve() error {

s.Log.Debug("Starting Pixiecore goroutines", "version", v.V.String())

go func() { s.errs <- s.serveDHCP(dhcp) }()
go func() { s.errs <- s.servePXE(pxe) }()
go func() { s.errs <- s.serveDHCP(dhcp, s.ServerIP) }()
go func() { s.errs <- s.servePXE(pxe, s.ServerIP) }()
go func() { s.errs <- s.serveTFTP(tftpAddr) }()
go func() { s.errs <- serveHTTP(http, s.serveHTTP) }()
go func() { s.errs <- serveHTTP(metrics, s.serveMetrics) }()
Expand Down
24 changes: 15 additions & 9 deletions pixiecore/pxe.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import (
"fmt"
"net"

"github.com/metal-stack/pixie/dhcp4"
"golang.org/x/net/ipv4"

"github.com/metal-stack/pixie/dhcp4"
)

// TODO: this may actually be the BINL protocol, a
Expand All @@ -29,7 +30,7 @@ import (
// TianoCore EDK2 source code to figure out if what this is doing is
// actually BINL, and if so rename everything.

func (s *Server) servePXE(conn net.PacketConn) error {
func (s *Server) servePXE(conn net.PacketConn, fixedServerIP *net.IP) error {
buf := make([]byte, 1024)
l := ipv4.NewPacketConn(conn)
if err := l.SetControlMessage(ipv4.FlagInterface, true); err != nil {
Expand All @@ -49,11 +50,11 @@ func (s *Server) servePXE(conn net.PacketConn) error {
}

if err = s.isBootDHCP(pkt); err != nil {
s.Log.Debug("Ignoring packet", "mac", pkt.HardwareAddr, "addr", addr, "error", err)
s.Log.Debug("Ignoring packet", "mac", pkt.HardwareAddr.String(), "addr", addr, "error", err)
}
fwtype, err := s.validatePXE(pkt)
if err != nil {
s.Log.Info("Unusable packet", "mac", pkt.HardwareAddr, "addr", addr, "error", err)
s.Log.Info("Unusable packet", "mac", pkt.HardwareAddr.String(), "addr", addr, "error", err)
continue
}

Expand All @@ -63,10 +64,15 @@ func (s *Server) servePXE(conn net.PacketConn) error {
continue
}

serverIP, err := interfaceIP(intf)
if err != nil {
s.Log.Info("Want to boot, but couldn't get a source address", "mac", pkt.HardwareAddr, "addr", addr, "interface", intf.Name, "error", err)
continue
var serverIP net.IP
if fixedServerIP != nil {
serverIP = *fixedServerIP
} else {
serverIP, err = interfaceIP(intf)
if err != nil {
s.Log.Info("Want to boot, but couldn't get a source address", "mac", pkt.HardwareAddr.String(), "addr", addr, "interface", intf.Name, "error", err)
continue
}
}

s.machineEvent(pkt.HardwareAddr, machineStatePXE, "Sent PXE configuration")
Expand All @@ -75,7 +81,7 @@ func (s *Server) servePXE(conn net.PacketConn) error {

bs, err := resp.Marshal()
if err != nil {
s.Log.Info("Failed to marshal PXE offer", "mac", pkt.HardwareAddr, "addr", addr, "error", err)
s.Log.Info("Failed to marshal PXE offer", "mac", pkt.HardwareAddr.String(), "addr", addr, "error", err)
continue
}

Expand Down