Pull to refresh

Отмечаем на карте IP соединения

Reading time4 min
Views3K
Добрый день.

Так получилось, что у себя дома в качестве шлюза использую обычный компьютер на Атоме под управлением Debian. Из-за этого он исполняет ряд дополнительных функций: файлопомойка, качалка торрентов, личный репозиторий и т.д. Но не это важно. Наличие торрент клиента подразумевает относительно большое количество соединений, и возникла идея: «а не визуализировать ли все это?».
Довольно быстро было найдено решение: xplanet (позволяет рисовать изображение земли с маркерами на ней) + geoip (IP -> координаты).

Настройка


geoip

Проект хостится на github.com/appliedsec/pygeoip. С установкой проблемы не возникает, единственное, требуется отдельно скачать базу: www.maxmind.com/download/geoip/database/GeoLiteCity.dat.xz (о чем можно узнать из вики проекта) и отдельно доставить python-six. У меня на шлюзе стоит тестируемая ветка дистрибутива, и этот пакет есть в репозитории (в стабильной ветке его нет, придется качать руками). Другие дистрибутивы с большой вероятностью так же имеют его в репозиториях.
Данное API позволяет получать различную информацию, например: название города, страну, индекс и т.д., но нас интересуют только координаты.

xplanet

Был в установлен из репозитория. Первое, что требуется сделать после установки — это подправить конфиг (у меня он располагался в: /etc/xplanet/config/default), а именно указать имя файла с маркерами, которые мы будет указывать на карте, как параметр при запуске указать расположения этого файла не представляется возможным. Для этого в секции [Earth] укажем:
marker_file=[тут должен быть путь к файлу маркеров]


Собираем все вместе


Теперь можно перейти к собиранию всего в единое целое. Список всех соединений можно получить командой:
netstat -ntp

Небольшие пояснения: -n — выводит IP адрес, а не имя, t — tcp соединения, p — выводит имя и PID процесса(для того чтобы увидеть имена всех процессов, а не только своих, придется использовать sudo). Далее следует отфильтровать все лишнее (заголовки и ipv6):
sudo netstat -ntp | grep tcp | grep -v ::

Ниже представлен небольшой скрипт, который будет делать часть работы.
#!/bin/sh

# создадим временный файл, в нем будет выхлоп netstat'a
STAT_FILE=`mktemp`
# укажем файл с маркерами
MARKER_FILE="ipm"
sudo netstat -nutp | grep tcp | grep -v :: > $STAT_FILE
# передадим выхлоп скрипту на питоне и получим файл с маркерами
./net_draw.py $STAT_FILE > $MARKER_FILE
# непосредственно нарисуем карту.
# -output p.jpg            - имя получаемого рисунка
# -geometry 1920X1080      - размер
# -projection rectangular  - тип проекции, мне больше нравится такая.
# -longitude 15            - устанавливает центр рисунка на эту координату (Россия теперь не будет "обрезана")
# -num_times 1             - выполнится только 1 раз
# -quality 100             - уровень сжатия
xplanet -output p.jpg -geometry 1920X1080 -projection rectangular -longitude 15 -quality 100 -num_times 1
# не забудем удалить временный файл
rm $STAT_FILE


Теперь очередь скрипта на питоне, который будет делать оставшуюся работу.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pygeoip
import sys

input_file = sys.argv[1]
# путь к базе
gi = pygeoip.GeoIP("GeoLiteCity.dat")
marker_map = {}

# Разным процессам - разные цвета
# идея: выдавать цвета из списка 
class Col:
  def __init__(self):
    # номер цвета, который будет присвоен следующим
    self.c_id = 0
    # соответствие процесса и цвета
    self.color_map = {}
    # набор доступных цветов
    self.colors = ["White", "Red", "Green", "Yellow", "Purple", "Brown", "Blue", "Pink", "Gray", "Orange"]
    
  def get_color(self,id):
    if id in self.color_map:
      return self.color_map[id]
    self.color_map[id] = self.colors[self.c_id]
    self.c_id = self.c_id + 1
    # на случай, если цвета кончатся
    if self.c_id == len(self.colors):
      self.c_id = 0
    return self.color_map[id]

c_c = Col()
i_f = open(input_file, 'r')
for l in i_f:
  # разделим всю строку на части
  fields = l.split()
  # отделим порт от IP адреса
  pos = fields[4].find(':')
  ip = fields[4][:pos]
  gi_pos = gi.record_by_addr(ip)
  # имя процесса
  name = fields[6]
  # бывают соединения с локальными адресами, при их обработке возвращается None
  if gi_pos != None:
    color = c_c.get_color(name)
    marker_id = "%5.2f %5.2f \"\" color=%s" % (gi_pos['latitude'], gi_pos['longitude'],color)
    # чем больше соединений с одинаковыми координатами и именами процессов, тем больше размер маркера, по умолчанию 2
    if marker_id in marker_map:
      marker_map[marker_id] = marker_map[marker_id] + 1
    else:
      marker_map[marker_id] = 2

# вывод наружу
for mid in marker_map:
  print (mid + " symbolsize=%d" % marker_map[mid])

# тут рисуем небольшую легенду, x, y - начальные координаты
x = 0
y = 50
for key in c_c.color_map:
  print ("%5.2f %5.2f \"%s\" color=%s" % (x, y, key, c_c.color_map[key]))
  x  = x - 2.5
  y  = y + 0


Теперь можно насладиться результатом или посмотреть на пример того, что получается. Соединения без PID'ов — всевозможные TIME_WAIT и прочие.

Данная реализация не лишена недостатков. Например, цвета повторяются по кругу и при заходе на второй или даже третий круг, размеры маркеров могут не отображаться правильно, т.к. цвет является частью ключа. Что бы этого не случалось необходимо генерировать уникальные цвета, но при малом количестве процессов этот подход имеет право на существование.
Если требуется непрерывное рисование карты, то имеется возможность запускать xplanet без ограничения на количество запусков и без привязки к терминалу. Тогда достаточно только с определенной периодичностью обновлять файл с маркерами (например cron).

Тем не менее, данный пример может служить не плохой базой для более сложной/простой реализации.
Tags:
Hubs:
Total votes 23: ↑21 and ↓2+19
Comments10

Articles