github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/acls/acl.go (about)

     1  package acls
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"reflect"
     8  	"strings"
     9  
    10  	"go.aporeto.io/enforcerd/trireme-lib/controller/constants"
    11  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packet"
    12  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    13  	"go.aporeto.io/enforcerd/trireme-lib/utils/ipprefix"
    14  	"go.aporeto.io/gaia/protocols"
    15  )
    16  
    17  // acl holds all the ACLS in an internal DB
    18  
    19  type acl struct {
    20  	tcpCache  ipprefix.IPcache
    21  	udpCache  ipprefix.IPcache
    22  	icmpCache ipprefix.IPcache
    23  }
    24  
    25  func newACL() *acl {
    26  	return &acl{
    27  		tcpCache:  ipprefix.NewIPCache(),
    28  		udpCache:  ipprefix.NewIPCache(),
    29  		icmpCache: ipprefix.NewIPCache(),
    30  	}
    31  }
    32  
    33  // errNoMatchFromRule must stop the LPM check
    34  var errNoMatchFromRule = errors.New("No Match")
    35  var errNotFound = errors.New("No Match")
    36  
    37  func (a *acl) addICMPToCache(ip net.IP, mask int, baseRule string, listOfDisjunctives []string, policy *policy.FlowPolicy) {
    38  	var icmpRuleList []*icmpRule
    39  
    40  	val, exists := a.icmpCache.Get(ip, mask)
    41  	if !exists {
    42  		icmpRuleList = []*icmpRule{}
    43  	} else {
    44  		icmpRuleList = val.([]*icmpRule)
    45  	}
    46  
    47  	newRule := &icmpRule{baseRule, listOfDisjunctives, policy}
    48  	icmpRuleList = append(icmpRuleList, newRule)
    49  	a.icmpCache.Put(ip, mask, icmpRuleList)
    50  }
    51  
    52  func (a *acl) removeICMPFromCache(ip net.IP, mask int, baseRule string, listOfDisjunctives []string, policy *policy.FlowPolicy) error {
    53  	var icmpRuleList []*icmpRule
    54  	val, exists := a.icmpCache.Get(ip, mask)
    55  	if !exists {
    56  		// nothing to remove
    57  		return nil
    58  	}
    59  	icmpRuleList = val.([]*icmpRule)
    60  
    61  	searchRule := icmpRule{baseRule, listOfDisjunctives, policy}
    62  	newIcmpRuleList := make([]*icmpRule, 0, len(icmpRuleList))
    63  	for _, rule := range icmpRuleList {
    64  		if reflect.DeepEqual(searchRule, *rule) {
    65  			// this is a full match, skip
    66  			continue
    67  		}
    68  		// TODO: partial matches aren't handled. Should they?
    69  		newIcmpRuleList = append(newIcmpRuleList, rule)
    70  	}
    71  
    72  	a.icmpCache.Put(ip, mask, newIcmpRuleList)
    73  
    74  	return nil
    75  }
    76  
    77  func (a *acl) removeFromCache(ip net.IP, mask int, nomatch bool, proto string, ports []string, policy *policy.FlowPolicy) error {
    78  
    79  	removeICMPCache := func(baseRule string, listOfDisjunctives []string) error {
    80  		return a.removeICMPFromCache(ip, mask, baseRule, listOfDisjunctives, policy)
    81  	}
    82  
    83  	// the TCP or UDP cases use this part
    84  	removeCache := func(lookupCache ipprefix.IPcache, port string) error {
    85  		val, exists := lookupCache.Get(ip, mask)
    86  		if !exists {
    87  			// nothing to remove
    88  			return nil
    89  		}
    90  		portList := val.(portActionList)
    91  
    92  		newPortList := make(portActionList, 0, len(portList))
    93  		r, err := newPortAction(port, policy, nomatch)
    94  		if err != nil {
    95  			return fmt.Errorf("unable to create port action: %s", err)
    96  		}
    97  
    98  		for _, portAction := range portList {
    99  			if reflect.DeepEqual(*r, *portAction) {
   100  				// this is a full match, skip
   101  				continue
   102  			}
   103  			// TODO: partial matches aren't handled. Should they?
   104  			newPortList = append(newPortList, portAction)
   105  		}
   106  
   107  		lookupCache.Put(ip, mask, newPortList)
   108  
   109  		return nil
   110  	}
   111  
   112  	switch strings.ToLower(proto) {
   113  	case constants.TCPProtoNum:
   114  		for _, port := range ports {
   115  			if err := removeCache(a.tcpCache, port); err != nil {
   116  				return err
   117  			}
   118  		}
   119  		return nil
   120  
   121  	case constants.UDPProtoNum:
   122  		for _, port := range ports {
   123  			if err := removeCache(a.udpCache, port); err != nil {
   124  				return err
   125  			}
   126  		}
   127  		return nil
   128  
   129  	default:
   130  		// ICMP protocol
   131  		if splits := strings.Split(proto, "/"); strings.ToUpper(splits[0]) == protocols.L4ProtocolICMP || strings.ToUpper(splits[0]) == protocols.L4ProtocolICMP6 {
   132  			return removeICMPCache(proto, ports)
   133  		}
   134  
   135  		// unknown protocol - nothing to do
   136  		return nil
   137  	}
   138  }
   139  
   140  func (a *acl) addToCache(ip net.IP, mask int, port string, proto string, policy *policy.FlowPolicy, nomatch bool) error {
   141  	var err error
   142  	var portList portActionList
   143  	var lookupCache ipprefix.IPcache
   144  	switch strings.ToLower(proto) {
   145  	case constants.TCPProtoNum:
   146  		{
   147  			lookupCache = a.tcpCache
   148  		}
   149  	case constants.UDPProtoNum:
   150  		{
   151  			lookupCache = a.udpCache
   152  		}
   153  	default:
   154  		return nil
   155  	}
   156  	r, err := newPortAction(port, policy, nomatch)
   157  	if err != nil {
   158  		return fmt.Errorf("unable to create port action: %s", err)
   159  	}
   160  	val, exists := lookupCache.Get(ip, mask)
   161  	if !exists {
   162  		portList = portActionList{}
   163  	} else {
   164  		portList = val.(portActionList)
   165  	}
   166  
   167  	/* check if this is duplicate entry */
   168  	for _, portAction := range portList {
   169  		if *r == *portAction {
   170  			return nil
   171  		}
   172  	}
   173  
   174  	portList = append(portList, r)
   175  	lookupCache.Put(ip, mask, portList)
   176  
   177  	return nil
   178  }
   179  
   180  func (a *acl) removeIPMask(ip net.IP, mask int) {
   181  	a.tcpCache.Put(ip, mask, nil)
   182  	a.udpCache.Put(ip, mask, nil)
   183  	a.icmpCache.Put(ip, mask, nil)
   184  }
   185  
   186  func (a *acl) matchRule(ip net.IP, port uint16, proto uint8, preReport *policy.FlowPolicy) (report *policy.FlowPolicy, packetPolicy *policy.FlowPolicy, err error) {
   187  	report = preReport
   188  
   189  	err = errNotFound
   190  
   191  	lookup := func(val interface{}) bool {
   192  		if val != nil {
   193  			portList := val.(portActionList)
   194  
   195  			report, packetPolicy, err = portList.lookup(port, report)
   196  			if err == nil || err == errNoMatchFromRule {
   197  				return true
   198  			}
   199  		}
   200  		return false
   201  	}
   202  	if proto == packet.IPProtocolTCP {
   203  		a.tcpCache.RunFuncOnLpmIP(ip, lookup)
   204  	} else if proto == packet.IPProtocolUDP {
   205  		a.udpCache.RunFuncOnLpmIP(ip, lookup)
   206  	}
   207  
   208  	return report, packetPolicy, err
   209  }
   210  
   211  func (a *acl) addRule(rule policy.IPRule) (err error) {
   212  
   213  	addCache := func(address, port, proto string) error {
   214  		addr, err := ParseAddress(address)
   215  		if err != nil {
   216  			return err
   217  		}
   218  
   219  		if err := a.addToCache(addr.IP, addr.Mask, port, proto, rule.Policy, addr.NoMatch); err != nil {
   220  			return err
   221  		}
   222  
   223  		return nil
   224  	}
   225  
   226  	addICMPCache := func(address, baseRule string, listOfDisjunctives []string) error {
   227  		addr, err := ParseAddress(address)
   228  		if err != nil {
   229  			return err
   230  		}
   231  
   232  		a.addICMPToCache(addr.IP, addr.Mask, baseRule, listOfDisjunctives, rule.Policy)
   233  
   234  		return nil
   235  	}
   236  
   237  	for _, proto := range rule.Protocols {
   238  		switch strings.ToLower(proto) {
   239  		case constants.TCPProtoNum, constants.UDPProtoNum:
   240  			for _, address := range rule.Addresses {
   241  				for _, port := range rule.Ports {
   242  					if err := addCache(address, port, proto); err != nil {
   243  						return err
   244  					}
   245  				}
   246  			}
   247  		}
   248  		if splits := strings.Split(proto, "/"); strings.ToUpper(splits[0]) == protocols.L4ProtocolICMP || strings.ToUpper(splits[0]) == protocols.L4ProtocolICMP6 {
   249  			for _, address := range rule.Addresses {
   250  				if err := addICMPCache(address, proto, rule.Ports); err != nil {
   251  					return err
   252  				}
   253  			}
   254  		}
   255  	}
   256  
   257  	return nil
   258  }
   259  
   260  // getMatchingAction does lookup in acl in a common way for accept/reject rules.
   261  func (a *acl) getMatchingAction(ip net.IP, port uint16, proto uint8, preReport *policy.FlowPolicy) (report *policy.FlowPolicy, packet *policy.FlowPolicy, err error) {
   262  
   263  	return a.matchRule(ip, port, proto, preReport)
   264  }