Last active
September 6, 2023 15:20
-
-
Save marklap/774d3c7f9286f4fecc605f94f598ba1d to your computer and use it in GitHub Desktop.
Messing around turning IP addresses into integers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ////////////////////////////////////////////////////////////////////////////// | |
| // 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 | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ////////////////////////////////////////////////////////////////////////////// | |
| // 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