Convert net mask into CIDR in Go - go

I'm looking for a way to convert a netmask string into CIDR notation in Go.
For instance, "255.255.255.0" -> "/24"
I'm currently obtaining an IP address and the net mask string with the below logic, which may just be complicating things.
I've been perusing the net library trying to see if there is a different function to use to accomplish what I'd like, which is really just a IP address in CIDR notation:
192.168.1.2/24
var mgmtInterface *net.Interface
var err error
mgmtInterface, err = net.InterfaceByName("eth0")
if err != nil {
log.Println("Unable to find interface eth0, trying en0")
mgmtInterface, err = net.InterfaceByName("en0")
}
addrs, err := mgmtInterface.Addrs()
if err != nil {
log.Println("interface has no address")
}
for _, addr := range addrs {
var ip net.IP
var mask net.IPMask
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
mask = v.Mask
case *net.IPAddr:
ip = v.IP
mask = ip.DefaultMask()
}
if ip == nil {
continue
}
ip = ip.To4()
if ip == nil {
continue
}
// create the netmask
cleanMask := fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3])
}

This isn't very obvious at first, but:
addr := ip.To4()
sz, _ := net.IPV4Mask(addr[0], addr[1], addr[2], addr[3]).Size()

I'm not aware about existense of such function but it's easy to create one.
CIDR notation is just a count of set bits in netmask.
So, crude solution could be:
func cidr(netmask string) int {
var mask uint32
for idx, dotpart := range strings.Split(netmask, ".") {
part, _ := strconv.Atoi(dotpart)
mask = mask | uint32(part) << uint32(24-idx*8)
}
return len(fmt.Sprintf("%b", mask))
}

This is straightforward using the IPAddress Go library. Note that this code works equally well with both IPv4 and IPv6. Disclaimer: I am the project manager.
import (
"fmt"
"github.com/seancfoley/ipaddress-go/ipaddr"
)
func main() {
maskStr := "255.255.255.0"
pref := ipaddr.NewIPAddressString(maskStr).GetAddress().
GetBlockMaskPrefixLen(true)
fmt.Printf("prefix length for %s is %d", maskStr, pref.Len())
}
Output:
prefix length for 255.255.255.0 is 24

Related

How to use Golang to get my wifi ip address?

func getLocalIP() ([]string, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return nil, err
}
IPs := make([]string, 0)
for _, a := range addrs {
if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
if ipNet.IP.To4() != nil {
IPs = append(IPs, ipNet.IP.To4().String())
}
}
}
return IPs, nil
}
func TestGetLocalIP() {
addrs, _ := getLocalIP()
for _, a := range addrs {
fmt.Println(a)
}
}
I used this,but it give me a list of ip address.
I just want to get my wifi local address,how to do that?
You need to know at least one of two pieces of information going in:
the name of the correct interface (typically en0 on a Mac, e.g., but YMMV)
the address for your wireless network, including the length of the mask - something like 192.168.0.0/16 or 192.168.0.0/24 is pretty common, but you'll have to figure this out ahead of time.
If you only know the interface name:
ifs, _ := net.Interfaces()
for _, ifi := range ifs {
if ifi.Name == "en0" { // Or whatever other filter you want
addrs, _ := ifi.Addresses()
for _, a := range addrs {
// Filter to get the one you want, typically unicast IP4
}
}
}
Still simpler:
if, _ := net.InterfaceByName("en0")
addrs, _ := if.Addresses()
for _, a := range addrs {
// As before, filter for the address you want
}
If you know the network address for your wireless network
// Loop over interfaces, get addrs, then loop over addresses and get IPs for those that have them
if ip.Mask(net.CIDRMask(16, 32)) == myNetworkAddress {
// The interface you got this IP from is probably your wifi interface
}
Which method to choose
Depending on the interface having a specific name is generally not going to be portable. So while my assumption was that you were just trying to get this working on your own workstation, if this is for something which needs to run across multiple hosts, you may need to start off at least with matching against the correct network address. There are other tricks as well - if you know e.g. that you are going to be running in a server farm in which every host has a NIC from manufacturer XXX, you can look up the MAC address and see if it comes from that manufacturer - you can try this out here. You can use other filters as well, but those are going to be pretty specific to your individual use case.

Getting number of IPv6 addresses from Ipv6 CIDR in GO

