1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
|
package iplib
import (
"math"
"net"
"sync"
)
// Net4 is an implementation of Net intended for IPv4 netblocks. It has
// functions to return the broadcast address and wildcard mask not present in
// the IPv6 implementation
type Net4 struct {
net.IPNet
is4in6 bool
}
// NewNet4 returns an initialized Net4 object at the specified masklen. If
// mask is greater than 32, or if a v6 address is supplied, an empty Net4
// will be returned
func NewNet4(ip net.IP, masklen int) Net4 {
var maskMax = 32
if masklen > maskMax {
return Net4{IPNet: net.IPNet{}}
}
mask := net.CIDRMask(masklen, maskMax)
n := net.IPNet{IP: ForceIP4(ip).Mask(mask), Mask: mask}
return Net4{IPNet: n, is4in6: Is4in6(ip)}
}
// Net4FromStr takes a string which should be a v4 address in CIDR notation
// and returns an initialized Net4. If the string isn't parseable an empty
// Net4 will be returned
func Net4FromStr(s string) Net4 {
_, n, err := ParseCIDR(s)
if err != nil {
return Net4{}
}
if n4, ok := n.(Net4); ok {
return n4
}
return Net4{}
}
// BroadcastAddress returns the broadcast address for the represented network.
// In the context of IPv6 broadcast is meaningless and the value will be
// equivalent to LastAddress().
func (n Net4) BroadcastAddress() net.IP {
xip, _ := n.finalAddress()
return xip
}
// Contains returns true if ip is contained in the represented netblock
func (n Net4) Contains(ip net.IP) bool {
return n.IPNet.Contains(ip)
}
// ContainsNet returns true if the given Net is contained within the
// represented block
func (n Net4) ContainsNet(network Net) bool {
l1, _ := n.Mask().Size()
l2, _ := network.Mask().Size()
return l1 <= l2 && n.Contains(network.IP())
}
// Count returns the total number of usable IP addresses in the represented
// network..
func (n Net4) Count() uint32 {
ones, all := n.Mask().Size()
exp := all - ones
if exp == 1 {
return uint32(2) // special handling for RFC3021 /31
}
if exp == 0 {
return uint32(1) // special handling for /32
}
return uint32(math.Pow(2, float64(exp))) - 2
}
// Enumerate generates an array of all usable addresses in Net up to the
// given size starting at the given offset. If size=0 the entire block is
// enumerated.
//
// NOTE: RFC3021 defines a use case for netblocks of /31 for use in point-to-
// point links. For this reason enumerating networks at these lengths will
// return a 2-element array even though it would naturally return none.
//
// For consistency, enumerating a /32 will return the IP in a 1 element array
func (n Net4) Enumerate(size, offset int) []net.IP {
if n.IP() == nil {
return nil
}
count := int(n.Count())
// offset exceeds total, return an empty array
if offset > count {
return []net.IP{}
}
// size is greater than the number of addresses that can be returned,
// adjust the size of the slice but keep going
if size > (count-offset) || size == 0 {
size = count - offset
}
// Handle edge-case mask sizes
if count == 1 { // Count() returns 1 if host-bits == 0
return []net.IP{getCloneIP(n.IPNet.IP)}
}
addrs := make([]net.IP, size)
netu := IP4ToUint32(n.FirstAddress())
netu += uint32(offset)
fip := Uint32ToIP4(netu)
limit := 65535
pos := 0
wg := sync.WaitGroup{}
for pos < size {
incr := limit
if limit > (size - pos) {
incr = size - pos
}
wg.Add(1)
go func(fip net.IP, pos, count int) {
defer wg.Done()
addrs[pos] = IncrementIP4By(fip, uint32(pos))
for i := 1; i < count; i++ {
pos++
addrs[pos] = NextIP(addrs[pos-1])
}
}(fip, pos, incr)
pos = pos + incr
}
wg.Wait()
return addrs
}
// FirstAddress returns the first usable address for the represented network
func (n Net4) FirstAddress() net.IP {
ones, _ := n.Mask().Size()
// if it's either a single IP or RFC 3021, return the network address
if ones >= 31 {
return n.IPNet.IP
}
return NextIP(n.IP())
}
// Is4in6 will return true if this Net4 object or any of its parents were
// explicitly initialized with a 4in6 address (::ffff:xxxx.xxx)
func (n Net4) Is4in6() bool {
return n.is4in6
}
// LastAddress returns the last usable address for the represented network
func (n Net4) LastAddress() net.IP {
xip, ones := n.finalAddress()
// if it's either a single IP or RFC 3021, return the last address
if ones >= 31 {
return xip
}
return PreviousIP(xip)
}
// Mask returns the netmask of the netblock
func (n Net4) Mask() net.IPMask {
return n.IPNet.Mask
}
// IP returns the network address for the represented network, e.g.
// the lowest IP address in the given block
func (n Net4) IP() net.IP {
return n.IPNet.IP
}
// NetworkAddress returns the network address for the represented network, e.g.
// the lowest IP address in the given block
func (n Net4) NetworkAddress() net.IP {
return n.IPNet.IP
}
// NextIP takes a net.IP as an argument and attempts to increment it by one.
// If the resulting address is outside of the range of the represented network
// it will return an empty net.IP and an ErrAddressOutOfRange. If the result
// is the broadcast address, the address _will_ be returned, but so will an
// ErrBroadcastAddress, to indicate that the address is technically
// outside the usable scope
func (n Net4) NextIP(ip net.IP) (net.IP, error) {
if !n.Contains(ip) {
return net.IP{}, ErrAddressOutOfRange
}
xip := NextIP(ip)
if !n.Contains(xip) {
return net.IP{}, ErrAddressOutOfRange
}
// if this is the broadcast address, return it but warn the caller via error
if n.BroadcastAddress().Equal(xip) {
return xip, ErrBroadcastAddress
}
return xip, nil
}
// NextNet takes a CIDR mask-size as an argument and attempts to create a new
// Net object just after the current Net, at the requested mask length
func (n Net4) NextNet(masklen int) Net4 {
return NewNet4(NextIP(n.BroadcastAddress()), masklen)
}
// PreviousIP takes a net.IP as an argument and attempts to decrement it by
// one. If the resulting address is outside of the range of the represented
// network it will return an empty net.IP and an ErrAddressOutOfRange. If the
// result is the network address, the address _will_ be returned, but so will
// an ErrNetworkAddress, to indicate that the address is technically outside
// the usable scope
func (n Net4) PreviousIP(ip net.IP) (net.IP, error) {
if !n.Contains(ip) {
return net.IP{}, ErrAddressOutOfRange
}
xip := PreviousIP(ip)
if !n.Contains(xip) {
return net.IP{}, ErrAddressOutOfRange
}
// if this is the network address, return it but warn the caller via error
if n.IP().Equal(xip) {
return xip, ErrNetworkAddress
}
return xip, nil
}
// PreviousNet takes a CIDR mask-size as an argument and creates a new Net
// object just before the current one, at the requested mask length. If the
// specified mask is for a larger network than the current one then the new
// network may encompass the current one, e.g.:
//
// iplib.Net{192.168.4.0/22}.Subnet(21) -> 192.168.0.0/21
//
// In the above case 192.168.4.0/22 is part of 192.168.0.0/21
func (n Net4) PreviousNet(masklen int) Net4 {
return NewNet4(PreviousIP(n.IP()), masklen)
}
// String returns the CIDR notation of the enclosed network e.g. 192.168.0.1/24
func (n Net4) String() string {
return n.IPNet.String()
}
// Subnet takes a CIDR mask-size as an argument and carves the current Net
// object into subnets of that size, returning them as a []Net. The mask
// provided must be a larger-integer than the current mask. If set to 0 Subnet
// will carve the network in half
func (n Net4) Subnet(masklen int) ([]Net4, error) {
ones, all := n.Mask().Size()
if masklen == 0 {
masklen = ones + 1
}
if ones > masklen || masklen > all {
return nil, ErrBadMaskLength
}
mask := net.CIDRMask(masklen, all)
netlist := []Net4{{IPNet: net.IPNet{IP: n.IP(), Mask: mask}, is4in6: n.is4in6}}
for CompareIPs(netlist[len(netlist)-1].BroadcastAddress(), n.BroadcastAddress()) == -1 {
ng := net.IPNet{IP: NextIP(netlist[len(netlist)-1].BroadcastAddress()), Mask: mask}
netlist = append(netlist, Net4{ng, n.is4in6})
}
return netlist, nil
}
// Supernet takes a CIDR mask-size as an argument and returns a Net object
// containing the supernet of the current Net at the requested mask length.
// The mask provided must be a smaller-integer than the current mask. If set
// to 0 Supernet will return the next-largest network
//
// Examples:
// Net{192.168.1.0/24}.Supernet(0) -> Net{192.168.0.0/23}
// Net{192.168.1.0/24}.Supernet(22) -> Net{Net{192.168.0.0/22}
func (n Net4) Supernet(masklen int) (Net4, error) {
ones, all := n.Mask().Size()
if ones < masklen {
return Net4{}, ErrBadMaskLength
}
if masklen == 0 {
masklen = ones - 1
}
mask := net.CIDRMask(masklen, all)
ng := net.IPNet{IP: n.IP().Mask(mask), Mask: mask}
return Net4{ng, n.is4in6}, nil
}
// Version returns the version of IP for the enclosed netblock, 4 in this case
func (n Net4) Version() int {
return IP4Version
}
// Wildcard will return the wildcard mask for a given netmask
func (n Net4) Wildcard() net.IPMask {
wc := make([]byte, len(n.Mask()))
for pos, b := range n.Mask() {
wc[pos] = 0xff - b
}
return wc
}
// finalAddress returns the last address in the network. It is private
// because both LastAddress() and BroadcastAddress() rely on it, and both use
// it differently. It returns the last address in the block as well as the
// number of masked bits as an int.
func (n Net4) finalAddress() (net.IP, int) {
xip := make([]byte, len(n.IP()))
ones, _ := n.Mask().Size()
// apply wildcard to network, byte by byte
wc := n.Wildcard()
for pos, b := range []byte(n.IP()) {
xip[pos] = b + wc[pos]
}
return xip, ones
}
|