Динамический шейпер CISCO+ACL+traffic_shape

Технические вопросы по UTM 5.0
Ответить
Minime
Сообщения: 17
Зарегистрирован: Чт ноя 26, 2009 08:47

Динамический шейпер CISCO+ACL+traffic_shape

Сообщение Minime »

Приветствую. Поскольку в стандартном функционале netup даже с модулем dyna_shape не реализован функционал по динамическому изменению скорости подключения пользователя в зависимости от текущей нагрузки на канал, предлагаю подключиться к разработке скрипта, предлагающего данный функционал. Надеюсь, энтузиасты найдутся.

На данный момент разработан следующий принцип работы скрипта:
1. Скрипт написанный на php подключается к роутеру cisco, терминирующему pppoe сессии, собирает список online юзеров: username,interface,ip
2. Для каждого юзера с помощью sh interface $userinterface, собирается текущий объем трафика на интерфейсе.
3. Из базы данных netup берётся тарифный план пользователя, из базы данных скрипта берётся максимально допустимый объем трафика за час(можно указать другой период в конфиге) для данного тарифного плана.
3.1. Если текущий трафик пользователя за час больше установленного лимита для тарифа, посылаем на циску команду, добавляющую ip пользователя в определенный ACL, на котором установлен traffic_shape, в базу данных скрипта записывается значение is_shaped='1' для $username.
3.2. Если меньше лимита:
3.2.1 и у пользователя стоит флаг is_shaped=1, удаляем ip пользователя из шейпящего ACL.
3.2.1 если флаг не стоит: do nothing
4. clear interface counters
5. Скрипт перезапускается кроном через 1час.

В дальнейшем можно реализовать функционал, выставляющий лимиты в зависимости от текущей утилизации аплинка, либо по другим параметрам.

На данный момент написан класс, работающий с cisco. Функция, собирающая список юзеров онлайн, функция собирающая текущий трафик пользователя.

Класс cisco (cisco.php):

Код: Выделить всё

<?php

