renew dhcp lease on link up

This commit is contained in:
Siyuan 2025-10-07 17:45:44 +00:00
parent 456ee66fc2
commit 50469c1fb6
4 changed files with 69 additions and 21 deletions

View File

@ -7,6 +7,7 @@ import (
"github.com/jetkvm/kvm/internal/network/types" "github.com/jetkvm/kvm/internal/network/types"
"github.com/jetkvm/kvm/pkg/nmlite/jetdhcpc" "github.com/jetkvm/kvm/pkg/nmlite/jetdhcpc"
"github.com/jetkvm/kvm/pkg/nmlite/udhcpc"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
@ -68,17 +69,16 @@ func (dc *DHCPClient) SetOnLeaseChange(callback func(lease *types.DHCPLease)) {
dc.onLeaseChange = callback dc.onLeaseChange = callback
} }
// Start starts the DHCP client func (dc *DHCPClient) initClient() (types.DHCPClient, error) {
func (dc *DHCPClient) Start() error { if false {
if dc.client != nil { return dc.initJetDHCPC()
dc.logger.Warn().Msg("DHCP client already started") } else {
return nil return dc.initUDHCPC()
} }
}
dc.logger.Info().Msg("starting DHCP client") func (dc *DHCPClient) initJetDHCPC() (types.DHCPClient, error) {
return jetdhcpc.NewClient(dc.ctx, []string{dc.ifaceName}, &jetdhcpc.Config{
// Create the underlying DHCP client
client, err := jetdhcpc.NewClient(dc.ctx, []string{dc.ifaceName}, &jetdhcpc.Config{
IPv4: dc.ipv4Enabled, IPv4: dc.ipv4Enabled,
IPv6: dc.ipv6Enabled, IPv6: dc.ipv6Enabled,
OnLease4Change: func(lease *types.DHCPLease) { OnLease4Change: func(lease *types.DHCPLease) {
@ -95,6 +95,31 @@ func (dc *DHCPClient) Start() error {
return nil return nil
}, },
}, dc.logger) }, 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 { if err != nil {
return fmt.Errorf("failed to create DHCP client: %w", err) return fmt.Errorf("failed to create DHCP client: %w", err)

View File

@ -493,6 +493,10 @@ func (im *InterfaceManager) handleLinkUp() {
im.logger.Info().Msg("link up") im.logger.Info().Msg("link up")
im.applyConfiguration() im.applyConfiguration()
if im.config.IPv4Mode.String == "dhcp" {
im.dhcpClient.Renew()
}
} }
func (im *InterfaceManager) handleLinkDown() { func (im *InterfaceManager) handleLinkDown() {

View File

@ -38,6 +38,11 @@ func (l *Lease) ToJSON() string {
return string(json) 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. // SetLeaseExpiry sets the lease expiry time.
func (l *Lease) SetLeaseExpiry() (time.Time, error) { func (l *Lease) SetLeaseExpiry() (time.Time, error) {
if l.Uptime == 0 || l.LeaseTime == 0 { if l.Uptime == 0 || l.LeaseTime == 0 {

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"sync"
"time" "time"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
@ -26,14 +27,15 @@ type DHCPClient struct {
lease *Lease lease *Lease
logger *zerolog.Logger logger *zerolog.Logger
process *os.Process process *os.Process
onLeaseChange func(lease *Lease) runOnce sync.Once
onLeaseChange func(lease *types.DHCPLease)
} }
type DHCPClientOptions struct { type DHCPClientOptions struct {
InterfaceName string InterfaceName string
PidFile string PidFile string
Logger *zerolog.Logger Logger *zerolog.Logger
OnLeaseChange func(lease *Lease) OnLeaseChange func(lease *types.DHCPLease)
} }
var defaultLogger = zerolog.New(os.Stdout).Level(zerolog.InfoLevel) 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. // 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. // this is a blocking call.
func (c *DHCPClient) Run() error { func (c *DHCPClient) run() error {
err := c.loadLeaseFile() err := c.loadLeaseFile()
if err != nil && !errors.Is(err, os.ErrNotExist) { if err != nil && !errors.Is(err, os.ErrNotExist) {
return err return err
@ -127,7 +129,7 @@ func (c *DHCPClient) Run() error {
// c.logger.Error().Msg("udhcpc process not found") // c.logger.Error().Msg("udhcpc process not found")
// } // }
// block the goroutine until the lease file is updated // block the goroutine
<-make(chan struct{}) <-make(chan struct{})
return nil return nil
@ -184,7 +186,7 @@ func (c *DHCPClient) loadLeaseFile() error {
Msg("current dhcp lease expiry time calculated") Msg("current dhcp lease expiry time calculated")
} }
c.onLeaseChange(lease) c.onLeaseChange(lease.ToDHCPLease())
c.logger.Info(). c.logger.Info().
Str("ip", lease.IPAddress.String()). Str("ip", lease.IPAddress.String()).
@ -203,12 +205,16 @@ func (c *DHCPClient) Domain() string {
return c.lease.Domain return c.lease.Domain
} }
func (c *DHCPClient) Lease4() *Lease { func (c *DHCPClient) Lease4() *types.DHCPLease {
return c.lease if c.lease == nil {
return nil
}
return c.lease.ToDHCPLease()
} }
func (c *DHCPClient) Lease6() *Lease { func (c *DHCPClient) Lease6() *types.DHCPLease {
return c.lease // TODO: implement
return nil
} }
func (c *DHCPClient) SetIPv4(enabled bool) { func (c *DHCPClient) SetIPv4(enabled bool) {
@ -219,12 +225,20 @@ func (c *DHCPClient) SetIPv6(enabled bool) {
// TODO: implement // TODO: implement
} }
func (c *DHCPClient) SetOnLeaseChange(callback func(lease *Lease)) { func (c *DHCPClient) SetOnLeaseChange(callback func(lease *types.DHCPLease)) {
c.onLeaseChange = callback c.onLeaseChange = callback
} }
func (c *DHCPClient) Start() error { 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 { func (c *DHCPClient) Stop() error {