mirror of https://github.com/jetkvm/kvm.git
fix race condition in link manager
This commit is contained in:
parent
8310077af6
commit
f128343187
4
main.go
4
main.go
|
|
@ -33,10 +33,8 @@ func Main() {
|
||||||
go runWatchdog()
|
go runWatchdog()
|
||||||
go confirmCurrentSystem()
|
go confirmCurrentSystem()
|
||||||
|
|
||||||
initNative(systemVersionLocal, appVersionLocal)
|
|
||||||
|
|
||||||
// initialize display
|
|
||||||
initDisplay()
|
initDisplay()
|
||||||
|
initNative(systemVersionLocal, appVersionLocal)
|
||||||
|
|
||||||
http.DefaultClient.Timeout = 1 * time.Minute
|
http.DefaultClient.Timeout = 1 * time.Minute
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,9 @@ func NewInterfaceManager(ctx context.Context, ifaceName string, config *types.Ne
|
||||||
|
|
||||||
// Start starts managing the interface
|
// Start starts managing the interface
|
||||||
func (im *InterfaceManager) Start() error {
|
func (im *InterfaceManager) Start() error {
|
||||||
|
im.stateMu.Lock()
|
||||||
|
defer im.stateMu.Unlock()
|
||||||
|
|
||||||
im.logger.Info().Msg("starting interface manager")
|
im.logger.Info().Msg("starting interface manager")
|
||||||
|
|
||||||
// Start monitoring interface state
|
// Start monitoring interface state
|
||||||
|
|
@ -113,6 +116,22 @@ func (im *InterfaceManager) Start() error {
|
||||||
go im.monitorInterfaceState()
|
go im.monitorInterfaceState()
|
||||||
|
|
||||||
nl := getNetlinkManager()
|
nl := getNetlinkManager()
|
||||||
|
|
||||||
|
// Set the link state
|
||||||
|
linkState, err := nl.GetLinkByName(im.ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get interface: %w", err)
|
||||||
|
}
|
||||||
|
im.linkState = linkState
|
||||||
|
|
||||||
|
// Bring the interface up
|
||||||
|
_, linkUpErr := nl.EnsureInterfaceUpWithTimeout(
|
||||||
|
im.ctx,
|
||||||
|
im.linkState,
|
||||||
|
30*time.Second,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set callback after the interface is up
|
||||||
nl.AddStateChangeCallback(im.ifaceName, link.StateChangeCallback{
|
nl.AddStateChangeCallback(im.ifaceName, link.StateChangeCallback{
|
||||||
Async: true,
|
Async: true,
|
||||||
Func: func(link *link.Link) {
|
Func: func(link *link.Link) {
|
||||||
|
|
@ -120,10 +139,14 @@ func (im *InterfaceManager) Start() error {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Apply initial configuration
|
if linkUpErr != nil {
|
||||||
if err := im.applyConfiguration(); err != nil {
|
im.logger.Error().Err(linkUpErr).Msg("failed to bring interface up, continuing anyway")
|
||||||
im.logger.Error().Err(err).Msg("failed to apply initial configuration")
|
} else {
|
||||||
return err
|
// Apply initial configuration
|
||||||
|
if err := im.applyConfiguration(); err != nil {
|
||||||
|
im.logger.Error().Err(err).Msg("failed to apply initial configuration")
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
im.logger.Info().Msg("interface manager started")
|
im.logger.Info().Msg("interface manager started")
|
||||||
|
|
@ -451,12 +474,18 @@ func (im *InterfaceManager) applyIPv6SLAAC() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
netlinkMgr := getNetlinkManager()
|
netlinkMgr := getNetlinkManager()
|
||||||
|
|
||||||
|
// Ensure interface is up
|
||||||
|
if err := netlinkMgr.EnsureInterfaceUp(l); err != nil {
|
||||||
|
return fmt.Errorf("failed to bring interface up: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := netlinkMgr.RemoveNonLinkLocalIPv6Addresses(l); err != nil {
|
if err := netlinkMgr.RemoveNonLinkLocalIPv6Addresses(l); err != nil {
|
||||||
return fmt.Errorf("failed to remove non-link-local IPv6 addresses: %w", err)
|
return fmt.Errorf("failed to remove non-link-local IPv6 addresses: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := im.SendRouterSolicitation(); err != nil {
|
if err := im.SendRouterSolicitation(); err != nil {
|
||||||
return fmt.Errorf("failed to send router solicitation: %w", err)
|
im.logger.Error().Err(err).Msg("failed to send router solicitation, continuing anyway")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable SLAAC
|
// Enable SLAAC
|
||||||
|
|
@ -561,6 +590,10 @@ func (im *InterfaceManager) SendRouterSolicitation() error {
|
||||||
return fmt.Errorf("failed to get interface: %w", err)
|
return fmt.Errorf("failed to get interface: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if l.Attrs().OperState != netlink.OperUp {
|
||||||
|
return fmt.Errorf("interface %s is not up", im.ifaceName)
|
||||||
|
}
|
||||||
|
|
||||||
iface := l.Interface()
|
iface := l.Interface()
|
||||||
if iface == nil {
|
if iface == nil {
|
||||||
return fmt.Errorf("failed to get net.Interface for %s", im.ifaceName)
|
return fmt.Errorf("failed to get net.Interface for %s", im.ifaceName)
|
||||||
|
|
@ -572,7 +605,6 @@ func (im *InterfaceManager) SendRouterSolicitation() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
c, _, err := ndp.Listen(iface, ndp.LinkLocal)
|
c, _, err := ndp.Listen(iface, ndp.LinkLocal)
|
||||||
defer c.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create NDP listener on %s: %w", im.ifaceName, err)
|
return fmt.Errorf("failed to create NDP listener on %s: %w", im.ifaceName, err)
|
||||||
}
|
}
|
||||||
|
|
@ -585,10 +617,12 @@ func (im *InterfaceManager) SendRouterSolicitation() error {
|
||||||
targetAddr := netip.MustParseAddr("ff02::2")
|
targetAddr := netip.MustParseAddr("ff02::2")
|
||||||
|
|
||||||
if err := c.WriteTo(m, nil, targetAddr); err != nil {
|
if err := c.WriteTo(m, nil, targetAddr); err != nil {
|
||||||
|
c.Close()
|
||||||
return fmt.Errorf("failed to write to %s: %w", targetAddr.String(), err)
|
return fmt.Errorf("failed to write to %s: %w", targetAddr.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
im.logger.Info().Msg("router solicitation sent")
|
im.logger.Info().Msg("router solicitation sent")
|
||||||
|
c.Close()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package jetdhcpc
|
package jetdhcpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||||
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
|
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
@ -46,7 +48,11 @@ func (c *Client) requestLease4(iface netlink.Link) (*Lease, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Info().Msgf("DHCPv4 lease acquired: %s", lease.ACK.Summary())
|
if lease == nil || lease.ACK == nil {
|
||||||
|
return nil, fmt.Errorf("failed to acquire DHCPv4 lease")
|
||||||
|
}
|
||||||
|
|
||||||
|
summaryStructured(lease.ACK, &l).Info().Msgf("DHCPv4 lease acquired: %s", lease.ACK.String())
|
||||||
|
|
||||||
return fromNclient4Lease(lease, ifname), nil
|
return fromNclient4Lease(lease, ifname), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,35 @@ func (s dhcpLogger) Printf(format string, v ...interface{}) {
|
||||||
|
|
||||||
// PrintMessage prints a DHCP message in the short format via predefined Printfer
|
// PrintMessage prints a DHCP message in the short format via predefined Printfer
|
||||||
func (s dhcpLogger) PrintMessage(prefix string, message *dhcpv4.DHCPv4) {
|
func (s dhcpLogger) PrintMessage(prefix string, message *dhcpv4.DHCPv4) {
|
||||||
s.l.Info().Str("prefix", prefix).Str("message", message.String()).Msg("DHCP message")
|
s.l.Info().Msgf("%s: %s", prefix, message.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func summaryStructured(d *dhcpv4.DHCPv4, l *zerolog.Logger) *zerolog.Logger {
|
||||||
|
logger := l.With().
|
||||||
|
Str("opCode", d.OpCode.String()).
|
||||||
|
Str("hwType", d.HWType.String()).
|
||||||
|
Int("hopCount", int(d.HopCount)).
|
||||||
|
Str("transactionID", d.TransactionID.String()).
|
||||||
|
Int("numSeconds", int(d.NumSeconds)).
|
||||||
|
Str("flagsString", d.FlagsToString()).
|
||||||
|
Int("flags", int(d.Flags)).
|
||||||
|
Str("clientIP", d.ClientIPAddr.String()).
|
||||||
|
Str("yourIP", d.YourIPAddr.String()).
|
||||||
|
Str("serverIP", d.ServerIPAddr.String()).
|
||||||
|
Str("gatewayIP", d.GatewayIPAddr.String()).
|
||||||
|
Str("clientMAC", d.ClientHWAddr.String()).
|
||||||
|
Str("serverHostname", d.ServerHostName).
|
||||||
|
Str("bootFileName", d.BootFileName).
|
||||||
|
Str("options", d.Options.Summary(nil)).
|
||||||
|
Logger()
|
||||||
|
return &logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getDHCP4Logger(ifname string) nclient4.ClientOpt {
|
func (c *Client) getDHCP4Logger(ifname string) nclient4.ClientOpt {
|
||||||
logger := c.l.With().Str("interface", ifname).Logger()
|
logger := c.l.With().
|
||||||
|
Str("interface", ifname).
|
||||||
|
Str("source", "dhcp4").
|
||||||
|
Logger()
|
||||||
|
|
||||||
return nclient4.WithLogger(dhcpLogger{
|
return nclient4.WithLogger(dhcpLogger{
|
||||||
l: &logger,
|
l: &logger,
|
||||||
|
|
|
||||||
|
|
@ -163,22 +163,40 @@ func (nm *NetlinkManager) EnsureInterfaceUpWithTimeout(ctx context.Context, ifac
|
||||||
}
|
}
|
||||||
|
|
||||||
state := link.Attrs().OperState
|
state := link.Attrs().OperState
|
||||||
|
|
||||||
|
l = l.With().
|
||||||
|
Int("attempt", attempt).
|
||||||
|
Dur("duration", time.Since(start)).
|
||||||
|
Str("state", state.String()).
|
||||||
|
Logger()
|
||||||
if state == netlink.OperUp || state == netlink.OperUnknown {
|
if state == netlink.OperUp || state == netlink.OperUnknown {
|
||||||
|
if attempt > 0 {
|
||||||
|
l.Info().Int("attempt", attempt-1).Msg("interface is up")
|
||||||
|
}
|
||||||
return link, nil
|
return link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Info().Str("state", state.String()).Msg("bringing up interface")
|
l.Info().Msg("bringing up interface")
|
||||||
|
|
||||||
|
// bring up the interface
|
||||||
if err = nm.LinkSetUp(link); err != nil {
|
if err = nm.LinkSetUp(link); err != nil {
|
||||||
l.Error().Err(err).Msg("interface can't make it up")
|
l.Error().Err(err).Msg("interface can't make it up")
|
||||||
}
|
}
|
||||||
|
|
||||||
l = l.With().Int("attempt", attempt).Dur("duration", time.Since(start)).Logger()
|
// refresh the link attributes
|
||||||
|
if err = link.Refresh(); err != nil {
|
||||||
if attempt > 0 {
|
l.Error().Err(err).Msg("failed to refresh link attributes")
|
||||||
l.Info().Msg("interface up")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check the state again
|
||||||
|
state = link.Attrs().OperState
|
||||||
|
l = l.With().Str("new_state", state.String()).Logger()
|
||||||
|
if state == netlink.OperUp {
|
||||||
|
l.Info().Msg("interface is up")
|
||||||
|
return link, nil
|
||||||
|
}
|
||||||
|
l.Warn().Msg("interface is still down, retrying")
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-time.After(500 * time.Millisecond):
|
case <-time.After(500 * time.Millisecond):
|
||||||
attempt++
|
attempt++
|
||||||
|
|
@ -381,24 +399,29 @@ func (nm *NetlinkManager) ReconcileLink(link *Link, expected []*types.IPAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
ipCidr := addr.Address.IP.String() + "/" + addr.Address.Mask.String()
|
ipCidr := addr.Address.IP.String() + "/" + addr.Address.Mask.String()
|
||||||
|
ipNet := &net.IPNet{
|
||||||
|
IP: addr.Address.IP,
|
||||||
|
Mask: addr.Address.Mask,
|
||||||
|
}
|
||||||
|
|
||||||
|
l := nm.logger.With().Str("address", ipNet.String()).Logger()
|
||||||
if ok := existingAddrs[ipCidr]; !ok {
|
if ok := existingAddrs[ipCidr]; !ok {
|
||||||
ipNet := &net.IPNet{
|
l.Trace().Msg("adding address")
|
||||||
IP: addr.Address.IP,
|
|
||||||
Mask: addr.Address.Mask,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := nm.AddrAdd(link, &netlink.Addr{IPNet: ipNet}); err != nil {
|
if err := nm.AddrAdd(link, &netlink.Addr{IPNet: ipNet}); err != nil {
|
||||||
return fmt.Errorf("failed to add address %s: %w", ipCidr, err)
|
return fmt.Errorf("failed to add address %s: %w", ipCidr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nm.logger.Info().Str("address", ipCidr).Msg("added address")
|
l.Info().Msg("address added")
|
||||||
}
|
}
|
||||||
|
|
||||||
if addr.Gateway != nil {
|
if addr.Gateway != nil {
|
||||||
nm.logger.Trace().Str("address", ipCidr).Str("gateway", addr.Gateway.String()).Msg("adding default route for address")
|
gl := l.With().Str("gateway", addr.Gateway.String()).Logger()
|
||||||
|
gl.Trace().Msg("adding default route")
|
||||||
if err := nm.AddDefaultRoute(link, addr.Gateway, family); err != nil {
|
if err := nm.AddDefaultRoute(link, addr.Gateway, family); err != nil {
|
||||||
return fmt.Errorf("failed to add default route for address %s: %w", ipCidr, err)
|
return fmt.Errorf("failed to add default route for address %s: %w", ipCidr, err)
|
||||||
}
|
}
|
||||||
|
gl.Info().Msg("default route added")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,14 @@ var (
|
||||||
// Link is a wrapper around netlink.Link
|
// Link is a wrapper around netlink.Link
|
||||||
type Link struct {
|
type Link struct {
|
||||||
netlink.Link
|
netlink.Link
|
||||||
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh refreshes the link
|
// All lock actions should be done in external functions
|
||||||
func (l *Link) Refresh() error {
|
// and the internal functions should not be called directly
|
||||||
linkName := l.Link.Attrs().Name
|
|
||||||
|
func (l *Link) refresh() error {
|
||||||
|
linkName := l.ifName()
|
||||||
link, err := netlink.LinkByName(linkName)
|
link, err := netlink.LinkByName(linkName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -51,13 +54,44 @@ func (l *Link) Refresh() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Link) attrs() *netlink.LinkAttrs {
|
||||||
|
return l.Link.Attrs()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Link) ifName() string {
|
||||||
|
attrs := l.attrs()
|
||||||
|
if attrs.Name == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return attrs.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh refreshes the link
|
||||||
|
func (l *Link) Refresh() error {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
return l.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attrs returns the attributes of the link
|
||||||
|
func (l *Link) Attrs() *netlink.LinkAttrs {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
return l.attrs()
|
||||||
|
}
|
||||||
|
|
||||||
// Interface returns the interface of the link
|
// Interface returns the interface of the link
|
||||||
func (l *Link) Interface() *net.Interface {
|
func (l *Link) Interface() *net.Interface {
|
||||||
attrs := l.Attrs()
|
l.mu.Lock()
|
||||||
if attrs.Name == "" {
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
ifname := l.ifName()
|
||||||
|
if ifname == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
iface, err := net.InterfaceByName(attrs.Name)
|
iface, err := net.InterfaceByName(ifname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -66,20 +100,22 @@ func (l *Link) Interface() *net.Interface {
|
||||||
|
|
||||||
// HardwareAddr returns the hardware address of the link
|
// HardwareAddr returns the hardware address of the link
|
||||||
func (l *Link) HardwareAddr() net.HardwareAddr {
|
func (l *Link) HardwareAddr() net.HardwareAddr {
|
||||||
attrs := l.Attrs()
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
attrs := l.attrs()
|
||||||
if attrs.HardwareAddr == nil {
|
if attrs.HardwareAddr == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return attrs.HardwareAddr
|
return attrs.HardwareAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attrs returns the attributes of the link
|
// AddrList returns the addresses of the link
|
||||||
func (l *Link) Attrs() *netlink.LinkAttrs {
|
|
||||||
return l.Link.Attrs()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Link) AddrList(family int) ([]netlink.Addr, error) {
|
func (l *Link) AddrList(family int) ([]netlink.Addr, error) {
|
||||||
return netlink.AddrList(l, family)
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
return netlink.AddrList(l.Link, family)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Link) IsSame(other *Link) bool {
|
func (l *Link) IsSame(other *Link) bool {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue