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 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
|
package iplib
import (
"encoding/hex"
"math/big"
"net"
)
// HostMask is a mask that can be applied to IPv6 addresses to mask out bits
// from the right side of the address instead of the left (which is the
// purview of a netmask), the intended use-case is for situations where there
// is a desire to reserve a portion of the address for some other purpose and
// only allow iplib to manage the remainder. A concrete example would be
// IPv6 Interface Identifiers as described in RFC4291, RFC4941 or RFC7217 in
// which the final 64bits of the address are used to construct a unique host
// identifier and the allocator only has control of the first 64bits. So the
// next IP from 2001:db8:1234:5678:: would be 2001:db8:1234:5679 instead of
// 2001:db8:1234:5678::1. Here is a Net6 object eing initialized without a
// hostmask:
//
// n := NewNet6(2001:db8::, 56, 0)
// Address 2001:db8::
// Netmask ffff:ffff:ffff:ff00:0000:0000:0000:0000
// Hostmask 0000:0000:0000:0000:0000:0000:0000:0000
// First 2001:0db8:0000:0000:0000:0000:0000:0000
// Last 2001:0db8:0000:00ff:ffff:ffff:ffff:ffff
// Count 4722366482869645213696
//
// This creates a block with 4.7 sextillion usable addresses. Below is he same
// block with a hostmask of /60. The mask is applied from the rightmost byte,
// leaving 12 unmasked bits for a total of 4096 allocatable addresses:
//
// n:= NewNet6(2001:db8::, 56, 60)
// Address 2001:db8::
// Netmask ffff:ffff:ffff:ff00:0000:0000:0000:0000
// Hostmask 0000:0000:0000:0000:0fff:ffff:ffff:ffff
// First 2001:0db8:0000:0000:0000:0000:0000:0000
// Last 2001:0db8:0000:00ff:f000:0000:0000:0000
// Count 4096
//
// In the first example the second IP address of the netblock is 2001:db8::1,
// in the second example it is 2001:db8:0:1::
//
// One important note: even though bytes are filled in from the right the bits
// within those bytes are still blocked out left-to-right, so that address
// incrementing/decrementing makes sense to the end user, as shown here:
//
// BINARY Base16 Base10 Example Max16 Max10
// 0000 0000 0x00 0 /56 0xFF 255
// 1000 0000 0x80 128 /57 0x7F 127
// 1100 0000 0xC0 192 /58 0x3F 63
// 1110 0000 0xE0 224 /59 0x1F 31
// 1111 0000 0xF0 240 /60 0x0F 15
// 1111 1000 0xF8 248 /61 0x07 7
// 1111 1100 0xFC 252 /62 0x03 3
// 1111 1110 0xFE 254 /63 0x01 1
//
// A hostmask of /1 will block out the left-most bit of the 16th byte
// while a /8 will block the entire 16th byte.
//
// To initialize a hostmask you must give it an integer value between 1 and
// 128, which represent the number of bits in the mask.
type HostMask []byte
// NewHostMask returns a HostMask initialized to masklen
func NewHostMask(masklen int) HostMask {
mask := make([]byte, 16)
if masklen == 0 {
return mask
}
for i := 15; i >= 0; i-- {
if masklen < 8 {
mask[i] = ^byte(0xff >> uint(masklen))
break
}
mask[i] = 0xff
masklen -= 8
}
return mask
}
// BoundaryByte returns the rightmost byte in the mask in which any bits fall
// inside the hostmask, as well as the position of that byte. For example a
// masklength of 58 would return "0xc0, 8" while 32 would return "0xff, 12".
// If the hostmask is unset "0x00, -1" will be returned
func (m HostMask) BoundaryByte() (byte, int) {
hmlen, _ := m.Size() // will be between 0 and 128, where 0 is "no mask"
if hmlen == 0 {
return 0x00, -1
}
quo, mod := hmlen/8, hmlen%8
if mod == 0 {
quo--
}
pos := 15 - quo
return m[pos], pos
}
// Size returns the number of ones and total bits in the mask
func (m HostMask) Size() (int, int) {
ones := 0
bits := 128
for i := len(m) - 1; i >= 0; i-- {
b := m[i]
if b == 0xff {
ones += 8
continue
}
for b&0x80 != 0 {
ones++
b <<= 1
}
break
}
return ones, bits
}
// String returns the hexadecimal form of m, with no punctuation
func (m HostMask) String() string {
return hex.EncodeToString(m)
}
// DecrementIP6WithinHostmask returns a net.IP that is less than the unmasked
// portion of the supplied net.IP by the supplied integer value. If the
// input or output value fall outside the boundaries of the hostmask a
// ErrAddressOutOfRange will be returned
func DecrementIP6WithinHostmask(ip net.IP, hm HostMask, count *big.Int) (net.IP, error) {
xcount := new(big.Int) // do not manipulate 'count'
xcount.Set(count)
bb, bbpos := hm.BoundaryByte()
if bbpos == 0 {
return net.IP{}, ErrBadMaskLength
}
if bbpos == -1 {
return DecrementIP6By(ip, xcount), nil
}
// check if ip is outside of hostmask already
for _, b := range ip[bbpos+1:] {
if b > 0 {
return net.IP{}, ErrAddressOutOfRange
}
}
if ip[bbpos]+bb < bb {
return net.IP{}, ErrAddressOutOfRange
}
bb = decrementBoundaryByte(bb, ip[bbpos], xcount)
xip := decrementUnmaskedBytes(ip[:bbpos], xcount)
if len(xip) == 0 {
return xip, ErrAddressOutOfRange
}
if len(xip) > bbpos {
return net.IP{}, ErrAddressOutOfRange
}
xip = append(xip, bb)
xip = append(xip, make([]byte, 15-bbpos)...)
return xip, nil
}
// IncrementIP6WithinHostmask returns a net.IP that is greater than the
// unmasked portion of the supplied net.IP by the supplied integer value. If
// the input or output value fall outside the boundaries of the hostmask a
// ErrAddressOutOfRange will be returned
func IncrementIP6WithinHostmask(ip net.IP, hm HostMask, count *big.Int) (net.IP, error) {
xcount := getCloneBigInt(count)
bb, bbpos := hm.BoundaryByte()
if bbpos == 0 {
return net.IP{}, ErrBadMaskLength
}
if bbpos == -1 {
return IncrementIP6By(ip, xcount), nil
}
// check if ip is outside of hostmask already
for _, b := range ip[bbpos+1:] {
if b > 0 {
return net.IP{}, ErrAddressOutOfRange
}
}
bb = incrementBoundaryByte(bb, ip[bbpos], xcount)
xip := incrementUnmaskedBytes(ip[:bbpos], xcount)
if len(xip) > bbpos {
return net.IP{}, ErrAddressOutOfRange
}
xip = append(xip, bb)
xip = append(xip, make([]byte, 15-bbpos)...)
return xip, nil
}
// NextIP6WithinHostmask takes a net.IP and Hostmask as arguments and attempts
// to increment the IP by one, within the boundary of the hostmask. If bits
// inside the hostmask are set, an empty net.IP{} and an ErrAddressOutOfRange
// will be returned
func NextIP6WithinHostmask(ip net.IP, hm HostMask) (net.IP, error) {
xip := getCloneIP(ip)
for i := len(xip) - 1; i >= 0; i-- {
if hm[i] == 0xff {
if xip[i] > 0 {
return net.IP{}, ErrAddressOutOfRange
}
continue
}
if (xip[i] | hm[i]) == 0xff {
// xip[i] is the boundary byte, and the accessible bits are at max
xip[i] = 0
continue
}
xip[i]++
if xip[i] > 0 {
return xip, nil
}
}
return net.IP{}, ErrAddressOutOfRange
}
// PreviousIP6WithinHostmask takes a net.IP and Hostmask as arguments and
// attempts to decrement the IP by one, within the boundary of the hostmask.
// If bits inside the hostmask are set, an empty net.IP{} and an
// ErrAddressOutOfRange will be returned
func PreviousIP6WithinHostmask(ip net.IP, hm HostMask) (net.IP, error) {
xip := getCloneIP(ip)
bb, bbpos := hm.BoundaryByte()
bbmax := 0xff - bb
for i := len(xip) - 1; i >= 0; i-- {
if hm[i] == 0xff {
if xip[i] > 0 {
return net.IP{}, ErrAddressOutOfRange
}
continue
}
xip[i]--
if xip[i] != 255 {
if i == bbpos-1 {
// if we underflowed into the boundary byte we need to adjust
// it to it's actual max, not 0xff
xip[bbpos] = bbmax
}
return xip, nil
}
}
return net.IP{}, ErrAddressOutOfRange
}
// decrementBoundaryByte takes a boundary-byte, a boundary-value and a count
// as input and returns a modified boundary byte and count for further
// processing. bb is used to calculate the maximum value for bv and then the
// count + bv is divided by that max. The function will return the modulus
// as a byte value, and the pointer to count will have the quotient
func decrementBoundaryByte(bb, bv byte, count *big.Int) byte {
bmax := 256 - int(bb) // max value of unmasked byte as int
if v := count.Cmp(big.NewInt(0)); v <= 0 {
return bv
}
bigbmax := big.NewInt(int64(bmax))
bigmod := new(big.Int)
count.DivMod(count, bigbmax, bigmod)
mod64 := bigmod.Int64()
mod := byte(mod64)
if mod > bv {
count.Add(count, big.NewInt(1))
return (byte(bmax) + bv) - mod
}
return bv - mod
}
// decrementUnmaskedBytes decrements an arbitrary []byte by count and returns
// a []byte of the same length
func decrementUnmaskedBytes(nb []byte, count *big.Int) []byte {
z := new(big.Int)
z.SetBytes(nb)
z.Sub(z, count)
if v := z.Sign(); v < 0 {
return []byte{}
}
zb := z.Bytes()
if len(zb) < len(nb) {
zb = append(make([]byte, len(nb)-len(zb)), zb...)
}
return zb
}
// incrementBoundaryByte takes a boundary-byte, a boundary-value and a count
// as input and returns a modified boundary byte and count for further
// processing. bb is used to calculate the maximum value for bv and then the
// count + bv is divided by that max. The function will return the modulus
// as a byte value, and the pointer to count will have the quotient
func incrementBoundaryByte(bb, bv byte, count *big.Int) byte {
if v := count.Cmp(big.NewInt(0)); v <= 0 {
return bv
}
bigbmax := big.NewInt(256 - int64(bb)) // max value of unmasked byte as an int
bigbval := big.NewInt(int64(bv)) // cur value of unmasked byte as an int
count.Add(count, bigbval)
// if count is less than bmax, we're done
if v := count.Cmp(bigbmax); v < 0 {
b := count.Bytes()
count.Set(big.NewInt(0))
if len(b) == 0 {
return 0
}
return b[0]
}
bigmod := new(big.Int)
count.DivMod(count, bigbmax, bigmod)
mod := bigmod.Bytes()
if len(mod) == 0 {
return byte(0)
}
return mod[0]
}
// incrementUnmaskedBytes increments an arbitrary []byte by count and returns
// a []byte of the same length
func incrementUnmaskedBytes(nb []byte, count *big.Int) []byte {
z := new(big.Int)
z.SetBytes(nb)
z.Add(z, count)
zb := z.Bytes()
if len(zb) < len(nb) {
zb = append(make([]byte, len(nb)-len(zb)), zb...)
}
return zb
}
|