ips-to-cidr.sh

Anonymous Coward 2017-05-01 22:39:46.76752 UTC

1#!/bin/sh
2
3if [[ $# -lt 1 ]]
4then
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
9fi
10
11if [[ ! -r "$1" ]]
12then
13 echo "The file '$1' does not exist or is not readable."
14 exit -2
15fi
16
17NETMASK_MIN=16
18NETMASK_MAX=31
19
20if [[ $# -ge 2 ]]
21then
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
38fi
39
40TMPFILE=`mktemp -t bin-ips.XXXXXXXXXX`
41PREFIXES=`mktemp -t prefixes.XXXXXXXXXX`
42if [[ -z "$TMPFILE" || -z "$PREFIXES" ]]
43then
44 echo "Could not create temporary file to store converted IPs."
45 exit -3
46fi
47
48sort -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
62while [[ $NETMASK_MIN -le $NETMASK_MAX ]]
63do
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))
87done
88
89# Let's append the IPs that we could not sum up into CIDR nets to $PREFIXES
90GREP_EXPRS=""
91for prefix in $(cat "$PREFIXES") # XXX This will fail if we have found too many CIDR nets
92do
93 GREP_EXPRS="$GREP_EXPRS -e ^$prefix"
94done
95# There are no quotes around $GREP_EXPRS because we want the shell to expand it.
96grep -v $GREP_EXPRS "$TMPFILE" >> "$PREFIXES"
97
98awk '{
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
117rm "$TMPFILE" "$PREFIXES"