Как напечатать вычисленную оценку для всех входных данных в файле? - PullRequest
1 голос
/ 30 апреля 2011

Вот некоторый код Perl, который принимает два файла в качестве входных данных. Файлы содержат пакеты TCP. Он готовит себя к обычным пакетам, используя пакеты в первом файле, а затем печатает аномальные пакеты во втором файле.

while (<>) {
    if (($time, $to, $port, $from, $duration, $flags, $length, $text) = /(.{19}) (.{15}):(\d+) (.{15}):\d+ \+(\d+) (\S+) (\d+) (.*)/) {
        $text =~ s/\^M//g;
        $text =~ s/\^ /\n/g;
        if (($port == 25 || $port == 80) && $text =~ /\n\n/) {$text = "$`\n";}
        $text =~ s/^\^@//;
        if ($time =~ /(\d\d)\/(\d\d)\/\d\d\d\d (\d\d):(\d\d):(\d\d)/) {
            $now = ((($1 * 31 + $2) * 24 + $3) * 60 + $4) * 60 + $5;
        }
        foreach ($text =~ /.*\n/g) {
            if (($k, $v) = /(\S*)(.*)/) {
                $k = substr($k, 0, 30);
                $v = substr($v, 0, 100);
                $score   = 0;
                $comment = "";
                &alarm($port,       $k);
                &alarm($to,         $flags);
                &alarm("To",        "$to:$port");
                &alarm($to,         $from);
                &alarm("$to:$port", $from);
                if ($score > 30000) {
                    $score = log($score) / (10 * log(10));
                    printf("    #   0 $time $to %8.6f \#%s\n", $score, substr($comment, 0, 300));
                }
            }
        }
    }
}

sub alarm {
    local ($key, $val, $sc) = @_;
    if ($now < 10300000) {
        ++$n{$key};
        if (++$v{$key . $val} == 1) {
            ++$r{$key};
            $t{$key} = $now;
        }
    } elsif ($n{$key} > 0 && !$v{$key . $val}) {
        $score += ($now - $t{$key}) * $n{$key} / $r{$key};
        $comment .= " $key=$val";
        $t{$key} = $now;
    }
}

exit;

Я новичок в Perl, и в качестве небольшой части моего проекта необходимо, чтобы оценка аномалий была напечатана для всех пакетов во втором файле. Кто-нибудь может подсказать, как изменить код?

Ответы [ 2 ]

2 голосов
/ 30 апреля 2011

Из того, что я вижу здесь, выглядит так, как будто код (как сейчас) ищет пакеты до некоторого времени отключения и сохраняет ли он определенные условия в хэшах %n и %v.

Почему бы не присвоить дополнительный флаг вашей функции alarm, которая называется $training.Если это правда, просто учтите значения пакетов, в противном случае рассчитайте оценку для этой аномалии (если она есть) и верните это значение.Если аномалии нет или вы находитесь в режиме обучения, просто верните ноль:

    sub alarm {
         my ($key, $val, $training) = @_;
         my $score = 0;
         if ( $training ) {
             ...do your accounting...
         } else {
             ...do your comparisons & set score accordingly...
         }
         return $score;
     }

Бросьте свой большой while в подпрограмму, и пусть эта подпрограмма примет имя файла и будет ли оноРежим обучения или нет.

     sub examine {
         my ($file, $training) = @_;
         if ( open my $fh, '<', $file ) {
             while (<$fh>) {
                 ...this is your big while loop...
                 ...pass $training along to your alarm() calls...
             }
         } else {
             die "Failed to open $file: $!\n';
         }
     }

Ваша основная программа теперь:

     use constant TRAINING => 1;

     examine('file1',  TRAINING);
     examine('file2', !TRAINING);

Дополнительные примечания:

  • Используйте my() вместо localхотя это не оказывает существенного влияния на эту программу, это хорошая привычка.
  • Не используйте хорошо известное имя функции alarm, когда оно действительно ничего не делает, вместо этогоназовите это как check_packet_values - или что-то, что имеет смысл для вас и вашей команды.
  • Прекратите использовать магические числа

    use constant {
        CUTOFF_TIME   => 10300000,
        ANOMALY_SCORE =>    30000
    };
    
  • Используйтереальный анализатор даты / времени, чтобы ваши значения имели какое-то значение.str2time из Date::Parse даст вам время в секундах эпохи (секунды с 1 января 1970 года).

  • Используйте имена переменных, которые что-то значат.%n и %v трудно понять в этом коде, но %n_seen и %value_seen (а также %first_seen_time вместо %t).Помните, что ваш код не работает быстрее, если вы используете более короткие имена переменных.
  • Прекратите использование глобальных переменных, когда это возможно.Счетчики могут быть глобальными, но ваш комментарий должен быть встроен только в процедуру, которая инициализирует и печатает комментарий.Итак, вместо того, чтобы делать то, что вы делаете, как:

    $to_score = check_packet_value($to, $flags)
        and push @comments, "$to=$flags";
    ...
    $score = $to_score + $from_score + ...
    if ( !$training && $score > ANOMALY_THRESHOLD ) {
        print "blah blah blah @comments\n";
    }
    
  • Кроме того, никогда, никогда не используйте $ `- это приводит к огромным потерям производительности во всем вашем сценарии (даже еслиона никогда не вызывает эту функцию).Вместо:

    if ( $text =~ /\n\n/ ) { $text = $` }
    

Использовать

    if ( $text =~ /(.*)\n\n/ ) {
        $text = $1;
    }

(Редактировать: добавлено предупреждение о $ `)

0 голосов
/ 30 апреля 2011

Возможно, я неправильно понял ваш вопрос и комментарий, так что извините, если это не то, что вы спрашиваете ...

Ваша функция printf в настоящее время находится внутри этой проверки if ($score > 30000), так что выполучить вывод, только если $score> 30000.

if ($score>30000) {
    $score=log($score)/(10*log(10));
    printf("    #   0 $time $to %8.6f \#%s\n", $score, substr($comment, 0, 300));
}

Если вы хотите распечатать вывод независимо от $score, вам просто нужно переместить строку printf за пределы этого, если проверка.

...