Compare commits
3 Commits
main
..
551e8db291
| Author | SHA1 | Date | |
|---|---|---|---|
| 551e8db291 | |||
| 9b727a93b2 | |||
| 7b0ee88293 |
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
__pycache__/
|
**/__pycache__/
|
||||||
unloading/
|
/unloading/
|
||||||
@@ -1,136 +1,3 @@
|
|||||||
# bird_list_ip
|
# bird_list_ip
|
||||||
|
|
||||||
Это скрипт написанный на Python3 для выгрузки пулов ip адресов, для bird2.
|
Это скрипт написанный на Python3 для выгрузки пулов ip адресов по номерам AS.
|
||||||
|
|
||||||
# Установка (Debian)
|
|
||||||
|
|
||||||
-- устанавливаем пакеты
|
|
||||||
> sudo apt install -y git bird2 m4
|
|
||||||
|
|
||||||
-- добавляем поддержку brotli и requests в python3
|
|
||||||
> apt -y install python3-pip\
|
|
||||||
> pip install brotli requests 2> /dev/null || pip install brotli requests --break-system-packages
|
|
||||||
|
|
||||||
-- клонируем репозиторий
|
|
||||||
> git clone https://git.alanbox.ru/alan/bird_list_ip.git /opt/bird_list_ip && chmod +x /opt/bird_list_ip/download.py
|
|
||||||
|
|
||||||
-- правим Unit Systemd bird2 для работы с m4
|
|
||||||
> nano /usr/lib/systemd/system/bird.service
|
|
||||||
|
|
||||||
> [Unit]\
|
|
||||||
> Description=BIRD Internet Routing Daemon\
|
|
||||||
> After=network.target\
|
|
||||||
>\
|
|
||||||
> [Service]\
|
|
||||||
> EnvironmentFile=/etc/bird/envvars\
|
|
||||||
> ExecStartPre=/bin/sh -c "/usr/bin/m4 /opt/bird > /etc/bird/bird.conf"\
|
|
||||||
> ExecStartPre=-/usr/lib/bird/prepare-environment\
|
|
||||||
> ExecStartPre=-/usr/sbin/bird -p\
|
|
||||||
> ExecReload=/bin/sh -c "/usr/bin/m4 /opt/bird > /etc/bird/bird.conf"\
|
|
||||||
> ExecReload=-/usr/sbin/birdc configure\
|
|
||||||
> ExecStart=/usr/sbin/bird -f -u $BIRD_RUN_USER -g $BIRD_RUN_GROUP $BIRD_ARGS\
|
|
||||||
> Restart=on-abort\
|
|
||||||
>\
|
|
||||||
> [Install]\
|
|
||||||
> WantedBy=multi-user.target\
|
|
||||||
|
|
||||||
-- пример конфигурации bird2 на m4, для работы со списками
|
|
||||||
> nano /opt/bird
|
|
||||||
|
|
||||||
> log syslog {error, fatal};\
|
|
||||||
> router id ~~**1.1.1.1**~~;\
|
|
||||||
>\
|
|
||||||
> protocol device {\
|
|
||||||
> }\
|
|
||||||
>\
|
|
||||||
> protocol direct {\
|
|
||||||
> ipv4; # Connect to default IPv4 table\
|
|
||||||
> #ipv6; # ... and to default IPv6 table\
|
|
||||||
> }\
|
|
||||||
>\
|
|
||||||
> protocol kernel {\
|
|
||||||
> learn;\
|
|
||||||
> merge paths on;\
|
|
||||||
> ipv4 { # Connect protocol to IPv4 table by channel\
|
|
||||||
> import none; # Import to table, default is import all\
|
|
||||||
> export filter { if (net.len > 0 && source=RTS_BGP) then { accept; } reject; }; # Export to protocol. default is export none\
|
|
||||||
> };\
|
|
||||||
> }\
|
|
||||||
>\
|
|
||||||
> \# SUB m4\
|
|
||||||
> define(\`LOCATION', \`~~**741**~~')\
|
|
||||||
> include(\`/opt/bird_list_ip/unloading/bird2_v4.m4')\
|
|
||||||
>\
|
|
||||||
> filter border_in {\
|
|
||||||
> if (net ~ 0.0.0.0/32) then { reject; }\
|
|
||||||
> if (net ~ 127.0.0.0/8) then { reject; }\
|
|
||||||
> if (net ~ 169.254.0.0/16) then { reject; }\
|
|
||||||
> if (net ~ 224.0.0.0/4) then { reject; }\
|
|
||||||
> if (net ~ 240.0.0.0/4) then { reject; }\
|
|
||||||
> if (net.len > 0) then { accept; }\
|
|
||||||
> reject;\
|
|
||||||
> }\
|
|
||||||
>\
|
|
||||||
> filter border_out {\
|
|
||||||
> if (net ~ 0.0.0.0/32) then { reject; }\
|
|
||||||
> if (net ~ 127.0.0.0/8) then { reject; }\
|
|
||||||
> if (net ~ 10.0.0.0/8) then { reject; }\
|
|
||||||
> if (net ~ 172.16.0.0/12) then { reject; }\
|
|
||||||
> if (net ~ 192.168.0.0/16) then { reject; }\
|
|
||||||
> if (net ~ 169.254.0.0/16) then { reject; }\
|
|
||||||
> if (net ~ 224.0.0.0/4) then { reject; }\
|
|
||||||
> if (net ~ 240.0.0.0/4) then { reject; }\
|
|
||||||
> accept;\
|
|
||||||
> }\
|
|
||||||
>\
|
|
||||||
> define(\`BGP_BORDER', \`\
|
|
||||||
> protocol bgp $1 {\
|
|
||||||
> ipv4 {\
|
|
||||||
> import filter border_in;\
|
|
||||||
> export filter border_out;\
|
|
||||||
> next hop self;\
|
|
||||||
> };\
|
|
||||||
> router id $2;\
|
|
||||||
> source address $2;\
|
|
||||||
> local $2 as ~~**65431**~~;\
|
|
||||||
> neighbor $3 as ~~**65949**~~;\
|
|
||||||
> hold time 90;\
|
|
||||||
> keepalive time 60;\
|
|
||||||
> passive off;\
|
|
||||||
> multihop;\
|
|
||||||
> bfd no;\
|
|
||||||
> }\
|
|
||||||
> ')\
|
|
||||||
>\
|
|
||||||
> BGP_BORDER(\`CLIENT1', \`~~**10.8.1.1**~~', \`~~**10.8.1.2**~~')\
|
|
||||||
> BGP_BORDER(\`CLIENT1', \`~~**10.8.2.1**~~', \`~~**10.8.2.2**~~')\
|
|
||||||
> ...\
|
|
||||||
> BGP_BORDER(\`CLIENT99', \`~~**10.8.99.1**~~', \`~~**10.8.99.2**~~')
|
|
||||||
|
|
||||||
~~**Тык**~~ - это требует вашего внимания, для вашей конфигурации bird2
|
|
||||||
|
|
||||||
-- добавляем задачу в cron, обновление раз в неделю в среду в 5 утра
|
|
||||||
> crontab -e
|
|
||||||
|
|
||||||
> 0 5 * * 3 /usr/bin/python3 /opt/bird_list_ip/download.py > /dev/null 2>&1 # обновление списков ip адресов
|
|
||||||
|
|
||||||
-- моя рекомендация cron, если у вас идет выгрузка по спискам RKN
|
|
||||||
> crontab -e
|
|
||||||
|
|
||||||
> 0 5 * * * /usr/bin/python3 /opt/bird_list_ip/download.py RKN > /dev/null 2>&1 # обновление списков ip адресов RKN\
|
|
||||||
> 5 5 10 * * /usr/bin/python3 /opt/bird_list_ip/download.py -RKN > /dev/null 2>&1 # обновление списков ip адресов
|
|
||||||
|
|
||||||
-- запуск загрузки в ручную
|
|
||||||
> python3 /opt/bird_list_ip/download.py # выгружаем все списки\
|
|
||||||
> python3 /opt/bird_list_ip/download.py RU # выгружаем конкретный список\
|
|
||||||
> python3 /opt/bird_list_ip/download.py RU JAPAN KOREA # выгружаем перечисленные списки\
|
|
||||||
> python3 /opt/bird_list_ip/download.py -RU # выгружаем все списки, кроме RU\
|
|
||||||
> python3 /opt/bird_list_ip/download.py -RU JAPAN KOREA # выгрузит списки JAPAN KOREA, -RU будет проигнорирован\
|
|
||||||
> python3 /opt/bird_list_ip/download.py -RU jaPAn korea # допускается указывать ключ в любом регистре
|
|
||||||
|
|
||||||
-- добавляем bird2 в автозагрузку и запускаем
|
|
||||||
> systemctl daemon-reload && systemctl enable bird.service && systemctl start bird.service
|
|
||||||
|
|
||||||
**#####################################################################**\
|
|
||||||
Пример конфигурация списка выгрузки можно посмотреть в файле list в репозитории,\
|
|
||||||
так же допускается указывать url ссылку на конфигурацию первой строкой в файле list
|
|
||||||
-398
@@ -1,398 +0,0 @@
|
|||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import ast
|
|
||||||
import requests
|
|
||||||
import ipaddress
|
|
||||||
from include import net_tree
|
|
||||||
from collections import defaultdict
|
|
||||||
from time import sleep as time_sleep
|
|
||||||
from shutil import get_terminal_size
|
|
||||||
from include.http_header import get_headers
|
|
||||||
|
|
||||||
# компилируем регулярку поиска ipv4 адреса
|
|
||||||
ipv4_find_str=re.compile(r"(?<![0-9.])(?!10\.|172\.(?:1[6-9]|2[0-9]|3[01])\.|192\.168\.)((?:25[0-5]|2[0-4][0-9]|1?[0-9][0-9]|[1-9])\.(?:25[0-5]|2[0-4][0-9]|1?[0-9][0-9]|[0-9])\.(?:25[0-5]|2[0-4][0-9]|1?[0-9][0-9]|[0-9])\.(?:25[0-5]|2[0-4][0-9]|1?[0-9][0-9]|[0-9]))(?:/(3[0-2]|[12][0-9]|[1-9]))?(?![0-9.])")
|
|
||||||
# компилируем регулярку поиска ipv6 адреса
|
|
||||||
ipv6_find_str=re.compile(r'(?<![0-9A-Fa-f:])([23][0-9A-Fa-f]{3}(?:(?::[0-9A-Fa-f]{1,4}){0,6}|(?:::[0-9A-Fa-f]{0,4})?)(?::[0-9A-Fa-f]{0,4})*)(?:/([1-9][0-9]?|1[01][0-9]|12[0-8]))?(?![0-9A-Fa-f:])')
|
|
||||||
|
|
||||||
# метод вывода прогресса на экран
|
|
||||||
def progres_print(lstr:list, mtype:int=0, ss:float=0.3):
|
|
||||||
"""
|
|
||||||
Метод вывода на экран прогресса выполнения
|
|
||||||
"""
|
|
||||||
term_width = get_terminal_size().columns
|
|
||||||
mtype: str = '33' if mtype == 1 else '31' if mtype == 2 else '32'
|
|
||||||
cstr: str = f"\033[{mtype}m | \033[0m".join(lstr)[:term_width]
|
|
||||||
print(f"\r{' ' * term_width}\r\033[{mtype}m>>>\033[0m {cstr}", end='', flush=True)
|
|
||||||
time_sleep(ss)
|
|
||||||
|
|
||||||
# метод сбора set адресов ipv4 из текста
|
|
||||||
def ipv4_find(strip:str, size:int):
|
|
||||||
"""
|
|
||||||
Метод сбора set адресов ipv4 из текста
|
|
||||||
возвращает set ip
|
|
||||||
где:
|
|
||||||
значение - [адрес в int формате, размер сети]
|
|
||||||
"""
|
|
||||||
ips=set()
|
|
||||||
for c in ipv4_find_str.finditer(strip):
|
|
||||||
ip_str = c.group(1)
|
|
||||||
prefix_str = c.group(2)
|
|
||||||
# определяем префикс
|
|
||||||
if (prefix:=int(prefix_str) if prefix_str else 32) > size: continue
|
|
||||||
# проверка корректности IPv4
|
|
||||||
try:
|
|
||||||
ips.add((int(ipaddress.IPv4Address(ip_str)), prefix))
|
|
||||||
except (ValueError, ipaddress.AddressValueError):
|
|
||||||
continue
|
|
||||||
return ips
|
|
||||||
|
|
||||||
# метод сбора set адресов ipv6 из текста
|
|
||||||
def ipv6_find(strip:str, size:int):
|
|
||||||
"""
|
|
||||||
Метод сбора set адресов ipv6 из текста
|
|
||||||
возвращает set ip
|
|
||||||
где:
|
|
||||||
значение - [адрес в int формате, размер сети]
|
|
||||||
"""
|
|
||||||
ips=set()
|
|
||||||
for c in ipv6_find_str.finditer(strip):
|
|
||||||
ip_str = c.group(1)
|
|
||||||
prefix_str = c.group(2)
|
|
||||||
# определяем префикс
|
|
||||||
if (prefix:=int(prefix_str) if prefix_str else 128) > size: continue
|
|
||||||
# проверка корректности IPv6
|
|
||||||
try:
|
|
||||||
ips.add((int(ipaddress.IPv6Address(ip_str)), prefix))
|
|
||||||
except (ValueError, ipaddress.AddressValueError):
|
|
||||||
continue
|
|
||||||
return ips
|
|
||||||
|
|
||||||
# метод группировки словаря
|
|
||||||
def get_dict_groups(input:dict):
|
|
||||||
"""
|
|
||||||
Метод получения сгрупированных данных
|
|
||||||
community и ip адресов
|
|
||||||
возвращает словарь сгрупированных данных
|
|
||||||
данные встречающиеся один раз, не попадают в вывод
|
|
||||||
"""
|
|
||||||
# если словарь короче 2х
|
|
||||||
if len(input) < 2: return {}
|
|
||||||
|
|
||||||
# строим битовые маски
|
|
||||||
name_to_bit = {name: 1 << i for i, name in enumerate(input.keys())}
|
|
||||||
line_communities = defaultdict(set)
|
|
||||||
line_mask = defaultdict(int)
|
|
||||||
|
|
||||||
# походим по строкам и назначаем маску
|
|
||||||
for name, (communities, lines) in input.items():
|
|
||||||
bit = name_to_bit[name]
|
|
||||||
for line in lines:
|
|
||||||
line_mask[line] |= bit
|
|
||||||
# добавляем комьюнити этого списка к конкретной строке
|
|
||||||
line_communities[line].update(communities)
|
|
||||||
|
|
||||||
# группируем строки по маске
|
|
||||||
groups = defaultdict(list)
|
|
||||||
groups_communities = defaultdict(set)
|
|
||||||
for line, mask in line_mask.items():
|
|
||||||
groups[mask].append(line)
|
|
||||||
groups_communities[mask].update(line_communities[line])
|
|
||||||
|
|
||||||
# конвертируем маску в имена
|
|
||||||
bit_to_names = {}
|
|
||||||
for mask in groups.keys():
|
|
||||||
names = [name for name, bit in name_to_bit.items() if mask & bit]
|
|
||||||
list_name = "__".join(names)
|
|
||||||
bit_to_names[mask] = list_name
|
|
||||||
|
|
||||||
# возвращаем словарь сгрупированных данных
|
|
||||||
return { bit_to_names[mask]: [ groups_communities[mask], set(groups[mask]) ] for mask in groups if "__" in bit_to_names[mask] and groups_communities[mask] and groups[mask] }
|
|
||||||
|
|
||||||
# метод получения списка ip адресов
|
|
||||||
def list_ip(c_list: list = []):
|
|
||||||
"""
|
|
||||||
Метод получения списка ip адресов
|
|
||||||
возвращает кортеж из 2-х списков: ipv4 и ipv6
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
ipv4_list=set()
|
|
||||||
ipv6_list=set()
|
|
||||||
# определяем, будем сжимать или нет
|
|
||||||
compress=c_list[0].get('compress', True)
|
|
||||||
# какие типы обрабытываем, от какого размера
|
|
||||||
# по умолчанию:
|
|
||||||
# ipv4 обрабатываем (<=24)
|
|
||||||
# ipv6 игнорируем (<=64)
|
|
||||||
# какие типы обрабытываем, от какого размера
|
|
||||||
ipv4 = False if not (ipv4:=c_list[0].get('ipv4', True)) else (ipv4 if type(ipv4) is int else 24)
|
|
||||||
ipv6 = False if not (ipv6:=c_list[0].get('ipv6', False)) else (ipv6 if type(ipv6) is int else 64)
|
|
||||||
|
|
||||||
c_list_len=len(c_list)
|
|
||||||
# пробегаем словарь выгрузки
|
|
||||||
for i, c_dict in enumerate(c_list, start=1):
|
|
||||||
# прогрес %
|
|
||||||
percent = int(i / c_list_len * 100)
|
|
||||||
# если есть источник ссылка
|
|
||||||
if 'url' in list(c_dict):
|
|
||||||
# бежим весь список ссылок пока не код 200
|
|
||||||
for c_url in c_dict['url']:
|
|
||||||
progres_print([c_url, f"{percent}%"])
|
|
||||||
try:
|
|
||||||
session = requests.Session()
|
|
||||||
session.headers.update(get_headers())
|
|
||||||
if (result:=session.get(c_url, timeout=(5, 60), stream=True)) and result.status_code == 200 and result.text:
|
|
||||||
# пополняем словарь ipv4_list
|
|
||||||
if ipv4: ipv4_list.update(ipv4_find(result.text,ipv4))
|
|
||||||
# пополняем словарь ipv6_list
|
|
||||||
if ipv6: ipv6_list.update(ipv6_find(result.text,ipv6))
|
|
||||||
break
|
|
||||||
except requests.exceptions.RequestException: pass
|
|
||||||
progres_print([c_url, f"{percent}%"],1,1)
|
|
||||||
|
|
||||||
# если есть статичные записи ipv4
|
|
||||||
if ipv4 and 'static4' in list(c_dict):
|
|
||||||
progres_print(["StaticIPv4", f"{percent}%"])
|
|
||||||
# пополняем словарь ipv4_list
|
|
||||||
ipv4_list.update(ipv4_find(str(c_dict['static4']),ipv4))
|
|
||||||
|
|
||||||
# если есть статичные записи ipv6
|
|
||||||
if ipv6 and 'static6' in list(c_dict):
|
|
||||||
progres_print(["StaticIPv6", f"{percent}%"])
|
|
||||||
# пополняем словарь ipv6_list
|
|
||||||
ipv6_list.update(ipv6_find(str(c_dict['static6']),ipv6))
|
|
||||||
|
|
||||||
# сжимаем подсети ipv4
|
|
||||||
if ipv4_list:
|
|
||||||
# создаем дерево
|
|
||||||
Root = net_tree.Node(net_tree.Net(0, 0, 4))
|
|
||||||
# добавляем IPv4 подсети
|
|
||||||
for ip_int, mask in sorted(ipv4_list, key=lambda x: x[0]):
|
|
||||||
Root.insert(net_tree.Net(ip_int, mask, 4))
|
|
||||||
# сжатие по CIDR, если ключ сжимать, иначе убираем только родителей, покрываемых детьми
|
|
||||||
ipv4_list = Root.export_compress('route {addr}/{masklen} blackhole;') if compress else Root.export('route {addr}/{masklen} blackhole;')
|
|
||||||
else:
|
|
||||||
ipv4_list:bool=False
|
|
||||||
|
|
||||||
# сжимаем подсети ipv6
|
|
||||||
if ipv6_list:
|
|
||||||
# строим дерево
|
|
||||||
Root = net_tree.Node(net_tree.Net(1 << 127, 0, 6))
|
|
||||||
# добавляем IPv6 подсети
|
|
||||||
for ip_int, mask in sorted(ipv6_list, key=lambda x: x[0]):
|
|
||||||
Root.insert(net_tree.Net(ip_int, mask, 6))
|
|
||||||
# сжатие по CIDR, если ключ сжимать, иначе убираем только родителей, покрываемых детьми
|
|
||||||
ipv6_list = Root.export_compress('route {addr}/{masklen} blackhole;') if compress else Root.export('route {addr}/{masklen} blackhole;')
|
|
||||||
else:
|
|
||||||
ipv6_list:bool=False
|
|
||||||
# возвращаем 2 списка маршрутов
|
|
||||||
return ipv4_list, ipv6_list
|
|
||||||
except Exception as e:
|
|
||||||
# исключение
|
|
||||||
print(f"\nОшибка: {e}")
|
|
||||||
return False, False
|
|
||||||
|
|
||||||
# метод анализа элементов списка (аргументов)
|
|
||||||
def right_list(ip_list: list, args_list: list=[]):
|
|
||||||
"""
|
|
||||||
Метод анализа элементов списка выгрузок, возвращает актульный список выгрузок,
|
|
||||||
основываясь на списке аргументов, переданных вторым параметром
|
|
||||||
"""
|
|
||||||
# собираем словарь аргументов
|
|
||||||
# элементы списка начинающиеся с "-"
|
|
||||||
# попадают в off, остальные в on
|
|
||||||
c_dict = defaultdict(list)
|
|
||||||
for c in args_list:
|
|
||||||
key = "off" if c[0] == "-" else "on"
|
|
||||||
value = c[1:].upper() if c[0] == "-" else c.upper()
|
|
||||||
c_dict[key].append(value)
|
|
||||||
c_dict = dict(c_dict)
|
|
||||||
# если словарь аргументов не пустой
|
|
||||||
if c_dict:
|
|
||||||
# пробегаем список выгрузок
|
|
||||||
for c in ip_list[:]:
|
|
||||||
# пропускаем
|
|
||||||
if len(c_dict) == 2 and c in c_dict["on"] and not c in c_dict["off"]: continue
|
|
||||||
if len(c_dict) == 1 and (("on" in c_dict and c in c_dict["on"]) or ("off" in c_dict and not c in c_dict["off"])): continue
|
|
||||||
# удаляем элемент из списка
|
|
||||||
ip_list = list(filter(lambda x: x != c, ip_list))
|
|
||||||
return ip_list
|
|
||||||
|
|
||||||
# главная фукция
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# словарь выгружаемых списков
|
|
||||||
ip_list = dict()
|
|
||||||
try:
|
|
||||||
# если файл list содержет json структуру, парсим его
|
|
||||||
with open(list_file:=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'list'), "r") as file:
|
|
||||||
ip_list = ast.literal_eval(file.read())
|
|
||||||
print(f"Список выгрузки (файл): {list_file}")
|
|
||||||
except (ValueError, SyntaxError):
|
|
||||||
try:
|
|
||||||
# если файл list ссылка, загружаем и парсим его
|
|
||||||
with open(list_file, "r") as file:
|
|
||||||
session = requests.Session()
|
|
||||||
session.headers.update(get_headers())
|
|
||||||
if (result:=session.get(url_list_file:=file.readline().strip(), timeout=(5, 5))) and result.status_code == 200 and result.text:
|
|
||||||
ip_list = ast.literal_eval(result.text)
|
|
||||||
print(f"Список выгрузки (url): {url_list_file}")
|
|
||||||
except requests.exceptions.RequestException:
|
|
||||||
print(f"Невалидный URL/ошибка выгрузки", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
except (ValueError, SyntaxError):
|
|
||||||
print(f"Ошибочная структура json", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Ошибка: {e}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
except FileNotFoundError:
|
|
||||||
print(f"Файл со списками не найден", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Ошибка: {e}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# проверяем на пустой список выгрузки
|
|
||||||
if (not ip_list):
|
|
||||||
print(f"Список выгрузки пустой", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# cловари для группировки
|
|
||||||
ipv4_dict=dict()
|
|
||||||
ipv6_dict=dict()
|
|
||||||
# список того, что будем выгружать/обновлять
|
|
||||||
download_ip_list=right_list(list(ip_list.keys()), sys.argv[1:])
|
|
||||||
# создаем дерриктори. для сохранения
|
|
||||||
outdir=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'unloading')
|
|
||||||
if not os.path.exists(outdir):
|
|
||||||
os.makedirs(outdir,exist_ok=True)
|
|
||||||
# создаём временный файл экспортируемой конфигурации, перед выгрузкой
|
|
||||||
open(ipv4_bird2_m4 := f"{outdir}/bird2_v4.m4.tmp", "w").close()
|
|
||||||
open(ipv6_bird2_m4 := f"{outdir}/bird2_v6.m4.tmp", "w").close()
|
|
||||||
# удаляем старые файлы группировок
|
|
||||||
[os.remove(path) for f in os.listdir(outdir) if "__" in f and os.path.isfile(path := os.path.join(outdir, f))]
|
|
||||||
# обходим массив списков для выгрузки
|
|
||||||
for clist, value in ip_list.items():
|
|
||||||
# имена выходых файлов
|
|
||||||
ipv4_out_file=f"{outdir}/{clist.lower()}_v4.txt"
|
|
||||||
ipv6_out_file=f"{outdir}/{clist.lower()}_v6.txt"
|
|
||||||
# извлекаем community
|
|
||||||
community=[c for c in value[0].get('community', "").split(",") if c]
|
|
||||||
# обновляем только указанные списки
|
|
||||||
if clist in download_ip_list:
|
|
||||||
print("")
|
|
||||||
# вычисляем кол-во записей прошлой выгрузки
|
|
||||||
ipv4_count_old = sum(1 for line in open(ipv4_out_file)) if os.path.isfile(ipv4_out_file) else 0
|
|
||||||
ipv6_count_old = sum(1 for line in open(ipv6_out_file)) if os.path.isfile(ipv6_out_file) else 0
|
|
||||||
# выполняем выгрузку
|
|
||||||
print(f"Выгрузка списка IP: {clist}")
|
|
||||||
ipv4_list, ipv6_list=list_ip(value)
|
|
||||||
# сохраняем ipv4
|
|
||||||
if ipv4_list and len(ipv4_list.splitlines()) >= ipv4_count_old * 0.5:
|
|
||||||
# сохраняем в файл
|
|
||||||
with open(ipv4_out_file, "w") as file:
|
|
||||||
file.write(ipv4_list)
|
|
||||||
progres_print([f"Файл {ipv4_out_file}", "сохранён"])
|
|
||||||
# сохраняем ipv6
|
|
||||||
if ipv6_list and len(ipv6_list.splitlines()) >= ipv6_count_old * 0.5:
|
|
||||||
# сохраняем в файл
|
|
||||||
with open(ipv6_out_file, "w") as file:
|
|
||||||
file.write(ipv6_list)
|
|
||||||
progres_print([f"Файл {ipv6_out_file}", "сохранён"])
|
|
||||||
# открываем файл выгрузки и пополняем словарь для группировки ipv4
|
|
||||||
if os.path.exists(ipv4_out_file):
|
|
||||||
with open(ipv4_out_file, "r") as file:
|
|
||||||
ipv4_dict[clist] = [community,list(file.readlines())]
|
|
||||||
# открываем файл выгрузки и пополняем словарь для группировки ipv6
|
|
||||||
if os.path.exists(ipv6_out_file):
|
|
||||||
with open(ipv6_out_file, "r") as file:
|
|
||||||
ipv6_dict[clist] = [community,list(file.readlines())]
|
|
||||||
|
|
||||||
print("\n\nКонфигурация Bird2:")
|
|
||||||
# обновляем временный файл конфигурации ipv4
|
|
||||||
# из группировок
|
|
||||||
if ipv4_dict:
|
|
||||||
for k,v in get_dict_groups(ipv4_dict).items():
|
|
||||||
# имена выходых файлов
|
|
||||||
ipv4_out_file=f"{outdir}/{k.lower()}_v4.txt"
|
|
||||||
# сохраняем в файл
|
|
||||||
with open(ipv4_out_file, "w") as file:
|
|
||||||
file.write("".join(v[1]))
|
|
||||||
progres_print([f"Группировка {ipv4_out_file}", "сохранена"])
|
|
||||||
# список комьюнити маршрутов
|
|
||||||
bgp_community=" ".join([f"bgp_community.add(({str(c).replace(':',',')}));" for c in sorted(v[0])])
|
|
||||||
with open(ipv4_bird2_m4, "a") as file:
|
|
||||||
file.write(f"protocol static static_{k.lower()}_v4 {{\n\tipv4 {{ import filter {{ {bgp_community} preference=400; accept; }}; }};\n\tinclude \"{ipv4_out_file}\";\n}}\n")
|
|
||||||
progres_print([f"В {ipv4_bird2_m4}", f"добавлен {k}"])
|
|
||||||
|
|
||||||
# обновляем временный файл конфигурации ipv6
|
|
||||||
# из группировок
|
|
||||||
if ipv6_dict:
|
|
||||||
for k,v in get_dict_groups(ipv6_dict).items():
|
|
||||||
# имена выходых файлов
|
|
||||||
ipv6_out_file=f"{outdir}/{k.lower()}_v6.txt"
|
|
||||||
# сохраняем в файл
|
|
||||||
with open(ipv6_out_file, "w") as file:
|
|
||||||
file.write("".join(v[1]))
|
|
||||||
progres_print([f"Группировка {ipv6_out_file}", "сохранена"])
|
|
||||||
# список комьюнити маршрутов
|
|
||||||
bgp_community=" ".join([f"bgp_community.add(({str(c).replace(':',',')}));" for c in sorted(v[0])])
|
|
||||||
with open(ipv6_bird2_m4, "a") as file:
|
|
||||||
file.write(f"protocol static static_{k.lower()}_v6 {{\n\tipv6 {{ import filter {{ {bgp_community} preference=400; accept; }}; }};\n\tinclude \"{ipv6_out_file}\";\n}}\n")
|
|
||||||
progres_print([f"В {ipv6_bird2_m4}", f"добавлен {k}"])
|
|
||||||
|
|
||||||
# дополняем временный файл конфигурации всей выгрузкой ipv4 и ipv6
|
|
||||||
for clist, value in ip_list.items():
|
|
||||||
# имена выходых файлов
|
|
||||||
ipv4_out_file=f"{outdir}/{clist.lower()}_v4.txt"
|
|
||||||
ipv6_out_file=f"{outdir}/{clist.lower()}_v6.txt"
|
|
||||||
# список комьюнити маршрутов
|
|
||||||
bgp_community=" ".join([f"bgp_community.add(({str(c).replace(':',',')}));" for c in sorted([c for c in value[0].get('community', "").split(",") if c])])
|
|
||||||
if os.path.exists(ipv4_out_file):
|
|
||||||
# фильтер маршрутов ipv4, если список ignore существует в конфигурации
|
|
||||||
ip_addresses_filter=set()
|
|
||||||
if (ignore:=value[0].get('ignore', [])):
|
|
||||||
ignore.append(f"-{clist}") # защита от фильтрации собственных маршрутов
|
|
||||||
for c in right_list(list(ip_list.keys()), ignore):
|
|
||||||
if os.path.exists(f_open:=f"{outdir}/{c.lower()}_v4.txt"):
|
|
||||||
with open(f_open, "r") as file:
|
|
||||||
ip_addresses_filter.update([line.strip().split()[1]+"+" for line in file])
|
|
||||||
with open(ipv4_bird2_m4, "a") as file:
|
|
||||||
bgp_filter=f"if net ~ [{','.join(ip_addresses_filter)}] then reject; " if ip_addresses_filter else ''
|
|
||||||
file.write(f"protocol static static_{clist.lower()}_v4 {{\n\tipv4 {{ import filter {{ {bgp_filter}{bgp_community} accept; }}; }};\n\tinclude \"{ipv4_out_file}\";\n}}\n")
|
|
||||||
progres_print([f"В {ipv4_bird2_m4}", f"добавлен {clist}"])
|
|
||||||
if os.path.exists(ipv6_out_file):
|
|
||||||
# фильтер маршрутов ipv6, если список ignore существует в конфигурации
|
|
||||||
ip_addresses_filter=set()
|
|
||||||
if (ignore:=value[0].get('ignore', [])):
|
|
||||||
ignore.append(f"-{clist}") # защита от фильтрации собственных маршрутов
|
|
||||||
for c in right_list(list(ip_list.keys()), ignore):
|
|
||||||
if os.path.exists(f_open:=f"{outdir}/{c.lower()}_v6.txt"):
|
|
||||||
with open(f_open, "r") as file:
|
|
||||||
ip_addresses_filter.update([line.strip().split()[1]+"+" for line in file])
|
|
||||||
with open(ipv6_bird2_m4, "a") as file:
|
|
||||||
bgp_filter=f"if net ~ [{','.join(ip_addresses_filter)}] then reject; " if ip_addresses_filter else ''
|
|
||||||
file.write(f"protocol static static_{clist.lower()}_v6 {{\n\tipv6 {{ import filter {{ {bgp_filter}{bgp_community} accept; }}; }};\n\tinclude \"{ipv6_out_file}\";\n}}\n")
|
|
||||||
progres_print([f"В {ipv6_bird2_m4}", f"добавлен {clist}"])
|
|
||||||
|
|
||||||
# проверяем, что временный файл конфигурации ipv6 не пустой, сохраняем в постоянный
|
|
||||||
if os.path.exists(ipv6_bird2_m4) and os.path.getsize(ipv6_bird2_m4) != 0:
|
|
||||||
os.replace(ipv6_bird2_m4, config_ipv6:=ipv6_bird2_m4.removesuffix(".tmp"))
|
|
||||||
progres_print([f"Конфиг {ipv6_bird2_m4}", f"перемещён в {os.path.basename(config_ipv6)}"])
|
|
||||||
else:
|
|
||||||
progres_print([f"Конфиг {ipv6_bird2_m4}", f"отсутствует/пуст"], 1)
|
|
||||||
|
|
||||||
# проверяем, что временный файл конфигурации ipv4 не пустой, сохраняем в постоянный
|
|
||||||
if os.path.exists(ipv4_bird2_m4) and os.path.getsize(ipv4_bird2_m4) != 0:
|
|
||||||
os.replace(ipv4_bird2_m4, config_ipv4:=ipv4_bird2_m4.removesuffix(".tmp"))
|
|
||||||
progres_print([f"Конфиг {ipv4_bird2_m4}", f"перемещён в {os.path.basename(config_ipv4)}"])
|
|
||||||
else:
|
|
||||||
progres_print([f"Конфиг {ipv4_bird2_m4}", f"отсутствует/пуст"], 1)
|
|
||||||
|
|
||||||
print("\n")
|
|
||||||
# реконфигурирование Bird2
|
|
||||||
os.system("systemctl reload bird.service >/dev/null 2>&1 || \
|
|
||||||
systemctl restart bird.service >/dev/null 2>&1 && \
|
|
||||||
echo '\\e[32mНовый конфиг Bird2 применён\\e[0m' || echo '\\e[31mBird2 error...\\e[0m'")
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
import random
|
|
||||||
|
|
||||||
def get_headers():
|
|
||||||
# ОС Chrome/Firefox
|
|
||||||
platforms = [
|
|
||||||
'Windows NT 10.0; Win64; x64',
|
|
||||||
'Windows NT 10.0; WOW64',
|
|
||||||
'Macintosh; Intel Mac OS X 10_15_7',
|
|
||||||
'X11; Linux x86_64',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Chrome версии
|
|
||||||
chrome_major = random.randint(120, 128)
|
|
||||||
chrome_build = random.randint(6000, 9999)
|
|
||||||
chrome_patch = random.randint(10, 200)
|
|
||||||
|
|
||||||
chrome_ua = (
|
|
||||||
f"Mozilla/5.0 ({random.choice(platforms)}) "
|
|
||||||
f"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
||||||
f"Chrome/{chrome_major}.0.{chrome_build}.{chrome_patch} Safari/537.36"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Firefox версии
|
|
||||||
ff_ver = random.randint(110, 125)
|
|
||||||
firefox_ua = (
|
|
||||||
f"Mozilla/5.0 ({random.choice(platforms)}; rv:{ff_ver}.0) "
|
|
||||||
f"Gecko/20100101 Firefox/{ff_ver}.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Выбираем браузер
|
|
||||||
user_agent = random.choice([chrome_ua, firefox_ua])
|
|
||||||
|
|
||||||
# sec-ch-ua зависит только от Chrome
|
|
||||||
if "Chrome" in user_agent:
|
|
||||||
sec_ch_ua = f'"Not_A Brand";v="8", "Chromium";v="{chrome_major}", "Google Chrome";v="{chrome_major}"'
|
|
||||||
sec_ch_mob = "?0"
|
|
||||||
sec_platform = '"Windows"' if "Windows" in user_agent else '"macOS"' if "Macintosh" in user_agent else '"Linux"'
|
|
||||||
else:
|
|
||||||
# Firefox их не отправляет
|
|
||||||
sec_ch_ua = None
|
|
||||||
sec_ch_mob = None
|
|
||||||
sec_platform = None
|
|
||||||
|
|
||||||
# Accept-Language
|
|
||||||
accept_lang = random.choice([
|
|
||||||
"ru-RU,ru;q=0.9,en-US;q=0.8",
|
|
||||||
"ru-RU,ru;q=0.8,en-US;q=0.7",
|
|
||||||
"ru-RU;q=0.9,ru;q=0.8,en-US;q=0.7",
|
|
||||||
"ru-RU;q=0.8,ru;q=0.7,en-US;q=0.9",
|
|
||||||
"ru;q=0.9,en;q=0.8",
|
|
||||||
"ru;q=0.8,en;q=0.7",
|
|
||||||
"ru;q=0.7,en;q=0.9",
|
|
||||||
"en-US,en;q=0.9,ru-RU,ru;q=0.8",
|
|
||||||
"en-US,en;q=0.8,ru-RU,ru;q=0.9",
|
|
||||||
"en-US;q=0.9,ru-RU,ru;q=0.7",
|
|
||||||
"en-US;q=0.8,ru-RU,ru;q=0.9",
|
|
||||||
"en-US;q=0.7,ru-RU,ru;q=0.8",
|
|
||||||
"en;q=0.9,ru;q=0.8",
|
|
||||||
"en;q=0.8,ru;q=0.9",
|
|
||||||
"en;q=0.7,ru;q=0.7",
|
|
||||||
])
|
|
||||||
|
|
||||||
# Реалистичные fetch-заголовки Chrome
|
|
||||||
sec_fetch_site = random.choice(["none", "same-site", "same-origin", "cross-site"])
|
|
||||||
sec_fetch_mode = "navigate"
|
|
||||||
sec_fetch_user = "?1"
|
|
||||||
sec_fetch_dest = "document"
|
|
||||||
|
|
||||||
# Заголовки в случайном порядке как в браузере
|
|
||||||
headers_list = [
|
|
||||||
("User-Agent", user_agent),
|
|
||||||
("Accept", "text/html,application/json,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"),
|
|
||||||
("Accept-Language", accept_lang),
|
|
||||||
("Accept-Encoding", "gzip, deflate, br, zstd"),
|
|
||||||
("Connection", "keep-alive"),
|
|
||||||
("Upgrade-Insecure-Requests", "1"),
|
|
||||||
]
|
|
||||||
|
|
||||||
if sec_ch_ua:
|
|
||||||
headers_list.extend([
|
|
||||||
("sec-ch-ua", sec_ch_ua),
|
|
||||||
("sec-ch-ua-mobile", sec_ch_mob),
|
|
||||||
("sec-ch-ua-platform", sec_platform),
|
|
||||||
("Sec-Fetch-Site", sec_fetch_site),
|
|
||||||
("Sec-Fetch-Mode", sec_fetch_mode),
|
|
||||||
("Sec-Fetch-User", sec_fetch_user),
|
|
||||||
("Sec-Fetch-Dest", sec_fetch_dest),
|
|
||||||
])
|
|
||||||
|
|
||||||
# Перемешиваем порядок (!) — браузеры могут менять порядок
|
|
||||||
random.shuffle(headers_list)
|
|
||||||
|
|
||||||
# Превращаем в dict
|
|
||||||
headers = {k: v for k, v in headers_list}
|
|
||||||
|
|
||||||
return headers
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
#
|
|
||||||
# CIDR AGGREGATOR (IPv4 + IPv6)
|
|
||||||
#
|
|
||||||
|
|
||||||
def mask_to_int(mask_size, total_bits):
|
|
||||||
return ((1 << total_bits) - 1) ^ ((1 << (total_bits - mask_size)) - 1)
|
|
||||||
|
|
||||||
|
|
||||||
def ip_volume(mask_size, total_bits):
|
|
||||||
return 1 << (total_bits - mask_size)
|
|
||||||
|
|
||||||
|
|
||||||
class Net:
|
|
||||||
__slots__ = ["version","bits","mask_size","net","mask","volume"]
|
|
||||||
|
|
||||||
def __init__(self, net: int, mask_size: int, version: int=4):
|
|
||||||
self.version, self.bits = (4, 32) if version==4 else (6, 128)
|
|
||||||
self.mask_size = mask_size
|
|
||||||
self.mask = mask_to_int(mask_size, self.bits)
|
|
||||||
self.net = net & self.mask
|
|
||||||
self.volume = ip_volume(mask_size, self.bits)
|
|
||||||
|
|
||||||
#
|
|
||||||
# --- IP CONVERSION ---
|
|
||||||
#
|
|
||||||
def __int_to_ipv4(self, n):
|
|
||||||
return ".".join(str((n >> (24 - 8*i)) & 0xFF) for i in range(4))
|
|
||||||
|
|
||||||
def __int_to_ipv6(self, n):
|
|
||||||
blocks = [(n >> (112 - 16*i)) & 0xFFFF for i in range(8)]
|
|
||||||
|
|
||||||
best_start = -1
|
|
||||||
best_len = 0
|
|
||||||
cur_start = -1
|
|
||||||
cur_len = 0
|
|
||||||
|
|
||||||
for i in range(8):
|
|
||||||
if blocks[i] == 0:
|
|
||||||
if cur_start < 0:
|
|
||||||
cur_start = i
|
|
||||||
cur_len = 1
|
|
||||||
else:
|
|
||||||
cur_len += 1
|
|
||||||
else:
|
|
||||||
if cur_len > best_len:
|
|
||||||
best_len = cur_len
|
|
||||||
best_start = cur_start
|
|
||||||
cur_start = -1
|
|
||||||
cur_len = 0
|
|
||||||
|
|
||||||
if cur_len > best_len:
|
|
||||||
best_len = cur_len
|
|
||||||
best_start = cur_start
|
|
||||||
|
|
||||||
if best_len > 1:
|
|
||||||
new = []
|
|
||||||
i = 0
|
|
||||||
while i < 8:
|
|
||||||
if i == best_start:
|
|
||||||
new.append('')
|
|
||||||
i += best_len
|
|
||||||
else:
|
|
||||||
new.append(format(blocks[i], 'x'))
|
|
||||||
i += 1
|
|
||||||
res = ":".join(new)
|
|
||||||
while ":::" in res:
|
|
||||||
res = res.replace(":::", "::")
|
|
||||||
return res
|
|
||||||
|
|
||||||
return ":".join(format(b, 'x') for b in blocks)
|
|
||||||
|
|
||||||
def __int_to_ip(self, n):
|
|
||||||
return self.__int_to_ipv4(n) if self.version == 4 else self.__int_to_ipv6(n)
|
|
||||||
|
|
||||||
#
|
|
||||||
# --- PUBLIC API ---
|
|
||||||
#
|
|
||||||
def getAsString(self, fmt='{addr}/{masklen}'):
|
|
||||||
return fmt.format(
|
|
||||||
addr=self.__int_to_ip(self.net),
|
|
||||||
masklen=self.mask_size
|
|
||||||
)
|
|
||||||
|
|
||||||
def is_adjacent(self, other):
|
|
||||||
if self.version != other.version: return False
|
|
||||||
if self.mask_size != other.mask_size: return False
|
|
||||||
step = 1 << (self.bits - self.mask_size)
|
|
||||||
return self.net + step == other.net or other.net + step == self.net
|
|
||||||
|
|
||||||
def supernet(self):
|
|
||||||
if self.mask_size == 0:
|
|
||||||
return self
|
|
||||||
new_mask = self.mask_size - 1
|
|
||||||
new_mask_int = mask_to_int(new_mask, self.bits)
|
|
||||||
new_net = self.net & new_mask_int
|
|
||||||
return Net(new_net, new_mask, self.version)
|
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
|
||||||
__slots__ = [
|
|
||||||
"net", "child0", "child1",
|
|
||||||
"is_real",
|
|
||||||
"real_volume", "real_count",
|
|
||||||
"fake_volume", "weight", "max_child_weight"
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, net: Net):
|
|
||||||
self.net = net
|
|
||||||
self.child0 = None
|
|
||||||
self.child1 = None
|
|
||||||
self.is_real = False
|
|
||||||
|
|
||||||
self.real_volume = 0
|
|
||||||
self.real_count = 0
|
|
||||||
self.fake_volume = 0
|
|
||||||
self.weight = 0
|
|
||||||
self.max_child_weight = 0
|
|
||||||
|
|
||||||
#
|
|
||||||
# INSERT NETWORK INTO TRIE
|
|
||||||
#
|
|
||||||
def insert(self, new_net: Net):
|
|
||||||
return self.__insert(new_net, level=0)
|
|
||||||
|
|
||||||
def __insert(self, new_net: Net, level):
|
|
||||||
# если дошли до маски сети — это лист
|
|
||||||
if level == new_net.mask_size:
|
|
||||||
if not self.is_real:
|
|
||||||
self.is_real = True
|
|
||||||
self.child0 = None
|
|
||||||
self.child1 = None
|
|
||||||
return
|
|
||||||
|
|
||||||
# разбираем бит адреса
|
|
||||||
bit_pos = self.net.bits - 1 - level
|
|
||||||
direction = (new_net.net >> bit_pos) & 1
|
|
||||||
|
|
||||||
if direction == 0:
|
|
||||||
if not self.child0:
|
|
||||||
child_net = Net(new_net.net & mask_to_int(level+1, self.net.bits), level+1, self.net.version)
|
|
||||||
self.child0 = Node(child_net)
|
|
||||||
self.child0.__insert(new_net, level+1)
|
|
||||||
|
|
||||||
else:
|
|
||||||
if not self.child1:
|
|
||||||
child_net = Net(new_net.net & mask_to_int(level+1, self.net.bits), level+1, self.net.version)
|
|
||||||
self.child1 = Node(child_net)
|
|
||||||
self.child1.__insert(new_net, level+1)
|
|
||||||
|
|
||||||
#
|
|
||||||
# CALCULATE WEIGHTS
|
|
||||||
#
|
|
||||||
def finalize(self):
|
|
||||||
if self.is_real:
|
|
||||||
self.real_volume = self.net.volume
|
|
||||||
self.real_count = 1
|
|
||||||
self.fake_volume = 0
|
|
||||||
self.weight = 0
|
|
||||||
self.max_child_weight = 0
|
|
||||||
return
|
|
||||||
|
|
||||||
self.real_volume = 0
|
|
||||||
self.real_count = 0
|
|
||||||
self.fake_volume = 0
|
|
||||||
self.max_child_weight = 0
|
|
||||||
|
|
||||||
for ch in (self.child0, self.child1):
|
|
||||||
if ch:
|
|
||||||
ch.finalize()
|
|
||||||
self.real_volume += ch.real_volume
|
|
||||||
self.real_count += ch.real_count
|
|
||||||
self.fake_volume += ch.fake_volume
|
|
||||||
self.max_child_weight = max(self.max_child_weight, ch.weight, ch.max_child_weight)
|
|
||||||
|
|
||||||
self.__recalc()
|
|
||||||
|
|
||||||
def __recalc(self):
|
|
||||||
missing = self.net.volume - self.real_volume - self.fake_volume
|
|
||||||
if missing > 0:
|
|
||||||
self.weight = (self.real_count - 1) / (missing ** 0.5)
|
|
||||||
else:
|
|
||||||
self.weight = float('inf')
|
|
||||||
|
|
||||||
#
|
|
||||||
# COLLAPSE / AGGREGATE
|
|
||||||
#
|
|
||||||
def collapse(self, min_weight=0, max_delta=float('inf')):
|
|
||||||
if self.is_real:
|
|
||||||
return 0,0
|
|
||||||
|
|
||||||
delta = 0
|
|
||||||
fake = 0
|
|
||||||
|
|
||||||
# сворачиваем детей
|
|
||||||
for ch in (self.child0, self.child1):
|
|
||||||
if ch:
|
|
||||||
d, f = ch.collapse(min_weight, max_delta - delta)
|
|
||||||
delta += d
|
|
||||||
fake += f
|
|
||||||
|
|
||||||
# попытаемся объединить
|
|
||||||
if self.child0 and self.child1:
|
|
||||||
c0 = self.child0
|
|
||||||
c1 = self.child1
|
|
||||||
|
|
||||||
if (c0.is_real and c1.is_real and
|
|
||||||
c0.net.is_adjacent(c1.net)):
|
|
||||||
|
|
||||||
super_net = c0.net.supernet()
|
|
||||||
|
|
||||||
# превращаем текущий узел в супернет
|
|
||||||
self.net = super_net
|
|
||||||
self.is_real = True
|
|
||||||
self.child0 = None
|
|
||||||
self.child1 = None
|
|
||||||
|
|
||||||
self.real_volume = c0.real_volume + c1.real_volume
|
|
||||||
self.fake_volume = super_net.volume - self.real_volume
|
|
||||||
self.real_count = 1
|
|
||||||
self.weight = 0
|
|
||||||
self.max_child_weight = 0
|
|
||||||
|
|
||||||
return delta + 2, fake + self.fake_volume
|
|
||||||
|
|
||||||
# пересчитываем статистику
|
|
||||||
if not self.is_real:
|
|
||||||
self.real_volume = 0
|
|
||||||
self.real_count = 0
|
|
||||||
self.fake_volume = 0
|
|
||||||
self.max_child_weight = 0
|
|
||||||
|
|
||||||
for ch in (self.child0, self.child1):
|
|
||||||
if ch:
|
|
||||||
self.real_volume += ch.real_volume
|
|
||||||
self.real_count += ch.real_count
|
|
||||||
self.fake_volume += ch.fake_volume
|
|
||||||
self.max_child_weight = max(self.max_child_weight, ch.weight, ch.max_child_weight)
|
|
||||||
self.__recalc()
|
|
||||||
return delta, fake
|
|
||||||
|
|
||||||
def export_compress(self, fmt='{addr}/{masklen}'):
|
|
||||||
# считаем статистику
|
|
||||||
self.finalize()
|
|
||||||
# сжимаем
|
|
||||||
self.collapse()
|
|
||||||
result = []
|
|
||||||
|
|
||||||
def walk(node):
|
|
||||||
if node is None:
|
|
||||||
return
|
|
||||||
# если суперсеть реальная –> дети не нужны
|
|
||||||
if node.is_real:
|
|
||||||
result.append(node.net.getAsString(fmt))
|
|
||||||
return
|
|
||||||
walk(node.child0)
|
|
||||||
walk(node.child1)
|
|
||||||
|
|
||||||
walk(self)
|
|
||||||
return "\n".join(result)
|
|
||||||
|
|
||||||
|
|
||||||
def export(self, fmt='{addr}/{masklen}'):
|
|
||||||
# считаем статистику
|
|
||||||
self.finalize()
|
|
||||||
result = []
|
|
||||||
|
|
||||||
def walk(node):
|
|
||||||
if node is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if node.is_real:
|
|
||||||
# дети полностью покрывают диапазон родителя?
|
|
||||||
child_real_vol = 0
|
|
||||||
for ch in (node.child0, node.child1):
|
|
||||||
if ch:
|
|
||||||
child_real_vol += ch.real_volume
|
|
||||||
|
|
||||||
# если дети полностью покрывают родителя -> родители не нужны
|
|
||||||
if child_real_vol >= node.net.volume:
|
|
||||||
walk(node.child0)
|
|
||||||
walk(node.child1)
|
|
||||||
return
|
|
||||||
|
|
||||||
# иначе выводим родителя и детей
|
|
||||||
result.append(node.net.getAsString(fmt))
|
|
||||||
|
|
||||||
walk(node.child0)
|
|
||||||
walk(node.child1)
|
|
||||||
|
|
||||||
walk(self)
|
|
||||||
return "\n".join(result)
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
{
|
|
||||||
'RU': [
|
|
||||||
# параметры конфигурации должны быть в самом верху списка
|
|
||||||
{
|
|
||||||
'ipv4': True, # не обязательный аргумент, по умолчанию True (24)
|
|
||||||
'ipv6': True, # не обязательный аргумент, по умолчанию False (64)
|
|
||||||
'ignore': ['CHINA', 'JAPAN'], # игнорируем маршруты, которые входят в перечисленный список выгрузок (работает кроме: -CHINA)
|
|
||||||
'compress': False, # не обязательный аргумент, по умолчанию True
|
|
||||||
'community': '65432:LOCATION,65432:200' # не обязательный аргумент, по умолчанию пусто
|
|
||||||
},
|
|
||||||
# Большая часть RU сегмента
|
|
||||||
{ 'url': ['https://stat.ripe.net/data/country-resource-list/data.json?resource=RU'] },
|
|
||||||
{ 'url': ['https://ipv4.fetus.jp/ru.txt'] },
|
|
||||||
{ 'url': ['https://github.com/ipverse/rir-ip/blob/master/country/ru/aggregated.json'] },
|
|
||||||
# HLL LLC
|
|
||||||
{ 'url': ['https://bgp.he.net/AS51115#_prefixes', 'https://ipinfo.io/widget/demo/AS51115?dataset=asn', 'https://api.hackertarget.com/aslookup/?q=AS51115'] },
|
|
||||||
# STATIC
|
|
||||||
{ 'static4': '188.130.255.0/24' },
|
|
||||||
],
|
|
||||||
'CHINA': [
|
|
||||||
{
|
|
||||||
# пример без community
|
|
||||||
'ipv4': True,
|
|
||||||
'ipv6': False,
|
|
||||||
'compress': True,
|
|
||||||
#'community': '65432:LOCATION,65432:201'
|
|
||||||
},
|
|
||||||
# Большая часть CH сегмента
|
|
||||||
{ 'url': ['https://stat.ripe.net/data/country-resource-list/data.json?resource=CN'] },
|
|
||||||
{ 'url': ['https://ipv4.fetus.jp/cn.txt'] },
|
|
||||||
{ 'url': ['https://github.com/ipverse/rir-ip/blob/master/country/cn/aggregated.json'] },
|
|
||||||
],
|
|
||||||
'JAPAN': [
|
|
||||||
{
|
|
||||||
'ipv6': True,
|
|
||||||
'community': '65432:LOCATION,65432:202',
|
|
||||||
},
|
|
||||||
# Большая часть KR сегмента
|
|
||||||
{ 'url': ['https://stat.ripe.net/data/country-resource-list/data.json?resource=JP'] },
|
|
||||||
{ 'url': ['https://ipv4.fetus.jp/jp.txt'] },
|
|
||||||
{ 'url': ['https://github.com/ipverse/rir-ip/blob/master/country/jp/aggregated.json'] },
|
|
||||||
],
|
|
||||||
'KOREA': [
|
|
||||||
{
|
|
||||||
'ipv6': True,
|
|
||||||
'community': '65432:LOCATION,65432:203'
|
|
||||||
},
|
|
||||||
# Большая часть KR сегмента
|
|
||||||
{ 'url': ['https://stat.ripe.net/data/country-resource-list/data.json?resource=KR'] },
|
|
||||||
{ 'url': ['https://ipv4.fetus.jp/kr.txt'] },
|
|
||||||
{ 'url': ['https://github.com/ipverse/rir-ip/blob/master/country/kr/aggregated.json'] },
|
|
||||||
# LG DACOM Corporation
|
|
||||||
{ 'url': ['https://bgp.he.net/AS3786#_prefixes', 'https://ipinfo.io/widget/demo/AS3786?dataset=asn', 'https://api.hackertarget.com/aslookup/?q=AS3786'] },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user