lxz 2 天之前
父節點
當前提交
0c3f9d0deb

+ 83 - 0
DHCP/api/api.go

@@ -0,0 +1,83 @@
+package api
+
+import (
+	"dhcp/global"
+	"dhcp/ip"
+	"dhcp/request"
+	"dhcp/result"
+	"dhcp/service"
+	"github.com/gin-gonic/gin"
+)
+
+//
+//func Redirect(c *gin.Context) {
+//	c.Redirect(http.StatusMovedPermanently, "")
+//}
+
+func DHCPServerRegisterRoute(r *gin.Engine) {
+	dhcpserver := r.Group("/dhcpserver")
+	dhcpserver.POST("/start", DHCPServerStart)
+	dhcpserver.POST("/stop", DHCPServerStop)
+	dhcpserver.GET("/info", DHCPServerInfo)
+	dhcpserver.GET("/localip", GetLocalIPAndInterfaces)
+	dhcpserver.POST("/delete", DHCPServerDelete)
+}
+
+func DHCPServerStart(c *gin.Context) {
+	var req request.DHCPService
+	if err := c.ShouldBindJSON(&req); err != nil {
+		c.JSON(200, result.Result{
+			Code: 400,
+			Data: nil,
+			Msg:  "参数错误",
+		})
+		return
+	}
+	re := service.StartDHCPService(c.Request.Context(), global.Log, req)
+	c.JSON(200, re)
+}
+
+func DHCPServerDelete(c *gin.Context) {
+	var req request.DHCPService
+	if err := c.ShouldBindJSON(&req); err != nil {
+		c.JSON(200, result.Result{
+			Code: 400,
+			Data: nil,
+			Msg:  "参数错误",
+		})
+		return
+	}
+	re := service.DeleteDHCPService(c.Request.Context(), global.Log, req)
+	c.JSON(200, re)
+}
+
+func DHCPServerStop(c *gin.Context) {
+	var req request.DHCPService
+	if err := c.ShouldBindJSON(&req); err != nil {
+		c.JSON(200, result.Result{
+			Code: 400,
+			Data: nil,
+			Msg:  "参数错误",
+		})
+		return
+	}
+	re := service.StopDHCPService(c.Request.Context(), global.Log, req)
+	c.JSON(200, re)
+}
+
+func DHCPServerInfo(c *gin.Context) {
+	var d []request.DHCPService
+	for _, v := range service.DHCPServiceInfoMap {
+		d = append(d, v)
+	}
+	c.JSON(200, result.Result{
+		Code: 200,
+		Data: d,
+		Msg:  "获取当前服务成功",
+	})
+}
+
+func GetLocalIPAndInterfaces(c *gin.Context) {
+	re := ip.GetLocalIPAndInterface()
+	c.JSON(200, re)
+}

+ 106 - 0
DHCP/backend/backend.go

@@ -0,0 +1,106 @@
+package backend
+
+import (
+	"context"
+	"dhcp/data"
+	"dhcp/handler"
+	"errors"
+	"net"
+	"net/netip"
+)
+
+var errParseSubnet = errors.New("failed to parse subnet mask")
+
+type Backend struct{}
+
+func NewBackend() handler.BackendReader {
+	return &Backend{}
+}
+
+func (b *Backend) GetByMac(ctx context.Context, addr net.HardwareAddr, s string) (*data.DHCP, error) {
+	dp, err := GetDhcp(addr.String())
+	if err != nil {
+		return nil, err
+	}
+	dhcp, err := translate(dp)
+	if err != nil {
+		return nil, err
+	}
+	return dhcp, nil
+}
+
+func (b *Backend) GetByIP(ctx context.Context, ip net.IP, s string) (*data.DHCP, error) {
+	return nil, nil
+}
+
+func translate(dp Dhcp) (*data.DHCP, error) {
+	var d data.DHCP
+	if dp.MACAddress != "" {
+		d.MACAddress = net.HardwareAddr(dp.MACAddress)
+	}
+	if dp.IPAddress != "" {
+		ip, err := netip.ParseAddr(dp.IPAddress)
+		if err != nil {
+			return nil, err
+		}
+		d.IPAddress = ip
+	}
+	if len(dp.DomainSearch) > 0 {
+		d.DomainSearch = dp.DomainSearch
+	}
+	if dp.SubnetMask != "" {
+		sm := net.ParseIP(dp.SubnetMask)
+		if sm == nil {
+			return nil, errParseSubnet
+		}
+		d.SubnetMask = net.IPMask(sm.To4())
+	}
+	if dp.DefaultGateway != "" {
+		dg, err := netip.ParseAddr(dp.DefaultGateway)
+		if err != nil {
+			return nil, err
+		}
+		d.DefaultGateway = dg
+	}
+	if len(dp.NameServers) > 0 {
+		for _, s := range dp.NameServers {
+			ip := net.ParseIP(string(s))
+			if ip == nil {
+				break
+			}
+			d.NameServers = append(d.NameServers, ip)
+		}
+	}
+	if dp.DomainName != "" {
+		d.DomainName = dp.DomainName
+	}
+	if dp.BroadcastAddress != "" {
+		ba, err := netip.ParseAddr(dp.BroadcastAddress)
+		if err != nil {
+			return nil, err
+		}
+		d.BroadcastAddress = ba
+	}
+
+	if len(dp.NTPServers) > 0 {
+		for _, s := range dp.NTPServers {
+			ip := net.ParseIP(string(s))
+			if ip == nil {
+				break
+			}
+			d.NTPServers = append(d.NTPServers, ip)
+		}
+	}
+	if dp.VLANID != "" {
+		d.VLANID = dp.VLANID
+	}
+	d.LeaseTime = uint32(dp.LeaseTime)
+	if dp.DomainSearch != nil {
+		d.DomainSearch = dp.DomainSearch
+	}
+
+	if dp.ServerIP != "" {
+		d.ServerIP = net.ParseIP(dp.ServerIP)
+	}
+	return &d, nil
+}

+ 84 - 0
DHCP/backend/dhcp.go

