Domanda

Is there a better way to rewrite this code to get enhanced performance?

If you were to get a bunch of IPs the system seems to hang.

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.*
È stato utile?

Soluzione

Sure, there are lots of ways that can be improved, but you should try to figure out where the real bottleneck is. (It may well be iptables, in which case you might want to try to do all the table updates in a single invocation instead of one at a time. But I'm just guessing.)

Here are a few suggestions; I didn't read all the way through:

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

If you're only interested in connections in SYN_RECV state, why list udp? Anyway, you're using three utilities (grep, awk and cut) to do one simple line-oriented action. You might as well just do it all in one, for example awk:

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

In fact, you could do the uniquifying and counting in awk as well:

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

Edit: you could also filter by required count here:

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

Now you only need to output the ip address, since you no longer need to filter in the bash script. I don't know if that's faster than piping through sort and uniq and sort again, but it might very well be.

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

You want to read two fields from stdin. Why don't you just do that:

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

That saves two subshells and two cut invocations. (The IGNORED in the read built-in is just paranoia, since there will only be two fields output by awk. It's not good paranoia, though, because it silently ignores errors.)

Edit: as above, you could get rid of the test here, too. So it would just be:

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

Next:

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

grep -c makes grep read the entire input file to get the count; you only want to know if the ip is present. You want grep -q:

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

(-F tells grep to interpret the pattern as a string instead of a regex, which is what you want since otherwise . are wildcards. -x tells grep to match the entire line. It's possible for one ip to be a prefix or a suffix or even an infix of another one, which would lead to false matches. The combination of -F and -x might be a bit faster, too, since grep can then optimize the matching quite a bit.)

There's probably more. That's as far as I got.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top