Remaining IPs in a range/CIDR - go

I would like to find the remaining IPs in a range (CIDR).
For example:
prefixLen, bits := n.Mask.Size()
return 1 << (uint64(bits) - uint64(prefixLen)), nil
This return the # of IPs in the range where n is *net.IPNet.
But say if I have few addresses that are already given out and want to know how many are left 'starting' from some IP addresses, how can I do it?

Here's something you could do
package main
import (
"fmt"
"net"
)
func inet_aton(ip string) uint32 {
var rv uint32
b := net.ParseIP(ip).To4()
rv=uint32(b[0]) << 24
rv=rv+uint32(b[1]) << 16
rv=rv+uint32(b[2]) << 8
rv=rv+uint32(b[3])
return rv
}
func main() {
start := inet_aton("192.168.0.1") //192.168.2.0/20 start
fin := inet_aton("192.168.15.255") //ditto end
here := inet_aton("192.168.15.248") //current allocation
fmt.Printf("you have %d remaining\n",(fin-start) - (here-start))
}

Here is a way to do it that works for both IPv4 and IPv6 using the IPAddress Go library. Disclaimer: I am the project manager.
func remaining(cidr string) (used, remaining, total *big.Int) {
cidrStr := ipaddr.NewIPAddressString(cidr)
cidrBlock := cidrStr.GetAddress().ToPrefixBlock()
host := cidrStr.GetHostAddress()
used = new(big.Int).Sub(host.GetValue(), cidrBlock.GetValue())
remaining = new(big.Int).Sub(cidrBlock.GetUpperValue(), host.GetValue())
total = cidrBlock.GetCount()
fmt.Printf("\nHost %s is address number %d of %d addresses in block %s.\n"+
"There remains %d addresses.\n", host, used, total, cidrBlock,
remaining)
return
}
If you want to stick with IPv4, then you can replace big.Int with unsigned int types.
func remainingIPv4(cidr string) (used, remaining uint32, total uint64) {
cidrStr := ipaddr.NewIPAddressString(cidr)
cidrBlock := cidrStr.GetAddress().ToPrefixBlock().ToIPv4()
host := cidrStr.GetHostAddress().ToIPv4()
used = host.Uint32Value() - cidrBlock.Uint32Value()
remaining = cidrBlock.UpperUint32Value() - host.Uint32Value()
total = cidrBlock.GetIPv4Count()
fmt.Printf("\nHost %s is address number %d of %d addresses in block %s.\n"+
"There remains %d addresses.\n", host, used, total, cidrBlock,
remaining)
return
}
Example:
package main
import (
"fmt"
"github.com/seancfoley/ipaddress-go/ipaddr"
"math/big"
)
func main() {
remaining("192.168.2.0/20")
remaining("2001:0db8:85a3::8a2e:0370:7334/64")
remainingIPv4("192.168.2.0/20")
}
Example output:
Host 192.168.2.0 is address number 512 of 4096 addresses in block 192.168.0.0/20.
There remains 3583 addresses.
Host 2001:db8:85a3::8a2e:370:7334 is address number 151930230829876 of 18446744073709551616 addresses in block 2001:db8:85a3::/64.
There remains 18446592143478721739 addresses.
Host 192.168.2.0 is address number 512 of 4096 addresses in block 192.168.0.0/20.
There remains 3583 addresses.

Related

Go - calculate the cidr below one bit of the cidr provided

Ask
How do you calculate the cidr below one bit of the cidr provided?
Given:
a network CIDR : (192.168.0.0/16)
wanted result
192.168.0.0/17, 192.168.128.0/17
Using packages such as the default net package and github.com/brotherpowers/ipsubnet, github.com/seancfoley/ipaddress-go/ipaddr did not get the desired results.
To split a network in two, increment the length of the prefix by one. That gives you the lower half. To compute the second half, increment the network part by one (error handling omitted for brevity):
package main
import (
"fmt"
"math/big"
"net/netip"
)
func main() {
p := netip.MustParsePrefix("192.168.0.0/16")
lo, hi := split(p)
fmt.Println(lo, hi) // 192.168.0.0/17 192.168.128.0/17
}
func split(p netip.Prefix) (lo, hi netip.Prefix) {
p = p.Masked()
lo, _ = p.Addr().Prefix(p.Bits() + 1)
delta := big.NewInt(1)
delta.Lsh(delta, uint(lo.Addr().BitLen()-lo.Bits()))
n := new(big.Int).SetBytes(lo.Addr().AsSlice())
n.Add(n, delta)
hiIP, _ := netip.AddrFromSlice(n.Bytes())
hi = netip.PrefixFrom(hiIP, lo.Bits())
return lo, hi
}
https://go.dev/play/p/0HLqUK0RmVC
"Using packages such as the default net package and github.com/brotherpowers/ipsubnet, github.com/seancfoley/ipaddress-go/ipaddr did not get the desired results."
Here is how to do it with github.com/seancfoley/ipaddress-go/ipaddr (note this code also works with IPv6 and any change in prefix length):
package main
import (
"fmt"
"github.com/seancfoley/ipaddress-go/ipaddr"
)
func main() {
cidr := ipaddr.NewIPAddressString("192.168.0.0/16").GetAddress()
for i := cidr.AdjustPrefixLen(1).PrefixBlockIterator(); i.HasNext(); {
fmt.Println(i.Next())
}
}
Output:
192.168.0.0/17
192.168.128.0/17