Is there anyway to retrieve the maximum numbers of IPv6 from a CIDR range? Currently I have this code:
package main
import (
"fmt"
"log"
"net"
)
func main() {
ip, ipnet, err := net.ParseCIDR("2001:200:905::/49")
var ips []string
if err != nil {
log.Fatal(err)
}
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
ips = append(ips, ip.String())
}
fmt.Println(len(ips))
}
func inc(ip net.IP) {
for j := len(ip)-1; j>=0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}
But this process runs so slow. Is there any efficient way of retrieving the total number of ip addresses?
The IPAddress Go library can do this (get the count, starting point, end, or any address in the middle) with a few lines of polymorphic code that works with both IPv4 and IPv6 addresses. Repository here. Disclaimer: I am the project manager.
func details(addrStr string) {
addr := ipaddr.NewIPAddressString(addrStr).GetAddress()
lower, upper := addr.GetLower(), addr.GetUpper()
count := addr.GetCount()
fmt.Printf("%s has size %d,\n\tranging from %v to %v\n",
addr, count, lower, upper)
fmt.Println("\thundredth address is", addr.Increment(100))
}
Using your IPv6 example as well as an IPv4 subnet:
details("2001:200:905::/49")
details("192.168.10.0/24")
Output:
2001:200:905::/49 has size 604462909807314587353088,
ranging from 2001:200:905::/49 to 2001:200:905:7fff:ffff:ffff:ffff:ffff/49
hundredth address is 2001:200:905::64/49
192.168.10.0/24 has size 256,
ranging from 192.168.10.0/24 to 192.168.10.255/24
hundredth address is 192.168.10.100/24

Array seems to lose values after end of for loop

I'm a beginner at go (and not a good programmer) but I wanted to write a small program which would dump from a switch the list of mac addresses & interfaces name using snmp. I store the snmp values into an array of struct using multiple loops (the code here is to show the behavior).
During the first loop, I store Ports Vlan id & mac addresses into an array of struct (var allTableArray [30]allTable). At the end of this loop, I print the content of the array to be sure the mac addresses are in the array.
But when the second loop begins (to register bridge port number), the array seems empty (fmt.Printf("deux %x\n",allTableArray[i].macAddr) and fmt.Printf("trois %s\n",allTableArray[i].ptVlan1id)).
I don't understand why my array seems empty. Do you have any idea ?
package main
import (
"flag"
"fmt"
"os"
"time"
"strings"
"github.com/soniah/gosnmp"
"math/big"
)
type oidMacAddr struct {
oid string
macaddr string
}
type allTable struct {
ptVlan1id string
macAddr []byte
brPortNb *big.Int
ifIndex *big.Int
ifName string
}
var macAddrTable [30]oidMacAddr
func main() {
flag.Parse()
if len(flag.Args()) < 1 {
flag.Usage()
os.Exit(1)
}
target := flag.Args()[0]
showMacAddrTable(target)
}
func printValue(pdu gosnmp.SnmpPDU) error {
fmt.Printf("%s = ", pdu.Name)
//fmt.Println(reflect.TypeOf(pdu.Value.([]byte)))
switch pdu.Type {
case gosnmp.OctetString:
b := pdu.Value.([]byte)
fmt.Printf("STRING: %x\n", b)
default:
fmt.Printf("TYPE %d: %d\n", pdu.Type, gosnmp.ToBigInt(pdu.Value))
}
return nil
}
func showMacAddrTable(target string) () {
var allTableArray [30]allTable
ptVlan1Oid := ".1.3.6.1.2.1.17.4.3.1.1"
brPortOid := ".1.3.6.1.2.1.17.4.3.1.2"
brPortIfIndex := ".1.3.6.1.2.1.17.1.4.1.2"
ifIndexIfName := ".1.3.6.1.2.1.31.1.1.1.1"
community := "public"
gosnmp.Default.Target = target
gosnmp.Default.Community = community
gosnmp.Default.Timeout = time.Duration(10 * time.Second) // Timeout better suited to walking
err := gosnmp.Default.Connect()
if err != nil {
fmt.Printf("Connect err: %v\n", err)
os.Exit(1)
}
var essai []gosnmp.SnmpPDU
essai, err = gosnmp.Default.BulkWalkAll(ptVlan1Oid)
if err != nil {
fmt.Printf("Walk Error: %v\n", err)
os.Exit(1)
}
for i :=0 ; i < len(essai); i++ {
s := strings.TrimPrefix(essai[i].Name, ".1.3.6.1.2.1.17.4.3.1.1")
fmt.Printf("%s = ", s)
fmt.Printf("%x\n", essai[i].Value.([]byte))
bytes := essai[i].Value.([]byte)
macAddrTable[i] = oidMacAddr {s, string(bytes)}
allTableArray[i] = allTable {ptVlan1id: s, macAddr: bytes}
if(allTableArray[i].macAddr != nil){
fmt.Printf("%x\n",allTableArray[i].macAddr)
}
}
essai, err = gosnmp.Default.BulkWalkAll(brPortOid)
if err != nil {
fmt.Printf("Walk Error: %v\n", err)
os.Exit(1)
}
for i:=0 ; i < len(essai); i++ {
s := strings.TrimPrefix(essai[i].Name, ".1.3.6.1.2.1.17.4.3.1.2")
fmt.Printf("%s = ", s)
fmt.Printf("%d\n", essai[i].Value)
for j:=0 ; j < len(allTableArray); j++ {
if (s == allTableArray[j].ptVlan1id) {
allTableArray[j] = allTable {brPortNb: gosnmp.ToBigInt(essai[i].Value) }
}
}
fmt.Printf("deux %x\n",allTableArray[i].macAddr)
fmt.Printf("trois %s\n",allTableArray[i].ptVlan1id)
}
os.Exit(1)
}
Apparently this line
allTableArray[j] = allTable {brPortNb: gosnmp.ToBigInt(essai[i].Value) }
Update each member with a new allTable instance, where every field other than brPortNb is not defined thus becomes nil.
If what you were trying to do is to update each member's brPortNb field, you could have done so by accessing the field and assign the value to it instead of assigning a new allTable to every member.
allTableArray[j].brPortNb = gosnmp.ToBigInt(essai[i].Value)
Also, try simplifying your loops like this, provided len(essai) == len(allTableArray):
for i, v := range essai {
s := strings.TrimPrefix(v.Name, ".1.3.6.1.2.1.17.4.3.1.1")
bytes := v.Value.([]byte)
macAddrTable[i] = oidMacAddr { s, string(bytes) }
allTableArray[i] = allTable { ptVlan1id: s, macAddr: bytes }
s = strings.TrimPrefix(v.Name, ".1.3.6.1.2.1.17.4.3.1.2")
if s == allTableArray[i].ptVlan1id {
allTableArray[i].brPortNb = gosnmp.ToBigInt(v.Value)
}
}
Notice that by using for i, v := range essai syntax, you have access to both the index and the value without having to use essai[i] for the value.
Now your two loops can become just one, plus no embedded loops which are really hard to make sense of.
I Also recommend you work with slice instead of array. It's more flexible that way.

