From owner-svn-src-head@FreeBSD.ORG Sat Sep 13 02:15:32 2014 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 8E664488; Sat, 13 Sep 2014 02:15:32 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 753AE995; Sat, 13 Sep 2014 02:15:32 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id s8D2FW10006081; Sat, 13 Sep 2014 02:15:32 GMT (envelope-from delphij@FreeBSD.org) Received: (from delphij@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id s8D2FWsl006078; Sat, 13 Sep 2014 02:15:32 GMT (envelope-from delphij@FreeBSD.org) Message-Id: <201409130215.s8D2FWsl006078@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: delphij set sender to delphij@FreeBSD.org using -f From: Xin LI Date: Sat, 13 Sep 2014 02:15:32 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r271493 - in head: contrib/hyperv contrib/hyperv/tools contrib/hyperv/tools/scripts etc/mtree etc/rc.d libexec libexec/hyperv share/mk sys/conf sys/dev/hyperv/include sys/dev/hyperv/uti... X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 13 Sep 2014 02:15:32 -0000 Author: delphij Date: Sat Sep 13 02:15:31 2014 New Revision: 271493 URL: http://svnweb.freebsd.org/changeset/base/271493 Log: Import HyperV Key-Value Pair (KVP) driver and daemon code by Microsoft, many thanks for their continued support of FreeBSD. While I'm there, also implement a new build knob, WITHOUT_HYPERV to disable building and installing of the HyperV utilities when necessary. The HyperV utilities are only built for i386 and amd64 targets. This is a stable/10 candidate for inclusion with 10.1-RELEASE. Submitted by: Wei Hu MFC after: 1 week Added: head/contrib/hyperv/ head/contrib/hyperv/tools/ head/contrib/hyperv/tools/hv_kvp_daemon.8 (contents, props changed) head/contrib/hyperv/tools/hv_kvp_daemon.c (contents, props changed) head/contrib/hyperv/tools/scripts/ head/contrib/hyperv/tools/scripts/hv_get_dhcp_info head/contrib/hyperv/tools/scripts/hv_get_dns_info head/contrib/hyperv/tools/scripts/hv_set_ifconfig head/etc/rc.d/hv_kvpd (contents, props changed) head/libexec/hyperv/ head/libexec/hyperv/Makefile (contents, props changed) head/sys/dev/hyperv/utilities/hv_kvp.c (contents, props changed) head/sys/dev/hyperv/utilities/unicode.h (contents, props changed) head/tools/build/options/WITHOUT_HYPERV (contents, props changed) head/tools/build/options/WITH_HYPERV (contents, props changed) head/usr.sbin/hyperv/ head/usr.sbin/hyperv/Makefile (contents, props changed) head/usr.sbin/hyperv/Makefile.inc (contents, props changed) head/usr.sbin/hyperv/tools/ head/usr.sbin/hyperv/tools/Makefile (contents, props changed) Modified: head/etc/mtree/BSD.usr.dist head/etc/mtree/BSD.var.dist head/etc/rc.d/Makefile head/libexec/Makefile head/share/mk/src.opts.mk head/sys/conf/files.amd64 head/sys/conf/files.i386 head/sys/dev/hyperv/include/hyperv.h head/sys/dev/hyperv/utilities/hv_kvp.h head/sys/dev/hyperv/utilities/hv_util.c head/sys/modules/hyperv/utilities/Makefile head/tools/build/mk/OptionalObsoleteFiles.inc head/usr.sbin/Makefile.amd64 head/usr.sbin/Makefile.i386 Added: head/contrib/hyperv/tools/hv_kvp_daemon.8 ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/contrib/hyperv/tools/hv_kvp_daemon.8 Sat Sep 13 02:15:31 2014 (r271493) @@ -0,0 +1,68 @@ +.\" Copyright (c) 2014 Microsoft Corp. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd September 10, 2013 +.Dt HYPER-V 4 +.Os +.Sh NAME +.Nm hv_kvp_daemon +.Nd Hyper-V Key Value Pair Daemon +.Sh SYNOPSIS +The \fBhv_kvp_daemon\fP daemon provides the ability to store, retrieve, modify and delete +Key Value pairs for FreeBSD guest partitions running on Hyper-V. +.Sh DESCRIPTION +Hyper-V allows administrators to store custom metadata in the form +of Key Value pairs inside the FreeBSD guest partition. Administrators can +use Windows Powershell scripts to add, read, modify and delete such +Key Value pairs. + +The \fBhv_kvp_daemon\fP accepts Key Value pair management requests from the +\fBhv_utils\fP driver and performs the actual metadata management on the file-system. + +The same daemon and driver combination is also used to set and get +IP addresses from a FreeBSD guest. + +The set functionality is particularly +useful when the FreeBSD guest is assigned a static IP address and is failed +over from one Hyper-V host to another. After failover, Hyper-V uses the set IP +functionality to automatically +update the FreeBSD guest's IP address to its original static value. + +On the other hand, the get IP functionality is used to update the guest IP +address in the Hyper-V management console window. +.Sh SEE ALSO +.Xr hv_vmbus 4 , +.Xr hv_utils 4 , +.Xr hv_netvsc 4 , +.Xr hv_storvsc 4 , +.Xr hv_ata_pci_disengage 4 +.Sh HISTORY +Support for Hyper-V in the form of ports was first released in September 2013. +The daemon was developed through a joint effort between Citrix Inc., +Microsoft Corp. and Network Appliance Inc.. +.Sh AUTHORS +.An -nosplit +.Fx +support for \fBhv_kvp_daemon\fP was first added by +.An Microsoft BSD Integration Services Team Aq bsdic@microsoft.com . Added: head/contrib/hyperv/tools/hv_kvp_daemon.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/contrib/hyperv/tools/hv_kvp_daemon.c Sat Sep 13 02:15:31 2014 (r271493) @@ -0,0 +1,1518 @@ +/*- + * Copyright (c) 2014 Microsoft Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hv_kvp.h" + +typedef uint8_t __u8; +typedef uint16_t __u16; +typedef uint32_t __u32; +typedef uint64_t __u64; + +/* + * ENUM Data + */ + +enum key_index { + FullyQualifiedDomainName = 0, + IntegrationServicesVersion, /*This key is serviced in the kernel*/ + NetworkAddressIPv4, + NetworkAddressIPv6, + OSBuildNumber, + OSName, + OSMajorVersion, + OSMinorVersion, + OSVersion, + ProcessorArchitecture +}; + + +enum { + IPADDR = 0, + NETMASK, + GATEWAY, + DNS +}; + + +/* Global Variables */ + +/* + * The structure for operation handlers. + */ +struct kvp_op_hdlr { + int kvp_op_key; + void (*kvp_op_init)(void); + int (*kvp_op_exec)(struct hv_kvp_msg *kvp_op_msg, void *data); +}; + +static struct kvp_op_hdlr kvp_op_hdlrs[HV_KVP_OP_COUNT]; + +/* OS information */ + +static const char *os_name = ""; +static const char *os_major = ""; +static const char *os_minor = ""; +static const char *processor_arch; +static const char *os_build; +static const char *lic_version = "BSD Pre-Release version"; +static struct utsname uts_buf; + +/* Global flags */ +static int is_daemon = 1; +static int is_debugging = 0; + +#define KVP_LOG(priority, format, args...) do { \ + if (is_debugging == 1) { \ + if (is_daemon == 1) \ + syslog(priority, format, ## args); \ + else \ + printf(format, ## args); \ + } else { \ + if (priority < LOG_DEBUG) { \ + if (is_daemon == 1) \ + syslog(priority, format, ## args); \ + else \ + printf(format, ## args); \ + } \ + } \ + } while(0) + +/* + * For KVP pool file + */ + +#define MAX_FILE_NAME 100 +#define ENTRIES_PER_BLOCK 50 + +struct kvp_record { + char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +}; + +struct kvp_pool { + int pool_fd; + int num_blocks; + struct kvp_record *records; + int num_records; + char fname[MAX_FILE_NAME]; +}; + +static struct kvp_pool kvp_pools[HV_KVP_POOL_COUNT]; + + +static void +kvp_acquire_lock(int pool) +{ + struct flock fl = { 0, 0, 0, F_WRLCK, SEEK_SET, 0 }; + + fl.l_pid = getpid(); + + if (fcntl(kvp_pools[pool].pool_fd, F_SETLKW, &fl) == -1) { + KVP_LOG(LOG_ERR, "Failed to acquire the lock pool: %d", pool); + exit(EXIT_FAILURE); + } +} + + +static void +kvp_release_lock(int pool) +{ + struct flock fl = { 0, 0, 0, F_UNLCK, SEEK_SET, 0 }; + + fl.l_pid = getpid(); + + if (fcntl(kvp_pools[pool].pool_fd, F_SETLK, &fl) == -1) { + perror("fcntl"); + KVP_LOG(LOG_ERR, "Failed to release the lock pool: %d\n", pool); + exit(EXIT_FAILURE); + } +} + + +/* + * Write in-memory copy of KVP to pool files + */ +static void +kvp_update_file(int pool) +{ + FILE *filep; + size_t bytes_written; + + kvp_acquire_lock(pool); + + filep = fopen(kvp_pools[pool].fname, "w"); + if (!filep) { + kvp_release_lock(pool); + KVP_LOG(LOG_ERR, "Failed to open file, pool: %d\n", pool); + exit(EXIT_FAILURE); + } + + bytes_written = fwrite(kvp_pools[pool].records, + sizeof(struct kvp_record), + kvp_pools[pool].num_records, filep); + + if (ferror(filep) || fclose(filep)) { + kvp_release_lock(pool); + KVP_LOG(LOG_ERR, "Failed to write file, pool: %d\n", pool); + exit(EXIT_FAILURE); + } + + kvp_release_lock(pool); +} + + +/* + * Read KVPs from pool files and store in memory + */ +static void +kvp_update_mem_state(int pool) +{ + FILE *filep; + size_t records_read = 0; + struct kvp_record *record = kvp_pools[pool].records; + struct kvp_record *readp; + int num_blocks = kvp_pools[pool].num_blocks; + int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; + + kvp_acquire_lock(pool); + + filep = fopen(kvp_pools[pool].fname, "r"); + if (!filep) { + kvp_release_lock(pool); + KVP_LOG(LOG_ERR, "Failed to open file, pool: %d\n", pool); + exit(EXIT_FAILURE); + } + for ( ; ; ) + { + readp = &record[records_read]; + records_read += fread(readp, sizeof(struct kvp_record), + ENTRIES_PER_BLOCK * num_blocks, + filep); + + if (ferror(filep)) { + KVP_LOG(LOG_ERR, "Failed to read file, pool: %d\n", pool); + exit(EXIT_FAILURE); + } + + if (!feof(filep)) { + /* + * Have more data to read. Expand the memory. + */ + num_blocks++; + record = realloc(record, alloc_unit * num_blocks); + + if (record == NULL) { + KVP_LOG(LOG_ERR, "malloc failed\n"); + exit(EXIT_FAILURE); + } + continue; + } + break; + } + + kvp_pools[pool].num_blocks = num_blocks; + kvp_pools[pool].records = record; + kvp_pools[pool].num_records = records_read; + + fclose(filep); + kvp_release_lock(pool); +} + + +static int +kvp_file_init(void) +{ + int fd; + FILE *filep; + size_t records_read; + char *fname; + struct kvp_record *record; + struct kvp_record *readp; + int num_blocks; + int i; + int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; + + if (access("/var/db/hyperv/pool", F_OK)) { + if (mkdir("/var/db/hyperv/pool", + S_IRUSR | S_IWUSR | S_IROTH)) { + KVP_LOG(LOG_ERR, " Failed to create /var/db/hyperv/pool\n"); + exit(EXIT_FAILURE); + } + } + + for (i = 0; i < HV_KVP_POOL_COUNT; i++) + { + fname = kvp_pools[i].fname; + records_read = 0; + num_blocks = 1; + snprintf(fname, MAX_FILE_NAME, "/var/db/hyperv/pool/.kvp_pool_%d", i); + fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH); + + if (fd == -1) { + return (1); + } + + + filep = fopen(fname, "r"); + if (!filep) { + return (1); + } + + record = malloc(alloc_unit * num_blocks); + if (record == NULL) { + fclose(filep); + return (1); + } + for ( ; ; ) + { + readp = &record[records_read]; + records_read += fread(readp, sizeof(struct kvp_record), + ENTRIES_PER_BLOCK, + filep); + + if (ferror(filep)) { + KVP_LOG(LOG_ERR, "Failed to read file, pool: %d\n", + i); + exit(EXIT_FAILURE); + } + + if (!feof(filep)) { + /* + * More data to read. + */ + num_blocks++; + record = realloc(record, alloc_unit * + num_blocks); + if (record == NULL) { + fclose(filep); + return (1); + } + continue; + } + break; + } + kvp_pools[i].pool_fd = fd; + kvp_pools[i].num_blocks = num_blocks; + kvp_pools[i].records = record; + kvp_pools[i].num_records = records_read; + fclose(filep); + } + + return (0); +} + + +static int +kvp_key_delete(int pool, __u8 *key, int key_size) +{ + int i; + int j, k; + int num_records; + struct kvp_record *record; + + KVP_LOG(LOG_DEBUG, "kvp_key_delete: pool = %d, " + "key = %s\n", pool, key); + + /* Update in-memory state */ + kvp_update_mem_state(pool); + + num_records = kvp_pools[pool].num_records; + record = kvp_pools[pool].records; + + for (i = 0; i < num_records; i++) + { + if (memcmp(key, record[i].key, key_size)) { + continue; + } + + KVP_LOG(LOG_DEBUG, "Found delete key in pool %d.\n", + pool); + /* + * We found a match at the end; Just update the number of + * entries and we are done. + */ + if (i == num_records) { + kvp_pools[pool].num_records--; + kvp_update_file(pool); + return (0); + } + + /* + * We found a match in the middle; Move the remaining + * entries up. + */ + 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_pools[pool].num_records--; + kvp_update_file(pool); + return (0); + } + KVP_LOG(LOG_DEBUG, "Not found delete key in pool %d.\n", + pool); + return (1); +} + + +static int +kvp_key_add_or_modify(int pool, __u8 *key, __u32 key_size, __u8 *value, + __u32 value_size) +{ + int i; + int num_records; + struct kvp_record *record; + int num_blocks; + + KVP_LOG(LOG_DEBUG, "kvp_key_add_or_modify: pool = %d, " + "key = %s, value = %s\n,", pool, key, value); + + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) { + KVP_LOG(LOG_ERR, "kvp_key_add_or_modify: returning 1\n"); + return (1); + } + + /* Update the in-memory state. */ + kvp_update_mem_state(pool); + + num_records = kvp_pools[pool].num_records; + record = kvp_pools[pool].records; + num_blocks = kvp_pools[pool].num_blocks; + + for (i = 0; i < num_records; i++) + { + if (memcmp(key, record[i].key, key_size)) { + continue; + } + + /* + * Key exists. Just update the value and we are done. + */ + memcpy(record[i].value, value, value_size); + kvp_update_file(pool); + return (0); + } + + /* + * Key doesn't exist; Add a new KVP. + */ + if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { + /* Increase the size of the recodrd array. */ + record = realloc(record, sizeof(struct kvp_record) * + ENTRIES_PER_BLOCK * (num_blocks + 1)); + + if (record == NULL) { + return (1); + } + kvp_pools[pool].num_blocks++; + } + memcpy(record[i].value, value, value_size); + memcpy(record[i].key, key, key_size); + kvp_pools[pool].records = record; + kvp_pools[pool].num_records++; + kvp_update_file(pool); + return (0); +} + + +static int +kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, + int value_size) +{ + int i; + int num_records; + struct kvp_record *record; + + KVP_LOG(LOG_DEBUG, "kvp_get_value: pool = %d, key = %s\n,", + pool, key); + + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) { + return (1); + } + + /* Update the in-memory state first. */ + kvp_update_mem_state(pool); + + num_records = kvp_pools[pool].num_records; + record = kvp_pools[pool].records; + + for (i = 0; i < num_records; i++) + { + if (memcmp(key, record[i].key, key_size)) { + continue; + } + + /* Found the key */ + memcpy(value, record[i].value, value_size); + return (0); + } + + return (1); +} + + +static int +kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, + __u8 *value, int value_size) +{ + struct kvp_record *record; + + KVP_LOG(LOG_DEBUG, "kvp_pool_enumerate: pool = %d, index = %d\n,", + pool, index); + + /* First update our in-memory state first. */ + kvp_update_mem_state(pool); + record = kvp_pools[pool].records; + + /* Index starts with 0 */ + if (index >= kvp_pools[pool].num_records) { + return (1); + } + + memcpy(key, record[index].key, key_size); + memcpy(value, record[index].value, value_size); + return (0); +} + + +static void +kvp_get_os_info(void) +{ + char *p; + + uname(&uts_buf); + os_build = uts_buf.release; + os_name = uts_buf.sysname; + processor_arch = uts_buf.machine; + + /* + * Win7 host expects the build string to be of the form: x.y.z + * Strip additional information we may have. + */ + p = strchr(os_build, '-'); + if (p) { + *p = '\0'; + } + + /* + * We don't have any other information about the FreeBSD os. + */ + return; +} + +/* + * Given the interface name, return the MAC address. + */ +static char * +kvp_if_name_to_mac(char *if_name) +{ + char *mac_addr = NULL; + struct ifaddrs *ifaddrs_ptr; + struct ifaddrs *head_ifaddrs_ptr; + struct sockaddr_dl *sdl; + int status; + + status = getifaddrs(&ifaddrs_ptr); + + if (status >= 0) { + head_ifaddrs_ptr = ifaddrs_ptr; + do { + sdl = (struct sockaddr_dl *)(uintptr_t)ifaddrs_ptr->ifa_addr; + if ((sdl->sdl_type == IFT_ETHER) && + (strcmp(ifaddrs_ptr->ifa_name, if_name) == 0)) { + mac_addr = strdup(ether_ntoa((struct ether_addr *)(LLADDR(sdl)))); + break; + } + } while ((ifaddrs_ptr = ifaddrs_ptr->ifa_next) != NULL); + freeifaddrs(head_ifaddrs_ptr); + } + + return (mac_addr); +} + + +/* + * Given the MAC address, return the interface name. + */ +static char * +kvp_mac_to_if_name(char *mac) +{ + char *if_name = NULL; + struct ifaddrs *ifaddrs_ptr; + struct ifaddrs *head_ifaddrs_ptr; + struct sockaddr_dl *sdl; + int status; + size_t i; + char *buf_ptr; + + status = getifaddrs(&ifaddrs_ptr); + + if (status >= 0) { + head_ifaddrs_ptr = ifaddrs_ptr; + do { + sdl = (struct sockaddr_dl *)(uintptr_t)ifaddrs_ptr->ifa_addr; + if (sdl->sdl_type == IFT_ETHER) { + buf_ptr = strdup(ether_ntoa((struct ether_addr *)(LLADDR(sdl)))); + for (i = 0; i < strlen(buf_ptr); i++) + { + buf_ptr[i] = toupper(buf_ptr[i]); + } + + if (strncmp(buf_ptr, mac, strlen(mac)) == 0) { + /* Caller will free the memory */ + if_name = strdup(ifaddrs_ptr->ifa_name); + free(buf_ptr); + break; + }else if (buf_ptr != NULL) { + free(buf_ptr); + } + } + } while ((ifaddrs_ptr = ifaddrs_ptr->ifa_next) != NULL); + freeifaddrs(head_ifaddrs_ptr); + } + return (if_name); +} + + +static void +kvp_process_ipconfig_file(char *cmd, + char *config_buf, size_t len, + size_t 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) { + if ((len - strlen(config_buf)) < (element_size + 1)) { + break; + } + + x = strchr(p, '\n'); + *x = '\0'; + strlcat(config_buf, p, len); + strlcat(config_buf, ";", len); + } + pclose(file); +} + + +static void +kvp_get_ipconfig_info(char *if_name, struct hv_kvp_ipaddr_value *buffer) +{ + char cmd[512]; + char dhcp_info[128]; + char *p; + FILE *file; + + /* + * Retrieve the IPV4 address of default gateway. + */ + snprintf(cmd, sizeof(cmd), "netstat -rn | grep %s | awk '/default/ {print $2 }'", if_name); + + /* + * Execute the command to gather gateway IPV4 info. + */ + kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, + (MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0); + + /* + * Retrieve the IPV6 address of default gateway. + */ + snprintf(cmd, sizeof(cmd), "netstat -rn inet6 | grep %s | awk '/default/ {print $2 }", if_name); + + /* + * Execute the command to gather gateway IPV6 info. + */ + kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, + (MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1); + + /* + * we just invoke an external script to get the DNS info. + * + * Following is the expected format of the information from the script: + * + * ipaddr1 (nameserver1) + * ipaddr2 (nameserver2) + * . + * . + */ + /* Scripts are stored in /usr/libexec/hyperv/ directory */ + snprintf(cmd, sizeof(cmd), "%s", "sh /usr/libexec/hyperv/hv_get_dns_info"); + + /* + * Execute the command to get DNS info. + */ + kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr, + (MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0); + + /* + * Invoke an external script to get the DHCP state info. + * The parameter to the script is the interface name. + * Here is the expected output: + * + * Enabled: DHCP enabled. + */ + + + snprintf(cmd, sizeof(cmd), "%s %s", + "sh /usr/libexec/hyperv/hv_get_dhcp_info", if_name); + + 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); +} + + +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); +} + + +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) { + addr = (struct sockaddr_in *)addrp; + str = inet_ntop(family, &addr->sin_addr, tmp, 50); + addr_length = INET_ADDRSTRLEN; + } else { + addr6 = (struct sockaddr_in6 *)addrp; + str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50); + addr_length = INET6_ADDRSTRLEN; + } + + if ((length - *offset) < addr_length + 1) { + return (HV_KVP_E_FAIL); + } + if (str == NULL) { + strlcpy(buffer, "inet_ntop failed\n", length); + return (HV_KVP_E_FAIL); + } + if (*offset == 0) { + strlcpy(buffer, tmp, length); + } else{ + strlcat(buffer, tmp, length); + } + strlcat(buffer, ";", length); + + *offset += strlen(str) + 1; + return (0); +} + + +static int +kvp_get_ip_info(int family, char *if_name, int op, + void *out_buffer, size_t length) +{ + struct ifaddrs *ifap; + struct ifaddrs *curp; + int offset = 0; + int sn_offset = 0; + int error = 0; + char *buffer; + size_t buffer_length; + struct hv_kvp_ipaddr_value *ip_buffer; + char cidr_mask[5]; + int weight; + int i; + unsigned int *w = NULL; + char *sn_str; + size_t sn_str_length; + struct sockaddr_in6 *addr6; + + if (op == HV_KVP_OP_ENUMERATE) { + buffer = out_buffer; + buffer_length = length; + } else { + ip_buffer = out_buffer; + buffer = (char *)ip_buffer->ip_addr; + buffer_length = sizeof(ip_buffer->ip_addr); + ip_buffer->addr_family = 0; + } + + if (getifaddrs(&ifap)) { + strlcpy(buffer, "getifaddrs failed\n", buffer_length); + return (HV_KVP_E_FAIL); + } + + curp = ifap; + while (curp != NULL) { + if (curp->ifa_addr == NULL) { + curp = curp->ifa_next; + continue; + } + + 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; + } + + /* + * We support two address families: AF_INET and AF_INET6. + * If family value is 0, we gather both supported + * address families; if not we gather info on + * the specified address family. + */ + if ((family != 0) && (curp->ifa_addr->sa_family != family)) { + 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; + } + + if (op == HV_KVP_OP_GET_IP_INFO) { + /* + * Get the info other than the IP address. + */ + if (curp->ifa_addr->sa_family == AF_INET) { + ip_buffer->addr_family |= ADDR_FAMILY_IPV4; + + /* + * Get subnet info. + */ + error = kvp_process_ip_address( + curp->ifa_netmask, + AF_INET, + (char *) + ip_buffer->sub_net, + length, + &sn_offset); + if (error) { + goto kvp_get_ip_info_ipaddr; + } + } else { + ip_buffer->addr_family |= ADDR_FAMILY_IPV6; + + /* + * Get subnet info in CIDR format. + */ + weight = 0; + sn_str = (char *)ip_buffer->sub_net; + sn_str_length = sizeof(ip_buffer->sub_net); + addr6 = (struct sockaddr_in6 *)(uintptr_t) + curp->ifa_netmask; + w = (unsigned int *)(uintptr_t)addr6->sin6_addr.s6_addr; + + for (i = 0; i < 4; i++) + { + weight += hweight32(&w[i]); + } + + snprintf(cidr_mask, sizeof(cidr_mask), "/%d", weight); + if ((length - sn_offset) < + (strlen(cidr_mask) + 1)) { + goto kvp_get_ip_info_ipaddr; + } + + if (sn_offset == 0) { + strlcpy(sn_str, cidr_mask, sn_str_length); + } else{ + strlcat(sn_str, cidr_mask, sn_str_length); + } + strlcat((char *)ip_buffer->sub_net, ";", sn_str_length); + sn_offset += strlen(sn_str) + 1; + } + + /* + * Collect other ip configuration info. *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***