11package csnet
22
33import (
4- "github.com/crowdsecurity/crowdsec/pkg/types"
4+ "encoding/binary"
5+ "errors"
6+ "math"
7+ "net/netip"
8+ "strings"
59)
610
711type IPAddrSize int
@@ -11,37 +15,98 @@ const (
1115 IPv6Size IPAddrSize = 16
1216)
1317
14- type IP struct {
18+ type IntIP struct {
1519 size IPAddrSize
1620 Addr int64
1721 Sfx int64
1822}
1923
2024type Range struct {
21- Start IP
22- End IP
25+ Start IntIP
26+ End IntIP
2327}
2428
2529func (r Range) Size() int {
2630 return int(r.Start.size)
2731}
2832
33+ // NewIP converts a netip.Addr into an IntIP for storage and comparison.
34+ func NewIP(addr netip.Addr) IntIP {
35+ if addr.Is4() {
36+ ipBytes := addr.As4()
37+
38+ return IntIP{
39+ size: IPv4Size,
40+ Addr: uint2int(uint64(binary.BigEndian.Uint32(ipBytes[:]))),
41+ Sfx: 0,
42+ }
43+ }
44+
45+ ipBytes := addr.As16()
46+
47+ return IntIP{
48+ size: IPv6Size,
49+ Addr: uint2int(binary.BigEndian.Uint64(ipBytes[0:8])),
50+ Sfx: uint2int(binary.BigEndian.Uint64(ipBytes[8:16])),
51+ }
52+ }
53+
54+ // NewRange parses an IP or CIDR string into a Range of IntIP addresses.
55+ // If the input is a single IP (e.g. "1.2.3.4"), the start and end of the range are equal.
56+ // If the input is a prefix (e.g. "1.2.3.0/24"), the function computes the first and last
57+ // addresses covered by the prefix.
2958func NewRange(anyIP string) (Range, error) {
30- size, start_ip, start_sfx, end_ip, end_sfx, err := types.Addr2Ints(anyIP)
59+ if !strings.Contains(anyIP, "/") {
60+ addr, err := netip.ParseAddr(anyIP)
61+ if err != nil {
62+ return Range{}, err
63+ }
64+
65+ ip := NewIP(addr)
66+
67+ return Range{Start: ip, End: ip}, nil
68+ }
69+
70+ prefix, err := netip.ParsePrefix(anyIP)
3171 if err != nil {
3272 return Range{}, err
3373 }
3474
35- return Range{
36- Start: IP{
37- size: IPAddrSize(size),
38- Addr: start_ip,
39- Sfx: start_sfx,
40- },
41- End: IP{
42- size: IPAddrSize(size),
43- Addr: end_ip,
44- Sfx: end_sfx,
45- },
46- }, nil
75+ start := prefix.Masked().Addr()
76+ bits := prefix.Bits()
77+
78+ if start.Is4In6() && bits < 96 {
79+ return Range{}, errors.New("prefix with 4in6 address must have mask >= 96")
80+ }
81+
82+ a16 := start.As16()
83+
84+ if start.Is4() {
85+ bits += 96
86+ }
87+
88+ // Fill host bits with 1s
89+ for b := bits; b < 128; b++ {
90+ a16[b/8] |= 1 << (7 - (b % 8))
91+ }
92+
93+ end := netip.AddrFrom16(a16)
94+ if start.Is4() {
95+ end = end.Unmap()
96+ }
97+
98+ return Range{Start: NewIP(start), End: NewIP(end)}, nil
99+ }
100+
101+ func uint2int(u uint64) int64 {
102+ switch {
103+ case u == math.MaxInt64:
104+ return 0
105+ case u == math.MaxUint64:
106+ return math.MaxInt64
107+ case u > math.MaxInt64:
108+ return int64(u - math.MaxInt64)
109+ default:
110+ return int64(u) - math.MaxInt64
111+ }
47112}
0 commit comments