Bash script to list all IPs in prefix

后端 未结 11 1566
[愿得一人]
[愿得一人] 2020-12-23 12:23

I\'m trying to create script that I can input a set of prefixes, which will then list all IP addresses within the prefixes (including network/host/broadcast).

An ex

相关标签:
11条回答
  • 2020-12-23 13:20

    Here is what I use to generate all the IP addresses in a given CIDR block

    nmap -sL -n 10.10.64.0/27 | awk '/Nmap scan report/{print $NF}'
    

    From the nmap man page, the flags are:

    -sL: List Scan - simply list targets to scan
    -n: Never do DNS resolution
    

    Just that simple

    The above command outputs this

    10.10.64.0
    10.10.64.1
    10.10.64.2
    10.10.64.3
    10.10.64.4
    10.10.64.5
    10.10.64.6
    10.10.64.7
    10.10.64.8
    10.10.64.9
    10.10.64.10
    10.10.64.11
    10.10.64.12
    10.10.64.13
    10.10.64.14
    10.10.64.15
    10.10.64.16
    10.10.64.17
    10.10.64.18
    10.10.64.19
    10.10.64.20
    10.10.64.21
    10.10.64.22
    10.10.64.23
    10.10.64.24
    10.10.64.25
    10.10.64.26
    10.10.64.27
    10.10.64.28
    10.10.64.29
    10.10.64.30
    10.10.64.31
    
    0 讨论(0)
  • 2020-12-23 13:21

    I recently wrote a function to generate all IP addresses from a given network address. The function takes the network address as argument and accepts CIDR and subnet masks. The script then stores all IPs in the array variable $ips.

    Code

    network_address_to_ips() {
      # create array containing network address and subnet
      local network=(${1//\// })
      # split network address by dot
      local iparr=(${network[0]//./ })
      # if no mask given it's the same as /32
      local mask=32
      [[ $((${#network[@]})) -gt 1 ]] && mask=${network[1]}
    
      # convert dot-notation subnet mask or convert CIDR to an array like (255 255 255 0)
      local maskarr
      if [[ ${mask} =~ '.' ]]; then  # already mask format like 255.255.255.0
        maskarr=(${mask//./ })
      else                           # assume CIDR like /24, convert to mask
        if [[ $((mask)) -lt 8 ]]; then
          maskarr=($((256-2**(8-mask))) 0 0 0)
        elif  [[ $((mask)) -lt 16 ]]; then
          maskarr=(255 $((256-2**(16-mask))) 0 0)
        elif  [[ $((mask)) -lt 24 ]]; then
          maskarr=(255 255 $((256-2**(24-mask))) 0)
        elif [[ $((mask)) -lt 32 ]]; then
          maskarr=(255 255 255 $((256-2**(32-mask))))
        elif [[ ${mask} == 32 ]]; then
          maskarr=(255 255 255 255)
        fi
      fi
    
      # correct wrong subnet masks (e.g. 240.192.255.0 to 255.255.255.0)
      [[ ${maskarr[2]} == 255 ]] && maskarr[1]=255
      [[ ${maskarr[1]} == 255 ]] && maskarr[0]=255
    
      # generate list of ip addresses
      local bytes=(0 0 0 0)
      for i in $(seq 0 $((255-maskarr[0]))); do
        bytes[0]="$(( i+(iparr[0] & maskarr[0]) ))"
        for j in $(seq 0 $((255-maskarr[1]))); do
          bytes[1]="$(( j+(iparr[1] & maskarr[1]) ))"
          for k in $(seq 0 $((255-maskarr[2]))); do
            bytes[2]="$(( k+(iparr[2] & maskarr[2]) ))"
            for l in $(seq 1 $((255-maskarr[3]))); do
              bytes[3]="$(( l+(iparr[3] & maskarr[3]) ))"
              printf "%d.%d.%d.%d\n" "${bytes[@]}"
            done
          done
        done
      done
    }
    

    Example

    network_address_to_ips 10.0.1.0/255.255.255.240
    network_address_to_ips 10.1.0.0/24
    
    0 讨论(0)
  • 2020-12-23 13:24

    nmap is useful, but an overkill.

    You can use prips instead. Saves you the hassle of grepping out the extra output from nmap and using awk.

    Calling prips 192.168.0.0/23 will print what you need.

    I use the following to skip the network address and broadcast: prips "$subnet" | sed -e '1d; $d'

    Prips also has other useful options, e.g. being able to sample every n-th IP.

    It's available via apt,brew,rpm and as tar.gz.

    0 讨论(0)
  • 2020-12-23 13:25

    I have extended @rberg script a little.

    • check if the "network" you provide really is a network (use -f to skip the check)
    • handle netmasks greater than /24

    Maybe this is of use for someone.

    #!/bin/bash
    
    ############################
    ##  Methods
    ############################   
    prefix_to_bit_netmask() {
        prefix=$1;
        shift=$(( 32 - prefix ));
    
        bitmask=""
        for (( i=0; i < 32; i++ )); do
            num=0
            if [ $i -lt $prefix ]; then
                num=1
            fi
    
            space=
            if [ $(( i % 8 )) -eq 0 ]; then
                space=" ";
            fi
    
            bitmask="${bitmask}${space}${num}"
        done
        echo $bitmask
    }
    
    bit_netmask_to_wildcard_netmask() {
        bitmask=$1;
        wildcard_mask=
        for octet in $bitmask; do
            wildcard_mask="${wildcard_mask} $(( 255 - 2#$octet ))"
        done
        echo $wildcard_mask;
    }
    
    check_net_boundary() {
        net=$1;
        wildcard_mask=$2;
        is_correct=1;
        for (( i = 1; i <= 4; i++ )); do
            net_octet=$(echo $net | cut -d '.' -f $i)
            mask_octet=$(echo $wildcard_mask | cut -d ' ' -f $i)
            if [ $mask_octet -gt 0 ]; then
                if [ $(( $net_octet&$mask_octet )) -ne 0 ]; then
                    is_correct=0;
                fi
            fi
        done
        echo $is_correct;
    }
    
    #######################
    ##  MAIN
    #######################
    OPTIND=1;
    getopts "f" force;
    shift $(( OPTIND-1 ));
    
    for ip in $@; do
        net=$(echo $ip | cut -d '/' -f 1);
        prefix=$(echo $ip | cut -d '/' -f 2);
        do_processing=1;
    
        bit_netmask=$(prefix_to_bit_netmask $prefix);
    
        wildcard_mask=$(bit_netmask_to_wildcard_netmask "$bit_netmask");
        is_net_boundary=$(check_net_boundary $net "$wildcard_mask");
    
        if [ $force != 'f' ] && [ $is_net_boundary -ne 1 ]; then
            read -p "Not a network boundary! Continue anyway (y/N)? " -n 1 -r
            echo    ## move to a new line
            if [[ $REPLY =~ ^[Yy]$ ]]; then
                do_processing=1;
            else
                do_processing=0;
            fi
        fi  
    
        if [ $do_processing -eq 1 ]; then
            str=
            for (( i = 1; i <= 4; i++ )); do
                range=$(echo $net | cut -d '.' -f $i)
                mask_octet=$(echo $wildcard_mask | cut -d ' ' -f $i)
                if [ $mask_octet -gt 0 ]; then
                    range="{$range..$(( $range | $mask_octet ))}";
                fi
                str="${str} $range"
            done
            ips=$(echo $str | sed "s, ,\\.,g"); ## replace spaces with periods, a join...
    
            eval echo $ips | tr ' ' '\012'
        fi
    
    done
    
    0 讨论(0)
  • 2020-12-23 13:28

    This script should do. It's (almost) pure Bash. The seq part can be replaced if a completely pure bash is required.

    Since Bash apparently uses signed two-complement 4-byte integers, the script is limited to /8 mask maximum. I found ranges larger than /16 impractical anyway so this doesn't bother me at all. If someone knows a simple way to overcome this, please share :)

    #!/usr/bin/env bash
    
    base=${1%/*}
    masksize=${1#*/}
    
    [ $masksize -lt 8 ] && { echo "Max range is /8."; exit 1;}
    
    mask=$(( 0xFFFFFFFF << (32 - $masksize) ))
    
    IFS=. read a b c d <<< $base
    
    ip=$(( ($b << 16) + ($c << 8) + $d ))
    
    ipstart=$(( $ip & $mask ))
    ipend=$(( ($ipstart | ~$mask ) & 0x7FFFFFFF ))
    
    seq $ipstart $ipend | while read i; do
        echo $a.$(( ($i & 0xFF0000) >> 16 )).$(( ($i & 0xFF00) >> 8 )).$(( $i & 0x00FF ))
    done 
    

    Usage:

    ./script.sh 192.168.13.55/22
    

    Tested with Bash version 4.4.23. YMMV.

    0 讨论(0)
提交回复
热议问题