Skip to content

Commit 42999c9

Browse files
Shradha Guptaliuw
authored andcommitted
hv/hv_kvp_daemon:Support for keyfile based connection profile
Ifcfg config file support in NetworkManger is deprecated. This patch provides support for the new keyfile config format for connection profiles in NetworkManager. The patch modifies the hv_kvp_daemon code to generate the new network configuration in keyfile format(.ini-style format) along with a ifcfg format configuration. The ifcfg format configuration is also retained to support easy backward compatibility for distro vendors. These configurations are stored in temp files which are further translated using the hv_set_ifconfig.sh script. This script is implemented by individual distros based on the network management commands supported. For example, RHEL's implementation could be found here: https://gitlab.com/redhat/centos-stream/src/hyperv-daemons/-/blob/c9s/hv_set_ifconfig.sh Debian's implementation could be found here: https://github.com/endlessm/linux/blob/master/debian/cloud-tools/hv_set_ifconfig The next part of this support is to let the Distro vendors consume these modified implementations to the new configuration format. Tested-on: Rhel9(Hyper-V, Azure)(nm and ifcfg files verified) Signed-off-by: Shradha Gupta <[email protected]> Reviewed-by: Saurabh Sengar <[email protected]> Reviewed-by: Ani Sinha <[email protected]> Signed-off-by: Wei Liu <[email protected]> Link: https://lore.kernel.org/r/1696847920-31125-1-git-send-email-shradhagupta@linux.microsoft.com
1 parent cfc7461 commit 42999c9

File tree

2 files changed

+235
-37
lines changed

2 files changed

+235
-37
lines changed

tools/hv/hv_kvp_daemon.c

Lines changed: 202 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,12 +1171,79 @@ static int process_ip_string(FILE *f, char *ip_string, int type)
11711171
return 0;
11721172
}
11731173

