#!/bin/sh

# FreeBSD L2TP/IPsec VPN Client Auto-Installer
# Автоматическая установка и настройка VPN-клиента на FreeBSD
# Версия: 1.0
# Лицензия: MIT
# Автор: OldenGeek
set -e

# Цвета для вывода
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Функции для цветного вывода
print_info() {
    echo -e "${BLUE}INFO: $1${NC}"
}

print_success() {
    echo -e "${GREEN}\033[1mSUCCESS:\033[0m${GREEN} $1${NC}"
}

print_warning() {
    echo -e "${YELLOW}\033[1mWARNING:\033[0m${YELLOW} $1${NC}"
}

print_error() {
    echo -e "${RED}\033[1mERROR:\033[0m${RED} $1${NC}"
}

print_header() {
    echo -e "${BLUE}"
    echo "=================================================="
    echo "$1"
    echo "=================================================="
    echo -e "${NC}"
}

# Проверка прав root
check_root() {
    if [ "$(id -u)" != "0" ]; then
        print_error "Скрипт должен запускаться от root"
        print_info "\033[1mИспользуйте: sudo $0\033[0m"
        exit 1
    fi
}

# Проверка ОС
check_os() {
    if [ "$(uname)" != "FreeBSD" ]; then
        print_error "\033[1mСкрипт предназначен только для FreeBSD\033[0m"
        exit 1
    fi
    
    FREEBSD_VERSION=$(freebsd-version | cut -d'-' -f1)
    print_info "\033[1mОбнаружена FreeBSD версии: $FREEBSD_VERSION\033[0m"
}

# Запрос данных для подключения
get_vpn_credentials() {
    print_header "\033[1mНАСТРОЙКА VPN ПОДКЛЮЧЕНИЯ\033[0m"
    
    echo -n "Введите IP-адрес VPN-сервера: "
    read VPN_SERVER
    
    echo -n "Введите Pre-Shared Key (PSK): "
    read VPN_PSK
    
    echo -n "Введите имя пользователя VPN: "
    read VPN_USERNAME
    
    echo -n "Введите пароль VPN: "
    read -s VPN_PASSWORD
    echo
    
    # Проверка введенных данных
    if [ -z "$VPN_SERVER" ] || [ -z "$VPN_PSK" ] || [ -z "$VPN_USERNAME" ] || [ -z "$VPN_PASSWORD" ]; then
        print_error "\033[1mВсе поля должны быть заполнены!\033[0m"
        exit 1
    fi
    
    print_success "\033[1mДанные для подключения получены\033[0m"
}

# Установка необходимых пакетов
install_packages() {
    print_header "\033[1mУСТАНОВКА НЕОБХОДИМЫХ ПАКЕТОВ\033[0m"
    
    print_info "Обновление списка пакетов..."
    pkg update -q
    
    print_info "Установка strongSwan..."
    pkg install -y security/strongswan
    
    print_info "Установка mpd5..."
    pkg install -y net/mpd5
    
    print_success "\033[1mВсе пакеты установлены\033[0m"
}

# Настройка strongSwan
configure_strongswan() {
    print_header "\033[1mНАСТРОЙКА STRONGSWAN (IPSEC)\033[0m"
    
    # Создание директории конфигурации
    mkdir -p /usr/local/etc/swanctl
    
    # Создание swanctl.conf
    print_info "Создание конфигурации swanctl.conf..."
    cat > /usr/local/etc/swanctl/swanctl.conf << EOF
connections {
   L2TP-PSK {
      local_addrs  = %defaultroute
      remote_addrs = $VPN_SERVER

      local {
         auth = psk
      }
      remote {
         auth = psk
      }

      children {
         L2TP-transport {
            local_ts  = dynamic[udp/l2f]
            remote_ts = $VPN_SERVER[udp/l2f]
            mode = transport
            esp_proposals = aes256-sha1-modp2048,aes128-sha1-modp1024,3des-sha1-modp1024
            start_action = none
         }
      }
      version = 1
      mobike = no
      reauth_time = 0
      proposals = aes256-sha1-modp2048,aes128-sha1-modp1024,3des-sha1-modp1024
   }
}

secrets {
   ike-1 {
      id = %any
      secret = "$VPN_PSK"
   }
}
EOF
    
    print_success "\033[1mКонфигурация strongSwan создана\033[0m"
}

