Skip to content

Commit

Permalink
feat: support binding different host addresses in VirtIO net device
Browse files Browse the repository at this point in the history
  • Loading branch information
edubart committed Jan 25, 2024
1 parent c9ef117 commit fd1a9e1
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 38 deletions.
99 changes: 68 additions & 31 deletions src/cartesi-machine.lua
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ where options are:
NON REPRODUCIBLE OPTION, DON'T USE THIS OPTION IN PRODUCTION
--virtio-net=user[,<key>:<value>[,...]...]
--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.
Expand All @@ -385,24 +385,29 @@ where options are:
DHCP Start: 10.0.2.15
Nameserver: 10.0.2.3
<key>:<value> 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.
this option implies --sync-init-date.
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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
.. [[
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 5 additions & 1 deletion src/clua-machine-util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}

Expand Down
8 changes: 5 additions & 3 deletions src/machine-c-api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
2 changes: 2 additions & 0 deletions src/machine-c-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/machine-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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};
};
Expand Down
5 changes: 2 additions & 3 deletions src/virtio-net-carrier-slirp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down

0 comments on commit fd1a9e1

Please sign in to comment.