CIDR to ip range supports
This commit is contained in:
parent
59cf547107
commit
353524325a
|
|
@ -16,19 +16,42 @@ type Segment struct {
|
|||
}
|
||||
|
||||
func SegmentFrom(seg string, cRegion func(string) *Region) (*Segment, error) {
|
||||
var ps = strings.SplitN(strings.TrimSpace(seg), "|", 3)
|
||||
if len(ps) != 3 {
|
||||
var count = 0
|
||||
var ps = StringTokenizer(strings.TrimSpace(seg), "|", func(s string, start int) bool {
|
||||
// CIDR format
|
||||
if strings.Index(s, "/") > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
count++
|
||||
return count < 2
|
||||
})
|
||||
|
||||
var err error
|
||||
var sip, eip []byte
|
||||
var rIdx = 0
|
||||
switch pl := len(ps); pl {
|
||||
case 2:
|
||||
// CIDR format
|
||||
rIdx = 1
|
||||
case 3:
|
||||
// triditional ip range
|
||||
rIdx = 2
|
||||
sip, err = ParseIP(ps[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parser start ip `%s`: %s", ps[0], err)
|
||||
}
|
||||
|
||||
eip, err = ParseIP(ps[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check end ip `%s`: %s", ps[1], err)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid ip segment `%s`", seg)
|
||||
}
|
||||
|
||||
sip, err := ParseIP(ps[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check start ip `%s`: %s", ps[0], err)
|
||||
}
|
||||
|
||||
eip, err := ParseIP(ps[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check end ip `%s`: %s", ps[1], err)
|
||||
if len(sip) != len(eip) {
|
||||
return nil, fmt.Errorf("invalid ip segment line `%s`, sip/eip version not match", seg)
|
||||
}
|
||||
|
||||
if IPCompare(sip, eip) > 0 {
|
||||
|
|
@ -38,7 +61,7 @@ func SegmentFrom(seg string, cRegion func(string) *Region) (*Segment, error) {
|
|||
return &Segment{
|
||||
StartIP: sip,
|
||||
EndIP: eip,
|
||||
Region: cRegion(ps[2]),
|
||||
Region: cRegion(ps[rIdx]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -35,6 +36,44 @@ func ParseIP(ip string) ([]byte, error) {
|
|||
return nil, fmt.Errorf("invalid ip address: %s", ip)
|
||||
}
|
||||
|
||||
var bitMaskList = []uint8{
|
||||
0b1111_1111, // all zero
|
||||
0b0111_1111,
|
||||
0b0011_1111,
|
||||
0b0001_1111,
|
||||
0b0000_1111,
|
||||
0b0000_0111,
|
||||
0b0000_0011,
|
||||
0b0000_0001,
|
||||
}
|
||||
|
||||
func CIDR2Range(cidrStr string) ([]byte, []byte, error) {
|
||||
prefix, err := netip.ParsePrefix(cidrStr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Get the start IP (Network Address)
|
||||
// Masked() zeros out the host bits, which gives the starting IP of the subnet.
|
||||
sip := prefix.Masked().Addr().AsSlice()
|
||||
eip := make([]byte, len(sip))
|
||||
copy(eip, sip)
|
||||
|
||||
// Calculate the end IP (Broadcast Address)
|
||||
bits := prefix.Bits()
|
||||
|
||||
// border byte rest bit filled with 1
|
||||
bByteIdx := bits / 8
|
||||
eip[bByteIdx] |= bitMaskList[bits-(bByteIdx*8)]
|
||||
|
||||
// fill all the rest bits with 1
|
||||
for bi := bByteIdx + 1; bi < len(sip); bi++ {
|
||||
eip[bi] |= 0b1111_1111
|
||||
}
|
||||
|
||||
return sip, eip, nil
|
||||
}
|
||||
|
||||
func IP2String(ip []byte) string {
|
||||
return net.IP(ip[:]).String()
|
||||
}
|
||||
|
|
@ -309,3 +348,41 @@ func RegionFiltering(region string, fields []int) (string, error) {
|
|||
|
||||
return strings.Join(sb, "|"), nil
|
||||
}
|
||||
|
||||
// do the string split step by step as caller needed
|
||||
func StringTokenizer(str, substr string, cb func(s string, start int) bool) []string {
|
||||
var tokens []string
|
||||
var token string
|
||||
var sIdx, oIdx, isEOF = 0, 0, false
|
||||
for {
|
||||
// do the token match
|
||||
nIdx := strings.Index(str[sIdx:], substr)
|
||||
if nIdx == -1 {
|
||||
isEOF = true
|
||||
token = str[sIdx:]
|
||||
} else {
|
||||
token = str[sIdx : sIdx+nIdx]
|
||||
}
|
||||
|
||||
oIdx = sIdx // backup the old index
|
||||
sIdx = sIdx + nIdx + 1 // reset the next start index
|
||||
tokens = append(tokens, token) // append the token
|
||||
|
||||
// check and call the callback
|
||||
if cb(token, oIdx) == false {
|
||||
// keep the last token
|
||||
if sIdx < len(str) {
|
||||
tokens = append(tokens, str[sIdx:])
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// check the EOF
|
||||
if isEOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return tokens
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -265,3 +266,59 @@ func TestIterateSegments(t *testing.T) {
|
|||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringTokenizer(t *testing.T) {
|
||||
var strList = []string{
|
||||
"24.231.126.0/24|14537 2914 29866|IGP",
|
||||
"24.231.126.0|24.231.126.255|14537 2914 29866|IGP",
|
||||
}
|
||||
|
||||
var counter = 0
|
||||
for _, str := range strList {
|
||||
tokens := StringTokenizer(str, "|", func(s string, start int) bool {
|
||||
// fmt.Printf("%s[idx=%d, |]=%s\n", str, start, s)
|
||||
if counter == 0 {
|
||||
if strings.Index(s, "/") > 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
counter++
|
||||
return counter < 2
|
||||
})
|
||||
fmt.Printf("%d tokens: %s\n", len(tokens), strings.Join(tokens, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDR2Range(t *testing.T) {
|
||||
var strList = [][3]string{
|
||||
{"43.247.92.0/22", "43.247.92.0", "43.247.95.255"},
|
||||
{"64.252.86.39/29", "64.252.86.32", "64.252.86.39"},
|
||||
{"103.37.44.0/22", "103.37.44.0", "103.37.47.255"},
|
||||
{"111.223.12.0/22", "111.223.12.0", "111.223.15.255"},
|
||||
{"43.248.80.0/20", "43.248.80.0", "43.248.95.255"},
|
||||
{"192.168.100.0/22", "192.168.100.0", "192.168.103.255"},
|
||||
{"2403:3380::/32", "2403:3380::", "2403:3380:ffff:ffff:ffff:ffff:ffff:ffff"},
|
||||
{"2001:db8:85a3::/64", "2001:db8:85a3::", "2001:db8:85a3:0:ffff:ffff:ffff:ffff"},
|
||||
{"2001:db8:abcd::/48", "2001:db8:abcd::", "2001:db8:abcd:ffff:ffff:ffff:ffff:ffff"},
|
||||
}
|
||||
|
||||
for _, item := range strList {
|
||||
sip, eip, err := CIDR2Range(item[0])
|
||||
if err != nil {
|
||||
t.Fatalf("CIDR2Range: %s", err)
|
||||
}
|
||||
|
||||
sStr := IP2String(sip)
|
||||
eStr := IP2String(eip)
|
||||
if sStr != item[1] {
|
||||
t.Fatalf("start ip %s != %s", sStr, item[1])
|
||||
}
|
||||
|
||||
if eStr != item[2] {
|
||||
t.Fatalf("end ip %s != %s", eStr, item[2])
|
||||
}
|
||||
|
||||
fmt.Printf("cidr=%s: {sip=%s, eip=%s}\n", item[0], sStr, eStr)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue