Browse Source

Merge branch 'main' of lxz/demo into master

lxz 6 months ago
parent
commit
232f006b2d
44 changed files with 2445 additions and 356 deletions
  1. 2 0
      .idea/vcs.xml
  2. 130 0
      DHCP/api/api.go
  3. 15 0
      DHCP/api/request/requst.go
  4. 19 0
      DHCP/code/common.go
  5. 20 0
      DHCP/global/global.go
  6. 58 0
      DHCP/go.mod
  7. 152 0
      DHCP/go.sum
  8. 50 0
      DHCP/initialize/initLog.go
  9. 22 0
      DHCP/initialize/initRouter.go
  10. 62 0
      DHCP/internal/dao/dhcp.go
  11. 108 0
      DHCP/internal/dhcpServer/backend/backend.go
  12. 162 0
      DHCP/internal/dhcpServer/backend/generateIP.go
  13. 43 0
      DHCP/internal/dhcpServer/data/data.go
  14. 18 0
      DHCP/internal/dhcpServer/handler/handler.go
  15. 153 0
      DHCP/internal/dhcpServer/handler/proxy/proxy.go
  16. 164 0
      DHCP/internal/dhcpServer/handler/reservation/handler.go
  17. 22 0
      DHCP/internal/dhcpServer/handler/reservation/noop.go
  18. 41 0
      DHCP/internal/dhcpServer/handler/reservation/option.go
  19. 23 0
      DHCP/internal/dhcpServer/handler/reservation/reservation.go
  20. 108 0
      DHCP/internal/dhcpServer/server/dhcp.go
  21. 83 0
      DHCP/internal/ip/IP.go
  22. 149 0
      DHCP/internal/service/dhcp.go
  23. 53 0
      DHCP/internal/service/dhcpInfo.go
  24. 0 0
      DHCP/log/app_2024-11-13.log
  25. 24 0
      DHCP/main.go
  26. 18 0
      DHCP/model/dhcp.go
  27. 14 0
      DHCP/result/result.go
  28. 37 42
      demo3.1/main.go
  29. 1 1
      demo3.2/main.go
  30. 4 32
      demo3.4/main.go
  31. 0 52
      demo3.9/main.go
  32. BIN
      demo4/demo4.exe
  33. 0 123
      demo4/main.go
  34. BIN
      demo4/snp.efi
  35. 59 3
      demo5/main.go
  36. 10 0
      etcd/global.go
  37. 7 33
      etcd/lock/test.go
  38. 89 43
      etcd/test.go
  39. 111 0
      fileupload/client/test.go
  40. 179 0
      fileupload/main.go
  41. 0 1
      go.mod
  42. 0 2
      go.sum
  43. 93 24
      gorm/main.go
  44. 142 0
      ipgen/main.go

+ 2 - 0
.idea/vcs.xml

@@ -2,5 +2,7 @@
 <project version="4">
   <component name="VcsDirectoryMappings">
     <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/FtpServer" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/goForward" vcs="Git" />
   </component>
 </project>

+ 130 - 0
DHCP/api/api.go

@@ -0,0 +1,130 @@
+package api
+
+import (
+	"dhcp/api/request"
+	"dhcp/global"
+	"dhcp/internal/ip"
+	"dhcp/internal/service"
+	"dhcp/model"
+	"dhcp/result"
+	"github.com/gin-gonic/gin"
+)
+
+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)
+	dhcpInfo := r.Group("/dhcpInfo")
+	dhcpInfo.POST("/add", AddDHCPInfo)
+	dhcpInfo.GET("/get", GetDHCPInfo)
+	dhcpInfo.GET("/delete", DeleteDHCPInfo)
+	dhcpInfo.POST("/update", UpdateDHCPInfo)
+	dhcpInfo.GET("/getByMac", GetDHCPInfoByMac)
+}
+
+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)
+}
+
+func AddDHCPInfo(c *gin.Context) {
+	var req model.DHCP
+	if err := c.ShouldBindJSON(&req); err != nil {
+		c.JSON(200, result.Result{
+			Code: 400,
+			Data: nil,
+			Msg:  "参数错误",
+		})
+		return
+	}
+	re := service.AddDHCPInfo(req)
+	c.JSON(200, re)
+}
+
+func GetDHCPInfo(c *gin.Context) {
+	re := service.GetDHCPInfo()
+	c.JSON(200, re)
+}
+
+func DeleteDHCPInfo(c *gin.Context) {
+	req := c.Query("mac")
+	re := service.DeleteDHCPInfo(req)
+	c.JSON(200, re)
+}
+
+func UpdateDHCPInfo(c *gin.Context) {
+	var req model.DHCP
+	if err := c.ShouldBindJSON(&req); err != nil {
+		c.JSON(200, result.Result{
+			Code: 400,
+			Data: nil,
+			Msg:  "参数错误",
+		})
+		return
+	}
+	re := service.UpdateDHCPInfo(req)
+	c.JSON(200, re)
+}
+
+func GetDHCPInfoByMac(c *gin.Context) {
+	req := c.Query("mac")
+	re := service.GetDHCPInfoByMac(req)
+	c.JSON(200, re)
+}

+ 15 - 0
DHCP/api/request/requst.go

@@ -0,0 +1,15 @@
+package request
+
+type DHCPService struct {
+	Mode           string   `json:"model"`
+	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/code/common.go

@@ -0,0 +1,19 @@
+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 // 连接数据库异常
+)

+ 20 - 0
DHCP/global/global.go

@@ -0,0 +1,20 @@
+package global
+
+import (
+	"github.com/go-logr/logr"
+	"gorm.io/gorm"
+)
+
+var Log logr.Logger
+
+// 定义数据库指针
+var DB *gorm.DB
+
+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=

+ 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
+}

+ 62 - 0
DHCP/internal/dao/dhcp.go

@@ -0,0 +1,62 @@
+package dao
+
+import (
+	"dhcp/global"
+	"dhcp/model"
+)
+
+// 初始化数据库指针
+
+var db = global.DB
+
+func AddDHCP(dhcp model.DHCP) error {
+	var dp model.DHCP
+	tx := db.Model(&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(&model.DHCP{}).Where("mac_address = ?", dhcp.MACAddress).Updates(dhcp)
+	}
+
+	if tx.Error != nil {
+		return tx.Error
+	}
+	return nil
+}
+
+func GetDHCPByMac(macAddress string) (model.DHCP, error) {
+	var dhcp model.DHCP
+	tx := db.Model(&model.DHCP{}).Where("mac_address = ?", macAddress).First(&dhcp)
+	if tx.Error != nil {
+		return dhcp, tx.Error
+	}
+	return dhcp, nil
+}
+
+func GetDHCP() ([]model.DHCP, error) {
+	var dhcp []model.DHCP
+	tx := db.Model(&model.DHCP{}).Find(&dhcp)
+	if tx.Error != nil {
+		return nil, tx.Error
+	}
+	return dhcp, nil
+}
+
+func DeleteDHCP(macAddress string) error {
+	tx := db.Model(&model.DHCP{}).Where("mac_address = ?", macAddress).Delete(&model.DHCP{})
+	if tx.Error != nil {
+		return tx.Error
+	}
+	return nil
+}
+
+func UpdateDHCP(dhcp model.DHCP) error {
+	tx := db.Model(&model.DHCP{}).Where("mac_address = ?", dhcp.MACAddress).Updates(dhcp)
+	if tx.Error != nil {
+		return tx.Error
+	}
+	return nil
+}

