Skip to content

run: add --ip-address option - #261

Merged
nirs merged 1 commit into
nirs:mainfrom
tofugarden:run/static-ip
Jul 29, 2026
Merged

run: add --ip-address option#261
nirs merged 1 commit into
nirs:mainfrom
tofugarden:run/static-ip

Conversation

@tofugarden

@tofugarden tofugarden commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Objective

Allow assigning a static IP to a VM started with run. Closes #260.

Implementation notes

Argument validation requires the requested IP to be in the same subnet as the DHCP range as defined by --start-address, --end-address, --subnet-mask. It does not require the address to be outside the range.

The file at testing/validate.py now contains all argument validation functions.

cloud-init supports setting multiple IP addresses, but we only support one command line argument. It wouldn't be that difficult to implement, it's just not needed.

@nirs nirs left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally:

Switch from dhcp to ip address

% ./run ubuntu --ip-address 192.168.200.128 --start-address 192.168.200.1 --end-address 192.168.200.127 --subnet-mask 255.255.255.0
[   0.042] INFO Starting vmnet-helper for 'ubuntu' with interface id '6bbc6ce9-5e26-4b39-bc9c-28c88776d519'
[   0.213] INFO Creating cloud-init iso '/Users/nir/.vmnet-helper/vms/ubuntu/cidata.iso'
[   0.235] INFO Starting 'vfkit' virtual machine 'ubuntu' with mac address '82:e9:dd:3d:68:1f'
[   5.080] INFO VM is ready at ubuntu-vmnet-helper.local
% ssh ubuntu -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 82:e9:dd:3d:68:1f brd ff:ff:ff:ff:ff:ff
    altname enx82e9dd3d681f
    inet 192.168.200.128/24 brd 192.168.200.255 scope global enp0s1
       valid_lft forever preferred_lft forever
    inet6 fdd3:28eb:866d:8319:80e9:ddff:fe3d:681f/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591905sec preferred_lft 604705sec
    inet6 fe80::80e9:ddff:fe3d:681f/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

Switch back to dhcp

% ./run ubuntu --start-address 192.168.200.1 --end-address 192.168.200.127 --subnet-mask 255.255.255.0 
[   0.041] INFO Starting vmnet-helper for 'ubuntu' with interface id '6bbc6ce9-5e26-4b39-bc9c-28c88776d519'
[   0.148] INFO Starting 'vfkit' virtual machine 'ubuntu' with mac address '82:e9:dd:3d:68:1f'
[   4.646] INFO VM is ready at ubuntu-vmnet-helper.local

We did not detect the change (ip-address change to None), and did not generate the ciabatta.iso.

% ssh ubuntu -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 82:e9:dd:3d:68:1f brd ff:ff:ff:ff:ff:ff
    altname enx82e9dd3d681f
    inet 192.168.200.128/24 brd 192.168.200.255 scope global enp0s1
       valid_lft forever preferred_lft forever
    inet6 fdd3:28eb:866d:8319:80e9:ddff:fe3d:681f/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591992sec preferred_lft 604792sec
    inet6 fe80::80e9:ddff:fe3d:681f/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

So we got the same IP address in the guest.

We need to generate the network config, read the previous one, and compare. If network-config changed, we to regenerate the iso. The same way will work or user-data later if we want to change packages or mDNS implementation.

Switch back to dhcp by deleting the iso

% rm ~/.vmnet-helper/vms/ubuntu/cidata.iso 

% ./run ubuntu --start-address 192.168.200.1 --end-address 192.168.200.127 --subnet-mask 255.255.255.0
[   0.041] INFO Starting vmnet-helper for 'ubuntu' with interface id '6bbc6ce9-5e26-4b39-bc9c-28c88776d519'
[   0.166] INFO Creating cloud-init iso '/Users/nir/.vmnet-helper/vms/ubuntu/cidata.iso'
[   0.178] INFO Starting 'vfkit' virtual machine 'ubuntu' with mac address '82:e9:dd:3d:68:1f'
[   5.083] INFO VM is ready at ubuntu-vmnet-helper.local
% ssh ubuntu -- ip a                     
Warning: Permanently added 'ubuntu-vmnet-helper.local' (ED25519) to the list of known hosts.
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 82:e9:dd:3d:68:1f brd ff:ff:ff:ff:ff:ff
    altname enx82e9dd3d681f
    inet 192.168.200.2/24 metric 100 brd 192.168.200.255 scope global dynamic enp0s1
       valid_lft 576sec preferred_lft 576sec
    inet6 fdd3:28eb:866d:8319:80e9:ddff:fe3d:681f/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591993sec preferred_lft 604793sec
    inet6 fe80::80e9:ddff:fe3d:681f/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

