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/poll.h> 26 #include <sys/utsname.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <string.h> 31 #include <ctype.h> 32 #include <errno.h> 33 #include <arpa/inet.h> 34 #include <linux/hyperv.h> 35 #include <ifaddrs.h> 36 #include <netdb.h> 37 #include <syslog.h> 38 #include <sys/stat.h> 39 #include <fcntl.h> 40 #include <dirent.h> 41 #include <net/if.h> 42 #include <limits.h> 43 #include <getopt.h> 44 45 /* 46 * KVP protocol: The user mode component first registers with the 47 * kernel component. Subsequently, the kernel component requests, data 48 * for the specified keys. In response to this message the user mode component 49 * fills in the value corresponding to the specified key. We overload the 50 * sequence field in the cn_msg header to define our KVP message types. 51 * 52 * We use this infrastructure for also supporting queries from user mode 53 * application for state that may be maintained in the KVP kernel component. 54 * 55 */ 56 57 58 enum key_index { 59 FullyQualifiedDomainName = 0, 60 IntegrationServicesVersion, /*This key is serviced in the kernel*/ 61 NetworkAddressIPv4, 62 NetworkAddressIPv6, 63 OSBuildNumber, 64 OSName, 65 OSMajorVersion, 66 OSMinorVersion, 67 OSVersion, 68 ProcessorArchitecture 69 }; 70 71 72 enum { 73 IPADDR = 0, 74 NETMASK, 75 GATEWAY, 76 DNS 77 }; 78 79 enum { 80 IPV4 = 1, 81 IPV6, 82 IP_TYPE_MAX 83 }; 84 85 static int in_hand_shake; 86 87 static char *os_name = ""; 88 static char *os_major = ""; 89 static char *os_minor = ""; 90 static char *processor_arch; 91 static char *os_build; 92 static char *os_version; 93 static char *lic_version = "Unknown version"; 94 static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; 95 static struct utsname uts_buf; 96 97 /* 98 * The location of the interface configuration file. 99 */ 100 101 #define KVP_CONFIG_LOC "/var/lib/hyperv" 102 103 #ifndef KVP_SCRIPTS_PATH 104 #define KVP_SCRIPTS_PATH "/usr/libexec/hypervkvpd/" 105 #endif 106 107 #define KVP_NET_DIR "/sys/class/net/" 108 109 #define MAX_FILE_NAME 100 110 #define ENTRIES_PER_BLOCK 50 111 /* 112 * Change this entry if the number of addresses increases in future 113 */ 114 #define MAX_IP_ENTRIES 64 115 #define OUTSTR_BUF_SIZE ((INET6_ADDRSTRLEN + 1) * MAX_IP_ENTRIES) 116 117 struct kvp_record { 118 char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; 119 char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; 120 }; 121 122 struct kvp_file_state { 123 int fd; 124 int num_blocks; 125 struct kvp_record *records; 126 int num_records; 127 char fname[MAX_FILE_NAME]; 128 }; 129 130 static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT]; 131 132 static void kvp_acquire_lock(int pool) 133 { 134 struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0}; 135 fl.l_pid = getpid(); 136 137 if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { 138 syslog(LOG_ERR, "Failed to acquire the lock pool: %d; error: %d %s", pool, 139 errno, strerror(errno)); 140 exit(EXIT_FAILURE); 141 } 142 } 143 144 static void kvp_release_lock(int pool) 145 { 146 struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0}; 147 fl.l_pid = getpid(); 148 149 if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { 150 syslog(LOG_ERR, "Failed to release the lock pool: %d; error: %d %s", pool, 151 errno, strerror(errno)); 152 exit(EXIT_FAILURE); 153 } 154 } 155 156 static void kvp_update_file(int pool) 157 { 158 FILE *filep; 159 160 /* 161 * We are going to write our in-memory registry out to 162 * disk; acquire the lock first. 163 */ 164 kvp_acquire_lock(pool); 165 166 filep = fopen(kvp_file_info[pool].fname, "we"); 167 if (!filep) { 168 syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, 169 errno, strerror(errno)); 170 kvp_release_lock(pool); 171 exit(EXIT_FAILURE); 172 } 173 174 fwrite(kvp_file_info[pool].records, sizeof(struct kvp_record), 175 kvp_file_info[pool].num_records, filep); 176 177 if (ferror(filep) || fclose(filep)) { 178 kvp_release_lock(pool); 179 syslog(LOG_ERR, "Failed to write file, pool: %d", pool); 180 exit(EXIT_FAILURE); 181 } 182 183 kvp_release_lock(pool); 184 } 185 186 static void kvp_update_mem_state(int pool) 187 { 188 FILE *filep; 189 size_t records_read = 0; 190 struct kvp_record *record = kvp_file_info[pool].records; 191 struct kvp_record *readp; 192 int num_blocks = kvp_file_info[pool].num_blocks; 193 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; 194 195 kvp_acquire_lock(pool); 196 197 filep = fopen(kvp_file_info[pool].fname, "re"); 198 if (!filep) { 199 syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool, 200 errno, strerror(errno)); 201 kvp_release_lock(pool); 202 exit(EXIT_FAILURE); 203 } 204 for (;;) { 205 readp = &record[records_read]; 206 records_read += fread(readp, sizeof(struct kvp_record), 207 ENTRIES_PER_BLOCK * num_blocks - records_read, 208 filep); 209 210 if (ferror(filep)) { 211 syslog(LOG_ERR, 212 "Failed to read file, pool: %d; error: %d %s", 213 pool, errno, strerror(errno)); 214 kvp_release_lock(pool); 215 exit(EXIT_FAILURE); 216 } 217 218 if (!feof(filep)) { 219 /* 220 * We have more data to read. 221 */ 222 num_blocks++; 223 record = realloc(record, alloc_unit * num_blocks); 224 225 if (record == NULL) { 226 syslog(LOG_ERR, "malloc failed"); 227 kvp_release_lock(pool); 228 exit(EXIT_FAILURE); 229 } 230 continue; 231 } 232 break; 233 } 234 235 kvp_file_info[pool].num_blocks = num_blocks; 236 kvp_file_info[pool].records = record; 237 kvp_file_info[pool].num_records = records_read; 238 239 fclose(filep); 240 kvp_release_lock(pool); 241 } 242 243 static int kvp_file_init(void) 244 { 245 int fd; 246 char *fname; 247 int i; 248 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; 249 250 if (access(KVP_CONFIG_LOC, F_OK)) { 251 if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) { 252 syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC, 253 errno, strerror(errno)); 254 exit(EXIT_FAILURE); 255 } 256 } 257 258 for (i = 0; i < KVP_POOL_COUNT; i++) { 259 fname = kvp_file_info[i].fname; 260 sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i); 261 fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */); 262 263 if (fd == -1) 264 return 1; 265 266 kvp_file_info[i].fd = fd; 267 kvp_file_info[i].num_blocks = 1; 268 kvp_file_info[i].records = malloc(alloc_unit); 269 if (kvp_file_info[i].records == NULL) 270 return 1; 271 kvp_file_info[i].num_records = 0; 272 kvp_update_mem_state(i); 273 } 274 275 return 0; 276 } 277 278 static int kvp_key_delete(int pool, const __u8 *key, int key_size) 279 { 280 int i; 281 int j, k; 282 int num_records; 283 struct kvp_record *record; 284 285 /* 286 * First update the in-memory state. 287 */ 288 kvp_update_mem_state(pool); 289 290 num_records = kvp_file_info[pool].num_records; 291 record = kvp_file_info[pool].records; 292 293 for (i = 0; i < num_records; i++) { 294 if (memcmp(key, record[i].key, key_size)) 295 continue; 296 /* 297 * Found a match; just move the remaining 298 * entries up. 299 */ 300 if (i == (num_records - 1)) { 301 kvp_file_info[pool].num_records--; 302 kvp_update_file(pool); 303 return 0; 304 } 305 306 j = i; 307 k = j + 1; 308 for (; k < num_records; k++) { 309 strcpy(record[j].key, record[k].key); 310 strcpy(record[j].value, record[k].value); 311 j++; 312 } 313 314 kvp_file_info[pool].num_records--; 315 kvp_update_file(pool); 316 return 0; 317 } 318 return 1; 319 } 320 321 static int kvp_key_add_or_modify(int pool, const __u8 *key, int key_size, 322 const __u8 *value, int value_size) 323 { 324 int i; 325 int num_records; 326 struct kvp_record *record; 327 int num_blocks; 328 329 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || 330 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) 331 return 1; 332 333 /* 334 * First update the in-memory state. 335 */ 336 kvp_update_mem_state(pool); 337 338 num_records = kvp_file_info[pool].num_records; 339 record = kvp_file_info[pool].records; 340 num_blocks = kvp_file_info[pool].num_blocks; 341 342 for (i = 0; i < num_records; i++) { 343 if (memcmp(key, record[i].key, key_size)) 344 continue; 345 /* 346 * Found a match; just update the value - 347 * this is the modify case. 348 */ 349 memcpy(record[i].value, value, value_size); 350 kvp_update_file(pool); 351 return 0; 352 } 353 354 /* 355 * Need to add a new entry; 356 */ 357 if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { 358 /* Need to allocate a larger array for reg entries. */ 359 record = realloc(record, sizeof(struct kvp_record) * 360 ENTRIES_PER_BLOCK * (num_blocks + 1)); 361 362 if (record == NULL) 363 return 1; 364 kvp_file_info[pool].num_blocks++; 365 366 } 367 memcpy(record[i].value, value, value_size); 368 memcpy(record[i].key, key, key_size); 369 kvp_file_info[pool].records = record; 370 kvp_file_info[pool].num_records++; 371 kvp_update_file(pool); 372 return 0; 373 } 374 375 static int kvp_get_value(int pool, const __u8 *key, int key_size, __u8 *value, 376 int value_size) 377 { 378 int i; 379 int num_records; 380 struct kvp_record *record; 381 382 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || 383 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) 384 return 1; 385 386 /* 387 * First update the in-memory state. 388 */ 389 kvp_update_mem_state(pool); 390 391 num_records = kvp_file_info[pool].num_records; 392 record = kvp_file_info[pool].records; 393 394 for (i = 0; i < num_records; i++) { 395 if (memcmp(key, record[i].key, key_size)) 396 continue; 397 /* 398 * Found a match; just copy the value out. 399 */ 400 memcpy(value, record[i].value, value_size); 401 return 0; 402 } 403 404 return 1; 405 } 406 407 static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, 408 __u8 *value, int value_size) 409 { 410 struct kvp_record *record; 411 412 /* 413 * First update our in-memory database. 414 */ 415 kvp_update_mem_state(pool); 416 record = kvp_file_info[pool].records; 417 418 if (index >= kvp_file_info[pool].num_records) { 419 return 1; 420 } 421 422 memcpy(key, record[index].key, key_size); 423 memcpy(value, record[index].value, value_size); 424 return 0; 425 } 426 427 428 void kvp_get_os_info(void) 429 { 430 FILE *file; 431 char *p, buf[512]; 432 433 uname(&uts_buf); 434 os_version = uts_buf.release; 435 os_build = strdup(uts_buf.release); 436 437 os_name = uts_buf.sysname; 438 processor_arch = uts_buf.machine; 439 440 /* 441 * The current windows host (win7) expects the build 442 * string to be of the form: x.y.z 443 * Strip additional information we may have. 444 */ 445 p = strchr(os_version, '-'); 446 if (p) 447 *p = '\0'; 448 449 /* 450 * Parse the /etc/os-release file if present: 451 * https://www.freedesktop.org/software/systemd/man/os-release.html 452 */ 453 file = fopen("/etc/os-release", "r"); 454 if (file != NULL) { 455 while (fgets(buf, sizeof(buf), file)) { 456 char *value, *q; 457 458 /* Ignore comments */ 459 if (buf[0] == '#') 460 continue; 461 462 /* Split into name=value */ 463 p = strchr(buf, '='); 464 if (!p) 465 continue; 466 *p++ = 0; 467 468 /* Remove quotes and newline; un-escape */ 469 value = p; 470 q = p; 471 while (*p) { 472 if (*p == '\\') { 473 ++p; 474 if (!*p) 475 break; 476 *q++ = *p++; 477 } else if (*p == '\'' || *p == '"' || 478 *p == '\n') { 479 ++p; 480 } else { 481 *q++ = *p++; 482 } 483 } 484 *q = 0; 485 486 if (!strcmp(buf, "NAME")) { 487 p = strdup(value); 488 if (!p) 489 break; 490 os_name = p; 491 } else if (!strcmp(buf, "VERSION_ID")) { 492 p = strdup(value); 493 if (!p) 494 break; 495 os_major = p; 496 } 497 } 498 fclose(file); 499 return; 500 } 501 502 /* Fallback for older RH/SUSE releases */ 503 file = fopen("/etc/SuSE-release", "r"); 504 if (file != NULL) 505 goto kvp_osinfo_found; 506 file = fopen("/etc/redhat-release", "r"); 507 if (file != NULL) 508 goto kvp_osinfo_found; 509 510 /* 511 * We don't have information about the os. 512 */ 513 return; 514 515 kvp_osinfo_found: 516 /* up to three lines */ 517 p = fgets(buf, sizeof(buf), file); 518 if (p) { 519 p = strchr(buf, '\n'); 520 if (p) 521 *p = '\0'; 522 p = strdup(buf); 523 if (!p) 524 goto done; 525 os_name = p; 526 527 /* second line */ 528 p = fgets(buf, sizeof(buf), file); 529 if (p) { 530 p = strchr(buf, '\n'); 531 if (p) 532 *p = '\0'; 533 p = strdup(buf); 534 if (!p) 535 goto done; 536 os_major = p; 537 538 /* third line */ 539 p = fgets(buf, sizeof(buf), file); 540 if (p) { 541 p = strchr(buf, '\n'); 542 if (p) 543 *p = '\0'; 544 p = strdup(buf); 545 if (p) 546 os_minor = p; 547 } 548 } 549 } 550 551 done: 552 fclose(file); 553 return; 554 } 555 556 557 558 /* 559 * Retrieve an interface name corresponding to the specified guid. 560 * If there is a match, the function returns a pointer 561 * to the interface name and if not, a NULL is returned. 562 * If a match is found, the caller is responsible for 563 * freeing the memory. 564 */ 565 566 static char *kvp_get_if_name(char *guid) 567 { 568 DIR *dir; 569 struct dirent *entry; 570 FILE *file; 571 char *p, *x; 572 char *if_name = NULL; 573 char buf[256]; 574 char dev_id[PATH_MAX]; 575 576 dir = opendir(KVP_NET_DIR); 577 if (dir == NULL) 578 return NULL; 579 580 while ((entry = readdir(dir)) != NULL) { 581 /* 582 * Set the state for the next pass. 583 */ 584 snprintf(dev_id, sizeof(dev_id), "%s%s/device/device_id", 585 KVP_NET_DIR, entry->d_name); 586 587 file = fopen(dev_id, "r"); 588 if (file == NULL) 589 continue; 590 591 p = fgets(buf, sizeof(buf), file); 592 if (p) { 593 x = strchr(p, '\n'); 594 if (x) 595 *x = '\0'; 596 597 if (!strcmp(p, guid)) { 598 /* 599 * Found the guid match; return the interface 600 * name. The caller will free the memory. 601 */ 602 if_name = strdup(entry->d_name); 603 fclose(file); 604 break; 605 } 606 } 607 fclose(file); 608 } 609 610 closedir(dir); 611 return if_name; 612 } 613 614 /* 615 * Retrieve the MAC address given the interface name. 616 */ 617 618 static char *kvp_if_name_to_mac(char *if_name) 619 { 620 FILE *file; 621 char *p, *x; 622 char buf[256]; 623 char addr_file[PATH_MAX]; 624 unsigned int i; 625 char *mac_addr = NULL; 626 627 snprintf(addr_file, sizeof(addr_file), "%s%s%s", KVP_NET_DIR, 628 if_name, "/address"); 629 630 file = fopen(addr_file, "r"); 631 if (file == NULL) 632 return NULL; 633 634 p = fgets(buf, sizeof(buf), file); 635 if (p) { 636 x = strchr(p, '\n'); 637 if (x) 638 *x = '\0'; 639 for (i = 0; i < strlen(p); i++) 640 p[i] = toupper(p[i]); 641 mac_addr = strdup(p); 642 } 643 644 fclose(file); 645 return mac_addr; 646 } 647 648 static void kvp_process_ipconfig_file(char *cmd, 649 char *config_buf, unsigned int len, 650 int element_size, int offset) 651 { 652 char buf[256]; 653 char *p; 654 char *x; 655 FILE *file; 656 657 /* 658 * First execute the command. 659 */ 660 file = popen(cmd, "r"); 661 if (file == NULL) 662 return; 663 664 if (offset == 0) 665 memset(config_buf, 0, len); 666 while ((p = fgets(buf, sizeof(buf), file)) != NULL) { 667 if (len < strlen(config_buf) + element_size + 1) 668 break; 669 670 x = strchr(p, '\n'); 671 if (x) 672 *x = '\0'; 673 674 strcat(config_buf, p); 675 strcat(config_buf, ";"); 676 } 677 pclose(file); 678 } 679 680 static void kvp_get_ipconfig_info(char *if_name, 681 struct hv_kvp_ipaddr_value *buffer) 682 { 683 char cmd[512]; 684 char dhcp_info[128]; 685 char *p; 686 FILE *file; 687 688 /* 689 * Get the address of default gateway (ipv4). 690 */ 691 sprintf(cmd, "%s %s", "ip route show dev", if_name); 692 strcat(cmd, " | awk '/default/ {print $3 }'"); 693 694 /* 695 * Execute the command to gather gateway info. 696 */ 697 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, 698 (MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0); 699 700 /* 701 * Get the address of default gateway (ipv6). 702 */ 703 sprintf(cmd, "%s %s", "ip -f inet6 route show dev", if_name); 704 strcat(cmd, " | awk '/default/ {print $3 }'"); 705 706 /* 707 * Execute the command to gather gateway info (ipv6). 708 */ 709 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way, 710 (MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1); 711 712 713 /* 714 * Gather the DNS state. 715 * Since there is no standard way to get this information 716 * across various distributions of interest; we just invoke 717 * an external script that needs to be ported across distros 718 * of interest. 719 * 720 * Following is the expected format of the information from the script: 721 * 722 * ipaddr1 (nameserver1) 723 * ipaddr2 (nameserver2) 724 * . 725 * . 726 */ 727 728 sprintf(cmd, KVP_SCRIPTS_PATH "%s", "hv_get_dns_info"); 729 730 /* 731 * Execute the command to gather DNS info. 732 */ 733 kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr, 734 (MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0); 735 736 /* 737 * Gather the DHCP state. 738 * We will gather this state by invoking an external script. 739 * The parameter to the script is the interface name. 740 * Here is the expected output: 741 * 742 * Enabled: DHCP enabled. 743 */ 744 745 sprintf(cmd, KVP_SCRIPTS_PATH "%s %s", "hv_get_dhcp_info", if_name); 746 747 file = popen(cmd, "r"); 748 if (file == NULL) 749 return; 750 751 p = fgets(dhcp_info, sizeof(dhcp_info), file); 752 if (p == NULL) { 753 pclose(file); 754 return; 755 } 756 757 if (!strncmp(p, "Enabled", 7)) 758 buffer->dhcp_enabled = 1; 759 else 760 buffer->dhcp_enabled = 0; 761 762 pclose(file); 763 } 764 765 766 static unsigned int hweight32(unsigned int *w) 767 { 768 unsigned int res = *w - ((*w >> 1) & 0x55555555); 769 res = (res & 0x33333333) + ((res >> 2) & 0x33333333); 770 res = (res + (res >> 4)) & 0x0F0F0F0F; 771 res = res + (res >> 8); 772 return (res + (res >> 16)) & 0x000000FF; 773 } 774 775 static int kvp_process_ip_address(void *addrp, 776 int family, char *buffer, 777 int length, int *offset) 778 { 779 struct sockaddr_in *addr; 780 struct sockaddr_in6 *addr6; 781 int addr_length; 782 char tmp[50]; 783 const char *str; 784 785 if (family == AF_INET) { 786 addr = addrp; 787 str = inet_ntop(family, &addr->sin_addr, tmp, 50); 788 addr_length = INET_ADDRSTRLEN; 789 } else { 790 addr6 = addrp; 791 str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50); 792 addr_length = INET6_ADDRSTRLEN; 793 } 794 795 if ((length - *offset) < addr_length + 2) 796 return HV_E_FAIL; 797 if (str == NULL) { 798 strcpy(buffer, "inet_ntop failed\n"); 799 return HV_E_FAIL; 800 } 801 if (*offset == 0) 802 strcpy(buffer, tmp); 803 else { 804 strcat(buffer, ";"); 805 strcat(buffer, tmp); 806 } 807 808 *offset += strlen(str) + 1; 809 810 return 0; 811 } 812 813 static int 814 kvp_get_ip_info(int family, char *if_name, int op, 815 void *out_buffer, unsigned int length) 816 { 817 struct ifaddrs *ifap; 818 struct ifaddrs *curp; 819 int offset = 0; 820 int sn_offset = 0; 821 int error = 0; 822 char *buffer; 823 struct hv_kvp_ipaddr_value *ip_buffer = NULL; 824 char cidr_mask[5]; /* /xyz */ 825 int weight; 826 int i; 827 unsigned int *w; 828 char *sn_str; 829 struct sockaddr_in6 *addr6; 830 831 if (op == KVP_OP_ENUMERATE) { 832 buffer = out_buffer; 833 } else { 834 ip_buffer = out_buffer; 835 buffer = (char *)ip_buffer->ip_addr; 836 ip_buffer->addr_family = 0; 837 } 838 /* 839 * On entry into this function, the buffer is capable of holding the 840 * maximum key value. 841 */ 842 843 if (getifaddrs(&ifap)) { 844 strcpy(buffer, "getifaddrs failed\n"); 845 return HV_E_FAIL; 846 } 847 848 curp = ifap; 849 while (curp != NULL) { 850 if (curp->ifa_addr == NULL) { 851 curp = curp->ifa_next; 852 continue; 853 } 854 855 if ((if_name != NULL) && 856 (strncmp(curp->ifa_name, if_name, strlen(if_name)))) { 857 /* 858 * We want info about a specific interface; 859 * just continue. 860 */ 861 curp = curp->ifa_next; 862 continue; 863 } 864 865 /* 866 * We only support two address families: AF_INET and AF_INET6. 867 * If a family value of 0 is specified, we collect both 868 * supported address families; if not we gather info on 869 * the specified address family. 870 */ 871 if ((((family != 0) && 872 (curp->ifa_addr->sa_family != family))) || 873 (curp->ifa_flags & IFF_LOOPBACK)) { 874 curp = curp->ifa_next; 875 continue; 876 } 877 if ((curp->ifa_addr->sa_family != AF_INET) && 878 (curp->ifa_addr->sa_family != AF_INET6)) { 879 curp = curp->ifa_next; 880 continue; 881 } 882 883 if (op == KVP_OP_GET_IP_INFO) { 884 /* 885 * Gather info other than the IP address. 886 * IP address info will be gathered later. 887 */ 888 if (curp->ifa_addr->sa_family == AF_INET) { 889 ip_buffer->addr_family |= ADDR_FAMILY_IPV4; 890 /* 891 * Get subnet info. 892 */ 893 error = kvp_process_ip_address( 894 curp->ifa_netmask, 895 AF_INET, 896 (char *) 897 ip_buffer->sub_net, 898 length, 899 &sn_offset); 900 if (error) 901 goto gather_ipaddr; 902 } else { 903 ip_buffer->addr_family |= ADDR_FAMILY_IPV6; 904 905 /* 906 * Get subnet info in CIDR format. 907 */ 908 weight = 0; 909 sn_str = (char *)ip_buffer->sub_net; 910 addr6 = (struct sockaddr_in6 *) 911 curp->ifa_netmask; 912 w = addr6->sin6_addr.s6_addr32; 913 914 for (i = 0; i < 4; i++) 915 weight += hweight32(&w[i]); 916 917 sprintf(cidr_mask, "/%d", weight); 918 if (length < sn_offset + strlen(cidr_mask) + 1) 919 goto gather_ipaddr; 920 921 if (sn_offset == 0) 922 strcpy(sn_str, cidr_mask); 923 else { 924 strcat((char *)ip_buffer->sub_net, ";"); 925 strcat(sn_str, cidr_mask); 926 } 927 sn_offset += strlen(sn_str) + 1; 928 } 929 930 /* 931 * Collect other ip related configuration info. 932 */ 933 934 kvp_get_ipconfig_info(if_name, ip_buffer); 935 } 936 937 gather_ipaddr: 938 error = kvp_process_ip_address(curp->ifa_addr, 939 curp->ifa_addr->sa_family, 940 buffer, 941 length, &offset); 942 if (error) 943 goto getaddr_done; 944 945 curp = curp->ifa_next; 946 } 947 948 getaddr_done: 949 freeifaddrs(ifap); 950 return error; 951 } 952 953 /* 954 * Retrieve the IP given the MAC address. 955 */ 956 static int kvp_mac_to_ip(struct hv_kvp_ipaddr_value *kvp_ip_val) 957 { 958 char *mac = (char *)kvp_ip_val->adapter_id; 959 DIR *dir; 960 struct dirent *entry; 961 FILE *file; 962 char *p, *x; 963 char *if_name = NULL; 964 char buf[256]; 965 char dev_id[PATH_MAX]; 966 unsigned int i; 967 int error = HV_E_FAIL; 968 969 dir = opendir(KVP_NET_DIR); 970 if (dir == NULL) 971 return HV_E_FAIL; 972 973 while ((entry = readdir(dir)) != NULL) { 974 /* 975 * Set the state for the next pass. 976 */ 977 snprintf(dev_id, sizeof(dev_id), "%s%s/address", KVP_NET_DIR, 978 entry->d_name); 979 980 file = fopen(dev_id, "r"); 981 if (file == NULL) 982 continue; 983 984 p = fgets(buf, sizeof(buf), file); 985 fclose(file); 986 if (!p) 987 continue; 988 989 x = strchr(p, '\n'); 990 if (x) 991 *x = '\0'; 992 993 for (i = 0; i < strlen(p); i++) 994 p[i] = toupper(p[i]); 995 996 if (strcmp(p, mac)) 997 continue; 998 999 /* 1000 * Found the MAC match. 1001 * A NIC (e.g. VF) matching the MAC, but without IP, is skipped. 1002 */ 1003 if_name = entry->d_name; 1004 if (!if_name) 1005 continue; 1006 1007 error = kvp_get_ip_info(0, if_name, KVP_OP_GET_IP_INFO, 1008 kvp_ip_val, MAX_IP_ADDR_SIZE * 2); 1009 1010 if (!error && strlen((char *)kvp_ip_val->ip_addr)) 1011 break; 1012 } 1013 1014 closedir(dir); 1015 return error; 1016 } 1017 1018 static int expand_ipv6(char *addr, int type) 1019 { 1020 int ret; 1021 struct in6_addr v6_addr; 1022 1023 ret = inet_pton(AF_INET6, addr, &v6_addr); 1024 1025 if (ret != 1) { 1026 if (type == NETMASK) 1027 return 1; 1028 return 0; 1029 } 1030 1031 sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:" 1032 "%02x%02x:%02x%02x:%02x%02x", 1033 (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1], 1034 (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3], 1035 (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5], 1036 (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7], 1037 (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9], 1038 (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11], 1039 (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13], 1040 (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]); 1041 1042 return 1; 1043 1044 } 1045 1046 static int is_ipv4(char *addr) 1047 { 1048 int ret; 1049 struct in_addr ipv4_addr; 1050 1051 ret = inet_pton(AF_INET, addr, &ipv4_addr); 1052 1053 if (ret == 1) 1054 return 1; 1055 return 0; 1056 } 1057 1058 static int parse_ip_val_buffer(char *in_buf, int *offset, 1059 char *out_buf, int out_len) 1060 { 1061 char *x; 1062 char *start; 1063 1064 /* 1065 * in_buf has sequence of characters that are separated by 1066 * the character ';'. The last sequence does not have the 1067 * terminating ";" character. 1068 */ 1069 start = in_buf + *offset; 1070 1071 x = strchr(start, ';'); 1072 if (x) 1073 *x = 0; 1074 else 1075 x = start + strlen(start); 1076 1077 if (strlen(start) != 0) { 1078 int i = 0; 1079 /* 1080 * Get rid of leading spaces. 1081 */ 1082 while (start[i] == ' ') 1083 i++; 1084 1085 if ((x - start) <= out_len) { 1086 strcpy(out_buf, (start + i)); 1087 *offset += (x - start) + 1; 1088 return 1; 1089 } 1090 } 1091 return 0; 1092 } 1093 1094 static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3) 1095 { 1096 int ret; 1097 1098 ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3); 1099 1100 if (ret < 0) 1101 return HV_E_FAIL; 1102 1103 return 0; 1104 } 1105 1106 1107 static int process_ip_string(FILE *f, char *ip_string, int type) 1108 { 1109 int error = 0; 1110 char addr[INET6_ADDRSTRLEN]; 1111 int i = 0; 1112 int j = 0; 1113 char str[256]; 1114 char sub_str[13]; 1115 int offset = 0; 1116 1117 memset(addr, 0, sizeof(addr)); 1118 1119 while (parse_ip_val_buffer(ip_string, &offset, addr, 1120 (MAX_IP_ADDR_SIZE * 2))) { 1121 1122 sub_str[0] = 0; 1123 if (is_ipv4(addr)) { 1124 switch (type) { 1125 case IPADDR: 1126 snprintf(str, sizeof(str), "%s", "IPADDR"); 1127 break; 1128 case NETMASK: 1129 snprintf(str, sizeof(str), "%s", "NETMASK"); 1130 break; 1131 case GATEWAY: 1132 snprintf(str, sizeof(str), "%s", "GATEWAY"); 1133 break; 1134 case DNS: 1135 snprintf(str, sizeof(str), "%s", "DNS"); 1136 break; 1137 } 1138 1139 if (type == DNS) { 1140 snprintf(sub_str, sizeof(sub_str), "%d", ++i); 1141 } else if (type == GATEWAY && i == 0) { 1142 ++i; 1143 } else { 1144 snprintf(sub_str, sizeof(sub_str), "%d", i++); 1145 } 1146 1147 1148 } else if (expand_ipv6(addr, type)) { 1149 switch (type) { 1150 case IPADDR: 1151 snprintf(str, sizeof(str), "%s", "IPV6ADDR"); 1152 break; 1153 case NETMASK: 1154 snprintf(str, sizeof(str), "%s", "IPV6NETMASK"); 1155 break; 1156 case GATEWAY: 1157 snprintf(str, sizeof(str), "%s", 1158 "IPV6_DEFAULTGW"); 1159 break; 1160 case DNS: 1161 snprintf(str, sizeof(str), "%s", "DNS"); 1162 break; 1163 } 1164 1165 if (type == DNS) { 1166 snprintf(sub_str, sizeof(sub_str), "%d", ++i); 1167 } else if (j == 0) { 1168 ++j; 1169 } else { 1170 snprintf(sub_str, sizeof(sub_str), "_%d", j++); 1171 } 1172 } else { 1173 return HV_INVALIDARG; 1174 } 1175 1176 error = kvp_write_file(f, str, sub_str, addr); 1177 if (error) 1178 return error; 1179 memset(addr, 0, sizeof(addr)); 1180 } 1181 1182 return 0; 1183 } 1184 1185 int ip_version_check(const char *input_addr) 1186 { 1187 struct in6_addr addr; 1188 1189 if (inet_pton(AF_INET, input_addr, &addr)) 1190 return IPV4; 1191 else if (inet_pton(AF_INET6, input_addr, &addr)) 1192 return IPV6; 1193 1194 return -EINVAL; 1195 } 1196 1197 /* 1198 * Only IPv4 subnet strings needs to be converted to plen 1199 * For IPv6 the subnet is already privided in plen format 1200 */ 1201 static int kvp_subnet_to_plen(char *subnet_addr_str) 1202 { 1203 int plen = 0; 1204 struct in_addr subnet_addr4; 1205 1206 /* 1207 * Convert subnet address to binary representation 1208 */ 1209 if (inet_pton(AF_INET, subnet_addr_str, &subnet_addr4) == 1) { 1210 uint32_t subnet_mask = ntohl(subnet_addr4.s_addr); 1211 1212 while (subnet_mask & 0x80000000) { 1213 plen++; 1214 subnet_mask <<= 1; 1215 } 1216 } else { 1217 return -1; 1218 } 1219 1220 return plen; 1221 } 1222 1223 static int process_dns_gateway_nm(FILE *f, char *ip_string, int type, 1224 int ip_sec) 1225 { 1226 char addr[INET6_ADDRSTRLEN], *output_str; 1227 int ip_offset = 0, error = 0, ip_ver; 1228 char *param_name; 1229 1230 if (type == DNS) 1231 param_name = "dns"; 1232 else if (type == GATEWAY) 1233 param_name = "gateway"; 1234 else 1235 return -EINVAL; 1236 1237 output_str = (char *)calloc(OUTSTR_BUF_SIZE, sizeof(char)); 1238 if (!output_str) 1239 return -ENOMEM; 1240 1241 while (1) { 1242 memset(addr, 0, sizeof(addr)); 1243 1244 if (!parse_ip_val_buffer(ip_string, &ip_offset, addr, 1245 (MAX_IP_ADDR_SIZE * 2))) 1246 break; 1247 1248 ip_ver = ip_version_check(addr); 1249 if (ip_ver < 0) 1250 continue; 1251 1252 if ((ip_ver == IPV4 && ip_sec == IPV4) || 1253 (ip_ver == IPV6 && ip_sec == IPV6)) { 1254 /* 1255 * do a bound check to avoid out-of bound writes 1256 */ 1257 if ((OUTSTR_BUF_SIZE - strlen(output_str)) > 1258 (strlen(addr) + 1)) { 1259 strncat(output_str, addr, 1260 OUTSTR_BUF_SIZE - 1261 strlen(output_str) - 1); 1262 strncat(output_str, ",", 1263 OUTSTR_BUF_SIZE - 1264 strlen(output_str) - 1); 1265 } 1266 } else { 1267 continue; 1268 } 1269 } 1270 1271 if (strlen(output_str)) { 1272 /* 1273 * This is to get rid of that extra comma character 1274 * in the end of the string 1275 */ 1276 output_str[strlen(output_str) - 1] = '\0'; 1277 error = fprintf(f, "%s=%s\n", param_name, output_str); 1278 } 1279 1280 free(output_str); 1281 return error; 1282 } 1283 1284 static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet, 1285 int ip_sec) 1286 { 1287 char addr[INET6_ADDRSTRLEN]; 1288 char subnet_addr[INET6_ADDRSTRLEN]; 1289 int error = 0, i = 0; 1290 int ip_offset = 0, subnet_offset = 0; 1291 int plen, ip_ver; 1292 1293 memset(addr, 0, sizeof(addr)); 1294 memset(subnet_addr, 0, sizeof(subnet_addr)); 1295 1296 while (parse_ip_val_buffer(ip_string, &ip_offset, addr, 1297 (MAX_IP_ADDR_SIZE * 2)) && 1298 parse_ip_val_buffer(subnet, 1299 &subnet_offset, 1300 subnet_addr, 1301 (MAX_IP_ADDR_SIZE * 1302 2))) { 1303 ip_ver = ip_version_check(addr); 1304 if (ip_ver < 0) 1305 continue; 1306 1307 if (ip_ver == IPV4 && ip_sec == IPV4) 1308 plen = kvp_subnet_to_plen((char *)subnet_addr); 1309 else if (ip_ver == IPV6 && ip_sec == IPV6) 1310 plen = atoi(subnet_addr); 1311 else 1312 continue; 1313 1314 if (plen < 0) 1315 return plen; 1316 1317 error = fprintf(f, "address%d=%s/%d\n", ++i, (char *)addr, 1318 plen); 1319 if (error < 0) 1320 return error; 1321 1322 memset(addr, 0, sizeof(addr)); 1323 memset(subnet_addr, 0, sizeof(subnet_addr)); 1324 } 1325 1326 return error; 1327 } 1328 1329 static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) 1330 { 1331 int error = 0, ip_ver; 1332 char if_filename[PATH_MAX]; 1333 char nm_filename[PATH_MAX]; 1334 FILE *ifcfg_file, *nmfile; 1335 char cmd[PATH_MAX]; 1336 char *mac_addr; 1337 int str_len; 1338 1339 /* 1340 * Set the configuration for the specified interface with 1341 * the information provided. Since there is no standard 1342 * way to configure an interface, we will have an external 1343 * script that does the job of configuring the interface and 1344 * flushing the configuration. 1345 * 1346 * The parameters passed to this external script are: 1347 * 1. A configuration file that has the specified configuration. 1348 * 1349 * We will embed the name of the interface in the configuration 1350 * file: ifcfg-ethx (where ethx is the interface name). 1351 * 1352 * The information provided here may be more than what is needed 1353 * in a given distro to configure the interface and so are free 1354 * ignore information that may not be relevant. 1355 * 1356 * Here is the ifcfg format of the ip configuration file: 1357 * 1358 * HWADDR=macaddr 1359 * DEVICE=interface name 1360 * BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured 1361 * or "none" if no boot-time protocol should be used) 1362 * 1363 * IPADDR0=ipaddr1 1364 * IPADDR1=ipaddr2 1365 * IPADDRx=ipaddry (where y = x + 1) 1366 * 1367 * NETMASK0=netmask1 1368 * NETMASKx=netmasky (where y = x + 1) 1369 * 1370 * GATEWAY=ipaddr1 1371 * GATEWAYx=ipaddry (where y = x + 1) 1372 * 1373 * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) 1374 * 1375 * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be 1376 * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as 1377 * IPV6NETMASK. 1378 * 1379 * Here is the keyfile format of the ip configuration file: 1380 * 1381 * [ethernet] 1382 * mac-address=macaddr 1383 * [connection] 1384 * interface-name=interface name 1385 * 1386 * [ipv4] 1387 * method=<protocol> (where <protocol> is "auto" if DHCP is configured 1388 * or "manual" if no boot-time protocol should be used) 1389 * 1390 * address1=ipaddr1/plen 1391 * address2=ipaddr2/plen 1392 * 1393 * gateway=gateway1;gateway2 1394 * 1395 * dns=dns1;dns2 1396 * 1397 * [ipv6] 1398 * address1=ipaddr1/plen 1399 * address2=ipaddr2/plen 1400 * 1401 * gateway=gateway1;gateway2 1402 * 1403 * dns=dns1;dns2 1404 * 1405 * The host can specify multiple ipv4 and ipv6 addresses to be 1406 * configured for the interface. Furthermore, the configuration 1407 * needs to be persistent. A subsequent GET call on the interface 1408 * is expected to return the configuration that is set via the SET 1409 * call. 1410 */ 1411 1412 /* 1413 * We are populating both ifcfg and nmconnection files 1414 */ 1415 snprintf(if_filename, sizeof(if_filename), "%s%s%s", KVP_CONFIG_LOC, 1416 "/ifcfg-", if_name); 1417 1418 ifcfg_file = fopen(if_filename, "w"); 1419 1420 if (!ifcfg_file) { 1421 syslog(LOG_ERR, "Failed to open config file; error: %d %s", 1422 errno, strerror(errno)); 1423 return HV_E_FAIL; 1424 } 1425 1426 snprintf(nm_filename, sizeof(nm_filename), "%s%s%s%s", KVP_CONFIG_LOC, 1427 "/", if_name, ".nmconnection"); 1428 1429 nmfile = fopen(nm_filename, "w"); 1430 1431 if (!nmfile) { 1432 syslog(LOG_ERR, "Failed to open config file; error: %d %s", 1433 errno, strerror(errno)); 1434 fclose(ifcfg_file); 1435 return HV_E_FAIL; 1436 } 1437 1438 /* 1439 * First write out the MAC address. 1440 */ 1441 1442 mac_addr = kvp_if_name_to_mac(if_name); 1443 if (mac_addr == NULL) { 1444 error = HV_E_FAIL; 1445 goto setval_error; 1446 } 1447 1448 error = kvp_write_file(ifcfg_file, "HWADDR", "", mac_addr); 1449 if (error < 0) 1450 goto setmac_error; 1451 1452 error = kvp_write_file(ifcfg_file, "DEVICE", "", if_name); 1453 if (error < 0) 1454 goto setmac_error; 1455 1456 error = fprintf(nmfile, "\n[connection]\n"); 1457 if (error < 0) 1458 goto setmac_error; 1459 1460 error = kvp_write_file(nmfile, "interface-name", "", if_name); 1461 if (error) 1462 goto setmac_error; 1463 1464 error = fprintf(nmfile, "\n[ethernet]\n"); 1465 if (error < 0) 1466 goto setmac_error; 1467 1468 error = kvp_write_file(nmfile, "mac-address", "", mac_addr); 1469 if (error) 1470 goto setmac_error; 1471 1472 free(mac_addr); 1473 1474 /* 1475 * The dhcp_enabled flag is only for IPv4. In the case the host only 1476 * injects an IPv6 address, the flag is true, but we still need to 1477 * proceed to parse and pass the IPv6 information to the 1478 * disto-specific script hv_set_ifconfig. 1479 */ 1480 1481 /* 1482 * First populate the ifcfg file format 1483 */ 1484 if (new_val->dhcp_enabled) { 1485 error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "dhcp"); 1486 if (error) 1487 goto setval_error; 1488 } else { 1489 error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "none"); 1490 if (error) 1491 goto setval_error; 1492 } 1493 1494 error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr, 1495 IPADDR); 1496 if (error) 1497 goto setval_error; 1498 1499 error = process_ip_string(ifcfg_file, (char *)new_val->sub_net, 1500 NETMASK); 1501 if (error) 1502 goto setval_error; 1503 1504 error = process_ip_string(ifcfg_file, (char *)new_val->gate_way, 1505 GATEWAY); 1506 if (error) 1507 goto setval_error; 1508 1509 error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS); 1510 if (error) 1511 goto setval_error; 1512 1513 /* 1514 * Now we populate the keyfile format 1515 * 1516 * The keyfile format expects the IPv6 and IPv4 configuration in 1517 * different sections. Therefore we iterate through the list twice, 1518 * once to populate the IPv4 section and the next time for IPv6 1519 */ 1520 ip_ver = IPV4; 1521 do { 1522 if (ip_ver == IPV4) { 1523 error = fprintf(nmfile, "\n[ipv4]\n"); 1524 if (error < 0) 1525 goto setval_error; 1526 } else { 1527 error = fprintf(nmfile, "\n[ipv6]\n"); 1528 if (error < 0) 1529 goto setval_error; 1530 } 1531 1532 /* 1533 * Write the configuration for ipaddress, netmask, gateway and 1534 * name services 1535 */ 1536 error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr, 1537 (char *)new_val->sub_net, 1538 ip_ver); 1539 if (error < 0) 1540 goto setval_error; 1541 1542 /* 1543 * As dhcp_enabled is only valid for ipv4, we do not set dhcp 1544 * methods for ipv6 based on dhcp_enabled flag. 1545 * 1546 * For ipv4, set method to manual only when dhcp_enabled is 1547 * false and specific ipv4 addresses are configured. If neither 1548 * dhcp_enabled is true and no ipv4 addresses are configured, 1549 * set method to 'disabled'. 1550 * 1551 * For ipv6, set method to manual when we configure ipv6 1552 * addresses. Otherwise set method to 'auto' so that SLAAC from 1553 * RA may be used. 1554 */ 1555 if (ip_ver == IPV4) { 1556 if (new_val->dhcp_enabled) { 1557 error = kvp_write_file(nmfile, "method", "", 1558 "auto"); 1559 if (error < 0) 1560 goto setval_error; 1561 } else if (error) { 1562 error = kvp_write_file(nmfile, "method", "", 1563 "manual"); 1564 if (error < 0) 1565 goto setval_error; 1566 } else { 1567 error = kvp_write_file(nmfile, "method", "", 1568 "disabled"); 1569 if (error < 0) 1570 goto setval_error; 1571 } 1572 } else if (ip_ver == IPV6) { 1573 if (error) { 1574 error = kvp_write_file(nmfile, "method", "", 1575 "manual"); 1576 if (error < 0) 1577 goto setval_error; 1578 } else { 1579 error = kvp_write_file(nmfile, "method", "", 1580 "auto"); 1581 if (error < 0) 1582 goto setval_error; 1583 } 1584 } 1585 1586 error = process_dns_gateway_nm(nmfile, 1587 (char *)new_val->gate_way, 1588 GATEWAY, ip_ver); 1589 if (error < 0) 1590 goto setval_error; 1591 1592 error = process_dns_gateway_nm(nmfile, 1593 (char *)new_val->dns_addr, DNS, 1594 ip_ver); 1595 if (error < 0) 1596 goto setval_error; 1597 1598 ip_ver++; 1599 } while (ip_ver < IP_TYPE_MAX); 1600 1601 fclose(nmfile); 1602 fclose(ifcfg_file); 1603 1604 /* 1605 * Now that we have populated the configuration file, 1606 * invoke the external script to do its magic. 1607 */ 1608 1609 str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s %s", 1610 "hv_set_ifconfig", if_filename, nm_filename); 1611 /* 1612 * This is a little overcautious, but it's necessary to suppress some 1613 * false warnings from gcc 8.0.1. 1614 */ 1615 if (str_len <= 0 || (unsigned int)str_len >= sizeof(cmd)) { 1616 syslog(LOG_ERR, "Cmd '%s' (len=%d) may be too long", 1617 cmd, str_len); 1618 return HV_E_FAIL; 1619 } 1620 1621 if (system(cmd)) { 1622 syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s", 1623 cmd, errno, strerror(errno)); 1624 return HV_E_FAIL; 1625 } 1626 return 0; 1627 setmac_error: 1628 free(mac_addr); 1629 setval_error: 1630 syslog(LOG_ERR, "Failed to write config file"); 1631 fclose(ifcfg_file); 1632 fclose(nmfile); 1633 return error; 1634 } 1635 1636 1637 static void 1638 kvp_get_domain_name(char *buffer, int length) 1639 { 1640 struct addrinfo hints, *info ; 1641 int error = 0; 1642 1643 gethostname(buffer, length); 1644 memset(&hints, 0, sizeof(hints)); 1645 hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */ 1646 hints.ai_socktype = SOCK_STREAM; 1647 hints.ai_flags = AI_CANONNAME; 1648 1649 error = getaddrinfo(buffer, NULL, &hints, &info); 1650 if (error != 0) { 1651 snprintf(buffer, length, "getaddrinfo failed: 0x%x %s", 1652 error, gai_strerror(error)); 1653 return; 1654 } 1655 snprintf(buffer, length, "%s", info->ai_canonname); 1656 freeaddrinfo(info); 1657 } 1658 1659 void print_usage(char *argv[]) 1660 { 1661 fprintf(stderr, "Usage: %s [options]\n" 1662 "Options are:\n" 1663 " -n, --no-daemon stay in foreground, don't daemonize\n" 1664 " -h, --help print this help\n", argv[0]); 1665 } 1666 1667 int main(int argc, char *argv[]) 1668 { 1669 int kvp_fd = -1, len; 1670 int error; 1671 struct pollfd pfd; 1672 char *p; 1673 struct hv_kvp_msg hv_msg[1]; 1674 char *key_value; 1675 char *key_name; 1676 int op; 1677 int pool; 1678 char *if_name; 1679 struct hv_kvp_ipaddr_value *kvp_ip_val; 1680 int daemonize = 1, long_index = 0, opt; 1681 1682 static struct option long_options[] = { 1683 {"help", no_argument, 0, 'h' }, 1684 {"no-daemon", no_argument, 0, 'n' }, 1685 {0, 0, 0, 0 } 1686 }; 1687 1688 while ((opt = getopt_long(argc, argv, "hn", long_options, 1689 &long_index)) != -1) { 1690 switch (opt) { 1691 case 'n': 1692 daemonize = 0; 1693 break; 1694 case 'h': 1695 print_usage(argv); 1696 exit(0); 1697 default: 1698 print_usage(argv); 1699 exit(EXIT_FAILURE); 1700 } 1701 } 1702 1703 if (daemonize && daemon(1, 0)) 1704 return 1; 1705 1706 openlog("KVP", 0, LOG_USER); 1707 syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); 1708 1709 /* 1710 * Retrieve OS release information. 1711 */ 1712 kvp_get_os_info(); 1713 /* 1714 * Cache Fully Qualified Domain Name because getaddrinfo takes an 1715 * unpredictable amount of time to finish. 1716 */ 1717 kvp_get_domain_name(full_domain_name, sizeof(full_domain_name)); 1718 1719 if (kvp_file_init()) { 1720 syslog(LOG_ERR, "Failed to initialize the pools"); 1721 exit(EXIT_FAILURE); 1722 } 1723 1724 reopen_kvp_fd: 1725 if (kvp_fd != -1) 1726 close(kvp_fd); 1727 in_hand_shake = 1; 1728 kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR | O_CLOEXEC); 1729 1730 if (kvp_fd < 0) { 1731 syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s", 1732 errno, strerror(errno)); 1733 exit(EXIT_FAILURE); 1734 } 1735 1736 /* 1737 * Register ourselves with the kernel. 1738 */ 1739 hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1; 1740 len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg)); 1741 if (len != sizeof(struct hv_kvp_msg)) { 1742 syslog(LOG_ERR, "registration to kernel failed; error: %d %s", 1743 errno, strerror(errno)); 1744 close(kvp_fd); 1745 exit(EXIT_FAILURE); 1746 } 1747 1748 pfd.fd = kvp_fd; 1749 1750 while (1) { 1751 pfd.events = POLLIN; 1752 pfd.revents = 0; 1753 1754 if (poll(&pfd, 1, -1) < 0) { 1755 syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno)); 1756 if (errno == EINVAL) { 1757 close(kvp_fd); 1758 exit(EXIT_FAILURE); 1759 } 1760 else 1761 continue; 1762 } 1763 1764 len = read(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg)); 1765 1766 if (len != sizeof(struct hv_kvp_msg)) { 1767 syslog(LOG_ERR, "read failed; error:%d %s", 1768 errno, strerror(errno)); 1769 goto reopen_kvp_fd; 1770 } 1771 1772 /* 1773 * We will use the KVP header information to pass back 1774 * the error from this daemon. So, first copy the state 1775 * and set the error code to success. 1776 */ 1777 op = hv_msg->kvp_hdr.operation; 1778 pool = hv_msg->kvp_hdr.pool; 1779 hv_msg->error = HV_S_OK; 1780 1781 if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) { 1782 /* 1783 * Driver is registering with us; stash away the version 1784 * information. 1785 */ 1786 in_hand_shake = 0; 1787 p = (char *)hv_msg->body.kvp_register.version; 1788 lic_version = malloc(strlen(p) + 1); 1789 if (lic_version) { 1790 strcpy(lic_version, p); 1791 syslog(LOG_INFO, "KVP LIC Version: %s", 1792 lic_version); 1793 } else { 1794 syslog(LOG_ERR, "malloc failed"); 1795 } 1796 continue; 1797 } 1798 1799 switch (op) { 1800 case KVP_OP_GET_IP_INFO: 1801 kvp_ip_val = &hv_msg->body.kvp_ip_val; 1802 1803 error = kvp_mac_to_ip(kvp_ip_val); 1804 1805 if (error) 1806 hv_msg->error = error; 1807 1808 break; 1809 1810 case KVP_OP_SET_IP_INFO: 1811 kvp_ip_val = &hv_msg->body.kvp_ip_val; 1812 if_name = kvp_get_if_name( 1813 (char *)kvp_ip_val->adapter_id); 1814 if (if_name == NULL) { 1815 /* 1816 * We could not map the guid to an 1817 * interface name; return error. 1818 */ 1819 hv_msg->error = HV_GUID_NOTFOUND; 1820 break; 1821 } 1822 error = kvp_set_ip_info(if_name, kvp_ip_val); 1823 if (error) 1824 hv_msg->error = error; 1825 1826 free(if_name); 1827 break; 1828 1829 case KVP_OP_SET: 1830 if (kvp_key_add_or_modify(pool, 1831 hv_msg->body.kvp_set.data.key, 1832 hv_msg->body.kvp_set.data.key_size, 1833 hv_msg->body.kvp_set.data.value, 1834 hv_msg->body.kvp_set.data.value_size)) 1835 hv_msg->error = HV_S_CONT; 1836 break; 1837 1838 case KVP_OP_GET: 1839 if (kvp_get_value(pool, 1840 hv_msg->body.kvp_set.data.key, 1841 hv_msg->body.kvp_set.data.key_size, 1842 hv_msg->body.kvp_set.data.value, 1843 hv_msg->body.kvp_set.data.value_size)) 1844 hv_msg->error = HV_S_CONT; 1845 break; 1846 1847 case KVP_OP_DELETE: 1848 if (kvp_key_delete(pool, 1849 hv_msg->body.kvp_delete.key, 1850 hv_msg->body.kvp_delete.key_size)) 1851 hv_msg->error = HV_S_CONT; 1852 break; 1853 1854 default: 1855 break; 1856 } 1857 1858 if (op != KVP_OP_ENUMERATE) 1859 goto kvp_done; 1860 1861 /* 1862 * If the pool is KVP_POOL_AUTO, dynamically generate 1863 * both the key and the value; if not read from the 1864 * appropriate pool. 1865 */ 1866 if (pool != KVP_POOL_AUTO) { 1867 if (kvp_pool_enumerate(pool, 1868 hv_msg->body.kvp_enum_data.index, 1869 hv_msg->body.kvp_enum_data.data.key, 1870 HV_KVP_EXCHANGE_MAX_KEY_SIZE, 1871 hv_msg->body.kvp_enum_data.data.value, 1872 HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) 1873 hv_msg->error = HV_S_CONT; 1874 goto kvp_done; 1875 } 1876 1877 key_name = (char *)hv_msg->body.kvp_enum_data.data.key; 1878 key_value = (char *)hv_msg->body.kvp_enum_data.data.value; 1879 1880 switch (hv_msg->body.kvp_enum_data.index) { 1881 case FullyQualifiedDomainName: 1882 strcpy(key_value, full_domain_name); 1883 strcpy(key_name, "FullyQualifiedDomainName"); 1884 break; 1885 case IntegrationServicesVersion: 1886 strcpy(key_name, "IntegrationServicesVersion"); 1887 strcpy(key_value, lic_version); 1888 break; 1889 case NetworkAddressIPv4: 1890 kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE, 1891 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); 1892 strcpy(key_name, "NetworkAddressIPv4"); 1893 break; 1894 case NetworkAddressIPv6: 1895 kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE, 1896 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); 1897 strcpy(key_name, "NetworkAddressIPv6"); 1898 break; 1899 case OSBuildNumber: 1900 strcpy(key_value, os_build); 1901 strcpy(key_name, "OSBuildNumber"); 1902 break; 1903 case OSName: 1904 strcpy(key_value, os_name); 1905 strcpy(key_name, "OSName"); 1906 break; 1907 case OSMajorVersion: 1908 strcpy(key_value, os_major); 1909 strcpy(key_name, "OSMajorVersion"); 1910 break; 1911 case OSMinorVersion: 1912 strcpy(key_value, os_minor); 1913 strcpy(key_name, "OSMinorVersion"); 1914 break; 1915 case OSVersion: 1916 strcpy(key_value, os_version); 1917 strcpy(key_name, "OSVersion"); 1918 break; 1919 case ProcessorArchitecture: 1920 strcpy(key_value, processor_arch); 1921 strcpy(key_name, "ProcessorArchitecture"); 1922 break; 1923 default: 1924 hv_msg->error = HV_S_CONT; 1925 break; 1926 } 1927 1928 /* 1929 * Send the value back to the kernel. Note: the write() may 1930 * return an error due to hibernation; we can ignore the error 1931 * by resetting the dev file, i.e. closing and re-opening it. 1932 */ 1933 kvp_done: 1934 len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg)); 1935 if (len != sizeof(struct hv_kvp_msg)) { 1936 syslog(LOG_ERR, "write failed; error: %d %s", errno, 1937 strerror(errno)); 1938 goto reopen_kvp_fd; 1939 } 1940 } 1941 1942 close(kvp_fd); 1943 exit(0); 1944 } 1945