crossroads

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

commit bee61e021322c813cba3c0d3a9f3c2572011abd8
parent c17283f4350c1868cacb273c2118f82f5f63b4e5
Author: finwo <finwo@pm.me>
Date:   Sat,  3 Jan 2026 19:36:26 +0100

2.33

Diffstat:
MChangeLog | 7+++++++
MMakefile | 21+++++++++++++++++----
Adoc/xr.1 | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/xrctl.1 | 37+++++++++++++++++++++++++++++++++++++
Adoc/xrctl.xml.5 | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dtest/udpfwd.c | 184-------------------------------------------------------------------------------
Mxr/Makefile | 14+++++++-------
Mxr/etc/c-conf | 4++--
Mxr/tcpdispatcher/execute.cc | 6+++---
9 files changed, 342 insertions(+), 200 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,10 @@ +2.33 [KK 2008-11-07] +- Solaris portability for gcc 3.4 issues in Tcpdispatcher::execute() +- Removed warnings about non-found libraries (on systems that don't + need them) +- Support for DESTDIR setting in top Makefile (for debian packaging) +- Man pages added as doc/xr.1, doc/xrctl.1 and doc/xrctl.xml.5. + 2.32 [KK 2008-11-05] - Bugfix in "stored-ip" dispatching algorithm. diff --git a/Makefile b/Makefile @@ -1,8 +1,10 @@ # Top-level Makefile for XR # ------------------------- -VER = 2.32 -BINDIR = /usr/sbin +VER = 2.33 +PREFIX = $(DESTDIR)/usr +BINDIR = $(PREFIX)/sbin +MANDIR = $(PREFIX)/share/man TAR = /tmp/crossroads-$(VER).tar.gz AUTHOR = Karel Kubat <karel@kubat.nl> MAINTAINER = Karel Kubat <karel@kubat.nl> @@ -32,7 +34,7 @@ local: localprof: PROF=-pg PROFILER=-DPROFILER make local -install: local $(BINDIR)/xrctl +install: local $(BINDIR)/xrctl install-manpages mkdir -p $(BINDIR) BASE=$(BASE) AUTHOR='$(AUTHOR)' MAINTAINER='$(MAINTAINER)' \ DISTSITE='$(DISTSITE)' \ @@ -41,7 +43,7 @@ install: local $(BINDIR)/xrctl @echo ' The balancer program xr is now installed to $(BINDIR).' @echo ' The control script xrctl is installed there too. In order to' @echo ' use it, you will have to create /etc/xrctl.xml (if you have' - @echo ' not done so yet). See test/sampleconf.xml for an example.' + @echo ' not done so yet). See "man xrctl.xml" for an example.' @echo @echo ' Have fun with Crossroads $(VER),' @echo ' -- $(MAINTAINER)' @@ -49,6 +51,17 @@ install: local $(BINDIR)/xrctl $(BINDIR)/xrctl: xrctl/xrctl cp xrctl/xrctl $(BINDIR)/xrctl chmod +x $(BINDIR)/xrctl +install-manpages: $(MANDIR)/man1/xr.1 $(MANDIR)/man1/xrctl.1 \ + $(MANDIR)/man5/xrctl.xml.5 +$(MANDIR)/man1/xr.1: doc/xr.1 + mkdir -p $(MANDIR)/man1 + cp $< $@ +$(MANDIR)/man1/xrctl.1: doc/xrctl.1 + mkdir -p $(MANDIR)/man1 + cp $< $@ +$(MANDIR)/man5/xrctl.xml.5: doc/xrctl.xml.5 + mkdir -p $(MANDIR)/man5 + cp $< $@ uninstall: rm -f $(BINDIR)/xr $(BINDIR)/xrctl diff --git a/doc/xr.1 b/doc/xr.1 @@ -0,0 +1,56 @@ +.TH "XR" "1" "Nov 6, 2008" "Man Pages" + +.SH "NAME" +xr \- Crossroads Load Balancer & Fail Over Utility + +.SH "SYNOPSIS" +\fBxr [--verbose] [--web-interface XRSERVER:PORT] --server +tcp:XRSERVER:PORT --backend BACKEND:PORT [--backend BACKEND:PORT] ...\fR + +.SH "DESCRIPTION" +This manual page briefly documents XR, the Crossroads Load Balancer & +Fail Over Utility. + +XR is an open source load balancer and fail over utility for TCP based +services. It is a dae mon running in user space, and features +extensive configurability, polling of back ends using wake up calls, +status reporting, many algorithms to select the 'right' back end for a +reques t (and user-defined algorithms for very special cases), and +much more. + +XR is service-independent: it is usable for any TCP service, such as +HTTP(S), SSH, SMTP, dat abase connections. In the case of HTTP +balancing, XR handles multiple host balancing, and can provide session +stickiness for back end processes that need sessions, but aren't +session-awa re of other back ends. + +XR furthermore features a management web interface and can be run as a +stand-alone daemon, or via inetd. + +Execute 'xr -h' to get a complete list of available command-line parameters. + +.SH "EXAMPLE" +xr --verbose --server tcp:0:80 --backend 10.1.1.1:80 --backend +10.1.1.2:80 --backend 10.1.1. 3:80 --web-interface 0:8001 + +This instructs XR to listen to port 80 and to dispatch traffic to the +servers 10.1.1.1, 10.1.1.2 and 10.1.1.2, port 80. A web interface for +the balancer is started on port 8001. + +Direct your browser to the server running XR. You will see the pages +served by one of the three back ends. The console where XR is +started, will show what's going on (due to the presence of +--verbose). + +Direct your browser to the server running XR, but port 8001. You will +see the web interface, which shows the status, and where you can +alter some settings. + +.SH "SEE ALSO" +xrctl(1) + +.SH "AUTHOR" +XR was written by Karel Kubat <karel@kubat.nl>. Web page: +http://crossroads.e-tunity.com +.P +This man page was written by Frederik Dannemare <frederik@dannemare.net>. diff --git a/doc/xrctl.1 b/doc/xrctl.1 @@ -0,0 +1,37 @@ +.TH "XRCTL" "1" "Nov 7, 2008" "Man Pages" + +.SH "NAME" +xrctl \- Crossroads control-script + +.SH "SYNOPSIS" +\fBxrctl [OPTION]...\fR + +.SH "DESCRIPTION" +This manual page briefly documents xrctl, the Crossroads +control-script. Instead of starting XR by hand, consider using xrctl. + +Edit /etc/xrctl.xml, which is the configuration file, and configure +your service(s), all their options, and back ends. Then type xrctl +start to start all your services, or xrctl stop to stop them. + +.SH "OPTIONS" +.nf +list [SERVICE] - show configuration of a service, or of all +start [SERVICE] - start a service, or all configured services +stop [SERVICE] - stop a service, or all configured services +force [SERVICE] - start a service (or all) if not running +restart [SERVICE] - stop and start a service, or all +status [SERVICE] - show running status of a service, or of all +rotate [SERVICE] - rotate logs of a service or of all +generateconfig [SERVICE] - queries running XR's for the configuration + and shows it in the format of /etc/xrctl.xml +.fi + +.SH "SEE ALSO" +xr(1), xrctl.xml(5) + +.SH "AUTHOR" +xrctl was written by Karel Kubat <karel@kubat.nl>. Web page: +http://crossroads.e-tunity.com +.P +This man page was written by Frederik Dannemare <frederik@dannemare.net>. diff --git a/doc/xrctl.xml.5 b/doc/xrctl.xml.5 @@ -0,0 +1,213 @@ +.TH "XRCTL.XML" "5" "Nov 8, 2008" "Man Pages" + +.SH "NAME" +xrctl.xml \- Crossroads control-script configuration file + +.SH "SYNOPSIS" +The file /etc/xrctl.xml is xrctl's configuration. It defines how xrctl +will start the balancer xr. If you wish to use xrctl to control the +balancer, then you must configure all services, options and back ends +in xrctl.xml. + +.SH "EXAMPLE" + +The following is a configuration example. See the file xr.pdf which is +distributed with the sources for a full description. + +.nf +<?xml version="1.0" encoding="UTF-8"> + +<configuration> + + <!-- General system configuration section --> + + <system> + <!-- Where do PID files get stored? --> + <piddir>/var/run</piddir> + <!-- "ps" command that shows the PID and command. On Solaris, use + /usr/bin/ps -ef "pid comm" --> + <pscmd>/bin/ps ax -o pid,command</pscmd> + <!-- Use "logger" to add output to syslog or not? Logger will be + used if the binary can be found, and if uselogger is true. --> + <uselogger>true</uselogger> + <!-- If logger is not used: where do logs get written? --> + <logdir>/var/log</logdir> + <!-- If logger is not used: how big may the logs become? + Manipulated during "xrctl rotate". --> + <maxlogsize>100000</maxlogsize> + <!-- If logger is not used: how many history logs to keep? --> + <loghistory>10</loghistory> + <!-- Path where the "xr" binary is searched, and zippers as "gzip" + and "bzip2" --> + <path>/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin</path> + </system> + + <!-- Service descriptions: This section defines all balancing + services that you want to start. Each service will lead to one + invocation of "xr". --> + + <!-- Very simple TCP service that dispatches SSH connections on + port 20.000 to three back ends. Most options are left to + their defaults. --> + <service> + <!-- Service name, must be unique --> + <name>ssh</name> + <server> + <!-- Type (tcp/http, here: tcp), and IP-address/port to bind + to. Use "0" for IP-address to bind to all interfaces. The + web interface will listen to localhost, port 20.001. --> + <type>tcp</type> + <address>0:20000</address> + <webinterface>0:20001</webinterface> + <!-- Clients may be idle for 30 minutes, then they are logged + out. --> + <clienttimeout>1800</clienttimeout> + </server> + + <!-- Back ends for the service. --> + <backend> + <!-- IP:port to dispatch to. --> + <address>server1:22</address> + </backend> + <backend> + <address>server2:22</address> + </backend> + <backend> + <address>server2:22</address> + </backend> + </service> + + <!-- Here is an HTTP service for web balancing. It shows more + advanced features. --> + <service> + <name>webone</name> + + <!-- Balancer server description --> + <server> + <!-- Server binding. XR will listen to any IP interface, on port + 20.010. It'll be an HTTP balancer. The web interface will + be on port 20.011. --> + <address>0:20010</address> + <type>http</type> + <webinterface>127.0.0.1:20011</webinterface> + + <!-- A non-default dispatch mode, here: by client IP.--> + <dispatchmode>lax-hashed-ip</dispatchmode> + + <!-- Checks. Dead back ends are checked each 3 seconds. There is + no checking of dead and live back ends (checkupinterval 0). --> + <checks> + <wakeupinterval>3</wakeupinterval> + <checkupinterval>0</checkupinterval> + </checks> + + <debugging> + <!-- Let's go with full messaging: verbose, debug, and logging + of transmitted messages. --> + <verbose>yes</verbose> + <debug>yes</debug> + <logtrafficdir>/tmp</logtrafficdir> + </debugging> + + <!-- If the balancer runs out of sockets because too many + closing connections are in TIME_WAIT state, use: --> + <closesocketsfast>yes</closesocketsfast> + + <!-- Access restrictions: we allow from two IP ranges, and deny + from one IP address. The overall results:will be: + - Access will be allowed from 10.*.*.* + - And allowed from 192.168.1.*, but not from 192.168.1.100 --> + <acl> + <allowfrom>10.255.255.255</allowfrom> + <allowfrom>192.168.1.255</allowfrom> + <denyfrom>192.168.1.100</denyfrom> + </acl> + + <dosprotection> + <!-- Here is some basic DOS protection. Connections from IP's + are counted over timeinterval seconds (here: 2 sec). When a + client exceeds the hard limit hardmaxconnrate (here: 200), + then it is denied access. When it exceeds the soft limit + softmaxconnrate (here: 150), then each connection is + delayed for defertime microsecs (here: 1.000.000, one + sec). + Finally, the entire balancer will be allowed to serve up + to 400 simultaneous connections. + --> + <timeinterval>2</timeinterval> + <hardmaxconnrate>200</hardmaxconnrate> + <softmaxconnrate>150</softmaxconnrate> + <defertime>1000000</defertime> + <maxconnections>400</maxconnections> + + <!-- Let's add some more protection. When a user exceeds their + hard maxconn rate, "/path/to/program" will be invoked + with the IP as argument. That program may eg. call + iptables to block the client. There is also a tag + softmaxconnexcess (not shown here). --> + <hardmaxconnexcess>/path/to/program</hardmaxconnexcess> + + </dosprotection> + + <http> + <!-- Since this is an HTTP balancer, let's add some goodies: + no header for the XR version, + a header X-Forwarded-For: client-ip + no sticky http sessions + two serverheaders to insert --> + <addxrversion>off</addxrversion> + <addxforwardedfor>on</addxforwardedfor> + <stickyhttp>off</stickyhttp> + <serverheaders> + <header>MyFirstHeader: Whatever</header> + <header>MySecondHeader: WhateverElse</header> + </serverheaders> + </http> + </server> + + <!-- Back end definitions --> + <backend> + <!-- Backend lives on server1:80 and is very big (weight 2). + XR will forward up to 300 connections to it. The back end + checking is left to the default, which is: connect to the + IP and port of the back end. Requests for host + www.mysite.org will be serviced here. --> + <address>server1:80</address> + <weight>2</weight> + <maxconnections>300</maxconnections> + <hostmatch>www.mysite.org</hostmatch> + </backend> + <backend> + <!-- Backend lives on server2:80, has the default weight 1. + XR will forward up to 100 connections to it. The back end + checking is done by connecting to an alternative port 81. + This back end will be eligible for requests for the site + www.myothersite.org. --> + <address>server2:80</address> + <maxconnections>100</maxconnections> + <backendcheck>connect::81</backendcheck> + <hostmatch>www.myothersite.org</hostmatch> + </backend> + <backend> + <!-- Backend lives on server3:80, has the standard weight and no + limitations for the max nr. of connections. Back end + checking is done by retrieving /healthcheck.cgi from the + server. The back end is eligible for www.myothersite.org. --> + <address>server3:80</address> + <backendcheck>get:server3:80/healthcheck.cgi</backendcheck> + <hostmatch>www.myothersite.org</hostmatch> + </backend> + </service> + +</configuration> +.fi + +.SH "SEE ALSO" +xr(1), xrctl(1) + +.SH "AUTHOR" +xrctl and the corresponding configuration file format were written by +Karel Kubat <karel@kubat.nl>. Web page: http://crossroads.e-tunity.com + +.p +This man page was written by Karel Kubat <karel@kubat.nl>. diff --git a/test/udpfwd.c b/test/udpfwd.c @@ -1,184 +0,0 @@ - -#include <ctype.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> -#include <unistd.h> - -#include <arpa/inet.h> -#include <netinet/in.h> -#include <sys/select.h> -#include <sys/socket.h> -#include <sys/types.h> - -#define MIN_RANGE 1 -#define MAX_RANGE 65000 - -void die (char const *fmt, ...) { - va_list args; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - fputc('\n', stderr); - exit(1); -} - -struct sockaddr_in si_backend; -fd_set listen_set; - -typedef struct { - struct sockaddr_in si; - int sock; -} Conn; - -typedef struct { - int cl_port, sr_port; - struct in_addr cl_addr, sr_addr; -} Db; - -Db *db; -int ndb; -Db *db_find (struct sockaddr_in s) { - int i; - for (i = 0; i < ndb; i++) - if (db[i].cl_port == s.sin_port && - (*(long*)(&db[i].cl_addr) == (*(long*)(&s.sin_addr)))) - return db + i; - return 0; -} - -void db_add(int clport, struct in_addr claddr, - int srport, struct in_addr sraddr) { - if (! (db = realloc(db, (ndb + 1) * sizeof(Db))) ) - die ("memory fault while reallocating db to %d entries\n", - ndb + 1); - db[ndb].cl_port = clport; - db[ndb].cl_addr = claddr; - db[ndb].sr_port = srport; - db[ndb].sr_addr = sraddr; -} - -void dumpbuf (int len, char const *buf) { - char disp[4]; - int i; - - printf ("Data, %d bytes: ", len); - for (i = 0; i < len; i++) { - if (isprint(buf[i])) { - putchar(buf[i]); - putchar (' '); - } else { - sprintf(disp, "%3.3o", buf[i] & 0xff); - printf(" \\%s", disp); - } - } - putchar('\n'); -} - -Conn make_server_conn (int port) { - Conn ret; - int p; - - printf ("Attempting to create connector for port %d\n", port); - if ( (ret.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 1 ) - die ("cannot create socket: %s", strerror(errno)); - memset((char *) &(ret.si), 0, sizeof(ret.si)); - ret.si.sin_family = AF_INET; - ret.si.sin_addr.s_addr = htonl(INADDR_ANY); - - for (p = port; p <= MAX_RANGE; p++) { - ret.si.sin_port = htons(p); - if (bind(ret.sock, (struct sockaddr *) &ret.si, sizeof(ret.si)) != 1) { - printf("Socket bound to port %d\n", p); - FD_SET(ret.sock, &listen_set); - return ret; - } - } - die ("Failed to bind socket to any port\n"); - /* To satisfy */ - return ret; -} - -void handle_socket(int sock, char *buf, int buflen) { - struct sockaddr_in si_other; - Conn c; - int nreceived; - unsigned slen = sizeof(si_other); - Db *db; - - nreceived = recvfrom(sock, buf, buflen, 0, - (struct sockaddr *)&si_other, &slen); - - if (nreceived < 1) - die ("receive from client failed: %s", strerror(errno)); - printf("Received packet from %s:%d on socket %d\n", - inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), sock); - dumpbuf(nreceived, buf); - - if (! (db = db_find (si_other)) ) { - printf ("New client connection, forwarding to back end\n"); - c = make_server_conn(20000); - if (sendto(c.sock, buf, nreceived, 0, - (struct sockaddr *)&si_backend, slen) == -1) - die ("failed to send to back end: %s", strerror(errno)); - db_add(c.si.sin_port, c.si.sin_addr, - si_other.sin_port, si_other.sin_addr); - } else { - print ("Return connection from back end, sending to client\n"); - - -} - -int main (int argc, char **argv) { - int buflen, port, backend_port; - char *buf; - - /* Get arguments */ - if (argc < 5) - die ("Usage: udpfwd buflen port backendip backendport"); - if (! (buflen = atoi(argv[1])) ) - die("bad buffer length '%s'", argv[1]); - if (! (buf = malloc(buflen)) ) - die("memory fault, cannot allocate buffer of %d bytes", buflen); - if (! (port = atoi(argv[2])) ) - die("bad server port '%s'", argv[2]); - if (! (backend_port = atoi(argv[4])) ) - die ("bad back end port '%s'", argv[4]); - - /* Set up the back end. */ - memset(&si_backend, 0, sizeof(si_backend)); - si_backend.sin_family = AF_INET; - si_backend.sin_port = htons(backend_port); - if (inet_aton(argv[3], &si_backend.sin_addr) == 0) - die ("bad back end IP '%s': %s", argv[3], strerror(errno)); - - /* Set up server */ - FD_ZERO(&listen_set); - make_server_conn (port); - - while (1) { - fd_set read_set; - int nready, i; - - FD_COPY(&listen_set, &read_set); - printf("Monitoring server sockets:"); - for (i = 0; i < FD_SETSIZE; i++) - if (FD_ISSET(i, &read_set)) - printf (" %d", i); - putchar ('\n'); - - nready = select(FD_SETSIZE, &read_set, 0, 0, 0); - if (nready < 1) - die("select error: %s", strerror(errno)); - for (i = 0; i < FD_SETSIZE; i++) - if (FD_ISSET(i, &read_set)) { - printf ("Activity on socket %d\n", i); - handle_socket(i, buf, buflen); - } - } - - /* All done */ - return 0; -} diff --git a/xr/Makefile b/xr/Makefile @@ -5,17 +5,17 @@ BUILDDIR = build BIN = $(BUILDDIR)/xr LIB = $(BUILDDIR)/libxr.a TMPXR = /tmp/xr-$(shell whoami) -CONF_CC = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache c++-compiler) -CONF_OPTFLAGS = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache optflags) -CONF_LIB = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \ +CONF_CC = $(shell etc/c-conf -c $(BUILDDIR)/config.cache c++-compiler) +CONF_OPTFLAGS = $(shell etc/c-conf -c $(BUILDDIR)/config.cache optflags) +CONF_LIB = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \ lib ucb nsl pthread socket m alf) -CONF_GETOPT = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \ +CONF_GETOPT = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \ ifheader getopt.h HAVE_GETOPT_H) -CONF_GETOPT_LONG = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \ +CONF_GETOPT_LONG = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \ libfunction getopt_long HAVE_GETOPT_LONG) -CONF_INET_ATON = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \ +CONF_INET_ATON = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \ libfunction inet_aton HAVE_INET_ATON) -CONF_STRNSTR = $(shell etc/c-conf -vc $(BUILDDIR)/config.cache \ +CONF_STRNSTR = $(shell etc/c-conf -c $(BUILDDIR)/config.cache \ libfunction strnstr HAVE_STRNSTR) foo: diff --git a/xr/etc/c-conf b/xr/etc/c-conf @@ -362,8 +362,8 @@ ENDHELP output ("-l$lib"); } } - warning ("Library '$lib' not found\n") - unless ($found); + #warning ("Library '$lib' not found\n") + # unless ($found); } } diff --git a/xr/tcpdispatcher/execute.cc b/xr/tcpdispatcher/execute.cc @@ -1,6 +1,7 @@ #include "tcpdispatcher" -static map <unsigned long, queue <time_t> > accesslog; +typedef map < unsigned long, std::queue<time_t> > AccessMap; +static AccessMap accesslog; static time_t accesslog_lastclean = 0; // Execute an external program upon excess of hard/soft rates @@ -53,8 +54,7 @@ void TcpDispatcher::execute() { accesslog_lastclean = now; Mutex::unlock(&accesslog_lastclean); - for (map<unsigned long, queue <time_t> >::iterator i = - accesslog.begin(); + for (AccessMap::iterator i = accesslog.begin(); i != accesslog.end(); i++ ) { if (accesslog[i->first].back() < min_ts) {