def _render_subnets(cls, iface_cfg, subnets, has_default_route, flavor): # setting base values if flavor == "suse": iface_cfg["BOOTPROTO"] = "static" if "BRIDGE" in iface_cfg: iface_cfg["BOOTPROTO"] = "dhcp" iface_cfg.drop("BRIDGE") else: iface_cfg["BOOTPROTO"] = "none" # modifying base values according to subnets for i, subnet in enumerate(subnets, start=len(iface_cfg.children)): mtu_key = "MTU" subnet_type = subnet.get("type") if subnet_type == "dhcp6" or subnet_type == "ipv6_dhcpv6-stateful": if flavor == "suse": # User wants dhcp for both protocols if iface_cfg["BOOTPROTO"] == "dhcp4": iface_cfg["BOOTPROTO"] = "dhcp" else: # Only IPv6 is DHCP, IPv4 may be static iface_cfg["BOOTPROTO"] = "dhcp6" iface_cfg["DHCLIENT6_MODE"] = "managed" # only if rhel AND dhcpv6 stateful elif (flavor == "rhel" and subnet_type == "ipv6_dhcpv6-stateful"): iface_cfg["BOOTPROTO"] = "dhcp" iface_cfg["DHCPV6C"] = True iface_cfg["IPV6INIT"] = True iface_cfg["IPV6_AUTOCONF"] = False else: iface_cfg["IPV6INIT"] = True # Configure network settings using DHCPv6 iface_cfg["DHCPV6C"] = True elif subnet_type == "ipv6_dhcpv6-stateless": if flavor == "suse": # User wants dhcp for both protocols if iface_cfg["BOOTPROTO"] == "dhcp4": iface_cfg["BOOTPROTO"] = "dhcp" else: # Only IPv6 is DHCP, IPv4 may be static iface_cfg["BOOTPROTO"] = "dhcp6" iface_cfg["DHCLIENT6_MODE"] = "info" else: iface_cfg["IPV6INIT"] = True # Configure network settings using SLAAC from RAs and # optional info from dhcp server using DHCPv6 iface_cfg["IPV6_AUTOCONF"] = True iface_cfg["DHCPV6C"] = True # Use Information-request to get only stateless # configuration parameters (i.e., without address). iface_cfg["DHCPV6C_OPTIONS"] = "-S" elif subnet_type == "ipv6_slaac": if flavor == "suse": # User wants dhcp for both protocols if iface_cfg["BOOTPROTO"] == "dhcp4": iface_cfg["BOOTPROTO"] = "dhcp" else: # Only IPv6 is DHCP, IPv4 may be static iface_cfg["BOOTPROTO"] = "dhcp6" iface_cfg["DHCLIENT6_MODE"] = "info" else: iface_cfg["IPV6INIT"] = True # Configure network settings using SLAAC from RAs iface_cfg["IPV6_AUTOCONF"] = True elif subnet_type in ["dhcp4", "dhcp"]: bootproto_in = iface_cfg["BOOTPROTO"] iface_cfg["BOOTPROTO"] = "dhcp" if flavor == "suse" and subnet_type == "dhcp4": # If dhcp6 is already specified the user wants dhcp # for both protocols if bootproto_in != "dhcp6": # Only IPv4 is DHCP, IPv6 may be static iface_cfg["BOOTPROTO"] = "dhcp4" elif subnet_type in ["static", "static6"]: # RH info # grep BOOTPROTO sysconfig.txt -A2 | head -3 # BOOTPROTO=none|bootp|dhcp # 'bootp' or 'dhcp' cause a DHCP client # to run on the device. Any other # value causes any static configuration # in the file to be applied. if subnet_is_ipv6(subnet) and flavor != "suse": mtu_key = "IPV6_MTU" iface_cfg["IPV6INIT"] = True if "mtu" in subnet: mtu_mismatch = bool( mtu_key in iface_cfg and subnet["mtu"] != iface_cfg[mtu_key]) if mtu_mismatch: LOG.warning( "Network config: ignoring %s device-level mtu:%s" " because ipv4 subnet-level mtu:%s provided.", iface_cfg.name, iface_cfg[mtu_key], subnet["mtu"], ) if subnet_is_ipv6(subnet): if flavor == "suse": # TODO(rjschwei) write mtu setting to # /etc/sysctl.d/ pass else: iface_cfg[mtu_key] = subnet["mtu"] else: iface_cfg[mtu_key] = subnet["mtu"] if subnet_is_ipv6(subnet) and flavor == "rhel": iface_cfg["IPV6_FORCE_ACCEPT_RA"] = False iface_cfg["IPV6_AUTOCONF"] = False elif subnet_type == "manual": if flavor == "suse": LOG.debug('Unknown subnet type setting "%s"', subnet_type) else: # If the subnet has an MTU setting, then ONBOOT=True # to apply the setting iface_cfg["ONBOOT"] = mtu_key in iface_cfg else: raise ValueError( "Unknown subnet type '%s' found for interface '%s'" % (subnet_type, iface_cfg.name)) if subnet.get("control") == "manual": if flavor == "suse": iface_cfg["STARTMODE"] = "manual" else: iface_cfg["ONBOOT"] = False # set IPv4 and IPv6 static addresses ipv4_index = -1 ipv6_index = -1 for i, subnet in enumerate(subnets, start=len(iface_cfg.children)): subnet_type = subnet.get("type") # metric may apply to both dhcp and static config if "metric" in subnet: if flavor != "suse": iface_cfg["METRIC"] = subnet["metric"] if subnet_type in ["dhcp", "dhcp4"]: # On SUSE distros 'DHCLIENT_SET_DEFAULT_ROUTE' is a global # setting in /etc/sysconfig/network/dhcp if flavor != "suse": if has_default_route and iface_cfg["BOOTPROTO"] != "none": iface_cfg["DHCLIENT_SET_DEFAULT_ROUTE"] = False continue elif subnet_type in IPV6_DYNAMIC_TYPES: continue elif subnet_type in ["static", "static6"]: if subnet_is_ipv6(subnet): ipv6_index = ipv6_index + 1 ipv6_cidr = "%s/%s" % (subnet["address"], subnet["prefix"]) if ipv6_index == 0: if flavor == "suse": iface_cfg["IPADDR6"] = ipv6_cidr else: iface_cfg["IPV6ADDR"] = ipv6_cidr elif ipv6_index == 1: if flavor == "suse": iface_cfg["IPADDR6_1"] = ipv6_cidr else: iface_cfg["IPV6ADDR_SECONDARIES"] = ipv6_cidr else: if flavor == "suse": iface_cfg["IPADDR6_%d" % ipv6_index] = ipv6_cidr else: iface_cfg["IPV6ADDR_SECONDARIES"] += (" " + ipv6_cidr) else: ipv4_index = ipv4_index + 1 suff = "" if ipv4_index == 0 else str(ipv4_index) iface_cfg["IPADDR" + suff] = subnet["address"] iface_cfg["NETMASK" + suff] = net_prefix_to_ipv4_mask( subnet["prefix"]) if "gateway" in subnet and flavor != "suse": iface_cfg["DEFROUTE"] = True if is_ipv6_address(subnet["gateway"]): iface_cfg["IPV6_DEFAULTGW"] = subnet["gateway"] else: iface_cfg["GATEWAY"] = subnet["gateway"] if "dns_search" in subnet and flavor != "suse": iface_cfg["DOMAIN"] = " ".join(subnet["dns_search"]) if "dns_nameservers" in subnet and flavor != "suse": if len(subnet["dns_nameservers"]) > 3: # per resolv.conf(5) MAXNS sets this to 3. LOG.debug( "%s has %d entries in dns_nameservers. " "Only 3 are used.", iface_cfg.name, len(subnet["dns_nameservers"]), ) for i, k in enumerate(subnet["dns_nameservers"][:3], 1): iface_cfg["DNS" + str(i)] = k
def translate_network(settings): # Get the standard cmd, args from the ubuntu format entries = [] for line in settings.splitlines(): line = line.strip() if not line or line.startswith("#"): continue split_up = line.split(None, 1) if len(split_up) <= 1: continue entries.append(split_up) # Figure out where each iface section is ifaces = [] consume = {} for (cmd, args) in entries: if cmd == "iface": if consume: ifaces.append(consume) consume = {} consume[cmd] = args else: consume[cmd] = args # Check if anything left over to consume absorb = False for (cmd, args) in consume.items(): if cmd == "iface": absorb = True if absorb: ifaces.append(consume) # Now translate real_ifaces = {} for info in ifaces: if "iface" not in info: continue iface_details = info["iface"].split(None) # Check if current device *may* have an ipv6 IP use_ipv6 = False if "inet6" in iface_details: use_ipv6 = True dev_name = None if len(iface_details) >= 1: dev = iface_details[0].strip().lower() if dev: dev_name = dev if not dev_name: continue iface_info = {} iface_info["ipv6"] = {} if len(iface_details) >= 3: proto_type = iface_details[2].strip().lower() # Seems like this can be 'loopback' which we don't # really care about if proto_type in ["dhcp", "static"]: iface_info["bootproto"] = proto_type # These can just be copied over if use_ipv6: for k in ["address", "gateway"]: if k in info: val = info[k].strip().lower() if val: iface_info["ipv6"][k] = val else: for k in ["netmask", "address", "gateway", "broadcast"]: if k in info: val = info[k].strip().lower() if val: iface_info[k] = val # handle static ip configurations using # ipaddress/prefix-length format if "address" in iface_info: if "netmask" not in iface_info: # check if the address has a network prefix addr, _, prefix = iface_info["address"].partition("/") if prefix: iface_info["netmask"] = net_prefix_to_ipv4_mask(prefix) iface_info["address"] = addr # if we set the netmask, we also can set the broadcast iface_info["broadcast"] = mask_and_ipv4_to_bcast_addr( iface_info["netmask"], addr) # Name server info provided?? if "dns-nameservers" in info: iface_info["dns-nameservers"] = info["dns-nameservers"].split() # Name server search info provided?? if "dns-search" in info: iface_info["dns-search"] = info["dns-search"].split() # Is any mac address spoofing going on?? if "hwaddress" in info: hw_info = info["hwaddress"].lower().strip() hw_split = hw_info.split(None, 1) if len(hw_split) == 2 and hw_split[0].startswith("ether"): hw_addr = hw_split[1] if hw_addr: iface_info["hwaddress"] = hw_addr # If ipv6 is enabled, device will have multiple IPs, so we need to # update the dictionary instead of overwriting it... if dev_name in real_ifaces: real_ifaces[dev_name].update(iface_info) else: real_ifaces[dev_name] = iface_info # Check for those that should be started on boot via 'auto' for (cmd, args) in entries: args = args.split(None) if not args: continue dev_name = args[0].strip().lower() if cmd == "auto": # Seems like auto can be like 'auto eth0 eth0:1' so just get the # first part out as the device name if dev_name in real_ifaces: real_ifaces[dev_name]["auto"] = True if cmd == "iface" and "inet6" in args: real_ifaces[dev_name]["inet6"] = True return real_ifaces
def _normalize_net_keys(network, address_keys=()): """Normalize dictionary network keys returning prefix and address keys. @param network: A dict of network-related definition containing prefix, netmask and address_keys. @param address_keys: A tuple of keys to search for representing the address or cidr. The first address_key discovered will be used for normalization. @returns: A dict containing normalized prefix and matching addr_key. """ net = dict((k, v) for k, v in network.items() if v) addr_key = None for key in address_keys: if net.get(key): addr_key = key break if not addr_key: message = "No config network address keys [%s] found in %s" % ( ",".join(address_keys), network, ) LOG.error(message) raise ValueError(message) addr = str(net.get(addr_key)) if not is_ip_network(addr): LOG.error("Address %s is not a valid ip network", addr) raise ValueError(f"Address {addr} is not a valid ip address") ipv6 = is_ipv6_network(addr) ipv4 = is_ipv4_network(addr) netmask = net.get("netmask") if "/" in addr: addr_part, _, maybe_prefix = addr.partition("/") net[addr_key] = addr_part if ipv6: # this supports input of ffff:ffff:ffff:: prefix = ipv6_mask_to_net_prefix(maybe_prefix) elif ipv4: # this supports input of 255.255.255.0 prefix = ipv4_mask_to_net_prefix(maybe_prefix) else: # In theory this never happens, is_ip_network() should catch all # invalid networks LOG.error("Address %s is not a valid ip network", addr) raise ValueError(f"Address {addr} is not a valid ip address") elif "prefix" in net: prefix = int(net["prefix"]) elif netmask and ipv4: prefix = ipv4_mask_to_net_prefix(netmask) elif netmask and ipv6: prefix = ipv6_mask_to_net_prefix(netmask) else: prefix = 64 if ipv6 else 24 if "prefix" in net and str(net["prefix"]) != str(prefix): LOG.warning( "Overwriting existing 'prefix' with '%s' in network info: %s", prefix, net, ) net["prefix"] = prefix if ipv6: # TODO: we could/maybe should add this back with the very uncommon # 'netmask' for ipv6. We need a 'net_prefix_to_ipv6_mask' for that. if "netmask" in net: del net["netmask"] elif ipv4: net["netmask"] = net_prefix_to_ipv4_mask(net["prefix"]) return net