commit 0680b47c11e05fd36cd924768d0da75f6b3da487
parent 9c22cc48450e3ae0488d0c3a175ae5df20d4258a
Author: finwo <finwo@pm.me>
Date: Sat, 3 Jan 2026 19:35:00 +0100
2.03
Diffstat:
27 files changed, 175 insertions(+), 39 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,3 +1,10 @@
+2.03 [KK 2008-08-10]
+Updated docs regarding the mailing list.
+Fixed verbose display upon accepting a client ("current back end
+states").
+Implemented dispatching algorithm "hashed client ip", in the variants
+strict and lax.
+
2.02 [KK 2008-08-09] Changes to the Makefile & some sources to avoid
warnings under RHL (thanks, Simon M.).
Bytes processed by a back end is now administered in
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
# Top-level Makefile for XR
# -------------------------
-VER = 2.02
+VER = 2.03
BINDIR = /usr/sbin
TAR = /tmp/crossroads-$(VER).tar.gz
AUTHOR = Karel Kubat <karel@kubat.nl>
diff --git a/doc/xr.odt b/doc/xr.odt
Binary files differ.
diff --git a/doc/xr.pdf b/doc/xr.pdf
Binary files differ.
diff --git a/test/iphashtest.cc b/test/iphashtest.cc
@@ -0,0 +1,51 @@
+// C
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+// C++
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+int main () {
+ for (int a = 0; a <= 255; a++) {
+ ostringstream o;
+ o << "192.168.1." << a;
+
+ struct in_addr ad;
+ inet_aton (o.str().c_str(), &ad);
+
+ unsigned index = 0;
+ for (char *cp = (char*) &ad;
+ unsigned(cp - (char*)&ad) < sizeof(struct in_addr);
+ cp++) {
+ index += *cp;
+ index %= 60;
+ //cout << " " << "byte: " << hex << *cp
+ // << ", index: " << index << "\n";
+ }
+
+ cout << inet_ntoa(ad) << ": " << dec << index << "\n";
+ }
+
+ return (0);
+}
+
diff --git a/xr/etc/xr-test b/test/xr-test
diff --git a/xr/algorithm/algorithm b/xr/algorithm/algorithm
@@ -6,7 +6,7 @@
class Algorithm: public Thread {
public:
virtual ~Algorithm();
- virtual int target() = 0;
+ virtual int target(struct in_addr clientip) = 0;
};
#endif
diff --git a/xr/balancer/serve.cc b/xr/balancer/serve.cc
@@ -132,6 +132,9 @@ void Balancer::serve() {
o << clsock;
msg ("Accepted connection from " + clientip +
" as client fd " + o.str() +"\n");
+ ostringstream n;
+ n << connections();
+ msg ("Balancer is serving " + n.str() + " clients\n");
msg ("Current back end states:\n");
for (unsigned i = 0; i < nbackends(); i++) {
ostringstream status;
@@ -139,11 +142,8 @@ void Balancer::serve() {
if (backend(i).maxconn())
status << " (max " << backend(i).maxconn() << ")";
status << ", status " << backend(i).availablestr();
- msg ("Back end " + backend(i).description() + ": " +
+ msg (" Back end " + backend(i).description() + ": " +
status.str() + "\n");
- ostringstream n;
- n << connections();
- msg ("Balancer is serving " + n.str() + " clients\n");
}
}
@@ -161,10 +161,10 @@ void Balancer::serve() {
TcpDispatcher *d;
switch (config.stype()) {
case Servertype::t_tcp:
- d = new TcpDispatcher (clsock, clientip);
+ d = new TcpDispatcher (clsock, clname.sin_addr);
break;
case Servertype::t_http:
- d = new HttpDispatcher (clsock, clientip);
+ d = new HttpDispatcher (clsock, clname.sin_addr);
break;
default:
throw ((Error) "Internal error, can't choose dispatcher");
@@ -185,12 +185,14 @@ void Balancer::serve() {
} else {
// If fd-serving, serve and close. Don't thread it up.
TcpDispatcher *d;
+ struct in_addr dummy;
+ inet_aton ("0.0.0.0", &dummy);
switch (config.stype()) {
case Servertype::t_tcp:
- d = new TcpDispatcher (server_fd, "0.0.0.0");
+ d = new TcpDispatcher (server_fd, dummy);
break;
case Servertype::t_http:
- d = new HttpDispatcher (server_fd, "0.0.0.0");
+ d = new HttpDispatcher (server_fd, dummy);
break;
default:
throw ((Error) "Internal error, can't choose dispatcher");
diff --git a/xr/config/setdispatcmode.cc b/xr/config/setdispatcmode.cc
@@ -1,19 +1,22 @@
#include "config"
void Config::setdispatchmode (string s) {
- if (s == "r" || s == "round-robin")
- dmode.mode (Dispatchmode::m_roundrobin);
- else if (s == "l" || s == "least-connections")
- dmode.mode (Dispatchmode::m_leastconn);
- else if (s == "f" || s == "first-available")
- dmode.mode (Dispatchmode::m_firstactive);
- else if (s.substr(0, 2) == "e:") {
+ if (s.substr(0, 2) == "e:") {
dmode.mode (Dispatchmode::m_external);
external_algorithm = s.substr (2);
} else if (s.substr (0, 9) == "external:") {
dmode.mode (Dispatchmode::m_external);
external_algorithm = s.substr(9);
- }
+ } else if (s == "f" || s == "first-available")
+ dmode.mode (Dispatchmode::m_firstactive);
+ else if (s == "h" || s == "strict-hashed-ip")
+ dmode.mode (Dispatchmode::m_strict_hashed_ip);
+ else if (s == "H" || s == "lax-hashed-ip")
+ dmode.mode (Dispatchmode::m_lax_hashed_ip);
+ else if (s == "l" || s == "least-connections")
+ dmode.mode (Dispatchmode::m_leastconn);
+ else if (s == "r" || s == "round-robin")
+ dmode.mode (Dispatchmode::m_roundrobin);
else
throw ((Error) "Bad dispatch mode -d" + s);
diff --git a/xr/dispatchmode/dispatchmode b/xr/dispatchmode/dispatchmode
@@ -13,6 +13,8 @@ public:
m_roundrobin,
m_firstactive,
m_external,
+ m_strict_hashed_ip,
+ m_lax_hashed_ip,
};
Dispatchmode() : mymode(m_leastconn) {
diff --git a/xr/etc/usage.txt b/xr/etc/usage.txt
@@ -21,10 +21,14 @@ Usage: xr [flags], where the flags may be:
Sets debugging on, more verbosity on top of -v
-d METHOD, --dispatch-mode METHOD
Defines how to dispatch over back ends, the method may be:
- r, round-robin - back ends take turns
- l, least-connections - back end with least TCP connections is taken
f, first-active - first live back end gets all traffic
e:EXT, external:EXT - external program EXT is queried
+ h, strict-hashed-ip - client IP is hashed to determine a back end,
+ client is denied when back end is down
+ H, lax-hashed-ip - client IP is hashed, fallback to least
+ connections when target back end is down
+ l, least-connections - back end with least TCP connections is taken
+ r, round-robin - back ends take turns
Default method is l (least-connections). When external mode is selected,
program EXT is started with arguments <nbackends> <b0> <b0-availability>
<b0-connections> (b0 repeated for all back ends). Here <b0> is the back
diff --git a/xr/external/external b/xr/external/external
@@ -8,7 +8,8 @@
#include "../config/config"
class External: public Algorithm {
- int target();
+public:
+ int target(struct in_addr clientip);
};
#endif
diff --git a/xr/external/target.cc b/xr/external/target.cc
@@ -1,6 +1,6 @@
#include "external"
-int External::target() {
+int External::target(struct in_addr clientip) {
// Prepare command to run
ostringstream o;
o << config.externalalgorithm() << ' ' << balancer.nbackends();
diff --git a/xr/firstactive/firstactive b/xr/firstactive/firstactive
@@ -7,7 +7,8 @@
#include "../balancer/balancer"
class Firstactive: public Algorithm {
- int target();
+public:
+ int target(struct in_addr clientip);
};
#endif
diff --git a/xr/firstactive/target.cc b/xr/firstactive/target.cc
@@ -1,6 +1,6 @@
#include "firstactive"
-int Firstactive::target() {
+int Firstactive::target(struct in_addr clientip) {
for (unsigned i = 0; i < balancer.nbackends(); i++)
if (balancer.backend(i).available())
return ( (int) i);
diff --git a/xr/hashedip/hashedip b/xr/hashedip/hashedip
@@ -0,0 +1,16 @@
+#ifndef _HASHEDIP_
+#define _HASHEDIP_
+
+#include "../sys/sys"
+#include "../error/error"
+#include "../algorithm/algorithm"
+#include "../balancer/balancer"
+#include "../config/config"
+#include "../leastconn/leastconn"
+
+class HashedIp: public Algorithm {
+public:
+ int target(struct in_addr clientip);
+};
+
+#endif
diff --git a/xr/hashedip/target.cc b/xr/hashedip/target.cc
@@ -0,0 +1,40 @@
+#include "hashedip"
+
+int HashedIp::target(struct in_addr clientip) {
+ // Hash the client's IP into an index
+ unsigned h = 0;
+ for (char *cp = (char*)&clientip;
+ unsigned(cp - (char*)&clientip) < sizeof(struct in_addr);
+ cp++) {
+ h += *cp;
+ h %= balancer.nbackends();
+ }
+ int index = int(h);
+
+ if (config.verbose()) {
+ ostringstream o;
+ o << index;
+ msg ("Client IP " + (string)inet_ntoa(clientip) + " hashes to " +
+ o.str() + ", back end " +
+ balancer.backend(index).description() +
+ "\n");
+ }
+
+ // In strict mode, back end must be available, or don't proceed
+ // In lax mode, fall back to least-connections dispatching
+ if (! balancer.backend(index).available()) {
+ if (config.dispatchmode() == Dispatchmode::m_strict_hashed_ip)
+ throw ((Error) "Hashed-IP algorithm: target back end " +
+ balancer.backend(index).description() + " unavailable");
+ else {
+ msg ("Hashed-IP algorithm: target back end " +
+ balancer.backend(index).description() + " unavailable, "
+ "falling back to least-connections\n");
+ Leastconn l;
+ index = l.target(clientip);
+ }
+ }
+
+ // Got it
+ return (index);
+}
diff --git a/xr/httpdispatcher/handle.cc b/xr/httpdispatcher/handle.cc
@@ -12,7 +12,8 @@ void HttpDispatcher::handle() {
if (config.addxrversion())
clientrequest.setheader ("XR", VER);
if (config.addxforwardedfor())
- clientrequest.addheader ("X-Forwarded-For", clientip());
+ clientrequest.addheader ("X-Forwarded-For",
+ string(inet_ntoa(clientip())));
for (unsigned n = 0; n < config.nserverheaders(); n++)
clientrequest.addheader (config.serverheader(n));
diff --git a/xr/httpdispatcher/httpdispatcher b/xr/httpdispatcher/httpdispatcher
@@ -7,7 +7,7 @@
class HttpDispatcher: public TcpDispatcher {
public:
- HttpDispatcher (int fd, string ip);
+ HttpDispatcher (int fd, struct in_addr ip);
void dispatch();
void handle();
diff --git a/xr/httpdispatcher/httpdispatcher1.cc b/xr/httpdispatcher/httpdispatcher1.cc
@@ -1,5 +1,5 @@
#include "httpdispatcher"
-HttpDispatcher::HttpDispatcher (int fd, string ip) :
+HttpDispatcher::HttpDispatcher (int fd, struct in_addr ip) :
TcpDispatcher (fd, ip), clientrequest(), serverresponse() {
}
diff --git a/xr/leastconn/leastconn b/xr/leastconn/leastconn
@@ -7,7 +7,8 @@
#include "../balancer/balancer"
class Leastconn: public Algorithm {
- int target();
+public:
+ int target(struct in_addr clientip);
};
#endif
diff --git a/xr/leastconn/target.cc b/xr/leastconn/target.cc
@@ -1,6 +1,6 @@
#include "leastconn"
-int Leastconn::target() {
+int Leastconn::target(struct in_addr clientip) {
bool found = false;
unsigned nconn;
int t;
diff --git a/xr/roundrobin/roundrobin b/xr/roundrobin/roundrobin
@@ -7,7 +7,8 @@
#include "../balancer/balancer"
class Roundrobin: public Algorithm {
- int target();
+public:
+ int target(struct in_addr clientip);
};
#endif
diff --git a/xr/roundrobin/target.cc b/xr/roundrobin/target.cc
@@ -1,6 +1,6 @@
#include "roundrobin"
-int Roundrobin::target() {
+int Roundrobin::target(struct in_addr clientip) {
static int last = -1;
int t = last + 1;
diff --git a/xr/tcpdispatcher/dispatch.cc b/xr/tcpdispatcher/dispatch.cc
@@ -5,7 +5,7 @@ void TcpDispatcher::dispatch() {
bool connected = false;
while (!connected) {
- target_backend = algorithm->target();
+ target_backend = algorithm->target(clientip());
tb = balancer.backend(target_backend);
if (!tb.connect()) {
lock();
diff --git a/xr/tcpdispatcher/tcpdispatcher b/xr/tcpdispatcher/tcpdispatcher
@@ -10,11 +10,12 @@
#include "../firstactive/firstactive"
#include "../leastconn/leastconn"
#include "../external/external"
+#include "../hashedip/hashedip"
class TcpDispatcher: public Thread {
public:
- TcpDispatcher (int fd, string ip);
+ TcpDispatcher (int fd, struct in_addr ip);
virtual ~TcpDispatcher();
virtual void execute();
@@ -24,7 +25,7 @@ public:
int targetbackend() const { return target_backend; }
void targetbackend(int t) { target_backend = t; }
- string clientip() const { return client_ip; }
+ struct in_addr clientip() const { return client_ip; }
int clientfd() const { return client_fd; }
void clientfd(int c) { client_fd = c; }
int backendfd() const { return backend_fd; }
@@ -37,7 +38,7 @@ public:
private:
string printable (char ch) const;
- string client_ip;
+ struct in_addr client_ip;
int target_backend, client_fd, backend_fd;
char *data_buf;
unsigned data_bufsz;
diff --git a/xr/tcpdispatcher/tcpdispatcher1.cc b/xr/tcpdispatcher/tcpdispatcher1.cc
@@ -1,6 +1,6 @@
#include "tcpdispatcher"
-TcpDispatcher::TcpDispatcher(int cfd, string cip):
+TcpDispatcher::TcpDispatcher(int cfd, struct in_addr cip):
Thread(), client_ip(cip), target_backend(-1), client_fd(cfd),
backend_fd(-1), data_bufsz(0) {
@@ -12,14 +12,19 @@ TcpDispatcher::TcpDispatcher(int cfd, string cip):
case Dispatchmode::m_roundrobin:
algorithm = new Roundrobin;
break;
- case Dispatchmode::m_leastconn:
- algorithm = new Leastconn;
- break;
case Dispatchmode::m_firstactive:
algorithm = new Firstactive;
break;
case Dispatchmode::m_external:
algorithm = new External;
break;
+ case Dispatchmode::m_strict_hashed_ip:
+ case Dispatchmode::m_lax_hashed_ip:
+ algorithm = new HashedIp;
+ break;
+ case Dispatchmode::m_leastconn:
+ default:
+ algorithm = new Leastconn;
+ break;
}
}