+ 108 - 0
DHCP/internal/dhcpServer/backend/backend.go

@@ -0,0 +1,108 @@
+package backend
+
+import (
+	"context"
+	"dhcp/internal/dao"
+	"dhcp/internal/dhcpServer/data"
+	"dhcp/internal/dhcpServer/handler"
+	"dhcp/model"
+	"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 := dao.GetDHCPByMac(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 model.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
+}

+ 162 - 0
DHCP/internal/dhcpServer/backend/generateIP.go

@@ -0,0 +1,162 @@
+package backend
+
+import (
+	"dhcp/global"
+	"dhcp/internal/dao"
+	"dhcp/internal/service"
+	"dhcp/model"
+	"math/rand"
+	"net"
+	"strconv"
+	"sync"
+	"time"
+)
+
+type DHCPTemplate struct {
+	DhcpInterface    string   //DHCP监听的端口
+	IPAddressRanges  string   // ip范围
+	DefaultGateway   string   // 默认网关
+	NameServers      []string // DNS
+	Hostname         string   // 主机名
+	DomainName       string   // 域名
+	BroadcastAddress string   // 广播地址
+	NTPServers       []string // NTP服务器
+	VLANID           string   // VLAN ID
+	LeaseTime        int      // 租约
+	Arch             string   // 架构
+	DomainSearch     []string // 域名搜索
+}
+
+var record = make(map[string]struct{})  //记录分配的ip
+var recordMac = make(map[string]string) //物理地址对应ip
+
+func InitIPRecord() {
+	all, err := dao.GetDHCP()
+	if err != nil {
+		global.Log.Info("初始化IP记录失败", err)
+	}
+	for _, v := range all {
+		record[v.IPAddress] = struct{}{}
+		recordMac[v.MACAddress] = v.IPAddress
+	}
+}
+
+func UpdateRecord(mac string, ip string) {
+	recordMac[mac] = ip
+	delete(record, ip)
+	record[ip] = struct{}{}
+}
+
+func DeleteRecord(mac string, ip string) {
+	delete(record, ip)
+	delete(recordMac, mac)
+}
+
+func CreateRecord(mac string, ip string) {
+	record[ip] = struct{}{}
+	recordMac[mac] = ip
+}
+
+func GenerateIP(mac string, ifName string) (model.DHCP, error) {
+	var m sync.Mutex
+
+	dh := service.DHCPServiceInfoMap[ifName]
+	d := DHCPTemplate{
+		DhcpInterface:   dh.BindInterface,
+		IPAddressRanges: dh.NetworkSegment,
+		DefaultGateway:  dh.DefaultGateway,
+		NameServers:     dh.NameServers,
+		Hostname:        dh.Hostname,
+		LeaseTime:       86400,
+	}
+	var randomIP net.IP
+
+	// 确定网段范围
+	ip, ipNet, err := net.ParseCIDR(d.IPAddressRanges)
+	if err != nil {
+		global.Log.Error(err, "解析网段失败")
+		return model.DHCP{}, err
+	}
+	if v, ok := recordMac[mac]; !ok {
+		network := ip.Mask(ipNet.Mask)
+		minIP := network.To4()
+		maxIP := net.IPv4(minIP[0]|^ipNet.Mask[0],
+			minIP[1]|^ipNet.Mask[1], minIP[2]|^ipNet.Mask[2],
+			minIP[3]|^ipNet.Mask[3]).To4()
+		// 生成随机种子
+		rand.NewSource(time.Now().UnixNano())
+		for {
+			randomIP = generateRandomIP(minIP, maxIP)
+			if _, ok := record[randomIP.String()]; !ok {
+				m.Lock()
+				record[randomIP.String()] = struct{}{}
+				recordMac[mac] = randomIP.String()
+				m.Unlock()
+				break
+			}
+		}
+	} else {
+		randomIP = net.ParseIP(v)
+	}
+
+	// 构建DHCP信息
+	subnetMask := strconv.Itoa(int(ipNet.Mask[0])) + "." + strconv.Itoa(int(ipNet.Mask[1])) + "." + strconv.Itoa(int(ipNet.Mask[2])) + "." + strconv.Itoa(int(ipNet.Mask[3]))
+	dhcp := buildDHCP(randomIP.String(), subnetMask, mac, d)
+
+	_ = dao.AddDHCP(dhcp)
+
+	return dhcp, err
+}
+
+func generateRandomIP(min, max net.IP) net.IP {
+	// IP地址是32位的,每位都是一个字节
+	ip := make(net.IP, len(min))
+	for i := range min {
+		n := int(max[i] - min[i])
+		if n == 0 {
+			ip[i] = min[i]
+		} else {
+			ip[i] = byte(rand.Intn(n) + int(min[i]))
+		}
+	}
+	if ip.Equal(min) || ip.Equal(max) {
+		return generateRandomIP(min, max)
+	}
+	return ip
+}
+
+func buildDHCP(randomIP string, subnetMask, mac string, d DHCPTemplate) model.DHCP {
+
+	ipinfo := model.DHCP{
+		Interface:        d.DhcpInterface,
+		MACAddress:       mac,
+		IPAddress:        randomIP,
+		SubnetMask:       subnetMask,
+		DefaultGateway:   d.DefaultGateway,
+		NameServers:      d.NameServers,
+		DomainName:       d.DomainName,
+		BroadcastAddress: d.BroadcastAddress,
+		NTPServers:       d.NTPServers,
+		VLANID:           d.VLANID,
+		LeaseTime:        uint32(d.LeaseTime),
+		DomainSearch:     d.DomainSearch,
+	}
+	return ipinfo
+}
+
+func isIPInSubnet(ip, ifName string) bool {
+	// Parse the IP address
+	ipAddr := net.ParseIP(ip)
+	if ipAddr == nil {
+		return false
+	}
+
+	// Parse the subnet
+	_, ipNet, err := net.ParseCIDR(service.DHCPServiceInfoMap[ifName].NetworkSegment)
+	if err != nil {
+		return false
+	}
+
+	// Check if the IP address is in the subnet
+	return ipNet.Contains(ipAddr)
+}

+ 43 - 0
DHCP/internal/dhcpServer/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.
+}

+ 18 - 0
DHCP/internal/dhcpServer/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/internal/dhcpServer/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/internal/dhcpServer/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/internal/dhcpServer/data"
+	"dhcp/internal/dhcpServer/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/internal/dhcpServer/handler/reservation/handler.go

@@ -0,0 +1,164 @@
+package reservation
+
+import (
+	"context"
+	"dhcp/internal/dhcpServer/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/internal/dhcpServer/handler/reservation/noop.go

@@ -0,0 +1,22 @@
+// Package noop is a macAndIPXE handler that does nothing.
+package reservation
+
+import (
+	"context"
+	"dhcp/internal/dhcpServer/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/internal/dhcpServer/handler/reservation/option.go

@@ -0,0 +1,41 @@
+package reservation
+
+import (
+	"context"
+	"dhcp/internal/dhcpServer/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/internal/dhcpServer/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/internal/dhcpServer/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
+}

+ 108 - 0
DHCP/internal/dhcpServer/server/dhcp.go

@@ -0,0 +1,108 @@
+// Package dhcpServer providers UDP listening and serving functionality.
+package server
+
+import (
+	"context"
+	"dhcp/internal/dhcpServer/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
+}

+ 83 - 0
DHCP/internal/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,
+	}
+}

