From 2caf97d5355c236cba87883c94315d375729231a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Moska=C5=82a?= Date: Fri, 16 Feb 2024 17:57:58 +0100 Subject: [PATCH] Scan subnets in parallel, update readme, update gitignore --- .gitignore | 3 +- README.md | 7 +++ minipam.go | 132 ++++++++++++++++++++++++++++------------------------- 3 files changed, 80 insertions(+), 62 deletions(-) diff --git a/.gitignore b/.gitignore index 0e8d217..cd015d8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ !.gitignore !.screenshot.png minipam +MinIPAM *.yaml -*.json \ No newline at end of file +*.json diff --git a/README.md b/README.md index 917ba58..4473af3 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ there is no authorization mechanism built in. Scanning is kind of slow, but I prefer to keep it that way, since I don't need it to be fast, and I don't want to waste my compute resources. It doesn't matter for me if my scan completes within 1 minute or within 30 minutes. +Subnets are scanned in parallel, but addresses in each subnet are still scanned sequentially. So complete scan takes as +much time, as scanning of biggest subnet that you have. + ![Screenshot](.screenshot.png) # Compilation @@ -33,9 +36,13 @@ scan_subnets: - 192.168.145.0/24 - 10.250.100.64.0/27 delay_between_scans: 15m +#Setting this to absolute path seems like a good idea :) persistence_location: "data.json" +#Don't scan network and broadcast address, usually you want to leave this set to true exclude_special_addresses: true use_tls: false +#IF use_tls is set to false, following two options are ignored. +#key and cert and fullchain can be in one file. In this case, specify the same file in both fields tls_key_file: "key.pem" tls_cert_file: "fullchain.pem" ``` diff --git a/minipam.go b/minipam.go index c602024..8044c6f 100644 --- a/minipam.go +++ b/minipam.go @@ -12,6 +12,7 @@ import ( "net/netip" "os" "os/exec" + "sync" "time" ) @@ -120,77 +121,86 @@ func ping(addr string) bool { func scanner() { for { + var wg sync.WaitGroup + var mutex sync.Mutex + for _, subnet := range conf.ScanSubnets { - for _, v := range conf.ScanSubnets { - - persistenceSubnet, ok := p.Subnets[v] - if !ok { - persistenceSubnet = SubnetT{} - persistenceSubnet.Hosts = make(map[string]HostT) - } - log.Printf("Scanning subnet %s", v) - prefix, err := netip.ParsePrefix(v) - if err != nil { - log.Printf("Error: %s", err) - continue - } - prefix = prefix.Masked() - addr := prefix.Addr() - - if conf.ExcludeSpecialAddresses { - addr = addr.Next() - } - persistenceSubnet.TotalAddresses = 0 - persistenceSubnet.UsedAddresses = 0 - persistenceSubnet.HostList = make([]string, 0) - for { - if !prefix.Contains(addr) { - break + go func(v string) { + wg.Add(1) + defer wg.Done() + persistenceSubnet, ok := p.Subnets[v] + if !ok { + persistenceSubnet = SubnetT{} + persistenceSubnet.Hosts = make(map[string]HostT) } - //skip broadcast address - if conf.ExcludeSpecialAddresses && !prefix.Contains(addr.Next()) { - break + log.Printf("Scanning subnet %s", v) + prefix, err := netip.ParsePrefix(v) + if err != nil { + log.Printf("Error: %s", err) + return } - persistenceSubnet.TotalAddresses++ - //fmt.Println(addr.String()) - persistenceSubnet.HostList = append(persistenceSubnet.HostList, addr.String()) - pingstate := ping(addr.String()) - host, ok := persistenceSubnet.Hosts[addr.String()] - if pingstate { - persistenceSubnet.UsedAddresses++ - //log.Printf("%s is up", addr.String()) - rdnsString := "" - rdns, err := net.LookupAddr(addr.String()) - if err == nil { - if len(rdns) > 0 { - rdnsString = rdns[0] - } + prefix = prefix.Masked() + addr := prefix.Addr() + + if conf.ExcludeSpecialAddresses { + addr = addr.Next() + } + persistenceSubnet.TotalAddresses = 0 + persistenceSubnet.UsedAddresses = 0 + persistenceSubnet.HostList = make([]string, 0) + for { + if !prefix.Contains(addr) { + break } - if !ok { - persistenceSubnet.Hosts[addr.String()] = HostT{ - FirstSeen: time.Now(), - LastSeen: time.Now(), - Online: true, - RevDNS: rdnsString, + //skip broadcast address + if conf.ExcludeSpecialAddresses && !prefix.Contains(addr.Next()) { + break + } + persistenceSubnet.TotalAddresses++ + //fmt.Println(addr.String()) + persistenceSubnet.HostList = append(persistenceSubnet.HostList, addr.String()) + pingstate := ping(addr.String()) + host, ok := persistenceSubnet.Hosts[addr.String()] + if pingstate { + persistenceSubnet.UsedAddresses++ + //log.Printf("%s is up", addr.String()) + rdnsString := "" + rdns, err := net.LookupAddr(addr.String()) + if err == nil { + if len(rdns) > 0 { + rdnsString = rdns[0] + } } - } else { - host.LastSeen = time.Now() - host.Online = true - host.RevDNS = rdnsString + if !ok { + persistenceSubnet.Hosts[addr.String()] = HostT{ + FirstSeen: time.Now(), + LastSeen: time.Now(), + Online: true, + RevDNS: rdnsString, + } + } else { + host.LastSeen = time.Now() + host.Online = true + host.RevDNS = rdnsString + persistenceSubnet.Hosts[addr.String()] = host + } + + } else if ok { + host.Online = false persistenceSubnet.Hosts[addr.String()] = host + persistenceSubnet.UsedAddresses++ } - } else if ok { - host.Online = false - persistenceSubnet.Hosts[addr.String()] = host - persistenceSubnet.UsedAddresses++ + addr = addr.Next() } - - addr = addr.Next() - } - p.Subnets[v] = persistenceSubnet + mutex.Lock() + p.Subnets[v] = persistenceSubnet + mutex.Unlock() + log.Printf("Scan of %s finished", v) + }(subnet) } - log.Printf("Scan finished") + wg.Wait() + log.Printf("All scans finished") f, err := os.Create(conf.PersistenceLocation) if err != nil { log.Printf("Failed to save persistence: %s", err)