How to get the IP range of a CIDR

How can I get the start and end for v4 and v6 ip addresses of a CIDR? I don't care about addresses which are between.
I have checked the net library and parseCIDR does not return this information. Is there an idiomatic way to calculate the ip range of a CIDR which will work for v6 and v4 addresses alike?
For example, given the CIDR 2001:db8:a0b:12f0::1/32 I would expect
2001:0db8:0000:0000:0000:0000:0000:0000 and 2001:0db8:ffff:ffff:ffff:ffff:ffff:ffff returned as the start and end addresses respectively.
Using the IPAddress Go library, this code will do it for either IPv4 or IPv6. Disclaimer: I am the project manager.
import (
"fmt"
"github.com/seancfoley/ipaddress-go/ipaddr"
)
func main() {
ipRange("2001:db8:a0b:12f0::1/32")
ipRange("1.2.3.1/16")
}
func ipRange(cidr string) {
block := ipaddr.NewIPAddressString(cidr).GetAddress().ToPrefixBlock()
addr := block.WithoutPrefixLen()
fmt.Printf("addr %s block %s lower %s upper %s\n",
cidr, block, addr.GetLower(), addr.GetUpper())
}
Output:
addr 2001:db8:a0b:12f0::1/32 block 2001:db8::/32 lower 2001:db8:: upper 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff
addr 1.2.3.1/16 block 1.2.0.0/16 lower 1.2.0.0 upper 1.2.255.255

How to find range (prefix?) of ipv6 addresses that a machine can use, and how to transform a subnet of ipv6 into a slice of ip addresses?