+ 149 - 0
DHCP/internal/service/dhcp.go

@@ -0,0 +1,149 @@
+package service
+
+import (
+	"context"
+	"dhcp/api/request"
+	"dhcp/code"
+	"dhcp/internal/dhcpServer/backend"
+	"dhcp/internal/dhcpServer/handler/proxy"
+	"dhcp/internal/dhcpServer/server"
+	"dhcp/internal/ip"
+	"dhcp/result"
+	"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:"model"`                // 模式
+	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
+}

+ 53 - 0
DHCP/internal/service/dhcpInfo.go

@@ -0,0 +1,53 @@
+package service
+
+import (
+	"dhcp/code"
+	"dhcp/internal/dao"
+	"dhcp/model"
+	"dhcp/result"
+)
+
+func AddDHCPInfo(dhcpInfo model.DHCP) result.Result {
+	err := dao.AddDHCP(dhcpInfo)
+	return result.Result{
+		Status: err == nil,
+		Code:   code.SUCCESS,
+		Msg:    "添加成功",
+	}
+}
+
+func GetDHCPInfoByMac(mac string) result.Result {
+	dhcpInfo, err := dao.GetDHCPByMac(mac)
+	return result.Result{
+		Status: err == nil,
+		Code:   code.SUCCESS,
+		Data:   dhcpInfo,
+	}
+}
+
+func GetDHCPInfo() result.Result {
+	dhcpInfo, err := dao.GetDHCP()
+	return result.Result{
+		Status: err == nil,
+		Code:   code.SUCCESS,
+		Data:   dhcpInfo,
+	}
+}
+
+func UpdateDHCPInfo(dhcpInfo model.DHCP) result.Result {
+	err := dao.UpdateDHCP(dhcpInfo)
+	return result.Result{
+		Status: err == nil,
+		Code:   code.SUCCESS,
+		Msg:    "更新成功",
+	}
+}
+
+func DeleteDHCPInfo(mac string) result.Result {
+	err := dao.DeleteDHCP(mac)
+	return result.Result{
+		Status: err == nil,
+		Code:   code.SUCCESS,
+		Msg:    "删除成功",
+	}
+}

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


+ 24 - 0
DHCP/main.go

@@ -0,0 +1,24 @@
+package main
+
+import (
+	"dhcp/initialize"
+	"os"
+	"os/signal"
+	"syscall"
+)
+
+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
+}

+ 18 - 0
DHCP/model/dhcp.go

@@ -0,0 +1,18 @@
+package model
+
+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
+	Interface        string   `json:"interface" gorm:"interface"`                 // 网卡
+}

+ 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"`
+}

+ 37 - 42
demo3.1/main.go

@@ -2,58 +2,53 @@ package main
 
 import (
 	"fmt"
-	"net"
 	"strconv"
 )
 
