blob: c8e101363735fdbff8bf6e6067edb8d6c14b4d87 [file] [log] [blame]
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001/*
2 * An implementation of key value pair (KVP) functionality for Linux.
3 *
4 *
5 * Copyright (C) 2010, Novell, Inc.
6 * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published
10 * by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
15 * NON INFRINGEMENT. See the GNU General Public License for more
16 * details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/poll.h>
28#include <sys/utsname.h>
29#include <linux/types.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <string.h>
K. Y. Srinivasan32061b42012-09-05 13:50:13 -070034#include <ctype.h>
Ky Srinivasancc04acf2010-12-16 18:56:54 -070035#include <errno.h>
36#include <arpa/inet.h>
37#include <linux/connector.h>
K. Y. Srinivasaneab6af72012-02-02 16:56:49 -080038#include <linux/hyperv.h>
Ky Srinivasancc04acf2010-12-16 18:56:54 -070039#include <linux/netlink.h>
Ky Srinivasancc04acf2010-12-16 18:56:54 -070040#include <ifaddrs.h>
41#include <netdb.h>
42#include <syslog.h>
K. Y. Srinivasandb425332012-03-16 08:02:26 -070043#include <sys/stat.h>
44#include <fcntl.h>
K. Y. Srinivasan32061b42012-09-05 13:50:13 -070045#include <dirent.h>
Ky Srinivasancc04acf2010-12-16 18:56:54 -070046
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 *
Ky Srinivasancc04acf2010-12-16 18:56:54 -070057 */
58
Ky Srinivasancc04acf2010-12-16 18:56:54 -070059
60enum 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
K. Y. Srinivasan32061b42012-09-05 13:50:13 -070073
74enum {
75 IPADDR = 0,
76 NETMASK,
77 GATEWAY,
78 DNS
79};
80
Ky Srinivasancc04acf2010-12-16 18:56:54 -070081static char kvp_send_buffer[4096];
K. Y. Srinivasan9b595782012-08-13 10:06:51 -070082static char kvp_recv_buffer[4096 * 2];
Ky Srinivasancc04acf2010-12-16 18:56:54 -070083static struct sockaddr_nl addr;
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -070084static int in_hand_shake = 1;
Ky Srinivasancc04acf2010-12-16 18:56:54 -070085
Olaf Hering7989f7d2011-03-22 10:02:17 +010086static char *os_name = "";
87static char *os_major = "";
88static char *os_minor = "";
89static char *processor_arch;
90static char *os_build;
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -070091static char *lic_version = "Unknown version";
Olaf Hering7989f7d2011-03-22 10:02:17 +010092static struct utsname uts_buf;
Ky Srinivasancc04acf2010-12-16 18:56:54 -070093
K. Y. Srinivasan32061b42012-09-05 13:50:13 -070094/*
95 * The location of the interface configuration file.
96 */
97
98#define KVP_CONFIG_LOC "/var/opt/"
K. Y. Srinivasandb425332012-03-16 08:02:26 -070099
100#define MAX_FILE_NAME 100
101#define ENTRIES_PER_BLOCK 50
102
103struct kvp_record {
K. Y. Srinivasand0cbc152012-09-04 14:46:34 -0700104 char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
105 char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700106};
107
108struct kvp_file_state {
109 int fd;
110 int num_blocks;
111 struct kvp_record *records;
112 int num_records;
K. Y. Srinivasand0cbc152012-09-04 14:46:34 -0700113 char fname[MAX_FILE_NAME];
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700114};
115
116static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
117
118static void kvp_acquire_lock(int pool)
119{
120 struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
121 fl.l_pid = getpid();
122
123 if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
124 syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool);
125 exit(-1);
126 }
127}
128
129static void kvp_release_lock(int pool)
130{
131 struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0};
132 fl.l_pid = getpid();
133
134 if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
135 perror("fcntl");
136 syslog(LOG_ERR, "Failed to release the lock pool: %d", pool);
137 exit(-1);
138 }
139}
140
141static void kvp_update_file(int pool)
142{
143 FILE *filep;
144 size_t bytes_written;
145
146 /*
147 * We are going to write our in-memory registry out to
148 * disk; acquire the lock first.
149 */
150 kvp_acquire_lock(pool);
151
152 filep = fopen(kvp_file_info[pool].fname, "w");
153 if (!filep) {
154 kvp_release_lock(pool);
155 syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
156 exit(-1);
157 }
158
159 bytes_written = fwrite(kvp_file_info[pool].records,
160 sizeof(struct kvp_record),
161 kvp_file_info[pool].num_records, filep);
162
163 fflush(filep);
164 kvp_release_lock(pool);
165}
166
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -0700167static void kvp_update_mem_state(int pool)
168{
169 FILE *filep;
170 size_t records_read = 0;
171 struct kvp_record *record = kvp_file_info[pool].records;
172 struct kvp_record *readp;
173 int num_blocks = kvp_file_info[pool].num_blocks;
174 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
175
176 kvp_acquire_lock(pool);
177
178 filep = fopen(kvp_file_info[pool].fname, "r");
179 if (!filep) {
180 kvp_release_lock(pool);
181 syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
182 exit(-1);
183 }
184 while (!feof(filep)) {
185 readp = &record[records_read];
186 records_read += fread(readp, sizeof(struct kvp_record),
187 ENTRIES_PER_BLOCK * num_blocks,
188 filep);
189
190 if (!feof(filep)) {
191 /*
192 * We have more data to read.
193 */
194 num_blocks++;
195 record = realloc(record, alloc_unit * num_blocks);
196
197 if (record == NULL) {
198 syslog(LOG_ERR, "malloc failed");
199 exit(-1);
200 }
201 continue;
202 }
203 break;
204 }
205
206 kvp_file_info[pool].num_blocks = num_blocks;
207 kvp_file_info[pool].records = record;
208 kvp_file_info[pool].num_records = records_read;
209
210 kvp_release_lock(pool);
211}
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700212static int kvp_file_init(void)
213{
K. Y. Srinivasan00b83352012-09-04 14:46:33 -0700214 int fd;
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700215 FILE *filep;
216 size_t records_read;
K. Y. Srinivasand0cbc152012-09-04 14:46:34 -0700217 char *fname;
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700218 struct kvp_record *record;
219 struct kvp_record *readp;
220 int num_blocks;
221 int i;
222 int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
223
224 if (access("/var/opt/hyperv", F_OK)) {
225 if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) {
226 syslog(LOG_ERR, " Failed to create /var/opt/hyperv");
227 exit(-1);
228 }
229 }
230
231 for (i = 0; i < KVP_POOL_COUNT; i++) {
232 fname = kvp_file_info[i].fname;
233 records_read = 0;
234 num_blocks = 1;
235 sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i);
236 fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
237
238 if (fd == -1)
239 return 1;
240
241
242 filep = fopen(fname, "r");
243 if (!filep)
244 return 1;
245
246 record = malloc(alloc_unit * num_blocks);
247 if (record == NULL) {
248 fclose(filep);
249 return 1;
250 }
251 while (!feof(filep)) {
252 readp = &record[records_read];
253 records_read += fread(readp, sizeof(struct kvp_record),
254 ENTRIES_PER_BLOCK,
255 filep);
256
257 if (!feof(filep)) {
258 /*
259 * We have more data to read.
260 */
261 num_blocks++;
262 record = realloc(record, alloc_unit *
263 num_blocks);
264 if (record == NULL) {
265 fclose(filep);
266 return 1;
267 }
268 continue;
269 }
270 break;
271 }
272 kvp_file_info[i].fd = fd;
273 kvp_file_info[i].num_blocks = num_blocks;
274 kvp_file_info[i].records = record;
275 kvp_file_info[i].num_records = records_read;
276 fclose(filep);
277
278 }
279
280 return 0;
281}
282
283static int kvp_key_delete(int pool, __u8 *key, int key_size)
284{
285 int i;
286 int j, k;
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -0700287 int num_records;
288 struct kvp_record *record;
289
290 /*
291 * First update the in-memory state.
292 */
293 kvp_update_mem_state(pool);
294
295 num_records = kvp_file_info[pool].num_records;
296 record = kvp_file_info[pool].records;
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700297
298 for (i = 0; i < num_records; i++) {
299 if (memcmp(key, record[i].key, key_size))
300 continue;
301 /*
302 * Found a match; just move the remaining
303 * entries up.
304 */
305 if (i == num_records) {
306 kvp_file_info[pool].num_records--;
307 kvp_update_file(pool);
308 return 0;
309 }
310
311 j = i;
312 k = j + 1;
313 for (; k < num_records; k++) {
314 strcpy(record[j].key, record[k].key);
315 strcpy(record[j].value, record[k].value);
316 j++;
317 }
318
319 kvp_file_info[pool].num_records--;
320 kvp_update_file(pool);
321 return 0;
322 }
323 return 1;
324}
325
326static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value,
327 int value_size)
328{
329 int i;
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -0700330 int num_records;
331 struct kvp_record *record;
332 int num_blocks;
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700333
334 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
335 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
336 return 1;
337
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -0700338 /*
339 * First update the in-memory state.
340 */
341 kvp_update_mem_state(pool);
342
343 num_records = kvp_file_info[pool].num_records;
344 record = kvp_file_info[pool].records;
345 num_blocks = kvp_file_info[pool].num_blocks;
346
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700347 for (i = 0; i < num_records; i++) {
348 if (memcmp(key, record[i].key, key_size))
349 continue;
350 /*
351 * Found a match; just update the value -
352 * this is the modify case.
353 */
354 memcpy(record[i].value, value, value_size);
355 kvp_update_file(pool);
356 return 0;
357 }
358
359 /*
360 * Need to add a new entry;
361 */
362 if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) {
363 /* Need to allocate a larger array for reg entries. */
364 record = realloc(record, sizeof(struct kvp_record) *
365 ENTRIES_PER_BLOCK * (num_blocks + 1));
366
367 if (record == NULL)
368 return 1;
369 kvp_file_info[pool].num_blocks++;
370
371 }
372 memcpy(record[i].value, value, value_size);
373 memcpy(record[i].key, key, key_size);
374 kvp_file_info[pool].records = record;
375 kvp_file_info[pool].num_records++;
376 kvp_update_file(pool);
377 return 0;
378}
379
380static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
381 int value_size)
382{
383 int i;
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -0700384 int num_records;
385 struct kvp_record *record;
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700386
387 if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) ||
388 (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
389 return 1;
390
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -0700391 /*
392 * First update the in-memory state.
393 */
394 kvp_update_mem_state(pool);
395
396 num_records = kvp_file_info[pool].num_records;
397 record = kvp_file_info[pool].records;
398
K. Y. Srinivasandb425332012-03-16 08:02:26 -0700399 for (i = 0; i < num_records; i++) {
400 if (memcmp(key, record[i].key, key_size))
401 continue;
402 /*
403 * Found a match; just copy the value out.
404 */
405 memcpy(value, record[i].value, value_size);
406 return 0;
407 }
408
409 return 1;
410}
411
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -0700412static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -0700413 __u8 *value, int value_size)
414{
415 struct kvp_record *record;
416
417 /*
418 * First update our in-memory database.
419 */
420 kvp_update_mem_state(pool);
421 record = kvp_file_info[pool].records;
422
423 if (index >= kvp_file_info[pool].num_records) {
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -0700424 return 1;
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -0700425 }
426
427 memcpy(key, record[index].key, key_size);
428 memcpy(value, record[index].value, value_size);
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -0700429 return 0;
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -0700430}
431
432
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700433void kvp_get_os_info(void)
434{
435 FILE *file;
Olaf Hering7989f7d2011-03-22 10:02:17 +0100436 char *p, buf[512];
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700437
Olaf Hering7989f7d2011-03-22 10:02:17 +0100438 uname(&uts_buf);
439 os_build = uts_buf.release;
K. Y. Srinivasan064931d2011-07-19 11:44:20 -0700440 processor_arch = uts_buf.machine;
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700441
K. Y. Srinivasane54bbc62011-07-22 10:14:31 -0700442 /*
443 * The current windows host (win7) expects the build
444 * string to be of the form: x.y.z
445 * Strip additional information we may have.
446 */
447 p = strchr(os_build, '-');
448 if (p)
449 *p = '\0';
450
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700451 file = fopen("/etc/SuSE-release", "r");
452 if (file != NULL)
453 goto kvp_osinfo_found;
454 file = fopen("/etc/redhat-release", "r");
455 if (file != NULL)
456 goto kvp_osinfo_found;
457 /*
458 * Add code for other supported platforms.
459 */
460
461 /*
462 * We don't have information about the os.
463 */
Olaf Hering7989f7d2011-03-22 10:02:17 +0100464 os_name = uts_buf.sysname;
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700465 return;
466
467kvp_osinfo_found:
Olaf Hering7989f7d2011-03-22 10:02:17 +0100468 /* up to three lines */
469 p = fgets(buf, sizeof(buf), file);
470 if (p) {
471 p = strchr(buf, '\n');
472 if (p)
473 *p = '\0';
474 p = strdup(buf);
475 if (!p)
476 goto done;
477 os_name = p;
478
479 /* second line */
480 p = fgets(buf, sizeof(buf), file);
481 if (p) {
482 p = strchr(buf, '\n');
483 if (p)
484 *p = '\0';
485 p = strdup(buf);
486 if (!p)
487 goto done;
488 os_major = p;
489
490 /* third line */
491 p = fgets(buf, sizeof(buf), file);
492 if (p) {
493 p = strchr(buf, '\n');
494 if (p)
495 *p = '\0';
496 p = strdup(buf);
497 if (p)
498 os_minor = p;
499 }
500 }
501 }
502
503done:
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700504 fclose(file);
505 return;
506}
507
K. Y. Srinivasan32061b42012-09-05 13:50:13 -0700508
509
510/*
511 * Retrieve an interface name corresponding to the specified guid.
512 * If there is a match, the function returns a pointer
513 * to the interface name and if not, a NULL is returned.
514 * If a match is found, the caller is responsible for
515 * freeing the memory.
516 */
517
518static char *kvp_get_if_name(char *guid)
519{
520 DIR *dir;
521 struct dirent *entry;
522 FILE *file;
523 char *p, *q, *x;
524 char *if_name = NULL;
525 char buf[256];
526 char *kvp_net_dir = "/sys/class/net/";
527 char dev_id[256];
528
529 dir = opendir(kvp_net_dir);
530 if (dir == NULL)
531 return NULL;
532
533 snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir);
534 q = dev_id + strlen(kvp_net_dir);
535
536 while ((entry = readdir(dir)) != NULL) {
537 /*
538 * Set the state for the next pass.
539 */
540 *q = '\0';
541 strcat(dev_id, entry->d_name);
542 strcat(dev_id, "/device/device_id");
543
544 file = fopen(dev_id, "r");
545 if (file == NULL)
546 continue;
547
548 p = fgets(buf, sizeof(buf), file);
549 if (p) {
550 x = strchr(p, '\n');
551 if (x)
552 *x = '\0';
553
554 if (!strcmp(p, guid)) {
555 /*
556 * Found the guid match; return the interface
557 * name. The caller will free the memory.
558 */
559 if_name = strdup(entry->d_name);
560 fclose(file);
561 break;
562 }
563 }
564 fclose(file);
565 }
566
567 closedir(dir);
568 return if_name;
569}
570
571/*
572 * Retrieve the MAC address given the interface name.
573 */
574
575static char *kvp_if_name_to_mac(char *if_name)
576{
577 FILE *file;
578 char *p, *x;
579 char buf[256];
580 char addr_file[256];
581 int i;
582 char *mac_addr = NULL;
583
584 snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/",
585 if_name, "/address");
586
587 file = fopen(addr_file, "r");
588 if (file == NULL)
589 return NULL;
590
591 p = fgets(buf, sizeof(buf), file);
592 if (p) {
593 x = strchr(p, '\n');
594 if (x)
595 *x = '\0';
596 for (i = 0; i < strlen(p); i++)
597 p[i] = toupper(p[i]);
598 mac_addr = strdup(p);
599 }
600
601 fclose(file);
602 return mac_addr;
603}
604
605
K. Y. Srinivasan16e87102012-09-05 13:50:15 -0700606/*
607 * Retrieve the interface name given tha MAC address.
608 */
609
610static char *kvp_mac_to_if_name(char *mac)
611{
612 DIR *dir;
613 struct dirent *entry;
614 FILE *file;
615 char *p, *q, *x;
616 char *if_name = NULL;
617 char buf[256];
618 char *kvp_net_dir = "/sys/class/net/";
619 char dev_id[256];
620 int i;
621
622 dir = opendir(kvp_net_dir);
623 if (dir == NULL)
624 return NULL;
625
626 snprintf(dev_id, sizeof(dev_id), kvp_net_dir);
627 q = dev_id + strlen(kvp_net_dir);
628
629 while ((entry = readdir(dir)) != NULL) {
630 /*
631 * Set the state for the next pass.
632 */
633 *q = '\0';
634
635 strcat(dev_id, entry->d_name);
636 strcat(dev_id, "/address");
637
638 file = fopen(dev_id, "r");
639 if (file == NULL)
640 continue;
641
642 p = fgets(buf, sizeof(buf), file);
643 if (p) {
644 x = strchr(p, '\n');
645 if (x)
646 *x = '\0';
647
648 for (i = 0; i < strlen(p); i++)
649 p[i] = toupper(p[i]);
650
651 if (!strcmp(p, mac)) {
652 /*
653 * Found the MAC match; return the interface
654 * name. The caller will free the memory.
655 */
656 if_name = strdup(entry->d_name);
657 fclose(file);
658 break;
659 }
660 }
661 fclose(file);
662 }
663
664 closedir(dir);
665 return if_name;
666}
667
668
K. Y. Srinivasan4a52c4a2012-08-16 18:32:18 -0700669static void kvp_process_ipconfig_file(char *cmd,
670 char *config_buf, int len,
671 int element_size, int offset)
672{
673 char buf[256];
674 char *p;
675 char *x;
676 FILE *file;
677
678 /*
679 * First execute the command.
680 */
681 file = popen(cmd, "r");
682 if (file == NULL)
683 return;
684
685 if (offset == 0)
686 memset(config_buf, 0, len);
687 while ((p = fgets(buf, sizeof(buf), file)) != NULL) {
688 if ((len - strlen(config_buf)) < (element_size + 1))
689 break;
690
691 x = strchr(p, '\n');
692 *x = '\0';
693 strcat(config_buf, p);
694 strcat(config_buf, ";");
695 }
696 pclose(file);
697}
698
699static void kvp_get_ipconfig_info(char *if_name,
700 struct hv_kvp_ipaddr_value *buffer)
701{
702 char cmd[512];
K. Y. Srinivasanc0503722012-09-05 13:50:11 -0700703 char dhcp_info[128];
704 char *p;
705 FILE *file;
K. Y. Srinivasan4a52c4a2012-08-16 18:32:18 -0700706
707 /*
708 * Get the address of default gateway (ipv4).
709 */
710 sprintf(cmd, "%s %s", "ip route show dev", if_name);
711 strcat(cmd, " | awk '/default/ {print $3 }'");
712
713 /*
714 * Execute the command to gather gateway info.
715 */
716 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
717 (MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0);
718
719 /*
720 * Get the address of default gateway (ipv6).
721 */
722 sprintf(cmd, "%s %s", "ip -f inet6 route show dev", if_name);
723 strcat(cmd, " | awk '/default/ {print $3 }'");
724
725 /*
726 * Execute the command to gather gateway info (ipv6).
727 */
728 kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
729 (MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1);
730
K. Y. Srinivasan96929882012-09-04 14:46:36 -0700731
732 /*
733 * Gather the DNS state.
734 * Since there is no standard way to get this information
735 * across various distributions of interest; we just invoke
736 * an external script that needs to be ported across distros
737 * of interest.
738 *
739 * Following is the expected format of the information from the script:
740 *
741 * ipaddr1 (nameserver1)
742 * ipaddr2 (nameserver2)
743 * .
744 * .
745 */
746
747 sprintf(cmd, "%s", "hv_get_dns_info");
748
749 /*
750 * Execute the command to gather DNS info.
751 */
752 kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr,
753 (MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0);
K. Y. Srinivasanc0503722012-09-05 13:50:11 -0700754
755 /*
756 * Gather the DHCP state.
757 * We will gather this state by invoking an external script.
758 * The parameter to the script is the interface name.
759 * Here is the expected output:
760 *
761 * Enabled: DHCP enabled.
762 */
763
764 sprintf(cmd, "%s %s", "hv_get_dhcp_info", if_name);
765
766 file = popen(cmd, "r");
767 if (file == NULL)
768 return;
769
770 p = fgets(dhcp_info, sizeof(dhcp_info), file);
771 if (p == NULL) {
772 pclose(file);
773 return;
774 }
775
776 if (!strncmp(p, "Enabled", 7))
777 buffer->dhcp_enabled = 1;
778 else
779 buffer->dhcp_enabled = 0;
780
781 pclose(file);
K. Y. Srinivasan4a52c4a2012-08-16 18:32:18 -0700782}
783
784
K. Y. Srinivasan6a60a6a2012-08-16 18:32:17 -0700785static unsigned int hweight32(unsigned int *w)
786{
787 unsigned int res = *w - ((*w >> 1) & 0x55555555);
788 res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
789 res = (res + (res >> 4)) & 0x0F0F0F0F;
790 res = res + (res >> 8);
791 return (res + (res >> 16)) & 0x000000FF;
792}
793
K. Y. Srinivasanaf733012012-08-16 18:32:14 -0700794static int kvp_process_ip_address(void *addrp,
795 int family, char *buffer,
796 int length, int *offset)
797{
798 struct sockaddr_in *addr;
799 struct sockaddr_in6 *addr6;
800 int addr_length;
801 char tmp[50];
802 const char *str;
803
804 if (family == AF_INET) {
805 addr = (struct sockaddr_in *)addrp;
806 str = inet_ntop(family, &addr->sin_addr, tmp, 50);
807 addr_length = INET_ADDRSTRLEN;
808 } else {
809 addr6 = (struct sockaddr_in6 *)addrp;
810 str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50);
811 addr_length = INET6_ADDRSTRLEN;
812 }
813
814 if ((length - *offset) < addr_length + 1)
K. Y. Srinivasan16e87102012-09-05 13:50:15 -0700815 return HV_E_FAIL;
K. Y. Srinivasanaf733012012-08-16 18:32:14 -0700816 if (str == NULL) {
817 strcpy(buffer, "inet_ntop failed\n");
K. Y. Srinivasan16e87102012-09-05 13:50:15 -0700818 return HV_E_FAIL;
K. Y. Srinivasanaf733012012-08-16 18:32:14 -0700819 }
820 if (*offset == 0)
821 strcpy(buffer, tmp);
822 else
823 strcat(buffer, tmp);
824 strcat(buffer, ";");
825
826 *offset += strlen(str) + 1;
827 return 0;
828}
829
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700830static int
K. Y. Srinivasan4a3b97e52012-09-05 13:50:14 -0700831kvp_get_ip_info(int family, char *if_name, int op,
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -0700832 void *out_buffer, int length)
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700833{
834 struct ifaddrs *ifap;
835 struct ifaddrs *curp;
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700836 int offset = 0;
K. Y. Srinivasan04405782012-08-16 18:32:16 -0700837 int sn_offset = 0;
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700838 int error = 0;
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -0700839 char *buffer;
840 struct hv_kvp_ipaddr_value *ip_buffer;
K. Y. Srinivasan6a60a6a2012-08-16 18:32:17 -0700841 char cidr_mask[5]; /* /xyz */
842 int weight;
843 int i;
844 unsigned int *w;
845 char *sn_str;
846 struct sockaddr_in6 *addr6;
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700847
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -0700848 if (op == KVP_OP_ENUMERATE) {
849 buffer = out_buffer;
850 } else {
851 ip_buffer = out_buffer;
852 buffer = (char *)ip_buffer->ip_addr;
853 ip_buffer->addr_family = 0;
854 }
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700855 /*
856 * On entry into this function, the buffer is capable of holding the
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -0700857 * maximum key value.
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700858 */
859
860 if (getifaddrs(&ifap)) {
861 strcpy(buffer, "getifaddrs failed\n");
K. Y. Srinivasan16e87102012-09-05 13:50:15 -0700862 return HV_E_FAIL;
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700863 }
864
865 curp = ifap;
866 while (curp != NULL) {
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -0700867 if (curp->ifa_addr == NULL) {
868 curp = curp->ifa_next;
869 continue;
870 }
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700871
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -0700872 if ((if_name != NULL) &&
873 (strncmp(curp->ifa_name, if_name, strlen(if_name)))) {
874 /*
875 * We want info about a specific interface;
876 * just continue.
877 */
878 curp = curp->ifa_next;
879 continue;
880 }
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700881
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -0700882 /*
883 * We only support two address families: AF_INET and AF_INET6.
884 * If a family value of 0 is specified, we collect both
885 * supported address families; if not we gather info on
886 * the specified address family.
887 */
888 if ((family != 0) && (curp->ifa_addr->sa_family != family)) {
889 curp = curp->ifa_next;
890 continue;
891 }
892 if ((curp->ifa_addr->sa_family != AF_INET) &&
893 (curp->ifa_addr->sa_family != AF_INET6)) {
894 curp = curp->ifa_next;
895 continue;
896 }
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700897
K. Y. Srinivasan0d5b6b12012-08-16 18:32:15 -0700898 if (op == KVP_OP_GET_IP_INFO) {
899 /*
900 * Gather info other than the IP address.
901 * IP address info will be gathered later.
902 */
K. Y. Srinivasan04405782012-08-16 18:32:16 -0700903 if (curp->ifa_addr->sa_family == AF_INET) {
K. Y. Srinivasan0d5b6b12012-08-16 18:32:15 -0700904 ip_buffer->addr_family |= ADDR_FAMILY_IPV4;
K. Y. Srinivasan04405782012-08-16 18:32:16 -0700905 /*
906 * Get subnet info.
907 */
908 error = kvp_process_ip_address(
909 curp->ifa_netmask,
910 AF_INET,
911 (char *)
912 ip_buffer->sub_net,
913 length,
914 &sn_offset);
915 if (error)
916 goto gather_ipaddr;
917 } else {
K. Y. Srinivasan0d5b6b12012-08-16 18:32:15 -0700918 ip_buffer->addr_family |= ADDR_FAMILY_IPV6;
K. Y. Srinivasan6a60a6a2012-08-16 18:32:17 -0700919
K. Y. Srinivasan04405782012-08-16 18:32:16 -0700920 /*
K. Y. Srinivasan6a60a6a2012-08-16 18:32:17 -0700921 * Get subnet info in CIDR format.
K. Y. Srinivasan04405782012-08-16 18:32:16 -0700922 */
K. Y. Srinivasan6a60a6a2012-08-16 18:32:17 -0700923 weight = 0;
924 sn_str = (char *)ip_buffer->sub_net;
925 addr6 = (struct sockaddr_in6 *)
926 curp->ifa_netmask;
927 w = addr6->sin6_addr.s6_addr32;
928
929 for (i = 0; i < 4; i++)
930 weight += hweight32(&w[i]);
931
932 sprintf(cidr_mask, "/%d", weight);
933 if ((length - sn_offset) <
934 (strlen(cidr_mask) + 1))
K. Y. Srinivasan04405782012-08-16 18:32:16 -0700935 goto gather_ipaddr;
K. Y. Srinivasan6a60a6a2012-08-16 18:32:17 -0700936
937 if (sn_offset == 0)
938 strcpy(sn_str, cidr_mask);
939 else
940 strcat(sn_str, cidr_mask);
941 strcat((char *)ip_buffer->sub_net, ";");
942 sn_offset += strlen(sn_str) + 1;
K. Y. Srinivasan04405782012-08-16 18:32:16 -0700943 }
K. Y. Srinivasan4a52c4a2012-08-16 18:32:18 -0700944
945 /*
946 * Collect other ip related configuration info.
947 */
948
949 kvp_get_ipconfig_info(if_name, ip_buffer);
K. Y. Srinivasan0d5b6b12012-08-16 18:32:15 -0700950 }
951
K. Y. Srinivasan04405782012-08-16 18:32:16 -0700952gather_ipaddr:
K. Y. Srinivasanaf733012012-08-16 18:32:14 -0700953 error = kvp_process_ip_address(curp->ifa_addr,
954 curp->ifa_addr->sa_family,
955 buffer,
956 length, &offset);
957 if (error)
958 goto getaddr_done;
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -0700959
Ky Srinivasancc04acf2010-12-16 18:56:54 -0700960 curp = curp->ifa_next;
961 }
962
963getaddr_done:
964 freeifaddrs(ifap);
965 return error;
966}
967
968
K. Y. Srinivasan32061b42012-09-05 13:50:13 -0700969static int expand_ipv6(char *addr, int type)
970{
971 int ret;
972 struct in6_addr v6_addr;
973
974 ret = inet_pton(AF_INET6, addr, &v6_addr);
975
976 if (ret != 1) {
977 if (type == NETMASK)
978 return 1;
979 return 0;
980 }
981
982 sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
983 "%02x%02x:%02x%02x:%02x%02x",
984 (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1],
985 (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3],
986 (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5],
987 (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7],
988 (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9],
989 (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11],
990 (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13],
991 (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]);
992
993 return 1;
994
995}
996
997static int is_ipv4(char *addr)
998{
999 int ret;
1000 struct in_addr ipv4_addr;
1001
1002 ret = inet_pton(AF_INET, addr, &ipv4_addr);
1003
1004 if (ret == 1)
1005 return 1;
1006 return 0;
1007}
1008
1009static int parse_ip_val_buffer(char *in_buf, int *offset,
1010 char *out_buf, int out_len)
1011{
1012 char *x;
1013 char *start;
1014
1015 /*
1016 * in_buf has sequence of characters that are seperated by
1017 * the character ';'. The last sequence does not have the
1018 * terminating ";" character.
1019 */
1020 start = in_buf + *offset;
1021
1022 x = strchr(start, ';');
1023 if (x)
1024 *x = 0;
1025 else
1026 x = start + strlen(start);
1027
1028 if (strlen(start) != 0) {
1029 int i = 0;
1030 /*
1031 * Get rid of leading spaces.
1032 */
1033 while (start[i] == ' ')
1034 i++;
1035
1036 if ((x - start) <= out_len) {
1037 strcpy(out_buf, (start + i));
1038 *offset += (x - start) + 1;
1039 return 1;
1040 }
1041 }
1042 return 0;
1043}
1044
1045static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)
1046{
1047 int ret;
1048
1049 ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3);
1050
1051 if (ret < 0)
1052 return HV_E_FAIL;
1053
1054 return 0;
1055}
1056
1057
1058static int process_ip_string(FILE *f, char *ip_string, int type)
1059{
1060 int error = 0;
1061 char addr[INET6_ADDRSTRLEN];
1062 int i = 0;
1063 int j = 0;
1064 char str[256];
1065 char sub_str[10];
1066 int offset = 0;
1067
1068 memset(addr, 0, sizeof(addr));
1069
1070 while (parse_ip_val_buffer(ip_string, &offset, addr,
1071 (MAX_IP_ADDR_SIZE * 2))) {
1072
1073 sub_str[0] = 0;
1074 if (is_ipv4(addr)) {
1075 switch (type) {
1076 case IPADDR:
1077 snprintf(str, sizeof(str), "%s", "IPADDR");
1078 break;
1079 case NETMASK:
1080 snprintf(str, sizeof(str), "%s", "NETMASK");
1081 break;
1082 case GATEWAY:
1083 snprintf(str, sizeof(str), "%s", "GATEWAY");
1084 break;
1085 case DNS:
1086 snprintf(str, sizeof(str), "%s", "DNS");
1087 break;
1088 }
1089 if (i != 0) {
1090 if (type != DNS) {
1091 snprintf(sub_str, sizeof(sub_str),
1092 "_%d", i++);
1093 } else {
1094 snprintf(sub_str, sizeof(sub_str),
1095 "%d", ++i);
1096 }
1097 } else if (type == DNS) {
1098 snprintf(sub_str, sizeof(sub_str), "%d", ++i);
1099 }
1100
1101
1102 } else if (expand_ipv6(addr, type)) {
1103 switch (type) {
1104 case IPADDR:
1105 snprintf(str, sizeof(str), "%s", "IPV6ADDR");
1106 break;
1107 case NETMASK:
1108 snprintf(str, sizeof(str), "%s", "IPV6NETMASK");
1109 break;
1110 case GATEWAY:
1111 snprintf(str, sizeof(str), "%s",
1112 "IPV6_DEFAULTGW");
1113 break;
1114 case DNS:
1115 snprintf(str, sizeof(str), "%s", "DNS");
1116 break;
1117 }
1118 if ((j != 0) || (type == DNS)) {
1119 if (type != DNS) {
1120 snprintf(sub_str, sizeof(sub_str),
1121 "_%d", j++);
1122 } else {
1123 snprintf(sub_str, sizeof(sub_str),
1124 "%d", ++i);
1125 }
1126 } else if (type == DNS) {
1127 snprintf(sub_str, sizeof(sub_str),
1128 "%d", ++i);
1129 }
1130 } else {
1131 return HV_INVALIDARG;
1132 }
1133
1134 error = kvp_write_file(f, str, sub_str, addr);
1135 if (error)
1136 return error;
1137 memset(addr, 0, sizeof(addr));
1138 }
1139
1140 return 0;
1141}
1142
1143static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
1144{
1145 int error = 0;
1146 char if_file[128];
1147 FILE *file;
1148 char cmd[512];
1149 char *mac_addr;
1150
1151 /*
1152 * Set the configuration for the specified interface with
1153 * the information provided. Since there is no standard
1154 * way to configure an interface, we will have an external
1155 * script that does the job of configuring the interface and
1156 * flushing the configuration.
1157 *
1158 * The parameters passed to this external script are:
1159 * 1. A configuration file that has the specified configuration.
1160 *
1161 * We will embed the name of the interface in the configuration
1162 * file: ifcfg-ethx (where ethx is the interface name).
1163 *
1164 * The information provided here may be more than what is needed
1165 * in a given distro to configure the interface and so are free
1166 * ignore information that may not be relevant.
1167 *
1168 * Here is the format of the ip configuration file:
1169 *
1170 * HWADDR=macaddr
1171 * IF_NAME=interface name
1172 * DHCP=yes (This is optional; if yes, DHCP is configured)
1173 *
1174 * IPADDR=ipaddr1
1175 * IPADDR_1=ipaddr2
1176 * IPADDR_x=ipaddry (where y = x + 1)
1177 *
1178 * NETMASK=netmask1
1179 * NETMASK_x=netmasky (where y = x + 1)
1180 *
1181 * GATEWAY=ipaddr1
1182 * GATEWAY_x=ipaddry (where y = x + 1)
1183 *
1184 * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
1185 *
1186 * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
1187 * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
1188 * IPV6NETMASK.
1189 *
1190 * The host can specify multiple ipv4 and ipv6 addresses to be
1191 * configured for the interface. Furthermore, the configuration
1192 * needs to be persistent. A subsequent GET call on the interface
1193 * is expected to return the configuration that is set via the SET
1194 * call.
1195 */
1196
1197 snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC,
1198 "hyperv/ifcfg-", if_name);
1199
1200 file = fopen(if_file, "w");
1201
1202 if (file == NULL) {
1203 syslog(LOG_ERR, "Failed to open config file");
1204 return HV_E_FAIL;
1205 }
1206
1207 /*
1208 * First write out the MAC address.
1209 */
1210
1211 mac_addr = kvp_if_name_to_mac(if_name);
1212 if (mac_addr == NULL) {
1213 error = HV_E_FAIL;
1214 goto setval_error;
1215 }
1216
1217 error = kvp_write_file(file, "HWADDR", "", mac_addr);
1218 if (error)
1219 goto setval_error;
1220
1221 error = kvp_write_file(file, "IF_NAME", "", if_name);
1222 if (error)
1223 goto setval_error;
1224
1225 if (new_val->dhcp_enabled) {
1226 error = kvp_write_file(file, "DHCP", "", "yes");
1227 if (error)
1228 goto setval_error;
1229
1230 /*
1231 * We are done!.
1232 */
1233 goto setval_done;
1234 }
1235
1236 /*
1237 * Write the configuration for ipaddress, netmask, gateway and
1238 * name servers.
1239 */
1240
1241 error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
1242 if (error)
1243 goto setval_error;
1244
1245 error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
1246 if (error)
1247 goto setval_error;
1248
1249 error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
1250 if (error)
1251 goto setval_error;
1252
1253 error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
1254 if (error)
1255 goto setval_error;
1256
1257setval_done:
1258 free(mac_addr);
1259 fclose(file);
1260
1261 /*
1262 * Now that we have populated the configuration file,
1263 * invoke the external script to do its magic.
1264 */
1265
1266 snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file);
1267 system(cmd);
1268 return 0;
1269
1270setval_error:
1271 syslog(LOG_ERR, "Failed to write config file");
1272 free(mac_addr);
1273 fclose(file);
1274 return error;
1275}
1276
1277
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001278static int
1279kvp_get_domain_name(char *buffer, int length)
1280{
1281 struct addrinfo hints, *info ;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001282 int error = 0;
1283
K. Y. Srinivasan5be528c2011-07-26 11:03:10 -07001284 gethostname(buffer, length);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001285 memset(&hints, 0, sizeof(hints));
1286 hints.ai_family = AF_INET; /*Get only ipv4 addrinfo. */
1287 hints.ai_socktype = SOCK_STREAM;
1288 hints.ai_flags = AI_CANONNAME;
1289
K. Y. Srinivasan5be528c2011-07-26 11:03:10 -07001290 error = getaddrinfo(buffer, NULL, &hints, &info);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001291 if (error != 0) {
1292 strcpy(buffer, "getaddrinfo failed\n");
K. Y. Srinivasan5be528c2011-07-26 11:03:10 -07001293 return error;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001294 }
1295 strcpy(buffer, info->ai_canonname);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001296 freeaddrinfo(info);
1297 return error;
1298}
1299
1300static int
1301netlink_send(int fd, struct cn_msg *msg)
1302{
1303 struct nlmsghdr *nlh;
1304 unsigned int size;
1305 struct msghdr message;
1306 char buffer[64];
1307 struct iovec iov[2];
1308
1309 size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
1310
1311 nlh = (struct nlmsghdr *)buffer;
1312 nlh->nlmsg_seq = 0;
1313 nlh->nlmsg_pid = getpid();
1314 nlh->nlmsg_type = NLMSG_DONE;
1315 nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
1316 nlh->nlmsg_flags = 0;
1317
1318 iov[0].iov_base = nlh;
1319 iov[0].iov_len = sizeof(*nlh);
1320
1321 iov[1].iov_base = msg;
1322 iov[1].iov_len = size;
1323
1324 memset(&message, 0, sizeof(message));
1325 message.msg_name = &addr;
1326 message.msg_namelen = sizeof(addr);
1327 message.msg_iov = iov;
1328 message.msg_iovlen = 2;
1329
1330 return sendmsg(fd, &message, 0);
1331}
1332
Olaf Hering7989f7d2011-03-22 10:02:17 +01001333int main(void)
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001334{
1335 int fd, len, sock_opt;
1336 int error;
1337 struct cn_msg *message;
1338 struct pollfd pfd;
1339 struct nlmsghdr *incoming_msg;
1340 struct cn_msg *incoming_cn_msg;
K. Y. Srinivasan2640335432012-02-02 16:56:50 -08001341 struct hv_kvp_msg *hv_msg;
Olaf Hering7989f7d2011-03-22 10:02:17 +01001342 char *p;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001343 char *key_value;
1344 char *key_name;
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001345 int op;
1346 int pool;
K. Y. Srinivasan32061b42012-09-05 13:50:13 -07001347 char *if_name;
1348 struct hv_kvp_ipaddr_value *kvp_ip_val;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001349
1350 daemon(1, 0);
1351 openlog("KVP", 0, LOG_USER);
1352 syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
1353 /*
1354 * Retrieve OS release information.
1355 */
1356 kvp_get_os_info();
1357
K. Y. Srinivasandb425332012-03-16 08:02:26 -07001358 if (kvp_file_init()) {
1359 syslog(LOG_ERR, "Failed to initialize the pools");
1360 exit(-1);
1361 }
1362
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001363 fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
1364 if (fd < 0) {
1365 syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
1366 exit(-1);
1367 }
1368 addr.nl_family = AF_NETLINK;
1369 addr.nl_pad = 0;
1370 addr.nl_pid = 0;
1371 addr.nl_groups = CN_KVP_IDX;
1372
1373
1374 error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
1375 if (error < 0) {
1376 syslog(LOG_ERR, "bind failed; error:%d", error);
1377 close(fd);
1378 exit(-1);
1379 }
1380 sock_opt = addr.nl_groups;
1381 setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
1382 /*
1383 * Register ourselves with the kernel.
1384 */
1385 message = (struct cn_msg *)kvp_send_buffer;
1386 message->id.idx = CN_KVP_IDX;
1387 message->id.val = CN_KVP_VAL;
K. Y. Srinivasan2640335432012-02-02 16:56:50 -08001388
1389 hv_msg = (struct hv_kvp_msg *)message->data;
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001390 hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001391 message->ack = 0;
K. Y. Srinivasan2640335432012-02-02 16:56:50 -08001392 message->len = sizeof(struct hv_kvp_msg);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001393
1394 len = netlink_send(fd, message);
1395 if (len < 0) {
1396 syslog(LOG_ERR, "netlink_send failed; error:%d", len);
1397 close(fd);
1398 exit(-1);
1399 }
1400
1401 pfd.fd = fd;
1402
1403 while (1) {
Olaf Heringbcc2c9c2012-05-31 16:40:06 +02001404 struct sockaddr *addr_p = (struct sockaddr *) &addr;
1405 socklen_t addr_l = sizeof(addr);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001406 pfd.events = POLLIN;
1407 pfd.revents = 0;
1408 poll(&pfd, 1, -1);
1409
Olaf Heringbcc2c9c2012-05-31 16:40:06 +02001410 len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0,
1411 addr_p, &addr_l);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001412
Olaf Heringbcc2c9c2012-05-31 16:40:06 +02001413 if (len < 0 || addr.nl_pid) {
1414 syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s",
1415 addr.nl_pid, errno, strerror(errno));
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001416 close(fd);
1417 return -1;
1418 }
1419
1420 incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
1421 incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
K. Y. Srinivasan2640335432012-02-02 16:56:50 -08001422 hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001423
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001424 /*
1425 * We will use the KVP header information to pass back
1426 * the error from this daemon. So, first copy the state
1427 * and set the error code to success.
1428 */
1429 op = hv_msg->kvp_hdr.operation;
1430 pool = hv_msg->kvp_hdr.pool;
1431 hv_msg->error = HV_S_OK;
1432
1433 if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) {
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001434 /*
1435 * Driver is registering with us; stash away the version
1436 * information.
1437 */
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001438 in_hand_shake = 0;
K. Y. Srinivasane485ceac2012-03-10 15:32:08 -08001439 p = (char *)hv_msg->body.kvp_register.version;
Olaf Hering7989f7d2011-03-22 10:02:17 +01001440 lic_version = malloc(strlen(p) + 1);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001441 if (lic_version) {
Olaf Hering7989f7d2011-03-22 10:02:17 +01001442 strcpy(lic_version, p);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001443 syslog(LOG_INFO, "KVP LIC Version: %s",
1444 lic_version);
1445 } else {
1446 syslog(LOG_ERR, "malloc failed");
1447 }
1448 continue;
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001449 }
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001450
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001451 switch (op) {
K. Y. Srinivasan16e87102012-09-05 13:50:15 -07001452 case KVP_OP_GET_IP_INFO:
1453 kvp_ip_val = &hv_msg->body.kvp_ip_val;
1454 if_name =
1455 kvp_mac_to_if_name((char *)kvp_ip_val->adapter_id);
1456
1457 if (if_name == NULL) {
1458 /*
1459 * We could not map the mac address to an
1460 * interface name; return error.
1461 */
1462 hv_msg->error = HV_E_FAIL;
1463 break;
1464 }
1465 error = kvp_get_ip_info(
1466 0, if_name, KVP_OP_GET_IP_INFO,
1467 kvp_ip_val,
1468 (MAX_IP_ADDR_SIZE * 2));
1469
1470 if (error)
1471 hv_msg->error = error;
1472
1473 free(if_name);
1474 break;
1475
K. Y. Srinivasan32061b42012-09-05 13:50:13 -07001476 case KVP_OP_SET_IP_INFO:
1477 kvp_ip_val = &hv_msg->body.kvp_ip_val;
1478 if_name = kvp_get_if_name(
1479 (char *)kvp_ip_val->adapter_id);
1480 if (if_name == NULL) {
1481 /*
1482 * We could not map the guid to an
1483 * interface name; return error.
1484 */
1485 hv_msg->error = HV_GUID_NOTFOUND;
1486 break;
1487 }
1488 error = kvp_set_ip_info(if_name, kvp_ip_val);
1489 if (error)
1490 hv_msg->error = error;
1491
1492 free(if_name);
1493 break;
1494
K. Y. Srinivasanfa3d5b82012-03-16 08:02:25 -07001495 case KVP_OP_SET:
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001496 if (kvp_key_add_or_modify(pool,
K. Y. Srinivasandb425332012-03-16 08:02:26 -07001497 hv_msg->body.kvp_set.data.key,
1498 hv_msg->body.kvp_set.data.key_size,
1499 hv_msg->body.kvp_set.data.value,
1500 hv_msg->body.kvp_set.data.value_size))
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001501 hv_msg->error = HV_S_CONT;
K. Y. Srinivasandb425332012-03-16 08:02:26 -07001502 break;
1503
K. Y. Srinivasanfa3d5b82012-03-16 08:02:25 -07001504 case KVP_OP_GET:
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001505 if (kvp_get_value(pool,
K. Y. Srinivasandb425332012-03-16 08:02:26 -07001506 hv_msg->body.kvp_set.data.key,
1507 hv_msg->body.kvp_set.data.key_size,
1508 hv_msg->body.kvp_set.data.value,
1509 hv_msg->body.kvp_set.data.value_size))
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001510 hv_msg->error = HV_S_CONT;
K. Y. Srinivasandb425332012-03-16 08:02:26 -07001511 break;
1512
K. Y. Srinivasanfa3d5b82012-03-16 08:02:25 -07001513 case KVP_OP_DELETE:
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001514 if (kvp_key_delete(pool,
K. Y. Srinivasandb425332012-03-16 08:02:26 -07001515 hv_msg->body.kvp_delete.key,
1516 hv_msg->body.kvp_delete.key_size))
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001517 hv_msg->error = HV_S_CONT;
K. Y. Srinivasandb425332012-03-16 08:02:26 -07001518 break;
1519
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001520 default:
K. Y. Srinivasan2640335432012-02-02 16:56:50 -08001521 break;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001522 }
1523
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001524 if (op != KVP_OP_ENUMERATE)
K. Y. Srinivasanfa3d5b82012-03-16 08:02:25 -07001525 goto kvp_done;
1526
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -07001527 /*
1528 * If the pool is KVP_POOL_AUTO, dynamically generate
1529 * both the key and the value; if not read from the
1530 * appropriate pool.
1531 */
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001532 if (pool != KVP_POOL_AUTO) {
1533 if (kvp_pool_enumerate(pool,
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -07001534 hv_msg->body.kvp_enum_data.index,
1535 hv_msg->body.kvp_enum_data.data.key,
1536 HV_KVP_EXCHANGE_MAX_KEY_SIZE,
1537 hv_msg->body.kvp_enum_data.data.value,
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001538 HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
1539 hv_msg->error = HV_S_CONT;
K. Y. Srinivasanadc80ae2012-03-16 08:02:27 -07001540 goto kvp_done;
1541 }
1542
K. Y. Srinivasan2640335432012-02-02 16:56:50 -08001543 hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
1544 key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
1545 key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001546
K. Y. Srinivasan2640335432012-02-02 16:56:50 -08001547 switch (hv_msg->body.kvp_enum_data.index) {
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001548 case FullyQualifiedDomainName:
1549 kvp_get_domain_name(key_value,
1550 HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
1551 strcpy(key_name, "FullyQualifiedDomainName");
1552 break;
1553 case IntegrationServicesVersion:
1554 strcpy(key_name, "IntegrationServicesVersion");
1555 strcpy(key_value, lic_version);
1556 break;
1557 case NetworkAddressIPv4:
K. Y. Srinivasan4a3b97e52012-09-05 13:50:14 -07001558 kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE,
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -07001559 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001560 strcpy(key_name, "NetworkAddressIPv4");
1561 break;
1562 case NetworkAddressIPv6:
K. Y. Srinivasan4a3b97e52012-09-05 13:50:14 -07001563 kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE,
K. Y. Srinivasan0ecaa192012-08-16 18:32:13 -07001564 key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001565 strcpy(key_name, "NetworkAddressIPv6");
1566 break;
1567 case OSBuildNumber:
1568 strcpy(key_value, os_build);
1569 strcpy(key_name, "OSBuildNumber");
1570 break;
1571 case OSName:
1572 strcpy(key_value, os_name);
1573 strcpy(key_name, "OSName");
1574 break;
1575 case OSMajorVersion:
1576 strcpy(key_value, os_major);
1577 strcpy(key_name, "OSMajorVersion");
1578 break;
1579 case OSMinorVersion:
1580 strcpy(key_value, os_minor);
1581 strcpy(key_name, "OSMinorVersion");
1582 break;
1583 case OSVersion:
1584 strcpy(key_value, os_build);
1585 strcpy(key_name, "OSVersion");
1586 break;
1587 case ProcessorArchitecture:
1588 strcpy(key_value, processor_arch);
1589 strcpy(key_name, "ProcessorArchitecture");
1590 break;
1591 default:
K. Y. Srinivasanb47a81d2012-08-13 10:06:52 -07001592 hv_msg->error = HV_S_CONT;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001593 break;
1594 }
1595 /*
1596 * Send the value back to the kernel. The response is
1597 * already in the receive buffer. Update the cn_msg header to
1598 * reflect the key value that has been added to the message
1599 */
K. Y. Srinivasanfa3d5b82012-03-16 08:02:25 -07001600kvp_done:
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001601
1602 incoming_cn_msg->id.idx = CN_KVP_IDX;
1603 incoming_cn_msg->id.val = CN_KVP_VAL;
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001604 incoming_cn_msg->ack = 0;
K. Y. Srinivasan2640335432012-02-02 16:56:50 -08001605 incoming_cn_msg->len = sizeof(struct hv_kvp_msg);
Ky Srinivasancc04acf2010-12-16 18:56:54 -07001606
1607 len = netlink_send(fd, incoming_cn_msg);
1608 if (len < 0) {
1609 syslog(LOG_ERR, "net_link send failed; error:%d", len);
1610 exit(-1);
1611 }
1612 }
1613
1614}