There are many things I don't understand about ipv6 and networking in general, which is why I need some further clarification on some the answers already posted to other questions. I'll list my questions, what I grasped from other answers, and what I'm still confused about.
Say I have a VPS with a /56 ipv6 subnet (256 * residential /64 subnets) allotted to it. How can I programmatically find the range (prefix?) of the ip's I "own".
How to get IPv4 and IPv6 address of local machine?. This is the answer I saw for this question: and what I think I understand is that I get the DNS hostname for the machine, then look up that same hostname to find the range. I'm wondering two things: How do I do this in Go, and
How do I transfer this range ^ into a slice (array) of ipv6 addresses. For this specific use case: the ideal solution would be to only get one ipv6 address per \64 subnet, resulting in 256 seperate ips
DNS is not very helpful in determining the local IP addresses, because a DNS entry is not required to make an IP address work, nor is it required to point to (only) the machine that you happen to run your program on.
Instead, inspect the network interfaces and their configuration:
package main
import (
"fmt"
"log"
"net"
"os"
"text/tabwriter"
)
func main() {
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
ifaces, err := net.Interfaces()
if err != nil {
log.Fatal(err)
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
log.Fatal(err)
}
for _, addr := range addrs {
addr, ok := addr.(*net.IPNet)
if !ok {
// Not an IP interface
continue
}
if addr.IP.To4() != nil {
// Skip IPv4 addresses
continue
}
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n",
iface.Name, addr.String(), addr.IP, addr.Mask)
}
}
tw.Flush()
}
For my local machine the output is:
lo ::1/128 ::1 ffffffffffffffffffffffffffffffff
enp2s0 fe80::52e5:49ff:fe3b:107a/64 fe80::52e5:49ff:fe3b:107a ffffffffffffffff0000000000000000
docker0 fe80::42:afff:fedb:7389/64 fe80::42:afff:fedb:7389 ffffffffffffffff0000000000000000
tun0 fe80::f22c:2d3b:a5a0:1b61/64 fe80::f22c:2d3b:a5a0:1b61 ffffffffffffffff0000000000000000
vethd176f0c fe80::1cc1:65ff:fe39:feff/64 fe80::1cc1:65ff:fe39:feff ffffffffffffffff0000000000000000
Note that these addresses are not necessarily reachable from the Internet. This all depends on how the routing of the hoster works. In any kind of cloud setup, you are almost always better off querying the providers APIs.
To list all /64 subnets in a particular /56 subnet, you have to leave the 56 upper bits of the subnet address as they are and permute the following 64-56 = 8 bits (which happens to be the eigth byte):
package main
import (
"fmt"
"net"
)
func main() {
_, subnet, _ := net.ParseCIDR("2001:db8::/56")
fmt.Println(subnet)
subnet.Mask = net.CIDRMask(64, 128) // change mask to /64
for i := 0; i <= 0xff; i++ {
subnet.IP[7] = byte(i) // permute the 8th byte
fmt.Println("\t", subnet)
}
// Output:
// 2001:db8::/56
// 2001:db8::/64
// 2001:db8:0:1::/64
// 2001:db8:0:2::/64
// 2001:db8:0:3::/64
// 2001:db8:0:4::/64
// 2001:db8:0:5::/64
// 2001:db8:0:6::/64
// 2001:db8:0:7::/64
// 2001:db8:0:8::/64
// [...]
}
Regarding the second part of your question, "How do I transfer this range ^ into a slice (array) of ipv6 addresses"
The IPAddress Go library can do this with polymorphic code that works with both IPv4 and IPv6 addresses and all prefix lengths. Repository here. Disclaimer: I am the project manager.
addrStr := "2001:db8::/56"
addr := ipaddr.NewIPAddressString(addrStr).GetAddress()
addrAdjusted := addr.SetPrefixLen(64) // adjust prefix
iterator := addrAdjusted.PrefixIterator()
var blocks []*ipaddr.IPAddress
for iterator.HasNext() {
blocks = append(blocks, iterator.Next())
}
// print the details
fmt.Println("first and last blocks are",
addrAdjusted.GetLower().ToPrefixBlock(), "and",
addrAdjusted.GetUpper().ToPrefixBlock())
fmt.Print("list: ")
for i, addr := range blocks {
if i < 3 || len(blocks)-i <= 3 {
if i > 0 {
fmt.Print(", ")
}
fmt.Print(addr)
} else if i == 3 {
fmt.Print(", ...")
}
}
Output:
first and last blocks are 2001:db8::/64 and 2001:db8:0:ff::/64
list: 2001:db8::/64, 2001:db8:0:1::/64, 2001:db8:0:2::/64, ..., 2001:db8:0:fd::/64, 2001:db8:0:fe::/64, 2001:db8:0:ff::/64

Read Random Memory Locations with Golang

Good evening,
I've been trying to build a golang application which scans values in memory but am struggling trying to understand how to address specific memory locations. I know that when accessing memory within the application you can use *variablename to deference and get the address location, but how would I provide an address location and print the value to the screen or grab the next allocated object of any size from RAM and print it's value?
Thanks in advance for any help you may be willing to share
I don't know how much useful this will be, but here is a sample code.
package main
import (
"fmt"
"unsafe"
)
func main() {
var i int = 1
fmt.Println("Address : ", &i, " Value : ", i)
var address *int
address = &i // getting the starting address
loc := (uintptr)(unsafe.Pointer(address))
p := unsafe.Pointer(loc)
// verification - it should print 1
var val int = *((* int)(p))
fmt.Println("Location : ", loc, " Val :",val) // it does print !!
// lets print 1000 bytes starting from address of variable i
// first memory location contains 1 as expected
printValueAtMemoryLocation(loc, 1000)
// now lets test for some arbitrary memory location
// not so random ! wanted to reduce the diff value also any arbitrary memory location you can't read !!
memoryToReach := 842350500000
loc = changeToInputLocation(loc, memoryToReach)
fmt.Println("Loc is now at : ", loc)
// lets print 1000 bytes starting from the memory location "memoryToReach"
printValueAtMemoryLocation(loc, 1000)
}
func changeToInputLocation(location uintptr, locationToreach int) uintptr {
var diff,i int
diff = locationToreach - int(location)
fmt.Println("We need to travel ", diff, " memory locations !")
if diff < 0 {
i= diff * -1
for i > 0 {
location--
i--
}
} else {
i= diff
for i > 0 {
location++
i--
}
}
return location
}
func printValueAtMemoryLocation(location uintptr, next int) {
var v byte
p := unsafe.Pointer(location)
fmt.Println("\n")
for i:=1; i<next; i++ {
p = unsafe.Pointer(location)
v = *((*byte)(p))
fmt.Print(v," ")
//fmt.Println("Loc : ", loc, " --- Val : ", v)
location++
}
fmt.Println("\n")
}
Using "unsafe" package is not a good idea, also you can not read any arbitrary location I believe.
For me when I tried some other random locations where, most probably, I didn't have read access, it threw me error like this:
unexpected fault address 0xc41ff8f780
fatal error: fault
[signal SIGBUS: bus error code=0x2 addr=0xc41ff8f780 pc=0x1093ec0]
But hopefully, it can be of some value to you.

