Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

udp: Add udp_set_source_membership binding + improve udp_set_membership #491

Merged
merged 5 commits into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion .ci/bindcov.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ skipped+=(
uv_loop_size uv_loop_fork uv_loop_get_data uv_loop_set_data uv_strerror uv_strerror_r uv_err_name
uv_err_name_r uv_handle_size uv_handle_get_type uv_handle_type_name uv_handle_get_data
uv_handle_get_loop uv_handle_set_data uv_req_size uv_req_get_data uv_req_set_data uv_req_get_type
uv_req_type_name uv_udp_set_source_membership uv_pipe_chmod uv_process_get_pid uv_get_osfhandle
uv_req_type_name uv_pipe_chmod uv_process_get_pid uv_get_osfhandle
uv_open_osfhandle uv_fs_get_type uv_fs_get_result uv_fs_get_ptr uv_fs_get_path uv_fs_get_statbuf
uv_ip4_addr uv_ip6_addr uv_ip4_name uv_ip6_name uv_inet_ntop uv_inet_pton uv_dlopen uv_dlclose
uv_dlsym uv_dlerror
Expand Down
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
87 changes: 87 additions & 0 deletions tests/test-udp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,91 @@ 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
elseif errname == "EADDRNOTAVAIL" and multicast_addr == "ff02::1" then
-- OSX, BSDs, and some other platforms need %lo in their multicast/interface addr
-- so try that instead
multicast_addr = "ff02::1%lo0"
interface_addr = "::1%lo0"
assert(uv.udp_set_membership(server, multicast_addr, interface_addr, "join"))
else
assert(not err, err)
end

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"))
_, err, errname = server:set_source_membership(multicast_addr, interface_addr, source_addr, "join")
if errname == "ENOSYS" then
-- not all systems support set_source_membership, so rejoin the previous group and continue on
assert(server:set_membership(multicast_addr, interface_addr, "join"))
else
assert(not err, err)
end
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)