master
go 171 lines 3.93 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package iprange
4
5 import (
6 "fmt"
7 "iter"
8 "math/big"
9 "net/netip"
10 )
11
12 // Family represents IP Range address-family.
13 type Family uint8
14
15 const (
16 // V4Family is IPv4 address-family.
17 V4Family Family = iota
18 // V6Family is IPv6 address-family.
19 V6Family
20 )
21
22 // String returns the string representation of the address family.
23 func (f Family) String() string {
24 switch f {
25 case V4Family:
26 return "IPv4"
27 case V6Family:
28 return "IPv6"
29 default:
30 return fmt.Sprintf("Unknown(%d)", f)
31 }
32 }
33
34 // Range represents an IP address range.
35 // It provides methods to check containment, iterate over addresses,
36 // and calculate the size of the range.
37 type Range interface {
38 // Family returns the address family (IPv4 or IPv6) of the range.
39 Family() Family
40
41 // Contains reports whether the range includes the given IP address.
42 Contains(ip netip.Addr) bool
43
44 // Size returns the number of IP addresses in the range.
45 Size() *big.Int
46
47 // Iterate returns an iterator over all IP addresses in the range.
48 Iterate() iter.Seq[netip.Addr]
49
50 // String returns the string representation of the range in "start-end" format.
51 fmt.Stringer
52
53 // Start returns the first IP address in the range.
54 Start() netip.Addr
55
56 // End returns the last IP address in the range.
57 End() netip.Addr
58 }
59
60 // New creates a new IP Range from start and end addresses.
61 // It returns nil if the range is invalid (start and end have different
62 // address families, or start > end).
63 func New(start, end netip.Addr) Range {
64 if !start.IsValid() || !end.IsValid() {
65 return nil
66 }
67
68 switch {
69 case start.Is4() && end.Is4():
70 if start.Compare(end) > 0 {
71 return nil
72 }
73 return &v4Range{start: start, end: end}
74 case start.Is6() && end.Is6() && !start.Is4In6() && !end.Is4In6():
75 if start.Compare(end) > 0 {
76 return nil
77 }
78 return &v6Range{start: start, end: end}
79 default:
80 return nil
81 }
82 }
83
84 // v4Range implements Range for IPv4 addresses.
85 type v4Range struct {
86 start netip.Addr
87 end netip.Addr
88 }
89
90 // compile-time check that v4Range implements Range
91 var _ Range = (*v4Range)(nil)
92
93 func (r *v4Range) Start() netip.Addr { return r.start }
94 func (r *v4Range) End() netip.Addr { return r.end }
95
96 func (r *v4Range) Iterate() iter.Seq[netip.Addr] {
97 return iterate(r)
98 }
99
100 func (r *v4Range) String() string {
101 return fmt.Sprintf("%s-%s", r.start, r.end)
102 }
103
104 func (r *v4Range) Family() Family {
105 return V4Family
106 }
107
108 func (r *v4Range) Contains(ip netip.Addr) bool {
109 if !ip.Is4() {
110 return false
111 }
112 return ip.Compare(r.start) >= 0 && ip.Compare(r.end) <= 0
113 }
114
115 func (r *v4Range) Size() *big.Int {
116 // For IPv4, we can safely use uint32 arithmetic
117 start := r.start.As4()
118 end := r.end.As4()
119
120 startUint := uint32(start[0])<<24 | uint32(start[1])<<16 | uint32(start[2])<<8 | uint32(start[3])
121 endUint := uint32(end[0])<<24 | uint32(end[1])<<16 | uint32(end[2])<<8 | uint32(end[3])
122
123 // Add 1 because the range is inclusive
124 return big.NewInt(int64(endUint - startUint + 1))
125 }
126
127 // v6Range implements Range for IPv6 addresses.
128 type v6Range struct {
129 start netip.Addr
130 end netip.Addr
131 }
132
133 // compile-time check that v6Range implements Range
134 var _ Range = (*v6Range)(nil)
135
136 func (r *v6Range) Start() netip.Addr { return r.start }
137 func (r *v6Range) End() netip.Addr { return r.end }
138
139 func (r *v6Range) Iterate() iter.Seq[netip.Addr] {
140 return iterate(r)
141 }
142
143 func (r *v6Range) String() string {
144 return fmt.Sprintf("%s-%s", r.start, r.end)
145 }
146
147 func (r *v6Range) Family() Family {
148 return V6Family
149 }
150
151 func (r *v6Range) Contains(ip netip.Addr) bool {
152 if !ip.Is6() || ip.Is4In6() {
153 return false
154 }
155 return ip.Compare(r.start) >= 0 && ip.Compare(r.end) <= 0
156 }
157
158 func (r *v6Range) Size() *big.Int {
159 // For IPv6, we must use big.Int to handle 128-bit arithmetic
160 startBytes := r.start.As16()
161 endBytes := r.end.As16()
162
163 startBig := new(big.Int).SetBytes(startBytes[:])
164 endBig := new(big.Int).SetBytes(endBytes[:])
165
166 // Calculate end - start + 1
167 size := new(big.Int).Sub(endBig, startBig)
168 size.Add(size, big.NewInt(1))
169
170 return size
171 }