class Cisco 
&#123;

    private $_hostname;
    private $_password;
    private $_username;
    private $_connection;
    private $_data;
    private $_timeout;
    private $_prompt;
	 
	public function getUserTraffic&#40;$user_interface&#41;
	&#123;
		$this->_send&#40;"sh int ".$user_interface." | in underruns"&#41;;
		$this->_readTo&#40;$this->_prompt&#41;;
		$result = array&#40;&#41;;
		$this->_data = explode&#40;"\r\n", $this->_data&#41;;
		array_pop&#40;$this->_data&#41;;
		$temp = $this->_data&#91;1&#93;;
		$temp = sscanf&#40;$temp, "%d %s %s %d %s %d %s"&#41;;
		$bytes = $temp&#91;3&#93;;
		$megabytes = $bytes/1048576;
		//TEST
		$string = $user_interface."&#58;".$megabytes."\n";
		return $string;
		//return $megabytes;
	&#125;
    public function __construct&#40;$hostname, $password, $username = "", $timeout = 10&#41; 
    &#123;
        $this->_hostname = $hostname;
        $this->_password = $password;
        $this->_username = $username;
        $this->_timeout = $timeout;
    &#125; // __construct

    /**
     * Подключаемся к устройству
     */
    public function connect&#40;&#41; 
    &#123;
        $this->_connection = fsockopen&#40;$this->_hostname, 23, $errno, $errstr, $this->_timeout&#41;;
        if &#40;$this->_connection === false&#41; &#123;
            die&#40;"Error&#58; Connection Failed for $this->_hostname\n"&#41;;
        &#125; // if
        stream_set_timeout&#40;$this->_connection, $this->_timeout&#41;;
        $this->_readTo&#40;'&#58;'&#41;;
        if &#40;substr&#40;$this->_data, -9&#41; == 'Username&#58;'&#41; &#123;
            $this->_send&#40;$this->_username&#41;;
            $this->_readTo&#40;'&#58;'&#41;;
        &#125; // if
        $this->_send&#40;$this->_password&#41;;
        $this->_prompt = '>';
        $this->_readTo&#40;$this->_prompt&#41;;
        if &#40;strpos&#40;$this->_data, $this->_prompt&#41; === false&#41; &#123;
            fclose&#40;$this->_connection&#41;;
            die&#40;"Error&#58; Authentication Failed for $this->_hostname\n"&#41;;
        &#125; // if
    &#125; // connect

    /**
     * Закрываем соединение
     */
    public function close&#40;&#41; 
    &#123;
        $this->_send&#40;'quit'&#41;;
        fclose&#40;$this->_connection&#41;;
    &#125; // close

    /**
     * Даём команду в терминал
     */
    private function _send&#40;$command&#41; 
    &#123;
        fputs&#40;$this->_connection, $command . "\r\n"&#41;;
    &#125; // _send

    /**
     * Читаем терминал
     * 
     */
    private function _readTo&#40;$char&#41; 
    &#123;
        // Reset $_data
        $this->_data = "";
        while &#40;&#40;$c = fgetc&#40;$this->_connection&#41;&#41; !== false&#41; &#123;
            $this->_data .= $c;
            if &#40;$c == $char&#91;0&#93;&#41; break;
            if &#40;$c == '-'&#41; &#123;
                // Continue at --More-- prompt
                if &#40;substr&#40;$this->_data, -8&#41; == '--More--'&#41; fputs&#40;$this->_connection, ' '&#41;;
            &#125; // if
        &#125; // while
        // Remove --More-- and backspace
        $this->_data = str_replace&#40;'--More--', "", $this->_data&#41;;
        $this->_data = str_replace&#40;chr&#40;8&#41;, "", $this->_data&#41;;
        // Set $_data as false if previous command failed.
        if &#40;strpos&#40;$this->_data, '% Invalid input detected'&#41; !== false&#41; $this->_data = false;
    &#125; // _readTo

    /**
     * заходим в привелегированный режим Enable
     * 
     *  
     */
    public function enable&#40;$password&#41; 
    &#123;
        $result = false;
        if &#40;$this->_prompt != '#'&#41; &#123;
            $this->_send&#40;'enable'&#41;;
            $this->_readTo&#40;'&#58;'&#41;;
            $this->_send&#40;$password&#41;;
            if &#40;$this->_data !== false&#41; &#123;
                $this->_prompt = '#';
                $result = true;
            &#125; // if
            $this->_readTo&#40;$this->_prompt&#41;;
            return $result;
        &#125; // if
    &#125; // enable

    /**
     * Disable - выходим из Enable
     */
    public function disable&#40;&#41; 
    &#123;
        if &#40;$this->_prompt == '#'&#41; &#123;
            $this->_send&#40;'disable'&#41;;
            $this->_prompt = '>';
            $this->_readTo&#40;$this->_prompt&#41;;
        &#125; // if
    &#125; // disable

    /**
     * Ping
     */
    public function ping&#40;$host&#41; 
    &#123;
        $this->_send&#40;"ping $host"&#41;;
        $this->_readTo&#40;$this->_prompt&#41;;
        $this->_data = explode&#40;"\r\n", $this->_data&#41;;
        for &#40;$i = 0; $i < 3; $i++&#41; array_shift&#40;$this->_data&#41;;
        array_pop&#40;$this->_data&#41;;
        $this->_data = implode&#40;"\n", $this->_data&#41;;        
        return $this->_data;
    &#125; // ping
	
	public function getUsers&#40;&#41;
	&#123;
		$this->_send&#40;'show users'&#41;;
		$this->_readTo&#40;$this->_prompt&#41;;
		$result = array&#40;&#41;;
		$this->_data = explode&#40;"\r\n", $this->_data&#41;;
		array_pop&#40;$this->_data&#41;;
        foreach &#40;$this->_data as $entry&#41; &#123;
		
			if &#40;preg_match&#40;'/Bundle/i',$entry&#41;&#41; &#123;
			$temp = sscanf&#40;$entry, "%s %s %s %s %s %s"&#41;;
			$entry = array&#40;&#41;;
			$entry&#91;'idle'&#93; = $temp&#91;4&#93;;
			$entry&#91;'ip'&#93; = $temp&#91;5&#93;;
			&#125; else &#123;
			$temp = sscanf&#40;$entry, "%s %s %s %s %s"&#41;;
			$entry = array&#40;&#41;;
			$entry&#91;'idle'&#93; = $temp&#91;3&#93;;
			$entry&#91;'ip'&#93; = $temp&#91;4&#93;;
			&#125;//if
			
            $entry&#91;'interface'&#93; = $temp&#91;0&#93;;
            $entry&#91;'username'&#93; = $temp&#91;1&#93;;
			if &#40;strstr&#40;$entry&#91;'ip'&#93;,"10.1."&#41;&#41; &#123;
			$entry&#91;'uplink'&#93; = "USI";
			&#125; else &#123;
			$entry&#91;'uplink'&#93; = "RTK";
			&#125;//if
			if &#40;preg_match&#40;'/u\d\d/i',$entry&#91;'username'&#93;&#41;&#41; &#123;
			$entry&#91;'client_type'&#93; = "HOME";
			&#125; else &#123;
			$entry&#91;'client_type'&#93; = "CORPORATE";
			&#125;//if
			
			if &#40;$entry&#91;'idle'&#93; != 'Idle' && $entry&#91;'ip'&#93; != '' && $entry&#91;'ip'&#93; != 'Idle'&#41; &#123;
            array_push&#40;$result, $entry&#41;;
			&#125; //if
        &#125; // foreach
		$this->_data = $result;
		return $this->_data;
	&#125;
&#125; // Cisco		
//

Исполняемый файл:

Код: Выделить всё

<?php

//ПОДКЛЮЧАЕМЫЕ МОДУЛИ
require 'Cisco.php';
///////////////////////////////////////////////////////////////////
//КОНФИГРУРАЦИЯ
$cisco_hostname = '11.11.11.11';	//ХОСТ
$cisco_username = 'user';	//ЮЗЕР
$cisco_password = 'pass';	//ПАРОЛЬ
$cisco_enpasswd = 'enablepass'; //ПАРОЛЬ НА ENABLE
///////////////////////////////////////////////////////////////////

//КОННЕКТИМСЯ
$cisco = new Cisco&#40;$cisco_hostname,$cisco_password,$cisco_username&#41;;
$cisco->connect&#40;&#41;;
$cisco -> enable&#40;$cisco_enpasswd&#41;;

///////////////////////////////////////////////////////////////////

$users_array = $cisco -> getUsers&#40;&#41;;

//for &#40;$i=0;$i<count&#40;$users_array&#41;;$i++&#41;&#123;
//sleep&#40;2&#41;;
//$test = $cisco -> getUserTraffic&#40;$users_array&#91;$i&#93;&#91;'interface'&#93;&#41;;
//print&#40;$test&#41;;
//&#125;

$cisco -> disable&#40;&#41;;
$cisco -> close&#40;&#41;;
print_r&#40;$users_array&#41;; //DEBUG USERS_ARRAY FUNCTION
?>

Minime
Сообщения: 17
Зарегистрирован: Чт ноя 26, 2009 08:47

Сообщение Minime »

Код: Выделить всё

if &#40;strstr&#40;$entry&#91;'ip'&#93;,"10.1."&#41;&#41; &#123;
         $entry&#91;'uplink'&#93; = "USI";
         &#125; else &#123;
         $entry&#91;'uplink'&#93; = "RTK";
         &#125;//if
         if &#40;preg_match&#40;'/u\d\d/i',$entry&#91;'username'&#93;&#41;&#41; &#123;
         $entry&#91;'client_type'&#93; = "HOME";
         &#125; else &#123;
         $entry&#91;'client_type'&#93; = "CORPORATE";
         &#125;//if 
$entry['uplink'] : Это исключительно специфика моей сетки, у меня 2 аплинка, по этому дополнительно определяю на каком из них сидит юзер.

$entry['client_type'] : Тоже замутка касающая только моей сети: логины домашних пользователей у меня имеют вид u123456, корпоративные - просто название организации.

Ответить