aboutsummaryrefslogtreecommitdiffstats
path: root/tools/hv
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2012-09-05 16:50:13 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-09-10 19:42:33 -0400
commit32061b4d3830e61975ede409df389804507fd220 (patch)
tree1729cfe9a6899e33a0f00b353fcbe38ef0b2769a /tools/hv
parent1fbdba4edd071daffbba1c172abb44bbe6a4344a (diff)
Tools: hv: Implement the KVP verb - KVP_OP_SET_IP_INFO
Implement the KVP verb - KVP_OP_SET_IP_INFO. This operation configures the specified interface based on the given configuration. Since configuring an interface is very distro specific, we invoke an external (Distro specific) script to configure the interface. Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'tools/hv')
-rw-r--r--tools/hv/hv_kvp_daemon.c443
1 files changed, 443 insertions, 0 deletions
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 6fb2c1c6c32e..ac144b9da0db 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -31,6 +31,7 @@
31#include <stdlib.h> 31#include <stdlib.h>
32#include <unistd.h> 32#include <unistd.h>
33#include <string.h> 33#include <string.h>
34#include <ctype.h>
34#include <errno.h> 35#include <errno.h>
35#include <arpa/inet.h> 36#include <arpa/inet.h>
36#include <linux/connector.h> 37#include <linux/connector.h>
@@ -41,6 +42,7 @@
41#include <syslog.h> 42#include <syslog.h>
42#include <sys/stat.h> 43#include <sys/stat.h>
43#include <fcntl.h> 44#include <fcntl.h>
45#include <dirent.h>
44 46
45/* 47/*
46 * KVP protocol: The user mode component first registers with the 48 * KVP protocol: The user mode component first registers with the
@@ -68,6 +70,14 @@ enum key_index {
68 ProcessorArchitecture 70 ProcessorArchitecture
69}; 71};
70 72
73
74enum {
75 IPADDR = 0,
76 NETMASK,
77 GATEWAY,
78 DNS
79};
80
71static char kvp_send_buffer[4096]; 81static char kvp_send_buffer[4096];
72static char kvp_recv_buffer[4096 * 2]; 82static char kvp_recv_buffer[4096 * 2];
73static struct sockaddr_nl addr; 83static struct sockaddr_nl addr;
@@ -81,6 +91,11 @@ static char *os_build;
81static char *lic_version = "Unknown version"; 91static char *lic_version = "Unknown version";
82static struct utsname uts_buf; 92static struct utsname uts_buf;
83 93
94/*
95 * The location of the interface configuration file.
96 */
97
98#define KVP_CONFIG_LOC "/var/opt/"
84 99
85#define MAX_FILE_NAME 100 100#define MAX_FILE_NAME 100
86#define ENTRIES_PER_BLOCK 50 101#define ENTRIES_PER_BLOCK 50
@@ -490,6 +505,104 @@ done:
490 return; 505 return;
491} 506}
492 507
508
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
493static void kvp_process_ipconfig_file(char *cmd, 606static void kvp_process_ipconfig_file(char *cmd,
494 char *config_buf, int len, 607 char *config_buf, int len,
495 int element_size, int offset) 608 int element_size, int offset)
@@ -790,6 +903,315 @@ getaddr_done:
790} 903}
791 904
792 905
906static int expand_ipv6(char *addr, int type)
907{
908 int ret;
909 struct in6_addr v6_addr;
910
911 ret = inet_pton(AF_INET6, addr, &v6_addr);
912
913 if (ret != 1) {
914 if (type == NETMASK)
915 return 1;
916 return 0;
917 }
918
919 sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
920 "%02x%02x:%02x%02x:%02x%02x",
921 (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1],
922 (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3],
923 (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5],
924 (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7],
925 (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9],
926 (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11],
927 (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13],
928 (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]);
929
930 return 1;
931
932}
933
934static int is_ipv4(char *addr)
935{
936 int ret;
937 struct in_addr ipv4_addr;
938
939 ret = inet_pton(AF_INET, addr, &ipv4_addr);
940
941 if (ret == 1)
942 return 1;
943 return 0;
944}
945
946static int parse_ip_val_buffer(char *in_buf, int *offset,
947 char *out_buf, int out_len)
948{
949 char *x;
950 char *start;
951
952 /*
953 * in_buf has sequence of characters that are seperated by
954 * the character ';'. The last sequence does not have the
955 * terminating ";" character.
956 */
957 start = in_buf + *offset;
958
959 x = strchr(start, ';');
960 if (x)
961 *x = 0;
962 else
963 x = start + strlen(start);
964
965 if (strlen(start) != 0) {
966 int i = 0;
967 /*
968 * Get rid of leading spaces.
969 */
970 while (start[i] == ' ')
971 i++;
972
973 if ((x - start) <= out_len) {
974 strcpy(out_buf, (start + i));
975 *offset += (x - start) + 1;
976 return 1;
977 }
978 }
979 return 0;
980}
981
982static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)
983{
984 int ret;
985
986 ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3);
987
988 if (ret < 0)
989 return HV_E_FAIL;
990
991 return 0;
992}
993
994
995static int process_ip_string(FILE *f, char *ip_string, int type)
996{
997 int error = 0;
998 char addr[INET6_ADDRSTRLEN];
999 int i = 0;
1000 int j = 0;
1001 char str[256];
1002 char sub_str[10];
1003 int offset = 0;
1004
1005 memset(addr, 0, sizeof(addr));
1006
1007 while (parse_ip_val_buffer(ip_string, &offset, addr,
1008 (MAX_IP_ADDR_SIZE * 2))) {
1009
1010 sub_str[0] = 0;
1011 if (is_ipv4(addr)) {
1012 switch (type) {
1013 case IPADDR:
1014 snprintf(str, sizeof(str), "%s", "IPADDR");
1015 break;
1016 case NETMASK:
1017 snprintf(str, sizeof(str), "%s", "NETMASK");
1018 break;
1019 case GATEWAY:
1020 snprintf(str, sizeof(str), "%s", "GATEWAY");
1021 break;
1022 case DNS:
1023 snprintf(str, sizeof(str), "%s", "DNS");
1024 break;
1025 }
1026 if (i != 0) {
1027 if (type != DNS) {
1028 snprintf(sub_str, sizeof(sub_str),
1029 "_%d", i++);
1030 } else {
1031 snprintf(sub_str, sizeof(sub_str),
1032 "%d", ++i);
1033 }
1034 } else if (type == DNS) {
1035 snprintf(sub_str, sizeof(sub_str), "%d", ++i);
1036 }
1037
1038
1039 } else if (expand_ipv6(addr, type)) {
1040 switch (type) {
1041 case IPADDR:
1042 snprintf(str, sizeof(str), "%s", "IPV6ADDR");
1043 break;
1044 case NETMASK:
1045 snprintf(str, sizeof(str), "%s", "IPV6NETMASK");
1046 break;
1047 case GATEWAY:
1048 snprintf(str, sizeof(str), "%s",
1049 "IPV6_DEFAULTGW");
1050 break;
1051 case DNS:
1052 snprintf(str, sizeof(str), "%s", "DNS");
1053 break;
1054 }
1055 if ((j != 0) || (type == DNS)) {
1056 if (type != DNS) {
1057 snprintf(sub_str, sizeof(sub_str),
1058 "_%d", j++);
1059 } else {
1060 snprintf(sub_str, sizeof(sub_str),
1061 "%d", ++i);
1062 }
1063 } else if (type == DNS) {
1064 snprintf(sub_str, sizeof(sub_str),
1065 "%d", ++i);
1066 }
1067 } else {
1068 return HV_INVALIDARG;
1069 }
1070
1071 error = kvp_write_file(f, str, sub_str, addr);
1072 if (error)
1073 return error;
1074 memset(addr, 0, sizeof(addr));
1075 }
1076
1077 return 0;
1078}
1079
1080static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
1081{
1082 int error = 0;
1083 char if_file[128];
1084 FILE *file;
1085 char cmd[512];
1086 char *mac_addr;
1087
1088 /*
1089 * Set the configuration for the specified interface with
1090 * the information provided. Since there is no standard
1091 * way to configure an interface, we will have an external
1092 * script that does the job of configuring the interface and
1093 * flushing the configuration.
1094 *
1095 * The parameters passed to this external script are:
1096 * 1. A configuration file that has the specified configuration.
1097 *
1098 * We will embed the name of the interface in the configuration
1099 * file: ifcfg-ethx (where ethx is the interface name).
1100 *
1101 * The information provided here may be more than what is needed
1102 * in a given distro to configure the interface and so are free
1103 * ignore information that may not be relevant.
1104 *
1105 * Here is the format of the ip configuration file:
1106 *
1107 * HWADDR=macaddr
1108 * IF_NAME=interface name
1109 * DHCP=yes (This is optional; if yes, DHCP is configured)
1110 *
1111 * IPADDR=ipaddr1
1112 * IPADDR_1=ipaddr2
1113 * IPADDR_x=ipaddry (where y = x + 1)
1114 *
1115 * NETMASK=netmask1
1116 * NETMASK_x=netmasky (where y = x + 1)
1117 *
1118 * GATEWAY=ipaddr1
1119 * GATEWAY_x=ipaddry (where y = x + 1)
1120 *
1121 * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
1122 *
1123 * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
1124 * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
1125 * IPV6NETMASK.
1126 *
1127 * The host can specify multiple ipv4 and ipv6 addresses to be
1128 * configured for the interface. Furthermore, the configuration
1129 * needs to be persistent. A subsequent GET call on the interface
1130 * is expected to return the configuration that is set via the SET
1131 * call.
1132 */
1133
1134 snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC,
1135 "hyperv/ifcfg-", if_name);
1136
1137 file = fopen(if_file, "w");
1138
1139 if (file == NULL) {
1140 syslog(LOG_ERR, "Failed to open config file");
1141 return HV_E_FAIL;
1142 }
1143
1144 /*
1145 * First write out the MAC address.
1146 */
1147
1148 mac_addr = kvp_if_name_to_mac(if_name);
1149 if (mac_addr == NULL) {
1150 error = HV_E_FAIL;
1151 goto setval_error;
1152 }
1153
1154 error = kvp_write_file(file, "HWADDR", "", mac_addr);
1155 if (error)
1156 goto setval_error;
1157
1158 error = kvp_write_file(file, "IF_NAME", "", if_name);
1159 if (error)
1160 goto setval_error;
1161
1162 if (new_val->dhcp_enabled) {
1163 error = kvp_write_file(file, "DHCP", "", "yes");
1164 if (error)
1165 goto setval_error;
1166
1167 /*
1168 * We are done!.
1169 */
1170 goto setval_done;
1171 }
1172
1173 /*
1174 * Write the configuration for ipaddress, netmask, gateway and
1175 * name servers.
1176 */
1177
1178 error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
1179 if (error)
1180 goto setval_error;
1181
1182 error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
1183 if (error)
1184 goto setval_error;
1185
1186 error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
1187 if (error)
1188 goto setval_error;
1189
1190 error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
1191 if (error)
1192 goto setval_error;
1193
1194setval_done:
1195 free(mac_addr);
1196 fclose(file);
1197
1198 /*
1199 * Now that we have populated the configuration file,
1200 * invoke the external script to do its magic.
1201 */
1202
1203 snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file);
1204 system(cmd);
1205 return 0;
1206
1207setval_error:
1208 syslog(LOG_ERR, "Failed to write config file");
1209 free(mac_addr);
1210 fclose(file);
1211 return error;
1212}
1213
1214
793static int 1215static int
794kvp_get_domain_name(char *buffer, int length) 1216kvp_get_domain_name(char *buffer, int length)
795{ 1217{
@@ -859,6 +1281,8 @@ int main(void)
859 char *key_name; 1281 char *key_name;
860 int op; 1282 int op;
861 int pool; 1283 int pool;
1284 char *if_name;
1285 struct hv_kvp_ipaddr_value *kvp_ip_val;
862 1286
863 daemon(1, 0); 1287 daemon(1, 0);
864 openlog("KVP", 0, LOG_USER); 1288 openlog("KVP", 0, LOG_USER);
@@ -962,6 +1386,25 @@ int main(void)
962 } 1386 }
963 1387
964 switch (op) { 1388 switch (op) {
1389 case KVP_OP_SET_IP_INFO:
1390 kvp_ip_val = &hv_msg->body.kvp_ip_val;
1391 if_name = kvp_get_if_name(
1392 (char *)kvp_ip_val->adapter_id);
1393 if (if_name == NULL) {
1394 /*
1395 * We could not map the guid to an
1396 * interface name; return error.
1397 */
1398 hv_msg->error = HV_GUID_NOTFOUND;
1399 break;
1400 }
1401 error = kvp_set_ip_info(if_name, kvp_ip_val);
1402 if (error)
1403 hv_msg->error = error;
1404
1405 free(if_name);
1406 break;
1407
965 case KVP_OP_SET: 1408 case KVP_OP_SET:
966 if (kvp_key_add_or_modify(pool, 1409 if (kvp_key_add_or_modify(pool,
967 hv_msg->body.kvp_set.data.key, 1410 hv_msg->body.kvp_set.data.key,