From 1d6d81a2d687b373a40319f31fa25344c5628fa3 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Thu, 4 Dec 2025 11:37:46 +0000 Subject: [PATCH] Import from AlmaLinux stable repository --- ...ng-keyfile-support-in-RHEL-specific-.patch | 67 +++ ...-Handle-IPv4-and-Ipv6-combination-fo.patch | 335 ++++++++++++++ ...-Some-small-fixes-for-handling-NM-ke.patch | 100 ++++ ...-Support-for-keyfile-based-connectio.patch | 426 ++++++++++++++++++ ...-Enable-debug-logs-for-hv_kvp_daemon.patch | 204 +++++++++ SPECS/hyperv-daemons.spec | 30 +- 6 files changed, 1161 insertions(+), 1 deletion(-) create mode 100644 SOURCES/hpvd-Changes-for-adding-keyfile-support-in-RHEL-specific-.patch create mode 100644 SOURCES/hpvd-hv-hv_kvp_daemon-Handle-IPv4-and-Ipv6-combination-fo.patch create mode 100644 SOURCES/hpvd-hv-hv_kvp_daemon-Some-small-fixes-for-handling-NM-ke.patch create mode 100644 SOURCES/hpvd-hv-hv_kvp_daemon-Support-for-keyfile-based-connectio.patch create mode 100644 SOURCES/hpvd-tools-hv-Enable-debug-logs-for-hv_kvp_daemon.patch diff --git a/SOURCES/hpvd-Changes-for-adding-keyfile-support-in-RHEL-specific-.patch b/SOURCES/hpvd-Changes-for-adding-keyfile-support-in-RHEL-specific-.patch new file mode 100644 index 0000000..46b6543 --- /dev/null +++ b/SOURCES/hpvd-Changes-for-adding-keyfile-support-in-RHEL-specific-.patch @@ -0,0 +1,67 @@ +From 1df9596722c093afd097fd7a9689092a5cee7d2a Mon Sep 17 00:00:00 2001 +From: Ani Sinha +Date: Tue, 10 Oct 2023 11:50:30 +0530 +Subject: [PATCH 4/4] Changes for adding keyfile support in RHEL specific + script + +RH-Author: Ani Sinha +RH-MergeRequest: 8: hv/hv_kvp_daemon:Support for keyfile based connection profile +RH-Jira: RHEL-9902 +RH-Acked-by: Cathy Avery +RH-Acked-by: Miroslav Rezanina +RH-Commit: [4/4] b60a8d644b0e777373c92f4778c3d4560f6f2642 (anisinha/centos-hyperv-daemons) + +Some adjustments to the RHEL specific customization script in order to support +Network Manager keyfiles. These changes were tested internally by Red Hat QE. +These changes are mostly trivial and are not pushed upstream at this momemnt. + +Jira: https://issues.redhat.com/browse/RHEL-9902 + +See also https://issues.redhat.com/browse/RHEL-14505 + +Signed-off-by: Ani Sinha +--- + hv_set_ifconfig.sh | 25 ++++++++++++++----------- + 1 file changed, 14 insertions(+), 11 deletions(-) + +diff --git a/hv_set_ifconfig.sh b/hv_set_ifconfig.sh +index 9c2ee30..0bdf2bc 100644 +--- a/hv_set_ifconfig.sh ++++ b/hv_set_ifconfig.sh +@@ -74,19 +74,22 @@ + # call. + # + ++# This is RHEL specific bash script that configures NM keyfiles. ++# ifcfg files passed as the first argument to this script remains untouched. + ++if [ -z "$2" ]; then ++ echo "No input NM keyfile. Exiting!" ++ exit 1 ++fi + +-echo "IPV6INIT=yes" >> $1 +-echo "PEERDNS=yes" >> $1 +-echo "ONBOOT=yes" >> $1 ++sed -i '/\[ipv4\]/a ignore-auto-dns=false' $2 ++sed -i '/\[connection\]/a autoconnect=true' $2 + +-#Unlike older sysconfig scripts, NetworkManager expects GATEWAYx=ipaddr for all values of x. +-#So the first gateway is GATEWAY0 instead of GATEWAY. Other values should remain unchanged. +-#Workaround this by replacing GATEWAY= with GATEWAY0=. +-sed -i "s/GATEWAY=/GATEWAY0=/" $1 ++filename="${2##*/}" ++chmod 600 $2 ++cp $2 /etc/NetworkManager/system-connections/ + +-cp $1 /etc/sysconfig/network-scripts/ ++nmcli connection load "/etc/NetworkManager/system-connections/${filename}" ++nmcli connection up filename "/etc/NetworkManager/system-connections/${filename}" + +-filename="${1##*/}" +-nmcli connection load "/etc/sysconfig/network-scripts/${filename}" +-nmcli connection up filename "/etc/sysconfig/network-scripts/${filename}" ++exit 0 +-- +2.39.3 + diff --git a/SOURCES/hpvd-hv-hv_kvp_daemon-Handle-IPv4-and-Ipv6-combination-fo.patch b/SOURCES/hpvd-hv-hv_kvp_daemon-Handle-IPv4-and-Ipv6-combination-fo.patch new file mode 100644 index 0000000..80c8394 --- /dev/null +++ b/SOURCES/hpvd-hv-hv_kvp_daemon-Handle-IPv4-and-Ipv6-combination-fo.patch @@ -0,0 +1,335 @@ +From d252f80372544b9b7e060b90bf5c5b0ccf6093d6 Mon Sep 17 00:00:00 2001 +From: Shradha Gupta +Date: Fri, 22 Mar 2024 06:46:02 -0700 +Subject: [PATCH 3/4] hv/hv_kvp_daemon: Handle IPv4 and Ipv6 combination for + keyfile format + +RH-Author: Ani Sinha +RH-MergeRequest: 8: hv/hv_kvp_daemon:Support for keyfile based connection profile +RH-Jira: RHEL-9902 +RH-Acked-by: Cathy Avery +RH-Acked-by: Miroslav Rezanina +RH-Commit: [3/4] e164b6951b873467f8c87e0d01b1fd89326aa64e (anisinha/centos-hyperv-daemons) + +If the network configuration strings are passed as a combination of IPv4 +and IPv6 addresses, the current KVP daemon does not handle processing for +the keyfile configuration format. +With these changes, the keyfile config generation logic scans through the +list twice to generate IPv4 and IPv6 sections for the configuration files +to handle this support. + +Testcases ran:Rhel 9, Hyper-V VMs + (IPv4 only, IPv6 only, IPv4 and IPv6 combination) + +Cherry-picked from Linux kernel upstream commit +f971f6dd3742d2 ("hv/hv_kvp_daemon: Handle IPv4 and Ipv6 combination for keyfile format") +Co-developed-by: Ani Sinha +Signed-off-by: Ani Sinha +Signed-off-by: Shradha Gupta +Reviewed-by: Easwar Hariharan +Tested-by: Ani Sinha +Reviewed-by: Ani Sinha +Link: https://lore.kernel.org/r/1711115162-11629-1-git-send-email-shradhagupta@linux.microsoft.com +Signed-off-by: Wei Liu +Message-ID: <1711115162-11629-1-git-send-email-shradhagupta@linux.microsoft.com> +--- + hv_kvp_daemon.c | 213 ++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 172 insertions(+), 41 deletions(-) + +diff --git a/hv_kvp_daemon.c b/hv_kvp_daemon.c +index 2f1862e..aa350d8 100644 +--- a/hv_kvp_daemon.c ++++ b/hv_kvp_daemon.c +@@ -76,6 +76,12 @@ enum { + DNS + }; + ++enum { ++ IPV4 = 1, ++ IPV6, ++ IP_TYPE_MAX ++}; ++ + static int in_hand_shake; + + static char *os_name = ""; +@@ -102,6 +108,11 @@ static struct utsname uts_buf; + + #define MAX_FILE_NAME 100 + #define ENTRIES_PER_BLOCK 50 ++/* ++ * Change this entry if the number of addresses increases in future ++ */ ++#define MAX_IP_ENTRIES 64 ++#define OUTSTR_BUF_SIZE ((INET6_ADDRSTRLEN + 1) * MAX_IP_ENTRIES) + + struct kvp_record { + char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +@@ -1171,6 +1182,18 @@ static int process_ip_string(FILE *f, char *ip_string, int type) + return 0; + } + ++int ip_version_check(const char *input_addr) ++{ ++ struct in6_addr addr; ++ ++ if (inet_pton(AF_INET, input_addr, &addr)) ++ return IPV4; ++ else if (inet_pton(AF_INET6, input_addr, &addr)) ++ return IPV6; ++ ++ return -EINVAL; ++} ++ + /* + * Only IPv4 subnet strings needs to be converted to plen + * For IPv6 the subnet is already privided in plen format +@@ -1197,14 +1220,75 @@ static int kvp_subnet_to_plen(char *subnet_addr_str) + return plen; + } + ++static int process_dns_gateway_nm(FILE *f, char *ip_string, int type, ++ int ip_sec) ++{ ++ char addr[INET6_ADDRSTRLEN], *output_str; ++ int ip_offset = 0, error = 0, ip_ver; ++ char *param_name; ++ ++ if (type == DNS) ++ param_name = "dns"; ++ else if (type == GATEWAY) ++ param_name = "gateway"; ++ else ++ return -EINVAL; ++ ++ output_str = (char *)calloc(OUTSTR_BUF_SIZE, sizeof(char)); ++ if (!output_str) ++ return -ENOMEM; ++ ++ while (1) { ++ memset(addr, 0, sizeof(addr)); ++ ++ if (!parse_ip_val_buffer(ip_string, &ip_offset, addr, ++ (MAX_IP_ADDR_SIZE * 2))) ++ break; ++ ++ ip_ver = ip_version_check(addr); ++ if (ip_ver < 0) ++ continue; ++ ++ if ((ip_ver == IPV4 && ip_sec == IPV4) || ++ (ip_ver == IPV6 && ip_sec == IPV6)) { ++ /* ++ * do a bound check to avoid out-of bound writes ++ */ ++ if ((OUTSTR_BUF_SIZE - strlen(output_str)) > ++ (strlen(addr) + 1)) { ++ strncat(output_str, addr, ++ OUTSTR_BUF_SIZE - ++ strlen(output_str) - 1); ++ strncat(output_str, ",", ++ OUTSTR_BUF_SIZE - ++ strlen(output_str) - 1); ++ } ++ } else { ++ continue; ++ } ++ } ++ ++ if (strlen(output_str)) { ++ /* ++ * This is to get rid of that extra comma character ++ * in the end of the string ++ */ ++ output_str[strlen(output_str) - 1] = '\0'; ++ error = fprintf(f, "%s=%s\n", param_name, output_str); ++ } ++ ++ free(output_str); ++ return error; ++} ++ + static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet, +- int is_ipv6) ++ int ip_sec) + { + char addr[INET6_ADDRSTRLEN]; + char subnet_addr[INET6_ADDRSTRLEN]; +- int error, i = 0; ++ int error = 0, i = 0; + int ip_offset = 0, subnet_offset = 0; +- int plen; ++ int plen, ip_ver; + + memset(addr, 0, sizeof(addr)); + memset(subnet_addr, 0, sizeof(subnet_addr)); +@@ -1216,10 +1300,16 @@ static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet, + subnet_addr, + (MAX_IP_ADDR_SIZE * + 2))) { +- if (!is_ipv6) ++ ip_ver = ip_version_check(addr); ++ if (ip_ver < 0) ++ continue; ++ ++ if (ip_ver == IPV4 && ip_sec == IPV4) + plen = kvp_subnet_to_plen((char *)subnet_addr); +- else ++ else if (ip_ver == IPV6 && ip_sec == IPV6) + plen = atoi(subnet_addr); ++ else ++ continue; + + if (plen < 0) + return plen; +@@ -1233,17 +1323,16 @@ static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet, + memset(subnet_addr, 0, sizeof(subnet_addr)); + } + +- return 0; ++ return error; + } + + static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) + { +- int error = 0; ++ int error = 0, ip_ver; + 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; + +@@ -1421,52 +1510,94 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) + if (error) + goto setval_error; + +- if (new_val->addr_family & ADDR_FAMILY_IPV6) { +- 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 ++ * ++ * The keyfile format expects the IPv6 and IPv4 configuration in ++ * different sections. Therefore we iterate through the list twice, ++ * once to populate the IPv4 section and the next time for IPv6 + */ ++ ip_ver = IPV4; ++ do { ++ if (ip_ver == IPV4) { ++ error = fprintf(nmfile, "\n[ipv4]\n"); ++ if (error < 0) ++ goto setval_error; ++ } else { ++ error = fprintf(nmfile, "\n[ipv6]\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"); ++ /* ++ * 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, ++ ip_ver); + 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; ++ /* ++ * As dhcp_enabled is only valid for ipv4, we do not set dhcp ++ * methods for ipv6 based on dhcp_enabled flag. ++ * ++ * For ipv4, set method to manual only when dhcp_enabled is ++ * false and specific ipv4 addresses are configured. If neither ++ * dhcp_enabled is true and no ipv4 addresses are configured, ++ * set method to 'disabled'. ++ * ++ * For ipv6, set method to manual when we configure ipv6 ++ * addresses. Otherwise set method to 'auto' so that SLAAC from ++ * RA may be used. ++ */ ++ if (ip_ver == IPV4) { ++ if (new_val->dhcp_enabled) { ++ error = kvp_write_file(nmfile, "method", "", ++ "auto"); ++ if (error < 0) ++ goto setval_error; ++ } else if (error) { ++ error = kvp_write_file(nmfile, "method", "", ++ "manual"); ++ if (error < 0) ++ goto setval_error; ++ } else { ++ error = kvp_write_file(nmfile, "method", "", ++ "disabled"); ++ if (error < 0) ++ goto setval_error; ++ } ++ } else if (ip_ver == IPV6) { ++ if (error) { ++ error = kvp_write_file(nmfile, "method", "", ++ "manual"); ++ if (error < 0) ++ goto setval_error; ++ } else { ++ error = kvp_write_file(nmfile, "method", "", ++ "auto"); ++ if (error < 0) ++ goto setval_error; ++ } ++ } + +- /* we do not want ipv4 addresses in ipv6 section and vice versa */ +- if (is_ipv6 != is_ipv4((char *)new_val->gate_way)) { +- error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way); ++ error = process_dns_gateway_nm(nmfile, ++ (char *)new_val->gate_way, ++ GATEWAY, ip_ver); + if (error < 0) + goto setval_error; +- } + +- if (is_ipv6 != is_ipv4((char *)new_val->dns_addr)) { +- error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr); ++ error = process_dns_gateway_nm(nmfile, ++ (char *)new_val->dns_addr, DNS, ++ ip_ver); + if (error < 0) + goto setval_error; +- } ++ ++ ip_ver++; ++ } while (ip_ver < IP_TYPE_MAX); ++ + fclose(nmfile); + fclose(ifcfg_file); + +-- +2.39.3 + diff --git a/SOURCES/hpvd-hv-hv_kvp_daemon-Some-small-fixes-for-handling-NM-ke.patch b/SOURCES/hpvd-hv-hv_kvp_daemon-Some-small-fixes-for-handling-NM-ke.patch new file mode 100644 index 0000000..80dfc97 --- /dev/null +++ b/SOURCES/hpvd-hv-hv_kvp_daemon-Some-small-fixes-for-handling-NM-ke.patch @@ -0,0 +1,100 @@ +From 61d2686d4b36e5a9099d80131044807f69142249 Mon Sep 17 00:00:00 2001 +From: Ani Sinha +Date: Mon, 16 Oct 2023 19:03:33 +0530 +Subject: [PATCH 2/4] hv/hv_kvp_daemon: Some small fixes for handling NM + keyfiles + +RH-Author: Ani Sinha +RH-MergeRequest: 8: hv/hv_kvp_daemon:Support for keyfile based connection profile +RH-Jira: RHEL-9902 +RH-Acked-by: Cathy Avery +RH-Acked-by: Miroslav Rezanina +RH-Commit: [2/4] 1676c48ed1f2d91dd1f8c43f6c8009e3bebf295a (anisinha/centos-hyperv-daemons) + +Some small fixes: +- lets make sure we are not adding ipv4 addresses in ipv6 section in + keyfile and vice versa. +- ADDR_FAMILY_IPV6 is a bit in addr_family. Test that bit instead of + checking the whole value of addr_family. +- Some trivial fixes in hv_set_ifconfig.sh. + +These fixes are proposed after doing some internal testing at Red Hat. + +Cherry-picked from upstream linux +kernel commit c3803203bc5ec910a ("hv/hv_kvp_daemon: Some small fixes for handling NM keyfiles") +CC: Shradha Gupta +CC: Saurabh Sengar +Fixes: 42999c904612 ("hv/hv_kvp_daemon:Support for keyfile based connection profile") +Signed-off-by: Ani Sinha +Reviewed-by: Shradha Gupta +Signed-off-by: Wei Liu +Message-ID: <20231016133122.2419537-1-anisinha@redhat.com> +--- + hv_kvp_daemon.c | 20 ++++++++++++-------- + hv_set_ifconfig.sh | 4 ++-- + 2 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/hv_kvp_daemon.c b/hv_kvp_daemon.c +index d50b4e8..2f1862e 100644 +--- a/hv_kvp_daemon.c ++++ b/hv_kvp_daemon.c +@@ -1421,7 +1421,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) + if (error) + goto setval_error; + +- if (new_val->addr_family == ADDR_FAMILY_IPV6) { ++ if (new_val->addr_family & ADDR_FAMILY_IPV6) { + error = fprintf(nmfile, "\n[ipv6]\n"); + if (error < 0) + goto setval_error; +@@ -1455,14 +1455,18 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) + 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; ++ /* we do not want ipv4 addresses in ipv6 section and vice versa */ ++ if (is_ipv6 != is_ipv4((char *)new_val->gate_way)) { ++ error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way); ++ if (error < 0) ++ goto setval_error; ++ } + ++ if (is_ipv6 != is_ipv4((char *)new_val->dns_addr)) { ++ error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr); ++ if (error < 0) ++ goto setval_error; ++ } + fclose(nmfile); + fclose(ifcfg_file); + +diff --git a/hv_set_ifconfig.sh b/hv_set_ifconfig.sh +index 35aae6f..9c2ee30 100644 +--- a/hv_set_ifconfig.sh ++++ b/hv_set_ifconfig.sh +@@ -53,7 +53,7 @@ + # or "manual" if no boot-time protocol should be used) + # + # address1=ipaddr1/plen +-# address=ipaddr2/plen ++# address2=ipaddr2/plen + # + # gateway=gateway1;gateway2 + # +@@ -61,7 +61,7 @@ + # + # [ipv6] + # address1=ipaddr1/plen +-# address2=ipaddr1/plen ++# address2=ipaddr2/plen + # + # gateway=gateway1;gateway2 + # +-- +2.39.3 + diff --git a/SOURCES/hpvd-hv-hv_kvp_daemon-Support-for-keyfile-based-connectio.patch b/SOURCES/hpvd-hv-hv_kvp_daemon-Support-for-keyfile-based-connectio.patch new file mode 100644 index 0000000..1bbad06 --- /dev/null +++ b/SOURCES/hpvd-hv-hv_kvp_daemon-Support-for-keyfile-based-connectio.patch @@ -0,0 +1,426 @@ +From 626a1af79f67bd9150dd6ff496d0dbbfb93bc320 Mon Sep 17 00:00:00 2001 +From: Shradha Gupta +Date: Mon, 9 Oct 2023 03:38:40 -0700 +Subject: [PATCH 1/4] hv/hv_kvp_daemon:Support for keyfile based connection + profile + +RH-Author: Ani Sinha +RH-MergeRequest: 8: hv/hv_kvp_daemon:Support for keyfile based connection profile +RH-Jira: RHEL-9902 +RH-Acked-by: Cathy Avery +RH-Acked-by: Miroslav Rezanina +RH-Commit: [1/4] 0cb079b0cc30bef47bddd193e262dfa52b7f2874 (anisinha/centos-hyperv-daemons) + +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. + +Cherry-picked from upstream linux +kernel commit 42999c904612 ("hv/hv_kvp_daemon:Support for keyfile based connection profile") +Tested-on: Rhel9(Hyper-V, Azure)(nm and ifcfg files verified) +Signed-off-by: Shradha Gupta +Reviewed-by: Saurabh Sengar +Reviewed-by: Ani Sinha +Signed-off-by: Wei Liu +Link: https://lore.kernel.org/r/1696847920-31125-1-git-send-email-shradhagupta@linux.microsoft.com +--- + hv_kvp_daemon.c | 233 +++++++++++++++++++++++++++++++++++++++------ + hv_set_ifconfig.sh | 30 +++++- + 2 files changed, 230 insertions(+), 33 deletions(-) + +diff --git a/hv_kvp_daemon.c b/hv_kvp_daemon.c +index 2ad9af3..d50b4e8 100644 +--- a/hv_kvp_daemon.c ++++ b/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; ++ 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= (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,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) { ++ 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/hv_set_ifconfig.sh b/hv_set_ifconfig.sh +index fe7fccf..35aae6f 100644 +--- a/hv_set_ifconfig.sh ++++ b/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 +-- +2.39.3 + diff --git a/SOURCES/hpvd-tools-hv-Enable-debug-logs-for-hv_kvp_daemon.patch b/SOURCES/hpvd-tools-hv-Enable-debug-logs-for-hv_kvp_daemon.patch new file mode 100644 index 0000000..c82a7eb --- /dev/null +++ b/SOURCES/hpvd-tools-hv-Enable-debug-logs-for-hv_kvp_daemon.patch @@ -0,0 +1,204 @@ +From 4ab581e5f376ecdd894a73e3284a32c1c3606577 Mon Sep 17 00:00:00 2001 +From: Shradha Gupta +Date: Tue, 15 Apr 2025 04:19:38 -0700 +Subject: [PATCH] tools: hv: Enable debug logs for hv_kvp_daemon + +RH-Author: Xuemin Li +RH-MergeRequest: 13: tools: hv: Enable debug logs for hv_kvp_daemon +RH-Jira: RHEL-95812 +RH-Acked-by: Vitaly Kuznetsov +RH-Acked-by: Ani Sinha +RH-Commit: [1/1] 7f88adf5ef8794d043811e66dba3e53dd7af2da9 + +Allow the KVP daemon to log the KVP updates triggered in the VM +with a new debug flag(-d). +When the daemon is started with this flag, it logs updates and debug +information in syslog with loglevel LOG_DEBUG. This information comes +in handy for debugging issues where the key-value pairs for certain +pools show mismatch/incorrect values. +The distro-vendors can further consume these changes and modify the +respective service files to redirect the logs to specific files as +needed. + +Signed-off-by: Shradha Gupta +Reviewed-by: Naman Jain +Reviewed-by: Dexuan Cui +Link: https://lore.kernel.org/r/1744715978-8185-1-git-send-email-shradhagupta@linux.microsoft.com +Signed-off-by: Wei Liu +Message-ID: <1744715978-8185-1-git-send-email-shradhagupta@linux.microsoft.com> +(cherry picked from commit a9c0b33ef2306327dd2db02c6274107065ff9307) +Signed-off-by: Xuemin Li +--- + hv_kvp_daemon.c | 64 +++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 59 insertions(+), 5 deletions(-) + +diff --git a/hv_kvp_daemon.c b/hv_kvp_daemon.c +index aa350d8..c8a55c9 100644 +--- a/hv_kvp_daemon.c ++++ b/hv_kvp_daemon.c +@@ -83,6 +83,7 @@ enum { + }; + + static int in_hand_shake; ++static int debug; + + static char *os_name = ""; + static char *os_major = ""; +@@ -183,6 +184,20 @@ static void kvp_update_file(int pool) + kvp_release_lock(pool); + } + ++static void kvp_dump_initial_pools(int pool) ++{ ++ int i; ++ ++ syslog(LOG_DEBUG, "===Start dumping the contents of pool %d ===\n", ++ pool); ++ ++ for (i = 0; i < kvp_file_info[pool].num_records; i++) ++ syslog(LOG_DEBUG, "pool: %d, %d/%d key=%s val=%s\n", ++ pool, i + 1, kvp_file_info[pool].num_records, ++ kvp_file_info[pool].records[i].key, ++ kvp_file_info[pool].records[i].value); ++} ++ + static void kvp_update_mem_state(int pool) + { + FILE *filep; +@@ -270,6 +285,8 @@ static int kvp_file_init(void) + return 1; + kvp_file_info[i].num_records = 0; + kvp_update_mem_state(i); ++ if (debug) ++ kvp_dump_initial_pools(i); + } + + return 0; +@@ -297,6 +314,9 @@ static int kvp_key_delete(int pool, const __u8 *key, int key_size) + * Found a match; just move the remaining + * entries up. + */ ++ if (debug) ++ syslog(LOG_DEBUG, "%s: deleting the KVP: pool=%d key=%s val=%s", ++ __func__, pool, record[i].key, record[i].value); + if (i == (num_records - 1)) { + kvp_file_info[pool].num_records--; + kvp_update_file(pool); +@@ -315,20 +335,36 @@ static int kvp_key_delete(int pool, const __u8 *key, int key_size) + kvp_update_file(pool); + return 0; + } ++ ++ if (debug) ++ syslog(LOG_DEBUG, "%s: could not delete KVP: pool=%d key=%s. Record not found", ++ __func__, pool, key); ++ + return 1; + } + + static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size, + const __u8 *value, int value_size) + { +- int i; +- int num_records; + struct kvp_record *record; ++ int num_records; + int num_blocks; ++ int i; ++ ++ if (debug) ++ syslog(LOG_DEBUG, "%s: got a KVP: pool=%d key=%s val=%s", ++ __func__, pool, key, value); + + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || +- (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) ++ (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) { ++ syslog(LOG_ERR, "%s: Too long key or value: key=%s, val=%s", ++ __func__, key, value); ++ ++ if (debug) ++ syslog(LOG_DEBUG, "%s: Too long key or value: pool=%d, key=%s, val=%s", ++ __func__, pool, key, value); + return 1; ++ } + + /* + * First update the in-memory state. +@@ -348,6 +384,9 @@ static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size, + */ + memcpy(record[i].value, value, value_size); + kvp_update_file(pool); ++ if (debug) ++ syslog(LOG_DEBUG, "%s: updated: pool=%d key=%s val=%s", ++ __func__, pool, key, value); + return 0; + } + +@@ -359,8 +398,10 @@ static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size, + record = realloc(record, sizeof(struct kvp_record) * + ENTRIES_PER_BLOCK * (num_blocks + 1)); + +- if (record == NULL) ++ if (!record) { ++ syslog(LOG_ERR, "%s: Memory alloc failure", __func__); + return 1; ++ } + kvp_file_info[pool].num_blocks++; + + } +@@ -368,6 +409,11 @@ static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size, + memcpy(record[i].key, key, key_size); + kvp_file_info[pool].records = record; + kvp_file_info[pool].num_records++; ++ ++ if (debug) ++ syslog(LOG_DEBUG, "%s: added: pool=%d key=%s val=%s", ++ __func__, pool, key, value); ++ + kvp_update_file(pool); + return 0; + } +@@ -1661,6 +1707,7 @@ void print_usage(char *argv[]) + fprintf(stderr, "Usage: %s [options]\n" + "Options are:\n" + " -n, --no-daemon stay in foreground, don't daemonize\n" ++ " -d, --debug Enable debug logs(syslog debug by default)\n" + " -h, --help print this help\n", argv[0]); + } + +@@ -1682,10 +1729,11 @@ int main(int argc, char *argv[]) + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"no-daemon", no_argument, 0, 'n' }, ++ {"debug", no_argument, 0, 'd' }, + {0, 0, 0, 0 } + }; + +- while ((opt = getopt_long(argc, argv, "hn", long_options, ++ while ((opt = getopt_long(argc, argv, "hnd", long_options, + &long_index)) != -1) { + switch (opt) { + case 'n': +@@ -1694,6 +1742,9 @@ int main(int argc, char *argv[]) + case 'h': + print_usage(argv); + exit(0); ++ case 'd': ++ debug = 1; ++ break; + default: + print_usage(argv); + exit(EXIT_FAILURE); +@@ -1716,6 +1767,9 @@ int main(int argc, char *argv[]) + */ + kvp_get_domain_name(full_domain_name, sizeof(full_domain_name)); + ++ if (debug) ++ syslog(LOG_INFO, "Logging debug info in syslog(debug)"); ++ + if (kvp_file_init()) { + syslog(LOG_ERR, "Failed to initialize the pools"); + exit(EXIT_FAILURE); +-- +2.48.1 + diff --git a/SPECS/hyperv-daemons.spec b/SPECS/hyperv-daemons.spec index 41ea4eb..4a4d244 100644 --- a/SPECS/hyperv-daemons.spec +++ b/SPECS/hyperv-daemons.spec @@ -13,7 +13,7 @@ Name: hyperv-daemons Version: 0 -Release: 0.42%{?snapver}%{?dist} +Release: 0.44%{?snapver}%{?dist} Summary: Hyper-V daemons suite License: GPLv2 @@ -60,6 +60,16 @@ Patch10: hpvd-tools-hv-Remove-an-extraneous-the.patch Patch11: hpvd-tools-hv-kvp-remove-unnecessary-void-conversions.patch # For bz#2218931 - [Hyper-V] [RHEL-9] /usr/sbin/vmbus_testing python script prints: "SyntaxWarning: "is" with a literal." Patch12: hpvd-vmbus_testing-fix-wrong-python-syntax-for-integer-va.patch +# For RHEL-9902 - [Hyper-V][RHEL-9] hyperv-daemons write incompatible IPv6 prefix (IPV6NETMASK) in ifcfg +Patch13: hpvd-hv-hv_kvp_daemon-Support-for-keyfile-based-connectio.patch +# For RHEL-9902 - [Hyper-V][RHEL-9] hyperv-daemons write incompatible IPv6 prefix (IPV6NETMASK) in ifcfg +Patch14: hpvd-hv-hv_kvp_daemon-Some-small-fixes-for-handling-NM-ke.patch +# For RHEL-9902 - [Hyper-V][RHEL-9] hyperv-daemons write incompatible IPv6 prefix (IPV6NETMASK) in ifcfg +Patch15: hpvd-hv-hv_kvp_daemon-Handle-IPv4-and-Ipv6-combination-fo.patch +# For RHEL-9902 - [Hyper-V][RHEL-9] hyperv-daemons write incompatible IPv6 prefix (IPV6NETMASK) in ifcfg +Patch16: hpvd-Changes-for-adding-keyfile-support-in-RHEL-specific-.patch +# For RHEL-95812 - [Hyper-V][RHEL-9]Backport tools: hv: Enable debug logs for hv_kvp_daemon for RHEL 9 +Patch17: hpvd-tools-hv-Enable-debug-logs-for-hv_kvp_daemon.patch # Source-git patches @@ -171,6 +181,11 @@ cp -pvL %{SOURCE301} lsvmbus %patch10 -p1 %patch11 -p1 %patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 %build # HYPERV KVP DAEMON @@ -288,6 +303,19 @@ fi %{_sbindir}/vmbus_testing %changelog +* Mon Jun 23 2025 Jon Maloy - 0-0.44.20190303git +- hpvd-tools-hv-Enable-debug-logs-for-hv_kvp_daemon.patch [RHEL-95812] +- Resolves: RHEL-95812 + ([Hyper-V][RHEL-9]Backport tools: hv: Enable debug logs for hv_kvp_daemon for RHEL 9) + +* Thu Apr 25 2024 Miroslav Rezanina - 0-0.43.20190303git +- hpvd-hv-hv_kvp_daemon-Support-for-keyfile-based-connectio.patch [RHEL-9902] +- hpvd-hv-hv_kvp_daemon-Some-small-fixes-for-handling-NM-ke.patch [RHEL-9902] +- hpvd-hv-hv_kvp_daemon-Handle-IPv4-and-Ipv6-combination-fo.patch [RHEL-9902] +- hpvd-Changes-for-adding-keyfile-support-in-RHEL-specific-.patch [RHEL-9902] +- Resolves: RHEL-9902 + ([Hyper-V][RHEL-9] hyperv-daemons write incompatible IPv6 prefix (IPV6NETMASK) in ifcfg) + * Mon Jul 10 2023 Miroslav Rezanina - 0-0.42.20190303git - hpvd-vmbus_testing-fix-wrong-python-syntax-for-integer-va.patch [bz#2218931] - Resolves: bz#2218931