1174+
/*
1175+
* Only IPv4 subnet strings needs to be converted to plen
1176+
* For IPv6 the subnet is already privided in plen format
1177+
*/
1178+
static int kvp_subnet_to_plen(char *subnet_addr_str)
1179+
{
1180+
int plen = 0;
1181+
struct in_addr subnet_addr4;
1182+
1183+
/*
1184+
* Convert subnet address to binary representation
1185+
*/
1186+
if (inet_pton(AF_INET, subnet_addr_str, &subnet_addr4) == 1) {
1187+
uint32_t subnet_mask = ntohl(subnet_addr4.s_addr);
1188+
1189+
while (subnet_mask & 0x80000000) {
1190+
plen++;
1191+
subnet_mask <<= 1;
1192+
}
1193+
} else {
1194+
return -1;
1195+
}
1196+
1197+
return plen;
1198+
}
1199+
1200+
static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet,
1201+
int is_ipv6)
1202+
{
1203+
char addr[INET6_ADDRSTRLEN];
1204+
char subnet_addr[INET6_ADDRSTRLEN];
1205+
int error, i = 0;
1206+
int ip_offset = 0, subnet_offset = 0;
1207+
int plen;
1208+
1209+
memset(addr, 0, sizeof(addr));
1210+
memset(subnet_addr, 0, sizeof(subnet_addr));
1211+
1212+
while (parse_ip_val_buffer(ip_string, &ip_offset, addr,
1213+
(MAX_IP_ADDR_SIZE * 2)) &&
1214+
parse_ip_val_buffer(subnet,
1215+
&subnet_offset,
1216+
subnet_addr,
1217+
(MAX_IP_ADDR_SIZE *
1218+
2))) {
1219+
if (!is_ipv6)
1220+
plen = kvp_subnet_to_plen((char *)subnet_addr);
1221+
else
1222+
plen = atoi(subnet_addr);
1223+
1224+
if (plen < 0)
1225+
return plen;
1226+
1227+
error = fprintf(f, "address%d=%s/%d\n", ++i, (char *)addr,
1228+
plen);
1229+
if (error < 0)
1230+
return error;
1231+
1232+
memset(addr, 0, sizeof(addr));
1233+
memset(subnet_addr, 0, sizeof(subnet_addr));
1234+
}
1235+
1236+
return 0;
1237+
}
1238+
11741239
static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
11751240
{
11761241
int error = 0;
1177-
char if_file[PATH_MAX];
1178-
FILE *file;
1242+
char if_filename[PATH_MAX];
1243+
char nm_filename[PATH_MAX];
1244+
FILE *ifcfg_file, *nmfile;
11791245
char cmd[PATH_MAX];
1246+
int is_ipv6 = 0;
11801247
char *mac_addr;
11811248
int str_len;
11821249

@@ -1197,7 +1264,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
11971264
* in a given distro to configure the interface and so are free
11981265
* ignore information that may not be relevant.
11991266
*
1200-
* Here is the format of the ip configuration file:
1267+
* Here is the ifcfg format of the ip configuration file:
12011268
*
12021269
* HWADDR=macaddr
12031270
* DEVICE=interface name
@@ -1220,21 +1287,62 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
12201287
* tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
12211288
* IPV6NETMASK.
12221289
*
1290+
* Here is the keyfile format of the ip configuration file:
1291+
*
1292+
* [ethernet]
1293+
* mac-address=macaddr
1294+
* [connection]
1295+
* interface-name=interface name
1296+
*
1297+
* [ipv4]
1298+
* method=<protocol> (where <protocol> is "auto" if DHCP is configured
1299+
* or "manual" if no boot-time protocol should be used)
1300+
*
1301+
* address1=ipaddr1/plen
1302+
* address2=ipaddr2/plen
1303+
*
1304+
* gateway=gateway1;gateway2
1305+
*
1306+
* dns=dns1;dns2
1307+
*
1308+
* [ipv6]
1309+
* address1=ipaddr1/plen
1310+
* address2=ipaddr2/plen
1311+
*
1312+
* gateway=gateway1;gateway2
1313+
*
1314+
* dns=dns1;dns2
1315+
*
12231316
* The host can specify multiple ipv4 and ipv6 addresses to be
12241317
* configured for the interface. Furthermore, the configuration
12251318
* needs to be persistent. A subsequent GET call on the interface
12261319
* is expected to return the configuration that is set via the SET
12271320
* call.
12281321
*/
12291322

1230-
snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC,
1231-
"/ifcfg-", if_name);
1323+
/*
1324+
* We are populating both ifcfg and nmconnection files
1325+
*/
1326+
snprintf(if_filename, sizeof(if_filename), "%s%s%s", KVP_CONFIG_LOC,
1327+
"/ifcfg-", if_name);
12321328

1233-
file = fopen(if_file, "w");
1329+
ifcfg_file = fopen(if_filename, "w");
12341330

1235-
if (file == NULL) {
1331+
if (!ifcfg_file) {
12361332
syslog(LOG_ERR, "Failed to open config file; error: %d %s",
1237-
errno, strerror(errno));
1333+
errno, strerror(errno));
1334+
return HV_E_FAIL;
1335+
}
1336+
1337+
snprintf(nm_filename, sizeof(nm_filename), "%s%s%s%s", KVP_CONFIG_LOC,
1338+
"/", if_name, ".nmconnection");
1339+
1340+
nmfile = fopen(nm_filename, "w");
1341+
1342+
if (!nmfile) {
1343+
syslog(LOG_ERR, "Failed to open config file; error: %d %s",
1344+
errno, strerror(errno));
1345+
fclose(ifcfg_file);
12381346
return HV_E_FAIL;
12391347
}
12401348

@@ -1248,62 +1356,123 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
12481356
goto setval_error;
12491357
}
12501358

