Skip to content

Commit

Permalink
udp: Add udp_set_source_membership binding + improve udp_set_membership
Browse files Browse the repository at this point in the history
- Allows the interface argument of udp_set_membership to be nil (meaning a NULL const char* passed to uv_udp_set_membership). This was needed to be able to port the libuv multicast join tests
- Adds a binding for udp_set_source_membership (contributes towards #410)
- Ports the test-udp-multicast-join and test-udp-multicast-join6 tests from libuv
  • Loading branch information
squeek502 committed May 11, 2020
1 parent ffe83b1 commit 5a9f2a8
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 2 deletions.
19 changes: 18 additions & 1 deletion docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1884,7 +1884,7 @@ Get the remote IP and port of the UDP handle on connected UDP handles.
**Parameters:**
- `udp`: `uv_udp_t userdata`
- `multicast_addr`: `string`
- `interface_addr`: `string`
- `interface_addr`: `string` or `nil`
- `membership`: `string`

Set membership for a multicast address. `multicast_addr` is multicast address to
Expand All @@ -1893,6 +1893,23 @@ the string `"leave"` or `"join"`.

**Returns:** `0` or `fail`

### `uv.udp_set_source_membership(udp, multicast_addr, interface_addr, source_addr, membership)`

> method form `udp:set_source_membership(multicast_addr, interface_addr, source_addr, membership)`
**Parameters:**
- `udp`: `uv_udp_t userdata`
- `multicast_addr`: `string`
- `interface_addr`: `string` or `nil`
- `source_addr`: `string`
- `membership`: `string`

Set membership for a source-specific multicast group. `multicast_addr` is multicast
address to set membership for. `interface_addr` is interface address. `source_addr`
is source address. `membership` can be the string `"leave"` or `"join"`.

**Returns:** `0` or `fail`

### `uv.udp_set_multicast_loop(udp, on)`

> method form `udp:set_multicast_loop(on)`
Expand Down
6 changes: 6 additions & 0 deletions src/luv.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ static const luaL_Reg luv_functions[] = {
{"udp_bind", luv_udp_bind},
{"udp_getsockname", luv_udp_getsockname},
{"udp_set_membership", luv_udp_set_membership},
#if LUV_UV_VERSION_GEQ(1, 32, 0)
{"udp_set_source_membership", luv_udp_set_source_membership},
#endif
{"udp_set_multicast_loop", luv_udp_set_multicast_loop},
{"udp_set_multicast_ttl", luv_udp_set_multicast_ttl},
{"udp_set_multicast_interface", luv_udp_set_multicast_interface},
Expand Down Expand Up @@ -488,6 +491,9 @@ static const luaL_Reg luv_udp_methods[] = {
{"bind", luv_udp_bind},
{"getsockname", luv_udp_getsockname},
{"set_membership", luv_udp_set_membership},
#if LUV_UV_VERSION_GEQ(1, 32, 0)
{"set_source_membership", luv_udp_set_source_membership},
#endif
{"set_multicast_loop", luv_udp_set_multicast_loop},
{"set_multicast_ttl", luv_udp_set_multicast_ttl},
{"set_multicast_interface", luv_udp_set_multicast_interface},
Expand Down
16 changes: 15 additions & 1 deletion src/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,26 @@ static const char *const luv_membership_opts[] = {
static int luv_udp_set_membership(lua_State* L) {
uv_udp_t* handle = luv_check_udp(L, 1);
const char* multicast_addr = luaL_checkstring(L, 2);
const char* interface_addr = luaL_checkstring(L, 3);
const char* interface_addr = lua_isstring(L, 3) ? lua_tostring(L, 3) : NULL;
luaL_argcheck(L, lua_isstring(L, 3) || lua_isnil(L, 3), 3, "expected string or nil");
uv_membership membership = (uv_membership)luaL_checkoption(L, 4, NULL, luv_membership_opts);
int ret = uv_udp_set_membership(handle, multicast_addr, interface_addr, membership);
return luv_result(L, ret);
}

#if LUV_UV_VERSION_GEQ(1, 32, 0)
static int luv_udp_set_source_membership(lua_State* L) {
uv_udp_t* handle = luv_check_udp(L, 1);
const char* multicast_addr = luaL_checkstring(L, 2);
const char* interface_addr = lua_isstring(L, 3) ? lua_tostring(L, 3) : NULL;
luaL_argcheck(L, lua_isstring(L, 3) || lua_isnil(L, 3), 3, "expected string or nil");
const char* source_addr = luaL_checkstring(L, 4);
uv_membership membership = (uv_membership)luaL_checkoption(L, 5, NULL, luv_membership_opts);
int ret = uv_udp_set_source_membership(handle, multicast_addr, interface_addr, source_addr, membership);
return luv_result(L, ret);
}
#endif

static int luv_udp_set_multicast_loop(lua_State* L) {
uv_udp_t* handle = luv_check_udp(L, 1);
int on, ret;
Expand Down
74 changes: 74 additions & 0 deletions tests/test-udp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,78 @@ return require('lib/tap')(function (test)
end))
end)))
end, "1.27.0")

-- return a test function reusable for ipv4 and ipv6
local function multicast_join_test(bind_addr, multicast_addr, interface_addr)
return function(print, p, expect, uv)
local uvVersionGEQ = require('lib/utils').uvVersionGEQ

local server = assert(uv.new_udp())
assert(uv.udp_bind(server, bind_addr, TEST_PORT))
local _, err, errname = uv.udp_set_membership(server, multicast_addr, interface_addr, "join")
if errname == "ENODEV" then
print("no ipv6 multicast route, skipping")
server:close()
return
end
assert(not err, err)

local client = assert(uv.new_udp())

local recv_cb_called = 0
local function recv_cb(err, data, addr, flags)
assert(not err, err)
p(data, addr)

assert(addr)
assert(data == "PING")

recv_cb_called = recv_cb_called + 1
if recv_cb_called == 2 then
server:close()
else
-- udp_set_source_membership added in 1.32.0
if uvVersionGEQ("1.32.0") then
local source_addr = addr.ip
assert(server:set_membership(multicast_addr, interface_addr, "leave"))
assert(server:set_source_membership(multicast_addr, interface_addr, source_addr, "join"))
end
assert(client:send("PING", multicast_addr, TEST_PORT, expect(function(err)
assert(not err, err)
client:close()
end)))
end
end

server:recv_start(expect(recv_cb, 2))

assert(client:send("PING", multicast_addr, TEST_PORT, expect(function(err)
assert(not err, err)
end)))
end
end

test("udp multicast join ipv4", multicast_join_test("0.0.0.0", "239.255.0.1", nil))

test("udp multicast join ipv6", function(print, p, expect, uv)
local function can_ipv6_external()
local addresses = assert(uv.interface_addresses())
for _, vals in pairs(addresses) do
for _, info in ipairs(vals) do
if info.family == "inet6" and not info.internal then
return true
end
end
end
return false
end

if not can_ipv6_external() then
print("no ipv6 support, skipping")
return
end

local testfn = multicast_join_test("::", "ff02::1", nil)
return testfn(print, p, expect, uv)
end)
end)

0 comments on commit 5a9f2a8

Please sign in to comment.