How to get the next IP address?

I'm considering to call net.IP.String(), strings.Split(ip, "."), some code to calculate all the corner cases and finally net.ParseIP(s). Is there a better way to this?. Below is the code of my current implementation (no special case handled).
package main
import (
"fmt"
"net"
"strconv"
"strings"
)
func main() {
ip := net.ParseIP("127.1.0.0")
next, err := NextIP(ip)
if err != nil {
panic(err)
}
fmt.Println(ip, next)
}
func NextIP(ip net.IP) (net.IP, error) {
s := ip.String()
sa := strings.Split(s, ".")
i, err := strconv.Atoi(sa[2])
if err != nil {
return nil, err
}
i++
sa[3] = strconv.Itoa(i)
s = strings.Join(sa, ".")
return net.ParseIP(s), nil
}
Just increment the last octet in the IP address
ip := net.ParseIP("127.1.0.0")
// make sure it's only 4 bytes
ip = ip.To4()
// check ip != nil
ip[3]++ // check for rollover
fmt.Println(ip)
//127.1.0.1
That however is technically incorrect, since the first address in the 127.1.0.1/8 subnet is 127.0.0.1. To get the true "first" address, you will also need an IPMask. Since you didn't specify one, you could use DefaultMask for IPv4 addresses (for IPv6 you can't assume a mask, and you must provide it).
http://play.golang.org/p/P_QWwRIBIm
ip := net.IP{192, 168, 1, 10}
ip = ip.To4()
if ip == nil {
log.Fatal("non ipv4 address")
}
ip = ip.Mask(ip.DefaultMask())
ip[3]++
fmt.Println(ip)
//192.168.1.1
If all you need is to compute the next IP address, the function nextIP() below will do the trick.
Usage:
// output is 1.0.1.0
fmt.Println(nextIP(net.ParseIP("1.0.0.255"), 1))
nextIP():
func nextIP(ip net.IP, inc uint) net.IP {
i := ip.To4()
v := uint(i[0])<<24 + uint(i[1])<<16 + uint(i[2])<<8 + uint(i[3])
v += inc
v3 := byte(v & 0xFF)
v2 := byte((v >> 8) & 0xFF)
v1 := byte((v >> 16) & 0xFF)
v0 := byte((v >> 24) & 0xFF)
return net.IPv4(v0, v1, v2, v3)
}
Playground: https://play.golang.org/p/vHrmftkVjn2
Gist: https://gist.github.com/udhos/b468fbfd376aa0b655b6b0c539a88c03
I would test against CIDR after the IP has been incremented, so overflows don't change the expected subnet.
func incrementIP(origIP, cidr string) (string, error) {
ip := net.ParseIP(origIP)
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return origIP, err
}
for i := len(ip) - 1; i >= 0; i-- {
ip[i]++
if ip[i] != 0 {
break
}
}
if !ipNet.Contains(ip) {
return origIP, errors.New("overflowed CIDR while incrementing IP")
}
return ip.String(), nil
}
I've encountered this problem just now, and I want to share my solution. It's not that efficient but solves the problem in a few lines.
func nextIP(ip net.IP) net.IP {
// Convert to big.Int and increment
ipb := big.NewInt(0).SetBytes([]byte(ip))
ipb.Add(ipb, big.NewInt(1))
// Add leading zeros
b := ipb.Bytes()
b = append(make([]byte, len(ip)-len(b)), b...)
return net.IP(b)
}
This is straightforward using the IPAddress Go library, works for both IPv4 and IPv6, and handles corner cases. Disclaimer: I am the project manager.
import (
"fmt"
"github.com/seancfoley/ipaddress-go/ipaddr"
)
func main() {
fmt.Println(increment("127.0.0.1"))
fmt.Println(increment("127.0.0.255"))
fmt.Println(increment("::1"))
fmt.Println(increment("255.255.255.255"))
}
func increment(addrString string) *ipaddr.IPAddress {
addr := ipaddr.NewIPAddressString(addrString).GetAddress()
return addr.Increment(1)
}
Output:
127.0.0.2
127.0.1.0
::2
<nil>

