Skip to content

Commit 45cc97e

Browse files
feature: add support for socket reuseport.
1 parent c5b3410 commit 45cc97e

3 files changed

Lines changed: 208 additions & 3 deletions

File tree

src/ngx_stream_lua_socket_udp.c

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,19 @@ static ssize_t ngx_stream_lua_udp_sendmsg(ngx_connection_t *c,
8080
static ngx_int_t ngx_stream_lua_udp_connect_set_transparent(
8181
ngx_stream_lua_udp_connection_t *uc, ngx_socket_t s);
8282
#endif
83+
#if (NGX_HAVE_REUSEPORT)
84+
static ngx_int_t ngx_stream_lua_udp_connect_set_reuseport(
85+
ngx_stream_lua_udp_connection_t *uc, ngx_socket_t s);
86+
#endif
8387
static int ngx_stream_lua_socket_udp_setoption(lua_State *L);
8488

8589

8690
enum {
8791
SOCKET_CTX_INDEX = 1,
8892
SOCKET_TIMEOUT_INDEX = 2,
8993
SOCKET_BIND_INDEX = 3, /* only in upstream cosocket */
90-
SOCKET_IP_TRANSPARENT_INDEX = 4
94+
SOCKET_IP_TRANSPARENT_INDEX = 4,
95+
SOCKET_REUSEPORT_INDEX = 5,
9196
};
9297

9398

@@ -454,6 +459,18 @@ ngx_stream_lua_socket_udp_setpeername(lua_State *L)
454459
lua_pop(L, 1);
455460
#endif
456461

462+
463+
#if (NGX_HAVE_REUSEPORT)
464+
lua_rawgeti(L, 1, SOCKET_REUSEPORT_INDEX);
465+
if (lua_toboolean(L, -1)) {
466+
uc->reuseport = 1;
467+
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0,
468+
"stream lua set UDP upstream with REUSEPORT");
469+
}
470+
471+
lua_pop(L, 1);
472+
#endif
473+
457474
lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX);
458475
timeout = (ngx_int_t) lua_tointeger(L, -1);
459476
lua_pop(L, 1);
@@ -1640,7 +1657,29 @@ ngx_stream_lua_udp_connect_set_transparent(ngx_stream_lua_udp_connection_t *uc,
16401657

16411658
#endif /* SO_BINDANY */
16421659

1643-
return NGX_OK;
1660+
return NGX_OK;
1661+
}
1662+
#endif
1663+
1664+
1665+
#if (NGX_HAVE_REUSEPORT)
1666+
static ngx_int_t
1667+
ngx_stream_lua_udp_connect_set_reuseport(ngx_stream_lua_udp_connection_t *uc,
1668+
ngx_socket_t s)
1669+
{
1670+
int value;
1671+
1672+
value = 1;
1673+
1674+
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT,
1675+
(const void *) &value, sizeof(int)) == -1)
1676+
{
1677+
ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,
1678+
"setsockopt(SO_REUSEPORT) failed");
1679+
return NGX_ERROR;
1680+
}
1681+
1682+
return NGX_OK;
16441683
}
16451684
#endif
16461685

@@ -1741,6 +1780,14 @@ ngx_stream_lua_udp_connect(ngx_stream_lua_socket_udp_upstream_t *u)
17411780
}
17421781
#endif
17431782

1783+
#if (NGX_HAVE_REUSEPORT)
1784+
if (uc->reuseport) {
1785+
if (ngx_stream_lua_udp_connect_set_reuseport(uc, s) != NGX_OK) {
1786+
return NGX_ERROR;
1787+
}
1788+
}
1789+
#endif
1790+
17441791
#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)
17451792
port = u->resolved->port;
17461793
#endif
@@ -1935,6 +1982,20 @@ ngx_stream_lua_socket_udp_setoption(lua_State *L)
19351982
return 1;
19361983
}
19371984

1985+
if (len == sizeof("reuseport") - 1
1986+
&& memcmp(option, "reuseport", len) == 0)
1987+
{
1988+
#if (NGX_HAVE_REUSEPORT)
1989+
int reuseport;
1990+
1991+
reuseport = lua_toboolean(L, 3);
1992+
lua_rawseti(L, 1, SOCKET_REUSEPORT_INDEX);
1993+
lua_pushboolean(L, reuseport);
1994+
#endif
1995+
lua_pushnumber(L, 0);
1996+
return 1;
1997+
}
1998+
19381999
lua_pushnil(L);
19392000
lua_pushfstring(L, "unknown option %s", option);
19402001

src/ngx_stream_lua_socket_udp.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ typedef struct {
4444
struct sockaddr *sockaddr;
4545
socklen_t socklen;
4646
#if (NGX_HAVE_TRANSPARENT_PROXY)
47-
unsigned transparent:1;
47+
unsigned transparent:1;
48+
#endif
49+
#if (NGX_HAVE_REUSEPORT)
50+
unsigned reuseport:1;
4851
#endif
4952
ngx_str_t server;
5053
ngx_log_t log;

t/144-udp-socket-reuseport.t

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# vim:set ft= ts=4 sw=4 et fdm=marker:
2+
3+
our $SkipReason;
4+
5+
BEGIN {
6+
if ($^O ne 'linux') {
7+
$SkipReason = "SO_REUSEPORT is only supported on Linux";
8+
}
9+
}
10+
11+
use Test::Nginx::Socket::Lua::Stream $SkipReason ? (skip_all => $SkipReason) : ();
12+
13+
repeat_each(2);
14+
15+
plan tests => blocks() * (repeat_each() * 3);
16+
17+
run_tests();
18+
19+
__DATA__
20+
21+
=== TEST 1: udp socket setoption reuseport sanity
22+
--- stream_config
23+
server {
24+
listen 127.0.0.1:2986 udp;
25+
content_by_lua_block {
26+
ngx.log(ngx.INFO, "udp reuseport test remote: " .. ngx.var.remote_addr)
27+
}
28+
}
29+
--- stream_server_config
30+
content_by_lua_block {
31+
local ip = "127.0.0.1"
32+
local port = 2986
33+
local sock = ngx.socket.udp()
34+
35+
local ok, err = sock:setoption("reuseport", true)
36+
if not ok then
37+
ngx.log(ngx.ERR, "failed to set reuseport: ", err)
38+
return
39+
end
40+
41+
local ok, err = sock:setpeername(ip, port)
42+
if not ok then
43+
ngx.log(ngx.ERR, "failed to setpeername: ", err)
44+
return
45+
end
46+
47+
local ok, err = sock:send("trigger")
48+
if not ok then
49+
ngx.log(ngx.ERR, "failed to send: ", err)
50+
end
51+
}
52+
53+
--- no_error_log
54+
[error]
55+
--- error_log eval
56+
["stream lua set UDP upstream with REUSEPORT"]
57+
58+
59+
60+
=== TEST 2: udp socket setoption reuseport false
61+
--- stream_config
62+
server {
63+
listen 127.0.0.1:2986 udp;
64+
content_by_lua_block {
65+
ngx.log(ngx.INFO, "udp reuseport test remote: " .. ngx.var.remote_addr)
66+
}
67+
}
68+
--- stream_server_config
69+
content_by_lua_block {
70+
local ip = "127.0.0.1"
71+
local port = 2986
72+
local sock = ngx.socket.udp()
73+
74+
local ok, err = sock:setoption("reuseport", false)
75+
if not ok then
76+
ngx.log(ngx.ERR, "failed to set reuseport: ", err)
77+
return
78+
end
79+
80+
local ok, err = sock:setpeername(ip, port)
81+
if not ok then
82+
ngx.log(ngx.ERR, "failed to setpeername: ", err)
83+
return
84+
end
85+
86+
local ok, err = sock:send("trigger")
87+
if not ok then
88+
ngx.log(ngx.ERR, "failed to send: ", err)
89+
end
90+
}
91+
92+
--- no_error_log
93+
[error]
94+
--- no_error_log eval
95+
["stream lua set UDP upstream with REUSEPORT"]
96+
97+
98+
99+
=== TEST 3: udp socket setoption reuseport with bind
100+
--- stream_config
101+
server {
102+
listen 127.0.0.1:2986 udp;
103+
content_by_lua_block {
104+
ngx.log(ngx.INFO, "udp reuseport test remote: " .. ngx.var.remote_addr)
105+
}
106+
}
107+
--- stream_server_config
108+
content_by_lua_block {
109+
local ip = "127.0.0.1"
110+
local port = 2986
111+
local sock = ngx.socket.udp()
112+
113+
local ok, err = sock:bind(ip)
114+
if not ok then
115+
ngx.log(ngx.ERR, "failed to bind: ", err)
116+
return
117+
end
118+
119+
local ok, err = sock:setoption("reuseport", true)
120+
if not ok then
121+
ngx.log(ngx.ERR, "failed to set reuseport: ", err)
122+
return
123+
end
124+
125+
local ok, err = sock:setpeername("127.0.0.1", port)
126+
if not ok then
127+
ngx.log(ngx.ERR, "failed to setpeername: ", err)
128+
return
129+
end
130+
131+
local ok, err = sock:send("trigger")
132+
if not ok then
133+
ngx.log(ngx.ERR, "failed to send: ", err)
134+
end
135+
}
136+
137+
--- no_error_log
138+
[error]
139+
--- error_log eval
140+
["lua udp socket bind ip: 127.0.0.1",
141+
"stream lua set UDP upstream with REUSEPORT"]

0 commit comments

Comments
 (0)