1251-
error = kvp_write_file(file, "HWADDR", "", mac_addr);
1252-
free(mac_addr);
1359+
error = kvp_write_file(ifcfg_file, "HWADDR", "", mac_addr);
1360+
if (error < 0)
1361+
goto setmac_error;
1362+
1363+
error = kvp_write_file(ifcfg_file, "DEVICE", "", if_name);
1364+
if (error < 0)
1365+
goto setmac_error;
1366+
1367+
error = fprintf(nmfile, "\n[connection]\n");
1368+
if (error < 0)
1369+
goto setmac_error;
1370+
1371+
error = kvp_write_file(nmfile, "interface-name", "", if_name);
12531372
if (error)
1254-
goto setval_error;
1373+
goto setmac_error;
12551374

1256-
error = kvp_write_file(file, "DEVICE", "", if_name);
1375+
error = fprintf(nmfile, "\n[ethernet]\n");
1376+
if (error < 0)
1377+
goto setmac_error;
1378+
1379+
error = kvp_write_file(nmfile, "mac-address", "", mac_addr);
12571380
if (error)
1258-
goto setval_error;
1381+
goto setmac_error;
1382+
1383+
free(mac_addr);
12591384

12601385
/*
12611386
* The dhcp_enabled flag is only for IPv4. In the case the host only
12621387
* injects an IPv6 address, the flag is true, but we still need to
12631388
* proceed to parse and pass the IPv6 information to the
12641389
* disto-specific script hv_set_ifconfig.
12651390
*/
1391+
1392+
/*
1393+
* First populate the ifcfg file format
1394+
*/
12661395
if (new_val->dhcp_enabled) {
1267-
error = kvp_write_file(file, "BOOTPROTO", "", "dhcp");
1396+
error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "dhcp");
12681397
if (error)
12691398
goto setval_error;
1270-
12711399
} else {
1272-
error = kvp_write_file(file, "BOOTPROTO", "", "none");
1400+
error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "none");
12731401
if (error)
12741402
goto setval_error;
12751403
}
12761404

1277-
/*
1278-
* Write the configuration for ipaddress, netmask, gateway and
1279-
* name servers.
1280-
*/
1281-
1282-
error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
1405+
error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr,
1406+
IPADDR);
12831407
if (error)
12841408
goto setval_error;
12851409

1286-
error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
1410+
error = process_ip_string(ifcfg_file, (char *)new_val->sub_net,
1411+
NETMASK);
12871412
if (error)
12881413
goto setval_error;
12891414

1290-
error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
1415+
error = process_ip_string(ifcfg_file, (char *)new_val->gate_way,
1416+
GATEWAY);
12911417
if (error)
12921418
goto setval_error;
12931419

1294-
error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
1420+
error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS);
12951421
if (error)
12961422
goto setval_error;
12971423

1298-
fclose(file);
1424+
if (new_val->addr_family == ADDR_FAMILY_IPV6) {
1425+
error = fprintf(nmfile, "\n[ipv6]\n");
1426+
if (error < 0)
1427+
goto setval_error;
1428+
is_ipv6 = 1;
1429+
} else {
1430+
error = fprintf(nmfile, "\n[ipv4]\n");
1431+
if (error < 0)
1432+
goto setval_error;
1433+
}
1434+
1435+
/*
1436+
* Now we populate the keyfile format
1437+
*/
1438+
1439+
if (new_val->dhcp_enabled) {
1440+
error = kvp_write_file(nmfile, "method", "", "auto");
1441+
if (error < 0)
1442+
goto setval_error;
1443+
} else {
1444+
error = kvp_write_file(nmfile, "method", "", "manual");
1445+
if (error < 0)
1446+
goto setval_error;
1447+
}
1448+
1449+
/*
1450+
* Write the configuration for ipaddress, netmask, gateway and
1451+
* name services
1452+
*/
1453+
error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr,
1454+
(char *)new_val->sub_net, is_ipv6);
1455+
if (error < 0)
1456+
goto setval_error;
1457+
1458+
error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way);
1459+
if (error < 0)
1460+
goto setval_error;
1461+
1462+
error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr);
1463+
if (error < 0)
1464+
goto setval_error;
1465+
1466+
fclose(nmfile);
1467+
fclose(ifcfg_file);
12991468

