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 }