@@ -0,0 +1,84 @@
+package backend
+
+import (
+	"github.com/glebarez/sqlite"
+	"gorm.io/gorm"
+	"os"
+	"path/filepath"
+)
+
+type Dhcp struct {
+	Id               int      `json:"id" gorm:"id"`                               // ID
+	MACAddress       string   `json:"mac_address" gorm:"mac_address"`             // MAC地址
+	IPAddress        string   `json:"ip_address" gorm:"ip_address"`               // IP地址
+	SubnetMask       string   `json:"subnet_mask" gorm:"subnet_mask"`             // 子网掩码
+	DefaultGateway   string   `json:"default_gateway" gorm:"default_gateway"`     // 默认网关
+	NameServers      []string `json:"name_servers" gorm:"name_servers"`           // DNS服务器
+	DomainName       string   `json:"domain_name" gorm:"domain_name"`             // 域名
+	BroadcastAddress string   `json:"broadcast_address" gorm:"broadcast_address"` // 广播地址
+	NTPServers       []string `json:"ntp_servers" gorm:"ntp_servers"`             // NTP服务器
+	VLANID           string   `json:"vlan_id" gorm:"vlan_id"`                     // VLAN ID
+	LeaseTime        uint32   `json:"lease_time" gorm:"lease_time"`               // 租约
+	DomainSearch     []string `json:"domain_search" gorm:"domain_search"`         // 域名搜索
+	ServerIP         string   `json:"server_ip" gorm:"server_ip"`                 // 服务器IP
+}
+
+// 定义数据库指针
+var db *gorm.DB
+
+// 初始化数据库指针
+func InitDB() {
+	executablePath, err := os.Executable()
+	if err != nil {
+		panic(err)
+	}
+	dbPath := filepath.Join(filepath.Dir(executablePath), "dhcp.db")
+	db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
+	if err != nil {
+		panic(err)
+	}
+	db.AutoMigrate(&Dhcp{})
+}
+
+func AddDhcp(dhcp Dhcp) error {
+	var dp Dhcp
+	tx := db.Model(&Dhcp{}).Where("mac_address = ?", dhcp.MACAddress).First(dp)
+	if tx.Error != nil {
+		return tx.Error
+	}
+	if dp.Id == 0 {
+		tx = db.Create(&dhcp)
+	} else {
+		tx = db.Model(&Dhcp{}).Where("mac_address = ?", dhcp.MACAddress).Updates(dhcp)
+	}
+
+	if tx.Error != nil {
+		return tx.Error
+	}
+	return nil
+}
+
+func GetDhcp(macAddress string) (Dhcp, error) {
+	var dhcp Dhcp
+	tx := db.Model(&Dhcp{}).Where("mac_address = ?", macAddress).First(&dhcp)
+	if tx.Error != nil {
+		return dhcp, tx.Error
+	}
+	return dhcp, nil
+}
+
+func DeleteDhcp(macAddress string) error {
+	tx := db.Model(&Dhcp{}).Where("mac_address = ?", macAddress).Delete(&Dhcp{})
+	if tx.Error != nil {
+		return tx.Error
+	}
+	return nil
+}
+
+func UpdateDhcp(dhcp Dhcp) error {
+	tx := db.Model(&Dhcp{}).Where("mac_address = ?", dhcp.MACAddress).Updates(dhcp)
+	if tx.Error != nil {
+		return tx.Error
+	}
+	return nil
+}

+ 24 - 0
DHCP/code/common.go

@@ -0,0 +1,24 @@
+package code
+
+const (
+	SUCCESS         = 200  // 操作成功
+	FAILED          = 400  // 操作失败
+	STADY_TUNED     = 999  // 敬请期待
+	SystemException = 1000 // 系统异常
+
+)
+
+// 数据库相关异常信息
+const (
+	DATA_PARSE_EXCEPTION           = 10001 // 数据解析异常
+	WRITE_TO_DATABASE_EXCEPTION    = 10002 // 写入数据库异常
+	UPDATE_TO_DATABASE_EXCEPTION   = 10003 // 更新数据库异常
+	READ_FROM_DATABASE_EXCEPTION   = 10004 // 读取数据库异常
+	DELETE_FROM_DATABASE_EXCEPTION = 10005 // 删除数据库数据异常
+	CONN_DATABASE_EXCEPTION        = 10006 // 连接数据库异常
+)
+
+const (
+	FILE_UPLOAD_EXCEPTION = 20001 // 文件上传异常
+	INVALID_PARAMS        = 1001  // 参数错误
+)

+ 43 - 0
DHCP/data/data.go

@@ -0,0 +1,43 @@
+// Package data is an interface between DHCP macAndIPXE implementations and the DHCP server.
+package data
+
+import (
+	"github.com/insomniacslk/dhcp/dhcpv4"
+	"net"
+	"net/netip"
+)
+
+// Packet holds the data that is passed to a DHCP handler.
+type Packet struct {
+	// Peer is the address of the client that sent the DHCP message.
+	Peer net.Addr
+	// Pkt is the DHCP message.
+	Pkt *dhcpv4.DHCPv4
+	// Md is the metadata that was passed to the DHCP server.
+	Md *Metadata
+}
+
+// Metadata holds metadata about the DHCP packet that was received.
+type Metadata struct {
+	// IfName is the name of the interface that the DHCP message was received on.
+	IfName string
+	// IfIndex is the index of the interface that the DHCP message was received on.
+	IfIndex int
+}
+
+// DHCP holds the DHCP headers and options to be set in a DHCP handler response.
+// This is the API between a DHCP handler and a macAndIPXE.
+type DHCP struct {
+	MACAddress       net.HardwareAddr // chaddr DHCP header.
+	IPAddress        netip.Addr       // yiaddr DHCP header.
+	SubnetMask       net.IPMask       // DHCP option 1.
+	DefaultGateway   netip.Addr       // DHCP option 3.
+	NameServers      []net.IP         // DHCP option 6.
+	DomainName       string           // DHCP option 15.
+	BroadcastAddress netip.Addr       // DHCP option 28.
+	NTPServers       []net.IP         // DHCP option 42.
+	VLANID           string           // DHCP option 43.116.
+	LeaseTime        uint32           // DHCP option 51.
+	DomainSearch     []string         // DHCP option 119.
+	ServerIP         net.IP           // DHCP option 54.
+}

+ 19 - 0
DHCP/global/global.go

@@ -0,0 +1,19 @@
+package global
+
+import (
+	"github.com/go-logr/logr"
+	"gorm.io/gorm"
+)
+
+var DB *gorm.DB
+
+var Log logr.Logger
+
+type JWTConfig struct {
+	SigningKey string `mapstructure:"key" json:"key"`
+}
+
+// JWT 签名密钥
+var JWT = JWTConfig{
+	SigningKey: "abcdefghijklmnopqrstuvwxyz",
+}

+ 58 - 0
DHCP/go.mod

@@ -0,0 +1,58 @@
+module dhcp
+
+go 1.23.0
+
+require (
+	github.com/gin-gonic/gin v1.10.0
+	github.com/glebarez/sqlite v1.11.0
+	github.com/go-logr/logr v1.4.2
+	github.com/go-logr/zapr v1.3.0
+	github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475
+	github.com/rs/zerolog v1.33.0
+	go.uber.org/zap v1.27.0
+	golang.org/x/net v0.31.0
+	gorm.io/gorm v1.25.12
+)
+
+require (
+	github.com/bytedance/sonic v1.11.6 // indirect
+	github.com/bytedance/sonic/loader v0.1.1 // indirect
+	github.com/cloudwego/base64x v0.1.4 // indirect
+	github.com/cloudwego/iasm v0.2.0 // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/glebarez/go-sqlite v1.21.2 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-playground/validator/v10 v10.20.0 // indirect
+	github.com/goccy/go-json v0.10.2 // indirect
+	github.com/google/uuid v1.3.0 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
+	github.com/josharian/native v1.1.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
+	github.com/pierrec/lz4/v4 v4.1.14 // indirect
+	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
+	github.com/ugorji/go/codec v1.2.12 // indirect
+	go.uber.org/multierr v1.10.0 // indirect
+	golang.org/x/arch v0.8.0 // indirect
+	golang.org/x/crypto v0.29.0 // indirect
+	golang.org/x/sys v0.27.0 // indirect
+	golang.org/x/text v0.20.0 // indirect
+	google.golang.org/protobuf v1.34.1 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	modernc.org/libc v1.22.5 // indirect
+	modernc.org/mathutil v1.5.0 // indirect
+	modernc.org/memory v1.5.0 // indirect
+	modernc.org/sqlite v1.23.1 // indirect
+)

+ 152 - 0
DHCP/go.sum

@@ -0,0 +1,152 @@
+github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
+github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
+github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
+github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
+github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
+github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
+github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
+github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
+github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
+github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
+github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
+github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas=
+github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
+github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
+github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
+github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
+github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
+github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
+github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
+github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
+github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
+github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
+go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
+golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
+golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
+golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
+golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
+golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
+golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
+golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
+golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
+gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
+modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
+modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
+modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
+modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
+modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
+modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
+modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

+ 18 - 0
DHCP/handler/handler.go

@@ -0,0 +1,18 @@
+// Package handler holds the interface that backends implement, handlers take in, and the top level dhcpServer package passes to handlers.
+package handler
+
+import (
+	"context"
+	"dhcp/data"
+	"net"
+)
+
+// BackendReader is the interface for getting data from a macAndIPXE.
+//
+// Backends implement this interface to provide dhcpServer and Netboot data to the handlers.
+type BackendReader interface {
+	// Read data (from a macAndIPXE) based on a mac address
+	// and return dhcpServer headers and options, including netboot info.
+	GetByMac(context.Context, net.HardwareAddr, string) (*data.DHCP, error)
+	GetByIP(context.Context, net.IP, string) (*data.DHCP, error)
+}

+ 153 - 0
DHCP/handler/proxy/proxy.go

@@ -0,0 +1,153 @@
+/*
+Package proxy implements a dhcpServer handler that provides proxyDHCP functionality.
+
+"[A] Proxy dhcpServer server behaves much like a dhcpServer server by listening for ordinary
+dhcpServer client traffic and responding to certain client requests. However, unlike the
+dhcpServer server, the PXE Proxy dhcpServer server does not administer network addresses, and
+it only responds to clients that identify themselves as PXE clients. The responses
+given by the PXE Proxy dhcpServer server contain the mechanism by which the client locates
+the boot servers or the network addresses and descriptions of the supported,
+compatible boot servers."
+
+Reference: https://www.ibm.com/docs/en/aix/7.1?topic=protocol-preboot-execution-environment-proxy-dhcp-daemon
+*/
+package proxy
+
+import (
+	"context"
+	"dhcp/data"
+	"dhcp/handler"
+	"errors"
+	"fmt"
+	"net"
+	"net/netip"
+
+	"github.com/go-logr/logr"
+	"github.com/insomniacslk/dhcp/dhcpv4"
+	"golang.org/x/net/ipv4"
+)
+
+// Handler holds the configuration details for the running the dhcpServer server.
+type Handler struct {
+	// Backend is the macAndIPXE to use for getting dhcpServer data.
+	Backend handler.BackendReader
+
+	// IPAddr is the IP address to use in dhcpServer responses.
+	// Option 54 and the sname dhcpServer header.
+	// This could be a load balancer IP address or an ingress IP address or a local IP address.
+	IPAddr netip.Addr
+
+	// Log is used to log messages.
+	// `logr.Discard()` can be used if no logging is desired.
+	Log logr.Logger
+}
+
+// Redirection name comes from section 2.5 of http://www.pix.net/software/pxeboot/archive/pxespec.pdf
+func (h *Handler) Handle(ctx context.Context, conn *ipv4.PacketConn, dp data.Packet) {
+	// validations
+	if dp.Pkt == nil {
+		h.Log.Error(errors.New("incoming packet is nil"), "not able to respond when the incoming packet is nil")
+		return
+	}
+	upeer, ok := dp.Peer.(*net.UDPAddr)
+	if !ok {
+		h.Log.Error(errors.New("peer is not a UDP connection"), "not able to respond when the peer is not a UDP connection")
+		return
+	}
+	if upeer == nil {
+		h.Log.Error(errors.New("peer is nil"), "not able to respond when the peer is nil")
+		return
+	}
+	if conn == nil {
+		h.Log.Error(errors.New("connection is nil"), "not able to respond when the connection is nil")
+		return
+	}
+
+	var ifName string
+	if dp.Md != nil {
+		ifName = dp.Md.IfName
+	}
+	log := h.Log.WithValues("mac", dp.Pkt.ClientHWAddr.String(), "xid", dp.Pkt.TransactionID.String(), "interface", ifName)
+
+	// We ignore the error here because:
+	// 1. it's only non-nil if the generation of a transaction id (XID) fails.
+	// 2. We always use the clients transaction id (XID) in responses. See dhcpv4.WithReply().
+	reply, _ := dhcpv4.NewReplyFromRequest(dp.Pkt)
+
+	if dp.Pkt.OpCode != dhcpv4.OpcodeBootRequest { // TODO(jacobweinstock): dont understand this, found it in an example here: https://github.com/insomniacslk/dhcp/blob/c51060810aaab9c8a0bd1b0fcbf72bc0b91e6427/dhcpv4/server4/server_test.go#L31
+		log.V(1).Info("Ignoring packet", "OpCode", dp.Pkt.OpCode)
+		return
+	}
+
+	if err := setMessageType(reply, dp.Pkt.MessageType()); err != nil {
+		log.V(1).Info("Ignoring packet", "error", err.Error())
+
+		return
+	}
+
+	// Set option 97
+	reply.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionClientMachineIdentifier, dp.Pkt.GetOneOption(dhcpv4.OptionClientMachineIdentifier)))
+
+	log.Info(
+		"received dhcpServer packet",
+		"type", dp.Pkt.MessageType().String(),
+	)
+
+	dst := replyDestination(dp.Peer, dp.Pkt.GatewayIPAddr)
+	cm := &ipv4.ControlMessage{}
+	if dp.Md != nil {
+		cm.IfIndex = dp.Md.IfIndex
+	}
+	log = log.WithValues(
+		"destination", dst.String(),
+		"bootFileName", reply.BootFileName,
+		"nextServer", reply.ServerIPAddr.String(),
+		"messageType", reply.MessageType().String(),
+		"serverHostname", reply.ServerHostName,
+	)
+	// send the dhcpServer packet
+	if _, err := conn.WriteTo(reply.ToBytes(), cm, dst); err != nil {
+		log.Error(err, "failed to send ProxyDHCP response")
+		return
+	}
+	log.Info("Sent ProxyDHCP response")
+}
+
+func setMessageType(reply *dhcpv4.DHCPv4, reqMsg dhcpv4.MessageType) error {
+	switch mt := reqMsg; mt {
+	case dhcpv4.MessageTypeDiscover:
+		reply.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeOffer))
+	case dhcpv4.MessageTypeRequest:
+		reply.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
+	default:
+		return IgnorePacketError{PacketType: mt, Details: "proxyDHCP only responds to Discover or Request message types"}
+	}
+	return nil
+}
+
+// IgnorePacketError is for when a dhcpServer packet should be ignored.
+type IgnorePacketError struct {
+	PacketType dhcpv4.MessageType
+	Details    string
+}
+
+// Error returns the string representation of ErrIgnorePacket.
+func (e IgnorePacketError) Error() string {
+	return fmt.Sprintf("Ignoring packet: message type %s: details %s", e.PacketType, e.Details)
+}
+
+// replyDestination determines the destination address for the dhcpServer reply.
+// If the giaddr is set, then the reply should be sent to the giaddr.
+// Otherwise, the reply should be sent to the direct peer.
+//
+// From page 22 of https://www.ietf.org/rfc/rfc2131.txt:
+// "If the 'giaddr' field in a dhcpServer message from a client is non-zero,
+// the server sends any return messages to the 'dhcpServer server' port on
+// the BOOTP relay agent whose address appears in 'giaddr'.".
+func replyDestination(directPeer net.Addr, giaddr net.IP) net.Addr {
+	if !giaddr.IsUnspecified() && giaddr != nil {
+		return &net.UDPAddr{IP: giaddr, Port: dhcpv4.ServerPort}
+	}
+
+	return directPeer
+}

