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  }