diff --git a/README.md b/README.md index e1a5978..b45bedd 100644 --- a/README.md +++ b/README.md @@ -12,27 +12,29 @@ Unlike `vde_vmnet`, `socket_vmnet` does not depend on VDE. -- [Install](#install) - - [From binary](#from-binary) - - [From source](#from-source) - - [From Homebrew](#from-homebrew) - - [From MacPorts](#from-macports) -- [Usage](#usage) - - [QEMU](#qemu) - - [Lima](#lima) -- [Advanced usage](#advanced-usage) - - [Multi VM](#multi-vm) - - [Bridged mode](#bridged-mode) -- [FAQs](#faqs) - - [Why does `socket_vmnet` require root?](#why-does-socket_vmnet-require-root) - - [Is it possible to run `socket_vmnet` with SETUID?](#is-it-possible-to-run-socket_vmnet-with-setuid) - - [How is socket_vmnet related to vde_vmnet?](#how-is-socket_vmnet-related-to-vde_vmnet) - - [How is socket_vmnet related to QEMU-builtin vmnet support?](#how-is-socket_vmnet-related-to-qemu-builtin-vmnet-support) - - [How to use static IP addresses?](#how-to-use-static-ip-addresses) - - [How to reserve DHCP addresses?](#how-to-reserve-dhcp-addresses) - - [IP address is not assigned](#ip-address-is-not-assigned) -- [Links](#links) -- [Troubleshooting](#troubleshooting) +- [socket_vmnet: vmnet.framework support for rootless and VDE-less QEMU](#socket_vmnet-vmnetframework-support-for-rootless-and-vde-less-qemu) + - [Install](#install) + - [From binary](#from-binary) + - [From source](#from-source) + - [From Homebrew](#from-homebrew) + - [From MacPorts](#from-macports) + - [Usage](#usage) + - [QEMU](#qemu) + - [Lima](#lima) + - [Advanced usage](#advanced-usage) + - [Multi VM](#multi-vm) + - [Bridged mode](#bridged-mode) + - [FAQs](#faqs) + - [Why does `socket_vmnet` require root?](#why-does-socket_vmnet-require-root) + - [Is it possible to run `socket_vmnet` with SETUID?](#is-it-possible-to-run-socket_vmnet-with-setuid) + - [How is socket_vmnet related to vde_vmnet?](#how-is-socket_vmnet-related-to-vde_vmnet) + - [How is socket_vmnet related to QEMU-builtin vmnet support?](#how-is-socket_vmnet-related-to-qemu-builtin-vmnet-support) + - [How to use static IP addresses?](#how-to-use-static-ip-addresses) + - [How to reserve DHCP addresses?](#how-to-reserve-dhcp-addresses) + - [IP address is not assigned](#ip-address-is-not-assigned) + - [How to setup a vmnet host network without DHCP?](#how-to-setup-a-vmnet-host-network-without-dhcp) + - [Links](#links) + - [Troubleshooting](#troubleshooting) @@ -122,8 +124,7 @@ Run the following command to start the daemon: sudo /opt/socket_vmnet/bin/socket_vmnet --vmnet-gateway=192.168.105.1 /var/run/socket_vmnet ``` -> [!TIP] -> `sudo make install` is also available in addition to `sudo make install.bin`. +> [!TIP] > `sudo make install` is also available in addition to `sudo make install.bin`. > The former one installs the launchd service (see below) too.
@@ -430,6 +431,17 @@ sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /usr/libexec/bootpd /usr/libexec/ApplicationFirewall/socketfilterfw --unblock /usr/libexec/bootpd ``` +### How to setup a vmnet host network without DHCP? + +You may need to disable the vmnet framework's DHCP to: + +- Create a host network where all VMs have static IPs. +- Run a custom DHCP server on one VM to assign IPs to others on the same network. + +To disable the macOS DHCP you must use `--vmnet-mode=host` and provide a `--vmnet-network-idenfitier` UUID. +You **_must not_** provide `--vmnet-gateway`. That is the signal to Apple's vmnet.framework to enable MacOS DHCP. +You can use `--vmnet-network-idenfitier=random` to get a random UUID assigned. + ## Links - https://developer.apple.com/documentation/vmnet diff --git a/cli.c b/cli.c index 4aeccc0..5f33e3e 100644 --- a/cli.c +++ b/cli.c @@ -48,6 +48,12 @@ static void print_usage(const char *argv0) { "specified\n"); printf("--vmnet-interface-id=UUID vmnet interface ID (default: " "random)\n"); + printf("--vmnet-network-identifier=UUID vmnet network identifier (UUID string, \"random\", " + "or \"\")\n" + " When vmnet mode is \"host\" and --vmnet-gateway is " + "not set, the internal DHCP will be disabled.\n" + " (default: \"\")\n"); + printf("--vmnet-nat66-prefix=PREFIX:: The IPv6 prefix to use with " "shared mode.\n"); printf(" The prefix must be a ULA i.e. " @@ -72,6 +78,7 @@ enum { CLI_OPT_VMNET_MASK, CLI_OPT_VMNET_INTERFACE_ID, CLI_OPT_VMNET_NAT66_PREFIX, + CLI_OPT_VMNET_NETWORK_IDENTIFIER, }; struct cli_options *cli_options_parse(int argc, char *argv[]) { @@ -82,18 +89,19 @@ struct cli_options *cli_options_parse(int argc, char *argv[]) { } const struct option longopts[] = { - {"socket-group", required_argument, NULL, CLI_OPT_SOCKET_GROUP }, - {"vmnet-mode", required_argument, NULL, CLI_OPT_VMNET_MODE }, - {"vmnet-interface", required_argument, NULL, CLI_OPT_VMNET_INTERFACE }, - {"vmnet-gateway", required_argument, NULL, CLI_OPT_VMNET_GATEWAY }, - {"vmnet-dhcp-end", required_argument, NULL, CLI_OPT_VMNET_DHCP_END }, - {"vmnet-mask", required_argument, NULL, CLI_OPT_VMNET_MASK }, - {"vmnet-interface-id", required_argument, NULL, CLI_OPT_VMNET_INTERFACE_ID}, - {"vmnet-nat66-prefix", required_argument, NULL, CLI_OPT_VMNET_NAT66_PREFIX}, - {"pidfile", required_argument, NULL, 'p' }, - {"help", no_argument, NULL, 'h' }, - {"version", no_argument, NULL, 'v' }, - {0, 0, 0, 0 }, + {"socket-group", required_argument, NULL, CLI_OPT_SOCKET_GROUP }, + {"vmnet-mode", required_argument, NULL, CLI_OPT_VMNET_MODE }, + {"vmnet-interface", required_argument, NULL, CLI_OPT_VMNET_INTERFACE }, + {"vmnet-gateway", required_argument, NULL, CLI_OPT_VMNET_GATEWAY }, + {"vmnet-dhcp-end", required_argument, NULL, CLI_OPT_VMNET_DHCP_END }, + {"vmnet-mask", required_argument, NULL, CLI_OPT_VMNET_MASK }, + {"vmnet-interface-id", required_argument, NULL, CLI_OPT_VMNET_INTERFACE_ID }, + {"vmnet-nat66-prefix", required_argument, NULL, CLI_OPT_VMNET_NAT66_PREFIX }, + {"vmnet-network-identifier", required_argument, NULL, CLI_OPT_VMNET_NETWORK_IDENTIFIER}, + {"pidfile", required_argument, NULL, 'p' }, + {"help", no_argument, NULL, 'h' }, + {"version", no_argument, NULL, 'v' }, + {0, 0, 0, 0 }, }; int opt = 0; while ((opt = getopt_long(argc, argv, "hvp:", longopts, NULL)) != -1) { @@ -134,6 +142,16 @@ struct cli_options *cli_options_parse(int argc, char *argv[]) { case CLI_OPT_VMNET_NAT66_PREFIX: res->vmnet_nat66_prefix = strdup(optarg); break; + case CLI_OPT_VMNET_NETWORK_IDENTIFIER: + if (strcmp(optarg, "random") == 0) { + uuid_generate_random(res->vmnet_network_identifier); + } else if (strcmp(optarg, "") == 0) { + uuid_clear(res->vmnet_network_identifier); + } else if (uuid_parse(optarg, res->vmnet_network_identifier) < 0) { + ERRORF("Failed to parse UUID \"%s\"", optarg); + goto error; + } + break; case 'p': res->pidfile = strdup(optarg); break; @@ -191,7 +209,7 @@ struct cli_options *cli_options_parse(int argc, char *argv[]) { goto error; } if (res->vmnet_gateway == NULL) { - if (res->vmnet_mode != VMNET_BRIDGED_MODE) { + if (res->vmnet_mode != VMNET_BRIDGED_MODE && res->vmnet_mode != VMNET_HOST_MODE) { WARN("--vmnet-gateway=IP should be explicitly specified to " "avoid conflicting with other applications"); } diff --git a/cli.h b/cli.h index f79fe00..8d67c92 100644 --- a/cli.h +++ b/cli.h @@ -20,6 +20,8 @@ struct cli_options { char *vmnet_mask; // --vmnet-interface-id, corresponds to vmnet_interface_id_key uuid_t vmnet_interface_id; + // --vmnet-network-identifier, corresponds to vmnet_network_identifier_key + uuid_t vmnet_network_identifier; // --vmnet-nat66-prefix, corresponds to vmnet_nat66_prefix_key char *vmnet_nat66_prefix; // -p, --pidfile; writes pidfile using permissions of socket_vmnet diff --git a/main.c b/main.c index 2506674..52730b6 100644 --- a/main.c +++ b/main.c @@ -230,10 +230,23 @@ static interface_ref start(struct state *state, struct cli_options *cliopt) { INFOF("Using network interface \"%s\"", cliopt->vmnet_interface); xpc_dictionary_set_string(dict, vmnet_shared_interface_name_key, cliopt->vmnet_interface); } - if (cliopt->vmnet_gateway != NULL) { - xpc_dictionary_set_string(dict, vmnet_start_address_key, cliopt->vmnet_gateway); - xpc_dictionary_set_string(dict, vmnet_end_address_key, cliopt->vmnet_dhcp_end); - xpc_dictionary_set_string(dict, vmnet_subnet_mask_key, cliopt->vmnet_mask); + + // no dhcp + if (cliopt->vmnet_mode == VMNET_HOST_MODE && cliopt->vmnet_gateway == NULL) { + if (!uuid_is_null(cliopt->vmnet_network_identifier)) { + xpc_dictionary_set_uuid(dict, vmnet_network_identifier_key, cliopt->vmnet_network_identifier); + uuid_string_t uuid_str; + uuid_unparse(cliopt->vmnet_network_identifier, uuid_str); + INFOF("Using network identifier \"%s\" and no vmnet gateway -> NO DHCP will be enabled on " + "this vmnet", + uuid_str); + } + } else { + if (cliopt->vmnet_gateway != NULL) { + xpc_dictionary_set_string(dict, vmnet_start_address_key, cliopt->vmnet_gateway); + xpc_dictionary_set_string(dict, vmnet_end_address_key, cliopt->vmnet_dhcp_end); + xpc_dictionary_set_string(dict, vmnet_subnet_mask_key, cliopt->vmnet_mask); + } } xpc_dictionary_set_uuid(dict, vmnet_interface_id_key, cliopt->vmnet_interface_id);