Parse multiple integers from a string in Go?

sorry for the title , i don't know how to say that
i have a questions about this code
func ip2long(ip string) (ret int64) {
p:= strings.Split(ip, ".")
n, _:= strconv.Atoi(p[0])
ret += int64(n)*16777216
n, _= strconv.Atoi(p[1])
ret += int64(n)*65536
n, _= strconv.Atoi(p[2])
ret += int64(n)*256
n, _= strconv.Atoi(p[3])
ret += int64(n)
return
}
I want to convert an ip address to integer number
you see I have wrote such ugly code
first retrive number from strconv.Atoi then convert it to int64
How to simplify this ?
If you want to parse multiple integers from a string, try the Sscanf function, like this:
func main() {
var ip [4] uint32
addr := "192.168.0.1"
_, err := fmt.Sscanf(addr, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3])
if err != nil {
fmt.Println(err)
return
}
fmt.Println(ip)
fmt.Println(ip[0]<<24 + ip[1]<<16 + ip[2]<<8 + ip[3])
}
This construct is heavy but appears more natural if you, as you probably should, were catching the parsing errors.
This being said, the correct solution to that exact problem is to use the existing net.ParseIP function which builds an IP
func ParseIP(s string) IP
If you must keep your current function prototype, I suggest this :
func ip2long(s string) (ret int64) {
bip := ([]byte)(net.ParseIP(s).To4())
return (int64)(bip[0]) * (1 << 24) + (int64)(bip[1]) * (1 << 16) + (int64)(bip[2]) * (1 << 8) + (int64)(bip[3])
}
Note that you may add a test on the return of ParseIP (which is nil in case of error)
I don't recommend it, because you would be ignoring errors, but you can write a function to give you the first value:
func first(x int, _ error) int {return x;}
Then use first(strconv.Atoi(p[0])) in your code.
For the particular code you listed though, I would use the (well tested!) standard library's net.ParseIP() and net.To4() functions:
// WARNING untested code!!
type InvalidIP string
func (InvalidIP ipStr) Error() string {
return "Invalid IPv4 address: "+ipStr
}
func ip2long(ipStr string) (ret int64, err error) {
var ip net.IP
if ip = net.ParseIP(ipStr); ip == nil {
return 0, InvalidIP(ip)
}
if ip = ip.To4(); ip == nil {
return 0, InvalidIP(ip)
}
for b := range ip {
ret <<= 8
ret += b
}
return ret, nil
}
Note that the above code adds error checking, and also accepts IPv6 formatted v4 addresses "::FFFF:C0A8:0101". Consider whether you really need an int64 type to represent IP addresses, or whether the standard library's net.IP type is good enough for your purposes.

Resources