1 /* 2 * An implementation of key value pair (KVP) functionality for Linux. 3 * 4 * 5 * Copyright (C) 2010, Novell, Inc. 6 * Author : K. Y. Srinivasan <ksrinivasan@novell.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License version 2 as published 10 * by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 15 * NON INFRINGEMENT. See the GNU General Public License for more 16 * details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 */ 23 24 25 #include <sys/types.h> 26 #include <sys/socket.h> 27 #include <sys/poll.h> 28 #include <sys/utsname.h> 29 #include <linux/types.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <string.h> 34 #include <ctype.h> 35 #include <errno.h> 36 #include <arpa/inet.h> 37 #include <linux/connector.h> 38 #include <linux/hyperv.h> 39 #include <linux/netlink.h> 40 #include <ifaddrs.h> 41 #include <netdb.h> 42 #include <syslog.h> 43 #include <sys/stat.h> 44 #include <fcntl.h> 45 #include <dirent.h> 46 47 /* 48 * KVP protocol: The user mode component first registers with the 49 * the kernel component. Subsequently, the kernel component requests, data 50 * for the specified keys. In response to this message the user mode component 51 * fills in the value corresponding to the specified key. We overload the 52 * sequence field in the cn_msg header to define our KVP message types. 53 * 54 * We use this infrastructure for also supporting queries from user mode 55 * application for state that may be maintained in the KVP kernel component. 56 * 57 */ 58 59 60 enum key_index { 61 FullyQualifiedDomainName = 0, 62 IntegrationServicesVersion, /*This key is serviced in the kernel*/ 63 NetworkAddressIPv4, 64 NetworkAddressIPv6, 65 OSBuildNumber, 66 OSName, 67 OSMajorVersion, 68 OSMinorVersion, 69 OSVersion, 70 ProcessorArchitecture 71 }; 72 73 74 enum { 75 IPADDR = 0, 76 NETMASK, 77 GATEWAY, 78 DNS 79 }; 80 81 static char kvp_send_buffer[4096]; 82 static char kvp_recv_buffer[4096 * 2]; 83 static struct sockaddr_nl addr; 84 static int in_hand_shake = 1; 85 86 static char *os_name = ""; 87 static char *os_major = ""; 88 static char *os_minor = ""; 89 static char *processor_arch; 90 static char *os_build; 91 static char *lic_version = "Unknown version"; 92 static struct utsname uts_buf; 93 94 /* 95 * The location of the interface configuration file. 96 */ 97 98 #define KVP_CONFIG_LOC "/var/opt/" 99 100 #define MAX_FILE_NAME 100 101 #define ENTRIES_PER_BLOCK 50 102 103 struct kvp_record { 104 char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; 105 char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; 106 }; 107 108 struct kvp_file_state { 109 int fd; 110 int num_blocks; 111 struct kvp_record *records; 112 int num_records; 113 char fname[MAX_FILE_NAME]; 114 }; 115 116 static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT]; 117 118 static void kvp_acquire_lock(int pool) 119 { 120 struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0}; 121 fl.l_pid = getpid(); 122 123 if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { 124 syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool); 125 exit(EXIT_FAILURE); 126 } 127 } 128 129 static void kvp_release_lock(int pool) 130 { 131 struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0}; 132 fl.l_pid = getpid(); 133 134 if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { 135 perror("fcntl"); 136 syslog(LOG_ERR, "Failed to release the lock pool: %d", pool); 137 exit(EXIT_FAILURE); 138 } 139 } 140 141 static void kvp_update_file(int pool) 142 { 143 FILE *filep; 144 size_t bytes_written; 145 146 /* 147 * We are going to write our in-memory registry out to 148 * disk; acquire the lock first. 149 */ 150 kvp_acquire_lock(pool); 151 152 filep = fopen(kvp_file_info[pool].fname, "w"); 153 if (!filep) { 154 kvp_release_lock(pool); 155 syslog(LOG_ERR, "Failed to open file, pool: %d", pool); 156 exit(EXIT_FAILURE); 157 } 158 159 bytes_written = fwrite(kvp_file_info[pool].records, 160 sizeof(struct kvp_record), 161 kvp_file_info[pool].num_records, filep); 162 163 if (ferror(filep) || fclose(filep)) { 164 kvp_release_lock(pool); 165 syslog(LOG_ERR, "Failed to write file, pool: %d", pool); 166 exit(EXIT_FAILURE); 167 } 168 169 kvp_release_lock(pool); 170 } 171 172 static void kvp_update_mem_state(int pool) 173 { 174 FILE *filep; 175 size_t records_read = 0; 176 struct kvp_record *record = kvp_file_info[pool].records; 177 struct kvp_record *readp; 178 int num_blocks = kvp_file_info[pool].num_blocks; 179 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; 180 181 kvp_acquire_lock(pool); 182 183 filep = fopen(kvp_file_info[pool].fname, "r"); 184 if (!filep) { 185 kvp_release_lock(pool); 186 syslog(LOG_ERR, "Failed to open file, pool: %d", pool); 187 exit(EXIT_FAILURE); 188 } 189 for (;;) { 190 readp = &record[records_read]; 191 records_read += fread(readp, sizeof(struct kvp_record), 192 ENTRIES_PER_BLOCK * num_blocks, 193 filep); 194 195 if (ferror(filep)) { 196 syslog(LOG_ERR, "Failed to read file, pool: %d", pool); 197 exit(EXIT_FAILURE); 198 } 199 200 if (!feof(filep)) { 201 /* 202 * We have more data to read. 203 */ 204 num_blocks++; 205 record = realloc(record, alloc_unit * num_blocks); 206 207 if (record == NULL) { 208 syslog(LOG_ERR, "malloc failed"); 209 exit(EXIT_FAILURE); 210 } 211 continue; 212 } 213 break; 214 } 215 216 kvp_file_info[pool].num_blocks = num_blocks; 217 kvp_file_info[pool].records = record; 218 kvp_file_info[pool].num_records = records_read; 219 220 fclose(filep); 221 kvp_release_lock(pool); 222 } 223 static int kvp_file_init(void) 224 { 225 int fd; 226 FILE *filep; 227 size_t records_read; 228 char *fname; 229 struct kvp_record *record; 230 struct kvp_record *readp; 231 int num_blocks; 232 int i; 233 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; 234 235 if (access("/var/opt/hyperv", F_OK)) { 236 if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) { 237 syslog(LOG_ERR, " Failed to create /var/opt/hyperv"); 238 exit(EXIT_FAILURE); 239 } 240 } 241 242 for (i = 0; i < KVP_POOL_COUNT; i++) { 243 fname = kvp_file_info[i].fname; 244 records_read = 0; 245 num_blocks = 1; 246 sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i); 247 fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH); 248 249 if (fd == -1) 250 return 1; 251 252 253 filep = fopen(fname, "r"); 254 if (!filep) 255 return 1; 256 257 record = malloc(alloc_unit * num_blocks); 258 if (record == NULL) { 259 fclose(filep); 260 return 1; 261 } 262 for (;;) { 263 readp = &record[records_read]; 264 records_read += fread(readp, sizeof(struct kvp_record), 265 ENTRIES_PER_BLOCK, 266 filep); 267 268 if (ferror(filep)) { 269 syslog(LOG_ERR, "Failed to read file, pool: %d", 270 i); 271 exit(EXIT_FAILURE); 272 } 273 274 if (!feof(filep)) { 275 /* 276 * We have more data to read. 277 */ 278 num_blocks++; 279 record = realloc(record, alloc_unit * 280 num_blocks); 281 if (record == NULL) { 282 fclose(filep); 283 return 1; 284 } 285 continue; 286 } 287 break; 288 } 289 kvp_file_info[i].fd = fd; 290 kvp_file_info[i].num_blocks = num_blocks; 291 kvp_file_info[i].records = record; 292 kvp_file_info[i].num_records = records_read; 293 fclose(filep); 294 295 } 296 297 return 0; 298 } 299 300 static int kvp_key_delete(int pool, __u8 *key, int key_size) 301 { 302 int i; 303 int j, k; 304 int num_records; 305 struct kvp_record *record; 306 307 /* 308 * First update the in-memory state. 309 */ 310 kvp_update_mem_state(pool); 311 312 num_records = kvp_file_info[pool].num_records; 313 record = kvp_file_info[pool].records; 314 315 for (i = 0; i < num_records; i++) { 316 if (memcmp(key, record[i].key, key_size)) 317 continue; 318 /* 319 * Found a match; just move the remaining 320 * entries up. 321 */ 322 if (i == num_records) { 323 kvp_file_info[pool].num_records--; 324 kvp_update_file(pool); 325 return 0; 326 } 327 328 j = i; 329 k = j + 1; 330 for (; k < num_records; k++) { 331 strcpy(record[j].key, record[k].key); 332 strcpy(record[j].value, record[k].value); 333 j++; 334 } 335 336 kvp_file_info[pool].num_records--; 337 kvp_update_file(pool); 338 return 0; 339 } 340 return 1; 341 } 342 343 static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, 344 int value_size) 345 { 346 int i; 347 int num_records; 348 struct kvp_record *record; 349 int num_blocks; 350 351 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || 352 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) 353 return 1; 354 355 /* 356 * First update the in-memory state. 357 */ 358 kvp_update_mem_state(pool); 359 360 num_records = kvp_file_info[pool].num_records; 361 record = kvp_file_info[pool].records; 362 num_blocks = kvp_file_info[pool].num_blocks; 363 364 for (i = 0; i < num_records; i++) { 365 if (memcmp(key, record[i].key, key_size)) 366 continue; 367 /* 368 * Found a match; just update the value - 369 * this is the modify case. 370 */ 371 memcpy(record[i].value, value, value_size); 372 kvp_update_file(pool); 373 return 0; 374 } 375 376 /* 377 * Need to add a new entry; 378 */ 379 if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { 380 /* Need to allocate a larger array for reg entries. */ 381 record = realloc(record, sizeof(struct kvp_record) * 382 ENTRIES_PER_BLOCK * (num_blocks + 1)); 383 384 if (record == NULL) 385 return 1; 386 kvp_file_info[pool].num_blocks++; 387 388 } 389 memcpy(record[i].value, value, value_size); 390 memcpy(record[i].key, key, key_size); 391 kvp_file_info[pool].records = record; 392 kvp_file_info[pool].num_records++; 393 kvp_update_file(pool); 394 return 0; 395 } 396 397 static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, 398 int value_size) 399 { 400 int i; 401 int num_records; 402 struct kvp_record *record; 403 404 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || 405 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) 406 return 1; 407 408 /* 409 * First update the in-memory state. 410 */ 411 kvp_update_mem_state(pool); 412 413 num_records = kvp_file_info[pool].num_records; 414 record = kvp_file_info[pool].records; 415 416 for (i = 0; i < num_records; i++) { 417 if (memcmp(key, record[i].key, key_size)) 418 continue; 419 /* 420 * Found a match; just copy the value out. 421 */ 422 memcpy(value, record[i].value, value_size); 423 return 0; 424 } 425 426 return 1; 427 } 428 429 static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, 430 __u8 *value, int value_size) 431 { 432 struct kvp_record *record; 433 434 /* 435 * First update our in-memory database. 436 */ 437 kvp_update_mem_state(pool); 438 record = kvp_file_info[pool].records; 439 440 if (index >= kvp_file_info[pool].num_records) { 441 return 1; 442 } 443 444 memcpy(key, record[index].key, key_size); 445 memcpy(value, record[index].value, value_size); 446 return 0; 447 } 448 449 450 void kvp_get_os_info(void) 451 { 452 FILE *file; 453 char *p, buf[512]; 454 455 uname(&uts_buf); 456 os_build = uts_buf.release; 457 os_name = uts_buf.sysname; 458 processor_arch = uts_buf.machine; 459 460 /* 461 * The current windows host (win7) expects the build 462 * string to be of the form: x.y.z 463 * Strip additional information we may have. 464 */ 465 p = strchr(os_build, '-'); 466 if (p) 467 *p = '\0'; 468 469 /* 470 * Parse the /etc/os-release file if present: 471 * http://www.freedesktop.org/software/systemd/man/os-release.html 472 */ 473 file = fopen("/etc/os-release", "r"); 474 if (file != NULL) { 475 while (fgets(buf, sizeof(buf), file)) { 476 char *value, *q; 477 478 /* Ignore comments */ 479 if (buf[0] == '#') 480 continue; 481 482 /* Split into name=value */ 483 p = strchr(buf, '='); 484 if (!p) 485 continue; 486 *p++ = 0; 487 488 /* Remove quotes and newline; un-escape */ 489 value = p; 490 q = p; 491 while (*p) { 492 if (*p == '\\') { 493 ++p; 494 if (!*p) 495 break; 496 *q++ = *p++; 497 } else if (*p == '\'' || *p == '"' || 498 *p == '\n') { 499 ++p; 500 } else { 501 *q++ = *p++; 502 } 503 } 504 *q = 0; 505 506 if (!strcmp(buf, "NAME")) { 507 p = strdup(value); 508 if (!p) 509 break; 510 os_name = p; 511 } else if (!strcmp(buf, "VERSION_ID")) { 512 p = strdup(value); 513 if (!p) 514 break; 515 os_major = p; 516 } 517 } 518 fclose(file); 519 return; 520 } 521 522 /* Fallback for older RH/SUSE releases */ 523 file = fopen("/etc/SuSE-release", "r"); 524 if (file != NULL) 525 goto kvp_osinfo_found; 526 file = fopen("/etc/redhat-release", "r"); 527 if (file != NULL) 528 goto kvp_osinfo_found; 529 530 /* 531 * We don't have information about the os. 532 */ 533 return; 534 535 kvp_osinfo_found: 536 /* up to three lines */ 537 p = fgets(buf, sizeof(buf), file); 538 if (p) { 539 p = strchr(buf, '\n'); 540 if (p) 541 *p = '\0'; 542 p = strdup(buf); 543 if (!p) 544 goto done; 545 os_name = p; 546 547 /* second line */ 548 p = fgets(buf, sizeof(buf), file); 549 if (p) { 550 p = strchr(buf, '\n'); 551 if (p) 552 *p = '\0'; 553 p = strdup(buf); 554 if (!p) 555 goto done; 556 os_major = p; 557 558 /* third line */ 559 p = fgets(buf, sizeof(buf), file); 560 if (p) { 561 p = strchr(buf, '\n'); 562 if (p) 563 *p = '\0'; 564 p = strdup(buf); 565 if (p) 566 os_minor = p; 567 } 568 } 569 } 570 571 done: 572 fclose(file); 573 return; 574 } 575 576 577 578 /* 579 * Retrieve an interface name corresponding to the specified guid. 580 * If there is a match, the function returns a pointer 581 * to the interface name and if not, a NULL is returned. 582 * If a match is found, the caller is responsible for 583 * freeing the memory. 584 */ 585 586 static char *kvp_get_if_name(char *guid) 587 { 588 DIR *dir; 589 struct dirent *entry; 590 FILE *file; 591 char *p, *q, *x; 592 char *if_name = NULL; 593 char buf[256]; 594 char *kvp_net_dir = "/sys/class/net/"; 595 char dev_id[256]; 596 597 dir = opendir(kvp_net_dir); 598 if (dir == NULL) 599 return NULL; 600 601 snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir); 602 q = dev_id + strlen(kvp_net_dir); 603 604 while ((entry = readdir(dir)) != NULL) { 605 /* 606 * Set the state for the next pass. 607 */ 608 *q = '\0'; 609 strcat(dev_id, entry->d_name); 610 strcat(dev_id, "/device/device_id"); 611 612 file = fopen(dev_id, "r"); 613 if (file == NULL) 614 continue; 615 616 p = fgets(buf, sizeof(buf), file); 617 if (p) { 618 x = strchr(p, '\n'); 619 if (x) 620 *x = '\0'; 621 622 if (!strcmp(p, guid)) { 623 /* 624 * Found the guid match; return the interface 625 * name. The caller will free the memory. 626 */ 627 if_name = strdup(entry->d_name); 628 fclose(file); 629 break; 630 } 631 } 632 fclose(file); 633 } 634 635 closedir(dir); 636 return if_name; 637 } 638 639 /* 640 * Retrieve the MAC address given the interface name. 641 */ 642 643 static char *kvp_if_name_to_mac(char *if_name) 644 { 645 FILE *file; 646 char *p, *x; 647 char buf[256]; 648 char addr_file[256]; 649 int i; 650 char *mac_addr = NULL; 651 652 snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/", 653 if_name, "/address"); 654 655 file = fopen(addr_file, "r"); 656 if (file == NULL) 657 return NULL; 658 659 p = fgets(buf, sizeof(buf), file); 660 if (p) { 661 x = strchr(p, '\n'); 662 if (x) 663 *x = '\0'; 664 for (i = 0; i < strlen(p); i++) 665 p[i] = toupper(p[i]); 666 mac_addr = strdup(p); 667 } 668 669 fclose(file); 670 return mac_addr; 671 } 672 673 674 /* 675 * Retrieve the interface name given tha MAC address. 676 */ 677 678 static char *kvp_mac_to_if_name(char *mac) 679 { 680 DIR *dir; 681 struct dirent *entry; 682 FILE *file; 683 char *p, *q, *x; 684 char *if_name = NULL; 685 char buf[256]; 686 char *kvp_net_dir = "/sys/class/net/"; 687 char dev_id[256]; 688 int i; 689 690 dir = opendir(kvp_net_dir); 691 if (dir == NULL) 692 return NULL; 693 694 snprintf(dev_id, sizeof(dev_id), kvp_net_dir); 695 q = dev_id + strlen(kvp_net_dir); 696 697 while ((entry = readdir(dir)) != NULL) { 698 /* 699 * Set the state for the next pass. 700 */ 701 *q = '\0'; 702 703 strcat(dev_id, entry->d_name); 704 strcat(dev_id, "/address"); 705 706 file = fopen(dev_id, "r"); 707 if (file == NULL) 708 continue; 709 710 p = fgets(buf, sizeof(buf), file); 711 if (p) { 712 x = strchr(p, '\n'); 713 if (x) 714 *x = '\0'; 715 716 for (i = 0; i < strlen(p); i++) 717 p[i] = toupper(p[i]); 718 719 if (!strcmp(p, mac)) { 720 /* 721 * Found the MAC match; return the interface 722 * name. The caller will free the memory. 723 */ 724 if_name = strdup(entry->d_name); 725 fclose(file); 726 break; 727 } 728 } 729 fclose(file); 730 } 731 732 closedir(dir); 733 return if_name; 734 } 735 736 737 static void kvp_process_ipconfig_file(char *cmd, 738 char *config_buf, int len, 739 int element_size, int offset) 740 { 741 char buf[256]; 742 char *p; 743 char *x; 744 FILE *file; 745 746 /* 747 * First execute the command. 748 */ 749 file = popen(cmd, "r"); 750 if (file == NULL) 751 return; 752 753 if (offset == 0) 754 memset(config_buf, 0, len); 755 while ((p = fgets(buf, sizeof(buf), file)) != NULL) { 756 if ((len - strlen(config_buf)) < (element_size + 1)) 757 break; 758 759 x = strchr(p, '\n'); 760 *x = '\0'; 761 strcat(config_buf, p); 762 strcat(config_buf, ";"); 763 } 764 pclose(file); 765 } 766 767 static void kvp_get_ipconfig_info(char *if_name, 768 struct hv_kvp_ipaddr_value *buffer) 769 { 770 char cmd[512]; 771 char dhcp_info[128]; 772 char *p; 773 FILE *file; 774 775 /* 776 * Get the address of default gateway (ipv4). 777 */ 778 sprintf(cmd, "%s %s", "ip route show dev", if_name); 779 strcat(cmd, " | awk '/default/ {print $3 }'"); 780 781 /* 782 * Execute the command to gather gateway info. 783 */ 784 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, 785 (MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0); 786 787 /* 788 * Get the address of default gateway (ipv6). 789 */ 790 sprintf(cmd, "%s %s", "ip -f inet6 route show dev", if_name); 791 strcat(cmd, " | awk '/default/ {print $3 }'"); 792 793 /* 794 * Execute the command to gather gateway info (ipv6). 795 */ 796 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, 797 (MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1); 798 799 800 /* 801 * Gather the DNS state. 802 * Since there is no standard way to get this information 803 * across various distributions of interest; we just invoke 804 * an external script that needs to be ported across distros 805 * of interest. 806 * 807 * Following is the expected format of the information from the script: 808 * 809 * ipaddr1 (nameserver1) 810 * ipaddr2 (nameserver2) 811 * . 812 * . 813 */ 814 815 sprintf(cmd, "%s", "hv_get_dns_info"); 816 817 /* 818 * Execute the command to gather DNS info. 819 */ 820 kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr, 821 (MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0); 822 823 /* 824 * Gather the DHCP state. 825 * We will gather this state by invoking an external script. 826 * The parameter to the script is the interface name. 827 * Here is the expected output: 828 * 829 * Enabled: DHCP enabled. 830 */ 831 832 sprintf(cmd, "%s %s", "hv_get_dhcp_info", if_name); 833 834 file = popen(cmd, "r"); 835 if (file == NULL) 836 return; 837 838 p = fgets(dhcp_info, sizeof(dhcp_info), file); 839 if (p == NULL) { 840 pclose(file); 841 return; 842 } 843 844 if (!strncmp(p, "Enabled", 7)) 845 buffer->dhcp_enabled = 1; 846 else 847 buffer->dhcp_enabled = 0; 848 849 pclose(file); 850 } 851 852 853 static unsigned int hweight32(unsigned int *w) 854 { 855 unsigned int res = *w - ((*w >> 1) & 0x55555555); 856 res = (res & 0x33333333) + ((res >> 2) & 0x33333333); 857 res = (res + (res >> 4)) & 0x0F0F0F0F; 858 res = res + (res >> 8); 859 return (res + (res >> 16)) & 0x000000FF; 860 } 861 862 static int kvp_process_ip_address(void *addrp, 863 int family, char *buffer, 864 int length, int *offset) 865 { 866 struct sockaddr_in *addr; 867 struct sockaddr_in6 *addr6; 868 int addr_length; 869 char tmp[50]; 870 const char *str; 871 872 if (family == AF_INET) { 873 addr = (struct sockaddr_in *)addrp; 874 str = inet_ntop(family, &addr->sin_addr, tmp, 50); 875 addr_length = INET_ADDRSTRLEN; 876 } else { 877 addr6 = (struct sockaddr_in6 *)addrp; 878 str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50); 879 addr_length = INET6_ADDRSTRLEN; 880 } 881 882 if ((length - *offset) < addr_length + 1) 883 return HV_E_FAIL; 884 if (str == NULL) { 885 strcpy(buffer, "inet_ntop failed\n"); 886 return HV_E_FAIL; 887 } 888 if (*offset == 0) 889 strcpy(buffer, tmp); 890 else 891 strcat(buffer, tmp); 892 strcat(buffer, ";"); 893 894 *offset += strlen(str) + 1; 895 return 0; 896 } 897 898 static int 899 kvp_get_ip_info(int family, char *if_name, int op, 900 void *out_buffer, int length) 901 { 902 struct ifaddrs *ifap; 903 struct ifaddrs *curp; 904 int offset = 0; 905 int sn_offset = 0; 906 int error = 0; 907 char *buffer; 908 struct hv_kvp_ipaddr_value *ip_buffer; 909 char cidr_mask[5]; /* /xyz */ 910 int weight; 911 int i; 912 unsigned int *w; 913 char *sn_str; 914 struct sockaddr_in6 *addr6; 915 916 if (op == KVP_OP_ENUMERATE) { 917 buffer = out_buffer; 918 } else { 919 ip_buffer = out_buffer; 920 buffer = (char *)ip_buffer->ip_addr; 921 ip_buffer->addr_family = 0; 922 } 923 /* 924 * On entry into this function, the buffer is capable of holding the 925 * maximum key value. 926 */ 927 928 if (getifaddrs(&ifap)) { 929 strcpy(buffer, "getifaddrs failed\n"); 930 return HV_E_FAIL; 931 } 932 933 curp = ifap; 934 while (curp != NULL) { 935 if (curp->ifa_addr == NULL) { 936 curp = curp->ifa_next; 937 continue; 938 } 939 940 if ((if_name != NULL) && 941 (strncmp(curp->ifa_name, if_name, strlen(if_name)))) { 942 /* 943 * We want info about a specific interface; 944 * just continue. 945 */ 946 curp = curp->ifa_next; 947 continue; 948 } 949 950 /* 951 * We only support two address families: AF_INET and AF_INET6. 952 * If a family value of 0 is specified, we collect both 953 * supported address families; if not we gather info on 954 * the specified address family. 955 */ 956 if ((family != 0) && (curp->ifa_addr->sa_family != family)) { 957 curp = curp->ifa_next; 958 continue; 959 } 960 if ((curp->ifa_addr->sa_family != AF_INET) && 961 (curp->ifa_addr->sa_family != AF_INET6)) { 962 curp = curp->ifa_next; 963 continue; 964 } 965 966 if (op == KVP_OP_GET_IP_INFO) { 967 /* 968 * Gather info other than the IP address. 969 * IP address info will be gathered later. 970 */ 971 if (curp->ifa_addr->sa_family == AF_INET) { 972 ip_buffer->addr_family |= ADDR_FAMILY_IPV4; 973 /* 974 * Get subnet info. 975 */ 976 error = kvp_process_ip_address( 977 curp->ifa_netmask, 978 AF_INET, 979 (char *) 980 ip_buffer->sub_net, 981 length, 982 &sn_offset); 983 if (error) 984 goto gather_ipaddr; 985 } else { 986 ip_buffer->addr_family |= ADDR_FAMILY_IPV6; 987 988 /* 989 * Get subnet info in CIDR format. 990 */ 991 weight = 0; 992 sn_str = (char *)ip_buffer->sub_net; 993 addr6 = (struct sockaddr_in6 *) 994 curp->ifa_netmask; 995 w = addr6->sin6_addr.s6_addr32; 996 997 for (i = 0; i < 4; i++) 998 weight += hweight32(&w[i]); 999 1000 sprintf(cidr_mask, "/%d", weight); 1001 if ((length - sn_offset) < 1002 (strlen(cidr_mask) + 1)) 1003 goto gather_ipaddr; 1004 1005 if (sn_offset == 0) 1006 strcpy(sn_str, cidr_mask); 1007 else 1008 strcat(sn_str, cidr_mask); 1009 strcat((char *)ip_buffer->sub_net, ";"); 1010 sn_offset += strlen(sn_str) + 1; 1011 } 1012 1013 /* 1014 * Collect other ip related configuration info. 1015 */ 1016 1017 kvp_get_ipconfig_info(if_name, ip_buffer); 1018 } 1019 1020 gather_ipaddr: 1021 error = kvp_process_ip_address(curp->ifa_addr, 1022 curp->ifa_addr->sa_family, 1023 buffer, 1024 length, &offset); 1025 if (error) 1026 goto getaddr_done; 1027 1028 curp = curp->ifa_next; 1029 } 1030 1031 getaddr_done: 1032 freeifaddrs(ifap); 1033 return error; 1034 } 1035 1036 1037 static int expand_ipv6(char *addr, int type) 1038 { 1039 int ret; 1040 struct in6_addr v6_addr; 1041 1042 ret = inet_pton(AF_INET6, addr, &v6_addr); 1043 1044 if (ret != 1) { 1045 if (type == NETMASK) 1046 return 1; 1047 return 0; 1048 } 1049 1050 sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:" 1051 "%02x%02x:%02x%02x:%02x%02x", 1052 (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1], 1053 (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3], 1054 (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5], 1055 (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7], 1056 (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9], 1057 (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11], 1058 (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13], 1059 (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]); 1060 1061 return 1; 1062 1063 } 1064 1065 static int is_ipv4(char *addr) 1066 { 1067 int ret; 1068 struct in_addr ipv4_addr; 1069 1070 ret = inet_pton(AF_INET, addr, &ipv4_addr); 1071 1072 if (ret == 1) 1073 return 1; 1074 return 0; 1075 } 1076 1077 static int parse_ip_val_buffer(char *in_buf, int *offset, 1078 char *out_buf, int out_len) 1079 { 1080 char *x; 1081 char *start; 1082 1083 /* 1084 * in_buf has sequence of characters that are seperated by 1085 * the character ';'. The last sequence does not have the 1086 * terminating ";" character. 1087 */ 1088 start = in_buf + *offset; 1089 1090 x = strchr(start, ';'); 1091 if (x) 1092 *x = 0; 1093 else 1094 x = start + strlen(start); 1095 1096 if (strlen(start) != 0) { 1097 int i = 0; 1098 /* 1099 * Get rid of leading spaces. 1100 */ 1101 while (start[i] == ' ') 1102 i++; 1103 1104 if ((x - start) <= out_len) { 1105 strcpy(out_buf, (start + i)); 1106 *offset += (x - start) + 1; 1107 return 1; 1108 } 1109 } 1110 return 0; 1111 } 1112 1113 static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3) 1114 { 1115 int ret; 1116 1117 ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3); 1118 1119 if (ret < 0) 1120 return HV_E_FAIL; 1121 1122 return 0; 1123 } 1124 1125 1126 static int process_ip_string(FILE *f, char *ip_string, int type) 1127 { 1128 int error = 0; 1129 char addr[INET6_ADDRSTRLEN]; 1130 int i = 0; 1131 int j = 0; 1132 char str[256]; 1133 char sub_str[10]; 1134 int offset = 0; 1135 1136 memset(addr, 0, sizeof(addr)); 1137 1138 while (parse_ip_val_buffer(ip_string, &offset, addr, 1139 (MAX_IP_ADDR_SIZE * 2))) { 1140 1141 sub_str[0] = 0; 1142 if (is_ipv4(addr)) { 1143 switch (type) { 1144 case IPADDR: 1145 snprintf(str, sizeof(str), "%s", "IPADDR"); 1146 break; 1147 case NETMASK: 1148 snprintf(str, sizeof(str), "%s", "NETMASK"); 1149 break; 1150 case GATEWAY: 1151 snprintf(str, sizeof(str), "%s", "GATEWAY"); 1152 break; 1153 case DNS: 1154 snprintf(str, sizeof(str), "%s", "DNS"); 1155 break; 1156 } 1157 if (i != 0) { 1158 if (type != DNS) { 1159 snprintf(sub_str, sizeof(sub_str), 1160 "_%d", i++); 1161 } else { 1162 snprintf(sub_str, sizeof(sub_str), 1163 "%d", ++i); 1164 } 1165 } else if (type == DNS) { 1166 snprintf(sub_str, sizeof(sub_str), "%d", ++i); 1167 } 1168 1169 1170 } else if (expand_ipv6(addr, type)) { 1171 switch (type) { 1172 case IPADDR: 1173 snprintf(str, sizeof(str), "%s", "IPV6ADDR"); 1174 break; 1175 case NETMASK: 1176 snprintf(str, sizeof(str), "%s", "IPV6NETMASK"); 1177 break; 1178 case GATEWAY: 1179 snprintf(str, sizeof(str), "%s", 1180 "IPV6_DEFAULTGW"); 1181 break; 1182 case DNS: 1183 snprintf(str, sizeof(str), "%s", "DNS"); 1184 break; 1185 } 1186 if ((j != 0) || (type == DNS)) { 1187 if (type != DNS) { 1188 snprintf(sub_str, sizeof(sub_str), 1189 "_%d", j++); 1190 } else { 1191 snprintf(sub_str, sizeof(sub_str), 1192 "%d", ++i); 1193 } 1194 } else if (type == DNS) { 1195 snprintf(sub_str, sizeof(sub_str), 1196 "%d", ++i); 1197 } 1198 } else { 1199 return HV_INVALIDARG; 1200 } 1201 1202 error = kvp_write_file(f, str, sub_str, addr); 1203 if (error) 1204 return error; 1205 memset(addr, 0, sizeof(addr)); 1206 } 1207 1208 return 0; 1209 } 1210 1211 static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) 1212 { 1213 int error = 0; 1214 char if_file[128]; 1215 FILE *file; 1216 char cmd[512]; 1217 char *mac_addr; 1218 1219 /* 1220 * Set the configuration for the specified interface with 1221 * the information provided. Since there is no standard 1222 * way to configure an interface, we will have an external 1223 * script that does the job of configuring the interface and 1224 * flushing the configuration. 1225 * 1226 * The parameters passed to this external script are: 1227 * 1. A configuration file that has the specified configuration. 1228 * 1229 * We will embed the name of the interface in the configuration 1230 * file: ifcfg-ethx (where ethx is the interface name). 1231 * 1232 * The information provided here may be more than what is needed 1233 * in a given distro to configure the interface and so are free 1234 * ignore information that may not be relevant. 1235 * 1236 * Here is the format of the ip configuration file: 1237 * 1238 * HWADDR=macaddr 1239 * IF_NAME=interface name 1240 * DHCP=yes (This is optional; if yes, DHCP is configured) 1241 * 1242 * IPADDR=ipaddr1 1243 * IPADDR_1=ipaddr2 1244 * IPADDR_x=ipaddry (where y = x + 1) 1245 * 1246 * NETMASK=netmask1 1247 * NETMASK_x=netmasky (where y = x + 1) 1248 * 1249 * GATEWAY=ipaddr1 1250 * GATEWAY_x=ipaddry (where y = x + 1) 1251 * 1252 * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) 1253 * 1254 * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be 1255 * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as 1256 * IPV6NETMASK. 1257 * 1258 * The host can specify multiple ipv4 and ipv6 addresses to be 1259 * configured for the interface. Furthermore, the configuration 1260 * needs to be persistent. A subsequent GET call on the interface 1261 * is expected to return the configuration that is set via the SET 1262 * call. 1263 */ 1264 1265 snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, 1266 "hyperv/ifcfg-", if_name); 1267 1268 file = fopen(if_file, "w"); 1269 1270 if (file == NULL) { 1271 syslog(LOG_ERR, "Failed to open config file"); 1272 return HV_E_FAIL; 1273 } 1274 1275 /* 1276 * First write out the MAC address. 1277 */ 1278 1279 mac_addr = kvp_if_name_to_mac(if_name); 1280 if (mac_addr == NULL) { 1281 error = HV_E_FAIL; 1282 goto setval_error; 1283 } 1284 1285 error = kvp_write_file(file, "HWADDR", "", mac_addr); 1286 if (error) 1287 goto setval_error; 1288 1289 error = kvp_write_file(file, "IF_NAME", "", if_name); 1290 if (error) 1291 goto setval_error; 1292 1293 if (new_val->dhcp_enabled) { 1294 error = kvp_write_file(file, "DHCP", "", "yes"); 1295 if (error) 1296 goto setval_error; 1297 1298 /* 1299 * We are done!. 1300 */ 1301 goto setval_done; 1302 } 1303 1304 /* 1305 * Write the configuration for ipaddress, netmask, gateway and 1306 * name servers. 1307 */ 1308 1309 error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR); 1310 if (error) 1311 goto setval_error; 1312 1313 error = process_ip_string(file, (char *)new_val->sub_net, NETMASK); 1314 if (error) 1315 goto setval_error; 1316 1317 error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY); 1318 if (error) 1319 goto setval_error; 1320 1321 error = process_ip_string(file, (char *)new_val->dns_addr, DNS); 1322 if (error) 1323 goto setval_error; 1324 1325 setval_done: 1326 free(mac_addr); 1327 fclose(file); 1328 1329 /* 1330 * Now that we have populated the configuration file, 1331 * invoke the external script to do its magic. 1332 */ 1333 1334 snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file); 1335 system(cmd); 1336 return 0; 1337 1338 setval_error: 1339 syslog(LOG_ERR, "Failed to write config file"); 1340 free(mac_addr); 1341 fclose(file); 1342 return error; 1343 } 1344 1345 1346 static int 1347 kvp_get_domain_name(char *buffer, int length) 1348 { 1349 struct addrinfo hints, *info ; 1350 int error = 0; 1351 1352 gethostname(buffer, length); 1353 memset(&hints, 0, sizeof(hints)); 1354 hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */ 1355 hints.ai_socktype = SOCK_STREAM; 1356 hints.ai_flags = AI_CANONNAME; 1357 1358 error = getaddrinfo(buffer, NULL, &hints, &info); 1359 if (error != 0) { 1360 strcpy(buffer, "getaddrinfo failed\n"); 1361 return error; 1362 } 1363 strcpy(buffer, info->ai_canonname); 1364 freeaddrinfo(info); 1365 return error; 1366 } 1367 1368 static int 1369 netlink_send(int fd, struct cn_msg *msg) 1370 { 1371 struct nlmsghdr *nlh; 1372 unsigned int size; 1373 struct msghdr message; 1374 char buffer[64]; 1375 struct iovec iov[2]; 1376 1377 size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); 1378 1379 nlh = (struct nlmsghdr *)buffer; 1380 nlh->nlmsg_seq = 0; 1381 nlh->nlmsg_pid = getpid(); 1382 nlh->nlmsg_type = NLMSG_DONE; 1383 nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); 1384 nlh->nlmsg_flags = 0; 1385 1386 iov[0].iov_base = nlh; 1387 iov[0].iov_len = sizeof(*nlh); 1388 1389 iov[1].iov_base = msg; 1390 iov[1].iov_len = size; 1391 1392 memset(&message, 0, sizeof(message)); 1393 message.msg_name = &addr; 1394 message.msg_namelen = sizeof(addr); 1395 message.msg_iov = iov; 1396 message.msg_iovlen = 2; 1397 1398 return sendmsg(fd, &message, 0); 1399 } 1400 1401 int main(void) 1402 { 1403 int fd, len, sock_opt; 1404 int error; 1405 struct cn_msg *message; 1406 struct pollfd pfd; 1407 struct nlmsghdr *incoming_msg; 1408 struct cn_msg *incoming_cn_msg; 1409 struct hv_kvp_msg *hv_msg; 1410 char *p; 1411 char *key_value; 1412 char *key_name; 1413 int op; 1414 int pool; 1415 char *if_name; 1416 struct hv_kvp_ipaddr_value *kvp_ip_val; 1417 1418 daemon(1, 0); 1419 openlog("KVP", 0, LOG_USER); 1420 syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); 1421 /* 1422 * Retrieve OS release information. 1423 */ 1424 kvp_get_os_info(); 1425 1426 if (kvp_file_init()) { 1427 syslog(LOG_ERR, "Failed to initialize the pools"); 1428 exit(EXIT_FAILURE); 1429 } 1430 1431 fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); 1432 if (fd < 0) { 1433 syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); 1434 exit(EXIT_FAILURE); 1435 } 1436 addr.nl_family = AF_NETLINK; 1437 addr.nl_pad = 0; 1438 addr.nl_pid = 0; 1439 addr.nl_groups = CN_KVP_IDX; 1440 1441 1442 error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); 1443 if (error < 0) { 1444 syslog(LOG_ERR, "bind failed; error:%d", error); 1445 close(fd); 1446 exit(EXIT_FAILURE); 1447 } 1448 sock_opt = addr.nl_groups; 1449 setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt)); 1450 /* 1451 * Register ourselves with the kernel. 1452 */ 1453 message = (struct cn_msg *)kvp_send_buffer; 1454 message->id.idx = CN_KVP_IDX; 1455 message->id.val = CN_KVP_VAL; 1456 1457 hv_msg = (struct hv_kvp_msg *)message->data; 1458 hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1; 1459 message->ack = 0; 1460 message->len = sizeof(struct hv_kvp_msg); 1461 1462 len = netlink_send(fd, message); 1463 if (len < 0) { 1464 syslog(LOG_ERR, "netlink_send failed; error:%d", len); 1465 close(fd); 1466 exit(EXIT_FAILURE); 1467 } 1468 1469 pfd.fd = fd; 1470 1471 while (1) { 1472 struct sockaddr *addr_p = (struct sockaddr *) &addr; 1473 socklen_t addr_l = sizeof(addr); 1474 pfd.events = POLLIN; 1475 pfd.revents = 0; 1476 poll(&pfd, 1, -1); 1477 1478 len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0, 1479 addr_p, &addr_l); 1480 1481 if (len < 0 || addr.nl_pid) { 1482 syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", 1483 addr.nl_pid, errno, strerror(errno)); 1484 close(fd); 1485 return -1; 1486 } 1487 1488 incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; 1489 incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); 1490 hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; 1491 1492 /* 1493 * We will use the KVP header information to pass back 1494 * the error from this daemon. So, first copy the state 1495 * and set the error code to success. 1496 */ 1497 op = hv_msg->kvp_hdr.operation; 1498 pool = hv_msg->kvp_hdr.pool; 1499 hv_msg->error = HV_S_OK; 1500 1501 if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) { 1502 /* 1503 * Driver is registering with us; stash away the version 1504 * information. 1505 */ 1506 in_hand_shake = 0; 1507 p = (char *)hv_msg->body.kvp_register.version; 1508 lic_version = malloc(strlen(p) + 1); 1509 if (lic_version) { 1510 strcpy(lic_version, p); 1511 syslog(LOG_INFO, "KVP LIC Version: %s", 1512 lic_version); 1513 } else { 1514 syslog(LOG_ERR, "malloc failed"); 1515 } 1516 continue; 1517 } 1518 1519 switch (op) { 1520 case KVP_OP_GET_IP_INFO: 1521 kvp_ip_val = &hv_msg->body.kvp_ip_val; 1522 if_name = 1523 kvp_mac_to_if_name((char *)kvp_ip_val->adapter_id); 1524 1525 if (if_name == NULL) { 1526 /* 1527 * We could not map the mac address to an 1528 * interface name; return error. 1529 */ 1530 hv_msg->error = HV_E_FAIL; 1531 break; 1532 } 1533 error = kvp_get_ip_info( 1534 0, if_name, KVP_OP_GET_IP_INFO, 1535 kvp_ip_val, 1536 (MAX_IP_ADDR_SIZE * 2)); 1537 1538 if (error) 1539 hv_msg->error = error; 1540 1541 free(if_name); 1542 break; 1543 1544 case KVP_OP_SET_IP_INFO: 1545 kvp_ip_val = &hv_msg->body.kvp_ip_val; 1546 if_name = kvp_get_if_name( 1547 (char *)kvp_ip_val->adapter_id); 1548 if (if_name == NULL) { 1549 /* 1550 * We could not map the guid to an 1551 * interface name; return error. 1552 */ 1553 hv_msg->error = HV_GUID_NOTFOUND; 1554 break; 1555 } 1556 error = kvp_set_ip_info(if_name, kvp_ip_val); 1557 if (error) 1558 hv_msg->error = error; 1559 1560 free(if_name); 1561 break; 1562 1563 case KVP_OP_SET: 1564 if (kvp_key_add_or_modify(pool, 1565 hv_msg->body.kvp_set.data.key, 1566 hv_msg->body.kvp_set.data.key_size, 1567 hv_msg->body.kvp_set.data.value, 1568 hv_msg->body.kvp_set.data.value_size)) 1569 hv_msg->error = HV_S_CONT; 1570 break; 1571 1572 case KVP_OP_GET: 1573 if (kvp_get_value(pool, 1574 hv_msg->body.kvp_set.data.key, 1575 hv_msg->body.kvp_set.data.key_size, 1576 hv_msg->body.kvp_set.data.value, 1577 hv_msg->body.kvp_set.data.value_size)) 1578 hv_msg->error = HV_S_CONT; 1579 break; 1580 1581 case KVP_OP_DELETE: 1582 if (kvp_key_delete(pool, 1583 hv_msg->body.kvp_delete.key, 1584 hv_msg->body.kvp_delete.key_size)) 1585 hv_msg->error = HV_S_CONT; 1586 break; 1587 1588 default: 1589 break; 1590 } 1591 1592 if (op != KVP_OP_ENUMERATE) 1593 goto kvp_done; 1594 1595 /* 1596 * If the pool is KVP_POOL_AUTO, dynamically generate 1597 * both the key and the value; if not read from the 1598 * appropriate pool. 1599 */ 1600 if (pool != KVP_POOL_AUTO) { 1601 if (kvp_pool_enumerate(pool, 1602 hv_msg->body.kvp_enum_data.index, 1603 hv_msg->body.kvp_enum_data.data.key, 1604 HV_KVP_EXCHANGE_MAX_KEY_SIZE, 1605 hv_msg->body.kvp_enum_data.data.value, 1606 HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) 1607 hv_msg->error = HV_S_CONT; 1608 goto kvp_done; 1609 } 1610 1611 hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; 1612 key_name = (char *)hv_msg->body.kvp_enum_data.data.key; 1613 key_value = (char *)hv_msg->body.kvp_enum_data.data.value; 1614 1615 switch (hv_msg->body.kvp_enum_data.index) { 1616 case FullyQualifiedDomainName: 1617 kvp_get_domain_name(key_value, 1618 HV_KVP_EXCHANGE_MAX_VALUE_SIZE); 1619 strcpy(key_name, "FullyQualifiedDomainName"); 1620 break; 1621 case IntegrationServicesVersion: 1622 strcpy(key_name, "IntegrationServicesVersion"); 1623 strcpy(key_value, lic_version); 1624 break; 1625 case NetworkAddressIPv4: 1626 kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE, 1627 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); 1628 strcpy(key_name, "NetworkAddressIPv4"); 1629 break; 1630 case NetworkAddressIPv6: 1631 kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE, 1632 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); 1633 strcpy(key_name, "NetworkAddressIPv6"); 1634 break; 1635 case OSBuildNumber: 1636 strcpy(key_value, os_build); 1637 strcpy(key_name, "OSBuildNumber"); 1638 break; 1639 case OSName: 1640 strcpy(key_value, os_name); 1641 strcpy(key_name, "OSName"); 1642 break; 1643 case OSMajorVersion: 1644 strcpy(key_value, os_major); 1645 strcpy(key_name, "OSMajorVersion"); 1646 break; 1647 case OSMinorVersion: 1648 strcpy(key_value, os_minor); 1649 strcpy(key_name, "OSMinorVersion"); 1650 break; 1651 case OSVersion: 1652 strcpy(key_value, os_build); 1653 strcpy(key_name, "OSVersion"); 1654 break; 1655 case ProcessorArchitecture: 1656 strcpy(key_value, processor_arch); 1657 strcpy(key_name, "ProcessorArchitecture"); 1658 break; 1659 default: 1660 hv_msg->error = HV_S_CONT; 1661 break; 1662 } 1663 /* 1664 * Send the value back to the kernel. The response is 1665 * already in the receive buffer. Update the cn_msg header to 1666 * reflect the key value that has been added to the message 1667 */ 1668 kvp_done: 1669 1670 incoming_cn_msg->id.idx = CN_KVP_IDX; 1671 incoming_cn_msg->id.val = CN_KVP_VAL; 1672 incoming_cn_msg->ack = 0; 1673 incoming_cn_msg->len = sizeof(struct hv_kvp_msg); 1674 1675 len = netlink_send(fd, incoming_cn_msg); 1676 if (len < 0) { 1677 syslog(LOG_ERR, "net_link send failed; error:%d", len); 1678 exit(EXIT_FAILURE); 1679 } 1680 } 1681 1682 } 1683