#!/bin/bash # Script Dinâmico de Configuração de Rede para Raspberry Pi # Localização: /usr/local/bin/configure_network.sh # Descrição: Configura MAC address persistente para interface eth0 e monitoriza estado da rede # --- Configurações --- LOG_FILE="/var/log/network_config.log" UDEV_RULES_FILE="/etc/udev/rules.d/81-mac-spoof.rules" IFACE="eth0" WLAN_IFACE="wlan0" # --- Funções Auxiliares --- # Função de Log log() { local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "$timestamp: $1" | tee -a "$LOG_FILE" } # Função para verificar se a interface tem IP has_ip_address() { local iface="$1" if ip addr show "$iface" | grep -q "inet "; then return 0 else return 1 fi } # Função para perguntar confirmação ao utilizador ask_confirmation() { local message="$1" local default="$2" # "y" or "n" while true; do if [ "$default" = "y" ]; then read -p "$message [Y/n]: " choice < /dev/tty else read -p "$message [y/N]: " choice < /dev/tty fi # Default choice if user just presses Enter if [ -z "$choice" ]; then choice="$default" fi case "$choice" in y|Y|yes|Yes|YES) return 0 ;; n|N|no|No|NO) return 1 ;; *) echo "Por favor, responda sim ou não." ;; esac done } # Função para gerar MAC único baseado no Serial do CPU generate_unique_mac() { local serial serial=$(awk '/Serial/ {print $3}' /proc/cpuinfo | tr -d ' ') if [ -z "$serial" ]; then # Fallback: generate random MAC if serial not available printf '%02X:%02X:%02X:%02X:%02X:%02X' $((0x02 | (RANDOM % 256) & 0xFE)) \ $((RANDOM % 256)) \ $((RANDOM % 256)) \ $((RANDOM % 256)) \ $((RANDOM % 256)) \ $((RANDOM % 256)) else local hash hash=$(echo -n "$serial-dm9601" | md5sum | cut -c1-10) echo "02:${hash:0:2}:${hash:2:2}:${hash:4:2}:${hash:6:2}:${hash:8:2}" fi } # Função para obter o MAC atual da interface get_current_mac() { local interface="$1" ip link show "$interface" | awk '/ether/ {print $2}' | tr '[:upper:]' '[:lower:]' } # Função para definir MAC imediatamente set_mac() { local interface="$1" local mac="$2" if ! ip link show "$interface" &> /dev/null; then log "Interface $interface não encontrada em set_mac." return 1 fi log "A definir MAC $mac em $interface..." if ip link set "$interface" down && \ ip link set dev "$interface" address "$mac" && \ ip link set "$interface" up; then log "MAC alterado com sucesso na sessão atual." return 0 else log "Falha ao alterar MAC na sessão atual." return 1 fi } # Função para criar/atualizar regra Udev setup_udev_persistence() { local interface="$1" local target_mac="$2" local original_mac local rule_content original_mac=$(get_current_mac "$interface") target_mac=$(echo "$target_mac" | tr '[:upper:]' '[:lower:]') if [ -z "$original_mac" ]; then log "Erro crítico: Não foi possível obter o MAC original para criar regra udev." return 1 fi # Cria o conteúdo da regra rule_content="ACTION==\"add\", SUBSYSTEM==\"net\", ATTR{address}==\"$original_mac\", RUN+=\"/usr/bin/ip link set dev \$name address $target_mac\"" # Cria diretório se não existir mkdir -p "$(dirname "$UDEV_RULES_FILE")" # Escreve no ficheiro de regras echo "$rule_content" > "$UDEV_RULES_FILE" # Verifica se o ficheiro foi criado com sucesso if [ -f "$UDEV_RULES_FILE" ]; then log "Sucesso: Ficheiro de regras Udev criado/atualizado em $UDEV_RULES_FILE." log "Conteúdo da regra: $rule_content" else log "Erro: Falha ao criar/atualizar o ficheiro de regras Udev em $UDEV_RULES_FILE." return 1 fi # Recarrega as regras do udev if udevadm control --reload-rules && udevadm trigger; then log "Regras Udev recarregadas e aplicadas com sucesso." return 0 else log "Erro: Falha ao recarregar/aplicar regras Udev." return 1 fi } # Função para verificar se a persistência está configurada corretamente is_persistence_configured() { local current_mac local udev_mac if [ ! -f "$UDEV_RULES_FILE" ] || [ ! -s "$UDEV_RULES_FILE" ]; then log "Ficheiro de persistência não encontrado ou vazio." return 1 fi current_mac=$(get_current_mac "$IFACE") udev_mac=$(grep -oP 'address \K([0-9a-f]{2}:){5}[0-9a-f]{2}' "$UDEV_RULES_FILE") if [ "$current_mac" = "$udev_mac" ]; then log "Persistência verificada: MAC atual ($current_mac) coincide com regra Udev." return 0 else log "Persistência desatualizada: MAC atual ($current_mac) não coincide com regra Udev ($udev_mac)." return 1 fi } # Função para desativar Wi-Fi disable_wifi() { # Cria diretório de log se não existir mkdir -p /var/log # Aguarda 60 segundos antes de verificar IP e desativar Wi-Fi log "A aguardar 60 segundos antes de verificar IP de $IFACE e desativar Wi-Fi..." sleep 60 # Verifica se eth0 tem IP antes de desativar Wi-Fi if has_ip_address "$IFACE"; then if ip link show "$WLAN_IFACE" &> /dev/null; then if ip link set "$WLAN_IFACE" down; then log "Wi-Fi ($WLAN_IFACE) desativado com sucesso ($IFACE tem IP)" else log "Falha ao desativar Wi-Fi ($WLAN_IFACE)" fi else log "Interface $WLAN_IFACE não encontrada (Wi-Fi já desativado?)" fi else log "$IFACE não tem endereço IP. Wi-Fi permanece ativo." fi } # Tarefa em Background para monitorizar rede monitor_network_task() { log "Monitor: A iniciar monitorização contínua da rede..." while true; do sleep 60 # Verifica a cada 60 segundos if ip link show "$IFACE" &> /dev/null; then # Verifica se eth0 está sem cabo (NO-CARRIER) if ip link show "$IFACE" | grep -q "NO-CARRIER"; then log "Monitor: Interface $IFACE sem cabo (NO-CARRIER). A ativar Wi-Fi..." if ip link show "$WLAN_IFACE" &> /dev/null; then if ip link set "$WLAN_IFACE" up; then log "Monitor: Wi-Fi ($WLAN_IFACE) ativado com sucesso." else log "Monitor: Falha ao ativar Wi-Fi ($WLAN_IFACE)." fi else log "Monitor: Interface $WLAN_IFACE não encontrada." fi else # eth0 está com cabo, verifica se tem IP if has_ip_address "$IFACE"; then log "Monitor: Interface $IFACE ativa e com IP. A verificar Wi-Fi..." if ip link show "$WLAN_IFACE" &> /dev/null && ip link show "$WLAN_IFACE" | grep -q "UP"; then if ip link set "$WLAN_IFACE" down; then log "Monitor: Wi-Fi ($WLAN_IFACE) desativado (eth0 tem IP)." else log "Monitor: Falha ao desativar Wi-Fi ($WLAN_IFACE)." fi else log "Monitor: Wi-Fi ($WLAN_IFACE) já está desativado ou não encontrado." fi else log "Monitor: Interface $IFACE ativa mas sem IP. Wi-Fi permanece ativo." fi fi else log "Monitor: Interface $IFACE não encontrada." fi done } # Função principal para configurar MAC configure_mac() { local target_mac="$1" # Validação do MAC if [[ ! "$target_mac" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then log "MAC Address inválido: $target_mac" return 1 fi log "A configurar sistema com MAC: $target_mac" # Aplica o MAC na sessão atual if set_mac "$IFACE" "$target_mac"; then # Atualiza a regra Udev independentemente do estado anterior if setup_udev_persistence "$IFACE" "$target_mac"; then log "Configuração de MAC e persistência concluída com sucesso." return 0 else log "Aviso: MAC definido mas persistência falhou." return 1 fi else log "Aviso: Falha ao definir MAC em tempo real, mas a tentar criar persistência..." if setup_udev_persistence "$IFACE" "$target_mac"; then log "Persistência criada, mas MAC não foi aplicado na sessão atual." return 1 else log "Erro: Falha completa na configuração de MAC." return 1 fi fi } # --- Execução Principal --- if [ "$(id -u)" -ne 0 ]; then echo "Este script deve ser executado como root." >&2 exit 1 fi mkdir -p "$(dirname "$LOG_FILE")" # 1. Verificar se a interface existe if ! ip link show "$IFACE" &> /dev/null; then log "Erro: Interface $IFACE não disponível. A sair." exit 1 fi # 2. Verificar se já existe configuração de persistência if is_persistence_configured; then log "Configuração de persistência verificada. A saltar configuração inicial." # Verifica se eth0 tem IP antes de desativar Wi-Fi if has_ip_address "$IFACE"; then if ! pgrep -f "disable_wifi" > /dev/null; then disable_wifi & else log "disable_wifi já está em execução." fi else log "$IFACE não tem endereço IP. Wi-Fi permanece ativo." fi monitor_network_task & exit 0 fi # 3. Determinar o MAC Address TARGET_MAC="" # Verificar argumento ($1) if [ -n "$1" ]; then TARGET_MAC="$1" log "MAC definido via argumento: $TARGET_MAC" else # Verificar se é boot (não interativo e sem /dev/tty disponível) if [ ! -t 0 ] && [ ! -e /dev/tty ]; then # Modo boot: Não fazer nada, assumindo que já foi configurado log "Modo boot: A saltar configuração de MAC (assumindo que já foi configurado)" if has_ip_address "$IFACE"; then if ! pgrep -f "disable_wifi" > /dev/null; then disable_wifi & else log "disable_wifi já está em execução." fi else log "$IFACE não tem endereço IP. Wi-Fi permanece ativo." fi exit 0 else # Modo interativo: Perguntar ao utilizador o que fazer if ask_confirmation "Deseja configurar um novo MAC para a interface $IFACE?" "y"; then if ask_confirmation "Deseja gerar um novo MAC único automaticamente?" "y"; then TARGET_MAC=$(generate_unique_mac) log "MAC gerado automaticamente: $TARGET_MAC" else # Pergunta para introduzir MAC manualmente while true; do read -p "Introduza o MAC (formato XX:XX:XX:XX:XX:XX): " TARGET_MAC < /dev/tty if [[ "$TARGET_MAC" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then break else echo "Formato de MAC inválido. Por favor, introduza no formato XX:XX:XX:XX:XX:XX." fi done fi else log "Configuração de MAC cancelada pelo utilizador" exit 0 fi fi fi # 4. Configurar MAC e persistência if configure_mac "$TARGET_MAC"; then log "Configuração concluída com sucesso." else log "Aviso: Configuração concluída com alguns erros." fi # 5. Lançar monitor de rede monitor_network_task & exit 0