Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 98726 | Differences between
and this patch

Collapse All | Expand All

(-)ucspi-tcp-0.88.org/README.tcpserver-limits-patch (+135 lines)
Line 0 Link Here
1
20050130 reinstated /proc/loadavg support for those compiling on Linux 
2
with dietlibc (see #define NO_GETLOADAVG at top of tcpserver.c). 
3
Also, we now compile on 64bit platforms (we avoid including unistd.h if
4
using getloadavg(3), so we don't conflict with readwrite.h header file)
5
Needed if your compile was breaking with:
6
readwrite.h:4: error: syntax error before "read"
7
readwrite.h:4: warning: data definition has no type or storage class
8
SUMMARY: If 20040725 worked for you, there is no reason to upgrade 
9
(no new features of bugfixes)
10
11
20040725 adds a sleep(1) before terminating (to prevent too high load from
12
many rapid fork()/exit() calls. It also changes the method for checking
13
system load to getloadavg(3) instead of parsing /proc/loadavg, therefore
14
making it working on *BSD and other non-Linux systems in addition to Linux.
15
It also adds DIEMSG="xxx" support.
16
17
20040327 fixes a bug in 20040124 related to MAXLOAD (it would not work
18
correctly when load was higher than 10.00)
19
20
21
This patch (20040725) makes tcpserver from DJB's ucspi-tcp-0.88 package (see
22
http://cr.yp.to/ucspi-tcp.html) to modify its behavior if some environment
23
variables are present.
24
25
The variables can be preset before starting tcpserver (thus acting as
26
default for all connections), or, if you use 'tcpserver -x xxx.cdb', they
27
can be set (or overridden) from xxx.cdb. If none of the variables are set,
28
tcpserver behaves same as non patched version (except for negligible
29
performance loss). Any or all variables can be set, as soon as first limit
30
is reached the connection is dropped. I'd recommend using .cdb files
31
exclusively though, as you can then modify configuration without killing
32
tcpserver.
33
34
The variables are:
35
36
(1) MAXLOAD 
37
    maximum 1-minute load average * 100. For example, if you have line
38
    :allow,MAXLOAD="350" 
39
    in your rules file from which you created .cdb, the connection will be
40
    accepted only if load average is below 3.50
41
    For this variable to have effect, you have to have working getloadavg(3)
42
    (most modern UN*Xoids have, including Linux and FreeBSD)
43
    Otherwise, you have to uncomment #define NO_GETLOADAVG in tcpserver.c 
44
    and have readable '/proc/loadavg' with linux-2.4.x/2.6.x syntax (see 
45
    the source -- this is needed if you're compiling with dietlibc 
46
    or such)
47
  
48
(2) MAXCONNIP
49
    maximum connections from one IP address. tcpserver's -c flag defines
50
    maximum number of allowed connections, but it can be abused if
51
    just one host goes wild and eats all the connections - no other host
52
    would be able to connect then. If you created your .cdb with:
53
    :allow,MAXCONNIP="5"
54
    and run tcpserver -c 50, then each IP address would be able to have at 
55
    most 5 concurrent connections, while there still could connect 50
56
    clients total
57
58
(3) MAXCONNC
59
60
    maximum connections from whole C-class (256 addresses). Extension of
61
    MAXCONNIP, as sometimes the problematic client has a whole farm of
62
    client machines with different IP addresses instead of just one IP
63
    address, and they all try to connect. It might have been more useful to
64
    be able to specify CIDR block than C-class, but I've decided to KISS.
65
66
    for example tcpserver -c 200, and .cdb with:
67
    :allow,MAXCONNC="15"
68
    will allow at most 15 host from any x.y.z.0/24 address block, while
69
    still allowing up to 200 total connections.
70
71
(4) DIEMSG
72
    
73
    if set and one of the above limits is exceeded, this is the message 
74
    to be sent to client (CRLF is always added to the text) before terminating
75
    connection. If unset, the connection simply terminates (after 1 sec delay) 
76
    if limit is exceeded.
77
78
    For example:
79
    DIEMSG="421 example.com Service temporarily not available, closing 
80
    transmission channel"
81
82
Notes: 
83
84
- if a connection is dropped due to some of those variables set, it will be
85
  flagged (if you run tcpserver -v) with "LOAD:", "MAXCONNIP:" or
86
  "MAXCONNC:" at the end of the "tcpserver: deny" line. If that bothers you
87
  (eg. you have a strict log parsers), don't apply that chunk of the patch.
88
89
- the idea for this patch came from my previous experience with xinetd, and
90
  need to limit incoming bursts of virus/spam SMTP connections, since I was
91
  running qmail-scanner to scan incoming and outgoing messages for viruses
92
  and spam.
93
94
When you make changes, please check that they work as expected. 
95
96
Examples (for tcprules created .cdb)
97
(a) 192.168.:allow,MAXLOAD="1000"
98
    :allow,MAXCONNIP="3"
99
100
    this would allow any connection from your local LAN (192.168.*.*
101
    addresses) if system load is less than 10.00. non-LAN connections would
102
    be accepted only if clients from that IP address have not already opened
103
    more than 2 connections (as your connection would be last allowed -- 3rd)
104
105
(b) 192.168.:allow
106
    5.6.7.8:allow,MAXCONNIP="3"
107
    1.2.:allow,MAXLOAD="500",MAXCONNIP="1",MAXCONNC="5"
108
    :allow,MAXLOAD="1000",MAXCONNIP="3",DIEMSG="421 example.com unavailable"
109
110
    if client connects from 192.168.*.* (ex: your LAN), it is allowed.
111
    if it connects from 5.6.7.8 (ex: little abusive customer of yours),
112
     it is allowed unless there are already 3active connections from 5.6.7.8
113
     to this service
114
    if it connects from 1.2.*.* (ex: some problematic networks which caused
115
     you grief in the past) it will connect only if load is less than 5.0,
116
     there is less than 5 active connections from whole C class
117
     (1.2.*.0/24), and if that specific IP address does not already have
118
     connection open.
119
    in all other cases, the client will be permitted to connect if load is
120
     less than 10.00 and client has 2 or less connections open. If load is
121
     higher than 10.00 or there are 3 or more connections open from this
122
     client, the message "421 example.com unavailable" will be returned to 
123
     the client and connection terminated.
124
125
126
Any bugs introduced are my own, do not bother DJB with them.
127
If you find any, or have neat ideas, or better documentation, or whatever,
128
contact me.
129
130
the latest version of the patch can be found at:
131
http://linux.voyager.hr/ucspi-tcp/
132
133
Enjoy,
134
Matija Nalis,
135
mnalis-tcpserver _at_ voyager.hr
(-)ucspi-tcp-0.88.org/tcpserver.c (-3 / +114 lines)
Lines 1-6 Link Here
1
#ifdef __dietlibc__
2
#define NO_GETLOADAVG
3
#endif
4
1
#include <sys/types.h>
5
#include <sys/types.h>
2
#include <sys/param.h>
6
#include <sys/param.h>
3
#include <netdb.h>
7
#include <netdb.h>
8
#include <stdlib.h>
9
#ifdef NO_GETLOADAVG
10
#include <unistd.h>
11
#endif
4
#include "uint16.h"
12
#include "uint16.h"
5
#include "str.h"
13
#include "str.h"
6
#include "byte.h"
14
#include "byte.h"
Lines 64-69 char bspace[16]; Link Here
64
buffer b;
72
buffer b;
65
73
66
74
75
typedef struct
76
{
77
  char ip[4];
78
  pid_t pid;
79
} baby;
80
81
baby *child;
67
82
68
/* ---------------------------- child */
83
/* ---------------------------- child */
69
84
Lines 72-77 buffer b; Link Here
72
int flagdeny = 0;
87
int flagdeny = 0;
73
int flagallownorules = 0;
88
int flagallownorules = 0;
74
char *fnrules = 0;
89
char *fnrules = 0;
90
unsigned long maxload = 0;
91
unsigned long maxconnip = 0;
92
unsigned long maxconnc = 0;
93
char *diemsg = "";
75
94
76
void drop_nomem(void)
95
void drop_nomem(void)
77
{
96
{
Lines 110-115 void drop_rules(void) Link Here
110
  strerr_die4sys(111,DROP,"unable to read ",fnrules,": ");
129
  strerr_die4sys(111,DROP,"unable to read ",fnrules,": ");
111
}
130
}
112
131
132
unsigned long limit = 40;
133
113
void found(char *data,unsigned int datalen)
134
void found(char *data,unsigned int datalen)
114
{
135
{
115
  unsigned int next0;
136
  unsigned int next0;
Lines 125-130 void found(char *data,unsigned int datal Link Here
125
	if (data[1 + split] == '=') {
146
	if (data[1 + split] == '=') {
126
	  data[1 + split] = 0;
147
	  data[1 + split] = 0;
127
	  env(data + 1,data + 1 + split + 1);
148
	  env(data + 1,data + 1 + split + 1);
149
	  if (str_diff(data+1, "MAXLOAD") == 0) scan_ulong(data+1+split+1,&maxload);
150
	  if (str_diff(data+1, "MAXCONNIP") == 0) scan_ulong(data+1+split+1,&maxconnip);
151
	  if (str_diff(data+1, "MAXCONNC") == 0) scan_ulong(data+1+split+1,&maxconnc);
152
	  if (str_diff(data+1, "DIEMSG") == 0) diemsg = data+1+split+1;
128
	}
153
	}
129
	break;
154
	break;
130
    }
155
    }
Lines 136-141 void found(char *data,unsigned int datal Link Here
136
void doit(int t)
161
void doit(int t)
137
{
162
{
138
  int j;
163
  int j;
164
  unsigned long curload;
139
165
140
  remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0;
166
  remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0;
141
167
Lines 211-216 void doit(int t) Link Here
211
    }
237
    }
212
  }
238
  }
213
239
240
  if (maxload) {
241
#ifdef NO_GETLOADAVG
242
	int lret;
243
	int i;
244
	unsigned long u1, u2;
245
	char *s;
246
	static stralloc loadavg_data = {0};
247
248
	lret = openreadclose("/proc/loadavg", &loadavg_data, 10);
249
	if (lret != -1) {
250
		/* /proc/loadavg format is:
251
		   13.08 3.04 1.00 34/170 14190 */
252
		s = loadavg_data.s;
253
		i = scan_ulong (s, &u1); s+=i;
254
		if ((i>0) && (i<5) && (*s == '.')) {    /* load should be < 10000 */
255
			i = scan_ulong (s+1,&u2);
256
			if (i==2) {     /* we require two decimal places */
257
				curload = u1 * 100 + u2;
258
				if (curload > maxload) flagdeny = 2;
259
			}
260
		}
261
	}
262
#else
263
	double result;
264
	if (getloadavg(&result, 1) == 1) {
265
		curload = result * 100;
266
		if (curload > maxload) flagdeny = 2;
267
	}
268
#endif
269
  }
270
  
271
  if (!flagdeny && (maxconnip || maxconnc)) {
272
  	unsigned long u, c1=0, cc=0;
273
  	for (u=0; u < limit; u++) if (child[u].pid != 0) { 
274
  		if ((child[u].ip[0] == remoteip[0]) &&
275
  		    (child[u].ip[1] == remoteip[1]) &&
276
  		    (child[u].ip[2] == remoteip[2]) ) {
277
  		    cc++;
278
  		    if (child[u].ip[3] == remoteip[3]) c1++;
279
  		}
280
  	}
281
	if (maxconnc && (cc >= maxconnc)) flagdeny = 4;
282
	if (maxconnip && (c1 >= maxconnip)) flagdeny = 3;
283
  }
284
214
  if (verbosity >= 2) {
285
  if (verbosity >= 2) {
215
    strnum[fmt_ulong(strnum,getpid())] = 0;
286
    strnum[fmt_ulong(strnum,getpid())] = 0;
216
    if (!stralloc_copys(&tmp,"tcpserver: ")) drop_nomem();
287
    if (!stralloc_copys(&tmp,"tcpserver: ")) drop_nomem();
Lines 223-233 void doit(int t) Link Here
223
    cats(":"); safecats(remoteipstr);
294
    cats(":"); safecats(remoteipstr);
224
    cats(":"); if (flagremoteinfo) safecats(tcpremoteinfo.s);
295
    cats(":"); if (flagremoteinfo) safecats(tcpremoteinfo.s);
225
    cats(":"); safecats(remoteportstr);
296
    cats(":"); safecats(remoteportstr);
297
    if (flagdeny == 2) {
298
    	char curloadstr[FMT_ULONG];
299
    	curloadstr[fmt_ulong(curloadstr,curload)] = 0;
300
    	cats(" "); safecats ("LOAD"); cats(":"); safecats(curloadstr);
301
    }
302
    if (flagdeny == 3) {
303
    	char maxconstr[FMT_ULONG];
304
    	maxconstr[fmt_ulong(maxconstr,maxconnip)] = 0;
305
    	cats(" "); safecats ("MAXCONNIP"); cats(":"); safecats(maxconstr);
306
    }
307
    if (flagdeny == 4) {
308
    	char maxconstr[FMT_ULONG];
309
    	maxconstr[fmt_ulong(maxconstr,maxconnc)] = 0;
310
    	cats(" "); safecats ("MAXCONNC"); cats(":"); safecats(maxconstr);
311
    }
226
    cats("\n");
312
    cats("\n");
227
    buffer_putflush(buffer_2,tmp.s,tmp.len);
313
    buffer_putflush(buffer_2,tmp.s,tmp.len);
228
  }
314
  }
229
315
230
  if (flagdeny) _exit(100);
316
  if (flagdeny) {
317
    if (*diemsg) {
318
      buffer_init(&b,write,t,bspace,sizeof bspace);
319
      buffer_puts(&b,diemsg);
320
      if (buffer_putsflush(&b,"\r\n") == -1)
321
        strerr_die2sys(111,DROP,"unable to print diemsg: ");
322
    }
323
    sleep(1);
324
    _exit(100);
325
  }
231
}
326
}
232
327
233
328
Lines 253-259 host port program",0); Link Here
253
  _exit(100);
348
  _exit(100);
254
}
349
}
255
350
256
unsigned long limit = 40;
257
unsigned long numchildren = 0;
351
unsigned long numchildren = 0;
258
352
259
int flag1 = 0;
353
int flag1 = 0;
Lines 278-283 void sigchld() Link Here
278
{
372
{
279
  int wstat;
373
  int wstat;
280
  int pid;
374
  int pid;
375
  unsigned long u;
281
 
376
 
282
  while ((pid = wait_nohang(&wstat)) > 0) {
377
  while ((pid = wait_nohang(&wstat)) > 0) {
283
    if (verbosity >= 2) {
378
    if (verbosity >= 2) {
Lines 286-291 void sigchld() Link Here
286
      strerr_warn4("tcpserver: end ",strnum," status ",strnum2,0);
381
      strerr_warn4("tcpserver: end ",strnum," status ",strnum2,0);
287
    }
382
    }
288
    if (numchildren) --numchildren; printstatus();
383
    if (numchildren) --numchildren; printstatus();
384
    for (u=0; u < limit; u++) if (child[u].pid == pid) { child[u].pid = 0; break; }
385
    if (u == limit) strerr_die1x(111,"tcpserver: ERROR: dead child not found?!"); /* never happens */
289
  }
386
  }
290
}
387
}
291
388
Lines 299-304 main(int argc,char **argv) Link Here
299
  unsigned long u;
