diff mbox series

[04/11] runqemu: allocate the slirp MAC address via lock files

Message ID 20261011195949.2118607-5-adrian.freihofer@siemens.com
State New
Headers show
Series runqemu: use QMP for slirp host port allocation | expand

Commit Message

AdrianF Oct. 11, 2026, 7:59 p.m. UTC
From: Adrian Freihofer <adrian.freihofer@siemens.com>

The last octet of the slirp MAC address was derived from how many times
a host port had to be incremented to find a free one. That is unreliable
in both directions: two concurrently starting instances that shift their
ports by the same amount end up with the same MAC address, while an
instance that finds all its wished-for ports free always picks the same
one.

It also does not scale with the number of forwarded ports, because every
single port that has to be moved bumps the octet once more. A
configuration asking for many ports, as devtool ide-sdk does for its
debug servers, therefore runs through the single byte quickly, and past
0xff the formatted address stops being a valid MAC address at all, e.g.
"52:54:00:12:35:124" once the counter passes 255.

Allocate the octet from its own lock file directory instead, reusing the
scheme already used for the tap and port locks, which makes it unique
among the qemu instances running on the host and independent of how many
ports are forwarded and whether any of them had to be moved.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 scripts/runqemu | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/scripts/runqemu b/scripts/runqemu
index 6544c3884b..ca1f74e9d7 100755
--- a/scripts/runqemu
+++ b/scripts/runqemu
@@ -1133,6 +1133,17 @@  to your build configuration.
                 pass
         return
 
+    def alloc_mac_slot(self, lockdir):
+        """MAC address arbitration for slirp addresses: lock the first free
+        numbered lockfile (1-254) to get an octet unique among concurrently
+        running qemu instances on this host."""
+        self.make_lock_dir(lockdir)
+        for slot in range(1, 255):
+            lockfile = os.path.join(lockdir, str(slot) + '.lock')
+            if self.acquire_lock(lockfile):
+                return slot
+        raise RunQemuError("Too many concurrent qemu slirp instances on this host, can't allocate a unique MAC address")
+
     def setup_slirp(self):
         """Setup user networking"""
 
@@ -1148,7 +1159,6 @@  to your build configuration.
         # Figure out the port
         ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
         ports = [int(i) for i in ports]
-        mac = 2
 
         lockdir = "/tmp/qemu-port-locks"
         self.make_lock_dir(lockdir)
@@ -1158,15 +1168,13 @@  to your build configuration.
             p_new = p
             while not self.check_free_port('localhost', p_new, lockdir):
                 p_new += 1
-                mac += 1
                 while p_new in ports:
                         p_new += 1
-                        mac += 1
             if p != p_new:
                 ports.append(p_new)
                 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
                 logger.info("Port forward changed: %s -> %s" % (p, p_new))
-        mac = "%s%02x" % (self.mac_slirp, mac)
+        mac = "%s%02x" % (self.mac_slirp, self.alloc_mac_slot("/tmp/qemu-mac-locks"))
         self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
         # Print out port foward
         hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)