Я не сомневаюсь в том, что эту задачу можно решить средствами готовых биллинговых систем - из свободных первым в голову приходит NeTAMS. Но слишком уж все они монсторобразны для такой простой задачи. Попробуем решить ее на коленке с помощью connexion по мотивам следующего примера.
Устанавливаем все необходимое:
# apt-get install python-module-cxnet python-module-MySQLdb MySQL-server # service mysqld startСоздаем базу данных и таблицу, в которой будем хранить билеты, выписываем первый билет:
$ mysql -u root ... mysql> create database cx; Query OK, 1 row affected (0.00 sec) mysql> use cx; Database changed mysql> create table tickets ( -> id serial, -> host varchar(50), -> bytes bigint, -> max_bytes bigint, -> enabled boolean -> ); Query OK, 0 rows affected (0.01 sec) mysql> insert into tickets(host, bytes, max_bytes, enabled) values ('192.168.100.103', 0, 100, true); Query OK, 1 row affected (0.00 sec) mysql> select * from tickets; +----+-----------------+-------+-----------+---------+ | id | host | bytes | max_bytes | enabled | +----+-----------------+-------+-----------+---------+ | 1 | 192.168.100.103 | 0| 100 | 1 | +----+-----------------+-------+-----------+---------+ 1 row in set (0.00 sec)Входящие пакеты для хоста 192.168.100.103 завернем в userspace следующим образом:
# modprobe ip_queue # iptables -A FORWARD -p icmp -d 192.168.100.103 -j QUEUEА обрабатывать эти пакеты и решать, пропустить или нет, мы будем следующей программой, использующей один из компонентов connexion - библиотеку cxnet:
#!/usr/bin/python from cxnet.netlink.ipq import * from cxnet.ip4 import * from cxnet.utils import * import MySQLdb db = MySQLdb.connect(host="localhost", user="root", passwd="", db="cx") cursor = db.cursor() ipqs = ipq_socket() while True: (l,msg) = ipqs.recv() header = iphdr.from_address(addressof(msg.data.payload)) if header.daddr>0: host = int_to_dqn(header.daddr) size = header.tot_len print "packet: %s:%s" % (host,size) cursor.execute("update tickets set bytes = bytes+%s where enabled = true and host = %s", (size, host)) cursor.execute("update tickets set enabled = false where enabled = true and bytes >= max_bytes") cursor.execute("select count(*) from tickets where enabled = true and host = %s", host) for record in cursor.fetchall(): print "count: %s" % record[0] if record[0] > 0: ipqs.verdict(msg.data.packet_id, NF_ACCEPT) continue ipqs.verdict(msg.data.packet_id, NF_DROP)Теперь отправляем первый пинг с хоста 192.168.100.103 наружу:
$ ping -c 1 192.168.1.1 PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data. 64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=1.12 ms --- 192.168.1.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 1.121/1.121/1.121/0.000 msПри этом билет изменится следующим образом:
mysql> select * from tickets; +----+-----------------+-------+-----------+---------+ | id | host | bytes | max_bytes | enabled | +----+-----------------+-------+-----------+---------+ | 1 | 192.168.100.103 | 84 | 100 | 1 | +----+-----------------+-------+-----------+---------+ 1 row in set (0.00 sec)Отправим следующий пинг:
$ ping -c 1 192.168.1.1 PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data. --- 192.168.1.1 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0msПри этом билет будет заблокирован, поскольку число принятых байт превысило лимит:
mysql> select * from tickets; +----+-----------------+-------+-----------+---------+ | id | host | bytes | max_bytes | enabled | +----+-----------------+-------+-----------+---------+ | 1 | 192.168.100.103 | 168 | 100 | 0 | +----+-----------------+-------+-----------+---------+ 1 row in set (0.00 sec)Работает! Правда, тормозить должно жутко, ибо задавать кучу вопросов СУБД (пусть даже и MySQL с дефолтным хранилищем MyISAM, которое и транзакций-то не умеет) на каждый входящий пакет - удовольствие не дешевое. Но о бенчмарках в следующий раз ...
Комментариев нет:
Отправить комментарий