2023-04-22 08:41:55

by Shradha Gupta

[permalink] [raw]
Subject: [PATCH v2] hv/hv_kvp_daemon: Add support for keyfile config based connection profile in NM

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) instead of ifcfg format.
This configuration is stored in a temp file which is 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, Rhel 8.6 (Hyper-V, Azure)
Signed-off-by: Shradha Gupta <[email protected]>
---

Changes in v2:
* Corrected the patch description to exclude bugzilla reference

---
tools/hv/hv_kvp_daemon.c | 252 ++++++++++++++++--------------------
tools/hv/hv_set_ifconfig.sh | 42 +++---
2 files changed, 136 insertions(+), 158 deletions(-)

diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 27f5e7dfc2f7..7f68f9f69fda 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -1004,46 +1004,6 @@ static int kvp_mac_to_ip(struct hv_kvp_ipaddr_value *kvp_ip_val)
return error;
}

-static int expand_ipv6(char *addr, int type)
-{
- int ret;
- struct in6_addr v6_addr;
-
- ret = inet_pton(AF_INET6, addr, &v6_addr);
-
- if (ret != 1) {
- if (type == NETMASK)
- return 1;
- return 0;
- }
-
- sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
- "%02x%02x:%02x%02x:%02x%02x",
- (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1],
- (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3],
- (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5],
- (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7],
- (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9],
- (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11],
- (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13],
- (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]);
-
- return 1;
-
-}
-
-static int is_ipv4(char *addr)
-{
- int ret;
- struct in_addr ipv4_addr;
-
- ret = inet_pton(AF_INET, addr, &ipv4_addr);
-
- if (ret == 1)
- return 1;
- return 0;
-}
-
static int parse_ip_val_buffer(char *in_buf, int *offset,
char *out_buf, int out_len)
{
@@ -1092,80 +1052,80 @@ static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)
return 0;
}

