Re: [PATCH v8] hv/hv_kvp_daemon:Support for keyfile based connection profile

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> On 13-Oct-2023, at 3:06 PM, Ani Sinha <anisinha@xxxxxxxxxx> wrote:
> 
> 
> 
>> On 09-Oct-2023, at 4:08 PM, Shradha Gupta <shradhagupta@xxxxxxxxxxxxxxxxxxx> wrote:
>> 
>> 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)
> 
> Was this patch tested with ipv6? We are seeing a mix of ipv6 and ipv4 addresses in ipv6 section:
> 
> ]# cat eth0.nmconnection 
> 
> [connection]
> autoconnect=yes
> interface-name=eth0
> 
> [ethernet]
> mac-address=00:15:5D:C4:63:45
> 
> [ipv6]
> method=manual
> address=1234:1234:1234:1234:0000:0000:0000:0113/120
> gateway=10.73.199.254
> dns=10.72.17.5
> 
> Gateway and dns should be in ipv6. 
> 
> Ipv4 dns addresses comes from /etc/resolv.conf and in our system:
> 
> # /usr/libexec/hypervkvpd/hv_get_dns_info
> 10.72.17.5
> 10.68.5.26
> 
> [root@vm-197-248 ~]# cat /etc/resolv.conf 
> # Generated by NetworkManager
> search lab.eng.pek2.redhat.com
> nameserver 10.72.17.5
> nameserver 10.68.5.26
> 
> So we should check if the addresses are indeed ipv6 or not and should not print it if !is_ipv4(). Ipv6 addresses may not be provisioned.
> 
> I will post a patch fix when we complete our internal testing.
> 
> 
>> Signed-off-by: Shradha Gupta <shradhagupta@xxxxxxxxxxxxxxxxxxx>
>> Reviewed-by: Saurabh Sengar <ssengar@xxxxxxxxxxxxxxxxxxx>
>> ---
>> Changes v7->v8
>> * Fix some filename variable names to avoid confusion
>> * Add initialization of is_ipv6 variable
>> * Add a few comments
>> ---
>> tools/hv/hv_kvp_daemon.c    | 233 +++++++++++++++++++++++++++++++-----
>> tools/hv/hv_set_ifconfig.sh |  39 +++++-
>> 2 files changed, 235 insertions(+), 37 deletions(-)
>> 
>> diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
>> index 27f5e7dfc2f7..264eeb9c46a9 100644
>> --- a/tools/hv/hv_kvp_daemon.c
>> +++ b/tools/hv/hv_kvp_daemon.c
>> @@ -1171,12 +1171,79 @@ static int process_ip_string(FILE *f, char *ip_string, int type)
>> 	return 0;
>> }
>> 
>> +/*
>> + * Only IPv4 subnet strings needs to be converted to plen
>> + * For IPv6 the subnet is already privided in plen format
>> + */
>> +static int kvp_subnet_to_plen(char *subnet_addr_str)
>> +{
>> +	int plen = 0;
>> +	struct in_addr subnet_addr4;
>> +
>> +	/*
>> +	 * 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 {
>> +		return -1;
>> +	}
>> +
>> +	return plen;
>> +}
>> +
>> +static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet,
>> +				int is_ipv6)
>> +{
>> +	char addr[INET6_ADDRSTRLEN];
>> +	char subnet_addr[INET6_ADDRSTRLEN];
>> +	int error, i = 0;
> 
> This should be initialised to 1

Sorry we already do a ++i and not i++ so it should be fine.

> 
> https://developer-old.gnome.org/NetworkManager/stable/nm-settings-keyfile.html#:~:text=File%20Format,%2Dsettings(5)).
> 
> Also see below.
> 
>> +	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))) {
>> +		if (!is_ipv6)
>> +			plen = kvp_subnet_to_plen((char *)subnet_addr);
>> +		else
>> +			plen = atoi(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 if_filename[PATH_MAX];
>> +	char nm_filename[PATH_MAX];
>> +	FILE *ifcfg_file, *nmfile;
>> 	char cmd[PATH_MAX];
>> +	int is_ipv6 = 0;
>> 	char *mac_addr;
>> 	int str_len;
>> 
>> @@ -1197,7 +1264,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 +1287,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=<protocol> (where <protocol> 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,14 +1320,29 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
>> 	 * call.
>> 	 */
>> 
>> -	snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC,
>> -		"/ifcfg-", if_name);
>> +	/*
>> +	 * We are populating both ifcfg and nmconnection files
>> +	 */
>> +	snprintf(if_filename, sizeof(if_filename), "%s%s%s", KVP_CONFIG_LOC,
>> +		 "/ifcfg-", if_name);
>> 
>> -	file = fopen(if_file, "w");
>> +	ifcfg_file = fopen(if_filename, "w");
>> 
>> -	if (file == NULL) {
>> +	if (!ifcfg_file) {
>> 		syslog(LOG_ERR, "Failed to open config file; error: %d %s",
>> -				errno, strerror(errno));
>> +		       errno, strerror(errno));
>> +		return HV_E_FAIL;
>> +	}
>> +
>> +	snprintf(nm_filename, sizeof(nm_filename), "%s%s%s%s", KVP_CONFIG_LOC,
>> +		 "/", if_name, ".nmconnection");
>> +
>> +	nmfile = fopen(nm_filename, "w");
>> +
>> +	if (!nmfile) {
>> +		syslog(LOG_ERR, "Failed to open config file; error: %d %s",
>> +		       errno, strerror(errno));
>> +		fclose(ifcfg_file);
>> 		return HV_E_FAIL;
>> 	}
>> 
>> @@ -1248,14 +1356,31 @@ 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 setmac_error;
>> +
>> +	error = kvp_write_file(ifcfg_file, "DEVICE", "", if_name);
>> +	if (error < 0)
>> +		goto setmac_error;
>> +
>> +	error = fprintf(nmfile, "\n[connection]\n");
>> +	if (error < 0)
>> +		goto setmac_error;
>> +
>> +	error = kvp_write_file(nmfile, "interface-name", "", if_name);
>> 	if (error)
>> -		goto setval_error;
>> +		goto setmac_error;
>> 
>> -	error = kvp_write_file(file, "DEVICE", "", if_name);
>> +	error = fprintf(nmfile, "\n[ethernet]\n");
>> +	if (error < 0)
>> +		goto setmac_error;
>> +
>> +	error = kvp_write_file(nmfile, "mac-address", "", mac_addr);
>> 	if (error)
>> -		goto setval_error;
>> +		goto setmac_error;
>> +
>> +	free(mac_addr);
>> 
>> 	/*
>> 	 * The dhcp_enabled flag is only for IPv4. In the case the host only
>> @@ -1263,47 +1388,91 @@ 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) {
> 
> I think we should have done something like
> 
> new_val->addr_family & ADDR_FAMILY_IPV6 here.
> 
>> +		error = fprintf(nmfile, "\n[ipv6]\n");
>> +		if (error < 0)
>> +			goto setval_error;
>> +		is_ipv6 = 1;
>> +	} else {
>> +		error = fprintf(nmfile, "\n[ipv4]\n");
>> +		if (error < 0)
>> +			goto setval_error;
>> +	}
>> +
>> +	/*
>> +	 * Now we populate the keyfile format
>> +	 */
>> +
>> +	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, is_ipv6);
>> +	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_filename, nm_filename);
>> 	/*
>> 	 * This is a little overcautious, but it's necessary to suppress some
>> 	 * false warnings from gcc 8.0.1.
>> @@ -1316,14 +1485,16 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
>> 
>> 	if (system(cmd)) {
>> 		syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s",
>> -				cmd, errno, strerror(errno));
>> +		       cmd, errno, strerror(errno));
>> 		return HV_E_FAIL;
>> 	}
>> 	return 0;
>> -
>> +setmac_error:
>> +	free(mac_addr);
>> 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=<protocol> (where <protocol> 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=<protocol> (where <protocol> is "auto" if DHCP is configured
>> +#                       or "manual" if no boot-time protocol should be used)
>> +#
>> +# address1=ipaddr1/plen
>> +# address=ipaddr2/plen
> 
> address2 here?
> 
>> +#
>> +# gateway=gateway1;gateway2
>> +#
>> +# dns=dns1;
>> +#
>> +# [ipv6]
>> +# address1=ipaddr1/plen
>> +# address2=ipaddr1/plen
> 
> ipaddr2 ?
> 
>> +#
>> +# 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






[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux