diff --git a/src/ngx_stream_lua_socket_udp.c b/src/ngx_stream_lua_socket_udp.c index 08bed2a3..b7e0a4ae 100644 --- a/src/ngx_stream_lua_socket_udp.c +++ b/src/ngx_stream_lua_socket_udp.c @@ -80,6 +80,10 @@ static ssize_t ngx_stream_lua_udp_sendmsg(ngx_connection_t *c, static ngx_int_t ngx_stream_lua_udp_connect_set_transparent( ngx_stream_lua_udp_connection_t *uc, ngx_socket_t s); #endif +#if (NGX_HAVE_REUSEPORT) +static ngx_int_t ngx_stream_lua_udp_connect_set_reuseport( + ngx_stream_lua_udp_connection_t *uc, ngx_socket_t s); +#endif static int ngx_stream_lua_socket_udp_setoption(lua_State *L); @@ -87,7 +91,8 @@ enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2, SOCKET_BIND_INDEX = 3, /* only in upstream cosocket */ - SOCKET_IP_TRANSPARENT_INDEX = 4 + SOCKET_IP_TRANSPARENT_INDEX = 4, + SOCKET_REUSEPORT_INDEX = 5, }; @@ -454,6 +459,18 @@ ngx_stream_lua_socket_udp_setpeername(lua_State *L) lua_pop(L, 1); #endif + +#if (NGX_HAVE_REUSEPORT) + lua_rawgeti(L, 1, SOCKET_REUSEPORT_INDEX); + if (lua_toboolean(L, -1)) { + uc->reuseport = 1; + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua set UDP upstream with REUSEPORT"); + } + + lua_pop(L, 1); +#endif + lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX); timeout = (ngx_int_t) lua_tointeger(L, -1); lua_pop(L, 1); @@ -1640,7 +1657,29 @@ ngx_stream_lua_udp_connect_set_transparent(ngx_stream_lua_udp_connection_t *uc, #endif /* SO_BINDANY */ -return NGX_OK; + return NGX_OK; +} +#endif + + +#if (NGX_HAVE_REUSEPORT) +static ngx_int_t +ngx_stream_lua_udp_connect_set_reuseport(ngx_stream_lua_udp_connection_t *uc, + ngx_socket_t s) +{ + int value; + + value = 1; + + if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + "setsockopt(SO_REUSEPORT) failed"); + return NGX_ERROR; + } + + return NGX_OK; } #endif @@ -1741,6 +1780,14 @@ ngx_stream_lua_udp_connect(ngx_stream_lua_socket_udp_upstream_t *u) } #endif +#if (NGX_HAVE_REUSEPORT) + if (uc->reuseport) { + if (ngx_stream_lua_udp_connect_set_reuseport(uc, s) != NGX_OK) { + return NGX_ERROR; + } + } +#endif + #if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX) port = u->resolved->port; #endif @@ -1935,6 +1982,20 @@ ngx_stream_lua_socket_udp_setoption(lua_State *L) return 1; } + if (len == sizeof("reuseport") - 1 + && memcmp(option, "reuseport", len) == 0) + { +#if (NGX_HAVE_REUSEPORT) + int reuseport; + + reuseport = lua_toboolean(L, 3); + lua_rawseti(L, 1, SOCKET_REUSEPORT_INDEX); + lua_pushboolean(L, reuseport); +#endif + lua_pushnumber(L, 0); + return 1; + } + lua_pushnil(L); lua_pushfstring(L, "unknown option %s", option); diff --git a/src/ngx_stream_lua_socket_udp.h b/src/ngx_stream_lua_socket_udp.h index 31851716..874078ca 100644 --- a/src/ngx_stream_lua_socket_udp.h +++ b/src/ngx_stream_lua_socket_udp.h @@ -44,7 +44,10 @@ typedef struct { struct sockaddr *sockaddr; socklen_t socklen; #if (NGX_HAVE_TRANSPARENT_PROXY) - unsigned transparent:1; + unsigned transparent:1; +#endif +#if (NGX_HAVE_REUSEPORT) + unsigned reuseport:1; #endif ngx_str_t server; ngx_log_t log; diff --git a/t/144-udp-socket-reuseport.t b/t/144-udp-socket-reuseport.t new file mode 100644 index 00000000..4039aef0 --- /dev/null +++ b/t/144-udp-socket-reuseport.t @@ -0,0 +1,141 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($^O ne 'linux') { + $SkipReason = "SO_REUSEPORT is only supported on Linux"; + } +} + +use Test::Nginx::Socket::Lua::Stream $SkipReason ? (skip_all => $SkipReason) : (); + +repeat_each(2); + +plan tests => blocks() * (repeat_each() * 3); + +run_tests(); + +__DATA__ + +=== TEST 1: udp socket setoption reuseport sanity +--- stream_config +server { + listen 127.0.0.1:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "udp reuseport test remote: " .. ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:setoption("reuseport", true) + if not ok then + ngx.log(ngx.ERR, "failed to set reuseport: ", err) + return + end + + local ok, err = sock:setpeername(ip, port) + if not ok then + ngx.log(ngx.ERR, "failed to setpeername: ", err) + return + end + + local ok, err = sock:send("trigger") + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + end + } + +--- no_error_log +[error] +--- error_log eval +["stream lua set UDP upstream with REUSEPORT"] + + + +=== TEST 2: udp socket setoption reuseport false +--- stream_config +server { + listen 127.0.0.1:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "udp reuseport test remote: " .. ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:setoption("reuseport", false) + if not ok then + ngx.log(ngx.ERR, "failed to set reuseport: ", err) + return + end + + local ok, err = sock:setpeername(ip, port) + if not ok then + ngx.log(ngx.ERR, "failed to setpeername: ", err) + return + end + + local ok, err = sock:send("trigger") + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + end + } + +--- no_error_log +[error] +--- no_error_log eval +["stream lua set UDP upstream with REUSEPORT"] + + + +=== TEST 3: udp socket setoption reuseport with bind +--- stream_config +server { + listen 127.0.0.1:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "udp reuseport test remote: " .. ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, "failed to bind: ", err) + return + end + + local ok, err = sock:setoption("reuseport", true) + if not ok then + ngx.log(ngx.ERR, "failed to set reuseport: ", err) + return + end + + local ok, err = sock:setpeername("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, "failed to setpeername: ", err) + return + end + + local ok, err = sock:send("trigger") + if not ok then + ngx.log(ngx.ERR, "failed to send: ", err) + end + } + +--- no_error_log +[error] +--- error_log eval +["lua udp socket bind ip: 127.0.0.1", +"stream lua set UDP upstream with REUSEPORT"]