#define FD_EXCL_SYSCALL_BIT 21 /* a syscall claims exclusivity on this FD */
#define FD_DISOWN_BIT 22 /* this fd will be closed by some external code */
#define FD_MUST_CLOSE_BIT 23 /* this fd will be closed by some external code */
+#define FD_HAS_PORT_BIT 24 /* This fd had a port allocated from a port range */
+#define FD_OWNER_PR_BIT 27 /* The owner is actually a port range */
/* and flag values */
#define FD_EXCL_SYSCALL (1U << FD_EXCL_SYSCALL_BIT)
#define FD_DISOWN (1U << FD_DISOWN_BIT)
#define FD_MUST_CLOSE (1U << FD_MUST_CLOSE_BIT)
+#define FD_HAS_PORT (1U << FD_HAS_PORT_BIT)
+#define FD_OWNER_PR (1U << FD_OWNER_PR_BIT)
/* This function is used to report flags in debugging tools. Please reflect
* below any single-bit flag addition above in the same order via the
unsigned long poll_send;
};
-/* less often used information */
-struct fdinfo {
- struct port_range *port_range; /* optional port range to bind to */
- int local_port; /* optional local port */
-};
-
/*
* Poller descriptors.
* - <name> is initialized by the poller's register() function, and should not
extern int nbpollers;
extern struct poller pollers[MAX_POLLERS]; /* all registered pollers */
extern struct fdtab *fdtab; /* array of all the file descriptors */
-extern struct fdinfo *fdinfo; /* less-often used infos for file descriptors */
extern int totalconn; /* total # of terminated sessions */
extern int actconn; /* # of active sessions */
/* QUIC connection level counters */ \
struct quic_conn_cntrs cntrs; \
struct connection *conn; \
+ struct port_range *sport_range; \
}
struct quic_conn {
struct fdtab *fdtab __read_mostly = NULL; /* array of all the file descriptors */
struct polled_mask *polled_mask __read_mostly = NULL; /* Array for the polled_mask of each fd */
-struct fdinfo *fdinfo __read_mostly = NULL; /* less-often used infos for file descriptors */
int totalconn; /* total # of terminated sessions */
int actconn; /* # of active sessions */
if (fd_nbupdt > 0 && fd_updt[fd_nbupdt - 1] == fd)
fd_nbupdt--;
- port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
+ if (unlikely(fdtab[fd].state & FD_HAS_PORT)) {
+ struct sockaddr_storage sa;
+ socklen_t addrlen = sizeof(sa);
+ int port;
+
+ BUG_ON(!(fdtab[fd].state & FD_OWNER_PR));
+ /*
+ * We have to release the port, let's use getsockname()
+ * to figure out what the port was.
+ */
+ BUG_ON(getsockname(fd, (struct sockaddr *)&sa, &addrlen) != 0);
+ if (sa.ss_family == AF_INET)
+ port = ((struct sockaddr_in *)&sa)->sin_port;
+ else if (sa.ss_family == AF_INET6)
+ port = ((struct sockaddr_in6 *)&sa)->sin6_port;
+ else
+ ABORT_NOW();
+ port_range_release_port(fdtab[fd].owner, port);
+ }
+
polled_mask[fd].poll_recv = polled_mask[fd].poll_send = 0;
fdtab[fd].state = 0;
#ifdef DEBUG_FD
fdtab[fd].event_count = 0;
#endif
- fdinfo[fd].port_range = NULL;
fdtab[fd].owner = NULL;
/* perform the close() call last as it's what unlocks the instant reuse
}
vma_set_name(polled_mask, global.maxsock * sizeof(*polled_mask), "fd", "polled_mask");
- if ((fdinfo = calloc(global.maxsock, sizeof(*fdinfo))) == NULL) {
- ha_alert("Not enough memory to allocate %d entries for fdinfo!\n", global.maxsock);
- goto fail_info;
- }
- vma_set_name(fdinfo, global.maxsock * sizeof(*fdinfo), "fd", "fdinfo");
-
for (p = 0; p < MAX_TGROUPS; p++)
update_list[p].first = update_list[p].last = -1;
}
} while (!bp || bp->pref == 0);
- free(fdinfo);
fail_info:
free(polled_mask);
fail_polledmask:
bp->term(bp);
}
- ha_free(&fdinfo);
ha_aligned_free(fdtab);
ha_free(&polled_mask);
}
struct sockaddr_storage *addr, saddr;
struct quic_conn *qc = conn->handle.qc;
socklen_t saddr_len;
+ int local_port = 0;
BUG_ON(qc->fd != -1);
BUG_ON(!conn->dst);
/* note: in case of retry, we may have to release a previously
* allocated port, hence this loop's construct.
*/
- port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
- fdinfo[fd].port_range = NULL;
+ port_range_release_port(src->sport_range, local_port);
+ local_port = 0;
+ qc->sport_range = NULL;
if (!attempts)
break;
attempts--;
- fdinfo[fd].local_port = port_range_alloc_port(src->sport_range);
- if (!fdinfo[fd].local_port) {
+ local_port = port_range_alloc_port(src->sport_range);
+ if (!local_port) {
conn->err_code = CO_ER_PORT_RANGE;
break;
}
- fdinfo[fd].port_range = src->sport_range;
- set_host_port(&sa, fdinfo[fd].local_port);
+ set_host_port(&sa, local_port);
+ qc->sport_range = src->sport_range;
ret = quic_bind_socket(fd, flags, &sa, conn->src);
if (ret != 0)
}
if (unlikely(ret != 0)) {
- port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
- fdinfo[fd].port_range = NULL;
+ port_range_release_port(src->sport_range, local_port);
close(fd);
if (ret == 1) {
addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
- port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
- fdinfo[fd].port_range = NULL;
+ port_range_release_port(src ? src->sport_range : NULL, local_port);
close(fd);
conn->flags |= CO_FL_ERROR;
return SF_ERR_SRVCL;
fd_want_recv(fd);
conn_ctrl_init(conn);
+ if (local_port != 0)
+ _HA_ATOMIC_OR(&fdtab[fd].state, FD_HAS_PORT);
return SF_ERR_NONE; /* connection is OK */
}
struct proxy *be;
struct conn_src *src;
int use_fastopen = 0;
+ int local_port = 0;
struct sockaddr_storage *addr;
BUG_ON(!conn->dst);
memcpy(&sa, &src->source_addr, sizeof(sa));
do {
+
/* note: in case of retry, we may have to release a previously
* allocated port, hence this loop's construct.
*/
- port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
- fdinfo[fd].port_range = NULL;
-
+ port_range_release_port(src->sport_range, local_port);
+ local_port = 0;
if (!attempts)
break;
attempts--;
- fdinfo[fd].local_port = port_range_alloc_port(src->sport_range);
- if (!fdinfo[fd].local_port) {
+ local_port = port_range_alloc_port(src->sport_range);
+ if (!local_port) {
conn->err_code = CO_ER_PORT_RANGE;
break;
}
- fdinfo[fd].port_range = src->sport_range;
- set_host_port(&sa, fdinfo[fd].local_port);
+ set_host_port(&sa, local_port);
ret = tcp_bind_socket(fd, flags, &sa, conn->src);
if (ret != 0)
}
if (unlikely(ret != 0)) {
- port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
- fdinfo[fd].port_range = NULL;
+ port_range_release_port(src->sport_range, local_port);
close(fd);
if (ret == 1) {
}
qfprintf(stderr,"Connect() failed for backend %s: %s.\n", be->id, msg);
- port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
- fdinfo[fd].port_range = NULL;
+ port_range_release_port(src ? src->sport_range : NULL, local_port);
close(fd);
send_log(be, LOG_ERR, "Connect() failed for backend %s: %s.\n", be->id, msg);
conn->flags |= CO_FL_ERROR;
return SF_ERR_RESOURCE;
} else if (errno == ETIMEDOUT) {
//qfprintf(stderr,"Connect(): ETIMEDOUT");
- port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
- fdinfo[fd].port_range = NULL;
+ port_range_release_port(src ? src->sport_range : NULL, local_port);
close(fd);
conn->err_code = CO_ER_SOCK_ERR;
conn->flags |= CO_FL_ERROR;
} else {
// (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM)
//qfprintf(stderr,"Connect(): %d", errno);
- port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
- fdinfo[fd].port_range = NULL;
+ port_range_release_port(src ? src->sport_range : NULL, local_port);
close(fd);
conn->err_code = CO_ER_SOCK_ERR;
conn->flags |= CO_FL_ERROR;
}
conn_ctrl_init(conn); /* registers the FD */
+ if (local_port != 0)
+ _HA_ATOMIC_OR(&fdtab[fd].state, FD_HAS_PORT);
HA_ATOMIC_OR(&fdtab[fd].state, FD_LINGER_RISK); /* close hard if needed */
if (conn->flags & CO_FL_WAIT_L4_CONN) {
qc->qcc = NULL;
qc->strm_reject = NULL;
qc->path = NULL;
+ qc->sport_range = NULL;
/* Keyupdate: required to safely call quic_tls_ku_free() from
* quic_conn_release().
void qc_release_fd(struct quic_conn *qc, int reinit)
{
if (qc_test_fd(qc)) {
+ if (unlikely(fdtab[qc->fd].state & FD_HAS_PORT)) {
+ fdtab[qc->fd].owner = qc->sport_range;
+ _HA_ATOMIC_OR(&fdtab[qc->fd].state, FD_OWNER_PR);
+ }
fd_delete(qc->fd);
qc->fd = DEAD_FD_MAGIC;
void sock_conn_ctrl_close(struct connection *conn)
{
BUG_ON(conn->flags & CO_FL_FDLESS);
+ if (unlikely(fdtab[conn->handle.fd].state & FD_HAS_PORT)) {
+ struct server *srv = objt_server(conn->target);
+ struct proxy *be;
+ struct port_range *port_range;
+
+ BUG_ON(srv == NULL);
+ be = srv->proxy;
+ if (srv->conn_src.opts & CO_SRC_BIND)
+ port_range = srv->conn_src.sport_range;
+ else if (be->conn_src.opts & CO_SRC_BIND)
+ port_range = be->conn_src.sport_range;
+ else
+ ABORT_NOW();
+ _HA_ATOMIC_OR(&fdtab[conn->handle.fd].state, FD_OWNER_PR);
+ fdtab[conn->handle.fd].owner = port_range;
+ }
fd_delete(conn->handle.fd);
conn->handle.fd = DEAD_FD_MAGIC;
}