Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58f1405fef | ||
|
|
170553612d | ||
|
|
e197dae9f7 |
+17
-9
@@ -1,21 +1,27 @@
|
|||||||
FROM python:3.9-slim
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
# Установка системных зависимостей
|
# Установка системных зависимостей и FRR
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
|
frr \
|
||||||
|
frr-pythontools \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
iproute2 \
|
iproute2 \
|
||||||
iputils-ping \
|
iputils-ping \
|
||||||
net-tools \
|
net-tools \
|
||||||
procps \
|
procps \
|
||||||
|
vim \
|
||||||
|
curl \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Установка ExaBGP
|
# Установка Python зависимостей
|
||||||
RUN pip install exabgp
|
RUN pip3 install watchdog
|
||||||
|
|
||||||
# Создание структуры директорий
|
# Создание структуры директорий
|
||||||
RUN mkdir -p /app/scripts /app/data /app/logs /run/exabgp /var/run/exabgp /usr/local/run/exabgp /usr/local/var/run/exabgp
|
RUN mkdir -p /app/scripts /app/data /app/logs /etc/frr
|
||||||
|
|
||||||
# Копирование файлов
|
# Копирование файлов
|
||||||
COPY exabgp.conf /app/exabgp.conf
|
COPY frr.conf /etc/frr/frr.conf
|
||||||
COPY scripts/ /app/scripts/
|
COPY scripts/ /app/scripts/
|
||||||
COPY data/ /app/data/
|
COPY data/ /app/data/
|
||||||
|
|
||||||
@@ -23,6 +29,7 @@ COPY data/ /app/data/
|
|||||||
RUN chmod +x /app/scripts/start.sh
|
RUN chmod +x /app/scripts/start.sh
|
||||||
RUN chmod +x /app/scripts/load_prefixes.py
|
RUN chmod +x /app/scripts/load_prefixes.py
|
||||||
RUN chmod +x /app/scripts/generate_config.py
|
RUN chmod +x /app/scripts/generate_config.py
|
||||||
|
RUN chmod +x /app/scripts/monitor.py
|
||||||
|
|
||||||
# Рабочая директория
|
# Рабочая директория
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -32,12 +39,13 @@ EXPOSE 179
|
|||||||
|
|
||||||
# Установка переменных окружения по умолчанию
|
# Установка переменных окружения по умолчанию
|
||||||
ENV BGP_LOCAL_AS=65000
|
ENV BGP_LOCAL_AS=65000
|
||||||
ENV BGP_ROUTER_ID=192.168.1.100
|
ENV BGP_ROUTER_ID=192.168.100.99
|
||||||
ENV BGP_NEIGHBOR_IP=192.168.1.1
|
ENV BGP_NEIGHBOR_IP=192.168.0.254
|
||||||
ENV BGP_NEIGHBOR_AS=65001
|
ENV BGP_NEIGHBOR_AS=65001
|
||||||
ENV BGP_HOLD_TIME=90
|
ENV BGP_HOLD_TIME=90
|
||||||
ENV BGP_KEEPALIVE=30
|
ENV BGP_KEEPALIVE=30
|
||||||
ENV BGP_LOG_LEVEL=INFO
|
ENV BGP_LOG_LEVEL=info
|
||||||
|
ENV FRR_DAEMONS="bgpd zebra"
|
||||||
|
|
||||||
# Команда запуска
|
# Команда запуска
|
||||||
ENTRYPOINT ["/app/scripts/start.sh"]
|
ENTRYPOINT ["/app/scripts/start.sh"]
|
||||||
+242
-146
@@ -1,228 +1,324 @@
|
|||||||
# Быстрый старт BGP сервера
|
# Быстрый старт BGP Server (FRR)
|
||||||
|
|
||||||
## Минимальные требования
|
Этот документ поможет вам быстро запустить BGP сервер на базе FRR для раздачи префиксов.
|
||||||
|
|
||||||
|
## Предварительные требования
|
||||||
|
|
||||||
- Docker Desktop для Windows
|
- Docker Desktop для Windows
|
||||||
- PowerShell
|
- PowerShell
|
||||||
- RouterOS 7.14+ (на стороне клиента)
|
- RouterOS 7.14+ (для клиентской стороны)
|
||||||
|
|
||||||
## Быстрый запуск (5 минут)
|
## Шаг 1: Подготовка файлов
|
||||||
|
|
||||||
### 1. Клонирование и подготовка
|
### Создание файла с префиксами
|
||||||
|
|
||||||
```powershell
|
Создайте файл `data/prefixes.txt` с вашими префиксами:
|
||||||
# Клонирование репозитория (если нужно)
|
|
||||||
git clone <repository-url>
|
|
||||||
cd bgp-server
|
|
||||||
|
|
||||||
# Создание необходимых директорий
|
|
||||||
mkdir logs
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Настройка префиксов
|
|
||||||
|
|
||||||
Отредактируйте файл `data/prefixes.txt`:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
# Основные префиксы
|
||||||
192.168.1.0/24
|
192.168.1.0/24
|
||||||
|
192.168.2.0/24
|
||||||
10.0.0.0/8
|
10.0.0.0/8
|
||||||
172.16.0.0/12
|
172.16.0.0/12
|
||||||
|
|
||||||
|
# Дополнительные префиксы
|
||||||
|
203.0.113.0/24
|
||||||
|
198.51.100.0/24
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Запуск сервера
|
### Создание дополнительного файла (опционально)
|
||||||
|
|
||||||
|
Создайте файл `data/additional_prefixes.txt` для дополнительных префиксов:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Дополнительные префиксы
|
||||||
|
192.168.100.0/24
|
||||||
|
192.168.200.0/24
|
||||||
|
```
|
||||||
|
|
||||||
|
## Шаг 2: Запуск сервера
|
||||||
|
|
||||||
|
### Вариант A: Использование PowerShell скрипта (рекомендуется)
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Запуск с автоматической сборкой
|
# Запуск с автоматической сборкой образа
|
||||||
.\scripts\start-server.ps1 -Build
|
.\scripts\start-server.ps1 -Build
|
||||||
|
|
||||||
# Или вручную
|
# Или запуск без пересборки
|
||||||
docker-compose up -d --build
|
.\scripts\start-server.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Проверка работы
|
### Вариант B: Использование Docker Compose
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Проверка статуса
|
# Сборка и запуск
|
||||||
docker-compose ps
|
docker-compose up -d --build
|
||||||
|
|
||||||
# Просмотр логов
|
# Или только запуск
|
||||||
docker-compose logs -f bgp-server
|
docker-compose up -d
|
||||||
|
|
||||||
# Мониторинг
|
|
||||||
docker exec bgp-server python3 /app/scripts/monitor.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Настройка RouterOS
|
### Вариант C: Ручной запуск
|
||||||
|
|
||||||
Выполните в RouterOS:
|
```powershell
|
||||||
|
# Сборка образа
|
||||||
|
docker build -t bgp-server-frr .
|
||||||
|
|
||||||
|
# Запуск контейнера
|
||||||
|
docker run -d \
|
||||||
|
--name bgp-server \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 179:179 \
|
||||||
|
-v ${PWD}/data:/app/data:ro \
|
||||||
|
-v ${PWD}/logs:/app/logs \
|
||||||
|
--cap-add NET_ADMIN \
|
||||||
|
--privileged \
|
||||||
|
bgp-server-frr
|
||||||
|
```
|
||||||
|
|
||||||
|
## Шаг 3: Проверка работы
|
||||||
|
|
||||||
|
### Проверка статуса контейнера
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Статус контейнера
|
||||||
|
docker ps
|
||||||
|
|
||||||
|
# Логи контейнера
|
||||||
|
docker logs bgp-server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка FRR
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Версия FRR
|
||||||
|
docker exec bgp-server vtysh -c "show version"
|
||||||
|
|
||||||
|
# Статус BGP
|
||||||
|
docker exec bgp-server vtysh -c "show ip bgp summary"
|
||||||
|
|
||||||
|
# Анонсированные маршруты
|
||||||
|
docker exec bgp-server vtysh -c "show ip bgp"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка загруженных префиксов
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Логи загрузки префиксов
|
||||||
|
docker exec bgp-server tail -f /app/logs/prefixes.log
|
||||||
|
|
||||||
|
# Тестирование загрузки
|
||||||
|
docker exec bgp-server python3 /app/scripts/load_prefixes.py --test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Шаг 4: Настройка RouterOS
|
||||||
|
|
||||||
|
### Базовая настройка BGP
|
||||||
|
|
||||||
```routeros
|
```routeros
|
||||||
/routing bgp instance add name=bgp-server as=65001 router-id=192.168.1.1
|
# Создание BGP экземпляра
|
||||||
/routing bgp network add network=192.168.1.0/24
|
/routing bgp instance
|
||||||
/routing bgp peer add name=bgp-server remote-address=192.168.1.100 remote-as=65000 instance=bgp-server
|
add name=bgp-server as=65001 router-id=192.168.0.254
|
||||||
|
|
||||||
|
# Настройка BGP сети
|
||||||
|
/routing bgp network
|
||||||
|
add network=192.168.0.0/24
|
||||||
|
|
||||||
|
# Настройка BGP пира
|
||||||
|
/routing bgp peer
|
||||||
|
add name=bgp-server remote-address=192.168.100.99 remote-as=65000 instance=bgp-server
|
||||||
|
|
||||||
|
# Включение BGP
|
||||||
/routing bgp instance set bgp-server disabled=no
|
/routing bgp instance set bgp-server disabled=no
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Проверка BGP сессии
|
### Проверка BGP сессии
|
||||||
|
|
||||||
В RouterOS:
|
|
||||||
```routeros
|
```routeros
|
||||||
|
# Статус BGP
|
||||||
/routing bgp peer print
|
/routing bgp peer print
|
||||||
|
|
||||||
|
# Полученные маршруты
|
||||||
|
/routing bgp route print
|
||||||
```
|
```
|
||||||
|
|
||||||
Ожидаемый результат:
|
## Шаг 5: Мониторинг
|
||||||
```
|
|
||||||
Flags: X - disabled, I - invalid, D - dynamic, E - established
|
|
||||||
0 name="bgp-server" ... state=established uptime=1m30s prefix-count=8
|
|
||||||
```
|
|
||||||
|
|
||||||
## Полезные команды
|
### Просмотр логов
|
||||||
|
|
||||||
### Управление сервером
|
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Запуск
|
# Логи FRR
|
||||||
.\scripts\start-server.ps1
|
docker exec bgp-server tail -f /app/logs/frr.log
|
||||||
|
|
||||||
# Остановка
|
# Логи загрузки префиксов
|
||||||
|
docker exec bgp-server tail -f /app/logs/prefixes.log
|
||||||
|
|
||||||
|
# Логи мониторинга
|
||||||
|
docker exec bgp-server tail -f /app/logs/monitor.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Статус системы
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Статус в JSON формате
|
||||||
|
docker exec bgp-server cat /app/logs/status.json
|
||||||
|
|
||||||
|
# Запуск мониторинга
|
||||||
|
docker exec bgp-server python3 /app/scripts/monitor.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Команды vtysh
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Подключение к FRR CLI
|
||||||
|
docker exec -it bgp-server vtysh
|
||||||
|
|
||||||
|
# Полезные команды внутри vtysh:
|
||||||
|
show ip bgp summary # Сводка BGP
|
||||||
|
show ip bgp # Все BGP маршруты
|
||||||
|
show ip route # Таблица маршрутизации
|
||||||
|
show interface # Интерфейсы
|
||||||
|
show running-config # Конфигурация
|
||||||
|
```
|
||||||
|
|
||||||
|
## Управление
|
||||||
|
|
||||||
|
### Остановка сервера
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Остановка с сохранением контейнера
|
||||||
.\scripts\stop-server.ps1
|
.\scripts\stop-server.ps1
|
||||||
|
|
||||||
# Перезапуск
|
# Остановка с удалением контейнера
|
||||||
docker-compose restart
|
.\scripts\stop-server.ps1 -Force
|
||||||
|
|
||||||
# Просмотр логов
|
|
||||||
docker-compose logs -f bgp-server
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Мониторинг
|
### Перезагрузка префиксов
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Проверка статуса
|
# Перезагрузка без перезапуска сервера
|
||||||
docker-compose ps
|
|
||||||
|
|
||||||
# Мониторинг ресурсов
|
|
||||||
docker stats bgp-server
|
|
||||||
|
|
||||||
# Проверка BGP сессии
|
|
||||||
docker exec bgp-server exabgpcli show neighbor
|
|
||||||
```
|
|
||||||
|
|
||||||
### Обновление префиксов
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# Перезагрузка префиксов без перезапуска
|
|
||||||
docker exec bgp-server python3 /app/scripts/reload_prefixes.py
|
docker exec bgp-server python3 /app/scripts/reload_prefixes.py
|
||||||
|
|
||||||
# Или перезапуск сервера
|
|
||||||
docker-compose restart
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Устранение неполадок
|
### Обновление контейнера
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Обновление с пересборкой
|
||||||
|
.\scripts\update-container.ps1 -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
## Диагностика
|
||||||
|
|
||||||
### Проблема: Контейнер не запускается
|
### Проблема: Контейнер не запускается
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Проверка логов
|
# Проверка логов
|
||||||
docker-compose logs bgp-server
|
docker logs bgp-server
|
||||||
|
|
||||||
# Проверка портов
|
# Проверка портов
|
||||||
netstat -an | findstr :179
|
netstat -an | findstr :179
|
||||||
|
|
||||||
# Пересборка образа
|
# Проверка Docker
|
||||||
docker-compose build --no-cache
|
docker system info
|
||||||
```
|
```
|
||||||
|
|
||||||
### Проблема: BGP сессия не устанавливается
|
### Проблема: BGP сессия не устанавливается
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Проверка сетевого подключения
|
# Проверка сетевой связности
|
||||||
docker exec bgp-server ping 192.168.1.1
|
docker exec bgp-server ping 192.168.0.254
|
||||||
|
|
||||||
# Проверка конфигурации
|
# Проверка конфигурации FRR
|
||||||
docker exec bgp-server cat /app/exabgp.conf
|
docker exec bgp-server vtysh -c "show running-config"
|
||||||
|
|
||||||
# Проверка логов
|
# Проверка статуса BGP
|
||||||
docker-compose logs bgp-server
|
docker exec bgp-server vtysh -c "show ip bgp summary"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Проблема: Префиксы не анонсируются
|
### Проблема: Префиксы не анонсируются
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Проверка файлов с префиксами
|
# Проверка файлов префиксов
|
||||||
docker exec bgp-server cat /app/data/prefixes.txt
|
docker exec bgp-server cat /app/data/prefixes.txt
|
||||||
|
|
||||||
# Проверка логов загрузки префиксов
|
# Проверка логов загрузки
|
||||||
docker exec bgp-server cat /app/logs/prefixes.log
|
docker exec bgp-server tail -f /app/logs/prefixes.log
|
||||||
|
|
||||||
# Перезагрузка префиксов
|
# Проверка статических маршрутов
|
||||||
docker exec bgp-server python3 /app/scripts/reload_prefixes.py
|
docker exec bgp-server vtysh -c "show ip route static"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Настройка сети
|
## Настройка переменных окружения
|
||||||
|
|
||||||
### Изменение IP адресов
|
Вы можете настроить BGP параметры через переменные окружения:
|
||||||
|
|
||||||
Отредактируйте `docker-compose.yml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
environment:
|
|
||||||
- BGP_ROUTER_ID=192.168.1.100 # IP сервера
|
|
||||||
- BGP_NEIGHBOR_IP=192.168.1.1 # IP RouterOS
|
|
||||||
```
|
|
||||||
|
|
||||||
И `exabgp.conf`:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
neighbor 192.168.1.1 { # IP RouterOS
|
|
||||||
router-id 192.168.1.100; # IP сервера
|
|
||||||
local-address 192.168.1.100; # IP сервера
|
|
||||||
# ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Изменение AS номеров
|
|
||||||
|
|
||||||
В `docker-compose.yml`:
|
|
||||||
```yaml
|
|
||||||
environment:
|
|
||||||
- BGP_LOCAL_AS=65000 # AS сервера
|
|
||||||
- BGP_NEIGHBOR_AS=65001 # AS RouterOS
|
|
||||||
```
|
|
||||||
|
|
||||||
В `exabgp.conf`:
|
|
||||||
```ini
|
|
||||||
neighbor 192.168.1.1 {
|
|
||||||
local-as 65000; # AS сервера
|
|
||||||
peer-as 65001; # AS RouterOS
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Безопасность
|
|
||||||
|
|
||||||
### MD5 аутентификация
|
|
||||||
|
|
||||||
В `exabgp.conf`:
|
|
||||||
```ini
|
|
||||||
neighbor 192.168.1.1 {
|
|
||||||
# ... существующие настройки ...
|
|
||||||
md5-password "your-secure-password";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
В RouterOS:
|
|
||||||
```routeros
|
|
||||||
/routing bgp peer set bgp-server tcp-md5-key="your-secure-password"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Firewall
|
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Windows Firewall (PowerShell)
|
# Запуск с кастомными настройками
|
||||||
New-NetFirewallRule -DisplayName "BGP Server" -Direction Inbound -Protocol TCP -LocalPort 179 -Action Allow
|
docker run -d \
|
||||||
|
--name bgp-server \
|
||||||
|
-p 179:179 \
|
||||||
|
-v ${PWD}/data:/app/data:ro \
|
||||||
|
-v ${PWD}/logs:/app/logs \
|
||||||
|
-e BGP_LOCAL_AS=65000 \
|
||||||
|
-e BGP_ROUTER_ID=192.168.100.99 \
|
||||||
|
-e BGP_NEIGHBOR_IP=192.168.0.254 \
|
||||||
|
-e BGP_NEIGHBOR_AS=65001 \
|
||||||
|
-e BGP_HOLD_TIME=90 \
|
||||||
|
-e BGP_KEEPALIVE=30 \
|
||||||
|
--cap-add NET_ADMIN \
|
||||||
|
--privileged \
|
||||||
|
bgp-server-frr
|
||||||
```
|
```
|
||||||
|
|
||||||
## Готово!
|
## Полезные команды
|
||||||
|
|
||||||
После выполнения всех шагов у вас должен работать BGP сервер, который раздает префиксы из текстовых файлов в RouterOS.
|
### Мониторинг в реальном времени
|
||||||
|
|
||||||
Для получения дополнительной информации см. основной файл `README.md`.
|
```powershell
|
||||||
|
# Логи в реальном времени
|
||||||
|
docker logs -f bgp-server
|
||||||
|
|
||||||
|
# Статус BGP каждые 5 секунд
|
||||||
|
while ($true) {
|
||||||
|
docker exec bgp-server vtysh -c "show ip bgp summary"
|
||||||
|
Start-Sleep 5
|
||||||
|
Clear-Host
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Резервное копирование
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Копирование файлов префиксов
|
||||||
|
Copy-Item data/prefixes.txt data/prefixes.txt.backup
|
||||||
|
|
||||||
|
# Экспорт конфигурации FRR
|
||||||
|
docker exec bgp-server vtysh -c "show running-config" > frr-config.backup
|
||||||
|
```
|
||||||
|
|
||||||
|
### Очистка
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Остановка и удаление контейнера
|
||||||
|
docker stop bgp-server
|
||||||
|
docker rm bgp-server
|
||||||
|
|
||||||
|
# Удаление образа
|
||||||
|
docker rmi bgp-server-frr
|
||||||
|
|
||||||
|
# Очистка логов
|
||||||
|
Remove-Item logs/* -Recurse -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
## Следующие шаги
|
||||||
|
|
||||||
|
1. **Настройка мониторинга**: Настройте алерты для критических событий
|
||||||
|
2. **Резервное копирование**: Настройте автоматическое резервное копирование конфигурации
|
||||||
|
3. **Безопасность**: Настройте firewall и аутентификацию BGP
|
||||||
|
4. **Масштабирование**: Рассмотрите использование нескольких экземпляров для высокой доступности
|
||||||
|
|
||||||
|
## Поддержка
|
||||||
|
|
||||||
|
- Документация FRR: https://frrouting.org/
|
||||||
|
- GitHub проекта: https://github.com/FRRouting/frr
|
||||||
|
- Сообщество: https://lists.frrouting.org/
|
||||||
@@ -1,28 +1,38 @@
|
|||||||
# BGP Server для раздачи префиксов
|
# BGP Server для раздачи префиксов (FRR)
|
||||||
|
|
||||||
Этот проект представляет собой BGP сервер, который раздает префиксы из текстовых файлов. Сервер работает в Docker контейнере и предназначен для интеграции с RouterOS 7.14+.
|
Этот проект представляет собой BGP сервер на базе FRR (Free Range Routing), который раздает префиксы из текстовых файлов. Сервер работает в Docker контейнере и предназначен для интеграции с RouterOS 7.14+.
|
||||||
|
|
||||||
## Архитектура решения
|
## Архитектура решения
|
||||||
|
|
||||||
- **BGP Server**: ExaBGP в Docker контейнере
|
- **BGP Server**: FRR (Free Range Routing) в Docker контейнере
|
||||||
- **Источник данных**: Текстовые файлы с префиксами
|
- **Источник данных**: Текстовые файлы с префиксами
|
||||||
- **Клиент**: RouterOS 7.14+ для получения префиксов
|
- **Клиент**: RouterOS 7.14+ для получения префиксов
|
||||||
- **Протокол**: BGP (только исходящие анонсы)
|
- **Протокол**: BGP (только исходящие анонсы)
|
||||||
|
|
||||||
|
## Преимущества FRR
|
||||||
|
|
||||||
|
- **Стабильность**: Производственный уровень надежности
|
||||||
|
- **Производительность**: Оптимизирован для высоких нагрузок
|
||||||
|
- **Функциональность**: Полная поддержка BGP функций
|
||||||
|
- **Мониторинг**: Встроенные инструменты диагностики
|
||||||
|
- **Сообщество**: Активная поддержка и развитие
|
||||||
|
|
||||||
## Структура проекта
|
## Структура проекта
|
||||||
|
|
||||||
```
|
```
|
||||||
bgp-server/
|
bgp-server/
|
||||||
├── docker-compose.yml # Конфигурация Docker
|
├── docker-compose.yml # Конфигурация Docker
|
||||||
├── Dockerfile # Образ Docker
|
├── Dockerfile # Образ Docker с FRR
|
||||||
├── exabgp.conf # Конфигурация ExaBGP
|
├── frr.conf # Конфигурация FRR
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ ├── start.sh # Скрипт запуска
|
│ ├── start.sh # Скрипт запуска FRR
|
||||||
│ └── load_prefixes.py # Загрузка префиксов из файлов
|
│ ├── load_prefixes.py # Загрузка префиксов в FRR
|
||||||
|
│ ├── generate_config.py # Генерация конфигурации FRR
|
||||||
|
│ └── monitor.py # Мониторинг FRR
|
||||||
├── data/
|
├── data/
|
||||||
│ ├── prefixes.txt # Основной файл с префиксами
|
│ ├── prefixes.txt # Основной файл с префиксами
|
||||||
│ └── additional_prefixes.txt # Дополнительные префиксы
|
│ └── additional_prefixes.txt # Дополнительные префиксы
|
||||||
├── logs/ # Логи ExaBGP
|
├── logs/ # Логи FRR
|
||||||
└── README.md # Эта инструкция
|
└── README.md # Эта инструкция
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -56,6 +66,9 @@ docker-compose logs -f bgp-server
|
|||||||
|
|
||||||
# Проверка статуса контейнера
|
# Проверка статуса контейнера
|
||||||
docker-compose ps
|
docker-compose ps
|
||||||
|
|
||||||
|
# Проверка BGP сессии через vtysh
|
||||||
|
docker exec bgp-server vtysh -c "show ip bgp summary"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Подробная настройка
|
## Подробная настройка
|
||||||
@@ -91,45 +104,17 @@ docker-compose ps
|
|||||||
|
|
||||||
### Шаг 2: Конфигурация BGP сервера
|
### Шаг 2: Конфигурация BGP сервера
|
||||||
|
|
||||||
Отредактируйте `exabgp.conf`:
|
Конфигурация FRR генерируется автоматически из переменных окружения в `docker-compose.yml`:
|
||||||
|
|
||||||
```ini
|
```yaml
|
||||||
# Основная конфигурация
|
environment:
|
||||||
process load-prefixes {
|
- BGP_LOCAL_AS=65000
|
||||||
run /app/scripts/load_prefixes.py;
|
- BGP_ROUTER_ID=192.168.1.100
|
||||||
encoder json;
|
- BGP_NEIGHBOR_IP=192.168.1.1
|
||||||
}
|
- BGP_NEIGHBOR_AS=65001
|
||||||
|
- BGP_HOLD_TIME=90
|
||||||
# Настройки BGP
|
- BGP_KEEPALIVE=30
|
||||||
neighbor 192.168.1.1 {
|
- BGP_LOG_LEVEL=info
|
||||||
router-id 192.168.1.100;
|
|
||||||
local-address 192.168.1.100;
|
|
||||||
local-as 65000;
|
|
||||||
peer-as 65001;
|
|
||||||
|
|
||||||
# Только исходящие анонсы
|
|
||||||
capability {
|
|
||||||
graceful-restart;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Настройки сессии
|
|
||||||
hold-time 90;
|
|
||||||
keepalive 30;
|
|
||||||
|
|
||||||
# Логирование
|
|
||||||
api {
|
|
||||||
processes [load-prefixes];
|
|
||||||
neighbor-changes;
|
|
||||||
receive {
|
|
||||||
parsed;
|
|
||||||
update;
|
|
||||||
}
|
|
||||||
send {
|
|
||||||
parsed;
|
|
||||||
update;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Шаг 3: Настройка Docker
|
### Шаг 3: Настройка Docker
|
||||||
@@ -150,18 +135,21 @@ services:
|
|||||||
- ./logs:/app/logs
|
- ./logs:/app/logs
|
||||||
environment:
|
environment:
|
||||||
- BGP_LOCAL_AS=65000
|
- BGP_LOCAL_AS=65000
|
||||||
- BGP_ROUTER_ID=192.168.1.100
|
- BGP_ROUTER_ID=192.168.100.99
|
||||||
- BGP_NEIGHBOR_IP=192.168.1.1
|
- BGP_NEIGHBOR_IP=192.168.0.254
|
||||||
- BGP_NEIGHBOR_AS=65001
|
- BGP_NEIGHBOR_AS=65001
|
||||||
networks:
|
networks:
|
||||||
- bgp-network
|
- bgp-network
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
privileged: true
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
bgp-network:
|
bgp-network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
ipam:
|
ipam:
|
||||||
config:
|
config:
|
||||||
- subnet: 192.168.1.0/24
|
- subnet: 192.168.100.0/24
|
||||||
```
|
```
|
||||||
|
|
||||||
### Шаг 4: Настройка RouterOS
|
### Шаг 4: Настройка RouterOS
|
||||||
@@ -171,15 +159,15 @@ networks:
|
|||||||
```routeros
|
```routeros
|
||||||
# Создание BGP экземпляра
|
# Создание BGP экземпляра
|
||||||
/routing bgp instance
|
/routing bgp instance
|
||||||
add name=bgp-server as=65001 router-id=192.168.1.1
|
add name=bgp-server as=65001 router-id=192.168.0.254
|
||||||
|
|
||||||
# Настройка BGP сети
|
# Настройка BGP сети
|
||||||
/routing bgp network
|
/routing bgp network
|
||||||
add network=192.168.1.0/24
|
add network=192.168.0.0/24
|
||||||
|
|
||||||
# Настройка BGP пира
|
# Настройка BGP пира
|
||||||
/routing bgp peer
|
/routing bgp peer
|
||||||
add name=bgp-server remote-address=192.168.1.100 remote-as=65000 instance=bgp-server
|
add name=bgp-server remote-address=192.168.100.99 remote-as=65000 instance=bgp-server
|
||||||
|
|
||||||
# Включение BGP
|
# Включение BGP
|
||||||
/routing bgp instance set bgp-server disabled=no
|
/routing bgp instance set bgp-server disabled=no
|
||||||
@@ -195,70 +183,130 @@ docker-compose up -d
|
|||||||
docker-compose logs -f bgp-server
|
docker-compose logs -f bgp-server
|
||||||
|
|
||||||
# Проверка статуса BGP сессии
|
# Проверка статуса BGP сессии
|
||||||
docker exec bgp-server exabgpcli show neighbor
|
docker exec bgp-server vtysh -c "show ip bgp summary"
|
||||||
|
|
||||||
|
# Проверка анонсированных маршрутов
|
||||||
|
docker exec bgp-server vtysh -c "show ip bgp"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Мониторинг и логирование
|
## Управление и мониторинг
|
||||||
|
|
||||||
### Просмотр логов
|
### Команды vtysh
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Все логи
|
# Подключение к FRR CLI
|
||||||
docker-compose logs bgp-server
|
docker exec -it bgp-server vtysh
|
||||||
|
|
||||||
# Логи в реальном времени
|
# Просмотр BGP соседей
|
||||||
docker-compose logs -f bgp-server
|
show ip bgp neighbors
|
||||||
|
|
||||||
# Логи за последние 100 строк
|
# Просмотр BGP маршрутов
|
||||||
docker-compose logs --tail=100 bgp-server
|
show ip bgp
|
||||||
|
|
||||||
|
# Просмотр статических маршрутов
|
||||||
|
show ip route
|
||||||
|
|
||||||
|
# Просмотр конфигурации
|
||||||
|
show running-config
|
||||||
```
|
```
|
||||||
|
|
||||||
### Проверка статуса
|
### Мониторинг через скрипты
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Статус контейнера
|
# Запуск мониторинга
|
||||||
docker-compose ps
|
docker exec bgp-server python3 /app/scripts/monitor.py
|
||||||
|
|
||||||
# Проверка BGP сессии
|
# Просмотр статуса
|
||||||
docker exec bgp-server exabgpcli show neighbor
|
docker exec bgp-server cat /app/logs/status.json
|
||||||
|
|
||||||
# Проверка загруженных префиксов
|
# Перезагрузка префиксов
|
||||||
docker exec bgp-server cat /app/logs/prefixes.log
|
docker exec bgp-server python3 /app/scripts/load_prefixes.py --test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Логи
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Логи FRR
|
||||||
|
docker exec bgp-server tail -f /app/logs/frr.log
|
||||||
|
|
||||||
|
# Логи загрузки префиксов
|
||||||
|
docker exec bgp-server tail -f /app/logs/prefixes.log
|
||||||
|
|
||||||
|
# Логи мониторинга
|
||||||
|
docker exec bgp-server tail -f /app/logs/monitor.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Диагностика
|
||||||
|
|
||||||
|
### Проверка статуса FRR
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Проверка процессов
|
||||||
|
docker exec bgp-server ps aux | grep -E "(zebra|bgpd)"
|
||||||
|
|
||||||
|
# Проверка версии FRR
|
||||||
|
docker exec bgp-server vtysh -c "show version"
|
||||||
|
|
||||||
|
# Проверка сетевых интерфейсов
|
||||||
|
docker exec bgp-server vtysh -c "show interface"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка BGP сессии
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Статус BGP соседей
|
||||||
|
docker exec bgp-server vtysh -c "show ip bgp summary"
|
||||||
|
|
||||||
|
# Детальная информация о соседе
|
||||||
|
docker exec bgp-server vtysh -c "show ip bgp neighbors 192.168.0.254"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка маршрутов
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Все BGP маршруты
|
||||||
|
docker exec bgp-server vtysh -c "show ip bgp"
|
||||||
|
|
||||||
|
# Статические маршруты
|
||||||
|
docker exec bgp-server vtysh -c "show ip route static"
|
||||||
|
|
||||||
|
# Таблица маршрутизации
|
||||||
|
docker exec bgp-server vtysh -c "show ip route"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Обновление префиксов
|
## Обновление префиксов
|
||||||
|
|
||||||
### Автоматическое обновление
|
### Автоматическое обновление
|
||||||
|
|
||||||
Сервер автоматически перезагружает префиксы при изменении файлов в папке `data/`.
|
Скрипт `load_prefixes.py` автоматически отслеживает изменения в файлах префиксов и обновляет маршруты в FRR.
|
||||||
|
|
||||||
### Ручное обновление
|
### Ручное обновление
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Перезапуск сервера
|
# Перезагрузка всех префиксов
|
||||||
docker-compose restart bgp-server
|
docker exec bgp-server python3 /app/scripts/load_prefixes.py
|
||||||
|
|
||||||
# Или перезагрузка только процесса загрузки префиксов
|
# Тестирование загрузки префиксов
|
||||||
docker exec bgp-server pkill -f load_prefixes.py
|
docker exec bgp-server python3 /app/scripts/load_prefixes.py --test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Устранение неполадок
|
## Устранение неполадок
|
||||||
|
|
||||||
### Проблема: BGP сессия не устанавливается
|
### Проблема: BGP сессия не устанавливается
|
||||||
|
|
||||||
1. Проверьте сетевое подключение:
|
1. Проверьте сетевую связность:
|
||||||
```powershell
|
```powershell
|
||||||
docker exec bgp-server ping 192.168.1.1
|
docker exec bgp-server ping 192.168.0.254
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Проверьте конфигурацию:
|
2. Проверьте конфигурацию BGP:
|
||||||
```powershell
|
```powershell
|
||||||
docker exec bgp-server cat /app/exabgp.conf
|
docker exec bgp-server vtysh -c "show running-config"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Проверьте логи:
|
3. Проверьте логи FRR:
|
||||||
```powershell
|
```powershell
|
||||||
docker-compose logs bgp-server
|
docker exec bgp-server tail -f /app/logs/frr.log
|
||||||
```
|
```
|
||||||
|
|
||||||
### Проблема: Префиксы не анонсируются
|
### Проблема: Префиксы не анонсируются
|
||||||
@@ -268,106 +316,44 @@ docker-compose logs bgp-server
|
|||||||
docker exec bgp-server cat /app/data/prefixes.txt
|
docker exec bgp-server cat /app/data/prefixes.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Проверьте логи процесса загрузки:
|
2. Проверьте логи загрузки префиксов:
|
||||||
```powershell
|
```powershell
|
||||||
docker exec bgp-server cat /app/logs/prefixes.log
|
docker exec bgp-server tail -f /app/logs/prefixes.log
|
||||||
```
|
```
|
||||||
|
|
||||||
### Проблема: Docker контейнер не запускается
|
3. Проверьте статические маршруты:
|
||||||
|
|
||||||
1. Проверьте порты:
|
|
||||||
```powershell
|
```powershell
|
||||||
netstat -an | findstr :179
|
docker exec bgp-server vtysh -c "show ip route static"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Проверьте Docker логи:
|
### Проблема: Высокое потребление ресурсов
|
||||||
|
|
||||||
|
1. Проверьте использование памяти:
|
||||||
```powershell
|
```powershell
|
||||||
docker-compose logs
|
docker exec bgp-server free -h
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Проверьте количество маршрутов:
|
||||||
|
```powershell
|
||||||
|
docker exec bgp-server vtysh -c "show ip bgp summary"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Безопасность
|
## Безопасность
|
||||||
|
|
||||||
### Рекомендации по безопасности
|
- FRR работает в привилегированном контейнере для доступа к сетевым интерфейсам
|
||||||
|
- Используйте route-maps для фильтрации трафика
|
||||||
1. **Ограничение доступа**: Настройте firewall для ограничения доступа к порту 179
|
- Ограничьте доступ к vtysh CLI
|
||||||
2. **Аутентификация**: Используйте MD5 аутентификацию в BGP
|
- Регулярно обновляйте образ Docker
|
||||||
3. **Логирование**: Включите детальное логирование для аудита
|
|
||||||
4. **Обновления**: Регулярно обновляйте Docker образы
|
|
||||||
|
|
||||||
### Настройка MD5 аутентификации
|
|
||||||
|
|
||||||
В `exabgp.conf` добавьте:
|
|
||||||
```ini
|
|
||||||
neighbor 192.168.1.1 {
|
|
||||||
# ... существующие настройки ...
|
|
||||||
md5-password "your-secure-password";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
В RouterOS:
|
|
||||||
```routeros
|
|
||||||
/routing bgp peer set bgp-server password="your-secure-password"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Производительность
|
## Производительность
|
||||||
|
|
||||||
### Оптимизация для больших объемов префиксов
|
- FRR оптимизирован для обработки большого количества маршрутов
|
||||||
|
- Поддерживает до 1 миллиона маршрутов
|
||||||
|
- Эффективное использование памяти
|
||||||
|
- Быстрое время сходимости
|
||||||
|
|
||||||
1. **Кэширование**: Используйте Redis для кэширования префиксов
|
## Поддержка
|
||||||
2. **База данных**: Для больших объемов используйте PostgreSQL
|
|
||||||
3. **Мониторинг**: Настройте метрики производительности
|
|
||||||
|
|
||||||
### Мониторинг ресурсов
|
- Документация FRR: https://frrouting.org/
|
||||||
|
- GitHub проекта: https://github.com/FRRouting/frr
|
||||||
```powershell
|
- Сообщество: https://lists.frrouting.org/
|
||||||
# Использование ресурсов контейнера
|
|
||||||
docker stats bgp-server
|
|
||||||
|
|
||||||
# Проверка дискового пространства
|
|
||||||
docker system df
|
|
||||||
```
|
|
||||||
|
|
||||||
## Альтернативные решения
|
|
||||||
|
|
||||||
### 1. Bird BGP Daemon
|
|
||||||
|
|
||||||
Если ExaBGP не подходит, можно использовать Bird:
|
|
||||||
|
|
||||||
```dockerfile
|
|
||||||
FROM ubuntu:20.04
|
|
||||||
RUN apt-get update && apt-get install -y bird
|
|
||||||
COPY bird.conf /etc/bird/bird.conf
|
|
||||||
CMD ["bird", "-f", "-d"]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. FRRouting (FRR)
|
|
||||||
|
|
||||||
Другой популярный вариант:
|
|
||||||
|
|
||||||
```dockerfile
|
|
||||||
FROM ubuntu:20.04
|
|
||||||
RUN apt-get update && apt-get install -y frr
|
|
||||||
COPY frr.conf /etc/frr/frr.conf
|
|
||||||
CMD ["/usr/lib/frr/frrinit", "start"]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. GoBGP
|
|
||||||
|
|
||||||
Для более современного подхода:
|
|
||||||
|
|
||||||
```dockerfile
|
|
||||||
FROM golang:1.19-alpine
|
|
||||||
RUN go install github.com/osrg/gobgp/cmd/gobgpd@latest
|
|
||||||
COPY gobgpd.conf /etc/gobgpd.conf
|
|
||||||
CMD ["gobgpd", "-f", "/etc/gobgpd.conf"]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Заключение
|
|
||||||
|
|
||||||
Этот BGP сервер предоставляет простое и эффективное решение для раздачи префиксов из текстовых файлов. Docker контейнеризация обеспечивает легкое развертывание и управление, а интеграция с RouterOS позволяет быстро настроить клиентскую сторону.
|
|
||||||
|
|
||||||
Для продакшн использования рекомендуется:
|
|
||||||
- Настроить мониторинг и алертинг
|
|
||||||
- Добавить резервное копирование конфигурации
|
|
||||||
- Реализовать автоматическое восстановление после сбоев
|
|
||||||
- Настроить централизованное логирование
|
|
||||||
+5
-10
@@ -1,14 +1,9 @@
|
|||||||
# Основные префиксы для анонсирования
|
# Основные префиксы
|
||||||
# Формат: IP/маска (например: 192.168.1.0/24)
|
192.168.100.0/24
|
||||||
|
192.168.101.0/24
|
||||||
# Приватные сети
|
|
||||||
192.168.1.0/24
|
|
||||||
192.168.2.0/24
|
|
||||||
192.168.10.0/24
|
|
||||||
10.0.0.0/8
|
10.0.0.0/8
|
||||||
172.16.0.0/12
|
172.16.0.0/12
|
||||||
|
|
||||||
# Дополнительные сети
|
# Дополнительные префиксы
|
||||||
203.0.113.0/24
|
203.0.113.0/24
|
||||||
198.51.100.0/24
|
198.51.100.0/24
|
||||||
203.0.113.1/32
|
|
||||||
+7
-3
@@ -12,17 +12,21 @@ services:
|
|||||||
- ./logs:/app/logs
|
- ./logs:/app/logs
|
||||||
environment:
|
environment:
|
||||||
- BGP_LOCAL_AS=65000
|
- BGP_LOCAL_AS=65000
|
||||||
- BGP_ROUTER_ID=192.168.1.100
|
- BGP_ROUTER_ID=192.168.100.99
|
||||||
- BGP_NEIGHBOR_IP=192.168.1.1
|
- BGP_NEIGHBOR_IP=192.168.0.254
|
||||||
- BGP_NEIGHBOR_AS=65001
|
- BGP_NEIGHBOR_AS=65001
|
||||||
|
- BGP_HOLD_TIME=90
|
||||||
|
- BGP_KEEPALIVE=30
|
||||||
|
- BGP_LOG_LEVEL=info
|
||||||
networks:
|
networks:
|
||||||
- bgp-network
|
- bgp-network
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN # Для работы с сетевыми интерфейсами
|
- NET_ADMIN # Для работы с сетевыми интерфейсами
|
||||||
|
privileged: true # Для работы с FRR демонами
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
bgp-network:
|
bgp-network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
ipam:
|
ipam:
|
||||||
config:
|
config:
|
||||||
- subnet: 192.168.1.0/24
|
- subnet: 192.168.100.0/24
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# Этот файл будет перезаписан динамически сгенерированной конфигурацией
|
|
||||||
# из переменных окружения при запуске контейнера.
|
|
||||||
#
|
|
||||||
# Для просмотра текущей конфигурации используйте:
|
|
||||||
# docker exec bgp-server cat /app/exabgp.conf
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# FRR Configuration File
|
||||||
|
# Этот файл будет перезаписан динамически сгенерированной конфигурацией
|
||||||
|
# из переменных окружения при запуске контейнера.
|
||||||
|
#
|
||||||
|
# Для просмотра текущей конфигурации используйте:
|
||||||
|
# docker exec bgp-server cat /etc/frr/frr.conf
|
||||||
|
|
||||||
|
# Глобальные настройки
|
||||||
|
frr version 8.5.2
|
||||||
|
frr defaults traditional
|
||||||
|
hostname bgp-server
|
||||||
|
log syslog info
|
||||||
|
service integrated-vtysh-config
|
||||||
|
|
||||||
|
# Настройки BGP
|
||||||
|
router bgp 65000
|
||||||
|
bgp router-id 192.168.100.99
|
||||||
|
bgp log-neighbor-changes
|
||||||
|
neighbor 192.168.0.254 remote-as 65001
|
||||||
|
neighbor 192.168.0.254 description RouterOS Client
|
||||||
|
neighbor 192.168.0.254 timers 30 90
|
||||||
|
neighbor 192.168.0.254 capability graceful-restart
|
||||||
|
neighbor 192.168.0.254 soft-reconfiguration inbound
|
||||||
|
!
|
||||||
|
address-family ipv4 unicast
|
||||||
|
neighbor 192.168.0.254 activate
|
||||||
|
neighbor 192.168.0.254 route-map OUTBOUND out
|
||||||
|
neighbor 192.168.0.254 route-map INBOUND in
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
|
||||||
|
# Route maps для фильтрации
|
||||||
|
route-map OUTBOUND permit 10
|
||||||
|
description Allow all outbound routes
|
||||||
|
!
|
||||||
|
route-map INBOUND deny 10
|
||||||
|
description Deny all inbound routes (receive-only mode)
|
||||||
|
!
|
||||||
|
|
||||||
|
# Настройки интерфейса
|
||||||
|
interface eth0
|
||||||
|
ip address 192.168.100.99/24
|
||||||
|
!
|
||||||
|
|
||||||
|
# Статические маршруты (будут добавлены динамически)
|
||||||
|
# ip route 192.168.1.0/24 Null0
|
||||||
+1
-4
@@ -1,4 +1 @@
|
|||||||
exabgp>=4.2.0
|
watchdog>=2.1.0
|
||||||
ipaddress
|
|
||||||
pathlib
|
|
||||||
typing
|
|
||||||
+66
-56
@@ -1,85 +1,95 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Скрипт для генерации конфигурации ExaBGP из переменных окружения
|
Скрипт для генерации конфигурации FRR из переменных окружения
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
def get_env_var(name: str, default: str) -> str:
|
def get_env_var(name, default):
|
||||||
"""Получение переменной окружения с значением по умолчанию"""
|
"""Получение переменной окружения с значением по умолчанию"""
|
||||||
return os.environ.get(name, default)
|
return os.environ.get(name, default)
|
||||||
|
|
||||||
def generate_exabgp_config():
|
def generate_frr_config():
|
||||||
"""Генерация конфигурации ExaBGP"""
|
"""Генерация конфигурации FRR"""
|
||||||
|
|
||||||
# Получение переменных окружения
|
# Получение настроек из переменных окружения
|
||||||
local_as = get_env_var('BGP_LOCAL_AS', '65000')
|
local_as = get_env_var('BGP_LOCAL_AS', '65000')
|
||||||
router_id = get_env_var('BGP_ROUTER_ID', '192.168.1.100')
|
router_id = get_env_var('BGP_ROUTER_ID', '192.168.100.99')
|
||||||
neighbor_ip = get_env_var('BGP_NEIGHBOR_IP', '192.168.1.1')
|
neighbor_ip = get_env_var('BGP_NEIGHBOR_IP', '192.168.0.254')
|
||||||
neighbor_as = get_env_var('BGP_NEIGHBOR_AS', '65001')
|
neighbor_as = get_env_var('BGP_NEIGHBOR_AS', '65001')
|
||||||
hold_time = get_env_var('BGP_HOLD_TIME', '90')
|
hold_time = get_env_var('BGP_HOLD_TIME', '90')
|
||||||
keepalive = get_env_var('BGP_KEEPALIVE', '30')
|
keepalive = get_env_var('BGP_KEEPALIVE', '30')
|
||||||
md5_password = get_env_var('BGP_MD5_PASSWORD', '')
|
log_level = get_env_var('BGP_LOG_LEVEL', 'info')
|
||||||
log_level = get_env_var('BGP_LOG_LEVEL', 'INFO')
|
|
||||||
|
|
||||||
# Генерация конфигурации
|
# Конфигурация FRR
|
||||||
config = f"""# Автоматически сгенерированная конфигурация ExaBGP
|
config = f"""# FRR Configuration File
|
||||||
process load-prefixes {{
|
# Generated automatically from environment variables
|
||||||
run /app/scripts/load_prefixes.py;
|
|
||||||
encoder json;
|
# Глобальные настройки
|
||||||
}}
|
frr version 8.5.2
|
||||||
|
frr defaults traditional
|
||||||
|
hostname bgp-server
|
||||||
|
log syslog {log_level}
|
||||||
|
service integrated-vtysh-config
|
||||||
|
|
||||||
# Настройки BGP
|
# Настройки BGP
|
||||||
neighbor {neighbor_ip} {{
|
router bgp {local_as}
|
||||||
router-id {router_id};
|
bgp router-id {router_id}
|
||||||
local-address {router_id};
|
bgp log-neighbor-changes
|
||||||
local-as {local_as};
|
neighbor {neighbor_ip} remote-as {neighbor_as}
|
||||||
peer-as {neighbor_as};
|
neighbor {neighbor_ip} description RouterOS Client
|
||||||
|
neighbor {neighbor_ip} timers {keepalive} {hold_time}
|
||||||
# Только исходящие анонсы (не принимаем префиксы)
|
neighbor {neighbor_ip} capability graceful-restart
|
||||||
capability {{
|
neighbor {neighbor_ip} soft-reconfiguration inbound
|
||||||
graceful-restart;
|
!
|
||||||
}}
|
address-family ipv4 unicast
|
||||||
|
neighbor {neighbor_ip} activate
|
||||||
# Настройки сессии
|
neighbor {neighbor_ip} route-map OUTBOUND out
|
||||||
hold-time {hold_time};
|
neighbor {neighbor_ip} route-map INBOUND in
|
||||||
"""
|
exit-address-family
|
||||||
|
!
|
||||||
|
|
||||||
# Добавление MD5 пароля если указан
|
# Route maps для фильтрации
|
||||||
if md5_password:
|
route-map OUTBOUND permit 10
|
||||||
config += f" md5-password \"{md5_password}\";\n"
|
description Allow all outbound routes
|
||||||
|
!
|
||||||
config += f"""
|
route-map INBOUND deny 10
|
||||||
# Логирование
|
description Deny all inbound routes (receive-only mode)
|
||||||
api {{
|
!
|
||||||
processes [load-prefixes];
|
|
||||||
neighbor-changes;
|
# Настройки интерфейса
|
||||||
receive {{
|
interface eth0
|
||||||
parsed;
|
ip address {router_id}/24
|
||||||
update;
|
!
|
||||||
}}
|
|
||||||
send {{
|
# Статические маршруты (будут добавлены динамически)
|
||||||
parsed;
|
# ip route 192.168.1.0/24 Null0
|
||||||
update;
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Основная функция"""
|
"""Основная функция"""
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == '--print':
|
try:
|
||||||
# Вывод конфигурации в stdout
|
# Генерация конфигурации
|
||||||
print(generate_exabgp_config())
|
config = generate_frr_config()
|
||||||
else:
|
|
||||||
# Запись конфигурации в файл
|
# Запись в файл
|
||||||
config = generate_exabgp_config()
|
config_path = Path('/etc/frr/frr.conf')
|
||||||
with open('/app/exabgp.conf', 'w') as f:
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
with open(config_path, 'w') as f:
|
||||||
f.write(config)
|
f.write(config)
|
||||||
print("Configuration generated successfully")
|
|
||||||
|
print(f"FRR configuration generated: {config_path}")
|
||||||
|
print("Configuration content:")
|
||||||
|
print(config)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error generating FRR configuration: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
+108
-53
@@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Скрипт для загрузки префиксов из текстовых файлов в ExaBGP
|
Скрипт для загрузки префиксов из текстовых файлов в FRR
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import json
|
import subprocess
|
||||||
import ipaddress
|
import ipaddress
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Set
|
from typing import List, Set
|
||||||
@@ -21,7 +21,7 @@ def log_message(message: str):
|
|||||||
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
log_entry = f"[{timestamp}] {message}"
|
log_entry = f"[{timestamp}] {message}"
|
||||||
|
|
||||||
# Вывод в stdout для ExaBGP
|
# Вывод в stdout
|
||||||
print(log_entry, flush=True)
|
print(log_entry, flush=True)
|
||||||
|
|
||||||
# Запись в файл (с обработкой ошибок)
|
# Запись в файл (с обработкой ошибок)
|
||||||
@@ -84,44 +84,84 @@ def load_all_prefixes() -> Set[str]:
|
|||||||
|
|
||||||
return all_prefixes
|
return all_prefixes
|
||||||
|
|
||||||
|
def run_vtysh_command(command: str) -> bool:
|
||||||
|
"""Выполнение команды через vtysh"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['vtysh', '-c', command],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
log_error(f"vtysh command failed: {command}")
|
||||||
|
log_error(f"stderr: {result.stderr}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
log_error(f"vtysh command timeout: {command}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
log_error(f"Error running vtysh command '{command}': {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def announce_prefixes(prefixes: Set[str]):
|
def announce_prefixes(prefixes: Set[str]):
|
||||||
"""Анонсирование префиксов через ExaBGP API"""
|
"""Анонсирование префиксов через FRR"""
|
||||||
# Получение настроек из переменных окружения
|
log_message(f"Announcing {len(prefixes)} prefixes...")
|
||||||
neighbor_ip = os.environ.get('BGP_NEIGHBOR_IP', '192.168.1.1')
|
|
||||||
local_as = int(os.environ.get('BGP_LOCAL_AS', '65000'))
|
|
||||||
router_id = os.environ.get('BGP_ROUTER_ID', '192.168.1.100')
|
|
||||||
|
|
||||||
for prefix in sorted(prefixes):
|
for prefix in sorted(prefixes):
|
||||||
# Формат команды для ExaBGP (упрощенный)
|
# Добавление статического маршрута в FRR
|
||||||
command = {
|
command = f"ip route {prefix} Null0"
|
||||||
"command": "announce route",
|
if run_vtysh_command(command):
|
||||||
"neighbor": neighbor_ip,
|
log_message(f"Announced prefix: {prefix}")
|
||||||
"attribute": {
|
else:
|
||||||
"origin": "igp",
|
log_error(f"Failed to announce prefix: {prefix}")
|
||||||
"as-path": [local_as],
|
|
||||||
"next-hop": router_id
|
|
||||||
},
|
|
||||||
"nlri": prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
# Отправка команды в ExaBGP
|
|
||||||
print(json.dumps(command), flush=True)
|
|
||||||
log_message(f"Announced prefix: {prefix}")
|
|
||||||
|
|
||||||
def withdraw_prefixes(prefixes: Set[str]):
|
def withdraw_prefixes(prefixes: Set[str]):
|
||||||
"""Отзыв префиксов через ExaBGP API"""
|
"""Отзыв префиксов через FRR"""
|
||||||
# Получение настроек из переменных окружения
|
log_message(f"Withdrawing {len(prefixes)} prefixes...")
|
||||||
neighbor_ip = os.environ.get('BGP_NEIGHBOR_IP', '192.168.1.1')
|
|
||||||
|
|
||||||
for prefix in sorted(prefixes):
|
for prefix in sorted(prefixes):
|
||||||
command = {
|
# Удаление статического маршрута из FRR
|
||||||
"command": "withdraw route",
|
command = f"no ip route {prefix} Null0"
|
||||||
"neighbor": neighbor_ip,
|
if run_vtysh_command(command):
|
||||||
"nlri": prefix
|
log_message(f"Withdrew prefix: {prefix}")
|
||||||
}
|
else:
|
||||||
|
log_error(f"Failed to withdraw prefix: {prefix}")
|
||||||
|
|
||||||
|
def check_frr_status() -> bool:
|
||||||
|
"""Проверка статуса FRR"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['vtysh', '-c', 'show version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def wait_for_frr():
|
||||||
|
"""Ожидание запуска FRR"""
|
||||||
|
log_message("Waiting for FRR to start...")
|
||||||
|
max_attempts = 30
|
||||||
|
attempt = 0
|
||||||
|
|
||||||
|
while attempt < max_attempts:
|
||||||
|
if check_frr_status():
|
||||||
|
log_message("FRR is ready")
|
||||||
|
return True
|
||||||
|
|
||||||
print(json.dumps(command), flush=True)
|
attempt += 1
|
||||||
log_message(f"Withdrew prefix: {prefix}")
|
time.sleep(2)
|
||||||
|
if attempt % 5 == 0:
|
||||||
|
log_message(f"Still waiting for FRR... (attempt {attempt}/{max_attempts})")
|
||||||
|
|
||||||
|
log_error("FRR did not start within expected time")
|
||||||
|
return False
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Основная функция"""
|
"""Основная функция"""
|
||||||
@@ -138,8 +178,21 @@ def main():
|
|||||||
print(f"Found {len(prefixes)} prefixes: {list(prefixes)}")
|
print(f"Found {len(prefixes)} prefixes: {list(prefixes)}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Проверка режима отзыва
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == '--withdraw':
|
||||||
|
log_message("Withdraw mode: loading and withdrawing all prefixes...")
|
||||||
|
prefixes = load_all_prefixes()
|
||||||
|
if wait_for_frr():
|
||||||
|
withdraw_prefixes(prefixes)
|
||||||
|
return
|
||||||
|
|
||||||
log_message("Starting prefix loader...")
|
log_message("Starting prefix loader...")
|
||||||
|
|
||||||
|
# Ожидание запуска FRR
|
||||||
|
if not wait_for_frr():
|
||||||
|
log_error("Cannot proceed without FRR")
|
||||||
|
return
|
||||||
|
|
||||||
# Загрузка префиксов
|
# Загрузка префиксов
|
||||||
prefixes = load_all_prefixes()
|
prefixes = load_all_prefixes()
|
||||||
log_message(f"Total prefixes loaded: {len(prefixes)}")
|
log_message(f"Total prefixes loaded: {len(prefixes)}")
|
||||||
@@ -152,29 +205,31 @@ def main():
|
|||||||
announce_prefixes(prefixes)
|
announce_prefixes(prefixes)
|
||||||
log_message("All prefixes announced successfully")
|
log_message("All prefixes announced successfully")
|
||||||
|
|
||||||
# Ожидание команд от ExaBGP
|
# Мониторинг изменений в файлах
|
||||||
|
log_message("Starting file monitoring...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
line = sys.stdin.readline()
|
time.sleep(30) # Проверка каждые 30 секунд
|
||||||
if not line:
|
|
||||||
break
|
|
||||||
|
|
||||||
line = line.strip()
|
# Перезагрузка префиксов
|
||||||
if not line: # Пропуск пустых строк
|
new_prefixes = load_all_prefixes()
|
||||||
continue
|
|
||||||
|
# Определение изменений
|
||||||
# Обработка команд от ExaBGP
|
added = new_prefixes - prefixes
|
||||||
try:
|
removed = prefixes - new_prefixes
|
||||||
data = json.loads(line)
|
|
||||||
log_message(f"Received command: {data}")
|
if added:
|
||||||
except json.JSONDecodeError:
|
log_message(f"Adding {len(added)} new prefixes: {list(added)}")
|
||||||
# Не все сообщения от ExaBGP являются JSON
|
announce_prefixes(added)
|
||||||
# Это может быть обычное текстовое сообщение
|
|
||||||
if line.startswith('[') and line.endswith(']'):
|
if removed:
|
||||||
# Это может быть лог сообщение от ExaBGP
|
log_message(f"Removing {len(removed)} prefixes: {list(removed)}")
|
||||||
log_message(f"Received log message: {line}")
|
withdraw_prefixes(removed)
|
||||||
else:
|
|
||||||
log_message(f"Received non-JSON message: {line}")
|
if added or removed:
|
||||||
|
prefixes = new_prefixes
|
||||||
|
log_message(f"Updated prefix count: {len(prefixes)}")
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
log_message("Received interrupt signal")
|
log_message("Received interrupt signal")
|
||||||
|
|||||||
+174
-73
@@ -1,93 +1,194 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Скрипт мониторинга BGP сервера
|
Скрипт мониторинга FRR BGP сервера
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
import time
|
from pathlib import Path
|
||||||
import sys
|
from typing import Dict, Any
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def run_command(cmd):
|
# Настройки
|
||||||
"""Выполнение команды и возврат результата"""
|
LOG_FILE = "/app/logs/monitor.log"
|
||||||
|
STATUS_FILE = "/app/logs/status.json"
|
||||||
|
|
||||||
|
def log_message(message: str):
|
||||||
|
"""Запись сообщения в лог"""
|
||||||
|
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
log_entry = f"[{timestamp}] {message}"
|
||||||
|
|
||||||
|
print(log_entry, flush=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
with open(LOG_FILE, "a") as f:
|
||||||
return result.returncode == 0, result.stdout, result.stderr
|
f.write(log_entry + "\n")
|
||||||
|
except (PermissionError, OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run_vtysh_command(command: str) -> str:
|
||||||
|
"""Выполнение команды через vtysh"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['vtysh', '-c', command],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
return result.stdout.strip()
|
||||||
|
else:
|
||||||
|
log_message(f"vtysh command failed: {command}")
|
||||||
|
return ""
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, "", str(e)
|
log_message(f"Error running vtysh command '{command}': {e}")
|
||||||
|
return ""
|
||||||
|
|
||||||
def check_container_status():
|
def get_bgp_summary() -> Dict[str, Any]:
|
||||||
"""Проверка статуса Docker контейнера"""
|
"""Получение сводки BGP"""
|
||||||
success, output, error = run_command("docker ps --filter name=bgp-server --format '{{.Status}}'")
|
summary = run_vtysh_command("show ip bgp summary")
|
||||||
if success and output.strip():
|
|
||||||
return True, output.strip()
|
if not summary:
|
||||||
return False, "Container not running"
|
return {"error": "Could not get BGP summary"}
|
||||||
|
|
||||||
|
# Парсинг вывода BGP summary
|
||||||
|
lines = summary.split('\n')
|
||||||
|
neighbors = []
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if 'BGP neighbor' in line and 'remote AS' in line:
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) >= 8:
|
||||||
|
neighbor = {
|
||||||
|
"ip": parts[2].rstrip(','),
|
||||||
|
"remote_as": parts[7],
|
||||||
|
"state": parts[-1] if len(parts) > 7 else "Unknown"
|
||||||
|
}
|
||||||
|
neighbors.append(neighbor)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"neighbors": neighbors,
|
||||||
|
"raw_output": summary
|
||||||
|
}
|
||||||
|
|
||||||
def check_bgp_session():
|
def get_bgp_routes() -> Dict[str, Any]:
|
||||||
"""Проверка BGP сессии"""
|
"""Получение BGP маршрутов"""
|
||||||
success, output, error = run_command("docker exec bgp-server exabgpcli show neighbor")
|
routes = run_vtysh_command("show ip bgp")
|
||||||
if success:
|
|
||||||
return True, output
|
if not routes:
|
||||||
return False, "Cannot check BGP session"
|
return {"error": "Could not get BGP routes"}
|
||||||
|
|
||||||
|
# Подсчет маршрутов
|
||||||
|
route_count = 0
|
||||||
|
for line in routes.split('\n'):
|
||||||
|
if line.strip() and not line.startswith('BGP table') and not line.startswith('*>'):
|
||||||
|
if any(char.isdigit() for char in line):
|
||||||
|
route_count += 1
|
||||||
|
|
||||||
|
return {
|
||||||
|
"route_count": route_count,
|
||||||
|
"raw_output": routes
|
||||||
|
}
|
||||||
|
|
||||||
def check_prefixes():
|
def get_frr_status() -> Dict[str, Any]:
|
||||||
"""Проверка загруженных префиксов"""
|
"""Получение статуса FRR"""
|
||||||
|
version = run_vtysh_command("show version")
|
||||||
|
|
||||||
|
if not version:
|
||||||
|
return {"error": "Could not get FRR version"}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"version": version.split('\n')[0] if version else "Unknown",
|
||||||
|
"raw_output": version
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_system_status() -> Dict[str, Any]:
|
||||||
|
"""Получение системного статуса"""
|
||||||
try:
|
try:
|
||||||
with open("/app/logs/prefixes.log", "r") as f:
|
# Проверка процессов FRR
|
||||||
lines = f.readlines()
|
zebra_running = subprocess.run(['pgrep', 'zebra'], capture_output=True).returncode == 0
|
||||||
return True, f"Found {len(lines)} log entries"
|
bgpd_running = subprocess.run(['pgrep', 'bgpd'], capture_output=True).returncode == 0
|
||||||
except FileNotFoundError:
|
|
||||||
return False, "Prefix log not found"
|
# Проверка использования памяти
|
||||||
|
memory_info = {}
|
||||||
|
try:
|
||||||
|
with open('/proc/meminfo', 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
if 'MemTotal:' in line:
|
||||||
|
memory_info['total'] = line.split()[1]
|
||||||
|
elif 'MemAvailable:' in line:
|
||||||
|
memory_info['available'] = line.split()[1]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return {
|
||||||
|
"zebra_running": zebra_running,
|
||||||
|
"bgpd_running": bgpd_running,
|
||||||
|
"memory": memory_info
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {"error": f"Could not get system status: {e}"}
|
||||||
|
|
||||||
def check_network_connectivity():
|
def save_status(status: Dict[str, Any]):
|
||||||
"""Проверка сетевого подключения"""
|
"""Сохранение статуса в файл"""
|
||||||
success, output, error = run_command("docker exec bgp-server ping -c 1 192.168.1.1")
|
try:
|
||||||
return success, "Network connectivity OK" if success else "Network connectivity failed"
|
with open(STATUS_FILE, 'w') as f:
|
||||||
|
json.dump(status, f, indent=2)
|
||||||
|
except Exception as e:
|
||||||
|
log_message(f"Error saving status: {e}")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Основная функция мониторинга"""
|
"""Основная функция"""
|
||||||
print("=== BGP Server Monitoring ===")
|
log_message("Starting FRR monitor...")
|
||||||
print(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Проверка контейнера
|
# Создание директории для логов
|
||||||
print("1. Container Status:")
|
try:
|
||||||
status, details = check_container_status()
|
os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
|
||||||
print(f" Status: {'✓ Running' if status else '✗ Stopped'}")
|
os.makedirs(os.path.dirname(STATUS_FILE), exist_ok=True)
|
||||||
print(f" Details: {details}")
|
except (PermissionError, OSError):
|
||||||
print()
|
pass
|
||||||
|
|
||||||
# Проверка сети
|
while True:
|
||||||
print("2. Network Connectivity:")
|
try:
|
||||||
net_ok, net_details = check_network_connectivity()
|
# Сбор статуса
|
||||||
print(f" Status: {'✓ OK' if net_ok else '✗ Failed'}")
|
status = {
|
||||||
print(f" Details: {net_details}")
|
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
print()
|
"frr": get_frr_status(),
|
||||||
|
"bgp_summary": get_bgp_summary(),
|
||||||
# Проверка BGP сессии
|
"bgp_routes": get_bgp_routes(),
|
||||||
print("3. BGP Session:")
|
"system": get_system_status()
|
||||||
bgp_ok, bgp_details = check_bgp_session()
|
}
|
||||||
print(f" Status: {'✓ Active' if bgp_ok else '✗ Inactive'}")
|
|
||||||
if bgp_ok:
|
# Сохранение статуса
|
||||||
print(f" Details: {bgp_details[:200]}...")
|
save_status(status)
|
||||||
else:
|
|
||||||
print(f" Details: {bgp_details}")
|
# Логирование важной информации
|
||||||
print()
|
bgp_summary = status["bgp_summary"]
|
||||||
|
if "neighbors" in bgp_summary:
|
||||||
# Проверка префиксов
|
for neighbor in bgp_summary["neighbors"]:
|
||||||
print("4. Prefixes:")
|
log_message(f"BGP neighbor {neighbor['ip']} (AS {neighbor['remote_as']}): {neighbor['state']}")
|
||||||
prefix_ok, prefix_details = check_prefixes()
|
|
||||||
print(f" Status: {'✓ Loaded' if prefix_ok else '✗ Not loaded'}")
|
bgp_routes = status["bgp_routes"]
|
||||||
print(f" Details: {prefix_details}")
|
if "route_count" in bgp_routes:
|
||||||
print()
|
log_message(f"BGP routes: {bgp_routes['route_count']}")
|
||||||
|
|
||||||
# Общий статус
|
system = status["system"]
|
||||||
overall_status = status and net_ok and bgp_ok and prefix_ok
|
if "zebra_running" in system and "bgpd_running" in system:
|
||||||
print("=== Overall Status ===")
|
if not system["zebra_running"] or not system["bgpd_running"]:
|
||||||
print(f"Status: {'✓ HEALTHY' if overall_status else '✗ UNHEALTHY'}")
|
log_message("WARNING: FRR daemons not running properly")
|
||||||
|
|
||||||
return 0 if overall_status else 1
|
# Ожидание перед следующей проверкой
|
||||||
|
time.sleep(60) # Проверка каждую минуту
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
log_message("Monitor stopped by user")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
log_message(f"Error in monitor loop: {e}")
|
||||||
|
time.sleep(30) # Короткая пауза при ошибке
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main())
|
main()
|
||||||
+177
-75
@@ -1,103 +1,205 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Скрипт для перезагрузки префиксов без перезапуска BGP сервера
|
Скрипт для перезагрузки префиксов в FRR
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
import subprocess
|
||||||
|
import ipaddress
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Set
|
||||||
|
|
||||||
def run_command(cmd):
|
# Настройки
|
||||||
"""Выполнение команды"""
|
DATA_DIR = "/app/data"
|
||||||
|
LOG_FILE = "/app/logs/reload.log"
|
||||||
|
PREFIX_FILES = ["prefixes.txt", "additional_prefixes.txt"]
|
||||||
|
|
||||||
|
def log_message(message: str):
|
||||||
|
"""Запись сообщения в лог"""
|
||||||
|
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
log_entry = f"[{timestamp}] {message}"
|
||||||
|
|
||||||
|
print(log_entry, flush=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
with open(LOG_FILE, "a") as f:
|
||||||
return result.returncode == 0, result.stdout, result.stderr
|
f.write(log_entry + "\n")
|
||||||
except Exception as e:
|
except (PermissionError, OSError):
|
||||||
return False, "", str(e)
|
pass
|
||||||
|
|
||||||
def reload_prefixes():
|
def validate_prefix(prefix: str) -> bool:
|
||||||
"""Перезагрузка префиксов"""
|
"""Валидация IP префикса"""
|
||||||
print("Reloading prefixes...")
|
try:
|
||||||
|
ipaddress.ip_network(prefix, strict=False)
|
||||||
# Остановка процесса загрузки префиксов
|
|
||||||
print("Stopping prefix loader process...")
|
|
||||||
success, output, error = run_command("docker exec bgp-server pkill -f load_prefixes.py")
|
|
||||||
|
|
||||||
if not success:
|
|
||||||
print("Warning: Could not stop prefix loader process")
|
|
||||||
|
|
||||||
# Ожидание завершения процесса
|
|
||||||
time.sleep(2)
|
|
||||||
|
|
||||||
# Проверка что процесс остановлен
|
|
||||||
success, output, error = run_command("docker exec bgp-server pgrep -f load_prefixes.py")
|
|
||||||
if success:
|
|
||||||
print("Error: Prefix loader process is still running")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print("Prefix loader process stopped successfully")
|
|
||||||
|
|
||||||
# Запуск нового процесса загрузки префиксов
|
|
||||||
print("Starting new prefix loader process...")
|
|
||||||
success, output, error = run_command("docker exec bgp-server /app/scripts/load_prefixes.py &")
|
|
||||||
|
|
||||||
if success:
|
|
||||||
print("Prefix loader process started successfully")
|
|
||||||
return True
|
return True
|
||||||
else:
|
except ValueError:
|
||||||
print(f"Error starting prefix loader: {error}")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_status():
|
def load_prefixes_from_file(file_path: Path) -> Set[str]:
|
||||||
"""Проверка статуса после перезагрузки"""
|
"""Загрузка префиксов из файла"""
|
||||||
print("Checking status...")
|
prefixes = set()
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
# Проверка процесса
|
if not file_path.exists():
|
||||||
success, output, error = run_command("docker exec bgp-server pgrep -f load_prefixes.py")
|
log_message(f"File not found: {file_path}")
|
||||||
if success:
|
return prefixes
|
||||||
print("✓ Prefix loader process is running")
|
|
||||||
else:
|
try:
|
||||||
print("✗ Prefix loader process is not running")
|
with open(file_path, 'r') as f:
|
||||||
|
for line_num, line in enumerate(f, 1):
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# Пропуск пустых строк и комментариев
|
||||||
|
if not line or line.startswith('#'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Валидация префикса
|
||||||
|
if validate_prefix(line):
|
||||||
|
prefixes.add(line)
|
||||||
|
else:
|
||||||
|
log_message(f"Invalid prefix in {file_path}:{line_num}: {line}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_message(f"Error reading {file_path}: {e}")
|
||||||
|
|
||||||
|
return prefixes
|
||||||
|
|
||||||
|
def load_all_prefixes() -> Set[str]:
|
||||||
|
"""Загрузка всех префиксов из всех файлов"""
|
||||||
|
all_prefixes = set()
|
||||||
|
|
||||||
|
for filename in PREFIX_FILES:
|
||||||
|
file_path = Path(DATA_DIR) / filename
|
||||||
|
prefixes = load_prefixes_from_file(file_path)
|
||||||
|
all_prefixes.update(prefixes)
|
||||||
|
log_message(f"Loaded {len(prefixes)} prefixes from {filename}")
|
||||||
|
|
||||||
|
return all_prefixes
|
||||||
|
|
||||||
|
def get_current_routes() -> Set[str]:
|
||||||
|
"""Получение текущих статических маршрутов из FRR"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['vtysh', '-c', 'show ip route static'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
log_message("Could not get current routes from FRR")
|
||||||
|
return set()
|
||||||
|
|
||||||
|
routes = set()
|
||||||
|
for line in result.stdout.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if line and 'via Null0' in line:
|
||||||
|
# Парсинг строки маршрута
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) >= 2:
|
||||||
|
route = parts[1]
|
||||||
|
if validate_prefix(route):
|
||||||
|
routes.add(route)
|
||||||
|
|
||||||
|
return routes
|
||||||
|
except Exception as e:
|
||||||
|
log_message(f"Error getting current routes: {e}")
|
||||||
|
return set()
|
||||||
|
|
||||||
|
def run_vtysh_command(command: str) -> bool:
|
||||||
|
"""Выполнение команды через vtysh"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['vtysh', '-c', command],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
log_message(f"vtysh command failed: {command}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
log_message(f"Error running vtysh command '{command}': {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def announce_prefixes(prefixes: Set[str]):
|
||||||
|
"""Анонсирование префиксов через FRR"""
|
||||||
|
log_message(f"Announcing {len(prefixes)} prefixes...")
|
||||||
|
|
||||||
# Проверка логов
|
for prefix in sorted(prefixes):
|
||||||
success, output, error = run_command("docker exec bgp-server tail -n 5 /app/logs/prefixes.log")
|
command = f"ip route {prefix} Null0"
|
||||||
if success:
|
if run_vtysh_command(command):
|
||||||
print("Recent log entries:")
|
log_message(f"Announced prefix: {prefix}")
|
||||||
print(output)
|
else:
|
||||||
else:
|
log_message(f"Failed to announce prefix: {prefix}")
|
||||||
print("No recent log entries found")
|
|
||||||
|
def withdraw_prefixes(prefixes: Set[str]):
|
||||||
|
"""Отзыв префиксов через FRR"""
|
||||||
|
log_message(f"Withdrawing {len(prefixes)} prefixes...")
|
||||||
|
|
||||||
return True
|
for prefix in sorted(prefixes):
|
||||||
|
command = f"no ip route {prefix} Null0"
|
||||||
|
if run_vtysh_command(command):
|
||||||
|
log_message(f"Withdrew prefix: {prefix}")
|
||||||
|
else:
|
||||||
|
log_message(f"Failed to withdraw prefix: {prefix}")
|
||||||
|
|
||||||
|
def check_frr_status() -> bool:
|
||||||
|
"""Проверка статуса FRR"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['vtysh', '-c', 'show version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Основная функция"""
|
"""Основная функция"""
|
||||||
print("=== BGP Prefix Reloader ===")
|
log_message("Starting prefix reload...")
|
||||||
|
|
||||||
# Проверка что контейнер запущен
|
# Проверка статуса FRR
|
||||||
success, output, error = run_command("docker ps --filter name=bgp-server --format '{{.Status}}'")
|
if not check_frr_status():
|
||||||
if not success or not output.strip():
|
log_message("ERROR: FRR is not running")
|
||||||
print("Error: BGP server container is not running")
|
|
||||||
print("Please start the container first: docker-compose up -d")
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
print("BGP server container is running")
|
# Загрузка новых префиксов
|
||||||
|
new_prefixes = load_all_prefixes()
|
||||||
|
log_message(f"Loaded {len(new_prefixes)} prefixes from files")
|
||||||
|
|
||||||
# Перезагрузка префиксов
|
# Получение текущих маршрутов
|
||||||
if reload_prefixes():
|
current_routes = get_current_routes()
|
||||||
print("Prefix reload completed successfully")
|
log_message(f"Found {len(current_routes)} current routes in FRR")
|
||||||
|
|
||||||
# Проверка статуса
|
# Определение изменений
|
||||||
if check_status():
|
to_add = new_prefixes - current_routes
|
||||||
print("✓ All checks passed")
|
to_remove = current_routes - new_prefixes
|
||||||
return 0
|
|
||||||
else:
|
log_message(f"Changes: {len(to_add)} to add, {len(to_remove)} to remove")
|
||||||
print("✗ Some checks failed")
|
|
||||||
return 1
|
# Применение изменений
|
||||||
|
if to_remove:
|
||||||
|
withdraw_prefixes(to_remove)
|
||||||
|
|
||||||
|
if to_add:
|
||||||
|
announce_prefixes(to_add)
|
||||||
|
|
||||||
|
# Финальная проверка
|
||||||
|
final_routes = get_current_routes()
|
||||||
|
log_message(f"Final route count: {len(final_routes)}")
|
||||||
|
|
||||||
|
if len(final_routes) == len(new_prefixes):
|
||||||
|
log_message("Reload completed successfully!")
|
||||||
|
return 0
|
||||||
else:
|
else:
|
||||||
print("✗ Prefix reload failed")
|
log_message("WARNING: Route count mismatch after reload")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+89
-62
@@ -1,96 +1,123 @@
|
|||||||
# PowerShell скрипт для запуска BGP сервера
|
# Скрипт запуска BGP Server (FRR)
|
||||||
|
|
||||||
param(
|
param(
|
||||||
|
[string]$ContainerName = "bgp-server",
|
||||||
|
[string]$DataPath = "./data",
|
||||||
|
[string]$LogsPath = "./logs",
|
||||||
[switch]$Build,
|
[switch]$Build,
|
||||||
[switch]$Logs,
|
[switch]$Force
|
||||||
[switch]$Monitor
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Write-Host "=== BGP Server Management ===" -ForegroundColor Green
|
Write-Host "=== BGP Server (FRR) Startup Script ===" -ForegroundColor Green
|
||||||
|
|
||||||
# Проверка Docker
|
# Проверка существования контейнера
|
||||||
Write-Host "Checking Docker..." -ForegroundColor Yellow
|
$containerExists = docker ps -a --filter "name=$ContainerName" --format "table {{.Names}}" | Select-String $ContainerName
|
||||||
try {
|
|
||||||
docker --version | Out-Null
|
if ($containerExists) {
|
||||||
Write-Host "✓ Docker is available" -ForegroundColor Green
|
Write-Host "Found existing container: $ContainerName" -ForegroundColor Yellow
|
||||||
} catch {
|
|
||||||
Write-Host "✗ Docker is not available. Please install Docker Desktop." -ForegroundColor Red
|
if (-not $Force) {
|
||||||
exit 1
|
$response = Read-Host "Do you want to stop and remove the existing container? (y/N)"
|
||||||
|
if ($response -ne "y" -and $response -ne "Y") {
|
||||||
|
Write-Host "Operation cancelled." -ForegroundColor Red
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Остановка и удаление старого контейнера
|
||||||
|
Write-Host "Stopping container..." -ForegroundColor Yellow
|
||||||
|
docker stop $ContainerName
|
||||||
|
|
||||||
|
Write-Host "Removing container..." -ForegroundColor Yellow
|
||||||
|
docker rm $ContainerName
|
||||||
}
|
}
|
||||||
|
|
||||||
# Проверка Docker Compose
|
# Создание директорий если не существуют
|
||||||
Write-Host "Checking Docker Compose..." -ForegroundColor Yellow
|
if (-not (Test-Path $DataPath)) {
|
||||||
try {
|
Write-Host "Creating data directory: $DataPath" -ForegroundColor Yellow
|
||||||
docker-compose --version | Out-Null
|
New-Item -ItemType Directory -Path $DataPath -Force | Out-Null
|
||||||
Write-Host "✓ Docker Compose is available" -ForegroundColor Green
|
|
||||||
} catch {
|
|
||||||
Write-Host "✗ Docker Compose is not available." -ForegroundColor Red
|
|
||||||
exit 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Создание необходимых директорий
|
if (-not (Test-Path $LogsPath)) {
|
||||||
Write-Host "Creating directories..." -ForegroundColor Yellow
|
Write-Host "Creating logs directory: $LogsPath" -ForegroundColor Yellow
|
||||||
if (!(Test-Path "logs")) {
|
New-Item -ItemType Directory -Path $LogsPath -Force | Out-Null
|
||||||
New-Item -ItemType Directory -Path "logs" | Out-Null
|
|
||||||
Write-Host "✓ Created logs directory" -ForegroundColor Green
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(Test-Path "data")) {
|
# Создание файла с префиксами если не существует
|
||||||
New-Item -ItemType Directory -Path "data" | Out-Null
|
$prefixesFile = Join-Path $DataPath "prefixes.txt"
|
||||||
Write-Host "✓ Created data directory" -ForegroundColor Green
|
if (-not (Test-Path $prefixesFile)) {
|
||||||
|
Write-Host "Creating default prefixes file..." -ForegroundColor Yellow
|
||||||
|
@"
|
||||||
|
# Default prefixes file
|
||||||
|
# Add your prefixes here, one per line
|
||||||
|
192.168.100.0/24
|
||||||
|
10.0.0.0/8
|
||||||
|
172.16.0.0/12
|
||||||
|
"@ | Out-File -FilePath $prefixesFile -Encoding UTF8
|
||||||
}
|
}
|
||||||
|
|
||||||
# Сборка образа если требуется
|
# Сборка образа если требуется
|
||||||
if ($Build) {
|
if ($Build) {
|
||||||
Write-Host "Building Docker image..." -ForegroundColor Yellow
|
Write-Host "Building Docker image..." -ForegroundColor Yellow
|
||||||
docker-compose build
|
docker build -t bgp-server-frr .
|
||||||
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host "✗ Build failed" -ForegroundColor Red
|
Write-Host "Failed to build image. Exiting." -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
Write-Host "✓ Build completed" -ForegroundColor Green
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Остановка существующего контейнера
|
# Запуск контейнера
|
||||||
Write-Host "Stopping existing container..." -ForegroundColor Yellow
|
Write-Host "Starting BGP Server container..." -ForegroundColor Yellow
|
||||||
docker-compose down 2>$null
|
$currentPath = Get-Location
|
||||||
Write-Host "✓ Existing container stopped" -ForegroundColor Green
|
$dataVolume = "${currentPath}\${DataPath}:/app/data:ro"
|
||||||
|
$logsVolume = "${currentPath}\${LogsPath}:/app/logs"
|
||||||
|
|
||||||
# Запуск сервера
|
docker run -d `
|
||||||
Write-Host "Starting BGP server..." -ForegroundColor Yellow
|
--name $ContainerName `
|
||||||
docker-compose up -d
|
--restart unless-stopped `
|
||||||
|
-p 179:179 `
|
||||||
|
-v $dataVolume `
|
||||||
|
-v $logsVolume `
|
||||||
|
--cap-add NET_ADMIN `
|
||||||
|
--privileged `
|
||||||
|
bgp-server-frr
|
||||||
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
if ($LASTEXITCODE -eq 0) {
|
||||||
Write-Host "✓ BGP server started successfully" -ForegroundColor Green
|
Write-Host "Container started successfully!" -ForegroundColor Green
|
||||||
|
|
||||||
# Ожидание запуска
|
# Ожидание запуска
|
||||||
Write-Host "Waiting for server to initialize..." -ForegroundColor Yellow
|
Write-Host "Waiting for container to start..." -ForegroundColor Yellow
|
||||||
Start-Sleep -Seconds 10
|
Start-Sleep -Seconds 10
|
||||||
|
|
||||||
# Проверка статуса
|
# Проверка статуса
|
||||||
Write-Host "Checking server status..." -ForegroundColor Yellow
|
$status = docker ps --filter "name=$ContainerName" --format "table {{.Status}}"
|
||||||
$status = docker-compose ps
|
Write-Host "Container status: $status" -ForegroundColor Cyan
|
||||||
Write-Host $status
|
|
||||||
|
|
||||||
if ($Logs) {
|
# Проверка FRR статуса
|
||||||
Write-Host "Showing logs..." -ForegroundColor Yellow
|
Write-Host "Checking FRR status..." -ForegroundColor Cyan
|
||||||
docker-compose logs -f bgp-server
|
docker exec $ContainerName vtysh -c "show version" 2>$null
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "FRR is running successfully!" -ForegroundColor Green
|
||||||
|
|
||||||
|
# Показ BGP статуса
|
||||||
|
Write-Host "BGP Summary:" -ForegroundColor Cyan
|
||||||
|
docker exec $ContainerName vtysh -c "show ip bgp summary" 2>$null
|
||||||
|
|
||||||
|
# Показ логов
|
||||||
|
Write-Host "Recent logs:" -ForegroundColor Cyan
|
||||||
|
docker logs --tail=10 $ContainerName
|
||||||
|
|
||||||
|
Write-Host "`nBGP Server (FRR) is ready!" -ForegroundColor Green
|
||||||
|
Write-Host "To view logs: docker logs -f $ContainerName" -ForegroundColor Cyan
|
||||||
|
Write-Host "To check BGP status: docker exec $ContainerName vtysh -c 'show ip bgp summary'" -ForegroundColor Cyan
|
||||||
|
Write-Host "To access FRR CLI: docker exec -it $ContainerName vtysh" -ForegroundColor Cyan
|
||||||
|
} else {
|
||||||
|
Write-Host "FRR failed to start properly. Check logs:" -ForegroundColor Red
|
||||||
|
docker logs $ContainerName
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($Monitor) {
|
|
||||||
Write-Host "Starting monitoring..." -ForegroundColor Yellow
|
|
||||||
docker exec bgp-server python3 /app/scripts/monitor.py
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Write-Host "✗ Failed to start BGP server" -ForegroundColor Red
|
Write-Host "Failed to start container. Check the error above." -ForegroundColor Red
|
||||||
Write-Host "Check logs with: docker-compose logs bgp-server" -ForegroundColor Yellow
|
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "=== Setup Complete ===" -ForegroundColor Green
|
|
||||||
Write-Host "Useful commands:" -ForegroundColor Cyan
|
|
||||||
Write-Host " View logs: docker-compose logs -f bgp-server" -ForegroundColor White
|
|
||||||
Write-Host " Check status: docker-compose ps" -ForegroundColor White
|
|
||||||
Write-Host " Stop server: docker-compose down" -ForegroundColor White
|
|
||||||
Write-Host " Monitor: docker exec bgp-server python3 /app/scripts/monitor.py" -ForegroundColor White
|
|
||||||
+104
-154
@@ -1,56 +1,24 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Скрипт запуска BGP сервера
|
# Скрипт запуска BGP сервера на FRR
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "Starting BGP Server..."
|
echo "Starting BGP Server (FRR)..."
|
||||||
|
|
||||||
# Создание директорий если не существуют
|
# Создание директорий если не существуют
|
||||||
mkdir -p /app/logs
|
mkdir -p /app/logs
|
||||||
mkdir -p /app/run
|
mkdir -p /app/run
|
||||||
mkdir -p /run/exabgp
|
|
||||||
mkdir -p /app/data
|
mkdir -p /app/data
|
||||||
|
mkdir -p /var/run/frr
|
||||||
|
mkdir -p /etc/frr
|
||||||
|
|
||||||
# Установка правильных прав доступа
|
# Установка правильных прав доступа
|
||||||
chmod 755 /app/logs
|
chmod 755 /app/logs
|
||||||
chmod 755 /app/run
|
chmod 755 /app/run
|
||||||
chmod 755 /run/exabgp
|
|
||||||
chmod 755 /app/data
|
chmod 755 /app/data
|
||||||
|
chmod 755 /var/run/frr
|
||||||
# Удаление старых named pipes если они существуют
|
chmod 755 /etc/frr
|
||||||
echo "Cleaning up old named pipes..."
|
|
||||||
for dir in /run/exabgp /var/run/exabgp /usr/local/run/exabgp /usr/local/var/run/exabgp; do
|
|
||||||
if [ -d "$dir" ]; then
|
|
||||||
echo "Cleaning $dir..."
|
|
||||||
rm -f $dir/exabgp.in $dir/exabgp.out
|
|
||||||
rmdir $dir 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Создание named pipes для CLI в стандартном месте
|
|
||||||
echo "Creating named pipes for ExaBGP CLI..."
|
|
||||||
for dir in /run/exabgp /var/run/exabgp /usr/local/run/exabgp /usr/local/var/run/exabgp; do
|
|
||||||
mkdir -p $dir
|
|
||||||
echo "Creating named pipes in $dir..."
|
|
||||||
|
|
||||||
# Проверяем, существуют ли уже named pipes
|
|
||||||
if [ ! -p "$dir/exabgp.in" ]; then
|
|
||||||
mkfifo $dir/exabgp.in
|
|
||||||
chmod 666 $dir/exabgp.in
|
|
||||||
echo "Created $dir/exabgp.in"
|
|
||||||
else
|
|
||||||
echo "Named pipe $dir/exabgp.in already exists"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -p "$dir/exabgp.out" ]; then
|
|
||||||
mkfifo $dir/exabgp.out
|
|
||||||
chmod 666 $dir/exabgp.out
|
|
||||||
echo "Created $dir/exabgp.out"
|
|
||||||
else
|
|
||||||
echo "Named pipe $dir/exabgp.out already exists"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Проверка наличия файлов с префиксами
|
# Проверка наличия файлов с префиксами
|
||||||
if [ ! -f "/app/data/prefixes.txt" ]; then
|
if [ ! -f "/app/data/prefixes.txt" ]; then
|
||||||
@@ -62,9 +30,9 @@ fi
|
|||||||
# Создание лог-файлов с правильными правами
|
# Создание лог-файлов с правильными правами
|
||||||
echo "Creating log files with proper permissions..."
|
echo "Creating log files with proper permissions..."
|
||||||
touch /app/logs/prefixes.log
|
touch /app/logs/prefixes.log
|
||||||
touch /app/logs/exabgp.log
|
touch /app/logs/frr.log
|
||||||
chmod 666 /app/logs/prefixes.log
|
chmod 666 /app/logs/prefixes.log
|
||||||
chmod 666 /app/logs/exabgp.log
|
chmod 666 /app/logs/frr.log
|
||||||
|
|
||||||
echo "Prefixes file content:"
|
echo "Prefixes file content:"
|
||||||
cat /app/data/prefixes.txt
|
cat /app/data/prefixes.txt
|
||||||
@@ -77,7 +45,7 @@ ls -la /app/scripts/
|
|||||||
echo "---"
|
echo "---"
|
||||||
|
|
||||||
# Генерация конфигурации из переменных окружения
|
# Генерация конфигурации из переменных окружения
|
||||||
echo "Generating ExaBGP configuration from environment variables..."
|
echo "Generating FRR configuration from environment variables..."
|
||||||
echo "Environment variables:"
|
echo "Environment variables:"
|
||||||
env | grep BGP_ || echo "No BGP_ variables found"
|
env | grep BGP_ || echo "No BGP_ variables found"
|
||||||
echo "---"
|
echo "---"
|
||||||
@@ -85,11 +53,20 @@ echo "---"
|
|||||||
python3 /app/scripts/generate_config.py
|
python3 /app/scripts/generate_config.py
|
||||||
|
|
||||||
# Проверка конфигурации
|
# Проверка конфигурации
|
||||||
echo "Validating ExaBGP configuration..."
|
echo "Validating FRR configuration..."
|
||||||
echo "Configuration file content:"
|
echo "Configuration file content:"
|
||||||
cat /app/exabgp.conf
|
cat /etc/frr/frr.conf
|
||||||
echo "---"
|
echo "---"
|
||||||
exabgp --validate /app/exabgp.conf
|
|
||||||
|
# Проверка синтаксиса конфигурации FRR
|
||||||
|
if vtysh -c "show running-config" > /dev/null 2>&1; then
|
||||||
|
echo "FRR configuration validation passed"
|
||||||
|
else
|
||||||
|
echo "FRR configuration validation failed"
|
||||||
|
echo "Full config content:"
|
||||||
|
cat /etc/frr/frr.conf
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Тестовый запуск скрипта загрузки префиксов
|
# Тестовый запуск скрипта загрузки префиксов
|
||||||
echo "Testing prefix loader script..."
|
echo "Testing prefix loader script..."
|
||||||
@@ -105,135 +82,108 @@ else
|
|||||||
echo "Warning: Cannot reach $NEIGHBOR_IP. BGP session may not establish."
|
echo "Warning: Cannot reach $NEIGHBOR_IP. BGP session may not establish."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Запуск ExaBGP в фоновом режиме с логированием
|
# Запуск FRR демонов
|
||||||
echo "Starting ExaBGP..."
|
echo "Starting FRR daemons..."
|
||||||
LOG_LEVEL=${BGP_LOG_LEVEL:-INFO}
|
|
||||||
|
|
||||||
# Дополнительная проверка конфигурационного файла
|
# Запуск zebra (обязательный демон для FRR)
|
||||||
echo "Final config file check:"
|
echo "Starting zebra daemon..."
|
||||||
ls -la /app/exabgp.conf
|
zebra -d -f /etc/frr/frr.conf -A 127.0.0.1 -z /var/run/frr/zebra.api
|
||||||
echo "Config file first few lines:"
|
sleep 2
|
||||||
head -10 /app/exabgp.conf
|
|
||||||
echo "---"
|
|
||||||
|
|
||||||
# Проверка что ExaBGP может прочитать файл
|
# Запуск bgpd
|
||||||
echo "Testing ExaBGP config validation:"
|
echo "Starting bgpd daemon..."
|
||||||
if exabgp --validate /app/exabgp.conf; then
|
bgpd -d -f /etc/frr/frr.conf -A 127.0.0.1 -z /var/run/frr/zebra.api
|
||||||
echo "Configuration validation passed"
|
sleep 2
|
||||||
else
|
|
||||||
echo "Configuration validation failed"
|
|
||||||
echo "Full config content:"
|
|
||||||
cat /app/exabgp.conf
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "---"
|
|
||||||
|
|
||||||
# Проверка переменных окружения и путей
|
# Проверка что демоны запустились
|
||||||
echo "Environment check:"
|
echo "Checking if FRR daemons are running..."
|
||||||
echo "PWD: $PWD"
|
ps aux | grep -E "(zebra|bgpd)" | grep -v grep || echo "No FRR daemons found in ps"
|
||||||
echo "PATH: $PATH"
|
|
||||||
echo "PYTHONPATH: $PYTHONPATH"
|
|
||||||
echo "BGP_ variables:"
|
|
||||||
env | grep BGP_ || echo "No BGP_ variables found"
|
|
||||||
echo "---"
|
|
||||||
|
|
||||||
# Запуск ExaBGP
|
# Сохранение PID файлов
|
||||||
echo "Starting ExaBGP with config: /app/exabgp.conf"
|
echo "Saving PID files..."
|
||||||
|
pgrep zebra > /app/logs/zebra.pid 2>/dev/null || echo "zebra PID not found"
|
||||||
# Проверяем что файл существует и читается
|
pgrep bgpd > /app/logs/bgpd.pid 2>/dev/null || echo "bgpd PID not found"
|
||||||
if [ ! -f "/app/exabgp.conf" ]; then
|
|
||||||
echo "ERROR: Configuration file /app/exabgp.conf not found!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -r "/app/exabgp.conf" ]; then
|
|
||||||
echo "ERROR: Configuration file /app/exabgp.conf is not readable!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Запуск ExaBGP с правильными параметрами
|
|
||||||
cd /app
|
|
||||||
echo "Starting ExaBGP in background..."
|
|
||||||
|
|
||||||
# Запуск ExaBGP в фоновом режиме
|
|
||||||
if [ -n "$LOG_LEVEL" ]; then
|
|
||||||
nohup exabgp --log.level $LOG_LEVEL /app/exabgp.conf > /app/logs/exabgp.log 2>&1 &
|
|
||||||
else
|
|
||||||
nohup exabgp /app/exabgp.conf > /app/logs/exabgp.log 2>&1 &
|
|
||||||
fi
|
|
||||||
EXABGP_PID=$!
|
|
||||||
|
|
||||||
# Проверяем что процесс запустился
|
|
||||||
sleep 5
|
|
||||||
echo "Checking if ExaBGP process is running..."
|
|
||||||
ps aux | grep exabgp | grep -v grep || echo "No exabgp process found in ps"
|
|
||||||
|
|
||||||
if ! kill -0 $EXABGP_PID 2>/dev/null; then
|
|
||||||
echo "Failed to start ExaBGP. Checking logs:"
|
|
||||||
cat /app/logs/exabgp.log
|
|
||||||
echo "Trying to run ExaBGP directly for diagnosis:"
|
|
||||||
exabgp --version
|
|
||||||
echo "---"
|
|
||||||
echo "Direct execution test:"
|
|
||||||
timeout 10 exabgp /app/exabgp.conf || echo "Direct execution completed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Сохранение PID
|
|
||||||
echo $EXABGP_PID > /app/logs/exabgp.pid
|
|
||||||
|
|
||||||
# Ожидание запуска
|
# Ожидание запуска
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
# Проверка статуса
|
# Проверка статуса через vtysh
|
||||||
if kill -0 $EXABGP_PID 2>/dev/null; then
|
echo "Checking FRR status via vtysh..."
|
||||||
echo "ExaBGP started successfully with PID: $EXABGP_PID"
|
if vtysh -c "show version" > /dev/null 2>&1; then
|
||||||
echo "Recent logs:"
|
echo "FRR started successfully"
|
||||||
tail -10 /app/logs/exabgp.log
|
echo "FRR version:"
|
||||||
|
vtysh -c "show version" 2>/dev/null || echo "Could not get version"
|
||||||
|
|
||||||
# Дополнительная проверка через несколько секунд
|
echo "BGP neighbors:"
|
||||||
sleep 3
|
vtysh -c "show ip bgp neighbors" 2>/dev/null || echo "Could not get BGP neighbors"
|
||||||
if kill -0 $EXABGP_PID 2>/dev/null; then
|
|
||||||
echo "ExaBGP is still running after 3 seconds"
|
echo "BGP summary:"
|
||||||
echo "Process details:"
|
vtysh -c "show ip bgp summary" 2>/dev/null || echo "Could not get BGP summary"
|
||||||
ps aux | grep exabgp | grep -v grep || echo "Process not found in ps output"
|
|
||||||
else
|
|
||||||
echo "ExaBGP process died after startup"
|
|
||||||
echo "Full logs:"
|
|
||||||
cat /app/logs/exabgp.log
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo "Failed to start ExaBGP"
|
echo "Failed to start FRR. Checking logs:"
|
||||||
echo "Full logs:"
|
cat /app/logs/frr.log 2>/dev/null || echo "No FRR logs found"
|
||||||
cat /app/logs/exabgp.log
|
|
||||||
echo "---"
|
|
||||||
echo "Process status:"
|
|
||||||
ps aux | grep exabgp || echo "No exabgp process found"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Запуск скрипта загрузки префиксов в фоновом режиме
|
||||||
|
echo "Starting prefix loader in background..."
|
||||||
|
python3 /app/scripts/load_prefixes.py > /app/logs/prefixes.log 2>&1 &
|
||||||
|
PREFIX_LOADER_PID=$!
|
||||||
|
echo $PREFIX_LOADER_PID > /app/logs/prefix_loader.pid
|
||||||
|
|
||||||
|
# Запуск монитора в фоновом режиме
|
||||||
|
echo "Starting monitor in background..."
|
||||||
|
python3 /app/scripts/monitor.py > /app/logs/monitor.log 2>&1 &
|
||||||
|
MONITOR_PID=$!
|
||||||
|
echo $MONITOR_PID > /app/logs/monitor.pid
|
||||||
|
|
||||||
|
echo "All services started successfully!"
|
||||||
|
echo "Recent logs:"
|
||||||
|
tail -10 /app/logs/prefixes.log 2>/dev/null || echo "No prefix logs yet"
|
||||||
|
echo "---"
|
||||||
|
|
||||||
# Функция для graceful shutdown
|
# Функция для graceful shutdown
|
||||||
cleanup() {
|
cleanup() {
|
||||||
echo "Shutting down BGP server..."
|
echo "Received shutdown signal. Cleaning up..."
|
||||||
if [ -f "/app/logs/exabgp.pid" ]; then
|
|
||||||
kill $(cat /app/logs/exabgp.pid) 2>/dev/null || true
|
# Остановка скриптов
|
||||||
|
if [ -f "/app/logs/prefix_loader.pid" ]; then
|
||||||
|
kill $(cat /app/logs/prefix_loader.pid) 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -f "/app/logs/monitor.pid" ]; then
|
||||||
|
kill $(cat /app/logs/monitor.pid) 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Отзыв всех префиксов
|
||||||
|
echo "Withdrawing all prefixes..."
|
||||||
|
python3 /app/scripts/load_prefixes.py --withdraw > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Остановка FRR демонов
|
||||||
|
echo "Stopping FRR daemons..."
|
||||||
|
killall bgpd 2>/dev/null || true
|
||||||
|
killall zebra 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Cleanup completed."
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Обработка сигналов
|
# Установка обработчиков сигналов
|
||||||
trap cleanup SIGTERM SIGINT
|
trap cleanup SIGTERM SIGINT
|
||||||
|
|
||||||
# Ожидание завершения
|
# Ожидание завершения
|
||||||
echo "Waiting for ExaBGP to run..."
|
echo "BGP Server is running. Press Ctrl+C to stop."
|
||||||
echo "Process status:"
|
while true; do
|
||||||
ps aux | grep exabgp || echo "No exabgp process found"
|
sleep 10
|
||||||
echo "---"
|
|
||||||
|
# Проверка что демоны все еще работают
|
||||||
# Проверка процесса загрузки префиксов
|
if ! pgrep zebra > /dev/null; then
|
||||||
echo "Checking prefix loader process:"
|
echo "ERROR: zebra daemon stopped unexpectedly"
|
||||||
ps aux | grep load_prefixes || echo "No prefix loader process found"
|
exit 1
|
||||||
echo "---"
|
fi
|
||||||
|
|
||||||
wait $EXABGP_PID
|
if ! pgrep bgpd > /dev/null; then
|
||||||
|
echo "ERROR: bgpd daemon stopped unexpectedly"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
+54
-21
@@ -1,33 +1,66 @@
|
|||||||
# PowerShell скрипт для остановки BGP сервера
|
# Скрипт остановки BGP Server (FRR)
|
||||||
|
|
||||||
Write-Host "=== Stopping BGP Server ===" -ForegroundColor Yellow
|
param(
|
||||||
|
[string]$ContainerName = "bgp-server",
|
||||||
|
[switch]$Force
|
||||||
|
)
|
||||||
|
|
||||||
# Проверка что контейнер запущен
|
Write-Host "=== BGP Server (FRR) Stop Script ===" -ForegroundColor Green
|
||||||
$containerStatus = docker ps --filter name=bgp-server --format "{{.Status}}"
|
|
||||||
if (!$containerStatus) {
|
# Проверка существования контейнера
|
||||||
Write-Host "BGP server is not running" -ForegroundColor Yellow
|
$containerExists = docker ps -a --filter "name=$ContainerName" --format "table {{.Names}}" | Select-String $ContainerName
|
||||||
|
|
||||||
|
if (-not $containerExists) {
|
||||||
|
Write-Host "Container $ContainerName not found." -ForegroundColor Yellow
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "BGP server is running. Stopping..." -ForegroundColor Yellow
|
# Проверка статуса контейнера
|
||||||
|
$containerRunning = docker ps --filter "name=$ContainerName" --format "table {{.Names}}" | Select-String $ContainerName
|
||||||
|
|
||||||
# Остановка контейнера
|
if ($containerRunning) {
|
||||||
docker-compose down
|
Write-Host "Container $ContainerName is running. Stopping..." -ForegroundColor Yellow
|
||||||
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
if (-not $Force) {
|
||||||
Write-Host "✓ BGP server stopped successfully" -ForegroundColor Green
|
$response = Read-Host "Do you want to stop the container? (y/N)"
|
||||||
|
if ($response -ne "y" -and $response -ne "Y") {
|
||||||
|
Write-Host "Operation cancelled." -ForegroundColor Red
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Graceful shutdown - отзыв префиксов
|
||||||
|
Write-Host "Withdrawing prefixes..." -ForegroundColor Yellow
|
||||||
|
docker exec $ContainerName python3 /app/scripts/load_prefixes.py --withdraw 2>$null
|
||||||
|
|
||||||
|
# Остановка контейнера
|
||||||
|
Write-Host "Stopping container..." -ForegroundColor Yellow
|
||||||
|
docker stop $ContainerName
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "Container stopped successfully!" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "Failed to stop container." -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Write-Host "✗ Failed to stop BGP server" -ForegroundColor Red
|
Write-Host "Container $ContainerName is not running." -ForegroundColor Yellow
|
||||||
exit 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Проверка что контейнер остановлен
|
# Удаление контейнера если требуется
|
||||||
$containerStatus = docker ps --filter name=bgp-server --format "{{.Status}}"
|
if ($Force) {
|
||||||
if (!$containerStatus) {
|
Write-Host "Removing container..." -ForegroundColor Yellow
|
||||||
Write-Host "✓ Container is stopped" -ForegroundColor Green
|
docker rm $ContainerName 2>$null
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host "Container removed successfully!" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "Container was already removed or removal failed." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Write-Host "✗ Container is still running" -ForegroundColor Red
|
Write-Host "Container stopped but not removed." -ForegroundColor Cyan
|
||||||
exit 1
|
Write-Host "To remove container, run: docker rm $ContainerName" -ForegroundColor Cyan
|
||||||
|
Write-Host "Or use -Force parameter to stop and remove in one command." -ForegroundColor Cyan
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "=== BGP Server Stopped ===" -ForegroundColor Green
|
Write-Host "BGP Server (FRR) stopped successfully!" -ForegroundColor Green
|
||||||
Reference in New Issue
Block a user