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