[CODE] 通过tcpserver实现对同一IP的最大连接数和连接频率的限制

类别:编程语言 点击:0 评论:0 推荐:
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 #include #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;ifd = -1; + + if (!periplimit) + periplimit = limit; + if (limit

= limit) sig_pause(); sig_unblock(sig_child); @@ -403,9 +446,43 @@ sig_block(sig_child); if (t == -1) continue; + + for (i=0;inum) + 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; jipaddr || 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

本文地址:http://com.8s8s.com/it/it28505.htm