diff --git a/ip_range/ip_range.go b/ip_range/ip_range.go index 51018b4..be35b18 100644 --- a/ip_range/ip_range.go +++ b/ip_range/ip_range.go @@ -3,9 +3,13 @@ package iprange import ( "fmt" "net/netip" + "regexp" + "strconv" "strings" ) +var maskPattern = regexp.MustCompile(`\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b`) + type IpRange struct { segments []ipSegment } @@ -96,7 +100,15 @@ func createSegment(ip string) (ipSegment, error) { }, nil case strings.Contains(ip, "/"): - if prefix, err := netip.ParsePrefix(ip); err != nil { + sec := strings.Split(ip, "/") + ip := sec[0] + mask := sec[1] + + if maskPattern.MatchString(mask) { + mask = strconv.Itoa(MaskToBits(mask)) + } + + if prefix, err := netip.ParsePrefix(ip + "/" + mask); err != nil { return nil, err } else { return &prefixSegments{prefix: prefix}, nil diff --git a/ip_range/ip_range_test.go b/ip_range/ip_range_test.go index e4e325c..32f0c9b 100644 --- a/ip_range/ip_range_test.go +++ b/ip_range/ip_range_test.go @@ -30,6 +30,31 @@ func TestSingleIp(t *testing.T) { assert.False(t, r.Contains("192.168.0.123")) } +func TestSinglePrefix(t *testing.T) { + r, err := NewRange("192.168.2.100/32") + if err != nil { + t.Fatal(err) + } + + assert.False(t, r.Contains("192.168.2.56")) + assert.True(t, r.Contains("192.168.2.100")) + assert.False(t, r.Contains("192.168.2.130")) +} + +func TestAllIp(t *testing.T) { + r, err := NewRange("0.0.0.0/0") + if err != nil { + t.Fatal(err) + } + + assert.True(t, r.Contains("192.168.2.100")) + assert.True(t, r.Contains("192.3.2.100")) + assert.True(t, r.Contains("192.65.2.100")) + assert.True(t, r.Contains("172.168.2.100")) + assert.True(t, r.Contains("8.8.8.8")) + assert.True(t, r.Contains("114.114.114.114")) +} + func TestPrefix(t *testing.T) { r, err := NewRange("192.168.2.0/24") if err != nil { @@ -53,6 +78,18 @@ func TestPrefix2(t *testing.T) { assert.False(t, r.Contains("192.168.2.162")) } +func TestDotMask(t *testing.T) { + r, err := NewRange("192.168.15.0/255.255.248.0") + if err != nil { + t.Fatal(err) + } + + assert.True(t, r.Contains("192.168.8.10")) + assert.True(t, r.Contains("192.168.14.162")) + assert.False(t, r.Contains("192.168.3.162")) + assert.False(t, r.Contains("192.168.2.162")) +} + func TestRange(t *testing.T) { r, err := NewRange("192.168.2.20-192.168.2.30") if err != nil { diff --git a/ip_range/mask_bits.go b/ip_range/mask_bits.go new file mode 100644 index 0000000..51e6cc8 --- /dev/null +++ b/ip_range/mask_bits.go @@ -0,0 +1,34 @@ +package iprange + +import "strings" + +var maskBits = map[string]int{ + "255": 8, + "254": 7, + "252": 6, + "248": 5, + "240": 4, + "224": 3, + "192": 2, + "128": 1, + "0": 0, +} + +func MaskToBits(mask string) int { + bits := 0 + + secs := strings.Split(mask, ".") + if len(secs) != 4 { + panic("the mask is incorrect") + } + + for _, s := range secs { + if v, ok := maskBits[s]; ok { + bits += v + } else { + panic("the mask is incorrect") + } + } + + return bits +} diff --git a/ip_range/mask_bits_test.go b/ip_range/mask_bits_test.go new file mode 100644 index 0000000..8fc232b --- /dev/null +++ b/ip_range/mask_bits_test.go @@ -0,0 +1,26 @@ +package iprange + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMaskToBits(t *testing.T) { + + masks := []struct { + mask string + expect int + }{ + {"255.255.255.0", 24}, + {"255.255.248.0", 21}, + {"255.255.192.0", 18}, + {"255.255.255.192", 26}, + } + + for _, m := range masks { + bits := MaskToBits(m.mask) + assert.Equal(t, m.expect, bits, fmt.Sprintf("IP:%s 掩码位数错误。", m.mask)) + } +}