Commit 32061b4d3830e61975ede409df389804507fd220

Authored by K. Y. Srinivasan
Committed by Greg Kroah-Hartman
1 parent 1fbdba4edd

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>

Showing 2 changed files with 445 additions and 0 deletions Side-by-side Diff

include/linux/hyperv.h
... ... @@ -191,6 +191,8 @@
191 191 #define HV_ERROR_NOT_SUPPORTED 0x80070032
192 192 #define HV_ERROR_MACHINE_LOCKED 0x800704F7
193 193 #define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F
  194 +#define HV_INVALIDARG 0x80070057
  195 +#define HV_GUID_NOTFOUND 0x80041002
194 196  
195 197 #define ADDR_FAMILY_NONE 0x00
196 198 #define ADDR_FAMILY_IPV4 0x01
tools/hv/hv_kvp_daemon.c
... ... @@ -31,6 +31,7 @@
31 31 #include <stdlib.h>
32 32 #include <unistd.h>
33 33 #include <string.h>
  34 +#include <ctype.h>
34 35 #include <errno.h>
35 36 #include <arpa/inet.h>
36 37 #include <linux/connector.h>
... ... @@ -41,6 +42,7 @@
41 42 #include <syslog.h>
42 43 #include <sys/stat.h>
43 44 #include <fcntl.h>
  45 +#include <dirent.h>
44 46  
45 47 /*
46 48 * KVP protocol: The user mode component first registers with the
... ... @@ -68,6 +70,14 @@
68 70 ProcessorArchitecture
69 71 };
70 72  
  73 +
  74 +enum {
  75 + IPADDR = 0,
  76 + NETMASK,
  77 + GATEWAY,
  78 + DNS
  79 +};
  80 +
71 81 static char kvp_send_buffer[4096];
72 82 static char kvp_recv_buffer[4096 * 2];
73 83 static struct sockaddr_nl addr;
74 84  
... ... @@ -81,7 +91,12 @@
81 91 static char *lic_version = "Unknown version";
82 92 static struct utsname uts_buf;
83 93  
  94 +/*
  95 + * The location of the interface configuration file.
  96 + */
84 97  
  98 +#define KVP_CONFIG_LOC "/var/opt/"
  99 +
85 100 #define MAX_FILE_NAME 100
86 101 #define ENTRIES_PER_BLOCK 50
87 102  
... ... @@ -490,6 +505,104 @@
490 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 +
  518 +static 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 +
  575 +static 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 +
493 606 static void kvp_process_ipconfig_file(char *cmd,
494 607 char *config_buf, int len,
495 608 int element_size, int offset)
... ... @@ -790,6 +903,315 @@
790 903 }
791 904  
792 905  
  906 +static 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 +
  934 +static 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 +
  946 +static 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 +
  982 +static 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 +
  995 +static 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 +
  1080 +static 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 +
  1194 +setval_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 +
  1207 +setval_error:
  1208 + syslog(LOG_ERR, "Failed to write config file");
  1209 + free(mac_addr);
  1210 + fclose(file);
  1211 + return error;
  1212 +}
  1213 +
  1214 +
793 1215 static int
794 1216 kvp_get_domain_name(char *buffer, int length)
795 1217 {
... ... @@ -859,6 +1281,8 @@
859 1281 char *key_name;
860 1282 int op;
861 1283 int pool;
  1284 + char *if_name;
  1285 + struct hv_kvp_ipaddr_value *kvp_ip_val;
862 1286  
863 1287 daemon(1, 0);
864 1288 openlog("KVP", 0, LOG_USER);
... ... @@ -962,6 +1386,25 @@
962 1386 }
963 1387  
964 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 1408 case KVP_OP_SET:
966 1409 if (kvp_key_add_or_modify(pool,
967 1410 hv_msg->body.kvp_set.data.key,