+ 164 - 0
DHCP/handler/reservation/handler.go

@@ -0,0 +1,164 @@
+package reservation
+
+import (
+	"context"
+	"dhcp/data"
+	"errors"
+	"github.com/go-logr/logr"
+	"github.com/insomniacslk/dhcp/dhcpv4"
+	"github.com/rs/zerolog/log"
+	"golang.org/x/net/ipv4"
+	"net"
+)
+
+// setDefaults will update the Handler struct to have default values so as
+// to avoid panic for nil pointers and such.
+func (h *Handler) setDefaults() {
+	if h.Backend == nil {
+		h.Backend = noop{}
+	}
+	if h.Log.GetSink() == nil {
+		h.Log = logr.Discard()
+	}
+}
+
+// Handle responds to dhcpServer messages with dhcpServer server options.
+func (h *Handler) Handle(ctx context.Context, conn *ipv4.PacketConn, p data.Packet) {
+	h.setDefaults()
+	if p.Pkt == nil {
+		h.Log.Error(errors.New("incoming packet is nil"), "not able to respond when the incoming packet is nil")
+		return
+	}
+	upeer, ok := p.Peer.(*net.UDPAddr)
+	if !ok {
+		h.Log.Error(errors.New("peer is not a UDP connection"), "not able to respond when the peer is not a UDP connection")
+		return
+	}
+	if upeer == nil {
+		h.Log.Error(errors.New("peer is nil"), "not able to respond when the peer is nil")
+		return
+	}
+	if conn == nil {
+		h.Log.Error(errors.New("connection is nil"), "not able to respond when the connection is nil")
+		return
+	}
+
+	var ifName string
+	if p.Md != nil {
+		ifName = p.Md.IfName
+	}
+	log := h.Log.WithValues("mac", p.Pkt.ClientHWAddr.String(), "xid", p.Pkt.TransactionID.String(), "interface", ifName)
+
+	var reply *dhcpv4.DHCPv4
+	switch mt := p.Pkt.MessageType(); mt {
+	case dhcpv4.MessageTypeDiscover:
+
+		d, err := h.readBackend(ctx, p.Pkt.ClientHWAddr, ifName)
+		if err != nil {
+			if hardwareNotFound(err) {
+				return
+			}
+			log.Info("error reading from macAndIPXE", "error", err)
+
+			return
+		}
+		log.Info("received dhcpServer packet", "type", p.Pkt.MessageType().String())
+		reply = h.updateMsg(ctx, &p, d, dhcpv4.MessageTypeOffer)
+		log = log.WithValues("type", dhcpv4.MessageTypeOffer.String())
+	case dhcpv4.MessageTypeRequest:
+		d, err := h.readBackend(ctx, p.Pkt.ClientHWAddr, ifName)
+		if err != nil {
+			if hardwareNotFound(err) {
+				return
+			}
+			log.Info("error reading from macAndIPXE", "error", err)
+			return
+		}
+		log.Info("received dhcpServer packet", "type", p.Pkt.MessageType().String())
+		reply = h.updateMsg(ctx, &p, d, dhcpv4.MessageTypeAck)
+		log = log.WithValues("type", dhcpv4.MessageTypeAck.String())
+	case dhcpv4.MessageTypeRelease:
+		// 固定ip
+		// 不需要客户端去主动释放
+		log.Info("received dhcpServer release packet, no response required, all IPs are host reservations", "type", p.Pkt.MessageType().String())
+
+		return
+	default:
+		log.Info("received unknown message type", "type", p.Pkt.MessageType().String())
+
+		return
+	}
+
+	if ns := reply.ServerIPAddr; ns != nil {
+		log = log.WithValues("nextServer", ns.String())
+	}
+
+	dst := replyDestination(p.Peer, p.Pkt.GatewayIPAddr)
+	log = log.WithValues("ipAddress", reply.YourIPAddr.String(), "destination", dst.String())
+	cm := &ipv4.ControlMessage{}
+	if p.Md != nil {
+		cm.IfIndex = p.Md.IfIndex
+	}
+
+	if _, err := conn.WriteTo(reply.ToBytes(), cm, dst); err != nil {
+		log.Error(err, "failed to send dhcpServer")
+
+		return
+	}
+
+	log.Info("sent dhcpServer response")
+}
+
+// replyDestination determines the destination address for the dhcpServer reply.
+// If the giaddr is set, then the reply should be sent to the giaddr.
+// Otherwise, the reply should be sent to the direct peer.
+//
+// From page 22 of https://www.ietf.org/rfc/rfc2131.txt:
+// "If the 'giaddr' field in a dhcpServer message from a client is non-zero,
+// the server sends any return messages to the 'dhcpServer server' port on
+// the BOOTP relay agent whose address appears in 'giaddr'.".
+func replyDestination(directPeer net.Addr, giaddr net.IP) net.Addr {
+	if !giaddr.IsUnspecified() && giaddr != nil {
+		return &net.UDPAddr{IP: giaddr, Port: dhcpv4.ServerPort}
+	}
+
+	return directPeer
+}
+
+// readBackend encapsulates the macAndIPXE read and opentelemetry handling.
+func (h *Handler) readBackend(ctx context.Context, mac net.HardwareAddr, ifName string) (*data.DHCP, error) {
+	h.setDefaults()
+
+	d, err := h.Backend.GetByMac(ctx, mac, ifName)
+	if err != nil {
+		return nil, err
+	}
+	return d, nil
+}
+
+// updateMsg handles updating dhcpServer packets with the data from the macAndIPXE.
+func (h *Handler) updateMsg(ctx context.Context, p *data.Packet, d *data.DHCP, msgType dhcpv4.MessageType) *dhcpv4.DHCPv4 {
+	h.setDefaults()
+	mods := []dhcpv4.Modifier{
+		dhcpv4.WithMessageType(msgType),
+		dhcpv4.WithGeneric(dhcpv4.OptionServerIdentifier, h.IPAddr.AsSlice()),
+		dhcpv4.WithServerIP(d.ServerIP),
+		dhcpv4.WithHwAddr(p.Pkt.ClientHWAddr),
+	}
+	mods = append(mods, h.setDHCPOpts(ctx, p.Pkt, d)...)
+	// We ignore the error here because:
+	// 1. it's only non-nil if the generation of a transaction id (XID) fails.
+	// 2. We always use the clients transaction id (XID) in responses. See dhcpv4.WithReply().
+	reply, _ := dhcpv4.NewReplyFromRequest(p.Pkt, mods...)
+	log.Printf("%+v", reply)
+	return reply
+}
+
+// hardwareNotFound returns true if the error is from a hardware record not being found.
+func hardwareNotFound(err error) bool {
+	type hardwareNotFound interface {
+		NotFound() bool
+	}
+	te, ok := err.(hardwareNotFound)
+	return ok && te.NotFound()
+}

