crossroads

Git mirror of https://crossroads.e-tunity.com/
git clone git://git.finwo.net/app/crossroads
Log | Files | Refs | LICENSE

commit 0680b47c11e05fd36cd924768d0da75f6b3da487
parent 9c22cc48450e3ae0488d0c3a175ae5df20d4258a
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:35:00 +0100

2.03

Diffstat:
MChangeLog | 7+++++++
MMakefile | 2+-
Mdoc/xr.odt | 0
Mdoc/xr.pdf | 0
Atest/iphashtest.cc | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Rxr/etc/xr-test -> test/xr-test | 0
Mxr/algorithm/algorithm | 2+-
Mxr/balancer/serve.cc | 18++++++++++--------
Mxr/config/setdispatcmode.cc | 19+++++++++++--------
Mxr/dispatchmode/dispatchmode | 2++
Mxr/etc/usage.txt | 8++++++--
Mxr/external/external | 3++-
Mxr/external/target.cc | 2+-
Mxr/firstactive/firstactive | 3++-
Mxr/firstactive/target.cc | 2+-
Axr/hashedip/hashedip | 16++++++++++++++++
Axr/hashedip/target.cc | 40++++++++++++++++++++++++++++++++++++++++
Mxr/httpdispatcher/handle.cc | 3++-
Mxr/httpdispatcher/httpdispatcher | 2+-
Mxr/httpdispatcher/httpdispatcher1.cc | 2+-
Mxr/leastconn/leastconn | 3++-
Mxr/leastconn/target.cc | 2+-
Mxr/roundrobin/roundrobin | 3++-
Mxr/roundrobin/target.cc | 2+-
Mxr/tcpdispatcher/dispatch.cc | 2+-
Mxr/tcpdispatcher/tcpdispatcher | 7++++---
Mxr/tcpdispatcher/tcpdispatcher1.cc | 13+++++++++----
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; } }