0

是否有更好的方法来重写此代码以增强性能?

如果您要获得一堆 IP,系统似乎会挂起。

TMP_PREFIX='/tmp/synd'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"
BANNED_IP_MAIL=`$TMP_FILE`
BANNED_IP_LIST=`$TMP_FILE`
echo "Banned the following ip addresses on `date`" > $BANNED_IP_MAIL
echo >> $BANNED_IP_MAIL
BAD_IP_LIST=`$TMP_FILE`
netstat -ntu | grep SYN_RECV | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST
cat $BAD_IP_LIST
if [ $KILL -eq 1 ]; then
    IP_BAN_NOW=0
    while read line; do
        CURR_LINE_CONN=$(echo $line | cut -d" " -f1)
        CURR_LINE_IP=$(echo $line | cut -d" " -f2)
        if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then
            break
        fi
        IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
        if [ $IGNORE_BAN -ge 1 ]; then
            continue
        fi
        IP_BAN_NOW=1
        echo "$CURR_LINE_IP with $CURR_LINE_CONN SYN_RECV connections" >> $BANNED_IP_MAIL
        echo $CURR_LINE_IP >> $BANNED_IP_LIST
        echo $CURR_LINE_IP >> $IGNORE_IP_LIST
        if [ $CSF_BAN -eq 1 ]; then
            $CSF -d $CURR_LINE_IP
        else
            $IPT -I INPUT -s $CURR_LINE_IP -j DROP
        fi
    done < $BAD_IP_LIST
    if [ $IP_BAN_NOW -eq 1 ]; then
        dt=`date`
                hn=`hostname`
        if [ $EMAIL_TO != "" ]; then
            cat $BANNED_IP_MAIL | mail -s "IP addresses banned on $dt $hn" $EMAIL_TO
        fi
    fi
fi
rm -f $TMP_PREFIX.*
4

1 回答 1

7

当然,有很多方法可以改进,但你应该尝试找出真正的瓶颈在哪里。(很可能是 iptables,在这种情况下,您可能想尝试在一次调用中完成所有表更新,而不是一次一次。但我只是在猜测。)

这里有一些建议;我没有通读一遍:

netstat -ntu | grep SYN_RECV | awk '{print $5}' | cut -d: -f1 |
sort | uniq -c | sort -nr > $BAD_IP_LIST

如果您只对 SYN_RECV 状态的连接感兴趣,为什么要列出 udp?无论如何,您正在使用三个实用程序(grepawkcut来执行一项简单的面向行的操作。你不妨一气呵成,例如 awk:

awk '$6 == "SYN_RECV" {print substr($5, 1, index($5, ":") - 1)}'

事实上,你也可以在 awk 中进行唯一化和计数:

awk '$6 == "SYN_RECV" {++ip[substr($5, 1, index($5, ":") - 1)]} END{for (i in ip) print ip[i], i}'

编辑:您还可以在此处按所需计数进行过滤:

awk '$6 == "SYN_RECV" {++ip[substr($5, 1, index($5, ":") - 1)]}
     END              {for (i in ip) if (ip[i] >= '$NO_OF_CONNECTIONS') print ip[i], i}'

现在您只需要输出 ip 地址,因为您不再需要在 bash 脚本中进行过滤。我不知道这是否比通过排序和 uniq 再排序更快,但很可能是这样。

while read line; do
    CURR_LINE_CONN=$(echo $line | cut -d" " -f1)
    CURR_LINE_IP=$(echo $line | cut -d" " -f2)
    if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then
        break
    fi

您想从标准输入读取两个字段。你为什么不这样做:

while read CURR_LINE_CONN CURR_LINE_IP IGNORED &&
      ((CURR_LINE_CONN >= NO_OF_CONNECTIONS)); do

这节省了两个子外壳和两个剪切调用。(read 内置的 IGNORED 只是妄想症,因为 awk 只会输出两个字段。不过,这不是好的妄想症,因为它会默默地忽略错误。)

编辑:如上所述,您也可以在这里摆脱测试。所以它只是:

netstat -nt |
awk '$6 == "SYN_RECV" {++ip[substr($5, 1, index($5, ":") - 1)]}
     END { for (i in ip)
             if (ip[i] >= '$NO_OF_CONNECTIONS')
               print ip[i], i}' | tee $BAD_IP_LIST
if ((KILL)); then
  IP_BAN_NOW=0
  while read IP IGNORED; do

下一个:

IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
    if [ $IGNORE_BAN -ge 1 ]; then
        continue
    fi

grep -c让 grep 读取整个输入文件以获取计数;你只想知道ip是否存在。你想要grep -q

if $(grep -q -F -x $CURR_LINE_IP $IGNORE_IP_LIST); then continue; fi

(-F告诉 grep 将模式解释为字符串而不是正则表达式,这是您想要的,因为否则.是通配符。-x告诉 grep 匹配整行。一个 ip 可能是前缀或后缀甚至是中缀另一个,这会导致错误匹配。-F 和 -x 的组合也可能会快一点,因为 grep 然后可以对匹配进行相当多的优化。)

可能还有更多。这就是我所得到的。

于 2012-10-15T16:50:57.200 回答