13001469
/*
13011470
* Now that we have populated the configuration file,
13021471
* invoke the external script to do its magic.
13031472
*/
13041473

1305-
str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s",
1306-
"hv_set_ifconfig", if_file);
1474+
str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s %s",
1475+
"hv_set_ifconfig", if_filename, nm_filename);
13071476
/*
13081477
* This is a little overcautious, but it's necessary to suppress some
13091478
* false warnings from gcc 8.0.1.
@@ -1316,14 +1485,16 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
13161485

13171486
if (system(cmd)) {
13181487
syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s",
1319-
cmd, errno, strerror(errno));
1488+
cmd, errno, strerror(errno));
13201489
return HV_E_FAIL;
13211490
}
13221491
return 0;
1323-
1492+
setmac_error:
1493+
free(mac_addr);
13241494
setval_error:
13251495
syslog(LOG_ERR, "Failed to write config file");
1326-
fclose(file);
1496+
fclose(ifcfg_file);
1497+
fclose(nmfile);
13271498
return error;
13281499
}
13291500

tools/hv/hv_set_ifconfig.sh

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
#
1919
# This example script is based on a RHEL environment.
2020
#
21-
# Here is the format of the ip configuration file:
21+
# Here is the ifcfg format of the ip configuration file:
2222
#
2323
# HWADDR=macaddr
2424
# DEVICE=interface name
2525
# BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured
26-
# or "none" if no boot-time protocol should be used)
26+
# or "none" if no boot-time protocol should be used)
2727
#
2828
# IPADDR0=ipaddr1
2929
# IPADDR1=ipaddr2
@@ -41,25 +41,52 @@
4141
# tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
4242
# IPV6NETMASK.
4343
#
44+
# Here is the keyfile format of the ip configuration file:
45+
#
46+
# [ethernet]
47+
# mac-address=macaddr
48+
# [connection]
49+
# interface-name=interface name
50+
#
51+
# [ipv4]
52+
# method=<protocol> (where <protocol> is "auto" if DHCP is configured
53+
# or "manual" if no boot-time protocol should be used)
54+
#
55+
# address1=ipaddr1/plen
56+
# address=ipaddr2/plen
57+
#
58+
# gateway=gateway1;gateway2
59+
#
60+
# dns=dns1;
61+
#
62+
# [ipv6]
63+
# address1=ipaddr1/plen
64+
# address2=ipaddr1/plen
65+
#
66+
# gateway=gateway1;gateway2
67+
#
68+
# dns=dns1;dns2
69+
#
4470
# The host can specify multiple ipv4 and ipv6 addresses to be
4571
# configured for the interface. Furthermore, the configuration
4672
# needs to be persistent. A subsequent GET call on the interface
4773
# is expected to return the configuration that is set via the SET
4874
# call.
4975
#
5076

51-
52-
5377
echo "IPV6INIT=yes" >> $1
5478
echo "NM_CONTROLLED=no" >> $1
5579
echo "PEERDNS=yes" >> $1
5680
echo "ONBOOT=yes" >> $1
5781

58-
5982
cp $1 /etc/sysconfig/network-scripts/
6083

84+
chmod 600 $2
85+
interface=$(echo $2 | awk -F - '{ print $2 }')
86+
filename="${2##*/}"
87+
88+
sed '/\[connection\]/a autoconnect=true' $2 > /etc/NetworkManager/system-connections/${filename}
6189

62-
interface=$(echo $1 | awk -F - '{ print $2 }')
6390

6491
/sbin/ifdown $interface 2>/dev/null
6592
/sbin/ifup $interface 2>/dev/null

0 commit comments

Comments
 (0)