2491. [func] Attempt to re-use a local port if we are already using

the port. [RT #18548]
This commit is contained in:
Mark Andrews 2008-11-12 23:10:57 +00:00
parent dc143a8f5c
commit 96b3cb85d3
2 changed files with 140 additions and 8 deletions

View file

@ -1,3 +1,6 @@
2491. [func] Attempt to re-use a local port if we are already using
the port. [RT #18548]
2490. [port] aix: work around a kernel bug where IPV6_RECVPKTINFO
is cleared when IPV6_V6ONLY is set. [RT #18785]

View file

@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: dispatch.c,v 1.154 2008/09/04 00:23:14 jinmei Exp $ */
/* $Id: dispatch.c,v 1.155 2008/11/12 23:10:57 marka Exp $ */
/*! \file */
@ -49,9 +49,12 @@
typedef ISC_LIST(dns_dispentry_t) dns_displist_t;
typedef struct dispsocket dispsocket_t;
typedef struct dispsocket dispsocket_t;
typedef ISC_LIST(dispsocket_t) dispsocketlist_t;
typedef struct dispportentry dispportentry_t;
typedef ISC_LIST(dispportentry_t) dispportlist_t;
/* ARC4 Random generator state */
typedef struct arc4ctx {
isc_uint8_t i;
@ -172,7 +175,8 @@ struct dispsocket {
isc_socket_t *socket;
dns_dispatch_t *disp;
isc_sockaddr_t host;
in_port_t localport;
in_port_t localport; /* XXX: should be removed later */
dispportentry_t *portentry;
dns_dispentry_t *resp;
isc_task_t *task;
ISC_LINK(dispsocket_t) link;
@ -180,6 +184,21 @@ struct dispsocket {
ISC_LINK(dispsocket_t) blink;
};
/*%
* A port table entry. We remember every port we first open in a table with a
* reference counter so that we can 'reuse' the same port (with different
* destination addresses) using the SO_REUSEADDR socket option.
*/
struct dispportentry {
in_port_t port;
unsigned int refs;
ISC_LINK(struct dispportentry) link;
};
#ifndef DNS_DISPATCH_PORTTABLESIZE
#define DNS_DISPATCH_PORTTABLESIZE 1024
#endif
#define INVALID_BUCKET (0xffffdead)
/*%
@ -229,6 +248,8 @@ struct dns_dispatch {
dns_tcpmsg_t tcpmsg; /*%< for tcp streams */
dns_qid_t *qid;
arc4ctx_t arc4ctx; /*%< for QID/UDP port num */
dispportlist_t *port_table; /*%< hold ports 'owned' by us */
isc_mempool_t *portpool; /*%< port table entries */
};
#define QID_MAGIC ISC_MAGIC('Q', 'i', 'd', ' ')
@ -678,6 +699,64 @@ destroy_disp(isc_task_t *task, isc_event_t *event) {
destroy_mgr(&mgr);
}
/*%
* Manipulate port table per dispatch: find an entry for a given port number,
* create a new entry, and decrement a given entry with possible clean-up.
*/
static dispportentry_t *
port_search(dns_dispatch_t *disp, in_port_t port) {
dispportentry_t *portentry;
REQUIRE(disp->port_table != NULL);
portentry = ISC_LIST_HEAD(disp->port_table[port %
DNS_DISPATCH_PORTTABLESIZE]);
while (portentry != NULL) {
if (portentry->port == port)
return (portentry);
portentry = ISC_LIST_NEXT(portentry, link);
}
return (NULL);
}
static dispportentry_t *
new_portentry(dns_dispatch_t *disp, in_port_t port) {
dispportentry_t *portentry;
REQUIRE(disp->port_table != NULL);
portentry = isc_mempool_get(disp->portpool);
if (portentry == NULL)
return (portentry);
portentry->port = port;
portentry->refs = 0;
ISC_LINK_INIT(portentry, link);
ISC_LIST_APPEND(disp->port_table[port % DNS_DISPATCH_PORTTABLESIZE],
portentry, link);
return (portentry);
}
static void
deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) {
dispportentry_t *portentry = *portentryp;
REQUIRE(disp->port_table != NULL);
REQUIRE(portentry != NULL && portentry->refs > 0);
portentry->refs--;
if (portentry->refs == 0) {
ISC_LIST_UNLINK(disp->port_table[portentry->port %
DNS_DISPATCH_PORTTABLESIZE],
portentry, link);
isc_mempool_put(disp->portpool, portentry);
}
*portentryp = NULL;
}
/*%
* Find a dispsocket for socket address 'dest', and port number 'port'.
* Return NULL if no such entry exists.
@ -694,7 +773,7 @@ socket_search(dns_qid_t *qid, isc_sockaddr_t *dest, in_port_t port,
while (dispsock != NULL) {
if (isc_sockaddr_equal(dest, &dispsock->host) &&
dispsock->localport == port)
dispsock->portentry->port == port)
return (dispsock);
dispsock = ISC_LIST_NEXT(dispsock, blink);
}
@ -722,6 +801,8 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
dispsocket_t *dispsock;
unsigned int nports;
in_port_t *ports;
unsigned int bindoptions = 0;
dispportentry_t *portentry = NULL;
if (isc_sockaddr_pf(&disp->local) == AF_INET) {
nports = disp->mgr->nv4ports;
@ -747,6 +828,7 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
dispsock->socket = NULL;
dispsock->disp = disp;
dispsock->resp = NULL;
dispsock->portentry = NULL;
isc_random_get(&r);
dispsock->task = NULL;
isc_task_attach(disp->task[r % disp->ntasks], &dispsock->task);
@ -769,16 +851,28 @@ get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
bucket = dns_hash(qid, dest, 0, port);
if (socket_search(qid, dest, port, bucket) != NULL)
continue;
result = open_socket(sockmgr, &localaddr, 0, &sock);
if (result == ISC_R_SUCCESS || result != ISC_R_ADDRINUSE)
portentry = port_search(disp, port);
if (portentry != NULL)
bindoptions |= ISC_SOCKET_REUSEADDRESS;
result = open_socket(sockmgr, &localaddr, bindoptions, &sock);
if (result == ISC_R_SUCCESS) {
if (portentry == NULL) {
portentry = new_portentry(disp, port);
if (portentry == NULL) {
result = ISC_R_NOMEMORY;
break;
}
}
portentry->refs++;
break;
} else if (result != ISC_R_ADDRINUSE)
break;
}
if (result == ISC_R_SUCCESS) {
dispsock->socket = sock;
dispsock->host = *dest;
dispsock->localport = port;
dispsock->portentry = portentry;
dispsock->bucket = bucket;
ISC_LIST_APPEND(qid->sock_table[bucket], dispsock, blink);
*dispsockp = dispsock;
@ -815,6 +909,8 @@ destroy_dispsocket(dns_dispatch_t *disp, dispsocket_t **dispsockp) {
disp->nsockets--;
dispsock->magic = 0;
if (dispsock->portentry != NULL)
deref_portentry(disp, &dispsock->portentry);
if (dispsock->socket != NULL)
isc_socket_detach(&dispsock->socket);
if (ISC_LINK_LINKED(dispsock, blink)) {
@ -849,6 +945,9 @@ deactivate_dispsocket(dns_dispatch_t *disp, dispsocket_t *dispsock) {
dispsock->resp->dispsocket = NULL;
}
INSIST(dispsock->portentry != NULL);
deref_portentry(disp, &dispsock->portentry);
if (disp->nsockets > DNS_DISPATCH_POOLSOCKS)
destroy_dispsocket(disp, &dispsock);
else {
@ -2289,6 +2388,8 @@ dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests,
ISC_LIST_INIT(disp->inactivesockets);
disp->nsockets = 0;
dispatch_arc4init(&disp->arc4ctx, mgr->entropy, NULL);
disp->port_table = NULL;
disp->portpool = NULL;
result = isc_mutex_init(&disp->lock);
if (result != ISC_R_SUCCESS)
@ -2325,6 +2426,7 @@ dispatch_free(dns_dispatch_t **dispp)
{
dns_dispatch_t *disp;
dns_dispatchmgr_t *mgr;
int i;
REQUIRE(VALID_DISPATCH(*dispp));
disp = *dispp;
@ -2349,6 +2451,18 @@ dispatch_free(dns_dispatch_t **dispp)
if (disp->qid != NULL)
qid_destroy(mgr->mctx, &disp->qid);
if (disp->port_table != NULL) {
for (i = 0; i < DNS_DISPATCH_PORTTABLESIZE; i++)
INSIST(ISC_LIST_EMPTY(disp->port_table[i]));
isc_mem_put(mgr->mctx, disp->port_table,
sizeof(disp->port_table[0]) *
DNS_DISPATCH_PORTTABLESIZE);
}
if (disp->portpool != NULL)
isc_mempool_destroy(&disp->portpool);
disp->mgr = NULL;
DESTROYLOCK(&disp->lock);
disp->magic = 0;
@ -2669,6 +2783,21 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr,
if (result != ISC_R_SUCCESS)
goto deallocate_dispatch;
}
disp->port_table = isc_mem_get(mgr->mctx,
sizeof(disp->port_table[0]) *
DNS_DISPATCH_PORTTABLESIZE);
if (disp->port_table == NULL)
goto deallocate_dispatch;
for (i = 0; i < DNS_DISPATCH_PORTTABLESIZE; i++)
ISC_LIST_INIT(disp->port_table[i]);
result = isc_mempool_create(mgr->mctx, sizeof(dispportentry_t),
&disp->portpool);
if (result != ISC_R_SUCCESS)
goto deallocate_dispatch;
isc_mempool_setname(disp->portpool, "disp_portpool");
isc_mempool_setfreemax(disp->portpool, 128);
}
disp->socktype = isc_sockettype_udp;
disp->socket = sock;