[MEDIUM] add support for binding to source port ranges during connect
Some users are already hitting the 64k source port limit when
connecting to servers. The system usually maintains a list of
unused source ports, regardless of the source IP they're bound
to. So in order to go beyond the 64k concurrent connections, we
have to manage the source ip:port lists ourselves.
The solution consists in assigning a source port range to each
server and use a free port in that range when connecting to that
server, either for a proxied connection or for a health check.
The port must then be put back into the server's range when the
connection is closed.
This mechanism is used only when a port range is specified on
a server. It makes it possible to reach 64k connections per
server, possibly all from the same IP address. Right now it
should be more than enough even for huge deployments.
diff --git a/src/standard.c b/src/standard.c
index de5b664..9dc56af 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -1,7 +1,7 @@
/*
* General purpose functions.
*
- * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
+ * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -241,6 +241,65 @@
}
/*
+ * converts <str> to a struct sockaddr_in* which is locally allocated, and a
+ * port range consisting in two integers. The low and high end are always set
+ * even if the port is unspecified, in which case (0,0) is returned. The low
+ * port is set in the sockaddr_in. Thus, it is enough to check the size of the
+ * returned range to know if an array must be allocated or not. The format is
+ * "addr[:port[-port]]", where "addr" can be a dotted IPv4 address, a host
+ * name, or empty or "*" to indicate INADDR_ANY.
+ */
+struct sockaddr_in *str2sa_range(char *str, int *low, int *high)
+{
+ static struct sockaddr_in sa;
+ char *c;
+ int portl, porth;
+
+ memset(&sa, 0, sizeof(sa));
+ str = strdup(str);
+ if (str == NULL)
+ goto out_nofree;
+
+ if ((c = strrchr(str,':')) != NULL) {
+ char *sep;
+ *c++ = '\0';
+ sep = strchr(c, '-');
+ if (sep)
+ *sep++ = '\0';
+ else
+ sep = c;
+ portl = atol(c);
+ porth = atol(sep);
+ }
+ else {
+ portl = 0;
+ porth = 0;
+ }
+
+ if (*str == '*' || *str == '\0') { /* INADDR_ANY */
+ sa.sin_addr.s_addr = INADDR_ANY;
+ }
+ else if (!inet_pton(AF_INET, str, &sa.sin_addr)) {
+ struct hostent *he;
+
+ if ((he = gethostbyname(str)) == NULL) {
+ Alert("Invalid server name: '%s'\n", str);
+ }
+ else
+ sa.sin_addr = *(struct in_addr *) *(he->h_addr_list);
+ }
+ sa.sin_port = htons(portl);
+ sa.sin_family = AF_INET;
+
+ *low = portl;
+ *high = porth;
+
+ free(str);
+ out_nofree:
+ return &sa;
+}
+
+/*
* converts <str> to two struct in_addr* which must be pre-allocated.
* The format is "addr[/mask]", where "addr" cannot be empty, and mask
* is optionnal and either in the dotted or CIDR notation.