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