pax_global_header00006660000000000000000000000064135260235540014517gustar00rootroot0000000000000052 comment=c0fb5fbe0acb592411e2db59add389a43260ad44 go-cidr-1.0.1/000077500000000000000000000000001352602355400130425ustar00rootroot00000000000000go-cidr-1.0.1/.travis.yml000066400000000000000000000000711352602355400151510ustar00rootroot00000000000000language: go go: - 1.x - 1.7.x - 1.8.x - master go-cidr-1.0.1/LICENSE000066400000000000000000000020411352602355400140440ustar00rootroot00000000000000Copyright (c) 2015 Martin Atkins 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. go-cidr-1.0.1/cidr/000077500000000000000000000000001352602355400137635ustar00rootroot00000000000000go-cidr-1.0.1/cidr/cidr.go000066400000000000000000000153311352602355400152360ustar00rootroot00000000000000// Package cidr is a collection of assorted utilities for computing // network and host addresses within network ranges. // // It expects a CIDR-type address structure where addresses are divided into // some number of prefix bits representing the network and then the remaining // suffix bits represent the host. // // For example, it can help to calculate addresses for sub-networks of a // parent network, or to calculate host addresses within a particular prefix. // // At present this package is prioritizing simplicity of implementation and // de-prioritizing speed and memory usage. Thus caution is advised before // using this package in performance-critical applications or hot codepaths. // Patches to improve the speed and memory usage may be accepted as long as // they do not result in a significant increase in code complexity. package cidr import ( "fmt" "math/big" "net" ) // Subnet takes a parent CIDR range and creates a subnet within it // with the given number of additional prefix bits and the given // network number. // // For example, 10.3.0.0/16, extended by 8 bits, with a network number // of 5, becomes 10.3.5.0/24 . func Subnet(base *net.IPNet, newBits int, num int) (*net.IPNet, error) { ip := base.IP mask := base.Mask parentLen, addrLen := mask.Size() newPrefixLen := parentLen + newBits if newPrefixLen > addrLen { return nil, fmt.Errorf("insufficient address space to extend prefix of %d by %d", parentLen, newBits) } maxNetNum := uint64(1< maxNetNum { return nil, fmt.Errorf("prefix extension of %d does not accommodate a subnet numbered %d", newBits, num) } return &net.IPNet{ IP: insertNumIntoIP(ip, big.NewInt(int64(num)), newPrefixLen), Mask: net.CIDRMask(newPrefixLen, addrLen), }, nil } // Host takes a parent CIDR range and turns it into a host IP address with // the given host number. // // For example, 10.3.0.0/16 with a host number of 2 gives 10.3.0.2. func Host(base *net.IPNet, num int) (net.IP, error) { ip := base.IP mask := base.Mask bigNum := big.NewInt(int64(num)) parentLen, addrLen := mask.Size() hostLen := addrLen - parentLen maxHostNum := big.NewInt(int64(1)) maxHostNum.Lsh(maxHostNum, uint(hostLen)) maxHostNum.Sub(maxHostNum, big.NewInt(1)) numUint64 := big.NewInt(int64(bigNum.Uint64())) if bigNum.Cmp(big.NewInt(0)) == -1 { numUint64.Neg(bigNum) numUint64.Sub(numUint64, big.NewInt(int64(1))) bigNum.Sub(maxHostNum, numUint64) } if numUint64.Cmp(maxHostNum) == 1 { return nil, fmt.Errorf("prefix of %d does not accommodate a host numbered %d", parentLen, num) } var bitlength int if ip.To4() != nil { bitlength = 32 } else { bitlength = 128 } return insertNumIntoIP(ip, bigNum, bitlength), nil } // AddressRange returns the first and last addresses in the given CIDR range. func AddressRange(network *net.IPNet) (net.IP, net.IP) { // the first IP is easy firstIP := network.IP // the last IP is the network address OR NOT the mask address prefixLen, bits := network.Mask.Size() if prefixLen == bits { // Easy! // But make sure that our two slices are distinct, since they // would be in all other cases. lastIP := make([]byte, len(firstIP)) copy(lastIP, firstIP) return firstIP, lastIP } firstIPInt, bits := ipToInt(firstIP) hostLen := uint(bits) - uint(prefixLen) lastIPInt := big.NewInt(1) lastIPInt.Lsh(lastIPInt, hostLen) lastIPInt.Sub(lastIPInt, big.NewInt(1)) lastIPInt.Or(lastIPInt, firstIPInt) return firstIP, intToIP(lastIPInt, bits) } // AddressCount returns the number of distinct host addresses within the given // CIDR range. // // Since the result is a uint64, this function returns meaningful information // only for IPv4 ranges and IPv6 ranges with a prefix size of at least 65. func AddressCount(network *net.IPNet) uint64 { prefixLen, bits := network.Mask.Size() return 1 << (uint64(bits) - uint64(prefixLen)) } //VerifyNoOverlap takes a list subnets and supernet (CIDRBlock) and verifies //none of the subnets overlap and all subnets are in the supernet //it returns an error if any of those conditions are not satisfied func VerifyNoOverlap(subnets []*net.IPNet, CIDRBlock *net.IPNet) error { firstLastIP := make([][]net.IP, len(subnets)) for i, s := range subnets { first, last := AddressRange(s) firstLastIP[i] = []net.IP{first, last} } for i, s := range subnets { if !CIDRBlock.Contains(firstLastIP[i][0]) || !CIDRBlock.Contains(firstLastIP[i][1]) { return fmt.Errorf("%s does not fully contain %s", CIDRBlock.String(), s.String()) } for j := 0; j < len(subnets); j++ { if i == j { continue } first := firstLastIP[j][0] last := firstLastIP[j][1] if s.Contains(first) || s.Contains(last) { return fmt.Errorf("%s overlaps with %s", subnets[j].String(), s.String()) } } } return nil } // PreviousSubnet returns the subnet of the desired mask in the IP space // just lower than the start of IPNet provided. If the IP space rolls over // then the second return value is true func PreviousSubnet(network *net.IPNet, prefixLen int) (*net.IPNet, bool) { startIP := checkIPv4(network.IP) previousIP := make(net.IP, len(startIP)) copy(previousIP, startIP) cMask := net.CIDRMask(prefixLen, 8*len(previousIP)) previousIP = Dec(previousIP) previous := &net.IPNet{IP: previousIP.Mask(cMask), Mask: cMask} if startIP.Equal(net.IPv4zero) || startIP.Equal(net.IPv6zero) { return previous, true } return previous, false } // NextSubnet returns the next available subnet of the desired mask size // starting for the maximum IP of the offset subnet // If the IP exceeds the maxium IP then the second return value is true func NextSubnet(network *net.IPNet, prefixLen int) (*net.IPNet, bool) { _, currentLast := AddressRange(network) mask := net.CIDRMask(prefixLen, 8*len(currentLast)) currentSubnet := &net.IPNet{IP: currentLast.Mask(mask), Mask: mask} _, last := AddressRange(currentSubnet) last = Inc(last) next := &net.IPNet{IP: last.Mask(mask), Mask: mask} if last.Equal(net.IPv4zero) || last.Equal(net.IPv6zero) { return next, true } return next, false } //Inc increases the IP by one this returns a new []byte for the IP func Inc(IP net.IP) net.IP { IP = checkIPv4(IP) incIP := make([]byte, len(IP)) copy(incIP, IP) for j := len(incIP) - 1; j >= 0; j-- { incIP[j]++ if incIP[j] > 0 { break } } return incIP } //Dec decreases the IP by one this returns a new []byte for the IP func Dec(IP net.IP) net.IP { IP = checkIPv4(IP) decIP := make([]byte, len(IP)) copy(decIP, IP) decIP = checkIPv4(decIP) for j := len(decIP) - 1; j >= 0; j-- { decIP[j]-- if decIP[j] < 255 { break } } return decIP } func checkIPv4(ip net.IP) net.IP { // Go for some reason allocs IPv6len for IPv4 so we have to correct it if v4 := ip.To4(); v4 != nil { return v4 } return ip } go-cidr-1.0.1/cidr/cidr_test.go000066400000000000000000000232761352602355400163040ustar00rootroot00000000000000package cidr import ( "bytes" "fmt" "net" "strconv" "testing" ) func TestSubnet(t *testing.T) { type Case struct { Base string Bits int Num int Output string Error bool } cases := []Case{ Case{ Base: "192.168.2.0/20", Bits: 4, Num: 6, Output: "192.168.6.0/24", }, Case{ Base: "192.168.2.0/20", Bits: 4, Num: 0, Output: "192.168.0.0/24", }, Case{ Base: "192.168.0.0/31", Bits: 1, Num: 1, Output: "192.168.0.1/32", }, Case{ Base: "192.168.0.0/21", Bits: 4, Num: 7, Output: "192.168.3.128/25", }, Case{ Base: "fe80::/48", Bits: 16, Num: 6, Output: "fe80:0:0:6::/64", }, Case{ Base: "fe80::/49", Bits: 16, Num: 7, Output: "fe80:0:0:3:8000::/65", }, Case{ Base: "192.168.2.0/31", Bits: 2, Num: 0, Error: true, // not enough bits to expand into }, Case{ Base: "fe80::/126", Bits: 4, Num: 0, Error: true, // not enough bits to expand into }, Case{ Base: "192.168.2.0/24", Bits: 4, Num: 16, Error: true, // can't fit 16 into 4 bits }, } for _, testCase := range cases { _, base, _ := net.ParseCIDR(testCase.Base) gotNet, err := Subnet(base, testCase.Bits, testCase.Num) desc := fmt.Sprintf("Subnet(%#v,%#v,%#v)", testCase.Base, testCase.Bits, testCase.Num) if err != nil { if !testCase.Error { t.Errorf("%s failed: %s", desc, err.Error()) } } else { got := gotNet.String() if testCase.Error { t.Errorf("%s = %s; want error", desc, got) } else { if got != testCase.Output { t.Errorf("%s = %s; want %s", desc, got, testCase.Output) } } } } } func TestHost(t *testing.T) { type Case struct { Range string Num int Output string Error bool } cases := []Case{ Case{ Range: "192.168.2.0/20", Num: 6, Output: "192.168.0.6", }, Case{ Range: "192.168.0.0/20", Num: 257, Output: "192.168.1.1", }, Case{ Range: "2001:db8::/32", Num: 1, Output: "2001:db8::1", }, Case{ Range: "192.168.1.0/24", Num: 256, Error: true, // only 0-255 will fit in 8 bits }, Case{ Range: "192.168.0.0/30", Num: -3, Output: "192.168.0.1", // 4 address (0-3) in 2 bits; 3rd from end = 1 }, Case{ Range: "192.168.0.0/30", Num: -4, Output: "192.168.0.0", // 4 address (0-3) in 2 bits; 4th from end = 0 }, Case{ Range: "192.168.0.0/30", Num: -5, Error: true, // 4 address (0-3) in 2 bits; cannot accomodate 5 }, Case{ Range: "fd9d:bc11:4020::/64", Num: 2, Output: "fd9d:bc11:4020::2", }, Case{ Range: "fd9d:bc11:4020::/64", Num: -2, Output: "fd9d:bc11:4020:0:ffff:ffff:ffff:fffe", }, } for _, testCase := range cases { _, network, _ := net.ParseCIDR(testCase.Range) gotIP, err := Host(network, testCase.Num) desc := fmt.Sprintf("Host(%#v,%#v)", testCase.Range, testCase.Num) if err != nil { if !testCase.Error { t.Errorf("%s failed: %s", desc, err.Error()) } } else { got := gotIP.String() if testCase.Error { t.Errorf("%s = %s; want error", desc, got) } else { if got != testCase.Output { t.Errorf("%s = %s; want %s", desc, got, testCase.Output) } } } } } func TestAddressRange(t *testing.T) { type Case struct { Range string First string Last string } cases := []Case{ Case{ Range: "192.168.0.0/16", First: "192.168.0.0", Last: "192.168.255.255", }, Case{ Range: "192.168.0.0/17", First: "192.168.0.0", Last: "192.168.127.255", }, Case{ Range: "fe80::/64", First: "fe80::", Last: "fe80::ffff:ffff:ffff:ffff", }, } for _, testCase := range cases { _, network, _ := net.ParseCIDR(testCase.Range) firstIP, lastIP := AddressRange(network) desc := fmt.Sprintf("AddressRange(%#v)", testCase.Range) gotFirstIP := firstIP.String() gotLastIP := lastIP.String() if gotFirstIP != testCase.First { t.Errorf("%s first is %s; want %s", desc, gotFirstIP, testCase.First) } if gotLastIP != testCase.Last { t.Errorf("%s last is %s; want %s", desc, gotLastIP, testCase.Last) } } } func TestAddressCount(t *testing.T) { type Case struct { Range string Count uint64 } cases := []Case{ Case{ Range: "192.168.0.0/16", Count: 65536, }, Case{ Range: "192.168.0.0/17", Count: 32768, }, Case{ Range: "192.168.0.0/32", Count: 1, }, Case{ Range: "192.168.0.0/31", Count: 2, }, Case{ Range: "0.0.0.0/0", Count: 4294967296, }, Case{ Range: "0.0.0.0/1", Count: 2147483648, }, Case{ Range: "::/65", Count: 9223372036854775808, }, Case{ Range: "::/128", Count: 1, }, Case{ Range: "::/127", Count: 2, }, } for _, testCase := range cases { _, network, _ := net.ParseCIDR(testCase.Range) gotCount := AddressCount(network) desc := fmt.Sprintf("AddressCount(%#v)", testCase.Range) if gotCount != testCase.Count { t.Errorf("%s = %d; want %d", desc, gotCount, testCase.Count) } } } func TestIncDec(t *testing.T) { testCase := [][]string{ []string{"0.0.0.0", "0.0.0.1"}, []string{"10.0.0.0", "10.0.0.1"}, []string{"9.255.255.255", "10.0.0.0"}, []string{"255.255.255.255", "0.0.0.0"}, []string{"::", "::1"}, []string{"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::"}, []string{"2001:db8:c001:ba00::", "2001:db8:c001:ba00::1"}, } for _, tc := range testCase { ip1 := net.ParseIP(tc[0]) ip2 := net.ParseIP(tc[1]) iIP := Inc(ip1) if !iIP.Equal(ip2) { t.Logf("%s should inc to equal %s\n", tc[0], tc[1]) t.Errorf("%v should equal %v\n", iIP, ip2) } if ip1.Equal(ip2) { t.Errorf("[%v] should not have been modified to [%v]", ip2, iIP) } } for _, tc := range testCase { ip1 := net.ParseIP(tc[0]) ip2 := net.ParseIP(tc[1]) dIP := Dec(ip2) if !ip1.Equal(dIP) { t.Logf("%s should dec equal %s\n", tc[0], tc[1]) t.Errorf("%v should equal %v\n", ip1, dIP) } if ip2.Equal(dIP) { t.Errorf("[%v] should not have been modified to [%v]", ip2, dIP) } } } func TestPreviousSubnet(t *testing.T) { testCases := [][]string{ []string{"10.0.0.0/24", "9.255.255.0/24", "false"}, []string{"100.0.0.0/26", "99.255.255.192/26", "false"}, []string{"0.0.0.0/26", "255.255.255.192/26", "true"}, []string{"2001:db8:e000::/36", "2001:db8:d000::/36", "false"}, []string{"::/64", "ffff:ffff:ffff:ffff::/64", "true"}, } for _, tc := range testCases { _, c1, _ := net.ParseCIDR(tc[0]) _, c2, _ := net.ParseCIDR(tc[1]) mask, _ := c1.Mask.Size() p1, rollback := PreviousSubnet(c1, mask) if !p1.IP.Equal(c2.IP) { t.Errorf("IP expected %v, got %v\n", c2.IP, p1.IP) } if !bytes.Equal(p1.Mask, c2.Mask) { t.Errorf("Mask expected %v, got %v\n", c2.Mask, p1.Mask) } if p1.String() != c2.String() { t.Errorf("%s should have been equal %s\n", p1.String(), c2.String()) } if check, _ := strconv.ParseBool(tc[2]); rollback != check { t.Errorf("%s to %s should have rolled\n", tc[0], tc[1]) } } for _, tc := range testCases { _, c1, _ := net.ParseCIDR(tc[0]) _, c2, _ := net.ParseCIDR(tc[1]) mask, _ := c1.Mask.Size() n1, rollover := NextSubnet(c2, mask) if !n1.IP.Equal(c1.IP) { t.Errorf("IP expected %v, got %v\n", c1.IP, n1.IP) } if !bytes.Equal(n1.Mask, c1.Mask) { t.Errorf("Mask expected %v, got %v\n", c1.Mask, n1.Mask) } if n1.String() != c1.String() { t.Errorf("%s should have been equal %s\n", n1.String(), c1.String()) } if check, _ := strconv.ParseBool(tc[2]); rollover != check { t.Errorf("%s to %s should have rolled\n", tc[0], tc[1]) } } } func TestVerifyNetowrk(t *testing.T) { type testVerifyNetwork struct { CIDRBlock string CIDRList []string } testCases := []*testVerifyNetwork{ &testVerifyNetwork{ CIDRBlock: "192.168.8.0/21", CIDRList: []string{ "192.168.8.0/24", "192.168.9.0/24", "192.168.10.0/24", "192.168.11.0/25", "192.168.11.128/25", "192.168.12.0/25", "192.168.12.128/26", "192.168.12.192/26", "192.168.13.0/26", "192.168.13.64/27", "192.168.13.96/27", "192.168.13.128/27", }, }, } failCases := []*testVerifyNetwork{ &testVerifyNetwork{ CIDRBlock: "192.168.8.0/21", CIDRList: []string{ "192.168.8.0/24", "192.168.9.0/24", "192.168.10.0/24", "192.168.11.0/25", "192.168.11.128/25", "192.168.12.0/25", "192.168.12.64/26", "192.168.12.128/26", }, }, &testVerifyNetwork{ CIDRBlock: "192.168.8.0/21", CIDRList: []string{ "192.168.7.0/24", "192.168.9.0/24", "192.168.10.0/24", "192.168.11.0/25", "192.168.11.128/25", "192.168.12.0/25", "192.168.12.64/26", "192.168.12.128/26", }, }, &testVerifyNetwork{ CIDRBlock: "10.42.0.0/24", CIDRList: []string{ "10.42.0.16/28", "10.42.0.32/28", "10.42.0.0/24", }, }, } for _, tc := range testCases { subnets := make([]*net.IPNet, len(tc.CIDRList)) for i, s := range tc.CIDRList { _, n, err := net.ParseCIDR(s) if err != nil { t.Errorf("Bad test data %s\n", s) } subnets[i] = n } _, CIDRBlock, perr := net.ParseCIDR(tc.CIDRBlock) if perr != nil { t.Errorf("Bad test data %s\n", tc.CIDRBlock) } test := VerifyNoOverlap(subnets, CIDRBlock) if test != nil { t.Errorf("Failed test with %v\n", test) } } for _, tc := range failCases { subnets := make([]*net.IPNet, len(tc.CIDRList)) for i, s := range tc.CIDRList { _, n, err := net.ParseCIDR(s) if err != nil { t.Errorf("Bad test data %s\n", s) } subnets[i] = n } _, CIDRBlock, perr := net.ParseCIDR(tc.CIDRBlock) if perr != nil { t.Errorf("Bad test data %s\n", tc.CIDRBlock) } test := VerifyNoOverlap(subnets, CIDRBlock) if test == nil { t.Errorf("Test should have failed with CIDR %s\n", tc.CIDRBlock) } } } go-cidr-1.0.1/cidr/wrangling.go000066400000000000000000000015241352602355400163040ustar00rootroot00000000000000package cidr import ( "fmt" "math/big" "net" ) func ipToInt(ip net.IP) (*big.Int, int) { val := &big.Int{} val.SetBytes([]byte(ip)) if len(ip) == net.IPv4len { return val, 32 } else if len(ip) == net.IPv6len { return val, 128 } else { panic(fmt.Errorf("Unsupported address length %d", len(ip))) } } func intToIP(ipInt *big.Int, bits int) net.IP { ipBytes := ipInt.Bytes() ret := make([]byte, bits/8) // Pack our IP bytes into the end of the return array, // since big.Int.Bytes() removes front zero padding. for i := 1; i <= len(ipBytes); i++ { ret[len(ret)-i] = ipBytes[len(ipBytes)-i] } return net.IP(ret) } func insertNumIntoIP(ip net.IP, bigNum *big.Int, prefixLen int) net.IP { ipInt, totalBits := ipToInt(ip) bigNum.Lsh(bigNum, uint(totalBits-prefixLen)) ipInt.Or(ipInt, bigNum) return intToIP(ipInt, totalBits) } go-cidr-1.0.1/go.mod000066400000000000000000000000511352602355400141440ustar00rootroot00000000000000module github.com/apparentlymart/go-cidr