@@ -413,22 +413,53 @@ func parseIPv4StaticConfig(
413413 return nil
414414}
415415
416- // parseInterfaceIPv4 configures the IPv4 stack for one interface.
417- // Dispatches to DHCP4 operator or static config based on ETH*_METHOD.
418- func parseInterfaceIPv4 (oneContext map [string ]string , ifaceName , ifaceNameLower string , networkConfig * runtime.PlatformNetworkConfig , allDNSIPs * []netip.Addr , allSearchDomains * []string ) error {
419- if oneContext [ifaceName + "_METHOD" ] == methodSkip {
420- return nil
416+ // parseIPv4Metric reads ETH*_METRIC and returns the parsed value, or 0 when
417+ // the variable is absent. Callers apply their own default (e.g.
418+ // network.DefaultRouteMetric for IPv4, 1 for IPv6 via parseIPv6Metric).
419+ func parseIPv4Metric (oneContext map [string ]string , ifaceName string ) (uint32 , error ) {
420+ if metricStr := oneContext [ifaceName + "_METRIC" ]; metricStr != "" {
421+ m , err := strconv .ParseUint (metricStr , 10 , 32 )
422+ if err != nil {
423+ return 0 , fmt .Errorf ("interface %s: failed to parse metric: %w" , ifaceName , err )
424+ }
425+
426+ return uint32 (m ), nil
421427 }
422428
423- routeMetric := uint32 (network .DefaultRouteMetric )
429+ return 0 , nil
430+ }
424431
425- if metricStr := oneContext [ifaceName + "_METRIC" ]; metricStr != "" {
432+ // parseIPv6Metric reads ETH*_IP6_METRIC; falls back to ipv4Metric (when > 0),
433+ // then to 1, matching the reference [ -z "$ip6_metric" ] && ip6_metric="${metric}".
434+ func parseIPv6Metric (oneContext map [string ]string , ifaceName string , ipv4Metric uint32 ) (uint32 , error ) {
435+ if metricStr := oneContext [ifaceName + "_IP6_METRIC" ]; metricStr != "" {
426436 m , err := strconv .ParseUint (metricStr , 10 , 32 )
427437 if err != nil {
428- return fmt .Errorf ("interface %s: failed to parse metric: %w" , ifaceName , err )
438+ return 0 , fmt .Errorf ("interface %s: failed to parse IPv6 metric: %w" , ifaceName , err )
429439 }
430440
431- routeMetric = uint32 (m )
441+ return uint32 (m ), nil
442+ }
443+
444+ if ipv4Metric > 0 {
445+ return ipv4Metric , nil
446+ }
447+
448+ return 1 , nil
449+ }
450+
451+ // parseInterfaceIPv4 configures the IPv4 stack for one interface.
452+ // Dispatches to DHCP4 operator or static config based on ETH*_METHOD.
453+ func parseInterfaceIPv4 (
454+ oneContext map [string ]string , ifaceName , ifaceNameLower string , routeMetric uint32 ,
455+ networkConfig * runtime.PlatformNetworkConfig , allDNSIPs * []netip.Addr , allSearchDomains * []string ,
456+ ) error {
457+ if oneContext [ifaceName + "_METHOD" ] == methodSkip {
458+ return nil
459+ }
460+
461+ if routeMetric == 0 {
462+ routeMetric = uint32 (network .DefaultRouteMetric )
432463 }
433464
434465 if oneContext [ifaceName + "_METHOD" ] == "dhcp" {
@@ -475,8 +506,8 @@ func ip6PrefixFrom(ipStr, prefixLenStr string) (netip.Prefix, error) {
475506}
476507
477508// parseIPv6Gateway reads ETH*_IP6_GATEWAY (or legacy GATEWAY6) and emits the
478- // default IPv6 route (::/0) with metric from ETH*_IP6_METRIC (default 1) .
479- func parseIPv6Gateway (oneContext map [string ]string , ifaceName , ifaceNameLower string , networkConfig * runtime.PlatformNetworkConfig ) error {
509+ // default IPv6 route (::/0) with metric from parseIPv6Metric .
510+ func parseIPv6Gateway (oneContext map [string ]string , ifaceName , ifaceNameLower string , ipv4Metric uint32 , networkConfig * runtime.PlatformNetworkConfig ) error {
480511 gwStr := oneContext [ifaceName + "_IP6_GATEWAY" ]
481512 if gwStr == "" {
482513 gwStr = oneContext [ifaceName + "_GATEWAY6" ]
@@ -491,15 +522,9 @@ func parseIPv6Gateway(oneContext map[string]string, ifaceName, ifaceNameLower st
491522 return fmt .Errorf ("interface %s: failed to parse IPv6 gateway %q: %w" , ifaceName , gwStr , err )
492523 }
493524
494- metric := uint32 (1 )
495-
496- if metricStr := oneContext [ifaceName + "_IP6_METRIC" ]; metricStr != "" {
497- m , err := strconv .ParseUint (metricStr , 10 , 32 )
498- if err != nil {
499- return fmt .Errorf ("interface %s: failed to parse IPv6 metric: %w" , ifaceName , err )
500- }
501-
502- metric = uint32 (m )
525+ metric , err := parseIPv6Metric (oneContext , ifaceName , ipv4Metric )
526+ if err != nil {
527+ return err
503528 }
504529
505530 route := network.RouteSpecSpec {
@@ -521,17 +546,11 @@ func parseIPv6Gateway(oneContext map[string]string, ifaceName, ifaceNameLower st
521546}
522547
523548// parseIPv6DHCP emits a DHCPv6 operator for an interface, with metric from
524- // ETH*_IP6_METRIC (default 1).
525- func parseIPv6DHCP (oneContext map [string ]string , ifaceName , ifaceNameLower string , networkConfig * runtime.PlatformNetworkConfig ) error {
526- metric := uint32 (1 )
527-
528- if metricStr := oneContext [ifaceName + "_IP6_METRIC" ]; metricStr != "" {
529- m , err := strconv .ParseUint (metricStr , 10 , 32 )
530- if err != nil {
531- return fmt .Errorf ("interface %s: failed to parse IPv6 metric: %w" , ifaceName , err )
532- }
533-
534- metric = uint32 (m )
549+ // parseIPv6Metric.
550+ func parseIPv6DHCP (oneContext map [string ]string , ifaceName , ifaceNameLower string , ipv4Metric uint32 , networkConfig * runtime.PlatformNetworkConfig ) error {
551+ metric , err := parseIPv6Metric (oneContext , ifaceName , ipv4Metric )
552+ if err != nil {
553+ return err
535554 }
536555
537556 networkConfig .Operators = append (networkConfig .Operators , network.OperatorSpecSpec {
@@ -549,18 +568,25 @@ func parseIPv6DHCP(oneContext map[string]string, ifaceName, ifaceNameLower strin
549568}
550569
551570// parseInterfaceIPv6 configures the IPv6 stack for one interface.
552- // Dispatches on ETH*_IP6_METHOD: disable (skip), auto (SLAAC via kernel),
553- // dhcp (DHCPv6 operator), or static/empty (Phase 2 static path).
554- func parseInterfaceIPv6 (oneContext map [string ]string , ifaceName , ifaceNameLower string , networkConfig * runtime.PlatformNetworkConfig ) error {
555- switch strings .ToLower (oneContext [ifaceName + "_IP6_METHOD" ]) {
571+ // Dispatches on the effective IP6_METHOD: disable/skip (no-op), auto (SLAAC),
572+ // dhcp (DHCPv6 operator), or static/empty (static address path).
573+ // When IP6_METHOD is unset, ipv4Method is used as fallback, matching the
574+ // reference: [ -z "$ip6_method" ] && ip6_method="${method}".
575+ func parseInterfaceIPv6 (oneContext map [string ]string , ifaceName , ifaceNameLower string , ipv4Method string , ipv4Metric uint32 , networkConfig * runtime.PlatformNetworkConfig ) error {
576+ ip6Method := strings .ToLower (oneContext [ifaceName + "_IP6_METHOD" ])
577+ if ip6Method == "" {
578+ ip6Method = ipv4Method
579+ }
580+
581+ switch ip6Method {
556582 case "disable" , methodSkip :
557583 return nil
558584 case "auto" :
559585 // SLAAC: the kernel accepts Router Advertisements by default in Talos;
560586 // no operator or sysctl is required to enable address auto-configuration.
561587 return nil
562588 case "dhcp" :
563- return parseIPv6DHCP (oneContext , ifaceName , ifaceNameLower , networkConfig )
589+ return parseIPv6DHCP (oneContext , ifaceName , ifaceNameLower , ipv4Metric , networkConfig )
564590 }
565591
566592 ip6Str := oneContext [ifaceName + "_IP6" ]
@@ -602,23 +628,33 @@ func parseInterfaceIPv6(oneContext map[string]string, ifaceName, ifaceNameLower
602628 })
603629 }
604630
605- return parseIPv6Gateway (oneContext , ifaceName , ifaceNameLower , networkConfig )
631+ return parseIPv6Gateway (oneContext , ifaceName , ifaceNameLower , ipv4Metric , networkConfig )
606632}
607633
608634// parseInterface runs all per-interface configuration (IPv4, IPv6, aliases).
609635func parseInterface (oneContext map [string ]string , ifaceName string , networkConfig * runtime.PlatformNetworkConfig , allDNSIPs * []netip.Addr , allSearchDomains * []string ) error {
610636 ifaceNameLower := strings .ToLower (ifaceName )
637+ ipv4Method := strings .ToLower (oneContext [ifaceName + "_METHOD" ])
611638
612639 ip6Method := strings .ToLower (oneContext [ifaceName + "_IP6_METHOD" ])
613- if oneContext [ifaceName + "_METHOD" ] == methodSkip && (ip6Method == "" || ip6Method == "disable" || ip6Method == methodSkip ) {
640+ if ip6Method == "" {
641+ ip6Method = ipv4Method
642+ }
643+
644+ if ipv4Method == methodSkip && (ip6Method == "" || ip6Method == methodSkip || ip6Method == "disable" ) {
614645 return nil
615646 }
616647
617- if err := parseInterfaceIPv4 (oneContext , ifaceName , ifaceNameLower , networkConfig , allDNSIPs , allSearchDomains ); err != nil {
648+ ipv4Metric , err := parseIPv4Metric (oneContext , ifaceName )
649+ if err != nil {
650+ return err
651+ }
652+
653+ if err := parseInterfaceIPv4 (oneContext , ifaceName , ifaceNameLower , ipv4Metric , networkConfig , allDNSIPs , allSearchDomains ); err != nil {
618654 return err
619655 }
620656
621- if err := parseInterfaceIPv6 (oneContext , ifaceName , ifaceNameLower , networkConfig ); err != nil {
657+ if err := parseInterfaceIPv6 (oneContext , ifaceName , ifaceNameLower , ipv4Method , ipv4Metric , networkConfig ); err != nil {
622658 return err
623659 }
624660
0 commit comments