Index: head/contrib/hyperv/tools/hv_kvp_daemon.c =================================================================== --- head/contrib/hyperv/tools/hv_kvp_daemon.c (revision 308200) +++ head/contrib/hyperv/tools/hv_kvp_daemon.c (revision 308201) @@ -1,1517 +1,1523 @@ /*- * 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 #include "hv_kvp.h" - +#include "hv_utilreg.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 (mkdir("/var/db/hyperv/pool", S_IRUSR | S_IWUSR | S_IROTH) < 0 && (errno != EEXIST && errno != EISDIR)) { 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) { close(fd); return (1); } record = malloc(alloc_unit * num_blocks); if (record == NULL) { close(fd); 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) { close(fd); 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 idx, __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, idx); /* First update our in-memory state first. */ kvp_update_mem_state(pool); record = kvp_pools[pool].records; /* Index starts with 0 */ if (idx >= kvp_pools[pool].num_records) { return (1); } memcpy(key, record[idx].key, key_size); memcpy(value, record[idx].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; char *buf_ptr, *p; 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)))); if (buf_ptr != NULL) { for (p = buf_ptr; *p != '\0'; p++) *p = toupper(*p); 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 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); + 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); + return (EINVAL); } if (str == NULL) { strlcpy(buffer, "inet_ntop failed\n", length); - return (HV_KVP_E_FAIL); + return (errno); } 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 = NULL; 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); + return (errno); } 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. */ - kvp_get_ipconfig_info(if_name, ip_buffer); } kvp_get_ip_info_ipaddr: error = kvp_process_ip_address(curp->ifa_addr, curp->ifa_addr->sa_family, buffer, length, &offset); if (error) { goto kvp_get_ip_info_done; } curp = curp->ifa_next; } kvp_get_ip_info_done: freeifaddrs(ifap); return (error); } static int kvp_write_file(FILE *f, const char *s1, const char *s2, const char *s3) { int ret; ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3); if (ret < 0) { - return (HV_KVP_E_FAIL); + return (EIO); } return (0); } static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) { int error = 0; char if_file[128]; FILE *file; char cmd[512]; char *mac_addr; /* * FreeBSD - Configuration File */ snprintf(if_file, sizeof(if_file), "%s%s", "/var/db/hyperv", "hv_set_ip_data"); file = fopen(if_file, "w"); if (file == NULL) { KVP_LOG(LOG_ERR, "FreeBSD Failed to open config file\n"); - return (HV_KVP_E_FAIL); + return (errno); } /* * Write out the MAC address. */ mac_addr = kvp_if_name_to_mac(if_name); if (mac_addr == NULL) { - error = HV_KVP_E_FAIL; + error = EINVAL; goto kvp_set_ip_info_error; } /* MAC Address */ error = kvp_write_file(file, "HWADDR", "", mac_addr); if (error) { goto kvp_set_ip_info_error; } /* Interface Name */ error = kvp_write_file(file, "IF_NAME", "", if_name); if (error) { goto kvp_set_ip_info_error; } /* IP Address */ error = kvp_write_file(file, "IP_ADDR", "", (char *)new_val->ip_addr); if (error) { goto kvp_set_ip_info_error; } /* Subnet Mask */ error = kvp_write_file(file, "SUBNET", "", (char *)new_val->sub_net); if (error) { goto kvp_set_ip_info_error; } /* Gateway */ error = kvp_write_file(file, "GATEWAY", "", (char *)new_val->gate_way); if (error) { goto kvp_set_ip_info_error; } /* DNS */ error = kvp_write_file(file, "DNS", "", (char *)new_val->dns_addr); if (error) { goto kvp_set_ip_info_error; } /* DHCP */ if (new_val->dhcp_enabled) { error = kvp_write_file(file, "DHCP", "", "1"); } else{ error = kvp_write_file(file, "DHCP", "", "0"); } if (error) { goto kvp_set_ip_info_error; } free(mac_addr); fclose(file); /* * Invoke the external script with the populated * configuration file. */ snprintf(cmd, sizeof(cmd), "%s %s", "sh /usr/libexec/hyperv/hv_set_ifconfig", if_file); system(cmd); return (0); kvp_set_ip_info_error: KVP_LOG(LOG_ERR, "Failed to write config file\n"); free(mac_addr); fclose(file); return (error); } static int kvp_get_domain_name(char *buffer, int length) { struct addrinfo hints, *info; int error = 0; gethostname(buffer, length); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; /* Get only ipv4 addrinfo. */ hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; error = getaddrinfo(buffer, NULL, &hints, &info); if (error != 0) { strlcpy(buffer, "getaddrinfo failed\n", length); return (error); } strlcpy(buffer, info->ai_canonname, length); freeaddrinfo(info); return (error); } static int kvp_op_getipinfo(struct hv_kvp_msg *op_msg, void *data __unused) { struct hv_kvp_ipaddr_value *ip_val; char *if_name; + int error = 0; assert(op_msg != NULL); KVP_LOG(LOG_DEBUG, "In kvp_op_getipinfo.\n"); ip_val = &op_msg->body.kvp_ip_val; - op_msg->hdr.error = HV_KVP_S_OK; + op_msg->hdr.error = HV_S_OK; if_name = kvp_mac_to_if_name((char *)ip_val->adapter_id); if (if_name == NULL) { /* No interface found with the mac address. */ - op_msg->hdr.error = HV_KVP_E_FAIL; + op_msg->hdr.error = HV_E_FAIL; goto kvp_op_getipinfo_done; } - op_msg->hdr.error = kvp_get_ip_info(0, if_name, + error = kvp_get_ip_info(0, if_name, HV_KVP_OP_GET_IP_INFO, ip_val, (MAX_IP_ADDR_SIZE * 2)); - + if (error) + op_msg->hdr.error = HV_E_FAIL; free(if_name); kvp_op_getipinfo_done: - return(op_msg->hdr.error); + return (error); } static int kvp_op_setipinfo(struct hv_kvp_msg *op_msg, void *data __unused) { struct hv_kvp_ipaddr_value *ip_val; char *if_name; + int error = 0; assert(op_msg != NULL); KVP_LOG(LOG_DEBUG, "In kvp_op_setipinfo.\n"); ip_val = &op_msg->body.kvp_ip_val; - op_msg->hdr.error = HV_KVP_S_OK; + op_msg->hdr.error = HV_S_OK; if_name = (char *)ip_val->adapter_id; if (if_name == NULL) { /* No adapter provided. */ - op_msg->hdr.error = HV_KVP_GUID_NOTFOUND; + op_msg->hdr.error = HV_GUID_NOTFOUND; goto kvp_op_setipinfo_done; } - op_msg->hdr.error = kvp_set_ip_info(if_name, ip_val); - + error = kvp_set_ip_info(if_name, ip_val); + if (error) + op_msg->hdr.error = HV_E_FAIL; kvp_op_setipinfo_done: - return(op_msg->hdr.error); + return (error); } static int kvp_op_setgetdel(struct hv_kvp_msg *op_msg, void *data) { struct kvp_op_hdlr *op_hdlr = (struct kvp_op_hdlr *)data; int error = 0; int op_pool; assert(op_msg != NULL); assert(op_hdlr != NULL); op_pool = op_msg->hdr.kvp_hdr.pool; - op_msg->hdr.error = HV_KVP_S_OK; + op_msg->hdr.error = HV_S_OK; switch(op_hdlr->kvp_op_key) { case HV_KVP_OP_SET: if (op_pool == HV_KVP_POOL_AUTO) { /* Auto Pool is not writeable from host side. */ error = 1; KVP_LOG(LOG_ERR, "Ilegal to write to pool %d from host\n", op_pool); } else { error = kvp_key_add_or_modify(op_pool, op_msg->body.kvp_set.data.key, op_msg->body.kvp_set.data.key_size, op_msg->body.kvp_set.data.msg_value.value, op_msg->body.kvp_set.data.value_size); } break; case HV_KVP_OP_GET: error = kvp_get_value(op_pool, op_msg->body.kvp_get.data.key, op_msg->body.kvp_get.data.key_size, op_msg->body.kvp_get.data.msg_value.value, op_msg->body.kvp_get.data.value_size); break; case HV_KVP_OP_DELETE: if (op_pool == HV_KVP_POOL_AUTO) { /* Auto Pool is not writeable from host side. */ error = 1; KVP_LOG(LOG_ERR, "Ilegal to change pool %d from host\n", op_pool); } else { error = kvp_key_delete(op_pool, op_msg->body.kvp_delete.key, op_msg->body.kvp_delete.key_size); } break; default: break; } if (error != 0) - op_msg->hdr.error = HV_KVP_S_CONT; - + op_msg->hdr.error = HV_S_CONT; return(error); } static int kvp_op_enumerate(struct hv_kvp_msg *op_msg, void *data __unused) { char *key_name, *key_value; int error = 0; int op_pool; int op; assert(op_msg != NULL); op = op_msg->hdr.kvp_hdr.operation; op_pool = op_msg->hdr.kvp_hdr.pool; - op_msg->hdr.error = HV_KVP_S_OK; + op_msg->hdr.error = HV_S_OK; /* * If the pool is not HV_KVP_POOL_AUTO, read from the appropriate * pool and return the KVP according to the index requested. */ if (op_pool != HV_KVP_POOL_AUTO) { if (kvp_pool_enumerate(op_pool, op_msg->body.kvp_enum_data.index, op_msg->body.kvp_enum_data.data.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE, op_msg->body.kvp_enum_data.data.msg_value.value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) { - op_msg->hdr.error = HV_KVP_S_CONT; + op_msg->hdr.error = HV_S_CONT; error = -1; } goto kvp_op_enumerate_done; } key_name = (char *)op_msg->body.kvp_enum_data.data.key; key_value = (char *)op_msg->body.kvp_enum_data.data.msg_value.value; switch (op_msg->body.kvp_enum_data.index) { case FullyQualifiedDomainName: kvp_get_domain_name(key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); strcpy(key_name, "FullyQualifiedDomainName"); break; case IntegrationServicesVersion: strcpy(key_name, "IntegrationServicesVersion"); strlcpy(key_value, lic_version, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); break; case NetworkAddressIPv4: kvp_get_ip_info(AF_INET, NULL, HV_KVP_OP_ENUMERATE, key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); strcpy(key_name, "NetworkAddressIPv4"); break; case NetworkAddressIPv6: kvp_get_ip_info(AF_INET6, NULL, HV_KVP_OP_ENUMERATE, key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); strcpy(key_name, "NetworkAddressIPv6"); break; case OSBuildNumber: strlcpy(key_value, os_build, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); strcpy(key_name, "OSBuildNumber"); break; case OSName: strlcpy(key_value, os_name, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); strcpy(key_name, "OSName"); break; case OSMajorVersion: strlcpy(key_value, os_major, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); strcpy(key_name, "OSMajorVersion"); break; case OSMinorVersion: strlcpy(key_value, os_minor, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); strcpy(key_name, "OSMinorVersion"); break; case OSVersion: strlcpy(key_value, os_build, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); strcpy(key_name, "OSVersion"); break; case ProcessorArchitecture: strlcpy(key_value, processor_arch, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); strcpy(key_name, "ProcessorArchitecture"); break; default: #ifdef DEBUG KVP_LOG(LOG_ERR, "Auto pool Index %d not found.\n", op_msg->body.kvp_enum_data.index); #endif - op_msg->hdr.error = HV_KVP_S_CONT; + op_msg->hdr.error = HV_S_CONT; error = -1; break; } kvp_op_enumerate_done: + if (error != 0) + op_msg->hdr.error = HV_S_CONT; return(error); } /* * Load handler, and call init routine if provided. */ static int kvp_op_load(int key, void (*init)(void), int (*exec)(struct hv_kvp_msg *, void *)) { int error = 0; if (key < 0 || key >= HV_KVP_OP_COUNT) { KVP_LOG(LOG_ERR, "Operation key out of supported range\n"); error = -1; goto kvp_op_load_done; } kvp_op_hdlrs[key].kvp_op_key = key; kvp_op_hdlrs[key].kvp_op_init = init; kvp_op_hdlrs[key].kvp_op_exec = exec; if (kvp_op_hdlrs[key].kvp_op_init != NULL) kvp_op_hdlrs[key].kvp_op_init(); kvp_op_load_done: return(error); } /* * Initialize the operation hanlders. */ static int kvp_ops_init(void) { int i; /* Set the initial values. */ for (i = 0; i < HV_KVP_OP_COUNT; i++) { kvp_op_hdlrs[i].kvp_op_key = -1; kvp_op_hdlrs[i].kvp_op_init = NULL; kvp_op_hdlrs[i].kvp_op_exec = NULL; } return(kvp_op_load(HV_KVP_OP_GET, NULL, kvp_op_setgetdel) | kvp_op_load(HV_KVP_OP_SET, NULL, kvp_op_setgetdel) | kvp_op_load(HV_KVP_OP_DELETE, NULL, kvp_op_setgetdel) | kvp_op_load(HV_KVP_OP_ENUMERATE, kvp_get_os_info, kvp_op_enumerate) | kvp_op_load(HV_KVP_OP_GET_IP_INFO, NULL, kvp_op_getipinfo) | kvp_op_load(HV_KVP_OP_SET_IP_INFO, NULL, kvp_op_setipinfo)); } int main(int argc, char *argv[]) { struct hv_kvp_msg *hv_kvp_dev_buf; struct hv_kvp_msg *hv_msg; struct pollfd hv_kvp_poll_fd[1]; int op, pool; int hv_kvp_dev_fd, error, len, r; int ch; while ((ch = getopt(argc, argv, "dn")) != -1) { switch (ch) { case 'n': /* Run as regular process for debugging purpose. */ is_daemon = 0; break; case 'd': /* Generate debugging output */ is_debugging = 1; break; default: break; } } openlog("HV_KVP", 0, LOG_USER); /* Become daemon first. */ if (is_daemon == 1) daemon(1, 0); else KVP_LOG(LOG_DEBUG, "Run as regular process.\n"); KVP_LOG(LOG_INFO, "HV_KVP starting; pid is: %d\n", getpid()); /* Communication buffer hv_kvp_dev_buf */ hv_kvp_dev_buf = malloc(sizeof(*hv_kvp_dev_buf)); /* Buffer for daemon internal use */ hv_msg = malloc(sizeof(*hv_msg)); /* Memory allocation failed */ if (hv_kvp_dev_buf == NULL || hv_msg == NULL) { KVP_LOG(LOG_ERR, "Failed to allocate memory for hv buffer\n"); exit(EXIT_FAILURE); } /* Initialize op handlers */ if (kvp_ops_init() != 0) { KVP_LOG(LOG_ERR, "Failed to initizlize operation handlers\n"); exit(EXIT_FAILURE); } if (kvp_file_init()) { KVP_LOG(LOG_ERR, "Failed to initialize the pools\n"); exit(EXIT_FAILURE); } /* Open the Character Device */ hv_kvp_dev_fd = open("/dev/hv_kvp_dev", O_RDWR); if (hv_kvp_dev_fd < 0) { KVP_LOG(LOG_ERR, "open /dev/hv_kvp_dev failed; error: %d %s\n", errno, strerror(errno)); exit(EXIT_FAILURE); } /* Initialize the struct for polling the char device */ hv_kvp_poll_fd[0].fd = hv_kvp_dev_fd; hv_kvp_poll_fd[0].events = (POLLIN | POLLRDNORM); /* Register the daemon to the KVP driver */ memset(hv_kvp_dev_buf, 0, sizeof(*hv_kvp_dev_buf)); hv_kvp_dev_buf->hdr.kvp_hdr.operation = HV_KVP_OP_REGISTER; len = write(hv_kvp_dev_fd, hv_kvp_dev_buf, sizeof(*hv_kvp_dev_buf)); for (;;) { r = poll (hv_kvp_poll_fd, 1, INFTIM); KVP_LOG(LOG_DEBUG, "poll returned r = %d, revent = 0x%x\n", r, hv_kvp_poll_fd[0].revents); if (r == 0 || (r < 0 && errno == EAGAIN) || (r < 0 && errno == EINTR)) { /* Nothing to read */ continue; } if (r < 0) { /* * For pread return failure other than EAGAIN, * we want to exit. */ KVP_LOG(LOG_ERR, "Poll failed.\n"); perror("poll"); exit(EIO); } /* Read from character device */ len = pread(hv_kvp_dev_fd, hv_kvp_dev_buf, sizeof(*hv_kvp_dev_buf), 0); if (len < 0) { KVP_LOG(LOG_ERR, "Read failed.\n"); perror("pread"); exit(EIO); } if (len != sizeof(struct hv_kvp_msg)) { KVP_LOG(LOG_ERR, "read len is: %d\n", len); continue; } /* Copy hv_kvp_dev_buf to hv_msg */ memcpy(hv_msg, hv_kvp_dev_buf, sizeof(*hv_msg)); /* * We will use the KVP header information to pass back * the error from this daemon. So, first save the op * and pool info to local variables. */ op = hv_msg->hdr.kvp_hdr.operation; pool = hv_msg->hdr.kvp_hdr.pool; if (op < 0 || op >= HV_KVP_OP_COUNT || kvp_op_hdlrs[op].kvp_op_exec == NULL) { KVP_LOG(LOG_WARNING, "Unsupported operation OP = %d\n", op); hv_msg->hdr.error = HV_ERROR_NOT_SUPPORTED; } else { /* * Call the operateion handler's execution routine. */ error = kvp_op_hdlrs[op].kvp_op_exec(hv_msg, (void *)&kvp_op_hdlrs[op]); - if (error != 0 && hv_msg->hdr.error != HV_KVP_S_CONT) - KVP_LOG(LOG_WARNING, - "Operation failed OP = %d, error = 0x%x\n", - op, error); + if (error != 0) { + assert(hv_msg->hdr.error != HV_S_OK); + if (hv_msg->hdr.error != HV_S_CONT) + KVP_LOG(LOG_WARNING, + "Operation failed OP = %d, error = 0x%x\n", + op, error); + } } /* * Send the value back to the kernel. The response is * already in the receive buffer. */ hv_kvp_done: len = pwrite(hv_kvp_dev_fd, hv_msg, sizeof(*hv_kvp_dev_buf), 0); if (len != sizeof(struct hv_kvp_msg)) { KVP_LOG(LOG_ERR, "write len is: %d\n", len); goto hv_kvp_done; } } } Index: head/sys/dev/hyperv/utilities/hv_kvp.c =================================================================== --- head/sys/dev/hyperv/utilities/hv_kvp.c (revision 308200) +++ head/sys/dev/hyperv/utilities/hv_kvp.c (revision 308201) @@ -1,918 +1,920 @@ /*- * Copyright (c) 2014,2016 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. */ /* * Author: Sainath Varanasi. * Date: 4/2012 * Email: bsdic@microsoft.com */ #include __FBSDID("$FreeBSD$"); #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 #include #include #include #include #include #include "hv_util.h" #include "unicode.h" #include "hv_kvp.h" #include "vmbus_if.h" /* hv_kvp defines */ #define BUFFERSIZE sizeof(struct hv_kvp_msg) -#define KVP_SUCCESS 0 -#define KVP_ERROR 1 #define kvp_hdr hdr.kvp_hdr #define KVP_FWVER_MAJOR 3 #define KVP_FWVER VMBUS_IC_VERSION(KVP_FWVER_MAJOR, 0) #define KVP_MSGVER_MAJOR 4 #define KVP_MSGVER VMBUS_IC_VERSION(KVP_MSGVER_MAJOR, 0) /* hv_kvp debug control */ static int hv_kvp_log = 0; #define hv_kvp_log_error(...) do { \ if (hv_kvp_log > 0) \ log(LOG_ERR, "hv_kvp: " __VA_ARGS__); \ } while (0) #define hv_kvp_log_info(...) do { \ if (hv_kvp_log > 1) \ log(LOG_INFO, "hv_kvp: " __VA_ARGS__); \ } while (0) static const struct vmbus_ic_desc vmbus_kvp_descs[] = { { .ic_guid = { .hv_guid = { 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 } }, .ic_desc = "Hyper-V KVP" }, VMBUS_IC_DESC_END }; /* character device prototypes */ static d_open_t hv_kvp_dev_open; static d_close_t hv_kvp_dev_close; static d_read_t hv_kvp_dev_daemon_read; static d_write_t hv_kvp_dev_daemon_write; static d_poll_t hv_kvp_dev_daemon_poll; /* hv_kvp character device structure */ static struct cdevsw hv_kvp_cdevsw = { .d_version = D_VERSION, .d_open = hv_kvp_dev_open, .d_close = hv_kvp_dev_close, .d_read = hv_kvp_dev_daemon_read, .d_write = hv_kvp_dev_daemon_write, .d_poll = hv_kvp_dev_daemon_poll, .d_name = "hv_kvp_dev", }; /* * Global state to track and synchronize multiple * KVP transaction requests from the host. */ typedef struct hv_kvp_sc { struct hv_util_sc util_sc; device_t dev; /* Unless specified the pending mutex should be * used to alter the values of the following parameters: * 1. req_in_progress * 2. req_timed_out */ struct mtx pending_mutex; struct task task; /* To track if transaction is active or not */ boolean_t req_in_progress; /* Tracks if daemon did not reply back in time */ boolean_t req_timed_out; /* Tracks if daemon is serving a request currently */ boolean_t daemon_busy; /* Length of host message */ uint32_t host_msg_len; /* Host message id */ uint64_t host_msg_id; /* Current kvp message from the host */ struct hv_kvp_msg *host_kvp_msg; /* Current kvp message for daemon */ struct hv_kvp_msg daemon_kvp_msg; /* Rcv buffer for communicating with the host*/ uint8_t *rcv_buf; /* Device semaphore to control communication */ struct sema dev_sema; /* Indicates if daemon registered with driver */ boolean_t register_done; /* Character device status */ boolean_t dev_accessed; struct cdev *hv_kvp_dev; struct proc *daemon_task; struct selinfo hv_kvp_selinfo; } hv_kvp_sc; /* hv_kvp prototypes */ static int hv_kvp_req_in_progress(hv_kvp_sc *sc); static void hv_kvp_transaction_init(hv_kvp_sc *sc, uint32_t, uint64_t, uint8_t *); static void hv_kvp_send_msg_to_daemon(hv_kvp_sc *sc); static void hv_kvp_process_request(void *context, int pending); /* * hv_kvp low level functions */ /* * Check if kvp transaction is in progres */ static int hv_kvp_req_in_progress(hv_kvp_sc *sc) { return (sc->req_in_progress); } /* * This routine is called whenever a message is received from the host */ static void hv_kvp_transaction_init(hv_kvp_sc *sc, uint32_t rcv_len, uint64_t request_id, uint8_t *rcv_buf) { /* Store all the relevant message details in the global structure */ /* Do not need to use mutex for req_in_progress here */ sc->req_in_progress = true; sc->host_msg_len = rcv_len; sc->host_msg_id = request_id; sc->rcv_buf = rcv_buf; sc->host_kvp_msg = (struct hv_kvp_msg *)&rcv_buf[ sizeof(struct hv_vmbus_pipe_hdr) + sizeof(struct hv_vmbus_icmsg_hdr)]; } /* * Convert ip related info in umsg from utf8 to utf16 and store in hmsg */ static int hv_kvp_convert_utf8_ipinfo_to_utf16(struct hv_kvp_msg *umsg, struct hv_kvp_ip_msg *host_ip_msg) { int err_ip, err_subnet, err_gway, err_dns, err_adap; int UNUSED_FLAG = 1; utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.ip_addr, MAX_IP_ADDR_SIZE, (char *)umsg->body.kvp_ip_val.ip_addr, strlen((char *)umsg->body.kvp_ip_val.ip_addr), UNUSED_FLAG, &err_ip); utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.sub_net, MAX_IP_ADDR_SIZE, (char *)umsg->body.kvp_ip_val.sub_net, strlen((char *)umsg->body.kvp_ip_val.sub_net), UNUSED_FLAG, &err_subnet); utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.gate_way, MAX_GATEWAY_SIZE, (char *)umsg->body.kvp_ip_val.gate_way, strlen((char *)umsg->body.kvp_ip_val.gate_way), UNUSED_FLAG, &err_gway); utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.dns_addr, MAX_IP_ADDR_SIZE, (char *)umsg->body.kvp_ip_val.dns_addr, strlen((char *)umsg->body.kvp_ip_val.dns_addr), UNUSED_FLAG, &err_dns); utf8_to_utf16((uint16_t *)host_ip_msg->kvp_ip_val.adapter_id, MAX_IP_ADDR_SIZE, (char *)umsg->body.kvp_ip_val.adapter_id, strlen((char *)umsg->body.kvp_ip_val.adapter_id), UNUSED_FLAG, &err_adap); host_ip_msg->kvp_ip_val.dhcp_enabled = umsg->body.kvp_ip_val.dhcp_enabled; host_ip_msg->kvp_ip_val.addr_family = umsg->body.kvp_ip_val.addr_family; return (err_ip | err_subnet | err_gway | err_dns | err_adap); } /* * Convert ip related info in hmsg from utf16 to utf8 and store in umsg */ static int hv_kvp_convert_utf16_ipinfo_to_utf8(struct hv_kvp_ip_msg *host_ip_msg, struct hv_kvp_msg *umsg) { int err_ip, err_subnet, err_gway, err_dns, err_adap; int UNUSED_FLAG = 1; device_t *devs; int devcnt; /* IP Address */ utf16_to_utf8((char *)umsg->body.kvp_ip_val.ip_addr, MAX_IP_ADDR_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.ip_addr, MAX_IP_ADDR_SIZE, UNUSED_FLAG, &err_ip); /* Adapter ID : GUID */ utf16_to_utf8((char *)umsg->body.kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, UNUSED_FLAG, &err_adap); if (devclass_get_devices(devclass_find("hn"), &devs, &devcnt) == 0) { for (devcnt = devcnt - 1; devcnt >= 0; devcnt--) { device_t dev = devs[devcnt]; struct vmbus_channel *chan; char buf[HYPERV_GUID_STRLEN]; int n; chan = vmbus_get_channel(dev); n = hyperv_guid2str(vmbus_chan_guid_inst(chan), buf, sizeof(buf)); /* * The string in the 'kvp_ip_val.adapter_id' has * braces around the GUID; skip the leading brace * in 'kvp_ip_val.adapter_id'. */ if (strncmp(buf, ((char *)&umsg->body.kvp_ip_val.adapter_id) + 1, n) == 0) { strlcpy((char *)umsg->body.kvp_ip_val.adapter_id, device_get_nameunit(dev), MAX_ADAPTER_ID_SIZE); break; } } free(devs, M_TEMP); } /* Address Family , DHCP , SUBNET, Gateway, DNS */ umsg->kvp_hdr.operation = host_ip_msg->operation; umsg->body.kvp_ip_val.addr_family = host_ip_msg->kvp_ip_val.addr_family; umsg->body.kvp_ip_val.dhcp_enabled = host_ip_msg->kvp_ip_val.dhcp_enabled; utf16_to_utf8((char *)umsg->body.kvp_ip_val.sub_net, MAX_IP_ADDR_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.sub_net, MAX_IP_ADDR_SIZE, UNUSED_FLAG, &err_subnet); utf16_to_utf8((char *)umsg->body.kvp_ip_val.gate_way, MAX_GATEWAY_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.gate_way, MAX_GATEWAY_SIZE, UNUSED_FLAG, &err_gway); utf16_to_utf8((char *)umsg->body.kvp_ip_val.dns_addr, MAX_IP_ADDR_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.dns_addr, MAX_IP_ADDR_SIZE, UNUSED_FLAG, &err_dns); return (err_ip | err_subnet | err_gway | err_dns | err_adap); } /* * Prepare a user kvp msg based on host kvp msg (utf16 to utf8) * Ensure utf16_utf8 takes care of the additional string terminating char!! */ static void hv_kvp_convert_hostmsg_to_usermsg(struct hv_kvp_msg *hmsg, struct hv_kvp_msg *umsg) { int utf_err = 0; uint32_t value_type; struct hv_kvp_ip_msg *host_ip_msg; host_ip_msg = (struct hv_kvp_ip_msg*)hmsg; memset(umsg, 0, sizeof(struct hv_kvp_msg)); umsg->kvp_hdr.operation = hmsg->kvp_hdr.operation; umsg->kvp_hdr.pool = hmsg->kvp_hdr.pool; switch (umsg->kvp_hdr.operation) { case HV_KVP_OP_SET_IP_INFO: hv_kvp_convert_utf16_ipinfo_to_utf8(host_ip_msg, umsg); break; case HV_KVP_OP_GET_IP_INFO: utf16_to_utf8((char *)umsg->body.kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, (uint16_t *)host_ip_msg->kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, 1, &utf_err); umsg->body.kvp_ip_val.addr_family = host_ip_msg->kvp_ip_val.addr_family; break; case HV_KVP_OP_SET: value_type = hmsg->body.kvp_set.data.value_type; switch (value_type) { case HV_REG_SZ: umsg->body.kvp_set.data.value_size = utf16_to_utf8( (char *)umsg->body.kvp_set.data.msg_value.value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1, (uint16_t *)hmsg->body.kvp_set.data.msg_value.value, hmsg->body.kvp_set.data.value_size, 1, &utf_err); /* utf8 encoding */ umsg->body.kvp_set.data.value_size = umsg->body.kvp_set.data.value_size / 2; break; case HV_REG_U32: umsg->body.kvp_set.data.value_size = sprintf(umsg->body.kvp_set.data.msg_value.value, "%d", hmsg->body.kvp_set.data.msg_value.value_u32) + 1; break; case HV_REG_U64: umsg->body.kvp_set.data.value_size = sprintf(umsg->body.kvp_set.data.msg_value.value, "%llu", (unsigned long long) hmsg->body.kvp_set.data.msg_value.value_u64) + 1; break; } umsg->body.kvp_set.data.key_size = utf16_to_utf8( umsg->body.kvp_set.data.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1, (uint16_t *)hmsg->body.kvp_set.data.key, hmsg->body.kvp_set.data.key_size, 1, &utf_err); /* utf8 encoding */ umsg->body.kvp_set.data.key_size = umsg->body.kvp_set.data.key_size / 2; break; case HV_KVP_OP_GET: umsg->body.kvp_get.data.key_size = utf16_to_utf8(umsg->body.kvp_get.data.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1, (uint16_t *)hmsg->body.kvp_get.data.key, hmsg->body.kvp_get.data.key_size, 1, &utf_err); /* utf8 encoding */ umsg->body.kvp_get.data.key_size = umsg->body.kvp_get.data.key_size / 2; break; case HV_KVP_OP_DELETE: umsg->body.kvp_delete.key_size = utf16_to_utf8(umsg->body.kvp_delete.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1, (uint16_t *)hmsg->body.kvp_delete.key, hmsg->body.kvp_delete.key_size, 1, &utf_err); /* utf8 encoding */ umsg->body.kvp_delete.key_size = umsg->body.kvp_delete.key_size / 2; break; case HV_KVP_OP_ENUMERATE: umsg->body.kvp_enum_data.index = hmsg->body.kvp_enum_data.index; break; default: hv_kvp_log_info("%s: daemon_kvp_msg: Invalid operation : %d\n", __func__, umsg->kvp_hdr.operation); } } /* * Prepare a host kvp msg based on user kvp msg (utf8 to utf16) */ static int hv_kvp_convert_usermsg_to_hostmsg(struct hv_kvp_msg *umsg, struct hv_kvp_msg *hmsg) { int hkey_len = 0, hvalue_len = 0, utf_err = 0; struct hv_kvp_exchg_msg_value *host_exchg_data; char *key_name, *value; struct hv_kvp_ip_msg *host_ip_msg = (struct hv_kvp_ip_msg *)hmsg; switch (hmsg->kvp_hdr.operation) { case HV_KVP_OP_GET_IP_INFO: return (hv_kvp_convert_utf8_ipinfo_to_utf16(umsg, host_ip_msg)); case HV_KVP_OP_SET_IP_INFO: case HV_KVP_OP_SET: case HV_KVP_OP_DELETE: - return (KVP_SUCCESS); + return (0); case HV_KVP_OP_ENUMERATE: host_exchg_data = &hmsg->body.kvp_enum_data.data; key_name = umsg->body.kvp_enum_data.data.key; hkey_len = utf8_to_utf16((uint16_t *)host_exchg_data->key, ((HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2), key_name, strlen(key_name), 1, &utf_err); /* utf16 encoding */ host_exchg_data->key_size = 2 * (hkey_len + 1); value = umsg->body.kvp_enum_data.data.msg_value.value; hvalue_len = utf8_to_utf16( (uint16_t *)host_exchg_data->msg_value.value, ((HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2), value, strlen(value), 1, &utf_err); host_exchg_data->value_size = 2 * (hvalue_len + 1); host_exchg_data->value_type = HV_REG_SZ; if ((hkey_len < 0) || (hvalue_len < 0)) - return (HV_KVP_E_FAIL); + return (EINVAL); - return (KVP_SUCCESS); + return (0); case HV_KVP_OP_GET: host_exchg_data = &hmsg->body.kvp_get.data; value = umsg->body.kvp_get.data.msg_value.value; hvalue_len = utf8_to_utf16( (uint16_t *)host_exchg_data->msg_value.value, ((HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2), value, strlen(value), 1, &utf_err); /* Convert value size to uft16 */ host_exchg_data->value_size = 2 * (hvalue_len + 1); /* Use values by string */ host_exchg_data->value_type = HV_REG_SZ; if ((hkey_len < 0) || (hvalue_len < 0)) - return (HV_KVP_E_FAIL); + return (EINVAL); - return (KVP_SUCCESS); + return (0); default: - return (HV_KVP_E_FAIL); + return (EINVAL); } } /* * Send the response back to the host. */ static void -hv_kvp_respond_host(hv_kvp_sc *sc, int error) +hv_kvp_respond_host(hv_kvp_sc *sc, uint32_t error) { struct hv_vmbus_icmsg_hdr *hv_icmsg_hdrp; hv_icmsg_hdrp = (struct hv_vmbus_icmsg_hdr *) &sc->rcv_buf[sizeof(struct hv_vmbus_pipe_hdr)]; - if (error) - error = HV_KVP_E_FAIL; - hv_icmsg_hdrp->status = error; hv_icmsg_hdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | HV_ICMSGHDRFLAG_RESPONSE; error = vmbus_chan_send(vmbus_get_channel(sc->dev), VMBUS_CHANPKT_TYPE_INBAND, 0, sc->rcv_buf, sc->host_msg_len, sc->host_msg_id); if (error) hv_kvp_log_info("%s: hv_kvp_respond_host: sendpacket error:%d\n", __func__, error); } /* * This is the main kvp kernel process that interacts with both user daemon * and the host */ static void hv_kvp_send_msg_to_daemon(hv_kvp_sc *sc) { struct hv_kvp_msg *hmsg = sc->host_kvp_msg; struct hv_kvp_msg *umsg = &sc->daemon_kvp_msg; /* Prepare kvp_msg to be sent to user */ hv_kvp_convert_hostmsg_to_usermsg(hmsg, umsg); /* Send the msg to user via function deamon_read - setting sema */ sema_post(&sc->dev_sema); /* We should wake up the daemon, in case it's doing poll() */ selwakeup(&sc->hv_kvp_selinfo); } /* * Function to read the kvp request buffer from host * and interact with daemon */ static void hv_kvp_process_request(void *context, int pending) { uint8_t *kvp_buf; struct vmbus_channel *channel; uint32_t recvlen = 0; uint64_t requestid; struct hv_vmbus_icmsg_hdr *icmsghdrp; int ret = 0, error; hv_kvp_sc *sc; hv_kvp_log_info("%s: entering hv_kvp_process_request\n", __func__); sc = (hv_kvp_sc*)context; kvp_buf = sc->util_sc.receive_buffer; channel = vmbus_get_channel(sc->dev); recvlen = sc->util_sc.ic_buflen; ret = vmbus_chan_recv(channel, kvp_buf, &recvlen, &requestid); KASSERT(ret != ENOBUFS, ("hvkvp recvbuf is not large enough")); /* XXX check recvlen to make sure that it contains enough data */ while ((ret == 0) && (recvlen > 0)) { icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &kvp_buf[sizeof(struct hv_vmbus_pipe_hdr)]; hv_kvp_transaction_init(sc, recvlen, requestid, kvp_buf); if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { error = vmbus_ic_negomsg(&sc->util_sc, kvp_buf, &recvlen, KVP_FWVER, KVP_MSGVER); /* XXX handle vmbus_ic_negomsg failure. */ - hv_kvp_respond_host(sc, error); - + if (!error) + hv_kvp_respond_host(sc, HV_S_OK); + else + hv_kvp_respond_host(sc, HV_E_FAIL); /* * It is ok to not acquire the mutex before setting * req_in_progress here because negotiation is the * first thing that happens and hence there is no * chance of a race condition. */ sc->req_in_progress = false; hv_kvp_log_info("%s :version negotiated\n", __func__); } else { if (!sc->daemon_busy) { hv_kvp_log_info("%s: issuing qury to daemon\n", __func__); mtx_lock(&sc->pending_mutex); sc->req_timed_out = false; sc->daemon_busy = true; mtx_unlock(&sc->pending_mutex); hv_kvp_send_msg_to_daemon(sc); hv_kvp_log_info("%s: waiting for daemon\n", __func__); } /* Wait 5 seconds for daemon to respond back */ tsleep(sc, 0, "kvpworkitem", 5 * hz); hv_kvp_log_info("%s: came out of wait\n", __func__); } mtx_lock(&sc->pending_mutex); /* Notice that once req_timed_out is set to true * it will remain true until the next request is * sent to the daemon. The response from daemon * is forwarded to host only when this flag is * false. */ sc->req_timed_out = true; /* * Cancel request if so need be. */ if (hv_kvp_req_in_progress(sc)) { hv_kvp_log_info("%s: request was still active after wait so failing\n", __func__); - hv_kvp_respond_host(sc, HV_KVP_E_FAIL); + hv_kvp_respond_host(sc, HV_E_FAIL); sc->req_in_progress = false; } mtx_unlock(&sc->pending_mutex); /* * Try reading next buffer */ recvlen = sc->util_sc.ic_buflen; ret = vmbus_chan_recv(channel, kvp_buf, &recvlen, &requestid); KASSERT(ret != ENOBUFS, ("hvkvp recvbuf is not large enough")); /* XXX check recvlen to make sure that it contains enough data */ hv_kvp_log_info("%s: read: context %p, ret =%d, recvlen=%d\n", __func__, context, ret, recvlen); } } /* * Callback routine that gets called whenever there is a message from host */ static void hv_kvp_callback(struct vmbus_channel *chan __unused, void *context) { hv_kvp_sc *sc = (hv_kvp_sc*)context; /* The first request from host will not be handled until daemon is registered. when callback is triggered without a registered daemon, callback just return. When a new daemon gets regsitered, this callbcak is trigged from _write op. */ if (sc->register_done) { hv_kvp_log_info("%s: Queuing work item\n", __func__); taskqueue_enqueue(taskqueue_thread, &sc->task); } } static int hv_kvp_dev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; hv_kvp_log_info("%s: Opened device \"hv_kvp_device\" successfully.\n", __func__); if (sc->dev_accessed) return (-EBUSY); sc->daemon_task = curproc; sc->dev_accessed = true; sc->daemon_busy = false; return (0); } static int hv_kvp_dev_close(struct cdev *dev __unused, int fflag __unused, int devtype __unused, struct thread *td __unused) { hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; hv_kvp_log_info("%s: Closing device \"hv_kvp_device\".\n", __func__); sc->dev_accessed = false; sc->register_done = false; return (0); } /* * hv_kvp_daemon read invokes this function * acts as a send to daemon */ static int hv_kvp_dev_daemon_read(struct cdev *dev, struct uio *uio, int ioflag __unused) { size_t amt; int error = 0; struct hv_kvp_msg *hv_kvp_dev_buf; hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; - /* Check hv_kvp daemon registration status*/ + /* Read is not allowed util registering is done. */ if (!sc->register_done) - return (KVP_ERROR); + return (EPERM); sema_wait(&sc->dev_sema); hv_kvp_dev_buf = malloc(sizeof(*hv_kvp_dev_buf), M_TEMP, M_WAITOK); memcpy(hv_kvp_dev_buf, &sc->daemon_kvp_msg, sizeof(struct hv_kvp_msg)); amt = MIN(uio->uio_resid, uio->uio_offset >= BUFFERSIZE + 1 ? 0 : BUFFERSIZE + 1 - uio->uio_offset); if ((error = uiomove(hv_kvp_dev_buf, amt, uio)) != 0) hv_kvp_log_info("%s: hv_kvp uiomove read failed!\n", __func__); free(hv_kvp_dev_buf, M_TEMP); return (error); } /* * hv_kvp_daemon write invokes this function * acts as a receive from daemon */ static int hv_kvp_dev_daemon_write(struct cdev *dev, struct uio *uio, int ioflag __unused) { size_t amt; int error = 0; struct hv_kvp_msg *hv_kvp_dev_buf; hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; uio->uio_offset = 0; hv_kvp_dev_buf = malloc(sizeof(*hv_kvp_dev_buf), M_TEMP, M_WAITOK); amt = MIN(uio->uio_resid, BUFFERSIZE); error = uiomove(hv_kvp_dev_buf, amt, uio); if (error != 0) { free(hv_kvp_dev_buf, M_TEMP); return (error); } memcpy(&sc->daemon_kvp_msg, hv_kvp_dev_buf, sizeof(struct hv_kvp_msg)); free(hv_kvp_dev_buf, M_TEMP); if (sc->register_done == false) { if (sc->daemon_kvp_msg.kvp_hdr.operation == HV_KVP_OP_REGISTER) { sc->register_done = true; hv_kvp_callback(vmbus_get_channel(sc->dev), dev->si_drv1); } else { hv_kvp_log_info("%s, KVP Registration Failed\n", __func__); - return (KVP_ERROR); + return (EINVAL); } } else { mtx_lock(&sc->pending_mutex); if(!sc->req_timed_out) { struct hv_kvp_msg *hmsg = sc->host_kvp_msg; struct hv_kvp_msg *umsg = &sc->daemon_kvp_msg; - hv_kvp_convert_usermsg_to_hostmsg(umsg, hmsg); - hv_kvp_respond_host(sc, KVP_SUCCESS); + error = hv_kvp_convert_usermsg_to_hostmsg(umsg, hmsg); + hv_kvp_respond_host(sc, umsg->hdr.error); wakeup(sc); sc->req_in_progress = false; + if (umsg->hdr.error != HV_S_OK) + hv_kvp_log_info("%s, Error 0x%x from daemon\n", + __func__, umsg->hdr.error); + if (error) + hv_kvp_log_info("%s, Error from convert\n", __func__); } sc->daemon_busy = false; mtx_unlock(&sc->pending_mutex); } return (error); } /* * hv_kvp_daemon poll invokes this function to check if data is available * for daemon to read. */ static int hv_kvp_dev_daemon_poll(struct cdev *dev, int events, struct thread *td) { int revents = 0; hv_kvp_sc *sc = (hv_kvp_sc*)dev->si_drv1; mtx_lock(&sc->pending_mutex); /* * We check global flag daemon_busy for the data availiability for * userland to read. Deamon_busy is set to true before driver has data * for daemon to read. It is set to false after daemon sends * then response back to driver. */ if (sc->daemon_busy == true) revents = POLLIN; else selrecord(td, &sc->hv_kvp_selinfo); mtx_unlock(&sc->pending_mutex); return (revents); } static int hv_kvp_probe(device_t dev) { return (vmbus_ic_probe(dev, vmbus_kvp_descs)); } static int hv_kvp_attach(device_t dev) { int error; struct sysctl_oid_list *child; struct sysctl_ctx_list *ctx; hv_kvp_sc *sc = (hv_kvp_sc*)device_get_softc(dev); sc->dev = dev; sema_init(&sc->dev_sema, 0, "hv_kvp device semaphore"); mtx_init(&sc->pending_mutex, "hv-kvp pending mutex", NULL, MTX_DEF); ctx = device_get_sysctl_ctx(dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_INT(ctx, child, OID_AUTO, "hv_kvp_log", - CTLFLAG_RW, &hv_kvp_log, 0, "Hyperv KVP service log level"); + CTLFLAG_RWTUN, &hv_kvp_log, 0, "Hyperv KVP service log level"); TASK_INIT(&sc->task, 0, hv_kvp_process_request, sc); /* create character device */ error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, &sc->hv_kvp_dev, &hv_kvp_cdevsw, 0, UID_ROOT, GID_WHEEL, 0640, "hv_kvp_dev"); if (error != 0) return (error); sc->hv_kvp_dev->si_drv1 = sc; return hv_util_attach(dev, hv_kvp_callback); } static int hv_kvp_detach(device_t dev) { hv_kvp_sc *sc = (hv_kvp_sc*)device_get_softc(dev); if (sc->daemon_task != NULL) { PROC_LOCK(sc->daemon_task); kern_psignal(sc->daemon_task, SIGKILL); PROC_UNLOCK(sc->daemon_task); } destroy_dev(sc->hv_kvp_dev); return hv_util_detach(dev); } static device_method_t kvp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hv_kvp_probe), DEVMETHOD(device_attach, hv_kvp_attach), DEVMETHOD(device_detach, hv_kvp_detach), { 0, 0 } }; static driver_t kvp_driver = { "hvkvp", kvp_methods, sizeof(hv_kvp_sc)}; static devclass_t kvp_devclass; DRIVER_MODULE(hv_kvp, vmbus, kvp_driver, kvp_devclass, NULL, NULL); MODULE_VERSION(hv_kvp, 1); MODULE_DEPEND(hv_kvp, vmbus, 1, 1, 1); Index: head/sys/dev/hyperv/utilities/hv_kvp.h =================================================================== --- head/sys/dev/hyperv/utilities/hv_kvp.h (revision 308200) +++ head/sys/dev/hyperv/utilities/hv_kvp.h (revision 308201) @@ -1,240 +1,227 @@ /*- * Copyright (c) 2014,2016 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. * * $FreeBSD$ */ #ifndef _KVP_H #define _KVP_H /* * An implementation of HyperV key value pair (KVP) functionality for FreeBSD * */ /* * Maximum value size - used for both key names and value data, and includes * any applicable NULL terminators. * * Note: This limit is somewhat arbitrary, but falls easily within what is * supported for all native guests (back to Win 2000) and what is reasonable * for the IC KVP exchange functionality. Note that Windows Me/98/95 are * limited to 255 character key names. * * MSDN recommends not storing data values larger than 2048 bytes in the * registry. * * Note: This value is used in defining the KVP exchange message - this value * cannot be modified without affecting the message size and compatibility. */ /* * bytes, including any null terminators */ #define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) /* * Maximum key size - the registry limit for the length of an entry name * is 256 characters, including the null terminator */ #define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) /* * In FreeBSD, we implement the KVP functionality in two components: * 1) The kernel component which is packaged as part of the hv_utils driver * is responsible for communicating with the host and responsible for * implementing the host/guest protocol. 2) A user level daemon that is * responsible for data gathering. * * Host/Guest Protocol: The host iterates over an index and expects the guest * to assign a key name to the index and also return the value corresponding to * the key. The host will have atmost one KVP transaction outstanding at any * given point in time. The host side iteration stops when the guest returns * an error. Microsoft has specified the following mapping of key names to * host specified index: * * Index Key Name * 0 FullyQualifiedDomainName * 1 IntegrationServicesVersion * 2 NetworkAddressIPv4 * 3 NetworkAddressIPv6 * 4 OSBuildNumber * 5 OSName * 6 OSMajorVersion * 7 OSMinorVersion * 8 OSVersion * 9 ProcessorArchitecture * * The Windows host expects the Key Name and Key Value to be encoded in utf16. * * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the * data gathering functionality in a user mode daemon. The user level daemon * is also responsible for binding the key name to the index as well. The * kernel and user-level daemon communicate using a connector channel. * * The user mode component first registers with the * the kernel component. Subsequently, the kernel component requests, data * 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. * * * The kernel component simply acts as a conduit for communication between the * Windows host and the user-level daemon. The kernel component passes up the * index received from the Host to the user-level daemon. If the index is * valid (supported), the corresponding key as well as its * value (both are strings) is returned. If the index is invalid * (not supported), a NULL key string is returned. */ /* * Registry value types. */ #define HV_REG_SZ 1 #define HV_REG_U32 4 #define HV_REG_U64 8 /* * Daemon code supporting IP injection. */ #define HV_KVP_OP_REGISTER 4 enum hv_kvp_exchg_op { HV_KVP_OP_GET = 0, HV_KVP_OP_SET, HV_KVP_OP_DELETE, HV_KVP_OP_ENUMERATE, HV_KVP_OP_GET_IP_INFO, HV_KVP_OP_SET_IP_INFO, HV_KVP_OP_COUNT /* Number of operations, must be last. */ }; enum hv_kvp_exchg_pool { HV_KVP_POOL_EXTERNAL = 0, HV_KVP_POOL_GUEST, HV_KVP_POOL_AUTO, HV_KVP_POOL_AUTO_EXTERNAL, HV_KVP_POOL_AUTO_INTERNAL, HV_KVP_POOL_COUNT /* Number of pools, must be last. */ }; - -/* - * Some Hyper-V status codes. - */ -#define HV_KVP_S_OK 0x00000000 -#define HV_KVP_E_FAIL 0x80004005 -#define HV_KVP_S_CONT 0x80070103 -#define HV_ERROR_NOT_SUPPORTED 0x80070032 -#define HV_ERROR_MACHINE_LOCKED 0x800704F7 -#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F -#define HV_INVALIDARG 0x80070057 -#define HV_KVP_GUID_NOTFOUND 0x80041002 - #define ADDR_FAMILY_NONE 0x00 #define ADDR_FAMILY_IPV4 0x01 #define ADDR_FAMILY_IPV6 0x02 #define MAX_ADAPTER_ID_SIZE 128 #define MAX_IP_ADDR_SIZE 1024 #define MAX_GATEWAY_SIZE 512 struct hv_kvp_ipaddr_value { uint16_t adapter_id[MAX_ADAPTER_ID_SIZE]; uint8_t addr_family; uint8_t dhcp_enabled; uint16_t ip_addr[MAX_IP_ADDR_SIZE]; uint16_t sub_net[MAX_IP_ADDR_SIZE]; uint16_t gate_way[MAX_GATEWAY_SIZE]; uint16_t dns_addr[MAX_IP_ADDR_SIZE]; }__attribute__((packed)); struct hv_kvp_hdr { uint8_t operation; uint8_t pool; uint16_t pad; } __attribute__((packed)); struct hv_kvp_exchg_msg_value { uint32_t value_type; uint32_t key_size; uint32_t value_size; uint8_t key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; union { uint8_t value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; uint32_t value_u32; uint64_t value_u64; } msg_value; } __attribute__((packed)); struct hv_kvp_msg_enumerate { uint32_t index; struct hv_kvp_exchg_msg_value data; } __attribute__((packed)); struct hv_kvp_msg_get { struct hv_kvp_exchg_msg_value data; } __attribute__((packed)); struct hv_kvp_msg_set { struct hv_kvp_exchg_msg_value data; } __attribute__((packed)); struct hv_kvp_msg_delete { uint32_t key_size; uint8_t key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; } __attribute__((packed)); struct hv_kvp_register { uint8_t version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; } __attribute__((packed)); struct hv_kvp_msg { union { struct hv_kvp_hdr kvp_hdr; uint32_t error; } hdr; union { struct hv_kvp_msg_get kvp_get; struct hv_kvp_msg_set kvp_set; struct hv_kvp_msg_delete kvp_delete; struct hv_kvp_msg_enumerate kvp_enum_data; struct hv_kvp_ipaddr_value kvp_ip_val; struct hv_kvp_register kvp_register; } body; } __attribute__((packed)); struct hv_kvp_ip_msg { uint8_t operation; uint8_t pool; struct hv_kvp_ipaddr_value kvp_ip_val; } __attribute__((packed)); #endif /* _KVP_H */ Index: head/sys/dev/hyperv/utilities/hv_utilreg.h =================================================================== --- head/sys/dev/hyperv/utilities/hv_utilreg.h (revision 308200) +++ head/sys/dev/hyperv/utilities/hv_utilreg.h (revision 308201) @@ -1,79 +1,86 @@ /*- * Copyright (c) 2016 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. * * $FreeBSD$ */ #ifndef _HV_UTILREG_H_ #define _HV_UTILREG_H_ -#define HV_S_OK 0x00000000 -#define HV_E_FAIL 0x80004005 -#define HV_ERROR_NOT_SUPPORTED 0x80070032 -#define HV_ERROR_MACHINE_LOCKED 0x800704F7 +/* + * Some Hyper-V status codes. + */ +#define HV_S_OK 0x00000000 +#define HV_E_FAIL 0x80004005 +#define HV_S_CONT 0x80070103 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 +#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F +#define HV_INVALIDARG 0x80070057 +#define HV_GUID_NOTFOUND 0x80041002 /* * Common defines for Hyper-V ICs */ #define HV_ICMSGTYPE_NEGOTIATE 0 #define HV_ICMSGTYPE_HEARTBEAT 1 #define HV_ICMSGTYPE_KVPEXCHANGE 2 #define HV_ICMSGTYPE_SHUTDOWN 3 #define HV_ICMSGTYPE_TIMESYNC 4 #define HV_ICMSGTYPE_VSS 5 #define HV_ICMSGHDRFLAG_TRANSACTION 1 #define HV_ICMSGHDRFLAG_REQUEST 2 #define HV_ICMSGHDRFLAG_RESPONSE 4 typedef struct hv_vmbus_pipe_hdr { uint32_t flags; uint32_t msgsize; } __packed hv_vmbus_pipe_hdr; typedef struct hv_vmbus_ic_version { uint16_t major; uint16_t minor; } __packed hv_vmbus_ic_version; typedef struct hv_vmbus_icmsg_hdr { hv_vmbus_ic_version icverframe; uint16_t icmsgtype; hv_vmbus_ic_version icvermsg; uint16_t icmsgsize; uint32_t status; uint8_t ictransaction_id; uint8_t icflags; uint8_t reserved[2]; } __packed hv_vmbus_icmsg_hdr; typedef struct hv_vmbus_icmsg_negotiate { uint16_t icframe_vercnt; uint16_t icmsg_vercnt; uint32_t reserved; hv_vmbus_ic_version icversion_data[1]; /* any size array */ } __packed hv_vmbus_icmsg_negotiate; #endif /* !_HV_UTILREG_H_ */