В общем вот скриптец на перле, и формат конфига:
Код:
#!/usr/bin/perl
# Эта утилитка читает конфиг и согласно конфигу конфигурирует ACL на коммутаторах DLink 3550/3526
# с помощью ACL разрешаются IP/ARP пакеты только от жестко указанных IP адресов на заданных портах
# Таким образом осуществляется привязка IP-Port. Для этого используются номера профилей 6,7 и 8
# 6,7ой разрешают все входящие с порта IP/ARP пакеты с разрешенными IP адресами отправителя. 8ой
# запрещает прохождение всех пакетов. При конфигурировании (через telnet) сначала удаляются профили 8,7,6
# а затем создаются новые (сначала разрешающие, потом запрещающие). Пример (адрес 10.10.10.8 на 3 порту):
#
# Разрешить фреймы 0x0806 с исход. ip = 10.10.10.8 на 3 порту (0x0806 - ARP)
# cr access packet offset_16-31 0xFFFF0000 0x0 0x0 0x0 offset_32-47 0xFFFFFFFF 0x0 0x0 0x0 prof 6
# co access profile_id 6 add access_id auto packet offset_16-31 0x08060000 0x0 0x0 0x0 offset_32-47 0x0A0A0A08 0x0 0x0 0x0 port 3 permit
#
# Разрешить фреймы 0x0800 с исход. ip = 10.10.10.8 на 3 порту (0x0800 -IPv4)
# cr access packet offset_16-31 0xFFFF0000 0x0 0x0 0x0000FFFF offset_32-47 0xFFFF0000 0x0 0x0 0x0 prof 7
# co access profile_id 7 add access_id auto packet offset_16-31 0x08000000 0x0 0x0 0x0A0A offset_32-47 0x0A080000 0x0 0x0 0x0 port 3 permit
#
# Запретить все остальные фреймы
# create access packet offset_0-15 0x0 0x0 0x0 0x0 profile_id 8
# config access profile_id 8 add access_id auto packet offset_0-15 0x0 0x0 0x0 0x0 port 3 deny
use strict;
use warnings;
use Net::Telnet ();
my $maxlineip = 10;
my $conffile = "./rules.conf";
my $prompt = '/DES.{1,18}\#/';
# Константы ACL профилей
my $cprofile6 = "create access_profile packet_content offset_16-31 0xFFFF0000 0x0 0x0 0x0 ".
"offset_32-47 0xFFFFFFFF 0x0 0x0 0x0 profile_id 6";
my $cprofile7 = "create access_profile packet_content offset_16-31 0xFFFF0000 0x0 0x0 ".
"0x0000FFFF offset_32-47 0xFFFF0000 0x0 0x0 0x0 profile_id 7";
my $cprofile8 = "create access_profile packet_content offset_0-15 0x0 0x0 0x0 0x0 profile_id 8";
my $dprofile6 = "delete access_profile prof 6";
my $dprofile7 = "delete access_profile prof 7";
my $dprofile8 = "delete access_profile prof 8";
my %config = ();
read_config();
process_config();
exit(0);
# Соединяемся с коммутатором и конфигурируем ACL
sub configure_switch
{
# Переменные и параметры функции
my $faddr = shift; my $login = shift; my $passw = shift; my $plist = shift;
my %plst = %{$plist}; my $sock = undef; my $i = 0;
my @out; my $result = undef; my %uplinks = ();
# Парсим строку адреса (адрес, телнет-порт, число портов коммутатора, список аплинков)
my ($addr, $port, $ifnum, $uplist) =
$faddr =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\:(\d{1,5})\:(\d{1,3})\:\[([\d,\s]{1,20})\]$/;
# Uplist это список аплинков/даунлинков. Пример: 47,49,50 (для этих портов нет deny правил)
my @upl = split /\,\s*/, $uplist;
$uplinks{int($_)} = 1 for (@upl);
# Подключаемся по telnet к коммутатору и логинимся
my $t = new Net::Telnet(Timeout => 1, Prompt => $prompt);
if (($t->open(Host => $addr, Port => $port)) && ($t->login($login, $passw)))
{
# 1. Удалим access профили с номерами 8,7,6
@out = $t->cmd($dprofile8); $result = join('', @out);
if (!(($result =~ /Success./) || ($result =~ /not exist/))) { print "Can't del prof8\n"; return; }
@out = $t->cmd($dprofile7); $result = join('', @out);
if (!(($result =~ /Success./) || ($result =~ /not exist/))) { print "Can't del prof7\n"; return; }
@out = $t->cmd($dprofile6); $result = join('', @out);
if (!(($result =~ /Success./) || ($result =~ /not exist/))) { print "Can't del prof6\n"; return; }
# 2. Создадим access профили 6,7,8
@out = $t->cmd($cprofile6); $result = join('', @out);
if (!($result =~ /Success./)) { print "Can't create prof6\n"; return; }
@out = $t->cmd($cprofile7); $result = join('', @out);
if (!($result =~ /Success./)) { print "Can't create prof7\n"; return; }
@out = $t->cmd($cprofile8); $result = join('', @out);
if (!($result =~ /Success./)) { print "Can't create prof8\n"; return; }
# 3. На профили 6,7 навешаем правила для всех портов из конфига
for (sort keys %plst)
{
my $prt = int($_);
if ($prt > $ifnum) { print "WARNING: Ifindex $prt out of range (> $ifnum)\n"; }
else
{
if (defined($plst{$_}))
{
my @ipl = @{$plst{$_}};
for (@ipl)
{
my $ip = $_;
my ($oct1, $oct2, $oct3, $oct4) = $ip =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/;
my $offs1 = sprintf("0x%02X%02X", $oct1, $oct2);
my $offs2 = sprintf("0x%02X%02X0000", $oct3, $oct4);
my $offs3 = sprintf("0x%02X%02X%02X%02X", $oct1, $oct2, $oct3, $oct4);
# Правило для профиля 6 (разрешает ARP для указанного адреса)
my $acl = "conf access profile_id 6 add access_id auto packet offset_16-31 0x08060000 ".
"0x0 0x0 0x0 offset_32-47 $offs3 0x0 0x0 0x0 port $prt permit";
@out = $t->cmd($acl); $result = join('', @out);
if (!($result =~ /Success./))
{ print "Can't create permit ACL (p6, ARP) for ip=$ip and port=$prt\n"; return; }
# Правило для профиля 7 (разрешает IPv4 для указанного профиля)
$acl = "co access profile_id 7 add access_id auto packet offset_16-31 0x08000000 ".
"0x0 0x0 $offs1 offset_32-47 $offs2 0x0 0x0 0x0 port $prt permit";
@out = $t->cmd($acl); $result = join('', @out);
if (!($result =~ /Success./))
{ print "Can't create permit ACL for ip=$ip and port=$prt\n"; return; }
}
}
}
}
# Пишем запрещающие правила для всех портов кроме аплинков
for ($i=1;$i<=$ifnum; $i++)
{
next if (defined($uplinks{$i}));
my $acl = "conf access profile 8 add access_id auto packet ".
"offset_0-15 0x0 0x0 0x0 0x0 port $i deny";
@out = $t->cmd($acl); $result = join('', @out);
if (!($result =~ /Success./))
{ print "Can't create deny all ACL for port=$i\n"; }
}
}
}
# Для каждого свича из конфига вызывает configure_switch()
sub process_config
{
for(keys %config)
{
my $addr = $_;
my %acfg = %{$config{$addr}};
my %plist = ();
if ((defined($acfg{ports})) &&
(defined($acfg{login})) &&
(defined($acfg{passw})))
{
%plist = %{$acfg{ports}};
print "Configuring rules on switch $addr\n";
configure_switch($addr, $acfg{login}, $acfg{passw}, \%plist);
}
else
{
print "Not all fields (user, pass, ports) defined for $addr\n";
}
}
}
# Читает конфигурационный файл (адреса коммутаторов, логины, пароли и правила)
sub read_config
{
(open my $CFH, "<".$conffile) || die("Can't open config file\n");
my $begin_switch = 0;
my $addr = "";
while(<$CFH>)
{
# Пропустим комментарии
my $str = $_; my $parsed = 0; chomp($str);
next if (($str eq "") || ($str =~ /^\#/) || ($str =~ /^\/\//));
next if ($str =~ /\-\-\-\-\-\-\-\-\-\-/);
next if ($str =~ /^\s+$/);
# Секция каждого коммутатора начинается с правила addr
if ($begin_switch == 1)
{
# Имя пользователя
if ($str =~ /user\:\s+(.{2,20})/)
{
my ($login) = $str =~ /user\:\s+(.{2,20})/;
$config{$addr}{login} = $login;
$parsed = 1;
}
# Пароль на коммутатор (operator)
if ($str =~ /pass\:\s+(.{2,20})/)
{
my ($passw) = $str =~ /pass\:\s+(.{2,20})/;
$config{$addr}{passw} = $passw;
$parsed = 1;
}
# Данные по порту (на строке порта максимум 10 адресов это 17*10 символов)
# Строк с одним номером порта может быть несколько.
my $maxipchars = $maxlineip*17;
if ($str =~ /^port\s+(\d{1,3})\:\s+(.{7,$maxipchars})$/)
{
my ($port, $iplist) = $str =~ /^port\s+(\d{1,3})\:\s+(.{7,$maxipchars})$/;
my $pname = "port.".$port;
# Парсим список адресов на порту
my @ipl = split /\,\s*/, $iplist;
for (@ipl)
{
my $ip = $_;
if ($ip =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)
{
my %plist = ();
if (defined($config{$addr}{ports})) {%plist = %{$config{$addr}{ports}};}
push @{$plist{$port}}, $ip;
$config{$addr}{ports} = \%plist;
$parsed = 1;
}
else
{
print "Bad IP value $_ (addr=$addr, port=$port)\n";
}
}
}
}
# Начало секции (address: ip:telnet_port:portsnum:[uplink1,uplink2,...])
if ($str =~ /^addr\:\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\:\d{1,3}\:\[[\d,\s]{1,20}\])\s*$/)
{
($addr) = $str =~ /^addr\:\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\:\d{1,3}\:\[[\d,\s]{1,20}\])\s*$/;
$begin_switch = 1;
$parsed = 1;
}
print "Unexpected string in config: $str\n" if ($parsed == 0);
}
}
А вот и пример конфига (в общем все должно быть понятно, кроме первой строчки). В первой строчке сначала адрес сервера, потом telnet порт, потом число портов в свиче и в конце список аплинков (на них deny правила не вешаются):
Код:
addr: 10.10.10.10:23:50:[1,5,50]
user: Operator
pass: PaSSwOrD
port 01: 10.12.1.2, 10.12.1.3, 10.12.1.4
port 03: 10.10.10.244, 10.10.10.245, 10.10.10.246
port 03: 10.10.100.12
port 04: 10.10.120.2, 10.10.120.3, 10.10.120.4
----------------------------------
addr: 10.10.10.11:23:50:[50]
user: Operator2
pass: PaSSwOrD2
port 01: 10.13.1.2, 10.13.1.3
port 03: 10.13.102.12
port 04: 10.13.121.2
----------------------------------