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;iipaddr || !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; 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<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);
}
?>

|