Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,15 @@ The following example uses the qemu driver, and connects using vmnet-run:
[ 16.630] INFO VM is ready at test-vmnet-helper.local
```

VMs use DHCP by default. To assign a static IP address, restrict the DHCP range,
then select an address outside of that range:
### Static IP addressing

VMs use DHCP by default. To assign a static IP address, the host interface
subnet must be defined. This can be done in two ways:

**In DHCP mode:**

Set a specific DHCP range, then assign an address on the same subnet.
**--start-address** and **--ip-address** must be different:

```console
% ./run test \
Expand All @@ -158,9 +165,20 @@ then select an address outside of that range:
> Setting `--ip-address` to a value inside the DHCP range may work, but may
> cause conflicts.

> [!NOTE]
> `--ip-address` must be difrerent from `--start-address`, but in the same
> subnet.
**When --network-id is set:**

In host mode only, **--network-id** disables DHCP. Assign a specific address
and subnet to the host, then a different address in the same subnet to the
guest:

```console
% ./run test \
--operation-mode=host \
--network-id=009D22BF-E40F-4251-A58F-DAC0B4E1250F \
--host-ip-address 192.168.200.1 \
--host-subnet-mask 255.255.255.0 \
--ip-address 192.168.200.2
```

When changing a VM's IP address or switching to DHCP, the instance ID and host
key will be reset. Remove the old host key before you ssh again:
Expand Down
45 changes: 44 additions & 1 deletion docs/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ Allows the vmnet interface to communicate with other vmnet interfaces
that are in host mode and also with the native host.

