Skip to content

Commit fbe47f4

Browse files
Added splitcidr function
1 parent 42c54f5 commit fbe47f4

3 files changed

Lines changed: 249 additions & 1 deletion

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ ip2locationio cidr2list <CIDR>
159159
ip2locationio range2list <START IP> <END IP>
160160
```
161161

162+
### Split a larger CIDR into smaller ones
163+
```bash
164+
ip2locationio splitcidr <CIDR> <SPLIT>
165+
```
166+
162167

163168
Example API Response
164169
====================

ip2locationio/ip2locationio.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var myLanguage string
1515
var myIP string
1616
var filterFields string
1717

18-
const version string = "1.0.2"
18+
const version string = "1.1.0"
1919
const programName string = "IP2Location.io Command Line"
2020

2121
var showVer bool = false
@@ -70,6 +70,9 @@ func main() {
7070
} else if arg == "range2cidr" {
7171
PrintRange2CIDR(flag.Arg(1), flag.Arg(2))
7272
return
73+
} else if arg == "splitcidr" {
74+
PrintSplitCIDR(flag.Arg(1), flag.Arg(2))
75+
return
7376
} else if len(arg) == 0 {
7477
myIP = MyPublicIP()
7578
} else if !IsIPv4(arg) && !IsIPv6(arg) {
@@ -182,6 +185,18 @@ func PrintRange2CIDR(fromIP string, toIP string) {
182185
}
183186
}
184187

188+
func PrintSplitCIDR(cidr string, split string) {
189+
res, err := SplitCIDR(cidr, split)
190+
191+
if err != nil {
192+
fmt.Println(err)
193+
} else {
194+
for _, element := range res {
195+
fmt.Println(element)
196+
}
197+
}
198+
}
199+
185200
func PrintFiltered() {
186201
ipl, err := LookUpMap(myIP, myLanguage)
187202

@@ -318,6 +333,10 @@ To list out the IPs in a CIDR
318333
To list out the IPs in a range
319334
320335
Usage: EXE range2list <START IP> <END IP>
336+
337+
To split a larger CIDR into smaller ones
338+
339+
Usage: EXE splitcidr <CIDR> <SPLIT>
321340
`
322341

323342
usage = strings.ReplaceAll(usage, "EXE", os.Args[0])