# Настройка mpd5
configure_mpd5() {
    print_header "\033[1mНАСТРОЙКА MPD5 (L2TP)\033[0m"
    
    # Создание директории конфигурации
    mkdir -p /usr/local/etc/mpd5
    
    # Создание mpd.conf
    print_info "Создание конфигурации mpd.conf..."
    cat > /usr/local/etc/mpd5/mpd.conf << EOF
startup:
    # configure mpd users
    set user foo bar admin
    set user foo1 bar1
    # configure the console
    set console self 127.0.0.1 5005
    set console open
    # configure the web server
    set web self 0.0.0.0 5006
    set web open

default:
    load l2tp

l2tp:
    create bundle static L2TP
    set iface mtu 1480
    set iface idle 0
    
    # Настройки IPCP для получения IP от сервера
    set ipcp yes vjcomp
    set ipcp ranges 0.0.0.0/0 0.0.0.0/0
    set ipcp dns 8.8.8.8 8.8.4.4

    create link static L2 l2tp
    set link action bundle L2TP
    set link latency 0
    set link max-redial 1
    set link disable incoming acfcomp protocomp magicnum check-magic shortseq
    set link deny chap-msv1 eap acfcomp protocomp shortseq
    set link accept pap
    set link keep-alive 10 75

    set l2tp peer $VPN_SERVER
    set auth authname "$VPN_USERNAME"
    set auth password "$VPN_PASSWORD"
    open
EOF
    
    print_success "\033[1mКонфигурация mpd5 создана\033[0m"
}

# Создание скриптов управления
create_management_scripts() {
    print_header "\033[1mСОЗДАНИЕ СКРИПТОВ УПРАВЛЕНИЯ\033[0m"
    
    # Скрипт vpnup
    print_info "Создание скрипта vpnup..."
    cat > /usr/local/bin/vpnup << 'EOF'
#!/bin/sh

# VPN UP Script for FreeBSD L2TP/IPsec
# Автоматический запуск strongSwan + mpd5

REDIRECT_ALL=1  # По умолчанию перенаправляем весь трафик

# Парсим аргументы
while [ $# -gt 0 ]; do
    case $1 in
        --redirect-no|-n)
            REDIRECT_ALL=0
            shift
            ;;
        --help|-h)
            echo "Использование: vpnup [--redirect-no|-n]"
            echo "  По умолчанию: весь трафик через VPN"
            echo "  --redirect-no, -n   Только VPN-туннель (без перенаправления трафика)"
            exit 0
            ;;
        *)
            echo "Неизвестный параметр: $1"
            echo "Используйте --help для справки"
            exit 1
            ;;
    esac
done

echo "Запуск VPN-клиента FreeBSD..."

# Проверяем права root
if [ "$(id -u)" != "0" ]; then
    echo "❌ Ошибка: Скрипт должен запускаться от root"
    exit 1
fi

# Запускаем strongSwan
echo "Запуск strongSwan..."
service strongswan start
if [ $? -ne 0 ]; then
    echo "ОШИБКА: Не удалось запустить strongSwan"
    exit 1
fi

# Ждем немного для инициализации
sleep 2

# Инициируем IPsec соединение
echo "Установка IPsec туннеля..."
swanctl --initiate --child L2TP-transport
if [ $? -ne 0 ]; then
    echo "ОШИБКА: Не удалось установить IPsec туннель"
    exit 1
fi

# Ждем установки IPsec
sleep 3

# Запускаем mpd5
echo "Запуск L2TP соединения..."
service mpd5 start
if [ $? -ne 0 ]; then
    echo "ОШИБКА: Не удалось запустить mpd5"
    exit 1
fi

# Ждем установки L2TP
echo "Ожидание установки L2TP соединения..."
sleep 10

# Проверяем результат
echo "Проверка состояния VPN..."

# Проверяем IPsec
IPSEC_STATUS=$(swanctl --list-sas | grep "ESTABLISHED\|INSTALLED")
if [ -z "$IPSEC_STATUS" ]; then
    echo "ПРЕДУПРЕЖДЕНИЕ: IPsec туннель не установлен"
    exit 1
else
    echo "IPsec туннель активен"
fi

