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
kvp_acquire_lock(int pool)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
kvp_release_lock(int pool)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
kvp_update_file(int pool)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
kvp_update_mem_state(int pool)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
kvp_file_init(void)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
kvp_key_delete(int pool,const __u8 * key,int key_size)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
kvp_key_add_or_modify(int pool,const __u8 * key,int key_size,const __u8 * value,int value_size)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
kvp_get_value(int pool,const __u8 * key,int key_size,__u8 * value,int value_size)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
kvp_pool_enumerate(int pool,int index,__u8 * key,int key_size,__u8 * value,int value_size)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
kvp_get_os_info(void)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
kvp_get_if_name(char * guid)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
kvp_if_name_to_mac(char * if_name)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
kvp_process_ipconfig_file(char * cmd,char * config_buf,unsigned int len,int element_size,int offset)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
kvp_verify_ip_address(const void * address_string)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
kvp_extract_routes(const char * line,void ** output,size_t * remaining)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
kvp_get_gateway(void * buffer,size_t buffer_len)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
kvp_get_ipconfig_info(char * if_name,struct hv_kvp_ipaddr_value * buffer)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
hweight32(unsigned int * w)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
kvp_process_ip_address(void * addrp,int family,char * buffer,int length,int * offset)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
kvp_get_ip_info(int family,char * if_name,int op,void * out_buffer,unsigned int length)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 */
kvp_mac_to_ip(struct hv_kvp_ipaddr_value * kvp_ip_val)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
expand_ipv6(char * addr,int type)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
is_ipv4(char * addr)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
parse_ip_val_buffer(char * in_buf,int * offset,char * out_buf,int out_len)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
kvp_write_file(FILE * f,char * s1,char * s2,char * s3)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
process_ip_string(FILE * f,char * ip_string,int type)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
ip_version_check(const char * input_addr)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 */
kvp_subnet_to_plen(char * subnet_addr_str)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
process_dns_gateway_nm(FILE * f,char * ip_string,int type,int ip_sec)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
process_ip_string_nm(FILE * f,char * ip_string,char * subnet,int ip_sec)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
kvp_set_ip_info(char * if_name,struct hv_kvp_ipaddr_value * new_val)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
kvp_get_domain_name(char * buffer,int length)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
print_usage(char * argv[])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
main(int argc,char * argv[])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