store multiple values in maps

problem: Store both an IP address as well as a time to a counter. something like this
ip, time, count
and then i would like to be able to increment the count for each ip:
ip++
in my map, and then on a given interval, i would like to iterate all keys and find the keys with a time stamp older than N minutes.
I need this to make sure i don't "forget" a key in memory if for some reason, client disconnect and i dont delete the key properly.
http://play.golang.org/p/RiWWOCARq7
Question: How do i add a time stamp to every IP address i store in my map. Also i need this be be used across multiple go routines
Im new to both programming and Golang, so if this is not even the correct approach, if anyone could point me in the right direction, i would appreciate it.
For example,
package main
import (
"sync"
"time"
)
type IPCounter struct {
IPAddr string
Time time.Time
Count int
}
type ipCounterMap struct {
counters map[string]IPCounter
mutex sync.RWMutex
}
var ipCounters = ipCounterMap{counters: make(map[string]IPCounter)}
// Get IP address counter
func Counter(ipAddr string) IPCounter {
ipCounters.mutex.RLock()
defer ipCounters.mutex.RUnlock()
counter, found := ipCounters.counters[ipAddr]
if !found {
counter.IPAddr = ipAddr
}
return counter
}
// Increment IP address counter
func Incr(ipAddr string) {
now := time.Now().UTC()
ipCounters.mutex.Lock()
defer ipCounters.mutex.Unlock()
counter, found := ipCounters.counters[ipAddr]
if !found {
counter.IPAddr = ipAddr
}
counter.Time = now
counter.Count++
ipCounters.counters[ipAddr] = counter
}
// Delete IP address counter
func Delete(ipAddr string) {
ipCounters.mutex.Lock()
defer ipCounters.mutex.Unlock()
delete(ipCounters.counters, ipAddr)
}
// Get old IP address counters old durations ago
func OldIPCounters(old time.Duration) []IPCounter {
var counters []IPCounter
oldTime := time.Now().UTC().Add(-old)
ipCounters.mutex.RLock()
defer ipCounters.mutex.RUnlock()
for _, counter := range ipCounters.counters {
if counter.Time.Before(oldTime) {
counters = append(counters, counter)
}
}
return counters
}
func main() {}
You probably want a map of ip -> struct { ip, counter, lastTime }
that way, you can look up the counter by ip and then update it
var Counters = map[string]*Counter{}
type Counter struct {
ip string
count int
lastTime time.Time
}
Here's a working example on Play http://play.golang.org/p/TlCTc_4iq5
Adding the find older than is simply range over the values of the map comparing to now and do something when it's old enough.
It sounds like you need a struct. A map is for storing a collection where all the keys are of one type and all the elements are of another (or the same type).
For grouping a collection of variables of different data types, that are related, use a struct
e.g.
type IPCounter struct {
ip string
time time.Time
count int
}
Here's some sample code that creates one of these objects and increments the count:
package main
import (
"fmt"
"time"
)
type IPAddressCounter struct {
ip string
time time.Time
count int
}
func (i *IPAddressCounter) IncrementCount() {
i.count++
}
func makeCounter(ip string) IPAddressCounter {
return IPAddressCounter{
ip: ip,
time: time.Now(),
}
}
func main() {
mapOfIPCounters := make(map[string]IPAddressCounter)
mapOfIPCounters["192.168.1.1"] = makeCounter("192.168.1.1")
mapOfIPCounters["192.168.1.2"] = makeCounter("192.168.1.2")
mapOfIPCounters["192.168.1.3"] = makeCounter("192.168.1.3")
for key, value := range mapOfIPCounters {
value.IncrementCount()
mapOfIPCounters[key] = value
fmt.Println("The counter for "+key+" is", mapOfIPCounters[key].count)
}
}

Resources