# Проверяем интерфейс ng0
NG0_STATUS=$(ifconfig ng0 2>/dev/null | grep "inet.*192.168")
if [ -z "$NG0_STATUS" ]; then
    echo "ПРЕДУПРЕЖДЕНИЕ: Интерфейс ng0 не настроен"
    exit 1
else
    echo "Интерфейс ng0 активен: $(echo $NG0_STATUS | awk '{print $2" -> "$4}')"
    
    # Если запрошено перенаправление всего трафика (по умолчанию)
    if [ $REDIRECT_ALL -eq 1 ]; then
        echo "Настройка перенаправления всего трафика через VPN..."
        
        # Сохраняем текущий маршрут по умолчанию
        DEFAULT_GW=$(route -n get default | grep gateway | awk '{print $2}')
        echo "$DEFAULT_GW" > /tmp/vpn_original_gateway
        
        # Получаем IP VPN-сервера из конфига
        VPN_SERVER_IP=$(grep "remote_addrs" /usr/local/etc/swanctl/swanctl.conf | awk '{print $3}')
        
        # Добавляем маршрут до VPN-сервера через исходный шлюз
        route add "$VPN_SERVER_IP" "$DEFAULT_GW" 2>/dev/null
        
        # Получаем IP VPN-шлюза из интерфейса ng0
        VPN_GATEWAY=$(ifconfig ng0 | grep "inet.*-->" | awk '{print $4}')
        
        # Меняем маршрут по умолчанию
        route delete default "$DEFAULT_GW" 2>/dev/null
        route add default "$VPN_GATEWAY" 2>/dev/null
        
        echo "Весь трафик перенаправлен через VPN"
        echo "Для восстановления используйте: vpndown"
    else
        echo "Режим только туннель (без перенаправления трафика)"
    fi
fi

# Проверяем ping до VPN-сервера
VPN_GATEWAY=$(ifconfig ng0 2>/dev/null | grep "inet.*-->" | awk '{print $4}')
if [ -n "$VPN_GATEWAY" ] && ping -c 1 "$VPN_GATEWAY" >/dev/null 2>&1; then
    echo "VPN-сервер доступен"
else
    echo "ПРЕДУПРЕЖДЕНИЕ: VPN-сервер недоступен"
fi

echo ""
echo "VPN-клиент запущен!"
if [ $REDIRECT_ALL -eq 1 ]; then
    echo "Весь интернет-трафик идет через VPN"
    echo "Ваш внешний IP теперь: $(curl -s --max-time 5 https://ipinfo.io/ip 2>/dev/null || echo 'проверьте вручную')"
else
    echo "Активен только VPN-туннель"
fi
echo "Для проверки статуса используйте: vpnstatus"
echo "Для остановки используйте: vpndown"
EOF
    
    # Скрипт vpndown
    print_info "Создание скрипта vpndown..."
    cat > /usr/local/bin/vpndown << 'EOF'
#!/bin/sh

# VPN DOWN Script for FreeBSD L2TP/IPsec
# Автоматическая остановка strongSwan + mpd5

echo "Остановка VPN-клиента FreeBSD..."

# Проверяем права root
if [ "$(id -u)" != "0" ]; then
    echo "❌ Ошибка: Скрипт должен запускаться от root"
    exit 1
fi

# Восстанавливаем исходные маршруты если они были изменены
if [ -f /tmp/vpn_original_gateway ]; then
    echo "Восстановление исходных маршрутов..."
    ORIGINAL_GW=$(cat /tmp/vpn_original_gateway)
    
    # Получаем IP VPN-сервера из конфига
    VPN_SERVER_IP=$(grep "remote_addrs" /usr/local/etc/swanctl/swanctl.conf | awk '{print $3}')
    
    # Получаем IP VPN-шлюза из интерфейса ng0
    VPN_GATEWAY=$(ifconfig ng0 2>/dev/null | grep "inet.*-->" | awk '{print $4}')
    
    # Удаляем VPN маршруты
    [ -n "$VPN_GATEWAY" ] && route delete default "$VPN_GATEWAY" 2>/dev/null
    [ -n "$VPN_SERVER_IP" ] && route delete "$VPN_SERVER_IP" 2>/dev/null
    
    # Восстанавливаем исходный маршрут
    route add default "$ORIGINAL_GW" 2>/dev/null
    
    rm -f /tmp/vpn_original_gateway
    echo "Исходные маршруты восстановлены"
