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である可能性があります。その場合、一度に1回ではなく、1回の呼び出しですべてのテーブル更新を実行しようとする可能性があります。しかし、私は推測しています。)

ここにいくつかの提案があります。私はずっと読んでいませんでした:

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

SYN_RECV状態の接続のみに関心がある場合は、なぜudpをリストするのですか?とにかく、3つのユーティリティ(、、および)を使用grepawkcut、1つの単純な行指向のアクションを実行しています。たとえば、awkのように、すべてを1つにまとめた方がよいでしょう。

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}'

これで、bashスクリプトでフィルタリングする必要がなくなったため、IPアドレスを出力するだけで済みます。それがsortと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

stdinから2つのフィールドを読み取りたいとします。なぜあなたはそれをしませんか:

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

これにより、2つのサブシェルと2つのカット呼び出しが節約されます。(readビルトインのIGNOREDは、awkによって出力されるフィールドが2つしかないため、単なるパラノイアです。ただし、エラーを黙って無視するため、パラノイアとしては適切ではありません。)

編集:上記のように、ここでもテストを取り除くことができます。したがって、次のようになります。

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 -cgrepに入力ファイル全体を読み取らせてカウントを取得します。IPが存在するかどうかだけを知りたいのです。あなたが欲しいgrep -q

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

-Fgrepに、パターンを正規表現ではなく文字列として解釈するように指示します。これは、.ワイルドカードであるため、必要なものです-x。grepに、行全体に一致するように指示します。1つのIPをプレフィックス、サフィックス、またはもう1つは、誤った一致につながる可能性があります。-Fと-xの組み合わせも、grepが一致をかなり最適化できるため、少し高速になる可能性があります。)

おそらくもっとあります。それは私が得た限りです。

于 2012-10-15T16:50:57.200 に答える