mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-05 12:26:21 +02:00
feat(machined): inherit IP6_METHOD from METHOD in OpenNebula driver
When ETH*_IP6_METHOD is unset, fall back to the value of ETH*_METHOD,
matching the reference [ -z "$ip6_method" ] && ip6_method="${method}"
logic in setup_iface_vars. This means a DHCP interface now also gets a
DHCPv6 operator, a static interface stays static, and a skip interface
remains fully skipped. Update golden testdata to include the DHCPv6
operator that ETH1_METHOD=dhcp now emits.
Signed-off-by: Mickaël Canévet <mickael.canevet@proton.ch>
Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
parent
4f4ec98060
commit
3bec5cc7ba
@ -144,6 +144,47 @@ func TestParseIPv6(t *testing.T) {
|
||||
extra: "ETH0_IP6 = \"2001:db8::1\"",
|
||||
wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")},
|
||||
},
|
||||
{
|
||||
name: "METHOD=dhcp with no IP6_METHOD inherits dhcp and emits OperatorDHCP6",
|
||||
extra: "ETH0_METHOD = \"dhcp\"",
|
||||
wantOperators: []network.OperatorSpecSpec{dhcp6Op(1)},
|
||||
},
|
||||
{
|
||||
name: "METHOD=dhcp with IP6_METHOD=static and IP6 set uses static IPv6",
|
||||
extra: "ETH0_METHOD = \"dhcp\"\nETH0_IP6_METHOD = \"static\"\nETH0_IP6 = \"2001:db8::1\"",
|
||||
wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")},
|
||||
},
|
||||
{
|
||||
name: "METHOD=dhcp with IP6_METHOD=disable emits no IPv6 config",
|
||||
extra: "ETH0_METHOD = \"dhcp\"\nETH0_IP6_METHOD = \"disable\"",
|
||||
},
|
||||
{
|
||||
name: "METHOD=static with no IP6_METHOD and no IP6 emits no IPv6 config",
|
||||
extra: "ETH0_METHOD = \"static\"",
|
||||
},
|
||||
{
|
||||
name: "METRIC=200 with no IP6_METRIC cascades to IPv6 gateway metric",
|
||||
extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_GATEWAY = \"2001:db8::fffe\"\nETH0_METRIC = \"200\"",
|
||||
wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")},
|
||||
wantRoutes: []network.RouteSpecSpec{gw6Route("2001:db8::fffe", 200)},
|
||||
},
|
||||
{
|
||||
name: "METRIC=200 with IP6_METRIC=50 uses explicit IP6_METRIC",
|
||||
extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_GATEWAY = \"2001:db8::fffe\"\nETH0_METRIC = \"200\"\nETH0_IP6_METRIC = \"50\"",
|
||||
wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")},
|
||||
wantRoutes: []network.RouteSpecSpec{gw6Route("2001:db8::fffe", 50)},
|
||||
},
|
||||
{
|
||||
name: "METRIC=200 with IP6_METHOD=dhcp and no IP6_METRIC cascades to DHCPv6 metric",
|
||||
extra: "ETH0_IP6_METHOD = \"dhcp\"\nETH0_METRIC = \"200\"",
|
||||
wantOperators: []network.OperatorSpecSpec{dhcp6Op(200)},
|
||||
},
|
||||
{
|
||||
name: "no METRIC and no IP6_METRIC uses IPv6 default of 1",
|
||||
extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_GATEWAY = \"2001:db8::fffe\"",
|
||||
wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")},
|
||||
wantRoutes: []network.RouteSpecSpec{gw6Route("2001:db8::fffe", 1)},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@ -413,22 +413,53 @@ func parseIPv4StaticConfig(
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseIPv4Metric reads ETH*_METRIC and returns the parsed value, or 0 when
|
||||
// the variable is absent. Callers apply their own default (e.g.
|
||||
// network.DefaultRouteMetric for IPv4, 1 for IPv6 via parseIPv6Metric).
|
||||
func parseIPv4Metric(oneContext map[string]string, ifaceName string) (uint32, error) {
|
||||
if metricStr := oneContext[ifaceName+"_METRIC"]; metricStr != "" {
|
||||
m, err := strconv.ParseUint(metricStr, 10, 32)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("interface %s: failed to parse metric: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
return uint32(m), nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// parseIPv6Metric reads ETH*_IP6_METRIC; falls back to ipv4Metric (when > 0),
|
||||
// then to 1, matching the reference [ -z "$ip6_metric" ] && ip6_metric="${metric}".
|
||||
func parseIPv6Metric(oneContext map[string]string, ifaceName string, ipv4Metric uint32) (uint32, error) {
|
||||
if metricStr := oneContext[ifaceName+"_IP6_METRIC"]; metricStr != "" {
|
||||
m, err := strconv.ParseUint(metricStr, 10, 32)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("interface %s: failed to parse IPv6 metric: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
return uint32(m), nil
|
||||
}
|
||||
|
||||
if ipv4Metric > 0 {
|
||||
return ipv4Metric, nil
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
// parseInterfaceIPv4 configures the IPv4 stack for one interface.
|
||||
// Dispatches to DHCP4 operator or static config based on ETH*_METHOD.
|
||||
func parseInterfaceIPv4(oneContext map[string]string, ifaceName, ifaceNameLower string, networkConfig *runtime.PlatformNetworkConfig, allDNSIPs *[]netip.Addr, allSearchDomains *[]string) error {
|
||||
func parseInterfaceIPv4(
|
||||
oneContext map[string]string, ifaceName, ifaceNameLower string, routeMetric uint32,
|
||||
networkConfig *runtime.PlatformNetworkConfig, allDNSIPs *[]netip.Addr, allSearchDomains *[]string,
|
||||
) error {
|
||||
if oneContext[ifaceName+"_METHOD"] == methodSkip {
|
||||
return nil
|
||||
}
|
||||
|
||||
routeMetric := uint32(network.DefaultRouteMetric)
|
||||
|
||||
if metricStr := oneContext[ifaceName+"_METRIC"]; metricStr != "" {
|
||||
m, err := strconv.ParseUint(metricStr, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("interface %s: failed to parse metric: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
routeMetric = uint32(m)
|
||||
if routeMetric == 0 {
|
||||
routeMetric = uint32(network.DefaultRouteMetric)
|
||||
}
|
||||
|
||||
if oneContext[ifaceName+"_METHOD"] == "dhcp" {
|
||||
@ -475,8 +506,8 @@ func ip6PrefixFrom(ipStr, prefixLenStr string) (netip.Prefix, error) {
|
||||
}
|
||||
|
||||
// parseIPv6Gateway reads ETH*_IP6_GATEWAY (or legacy GATEWAY6) and emits the
|
||||
// default IPv6 route (::/0) with metric from ETH*_IP6_METRIC (default 1).
|
||||
func parseIPv6Gateway(oneContext map[string]string, ifaceName, ifaceNameLower string, networkConfig *runtime.PlatformNetworkConfig) error {
|
||||
// default IPv6 route (::/0) with metric from parseIPv6Metric.
|
||||
func parseIPv6Gateway(oneContext map[string]string, ifaceName, ifaceNameLower string, ipv4Metric uint32, networkConfig *runtime.PlatformNetworkConfig) error {
|
||||
gwStr := oneContext[ifaceName+"_IP6_GATEWAY"]
|
||||
if gwStr == "" {
|
||||
gwStr = oneContext[ifaceName+"_GATEWAY6"]
|
||||
@ -491,15 +522,9 @@ func parseIPv6Gateway(oneContext map[string]string, ifaceName, ifaceNameLower st
|
||||
return fmt.Errorf("interface %s: failed to parse IPv6 gateway %q: %w", ifaceName, gwStr, err)
|
||||
}
|
||||
|
||||
metric := uint32(1)
|
||||
|
||||
if metricStr := oneContext[ifaceName+"_IP6_METRIC"]; metricStr != "" {
|
||||
m, err := strconv.ParseUint(metricStr, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("interface %s: failed to parse IPv6 metric: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
metric = uint32(m)
|
||||
metric, err := parseIPv6Metric(oneContext, ifaceName, ipv4Metric)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
route := network.RouteSpecSpec{
|
||||
@ -521,17 +546,11 @@ func parseIPv6Gateway(oneContext map[string]string, ifaceName, ifaceNameLower st
|
||||
}
|
||||
|
||||
// parseIPv6DHCP emits a DHCPv6 operator for an interface, with metric from
|
||||
// ETH*_IP6_METRIC (default 1).
|
||||
func parseIPv6DHCP(oneContext map[string]string, ifaceName, ifaceNameLower string, networkConfig *runtime.PlatformNetworkConfig) error {
|
||||
metric := uint32(1)
|
||||
|
||||
if metricStr := oneContext[ifaceName+"_IP6_METRIC"]; metricStr != "" {
|
||||
m, err := strconv.ParseUint(metricStr, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("interface %s: failed to parse IPv6 metric: %w", ifaceName, err)
|
||||
}
|
||||
|
||||
metric = uint32(m)
|
||||
// parseIPv6Metric.
|
||||
func parseIPv6DHCP(oneContext map[string]string, ifaceName, ifaceNameLower string, ipv4Metric uint32, networkConfig *runtime.PlatformNetworkConfig) error {
|
||||
metric, err := parseIPv6Metric(oneContext, ifaceName, ipv4Metric)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{
|
||||
@ -549,10 +568,17 @@ func parseIPv6DHCP(oneContext map[string]string, ifaceName, ifaceNameLower strin
|
||||
}
|
||||
|
||||
// parseInterfaceIPv6 configures the IPv6 stack for one interface.
|
||||
// Dispatches on ETH*_IP6_METHOD: disable (skip), auto (SLAAC via kernel),
|
||||
// dhcp (DHCPv6 operator), or static/empty (Phase 2 static path).
|
||||
func parseInterfaceIPv6(oneContext map[string]string, ifaceName, ifaceNameLower string, networkConfig *runtime.PlatformNetworkConfig) error {
|
||||
switch strings.ToLower(oneContext[ifaceName+"_IP6_METHOD"]) {
|
||||
// Dispatches on the effective IP6_METHOD: disable/skip (no-op), auto (SLAAC),
|
||||
// dhcp (DHCPv6 operator), or static/empty (static address path).
|
||||
// When IP6_METHOD is unset, ipv4Method is used as fallback, matching the
|
||||
// reference: [ -z "$ip6_method" ] && ip6_method="${method}".
|
||||
func parseInterfaceIPv6(oneContext map[string]string, ifaceName, ifaceNameLower string, ipv4Method string, ipv4Metric uint32, networkConfig *runtime.PlatformNetworkConfig) error {
|
||||
ip6Method := strings.ToLower(oneContext[ifaceName+"_IP6_METHOD"])
|
||||
if ip6Method == "" {
|
||||
ip6Method = ipv4Method
|
||||
}
|
||||
|
||||
switch ip6Method {
|
||||
case "disable", methodSkip:
|
||||
return nil
|
||||
case "auto":
|
||||
@ -560,7 +586,7 @@ func parseInterfaceIPv6(oneContext map[string]string, ifaceName, ifaceNameLower
|
||||
// no operator or sysctl is required to enable address auto-configuration.
|
||||
return nil
|
||||
case "dhcp":
|
||||
return parseIPv6DHCP(oneContext, ifaceName, ifaceNameLower, networkConfig)
|
||||
return parseIPv6DHCP(oneContext, ifaceName, ifaceNameLower, ipv4Metric, networkConfig)
|
||||
}
|
||||
|
||||
ip6Str := oneContext[ifaceName+"_IP6"]
|
||||
@ -602,23 +628,33 @@ func parseInterfaceIPv6(oneContext map[string]string, ifaceName, ifaceNameLower
|
||||
})
|
||||
}
|
||||
|
||||
return parseIPv6Gateway(oneContext, ifaceName, ifaceNameLower, networkConfig)
|
||||
return parseIPv6Gateway(oneContext, ifaceName, ifaceNameLower, ipv4Metric, networkConfig)
|
||||
}
|
||||
|
||||
// parseInterface runs all per-interface configuration (IPv4, IPv6, aliases).
|
||||
func parseInterface(oneContext map[string]string, ifaceName string, networkConfig *runtime.PlatformNetworkConfig, allDNSIPs *[]netip.Addr, allSearchDomains *[]string) error {
|
||||
ifaceNameLower := strings.ToLower(ifaceName)
|
||||
ipv4Method := strings.ToLower(oneContext[ifaceName+"_METHOD"])
|
||||
|
||||
ip6Method := strings.ToLower(oneContext[ifaceName+"_IP6_METHOD"])
|
||||
if oneContext[ifaceName+"_METHOD"] == methodSkip && (ip6Method == "" || ip6Method == "disable" || ip6Method == methodSkip) {
|
||||
if ip6Method == "" {
|
||||
ip6Method = ipv4Method
|
||||
}
|
||||
|
||||
if ipv4Method == methodSkip && (ip6Method == "" || ip6Method == methodSkip || ip6Method == "disable") {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := parseInterfaceIPv4(oneContext, ifaceName, ifaceNameLower, networkConfig, allDNSIPs, allSearchDomains); err != nil {
|
||||
ipv4Metric, err := parseIPv4Metric(oneContext, ifaceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := parseInterfaceIPv6(oneContext, ifaceName, ifaceNameLower, networkConfig); err != nil {
|
||||
if err := parseInterfaceIPv4(oneContext, ifaceName, ifaceNameLower, ipv4Metric, networkConfig, allDNSIPs, allSearchDomains); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := parseInterfaceIPv6(oneContext, ifaceName, ifaceNameLower, ipv4Method, ipv4Metric, networkConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -79,6 +79,13 @@ operators:
|
||||
routeMetric: 200
|
||||
skipHostnameRequest: true
|
||||
layer: platform
|
||||
- operator: dhcp6
|
||||
linkName: eth1
|
||||
requireUp: true
|
||||
dhcp6:
|
||||
routeMetric: 200
|
||||
skipHostnameRequest: true
|
||||
layer: platform
|
||||
externalIPs: []
|
||||
metadata:
|
||||
platform: opennebula
|
||||
|
||||
@ -79,6 +79,13 @@ operators:
|
||||
routeMetric: 200
|
||||
skipHostnameRequest: true
|
||||
layer: platform
|
||||
- operator: dhcp6
|
||||
linkName: eth1
|
||||
requireUp: true
|
||||
dhcp6:
|
||||
routeMetric: 200
|
||||
skipHostnameRequest: true
|
||||
layer: platform
|
||||
externalIPs: []
|
||||
metadata:
|
||||
platform: opennebula
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user