Скрипт на питоне для мониторинга трафика на шлюзе и отдельном хосте

0. Возможности скрипта
— мониторинг трафика по подсети, списку ip адресов, отдельному ip
— отображение суммарной нагрузки на канал. Общая и раздельная по каждому ip
— суммарный объем трафика за все время измерений. Общий и раздельный по каждому ip
— сортировка вывода по нагрузке и объему трафика
— переключение между входящим и исходящим трафиком
— веселые, белые полоски, для удобочитаемости

1. Системные требования
— установленный в системе интерпретатор языка питон. Данный скрипт написан под версию 2.7
— для нормального функционирования должны быть установлены следующие библиотеки для питона:
     — pcap
     — curses
     — dpkt

2. Настройки скрипта
Настройки скрипта осуществляются путем изменения переменных в тексте самого скрипта.

chanel_width — ширина канала. Необходимо для правильного отображения нагрузки.
Считается по следующей формуле (ширина канала в mbits)*1024*1024/50
chanel_width = 209715
ip_adress — здесь указывается подсеть, список ip адресов, разделенных запятой или одиночный ip.
ip_adress = '192.168.1.'
iface_name — название сетевого интерфейса, на котором осуществляется мониторинг.
iface_name='xl0'
net_type — тип мониторинга. net — мониторинг подсети, list — список ip, host — одиночный хост, не являющийся шлюзом.
net_type = 'net'

3.Управление
Управление осуществляется через функциональные клавиши и они таковы:
    -[d] переключение между входящим и исходящим трафиком
    -[s] включить/выключить веселые полоски
    -[m] сортировать по загруженности канала
    -[t] сортировать по суммарному объему трафика
    -[u] отключить сортировку
    -[q] выход
 
4.Внешний вид
Без веселых полосок

 
С веселыми полосками

 
5. Код скрипта

#!/usr/local/bin/python2.7

from dpkt.ethernet import *
from dpkt.ip import *
from dpkt.tcp import *
import pcap
import socket
import sys
import curses
import time
import datetime
import traceback

#------- init curses -------
stdscr = curses.initscr()
stdscr.nodelay(1)
curses.noecho()
window_size = stdscr.getmaxyx()

#------- var -------
sorted_out = {}
ip_list_current_in = {}
ip_list_amount_in = {}
ip_list_current_out = {}
ip_list_amount_out = {}
ip_list_current = {}
ip_list_amount = {}

ts = ''
eth_hdr = ''

sort_direct = True
sort_sym = ['','']
mbit = 0
tmb = 0
uns = 1
direct_traff = 'incoming'
strips_status = 'on'
strips_color = 0

total_amount_all = 0
total_fill_mb = 0
total_fill_b = 0

start_time = datetime.datetime.now()
current_unixtime = time.time()
chanel_width = 209715 #10Mbit: 209715 100Mbit: 2097152 exam. 10mbit [10*1024*1024 / 50]

#------- network, list of ip', single host
ip_adress = '192.168.1.'
#ip_adress = '192.168.1.111, 192.168.1.112, 192.168.1.113, 192.168.1.140, 192.168.1.142'

#------- interface name for listen
iface_name = ''

#------- net — for network, list — list ip's comma separated, host — for single host
net_type = 'net'

