- 作者:zhaozj
- 发表时间:2020-12-23 10:52
- 来源:未知
1. 同一IP最大连接数的限制 使用 Balazs Nagy的 periplimit patch 实现同一IP的最大连接数的限制。 相关地址: http://js.hu/package/ucspi-tcp/ 实现: ucspi-tcp-0.88-periplimit.6.patch diff -ru ucspi-tcp-0.88-orig/Makefile ucspi-tcp-0.88/Makefile --- ucspi-tcp-0.88-orig/Makefile Sat Mar 18 16:18:42 2000 +++ ucspi-tcp-0.88/Makefile Fri May 28 06:48:15 2004 @@ -753,7 +753,7 @@ alloc.h buffer.h error.h strerr.h sgetopt.h subgetopt.h pathexec.h / socket.h uint16.h ndelay.h remoteinfo.h stralloc.h uint16.h rules.h / stralloc.h sig.h dns.h stralloc.h iopause.h taia.h tai.h uint64.h / -taia.h +taia.h uint32.h ./compile tcpserver.c time.a: / diff -ru ucspi-tcp-0.88-orig/tcpserver.c ucspi-tcp-0.88/tcpserver.c --- ucspi-tcp-0.88-orig/tcpserver.c Sat Mar 18 16:18:42 2000 +++ ucspi-tcp-0.88/tcpserver.c Thu Jul 1 08:37:03 2004 @@ -2,6 +2,7 @@ #include <sys/param.h> #include <netdb.h> #include "uint16.h" +#include "uint32.h" #include "str.h" #include "byte.h" #include "fmt.h" @@ -242,6 +243,7 @@ tcpserver: usage: tcpserver / [ -1UXpPhHrRoOdDqQv ] / [ -c limit ] / +[ -s perip limit ] / [ -x rules.cdb ] / [ -B banner ] / [ -g gid ] / @@ -254,8 +256,24 @@ } unsigned long limit = 40; +unsigned long periplimit = 0; unsigned long numchildren = 0; +typedef struct +{ + pid_t pid; + int offset; +} connections; + +typedef struct +{ + uint32 ipaddr; + unsigned long num; +} ipchildren; + +connections *children; +ipchildren *numipchildren; + int flag1 = 0; unsigned long backlog = 20; unsigned long uid = 0; @@ -278,6 +296,7 @@ { int wstat; int pid; + int i; while ((pid = wait_nohang(&wstat)) > 0) { if (verbosity >= 2) { @@ -286,6 +305,12 @@ strerr_warn4("tcpserver: end ",strnum," status ",strnum2,0); } if (numchildren) --numchildren; printstatus(); + for (i=0;i<limit;++i) + if (children[i].pid == pid) { + children[i].pid=0; + numipchildren[children[i].offset].num--; + break; + } } } @@ -300,10 +325,11 @@ int s; int t; - while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:pPoO")) != opteof) + while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:s:pPoO")) != opteof) switch(opt) { case 'b': scan_ulong(optarg,&backlog); break; case 'c': scan_ulong(optarg,&limit); break; + case 's': scan_ulong(optarg,&periplimit); break; case 'X': flagallownorules = 1; break; case 'x': fnrules = optarg; break; case 'B': banner = optarg; break; @@ -334,6 +360,11 @@ if (!verbosity) buffer_2->fd = -1; + + if (!periplimit) + periplimit = limit; + if (limit<periplimit) + strerr_die2x(111,FATAL,"periplimit must be smaller than or equal to connection limit"); hostname = *argv++; if (!hostname) usage(); @@ -358,6 +389,11 @@ sig_catch(sig_term,sigterm); sig_ignore(sig_pipe); + if (!(numipchildren = (ipchildren*)malloc(sizeof(ipchildren)*limit))) + strerr_die2x(111,FATAL,"out of memory"); + byte_zero(numipchildren,sizeof(ipchildren)*limit); + if (!(children = (connections*)malloc(sizeof(connections)*limit))) + strerr_die2x(111,FATAL,"out of memory"); if (!stralloc_copys(&tmp,hostname)) strerr_die2x(111,FATAL,"out of memory"); if (dns_ip4_qualify(&addresses,&fqdn,&tmp) == -1) @@ -396,6 +432,13 @@ printstatus(); for (;;) { + uint32 ipaddr; + pid_t pid; + int i; + int lastempty = 0; + int freechild = 0; + ipchildren *ipcount; + while (numchildren >= limit) sig_pause(); sig_unblock(sig_child); @@ -403,9 +446,43 @@ sig_block(sig_child); if (t == -1) continue; + + for (i=0;i<limit;++i) { + if (children[i].pid == 0) { + freechild = i; + break; + } + } + uint32_unpack(remoteip,&ipaddr); + for (i=0;i<limit;++i) { + ipcount = &numipchildren[i]; + if (!ipcount->ipaddr || !ipcount->num) + lastempty = i; + else if (ipcount->ipaddr == ipaddr) { + ++ipcount->num; + break; + } + } + if (i == limit) { + if (lastempty) { + i = lastempty; + ipcount = &numipchildren[i]; + ipcount->ipaddr = ipaddr; + ipcount->num = 1; + } else + /* never reached */ + strerr_die2x(111,DROP,"internal problem"); + } + if (ipcount->num > periplimit) { + remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0; + strerr_warn3(DROP, "per ip limit reached for ", remoteipstr, 0); + close(t); + --ipcount->num; + continue; + } ++numchildren; printstatus(); - switch(fork()) { + switch(pid = fork()) { case 0: close(s); doit(t); @@ -420,6 +497,10 @@ case -1: strerr_warn2(DROP,"unable to fork: ",&strerr_sys); --numchildren; printstatus(); + break; + default: + children[freechild].pid = pid; + children[freechild].offset = i; } close(t); } 2. 修改 periplimit patch, 实现对特定的IP地址不受限制 (实现: myhan) 上面的patch对所有的IP地址都限制了, 但是我们在使用的时候, 往往有部分IP地址, 如 127.0.0.1 或者本机地址, 不希望受此限制. 通过设置控制文件, 可以实现从控制文件中读取不受限制的IP地址. 控制文件的位置: /var/qmail/control/freeip 控制文件的格式: 每一行一个完整的IP地址 3. 实现对同一IP的连接频率的限制 (实现: qftang) 2和3 的实现: ucspi-tcp-0.88-frequencylimit.1.patch --- tcpserver.c.1.2 2004-08-13 16:50:41.000000000 +0800 +++ tcpserver.c 2004-08-17 16:11:59.000000000 +0800 @@ -28,6 +28,7 @@ #include "rules.h" #include "sig.h" #include "dns.h" +#include <stdio.h> int verbosity = 1; int flagkillopts = 1; @@ -236,6 +237,7 @@ /* ---------------------------- parent */ #define FATAL "tcpserver: fatal: " +#define MAX_IP_TIME 4096 void usage(void) { @@ -244,6 +246,7 @@ [ -1UXpPhHrRoOdDqQv ] / [ -c limit ] / [ -s perip limit ] / +[ -f max connection per ip in one minite ] / [ -x rules.cdb ] / [ -B banner ] / [ -g gid ] / @@ -258,6 +261,7 @@ unsigned long limit = 40; unsigned long periplimit = 0; unsigned long numchildren = 0; +unsigned long max_freq = 20; typedef struct { @@ -271,14 +275,143 @@ unsigned long num; } ipchildren; +typedef struct freeip_t +{ + char ip[4]; + uint32 ipaddr; + unsigned long num; + struct freeip_t *next; +} freeip; + +typedef struct +{ + uint32 ipaddr; + unsigned long time; +} iptime; + + connections *children; ipchildren *numipchildren; +freeip *freeiplist = NULL; +iptime iptimebuf[MAX_IP_TIME]; int flag1 = 0; unsigned long backlog = 20; unsigned long uid = 0; unsigned long gid = 0; +void initfreeip(void) +{ + freeip *head = NULL; + freeip *tail = NULL; + char tempip[4]; + char buf[256] = {0}; + FILE *fp; + + if (!(head = (freeip *)malloc(sizeof(freeip)))) + strerr_die2x(111,FATAL,"out of memory"); + if (!(tail = (freeip *)malloc(sizeof(freeip)))) + strerr_die2x(111,FATAL,"out of memory"); + + uint32_unpack(localip, &head->ipaddr); + head->num = 100; + head->next = NULL; + + if (ip4_scan("127.0.0.1", tempip)) { + uint32_unpack(tempip, &tail->ipaddr); + tail->num = 100; + tail->next = NULL; + head->next = tail; + } + + fp = fopen("/var/qmail/control/freeip", "r"); + if( !fp ) { + freeiplist = head; + return; + } + while(fgets(buf, 256, fp)) { + freeip *new; + if (ip4_scan(buf, tempip)) { + new = (freeip *)malloc(sizeof(freeip)); + uint32_unpack(tempip, &new->ipaddr); + new->num = 100; + new->next = NULL; + tail->next = new; + tail = tail->next; + } else { + continue; + } + } + + freeiplist = head; +} + +void dumpfreeip(void) +{ + freeip *temp = freeiplist; + char tempip[4]; + char ipstr[IP4_FMT]; + char buf[256] = {0}; + do { + uint32_pack(tempip, temp->ipaddr); + ipstr[ip4_fmt(ipstr, tempip)] = 0; + sprintf (buf, "freeip: %s, num: %d/n", ipstr, temp->num); + write(2, buf, strlen(buf)); + temp = temp->next; + } while (temp); +} + +int isfreeip(uint32 ipaddr) +{ + freeip *temp = freeiplist; + int isfree = 0; + + do { + if (temp->ipaddr == ipaddr) { + isfree = 1; + break; + } + temp = temp->next; + } while (temp); + return isfree; +} + +void freefreeip(void) +{ +} + +int isoverfrequency(uint32 ipaddr) +{ + iptime *empty_iptime = NULL; + iptime *piptime = NULL; + time_t now = time(0); + int j; + int count = 0; + int emp_pos = 0; + + for(j=0; j<MAX_IP_TIME; j++) { + piptime = &iptimebuf[j]; + if(!empty_iptime) { + if( !piptime->ipaddr || piptime->time < (now - 60)) { + empty_iptime = piptime; + emp_pos = j; + } + } + if(piptime->ipaddr == ipaddr && piptime->time > (now - 60)) { + count ++; + if(empty_iptime && count >= max_freq) + break; + } + } + + if (!empty_iptime) empty_iptime = &iptimebuf[0]; + + empty_iptime->ipaddr = ipaddr; + empty_iptime->time = now; + + return (count >= max_freq) ? 1 : 0; +} + void printstatus(void) { if (verbosity < 2) return; @@ -325,11 +458,12 @@ int s; int t; - while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:s:pPoO")) != opteof) + while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:s:f:pPoO")) != opteof) switch(opt) { case 'b': scan_ulong(optarg,&backlog); break; case 'c': scan_ulong(optarg,&limit); break; case 's': scan_ulong(optarg,&periplimit); break; + case 'f': scan_ulong(optarg,&max_freq);break; case 'X': flagallownorules = 1; break; case 'x': fnrules = optarg; break; case 'B': banner = optarg; break; @@ -394,6 +528,8 @@ byte_zero(numipchildren,sizeof(ipchildren)*limit); if (!(children = (connections*)malloc(sizeof(connections)*limit))) strerr_die2x(111,FATAL,"out of memory"); + byte_zero(children,sizeof(connections)*limit); + byte_zero(iptimebuf,sizeof(iptime)*MAX_IP_TIME); if (!stralloc_copys(&tmp,hostname)) strerr_die2x(111,FATAL,"out of memory"); if (dns_ip4_qualify(&addresses,&fqdn,&tmp) == -1) @@ -426,6 +562,10 @@ buffer_puts(&b,"/n"); buffer_flush(&b); } + + initfreeip(); + + dumpfreeip(); close(0); close(1); @@ -473,13 +613,23 @@ /* never reached */ strerr_die2x(111,DROP,"internal problem"); } - if (ipcount->num > periplimit) { + //if (ipcount->num > periplimit) { + if (!isfreeip(ipaddr) && (ipcount->num > periplimit)) { remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0; strerr_warn3(DROP, "per ip limit reached for ", remoteipstr, 0); close(t); --ipcount->num; continue; } + + if (isoverfrequency(ipaddr)) { + remoteipstr[ip4_fmt(remoteipstr, remoteip)] = 0; + strerr_warn3(DROP, "frequency limit reached for ", remoteipstr, 0); + close(t); + continue; + } + + ++numchildren; printstatus(); switch(pid = fork()) { 4. 测试工具 :) tcpservertester.php #!/usr/local/bin/php -f <?php error_reporting(0); set_time_limit(6000); $keepconnect = false; for ($i=0; $i<50; $i++) { $fd = "fp_" . $i; //$$fd = fsockopen("localhost", 110, $errno, $errstr, 30); $$fd = fsockopen("172.18.10.37", 110, $errno, $errstr, 30); if ($$fd === false) { printf("No. %2d: [Error] ErrorNo: %d, ErrorInfo: %s/n", $i+1, $errno, $errstr); } else { printf("No. %2d: Connect Success!/n", $i+1); $buf = fgets($$fd, 1024); if (substr($buf, 0, 3) == '+OK') { printf("/tS: %s", $buf); if (!$keepconnect) { fwrite($$fd, "QUIT/r/n"); printf("/tC: QUIT/n"); $reply = fgets($$fd, 1024); printf("/tS: %s", $reply); fclose($$fd); printf("/tConnection closed/n"); } printf("/tSleep 10 seconds/n"); sleep(10); } else { printf("/tService is not available!/n"); if (!$keepconnect) { fclose($$fd); printf("/tConnection closed/n"); } printf("/tSleep 15 seconds/n"); sleep(15); } } } if ($keepconnect) { sleep(1000); } ?>