+ 22 - 0
DHCP/handler/reservation/noop.go

@@ -0,0 +1,22 @@
+// Package noop is a macAndIPXE handler that does nothing.
+package reservation
+
+import (
+	"context"
+	"dhcp/data"
+	"errors"
+	"net"
+)
+
+// Handler is a noop macAndIPXE.
+type noop struct{}
+
+// GetByMac returns an error.
+func (h noop) GetByMac(_ context.Context, _ net.HardwareAddr, _ string) (*data.DHCP, error) {
+	return nil, errors.New("no macAndIPXE specified, please specify a macAndIPXE")
+}
+
+// GetByIP returns an error.
+func (h noop) GetByIP(_ context.Context, _ net.IP, _ string) (*data.DHCP, error) {
+	return nil, errors.New("no macAndIPXE specified, please specify a macAndIPXE")
+}

+ 41 - 0
DHCP/handler/reservation/option.go

@@ -0,0 +1,41 @@
+package reservation
+
+import (
+	"context"
+	"dhcp/data"
+	"github.com/insomniacslk/dhcp/dhcpv4"
+	"net/netip"
+)
+
+// setDHCPOpts takes a client dhcpServer packet and data (typically from a macAndIPXE) and creates a slice of dhcpServer packet modifiers.
+// m is the dhcpServer request from a client. d is the data to use to create the dhcpServer packet modifiers.
+// This is most likely the place where we would have any business logic for determining dhcpServer option setting.
+func (h *Handler) setDHCPOpts(_ context.Context, _ *dhcpv4.DHCPv4, d *data.DHCP) []dhcpv4.Modifier {
+	mods := []dhcpv4.Modifier{
+		dhcpv4.WithLeaseTime(d.LeaseTime),
+		dhcpv4.WithYourIP(d.IPAddress.AsSlice()),
+	}
+	if len(d.NameServers) > 0 {
+		mods = append(mods, dhcpv4.WithDNS(d.NameServers...))
+	}
+	if len(d.DomainSearch) > 0 {
+		mods = append(mods, dhcpv4.WithDomainSearchList(d.DomainSearch...))
+	}
+	if len(d.NTPServers) > 0 {
+		mods = append(mods, dhcpv4.WithOption(dhcpv4.OptNTPServers(d.NTPServers...)))
+	}
+	if d.BroadcastAddress.Compare(netip.Addr{}) != 0 {
+		mods = append(mods, dhcpv4.WithGeneric(dhcpv4.OptionBroadcastAddress, d.BroadcastAddress.AsSlice()))
+	}
+	if d.DomainName != "" {
+		mods = append(mods, dhcpv4.WithGeneric(dhcpv4.OptionDomainName, []byte(d.DomainName)))
+	}
+	if len(d.SubnetMask) > 0 {
+		mods = append(mods, dhcpv4.WithNetmask(d.SubnetMask))
+	}
+	if d.DefaultGateway.Compare(netip.Addr{}) != 0 {
+		mods = append(mods, dhcpv4.WithRouter(d.DefaultGateway.AsSlice()))
+	}
+
+	return mods
+}

+ 23 - 0
DHCP/handler/reservation/reservation.go

@@ -0,0 +1,23 @@
+// Package reservation is the handler for responding to DHCPv4 messages with only host reservations.
+package reservation
+
+import (
+	"dhcp/handler"
+	"github.com/go-logr/logr"
+	"net/netip"
+)
+
+// Handler holds the configuration details for the running the dhcpServer server.
+type Handler struct {
+	// Backend is the macAndIPXE to use for getting dhcpServer data.
+	Backend handler.BackendReader
+
+	// IPAddr is the IP address to use in dhcpServer responses.
+	// Option 54 and the sname dhcpServer header.
+	// This could be a load balancer IP address or an ingress IP address or a local IP address.
+	IPAddr netip.Addr
+
+	// Log is used to log messages.
+	// `logr.Discard()` can be used if no logging is desired.
+	Log logr.Logger
+}

+ 50 - 0
DHCP/initialize/initLog.go

@@ -0,0 +1,50 @@
+package initialize
+
+import (
+	"dhcp/global"
+	"fmt"
+	"github.com/go-logr/zapr"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+	"os"
+	"time"
+)
+
+// defaultLogger is zap logr implementation.
+func InitLogger(level string) {
+
+	config := zap.NewProductionConfig()
+	file := initLogFile()
+	config.OutputPaths = []string{"stdout", file}
+	switch level {
+	case "debug":
+		config.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
+	default:
+		config.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
+	}
+	zapLogger, err := config.Build()
+	if err != nil {
+		panic(fmt.Sprintf("who watches the watchmen (%v)?", err))
+	}
+
+	global.Log = zapr.NewLogger(zapLogger)
+}
+
+func initLogFile() string {
+	// 获取当前日期
+
+	currentTime := time.Now()
+
+	err := os.MkdirAll("log", os.ModePerm)
+	if err != nil {
+		panic(err)
+	}
+	// 格式化日期作为文件名
+	filename := "log/app_" + currentTime.Format("2006-01-02") + ".log"
+
+	_, err2 := os.Create(filename)
+	if err2 != nil {
+		global.Log.Error(err2, "create log file failed")
+	}
+	return filename
+}

