Anonymous Coward 2017-05-01 22:39:46.76752 UTC
| 1 | #!/bin/sh |
| 2 | |
| 3 | if [[ $# -lt 1 ]] |
| 4 | then |
| 5 | echo $(basename "$0")" ip-file [netmask_min [netmask_max]]" |
| 6 | echo "Please supply an input file as the first parameter. It should contain a list of IPv4 addresses, one per line." |
| 7 | echo "You may additionally specify a minimum and maximum netmask length to search for. (default: 16-31)" |
| 8 | exit -1 |
| 9 | fi |
| 10 | |
| 11 | if [[ ! -r "$1" ]] |
| 12 | then |
| 13 | echo "The file '$1' does not exist or is not readable." |
| 14 | exit -2 |
| 15 | fi |
| 16 | |
| 17 | NETMASK_MIN=16 |
| 18 | NETMASK_MAX=31 |
| 19 | |
| 20 | if [[ $# -ge 2 ]] |
| 21 | then |
| 22 | if [[ $2 -lt 1 || $2 -gt 31 ]] |
| 23 | then |
| 24 | echo "You can only search for netmasks of length 1-31." |
| 25 | exit -1 |
| 26 | fi |
| 27 | NETMASK_MIN=$2 |
| 28 | |
| 29 | if [[ $# -ge 3 ]] |
| 30 | then |
| 31 | if [[ $2 -lt 1 || $2 -gt 31 || $3 -lt $NETMASK_MIN ]] |
| 32 | then |
| 33 | echo "You can only search for netmasks of length 1-31 (and the maximum netmask length must be greater or equal to the minimum netmask length)." |
| 34 | exit -1 |
| 35 | fi |
| 36 | NETMASK_MAX=$3 |
| 37 | fi |
| 38 | fi |
| 39 | |
| 40 | TMPFILE=`mktemp -t bin-ips.XXXXXXXXXX` |
| 41 | PREFIXES=`mktemp -t prefixes.XXXXXXXXXX` |
| 42 | if [[ -z "$TMPFILE" || -z "$PREFIXES" ]] |
| 43 | then |
| 44 | echo "Could not create temporary file to store converted IPs." |
| 45 | exit -3 |
| 46 | fi |
| 47 | |
| 48 | sort -u "$1" | awk -F '.' 'function bits(i) {\ |
| 49 | return sprintf("%d%d%d%d%d%d%d%d", \ |
| 50 | rshift(and(i, 128), 7), \ |
| 51 | rshift(and(i, 64), 6), \ |
| 52 | rshift(and(i, 32), 5), \ |
| 53 | rshift(and(i, 16), 4), \ |
| 54 | rshift(and(i, 8), 3), \ |
| 55 | rshift(and(i, 4), 2), \ |
| 56 | rshift(and(i, 2), 1), \ |
| 57 | and(i, 1));\ |
| 58 | } \ |
| 59 | \ |
| 60 | {printf("%s%s%s%s\n", bits($1), bits($2), bits($3), bits($4)) }' > "$TMPFILE" |
| 61 | |
| 62 | while [[ $NETMASK_MIN -le $NETMASK_MAX ]] |
| 63 | do |
| 64 | #echo "Searching for /$NETMASK_MIN nets..." |
| 65 | cidr_full_count=$(echo "2^(32-$NETMASK_MIN)" | bc) |
| 66 | cut -c 1-$NETMASK_MIN "$TMPFILE" | sort | uniq -c | sort -n | grep -v '^\s*1\s' | while read candidate |
| 67 | do |
| 68 | dup_count=$(echo -n "$candidate" | awk '{print $1}') |
| 69 | if [[ $dup_count -eq $cidr_full_count ]] |
| 70 | then |
| 71 | net=$(echo -n "$candidate" | awk '{print $2;}') |
| 72 | for prefix in $(cat "$PREFIXES") # XXX This will fail if we have found too many CIDR nets |
| 73 | do |
| 74 | if echo $net | grep -q "^$prefix" |
| 75 | then |
| 76 | #echo "We already have a prefix $prefix matching $net" |
| 77 | continue 2; |
| 78 | fi |
| 79 | done |
| 80 | #echo "Got a match, there are $dup_count entries for $net/$NETMASK_MIN" |
| 81 | echo "$net" >> "$PREFIXES" |
| 82 | #else |
| 83 | #echo "not enough matches ($cidr_full_count required) in $candidate" |
| 84 | fi |
| 85 | done |
| 86 | NETMASK_MIN=$(($NETMASK_MIN+1)) |
| 87 | done |
| 88 | |
| 89 | # Let's append the IPs that we could not sum up into CIDR nets to $PREFIXES |
| 90 | GREP_EXPRS="" |
| 91 | for prefix in $(cat "$PREFIXES") # XXX This will fail if we have found too many CIDR nets |
| 92 | do |
| 93 | GREP_EXPRS="$GREP_EXPRS -e ^$prefix" |
| 94 | done |
| 95 | # There are no quotes around $GREP_EXPRS because we want the shell to expand it. |
| 96 | grep -v $GREP_EXPRS "$TMPFILE" >> "$PREFIXES" |
| 97 | |
| 98 | awk '{ |
| 99 | padlen = 32-length($0); |
| 100 | padded = sprintf(sprintf("%%s%%0%dd", padlen), $0, 0); |
| 101 | sep = "."; |
| 102 | for(octet = 0; octet < 4; octet++) |
| 103 | { |
| 104 | value = 0; |
| 105 | for(i = 1; i <= 8; i++) { |
| 106 | value *= 2; |
| 107 | value += int(substr(padded, octet*8+i, 1)); |
| 108 | } |
| 109 | if(octet >= 3) { |
| 110 | sep = "/"; |
| 111 | } |
| 112 | printf("%d%c", value, sep); |
| 113 | } |
| 114 | print(32-padlen); |
| 115 | }' "$PREFIXES" |
| 116 | |
| 117 | rm "$TMPFILE" "$PREFIXES" |