ip2locationio/splitcidr.go

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package main
2+
3+
import (
4+
"encoding/binary"
5+
"errors"
6+
"math"
7+
"math/big"
8+
"net"
9+
"strconv"
10+
)
11+
12+
func SplitCIDR(cidr string, split string) ([]string, error) {
13+
var res []string
14+
ip, _, err := net.ParseCIDR(cidr)
15+
if err != nil {
16+
return nil, err
17+
}
18+
19+
toSplit, err := strconv.Atoi(split)
20+
if err != nil {
21+
return nil, err
22+
}
23+
24+
if ip.To4() != nil {
25+
ipsubnet, err := GetIPv4Subnet(cidr)
26+
if err != nil {
27+
return nil, err
28+
}
29+
30+
subs, err := SplitCIDRIPv4(ipsubnet, toSplit)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
for _, s := range subs {
36+
netBitCntStr := strconv.Itoa(int(s.NetBitCnt))
37+
ipStr := net.IPv4(byte(s.LoIP>>24), byte(s.LoIP>>16), byte(s.LoIP>>8), byte(s.LoIP)).String()
38+
39+
res = append(res, ipStr+"/"+netBitCntStr)
40+
}
41+
} else {
42+
ipsubnet, err := GetIPv6Subnet(cidr)
43+
if err != nil {
44+
return nil, err
45+
}
46+
subs, err := SplitCIDRIPv6(ipsubnet, toSplit)
47+
if err != nil {
48+
return nil, err
49+
}
50+
for _, s := range subs {
51+
netBitCntStr := strconv.Itoa(int(s.NetBitCnt))
52+
b := make([]byte, 16)
53+
binary.BigEndian.PutUint64(b[:8], s.LoIP.Hi)
54+
binary.BigEndian.PutUint64(b[8:], s.LoIP.Lo)
55+
ipStr := net.IP(b).String()
56+
res = append(res, ipStr+"/"+netBitCntStr)
57+
}
58+
}
59+
60+
return res, nil
61+
}
62+
63+
type uint128 struct {
64+
Hi uint64
65+
Lo uint64
66+
}
67+
68+
type IPv4Subnet struct {
69+
NetBitCnt uint32
70+
NetMask uint32
71+
HostBitCnt uint32
72+
HostMask uint32
73+
LoIP uint32
74+
HiIP uint32
75+
}
76+
77+
type IPv6Subnet struct {
78+
NetBitCnt uint32
79+
NetMask uint128
80+
HostBitCnt uint32
81+
HostMask uint128
82+
LoIP uint128
83+
HiIP uint128
84+
}
85+
86+
func NetAndHostMasksIPv4(size uint32) (uint32, uint32) {
87+
if size > 32 {
88+
size = 32
89+
}
90+
91+
var mask uint32 = 0
92+
for i := uint32(0); i < size; i++ {
93+
mask += 1 << (32 - (i + 1))
94+
}
95+
96+
return mask, ^mask
97+
}
98+
99+
func NetAndHostMasksIPv6(size uint32) (uint128, uint128) {
100+
if size > 128 {
101+
size = 128
102+
}
103+
104+
var mask uint128
105+
if size > 64 {
106+
mask = uint128{^uint64(0), ^uint64(0) << (128 - size)}
107+
} else {
108+
mask = uint128{^uint64(0) << (64 - size), 0}
109+
}
110+
maskNot := uint128{^mask.Hi, ^mask.Lo}
111+
112+
return mask, maskNot
113+
}
114+
115+
func GetIPv4Subnet(cidr string) (IPv4Subnet, error) {
116+
_, network, err := net.ParseCIDR(cidr)
117+
if err != nil {
118+
return IPv4Subnet{}, err
119+
}
120+
121+
ones, _ := network.Mask.Size()
122+
netMask, hostMask := NetAndHostMasksIPv4(uint32(ones))
123+
start := binary.BigEndian.Uint32(network.IP)
124+
ipsubnet := IPv4Subnet{
125+
HostBitCnt: uint32(32 - ones),
126+
HostMask: hostMask,
127+
NetBitCnt: uint32(ones),
128+
NetMask: netMask,
129+
LoIP: uint32(start) & netMask,
130+
HiIP: (uint32(start) & netMask) | hostMask,
131+
}
132+
133+
return ipsubnet, nil
134+
}
135+
136+
func GetIPv6Subnet(cidr string) (IPv6Subnet, error) {
137+
_, network, err := net.ParseCIDR(cidr)
138+
if err != nil {
139+
return IPv6Subnet{}, err
140+
}
141+
142+
ones, _ := network.Mask.Size()
143+
netMask, hostMask := NetAndHostMasksIPv6(uint32(ones))
144+
starthi := binary.BigEndian.Uint64(network.IP[:8])
145+
startlo := binary.BigEndian.Uint64(network.IP[8:])
146+
start := uint128{Hi: starthi, Lo: startlo}
147+
ip6subnet := IPv6Subnet{
148+
HostBitCnt: uint32(128 - ones),
149+
HostMask: hostMask,
150+
NetBitCnt: uint32(ones),
151+
NetMask: netMask,
152+
LoIP: uint128{Hi: start.Hi & netMask.Hi, Lo: start.Lo & netMask.Lo},
153+
HiIP: uint128{Hi: (start.Hi & netMask.Hi) | hostMask.Hi, Lo: (start.Lo & netMask.Lo) | hostMask.Lo},
154+
}
155+
156+
return ip6subnet, nil
157+
}
158+
159+
func SplitCIDRIPv4(s IPv4Subnet, split int) ([]IPv4Subnet, error) {
160+
bitshifts := int(uint32(split) - s.NetBitCnt)
161+
if bitshifts < 0 || bitshifts > 31 || int(s.NetBitCnt)+bitshifts > 32 {
162+
return nil, errors.New("Invalid split.")
163+
}
164+
165+
hostBits := (32 - s.NetBitCnt) - uint32(bitshifts)
166+
netMask, hostMask := NetAndHostMasksIPv4(uint32(split))
167+
ipsubnets := make([]IPv4Subnet, 1<<bitshifts)
168+
for i := range ipsubnets {
169+
start := uint32(s.LoIP) + uint32(i*(1<<hostBits))
170+
ipsubnets[i] = IPv4Subnet{
171+
HostBitCnt: uint32(32 - split),
172+
HostMask: hostMask,
173+
NetBitCnt: uint32(split),
174+
LoIP: uint32(start) & netMask,
175+
HiIP: (uint32(start) & netMask) | hostMask,
176+
}
177+
}
178+
179+
return ipsubnets, nil
180+
}
181+
182+
func SplitCIDRIPv6(s IPv6Subnet, split int) ([]IPv6Subnet, error) {
183+
bitshifts := int(uint32(split) - s.NetBitCnt)
184+
if bitshifts < 0 || bitshifts > 128 || int(s.NetBitCnt)+bitshifts > 128 {
185+
return nil, errors.New("Invalid split. ")
186+
}
187+
188+
hostBits := (128 - s.NetBitCnt) - uint32(bitshifts)
189+
netMask, hostMask := NetAndHostMasksIPv6(uint32(split))
190+
subnetCount := math.Pow(2, float64(bitshifts))
191+
subnetCountBig := big.NewFloat(subnetCount)
192+
hostCount := math.Pow(2, float64(hostBits))
193+
hostCountbig := big.NewFloat(hostCount)
194+
195+
var ipsubnets []IPv6Subnet
196+
for i := big.NewFloat(0); i.Cmp(subnetCountBig) < 0; i.Add(i, big.NewFloat(1)) {
197+
hostCountMul := new(big.Float)
198+
hostCountMul.Mul(i, hostCountbig)
199+
newIP := new(big.Int)
200+
b := make([]byte, 16)
201+
binary.BigEndian.PutUint64(b[:8], s.LoIP.Hi)
202+
binary.BigEndian.PutUint64(b[8:], s.LoIP.Lo)
203+
newIP.SetBytes(b)
204+
hostCountAdd := new(big.Int)
205+
result := new(big.Int)
206+
hostCountMul.Int(result)
207+
hostCountAdd.Add(newIP, result)
208+
209+
ipArr := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
210+
copy(ipArr[16-len(hostCountAdd.Bytes()):], hostCountAdd.Bytes())
211+
startIP := uint128{Hi: binary.BigEndian.Uint64(ipArr[:8]), Lo: binary.BigEndian.Uint64(ipArr[8:])}
212+
213+
subnet := IPv6Subnet{
214+
HostBitCnt: uint32(128 - split),
215+
HostMask: hostMask,
216+
NetBitCnt: uint32(split),
217+
LoIP: uint128{Hi: startIP.Hi & netMask.Hi, Lo: startIP.Lo & netMask.Lo},
218+
HiIP: uint128{Hi: (startIP.Hi & netMask.Hi) | hostMask.Hi, Lo: (startIP.Lo & netMask.Lo) | hostMask.Lo},
219+
}
220+
ipsubnets = append(ipsubnets, subnet)
221+
}
222+
223+
return ipsubnets, nil
224+
}

0 commit comments

Comments
 (0)