fi

# Останавливаем mpd5
echo "Остановка L2TP соединения..."
service mpd5 stop

# Останавливаем strongSwan
echo "Остановка strongSwan..."
service strongswan stop

echo "VPN-клиент остановлен"
EOF
    
    # Скрипт vpnstatus
    print_info "Создание скрипта vpnstatus..."
    cat > /usr/local/bin/vpnstatus << 'EOF'
#!/bin/sh

# VPN Status Script for FreeBSD L2TP/IPsec
# Проверка статуса VPN соединения

echo "Статус VPN-клиента FreeBSD"
echo "=============================="

# Проверка strongSwan
echo "IPsec (strongSwan):"
if service strongswan status >/dev/null 2>&1; then
    echo "  ✅ Сервис запущен"
    
    # Проверка активных соединений
    CONNECTIONS=$(swanctl --list-conns 2>/dev/null | grep -c "L2TP-PSK")
    if [ "$CONNECTIONS" -gt 0 ]; then
        echo "  Конфигурация загружена ($CONNECTIONS соединений)"
    else
        echo "  ПРЕДУПРЕЖДЕНИЕ: Конфигурация не загружена"
    fi
    
    # Проверка активных SA
    ACTIVE_SA=$(swanctl --list-sas 2>/dev/null | grep -c "ESTABLISHED\|INSTALLED")
    if [ "$ACTIVE_SA" -gt 0 ]; then
        echo "  Активных SA: $ACTIVE_SA"
        
        # Показываем детали SA
        echo "  Детали IPsec туннеля:"
        swanctl --list-sas 2>/dev/null | grep -E "L2TP-PSK|ESTABLISHED|INSTALLED|bytes_i|bytes_o" | sed 's/^/    /'
    else
        echo "  ПРЕДУПРЕЖДЕНИЕ: Нет активных SA"
    fi
else
    echo "  ❌ Сервис остановлен"
fi

echo ""

# Проверка mpd5
echo "L2TP (mpd5):"
if service mpd5 status >/dev/null 2>&1; then
    echo "  ✅ Сервис запущен"
    
    # Проверка интерфейса ng0
    NG0_STATUS=$(ifconfig ng0 2>/dev/null)
    if [ -n "$NG0_STATUS" ]; then
        NG0_IP=$(echo "$NG0_STATUS" | grep "inet.*-->" | awk '{print $2}')
        NG0_PEER=$(echo "$NG0_STATUS" | grep "inet.*-->" | awk '{print $4}')
        
        if [ -n "$NG0_IP" ] && [ -n "$NG0_PEER" ]; then
            echo "  Интерфейс ng0 активен"
            echo "    Локальный IP: $NG0_IP"
            echo "    Удаленный IP: $NG0_PEER"
            
            # Проверка MTU
            NG0_MTU=$(echo "$NG0_STATUS" | grep "mtu" | awk '{print $4}')
            echo "    MTU: $NG0_MTU"
        else
            echo "  ПРЕДУПРЕЖДЕНИЕ: Интерфейс ng0 без IP-адреса"
        fi
    else
        echo "  ПРЕДУПРЕЖДЕНИЕ: Интерфейс ng0 не найден"
    fi
else
    echo "  ❌ Сервис остановлен"
fi

echo ""

# Проверка маршрутизации
echo "Маршрутизация:"
DEFAULT_ROUTE=$(route -n get default 2>/dev/null | grep gateway | awk '{print $2}')
if [ -n "$DEFAULT_ROUTE" ]; then
    echo "  Маршрут по умолчанию: $DEFAULT_ROUTE"
    
    # Проверяем, идет ли трафик через VPN
    if echo "$DEFAULT_ROUTE" | grep -q "192.168.42"; then
        echo "  Трафик идет через VPN"
    else
        echo "  Трафик идет через локальную сеть"
    fi
else
    echo "  ПРЕДУПРЕЖДЕНИЕ: Маршрут по умолчанию не найден"
fi

# Показываем маршруты через ng0
NG0_ROUTES=$(netstat -rn | grep ng0)
if [ -n "$NG0_ROUTES" ]; then
    echo "  Маршруты через ng0:"
    echo "$NG0_ROUTES" | sed 's/^/    /'