-
-static int process_ip_string(FILE *f, char *ip_string, int type)
+static int kvp_subnet_to_plen(char *subnet_addr_str)
{
- int error = 0;
- char addr[INET6_ADDRSTRLEN];
- int i = 0;
- int j = 0;
- char str[256];
- char sub_str[13];
- int offset = 0;
+ int plen = 0;
+ struct in_addr subnet_addr4;
+ struct in6_addr subnet_addr6;

- memset(addr, 0, sizeof(addr));
+ /*
+ * Convert subnet address to binary representation
+ */
+ if (inet_pton(AF_INET, subnet_addr_str, &subnet_addr4) == 1) {

- while (parse_ip_val_buffer(ip_string, &offset, addr,
- (MAX_IP_ADDR_SIZE * 2))) {
+ uint32_t subnet_mask = ntohl(subnet_addr4.s_addr);

- sub_str[0] = 0;
- if (is_ipv4(addr)) {
- switch (type) {
- case IPADDR:
- snprintf(str, sizeof(str), "%s", "IPADDR");
- break;
- case NETMASK:
- snprintf(str, sizeof(str), "%s", "NETMASK");
- break;
- case GATEWAY:
- snprintf(str, sizeof(str), "%s", "GATEWAY");
- break;
- case DNS:
- snprintf(str, sizeof(str), "%s", "DNS");
- break;
- }
+ while (subnet_mask & 0x80000000) {

- if (type == DNS) {
- snprintf(sub_str, sizeof(sub_str), "%d", ++i);
- } else if (type == GATEWAY && i == 0) {
- ++i;
- } else {
- snprintf(sub_str, sizeof(sub_str), "%d", i++);
- }
+ 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;

- } else if (expand_ipv6(addr, type)) {
- switch (type) {
- case IPADDR:
- snprintf(str, sizeof(str), "%s", "IPV6ADDR");
- break;
- case NETMASK:
- snprintf(str, sizeof(str), "%s", "IPV6NETMASK");
- break;
- case GATEWAY:
- snprintf(str, sizeof(str), "%s",
- "IPV6_DEFAULTGW");
- break;
- case DNS:
- snprintf(str, sizeof(str), "%s", "DNS");
- break;
- }
+ while (i < 16 && subnet_mask[i] == 0xff) {

- if (type == DNS) {
- snprintf(sub_str, sizeof(sub_str), "%d", ++i);
- } else if (j == 0) {
- ++j;
- } else {
- snprintf(sub_str, sizeof(sub_str), "_%d", j++);
+ plen += 8;
+ i++;
+ }
+ if (i < 16) {
+
+ uint8_t mask_byte = subnet_mask[i];
+
+ while (mask_byte & 0x80) {
+
+ plen++;
+ mask_byte <<= 1;
}
- } else {
- return HV_INVALIDARG;
}
+ } else {
+ return -1;
+ }

- error = kvp_write_file(f, str, sub_str, addr);
- if (error)
+ return plen;
+}
+
+static int process_ip_string(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;
@@ -1199,26 +1159,29 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
*
* Here is the 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)
+ * [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)
*
- * IPADDR0=ipaddr1
- * IPADDR1=ipaddr2
- * IPADDRx=ipaddry (where y = x + 1)
+ * address1=ipaddr1/plen
+ * address2=ipaddr2/plen
*
- * NETMASK0=netmask1
- * NETMASKx=netmasky (where y = x + 1)
+ * gateway=gateway1;gateway2
*
- * GATEWAY=ipaddr1
- * GATEWAYx=ipaddry (where y = x + 1)
+ * dns=dns1;dns2
*
- * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
+ * [ipv6]
+ * address1=ipaddr1/plen
+ * address2=ipaddr2/plen
*
- * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
- * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
- * IPV6NETMASK.
+ * gateway=gateway1;gateway2
+ *
+ * dns=dns1;dns2
*
* The host can specify multiple ipv4 and ipv6 addresses to be
* configured for the interface. Furthermore, the configuration
@@ -1248,12 +1211,20 @@ 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 = fprintf(file, "\n[connection]\n");
+ if (error < 0)
+ goto setval_error;
+
+ error = kvp_write_file(file, "interface-name", "", if_name);
if (error)
goto setval_error;

- error = kvp_write_file(file, "DEVICE", "", if_name);
+ error = fprintf(file, "\n[ethernet]\n");
+ if (error < 0)
+ goto setval_error;
+
+ error = kvp_write_file(file, "mac-address", "", mac_addr);
+ free(mac_addr);
if (error)
goto setval_error;

@@ -1263,36 +1234,43 @@ 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.
*/
- if (new_val->dhcp_enabled) {
- error = kvp_write_file(file, "BOOTPROTO", "", "dhcp");
- if (error)
- goto setval_error;
+ if (new_val->addr_family == ADDR_FAMILY_IPV6) {

+ error = fprintf(file, "\n[ipv6]\n");
+ if (error < 0)
+ goto setval_error;
} else {
- error = kvp_write_file(file, "BOOTPROTO", "", "none");
- if (error)
+
+ error = fprintf(file, "\n[ipv4]\n");
+ if (error < 0)
goto setval_error;
+
+ if (new_val->dhcp_enabled) {
+ error = kvp_write_file(file, "method", "", "auto");
+ if (error < 0)
+ goto setval_error;
+ } else {
+ error = kvp_write_file(file, "method", "", "manual");
+ if (error < 0)
+ goto setval_error;
+ }
}

/*
* Write the configuration for ipaddress, netmask, gateway and
- * name servers.
+ * name services
*/
-
- error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
- if (error)
+ error = process_ip_string(file, (char *)new_val->ip_addr,
+ (char *)new_val->sub_net);
+ if (error < 0)
goto setval_error;

- error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
- if (error)
+ error = fprintf(file, "gateway=%s\n", (char *)new_val->gate_way);
+ if (error < 0)
goto setval_error;

- error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
- if (error)
- goto setval_error;
-
- error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
- if (error)
+ error = fprintf(file, "dns=%s\n", (char *)new_val->dns_addr);
+ if (error < 0)
goto setval_error;

fclose(file);
diff --git a/tools/hv/hv_set_ifconfig.sh b/tools/hv/hv_set_ifconfig.sh
index d10fe35b7f25..22238767ef7e 100755
--- a/tools/hv/hv_set_ifconfig.sh
+++ b/tools/hv/hv_set_ifconfig.sh
@@ -20,26 +20,29 @@
#
# Here is the 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)
+# [ethernet]
+# mac-address=macaddr
+# [connection]
+# interface-name=interface name
#
-# IPADDR0=ipaddr1
-# IPADDR1=ipaddr2
-# IPADDRx=ipaddry (where y = x + 1)
+# [ipv4]
+# method=<protocol> (where <protocol> is "auto" if DHCP is configured
+# or "manual" if no boot-time protocol should be used)
#
-# NETMASK0=netmask1
-# NETMASKx=netmasky (where y = x + 1)
+# address1=ipaddr1/plen
+# address=ipaddr2/plen
#
-# GATEWAY=ipaddr1
-# GATEWAYx=ipaddry (where y = x + 1)
+# gateway=gateway1;gateway2
#
-# DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
+# dns=dns1;
#
-# IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
-# tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
-# IPV6NETMASK.
+# [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
@@ -50,13 +53,10 @@



-echo "IPV6INIT=yes" >> $1
-echo "NM_CONTROLLED=no" >> $1
-echo "PEERDNS=yes" >> $1
-echo "ONBOOT=yes" >> $1
-
+sed '/\[connection]\/a autoconnect=true' $1

-cp $1 /etc/sysconfig/network-scripts/
+chmod 600 $1
+cp $1 /etc/NetworkManager/system-connections/


interface=$(echo $1 | awk -F - '{ print $2 }')
--
2.37.2


2023-04-22 12:04:00

by Olaf Hering

[permalink] [raw]
Subject: Re: [PATCH v2] hv/hv_kvp_daemon: Add support for keyfile config based connection profile in NM

Sat, 22 Apr 2023 01:39:43 -0700 Shradha Gupta <[email protected]>:

> The next part of this support is to let the Distro vendors consume
> these modified implementations to the new configuration format.

The approach taken here makes it difficult to consume the changes.
The way it is implemented means a given variant of the daemon produces
either ifcfg or .nmconnection files. In practice there is a single
source of hv_kvp_daemon.c which is compatible with all versions of a distro.

IMO the new feature should be implemented such that hv_kvp_daemon
writes both files. Then the tooling can decide which of the files will
actually be used.


I think the configuration file for NetworkManager should have the
suffix .nmconnection, even if it may not be mandatory.

Olaf


Attachments:
(No filename) (849.00 B)
Digitale Signatur von OpenPGP

2023-05-05 05:50:54

by Shradha Gupta

[permalink] [raw]
Subject: [PATCH v3] hv/hv_kvp_daemon: Add support for keyfile config based connection profile in NM

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 <[email protected]>
---

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
<interface>.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=<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,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=<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
+#
+# 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