@@ -2,12 +2,10 @@ package lib
22
33import (
44 "bufio"
5- "bytes"
65 "fmt"
76 "io"
87 "net"
98 "os"
10- "sort"
119 "strings"
1210
1311 "github.com/spf13/pflag"
@@ -55,72 +53,21 @@ func CmdToolAggregate(
5553 return nil
5654 }
5755
58- // Parses a list of CIDRs.
59- parseCIDRs := func (cidrs []string ) []net.IPNet {
60- parsedCIDRs := make ([]net.IPNet , 0 )
61- for _ , cidrStr := range cidrs {
62- _ , ipNet , err := net .ParseCIDR (cidrStr )
63- if err != nil {
64- if ! f .Quiet {
65- fmt .Printf ("Invalid CIDR: %s\n " , cidrStr )
66- }
67- continue
68- }
69- parsedCIDRs = append (parsedCIDRs , * ipNet )
70- }
71-
72- return parsedCIDRs
73- }
74-
7556 // Input parser.
76- parseInput := func (rows []string ) ([]net. IPNet , []net.IP ) {
77- parsedCIDRs := make ([]net. IPNet , 0 )
57+ parseInput := func (rows []string ) ([]string , []net.IP ) {
58+ parsedCIDRs := make ([]string , 0 )
7859 parsedIPs := make ([]net.IP , 0 )
79- var separator string
8060 for _ , rowStr := range rows {
8161 if strings .ContainsAny (rowStr , ",-" ) {
82- if delim := strings .ContainsRune (rowStr , ',' ); delim {
83- separator = ","
84- } else {
85- separator = "-"
86- }
87-
88- ipRange := strings .Split (rowStr , separator )
89- if len (ipRange ) != 2 {
90- if ! f .Quiet {
91- fmt .Printf ("Invalid IP range: %s\n " , rowStr )
92- }
93- continue
94- }
95-
96- if strings .ContainsRune (rowStr , ':' ) {
97- cidrs , err := CIDRsFromIP6RangeStrRaw (rowStr )
98- if err == nil {
99- parsedCIDRs = append (parsedCIDRs , parseCIDRs (cidrs )... )
100- continue
101- } else {
102- if ! f .Quiet {
103- fmt .Printf ("Invalid IP range %s. Err: %v\n " , rowStr , err )
104- }
105- continue
106- }
107- } else {
108- cidrs , err := CIDRsFromIPRangeStrRaw (rowStr )
109- if err == nil {
110- parsedCIDRs = append (parsedCIDRs , parseCIDRs (cidrs )... )
111- continue
112- } else {
113- if ! f .Quiet {
114- fmt .Printf ("Invalid IP range %s. Err: %v\n " , rowStr , err )
115- }
116- continue
117- }
118- }
62+ continue
11963 } else if strings .ContainsRune (rowStr , '/' ) {
120- parsedCIDRs = append (parsedCIDRs , parseCIDRs ([]string {rowStr })... )
64+ _ , ipnet , err := net .ParseCIDR (rowStr )
65+ if err == nil && IsCIDRIPv4 (ipnet ) {
66+ parsedCIDRs = append (parsedCIDRs , []string {rowStr }... )
67+ }
12168 continue
12269 } else {
123- if ip := net .ParseIP (rowStr ); ip != nil {
70+ if ip := net .ParseIP (rowStr ); IsIPv4 ( ip ) {
12471 parsedIPs = append (parsedIPs , ip )
12572 } else {
12673 if ! f .Quiet {
@@ -165,7 +112,7 @@ func CmdToolAggregate(
165112 }
166113
167114 // Vars to contain CIDRs/IPs from all input sources.
168- parsedCIDRs := make ([]net. IPNet , 0 )
115+ parsedCIDRs := make ([]string , 0 )
169116 parsedIPs := make ([]net.IP , 0 )
170117
171118 // Collect CIDRs/IPs from stdin.
@@ -187,93 +134,106 @@ func CmdToolAggregate(
187134 rows := scanrdr (file )
188135 file .Close ()
189136 cidrs , ips := parseInput (rows )
137+
190138 parsedCIDRs = append (parsedCIDRs , cidrs ... )
191139 parsedIPs = append (parsedIPs , ips ... )
192140 }
193141
194- // Sort and merge collected CIDRs and IPs.
195- aggregatedCIDRs := aggregateCIDRs ( parsedCIDRs )
142+ adjacentCombined := combineAdjacent ( stripOverlapping ( list ( parsedCIDRs )))
143+
196144 outlierIPs := make ([]net.IP , 0 )
197- length := len (aggregatedCIDRs )
198- for _ , ip := range parsedIPs {
199- for i , cidr := range aggregatedCIDRs {
200- if cidr .Contains (ip ) {
201- break
202- } else if i == length - 1 {
203- outlierIPs = append (outlierIPs , ip )
145+ length := len (adjacentCombined )
146+ if length != 0 {
147+ for _ , ip := range parsedIPs {
148+ for i , cidr := range adjacentCombined {
149+ if cidr .Network .Contains (ip ) {
150+ break
151+ } else if i == length - 1 {
152+ outlierIPs = append (outlierIPs , ip )
153+ }
204154 }
205155 }
156+ } else {
157+ outlierIPs = append (outlierIPs , parsedIPs ... )
206158 }
207159
208160 // Print the aggregated CIDRs.
209- for _ , r := range aggregatedCIDRs {
161+ for _ , r := range adjacentCombined {
210162 fmt .Println (r .String ())
211163 }
212164
213- // Print outliers .
165+ // Print the outlierIPs .
214166 for _ , r := range outlierIPs {
215167 fmt .Println (r .String ())
216168 }
217169
218170 return nil
219171}
220172
221- // Helper function to aggregate IP ranges.
222- func aggregateCIDRs (cidrs []net.IPNet ) []net.IPNet {
223- aggregatedCIDRs := make ([]net.IPNet , 0 )
224-
225- // Sort CIDRs by starting IP.
226- sortCIDRs (cidrs )
227-
228- for _ , r := range cidrs {
229- if len (aggregatedCIDRs ) == 0 {
230- aggregatedCIDRs = append (aggregatedCIDRs , r )
173+ // stripOverlapping returns a slice of CIDR structures with overlapping ranges
174+ // stripped.
175+ func stripOverlapping (s []* CIDR ) []* CIDR {
176+ l := len (s )
177+ for i := 0 ; i < l - 1 ; i ++ {
178+ if s [i ] == nil {
231179 continue
232180 }
233-
234- last := len (aggregatedCIDRs ) - 1
235- prev := aggregatedCIDRs [last ]
236-
237- if canAggregate (prev , r ) {
238- // Merge overlapping CIDRs.
239- aggregatedCIDRs [last ] = aggregateCIDR (prev , r )
240- } else {
241- aggregatedCIDRs = append (aggregatedCIDRs , r )
181+ for j := i + 1 ; j < l ; j ++ {
182+ if overlaps (s [j ], s [i ]) {
183+ s [j ] = nil
184+ }
242185 }
243186 }
244-
245- return aggregatedCIDRs
246- }
247-
248- // Helper function to sort IP ranges by starting IP.
249- func sortCIDRs (ipRanges []net.IPNet ) {
250- sort .SliceStable (ipRanges , func (i , j int ) bool {
251- return bytes .Compare (ipRanges [i ].IP , ipRanges [j ].IP ) < 0
252- })
187+ return filter (s )
253188}
254189
255- // Helper function to check if two CIDRs can be aggregated.
256- func canAggregate ( r1 , r2 net. IPNet ) bool {
257- return r1 . Contains ( r2 . IP ) || r2 . Contains ( r1 . IP )
190+ func overlaps ( a , b * CIDR ) bool {
191+ return ( a . PrefixUint32 () / ( 1 << ( 32 - b . MaskLen ()))) ==
192+ ( b . PrefixUint32 () / ( 1 << ( 32 - b . MaskLen ())) )
258193}
259194
260- // Helper function to aggregate two CIDRs.
261- func aggregateCIDR (r1 , r2 net.IPNet ) net.IPNet {
262- mask1 , _ := r1 .Mask .Size ()
263- mask2 , _ := r2 .Mask .Size ()
264-
265- ipLen := net .IPv6len * 8
266- if r1 .IP .To4 () != nil {
267- ipLen = net .IPv4len * 8
268- }
195+ // combineAdjacent returns a slice of CIDR structures with adjacent ranges
196+ // combined.
197+ func combineAdjacent (s []* CIDR ) []* CIDR {
198+ for {
199+ found := false
200+ l := len (s )
201+ for i := 0 ; i < l - 1 ; i ++ {
202+ if s [i ] == nil {
203+ continue
204+ }
205+ for j := i + 1 ; j < l ; j ++ {
206+ if s [j ] == nil {
207+ continue
208+ }
209+ if adjacent (s [i ], s [j ]) {
210+ c := fmt .Sprintf ("%s/%d" , s [i ].IP .String (), s [i ].MaskLen ()- 1 )
211+ s [i ] = newCidr (c )
212+ s [j ] = nil
213+ found = true
214+ }
215+ }
216+ }
269217
270- // Find the common prefix length
271- commonPrefixLen := mask1
272- if mask2 < commonPrefixLen {
273- commonPrefixLen = mask2
218+ if ! found {
219+ break
220+ }
274221 }
222+ return filter (s )
223+ }
275224
276- commonPrefix := r1 .IP .Mask (net .CIDRMask (commonPrefixLen , ipLen ))
225+ func adjacent (a , b * CIDR ) bool {
226+ return (a .MaskLen () == b .MaskLen ()) &&
227+ (a .PrefixUint32 ()% (2 << (32 - b .MaskLen ())) == 0 ) &&
228+ (b .PrefixUint32 ()- a .PrefixUint32 () == (1 << (32 - a .MaskLen ())))
229+ }
277230
278- return net.IPNet {IP : commonPrefix , Mask : net .CIDRMask (commonPrefixLen , ipLen )}
231+ func filter (s []* CIDR ) []* CIDR {
232+ out := s [:0 ]
233+ for _ , x := range s {
234+ if x != nil {
235+ out = append (out , x )
236+ }
237+ }
238+ return out
279239}
0 commit comments