The network can be configured using the
[network address options](#network-address-options).
[network address options](#network-address-options) and the
[network id options](#network-id-options).

### --operation-mode=shared

Expand Down Expand Up @@ -335,6 +336,48 @@ cases and much worse performance in other cases. See the
> You must use both **--enable-tso** and **--enable-checksum-offload** when
> using krunkit **offloading=on** virtio-net option.

## Network ID options

In host mode, the **--network-id** can be set to a UUID. Any interfaces
started with the same identifier can communicate with each other, but are
isolated from other host mode interfaces. This also disables DHCP on the
network.

```console
% vmnet-helper --socket ./test.sock --operation-mode host --network-id C6E763C8-F8E9-4CC4-9280-F2E40ABC5B73
INFO [main] running vmnet-helper v0.12.0-24-g4964378 on macOS 26.5.2
INFO [main] running as uid: 501 gid: 20
INFO [main] using bulk_forwarding: true
{"vmnet_write_max_packets":256,"vmnet_read_max_packets":256,"vmnet_subnet_mask":"255.255.255.0","vmnet_mtu":1500,"vmnet_end_address":"192.168.128.254","vmnet_start_address":"192.168.128.1","vmnet_interface_id":"E496A990-DB40-453B-85E3-1F758908EE06","vmnet_max_packet_size":1514,"vmnet_mac_address":"1e:2a:95:42:af:9e"}
INFO [main] started vmnet interface
INFO [main] waiting for client on "./test.sock"
```

> [!NOTE]
> On macOS 15 or earlier, no address is assigned to the host interface when
> **--network-id** is set. **vmnet_start_address** will be **0.0.0.0**. If
> you need a host address, see the host address options below.

Since DHCP is disabled, the [network address options](#network-address-options)
are not available. To control the address and subnet mask assigned to the host
interface, use **--host-ip-address** and **--host-subnet-mask**. vmnet returns
the host address as **vmnet_start_address**.

```console
% vmnet-helper \
--socket ./test.sock \
--operation-mode host \
--network-id C6E763C8-F8E9-4CC4-9280-F2E40ABC5B73 \
--host-ip-address 192.168.200.12 \
--host-subnet-mask 255.255.255.0
INFO [main] running vmnet-helper v0.12.0-24-g4964378 on macOS 26.5.2
INFO [main] running as uid: 501 gid: 20
INFO [main] using bulk_forwarding: true
{"vmnet_write_max_packets":256,"vmnet_read_max_packets":256,"vmnet_subnet_mask":"255.255.255.0","vmnet_mtu":1500,"vmnet_end_address":"192.168.200.254","vmnet_start_address":"192.168.200.12","vmnet_interface_id":"345CD3A2-9327-4BBE-B0D8-4424B30A0F19","vmnet_max_packet_size":1514,"vmnet_mac_address":"be:82:e0:83:ab:59"}
INFO [main] started vmnet interface
INFO [main] waiting for client on "./test.sock"
```

## Stopping the interface

Terminate the vmnet-helper process gracefully. Send a SIGTERM or SIGINT
Expand Down
21 changes: 21 additions & 0 deletions programs/helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ static const char *mode_name(operating_modes_t mode)
// Start interface with the specified operation mode and options.
static void start_interface_with_options(void)
{
char network_id[37] = "(unset)";
char interface_id[37] = "(unset)";
if (!uuid_is_null(options.interface_id)) {
uuid_unparse(options.interface_id, interface_id);
Expand All @@ -308,15 +309,22 @@ static void start_interface_with_options(void)
switch (options.operation_mode) {
case VMNET_SHARED_MODE:
case VMNET_HOST_MODE:
Comment thread
tofugarden marked this conversation as resolved.
if (!uuid_is_null(options.network_id)) {
uuid_unparse(options.network_id, network_id);
}
DEBUGF("[main] starting interface mode '%s' interface-id '%s' "
"start-address '%s' end-address '%s' subnet-mask '%s' "
"network-id '%s' host-ip-address '%s' host-subnet-mask '%s' "
"enable-tso %s enable-checksum-offload %s "
"enable-isolation %s",
mode_name(options.operation_mode),
interface_id,
options.start_address,
options.end_address,
options.subnet_mask,
network_id,
options.host_ip_address,
options.host_subnet_mask,
bool_str(options.enable_tso),
bool_str(options.enable_checksum_offload),
bool_str(options.enable_isolation));
Expand All @@ -342,13 +350,26 @@ static void start_interface_with_options(void)

switch (options.operation_mode) {
case VMNET_SHARED_MODE:
if (options.start_address != NULL) {
xpc_dictionary_set_string(desc, vmnet_start_address_key, options.start_address);
xpc_dictionary_set_string(desc, vmnet_end_address_key, options.end_address);
xpc_dictionary_set_string(desc, vmnet_subnet_mask_key, options.subnet_mask);
}
xpc_dictionary_set_bool(desc, vmnet_enable_isolation_key, options.enable_isolation);
case VMNET_HOST_MODE:
if (options.start_address != NULL) {
xpc_dictionary_set_string(desc, vmnet_start_address_key, options.start_address);
xpc_dictionary_set_string(desc, vmnet_end_address_key, options.end_address);
xpc_dictionary_set_string(desc, vmnet_subnet_mask_key, options.subnet_mask);
}
xpc_dictionary_set_bool(desc, vmnet_enable_isolation_key, options.enable_isolation);
if (!uuid_is_null(options.network_id)) {

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.

We cannot have start_address and network_id, host_ip_* at the same time, right?

The code try to handle both at the same time. We can use this to make the intent more clear:

if (options.start_address != NULL) {
    add address options...
} else if (!uuid_is_null(options.network_id)) {
    add optional host_ip_* otions...
}

xpc_dictionary_set_uuid(desc, vmnet_network_identifier_key, options.network_id);
if (options.host_ip_address != NULL && options.host_subnet_mask != NULL) {
xpc_dictionary_set_string(desc, vmnet_host_ip_address_key, options.host_ip_address);
xpc_dictionary_set_string(desc, vmnet_host_subnet_mask_key, options.host_subnet_mask);
}
}
break;
case VMNET_BRIDGED_MODE:
xpc_dictionary_set_string(desc, vmnet_shared_interface_name_key, options.shared_interface);
Expand Down
86 changes: 85 additions & 1 deletion programs/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ static void usage(int code)
" vmnet-helper (--fd FD|--socket SOCKET) [--interface-id UUID]\n"
" [--operation-mode shared|bridged|host] [--shared-interface NAME]\n"
" [--start-address ADDR] [--end-address ADDR] [--subnet-mask MASK]\n"
" [--network-id UUID] [--host-ip-address ADDR]\n"
" [--host-subnet-mask MASK]\n"

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.

Less keep these in the same line - they are releated.

" [--enable-tso] [--enable-checksum-offload] [--enable-isolation]\n"
" [--network NAME] [--list-shared-interfaces]\n"
" [--stats-interval SECONDS]\n"
Expand All @@ -40,7 +42,8 @@ static void usage(int code)
" With --network, vmnet-helper joins a network managed by vmnet-broker.\n"
"\n"
" --network is mutually exclusive with: --operation-mode, --shared-interface,\n"
" --start-address, --end-address, --subnet-mask.\n"
" --start-address, --end-address, --subnet-mask, --network-id, --host-ip-address,\n"
" --host-subnet-mask.\n"
"\n"
" --network requires macOS 26 or later.\n"
"\n";
Expand All @@ -54,6 +57,9 @@ enum {
OPT_START_ADDRESS,
OPT_END_ADDRESS,
OPT_SUBNET_MASK,
OPT_NETWORK_ID,
OPT_HOST_IP_ADDRESS,
OPT_HOST_SUBNET_MASK,
OPT_ENABLE_TSO,
OPT_ENABLE_CHECKSUM_OFFLOAD,
OPT_ENABLE_ISOLATION,
Expand All @@ -74,6 +80,9 @@ static struct option long_options[] = {
{"start-address", required_argument, 0, OPT_START_ADDRESS},
{"end-address", required_argument, 0, OPT_END_ADDRESS},
{"subnet-mask", required_argument, 0, OPT_SUBNET_MASK},
{"network-id", required_argument, 0, OPT_NETWORK_ID},
{"host-ip-address", required_argument, 0, OPT_HOST_IP_ADDRESS},
{"host-subnet-mask", required_argument, 0, OPT_HOST_SUBNET_MASK},
{"enable-tso", no_argument, 0, OPT_ENABLE_TSO},
{"enable-checksum-offload", no_argument, 0, OPT_ENABLE_CHECKSUM_OFFLOAD},
{"enable-isolation", no_argument, 0, OPT_ENABLE_ISOLATION},
Expand Down Expand Up @@ -154,6 +163,14 @@ static void parse_interface_id(const char *arg, uuid_t uuid)
}
}

static void parse_network_id(const char *arg, uuid_t uuid)
{
if (uuid_parse(arg, uuid) < 0) {
ERRORF("Invalid network-id: \"%s\"", arg);
exit(EXIT_FAILURE);
}
}

static void parse_operation_mode(const char *arg, const char *name, uint32_t *mode)
{
if (strcmp(arg, "shared") == 0) {
Expand Down Expand Up @@ -205,6 +222,25 @@ static void validate_network_options(struct options *opts)
}
}

static void validate_network_id_options(struct options *opts)
{
int host_address_options_set = (opts->host_ip_address != NULL) + (opts->host_subnet_mask != NULL);
int dhcp_options_set = (opts->start_address != NULL) + (opts->end_address != NULL) +
(opts->subnet_mask != NULL);
if (host_address_options_set != 0 && uuid_is_null(opts->network_id)) {
ERROR("--host-ip-address and --host-subnet-mask require --network-id");
exit(EXIT_FAILURE);
}
if (host_address_options_set != 0 && host_address_options_set != 2) {
ERROR("--host-ip-address and --host-subnet-mask must be given together, or all omitted");
exit(EXIT_FAILURE);
}
if (!uuid_is_null(opts->network_id) && dhcp_options_set != 0) {
ERROR("--network-id cannot be used with --start-address, --end-address, --subnet-mask");
exit(EXIT_FAILURE);
}
}

void parse_options(struct options *opts, int argc, char **argv)
{
const char *optname;
Expand Down Expand Up @@ -248,6 +284,15 @@ void parse_options(struct options *opts, int argc, char **argv)
case OPT_SUBNET_MASK:
parse_address(optarg, optname, &opts->subnet_mask);
break;
case OPT_NETWORK_ID:
parse_network_id(optarg, opts->network_id);
break;
case OPT_HOST_IP_ADDRESS:
parse_address(optarg, optname, &opts->host_ip_address);
break;
case OPT_HOST_SUBNET_MASK:
parse_address(optarg, optname, &opts->host_subnet_mask);
break;
case OPT_ENABLE_TSO:
opts->enable_tso = true;
break;
Expand Down Expand Up @@ -304,6 +349,18 @@ void parse_options(struct options *opts, int argc, char **argv)
ERROR("Conflicting arguments: --network cannot be used with --subnet-mask");
exit(EXIT_FAILURE);
}
if (!uuid_is_null(opts->network_id)) {
ERROR("Conflicting arguments: --network cannot be used with --network-id");
exit(EXIT_FAILURE);
}
if (opts->host_ip_address != NULL) {
ERROR("Conflicting arguments: --network cannot be used with --host-ip-address");
exit(EXIT_FAILURE);
}
if (opts->host_subnet_mask != NULL) {
ERROR("Conflicting arguments: --network cannot be used with --host-subnet-mask");
exit(EXIT_FAILURE);
}
} else {
// Apply defaults and validate when not using vmnet-broker network.
if (opts->operation_mode == 0) {
Expand All @@ -312,10 +369,37 @@ void parse_options(struct options *opts, int argc, char **argv)

switch (opts->operation_mode) {
case VMNET_SHARED_MODE:
validate_network_options(opts);
if (!uuid_is_null(opts->network_id)) {
ERROR("--network-id cannot be used with operation-mode=shared");
exit(EXIT_FAILURE);
}
if (opts->host_ip_address != NULL) {
ERROR("--host-ip-address cannot be used with operation-mode=shared");
exit(EXIT_FAILURE);
}
if (opts->host_subnet_mask != NULL) {
ERROR("--host-subnet-mask cannot be used with operation-mode=shared");
exit(EXIT_FAILURE);
}
break;
case VMNET_HOST_MODE:
validate_network_options(opts);
validate_network_id_options(opts);
break;
case VMNET_BRIDGED_MODE:
if (!uuid_is_null(opts->network_id)) {
ERROR("--network-id cannot be used with operation-mode=bridged");
exit(EXIT_FAILURE);
}
if (opts->host_ip_address != NULL) {
ERROR("--host-ip-address cannot be used with operation-mode=bridged");
exit(EXIT_FAILURE);
}
if (opts->host_subnet_mask != NULL) {
ERROR("--host-subnet-mask cannot be used with operation-mode=bridged");
exit(EXIT_FAILURE);
}
if (opts->shared_interface == NULL) {
ERROR("Missing argument: shared-interface is required for operation-mode=bridged");
exit(EXIT_FAILURE);
Expand Down
3 changes: 3 additions & 0 deletions programs/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ struct options {
const char *start_address;
const char *end_address;
const char *subnet_mask;
uuid_t network_id;
const char *host_ip_address;
const char *host_subnet_mask;
const char *shared_interface;
const char *network_name;
bool enable_isolation;
Expand Down
Loading
Loading