通过tcpserver实现对同一IP的最大连接数和连接频率的限制
2004-08-171. 同一IP最大连接数的限制
使用Balazs Nagy的 periplimit patch 实现同一IP的最大连接数的限制。
相关地址: http://js.hu/package/ucspi-tcp/
实现: ucspi-tcp-0.88-periplimit.6.patch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
--- 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: %dn", 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#!/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: %sn", $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, "QUITrn"); printf("tC: QUITn"); $reply = fgets($$fd, 1024); printf("tS: %s", $reply); fclose($$fd); printf("tConnection closedn"); } printf("tSleep 10 secondsn"); sleep(10); } else { printf("tService is not available!n"); if (!$keepconnect) { fclose($$fd); printf("tConnection closedn"); } printf("tSleep 15 secondsn"); sleep(15); } } } if ($keepconnect) { sleep(1000); } ?> |
发表评论