forked from digitalocean/go-openvswitch
/
portrange.go
130 lines (104 loc) · 3.33 KB
/
portrange.go
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
// Copyright 2017 DigitalOcean.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ovs
import (
"errors"
"math"
)
var (
// ErrInvalidPortRange is returned when there's a port range that invalid.
ErrInvalidPortRange = errors.New("invalid port range")
)
// An PortRange represents a range of ports expressed in 16 bit integers. The start and
// end values of this range are inclusive.
type PortRange struct {
Start uint16
End uint16
}
// A BitRange is a representation of a range of values from base value with a bitmask
// applied.
type BitRange struct {
Value uint16
Mask uint16
}
// BitwiseMatch returns an array of BitRanges that represent the range of integers
// in the PortRange.
func (r *PortRange) BitwiseMatch() ([]BitRange, error) {
if r.Start <= 0 || r.End <= 0 {
return nil, ErrInvalidPortRange
}
if r.Start > r.End {
return nil, ErrInvalidPortRange
}
if r.Start == r.End {
return []BitRange{
{Value: r.Start, Mask: 0xffff},
}, nil
}
bitRanges := []BitRange{}
// Find the largest window we can get on a binary boundary
window := (r.End - r.Start) + 1
bitLength := uint(math.Floor(math.Log2(float64(window))))
rangeStart, rangeEnd := getRange(r.End, bitLength)
// Decrement our mask until we fit inside the range we want from a binary boundary.
for rangeEnd > r.End {
bitLength--
rangeStart, rangeEnd = getRange(r.End, bitLength)
}
current := BitRange{
Value: rangeStart,
Mask: getMask(bitLength),
}
// The range we picked out was from the middle of our set, so we'll need to recurse on
// the remaining values for anything less than or greater than the current
// range.
if r.Start != rangeStart {
leftRemainder := PortRange{
Start: r.Start,
End: rangeStart - 1,
}
leftRemainingBitRanges, err := leftRemainder.BitwiseMatch()
if err != nil {
return nil, err
}
bitRanges = append(bitRanges, leftRemainingBitRanges...)
}
// We append our current range here, so we're ordered properly.
bitRanges = append(bitRanges, current)
if r.End != rangeEnd {
rightRemainder := PortRange{
Start: rangeEnd + 1,
End: r.End,
}
rightRemainingBitRanges, err := rightRemainder.BitwiseMatch()
if err != nil {
return nil, err
}
bitRanges = append(bitRanges, rightRemainingBitRanges...)
}
return bitRanges, nil
}
func getMask(bitLength uint) uint16 {
// All 1s for everything that doesn't change in the range
return math.MaxUint16 ^ uint16((1<<bitLength)-1)
}
func getRange(end uint16, bitLength uint) (rangeStart uint16, rangeEnd uint16) {
// Represents the upper bound of our range window (all 1s to binary boundary)
rangeLength := uint16((1 << bitLength) - 1)
// Zero out our mask, so we start at a binary boundary.
rangeStart = end &^ rangeLength
// Simply add the mask so we end at a binary boundary.
rangeEnd = rangeStart + rangeLength
return rangeStart, rangeEnd
}