try:
    for pkt in pcap.pcap(iface_name):

        press_key = stdscr.getch()
        if press_key == ord('q'):
            curses.endwin()
            sys.exit()
        elif press_key == ord('m'):
            if mbit == 1:
                if sort_direct == True:
                    sort_sym = [' +','']
                    sort_direct = False
                elif sort_direct == False:
                    sort_sym = [' -','']
                    sort_direct = True
            elif mbit == 0:
                sort_sym = [' -','']
                sort_direct = True
            mbit = 1
            tmb = 0
            uns = 0
        elif press_key == ord('t'):
            if tmb == 1:
                if sort_direct == True:
                    sort_sym = ['',' +']
                    sort_direct = False
                elif sort_direct == False:
                    sort_sym = ['',' -']
                    sort_direct = True
            elif tmb == 0:
                sort_sym = ['',' -']
                sort_direct = True
            tmb = 1
            mbit = 0
            uns = 0
        elif press_key == ord('u'):
            sort_sym = ['','']
            uns = 1
            tmb = 0
            mbit = 0
        elif press_key == ord('d'):
            stdscr.clear()
            if direct_traff == 'incoming':
                direct_traff = 'outgoing'
            else:
                direct_traff = 'incoming'
        elif press_key == ord('s'):
            if strips_status == 'off':
                strips_status = 'on'
                strips_color = 0
            else:
                strips_status = 'off'
                strips_color = 262144  #curses.A_REVERSE
       
        if str(pkt) != 'None':
            ts = pkt[0]
            eth_hdr = Ethernet(pkt[1])
       
            if eth_hdr.type == ETH_TYPE_IP:
                ip_hdr = eth_hdr.data

                if ip_hdr.p == IP_PROTO_TCP or ip_hdr.p == IP_PROTO_UDP:
                    tcp_hdr = ip_hdr.data
                    data_len = len(eth_hdr.data) * 8
                   
                    ip_dst = socket.inet_ntoa(ip_hdr.dst)
                    ip_src = socket.inet_ntoa(ip_hdr.src)
                   
                    if net_type == 'list':
                        ip_dst_find = ip_adress.find(ip_dst)
                        ip_src_find = ip_adress.find(ip_src)
                    else:
                        ip_dst_find = ip_dst.find(ip_adress)
                        ip_src_find = ip_src.find(ip_adress)
                   
                    if ip_dst_find >= 0 and ip_src_find == -1 :
                   
                        if net_type == 'host':
                            ip_dst = socket.inet_ntoa(ip_hdr.src)
                            ip_src = socket.inet_ntoa(ip_hdr.dst)
                       
                        ip_find = ip_list_current_in.has_key(ip_dst)
                        if ip_find == False:
                            dict_tmp = {ip_dst:data_len}
                            ip_list_current_in.update(dict_tmp)
                            ip_list_amount_in.update(dict_tmp)
                        elif ip_find == True:
                            count_data = ip_list_current_in.get(ip_dst) + data_len
                            dict_tmp = {ip_dst:count_data}
                            ip_list_current_in.update(dict_tmp)
                            count_data = ip_list_amount_in.get(ip_dst) + data_len
                            dict_tmp = {ip_dst:count_data}
                            ip_list_amount_in.update(dict_tmp)
                            count_data = 0

                    if ip_dst_find == -1 and ip_src_find >= 0 :
                   
                        if net_type == 'host':
                            ip_dst = socket.inet_ntoa(ip_hdr.src)
                            ip_src = socket.inet_ntoa(ip_hdr.dst)
                           
                        ip_find = ip_list_current_out.has_key(ip_src)
                        if ip_find == False:
                            dict_tmp = {ip_src:data_len}
                            ip_list_current_out.update(dict_tmp)
                            ip_list_amount_out.update(dict_tmp)
                        elif ip_find == True:
                            count_data = ip_list_current_out.get(ip_src) + data_len
                            dict_tmp = {ip_src:count_data}
                            ip_list_current_out.update(dict_tmp)
                            count_data = ip_list_amount_out.get(ip_src) + data_len
                            dict_tmp = {ip_src:count_data}
                            ip_list_amount_out.update(dict_tmp)
                            count_data = 0

            if direct_traff == 'incoming':
                ip_list_current = ip_list_current_in
                ip_list_amount = ip_list_amount_in
            elif direct_traff == 'outgoing':
                ip_list_current = ip_list_current_out
                ip_list_amount = ip_list_amount_out
       
            if (ts — current_unixtime) > 1:
                stdscr.addstr(0, 0, ' ip'.ljust(16, ' ') + '   filling channel'.ljust(54, ' ') + '  mbit/s' + '     total Mb')
                stdscr.addstr(1, 0, ''.ljust(93,'-'))
                i = 0

                if mbit == 1:
                    sorted_out = sorted(ip_list_current.iteritems(), key=lambda (k,v): (v,k), reverse = sort_direct)

                if tmb == 1:
                    sorted_out = sorted(ip_list_amount.iteritems(), key=lambda (k,v): (v,k), reverse = sort_direct)

                if uns == 1:
                    sorted_out = ip_list_current.iteritems()

                for ip, channel_fill in sorted_out:
                    chanel_fill_mb = round(float(ip_list_current.get(ip)) / 1000 /1000, 4)
                    total_amount_mb = round(float(ip_list_amount.get(ip)) / 1024 /1024 /8, 2)
                    total_amount_all = total_amount_all + total_amount_mb

                    star_count = int(ip_list_current.get(ip)/chanel_width)
                    if star_count > 50:
                        star_count = 50
                    star_fill = ''.ljust(star_count, '>')

                    stdscr.addstr(i + 2, 0, ''.ljust(94, ' '))
           
#------- strips -------
                    if i < (window_size[0] — 15):
                        if i % 2 == 0:
                            stdscr.addstr(i + 2, 0, ' ' + str(ip).ljust(16, ' ') +  ' [' + star_fill.ljust(50, ' ') + ']' +  ' [' + str(chanel_fill_mb).ljust(7, ' ') + ']  [' + str(total_amount_mb).ljust(8, ' ') + '] ', strips_color)
                        elif i % 2 > 0:
                            stdscr.addstr(i + 2, 0, ' ' + str(ip).ljust(16, ' ') +  ' [' + star_fill.ljust(50, ' ') + ']' + ' [' + str(chanel_fill_mb).ljust(7, ' ') + ']  [' + str(total_amount_mb).ljust(8, ' ') + '] ', curses.A_NORMAL)
                        i = i + 1   

                    total_fill_mb = total_fill_mb + chanel_fill_mb
                    total_fill_b = total_fill_b + ip_list_current.get(ip)

                    dict_tmp = {ip:0}
                    ip_list_current.update(dict_tmp)
               
                star_count = int(total_fill_b/chanel_width)
                if star_count > 50:
                    star_count = 50
                star_fill = ''.ljust(star_count, '>')

                stdscr.addstr(i + 2, 0, ''.ljust(93,'-'))
                stdscr.addstr(i + 3, 0, ' total'.ljust(16, ' ') + '  [' + star_fill.ljust(50, ' ') + ']' + ' [' + str(total_fill_mb).ljust(7, ' ') + ']  [' + str(total_amount_all).ljust(8, ' ') + '] ')

                stdscr.addstr(i + 4, 0, ''.ljust(93,' '))
                stdscr.addstr(i + 5, 0, ''.ljust(93,'-'))
                stdscr.addstr(i + 6, 0, (' start [' + start_time.strftime("%d %B %Y %H:%M:%S") +  ']  elapsed [' + str(datetime.datetime.now() — start_time) + ']').ljust(72, ' ') + 'direction [' + direct_traff + '] ')

                stdscr.addstr(i + 7, 0, ''.ljust(93,'-'))
                stdscr.addstr(i + 8, 0, (' [d] direction  [s] strips').ljust(93, ' '))
                stdscr.addstr(i + 9, 0, (' [m' + sort_sym[0] + '] sort by mbit/s  [t' + sort_sym[1] + '] sort by total Mb  [u] unsorted').ljust(76, ' ') + '[q] quit'.rjust(16, ' '))

#           stdscr.addstr(i+10, 0, ' eth: ' + str(len(eth_hdr.data)) + ' header: ' + str(ip_hdr.len) + '  port: ' + str(tcp_hdr.dport).ljust(93, ' '))
                total_fill_mb = 0
                total_fill_b = 0
                total_amount_all = 0
                current_unixtime = time.time()
except:
     curses.endwin()
     traceback.print_exc()
 
6. Тож самое на pastebin
pastebin.com/X4Tyz0UW

2 комментария

avatar
Теги поправь.
avatar
С тэгами все нормально. В принципе могу добавит картинку с недостающим, чтоб соотвествовало.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.