2010-12-16 18:56:54 -07:00
|
|
|
/*
|
|
|
|
* An implementation of key value pair (KVP) functionality for Linux.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010, Novell, Inc.
|
|
|
|
* Author : K. Y. Srinivasan <ksrinivasan@novell.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 as published
|
|
|
|
* by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
|
|
|
|
* NON INFRINGEMENT. See the GNU General Public License for more
|
|
|
|
* details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <sys/poll.h>
|
|
|
|
#include <sys/utsname.h>
|
tools/hv: update route parsing in kvp daemon
After recent changes in the VM network stack, the host fails to
display the IP addresses of the VM. As a result the "IP Addresses"
column in the "Networking" tab in the Windows Hyper-V Manager is
empty. This is caused by a change in the expected output of the
"ip route show" command. Previously the gateway address was shown
in the third row. Now the gateway addresses might be split into
several lines of output. As a result, the string "ra" instead of
an IP address is sent to the host.
To me more specific, a VM with the wellknown wicked network
managing tool still shows the expected output in recent openSUSE
Tumbleweed snapshots:
ip a show dev uplink;ip -4 route show;ip -6 route show
2: uplink: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state ...
link/ether 00:15:5d:d0:93:08 brd ff:ff:ff:ff:ff:ff
inet 1.2.3.4/22 brd 1.2.3.255 scope global uplink
valid_lft forever preferred_lft forever
inet6 fe80::215:5dff:fed0:9308/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
default via 1.2.3.254 dev uplink proto dhcp
1.2.3.0/22 dev uplink proto kernel scope link src 1.2.3.4
fe80::/64 dev uplink proto kernel metric 256 pref medium
default via fe80::26fc:4e00:3b:74 dev uplink proto ra metric 1024 exp...
default via fe80::6a22:8e00:fb:14f8 dev uplink proto ra metric 1024 e...
A similar VM, but with NetworkManager as network managing tool:
ip a show dev eth0;ip -4 route show;ip -6 route show
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP...
link/ether 00:15:5d:d0:93:0b brd ff:ff:ff:ff:ff:ff
inet 1.2.3.8/22 brd 1.2.3.255 scope global dynamic noprefixroute ...
valid_lft 1022sec preferred_lft 1022sec
inet6 fe80::215:5dff:fed0:930b/64 scope link noprefixroute
valid_lft forever preferred_lft forever
default via 1.2.3.254 dev eth0 proto dhcp src 1.2.3.8 metric 100
1.2.3.0/22 dev eth0 proto kernel scope link src 1.2.3.8 metric 100
fe80::/64 dev eth0 proto kernel metric 1024 pref medium
default proto ra metric 20100 pref medium
nexthop via fe80::6a22:8e00:fb:14f8 dev eth0 weight 1
nexthop via fe80::26fc:4e00:3b:74 dev eth0 weight 1
Adjust the route parsing to use a single line for each line of
output. Also use a single shell invocation to retrieve both IPv4
and IPv6 information. The actual IP addresses are expected after
the "via" keyword.
Signed-off-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Shradha Gupta <shradhagupta@linux.microsoft.com>
Link: https://lore.kernel.org/r/20241202102235.9701-1-olaf@aepfle.de
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Message-ID: <20241202102235.9701-1-olaf@aepfle.de>
2024-12-02 11:19:55 +01:00
|
|
|
#include <stdbool.h>
|
2010-12-16 18:56:54 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
2012-09-05 13:50:13 -07:00
|
|
|
#include <ctype.h>
|
2010-12-16 18:56:54 -07:00
|
|
|
#include <errno.h>
|
|
|
|
#include <arpa/inet.h>
|
2012-02-02 16:56:49 -08:00
|
|
|
#include <linux/hyperv.h>
|
2010-12-16 18:56:54 -07:00
|
|
|
#include <ifaddrs.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <syslog.h>
|
2012-03-16 08:02:26 -07:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2012-09-05 13:50:13 -07:00
|
|
|
#include <dirent.h>
|
2012-10-25 14:15:50 -07:00
|
|
|
#include <net/if.h>
|
2017-04-30 16:21:15 -07:00
|
|
|
#include <limits.h>
|
2014-10-22 18:07:11 +02:00
|
|
|
#include <getopt.h>
|
2010-12-16 18:56:54 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* KVP protocol: The user mode component first registers with the
|
2022-08-11 21:34:33 +08:00
|
|
|
* kernel component. Subsequently, the kernel component requests, data
|
2010-12-16 18:56:54 -07:00
|
|
|
* for the specified keys. In response to this message the user mode component
|
|
|
|
* fills in the value corresponding to the specified key. We overload the
|
|
|
|
* sequence field in the cn_msg header to define our KVP message types.
|
|
|
|
*
|
|
|
|
* We use this infrastructure for also supporting queries from user mode
|
|
|
|
* application for state that may be maintained in the KVP kernel component.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
enum key_index {
|
|
|
|
FullyQualifiedDomainName = 0,
|
|
|
|
IntegrationServicesVersion, /*This key is serviced in the kernel*/
|
|
|
|
NetworkAddressIPv4,
|
|
|
|
NetworkAddressIPv6,
|
|
|
|
OSBuildNumber,
|
|
|
|
OSName,
|
|
|
|
OSMajorVersion,
|
|
|
|
OSMinorVersion,
|
|
|
|
OSVersion,
|
|
|
|
ProcessorArchitecture
|
|
|
|
};
|
|
|
|
|
2012-09-05 13:50:13 -07:00
|
|
|
|
|
|
|
enum {
|
|
|
|
IPADDR = 0,
|
|
|
|
NETMASK,
|
|
|
|
GATEWAY,
|
|
|
|
DNS
|
|
|
|
};
|
|
|
|
|
2024-03-22 06:46:02 -07:00
|
|
|
enum {
|
|
|
|
IPV4 = 1,
|
|
|
|
IPV6,
|
|
|
|
IP_TYPE_MAX
|
|
|
|
};
|
|
|
|
|
2020-01-25 21:49:41 -08:00
|
|
|
static int in_hand_shake;
|
2025-04-15 04:19:38 -07:00
|
|
|
static int debug;
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2011-03-22 10:02:17 +01:00
|
|
|
static char *os_name = "";
|
|
|
|
static char *os_major = "";
|
|
|
|
static char *os_minor = "";
|
|
|
|
static char *processor_arch;
|
|
|
|
static char *os_build;
|
2012-10-25 14:15:49 -07:00
|
|
|
static char *os_version;
|
2012-08-13 10:06:52 -07:00
|
|
|
static char *lic_version = "Unknown version";
|
2013-08-07 19:14:37 +02:00
|
|
|
static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
|
2011-03-22 10:02:17 +01:00
|
|
|
static struct utsname uts_buf;
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2012-09-05 13:50:13 -07:00
|
|
|
/*
|
|
|
|
* The location of the interface configuration file.
|
|
|
|
*/
|
|
|
|
|
2012-11-27 08:56:33 +01:00
|
|
|
#define KVP_CONFIG_LOC "/var/lib/hyperv"
|
2012-03-16 08:02:26 -07:00
|
|
|
|
2016-12-03 12:34:41 -08:00
|
|
|
#ifndef KVP_SCRIPTS_PATH
|
|
|
|
#define KVP_SCRIPTS_PATH "/usr/libexec/hypervkvpd/"
|
|
|
|
#endif
|
|
|
|
|
2017-04-30 16:21:15 -07:00
|
|
|
#define KVP_NET_DIR "/sys/class/net/"
|
|
|
|
|
2012-03-16 08:02:26 -07:00
|
|
|
#define MAX_FILE_NAME 100
|
|
|
|
#define ENTRIES_PER_BLOCK 50
|
2024-03-22 06:46:02 -07:00
|
|
|
/*
|
|
|
|
* 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)
|
2012-03-16 08:02:26 -07:00
|
|
|
|
|
|
|
struct kvp_record {
|
2012-09-04 14:46:34 -07:00
|
|
|
char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
|
|
|
|
char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
|
2012-03-16 08:02:26 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct kvp_file_state {
|
|
|
|
int fd;
|
|
|
|
int num_blocks;
|
|
|
|
struct kvp_record *records;
|
|
|
|
int num_records;
|
2012-09-04 14:46:34 -07:00
|
|
|
char fname[MAX_FILE_NAME];
|
2012-03-16 08:02:26 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
|
|
|
|
|
|
|
|
static void kvp_acquire_lock(int pool)
|
|
|
|
{
|
|
|
|
struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
|
|
|
|
fl.l_pid = getpid();
|
|
|
|
|
|
|
|
if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
|
2013-06-17 10:39:44 +02:00
|
|
|
syslog(LOG_ERR, "Failed to acquire the lock pool: %d; error: %d %s", pool,
|
|
|
|
errno, strerror(errno));
|
2012-09-05 14:37:36 -07:00
|
|
|
exit(EXIT_FAILURE);
|
2012-03-16 08:02:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void kvp_release_lock(int pool)
|
|
|
|
{
|
|
|
|
struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
|
|
|
|
fl.l_pid = getpid();
|
|
|
|
|
|
|
|
if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
|
2013-06-17 10:39:44 +02:00
|
|
|
syslog(LOG_ERR, "Failed to release the lock pool: %d; error: %d %s", pool,
|
|
|
|
errno, strerror(errno));
|
2012-09-05 14:37:36 -07:00
|
|
|
exit(EXIT_FAILURE);
|
2012-03-16 08:02:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void kvp_update_file(int pool)
|
|
|
|
{
|
|
|
|
FILE *filep;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We are going to write our in-memory registry out to
|
|
|
|
* disk; acquire the lock first.
|
|
|
|
*/
|
|
|
|
kvp_acquire_lock(pool);
|
|
|
|
|
2013-01-18 15:23:41 +01:00
|
|
|
filep = fopen(kvp_file_info[pool].fname, "we");
|
2012-03-16 08:02:26 -07:00
|
|
|
if (!filep) {
|
2013-06-17 10:39:44 +02:00
|
|
|
syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool,
|
|
|
|
errno, strerror(errno));
|
2012-03-16 08:02:26 -07:00
|
|
|
kvp_release_lock(pool);
|
2012-09-05 14:37:36 -07:00
|
|
|
exit(EXIT_FAILURE);
|
2012-03-16 08:02:26 -07:00
|
|
|
}
|
|
|
|
|
2015-01-09 22:18:52 -08:00
|
|
|
fwrite(kvp_file_info[pool].records, sizeof(struct kvp_record),
|
2012-03-16 08:02:26 -07:00
|
|
|
kvp_file_info[pool].num_records, filep);
|
|
|
|
|
2012-09-05 14:37:37 -07:00
|
|
|
if (ferror(filep) || fclose(filep)) {
|
|
|
|
kvp_release_lock(pool);
|
|
|
|
syslog(LOG_ERR, "Failed to write file, pool: %d", pool);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2012-03-16 08:02:26 -07:00
|
|
|
kvp_release_lock(pool);
|
|
|
|
}
|
|
|
|
|
2025-04-15 04:19:38 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-03-16 08:02:27 -07:00
|
|
|
static void kvp_update_mem_state(int pool)
|
|
|
|
{
|
|
|
|
FILE *filep;
|
|
|
|
size_t records_read = 0;
|
|
|
|
struct kvp_record *record = kvp_file_info[pool].records;
|
|
|
|
struct kvp_record *readp;
|
|
|
|
int num_blocks = kvp_file_info[pool].num_blocks;
|
|
|
|
int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
|
|
|
|
|
|
|
|
kvp_acquire_lock(pool);
|
|
|
|
|
2013-01-18 15:23:41 +01:00
|
|
|
filep = fopen(kvp_file_info[pool].fname, "re");
|
2012-03-16 08:02:27 -07:00
|
|
|
if (!filep) {
|
2013-06-17 10:39:44 +02:00
|
|
|
syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool,
|
|
|
|
errno, strerror(errno));
|
2012-03-16 08:02:27 -07:00
|
|
|
kvp_release_lock(pool);
|
2012-09-05 14:37:36 -07:00
|
|
|
exit(EXIT_FAILURE);
|
2012-03-16 08:02:27 -07:00
|
|
|
}
|
2012-09-05 14:37:37 -07:00
|
|
|
for (;;) {
|
2012-03-16 08:02:27 -07:00
|
|
|
readp = &record[records_read];
|
|
|
|
records_read += fread(readp, sizeof(struct kvp_record),
|
2017-11-14 13:06:47 -07:00
|
|
|
ENTRIES_PER_BLOCK * num_blocks - records_read,
|
|
|
|
filep);
|
2012-03-16 08:02:27 -07:00
|
|
|
|
2012-09-05 14:37:37 -07:00
|
|
|
if (ferror(filep)) {
|
2017-11-14 13:06:47 -07:00
|
|
|
syslog(LOG_ERR,
|
|
|
|
"Failed to read file, pool: %d; error: %d %s",
|
|
|
|
pool, errno, strerror(errno));
|
|
|
|
kvp_release_lock(pool);
|
2012-09-05 14:37:37 -07:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2012-03-16 08:02:27 -07:00
|
|
|
if (!feof(filep)) {
|
|
|
|
/*
|
|
|
|
* We have more data to read.
|
|
|
|
*/
|
|
|
|
num_blocks++;
|
|
|
|
record = realloc(record, alloc_unit * num_blocks);
|
|
|
|
|
|
|
|
if (record == NULL) {
|
|
|
|
syslog(LOG_ERR, "malloc failed");
|
2017-11-14 13:06:47 -07:00
|
|
|
kvp_release_lock(pool);
|
2012-09-05 14:37:36 -07:00
|
|
|
exit(EXIT_FAILURE);
|
2012-03-16 08:02:27 -07:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
kvp_file_info[pool].num_blocks = num_blocks;
|
|
|
|
kvp_file_info[pool].records = record;
|
|
|
|
kvp_file_info[pool].num_records = records_read;
|
|
|
|
|
2012-09-05 14:37:35 -07:00
|
|
|
fclose(filep);
|
2012-03-16 08:02:27 -07:00
|
|
|
kvp_release_lock(pool);
|
|
|
|
}
|
2017-11-14 13:06:47 -07:00
|
|
|
|
2012-03-16 08:02:26 -07:00
|
|
|
static int kvp_file_init(void)
|
|
|
|
{
|
2012-09-04 14:46:33 -07:00
|
|
|
int fd;
|
2012-09-04 14:46:34 -07:00
|
|
|
char *fname;
|
2012-03-16 08:02:26 -07:00
|
|
|
int i;
|
|
|
|
int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
|
|
|
|
|
2012-11-27 08:56:33 +01:00
|
|
|
if (access(KVP_CONFIG_LOC, F_OK)) {
|
2012-11-27 08:56:34 +01:00
|
|
|
if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) {
|
2013-06-17 10:39:44 +02:00
|
|
|
syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC,
|
|
|
|
errno, strerror(errno));
|
2012-09-05 14:37:36 -07:00
|
|
|
exit(EXIT_FAILURE);
|
2012-03-16 08:02:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < KVP_POOL_COUNT; i++) {
|
|
|
|
fname = kvp_file_info[i].fname;
|
2012-11-27 08:56:33 +01:00
|
|
|
sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i);
|
2013-01-18 15:23:41 +01:00
|
|
|
fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */);
|
2012-03-16 08:02:26 -07:00
|
|
|
|
|
|
|
if (fd == -1)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
kvp_file_info[i].fd = fd;
|
2017-11-14 13:06:47 -07:00
|
|
|
kvp_file_info[i].num_blocks = 1;
|
|
|
|
kvp_file_info[i].records = malloc(alloc_unit);
|
|
|
|
if (kvp_file_info[i].records == NULL)
|
|
|
|
return 1;
|
|
|
|
kvp_file_info[i].num_records = 0;
|
|
|
|
kvp_update_mem_state(i);
|
2025-04-15 04:19:38 -07:00
|
|
|
if (debug)
|
|
|
|
kvp_dump_initial_pools(i);
|
2012-03-16 08:02:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-09 22:18:53 -08:00
|
|
|
static int kvp_key_delete(int pool, const __u8 *key, int key_size)
|
2012-03-16 08:02:26 -07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int j, k;
|
2012-03-16 08:02:27 -07:00
|
|
|
int num_records;
|
|
|
|
struct kvp_record *record;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First update the in-memory state.
|
|
|
|
*/
|
|
|
|
kvp_update_mem_state(pool);
|
|
|
|
|
|
|
|
num_records = kvp_file_info[pool].num_records;
|
|
|
|
record = kvp_file_info[pool].records;
|
2012-03-16 08:02:26 -07:00
|
|
|
|
|
|
|
for (i = 0; i < num_records; i++) {
|
|
|
|
if (memcmp(key, record[i].key, key_size))
|
|
|
|
continue;
|
|
|
|
/*
|
|
|
|
* Found a match; just move the remaining
|
|
|
|
* entries up.
|
|
|
|
*/
|
2025-04-15 04:19:38 -07:00
|
|
|
if (debug)
|
|
|
|
syslog(LOG_DEBUG, "%s: deleting the KVP: pool=%d key=%s val=%s",
|
|
|
|
__func__, pool, record[i].key, record[i].value);
|
2018-08-10 23:06:07 +00:00
|
|
|
if (i == (num_records - 1)) {
|
2012-03-16 08:02:26 -07:00
|
|
|
kvp_file_info[pool].num_records--;
|
|
|
|
kvp_update_file(pool);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
j = i;
|
|
|
|
k = j + 1;
|
|
|
|
for (; k < num_records; k++) {
|
|
|
|
strcpy(record[j].key, record[k].key);
|
|
|
|
strcpy(record[j].value, record[k].value);
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
|
|
|
kvp_file_info[pool].num_records--;
|
|
|
|
kvp_update_file(pool);
|
|
|
|
return 0;
|
|
|
|
}
|
2025-04-15 04:19:38 -07:00
|
|
|
|
|
|
|
if (debug)
|
|
|
|
syslog(LOG_DEBUG, "%s: could not delete KVP: pool=%d key=%s. Record not found",
|
|
|
|
__func__, pool, key);
|
|
|
|
|
2012-03-16 08:02:26 -07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-01-09 22:18:53 -08:00
|
|
|
static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size,
|
|
|
|
const __u8 *value, int value_size)
|
2012-03-16 08:02:26 -07:00
|
|
|
{
|
2012-03-16 08:02:27 -07:00
|
|
|
struct kvp_record *record;
|
2025-04-15 04:19:38 -07:00
|
|
|
int num_records;
|
2012-03-16 08:02:27 -07:00
|
|
|
int num_blocks;
|
2025-04-15 04:19:38 -07:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
syslog(LOG_DEBUG, "%s: got a KVP: pool=%d key=%s val=%s",
|
|
|
|
__func__, pool, key, value);
|
2012-03-16 08:02:26 -07:00
|
|
|
|
|
|
|
if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
|
2025-04-15 04:19:38 -07:00
|
|
|
(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);
|
2012-03-16 08:02:26 -07:00
|
|
|
return 1;
|
2025-04-15 04:19:38 -07:00
|
|
|
}
|
2012-03-16 08:02:26 -07:00
|
|
|
|
2012-03-16 08:02:27 -07:00
|
|
|
/*
|
|
|
|
* First update the in-memory state.
|
|
|
|
*/
|
|
|
|
kvp_update_mem_state(pool);
|
|
|
|
|
|
|
|
num_records = kvp_file_info[pool].num_records;
|
|
|
|
record = kvp_file_info[pool].records;
|
|
|
|
num_blocks = kvp_file_info[pool].num_blocks;
|
|
|
|
|
2012-03-16 08:02:26 -07:00
|
|
|
for (i = 0; i < num_records; i++) {
|
|
|
|
if (memcmp(key, record[i].key, key_size))
|
|
|
|
continue;
|
|
|
|
/*
|
|
|
|
* Found a match; just update the value -
|
|
|
|
* this is the modify case.
|
|
|
|
*/
|
|
|
|
memcpy(record[i].value, value, value_size);
|
|
|
|
kvp_update_file(pool);
|
2025-04-15 04:19:38 -07:00
|
|
|
if (debug)
|
|
|
|
syslog(LOG_DEBUG, "%s: updated: pool=%d key=%s val=%s",
|
|
|
|
__func__, pool, key, value);
|
2012-03-16 08:02:26 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Need to add a new entry;
|
|
|
|
*/
|
|
|
|
if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {
|
|
|
|
/* Need to allocate a larger array for reg entries. */
|
|
|
|
record = realloc(record, sizeof(struct kvp_record) *
|
|
|
|
ENTRIES_PER_BLOCK * (num_blocks + 1));
|
|
|
|
|
2025-04-15 04:19:38 -07:00
|
|
|
if (!record) {
|
|
|
|
syslog(LOG_ERR, "%s: Memory alloc failure", __func__);
|
2012-03-16 08:02:26 -07:00
|
|
|
return 1;
|
2025-04-15 04:19:38 -07:00
|
|
|
}
|
2012-03-16 08:02:26 -07:00
|
|
|
kvp_file_info[pool].num_blocks++;
|
|
|
|
|
|
|
|
}
|
|
|
|
memcpy(record[i].value, value, value_size);
|
|
|
|
memcpy(record[i].key, key, key_size);
|
|
|
|
kvp_file_info[pool].records = record;
|
|
|
|
kvp_file_info[pool].num_records++;
|
2025-04-15 04:19:38 -07:00
|
|
|
|
|
|
|
if (debug)
|
|
|
|
syslog(LOG_DEBUG, "%s: added: pool=%d key=%s val=%s",
|
|
|
|
__func__, pool, key, value);
|
|
|
|
|
2012-03-16 08:02:26 -07:00
|
|
|
kvp_update_file(pool);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-09 22:18:53 -08:00
|
|
|
static int kvp_get_value(int pool, const __u8 *key, int key_size, __u8 *value,
|
2012-03-16 08:02:26 -07:00
|
|
|
int value_size)
|
|
|
|
{
|
|
|
|
int i;
|
2012-03-16 08:02:27 -07:00
|
|
|
int num_records;
|
|
|
|
struct kvp_record *record;
|
2012-03-16 08:02:26 -07:00
|
|
|
|
|
|
|
if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
|
|
|
|
(value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
|
|
|
|
return 1;
|
|
|
|
|
2012-03-16 08:02:27 -07:00
|
|
|
/*
|
|
|
|
* First update the in-memory state.
|
|
|
|
*/
|
|
|
|
kvp_update_mem_state(pool);
|
|
|
|
|
|
|
|
num_records = kvp_file_info[pool].num_records;
|
|
|
|
record = kvp_file_info[pool].records;
|
|
|
|
|
2012-03-16 08:02:26 -07:00
|
|
|
for (i = 0; i < num_records; i++) {
|
|
|
|
if (memcmp(key, record[i].key, key_size))
|
|
|
|
continue;
|
|
|
|
/*
|
|
|
|
* Found a match; just copy the value out.
|
|
|
|
*/
|
|
|
|
memcpy(value, record[i].value, value_size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-01-09 22:18:53 -08:00
|
|
|
static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
|
|
|
|
__u8 *value, int value_size)
|
2012-03-16 08:02:27 -07:00
|
|
|
{
|
|
|
|
struct kvp_record *record;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First update our in-memory database.
|
|
|
|
*/
|
|
|
|
kvp_update_mem_state(pool);
|
|
|
|
record = kvp_file_info[pool].records;
|
|
|
|
|
|
|
|
if (index >= kvp_file_info[pool].num_records) {
|
2012-08-13 10:06:52 -07:00
|
|
|
return 1;
|
2012-03-16 08:02:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(key, record[index].key, key_size);
|
|
|
|
memcpy(value, record[index].value, value_size);
|
2012-08-13 10:06:52 -07:00
|
|
|
return 0;
|
2012-03-16 08:02:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-16 18:56:54 -07:00
|
|
|
void kvp_get_os_info(void)
|
|
|
|
{
|
|
|
|
FILE *file;
|
2011-03-22 10:02:17 +01:00
|
|
|
char *p, buf[512];
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2011-03-22 10:02:17 +01:00
|
|
|
uname(&uts_buf);
|
2012-10-25 14:15:49 -07:00
|
|
|
os_version = uts_buf.release;
|
|
|
|
os_build = strdup(uts_buf.release);
|
|
|
|
|
2012-09-06 13:32:14 -07:00
|
|
|
os_name = uts_buf.sysname;
|
2011-07-19 11:44:20 -07:00
|
|
|
processor_arch = uts_buf.machine;
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2011-07-22 10:14:31 -07:00
|
|
|
/*
|
|
|
|
* The current windows host (win7) expects the build
|
|
|
|
* string to be of the form: x.y.z
|
|
|
|
* Strip additional information we may have.
|
|
|
|
*/
|
2012-10-25 14:15:49 -07:00
|
|
|
p = strchr(os_version, '-');
|
2011-07-22 10:14:31 -07:00
|
|
|
if (p)
|
|
|
|
*p = '\0';
|
|
|
|
|
2012-09-06 13:32:14 -07:00
|
|
|
/*
|
|
|
|
* Parse the /etc/os-release file if present:
|
2020-07-05 23:44:57 +02:00
|
|
|
* https://www.freedesktop.org/software/systemd/man/os-release.html
|
2012-09-06 13:32:14 -07:00
|
|
|
*/
|
|
|
|
file = fopen("/etc/os-release", "r");
|
|
|
|
if (file != NULL) {
|
|
|
|
while (fgets(buf, sizeof(buf), file)) {
|
|
|
|
char *value, *q;
|
|
|
|
|
|
|
|
/* Ignore comments */
|
|
|
|
if (buf[0] == '#')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Split into name=value */
|
|
|
|
p = strchr(buf, '=');
|
|
|
|
if (!p)
|
|
|
|
continue;
|
|
|
|
*p++ = 0;
|
|
|
|
|
|
|
|
/* Remove quotes and newline; un-escape */
|
|
|
|
value = p;
|
|
|
|
q = p;
|
|
|
|
while (*p) {
|
|
|
|
if (*p == '\\') {
|
|
|
|
++p;
|
|
|
|
if (!*p)
|
|
|
|
break;
|
|
|
|
*q++ = *p++;
|
|
|
|
} else if (*p == '\'' || *p == '"' ||
|
|
|
|
*p == '\n') {
|
|
|
|
++p;
|
|
|
|
} else {
|
|
|
|
*q++ = *p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*q = 0;
|
|
|
|
|
|
|
|
if (!strcmp(buf, "NAME")) {
|
|
|
|
p = strdup(value);
|
|
|
|
if (!p)
|
|
|
|
break;
|
|
|
|
os_name = p;
|
|
|
|
} else if (!strcmp(buf, "VERSION_ID")) {
|
|
|
|
p = strdup(value);
|
|
|
|
if (!p)
|
|
|
|
break;
|
|
|
|
os_major = p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fallback for older RH/SUSE releases */
|
2010-12-16 18:56:54 -07:00
|
|
|
file = fopen("/etc/SuSE-release", "r");
|
|
|
|
if (file != NULL)
|
|
|
|
goto kvp_osinfo_found;
|
|
|
|
file = fopen("/etc/redhat-release", "r");
|
|
|
|
if (file != NULL)
|
|
|
|
goto kvp_osinfo_found;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't have information about the os.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
|
|
|
|
kvp_osinfo_found:
|
2011-03-22 10:02:17 +01:00
|
|
|
/* up to three lines */
|
|
|
|
p = fgets(buf, sizeof(buf), file);
|
|
|
|
if (p) {
|
|
|
|
p = strchr(buf, '\n');
|
|
|
|
if (p)
|
|
|
|
*p = '\0';
|
|
|
|
p = strdup(buf);
|
|
|
|
if (!p)
|
|
|
|
goto done;
|
|
|
|
os_name = p;
|
|
|
|
|
|
|
|
/* second line */
|
|
|
|
p = fgets(buf, sizeof(buf), file);
|
|
|
|
if (p) {
|
|
|
|
p = strchr(buf, '\n');
|
|
|
|
if (p)
|
|
|
|
*p = '\0';
|
|
|
|
p = strdup(buf);
|
|
|
|
if (!p)
|
|
|
|
goto done;
|
|
|
|
os_major = p;
|
|
|
|
|
|
|
|
/* third line */
|
|
|
|
p = fgets(buf, sizeof(buf), file);
|
|
|
|
if (p) {
|
|
|
|
p = strchr(buf, '\n');
|
|
|
|
if (p)
|
|
|
|
*p = '\0';
|
|
|
|
p = strdup(buf);
|
|
|
|
if (p)
|
|
|
|
os_minor = p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2010-12-16 18:56:54 -07:00
|
|
|
fclose(file);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-09-05 13:50:13 -07:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Retrieve an interface name corresponding to the specified guid.
|
|
|
|
* If there is a match, the function returns a pointer
|
|
|
|
* to the interface name and if not, a NULL is returned.
|
|
|
|
* If a match is found, the caller is responsible for
|
|
|
|
* freeing the memory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static char *kvp_get_if_name(char *guid)
|
|
|
|
{
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *entry;
|
|
|
|
FILE *file;
|
2017-04-30 16:21:15 -07:00
|
|
|
char *p, *x;
|
2012-09-05 13:50:13 -07:00
|
|
|
char *if_name = NULL;
|
|
|
|
char buf[256];
|
2017-04-30 16:21:15 -07:00
|
|
|
char dev_id[PATH_MAX];
|
2012-09-05 13:50:13 -07:00
|
|
|
|
2017-04-30 16:21:15 -07:00
|
|
|
dir = opendir(KVP_NET_DIR);
|
2012-09-05 13:50:13 -07:00
|
|
|
if (dir == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
/*
|
|
|
|
* Set the state for the next pass.
|
|
|
|
*/
|
2017-04-30 16:21:15 -07:00
|
|
|
snprintf(dev_id, sizeof(dev_id), "%s%s/device/device_id",
|
|
|
|
KVP_NET_DIR, entry->d_name);
|
2012-09-05 13:50:13 -07:00
|
|
|
|
|
|
|
file = fopen(dev_id, "r");
|
|
|
|
if (file == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
p = fgets(buf, sizeof(buf), file);
|
|
|
|
if (p) {
|
|
|
|
x = strchr(p, '\n');
|
|
|
|
if (x)
|
|
|
|
*x = '\0';
|
|
|
|
|
|
|
|
if (!strcmp(p, guid)) {
|
|
|
|
/*
|
|
|
|
* Found the guid match; return the interface
|
|
|
|
* name. The caller will free the memory.
|
|
|
|
*/
|
|
|
|
if_name = strdup(entry->d_name);
|
|
|
|
fclose(file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
return if_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Retrieve the MAC address given the interface name.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static char *kvp_if_name_to_mac(char *if_name)
|
|
|
|
{
|
|
|
|
FILE *file;
|
|
|
|
char *p, *x;
|
|
|
|
char buf[256];
|
2017-04-30 16:21:15 -07:00
|
|
|
char addr_file[PATH_MAX];
|
2015-01-09 22:18:53 -08:00
|
|
|
unsigned int i;
|
2012-09-05 13:50:13 -07:00
|
|
|
char *mac_addr = NULL;
|
|
|
|
|
2017-04-30 16:21:15 -07:00
|
|
|
snprintf(addr_file, sizeof(addr_file), "%s%s%s", KVP_NET_DIR,
|
|
|
|
if_name, "/address");
|
2012-09-05 13:50:13 -07:00
|
|
|
|
|
|
|
file = fopen(addr_file, "r");
|
|
|
|
if (file == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
p = fgets(buf, sizeof(buf), file);
|
|
|
|
if (p) {
|
|
|
|
x = strchr(p, '\n');
|
|
|
|
if (x)
|
|
|
|
*x = '\0';
|
|
|
|
for (i = 0; i < strlen(p); i++)
|
|
|
|
p[i] = toupper(p[i]);
|
|
|
|
mac_addr = strdup(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
return mac_addr;
|
|
|
|
}
|
|
|
|
|
2012-08-16 18:32:18 -07:00
|
|
|
static void kvp_process_ipconfig_file(char *cmd,
|
2015-01-09 22:18:53 -08:00
|
|
|
char *config_buf, unsigned int len,
|
2012-08-16 18:32:18 -07:00
|
|
|
int element_size, int offset)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
char *p;
|
|
|
|
char *x;
|
|
|
|
FILE *file;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First execute the command.
|
|
|
|
*/
|
|
|
|
file = popen(cmd, "r");
|
|
|
|
if (file == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (offset == 0)
|
|
|
|
memset(config_buf, 0, len);
|
|
|
|
while ((p = fgets(buf, sizeof(buf), file)) != NULL) {
|
2015-01-09 22:18:53 -08:00
|
|
|
if (len < strlen(config_buf) + element_size + 1)
|
2012-08-16 18:32:18 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
x = strchr(p, '\n');
|
2013-05-22 14:54:32 +02:00
|
|
|
if (x)
|
|
|
|
*x = '\0';
|
|
|
|
|
2012-08-16 18:32:18 -07:00
|
|
|
strcat(config_buf, p);
|
|
|
|
strcat(config_buf, ";");
|
|
|
|
}
|
|
|
|
pclose(file);
|
|
|
|
}
|
|
|
|
|
tools/hv: update route parsing in kvp daemon
After recent changes in the VM network stack, the host fails to
display the IP addresses of the VM. As a result the "IP Addresses"
column in the "Networking" tab in the Windows Hyper-V Manager is
empty. This is caused by a change in the expected output of the
"ip route show" command. Previously the gateway address was shown
in the third row. Now the gateway addresses might be split into
several lines of output. As a result, the string "ra" instead of
an IP address is sent to the host.
To me more specific, a VM with the wellknown wicked network
managing tool still shows the expected output in recent openSUSE
Tumbleweed snapshots:
ip a show dev uplink;ip -4 route show;ip -6 route show
2: uplink: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state ...
link/ether 00:15:5d:d0:93:08 brd ff:ff:ff:ff:ff:ff
inet 1.2.3.4/22 brd 1.2.3.255 scope global uplink
valid_lft forever preferred_lft forever
inet6 fe80::215:5dff:fed0:9308/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
default via 1.2.3.254 dev uplink proto dhcp
1.2.3.0/22 dev uplink proto kernel scope link src 1.2.3.4
fe80::/64 dev uplink proto kernel metric 256 pref medium
default via fe80::26fc:4e00:3b:74 dev uplink proto ra metric 1024 exp...
default via fe80::6a22:8e00:fb:14f8 dev uplink proto ra metric 1024 e...
A similar VM, but with NetworkManager as network managing tool:
ip a show dev eth0;ip -4 route show;ip -6 route show
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP...
link/ether 00:15:5d:d0:93:0b brd ff:ff:ff:ff:ff:ff
inet 1.2.3.8/22 brd 1.2.3.255 scope global dynamic noprefixroute ...
valid_lft 1022sec preferred_lft 1022sec
inet6 fe80::215:5dff:fed0:930b/64 scope link noprefixroute
valid_lft forever preferred_lft forever
default via 1.2.3.254 dev eth0 proto dhcp src 1.2.3.8 metric 100
1.2.3.0/22 dev eth0 proto kernel scope link src 1.2.3.8 metric 100
fe80::/64 dev eth0 proto kernel metric 1024 pref medium
default proto ra metric 20100 pref medium
nexthop via fe80::6a22:8e00:fb:14f8 dev eth0 weight 1
nexthop via fe80::26fc:4e00:3b:74 dev eth0 weight 1
Adjust the route parsing to use a single line for each line of
output. Also use a single shell invocation to retrieve both IPv4
and IPv6 information. The actual IP addresses are expected after
the "via" keyword.
Signed-off-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Shradha Gupta <shradhagupta@linux.microsoft.com>
Link: https://lore.kernel.org/r/20241202102235.9701-1-olaf@aepfle.de
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Message-ID: <20241202102235.9701-1-olaf@aepfle.de>
2024-12-02 11:19:55 +01:00
|
|
|
static bool kvp_verify_ip_address(const void *address_string)
|
|
|
|
{
|
|
|
|
char verify_buf[sizeof(struct in6_addr)];
|
|
|
|
|
|
|
|
if (inet_pton(AF_INET, address_string, verify_buf) == 1)
|
|
|
|
return true;
|
|
|
|
if (inet_pton(AF_INET6, address_string, verify_buf) == 1)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void kvp_extract_routes(const char *line, void **output, size_t *remaining)
|
|
|
|
{
|
|
|
|
static const char needle[] = "via ";
|
|
|
|
const char *match, *haystack = line;
|
|
|
|
|
|
|
|
while ((match = strstr(haystack, needle))) {
|
|
|
|
const char *address, *next_char;
|
|
|
|
|
|
|
|
/* Address starts after needle. */
|
|
|
|
address = match + strlen(needle);
|
|
|
|
|
|
|
|
/* The char following address is a space or end of line. */
|
|
|
|
next_char = strpbrk(address, " \t\\");
|
|
|
|
if (!next_char)
|
|
|
|
next_char = address + strlen(address) + 1;
|
|
|
|
|
|
|
|
/* Enough room for address and semicolon. */
|
|
|
|
if (*remaining >= (next_char - address) + 1) {
|
|
|
|
memcpy(*output, address, next_char - address);
|
|
|
|
/* Terminate string for verification. */
|
|
|
|
memcpy(*output + (next_char - address), "", 1);
|
|
|
|
if (kvp_verify_ip_address(*output)) {
|
|
|
|
/* Advance output buffer. */
|
|
|
|
*output += next_char - address;
|
|
|
|
*remaining -= next_char - address;
|
|
|
|
|
|
|
|
/* Each address needs a trailing semicolon. */
|
|
|
|
memcpy(*output, ";", 1);
|
|
|
|
*output += 1;
|
|
|
|
*remaining -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
haystack = next_char;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void kvp_get_gateway(void *buffer, size_t buffer_len)
|
|
|
|
{
|
|
|
|
static const char needle[] = "default ";
|
|
|
|
FILE *f;
|
|
|
|
void *output = buffer;
|
|
|
|
char *line = NULL;
|
|
|
|
size_t alloc_size = 0, remaining = buffer_len - 1;
|
|
|
|
ssize_t num_chars;
|
|
|
|
|
|
|
|
/* Show route information in a single line, for each address family */
|
|
|
|
f = popen("ip --oneline -4 route show;ip --oneline -6 route show", "r");
|
|
|
|
if (!f) {
|
|
|
|
/* Convert buffer into C-String. */
|
|
|
|
memcpy(output, "", 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
while ((num_chars = getline(&line, &alloc_size, f)) > 0) {
|
|
|
|
/* Skip short lines. */
|
|
|
|
if (num_chars <= strlen(needle))
|
|
|
|
continue;
|
|
|
|
/* Skip lines without default route. */
|
|
|
|
if (memcmp(line, needle, strlen(needle)))
|
|
|
|
continue;
|
|
|
|
/* Remove trailing newline to simplify further parsing. */
|
|
|
|
if (line[num_chars - 1] == '\n')
|
|
|
|
line[num_chars - 1] = '\0';
|
|
|
|
/* Search routes after match. */
|
|
|
|
kvp_extract_routes(line + strlen(needle), &output, &remaining);
|
|
|
|
}
|
|
|
|
/* Convert buffer into C-String. */
|
|
|
|
memcpy(output, "", 1);
|
|
|
|
free(line);
|
|
|
|
pclose(f);
|
|
|
|
}
|
|
|
|
|
2012-08-16 18:32:18 -07:00
|
|
|
static void kvp_get_ipconfig_info(char *if_name,
|
|
|
|
struct hv_kvp_ipaddr_value *buffer)
|
|
|
|
{
|
|
|
|
char cmd[512];
|
2012-09-05 13:50:11 -07:00
|
|
|
char dhcp_info[128];
|
|
|
|
char *p;
|
|
|
|
FILE *file;
|
2012-08-16 18:32:18 -07:00
|
|
|
|
tools/hv: update route parsing in kvp daemon
After recent changes in the VM network stack, the host fails to
display the IP addresses of the VM. As a result the "IP Addresses"
column in the "Networking" tab in the Windows Hyper-V Manager is
empty. This is caused by a change in the expected output of the
"ip route show" command. Previously the gateway address was shown
in the third row. Now the gateway addresses might be split into
several lines of output. As a result, the string "ra" instead of
an IP address is sent to the host.
To me more specific, a VM with the wellknown wicked network
managing tool still shows the expected output in recent openSUSE
Tumbleweed snapshots:
ip a show dev uplink;ip -4 route show;ip -6 route show
2: uplink: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state ...
link/ether 00:15:5d:d0:93:08 brd ff:ff:ff:ff:ff:ff
inet 1.2.3.4/22 brd 1.2.3.255 scope global uplink
valid_lft forever preferred_lft forever
inet6 fe80::215:5dff:fed0:9308/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
default via 1.2.3.254 dev uplink proto dhcp
1.2.3.0/22 dev uplink proto kernel scope link src 1.2.3.4
fe80::/64 dev uplink proto kernel metric 256 pref medium
default via fe80::26fc:4e00:3b:74 dev uplink proto ra metric 1024 exp...
default via fe80::6a22:8e00:fb:14f8 dev uplink proto ra metric 1024 e...
A similar VM, but with NetworkManager as network managing tool:
ip a show dev eth0;ip -4 route show;ip -6 route show
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP...
link/ether 00:15:5d:d0:93:0b brd ff:ff:ff:ff:ff:ff
inet 1.2.3.8/22 brd 1.2.3.255 scope global dynamic noprefixroute ...
valid_lft 1022sec preferred_lft 1022sec
inet6 fe80::215:5dff:fed0:930b/64 scope link noprefixroute
valid_lft forever preferred_lft forever
default via 1.2.3.254 dev eth0 proto dhcp src 1.2.3.8 metric 100
1.2.3.0/22 dev eth0 proto kernel scope link src 1.2.3.8 metric 100
fe80::/64 dev eth0 proto kernel metric 1024 pref medium
default proto ra metric 20100 pref medium
nexthop via fe80::6a22:8e00:fb:14f8 dev eth0 weight 1
nexthop via fe80::26fc:4e00:3b:74 dev eth0 weight 1
Adjust the route parsing to use a single line for each line of
output. Also use a single shell invocation to retrieve both IPv4
and IPv6 information. The actual IP addresses are expected after
the "via" keyword.
Signed-off-by: Olaf Hering <olaf@aepfle.de>
Reviewed-by: Shradha Gupta <shradhagupta@linux.microsoft.com>
Link: https://lore.kernel.org/r/20241202102235.9701-1-olaf@aepfle.de
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Message-ID: <20241202102235.9701-1-olaf@aepfle.de>
2024-12-02 11:19:55 +01:00
|
|
|
kvp_get_gateway(buffer->gate_way, sizeof(buffer->gate_way));
|
2012-09-04 14:46:36 -07:00
|
|
|
|
|
|
|
/*
|
2019-05-06 16:51:24 +00:00
|
|
|
* Gather the DNS state.
|
2012-09-04 14:46:36 -07:00
|
|
|
* Since there is no standard way to get this information
|
|
|
|
* across various distributions of interest; we just invoke
|
|
|
|
* an external script that needs to be ported across distros
|
|
|
|
* of interest.
|
|
|
|
*
|
|
|
|
* Following is the expected format of the information from the script:
|
|
|
|
*
|
|
|
|
* ipaddr1 (nameserver1)
|
|
|
|
* ipaddr2 (nameserver2)
|
|
|
|
* .
|
|
|
|
* .
|
|
|
|
*/
|
|
|
|
|
2024-12-08 23:47:17 +00:00
|
|
|
sprintf(cmd, "exec %s %s", KVP_SCRIPTS_PATH "hv_get_dns_info", if_name);
|
2012-09-04 14:46:36 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Execute the command to gather DNS info.
|
|
|
|
*/
|
|
|
|
kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr,
|
|
|
|
(MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0);
|
2012-09-05 13:50:11 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Gather the DHCP state.
|
|
|
|
* We will gather this state by invoking an external script.
|
|
|
|
* The parameter to the script is the interface name.
|
|
|
|
* Here is the expected output:
|
|
|
|
*
|
|
|
|
* Enabled: DHCP enabled.
|
|
|
|
*/
|
|
|
|
|
2024-12-08 23:47:17 +00:00
|
|
|
sprintf(cmd, "exec %s %s", KVP_SCRIPTS_PATH "hv_get_dhcp_info", if_name);
|
2012-09-05 13:50:11 -07:00
|
|
|
|
|
|
|
file = popen(cmd, "r");
|
|
|
|
if (file == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
p = fgets(dhcp_info, sizeof(dhcp_info), file);
|
|
|
|
if (p == NULL) {
|
|
|
|
pclose(file);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strncmp(p, "Enabled", 7))
|
|
|
|
buffer->dhcp_enabled = 1;
|
|
|
|
else
|
|
|
|
buffer->dhcp_enabled = 0;
|
|
|
|
|
|
|
|
pclose(file);
|
2012-08-16 18:32:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-16 18:32:17 -07:00
|
|
|
static unsigned int hweight32(unsigned int *w)
|
|
|
|
{
|
|
|
|
unsigned int res = *w - ((*w >> 1) & 0x55555555);
|
|
|
|
res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
|
|
|
|
res = (res + (res >> 4)) & 0x0F0F0F0F;
|
|
|
|
res = res + (res >> 8);
|
|
|
|
return (res + (res >> 16)) & 0x000000FF;
|
|
|
|
}
|
|
|
|
|
2012-08-16 18:32:14 -07:00
|
|
|
static int kvp_process_ip_address(void *addrp,
|
|
|
|
int family, char *buffer,
|
|
|
|
int length, int *offset)
|
|
|
|
{
|
|
|
|
struct sockaddr_in *addr;
|
|
|
|
struct sockaddr_in6 *addr6;
|
|
|
|
int addr_length;
|
|
|
|
char tmp[50];
|
|
|
|
const char *str;
|
|
|
|
|
|
|
|
if (family == AF_INET) {
|
2022-08-23 11:45:52 +08:00
|
|
|
addr = addrp;
|
2012-08-16 18:32:14 -07:00
|
|
|
str = inet_ntop(family, &addr->sin_addr, tmp, 50);
|
|
|
|
addr_length = INET_ADDRSTRLEN;
|
|
|
|
} else {
|
2022-08-23 11:45:52 +08:00
|
|
|
addr6 = addrp;
|
2012-08-16 18:32:14 -07:00
|
|
|
str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50);
|
|
|
|
addr_length = INET6_ADDRSTRLEN;
|
|
|
|
}
|
|
|
|
|
2012-10-25 14:15:50 -07:00
|
|
|
if ((length - *offset) < addr_length + 2)
|
2012-09-05 13:50:15 -07:00
|
|
|
return HV_E_FAIL;
|
2012-08-16 18:32:14 -07:00
|
|
|
if (str == NULL) {
|
|
|
|
strcpy(buffer, "inet_ntop failed\n");
|
2012-09-05 13:50:15 -07:00
|
|
|
return HV_E_FAIL;
|
2012-08-16 18:32:14 -07:00
|
|
|
}
|
|
|
|
if (*offset == 0)
|
|
|
|
strcpy(buffer, tmp);
|
2012-10-25 14:15:50 -07:00
|
|
|
else {
|
|
|
|
strcat(buffer, ";");
|
2012-08-16 18:32:14 -07:00
|
|
|
strcat(buffer, tmp);
|
2012-10-25 14:15:50 -07:00
|
|
|
}
|
2012-08-16 18:32:14 -07:00
|
|
|
|
|
|
|
*offset += strlen(str) + 1;
|
2012-10-25 14:15:50 -07:00
|
|
|
|
2012-08-16 18:32:14 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-16 18:56:54 -07:00
|
|
|
static int
|
2012-09-05 13:50:14 -07:00
|
|
|
kvp_get_ip_info(int family, char *if_name, int op,
|
2015-01-09 22:18:53 -08:00
|
|
|
void *out_buffer, unsigned int length)
|
2010-12-16 18:56:54 -07:00
|
|
|
{
|
|
|
|
struct ifaddrs *ifap;
|
|
|
|
struct ifaddrs *curp;
|
|
|
|
int offset = 0;
|
2012-08-16 18:32:16 -07:00
|
|
|
int sn_offset = 0;
|
2010-12-16 18:56:54 -07:00
|
|
|
int error = 0;
|
2012-08-16 18:32:13 -07:00
|
|
|
char *buffer;
|
2019-08-19 16:44:09 +02:00
|
|
|
struct hv_kvp_ipaddr_value *ip_buffer = NULL;
|
2012-08-16 18:32:17 -07:00
|
|
|
char cidr_mask[5]; /* /xyz */
|
|
|
|
int weight;
|
|
|
|
int i;
|
|
|
|
unsigned int *w;
|
|
|
|
char *sn_str;
|
|
|
|
struct sockaddr_in6 *addr6;
|
2012-08-16 18:32:13 -07:00
|
|
|
|
|
|
|
if (op == KVP_OP_ENUMERATE) {
|
|
|
|
buffer = out_buffer;
|
|
|
|
} else {
|
|
|
|
ip_buffer = out_buffer;
|
|
|
|
buffer = (char *)ip_buffer->ip_addr;
|
|
|
|
ip_buffer->addr_family = 0;
|
|
|
|
}
|
2010-12-16 18:56:54 -07:00
|
|
|
/*
|
|
|
|
* On entry into this function, the buffer is capable of holding the
|
2012-08-16 18:32:13 -07:00
|
|
|
* maximum key value.
|
2010-12-16 18:56:54 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
if (getifaddrs(&ifap)) {
|
|
|
|
strcpy(buffer, "getifaddrs failed\n");
|
2012-09-05 13:50:15 -07:00
|
|
|
return HV_E_FAIL;
|
2010-12-16 18:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
curp = ifap;
|
|
|
|
while (curp != NULL) {
|
2012-08-16 18:32:13 -07:00
|
|
|
if (curp->ifa_addr == NULL) {
|
|
|
|
curp = curp->ifa_next;
|
|
|
|
continue;
|
|
|
|
}
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2012-08-16 18:32:13 -07:00
|
|
|
if ((if_name != NULL) &&
|
|
|
|
(strncmp(curp->ifa_name, if_name, strlen(if_name)))) {
|
|
|
|
/*
|
|
|
|
* We want info about a specific interface;
|
|
|
|
* just continue.
|
|
|
|
*/
|
|
|
|
curp = curp->ifa_next;
|
|
|
|
continue;
|
|
|
|
}
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2012-08-16 18:32:13 -07:00
|
|
|
/*
|
|
|
|
* We only support two address families: AF_INET and AF_INET6.
|
|
|
|
* If a family value of 0 is specified, we collect both
|
|
|
|
* supported address families; if not we gather info on
|
|
|
|
* the specified address family.
|
|
|
|
*/
|
2012-10-25 14:15:50 -07:00
|
|
|
if ((((family != 0) &&
|
|
|
|
(curp->ifa_addr->sa_family != family))) ||
|
|
|
|
(curp->ifa_flags & IFF_LOOPBACK)) {
|
2012-08-16 18:32:13 -07:00
|
|
|
curp = curp->ifa_next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((curp->ifa_addr->sa_family != AF_INET) &&
|
|
|
|
(curp->ifa_addr->sa_family != AF_INET6)) {
|
|
|
|
curp = curp->ifa_next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-08-16 18:32:15 -07:00
|
|
|
if (op == KVP_OP_GET_IP_INFO) {
|
|
|
|
/*
|
|
|
|
* Gather info other than the IP address.
|
|
|
|
* IP address info will be gathered later.
|
|
|
|
*/
|
2012-08-16 18:32:16 -07:00
|
|
|
if (curp->ifa_addr->sa_family == AF_INET) {
|
2012-08-16 18:32:15 -07:00
|
|
|
ip_buffer->addr_family |= ADDR_FAMILY_IPV4;
|
2012-08-16 18:32:16 -07:00
|
|
|
/*
|
|
|
|
* Get subnet info.
|
|
|
|
*/
|
|
|
|
error = kvp_process_ip_address(
|
|
|
|
curp->ifa_netmask,
|
|
|
|
AF_INET,
|
|
|
|
(char *)
|
|
|
|
ip_buffer->sub_net,
|
|
|
|
length,
|
|
|
|
&sn_offset);
|
|
|
|
if (error)
|
|
|
|
goto gather_ipaddr;
|
|
|
|
} else {
|
2012-08-16 18:32:15 -07:00
|
|
|
ip_buffer->addr_family |= ADDR_FAMILY_IPV6;
|
2012-08-16 18:32:17 -07:00
|
|
|
|
2012-08-16 18:32:16 -07:00
|
|
|
/*
|
2012-08-16 18:32:17 -07:00
|
|
|
* Get subnet info in CIDR format.
|
2012-08-16 18:32:16 -07:00
|
|
|
*/
|
2012-08-16 18:32:17 -07:00
|
|
|
weight = 0;
|
|
|
|
sn_str = (char *)ip_buffer->sub_net;
|
|
|
|
addr6 = (struct sockaddr_in6 *)
|
|
|
|
curp->ifa_netmask;
|
|
|
|
w = addr6->sin6_addr.s6_addr32;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
weight += hweight32(&w[i]);
|
|
|
|
|
|
|
|
sprintf(cidr_mask, "/%d", weight);
|
2015-01-09 22:18:53 -08:00
|
|
|
if (length < sn_offset + strlen(cidr_mask) + 1)
|
2012-08-16 18:32:16 -07:00
|
|
|
goto gather_ipaddr;
|
2012-08-16 18:32:17 -07:00
|
|
|
|
|
|
|
if (sn_offset == 0)
|
|
|
|
strcpy(sn_str, cidr_mask);
|
2013-07-11 12:03:31 -07:00
|
|
|
else {
|
|
|
|
strcat((char *)ip_buffer->sub_net, ";");
|
2012-08-16 18:32:17 -07:00
|
|
|
strcat(sn_str, cidr_mask);
|
2013-07-11 12:03:31 -07:00
|
|
|
}
|
2012-08-16 18:32:17 -07:00
|
|
|
sn_offset += strlen(sn_str) + 1;
|
2012-08-16 18:32:16 -07:00
|
|
|
}
|
2012-08-16 18:32:18 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Collect other ip related configuration info.
|
|
|
|
*/
|
|
|
|
|
|
|
|
kvp_get_ipconfig_info(if_name, ip_buffer);
|
2012-08-16 18:32:15 -07:00
|
|
|
}
|
|
|
|
|
2012-08-16 18:32:16 -07:00
|
|
|
gather_ipaddr:
|
2012-08-16 18:32:14 -07:00
|
|
|
error = kvp_process_ip_address(curp->ifa_addr,
|
|
|
|
curp->ifa_addr->sa_family,
|
|
|
|
buffer,
|
|
|
|
length, &offset);
|
|
|
|
if (error)
|
|
|
|
goto getaddr_done;
|
2012-08-16 18:32:13 -07:00
|
|
|
|
2010-12-16 18:56:54 -07:00
|
|
|
curp = curp->ifa_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
getaddr_done:
|
|
|
|
freeifaddrs(ifap);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2018-03-04 22:17:11 -07:00
|
|
|
/*
|
|
|
|
* Retrieve the IP given the MAC address.
|
|
|
|
*/
|
|
|
|
static int kvp_mac_to_ip(struct hv_kvp_ipaddr_value *kvp_ip_val)
|
|
|
|
{
|
|
|
|
char *mac = (char *)kvp_ip_val->adapter_id;
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *entry;
|
|
|
|
FILE *file;
|
|
|
|
char *p, *x;
|
|
|
|
char *if_name = NULL;
|
|
|
|
char buf[256];
|
|
|
|
char dev_id[PATH_MAX];
|
|
|
|
unsigned int i;
|
|
|
|
int error = HV_E_FAIL;
|
|
|
|
|
|
|
|
dir = opendir(KVP_NET_DIR);
|
|
|
|
if (dir == NULL)
|
|
|
|
return HV_E_FAIL;
|
|
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
/*
|
|
|
|
* Set the state for the next pass.
|
|
|
|
*/
|
|
|
|
snprintf(dev_id, sizeof(dev_id), "%s%s/address", KVP_NET_DIR,
|
|
|
|
entry->d_name);
|
|
|
|
|
|
|
|
file = fopen(dev_id, "r");
|
|
|
|
if (file == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
p = fgets(buf, sizeof(buf), file);
|
|
|
|
fclose(file);
|
|
|
|
if (!p)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
x = strchr(p, '\n');
|
|
|
|
if (x)
|
|
|
|
*x = '\0';
|
|
|
|
|
|
|
|
for (i = 0; i < strlen(p); i++)
|
|
|
|
p[i] = toupper(p[i]);
|
|
|
|
|
|
|
|
if (strcmp(p, mac))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Found the MAC match.
|
|
|
|
* A NIC (e.g. VF) matching the MAC, but without IP, is skipped.
|
|
|
|
*/
|
|
|
|
if_name = entry->d_name;
|
|
|
|
if (!if_name)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
error = kvp_get_ip_info(0, if_name, KVP_OP_GET_IP_INFO,
|
|
|
|
kvp_ip_val, MAX_IP_ADDR_SIZE * 2);
|
|
|
|
|
|
|
|
if (!error && strlen((char *)kvp_ip_val->ip_addr))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
return error;
|
|
|
|
}
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2012-09-05 13:50:13 -07:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
char *x;
|
|
|
|
char *start;
|
|
|
|
|
|
|
|
/*
|
2019-05-06 16:51:24 +00:00
|
|
|
* in_buf has sequence of characters that are separated by
|
2012-09-05 13:50:13 -07:00
|
|
|
* the character ';'. The last sequence does not have the
|
|
|
|
* terminating ";" character.
|
|
|
|
*/
|
|
|
|
start = in_buf + *offset;
|
|
|
|
|
|
|
|
x = strchr(start, ';');
|
|
|
|
if (x)
|
|
|
|
*x = 0;
|
|
|
|
else
|
|
|
|
x = start + strlen(start);
|
|
|
|
|
|
|
|
if (strlen(start) != 0) {
|
|
|
|
int i = 0;
|
|
|
|
/*
|
|
|
|
* Get rid of leading spaces.
|
|
|
|
*/
|
|
|
|
while (start[i] == ' ')
|
|
|
|
i++;
|
|
|
|
|
|
|
|
if ((x - start) <= out_len) {
|
|
|
|
strcpy(out_buf, (start + i));
|
|
|
|
*offset += (x - start) + 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
return HV_E_FAIL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int process_ip_string(FILE *f, char *ip_string, int type)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
char addr[INET6_ADDRSTRLEN];
|
|
|
|
int i = 0;
|
|
|
|
int j = 0;
|
|
|
|
char str[256];
|
2017-08-10 15:45:15 -07:00
|
|
|
char sub_str[13];
|
2012-09-05 13:50:13 -07:00
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
memset(addr, 0, sizeof(addr));
|
|
|
|
|
|
|
|
while (parse_ip_val_buffer(ip_string, &offset, addr,
|
|
|
|
(MAX_IP_ADDR_SIZE * 2))) {
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2013-01-13 22:27:40 +01:00
|
|
|
|
|
|
|
if (type == DNS) {
|
2012-09-05 13:50:13 -07:00
|
|
|
snprintf(sub_str, sizeof(sub_str), "%d", ++i);
|
2013-01-13 22:27:40 +01:00
|
|
|
} else if (type == GATEWAY && i == 0) {
|
|
|
|
++i;
|
|
|
|
} else {
|
|
|
|
snprintf(sub_str, sizeof(sub_str), "%d", i++);
|
2012-09-05 13:50:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} 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;
|
|
|
|
}
|
2013-01-13 22:27:40 +01:00
|
|
|
|
|
|
|
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++);
|
2012-09-05 13:50:13 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return HV_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = kvp_write_file(f, str, sub_str, addr);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
memset(addr, 0, sizeof(addr));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-03-22 06:46:02 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2024-03-22 06:46:02 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet,
|
2024-03-22 06:46:02 -07:00
|
|
|
int ip_sec)
|
2023-10-09 03:38:40 -07:00
|
|
|
{
|
|
|
|
char addr[INET6_ADDRSTRLEN];
|
|
|
|
char subnet_addr[INET6_ADDRSTRLEN];
|
2024-03-22 06:46:02 -07:00
|
|
|
int error = 0, i = 0;
|
2023-10-09 03:38:40 -07:00
|
|
|
int ip_offset = 0, subnet_offset = 0;
|
2024-03-22 06:46:02 -07:00
|
|
|
int plen, ip_ver;
|
2023-10-09 03:38:40 -07:00
|
|
|
|
|
|
|
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))) {
|
2024-03-22 06:46:02 -07:00
|
|
|
ip_ver = ip_version_check(addr);
|
|
|
|
if (ip_ver < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (ip_ver == IPV4 && ip_sec == IPV4)
|
2023-10-09 03:38:40 -07:00
|
|
|
plen = kvp_subnet_to_plen((char *)subnet_addr);
|
2024-03-22 06:46:02 -07:00
|
|
|
else if (ip_ver == IPV6 && ip_sec == IPV6)
|
2023-10-09 03:38:40 -07:00
|
|
|
plen = atoi(subnet_addr);
|
2024-03-22 06:46:02 -07:00
|
|
|
else
|
|
|
|
continue;
|
2023-10-09 03:38:40 -07:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2024-03-22 06:46:02 -07:00
|
|
|
return error;
|
2023-10-09 03:38:40 -07:00
|
|
|
}
|
|
|
|
|
2012-09-05 13:50:13 -07:00
|
|
|
static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
|
|
|
|
{
|
2024-03-22 06:46:02 -07:00
|
|
|
int error = 0, ip_ver;
|
2023-10-09 03:38:40 -07:00
|
|
|
char if_filename[PATH_MAX];
|
|
|
|
char nm_filename[PATH_MAX];
|
|
|
|
FILE *ifcfg_file, *nmfile;
|
2017-04-30 16:21:15 -07:00
|
|
|
char cmd[PATH_MAX];
|
2012-09-05 13:50:13 -07:00
|
|
|
char *mac_addr;
|
2018-10-18 05:09:32 +00:00
|
|
|
int str_len;
|
2012-09-05 13:50:13 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the configuration for the specified interface with
|
|
|
|
* the information provided. Since there is no standard
|
|
|
|
* way to configure an interface, we will have an external
|
|
|
|
* script that does the job of configuring the interface and
|
|
|
|
* flushing the configuration.
|
|
|
|
*
|
|
|
|
* The parameters passed to this external script are:
|
|
|
|
* 1. A configuration file that has the specified configuration.
|
|
|
|
*
|
|
|
|
* We will embed the name of the interface in the configuration
|
|
|
|
* file: ifcfg-ethx (where ethx is the interface name).
|
|
|
|
*
|
|
|
|
* The information provided here may be more than what is needed
|
|
|
|
* in a given distro to configure the interface and so are free
|
|
|
|
* ignore information that may not be relevant.
|
|
|
|
*
|
2023-10-09 03:38:40 -07:00
|
|
|
* Here is the ifcfg format of the ip configuration file:
|
2012-09-05 13:50:13 -07:00
|
|
|
*
|
|
|
|
* HWADDR=macaddr
|
2013-01-13 22:27:40 +01:00
|
|
|
* DEVICE=interface name
|
|
|
|
* BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured
|
|
|
|
* or "none" if no boot-time protocol should be used)
|
2012-09-05 13:50:13 -07:00
|
|
|
*
|
2013-01-13 22:27:40 +01:00
|
|
|
* IPADDR0=ipaddr1
|
|
|
|
* IPADDR1=ipaddr2
|
|
|
|
* IPADDRx=ipaddry (where y = x + 1)
|
2012-09-05 13:50:13 -07:00
|
|
|
*
|
2013-01-13 22:27:40 +01:00
|
|
|
* NETMASK0=netmask1
|
|
|
|
* NETMASKx=netmasky (where y = x + 1)
|
2012-09-05 13:50:13 -07:00
|
|
|
*
|
|
|
|
* GATEWAY=ipaddr1
|
2013-01-13 22:27:40 +01:00
|
|
|
* GATEWAYx=ipaddry (where y = x + 1)
|
2012-09-05 13:50:13 -07:00
|
|
|
*
|
|
|
|
* DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
|
|
|
|
*
|
|
|
|
* IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
|
|
|
|
* tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
|
|
|
|
* IPV6NETMASK.
|
|
|
|
*
|
2023-10-09 03:38:40 -07:00
|
|
|
* 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
|
|
|
|
*
|
2012-09-05 13:50:13 -07:00
|
|
|
* 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
|
|
|
|
* is expected to return the configuration that is set via the SET
|
|
|
|
* call.
|
|
|
|
*/
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
/*
|
|
|
|
* We are populating both ifcfg and nmconnection files
|
|
|
|
*/
|
|
|
|
snprintf(if_filename, sizeof(if_filename), "%s%s%s", KVP_CONFIG_LOC,
|
|
|
|
"/ifcfg-", if_name);
|
2012-09-05 13:50:13 -07:00
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
ifcfg_file = fopen(if_filename, "w");
|
2012-09-05 13:50:13 -07:00
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
if (!ifcfg_file) {
|
2013-06-17 10:39:44 +02:00
|
|
|
syslog(LOG_ERR, "Failed to open config file; error: %d %s",
|
2023-10-09 03:38:40 -07:00
|
|
|
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);
|
2012-09-05 13:50:13 -07:00
|
|
|
return HV_E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First write out the MAC address.
|
|
|
|
*/
|
|
|
|
|
|
|
|
mac_addr = kvp_if_name_to_mac(if_name);
|
|
|
|
if (mac_addr == NULL) {
|
|
|
|
error = HV_E_FAIL;
|
|
|
|
goto setval_error;
|
|
|
|
}
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
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);
|
2012-09-05 13:50:13 -07:00
|
|
|
if (error)
|
2023-10-09 03:38:40 -07:00
|
|
|
goto setmac_error;
|
|
|
|
|
|
|
|
error = fprintf(nmfile, "\n[ethernet]\n");
|
|
|
|
if (error < 0)
|
|
|
|
goto setmac_error;
|
2012-09-05 13:50:13 -07:00
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
error = kvp_write_file(nmfile, "mac-address", "", mac_addr);
|
2012-09-05 13:50:13 -07:00
|
|
|
if (error)
|
2023-10-09 03:38:40 -07:00
|
|
|
goto setmac_error;
|
|
|
|
|
|
|
|
free(mac_addr);
|
2012-09-05 13:50:13 -07:00
|
|
|
|
2014-12-10 03:33:20 -08:00
|
|
|
/*
|
|
|
|
* The dhcp_enabled flag is only for IPv4. In the case the host only
|
|
|
|
* injects an IPv6 address, the flag is true, but we still need to
|
|
|
|
* proceed to parse and pass the IPv6 information to the
|
|
|
|
* disto-specific script hv_set_ifconfig.
|
|
|
|
*/
|
2023-10-09 03:38:40 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* First populate the ifcfg file format
|
|
|
|
*/
|
2012-09-05 13:50:13 -07:00
|
|
|
if (new_val->dhcp_enabled) {
|
2023-10-09 03:38:40 -07:00
|
|
|
error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "dhcp");
|
2012-09-05 13:50:13 -07:00
|
|
|
if (error)
|
|
|
|
goto setval_error;
|
2013-01-13 22:27:40 +01:00
|
|
|
} else {
|
2023-10-09 03:38:40 -07:00
|
|
|
error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "none");
|
2013-01-13 22:27:40 +01:00
|
|
|
if (error)
|
|
|
|
goto setval_error;
|
2012-09-05 13:50:13 -07:00
|
|
|
}
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr,
|
|
|
|
IPADDR);
|
2012-09-05 13:50:13 -07:00
|
|
|
if (error)
|
|
|
|
goto setval_error;
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
error = process_ip_string(ifcfg_file, (char *)new_val->sub_net,
|
|
|
|
NETMASK);
|
2012-09-05 13:50:13 -07:00
|
|
|
if (error)
|
|
|
|
goto setval_error;
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
error = process_ip_string(ifcfg_file, (char *)new_val->gate_way,
|
|
|
|
GATEWAY);
|
2012-09-05 13:50:13 -07:00
|
|
|
if (error)
|
|
|
|
goto setval_error;
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS);
|
2012-09-05 13:50:13 -07:00
|
|
|
if (error)
|
|
|
|
goto setval_error;
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
/*
|
|
|
|
* Now we populate the keyfile format
|
2024-03-22 06:46:02 -07:00
|
|
|
*
|
|
|
|
* 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
|
2023-10-09 03:38:40 -07:00
|
|
|
*/
|
2024-03-22 06:46:02 -07:00
|
|
|
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;
|
|
|
|
}
|
2023-10-09 03:38:40 -07:00
|
|
|
|
2024-03-22 06:46:02 -07:00
|
|
|
/*
|
|
|
|
* 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);
|
2023-10-09 03:38:40 -07:00
|
|
|
if (error < 0)
|
|
|
|
goto setval_error;
|
|
|
|
|
2024-03-22 06:46:02 -07:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
}
|
2023-10-09 03:38:40 -07:00
|
|
|
|
2024-03-22 06:46:02 -07:00
|
|
|
error = process_dns_gateway_nm(nmfile,
|
|
|
|
(char *)new_val->gate_way,
|
|
|
|
GATEWAY, ip_ver);
|
2023-10-16 19:01:22 +05:30
|
|
|
if (error < 0)
|
|
|
|
goto setval_error;
|
2023-10-09 03:38:40 -07:00
|
|
|
|
2024-03-22 06:46:02 -07:00
|
|
|
error = process_dns_gateway_nm(nmfile,
|
|
|
|
(char *)new_val->dns_addr, DNS,
|
|
|
|
ip_ver);
|
2023-10-16 19:01:22 +05:30
|
|
|
if (error < 0)
|
|
|
|
goto setval_error;
|
2024-03-22 06:46:02 -07:00
|
|
|
|
|
|
|
ip_ver++;
|
|
|
|
} while (ip_ver < IP_TYPE_MAX);
|
|
|
|
|
2023-10-09 03:38:40 -07:00
|
|
|
fclose(nmfile);
|
|
|
|
fclose(ifcfg_file);
|
2012-09-05 13:50:13 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that we have populated the configuration file,
|
|
|
|
* invoke the external script to do its magic.
|
|
|
|
*/
|
|
|
|
|
2024-12-08 23:47:17 +00:00
|
|
|
str_len = snprintf(cmd, sizeof(cmd), "exec %s %s %s",
|
|
|
|
KVP_SCRIPTS_PATH "hv_set_ifconfig",
|
|
|
|
if_filename, nm_filename);
|
2018-10-18 05:09:32 +00:00
|
|
|
/*
|
|
|
|
* This is a little overcautious, but it's necessary to suppress some
|
|
|
|
* false warnings from gcc 8.0.1.
|
|
|
|
*/
|
|
|
|
if (str_len <= 0 || (unsigned int)str_len >= sizeof(cmd)) {
|
|
|
|
syslog(LOG_ERR, "Cmd '%s' (len=%d) may be too long",
|
|
|
|
cmd, str_len);
|
|
|
|
return HV_E_FAIL;
|
|
|
|
}
|
|
|
|
|
2013-08-04 16:40:44 +02:00
|
|
|
if (system(cmd)) {
|
|
|
|
syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s",
|
2023-10-09 03:38:40 -07:00
|
|
|
cmd, errno, strerror(errno));
|
2013-08-04 16:40:44 +02:00
|
|
|
return HV_E_FAIL;
|
|
|
|
}
|
2012-09-05 13:50:13 -07:00
|
|
|
return 0;
|
2023-10-09 03:38:40 -07:00
|
|
|
setmac_error:
|
|
|
|
free(mac_addr);
|
2012-09-05 13:50:13 -07:00
|
|
|
setval_error:
|
|
|
|
syslog(LOG_ERR, "Failed to write config file");
|
2023-10-09 03:38:40 -07:00
|
|
|
fclose(ifcfg_file);
|
|
|
|
fclose(nmfile);
|
2012-09-05 13:50:13 -07:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-07 19:14:37 +02:00
|
|
|
static void
|
2010-12-16 18:56:54 -07:00
|
|
|
kvp_get_domain_name(char *buffer, int length)
|
|
|
|
{
|
|
|
|
struct addrinfo hints, *info ;
|
|
|
|
int error = 0;
|
|
|
|
|
2011-07-26 11:03:10 -07:00
|
|
|
gethostname(buffer, length);
|
2010-12-16 18:56:54 -07:00
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_flags = AI_CANONNAME;
|
|
|
|
|
2011-07-26 11:03:10 -07:00
|
|
|
error = getaddrinfo(buffer, NULL, &hints, &info);
|
2010-12-16 18:56:54 -07:00
|
|
|
if (error != 0) {
|
2013-08-07 19:14:37 +02:00
|
|
|
snprintf(buffer, length, "getaddrinfo failed: 0x%x %s",
|
|
|
|
error, gai_strerror(error));
|
|
|
|
return;
|
2010-12-16 18:56:54 -07:00
|
|
|
}
|
2013-08-07 19:14:37 +02:00
|
|
|
snprintf(buffer, length, "%s", info->ai_canonname);
|
2010-12-16 18:56:54 -07:00
|
|
|
freeaddrinfo(info);
|
|
|
|
}
|
|
|
|
|
2014-10-22 18:07:11 +02:00
|
|
|
void print_usage(char *argv[])
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Usage: %s [options]\n"
|
|
|
|
"Options are:\n"
|
|
|
|
" -n, --no-daemon stay in foreground, don't daemonize\n"
|
2025-04-15 04:19:38 -07:00
|
|
|
" -d, --debug Enable debug logs(syslog debug by default)\n"
|
2014-10-22 18:07:11 +02:00
|
|
|
" -h, --help print this help\n", argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
2010-12-16 18:56:54 -07:00
|
|
|
{
|
2020-01-25 21:49:41 -08:00
|
|
|
int kvp_fd = -1, len;
|
2010-12-16 18:56:54 -07:00
|
|
|
int error;
|
|
|
|
struct pollfd pfd;
|
2015-04-11 18:07:55 -07:00
|
|
|
char *p;
|
|
|
|
struct hv_kvp_msg hv_msg[1];
|
2010-12-16 18:56:54 -07:00
|
|
|
char *key_value;
|
|
|
|
char *key_name;
|
2012-08-13 10:06:52 -07:00
|
|
|
int op;
|
|
|
|
int pool;
|
2012-09-05 13:50:13 -07:00
|
|
|
char *if_name;
|
|
|
|
struct hv_kvp_ipaddr_value *kvp_ip_val;
|
2014-10-22 18:07:11 +02:00
|
|
|
int daemonize = 1, long_index = 0, opt;
|
|
|
|
|
|
|
|
static struct option long_options[] = {
|
|
|
|
{"help", no_argument, 0, 'h' },
|
|
|
|
{"no-daemon", no_argument, 0, 'n' },
|
2025-04-15 04:19:38 -07:00
|
|
|
{"debug", no_argument, 0, 'd' },
|
2014-10-22 18:07:11 +02:00
|
|
|
{0, 0, 0, 0 }
|
|
|
|
};
|
|
|
|
|
2025-04-15 04:19:38 -07:00
|
|
|
while ((opt = getopt_long(argc, argv, "hnd", long_options,
|
2014-10-22 18:07:11 +02:00
|
|
|
&long_index)) != -1) {
|
|
|
|
switch (opt) {
|
|
|
|
case 'n':
|
|
|
|
daemonize = 0;
|
|
|
|
break;
|
|
|
|
case 'h':
|
2019-05-06 16:50:58 +00:00
|
|
|
print_usage(argv);
|
|
|
|
exit(0);
|
2025-04-15 04:19:38 -07:00
|
|
|
case 'd':
|
|
|
|
debug = 1;
|
|
|
|
break;
|
2014-10-22 18:07:11 +02:00
|
|
|
default:
|
|
|
|
print_usage(argv);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2014-10-22 18:07:11 +02:00
|
|
|
if (daemonize && daemon(1, 0))
|
2013-08-01 14:43:12 +02:00
|
|
|
return 1;
|
2014-10-22 18:07:11 +02:00
|
|
|
|
2010-12-16 18:56:54 -07:00
|
|
|
openlog("KVP", 0, LOG_USER);
|
|
|
|
syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
|
2013-08-01 14:34:26 +02:00
|
|
|
|
2010-12-16 18:56:54 -07:00
|
|
|
/*
|
|
|
|
* Retrieve OS release information.
|
|
|
|
*/
|
|
|
|
kvp_get_os_info();
|
2013-08-07 19:14:37 +02:00
|
|
|
/*
|
|
|
|
* Cache Fully Qualified Domain Name because getaddrinfo takes an
|
|
|
|
* unpredictable amount of time to finish.
|
|
|
|
*/
|
|
|
|
kvp_get_domain_name(full_domain_name, sizeof(full_domain_name));
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2025-04-15 04:19:38 -07:00
|
|
|
if (debug)
|
|
|
|
syslog(LOG_INFO, "Logging debug info in syslog(debug)");
|
|
|
|
|
2012-03-16 08:02:26 -07:00
|
|
|
if (kvp_file_init()) {
|
|
|
|
syslog(LOG_ERR, "Failed to initialize the pools");
|
2012-09-05 14:37:36 -07:00
|
|
|
exit(EXIT_FAILURE);
|
2012-03-16 08:02:26 -07:00
|
|
|
}
|
|
|
|
|
2020-01-25 21:49:41 -08:00
|
|
|
reopen_kvp_fd:
|
|
|
|
if (kvp_fd != -1)
|
|
|
|
close(kvp_fd);
|
|
|
|
in_hand_shake = 1;
|
|
|
|
kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR | O_CLOEXEC);
|
|
|
|
|
|
|
|
if (kvp_fd < 0) {
|
|
|
|
syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s",
|
|
|
|
errno, strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2010-12-16 18:56:54 -07:00
|
|
|
/*
|
|
|
|
* Register ourselves with the kernel.
|
|
|
|
*/
|
2012-08-13 10:06:52 -07:00
|
|
|
hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1;
|
2015-04-11 18:07:55 -07:00
|
|
|
len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
|
|
|
|
if (len != sizeof(struct hv_kvp_msg)) {
|
|
|
|
syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
|
|
|
|
errno, strerror(errno));
|
|
|
|
close(kvp_fd);
|
2012-09-05 14:37:36 -07:00
|
|
|
exit(EXIT_FAILURE);
|
2010-12-16 18:56:54 -07:00
|
|
|
}
|
|
|
|
|
2015-04-11 18:07:55 -07:00
|
|
|
pfd.fd = kvp_fd;
|
2010-12-16 18:56:54 -07:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
pfd.events = POLLIN;
|
|
|
|
pfd.revents = 0;
|
2013-05-22 14:54:31 +02:00
|
|
|
|
|
|
|
if (poll(&pfd, 1, -1) < 0) {
|
|
|
|
syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno));
|
|
|
|
if (errno == EINVAL) {
|
2015-04-11 18:07:55 -07:00
|
|
|
close(kvp_fd);
|
2013-05-22 14:54:31 +02:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2015-04-11 18:07:55 -07:00
|
|
|
len = read(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
|
2014-11-19 21:51:22 -08:00
|
|
|
|
2015-04-11 18:07:55 -07:00
|
|
|
if (len != sizeof(struct hv_kvp_msg)) {
|
|
|
|
syslog(LOG_ERR, "read failed; error:%d %s",
|
|
|
|
errno, strerror(errno));
|
2020-01-25 21:49:41 -08:00
|
|
|
goto reopen_kvp_fd;
|
2010-12-16 18:56:54 -07:00
|
|
|
}
|
|
|
|
|
2012-08-13 10:06:52 -07:00
|
|
|
/*
|
|
|
|
* We will use the KVP header information to pass back
|
|
|
|
* the error from this daemon. So, first copy the state
|
|
|
|
* and set the error code to success.
|
|
|
|
*/
|
|
|
|
op = hv_msg->kvp_hdr.operation;
|
|
|
|
pool = hv_msg->kvp_hdr.pool;
|
|
|
|
hv_msg->error = HV_S_OK;
|
|
|
|
|
|
|
|
if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) {
|
2010-12-16 18:56:54 -07:00
|
|
|
/*
|
|
|
|
* Driver is registering with us; stash away the version
|
|
|
|
* information.
|
|
|
|
*/
|
2012-08-13 10:06:52 -07:00
|
|
|
in_hand_shake = 0;
|
2012-03-10 15:32:08 -08:00
|
|
|
p = (char *)hv_msg->body.kvp_register.version;
|
2011-03-22 10:02:17 +01:00
|
|
|
lic_version = malloc(strlen(p) + 1);
|
2010-12-16 18:56:54 -07:00
|
|
|
if (lic_version) {
|
2011-03-22 10:02:17 +01:00
|
|
|
strcpy(lic_version, p);
|
2010-12-16 18:56:54 -07:00
|
|
|
syslog(LOG_INFO, "KVP LIC Version: %s",
|
2015-04-11 18:07:55 -07:00
|
|
|
lic_version);
|
2010-12-16 18:56:54 -07:00
|
|
|
} else {
|
|
|
|
syslog(LOG_ERR, "malloc failed");
|
|
|
|
}
|
|
|
|
continue;
|
2012-08-13 10:06:52 -07:00
|
|
|
}
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2012-08-13 10:06:52 -07:00
|
|
|
switch (op) {
|
2012-09-05 13:50:15 -07:00
|
|
|
case KVP_OP_GET_IP_INFO:
|
|
|
|
kvp_ip_val = &hv_msg->body.kvp_ip_val;
|
|
|
|
|
2019-05-06 16:51:24 +00:00
|
|
|
error = kvp_mac_to_ip(kvp_ip_val);
|
2012-09-05 13:50:15 -07:00
|
|
|
|
|
|
|
if (error)
|
|
|
|
hv_msg->error = error;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2012-09-05 13:50:13 -07:00
|
|
|
case KVP_OP_SET_IP_INFO:
|
|
|
|
kvp_ip_val = &hv_msg->body.kvp_ip_val;
|
|
|
|
if_name = kvp_get_if_name(
|
|
|
|
(char *)kvp_ip_val->adapter_id);
|
|
|
|
if (if_name == NULL) {
|
|
|
|
/*
|
|
|
|
* We could not map the guid to an
|
|
|
|
* interface name; return error.
|
|
|
|
*/
|
|
|
|
hv_msg->error = HV_GUID_NOTFOUND;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = kvp_set_ip_info(if_name, kvp_ip_val);
|
|
|
|
if (error)
|
|
|
|
hv_msg->error = error;
|
|
|
|
|
|
|
|
free(if_name);
|
|
|
|
break;
|
|
|
|
|
2012-03-16 08:02:25 -07:00
|
|
|
case KVP_OP_SET:
|
2012-08-13 10:06:52 -07:00
|
|
|
if (kvp_key_add_or_modify(pool,
|
2012-03-16 08:02:26 -07:00
|
|
|
hv_msg->body.kvp_set.data.key,
|
|
|
|
hv_msg->body.kvp_set.data.key_size,
|
|
|
|
hv_msg->body.kvp_set.data.value,
|
|
|
|
hv_msg->body.kvp_set.data.value_size))
|
2012-08-13 10:06:52 -07:00
|
|
|
hv_msg->error = HV_S_CONT;
|
2012-03-16 08:02:26 -07:00
|
|
|
break;
|
|
|
|
|
2012-03-16 08:02:25 -07:00
|
|
|
case KVP_OP_GET:
|
2012-08-13 10:06:52 -07:00
|
|
|
if (kvp_get_value(pool,
|
2012-03-16 08:02:26 -07:00
|
|
|
hv_msg->body.kvp_set.data.key,
|
|
|
|
hv_msg->body.kvp_set.data.key_size,
|
|
|
|
hv_msg->body.kvp_set.data.value,
|
|
|
|
hv_msg->body.kvp_set.data.value_size))
|
2012-08-13 10:06:52 -07:00
|
|
|
hv_msg->error = HV_S_CONT;
|
2012-03-16 08:02:26 -07:00
|
|
|
break;
|
|
|
|
|
2012-03-16 08:02:25 -07:00
|
|
|
case KVP_OP_DELETE:
|
2012-08-13 10:06:52 -07:00
|
|
|
if (kvp_key_delete(pool,
|
2012-03-16 08:02:26 -07:00
|
|
|
hv_msg->body.kvp_delete.key,
|
|
|
|
hv_msg->body.kvp_delete.key_size))
|
2012-08-13 10:06:52 -07:00
|
|
|
hv_msg->error = HV_S_CONT;
|
2012-03-16 08:02:26 -07:00
|
|
|
break;
|
|
|
|
|
2010-12-16 18:56:54 -07:00
|
|
|
default:
|
2012-02-02 16:56:50 -08:00
|
|
|
break;
|
2010-12-16 18:56:54 -07:00
|
|
|
}
|
|
|
|
|
2012-08-13 10:06:52 -07:00
|
|
|
if (op != KVP_OP_ENUMERATE)
|
2012-03-16 08:02:25 -07:00
|
|
|
goto kvp_done;
|
|
|
|
|
2012-03-16 08:02:27 -07:00
|
|
|
/*
|
|
|
|
* If the pool is KVP_POOL_AUTO, dynamically generate
|
|
|
|
* both the key and the value; if not read from the
|
|
|
|
* appropriate pool.
|
|
|
|
*/
|
2012-08-13 10:06:52 -07:00
|
|
|
if (pool != KVP_POOL_AUTO) {
|
|
|
|
if (kvp_pool_enumerate(pool,
|
2012-03-16 08:02:27 -07:00
|
|
|
hv_msg->body.kvp_enum_data.index,
|
|
|
|
hv_msg->body.kvp_enum_data.data.key,
|
|
|
|
HV_KVP_EXCHANGE_MAX_KEY_SIZE,
|
|
|
|
hv_msg->body.kvp_enum_data.data.value,
|
2012-08-13 10:06:52 -07:00
|
|
|
HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
|
|
|
|
hv_msg->error = HV_S_CONT;
|
2012-03-16 08:02:27 -07:00
|
|
|
goto kvp_done;
|
|
|
|
}
|
|
|
|
|
2012-02-02 16:56:50 -08:00
|
|
|
key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
|
|
|
|
key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
|
2010-12-16 18:56:54 -07:00
|
|
|
|
2012-02-02 16:56:50 -08:00
|
|
|
switch (hv_msg->body.kvp_enum_data.index) {
|
2010-12-16 18:56:54 -07:00
|
|
|
case FullyQualifiedDomainName:
|
2013-08-07 19:14:37 +02:00
|
|
|
strcpy(key_value, full_domain_name);
|
2010-12-16 18:56:54 -07:00
|
|
|
strcpy(key_name, "FullyQualifiedDomainName");
|
|
|
|
break;
|
|
|
|
case IntegrationServicesVersion:
|
|
|
|
strcpy(key_name, "IntegrationServicesVersion");
|
|
|
|
strcpy(key_value, lic_version);
|
|
|
|
break;
|
|
|
|
case NetworkAddressIPv4:
|
2012-09-05 13:50:14 -07:00
|
|
|
kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE,
|
2012-08-16 18:32:13 -07:00
|
|
|
key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
|
2010-12-16 18:56:54 -07:00
|
|
|
strcpy(key_name, "NetworkAddressIPv4");
|
|
|
|
break;
|
|
|
|
case NetworkAddressIPv6:
|
2012-09-05 13:50:14 -07:00
|
|
|
kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE,
|
2012-08-16 18:32:13 -07:00
|
|
|
key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
|
2010-12-16 18:56:54 -07:00
|
|
|
strcpy(key_name, "NetworkAddressIPv6");
|
|
|
|
break;
|
|
|
|
case OSBuildNumber:
|
|
|
|
strcpy(key_value, os_build);
|
|
|
|
strcpy(key_name, "OSBuildNumber");
|
|
|
|
break;
|
|
|
|
case OSName:
|
|
|
|
strcpy(key_value, os_name);
|
|
|
|
strcpy(key_name, "OSName");
|
|
|
|
break;
|
|
|
|
case OSMajorVersion:
|
|
|
|
strcpy(key_value, os_major);
|
|
|
|
strcpy(key_name, "OSMajorVersion");
|
|
|
|
break;
|
|
|
|
case OSMinorVersion:
|
|
|
|
strcpy(key_value, os_minor);
|
|
|
|
strcpy(key_name, "OSMinorVersion");
|
|
|
|
break;
|
|
|
|
case OSVersion:
|
2012-10-25 14:15:49 -07:00
|
|
|
strcpy(key_value, os_version);
|
2010-12-16 18:56:54 -07:00
|
|
|
strcpy(key_name, "OSVersion");
|
|
|
|
break;
|
|
|
|
case ProcessorArchitecture:
|
|
|
|
strcpy(key_value, processor_arch);
|
|
|
|
strcpy(key_name, "ProcessorArchitecture");
|
|
|
|
break;
|
|
|
|
default:
|
2012-08-13 10:06:52 -07:00
|
|
|
hv_msg->error = HV_S_CONT;
|
2010-12-16 18:56:54 -07:00
|
|
|
break;
|
|
|
|
}
|
2014-11-19 21:51:22 -08:00
|
|
|
|
2020-01-25 21:49:41 -08:00
|
|
|
/*
|
|
|
|
* Send the value back to the kernel. Note: the write() may
|
|
|
|
* return an error due to hibernation; we can ignore the error
|
|
|
|
* by resetting the dev file, i.e. closing and re-opening it.
|
|
|
|
*/
|
2015-04-11 18:07:55 -07:00
|
|
|
kvp_done:
|
|
|
|
len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
|
|
|
|
if (len != sizeof(struct hv_kvp_msg)) {
|
|
|
|
syslog(LOG_ERR, "write failed; error: %d %s", errno,
|
|
|
|
strerror(errno));
|
2020-01-25 21:49:41 -08:00
|
|
|
goto reopen_kvp_fd;
|
2010-12-16 18:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-11 18:07:55 -07:00
|
|
|
close(kvp_fd);
|
|
|
|
exit(0);
|
2010-12-16 18:56:54 -07:00
|
|
|
}
|