-// ipToInt 将IP地址转换为整数
-func ipToInt(ip net.IP) uint32 {
-	ip = ip.To4()
-	return uint32(ip[0])<<24 + uint32(ip[1])<<16 + uint32(ip[2])<<8 + uint32(ip[3])
+// 假设的 VXLAN 信息结构体
+type VXLANInfo struct {
+	VxLANPortName string
+	VID           uint32
+	GroupIp       string
+	LocalIp       string
 }
 
-// intToIP 将整数转换为IP地址
-func intToIP(n uint32) net.IP {
-	return net.IPv4(byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
+// 假设的全局配置结构体
+type SysBasicConf struct {
+	BusinessOvsName string
 }
 
-// generateIPs 生成从startIP到endIP的所有IP地址
-func generateIPs(startIP, endIP string) ([]string, error) {
-	start := net.ParseIP(startIP)
-	end := net.ParseIP(endIP)
-	if start == nil || end == nil {
-		return nil, fmt.Errorf("invalid IP address")
-	}
-
-	startInt := ipToInt(start)
-	endInt := ipToInt(end)
-
-	if startInt > endInt {
-		return nil, fmt.Errorf("start IP must be less than or equal to end IP")
-	}
-
-	var ips []string
-	for ip := startInt; ip <= endInt; ip++ {
-		ips = append(ips, intToIP(ip).String())
-	}
-	return ips, nil
+var Global = SysBasicConf{
+	BusinessOvsName: "eth0", // 示例值,根据实际情况修改
 }
 
 func main() {
-	startIP := "10.10.1.200"
-	endIP := "10.10.2.50"
-	_, ipNet, err := net.ParseCIDR("10.10.1.1/22")
-	if err != nil {
-		panic(err)
-	}
-	fmt.Println(strconv.Itoa(int(ipNet.Mask[0])) + "." + strconv.Itoa(int(ipNet.Mask[1])) + "." + strconv.Itoa(int(ipNet.Mask[2])) + "." + strconv.Itoa(int(ipNet.Mask[3])))
-	ips, err := generateIPs(startIP, endIP)
-	if err != nil {
-		fmt.Println("Error:", err)
-		return
+	// 示例 VXLAN 信息
+	vxLANInfo := VXLANInfo{
+		VxLANPortName: "vxlan0",
+		VID:           1000,
+		GroupIp:       "239.1.1.1",
+		LocalIp:       "192.168.1.1",
 	}
 
-	for _, ip := range ips {
-		fmt.Println(ip)
-	}
+	// 构建命令参数
+	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)
+
+	fmt.Println("commandArg:", commandArg)
 }

+ 1 - 1
demo3.2/main.go

@@ -17,7 +17,7 @@ import (
 		password := "password"
 
 		client, err := ipmi.NewClient(host, port, username, password)
-		// Support local mode client if runs directly on linux
+		// Support local model client if runs directly on linux
 		// client, err := ipmi.NewOpenClient()
 		if err != nil {
 			zap.S().Infof("err: %v", err)

+ 4 - 32
demo3.4/main.go

@@ -1,37 +1,9 @@
 package main
 
-import (
-	"fmt"
-	"github.com/lucklucif/ipmigo"
-)
+import "fmt"
 
 func main() {
-	c, err := ipmigo.NewClient(ipmigo.Arguments{
-		Version:       ipmigo.V2_0,
-		Address:       "120.46.150.226:623",
-		Username:      "ADMIN",
-		Password:      "ADMIN",
-		CipherSuiteID: 3,
-	})
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
-
-	if err := c.Open(); err != nil {
-		fmt.Println(err)
-		return
-	}
-	defer c.Close()
-	//err = c.Ping()
-	//if err != nil {
-	//	fmt.Println(err)
-	//	return
-	//}
-	//cmd := ipmigo.GetChassisStatusCommand{}
-	//if err := c.Execute(&cmd); err != nil {
-	//	fmt.Println(err)
-	//	return
-	//}
-	//fmt.Println("Power On Hours", cmd)
+	nums := []int{1, 2, 3, 4}
+	nums = append(nums[:1], nums[2:]...)
+	fmt.Println(nums)
 }

+ 0 - 52
demo3.9/main.go

@@ -1,52 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"io"
-	"net/http"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/gin-gonic/gin"
-)
-
-func main() {
-	router := gin.Default()
-
-	router.POST("/upload", func(c *gin.Context) {
-		file, header, err := c.Request.FormFile("file")
-		if err != nil {
-			c.String(http.StatusBadRequest, fmt.Sprintf("文件上传失败: %s", err.Error()))
-			return
-		}
-		defer file.Close()
-
-		// 指定保存目录
-		uploadDir := filepath.Join("./uploads", strings.Split(header.Filename, ".")[0])
-		err = os.MkdirAll(uploadDir, os.ModePerm)
-		if err != nil {
-			c.String(http.StatusInternalServerError, fmt.Sprintf("创建上传目录失败: %s", err.Error()))
-			return
-		}
-
-		// 指定保存文件路径
-		filePath := filepath.Join(uploadDir, header.Filename)
-		out, err := os.Create(filePath)
-		if err != nil {
-			c.String(http.StatusInternalServerError, fmt.Sprintf("保存文件失败: %s", err.Error()))
-			return
-		}
-		defer out.Close()
-
-		_, err = io.Copy(out, file)
-		if err != nil {
-			c.String(http.StatusInternalServerError, fmt.Sprintf("保存文件失败: %s", err.Error()))
-			return
-		}
-
-		c.String(http.StatusOK, fmt.Sprintf("文件 %s 上传成功", header.Filename))
-	})
-
-	router.Run(":8080")
-}

BIN
demo4/demo4.exe


+ 0 - 123
demo4/main.go

@@ -1,123 +0,0 @@
-package main
-
-import (
-	"errors"
-	"fmt"
-	"reflect"
-	"strings"
-)
-
-func main() {
-	type Address struct {
-		City  string
-		State string
-	}
-	type User struct {
-		Name    string
-		Age     int
-		Address *Address
-	}
-
-	var test = new(User)
-	test.Name = "ra"
-	fmt.Println(test)
-}
-
-// Struct2MgrStr 结构提转换为字符串
-func Struct2MgrStr(in interface{}, tag, connector string) (string, error) {
-	var out string
-	var val = reflect.ValueOf(in)
-	if val.Kind() == reflect.Array || val.Kind() == reflect.Slice && !val.IsZero() {
-		val = reflect.ValueOf(in.([]interface{})[0])
-	}
-
-	// 判断in是否为指针类型, 如果v为指针类型这将v替换为指针对应的值
-	if val.Kind() == reflect.Ptr {
-		val = val.Elem()
-	}
-
-	if val.Kind() != reflect.Struct && val.Kind() != reflect.Map {
-		return "", errors.New("输入格式异常,不是Struct/Map类型数据")
-	}
-
-	var tp = val.Type()
-	var args = make([]string, 0)
-
-	for i := 0; i < val.NumField(); i++ {
-		v := val.Field(i)
-		if v.IsZero() {
-			continue
-		}
-		if v.Kind() == reflect.Ptr {
-			v = v.Elem()
-		}
-		fi := tp.Field(i)
-		tagVal := fi.Tag.Get(tag)
-		if tagVal == "-" || tagVal == "" {
-			continue
-		}
-		if v.Kind() == reflect.String && v.String() == "" {
-			continue
-		}
-		args = append(args, fmt.Sprintf("%s%s%v", tagVal, connector, v.Interface()))
-	}
-	out = strings.Join(args, " ")
-	return out, nil
-}
-
-//package main
-//
-//import (
-//	"fmt"
-//	"github.com/robfig/cron/v3"
-//	"os"
-//	"time"
-//
-//	"go.uber.org/zap"
-//)
-//
-//func main() {
-//
-//	c := cron.New()
-//
-//	c.Start()
-//	// 初始化 logger
-//	logger := initLogger()
-//
-//	// 监听 SIGUSR1 信号
-//
-//	EntryID, err := c.AddFunc("*/1 * * * *", func() {
-//		logger.Info("cron running")
-//
-//		logger = initLogger()
-//	})
-//	fmt.Println(time.Now(), EntryID, err)
-//
-//	// 保持进程运行
-//	select {}
-//}
-//
-//func initLogger() *zap.Logger {
-//	// 获取当前日期
-//	currentTime := time.Now()
-//
-//	// 格式化日期作为文件名
-//	filename := "log/app_" + currentTime.Format("2006-01-02-15-04-05") + ".log"
-//
-//	_, err2 := os.Create(filename)
-//	if err2 != nil {
-//		panic(err2)
-//
-//	}
-//	// 配置日志输出路径
-//	config := zap.NewProductionConfig()
-//	config.OutputPaths = []string{filename, "stdout"}
-//
-//	// 创建 logger 实例
-//	logger, err := config.Build()
-//	if err != nil {
-//		panic(err)
-//	}
-//
-//	return logger
-//}

BIN
demo4/snp.efi


+ 59 - 3
demo5/main.go

@@ -2,17 +2,73 @@ package main
 
 import (
 	"encoding/hex"
+	"errors"
 	"github.com/tjfoc/gmsm/sm3"
+	"reflect"
 )
 
-var a *int
-
 func main() {
-	if true && false && false || false {
 
+	s := new(string)
+	if s == nil {
+		VarAssignment(s, "/home")
 	}
+	VarAssignment(s, "/")
+	println(*s)
 }
 
+// VarAssignment 变量赋值
+func VarAssignment(to, from any) error {
+	var oldTv, oldFv, tv, fv reflect.Value
+	tv = reflect.ValueOf(to)
+	fv = reflect.ValueOf(from)
+	if _, ok := to.(reflect.Value); ok {
+		tv = to.(reflect.Value)
+	}
+	if _, ok := from.(reflect.Value); ok {
+		fv = from.(reflect.Value)
+	}
+	if _, ok := to.(*reflect.Value); ok {
+		tv = *(to.(*reflect.Value))
+	}
+	if _, ok := from.(*reflect.Value); ok {
+		fv = *(from.(*reflect.Value))
+	}
+
+	oldTv = tv
+	oldFv = fv
+
+	if tv.IsNil() {
+		return errors.New("The target data cannot be nil\n")
+	}
+	// 判断目标变量类型是否为指针类型,非指针类型返回错误
+	if tv.Kind() == reflect.Ptr {
+		tv = tv.Elem()
+	} else {
+		return errors.New("The target variable must be of pointer type ")
+	}
+	if fv.Kind() == reflect.Ptr {
+		fv = fv.Elem()
+	}
+
+	// 判断目标变量类型和原变量类型是否一致
+	if oldTv.Type().Elem().Kind() != fv.Kind() {
+		return errors.New("Inconsistent data types ")
+	}
+
+	// 判断来源变量是否为空指针,如果为空指针则直接退出
+	if oldFv.IsZero() && oldFv.Kind() == reflect.Ptr {
+		return nil
+	}
+
+	// 判断目标变量是否为空,如果为空则创建内存空间
+	if oldTv.IsZero() {
+		val := reflect.New(oldTv.Type().Elem())
+		oldTv.Set(val)
+	}
+	oldTv.Elem().Set(fv)
+	return nil
+}
 func SM3SUM(in string) string {
 	sm3Sum := sm3.Sm3Sum([]byte(in))
 	return hex.EncodeToString(sm3Sum)

+ 10 - 0
etcd/global.go

@@ -0,0 +1,10 @@
+package main
+
+import (
+	"context"
+	clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+var client *clientv3.Client
+
+var ctx = context.Background()

+ 7 - 33
etcd/lock/test.go

@@ -2,7 +2,6 @@ package main
 
 import (
 	"context"
-	"fmt"
 	clientv3 "go.etcd.io/etcd/client/v3"
 	"go.etcd.io/etcd/client/v3/concurrency"
 	"time"
@@ -18,40 +17,15 @@ func main() {
 	}
 	defer cli.Close()
 
-	for i := 0; i < 10; i++ {
+	grant, _ := cli.Grant(context.Background(), 15)
 
-		go func(i int) {
-			m, err := NewPciLock(cli)
-			if err != nil {
-				fmt.Printf("Error creating")
-			}
-			m.IsOwner()
-			err = m.Lock(context.Background())
-			if err != nil {
-				fmt.Printf("Failed to lock: %v\n", err)
-				return
-			}
-			if i == 1 {
-				time.Sleep(time.Second * 10)
-			}
-			fmt.Println(i)
-			err = m.Unlock(context.Background())
-			if err != nil {
-				fmt.Printf("Failed to Unlock: %v\n", err)
-				return
-			}
-		}(i)
-	}
-	time.Sleep(time.Second * 15)
-}
-
-func NewPciLock(etcd *clientv3.Client) (*concurrency.Mutex, error) {
-	session, err := concurrency.NewSession(etcd, concurrency.WithTTL(5))
+	session, err := concurrency.NewSession(cli, concurrency.WithLease(grant.ID))
 	if err != nil {
-		return nil, err
+		panic(err)
 	}
+	m := concurrency.NewLocker(session, "lock/test")
+	m.Lock()
+	defer m.Unlock()
+	time.Sleep(20 * time.Second)
 
-	m := concurrency.NewMutex(session, "hstack/server/pciLock")
-
-	return m, nil
 }

+ 89 - 43
etcd/test.go

@@ -1,10 +1,11 @@
 package main
 
 import (
-	"context"
 	"fmt"
 	"go.etcd.io/etcd/client/v3"
+	"go.etcd.io/etcd/client/v3/concurrency"
 	"log"
+	"strings"
 	"time"
 )
 
@@ -66,12 +67,74 @@ func main() {
 		panic(err)
 	}
 	defer cli.Close()
+	session, err := concurrency.NewSession(cli, concurrency.WithTTL(5))
+	if err != nil {
+		panic(err)
+	}
+	m := concurrency.NewMutex(session, "lock/test")
+	for i := 0; i < 10; i++ {
+		err = m.TryLock(ctx)
+		if err != nil {
+			fmt.Printf("Failed to lock: %v\n", err)
+			time.Sleep(1 * time.Second)
+			continue
+		}
+		fmt.Println("Locked")
+		time.Sleep(1 * time.Second)
+		m.Unlock(ctx)
+		fmt.Println("Unlocked")
+	}
+}
+
+// 保持租约
+func keepAlive(cli *clientv3.Client, leaseID clientv3.LeaseID) {
+	alive, err := cli.KeepAlive(ctx, leaseID)
+	if err != nil {
+		fmt.Printf("Failed to keep alive: %v\n", err)
+		return
+	}
+	//监听KeepAlive的响应
+	go func() {
+		for {
+			ka := <-alive
+			if ka == nil {
+				log.Println("租约已失效或KeepAlive通道已关闭")
+				return
+			}
+			log.Printf("续租成功: %v\n", ka)
+		}
+	}()
+}
+
+// 新增键值对
+func put(cli *clientv3.Client, key, value string) error {
+	_, err := cli.Put(ctx, key, value)
+	if err != nil {
+		log.Fatal(err)
+		return err
+	}
+	return nil
+}
+
+// 新增键值对,带有过期时间
+func putWithTTL(cli *clientv3.Client, key, value string, ttl int64) (int64, error) {
+	leaseResp, err := cli.Grant(ctx, ttl)
+	if err != nil {
+		log.Fatal(err)
+		return 0, err
+	}
+	_, err = cli.Put(ctx, key, value, clientv3.WithLease(leaseResp.ID))
+	if err != nil {
+		log.Fatal(err)
+		return 0, err
+	}
+	return int64(leaseResp.ID), nil
+}
 
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
+// 监听键值变化
+func watch(cli *clientv3.Client, key string) {
 
-	// 开始监听以 "/services/" 为前缀的键值变化
-	watchCh := cli.Watch(ctx, "/services/", clientv3.WithPrefix())
+	watchCh := cli.Watch(ctx, key, clientv3.WithPrefix())
 
 	go func() {
 
@@ -91,48 +154,31 @@ func main() {
 			}
 		}
 	}()
-	// 创建租约
-	leaseResp, err := cli.Grant(ctx, 15) // 租约时间为 15 秒
-	if err != nil {
-		fmt.Printf("Failed to grant lease: %v\n", err)
-		return
-	}
+}
 
-	// 使用租约创建服务实例的键
-	_, err = cli.Put(ctx, "/services/service1", "", clientv3.WithLease(leaseResp.ID))
+// 获取键的值
+func get(cli *clientv3.Client, key string) (string, error) {
+	resp, err := cli.Get(ctx, key)
 	if err != nil {
-		fmt.Printf("Failed to put service instance: %v\n", err)
-		return
+		log.Fatal(err)
+		return "", err
+	}
+	if resp.Count == 0 {
+		return "", fmt.Errorf("key %s not found", key)
 	}
+	return string(resp.Kvs[0].Value), nil
+}
 
-	alive, err := cli.KeepAlive(ctx, leaseResp.ID) // 续租(ttl/3)
+// 获取当前前缀下所有的键并截取前缀
+func getAndCutPrefix(cli *clientv3.Client, prefix string) ([]string, error) {
+	resp, err := cli.Get(ctx, prefix, clientv3.WithPrefix())
 	if err != nil {
-		fmt.Printf("Failed to keep alive: %v\n", err)
-		return
+		log.Fatal(err)
+		return nil, err
 	}
-	//监听KeepAlive的响应
-	go func() {
-		for {
-			ka := <-alive
-			if ka == nil {
-				log.Println("租约已失效或KeepAlive通道已关闭")
-				return
-			}
-			log.Printf("续租成功: %v\n", ka)
-		}
-	}()
-
-	fmt.Println("Watching for changes...")
-	get, _ := cli.Get(ctx, "/services/service1")
-	fmt.Println(get.Kvs)
-	// 处理监听结果
-	go func() {
-		time.Sleep(10 * time.Second)
-		// 注意: 程序退出时,应当撤销租约或删除键
-		_, err = cli.Revoke(context.TODO(), leaseResp.ID)
-		if err != nil {
-			log.Fatal(err)
-		}
-	}()
-	time.Sleep(15 * time.Second)
+	keys := make([]string, 0)
+	for _, kv := range resp.Kvs {
+		keys = append(keys, strings.TrimPrefix(string(kv.Key), prefix))
+	}
+	return keys, nil
 }

+ 111 - 0
fileupload/client/test.go

@@ -0,0 +1,111 @@
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"mime/multipart"
+	"net/http"
+	"os"
+)
+
+func main() {
+	filePath := "C:\\Users\\lx\\Downloads\\ubuntu-22.04.4-live-server-amd64.iso"
+	chunkSize := int64(1024 * 1024 * 100) // 100MB 分块大小
+	fileID := "unique-file-id"            // 文件唯一标识符
+
+	// 打开文件
+	file, err := os.Open(filePath)
+	if err != nil {
+		panic(err)
+	}
+	defer file.Close()
+
+	// 获取文件信息
+	fileInfo, err := file.Stat()
+	if err != nil {
+		panic(err)
+	}
+	fileSize := fileInfo.Size()
+
+	// 计算总块数
+	totalChunks := (fileSize + chunkSize - 1) / chunkSize
+
+	// 记录已上传的块(可以保存在文件或数据库中)
+	uploadedChunks := make(map[int64]bool)
+
+	// 遍历每个块并上传
+	for chunkIndex := int64(0); chunkIndex < totalChunks; chunkIndex++ {
+		if uploadedChunks[chunkIndex] {
+			// 如果该块已经上传,则跳过
+			continue
+		}
+
+		// 创建块的缓冲区
+		buffer := make([]byte, chunkSize)
+		readBytes, err := file.Read(buffer)
+		if err != nil && err != io.EOF {
+			panic(err)
+		}
+		if readBytes == 0 {
+			break
+		}
+
+		// 上传块
+		err = uploadChunk(buffer[:readBytes], chunkIndex, totalChunks, fileID, fileInfo.Name())
+		if err != nil {
+			fmt.Printf("Failed to upload chunk %d: %v\n", chunkIndex, err)
+			continue
+		}
+
+		// 记录已上传块
+		uploadedChunks[chunkIndex] = true
+	}
+}
+
+func uploadChunk(chunkData []byte, chunkIndex, totalChunks int64, fileID, filename string) error {
+	// 创建 multipart 请求体
+	body := new(bytes.Buffer)
+	writer := multipart.NewWriter(body)
+
+	// 添加文件块
+	part, err := writer.CreateFormFile("file", filename)
+	if err != nil {
+		return err
+	}
+	part.Write(chunkData)
+
+	// 添加其他字段
+	writer.WriteField("chunkIndex", fmt.Sprintf("%d", chunkIndex))
+	writer.WriteField("totalChunks", fmt.Sprintf("%d", totalChunks))
+	writer.WriteField("fileID", fileID)
+
+	// 关闭 multipart 编写器
+	err = writer.Close()
+	if err != nil {
+		return err
+	}
+
+	// 发送请求
+	req, err := http.NewRequest("POST", "http://localhost:8080/upload", body)
+	if err != nil {
+		return err
+	}
+	req.Header.Set("Content-Type", writer.FormDataContentType())
+
+	client := &http.Client{}
+	resp, err := client.Do(req)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	// 读取响应
+	respBody, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return err
+	}
+
+	fmt.Printf("Response: %s\n", string(respBody))
+	return nil
+}

+ 179 - 0
fileupload/main.go

@@ -0,0 +1,179 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strconv"
+	"sync"
+
+	"github.com/gin-gonic/gin"
+)
+
+const uploadPath = "C:\\Users\\lx\\GolandProjects\\demo\\fileupload\\uploads"
+const progressFilePath = "C:\\Users\\lx\\GolandProjects\\demo\\fileupload\\uploads\\progress"
+
+var mu sync.Mutex // 使用互斥锁来确保并发时的进度文件读写安全
+
+func main() {
+	router := gin.Default()
+
+	// 确保上传和进度记录目录存在
+	err := os.MkdirAll(uploadPath, os.ModePerm)
+	if err != nil {
+		panic(err)
+	}
+	err = os.MkdirAll(progressFilePath, os.ModePerm)
+	if err != nil {
+		panic(err)
+	}
+
+	// 定义分块上传接口,支持断点续传
+	router.POST("/upload", upload)
+
+	router.Run(":8080")
+}
+
+func upload(c *gin.Context) {
+	// 获取上传的文件块
+	file, header, err := c.Request.FormFile("file")
+	if err != nil {
+		c.String(http.StatusBadRequest, "Failed to get file: %s", err.Error())
+		return
+	}
+	defer file.Close()
+
+	// 获取文件块编号和总块数
+	chunkIndex := c.PostForm("chunkIndex")   // 当前块编号
+	totalChunks := c.PostForm("totalChunks") // 总块数
+	fileID := c.PostForm("fileID")           // 文件标识符
+
+	chunkIdx, err := strconv.Atoi(chunkIndex)
+	if err != nil {
+		c.String(http.StatusBadRequest, "Invalid chunk index")
+		return
+	}
+	totalChunksNum, err := strconv.Atoi(totalChunks)
+	if err != nil {
+		c.String(http.StatusBadRequest, "Invalid total chunks")
+		return
+	}
+
+	// 先检查该文件的进度文件,看看该块是否已经上传
+	if isChunkUploaded(fileID, chunkIdx) {
+		c.String(http.StatusOK, "Chunk %d already uploaded", chunkIdx)
+		return
+	}
+
+	// 构造临时文件路径
+	tempFilePath := filepath.Join(uploadPath, fmt.Sprintf("%s.tmp", fileID))
+
+	// 打开或创建临时文件
+	tempFile, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
+	if err != nil {
+		c.String(http.StatusInternalServerError, "Failed to create temp file: %s", err.Error())
+		return
+	}
+	defer tempFile.Close()
+
+	// 计算文件块的偏移量
+	chunkSize := header.Size
+	offset := int64(chunkIdx) * chunkSize
+
+	// 定位文件指针到正确的偏移量并写入块数据
+	_, err = tempFile.Seek(offset, 0)
+	if err != nil {
+		c.String(http.StatusInternalServerError, "Failed to seek in temp file: %s", err.Error())
+		return
+	}
+
+	// 将块写入临时文件
+	_, err = io.Copy(tempFile, file)
+	if err != nil {
+		c.String(http.StatusInternalServerError, "Failed to write to temp file: %s", err.Error())
+		return
+	}
+
+	// 记录该块已上传
+	recordChunkProgress(fileID, chunkIdx)
+
+	// 当所有块上传完毕时,将临时文件重命名为最终的文件
+	if chunkIdx == totalChunksNum-1 {
+		tempFile.Close()
+		finalFilePath := filepath.Join(uploadPath, header.Filename)
+		err = os.Rename(tempFilePath, finalFilePath)
+		if err != nil {
+			c.String(http.StatusInternalServerError, "Failed to finalize file: %s", err.Error())
+			return
+		}
+
+		// 上传完成后删除进度记录文件
+		deleteProgressFile(fileID)
+
+		c.String(http.StatusOK, "Upload complete: %s", finalFilePath)
+	} else {
+		c.String(http.StatusOK, "Chunk %d of %d uploaded", chunkIdx+1, totalChunksNum)
+	}
+}
+
+// 检查该文件的某个块是否已经上传
+func isChunkUploaded(fileID string, chunkIndex int) bool {
+	progressFile := filepath.Join(progressFilePath, fileID+".progress")
+
+	mu.Lock()
+	defer mu.Unlock()
+
+	if _, err := os.Stat(progressFile); os.IsNotExist(err) {
+		return false
+	}
+
+	file, err := os.Open(progressFile)
+	if err != nil {
+		return false
+	}
+	defer file.Close()
+
+	// 读取进度文件中的内容
+	var uploadedChunk int
+	for {
+		_, err := fmt.Fscanf(file, "%d\n", &uploadedChunk)
+		if err != nil {
+			break
+		}
+		if uploadedChunk == chunkIndex {
+			return true
+		}
+	}
+
+	return false
+}
+
+// 记录某个文件的某块已经上传
+func recordChunkProgress(fileID string, chunkIndex int) {
+	progressFile := filepath.Join(progressFilePath, fileID+".progress")
+
+	mu.Lock()
+	defer mu.Unlock()
+
+	file, err := os.OpenFile(progressFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModePerm)
+	if err != nil {
+		fmt.Println("Failed to open progress file:", err)
+		return
+	}
+	defer file.Close()
+
+	_, err = fmt.Fprintf(file, "%d\n", chunkIndex)
+	if err != nil {
+		fmt.Println("Failed to write to progress file:", err)
+	}
+}
+
+// 删除进度文件(在文件上传完成后)
+func deleteProgressFile(fileID string) {
+	progressFile := filepath.Join(progressFilePath, fileID+".progress")
+	mu.Lock()
+	defer mu.Unlock()
+	os.Remove(progressFile)
+}

+ 0 - 1
go.mod

@@ -9,7 +9,6 @@ require (
 	github.com/go-ping/ping v1.1.0
 	github.com/google/wire v0.6.0
 	github.com/lithammer/shortuuid/v3 v3.0.7
-	github.com/lucklucif/ipmigo v0.0.0-20220207161123-b2ae2e5288df
 	github.com/pin/tftp/v3 v3.1.0
 	github.com/spf13/viper v1.19.0
 	github.com/streadway/amqp v1.1.0

+ 0 - 2
go.sum

@@ -263,8 +263,6 @@ 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/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8=
 github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts=
-github.com/lucklucif/ipmigo v0.0.0-20220207161123-b2ae2e5288df h1:CMRy+hqaA7Qyb5YLePQ9MaSa8RsFzhCyrUn5vuCRr1o=
-github.com/lucklucif/ipmigo v0.0.0-20220207161123-b2ae2e5288df/go.mod h1:LoolTif163tJ8JEFud3n1ow7MtfG2yelWQklvVrGNyA=
 github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=

+ 93 - 24
gorm/main.go

@@ -4,6 +4,8 @@ import (
 	"fmt"
 	"gorm.io/driver/mysql"
 	"gorm.io/gorm"
+	"gorm.io/gorm/logger"
+	"time"
 )
 
 // 定义 User 模型
@@ -11,11 +13,24 @@ type User struct {
 	gorm.Model
 	Name  string
 	Email string
+	Test  Test
+}
+
+type Test struct {
+	gorm.Model
+	Name   string
+	UserID *uint
 }
 
 func main() {
 
-	user := User{Name: "John Doe", Email: "john.doe@example.com"}
+	user := User{
+		Name:  "John Doe",
+		Email: "john.doe@example.com",
+		Test: Test{
+			Name: "test",
+		},
+	}
 	Created(&user)
 	// 打印成功创建的消息
 	fmt.Printf("User created with ID: %d\n", user.ID)
@@ -23,35 +38,89 @@ func main() {
 }
 
 func Created(u *User) {
-	dsn := "root:lxz664278@tcp(106.54.33.152:33369)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
-	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
+	dsn := "root:lxz664278@tcp(106.54.33.152:33369)/hstack?charset=utf8mb4&parseTime=True&loc=Local"
+	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
+		Logger: logger.Default.LogMode(logger.Info),
+	})
 	if err != nil {
 		panic("failed to connect database")
 	}
+	info, _ := GetNodePartInfo(&Node{Model: Model{ID: 2}}, *db)
+	fmt.Println(info)
+}
 
-	// 创建记录
-	result := db.Create(u)
+func GetNodePartInfo(node *Node, DBConn gorm.DB) (*Node, error) {
+	var result = new(Node)
 
-	if result.Error != nil {
-		fmt.Println("Error creating user:", result.Error)
-		return
+	tx := DBConn.Model(Node{}).Select([]string{
+		"id", "node_tag",
+		"max_v_cpu", "use_v_cpu",
+		"max_mem", "use_mem",
+		"max_gpu", "use_gpu",
+		"max_vpc", "use_vpc",
+		"stat"}).Where("id=?", node.ID).Find(result)
+	if tx.Error != nil {
+		return nil, tx.Error
 	}
-
-	// 更新记录
-	var t = &User{
-		Model: gorm.Model{
-			ID: u.ID,
-		},
-		Name:  "John Doe Updated",
-		Email: "",
+	if result.ID <= 0 {
+		result = nil
 	}
-	db.Updates(t)
+	return result, nil
+}
 
-	var user User
-	re := db.Model(&User{}).First(&user, u.ID)
-	if re.Error != nil {
-		fmt.Println("Error getting user:", re.Error)
-		return
-	}
-	fmt.Println("User:", user)
+// Node 节点信息
+type Node struct {
+	Model
+	NodeName               string `json:"node_name" gorm:"node_name" `                     // 节点名称
+	ZoneId                 int    `json:"zone_id" gorm:"zone_id" `                         // 所属资源区ID
+	ZoneName               string `json:"zone_name" gorm:"zone_name" `                     // 所属资源区名
+	BmcIP                  string `json:"bmc_ip" gorm:"bmc_ip" `                           // BMC IP
+	BmcUser                string `json:"bmc_user" gorm:"bmc_user" `                       // BMC 用户名
+	BmcPass                string `json:"bmc_pass" gorm:"bmc_pass" `                       // BMC 密码
+	MgrIp                  string `json:"mgr_ip" gorm:"mgr_ip" `                           // 管理IP
+	NodeIp                 string `json:"node_ip" gorm:"node_ip" `                         // 节点IP
+	ListenPort             int    `json:"listen_port" gorm:"listen_port" `                 // 监听端口
+	SSHPort                int    `json:"ssh_port" gorm:"ssh_port" `                       // SSH端口号
+	RootUser               string `json:"root_user" gorm:"root_user" `                     // root用户
+	RootPass               string `json:"root_pass" gorm:"root_pass" `                     // root密码
+	RootSecretKey          string `json:"root_secret_key" gorm:"root_secret_key" `         // Root账号密钥
+	SecretKeyPass          string `json:"secret_key_pass" gorm:"secret_key_pass" `         // 密钥密码
+	NodeMode               int    `json:"node_mode" gorm:"node_mode" `                     // 节点模式
+	NodeTag                string `json:"node_tag" gorm:"node_tag" `                       // 节点标记
+	MaxVCpu                int    `json:"max_v_cpu" gorm:"max_v_cpu" `                     // 最大可虚拟化CPU数量
+	UseVCpu                int    `json:"use_v_cpu" gorm:"use_v_cpu" `                     // 已使用虚拟化CPU数量
+	Cpu                    int    `json:"cpu" gorm:"cpu" `                                 // CPU数量
+	CpuType                int    `json:"cpu_type" gorm:"cpu_type" `                       // CPU类型 (x86_64:1;amd64:2;arm:3;other:4)
+	MaxMem                 int64  `json:"max_mem" gorm:"max_mem" `                         // 最大内存
+	UseMem                 int64  `json:"use_mem" gorm:"use_mem" `                         // 已使用内存
+	MaxGpu                 int    `json:"max_gpu" gorm:"max_gpu" `                         // 最大GPU数
+	UseGpu                 int    `json:"use_gpu" gorm:"use_gpu" `                         // 已使用GPU数
+	NodeWidget             int    `json:"node_widget" gorm:"node_widget" `                 // 节点权重
+	NodeLoadStat           int    `json:"node_load_stat" gorm:"node_load_stat" `           // 节点负载状态
+	MaxVPC                 int    `json:"max_vpc" gorm:"column:max_vpc;default:5" `        // 节点创建的最大VPC数量
+	UseVPC                 int    `json:"use_vpc" gorm:"column:use_vpc;default:0" `        // 节点已创建的VPC
+	RunStat                int    `json:"run_stat" gorm:"run_stat" `                       // 节点运行状态
+	AutoSyncNodeStat       int    `json:"auto_sync_node_stat" gorm:"auto_sync_node_stat" ` // 自动同步节点状态
+	PcieIdList             []int  `json:"pcie_id_list" gorm:"-" `                          // PCIE设备ID列表
+	HPCInitCommandIdList   []int  `json:"hpc_init_command_id_list" gorm:"-" `              // hpc命令ID数组
+	CloudInitCommandIdList []int  `json:"cloud_init_command_id_list" gorm:"-" `            // 云命令ID数组
+}
+type Model struct {
+	ID           int            `json:"id" gorm:"column:id;primarykey" `                                               // 主键ID
+	CreatedAt    *time.Time     `json:"created_at" gorm:"column:created_at;autoCreateTime:nano" swaggerignore:"true" ` // 创建时间
+	UpdatedAt    *time.Time     `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:nano" swaggerignore:"true"`  // 更新时间
+	DeletedAt    gorm.DeletedAt `json:"deleted_at" gorm:"column:deleted_at;index" swaggerignore:"true"`                // 删除时间
+	CreateUser   string         `json:"create_user" gorm:"create_user" swaggerignore:"true"`                           // 创建用户
+	CreateUserId int            `json:"create_user_id" gorm:"create_user_id" swaggerignore:"true"`                     // 创建用户ID
+	UpdateUser   string         `json:"update_user" gorm:"update_user" swaggerignore:"true"`                           // 更新用户
+	UpdateUserId int            `json:"update_user_id" gorm:"update_user_id" swaggerignore:"true"`                     // 更新用户ID
+	DeleteUser   string         `json:"delete_user" gorm:"delete_user" swaggerignore:"true" `                          // 删除用户
+	DeleteUserId int            `json:"delete_user_id" gorm:"delete_user_id" swaggerignore:"true"`                     // 删除用户ID
+	Remark       string         `json:"remark" gorm:"remark" swaggerignore:"true"`                                     // 备注
+	Stat         int            `json:"stat" gorm:"stat" swaggerignore:"true"`                                         // 状态
+	ExpireAt     *time.Time     `json:"expire_at" gorm:"expire_at" swaggerignore:"true"`                               // 过期时间
+}
+
+func (Node) TableName() string {
+	return "node"
 }

+ 142 - 0
ipgen/main.go

@@ -0,0 +1,142 @@
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"net"
+	"strconv"
+	"strings"
+)
+
+func generateIP(startIP, endIP string) (string, error) {
+	start := net.ParseIP(startIP)
+	end := net.ParseIP(endIP)
+
+	if start == nil || end == nil {
+		return "", fmt.Errorf("invalid IP address")
+	}
+
+	ip := incrementIP(start)
+	if ip.Equal(end) {
+		return "", fmt.Errorf("invalid IP address")
+	}
+
+	return ip.String(), nil
+}
+
+func incrementIP(ip net.IP) net.IP {
+
+	ip = ip.To4()
+	if ip == nil {
+		return ip // Return if it's not a valid IPv4 address
+	}
+
+	for i := len(ip) - 1; i >= 0; i-- {
+		if ip[i] < 255 {
+			ip[i]++
+			return ip
+		}
+		ip[i] = 0 // Reset to 0 if it overflows
+	}
+	return ip // Return the incremented IP
+}
+
+// checkIPInRange 检查给定 IP 是否在指定的范围内
+func checkIPInRange(ipStr, startStr, endStr string) (bool, error) {
+	ip := net.ParseIP(ipStr)
+	start := net.ParseIP(startStr)
+	end := net.ParseIP(endStr)
+
+	if ip == nil || start == nil || end == nil {
+		return false, fmt.Errorf("invalid IP address")
+	}
+
+	return bytesCompare(ip, start) >= 0 && bytesCompare(ip, end) <= 0, nil
+}
+
+// bytesCompare 对 IP 进行字节比较
+func bytesCompare(a, b net.IP) int {
+	return bytes.Compare(a, b)
+}
+
+func ipToInt(ip net.IP) int64 {
+	// 将 IP 地址转换为整数
+	return int64(ip[0])<<24 + int64(ip[1])<<16 + int64(ip[2])<<8 + int64(ip[3])
+}
+
+func generateSequentialMAC(start [6]byte, count int) [][6]byte {
+	macAddresses := make([][6]byte, count)
+
+	for i := 0; i < count; i++ {
+		macAddresses[i] = start
+		// 递增MAC地址
+		for j := 5; j >= 0; j-- {
+			if start[j]++; start[j] != 0 {
+				break
+			}
+		}
+	}
+
+	return macAddresses
+}
+
+// parseMACString 将字符串格式的 MAC 地址转换为 [6]byte 类型
+func parseMACString(macStr string) ([6]byte, error) {
+	macParts := strings.Split(macStr, ":")
+	if len(macParts) != 6 {
+		return [6]byte{}, fmt.Errorf("invalid MAC address format: %s", macStr)
+	}
+
+	var mac [6]byte
+	for i, part := range macParts {
+		num, err := strconv.ParseUint(part, 16, 8)
+		if err != nil {
+			return [6]byte{}, fmt.Errorf("invalid MAC address format: %s", macStr)
+		}
+		mac[i] = byte(num)
+	}
+	return mac, nil
+}
+func formatMAC(mac [6]byte) string {
+	return fmt.Sprintf("%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])
+}
+
+func main() {
+	inRange, err := checkIPInRange("192.168.2.123", "192.168.2.123", "192.168.2.254")
+	if err != nil {
+		fmt.Println("Error:", err)
+		return
+	}
+	fmt.Println(inRange)
+}
+
+func NowIP() string {
+	return "192.168.1.13"
+}
+
+func GenerateIP(start, end string) string {
+	//startIp := net.ParseIP(start)
+	//endIp := net.ParseIP(end)
+	//
+	//ip := net.ParseIP(NowIP())
+	return ""
+}
+
+// isValidMACAddressInRange 检查 MAC 地址是否在指定范围内
+func isValidMACAddressInRange(mac [6]byte, networkType int) bool {
+	startMAC, err := parseMACString("23:E0:00:00:00:00")
+	if err != nil {
+		return false
+	}
+	endMAC, err := parseMACString("23:E0:00:00:00:FF")
+	if err != nil {
+		return false
+	}
+
+	for i := 0; i < 6; i++ {
+		if mac[i] < startMAC[i] || mac[i] > endMAC[i] {
+			return false
+		}
+	}
+	return true
+}