Whenever I try to learn a new language, I write a subnetting calculator. Here's my attempt with Go.
In particular, I think I'm not writing idiomatic Go, as well as abusing float64()
, uint64()
, etc.
My math should be correct, although I might have goofed when trying to translate JavaScript's number types into Go's C-esque versions.
Also, I know I should do some error-checking on the inputs (e.g. seeing if I get a logical IP instead of a string like "cat", but that'll come once I learn how to write correct Go ;))
package main
import (
"flag"
"fmt"
"math"
"os"
"strconv"
"strings"
)
const SIXTY_FOUR_BITS uint64 = 18446744073709551615
const MAX_BIT_VALUE int = 32
const MAX_BIT_BIN uint64 = 255
func main() {
var pf uint64
var unpack_pf uint64
flag.Parse()
if len(flag.Args()) == 0 {
flag.Usage()
os.Exit(1)
}
var ip_address string = flag.Args()[0]
var netmask = flag.Args()[1]
var ip_arr []string = strings.Split(ip_address, ".")
var sm_arr []string = strings.Split(netmask, ".")
pf, _ = strconv.ParseUint(netmask, 10, 32)
if len(sm_arr) == 1 {
if pf <= 32 {
unpack_pf = unpack_int(pf)
sm_arr = strings.Split(int_qdot(unpack_pf), ".")
netmask = strings.Join(sm_arr, ".")
} else if pf > 32 {
pf = host_pf(pf)
unpack_pf = unpack_int(pf)
sm_arr = strings.Split(int_qdot(unpack_pf), ".")
netmask = strings.Join(sm_arr, ".")
}
if pf == 0 {
panic("Cannot have netmask < 0 or 0.0.0.0")
}
}
var ip_dec uint64 = qdot_int(ip_arr)
var nm_dec uint64 = qdot_int(sm_arr)
var network string = network_address(ip_dec, nm_dec)
var broadcast string = broadcast_address(ip_dec, nm_dec)
var wildcard string = int_qdot(^nm_dec)
var prefix uint64 = qdot_pf(nm_dec)
var ip_class string = class(ip_arr[0])
var num_hosts float64 = hosts(prefix)
var num_subnets float64 = subnets(nm_dec)
var net_min string = int_qdot(qdot_int(strings.Split(network, ".")) + 1)
var net_max string = int_qdot(qdot_int(strings.Split(broadcast, ".")) - 1)
var ip_range string = net_min + " - " + net_max
var addr_type string = "IPv4"
write_results(addr_type, ip_address, ip_dec, netmask, nm_dec, wildcard, ip_class, network, prefix, net_min, net_max, broadcast, ip_range, num_subnets, num_hosts)
}
func write_results(addr_type, ip_address string, ip_dec uint64, netmask string, nm_dec uint64, wildcard string, ip_class string, network string, prefix uint64, net_min string, net_max string, broadcast string, ip_range string, num_subnets float64, num_hosts float64) int {
fmt.Printf("====================\n %s RESULTS \n====================\n\nAddress: - %s\n (hex): - %#X\n (dec): - %d\n (bin): - %b\nNetmask: - %s = %d\n (hex): - %#X\n (dec): - %d\n (bin): - %b\nWildcard: - %s\nClass: - %s\nNetwork: - %s\nNet Min: - %s\nNet Max: - %s\nBroadcast: - %s\nRange: - %s\nSubnets: - %.0f\nHosts/net: - %.0f\n\n-\n", addr_type, ip_address, ip_dec, ip_dec, ip_dec, netmask, prefix, nm_dec, nm_dec, nm_dec, wildcard, ip_class, network, net_min, net_max, broadcast, ip_range, num_subnets, num_hosts)
return 0
}
func unpack_int(sm_int uint64) uint64 {
return SIXTY_FOUR_BITS << (uint64(MAX_BIT_VALUE) - sm_int)
}
func qdot_int(qdot []string) uint64 {
var a uint64 = 0
var w uint64
var x uint64
var y uint64
var z uint64
w, _ = strconv.ParseUint(qdot[0], 10, MAX_BIT_VALUE)
x, _ = strconv.ParseUint(qdot[1], 10, MAX_BIT_VALUE)
y, _ = strconv.ParseUint(qdot[2], 10, MAX_BIT_VALUE)
z, _ = strconv.ParseUint(qdot[3], 10, MAX_BIT_VALUE)
a += w << 24
a += x << 16
a += y << 8
a += z << 0
return a
}
func int_qdot(integer uint64) string {
var w string = strconv.FormatUint(integer>>24&MAX_BIT_BIN, 10)
var x string = strconv.FormatUint(integer>>16&MAX_BIT_BIN, 10)
var y string = strconv.FormatUint(integer>>8&MAX_BIT_BIN, 10)
var z string = strconv.FormatUint(integer&MAX_BIT_BIN, 10)
var a []string = []string{w, x, y, z}
return strings.Join(a, ".")
}
func network_address(ip uint64, sm uint64) string {
return int_qdot(ip & sm)
}
func broadcast_address(ip uint64, sm uint64) string {
return int_qdot(ip | ^sm)
}
func host_pf(hn uint64) uint64 {
var x float64
if 0 < hn {
x = float64(MAX_BIT_VALUE) - math.Ceil(math.Log2(float64(hn)))
} else {
x = 0
}
return uint64(x)
}
func qdot_pf(qdot uint64) uint64 {
var x uint64 = qdot
var mask []uint64 = []uint64{
0x55555555,
0x33333333,
0x0F0F0F0F,
0x00FF00FF,
0x0000FFFF,
}
var shift uint64
var i uint64
for i, shift = 0, 1; i < 5; i, shift = i+1, shift*2 {
x = x&mask[i] + x>>shift&mask[i]
}
return x
}
func class(bit string) string {
var ip uint64
var x string
ip, _ = strconv.ParseUint(bit, 10, 32)
if ip < 128 {
x = "Class A"
} else if ip < 192 {
x = "Class B"
} else if ip < 224 {
x = "Class C"
} else if ip < 240 {
x = "Class D"
} else if ip < 256 {
x = "Class E"
} else if ip < 0 {
panic("Invalid IP")
} else {
panic("Super invalid IP")
}
return x
}
func subnets(pf uint64) float64 {
var mod_base float64 = math.Mod(float64(pf), 8)
var x float64
if mod_base > 0 {
x = math.Pow(2, mod_base)
} else {
x = 256
}
return x
}
func hosts(bits uint64) float64 {
var x float64 = float64(bits)
if x >= 2 {
x = math.Pow(2, (float64(MAX_BIT_VALUE)-x)) - 2
}
return x
}