diff --git a/src/cartesi-machine.lua b/src/cartesi-machine.lua index 399ff3ded..29535f22c 100755 --- a/src/cartesi-machine.lua +++ b/src/cartesi-machine.lua @@ -361,7 +361,7 @@ where options are: NON REPRODUCIBLE OPTION, DON'T USE THIS OPTION IN PRODUCTION - --virtio-net=user[,:[,...]...] + --virtio-net=user add a VirtIO network device using host user-space networking. this allows the use of the host network from inside the machine. you don't need root privilege or any configuration in the host to use this. @@ -385,17 +385,9 @@ where options are: DHCP Start: 10.0.2.15 Nameserver: 10.0.2.3 - : is one of - hostfwd:[tcp|udp]:hostport[:guestport] - redirect incoming TCP or UDP connections. - bind the host 127.0.0.1:hostport to the guest 10.0.2.15:guestport. - if connection type is absent, TCP is assumed. - if guest port is absent, it's set to the same as host port. - you can pass this option multiple times. - NON REPRODUCIBLE OPTION, DON'T USE THIS OPTION IN PRODUCTION - -n[=...] or --network[=...] + -n or --network like --virtio-net=user, but automatically appends init commands to initialize the network in the guest. @@ -403,6 +395,19 @@ where options are: NON REPRODUCIBLE OPTION, DON'T USE THIS OPTION IN PRODUCTION + -p=... or --port-forward=[hostip:]hostport[:guestip][:guestport][/protocol] + redirect incoming TCP or UDP connections. + bind the host hostip:hostport to the guest guestip:guestport. + protocol can be "tcp" or "udp". + if host ip is absent, it's set to "127.0.0.1". + if guest ip is absent, it's set to "10.0.2.15". + if guest port is absent, it's set to the same as host port. + if protocol is absent, it's set to "tcp". + you can pass this option multiple times. + this options requires --network or --virtio-net=user option. + + NON REPRODUCIBLE OPTION, DON'T USE THIS OPTION IN PRODUCTION + -i or --htif-console-getchar run in interactive mode using a HTIF console device. @@ -570,6 +575,7 @@ local flash_start = {} local flash_length = {} local unreproducible = false local virtio = {} +local virtio_net_user_config = false local virtio_volume_count = 0 local has_virtio_console = false local has_network = false @@ -676,37 +682,60 @@ local function handle_htif_console_getchar(all) return true end -local function parse_virtio_net_user_config(opts) - local o = opts ~= "" and util.parse_options(opts, { - hostfwd = "array", - }) or {} - local vdev_config = { type = "net-user" } - if o.hostfwd then - for _, hostfwd in ipairs(o.hostfwd) do - local is_udp = nil - local host_port, guest_port = hostfwd:match("^([0-9]+):?([0-9]*)$") +local function parse_ipv4(s) + local a, b, c, d = s:match("^([0-9]+)%.([0-9]+)%.([0-9]+)%.([0-9]+)$") + a, b, c, d = tonumber(a), tonumber(b), tonumber(c), tonumber(d) + assert(a and b and c and d and a <= 255 and b <= 255 and c <= 255 and d <= 255, "malformed IPv4 " .. s) + return (a << 24) | (b << 18) | (c << 8) | d +end + +local function handle_port_forward_option(opts) + if not opts then return false end + assert(virtio_net_user_config, "--port-forward option requires --network or --virtio-net=user option") + local host_ip, guest_ip, host_port, guest_port, proto + for s in opts:gmatch("[%w.]+") do + if (not host_port or not guest_port) and s:find("^[0-9]+$") then if not host_port then - host_port, guest_port = hostfwd:match("^tcp:([0-9]+):?([0-9]*)$") + host_port = tonumber(s) + else + guest_port = tonumber(s) end - if not host_port then - host_port, guest_port = hostfwd:match("^udp:([0-9]+):?([0-9]*)$") - is_udp = true + elseif (not host_ip or not guest_ip) and s:find("^[0-9]+%.[0-9]+%.[0-9]+%.[0-9]+$") then + if not host_ip then + host_ip = parse_ipv4(s) + else + guest_ip = parse_ipv4(s) end - host_port = tonumber(host_port) - guest_port = guest_port ~= "" and tonumber(guest_port) or host_port - assert(host_port and guest_port, "malformed hostfwd option") - vdev_config.hostfwd = vdev_config.hostfwd or {} - table.insert(vdev_config.hostfwd, { is_udp = is_udp, host_port = host_port, guest_port = guest_port }) + elseif proto == nil and (s == "tcp" or s == "udp") then + proto = s + else + error("malformed --port-forward option") end end - return vdev_config + host_ip = host_ip or parse_ipv4("127.0.0.1") + guest_ip = guest_ip or parse_ipv4("10.0.2.15") + assert(host_port, "malformed --port-forward option") + guest_port = guest_port or host_port + local is_udp = proto == "udp" + virtio_net_user_config.hostfwd = virtio_net_user_config.hostfwd or {} + table.insert(virtio_net_user_config.hostfwd, { + is_udp = is_udp, + host_ip = host_ip, + guest_ip = guest_ip, + host_port = host_port, + guest_port = guest_port, + }) + return true end local function handle_virtio_net(mode, opts) if not mode then return false end unreproducible = true if mode == "user" then - table.insert(virtio, parse_virtio_net_user_config(opts)) + if not virtio_net_user_config then + virtio_net_user_config = { type = "net-user" } + table.insert(virtio, virtio_net_user_config) + end else table.insert(virtio, { type = "net-tuntap", iface = opts }) end @@ -716,8 +745,8 @@ end local function handle_network_option(opts) if not opts then return false end if has_network then return true end + handle_virtio_net("user") has_network = true - table.insert(virtio, parse_virtio_net_user_config(opts)) -- initialize network append_init = append_init .. [[ @@ -924,6 +953,14 @@ local options = { "^%-n=?([%w:,]*)$", handle_network_option, }, + { + "^%-%-port-forward=([0-9:.]+/?[udptcp]*)$", + handle_port_forward_option, + }, + { + "^%-p=([0-9:.]+/?[udptcp]*)$", + handle_port_forward_option, + }, { "^%-%-htif%-console%-getchar$", handle_htif_console_getchar, diff --git a/src/clua-machine-util.cpp b/src/clua-machine-util.cpp index 6eecce92b..4643e416b 100644 --- a/src/clua-machine-util.cpp +++ b/src/clua-machine-util.cpp @@ -893,6 +893,8 @@ static void push_cm_memory_range_config(lua_State *L, const cm_memory_range_conf static void push_cm_virtio_hostfwd_config(lua_State *L, const cm_virtio_hostfwd_config *m) { lua_newtable(L); clua_setbooleanfield(L, m->is_udp, "is_udp", -1); + clua_setintegerfield(L, m->host_ip, "host_ip", -1); + clua_setintegerfield(L, m->guest_ip, "guest_ip", -1); clua_setintegerfield(L, m->host_port, "host_port", -1); clua_setintegerfield(L, m->guest_port, "guest_port", -1); } @@ -1101,8 +1103,10 @@ cm_virtio_hostfwd_config *clua_check_cm_virtio_hostfwd_config(lua_State *L, int luaL_error(L, "%s virtio hostfwd not a table", what); } m->is_udp = opt_boolean_field(L, tabidx, "is_udp"); - m->guest_port = check_uint_field(L, tabidx, "guest_port"); + m->host_ip = check_uint_field(L, tabidx, "host_ip"); + m->guest_ip = check_uint_field(L, tabidx, "guest_ip"); m->host_port = check_uint_field(L, tabidx, "host_port"); + m->guest_port = check_uint_field(L, tabidx, "guest_port"); return m; } diff --git a/src/machine-c-api.cpp b/src/machine-c-api.cpp index a23824fcd..037a74777 100644 --- a/src/machine-c-api.cpp +++ b/src/machine-c-api.cpp @@ -229,16 +229,18 @@ static cartesi::virtio_hostfwd_config convert_from_c(const cm_virtio_hostfwd_con if (c_config == nullptr) { throw std::invalid_argument("invalid memory range configuration"); } - cartesi::virtio_hostfwd_config new_cpp_virtio_hostfwd_config{c_config->is_udp, c_config->host_port, - c_config->guest_port}; + cartesi::virtio_hostfwd_config new_cpp_virtio_hostfwd_config{c_config->is_udp, c_config->host_ip, + c_config->guest_ip, c_config->host_port, c_config->guest_port}; return new_cpp_virtio_hostfwd_config; } static cm_virtio_hostfwd_config convert_to_c(const cartesi::virtio_hostfwd_config &cpp_config) { cm_virtio_hostfwd_config new_c_virtio_hostfwd_config{}; new_c_virtio_hostfwd_config.is_udp = cpp_config.is_udp; - new_c_virtio_hostfwd_config.guest_port = cpp_config.guest_port; + new_c_virtio_hostfwd_config.host_ip = cpp_config.host_ip; + new_c_virtio_hostfwd_config.guest_ip = cpp_config.guest_ip; new_c_virtio_hostfwd_config.host_port = cpp_config.host_port; + new_c_virtio_hostfwd_config.guest_port = cpp_config.guest_port; return new_c_virtio_hostfwd_config; } diff --git a/src/machine-c-api.h b/src/machine-c-api.h index 5f565bb1b..cccfb173d 100644 --- a/src/machine-c-api.h +++ b/src/machine-c-api.h @@ -257,6 +257,8 @@ typedef struct { /// \brief VirtIO host forward state config typedef struct cm_virtio_hostfwd_config { bool is_udp; + uint64_t host_ip; + uint64_t guest_ip; uint16_t host_port; uint16_t guest_port; } cm_virtio_hostfwd_config; diff --git a/src/machine-config.h b/src/machine-config.h index afd8beb0b..e58ee2e99 100644 --- a/src/machine-config.h +++ b/src/machine-config.h @@ -138,6 +138,8 @@ struct virtio_p9fs_config final { /// \brief VirtIO host forward state config struct virtio_hostfwd_config final { bool is_udp{false}; + uint64_t host_ip{0}; + uint64_t guest_ip{0}; uint16_t host_port{0}; uint16_t guest_port{0}; }; diff --git a/src/virtio-net-carrier-slirp.cpp b/src/virtio-net-carrier-slirp.cpp index 04b2e5d25..018c7d7a7 100644 --- a/src/virtio-net-carrier-slirp.cpp +++ b/src/virtio-net-carrier-slirp.cpp @@ -206,9 +206,8 @@ virtio_net_carrier_slirp::virtio_net_carrier_slirp(const cartesi::virtio_net_use for (const auto &hostfwd : config.hostfwd) { struct in_addr host_addr {}; struct in_addr guest_addr {}; - // ??(edubart): Should we allow configuring host/guest addrs? - host_addr.s_addr = htonl(INADDR_LOOPBACK); - guest_addr.s_addr = htonl(SLIRP_DEFAULT_IPV4_VDHCP_START); + host_addr.s_addr = htonl(hostfwd.host_ip); + guest_addr.s_addr = htonl(hostfwd.guest_ip); if (slirp_add_hostfwd(slirp, hostfwd.is_udp, host_addr, hostfwd.host_port, guest_addr, hostfwd.guest_port) < 0) { throw std::system_error{errno, std::generic_category(),