Comment thread docs/development.md Outdated
Comment thread docs/development.md Outdated
Comment thread testing/cidata.py Outdated
Comment thread testing/cidata.py Outdated
Comment thread testing/cidata.py Outdated
Comment thread run
Comment thread run Outdated
Comment thread run Outdated
Comment thread run Outdated
Comment thread run Outdated
@tofugarden
tofugarden force-pushed the run/static-ip branch 3 times, most recently from 4688d38 to 1fa37ea Compare July 8, 2026 14:56

@nirs nirs left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! see comment on how to fix the cidata.iso in a more generic way. I think it will be best to fix it separate from this PR.

Comment thread testing/cidata.py Outdated
Comment thread testing/net.py Outdated
Comment thread testing/net.py Outdated
Comment thread testing/net.py Outdated
Comment thread testing/net.py Outdated
Comment thread testing/net.py Outdated
Comment thread docs/development.md Outdated
Comment thread docs/development.md Outdated
Comment thread docs/development.md
Comment thread docs/development.md
@nirs

nirs commented Jul 14, 2026

Copy link
Copy Markdown
Owner

@tofugarden we can finish this now, will be much simpler after rebase.

@tofugarden

Copy link
Copy Markdown
Contributor Author

@nirs would you consider #264 first? That will also narrow down the documentation changes.

@nirs

nirs commented Jul 15, 2026

Copy link
Copy Markdown
Owner

@nirs would you consider #264 first? That will also narrow down the documentation changes.

Merged now

@nirs nirs left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, added some comments and questions.

Comment thread testing/net.py Outdated
Comment thread testing/net.py Outdated
Comment thread run Outdated
Comment thread run Outdated
Comment thread testing/net.py Outdated

@nirs nirs left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment on unneeded import. Did not have time to review and test the latest version yet. I hope to do this this weekend.

Comment thread testing/__init__.py Outdated

@nirs nirs left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting that vment does not enforce that ip address is not the network address or broadcast address. I could start and access a vm with both:

% ./run server --start-address 192.168.200.1 --end-address 192.168.200.128 --subnet-mask 255.255.255.0 --ip-address 192.168.200.0 
[   0.066] INFO Starting vmnet-helper for 'server' with interface id 'bdc7b3e0-8594-4814-aa25-05187ad2a36e'
[   0.204] INFO Creating cloud-init iso '/Users/nir/.vmnet-helper/vms/server/cidata.iso'
[   0.216] INFO Starting 'vfkit' virtual machine 'server' with mac address '92:c9:52:b7:6c:08'
[   6.526] INFO VM is ready at server-vmnet-helper.local
% ssh server -- ip a                     
Warning: Permanently added 'server-vmnet-helper.local' (ED25519) to the list of known hosts.
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 92:c9:52:b7:6c:08 brd ff:ff:ff:ff:ff:ff
    altname enx92c952b76c08
    inet 192.168.200.0/24 brd 192.168.200.255 scope global enp0s1
       valid_lft forever preferred_lft forever
    inet6 fdd3:28eb:866d:8319:90c9:52ff:feb7:6c08/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591976sec preferred_lft 604776sec
    inet6 fe80::90c9:52ff:feb7:6c08/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever
% ./run server --start-address 192.168.200.1 --end-address 192.168.200.128 --subnet-mask 255.255.255.0 --ip-address 192.168.200.255
[   0.069] INFO Starting vmnet-helper for 'server' with interface id 'bdc7b3e0-8594-4814-aa25-05187ad2a36e'
[   0.219] INFO Creating cloud-init iso '/Users/nir/.vmnet-helper/vms/server/cidata.iso'
[   0.234] INFO Starting 'vfkit' virtual machine 'server' with mac address '92:c9:52:b7:6c:08'
[   7.108] INFO VM is ready at server-vmnet-helper.local
% ssh server -- ip a                     
Warning: Permanently added 'server-vmnet-helper.local' (ED25519) to the list of known hosts.
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 92:c9:52:b7:6c:08 brd ff:ff:ff:ff:ff:ff
    altname enx92c952b76c08
    inet 192.168.200.255/24 brd 192.168.200.255 scope global enp0s1
       valid_lft forever preferred_lft forever
    inet6 fdd3:28eb:866d:8319:90c9:52ff:feb7:6c08/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591991sec preferred_lft 604791sec
    inet6 fe80::90c9:52ff:feb7:6c08/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

