github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/lookup/lookup.go (about) 1 package lookup 2 3 import ( 4 "errors" 5 "fmt" 6 "sort" 7 "strings" 8 9 "go.aporeto.io/enforcerd/trireme-lib/controller/constants" 10 "go.aporeto.io/enforcerd/trireme-lib/policy" 11 "go.aporeto.io/enforcerd/trireme-lib/utils/portspec" 12 "go.uber.org/zap" 13 ) 14 15 // ForwardingPolicy is an instance of the forwarding policy 16 type ForwardingPolicy struct { 17 tags []policy.KeyValueOperator 18 count int 19 index int 20 actions interface{} 21 } 22 23 // intList is a list of integeres 24 type intList []int 25 26 //PolicyDB is the structure of a policy 27 type PolicyDB struct { 28 // rules []policy 29 numberOfPolicies int 30 equalPrefixes map[string]intList 31 equalMapTable map[string]map[string][]*ForwardingPolicy 32 equalIDMapTable map[string][]*ForwardingPolicy 33 notEqualMapTable map[string]map[string][]*ForwardingPolicy 34 notStarTable map[string][]*ForwardingPolicy 35 defaultNotExistsPolicy *ForwardingPolicy 36 } 37 38 //NewPolicyDB creates a new PolicyDB for efficient search of policies 39 func NewPolicyDB() (m *PolicyDB) { 40 41 m = &PolicyDB{ 42 numberOfPolicies: 0, 43 equalPrefixes: map[string]intList{}, 44 equalMapTable: map[string]map[string][]*ForwardingPolicy{}, 45 equalIDMapTable: map[string][]*ForwardingPolicy{}, 46 notEqualMapTable: map[string]map[string][]*ForwardingPolicy{}, 47 notStarTable: map[string][]*ForwardingPolicy{}, 48 defaultNotExistsPolicy: nil, 49 } 50 51 return m 52 } 53 54 func (array intList) sortedInsert(value int) intList { 55 l := len(array) 56 if l == 0 { 57 array = append(array, value) 58 return array 59 } 60 61 i := sort.Search(l, func(i int) bool { 62 return array[i] <= value 63 }) 64 65 if i == 0 { // new value is the largest 66 array = append([]int{value}, array...) 67 return array 68 } 69 70 if i == l-1 { // new value is the smallest 71 array = append(array, value) 72 return array 73 } 74 75 inserted := append(array[0:i], value) 76 77 return append(inserted, array[i:]...) 78 79 } 80 81 //AddPolicy adds a policy to the database 82 func (m *PolicyDB) AddPolicy(selector policy.TagSelector) (policyID int) { 83 84 // Create a new policy object 85 e := ForwardingPolicy{ 86 count: 0, 87 tags: selector.Clause, 88 actions: selector.Policy, 89 } 90 91 // For each tag of the incoming policy add a mapping between the map tables 92 // and the structure that represents the policy 93 for _, keyValueOp := range selector.Clause { 94 95 switch keyValueOp.Operator { 96 97 case policy.KeyExists: 98 m.equalPrefixes[keyValueOp.Key] = m.equalPrefixes[keyValueOp.Key].sortedInsert(0) 99 if _, ok := m.equalMapTable[keyValueOp.Key]; !ok { 100 m.equalMapTable[keyValueOp.Key] = map[string][]*ForwardingPolicy{} 101 } 102 m.equalMapTable[keyValueOp.Key][""] = append(m.equalMapTable[keyValueOp.Key][""], &e) 103 e.count++ 104 105 case policy.KeyNotExists: 106 m.notStarTable[keyValueOp.Key] = append(m.notStarTable[keyValueOp.Key], &e) 107 if len(selector.Clause) == 1 { 108 m.defaultNotExistsPolicy = &e 109 } 110 111 case policy.Equal: 112 if _, ok := m.equalMapTable[keyValueOp.Key]; !ok { 113 m.equalMapTable[keyValueOp.Key] = map[string][]*ForwardingPolicy{} 114 } 115 for _, v := range keyValueOp.Value { 116 if end := len(v) - 1; v[end] == '*' { 117 m.equalPrefixes[keyValueOp.Key] = m.equalPrefixes[keyValueOp.Key].sortedInsert(end) 118 m.equalMapTable[keyValueOp.Key][v[:end]] = append(m.equalMapTable[keyValueOp.Key][v[:end]], &e) 119 } else { 120 m.equalMapTable[keyValueOp.Key][v] = append(m.equalMapTable[keyValueOp.Key][v], &e) 121 } 122 } 123 if keyValueOp.ID != "" { 124 if _, ok := m.equalIDMapTable[keyValueOp.ID]; !ok { 125 m.equalIDMapTable[keyValueOp.ID] = []*ForwardingPolicy{} 126 } 127 m.equalIDMapTable[keyValueOp.ID] = append(m.equalIDMapTable[keyValueOp.ID], &e) 128 } 129 e.count++ 130 131 default: // policy.NotEqual 132 if _, ok := m.notEqualMapTable[keyValueOp.Key]; !ok { 133 m.notEqualMapTable[keyValueOp.Key] = map[string][]*ForwardingPolicy{} 134 } 135 for _, v := range keyValueOp.Value { 136 m.notEqualMapTable[keyValueOp.Key][v] = append(m.notEqualMapTable[keyValueOp.Key][v], &e) 137 e.count++ 138 } 139 } 140 } 141 142 // Increase the number of policies 143 m.numberOfPolicies++ 144 145 // Give the policy an index 146 e.index = m.numberOfPolicies 147 148 // Return the ID 149 return e.index 150 151 } 152 153 var ( 154 errInvalidTag = errors.New("tag must be k=v") 155 ) 156 157 // Custom implementation for splitting strings. Gives significant performance 158 // improvement. Do not allocate new strings 159 func (m *PolicyDB) tagSplit(tag string, k *string, v *string) error { 160 l := len(tag) 161 if l < 3 { 162 return errInvalidTag 163 } 164 165 if tag[0] == '=' { 166 return errInvalidTag 167 } 168 169 for i := 0; i < l; i++ { 170 if tag[i] == '=' { 171 if i+1 >= l { 172 return errInvalidTag 173 } 174 *k = tag[:i] 175 *v = tag[i+1:] 176 return nil 177 } 178 } 179 180 return errInvalidTag 181 } 182 183 // Search searches for a set of tags in the database to find a policy match 184 func (m *PolicyDB) Search(tags *policy.TagStore) (int, interface{}) { 185 186 count := make([]int, m.numberOfPolicies+1) 187 188 skip := make([]bool, m.numberOfPolicies+1) 189 190 // Disable all policies that fail the not key exists 191 copiedTags := tags.GetSlice() 192 var k, v string 193 194 for _, t := range copiedTags { 195 if err := m.tagSplit(t, &k, &v); err != nil { 196 continue 197 } 198 for _, policy := range m.notStarTable[k] { 199 skip[policy.index] = true 200 } 201 } 202 203 // Go through the list of tags 204 for _, t := range copiedTags { 205 206 // Search for matches of t (tag id) 207 if index, action := searchInMapTable(m.equalIDMapTable[t], nil, count, skip); index >= 0 { 208 return index, action 209 } 210 211 if err := m.tagSplit(t, &k, &v); err != nil { 212 continue 213 } 214 215 var ports *portspec.PortSpec 216 if k == constants.PortNumberLabelString { 217 // We should get range here 218 tagValue, servicePorts, err := parseTagValueRange(v) 219 if err != nil || servicePorts == nil { 220 continue 221 } 222 v = tagValue 223 ports = servicePorts 224 } 225 226 // Search for matches of k=v 227 if index, action := searchInMapTable(m.equalMapTable[k][v], ports, count, skip); index >= 0 { 228 return index, action 229 } 230 231 // Search for matches in prefixes 232 for _, i := range m.equalPrefixes[k] { 233 if i <= len(v) { 234 if index, action := searchInMapTable(m.equalMapTable[k][v[:i]], nil, count, skip); index >= 0 { 235 return index, action 236 } 237 } 238 } 239 240 // Parse all of the policies that have a key that matches the incoming tag key 241 // and a not equal operator and that has a not match rule 242 for value, policies := range m.notEqualMapTable[k] { 243 if v == value { 244 continue 245 } 246 247 if index, action := searchInMapTable(policies, nil, count, skip); index >= 0 { 248 return index, action 249 } 250 } 251 } 252 253 if m.defaultNotExistsPolicy != nil && !skip[m.defaultNotExistsPolicy.index] { 254 return m.defaultNotExistsPolicy.index, m.defaultNotExistsPolicy.actions 255 } 256 257 return -1, nil 258 } 259 260 func searchInMapTable(table []*ForwardingPolicy, ports *portspec.PortSpec, count []int, skip []bool) (int, interface{}) { 261 for _, policy := range table { 262 263 // Skip the policy if we have marked it 264 if skip[policy.index] { 265 continue 266 } 267 268 if ports != nil { 269 for _, tag := range policy.tags { 270 if tag.PortRange != nil && tag.PortRange.Intersects(ports) { 271 count[policy.index]++ 272 break 273 } 274 } 275 } else { 276 // Since a policy is hit, the count of remaining tags is reduced by one 277 count[policy.index]++ 278 } 279 280 // If all tags of the policy have been hit, there is a match 281 if count[policy.index] == policy.count { 282 return policy.index, policy.actions 283 } 284 285 } 286 287 return -1, nil 288 } 289 290 // PrintPolicyDB is a debugging function to dump the map 291 func (m *PolicyDB) PrintPolicyDB() { 292 293 zap.L().Debug("Print Policy DB: equal table") 294 295 for key, values := range m.equalMapTable { 296 for value, policies := range values { 297 zap.L().Debug("Print Policy DB", 298 zap.String("policies", fmt.Sprintf("%#v", policies)), 299 zap.String("key", key), 300 zap.String("value", value), 301 ) 302 } 303 } 304 305 zap.L().Debug("Print Policy DB: equal id table") 306 307 for key, values := range m.equalIDMapTable { 308 for _, policies := range values { 309 zap.L().Debug("Print Policy DB", 310 zap.String("policies", fmt.Sprintf("%#v", policies)), 311 zap.String("key", key), 312 ) 313 } 314 } 315 316 zap.L().Debug("Print Policy DB - not equal table") 317 318 for key, values := range m.notEqualMapTable { 319 for value, policies := range values { 320 zap.L().Debug("Print Policy DB", 321 zap.String("policies", fmt.Sprintf("%#v", policies)), 322 zap.String("key", key), 323 zap.String("value", value), 324 ) 325 } 326 } 327 328 } 329 330 func parseTagValueRange(value string) (string, *portspec.PortSpec, error) { 331 index := strings.Index(value, "/") 332 if index == -1 { 333 // means there was no range 334 return value, nil, nil 335 } 336 rangeSpec, err := portspec.NewPortSpecFromString(value[index+1:], nil) 337 if err != nil { 338 return "", nil, err 339 } 340 return value[:index], rangeSpec, nil 341 }