+ 22 - 0
DHCP/initialize/initRouter.go

@@ -0,0 +1,22 @@
+package initialize
+
+import (
+	"dhcp/api"
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+func InitRouter() *gin.Engine {
+	r := gin.Default()
+
+	// 处理根路径
+	r.GET("/", func(ctx *gin.Context) {
+		ctx.HTML(http.StatusOK, "index.html", nil)
+	})
+	// 处理其它所有路径
+	r.NoRoute(func(ctx *gin.Context) {
+		ctx.HTML(http.StatusOK, "index.html", nil)
+	})
+	api.DHCPServerRegisterRoute(r)
+	return r
+}

+ 83 - 0
DHCP/ip/IP.go

@@ -0,0 +1,83 @@
+package ip
+
+import (
+	"dhcp/code"
+	"dhcp/result"
+	"errors"
+	"fmt"
+	"net"
+)
+
+// DetectPublicIPv4 获取公网IP
+func DetectPublicIPv4(extra string) string {
+	ip, err := autoDetectPublicIPv4()
+	if err != nil {
+		return ""
+	}
+
+	return fmt.Sprintf("%v%v", ip.String(), extra)
+}
+
+// autoDetectPublicIPv4 自动检测公网IP
+func autoDetectPublicIPv4() (net.IP, error) {
+	ifaces, err := net.Interfaces()
+	if err != nil {
+		return nil, err
+	}
+	for i := 0; i < len(ifaces); i++ {
+		if (ifaces[i].Flags & net.FlagUp) == 0 {
+			continue
+		}
+		addrs, _ := ifaces[i].Addrs()
+		for _, address := range addrs {
+			ipnet, ok := address.(*net.IPNet)
+			if ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil {
+				return ipnet.IP, nil
+			}
+		}
+	}
+	return nil, errors.New("unable to auto-detect public IPv4")
+}
+
+// GetLocalIPAndInterface 获取本地IP和网络接口
+func GetLocalIPAndInterface() result.Result {
+	type IPAddInterface struct {
+		IP        string `json:"ip"`
+		Interface string `json:"interface"`
+	}
+
+	var ipAddInterfaces []IPAddInterface
+
+	// 获取所有网络接口
+	ifaces, err := net.Interfaces()
+	if err != nil {
+		return result.Result{
+			Status: false,
+			Code:   code.SystemException,
+			Msg:    "获取网络接口失败",
+		}
+	}
+	for i := 0; i < len(ifaces); i++ {
+		if (ifaces[i].Flags & net.FlagUp) == 0 {
+			continue
+		}
+		addrs, _ := ifaces[i].Addrs()
+		for _, address := range addrs {
+			ipnet, ok := address.(*net.IPNet)
+			if ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil {
+				ipface := IPAddInterface{
+					IP:        ipnet.IP.String(),
+					Interface: ifaces[i].Name,
+				}
+				ipAddInterfaces = append(ipAddInterfaces, ipface)
+			}
+		}
+	}
+
+	return result.Result{
+		Status: true,
+		Code:   code.SUCCESS,
+		Msg:    "获取网络接口成功",
+		Data:   ipAddInterfaces,
+	}
+}

+ 0 - 0
DHCP/log/app_2024-11-13.log


+ 32 - 0
DHCP/main.go

@@ -0,0 +1,32 @@
+package main
+
+import (
+	"dhcp/initialize"
+	"os"
+	"os/signal"
+	"syscall"
+)
+
+//	@title			smee
+//	@version		1.0
+//	@description	ipxe管理.
+
+//  @securityDefinitions.apikey ApiKeyAuth
+//  @in header
+//  @name Authorization
+
+func main() {
+
+	// 初始化日志
+	initialize.InitLogger("info")
+
+	//初始化http服务
+	r := initialize.InitRouter()
+	if err := r.Run("0.0.0.0:9999"); err != nil {
+		panic(err)
+	}
+	//优雅退出
+	quit := make(chan os.Signal)
+	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+	<-quit
+}

+ 15 - 0
DHCP/request/requst.go

@@ -0,0 +1,15 @@
+package request
+
+type DHCPService struct {
+	Mode           string   `json:"mode"`
+	BindInterface  string   `json:"bindInterface"`
+	NetworkSegment string   `json:"networkSegment"`
+	DefaultGateway string   `json:"defaultGateway"`
+	NameServers    []string `json:"nameServers"`
+	Hostname       string   `json:"hostname"`
+	EnabledIPMI    bool     `json:"enabledIpmi"`
+	IPMIUserName   string   `json:"ipmiUsername"`
+	IPMIPassword   string   `json:"ipmiPassword"`
+	Status         bool     `json:"status"`
+	Info           string   `json:"info"`
+}

+ 19 - 0
DHCP/response/response.go

@@ -0,0 +1,19 @@
+package response
+
+type AllInfo struct {
+	BmcID            int    `json:"bmcId" `            //bmc的id
+	BMCName          string `json:"BMCname" `          //名字
+	BMCMac           string `json:"BMCMac" `           //bmc的物理地址
+	ServerMac        string `json:"serverMac"`         //服务器网卡的物理地址
+	BmcIP            string `json:"bmcIP" `            //bmc的ip地址
+	BMCSubnetMask    string `json:"BMCSubnetMask"`     //bmc的子网掩码
+	Username         string `json:"username"`          //bmc的用户名
+	Password         string `json:"password" `         //bmc的密码
+	ServerID         int    `json:"serverID" `         //服务器的id
+	ServerName       string `json:"serverName"    `    //服务器名
+	ServerIP         string `json:"serverIP" `         //服务器的ip地址
+	ServerSubnetMask string `json:"serverSubnetMask" ` //服务器的子网掩码
+	ImageID          int    `json:"imageID"`           // 系统镜像id
+	ImageAndConfig   string `json:"imageAndConfig"`    // 系统镜像及配置名
+	IPXEScriptURL    string `json:"ipxeScriptUrl"`     // 脚本地址 覆盖在启动时传递到DHCP的默认值。
+}

+ 14 - 0
DHCP/result/result.go

@@ -0,0 +1,14 @@
+package result
+
+type Result struct {
+	Status bool        `json:"status"`
+	Code   int         `json:"code"`
+	Msg    string      `json:"msg"`
+	Data   interface{} `json:"data"`
+	Token  string      `json:"token"`
+}
+
+type Data struct {
+	List  interface{} `json:"list"`
+	Total int64       `json:"total"`
+}

+ 108 - 0
DHCP/server/dhcp.go

@@ -0,0 +1,108 @@
+// Package dhcpServer providers UDP listening and serving functionality.
+package server
+
+import (
+	"context"
+	"dhcp/data"
+	"net"
+
+	"github.com/go-logr/logr"
+	"github.com/insomniacslk/dhcp/dhcpv4"
+	"github.com/insomniacslk/dhcp/dhcpv4/server4"
+	"golang.org/x/net/ipv4"
+)
+
+// Handler is a type that defines the handler function to be called every time a
+// valid DHCPv4 message is received
+// type Handler func(ctx context.Context, conn net.PacketConn, d data.Packet).
+type Handler interface {
+	Handle(ctx context.Context, conn *ipv4.PacketConn, d data.Packet)
+}
+
+// DHCP represents a DHCPv4 server object.
+type DHCP struct {
+	Conn     net.PacketConn
+	Handlers []Handler
+	Logger   logr.Logger
+}
+
+// Serve serves requests.
+func (s *DHCP) Serve(ctx context.Context) error {
+	s.Logger.Info("Server listening on", "addr", s.Conn.LocalAddr())
+
+	nConn := ipv4.NewPacketConn(s.Conn)
+	if err := nConn.SetControlMessage(ipv4.FlagInterface, true); err != nil {
+		s.Logger.Info("error setting control message", "err", err)
+		return err
+	}
+
+	defer func() {
+		_ = nConn.Close()
+	}()
+	for {
+		// Max UDP packet size is 65535. Max DHCPv4 packet size is 576. An ethernet frame is 1500 bytes.
+		// We use 4096 as a reasonable buffer size. dhcpv4.FromBytes will handle the rest.
+		rbuf := make([]byte, 4096)
+		n, cm, peer, err := nConn.ReadFrom(rbuf)
+		if err != nil {
+			select {
+			case <-ctx.Done():
+				return nil
+			default:
+			}
+			s.Logger.Info("error reading from packet conn", "err", err)
+			return err
+		}
+
+		m, err := dhcpv4.FromBytes(rbuf[:n])
+		if err != nil {
+			s.Logger.Info("error parsing DHCPv4 request", "err", err)
+			continue
+		}
+
+		upeer, ok := peer.(*net.UDPAddr)
+		if !ok {
+			s.Logger.Info("not a UDP connection? Peer is", "peer", peer)
+			continue
+		}
+		// Set peer to broadcast if the client did not have an IP.
+		if upeer.IP == nil || upeer.IP.To4().Equal(net.IPv4zero) {
+			upeer = &net.UDPAddr{
+				IP:   net.IPv4bcast,
+				Port: upeer.Port,
+			}
+		}
+
+		var ifName string
+		if n, err := net.InterfaceByIndex(cm.IfIndex); err == nil {
+			ifName = n.Name
+		}
+
+		for _, handler := range s.Handlers {
+			go handler.Handle(ctx, nConn, data.Packet{Peer: upeer, Pkt: m, Md: &data.Metadata{IfName: ifName, IfIndex: cm.IfIndex}})
+		}
+	}
+}
+
+// Close sends a termination request to the server, and closes the UDP listener.
+func (s *DHCP) Close() error {
+	return s.Conn.Close()
+}
+
+// NewServer initializes and returns a new Server object.
+func NewServer(ifname string, addr *net.UDPAddr, handler ...Handler) (*DHCP, error) {
+	s := &DHCP{
+		Handlers: handler,
+		Logger:   logr.Discard(),
+	}
+
+	if s.Conn == nil {
+		var err error
+		conn, err := server4.NewIPv4UDPConn(ifname, addr)
+		if err != nil {
+			return nil, err
+		}
+		s.Conn = conn
+	}
+	return s, nil
+}

+ 149 - 0
DHCP/service/dhcp.go

@@ -0,0 +1,149 @@
+package service
+
+import (
+	"context"
+	"dhcp/backend"
+	"dhcp/code"
+	"dhcp/handler/proxy"
+	"dhcp/ip"
+	"dhcp/request"
+	"dhcp/result"
+	"dhcp/server"
+	"fmt"
+	"github.com/go-logr/logr"
+	"github.com/insomniacslk/dhcp/dhcpv4/server4"
+	"net"
+	"net/netip"
+)
+
+var DHCPServiceMap = map[string]*DHCPService{}
+var DHCPServiceInfoMap = map[string]request.DHCPService{}
+
+type DhcpConfig struct {
+	Mode              string `yaml:"mode"`                 // 模式
+	BindAddr          string `yaml:"bind_addr"`            // 绑定地址
+	BindInterface     string `yaml:"bind_interface"`       //	绑定接口
+	IpForPacket       string `yaml:"ip_for_packet"`        // 包的ip
+	SyslogIP          string `yaml:"syslog_ip"`            // syslog ip
+	TftpIP            string `yaml:"tftp_ip"`              // tftp ip
+	HttpIpxeBinaryURL string `yaml:"http_ipxe_binary_url"` // http ipxe二进制url
+}
+
+func StartDHCPService(ctx context.Context, log logr.Logger, req request.DHCPService) result.Result {
+	dhcp := DhcpConfig{
+		Mode:          req.Mode,
+		BindAddr:      "0.0.0.0:67",
+		BindInterface: req.BindInterface,
+		IpForPacket:   ip.DetectPublicIPv4(""),
+		SyslogIP:      ip.DetectPublicIPv4(""),
+		TftpIP:        ip.DetectPublicIPv4(":69"),
+	}
+	var dhcpService = &DHCPService{}
+	dhcpService.StartDHCP(ctx, dhcp, log)
+	DHCPServiceMap[req.BindInterface] = dhcpService
+	req.Status = true
+	DHCPServiceInfoMap[req.BindInterface] = req
+	return result.Result{
+		Status: true,
+		Code:   code.SUCCESS,
+		Msg:    "启动成功",
+	}
+}
+
+func StopDHCPService(ctx context.Context, log logr.Logger, req request.DHCPService) result.Result {
+	if dhcpService, ok := DHCPServiceMap[req.BindInterface]; ok {
+		err := dhcpService.server.Close()
+		if err != nil {
+			return result.Result{
+				Status: false,
+				Code:   code.SystemException,
+				Msg:    "关闭失败",
+			}
+		}
+		delete(DHCPServiceMap, req.BindInterface)
+		DHCPServiceInfo := DHCPServiceInfoMap[req.BindInterface]
+		DHCPServiceInfo.Status = false
+		DHCPServiceInfo.Info = "自主关闭"
+		DHCPServiceInfoMap[req.BindInterface] = DHCPServiceInfo
+	}
+	return result.Result{
+		Status: true,
+		Code:   code.SUCCESS,
+		Msg:    "关闭成功",
+	}
+}
+
+func DeleteDHCPService(ctx context.Context, log logr.Logger, req request.DHCPService) result.Result {
+	if dhcpService, ok := DHCPServiceMap[req.BindInterface]; ok {
+		_ = dhcpService.server.Close()
+		delete(DHCPServiceMap, req.BindInterface)
+		delete(DHCPServiceInfoMap, req.BindInterface)
+	} else {
+		return result.Result{
+			Status: false,
+			Code:   code.SystemException,
+			Msg:    "删除失败",
+		}
+	}
+	return result.Result{
+		Status: true,
+		Code:   code.SUCCESS,
+		Msg:    "删除成功",
+	}
+}
+
+type DHCPService struct {
+	server *server.DHCP
+}
+
+func (dhcp *DHCPService) StartDHCP(ctx context.Context, d DhcpConfig, log logr.Logger) {
+	dh, err := dhcpHandler(ctx, &d, log)
+	if err != nil {
+		log.Error(err, "failed to create dhcpServer listener")
+		panic(fmt.Errorf("failed to create dhcpServer listener: %w", err))
+	}
+	log.Info("starting dhcpServer server", "bind_addr", d.BindAddr)
+	go func(ctx context.Context) {
+		bindAddr, err := netip.ParseAddrPort(d.BindAddr)
+		if err != nil {
+			panic(fmt.Errorf("invalid tftp address for dhcpServer server: %w", err))
+		}
+		conn, err := server4.NewIPv4UDPConn(d.BindInterface, net.UDPAddrFromAddrPort(bindAddr))
+		if err != nil {
+			log.Info("failed to create dhcpServer listener", "error", err)
+			panic(err)
+		}
+		defer conn.Close()
+		dhcp.server = &server.DHCP{Logger: log, Conn: conn, Handlers: []server.Handler{dh}}
+		err = dhcp.server.Serve(ctx)
+
+		if err != nil {
+			delete(DHCPServiceMap, d.BindInterface)
+			DHCPServiceInfo := DHCPServiceInfoMap[d.BindInterface]
+			DHCPServiceInfo.Status = false
+			DHCPServiceInfo.Info = err.Error()
+			DHCPServiceInfoMap[d.BindInterface] = DHCPServiceInfo
+		}
+	}(ctx)
+}
+
+func (dhcp *DHCPService) StopDHCP(ctx context.Context) {
+	dhcp.server.Close()
+}
+
+func dhcpHandler(ctx context.Context, c *DhcpConfig, log logr.Logger) (server.Handler, error) {
+	// 1. create the handler
+	// 2. create the macAndIPXE
+	// 3. add the macAndIPXE to the handler
+	pktIP, err := netip.ParseAddr(c.IpForPacket)
+	if err != nil {
+		return nil, fmt.Errorf("invalid bind address: %w", err)
+	}
+	br := backend.NewBackend()
+	dh := &proxy.Handler{
+		Backend: br,
+		IPAddr:  pktIP,
+		Log:     log,
+	}
+	return dh, nil
+}

+ 43 - 12
demo3.1/main.go

@@ -2,22 +2,53 @@ package main
 
 import (
 	"fmt"
-
-	"github.com/bwmarrin/snowflake"
+	"strconv"
 )
 
-func main() {
+// 假设的 VXLAN 信息结构体
+type VXLANInfo struct {
+	VxLANPortName string
+	VID           uint32
+	GroupIp       string
+	LocalIp       string
+}
+
+// 假设的全局配置结构体
+type SysBasicConf struct {
+	BusinessOvsName string
+}
+
+var Global = SysBasicConf{
+	BusinessOvsName: "eth0", // 示例值,根据实际情况修改
+}
 
-	// Create a new Node with a Node number of 1
-	node, err := snowflake.NewNode(1)
-	if err != nil {
-		fmt.Println(err)
-		return
+func main() {
+	// 示例 VXLAN 信息
+	vxLANInfo := VXLANInfo{
+		VxLANPortName: "vxlan0",
+		VID:           1000,
+		GroupIp:       "239.1.1.1",
+		LocalIp:       "192.168.1.1",
 	}
 
-	// Generate a snowflake ID.
-	id := node.Generate()
+	// 构建命令参数
+	commandArg := []string{}
+	commandArg = append(commandArg, "ip")
+	commandArg = append(commandArg, "link")
+	commandArg = append(commandArg, "add")
+	commandArg = append(commandArg, vxLANInfo.VxLANPortName)
+	commandArg = append(commandArg, "type")
+	commandArg = append(commandArg, "vxlan")
+	commandArg = append(commandArg, "id")
+	commandArg = append(commandArg, strconv.Itoa(int(vxLANInfo.VID)))
+	commandArg = append(commandArg, "group")
+	commandArg = append(commandArg, vxLANInfo.GroupIp)
+	commandArg = append(commandArg, "local")
+	commandArg = append(commandArg, vxLANInfo.LocalIp)
+	commandArg = append(commandArg, "dstport")
+	commandArg = append(commandArg, "4789")
+	commandArg = append(commandArg, "dev")
+	commandArg = append(commandArg, Global.BusinessOvsName)
 
-	json, _ := id.MarshalJSON()
-	fmt.Println(string(json))
+	fmt.Println("commandArg:", commandArg)
 }

+ 0 - 2
go.mod

@@ -3,7 +3,6 @@ module demo
 go 1.22
 
 require (
-	github.com/bwmarrin/snowflake v0.3.0
 	github.com/dtm-labs/client v1.18.7
 	github.com/fsnotify/fsnotify v1.7.0
 	github.com/gin-gonic/gin v1.10.0
@@ -94,7 +93,6 @@ require (
 	github.com/sagikazarmark/crypt v0.19.0 // indirect
 	github.com/sagikazarmark/locafero v0.4.0 // indirect
 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
-	github.com/sony/sonyflake v1.2.0 // indirect
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/spf13/afero v1.11.0 // indirect
 	github.com/spf13/cast v1.6.0 // indirect

+ 0 - 4
go.sum

@@ -30,8 +30,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
-github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
 github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
 github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
 github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
@@ -369,8 +367,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ=
-github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y=
 github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
 github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
 github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=

+ 2 - 9
ipgen/main.go

@@ -102,19 +102,12 @@ func formatMAC(mac [6]byte) string {
 }
 
 func main() {
-	macStr := "23:E0:00:00:01:01"
-
-	// 转换为 [6]byte
-	startMAC, err := parseMACString(macStr)
+	inRange, err := checkIPInRange("192.168.2.123", "192.168.2.123", "192.168.2.254")
 	if err != nil {
 		fmt.Println("Error:", err)
 		return
 	}
-	if isValidMACAddressInRange(startMAC, 0) {
-		fmt.Println("MAC地址在指定范围内")
-	} else {
-		fmt.Println("MAC地址不在指定范围内")
-	}
+	fmt.Println(inRange)
 }
 
 func NowIP() string {