From 50469c1fb69f152d434d74b2ba6b0c2fba6d1820 Mon Sep 17 00:00:00 2001 From: Siyuan Date: Tue, 7 Oct 2025 17:45:44 +0000 Subject: [PATCH] renew dhcp lease on link up --- pkg/nmlite/dhcp.go | 43 +++++++++++++++++++++++++++++-------- pkg/nmlite/interface.go | 4 ++++ pkg/nmlite/udhcpc/parser.go | 5 +++++ pkg/nmlite/udhcpc/udhcpc.go | 38 +++++++++++++++++++++----------- 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/pkg/nmlite/dhcp.go b/pkg/nmlite/dhcp.go index f64d4b80..74e8df28 100644 --- a/pkg/nmlite/dhcp.go +++ b/pkg/nmlite/dhcp.go @@ -7,6 +7,7 @@ import ( "github.com/jetkvm/kvm/internal/network/types" "github.com/jetkvm/kvm/pkg/nmlite/jetdhcpc" + "github.com/jetkvm/kvm/pkg/nmlite/udhcpc" "github.com/rs/zerolog" "github.com/vishvananda/netlink" ) @@ -68,17 +69,16 @@ func (dc *DHCPClient) SetOnLeaseChange(callback func(lease *types.DHCPLease)) { dc.onLeaseChange = callback } -// Start starts the DHCP client -func (dc *DHCPClient) Start() error { - if dc.client != nil { - dc.logger.Warn().Msg("DHCP client already started") - return nil +func (dc *DHCPClient) initClient() (types.DHCPClient, error) { + if false { + return dc.initJetDHCPC() + } else { + return dc.initUDHCPC() } +} - dc.logger.Info().Msg("starting DHCP client") - - // Create the underlying DHCP client - client, err := jetdhcpc.NewClient(dc.ctx, []string{dc.ifaceName}, &jetdhcpc.Config{ +func (dc *DHCPClient) initJetDHCPC() (types.DHCPClient, error) { + return jetdhcpc.NewClient(dc.ctx, []string{dc.ifaceName}, &jetdhcpc.Config{ IPv4: dc.ipv4Enabled, IPv6: dc.ipv6Enabled, OnLease4Change: func(lease *types.DHCPLease) { @@ -95,6 +95,31 @@ func (dc *DHCPClient) Start() error { return nil }, }, dc.logger) +} + +func (dc *DHCPClient) initUDHCPC() (types.DHCPClient, error) { + c := udhcpc.NewDHCPClient(&udhcpc.DHCPClientOptions{ + InterfaceName: dc.ifaceName, + PidFile: "", + Logger: dc.logger, + OnLeaseChange: func(lease *types.DHCPLease) { + dc.handleLeaseChange(lease, false) + }, + }) + return c, nil +} + +// Start starts the DHCP client +func (dc *DHCPClient) Start() error { + if dc.client != nil { + dc.logger.Warn().Msg("DHCP client already started") + return nil + } + + dc.logger.Info().Msg("starting DHCP client") + + // Create the underlying DHCP client + client, err := dc.initClient() if err != nil { return fmt.Errorf("failed to create DHCP client: %w", err) diff --git a/pkg/nmlite/interface.go b/pkg/nmlite/interface.go index 8928ce14..91e1b6f9 100644 --- a/pkg/nmlite/interface.go +++ b/pkg/nmlite/interface.go @@ -493,6 +493,10 @@ func (im *InterfaceManager) handleLinkUp() { im.logger.Info().Msg("link up") im.applyConfiguration() + + if im.config.IPv4Mode.String == "dhcp" { + im.dhcpClient.Renew() + } } func (im *InterfaceManager) handleLinkDown() { diff --git a/pkg/nmlite/udhcpc/parser.go b/pkg/nmlite/udhcpc/parser.go index 0c15b031..0c08ad15 100644 --- a/pkg/nmlite/udhcpc/parser.go +++ b/pkg/nmlite/udhcpc/parser.go @@ -38,6 +38,11 @@ func (l *Lease) ToJSON() string { return string(json) } +// ToDHCPLease converts a lease to a DHCP lease. +func (l *Lease) ToDHCPLease() *types.DHCPLease { + return &l.DHCPLease +} + // SetLeaseExpiry sets the lease expiry time. func (l *Lease) SetLeaseExpiry() (time.Time, error) { if l.Uptime == 0 || l.LeaseTime == 0 { diff --git a/pkg/nmlite/udhcpc/udhcpc.go b/pkg/nmlite/udhcpc/udhcpc.go index 1c6664b5..12e1374d 100644 --- a/pkg/nmlite/udhcpc/udhcpc.go +++ b/pkg/nmlite/udhcpc/udhcpc.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "reflect" + "sync" "time" "github.com/fsnotify/fsnotify" @@ -26,14 +27,15 @@ type DHCPClient struct { lease *Lease logger *zerolog.Logger process *os.Process - onLeaseChange func(lease *Lease) + runOnce sync.Once + onLeaseChange func(lease *types.DHCPLease) } type DHCPClientOptions struct { InterfaceName string PidFile string Logger *zerolog.Logger - OnLeaseChange func(lease *Lease) + OnLeaseChange func(lease *types.DHCPLease) } var defaultLogger = zerolog.New(os.Stdout).Level(zerolog.InfoLevel) @@ -69,8 +71,8 @@ func (c *DHCPClient) getWatchPaths() []string { } // Run starts the DHCP client and watches the lease file for changes. -// this isn't a blocking call, and the lease file is reloaded when a change is detected. -func (c *DHCPClient) Run() error { +// this is a blocking call. +func (c *DHCPClient) run() error { err := c.loadLeaseFile() if err != nil && !errors.Is(err, os.ErrNotExist) { return err @@ -127,7 +129,7 @@ func (c *DHCPClient) Run() error { // c.logger.Error().Msg("udhcpc process not found") // } - // block the goroutine until the lease file is updated + // block the goroutine <-make(chan struct{}) return nil @@ -184,7 +186,7 @@ func (c *DHCPClient) loadLeaseFile() error { Msg("current dhcp lease expiry time calculated") } - c.onLeaseChange(lease) + c.onLeaseChange(lease.ToDHCPLease()) c.logger.Info(). Str("ip", lease.IPAddress.String()). @@ -203,12 +205,16 @@ func (c *DHCPClient) Domain() string { return c.lease.Domain } -func (c *DHCPClient) Lease4() *Lease { - return c.lease +func (c *DHCPClient) Lease4() *types.DHCPLease { + if c.lease == nil { + return nil + } + return c.lease.ToDHCPLease() } -func (c *DHCPClient) Lease6() *Lease { - return c.lease +func (c *DHCPClient) Lease6() *types.DHCPLease { + // TODO: implement + return nil } func (c *DHCPClient) SetIPv4(enabled bool) { @@ -219,12 +225,20 @@ func (c *DHCPClient) SetIPv6(enabled bool) { // TODO: implement } -func (c *DHCPClient) SetOnLeaseChange(callback func(lease *Lease)) { +func (c *DHCPClient) SetOnLeaseChange(callback func(lease *types.DHCPLease)) { c.onLeaseChange = callback } func (c *DHCPClient) Start() error { - return c.Run() // udhcpc already has Run() + c.runOnce.Do(func() { + go func() { + err := c.run() + if err != nil { + c.logger.Error().Err(err).Msg("failed to run udhcpc") + } + }() + }) + return nil } func (c *DHCPClient) Stop() error {