Since vmnet does not enforce this we are good.

Comment thread testing/validate.py
Comment thread testing/cidata.py
Comment thread testing/cidata.py Outdated
Comment thread run Outdated
Comment thread testing/validate.py
Comment thread testing/validate.py Outdated
Comment thread testing/validate.py Outdated
Comment thread run Outdated
Comment thread testing/validate.py Outdated
Comment thread docs/development.md Outdated

@nirs nirs left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surprisingly this works:

% ./run server --start-address 192.168.200.130 --end-address 192.168.200.131 --subnet-mask 255.255.255.252 --ip-address 192.168.200.129
[   0.058] INFO Starting vmnet-helper for 'server' with interface id 'bdc7b3e0-8594-4814-aa25-05187ad2a36e'
[   0.177] INFO Creating cloud-init iso '/Users/nir/.vmnet-helper/vms/server/cidata.iso'
[   0.189] INFO Starting 'vfkit' virtual machine 'server' with mac address '92:c9:52:b7:6c:08'
[   7.532] INFO VM is ready at server-vmnet-helper.local
% ssh server -- ip a                     
Warning: Permanently added 'server-vmnet-helper.local' (ED25519) to the list of known hosts.
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 92:c9:52:b7:6c:08 brd ff:ff:ff:ff:ff:ff
    altname enx92c952b76c08
    inet 192.168.200.129/30 brd 192.168.200.131 scope global enp0s1
       valid_lft forever preferred_lft forever
    inet6 fd73:42db:f3c7:cfb3:90c9:52ff:feb7:6c08/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591992sec preferred_lft 604792sec
    inet6 fe80::90c9:52ff:feb7:6c08/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

% ifconfig bridge100
bridge100: flags=8a63<UP,BROADCAST,SMART,RUNNING,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
	options=3<RXCSUM,TXCSUM>
	ether 5e:e9:1e:67:7a:64
	inet 192.168.200.130 netmask 0xfffffffc broadcast 192.168.200.131
	inet6 fe80::5ce9:1eff:fe67:7a64%bridge100 prefixlen 64 scopeid 0x1b 
	inet6 fd73:42db:f3c7:cfb3:106c:ab15:162d:a768 prefixlen 64 autoconf secured 
	Configuration:
		id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
		maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
		root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
		ipfilter disabled flags 0x0
	member: vmenet0 flags=3<LEARNING,DISCOVER>
	        ifmaxaddr 0 port 26 priority 0 path cost 0
	Address cache:
		92:c9:52:b7:6c:8 Vlan1 vmenet0 1197 flags=0<>
	nd6 options=201<PERFORMNUD,DAD>
	media: autoselect
	status: active

And it matches the docs in vmnet.h:

% cat /Library/Developer/CommandLineTools/SDKs/MacOSX26.5.sdk/System/Library/Frameworks/vmnet.framework/Versions/A/Headers/vmnet.h
...
/*!
 * @constant vmnet_start_address_key
 *
 * @abstract
 * The starting IPv4 address (string) to use for the interface.
 *
 * @discussion
 * This address is used as the gateway address. The subsequent
 * address up to and including vmnet_end_address_key are placed
 * in the DHCP pool. All other addresses are available for static
 * assignment.
 *
 * The address must be in the private IP range (RFC 1918).
 *
 * Must be specified along with vmnet_end_address_key and vmnet_subnet_mask_key.
 *
 * May be present in the interface_desc and interface_param dictionaries.
 */
...

So the validation is correct.

Comment thread testing/validate.py Outdated
Allows assigning a static IP address to a VM.
Move argument validation to testing/validate.py.

@nirs nirs left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Comment thread testing/cidata.py
Comment thread testing/cidata.py
Comment thread testing/validate.py
Comment thread testing/validate.py
@nirs
nirs merged commit 4964378 into nirs:main Jul 29, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

run doesn't support static IP address assignment

2 participants