Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
network
ucspi-tcp
ucspi-tcp-0.88.limits.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File ucspi-tcp-0.88.limits.patch of Package ucspi-tcp
Index: CHANGES.tcpserver-limits-patch =================================================================== --- /dev/null +++ CHANGES.tcpserver-limits-patch @@ -0,0 +1,28 @@ +20060126 Added support for "always reject" (setting MAXCONNIP and/or +MAXCONNC to 0) and fixing a bug when sometimes DIEMSG would not be +shown (by Mark Powell) + +20050903 Added support for Solaris (by Jorge Valdes). +Moved MAXLOAD code to getprocla(). +Modified documentation a little to accommodate recent changes. + +20050130 reinstated /proc/loadavg support for those compiling on Linux +with dietlibc (see #define NO_GETLOADAVG at top of tcpserver.c). +Also, we now compile on 64bit platforms (we avoid including unistd.h if +using getloadavg(3), so we don't conflict with readwrite.h header file) +Needed if your compile was breaking with: +readwrite.h:4: error: syntax error before "read" +readwrite.h:4: warning: data definition has no type or storage class +SUMMARY: If 20040725 worked for you, there is no reason to upgrade +(no new features of bugfixes) + +20040725 adds a sleep(1) before terminating (to prevent too high load from +many rapid fork()/exit() calls. It also changes the method for checking +system load to getloadavg(3) instead of parsing /proc/loadavg, therefore +making it working on *BSD and other non-Linux systems in addition to Linux. +It also adds DIEMSG="xxx" support. + +20040327 fixes a bug in 20040124 related to MAXLOAD (it would not work +correctly when load was higher than 10.00) + + Index: README.tcpserver-limits-patch =================================================================== --- /dev/null +++ README.tcpserver-limits-patch @@ -0,0 +1,136 @@ +See CHANGES.tcpserver-limits-patch for changes summary. + +:::COMPILING::: + + For MAXLOAD variable to have effect, you have 3 options: + +(1) By default the patch assumes that you have working getloadavg(3) + (most modern UN*Xoids have, including Linux and FreeBSD). No changes + are needed to standard ucspi-tcp compilation procedures. + +(2) If you have Solaris system: + - conf-cc needs to be modified to include "-DSOLARIS" + - Makefile needs to be modified so that tcpserver links with "-lkstat" + (add "-lkstat" to the end of line 748 after "`cat socket.lib`") + +(3) If you have a non-Solaris system without getloadavg(3), but with + readable '/proc/loadavg' (in linux-2.4.x/2.6.x syntax); for example + if you're compiling on Linux system with dietlibc: + - conf-cc needs to be modified to include "-DNO_GETLOADAVG" + + +:::USING::: + +This patch (20060126) makes tcpserver from DJB's ucspi-tcp-0.88 package (see +http://cr.yp.to/ucspi-tcp.html) to modify its behavior if some environment +variables are present. + +The variables can be preset before starting tcpserver (thus acting as +default for all connections), or, if you use 'tcpserver -x xxx.cdb', they +can be set (or overridden) from xxx.cdb. If none of the variables are set, +tcpserver behaves same as non patched version (except for negligible +performance loss). Any or all variables can be set, as soon as first limit +is reached the connection is dropped. I'd recommend using .cdb files +exclusively though, as you can then modify configuration without killing +tcpserver. + +The variables are: + +(1) MAXLOAD + maximum 1-minute load average * 100. For example, if you have line + :allow,MAXLOAD="350" + in your rules file from which you created .cdb, the connection will be + accepted only if load average is below 3.50 + + See COMPILING instructions above for info on supported systems. + +(2) MAXCONNIP + maximum connections from one IP address. tcpserver's -c flag defines + maximum number of allowed connections, but it can be abused if + just one host goes wild and eats all the connections - no other host + would be able to connect then. If you created your .cdb with: + :allow,MAXCONNIP="5" + and run tcpserver -c 50, then each IP address would be able to have at + most 5 concurrent connections, while there still could connect 50 + clients total. + 0 is valid value and means 'always reject' + +(3) MAXCONNC + + maximum connections from whole C-class (256 addresses). Extension of + MAXCONNIP, as sometimes the problematic client has a whole farm of + client machines with different IP addresses instead of just one IP + address, and they all try to connect. It might have been more useful to + be able to specify CIDR block than C-class, but I've decided to KISS. + + for example tcpserver -c 200, and .cdb with: + :allow,MAXCONNC="15" + will allow at most 15 host from any x.y.z.0/24 address block, while + still allowing up to 200 total connections. + 0 is valid value and means 'always reject' + +(4) DIEMSG + + if set and one of the above limits is exceeded, this is the message + to be sent to client (CRLF is always added to the text) before terminating + connection. If unset, the connection simply terminates (after 1 sec delay) + if limit is exceeded. + + For example: + DIEMSG="421 example.com Service temporarily not available, closing + transmission channel" + +Notes: + +- if a connection is dropped due to some of those variables set, it will be + flagged (if you run tcpserver -v) with "LOAD:", "MAXCONNIP:" or + "MAXCONNC:" at the end of the "tcpserver: deny" line. If that bothers you + (eg. you have a strict log parsers), don't apply that chunk of the patch. + +- the idea for this patch came from my previous experience with xinetd, and + need to limit incoming bursts of virus/spam SMTP connections, since I was + running qmail-scanner to scan incoming and outgoing messages for viruses + and spam. + +When you make changes, please check that they work as expected. + +Examples (for tcprules created .cdb) +(a) 192.168.:allow,MAXLOAD="1000" + :allow,MAXCONNIP="3" + + this would allow any connection from your local LAN (192.168.*.* + addresses) if system load is less than 10.00. non-LAN connections would + be accepted only if clients from that IP address have not already opened + more than 2 connections (as your connection would be last allowed -- 3rd) + +(b) 192.168.:allow + 5.6.7.8:allow,MAXCONNIP="3" + 1.2.:allow,MAXLOAD="500",MAXCONNIP="1",MAXCONNC="5" + :allow,MAXLOAD="1000",MAXCONNIP="3",DIEMSG="421 example.com unavailable" + + if client connects from 192.168.*.* (ex: your LAN), it is allowed. + if it connects from 5.6.7.8 (ex: little abusive customer of yours), + it is allowed unless there are already 3active connections from 5.6.7.8 + to this service + if it connects from 1.2.*.* (ex: some problematic networks which caused + you grief in the past) it will connect only if load is less than 5.0, + there is less than 5 active connections from whole C class + (1.2.*.0/24), and if that specific IP address does not already have + connection open. + in all other cases, the client will be permitted to connect if load is + less than 10.00 and client has 2 or less connections open. If load is + higher than 10.00 or there are 3 or more connections open from this + client, the message "421 example.com unavailable" will be returned to + the client and connection terminated. + + +Any bugs introduced are my own, do not bother DJB with them. +If you find any, or have neat ideas, or better documentation, or whatever, +contact me. + +the latest version of the patch can be found at: +http://linux.voyager.hr/ucspi-tcp/ + +Enjoy, +Matija Nalis, +mnalis-tcpserver _at_ voyager.hr Index: tcpserver.c =================================================================== --- tcpserver.c.orig +++ tcpserver.c @@ -1,6 +1,14 @@ +#ifdef __dietlibc__ +#define NO_GETLOADAVG +#endif + #include <sys/types.h> #include <sys/param.h> #include <netdb.h> +#include <stdlib.h> +#ifdef NO_GETLOADAVG +#include <unistd.h> +#endif #include "uint16.h" #include "str.h" #include "byte.h" @@ -28,6 +36,18 @@ #include "sig.h" #include "dns.h" + +#ifdef SOLARIS +#include <kstat.h> +static kstat_ctl_t *kc; +#ifndef FSCALE +#define FSHIFT 8 /* bits to right of fixed binary point */ +#define FSCALE (1<<FSHIFT) +#endif /* FSCALE */ + +#define loaddouble(la) ((double)(la) / FSCALE) +#endif + int verbosity = 1; int flagkillopts = 1; int flagdelay = 1; @@ -59,11 +79,19 @@ char strnum2[FMT_ULONG]; static stralloc tmp; static stralloc fqdn; static stralloc addresses; +static stralloc diemsg_buf; char bspace[16]; buffer b; +typedef struct +{ + char ip[4]; + pid_t pid; +} baby; + +baby *child; /* ---------------------------- child */ @@ -72,6 +100,10 @@ buffer b; int flagdeny = 0; int flagallownorules = 0; char *fnrules = 0; +unsigned long maxload = 0; +long maxconnip = -1; +long maxconnc = -1; +char *diemsg = ""; void drop_nomem(void) { @@ -110,6 +142,8 @@ void drop_rules(void) strerr_die4sys(111,DROP,"unable to read ",fnrules,": "); } +unsigned long limit = 40; + void found(char *data,unsigned int datalen) { unsigned int next0; @@ -125,6 +159,14 @@ void found(char *data,unsigned int datal if (data[1 + split] == '=') { data[1 + split] = 0; env(data + 1,data + 1 + split + 1); + if (str_diff(data+1, "MAXLOAD") == 0) scan_ulong(data+1+split+1,&maxload); + if (str_diff(data+1, "MAXCONNIP") == 0) scan_ulong(data+1+split+1,&maxconnip); + if (str_diff(data+1, "MAXCONNC") == 0) scan_ulong(data+1+split+1,&maxconnc); + if (str_diff(data+1, "DIEMSG") == 0) { + if (!stralloc_copys(&diemsg_buf,data+1+split+1)) drop_nomem(); + if (!stralloc_0(&diemsg_buf)) drop_nomem(); + diemsg = diemsg_buf.s; + } } break; } @@ -133,9 +175,53 @@ void found(char *data,unsigned int datal } } +unsigned long getprocla(void) +{ +#ifdef SOLARIS + kstat_t *ksp; + kstat_named_t *knp; + double lavg; + kstat_chain_update(kc); + ksp = kstat_lookup(kc, "unix", 0, "system_misc"); + kstat_read(kc,ksp,NULL); + knp = kstat_data_lookup(ksp,"avenrun_1min"); + lavg = loaddouble(knp->value.ui32); + return (unsigned long)(lavg * 100); +#else +#ifdef NO_GETLOADAVG + int lret; + int i; + unsigned long u1, u2; + char *s; + static stralloc loadavg_data = {0}; + + lret = openreadclose("/proc/loadavg", &loadavg_data, 10); + if (lret != -1) { + /* /proc/loadavg format is: + * 13.08 3.04 1.00 34/170 14190 */ + s = loadavg_data.s; + i = scan_ulong (s, &u1); s+=i; + if ((i>0) && (i<5) && (*s == '.')) { /* load should be < 10000 */ + i = scan_ulong (s+1,&u2); + if (i==2) { /* we require two decimal places */ + return (u1 * 100 + u2); + } + return (u1 * 100); + } + } +#else + double result; + if (getloadavg(&result, 1) == 1) { + return (result * 100); + } +#endif +#endif +} + void doit(int t) { int j; + unsigned long curload = 0; remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0; @@ -211,6 +297,26 @@ void doit(int t) } } + if (maxload) { + curload = getprocla(); + if (curload > maxload) flagdeny = 2; + } + + if (!flagdeny && (maxconnip != -1 || maxconnc != -1)) { + unsigned long u; + long c1=0, cc=0; + for (u=0; u < limit; u++) if (child[u].pid != 0) { + if ((child[u].ip[0] == remoteip[0]) && + (child[u].ip[1] == remoteip[1]) && + (child[u].ip[2] == remoteip[2]) ) { + cc++; + if (child[u].ip[3] == remoteip[3]) c1++; + } + } + if (maxconnc != -1 && (cc >= maxconnc)) flagdeny = 4; + if (maxconnip != -1 && (c1 >= maxconnip)) flagdeny = 3; + } + if (verbosity >= 2) { strnum[fmt_ulong(strnum,getpid())] = 0; if (!stralloc_copys(&tmp,"tcpserver: ")) drop_nomem(); @@ -223,11 +329,35 @@ void doit(int t) cats(":"); safecats(remoteipstr); cats(":"); if (flagremoteinfo) safecats(tcpremoteinfo.s); cats(":"); safecats(remoteportstr); + if (flagdeny == 2) { + char curloadstr[FMT_ULONG]; + curloadstr[fmt_ulong(curloadstr,curload)] = 0; + cats(" "); safecats ("LOAD"); cats(":"); safecats(curloadstr); + } + if (flagdeny == 3) { + char maxconstr[FMT_ULONG]; + maxconstr[fmt_ulong(maxconstr,maxconnip)] = 0; + cats(" "); safecats ("MAXCONNIP"); cats(":"); safecats(maxconstr); + } + if (flagdeny == 4) { + char maxconstr[FMT_ULONG]; + maxconstr[fmt_ulong(maxconstr,maxconnc)] = 0; + cats(" "); safecats ("MAXCONNC"); cats(":"); safecats(maxconstr); + } cats("\n"); buffer_putflush(buffer_2,tmp.s,tmp.len); } - if (flagdeny) _exit(100); + if (flagdeny) { + if (*diemsg) { + buffer_init(&b,write,t,bspace,sizeof bspace); + buffer_puts(&b,diemsg); + if (buffer_putsflush(&b,"\r\n") == -1) + strerr_die2sys(111,DROP,"unable to print diemsg: "); + } + sleep(1); + _exit(100); + } } @@ -253,7 +383,6 @@ host port program",0); _exit(100); } -unsigned long limit = 40; unsigned long numchildren = 0; int flag1 = 0; @@ -278,6 +407,7 @@ void sigchld() { int wstat; int pid; + unsigned long u; while ((pid = wait_nohang(&wstat)) > 0) { if (verbosity >= 2) { @@ -286,6 +416,8 @@ void sigchld() strerr_warn4("tcpserver: end ",strnum," status ",strnum2,0); } if (numchildren) --numchildren; printstatus(); + for (u=0; u < limit; u++) if (child[u].pid == pid) { child[u].pid = 0; break; } + if (u == limit) strerr_die1x(111,"tcpserver: ERROR: dead child not found?!"); /* never happens */ } } @@ -299,6 +431,7 @@ main(int argc,char **argv) unsigned long u; int s; int t; + pid_t pid; while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:pPoO")) != opteof) switch(opt) { @@ -332,6 +465,11 @@ main(int argc,char **argv) argc -= optind; argv += optind; + x = env_get("MAXLOAD"); if (x) scan_ulong(x,&maxload); + x = env_get("MAXCONNIP"); if (x) scan_ulong(x,&maxconnip); + x = env_get("MAXCONNC"); if (x) scan_ulong(x,&maxconnc); + x = env_get("DIEMSG"); if (x) diemsg = x; + if (!verbosity) buffer_2->fd = -1; @@ -352,6 +490,10 @@ main(int argc,char **argv) } if (!*argv) usage(); + + child = calloc(sizeof(baby),limit); + if (!child) + strerr_die2x(111,FATAL,"out of memory for MAXCONNIP tracking"); sig_block(sig_child); sig_catch(sig_child,sigchld); @@ -393,6 +535,9 @@ main(int argc,char **argv) close(0); close(1); + #ifdef SOLARIS + kc = kstat_open(); + #endif printstatus(); for (;;) { @@ -405,7 +550,7 @@ main(int argc,char **argv) if (t == -1) continue; ++numchildren; printstatus(); - switch(fork()) { + switch(pid=fork()) { case 0: close(s); doit(t); @@ -420,6 +565,10 @@ main(int argc,char **argv) case -1: strerr_warn2(DROP,"unable to fork: ",&strerr_sys); --numchildren; printstatus(); + break; + default: + for (u=0; u < limit; u++) if (child[u].pid == 0) { byte_copy(child[u].ip,4,remoteip); child[u].pid = pid; break; } + if (u == limit) strerr_die1x(111,"tcpserver: ERROR: no empty space for new child?!"); /* never happens */ } close(t); }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor