lguest: Support assigning a MAC address

If you've got a nice DHCP configuration which maps MAC
addresses to specific IP addresses, then you're going to
want to start your guest with one of those MAC addresses.

Also, in Fedora, we have persistent network interface naming
based on the MAC address, so with randomly assigned
addresses you're soon going to hit eth13. Who knows what
will happen then!

Allow assigning a MAC address to the network interface with
e.g.

--tunnet=bridge:eth0:00:FF:95:6B:DA:3D

or:

--tunnet=192.168.121.1:00:FF:95:6B:DA:3D

which is pretty unintelligable, but ...

(includes Rusty's minor rework)

Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>

authored by

Mark McLoughlin and committed by
Rusty Russell
dec6a2be 34bdaab4

+89 -33
+89 -33
Documentation/lguest/lguest.c
··· 1265 1265 1266 1266 static u32 str2ip(const char *ipaddr) 1267 1267 { 1268 - unsigned int byte[4]; 1268 + unsigned int b[4]; 1269 1269 1270 - sscanf(ipaddr, "%u.%u.%u.%u", &byte[0], &byte[1], &byte[2], &byte[3]); 1271 - return (byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | byte[3]; 1270 + if (sscanf(ipaddr, "%u.%u.%u.%u", &b[0], &b[1], &b[2], &b[3]) != 4) 1271 + errx(1, "Failed to parse IP address '%s'", ipaddr); 1272 + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; 1273 + } 1274 + 1275 + static void str2mac(const char *macaddr, unsigned char mac[6]) 1276 + { 1277 + unsigned int m[6]; 1278 + if (sscanf(macaddr, "%02x:%02x:%02x:%02x:%02x:%02x", 1279 + &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) != 6) 1280 + errx(1, "Failed to parse mac address '%s'", macaddr); 1281 + mac[0] = m[0]; 1282 + mac[1] = m[1]; 1283 + mac[2] = m[2]; 1284 + mac[3] = m[3]; 1285 + mac[4] = m[4]; 1286 + mac[5] = m[5]; 1272 1287 } 1273 1288 1274 1289 /* This code is "adapted" from libbridge: it attaches the Host end of the ··· 1304 1289 errx(1, "interface %s does not exist!", if_name); 1305 1290 1306 1291 strncpy(ifr.ifr_name, br_name, IFNAMSIZ); 1292 + ifr.ifr_name[IFNAMSIZ-1] = '\0'; 1307 1293 ifr.ifr_ifindex = ifidx; 1308 1294 if (ioctl(fd, SIOCBRADDIF, &ifr) < 0) 1309 1295 err(1, "can't add %s to bridge %s", if_name, br_name); ··· 1313 1297 /* This sets up the Host end of the network device with an IP address, brings 1314 1298 * it up so packets will flow, the copies the MAC address into the hwaddr 1315 1299 * pointer. */ 1316 - static void configure_device(int fd, const char *devname, u32 ipaddr, 1317 - unsigned char hwaddr[6]) 1300 + static void configure_device(int fd, const char *tapif, u32 ipaddr) 1318 1301 { 1319 1302 struct ifreq ifr; 1320 1303 struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 1321 1304 1322 - /* Don't read these incantations. Just cut & paste them like I did! */ 1323 1305 memset(&ifr, 0, sizeof(ifr)); 1324 - strcpy(ifr.ifr_name, devname); 1306 + strcpy(ifr.ifr_name, tapif); 1307 + 1308 + /* Don't read these incantations. Just cut & paste them like I did! */ 1325 1309 sin->sin_family = AF_INET; 1326 1310 sin->sin_addr.s_addr = htonl(ipaddr); 1327 1311 if (ioctl(fd, SIOCSIFADDR, &ifr) != 0) 1328 - err(1, "Setting %s interface address", devname); 1312 + err(1, "Setting %s interface address", tapif); 1329 1313 ifr.ifr_flags = IFF_UP; 1330 1314 if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) 1331 - err(1, "Bringing interface %s up", devname); 1315 + err(1, "Bringing interface %s up", tapif); 1316 + } 1317 + 1318 + static void get_mac(int fd, const char *tapif, unsigned char hwaddr[6]) 1319 + { 1320 + struct ifreq ifr; 1321 + 1322 + memset(&ifr, 0, sizeof(ifr)); 1323 + strcpy(ifr.ifr_name, tapif); 1332 1324 1333 1325 /* SIOC stands for Socket I/O Control. G means Get (vs S for Set 1334 1326 * above). IF means Interface, and HWADDR is hardware address. 1335 1327 * Simple! */ 1336 1328 if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) 1337 - err(1, "getting hw address for %s", devname); 1329 + err(1, "getting hw address for %s", tapif); 1338 1330 memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6); 1339 1331 } 1340 1332 1341 - /*L:195 Our network is a Host<->Guest network. This can either use bridging or 1342 - * routing, but the principle is the same: it uses the "tun" device to inject 1343 - * packets into the Host as if they came in from a normal network card. We 1344 - * just shunt packets between the Guest and the tun device. */ 1345 - static void setup_tun_net(const char *arg) 1333 + static int get_tun_device(char tapif[IFNAMSIZ]) 1346 1334 { 1347 - struct device *dev; 1348 1335 struct ifreq ifr; 1349 - int netfd, ipfd; 1350 - u32 ip; 1351 - const char *br_name = NULL; 1352 - struct virtio_net_config conf; 1336 + int netfd; 1337 + 1338 + /* Start with this zeroed. Messy but sure. */ 1339 + memset(&ifr, 0, sizeof(ifr)); 1353 1340 1354 1341 /* We open the /dev/net/tun device and tell it we want a tap device. A 1355 1342 * tap device is like a tun device, only somehow different. To tell 1356 1343 * the truth, I completely blundered my way through this code, but it 1357 1344 * works now! */ 1358 1345 netfd = open_or_die("/dev/net/tun", O_RDWR); 1359 - memset(&ifr, 0, sizeof(ifr)); 1360 1346 ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 1361 1347 strcpy(ifr.ifr_name, "tap%d"); 1362 1348 if (ioctl(netfd, TUNSETIFF, &ifr) != 0) 1363 1349 err(1, "configuring /dev/net/tun"); 1350 + 1364 1351 /* We don't need checksums calculated for packets coming in this 1365 1352 * device: trust us! */ 1366 1353 ioctl(netfd, TUNSETNOCSUM, 1); 1354 + 1355 + memcpy(tapif, ifr.ifr_name, IFNAMSIZ); 1356 + return netfd; 1357 + } 1358 + 1359 + /*L:195 Our network is a Host<->Guest network. This can either use bridging or 1360 + * routing, but the principle is the same: it uses the "tun" device to inject 1361 + * packets into the Host as if they came in from a normal network card. We 1362 + * just shunt packets between the Guest and the tun device. */ 1363 + static void setup_tun_net(char *arg) 1364 + { 1365 + struct device *dev; 1366 + int netfd, ipfd; 1367 + u32 ip = INADDR_ANY; 1368 + bool bridging = false; 1369 + char tapif[IFNAMSIZ], *p; 1370 + struct virtio_net_config conf; 1371 + 1372 + netfd = get_tun_device(tapif); 1367 1373 1368 1374 /* First we create a new network device. */ 1369 1375 dev = new_device("net", VIRTIO_ID_NET, netfd, handle_tun_input); ··· 1403 1365 1404 1366 /* If the command line was --tunnet=bridge:<name> do bridging. */ 1405 1367 if (!strncmp(BRIDGE_PFX, arg, strlen(BRIDGE_PFX))) { 1406 - ip = INADDR_ANY; 1407 - br_name = arg + strlen(BRIDGE_PFX); 1408 - add_to_bridge(ipfd, ifr.ifr_name, br_name); 1409 - } else /* It is an IP address to set up the device with */ 1368 + arg += strlen(BRIDGE_PFX); 1369 + bridging = true; 1370 + } 1371 + 1372 + /* A mac address may follow the bridge name or IP address */ 1373 + p = strchr(arg, ':'); 1374 + if (p) { 1375 + str2mac(p+1, conf.mac); 1376 + *p = '\0'; 1377 + } else { 1378 + p = arg + strlen(arg); 1379 + /* None supplied; query the randomly assigned mac. */ 1380 + get_mac(ipfd, tapif, conf.mac); 1381 + } 1382 + 1383 + /* arg is now either an IP address or a bridge name */ 1384 + if (bridging) 1385 + add_to_bridge(ipfd, tapif, arg); 1386 + else 1410 1387 ip = str2ip(arg); 1411 1388 1412 - /* Set up the tun device, and get the mac address for the interface. */ 1413 - configure_device(ipfd, ifr.ifr_name, ip, conf.mac); 1389 + /* Set up the tun device. */ 1390 + configure_device(ipfd, tapif, ip); 1414 1391 1415 1392 /* Tell Guest what MAC address to use. */ 1416 1393 add_feature(dev, VIRTIO_NET_F_MAC); ··· 1435 1382 /* We don't need the socket any more; setup is done. */ 1436 1383 close(ipfd); 1437 1384 1438 - verbose("device %u: tun net %u.%u.%u.%u\n", 1439 - devices.device_num++, 1440 - (u8)(ip>>24),(u8)(ip>>16),(u8)(ip>>8),(u8)ip); 1441 - if (br_name) 1442 - verbose("attached to bridge: %s\n", br_name); 1385 + devices.device_num++; 1386 + 1387 + if (bridging) 1388 + verbose("device %u: tun %s attached to bridge: %s\n", 1389 + devices.device_num, tapif, arg); 1390 + else 1391 + verbose("device %u: tun %s: %s\n", 1392 + devices.device_num, tapif, arg); 1443 1393 } 1444 1394 1445 1395 /* Our block (disk) device should be really simple: the Guest asks for a block ··· 1754 1698 static void usage(void) 1755 1699 { 1756 1700 errx(1, "Usage: lguest [--verbose] " 1757 - "[--tunnet=(<ipaddr>|bridge:<bridgename>)\n" 1701 + "[--tunnet=(<ipaddr>:<macaddr>|bridge:<bridgename>:<macaddr>)\n" 1758 1702 "|--block=<filename>|--initrd=<filename>]...\n" 1759 1703 "<mem-in-mb> vmlinux [args...]"); 1760 1704 }