396
  unsigned long u;
300
  int s;
397
  int s;
301
  int t;
398
  int t;
399
  pid_t pid;
302
 
400
 
303
  while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:pPoO")) != opteof)
401
  while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:pPoO")) != opteof)
304
    switch(opt) {
402
    switch(opt) {
Lines 332-337 main(int argc,char **argv) Link Here
332
  argc -= optind;
430
  argc -= optind;
333
  argv += optind;
431
  argv += optind;
334
432
433
  x = env_get("MAXLOAD"); if (x) scan_ulong(x,&maxload);
434
  x = env_get("MAXCONNIP"); if (x) scan_ulong(x,&maxconnip);
435
  x = env_get("MAXCONNC"); if (x) scan_ulong(x,&maxconnc);
436
  x = env_get("DIEMSG"); if (x) diemsg = x;
437
  
335
  if (!verbosity)
438
  if (!verbosity)
336
    buffer_2->fd = -1;
439
    buffer_2->fd = -1;
337
 
440
 
Lines 352-357 main(int argc,char **argv) Link Here
352
  }
455
  }
353
456
354
  if (!*argv) usage();
457
  if (!*argv) usage();
458
  
459
  child = calloc(sizeof(baby),limit);
460
  if (!child)
461
    strerr_die2x(111,FATAL,"out of memory for MAXCONNIP tracking");
355
 
462
 
356
  sig_block(sig_child);
463
  sig_block(sig_child);
357
  sig_catch(sig_child,sigchld);
464
  sig_catch(sig_child,sigchld);
Lines 405-411 main(int argc,char **argv) Link Here
405
    if (t == -1) continue;
512
    if (t == -1) continue;
406
    ++numchildren; printstatus();
513
    ++numchildren; printstatus();
407
 
514
 
408
    switch(fork()) {
515
    switch(pid=fork()) {
409
      case 0:
516
      case 0:
410
        close(s);
517
        close(s);
411
        doit(t);
518
        doit(t);
Lines 420-425 main(int argc,char **argv) Link Here
420
      case -1:
527
      case -1:
421
        strerr_warn2(DROP,"unable to fork: ",&strerr_sys);
528
        strerr_warn2(DROP,"unable to fork: ",&strerr_sys);
422
        --numchildren; printstatus();
529
        --numchildren; printstatus();
530
        break;
531
      default:
532
        for (u=0; u < limit; u++) if (child[u].pid == 0) { byte_copy(child[u].ip,4,remoteip); child[u].pid = pid; break; }
533
	if (u == limit) strerr_die1x(111,"tcpserver: ERROR: no empty space for new child?!"); /* never happens */
423
    }
534
    }
424
    close(t);
535
    close(t);
425
  }
536
  }

Return to bug 98726