Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hv/hv_kvp_daemon: Handle IPv4 and Ipv6 combination for keyfile format

If the network configuration strings are passed as a combination of IPv4
and IPv6 addresses, the current KVP daemon does not handle processing for
the keyfile configuration format.
With these changes, the keyfile config generation logic scans through the
list twice to generate IPv4 and IPv6 sections for the configuration files
to handle this support.

Testcases ran:Rhel 9, Hyper-V VMs
(IPv4 only, IPv6 only, IPv4 and IPv6 combination)

Co-developed-by: Ani Sinha <anisinha@redhat.com>
Signed-off-by: Ani Sinha <anisinha@redhat.com>
Signed-off-by: Shradha Gupta <shradhagupta@linux.microsoft.com>
Reviewed-by: Easwar Hariharan <eahariha@linux.microsoft.com>
Tested-by: Ani Sinha <anisinha@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Link: https://lore.kernel.org/r/1711115162-11629-1-git-send-email-shradhagupta@linux.microsoft.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>
Message-ID: <1711115162-11629-1-git-send-email-shradhagupta@linux.microsoft.com>

authored by

Shradha Gupta and committed by
Wei Liu
f971f6dd d9ea7a3f

+173 -42
+173 -42
tools/hv/hv_kvp_daemon.c
··· 76 76 DNS 77 77 }; 78 78 79 + enum { 80 + IPV4 = 1, 81 + IPV6, 82 + IP_TYPE_MAX 83 + }; 84 + 79 85 static int in_hand_shake; 80 86 81 87 static char *os_name = ""; ··· 108 102 109 103 #define MAX_FILE_NAME 100 110 104 #define ENTRIES_PER_BLOCK 50 105 + /* 106 + * Change this entry if the number of addresses increases in future 107 + */ 108 + #define MAX_IP_ENTRIES 64 109 + #define OUTSTR_BUF_SIZE ((INET6_ADDRSTRLEN + 1) * MAX_IP_ENTRIES) 111 110 112 111 struct kvp_record { 113 112 char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; ··· 1182 1171 return 0; 1183 1172 } 1184 1173 1174 + int ip_version_check(const char *input_addr) 1175 + { 1176 + struct in6_addr addr; 1177 + 1178 + if (inet_pton(AF_INET, input_addr, &addr)) 1179 + return IPV4; 1180 + else if (inet_pton(AF_INET6, input_addr, &addr)) 1181 + return IPV6; 1182 + 1183 + return -EINVAL; 1184 + } 1185 + 1185 1186 /* 1186 1187 * Only IPv4 subnet strings needs to be converted to plen 1187 1188 * For IPv6 the subnet is already privided in plen format ··· 1220 1197 return plen; 1221 1198 } 1222 1199 1200 + static int process_dns_gateway_nm(FILE *f, char *ip_string, int type, 1201 + int ip_sec) 1202 + { 1203 + char addr[INET6_ADDRSTRLEN], *output_str; 1204 + int ip_offset = 0, error = 0, ip_ver; 1205 + char *param_name; 1206 + 1207 + if (type == DNS) 1208 + param_name = "dns"; 1209 + else if (type == GATEWAY) 1210 + param_name = "gateway"; 1211 + else 1212 + return -EINVAL; 1213 + 1214 + output_str = (char *)calloc(OUTSTR_BUF_SIZE, sizeof(char)); 1215 + if (!output_str) 1216 + return -ENOMEM; 1217 + 1218 + while (1) { 1219 + memset(addr, 0, sizeof(addr)); 1220 + 1221 + if (!parse_ip_val_buffer(ip_string, &ip_offset, addr, 1222 + (MAX_IP_ADDR_SIZE * 2))) 1223 + break; 1224 + 1225 + ip_ver = ip_version_check(addr); 1226 + if (ip_ver < 0) 1227 + continue; 1228 + 1229 + if ((ip_ver == IPV4 && ip_sec == IPV4) || 1230 + (ip_ver == IPV6 && ip_sec == IPV6)) { 1231 + /* 1232 + * do a bound check to avoid out-of bound writes 1233 + */ 1234 + if ((OUTSTR_BUF_SIZE - strlen(output_str)) > 1235 + (strlen(addr) + 1)) { 1236 + strncat(output_str, addr, 1237 + OUTSTR_BUF_SIZE - 1238 + strlen(output_str) - 1); 1239 + strncat(output_str, ",", 1240 + OUTSTR_BUF_SIZE - 1241 + strlen(output_str) - 1); 1242 + } 1243 + } else { 1244 + continue; 1245 + } 1246 + } 1247 + 1248 + if (strlen(output_str)) { 1249 + /* 1250 + * This is to get rid of that extra comma character 1251 + * in the end of the string 1252 + */ 1253 + output_str[strlen(output_str) - 1] = '\0'; 1254 + error = fprintf(f, "%s=%s\n", param_name, output_str); 1255 + } 1256 + 1257 + free(output_str); 1258 + return error; 1259 + } 1260 + 1223 1261 static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet, 1224 - int is_ipv6) 1262 + int ip_sec) 1225 1263 { 1226 1264 char addr[INET6_ADDRSTRLEN]; 1227 1265 char subnet_addr[INET6_ADDRSTRLEN]; 1228 - int error, i = 0; 1266 + int error = 0, i = 0; 1229 1267 int ip_offset = 0, subnet_offset = 0; 1230 - int plen; 1268 + int plen, ip_ver; 1231 1269 1232 1270 memset(addr, 0, sizeof(addr)); 1233 1271 memset(subnet_addr, 0, sizeof(subnet_addr)); ··· 1300 1216 subnet_addr, 1301 1217 (MAX_IP_ADDR_SIZE * 1302 1218 2))) { 1303 - if (!is_ipv6) 1219 + ip_ver = ip_version_check(addr); 1220 + if (ip_ver < 0) 1221 + continue; 1222 + 1223 + if (ip_ver == IPV4 && ip_sec == IPV4) 1304 1224 plen = kvp_subnet_to_plen((char *)subnet_addr); 1305 - else 1225 + else if (ip_ver == IPV6 && ip_sec == IPV6) 1306 1226 plen = atoi(subnet_addr); 1227 + else 1228 + continue; 1307 1229 1308 1230 if (plen < 0) 1309 1231 return plen; ··· 1323 1233 memset(subnet_addr, 0, sizeof(subnet_addr)); 1324 1234 } 1325 1235 1326 - return 0; 1236 + return error; 1327 1237 } 1328 1238 1329 1239 static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) 1330 1240 { 1331 - int error = 0; 1241 + int error = 0, ip_ver; 1332 1242 char if_filename[PATH_MAX]; 1333 1243 char nm_filename[PATH_MAX]; 1334 1244 FILE *ifcfg_file, *nmfile; 1335 1245 char cmd[PATH_MAX]; 1336 - int is_ipv6 = 0; 1337 1246 char *mac_addr; 1338 1247 int str_len; 1339 1248 ··· 1510 1421 if (error) 1511 1422 goto setval_error; 1512 1423 1513 - if (new_val->addr_family & ADDR_FAMILY_IPV6) { 1514 - error = fprintf(nmfile, "\n[ipv6]\n"); 1515 - if (error < 0) 1516 - goto setval_error; 1517 - is_ipv6 = 1; 1518 - } else { 1519 - error = fprintf(nmfile, "\n[ipv4]\n"); 1520 - if (error < 0) 1521 - goto setval_error; 1522 - } 1523 - 1524 1424 /* 1525 1425 * Now we populate the keyfile format 1426 + * 1427 + * The keyfile format expects the IPv6 and IPv4 configuration in 1428 + * different sections. Therefore we iterate through the list twice, 1429 + * once to populate the IPv4 section and the next time for IPv6 1526 1430 */ 1431 + ip_ver = IPV4; 1432 + do { 1433 + if (ip_ver == IPV4) { 1434 + error = fprintf(nmfile, "\n[ipv4]\n"); 1435 + if (error < 0) 1436 + goto setval_error; 1437 + } else { 1438 + error = fprintf(nmfile, "\n[ipv6]\n"); 1439 + if (error < 0) 1440 + goto setval_error; 1441 + } 1527 1442 1528 - if (new_val->dhcp_enabled) { 1529 - error = kvp_write_file(nmfile, "method", "", "auto"); 1443 + /* 1444 + * Write the configuration for ipaddress, netmask, gateway and 1445 + * name services 1446 + */ 1447 + error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr, 1448 + (char *)new_val->sub_net, 1449 + ip_ver); 1530 1450 if (error < 0) 1531 1451 goto setval_error; 1532 - } else { 1533 - error = kvp_write_file(nmfile, "method", "", "manual"); 1452 + 1453 + /* 1454 + * As dhcp_enabled is only valid for ipv4, we do not set dhcp 1455 + * methods for ipv6 based on dhcp_enabled flag. 1456 + * 1457 + * For ipv4, set method to manual only when dhcp_enabled is 1458 + * false and specific ipv4 addresses are configured. If neither 1459 + * dhcp_enabled is true and no ipv4 addresses are configured, 1460 + * set method to 'disabled'. 1461 + * 1462 + * For ipv6, set method to manual when we configure ipv6 1463 + * addresses. Otherwise set method to 'auto' so that SLAAC from 1464 + * RA may be used. 1465 + */ 1466 + if (ip_ver == IPV4) { 1467 + if (new_val->dhcp_enabled) { 1468 + error = kvp_write_file(nmfile, "method", "", 1469 + "auto"); 1470 + if (error < 0) 1471 + goto setval_error; 1472 + } else if (error) { 1473 + error = kvp_write_file(nmfile, "method", "", 1474 + "manual"); 1475 + if (error < 0) 1476 + goto setval_error; 1477 + } else { 1478 + error = kvp_write_file(nmfile, "method", "", 1479 + "disabled"); 1480 + if (error < 0) 1481 + goto setval_error; 1482 + } 1483 + } else if (ip_ver == IPV6) { 1484 + if (error) { 1485 + error = kvp_write_file(nmfile, "method", "", 1486 + "manual"); 1487 + if (error < 0) 1488 + goto setval_error; 1489 + } else { 1490 + error = kvp_write_file(nmfile, "method", "", 1491 + "auto"); 1492 + if (error < 0) 1493 + goto setval_error; 1494 + } 1495 + } 1496 + 1497 + error = process_dns_gateway_nm(nmfile, 1498 + (char *)new_val->gate_way, 1499 + GATEWAY, ip_ver); 1534 1500 if (error < 0) 1535 1501 goto setval_error; 1536 - } 1537 1502 1538 - /* 1539 - * Write the configuration for ipaddress, netmask, gateway and 1540 - * name services 1541 - */ 1542 - error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr, 1543 - (char *)new_val->sub_net, is_ipv6); 1544 - if (error < 0) 1545 - goto setval_error; 1546 - 1547 - /* we do not want ipv4 addresses in ipv6 section and vice versa */ 1548 - if (is_ipv6 != is_ipv4((char *)new_val->gate_way)) { 1549 - error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way); 1503 + error = process_dns_gateway_nm(nmfile, 1504 + (char *)new_val->dns_addr, DNS, 1505 + ip_ver); 1550 1506 if (error < 0) 1551 1507 goto setval_error; 1552 - } 1553 1508 1554 - if (is_ipv6 != is_ipv4((char *)new_val->dns_addr)) { 1555 - error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr); 1556 - if (error < 0) 1557 - goto setval_error; 1558 - } 1509 + ip_ver++; 1510 + } while (ip_ver < IP_TYPE_MAX); 1511 + 1559 1512 fclose(nmfile); 1560 1513 fclose(ifcfg_file); 1561 1514