Received: by 2002:a05:6358:9144:b0:117:f937:c515 with SMTP id r4csp450120rwr; Thu, 4 May 2023 22:58:53 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5KIgAkwtulkdeXmZuuBrhI1khK2D9lVfKf0L903pXtMx2b+DddCBz/SvRcegtfpKeLJjhz X-Received: by 2002:a17:90b:3e82:b0:23f:1165:b49f with SMTP id rj2-20020a17090b3e8200b0023f1165b49fmr314382pjb.38.1683266333335; Thu, 04 May 2023 22:58:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1683266333; cv=none; d=google.com; s=arc-20160816; b=Ha3qsltnJ/iI1FdMhJtUYvaAjZ+VO8ESaSRTg94SG9GKyoY32a6/xcnoBxTaI6p47Q p8zo3dxG74HUvyFHO2/XxCeUuAl2H4qoiNeVD9MqiX5jtrjyibnzIt4OlWAe4j2W1jby rXYZ766zqTC74um07nj3+wLsUIN/nVAP1JIKnhrWGdxIE3A1Hh7VRAcRw9uBUWR4m2p4 jMGg2CgnEI9f2d2Vkre59rvocg6PpLzSZonlj3Q8MBA/InOoHHk1Ce8CGIbGxrVA0nEu Yf5675DodYSPIEd6bnypQ5uEuKq9VPpZBQuouLyeMk8jO9OXxuPG1r2XfDGtHXMQljY5 zlpA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:message-id:date:subject:cc:to:from :dkim-signature:dkim-filter; bh=RkYIlRpSGE3RkwByOMK1BBL7I2+pvXPyV2rSwbVWbgE=; b=AnJjdIw5oe1kMNMWGFk94W4rHxhkxQojVA9F6xu6F5m36ByzSgyzakWlSL700lL9He gVrgOWFMC1KAGqmQPPYG1tVEQaeimPysg+JSTho6YMIxTZ4HyWA4NRJshI1SkiBfuz2w nMGz/90adt+CuOw7jcYukp6BZmFTRspzNNxg9Bl4qKTZWhhO/KRBKPUWuwb5a+sSR7m5 NaajV9Nh2IGkuw3G76pxrGvgso+v0Rd4cLzzwARcfZh23Di9EwLet0QqV8dA8E5Fp4Cw PB+kQ6X6twiHC4UA30JTLJsbQ6zlso20iBOOVdGSEOc0c6c6zVR53PIjOZlKzp/sT6jZ rviQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linux.microsoft.com header.s=default header.b=DPKyhacK; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linux.microsoft.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id 14-20020a630e4e000000b004fb97a4c536si921684pgo.873.2023.05.04.22.58.30; Thu, 04 May 2023 22:58:53 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@linux.microsoft.com header.s=default header.b=DPKyhacK; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linux.microsoft.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230104AbjEEFv3 (ORCPT + 99 others); Fri, 5 May 2023 01:51:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35828 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229577AbjEEFv2 (ORCPT ); Fri, 5 May 2023 01:51:28 -0400 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 53C941FE1; Thu, 4 May 2023 22:51:26 -0700 (PDT) Received: by linux.microsoft.com (Postfix, from userid 1134) id DB02320EA25D; Thu, 4 May 2023 22:51:25 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com DB02320EA25D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1683265885; bh=RkYIlRpSGE3RkwByOMK1BBL7I2+pvXPyV2rSwbVWbgE=; h=From:To:Cc:Subject:Date:From; b=DPKyhacKJTGP09xQiYct3033LSZl8mkNnioERU8XDvVvKh2Q+srmTEdSeYquCREek sQplfvVy82Qq2RftBAHnQe4UU4cb0tzau7lI5U0FiWeyqz7QUmfj2LRpEcZ1W3hKQX jiyRk9DM33J2P4nnXt53VTh8o8lPKkQwGOCt9QSQ= From: Shradha Gupta To: linux-kernel@vger.kernel.org, linux-hyperv@vger.kernel.org Cc: Shradha Gupta , "K. Y. Srinivasan" , Haiyang Zhang , Stephen Hemminger , Wei Liu , Dexuan Cui , Long Li , Michael Kelley , Olaf Hering Subject: [PATCH v3] hv/hv_kvp_daemon: Add support for keyfile config based connection profile in NM Date: Thu, 4 May 2023 22:51:15 -0700 Message-Id: <1683265875-3706-1-git-send-email-shradhagupta@linux.microsoft.com> X-Mailer: git-send-email 1.8.3.1 X-Spam-Status: No, score=-19.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,ENV_AND_HDR_SPF_MATCH,RCVD_IN_DNSWL_MED, SPF_HELO_PASS,SPF_PASS,T_SCC_BODY_TEXT_LINE,USER_IN_DEF_DKIM_WL, USER_IN_DEF_SPF_WL autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Ifcfg config file support in NetworkManger is deprecated. This patch provides support for the new keyfile config format for connection profiles in NetworkManager. The patch modifies the hv_kvp_daemon code to generate the new network configuration in keyfile format(.ini-style format) along with a ifcfg format configuration. The ifcfg format configuration is also retained to support easy backward compatibility for distro vendors. These configurations are stored in temp files which are further translated using the hv_set_ifconfig.sh script. This script is implemented by individual distros based on the network management commands supported. For example, RHEL's implementation could be found here: https://gitlab.com/redhat/centos-stream/src/hyperv-daemons/-/blob/c9s/hv_set_ifconfig.sh Debian's implementation could be found here: https://github.com/endlessm/linux/blob/master/debian/cloud-tools/hv_set_ifconfig The next part of this support is to let the Distro vendors consume these modified implementations to the new configuration format. Tested-on: Rhel9(Hyper-V, Azure)(nm and ifcfg files verified) Signed-off-by: Shradha Gupta --- Changes in v3: * Corrected the patch description to include information about new changes to persist ifcfg file format too * Code changes to support population of ifcfg format configuration file along with keyfiles * Renamed the new temp keyfile configuration to .nmconnection --- tools/hv/hv_kvp_daemon.c | 224 ++++++++++++++++++++++++++++++++---- tools/hv/hv_set_ifconfig.sh | 39 ++++++- 2 files changed, 234 insertions(+), 29 deletions(-) diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 27f5e7dfc2f7..ae1e2eb10428 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -1171,11 +1171,91 @@ static int process_ip_string(FILE *f, char *ip_string, int type) return 0; } +static int kvp_subnet_to_plen(char *subnet_addr_str) +{ + int plen = 0; + struct in_addr subnet_addr4; + struct in6_addr subnet_addr6; + + /* + * Convert subnet address to binary representation + */ + if (inet_pton(AF_INET, subnet_addr_str, &subnet_addr4) == 1) { + + uint32_t subnet_mask = ntohl(subnet_addr4.s_addr); + + while (subnet_mask & 0x80000000) { + + plen++; + subnet_mask <<= 1; + } + } else if (inet_pton(AF_INET6, subnet_addr_str, &subnet_addr6) == 1) { + + const uint8_t *subnet_mask = subnet_addr6.s6_addr; + int i = 0; + + while (i < 16 && subnet_mask[i] == 0xff) { + + plen += 8; + i++; + } + if (i < 16) { + + uint8_t mask_byte = subnet_mask[i]; + + while (mask_byte & 0x80) { + + plen++; + mask_byte <<= 1; + } + } + } else { + return -1; + } + + return plen; +} + +static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet) +{ + int error = 0; + char addr[INET6_ADDRSTRLEN]; + char subnet_addr[INET6_ADDRSTRLEN]; + int i = 0; + int ip_offset = 0, subnet_offset = 0; + int plen; + + memset(addr, 0, sizeof(addr)); + memset(subnet_addr, 0, sizeof(subnet_addr)); + + while (parse_ip_val_buffer(ip_string, &ip_offset, addr, + (MAX_IP_ADDR_SIZE * 2)) && + parse_ip_val_buffer(subnet, + &subnet_offset, subnet_addr, + (MAX_IP_ADDR_SIZE * 2))) { + + plen = kvp_subnet_to_plen((char *)subnet_addr); + if (plen < 0) + return plen; + + error = fprintf(f, "address%d=%s/%d\n", ++i, (char *)addr, + plen); + if (error < 0) + return error; + + memset(addr, 0, sizeof(addr)); + memset(subnet_addr, 0, sizeof(subnet_addr)); + } + + return 0; +} + static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) { int error = 0; char if_file[PATH_MAX]; - FILE *file; + char nm_file[PATH_MAX]; + FILE *ifcfg_file, *nmfile; char cmd[PATH_MAX]; char *mac_addr; int str_len; @@ -1197,7 +1277,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * in a given distro to configure the interface and so are free * ignore information that may not be relevant. * - * Here is the format of the ip configuration file: + * Here is the ifcfg format of the ip configuration file: * * HWADDR=macaddr * DEVICE=interface name @@ -1220,6 +1300,32 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as * IPV6NETMASK. * + * Here is the keyfile format of the ip configuration file: + * + * [ethernet] + * mac-address=macaddr + * [connection] + * interface-name=interface name + * + * [ipv4] + * method= (where is "auto" if DHCP is configured + * or "manual" if no boot-time protocol should be used) + * + * address1=ipaddr1/plen + * address2=ipaddr2/plen + * + * gateway=gateway1;gateway2 + * + * dns=dns1;dns2 + * + * [ipv6] + * address1=ipaddr1/plen + * address2=ipaddr2/plen + * + * gateway=gateway1;gateway2 + * + * dns=dns1;dns2 + * * The host can specify multiple ipv4 and ipv6 addresses to be * configured for the interface. Furthermore, the configuration * needs to be persistent. A subsequent GET call on the interface @@ -1227,12 +1333,26 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * call. */ + /* + * We are populating both ifcfg and nmconnection files + */ snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, "/ifcfg-", if_name); - file = fopen(if_file, "w"); + ifcfg_file = fopen(if_file, "w"); - if (file == NULL) { + if (ifcfg_file == NULL) { + syslog(LOG_ERR, "Failed to open config file; error: %d %s", + errno, strerror(errno)); + return HV_E_FAIL; + } + + snprintf(nm_file, sizeof(if_file), "%s%s%s%s", KVP_CONFIG_LOC, + "/", if_name, ".nmconnection"); + + nmfile = fopen(nm_file, "w"); + + if (nmfile == NULL) { syslog(LOG_ERR, "Failed to open config file; error: %d %s", errno, strerror(errno)); return HV_E_FAIL; @@ -1248,12 +1368,28 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) goto setval_error; } - error = kvp_write_file(file, "HWADDR", "", mac_addr); - free(mac_addr); + error = kvp_write_file(ifcfg_file, "HWADDR", "", mac_addr); + if (error < 0) + goto setval_error; + + error = kvp_write_file(ifcfg_file, "DEVICE", "", if_name); + if (error < 0) + goto setval_error; + + error = fprintf(nmfile, "\n[connection]\n"); + if (error < 0) + goto setval_error; + + error = kvp_write_file(nmfile, "interface-name", "", if_name); if (error) goto setval_error; - error = kvp_write_file(file, "DEVICE", "", if_name); + error = fprintf(nmfile, "\n[ethernet]\n"); + if (error < 0) + goto setval_error; + + error = kvp_write_file(nmfile, "mac-address", "", mac_addr); + free(mac_addr); if (error) goto setval_error; @@ -1263,47 +1399,88 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * proceed to parse and pass the IPv6 information to the * disto-specific script hv_set_ifconfig. */ + + /* + * First populate the ifcfg file format + */ if (new_val->dhcp_enabled) { - error = kvp_write_file(file, "BOOTPROTO", "", "dhcp"); + error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "dhcp"); if (error) goto setval_error; - } else { - error = kvp_write_file(file, "BOOTPROTO", "", "none"); + error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "none"); if (error) goto setval_error; } - /* - * Write the configuration for ipaddress, netmask, gateway and - * name servers. - */ - - error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR); + error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr, + IPADDR); if (error) goto setval_error; - error = process_ip_string(file, (char *)new_val->sub_net, NETMASK); + error = process_ip_string(ifcfg_file, (char *)new_val->sub_net, + NETMASK); if (error) goto setval_error; - error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY); + error = process_ip_string(ifcfg_file, (char *)new_val->gate_way, + GATEWAY); if (error) goto setval_error; - error = process_ip_string(file, (char *)new_val->dns_addr, DNS); + error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS); if (error) goto setval_error; - fclose(file); + if (new_val->addr_family == ADDR_FAMILY_IPV6) { + + error = fprintf(nmfile, "\n[ipv6]\n"); + if (error < 0) + goto setval_error; + } else { + + error = fprintf(nmfile, "\n[ipv4]\n"); + if (error < 0) + goto setval_error; + + if (new_val->dhcp_enabled) { + error = kvp_write_file(nmfile, "method", "", "auto"); + if (error < 0) + goto setval_error; + } else { + error = kvp_write_file(nmfile, "method", "", "manual"); + if (error < 0) + goto setval_error; + } + } + + /* + * Write the configuration for ipaddress, netmask, gateway and + * name services + */ + error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr, + (char *)new_val->sub_net); + if (error < 0) + goto setval_error; + + error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way); + if (error < 0) + goto setval_error; + + error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr); + if (error < 0) + goto setval_error; + + fclose(nmfile); + fclose(ifcfg_file); /* * Now that we have populated the configuration file, * invoke the external script to do its magic. */ - str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s", - "hv_set_ifconfig", if_file); + str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s %s", + "hv_set_ifconfig", if_file, nm_file); /* * This is a little overcautious, but it's necessary to suppress some * false warnings from gcc 8.0.1. @@ -1323,7 +1500,8 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) setval_error: syslog(LOG_ERR, "Failed to write config file"); - fclose(file); + fclose(ifcfg_file); + fclose(nmfile); return error; } diff --git a/tools/hv/hv_set_ifconfig.sh b/tools/hv/hv_set_ifconfig.sh index d10fe35b7f25..ae5a7a8249a2 100755 --- a/tools/hv/hv_set_ifconfig.sh +++ b/tools/hv/hv_set_ifconfig.sh @@ -18,12 +18,12 @@ # # This example script is based on a RHEL environment. # -# Here is the format of the ip configuration file: +# Here is the ifcfg format of the ip configuration file: # # HWADDR=macaddr # DEVICE=interface name # BOOTPROTO= (where is "dhcp" if DHCP is configured -# or "none" if no boot-time protocol should be used) +# or "none" if no boot-time protocol should be used) # # IPADDR0=ipaddr1 # IPADDR1=ipaddr2 @@ -41,6 +41,32 @@ # tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as # IPV6NETMASK. # +# Here is the keyfile format of the ip configuration file: +# +# [ethernet] +# mac-address=macaddr +# [connection] +# interface-name=interface name +# +# [ipv4] +# method= (where is "auto" if DHCP is configured +# or "manual" if no boot-time protocol should be used) +# +# address1=ipaddr1/plen +# address=ipaddr2/plen +# +# gateway=gateway1;gateway2 +# +# dns=dns1; +# +# [ipv6] +# address1=ipaddr1/plen +# address2=ipaddr1/plen +# +# gateway=gateway1;gateway2 +# +# dns=dns1;dns2 +# # The host can specify multiple ipv4 and ipv6 addresses to be # configured for the interface. Furthermore, the configuration # needs to be persistent. A subsequent GET call on the interface @@ -48,18 +74,19 @@ # call. # - - echo "IPV6INIT=yes" >> $1 echo "NM_CONTROLLED=no" >> $1 echo "PEERDNS=yes" >> $1 echo "ONBOOT=yes" >> $1 - cp $1 /etc/sysconfig/network-scripts/ +chmod 600 $2 +interface=$(echo $2 | awk -F - '{ print $2 }') +filename="${2##*/}" + +sed '/\[connection\]/a autoconnect=true' $2 > /etc/NetworkManager/system-connections/${filename} -interface=$(echo $1 | awk -F - '{ print $2 }') /sbin/ifdown $interface 2>/dev/null /sbin/ifup $interface 2>/dev/null -- 2.34.1