fi

echo ""

# Проверка подключения
echo "Подключение:"
if [ -n "$NG0_PEER" ]; then
    if ping -c 1 "$NG0_PEER" >/dev/null 2>&1; then
        echo "  VPN-сервер ($NG0_PEER) доступен"
    else
        echo "  VPN-сервер ($NG0_PEER) недоступен"
    fi
fi

# Проверка внешнего IP
EXTERNAL_IP=$(curl -s --max-time 5 https://ipinfo.io/ip 2>/dev/null)
if [ -n "$EXTERNAL_IP" ]; then
    echo "  Внешний IP: $EXTERNAL_IP"
else
    echo "  ПРЕДУПРЕЖДЕНИЕ: Не удалось определить внешний IP"
fi

echo ""
echo "Команды управления:"
echo "  vpnup    - запуск VPN"
echo "  vpndown  - остановка VPN"
echo "  vpnstatus - этот статус"
EOF
    
    # Делаем скрипты исполняемыми
    chmod +x /usr/local/bin/vpnup
    chmod +x /usr/local/bin/vpndown
    chmod +x /usr/local/bin/vpnstatus
    
    print_success "\033[1mСкрипты управления созданы\033[0m"
}

# Настройка автозапуска сервисов
configure_services() {
    print_header "\033[1mНАСТРОЙКА АВТОЗАПУСКА СЕРВИСОВ\033[0m"
    
    print_info "Добавление сервисов в автозапуск..."
    
    # Добавляем в rc.conf
    sysrc strongswan_enable="YES"
    sysrc mpd5_enable="YES"
    
    print_success "\033[1mСервисы добавлены в автозапуск\033[0m"
}

# Тестирование подключения
test_connection() {
    print_header "\033[1mТЕСТИРОВАНИЕ VPN ПОДКЛЮЧЕНИЯ\033[0m"
    
    print_info "Запуск тестового подключения..."
    
    # Запускаем VPN
    /usr/local/bin/vpnup --redirect-no
    
    sleep 5
    
    # Проверяем статус
    /usr/local/bin/vpnstatus
    
    print_info "Остановка тестового подключения..."
    /usr/local/bin/vpndown
    
    print_success "\033[1mТестирование завершено\033[0m"
}

# Вывод итоговой информации
print_final_info() {
    print_header "\033[1mУСТАНОВКА ЗАВЕРШЕНА\033[0m"
    
    print_success "\033[1mL2TP/IPsec VPN-клиент успешно установлен и настроен!\033[0m"
    
    echo ""
    print_info "Данные подключения:"
    echo "  Сервер: \033[1m$VPN_SERVER\033[0m"
    echo "  Пользователь: \033[1m$VPN_USERNAME\033[0m"
    echo "  PSK: \033[1m${VPN_PSK:0:10}...\033[0m"
    
    echo ""
    print_info "Команды управления:"
    echo "  \033[1mvpnup\033[0m           - запуск VPN (весь трафик через VPN)"
    echo "  \033[1mvpnup -n\033[0m        - запуск VPN (только туннель)"
    echo "  \033[1mvpndown\033[0m         - остановка VPN"
    echo "  \033[1mvpnstatus\033[0m       - проверка статуса"
    
    echo ""
    print_info "Файлы конфигурации:"
    echo "  /usr/local/etc/swanctl/swanctl.conf"
    echo "  /usr/local/etc/mpd5/mpd.conf"
    
    echo ""
    print_info "Логи:"
    echo "  /var/log/mpd.log"
    echo "  /var/log/messages (strongSwan)"
    
    echo ""
    print_warning "\033[1mДля запуска VPN выполните: vpnup\033[0m"
}

# Основная функция
main() {
    print_header "\033[1mFREEBSD L2TP/IPSEC VPN CLIENT INSTALLER\033[0m"
    print_info "\033[1mАвтоматическая установка и настройка VPN-клиента\033[0m"
    
    check_root
    check_os
    get_vpn_credentials
    install_packages
    configure_strongswan
    configure_mpd5
    create_management_scripts
    configure_services
    test_connection
    print_final_info
    
    print_success "\033[1mУстановка успешно завершена!\033[0m"
}

# Запуск основной функции
main "$@"
