본문 바로가기
Shell Script/Routing Check

(Bash) 리눅스 라우팅 파일 검증 스크립트

by 미니몬 2023. 9. 15.

목차

    728x90
    반응형

    Bash 스크립트 분석: 네트워크 라우팅 파일 검증 도구

    이번에 소개할 Bash 스크립트는 리눅스의 네트워크 라우팅 파일을 검증하기 위한 스크립트입니다.

    파일의 형식과 시스템의 라우팅 테이블과의 일치성을 확인합니다.

    스크립트의 주요 기능과 동작 방식에 대해 설명하겠습니다.

     

     

     

    1) 스크립트 개요

    이 스크립트는 /etc/sysconfig/network-scripts 경로 아래의 특정 네트워크 인터페이스에 대한

    라우팅 설정 파일(route-<InterfaceName>)을 검증합니다.

     

    파일 내용은 일반적으로 아래와 같습니다.

    #des1
    ADDRESS0=10.10.10.128
    NETMASK0=255.255.255.128
    GATEWAY0=192.168.227.1
    
    ADDRESS1=20.20.20.64
    NETMASK1=255.255.255.192
    GATEWAY1=192.168.227.1
    
    ADDRESS2=30.30.30.32
    NETMASK2=255.255.255.224
    GATEWAY2=192.168.227.1
    
    ADDRESS3=40.40.40.16
    NETMASK3=255.255.255.240
    GATEWAY3=192.168.227.1

    검증 과정은 기재된 ADDRESS, NETMASK, GATEWAY 값들이 순서대로 기재되어 있는지 검사하고, 

    IP주소 형식으로 되어 있는지와 각 옥텟별 구분자나 10진수의 0~255 의 값인지 확인합니다.

    그리고 마지막으로 현재 시스템에 적용되어 있는 라우팅 테이블과 다른 것이 있는 경우 알려줍니다.

    이 파일은 주로 CentOS나 Red Hat 기반 시스템에서 사용 라우팅 설정을 위해 사용하는 파일입니다.



     

    2) 스크립트 동작

    • 사용법 및 인자 확인:
      인자로 네트워크 인터페이스 이름(예: bond0)을 받아야 합니다.
      인자가 제공되지 않으면 스크립트는 사용법을 출력하고 종료합니다.
    usage_and_exit() {
        echo "    Error: No input value provided."
        echo "    Usage: $0 <Device_Name>"
        echo "       Ex: $0 bond0"
        exit 1
    }
    
    if [ -z "$1" ]; then
        usage_and_exit
    fi

     

     

    • 라우팅 파일 존재 확인:
      해당 인터페이스에 대한 라우팅 파일이 존재하는지 확인합니다.
    INTERFACE_NAME="$1"
    ROUTE_PATH="/etc/sysconfig/network-scripts"
    ROUTING_FILE="${ROUTE_PATH}/route-${INTERFACE_NAME}"
    
    if [ ! -e "$ROUTING_FILE" ]; then
        echo "Error: ${ROUTING_FILE} not found."
        exit 1
    fi

     

     

    • 구분자 및 형식 검사:
      파일 내의 각 라인에서 쉼표(,)가 있는지 검사합니다.
      라우팅 정보가 올바른 IP 주소 형식인지 검사합니다.
    ###########################################################
    # Check that the values that separate the fields in the file contain a comma (,) or are in valid IP address format.
    ###########################################################
    check_delimiter() {
        addresses=$(grep -E "^ADDRESS[0-9]+=.*" $ROUTING_FILE)
        netmasks=$(grep -E "^NETMASK[0-9]+=.*" $ROUTING_FILE)
        gateways=$(grep -E "^GATEWAY[0-9]+=.*" $ROUTING_FILE)
        all_info=$(echo "$addresses"; echo "$netmasks"; echo "$gateways")
    
        err_chk=0
        while IFS= read -r line; do
            if [[ $line == \#* ]]; then
                continue
            fi
            if echo "$line" | grep -q ','; then
                echo "Error: ',' (comma) found in line: $line"
                err_chk=1
            elif ! echo "$line" | grep -q -P '=(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])'; then
                echo "Error: Value in line is not in correct format: $line"
                err_chk=1
            fi
        done <<< "$all_info"
        return $err_chk
    }

     

     

    • 옥텟 값 검증:
      IP 주소의 각 옥텟 값이 0과 255 사이인지 확인합니다.
    ###########################################################
    # For each octet, check whether the value is between 0 and 255.
    ###########################################################
    check_octet() {
        while IFS= read -r line; do
            if [[ $line == \#* ]] || [[ -z $line ]]; then
                continue
            fi
    
            if [[ $line == ADDRESS* ]] || [[ $line == NETMASK* ]] || [[ $line == GATEWAY* ]]; then
                IFS="=" read -ra ADDR <<< "$line"
                IFS="." read -ra OCTETS <<< "${ADDR[1]}"
        
                for octet in "${OCTETS[@]}"; do
                    if [[ $octet -lt 0 || $octet -gt 255 ]]; then
                        echo "Error: Invalid value detected: $octet in $line"
                        exit 1
                    fi
                done
            fi
        done < $ROUTING_FILE
    }

     

     

    • 순서 확인:
      ADDRESS, NETMASK, GATEWAY 항목의 순서가 연속적인지 확인합니다.
      (예를 들어 ADDRESS0, ADDRESS1, ADDRESS2 ... 이런식으로)
    ###########################################################
    # Check that the order of fields (numbers) in the file is in the correct order (0, 1, 2, ...).
    ###########################################################
    check_sequence() {
        seq_chk=$(grep -E "^ADDRESS[0-9]+|^NETMASK[0-9]+|^GATEWAY[0-9]+" $ROUTING_FILE | awk -F'[A-Za-z]+' '{print $2}' | sort -nu | awk -F= '{print $1}')
        expected_seq=0
        err_chk=0
        for num in $seq_chk; do
            if [[ $num -ne $expected_seq ]]; then
                echo "Warning: Check the sequence of $ROUTING_FILE. Expected ${expected_seq} but got ${num}"
                err_chk=1
                break
            fi
            ((expected_seq+=1))
        done
    
        return $err_chk
    }

     

     

    • 라우팅 정보 검증:
      파일에 명시된 라우팅 정보와 시스템의 실제 라우팅 테이블이 일치하는지 확인합니다.
    ###########################################################
    # Reads the routing file and extracts address, netmask, and gateway information.
    ###########################################################
    read_routing_file() {
        local route_info=()
        while IFS="=" read -r key value; do
            if [[ $key == \#* ]]; then
                continue
            fi
            if [[ $key == ADDRESS* ]]; then
                local index="${key#ADDRESS}"
                local address="${value}"
                local netmask=$(eval echo "\${NETMASK${index}}")
                local gateway=$(eval echo "\${GATEWAY${index}}")
                route_info+=("${address} ${netmask} ${gateway}")
            fi
        done < "$ROUTING_FILE"
        echo "${route_info[@]}"
    }
    
    ###########################################################
    # Extract system routing table information related to a given interface.
    ###########################################################
    read_system_routing_table() {
        netstat -rn | grep -wv U | awk -v iface="$INTERFACE_NAME" 'NR > 2 && $1 != "0.0.0.0" && $NF == iface { print $1, $3, $2 }'
    }
    
    ###########################################################
    # Compares the information in the 'Routing Table' vs 'Routing File' 
    ###########################################################
    check_compare_routes() {
        local file_routes=($(read_routing_file))
        local sys_routes=($(read_system_routing_table))
        local found=false
        local chk_error=0
    
        for file_route in "${file_routes[@]}"; do
            found=false
            for sys_route in "${sys_routes[@]}"; do
                [[ "$file_route" == "$sys_route" ]] && found=true && break
            done
            $found || { echo "Warning: Route ${file_route} not found in the system routing table."; chk_error=1; }
        done
    
        return $chk_error
    }

     

     

     

    3) 스크립트 구조

    • 함수정의:
      스크립트 상단에는 여러 유틸리티 함수가 정의되어 있습니다.
      각 함수는 특정 검증 작업을 수행하며, 검증에 실패하면 오류 메시지를 출력합니다.

    • 실행 부분:
      스크립트 하단에서는 순서대로 라우팅 관련 정보를 검증하는 작업을 수행합니다.
    echo "===================================================="
    echo "### Check the delimiter of $ROUTING_FILE ###"
    check_delimiter
    if [ $? -eq 0 ]; then
       echo "Delimiter is OK.."
    else
       echo ""
       exit 1
    fi
    
    echo ""
    echo "### Check the Sequence of $ROUTING_FILE ###"
    check_sequence
    if [ $? -eq 0 ]; then
       echo "Sequence is OK.."
    else
       echo ""
       exit 1
    fi
    
    echo ""
    echo "### Check the Octet's Value of $ROUTING_FILE ###"
    check_file_octet
    if [ $? -eq 0 ]; then
        echo "Octet's value is OK.."
    else
        echo ""
        exit 1
    fi
    
    echo ""
    echo "### Check the routing information of $ROUTING_FILE ###"
    check_compare_routes
    if [ $? -eq 0 ]; then
        echo "Routing information is OK.."
    else
        echo "${ROUTING_FILE} file needs to be verified.."
    fi
    echo "===================================================="

     

     

    4) 결론

    전체코드 ↓

    더보기
    #!/bin/bash
    
    usage_and_exit() {
        echo "    Error: No input value provided."
        echo "    Usage: $0 <Device_Name>"
        echo "       Ex: $0 bond0"
        exit 1
    }
    
    if [ -z "$1" ]; then
        usage_and_exit
    fi
    
    INTERFACE_NAME="$1"
    ROUTE_PATH="/etc/sysconfig/network-scripts"
    ROUTING_FILE="${ROUTE_PATH}/route-${INTERFACE_NAME}"
    
    if [ ! -e "$ROUTING_FILE" ]; then
        echo "Error: ${ROUTING_FILE} not found."
        exit 1
    fi
    
    ###########################################################
    # Check that the values that separate the fields in the file contain a comma (,) or are in valid IP address format.
    ###########################################################
    check_delimiter() {
        addresses=$(grep -E "^ADDRESS[0-9]+=.*" $ROUTING_FILE)
        netmasks=$(grep -E "^NETMASK[0-9]+=.*" $ROUTING_FILE)
        gateways=$(grep -E "^GATEWAY[0-9]+=.*" $ROUTING_FILE)
        all_info=$(echo "$addresses"; echo "$netmasks"; echo "$gateways")
    
        err_chk=0
        while IFS= read -r line; do
            if [[ $line == \#* ]]; then
                continue
            fi
            if echo "$line" | grep -q ','; then
                echo "Error: ',' (comma) found in line: $line"
                err_chk=1
            elif ! echo "$line" | grep -q -P '=(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])'; then
                echo "Error: Value in line is not in correct format: $line"
                err_chk=1
            fi
        done <<< "$all_info"
        return $err_chk
    }
    
    ###########################################################
    # Check that the order of fields (numbers) in the file is in the correct order (0, 1, 2, ...).
    ###########################################################
    check_sequence() {
        seq_chk=$(grep -E "^ADDRESS[0-9]+|^NETMASK[0-9]+|^GATEWAY[0-9]+" $ROUTING_FILE | awk -F'[A-Za-z]+' '{print $2}' | sort -nu | awk -F= '{print $1}')
        expected_seq=0
        err_chk=0
        for num in $seq_chk; do
            if [[ $num -ne $expected_seq ]]; then
                echo "Warning: Check the sequence of $ROUTING_FILE. Expected ${expected_seq} but got ${num}"
                err_chk=1
                break
            fi
            ((expected_seq+=1))
        done
    
        return $err_chk
    }
    
    ###########################################################
    # For each octet, check whether the value is between 0 and 255.
    ###########################################################
    check_octet() {
        while IFS= read -r line; do
            if [[ $line == \#* ]] || [[ -z $line ]]; then
                continue
            fi
    
            if [[ $line == ADDRESS* ]] || [[ $line == NETMASK* ]] || [[ $line == GATEWAY* ]]; then
                IFS="=" read -ra ADDR <<< "$line"
                IFS="." read -ra OCTETS <<< "${ADDR[1]}"
        
                for octet in "${OCTETS[@]}"; do
                    if [[ $octet -lt 0 || $octet -gt 255 ]]; then
                        echo "Error: Invalid value detected: $octet in $line"
                        exit 1
                    fi
                done
            fi
        done < $ROUTING_FILE
    }
    
    ###########################################################
    # Reads the routing file and extracts address, netmask, and gateway information.
    ###########################################################
    read_routing_file() {
        local route_info=()
        while IFS="=" read -r key value; do
            if [[ $key == \#* ]]; then
                continue
            fi
            if [[ $key == ADDRESS* ]]; then
                local index="${key#ADDRESS}"
                local address="${value}"
                local netmask=$(eval echo "\${NETMASK${index}}")
                local gateway=$(eval echo "\${GATEWAY${index}}")
                route_info+=("${address} ${netmask} ${gateway}")
            fi
        done < "$ROUTING_FILE"
        echo "${route_info[@]}"
    }
    
    ###########################################################
    # Extract system routing table information related to a given interface.
    ###########################################################
    read_system_routing_table() {
        netstat -rn | grep -wv U | awk -v iface="$INTERFACE_NAME" 'NR > 2 && $1 != "0.0.0.0" && $NF == iface { print $1, $3, $2 }'
    }
    
    ###########################################################
    # Compares the information in the 'Routing Table' vs 'Routing File' 
    ###########################################################
    check_compare_routes() {
        local file_routes=($(read_routing_file))
        local sys_routes=($(read_system_routing_table))
        local found=false
        local chk_error=0
    
        for file_route in "${file_routes[@]}"; do
            found=false
            for sys_route in "${sys_routes[@]}"; do
                [[ "$file_route" == "$sys_route" ]] && found=true && break
            done
            $found || { echo "Warning: Route ${file_route} not found in the system routing table."; chk_error=1; }
        done
    
        return $chk_error
    }
    
    echo "===================================================="
    echo "### Check the delimiter of $ROUTING_FILE ###"
    check_delimiter
    if [ $? -eq 0 ]; then
       echo "Delimiter is OK.."
    else
       echo ""
       exit 1
    fi
    
    echo ""
    echo "### Check the sequence of $ROUTING_FILE ###"
    check_sequence
    if [ $? -eq 0 ]; then
       echo "sequence is OK.."
    else
       echo ""
       exit 1
    fi
    
    echo ""
    echo "### Check the Octet's Value of $ROUTING_FILE ###"
    check_file_octet
    if [ $? -eq 0 ]; then
        echo "Octet's value is OK.."
    else
        echo ""
        exit 1
    fi
    
    echo ""
    echo "### Check the routing information of $ROUTING_FILE ###"
    check_compare_routes
    if [ $? -eq 0 ]; then
        echo "Routing information is OK.."
    else
        echo "${ROUTING_FILE} file needs to be verified.."
    fi
    echo "===================================================="


    네트워크 관리자나 시스템 관리자가 라우팅 설정의 오류나 불일치를 빠르게 찾아낼 수 있도록 도와줍니다.

    정확한 네트워크 구성은 시스템의 안정성과 성능에 중요한 역할을 하므로,

    이와 같은 검증 도구는 유용하게 사용될 수 있습니다.

    이상으로, 리눅스에서 사용되는 라우팅 설정 파일을 검증하는 bash 스크립트에 대한 설명을 마치겠습니다. 

    스크립트 사용이나 수정에 필요한 추가 정보가 있으면 문의해주세요!

    728x90
    반응형