mirror of https://github.com/jetkvm/kvm.git
Merge 3418d4bd30 into d24ce1c76f
This commit is contained in:
commit
04c29b31df
|
|
@ -8,12 +8,13 @@ import (
|
|||
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/sync"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/confparser"
|
||||
"github.com/jetkvm/kvm/internal/logging"
|
||||
"github.com/jetkvm/kvm/internal/network/types"
|
||||
"github.com/jetkvm/kvm/internal/sync"
|
||||
|
||||
"github.com/jetkvm/kvm/pkg/nmlite/link"
|
||||
|
||||
"github.com/mdlayher/ndp"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
|
@ -346,11 +347,6 @@ func (im *InterfaceManager) GetConfig() *types.NetworkConfig {
|
|||
return &config
|
||||
}
|
||||
|
||||
// ApplyConfiguration applies the current configuration to the interface
|
||||
func (im *InterfaceManager) ApplyConfiguration() error {
|
||||
return im.applyConfiguration()
|
||||
}
|
||||
|
||||
// SetConfig updates the interface configuration
|
||||
func (im *InterfaceManager) SetConfig(config *types.NetworkConfig) error {
|
||||
if config == nil {
|
||||
|
|
@ -471,7 +467,7 @@ func (im *InterfaceManager) applyIPv4Static() error {
|
|||
return fmt.Errorf("IPv4 static configuration is nil")
|
||||
}
|
||||
|
||||
im.logger.Info().Msg("stopping DHCP")
|
||||
im.logger.Info().Msg("stopping DHCP for IPv4")
|
||||
|
||||
// Disable DHCP
|
||||
if im.dhcpClient != nil {
|
||||
|
|
@ -494,7 +490,7 @@ func (im *InterfaceManager) applyIPv4Static() error {
|
|||
im.logger.Warn().Err(err).Msg("failed to update resolv.conf")
|
||||
}
|
||||
|
||||
return im.ReconcileLinkAddrs(config.Addresses, link.AfInet)
|
||||
return im.ReconcileLinkAddrs(config.Addresses, link.AfInet, link.StaticProtocol)
|
||||
}
|
||||
|
||||
// applyIPv4DHCP applies DHCP IPv4 configuration
|
||||
|
|
@ -545,7 +541,7 @@ func (im *InterfaceManager) applyIPv6Static() error {
|
|||
im.logger.Warn().Err(err).Msg("failed to update resolv.conf")
|
||||
}
|
||||
|
||||
return im.ReconcileLinkAddrs(config.Addresses, link.AfInet6)
|
||||
return im.ReconcileLinkAddrs(config.Addresses, link.AfInet6, link.StaticProtocol)
|
||||
}
|
||||
|
||||
// applyIPv6DHCP applies DHCPv6 configuration
|
||||
|
|
@ -793,7 +789,7 @@ func (im *InterfaceManager) updateStateFromDHCPLease(lease *types.DHCPLease) {
|
|||
}
|
||||
|
||||
// ReconcileLinkAddrs reconciles the link addresses
|
||||
func (im *InterfaceManager) ReconcileLinkAddrs(addrs []types.IPAddress, family int) error {
|
||||
func (im *InterfaceManager) ReconcileLinkAddrs(addrs []types.IPAddress, family int, protocol netlink.RouteProtocol) error {
|
||||
nl := getNetlinkManager()
|
||||
link, err := im.link()
|
||||
if err != nil {
|
||||
|
|
@ -802,7 +798,8 @@ func (im *InterfaceManager) ReconcileLinkAddrs(addrs []types.IPAddress, family i
|
|||
if link == nil {
|
||||
return fmt.Errorf("failed to get interface: %w", err)
|
||||
}
|
||||
return nl.ReconcileLink(link, addrs, family)
|
||||
|
||||
return nl.ReconcileLink(link, addrs, family, protocol)
|
||||
}
|
||||
|
||||
// applyDHCPLease applies DHCP lease configuration using ReconcileLinkAddrs
|
||||
|
|
@ -825,7 +822,7 @@ func (im *InterfaceManager) applyDHCPLease(lease *types.DHCPLease) error {
|
|||
ipv4Config := im.convertDHCPLeaseToIPv4Config(lease)
|
||||
|
||||
// Apply the configuration using ReconcileLinkAddrs
|
||||
return im.ReconcileLinkAddrs([]types.IPAddress{*ipv4Config}, link.AfInet)
|
||||
return im.ReconcileLinkAddrs([]types.IPAddress{*ipv4Config}, link.AfInet, link.DhcpProtocol)
|
||||
}
|
||||
|
||||
// convertDHCPLeaseToIPv4Config converts a DHCP lease to IPv4Config
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import (
|
|||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/network/types"
|
||||
"github.com/jetkvm/kvm/internal/sync"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/network/types"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
|
@ -309,7 +309,7 @@ func (nm *NetlinkManager) RouteReplace(route *netlink.Route) error {
|
|||
func (nm *NetlinkManager) ListDefaultRoutes(family int) ([]netlink.Route, error) {
|
||||
routes, err := netlink.RouteListFiltered(
|
||||
family,
|
||||
&netlink.Route{Dst: nil, Table: 254},
|
||||
&netlink.Route{Dst: nil, Table: MainRoutingTable},
|
||||
netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -330,7 +330,7 @@ func (nm *NetlinkManager) HasDefaultRoute(family int) bool {
|
|||
}
|
||||
|
||||
// AddDefaultRoute adds a default route
|
||||
func (nm *NetlinkManager) AddDefaultRoute(link *Link, gateway net.IP, family int) error {
|
||||
func (nm *NetlinkManager) AddDefaultRoute(link *Link, gateway net.IP, family int, protocol netlink.RouteProtocol) error {
|
||||
var dst *net.IPNet
|
||||
switch family {
|
||||
case AfInet:
|
||||
|
|
@ -345,6 +345,9 @@ func (nm *NetlinkManager) AddDefaultRoute(link *Link, gateway net.IP, family int
|
|||
Dst: dst,
|
||||
Gw: gateway,
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
Table: MainRoutingTable,
|
||||
Protocol: protocol,
|
||||
}
|
||||
|
||||
return nm.RouteReplace(route)
|
||||
|
|
@ -352,21 +355,26 @@ func (nm *NetlinkManager) AddDefaultRoute(link *Link, gateway net.IP, family int
|
|||
|
||||
// RemoveDefaultRoute removes the default route for the given family
|
||||
func (nm *NetlinkManager) RemoveDefaultRoute(family int) error {
|
||||
l := nm.logger.With().Int("family", family).Logger()
|
||||
routes, err := nm.RouteList(nil, family)
|
||||
if err != nil {
|
||||
l.Error().Err(err).Msg("failed to get route list")
|
||||
return fmt.Errorf("failed to get routes: %w", err)
|
||||
}
|
||||
l.Trace().Int("route_count", len(routes)).Msg("checking routes for default route removal")
|
||||
|
||||
for _, route := range routes {
|
||||
if route.Dst != nil {
|
||||
if family == AfInet && route.Dst.IP.Equal(net.IPv4zero) && route.Dst.Mask.String() == "0.0.0.0/0" {
|
||||
l.Trace().Interface("destination", route.Dst).Msg("removing IPv4 default route")
|
||||
if err := nm.RouteDel(&route); err != nil {
|
||||
nm.logger.Warn().Err(err).Msg("failed to remove IPv4 default route")
|
||||
l.Warn().Err(err).Msg("failed to remove IPv4 default route")
|
||||
}
|
||||
}
|
||||
if family == AfInet6 && route.Dst.IP.Equal(net.IPv6zero) && route.Dst.Mask.String() == "::/0" {
|
||||
l.Trace().Interface("destination", route.Dst).Msg("removing IPv6 default route")
|
||||
if err := nm.RouteDel(&route); err != nil {
|
||||
nm.logger.Warn().Err(err).Msg("failed to remove IPv6 default route")
|
||||
l.Warn().Err(err).Msg("failed to remove IPv6 default route")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -375,169 +383,202 @@ func (nm *NetlinkManager) RemoveDefaultRoute(family int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (nm *NetlinkManager) reconcileDefaultRoute(link *Link, expected map[string]net.IP, family int) error {
|
||||
linkIndex := link.Attrs().Index
|
||||
func (nm *NetlinkManager) reconcileDefaultRoutes(link *Link, expected map[string]net.IP, family int, protocol netlink.RouteProtocol) error {
|
||||
linkAttrs := link.Attrs()
|
||||
l := nm.logger.With().Str("interface", linkAttrs.Name).Int("linkIndex", linkAttrs.Index).Int("family", family).Logger()
|
||||
|
||||
added := 0
|
||||
removed := 0
|
||||
toRemove := make([]*netlink.Route, 0)
|
||||
|
||||
defaultRoutes, err := nm.ListDefaultRoutes(family)
|
||||
if err != nil {
|
||||
l.Warn().Err(err).Msg("failed get default routes")
|
||||
return fmt.Errorf("failed to get default routes: %w", err)
|
||||
}
|
||||
l.Debug().Int("defaultRoutes_count", len(defaultRoutes)).Msg("current default routes")
|
||||
|
||||
// check existing default routes
|
||||
for _, defaultRoute := range defaultRoutes {
|
||||
ll := l.With().Interface("defaultRoute", defaultRoute).Logger()
|
||||
|
||||
// only check the default routes for the current link
|
||||
// TODO: we should also check others later
|
||||
if defaultRoute.LinkIndex != linkIndex {
|
||||
if defaultRoute.LinkIndex != linkAttrs.Index {
|
||||
ll.Trace().Msg("wrong link index, skipping")
|
||||
continue
|
||||
}
|
||||
|
||||
key := defaultRoute.Gw.String()
|
||||
ll.Trace().Str("key", key).Msg("checking default route")
|
||||
|
||||
if _, ok := expected[key]; !ok {
|
||||
ll.Debug().Str("key", key).Msg("not in expected routes, marked for removal")
|
||||
toRemove = append(toRemove, &defaultRoute)
|
||||
continue
|
||||
}
|
||||
|
||||
nm.logger.Warn().Str("gateway", key).Msg("keeping default route")
|
||||
l.Debug().Msg("will keep default route")
|
||||
delete(expected, key)
|
||||
}
|
||||
|
||||
// remove remaining default routes
|
||||
for _, defaultRoute := range toRemove {
|
||||
nm.logger.Warn().Str("gateway", defaultRoute.Gw.String()).Msg("removing default route")
|
||||
if err := nm.RouteDel(defaultRoute); err != nil {
|
||||
nm.logger.Warn().Err(err).Msg("failed to remove default route")
|
||||
l.Warn().Err(err).Msg("failed to remove default route")
|
||||
// do not abandon the reconciliation for route removal failure
|
||||
}
|
||||
l.Debug().Stringer("gateway", defaultRoute.Gw).Msg("removed default route")
|
||||
removed++
|
||||
}
|
||||
|
||||
// add remaining expected default routes
|
||||
for _, gateway := range expected {
|
||||
nm.logger.Warn().Str("gateway", gateway.String()).Msg("adding default route")
|
||||
l.Debug().Stringer("gateway", gateway).Msg("adding default route")
|
||||
|
||||
route := &netlink.Route{
|
||||
Dst: &ipv4DefaultRoute,
|
||||
Gw: gateway,
|
||||
LinkIndex: linkIndex,
|
||||
LinkIndex: linkAttrs.Index,
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
Table: MainRoutingTable,
|
||||
Protocol: protocol,
|
||||
}
|
||||
if family == AfInet6 {
|
||||
|
||||
switch family {
|
||||
case AfInet6:
|
||||
route.Dst = &ipv6DefaultRoute
|
||||
case AfInet:
|
||||
route.Dst = &ipv4DefaultRoute
|
||||
}
|
||||
|
||||
if err := nm.RouteAdd(route); err != nil {
|
||||
nm.logger.Warn().Err(err).Interface("route", route).Msg("failed to add default route")
|
||||
l.Warn().Err(err).Interface("route", route).Msg("failed to add default route")
|
||||
// do not abandon the reconciliation for route addition failure
|
||||
continue
|
||||
}
|
||||
l.Debug().IPAddr("gateway", gateway).Msg("added default route")
|
||||
added++
|
||||
}
|
||||
|
||||
nm.logger.Info().
|
||||
Int("added", added).
|
||||
Int("removed", len(toRemove)).
|
||||
Int("removed", removed).
|
||||
Msg("default routes reconciled")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReconcileLink reconciles the addresses and routes of a link
|
||||
func (nm *NetlinkManager) ReconcileLink(link *Link, expected []types.IPAddress, family int) error {
|
||||
func (nm *NetlinkManager) ReconcileLink(link *Link, expected []types.IPAddress, family int, protocol netlink.RouteProtocol) error {
|
||||
l := nm.logger.With().Interface("link", link.Link).Int("family", family).Logger()
|
||||
|
||||
toAdd := make([]*types.IPAddress, 0)
|
||||
toRemove := make([]*netlink.Addr, 0)
|
||||
toUpdate := make([]*types.IPAddress, 0)
|
||||
expectedAddrs := make(map[string]*types.IPAddress)
|
||||
|
||||
expectedGateways := make(map[string]net.IP)
|
||||
|
||||
mtu := link.Attrs().MTU
|
||||
expectedMTU := mtu
|
||||
expectedMTU := 0
|
||||
|
||||
// add all expected addresses to the map
|
||||
for _, addr := range expected {
|
||||
expectedAddrs[addr.String()] = &addr
|
||||
if addr.Gateway != nil {
|
||||
expectedGateways[addr.String()] = addr.Gateway
|
||||
expectedGateways[addr.Gateway.String()] = addr.Gateway
|
||||
}
|
||||
if addr.MTU != 0 {
|
||||
mtu = addr.MTU
|
||||
// we take the smallest MTU among expected addresses
|
||||
if expectedMTU == 0 || addr.MTU < expectedMTU {
|
||||
expectedMTU = addr.MTU
|
||||
}
|
||||
}
|
||||
}
|
||||
if expectedMTU != mtu {
|
||||
|
||||
l.Trace().Int("expected_mtu", expectedMTU).Int("link_mtu", mtu).Msg("computed MTU")
|
||||
if expectedMTU != 0 && expectedMTU != mtu {
|
||||
if err := link.SetMTU(expectedMTU); err != nil {
|
||||
nm.logger.Warn().Err(err).Int("expected_mtu", expectedMTU).Int("mtu", mtu).Msg("failed to set MTU")
|
||||
l.Warn().Err(err).Int("expected_mtu", expectedMTU).Int("current_mtu", mtu).Msg("failed to set MTU")
|
||||
// do not abandon the reconciliation for MTU failure
|
||||
}
|
||||
}
|
||||
|
||||
addrs, err := nm.AddrList(link, family)
|
||||
if err != nil {
|
||||
l.Error().Err(err).Msg("failed to get addresses")
|
||||
return fmt.Errorf("failed to get addresses: %w", err)
|
||||
}
|
||||
l.Debug().Int("address_count", len(addrs)).Msg("current addresses")
|
||||
|
||||
// check existing addresses
|
||||
for _, addr := range addrs {
|
||||
// skip the link-local address
|
||||
if addr.IP.IsLinkLocalUnicast() {
|
||||
l.Trace().Interface("addr", addr).Msg("link lock unicast address, skipping")
|
||||
continue
|
||||
}
|
||||
|
||||
expectedAddr, ok := expectedAddrs[addr.IPNet.String()]
|
||||
key := addr.IPNet.String()
|
||||
expectedAddr, ok := expectedAddrs[key]
|
||||
if !ok {
|
||||
l.Trace().Interface("addr", addr).Str("key", key).Msg("not in expected addresses, marked for removal")
|
||||
toRemove = append(toRemove, &addr)
|
||||
continue
|
||||
}
|
||||
|
||||
// if it's not fully equal, we need to update it
|
||||
if !expectedAddr.Compare(addr) {
|
||||
toUpdate = append(toUpdate, expectedAddr)
|
||||
continue
|
||||
}
|
||||
// found it, so remove it from expected addresses
|
||||
delete(expectedAddrs, key)
|
||||
|
||||
// remove it from expected addresses
|
||||
delete(expectedAddrs, addr.IPNet.String())
|
||||
// if it's not fully equal, we will need to update it
|
||||
if !expectedAddr.Compare(addr) {
|
||||
l.Trace().Interface("addr", addr).Interface("expectedAddr", expectedAddr).Msg("addresses are not equal, marked for update")
|
||||
toUpdate = append(toUpdate, expectedAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// add remaining expected addresses
|
||||
// add remaining unmatched expected addresses
|
||||
for _, addr := range expectedAddrs {
|
||||
l.Trace().Interface("addr", addr).Msg("addresses not found, marked for addition")
|
||||
toAdd = append(toAdd, addr)
|
||||
}
|
||||
|
||||
l.Trace().Int("toAdd_count", len(toAdd)).Int("toRemove_count", len(toRemove)).Int("toUpdate_count", len(toUpdate)).Msg("reconcilliations computed")
|
||||
|
||||
for _, addr := range toUpdate {
|
||||
netlinkAddr := addr.NetlinkAddr()
|
||||
if err := nm.AddrDel(link, &netlinkAddr); err != nil {
|
||||
nm.logger.Warn().Err(err).Str("address", addr.Address.String()).Msg("failed to update address")
|
||||
}
|
||||
// we'll add it again later
|
||||
toAdd = append(toAdd, addr)
|
||||
}
|
||||
|
||||
for _, addr := range toAdd {
|
||||
netlinkAddr := addr.NetlinkAddr()
|
||||
if err := nm.AddrAdd(link, &netlinkAddr); err != nil {
|
||||
nm.logger.Warn().Err(err).Str("address", addr.Address.String()).Msg("failed to add address")
|
||||
l.Warn().Err(err).Stringer("address", netlinkAddr).Msg("failed to remove address for update")
|
||||
}
|
||||
l.Trace().Stringer("address", netlinkAddr).Msg("address removed for update/readdition")
|
||||
toAdd = append(toAdd, addr) // add it back after all the other removals
|
||||
}
|
||||
|
||||
for _, netlinkAddr := range toRemove {
|
||||
if err := nm.AddrDel(link, netlinkAddr); err != nil {
|
||||
nm.logger.Warn().Err(err).Str("address", netlinkAddr.IP.String()).Msg("failed to remove address")
|
||||
l.Warn().Err(err).Stringer("address", netlinkAddr).Msg("failed to remove address")
|
||||
}
|
||||
l.Trace().Stringer("address", netlinkAddr).Msg("removed address")
|
||||
}
|
||||
|
||||
for _, addr := range toAdd {
|
||||
netlinkAddr := addr.NetlinkAddr()
|
||||
if err := nm.AddrAdd(link, &netlinkAddr); err != nil {
|
||||
nm.logger.Warn().Err(err).Str("address", addr.Address.String()).Msg("failed to add address")
|
||||
l.Warn().Err(err).Stringer("address", netlinkAddr).Msg("failed to add address")
|
||||
}
|
||||
l.Trace().Stringer("address", netlinkAddr).Msg("added address")
|
||||
}
|
||||
|
||||
actualToAdd := len(toAdd) - len(toUpdate)
|
||||
if len(toAdd) > 0 || len(toUpdate) > 0 || len(toRemove) > 0 {
|
||||
nm.logger.Info().
|
||||
l.Info().
|
||||
Int("added", actualToAdd).
|
||||
Int("updated", len(toUpdate)).
|
||||
Int("removed", len(toRemove)).
|
||||
Msg("addresses reconciled")
|
||||
}
|
||||
|
||||
if err := nm.reconcileDefaultRoute(link, expectedGateways, family); err != nil {
|
||||
nm.logger.Warn().Err(err).Msg("failed to reconcile default route")
|
||||
if err := nm.reconcileDefaultRoutes(link, expectedGateways, family, protocol); err != nil {
|
||||
l.Warn().Err(err).Msg("failed to reconcile default route")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package link
|
|||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// IPv4Address represents an IPv4 address and its gateway
|
||||
|
|
@ -11,3 +13,9 @@ type IPv4Address struct {
|
|||
Secondary bool
|
||||
Permanent bool
|
||||
}
|
||||
|
||||
const (
|
||||
MainRoutingTable int = 254
|
||||
DhcpProtocol netlink.RouteProtocol = 3
|
||||
StaticProtocol netlink.RouteProtocol = 4
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue