From ed80f025f6f87f144ee7b63931890efb4421ee78 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 22 Nov 2016 10:06:18 +0000 Subject: This is patch.002 from ticket [0b9d3ba2ba3e1e3fc33c97d5a9fa7ef85d11a696|0b9d3ba2ba], as first start of tip-456 implementation --- generic/tclIOCmd.c | 15 ++++++++++++--- unix/tclUnixSock.c | 20 ++++++++++++++++++++ win/tclWinSock.c | 4 ++-- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index de65da5..9dc8f07 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -1485,12 +1485,12 @@ Tcl_SocketObjCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { static const char *const socketOptions[] = { - "-async", "-myaddr", "-myport", "-server", NULL + "-async", "-myaddr", "-myport", "-server", "-reuseport", NULL }; enum socketOptions { - SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER + SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEPORT }; - int optionIndex, a, server = 0, port, myport = 0, async = 0; + int optionIndex, a, server = 0, port, myport = 0, async = 0, reuseport = 0; const char *host, *myaddr = NULL; Tcl_Obj *script = NULL; Tcl_Channel chan; @@ -1557,6 +1557,9 @@ Tcl_SocketObjCmd( } script = objv[a]; break; + case SKT_REUSEPORT: + reuseport = 1; + break; default: Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions"); } @@ -1600,6 +1603,12 @@ Tcl_SocketObjCmd( Tcl_IncrRefCount(script); acceptCallbackPtr->script = script; acceptCallbackPtr->interp = interp; + + /* Hint for Tcl_OpenTcpServer to set socket option REUSEPORT */ + if(reuseport) { + port |= (1 << 16); + } + chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc, acceptCallbackPtr); if (chan == NULL) { diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index 170aea9..d8a33f9 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -113,6 +113,11 @@ struct TcpState { #define SOCKET_BUFSIZE 4096 +#ifdef SO_REUSEPORT +/* Bitmask to check if the setting of SO_REUSEPORT was requested by the caller. */ +#define USE_SOCK_REUSEPORT (1 << 16) +#endif + /* * Static routines for this file: */ @@ -1435,6 +1440,10 @@ Tcl_OpenTcpServer( char channelName[SOCK_CHAN_LENGTH]; const char *errorMsg = NULL; TcpFdList *fds = NULL, *newfds; +#ifdef SO_REUSEPORT + int reuseport = port & USE_SOCK_REUSEPORT; + CLEAR_BITS(port, USE_SOCK_REUSEPORT); +#endif /* * Try to record and return the most meaningful error message, i.e. the @@ -1512,6 +1521,17 @@ Tcl_OpenTcpServer( (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuseaddr, sizeof(reuseaddr)); +#ifdef SO_REUSEPORT + /* + * Set up to allows multiple sockets on the same host to bind to the same port. + * The flag can be switched on by setting the lowest bit above the valid maximum port (0xffff). + */ + if(reuseport) { + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + (char *) &reuseport, sizeof(reuseport)); + } +#endif + /* * Make sure we use the same port number when opening two server * sockets for IPv4 and IPv6 on a random port. diff --git a/win/tclWinSock.c b/win/tclWinSock.c index ec881d2..af8dda1 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -2111,8 +2111,8 @@ Tcl_OpenTcpServer( /* * Bind to the specified port. Note that we must not call - * setsockopt with SO_REUSEADDR because Microsoft allows addresses - * to be reused even if they are still in use. + * setsockopt with SO_REUSEADDR or SO_REUSEPORT because Microsoft + * allows addresses and ports to be reused even if they are still in use. * * Bind should not be affected by the socket having already been * set into nonblocking mode. If there is trouble, this is one -- cgit v0.12