// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "fmt" "net" "sort" "strings" "github.com/talos-systems/go-procfs/procfs" "inet.af/netaddr" "github.com/talos-systems/talos/pkg/machinery/constants" ) // CmdlineNetworking contains parsed cmdline networking settings. type CmdlineNetworking struct { DHCP bool Address netaddr.IPPrefix Gateway netaddr.IP Hostname string LinkName string DNSAddresses []netaddr.IP NTPAddresses []netaddr.IP IgnoreInterfaces []string } // ParseCmdlineNetwork parses `ip=` and Talos specific kernel cmdline argument producing all the available configuration options. // //nolint:gocyclo,cyclop func ParseCmdlineNetwork(cmdline *procfs.Cmdline) (CmdlineNetworking, error) { var ( settings CmdlineNetworking err error ) // process Talos specific kernel params cmdlineHostname := cmdline.Get(constants.KernelParamHostname).First() if cmdlineHostname != nil { settings.Hostname = *cmdlineHostname } ignoreInterfaces := cmdline.Get(constants.KernelParamNetworkInterfaceIgnore) for i := 0; ignoreInterfaces.Get(i) != nil; i++ { settings.IgnoreInterfaces = append(settings.IgnoreInterfaces, *ignoreInterfaces.Get(i)) } // standard ip= ipSettings := cmdline.Get("ip").First() if ipSettings == nil { return settings, nil } // https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt // ip=::::::::: fields := strings.Split(*ipSettings, ":") // If dhcp is specified, we'll handle it as a normal discovered // interface if len(fields) == 1 && fields[0] == "dhcp" { settings.DHCP = true } if !settings.DHCP { for i := range fields { if fields[i] == "" { continue } switch i { case 0: var ip netaddr.IP ip, err = netaddr.ParseIP(fields[0]) if err != nil { return settings, fmt.Errorf("cmdline address parse failure: %s", err) } // default is to have complete address masked settings.Address = netaddr.IPPrefixFrom(ip, ip.BitLen()) case 2: settings.Gateway, err = netaddr.ParseIP(fields[2]) if err != nil { return settings, fmt.Errorf("cmdline gateway parse failure: %s", err) } case 3: var netmask netaddr.IP netmask, err = netaddr.ParseIP(fields[3]) if err != nil { return settings, fmt.Errorf("cmdline netmask parse failure: %s", err) } ones, _ := net.IPMask(netmask.IPAddr().IP).Size() settings.Address = netaddr.IPPrefixFrom(settings.Address.IP(), uint8(ones)) case 4: if settings.Hostname == "" { settings.Hostname = fields[4] } case 5: settings.LinkName = fields[5] case 7, 8: var dnsIP netaddr.IP dnsIP, err = netaddr.ParseIP(fields[i]) if err != nil { return settings, fmt.Errorf("error parsing DNS IP: %w", err) } settings.DNSAddresses = append(settings.DNSAddresses, dnsIP) case 9: var ntpIP netaddr.IP ntpIP, err = netaddr.ParseIP(fields[i]) if err != nil { return settings, fmt.Errorf("error parsing DNS IP: %w", err) } settings.NTPAddresses = append(settings.NTPAddresses, ntpIP) } } } // if interface name is not set, pick the first non-loopback interface if settings.LinkName == "" { ifaces, _ := net.Interfaces() //nolint:errcheck // ignoring error here as ifaces will be empty sort.Slice(ifaces, func(i, j int) bool { return ifaces[i].Name < ifaces[j].Name }) for _, iface := range ifaces { if iface.Flags&net.FlagLoopback != 0 { continue } settings.LinkName = iface.Name break } } return settings, nil }