Skip to content

Instantly share code, notes, and snippets.

@marklap
Last active September 6, 2023 15:20
Show Gist options
  • Select an option

  • Save marklap/774d3c7f9286f4fecc605f94f598ba1d to your computer and use it in GitHub Desktop.

Select an option

Save marklap/774d3c7f9286f4fecc605f94f598ba1d to your computer and use it in GitHub Desktop.
Messing around turning IP addresses into integers
// //////////////////////////////////////////////////////////////////////////////
// The MIT License (MIT)
//
// # Copyright (c) 2016 Mark LaPerriere
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// //////////////////////////////////////////////////////////////////////////////
package ipconv
import (
"math/big"
"net"
)
const (
MinIPv4 = uint32(0)
MaxIPv4 = uint32(1<<32 - 1)
MinIPv6 = uint64(0)
MaxIPv6 = uint64(1<<64 - 1)
)
// IPv4ToUInt converts an net.IP IPv4 address to a uint32.
func IPv4ToUInt(ip net.IP) uint32 {
n := []byte(ip.To4())
return (uint32(n[0]) << 24) |
(uint32(n[1]) << 16) |
(uint32(n[2]) << 8) |
uint32(n[3])
}
// UintToIPv4 converts a uint32 to an net.IPv4 address.
func UintToIPv4(n uint32) net.IP {
return net.IPv4(byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
}
// IPv4NetStartEnd returns the start and end IPs of an IPv4 network given an IP and CIDR mask.
func IPv4NetStartEnd(ip net.IP, mask int) (start, end uint32) {
switch {
case mask < 0:
return MinIPv4, MaxIPv4
case mask > 32:
return MaxIPv4, MaxIPv4
}
i := IPv4ToUInt(ip)
sm := uint32(MaxIPv4 << uint32(32-mask))
em := ^sm
start = i & sm
end = i | em
return start, end
}
// UintToBig converts two uint64s to a big.Int.
func UintToBig(net, host uint64) *big.Int {
r, b := new(big.Int), new(big.Int)
r.SetUint64(net)
b.SetUint64(host)
r.Lsh(r, 64)
r.Or(r, b)
return r
}
// BigToUint converts a big.Int to two uint64s.
func BigToUint(b *big.Int) (uint64, uint64) {
net, host := new(big.Int), new(big.Int)
net = net.Rsh(b, 64)
host = host.And(b, big.NewInt(0).SetUint64(0xffffffffffffffff))
return net.Uint64(), host.Uint64()
}
// IPv6ToUint converts an net.IP IPv6 address into two uint64s; one each to represent network and host.
func IPv6ToUint(ip net.IP) (uint64, uint64) {
n := []byte(ip.To16())
return (uint64(n[0]) << 56) |
(uint64(n[1]) << 48) |
(uint64(n[2]) << 40) |
(uint64(n[3]) << 32) |
(uint64(n[4]) << 24) |
(uint64(n[5]) << 16) |
(uint64(n[6]) << 8) |
uint64(n[7]),
(uint64(n[8]) << 56) |
(uint64(n[9]) << 48) |
(uint64(n[10]) << 40) |
(uint64(n[11]) << 32) |
(uint64(n[12]) << 24) |
(uint64(n[13]) << 16) |
(uint64(n[14]) << 8) |
uint64(n[15])
}
// UintToIPv6 converts two uint64s (representing an IPv6 network and host) in to a net.IP.
func UintToIPv6(network, host uint64) net.IP {
var ip = make(net.IP, net.IPv6len)
ip[0] = byte(network >> 56)
ip[1] = byte(network >> 48)
ip[2] = byte(network >> 40)
ip[3] = byte(network >> 32)
ip[4] = byte(network >> 24)
ip[5] = byte(network >> 16)
ip[6] = byte(network >> 8)
ip[7] = byte(network)
ip[8] = byte(host >> 56)
ip[9] = byte(host >> 48)
ip[10] = byte(host >> 40)
ip[11] = byte(host >> 32)
ip[12] = byte(host >> 24)
ip[13] = byte(host >> 16)
ip[14] = byte(host >> 8)
ip[15] = byte(host)
return ip
}
// IPv6NetStartEnd returns the start and end networks and hosts of an IPv6 network given an IP and CIDR mask.
func IPv6NetStartEnd(ip net.IP, mask int) (sNet, sHost, eNet, eHost uint64) {
n, h := IPv6ToUint(ip)
var subj, cMask uint64
switch {
case mask < 0:
return MinIPv6, MaxIPv6, MinIPv6, MaxIPv6
case mask > 128:
return MaxIPv6, MaxIPv6, MaxIPv6, MaxIPv6
case mask <= 64:
subj = n
cMask = 64 - uint64(mask)
case mask > 64 && mask <= 128:
subj = h
cMask = uint64(mask) - 64
}
sm := uint64(MaxIPv6 << cMask)
em := ^sm
start := subj & sm
end := subj | em
switch {
case mask >= 0 && mask <= 64:
sNet = start
sHost = MinIPv6
eNet = end
eHost = MaxIPv6
case mask > 64 && mask <= 128:
sNet = n
sHost = start
eNet = n
eHost = end
}
return sNet, sHost, eNet, eHost
}
// //////////////////////////////////////////////////////////////////////////////
// The MIT License (MIT)
//
// # Copyright (c) 2016 Mark LaPerriere
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// //////////////////////////////////////////////////////////////////////////////
package ipconv
import (
"math/big"
"net"
"testing"
)
func TestUintToBig(t *testing.T) {
fxtNet := uint64(18279849776823276130)
fxtHost := uint64(8002485518114779956)
want, _ := new(big.Int).SetString("337203710538915638676904557932570506036", 10)
got := UintToBig(fxtNet, fxtHost)
if want.Cmp(got) != 0 {
t.Errorf("Failed to convert uint64s to big.Int - want: %s; got: %s", want, got)
}
}
func TestBigToUint(t *testing.T) {
fxtBigInt, _ := new(big.Int).SetString("337203710538915638676904557932570506036", 10)
wantNet := uint64(18279849776823276130)
wantHost := uint64(8002485518114779956)
gotNet, gotHost := BigToUint(fxtBigInt)
if wantNet != gotNet || wantHost != gotHost {
t.Errorf("Failed to convert big.Int to uint64s - want: %d:%d; got: %d:%d", wantNet, wantHost, gotNet, gotHost)
}
}
// TestIPv4Conversion tests that an IPv4 address can be converted to a uint32 and back again
func TestIPv4Conversion(t *testing.T) {
ipWant := net.ParseIP("192.168.100.100")
uWant := uint32(3232261220)
if uGot := IPv4ToUInt(ipWant); uGot != uWant {
t.Errorf("Failed to convert IPv4 %s to correct uint32 - got %d, want %d", ipWant, uGot, uWant)
}
if ipGot := UintToIPv4(uWant); !ipGot.Equal(ipWant) {
t.Errorf("Failed to convert uint32 %d to IPv4 - got %s, want %s", uWant, ipGot, ipWant)
}
}
// TestIPv6Conversion tests that an IPv6 address can be converted to two uint64s (net and host)
// and back again
func TestIPv6Conversion(t *testing.T) {
ipWant := net.ParseIP("fdaf:1285:6f0e:1262:6f0e:8a2e:0370:7334")
uNetWant, uHostWant := uint64(18279849776823276130), uint64(8002485518114779956)
bWant, _ := new(big.Int).SetString("337203710538915638676904557932570506036", 10)
uNetGot, uHostGot := IPv6ToUint(ipWant)
if uNetGot != uNetWant ||
uHostGot != uHostWant ||
bWant.Cmp(UintToBig(uNetGot, uHostGot)) != 0 {
t.Errorf("Failed to convert IPv6 %s to correct uint64s - got %d::%d, want %d::%d",
ipWant, uNetGot, uHostGot, uNetWant, uHostWant)
}
ipGot := UintToIPv6(uNetWant, uHostWant)
if !ipGot.Equal(ipWant) {
t.Errorf("Failed to convert uint64s %d::%d to IPv6 - got %s, want %s", uNetWant, uHostWant, ipGot, ipWant)
}
}
// TestIPv4Subnetting tests that an IPv4 address range can be calculated from CIDR notation
func TestIPv4Subnetting(t *testing.T) {
mBits := 27
ipWant := net.ParseIP("192.168.100.100")
sUWant := uint32(3232261216)
eUWant := uint32(3232261247)
if sUGot, eUGot := IPv4NetStartEnd(ipWant, mBits); sUGot != sUWant || eUGot != eUWant {
t.Errorf("Failed to convert IPv4 CIDR %s/%d to start and end uint32s - got %d-%d, want %d-%d",
ipWant, mBits, sUWant, eUWant, sUGot, eUGot)
}
}
// TestIPv6NetSubnetting tests that an IPv6 address range can be calculated from CIDR notation
// where the mask only applies to the network bits
func TestIPv6NetSubnetting(t *testing.T) {
// Mask: 33 ->
// 18279849774960082944::0-18279849777107566591::18446744073709551615 ->
// fdaf:1285::-fdaf:1285:7fff:ffff:ffff:ffff:ffff:ffff
mBits := 33
ipWant := net.ParseIP("fdaf:1285:6f0e:1262:6f0e:8a2e:0370:7334")
uSNetWant, uSHostWant := uint64(18279849774960082944), uint64(0)
uENetWant, uEHostWant := uint64(18279849777107566591), uint64(18446744073709551615)
bSWant, _ := new(big.Int).SetString("337203710504545790806880554100409237504", 10)
bEWant, _ := new(big.Int).SetString("337203710544159872064012722897181212671", 10)
eq := func(sn, sh, en, eh uint64) bool {
return sn == uSNetWant &&
sh == uSHostWant &&
en == uENetWant &&
eh == uEHostWant &&
bSWant.Cmp(UintToBig(sn, sh)) == 0 &&
bEWant.Cmp(UintToBig(en, eh)) == 0
}
sn, sh, en, eh := IPv6NetStartEnd(ipWant, mBits)
if !eq(sn, sh, en, eh) {
t.Errorf("Failed to convert IPv6 CIDR %s/%d to start and end uint64s - got %d::%d-%d::%d, want %d::%d-%d::%d",
ipWant, mBits, uSNetWant, uSHostWant, uENetWant, uEHostWant, sn, sh, en, eh)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment