Last active
April 3, 2025 21:36
-
-
Save ecnepsnai/5fe93f90f3c0ae5421c56a3c44096d47 to your computer and use it in GitHub Desktop.
Deduplicate an list of CIDR subnets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Command uniquenet takes a file containing a list of CIDR-notated subnets and returns a de-duplicated list. | |
// Deduplication is more than just literal duplicates, it also takes into consideration if a subnet is fully represented | |
// by another larger subnet in the list. | |
// | |
// Usage: uniquenet [-6] <File path> | |
// By default, uniquenet only returns a list of IPv4 subnets. Pass -6 to switch to IPv6 only. | |
// File path must be a text file where each line contains only a CIDR address, that is a network address '/' prefix | |
// length. | |
package main | |
import ( | |
"bufio" | |
"fmt" | |
"net" | |
"os" | |
"strings" | |
) | |
func main() { | |
if len(os.Args) < 2 { | |
fmt.Fprintln(os.Stderr, fmt.Sprintf("Usage: %s [-6] <File path>", os.Args[0])) | |
os.Exit(1) | |
} | |
var filePath string | |
var includeV4 = true | |
var includeV6 = false | |
for _, arg := range os.Args[1:] { | |
if arg == "-6" { | |
includeV4 = false | |
includeV6 = true | |
} else { | |
filePath = arg | |
} | |
} | |
f, err := os.Open(filePath) | |
if err != nil { | |
fmt.Fprintln(os.Stderr, err.Error()) | |
os.Exit(1) | |
} | |
defer f.Close() | |
subnets := []net.IPNet{} | |
scanner := bufio.NewScanner(f) | |
for scanner.Scan() { | |
line := scanner.Text() | |
_, net, err := net.ParseCIDR(line) | |
if err != nil { | |
fmt.Fprintln(os.Stderr, fmt.Sprintf("Invalid line '%s': %s", line, err.Error())) | |
os.Exit(1) | |
} | |
isV6 := strings.Contains(line, ":") | |
if (!isV6 && includeV4) || (isV6 && includeV6) { | |
subnets = append(subnets, *net) | |
} | |
} | |
netMap := map[string]int{} | |
for _, subnet := range subnets { | |
netStr := subnet.String() | |
for _, otherSubnet := range subnets { | |
otherNetStr := otherSubnet.String() | |
if netStr == otherNetStr { | |
continue | |
} | |
if otherSubnet.Contains(subnet.IP) { | |
v := netMap[netStr] | |
v++ | |
netMap[netStr] = v | |
} | |
} | |
} | |
for i := len(subnets) - 1; i >= 0; i-- { | |
netStr := subnets[i].String() | |
if netMap[netStr] > 0 { | |
subnets = append(subnets[:i], subnets[i+1:]...) | |
} | |
} | |
for _, subnet := range subnets { | |
fmt.Println(subnet.String()) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment