go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/aclplugin/descriptor/acl.go (about) 1 // Copyright (c) 2018 Cisco and/or its affiliates. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at: 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package descriptor 16 17 import ( 18 "bytes" 19 "net" 20 21 "github.com/pkg/errors" 22 "go.ligato.io/cn-infra/v2/idxmap" 23 "go.ligato.io/cn-infra/v2/logging" 24 "google.golang.org/protobuf/proto" 25 "google.golang.org/protobuf/types/known/emptypb" 26 27 "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 28 "go.ligato.io/vpp-agent/v3/plugins/vpp/aclplugin/aclidx" 29 "go.ligato.io/vpp-agent/v3/plugins/vpp/aclplugin/descriptor/adapter" 30 "go.ligato.io/vpp-agent/v3/plugins/vpp/aclplugin/vppcalls" 31 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin" 32 ifdescriptor "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor" 33 acl "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/acl" 34 ) 35 36 const ( 37 // ACLDescriptorName is descriptor name 38 ACLDescriptorName = "vpp-acl" 39 ) 40 41 // ACLDescriptor is descriptor for ACL 42 type ACLDescriptor struct { 43 // dependencies 44 log logging.Logger 45 aclHandler vppcalls.ACLVppAPI 46 47 // runtime 48 ifPlugin ifplugin.API 49 } 50 51 // NewACLDescriptor is constructor for ACL descriptor 52 func NewACLDescriptor(aclHandler vppcalls.ACLVppAPI, ifPlugin ifplugin.API, 53 logger logging.PluginLogger) *ACLDescriptor { 54 return &ACLDescriptor{ 55 log: logger.NewLogger("acl-descriptor"), 56 ifPlugin: ifPlugin, 57 aclHandler: aclHandler, 58 } 59 } 60 61 // GetDescriptor returns descriptor suitable for registration (via adapter) with 62 // the KVScheduler. 63 func (d *ACLDescriptor) GetDescriptor() *adapter.ACLDescriptor { 64 return &adapter.ACLDescriptor{ 65 Name: ACLDescriptorName, 66 NBKeyPrefix: acl.ModelACL.KeyPrefix(), 67 ValueTypeName: acl.ModelACL.ProtoName(), 68 KeySelector: acl.ModelACL.IsKeyValid, 69 KeyLabel: acl.ModelACL.StripKeyPrefix, 70 ValueComparator: d.EquivalentACLs, 71 WithMetadata: true, 72 MetadataMapFactory: func() idxmap.NamedMappingRW { 73 return aclidx.NewACLIndex(d.log, "vpp-acl-index") 74 }, 75 Create: d.Create, 76 Delete: d.Delete, 77 Update: d.Update, 78 UpdateWithRecreate: d.UpdateWithRecreate, 79 Retrieve: d.Retrieve, 80 DerivedValues: d.DerivedValues, 81 RetrieveDependencies: []string{ifdescriptor.InterfaceDescriptorName}, 82 } 83 } 84 85 // EquivalentACLs compares two ACLs 86 func (d *ACLDescriptor) EquivalentACLs(key string, oldACL, newACL *acl.ACL) bool { 87 // check if ACL name changed 88 if oldACL.Name != newACL.Name { 89 return false 90 } 91 92 // check if rules changed (order matters) 93 if len(oldACL.Rules) != len(newACL.Rules) { 94 return false 95 } 96 for i := 0; i < len(oldACL.Rules); i++ { 97 if !d.equivalentACLRules(oldACL.Rules[i], newACL.Rules[i]) { 98 return false 99 } 100 } 101 102 return true 103 } 104 105 // validateRules provided in ACL. Every rule has to contain actions and matches. 106 // Current limitation: L2 and L3/4 have to be split to different ACLs and 107 // there cannot be L2 rules and L3/4 rules in the same ACL. 108 func (d *ACLDescriptor) validateRules(aclName string, rules []*acl.ACL_Rule) ([]*acl.ACL_Rule, bool) { 109 var validL3L4Rules []*acl.ACL_Rule 110 var validL2Rules []*acl.ACL_Rule 111 112 for _, rule := range rules { 113 if rule.GetIpRule() != nil { 114 validL3L4Rules = append(validL3L4Rules, rule) 115 } 116 if rule.GetMacipRule() != nil { 117 validL2Rules = append(validL2Rules, rule) 118 } 119 } 120 if len(validL3L4Rules) > 0 && len(validL2Rules) > 0 { 121 d.log.Warnf("ACL %s contains L2 rules and L3/L4 rules as well. This case is not supported, only L3/L4 rules will be resolved", 122 aclName) 123 return validL3L4Rules, false 124 } else if len(validL3L4Rules) > 0 { 125 return validL3L4Rules, false 126 } else { 127 return validL2Rules, true 128 } 129 } 130 131 // Create configures ACL 132 func (d *ACLDescriptor) Create(key string, acl *acl.ACL) (metadata *aclidx.ACLMetadata, err error) { 133 if len(acl.Rules) == 0 { 134 return nil, errors.Errorf("failed to configure ACL %s, no rules to set", acl.Name) 135 } 136 137 rules, isL2MacIP := d.validateRules(acl.Name, acl.Rules) 138 139 // Configure ACL rules. 140 var vppACLIndex uint32 141 if isL2MacIP { 142 vppACLIndex, err = d.aclHandler.AddMACIPACL(rules, acl.Name) 143 if err != nil { 144 return nil, errors.Errorf("failed to add MACIP ACL %s: %v", acl.Name, err) 145 } 146 } else { 147 vppACLIndex, err = d.aclHandler.AddACL(rules, acl.Name) 148 if err != nil { 149 return nil, errors.Errorf("failed to add IP ACL %s: %v", acl.Name, err) 150 } 151 } 152 153 metadata = &aclidx.ACLMetadata{ 154 Index: vppACLIndex, 155 L2: isL2MacIP, 156 } 157 return metadata, nil 158 } 159 160 // Delete deletes ACL 161 func (d *ACLDescriptor) Delete(key string, acl *acl.ACL, metadata *aclidx.ACLMetadata) error { 162 if metadata.L2 { 163 // Remove ACL L2. 164 err := d.aclHandler.DeleteMACIPACL(metadata.Index) 165 if err != nil { 166 return errors.Errorf("failed to delete MACIP ACL %s: %v", acl.Name, err) 167 } 168 } else { 169 // Remove ACL L3/L4. 170 err := d.aclHandler.DeleteACL(metadata.Index) 171 if err != nil { 172 return errors.Errorf("failed to delete IP ACL %s: %v", acl.Name, err) 173 } 174 } 175 return nil 176 } 177 178 // Update modifies ACL 179 func (d *ACLDescriptor) Update(key string, oldACL, newACL *acl.ACL, oldMetadata *aclidx.ACLMetadata) (newMetadata *aclidx.ACLMetadata, err error) { 180 // Validate rules. 181 rules, isL2MacIP := d.validateRules(newACL.Name, newACL.Rules) 182 183 if isL2MacIP { 184 // L2 ACL 185 err := d.aclHandler.ModifyMACIPACL(oldMetadata.Index, rules, newACL.Name) 186 if err != nil { 187 return nil, errors.Errorf("failed to modify MACIP ACL %s: %v", newACL.Name, err) 188 } 189 } else { 190 // L3/L4 ACL can be modified directly. 191 err := d.aclHandler.ModifyACL(oldMetadata.Index, rules, newACL.Name) 192 if err != nil { 193 return nil, errors.Errorf("failed to modify IP ACL %s: %v", newACL.Name, err) 194 } 195 } 196 197 newMetadata = &aclidx.ACLMetadata{ 198 Index: oldMetadata.Index, 199 L2: isL2MacIP, 200 } 201 return newMetadata, nil 202 } 203 204 // UpdateWithRecreate checks if modification requires recreation 205 func (d *ACLDescriptor) UpdateWithRecreate(key string, oldACL, newACL *acl.ACL, metadata *aclidx.ACLMetadata) bool { 206 var hasL2 bool 207 for _, rule := range oldACL.Rules { 208 if rule.GetMacipRule() != nil { 209 hasL2 = true 210 } else if rule.GetIpRule() != nil && hasL2 { 211 return true 212 } 213 } 214 return false 215 } 216 217 // DerivedValues returns list of derived values for ACL. 218 func (d *ACLDescriptor) DerivedValues(key string, value *acl.ACL) (derived []api.KeyValuePair) { 219 for _, ifName := range value.GetInterfaces().GetIngress() { 220 derived = append(derived, api.KeyValuePair{ 221 Key: acl.ToInterfaceKey(value.Name, ifName, acl.IngressFlow), 222 Value: &emptypb.Empty{}, 223 }) 224 } 225 for _, ifName := range value.GetInterfaces().GetEgress() { 226 derived = append(derived, api.KeyValuePair{ 227 Key: acl.ToInterfaceKey(value.Name, ifName, acl.EgressFlow), 228 Value: &emptypb.Empty{}, 229 }) 230 } 231 return derived 232 } 233 234 // Retrieve returns list of configured ACLs with metadata. 235 func (d *ACLDescriptor) Retrieve(correlate []adapter.ACLKVWithMetadata) ( 236 acls []adapter.ACLKVWithMetadata, err error, 237 ) { 238 // Retrieve VPP configuration. 239 ipACLs, err := d.aclHandler.DumpACL() 240 if err != nil { 241 return nil, errors.Errorf("failed to dump IP ACLs: %v", err) 242 } 243 macipACLs, err := d.aclHandler.DumpMACIPACL() 244 if err != nil { 245 return nil, errors.Errorf("failed to dump MAC IP ACLs: %v", err) 246 } 247 248 for _, ipACL := range ipACLs { 249 acls = append(acls, adapter.ACLKVWithMetadata{ 250 Key: acl.Key(ipACL.ACL.Name), 251 Value: ipACL.ACL, 252 Metadata: &aclidx.ACLMetadata{ 253 Index: ipACL.Meta.Index, 254 }, 255 Origin: api.FromNB, 256 }) 257 } 258 for _, macipACL := range macipACLs { 259 acls = append(acls, adapter.ACLKVWithMetadata{ 260 Key: acl.Key(macipACL.ACL.Name), 261 Value: macipACL.ACL, 262 Metadata: &aclidx.ACLMetadata{ 263 Index: macipACL.Meta.Index, 264 L2: true, 265 }, 266 Origin: api.FromNB, 267 }) 268 } 269 270 return 271 } 272 273 // equivalentACLRules compares two ACL rules, handling the cases of unspecified 274 // source/destination networks. 275 func (d *ACLDescriptor) equivalentACLRules(rule1, rule2 *acl.ACL_Rule) bool { 276 // Action 277 if rule1.Action != rule2.Action { 278 return false 279 } 280 281 // MAC IP Rule 282 if !proto.Equal(rule1.MacipRule, rule2.MacipRule) { 283 return false 284 } 285 286 // IP Rule 287 ipRule1 := rule1.GetIpRule() 288 ipRule2 := rule2.GetIpRule() 289 if !proto.Equal(ipRule1.GetIcmp(), ipRule2.GetIcmp()) || 290 !proto.Equal(ipRule1.GetTcp(), ipRule2.GetTcp()) || 291 !proto.Equal(ipRule1.GetUdp(), ipRule2.GetUdp()) { 292 return false 293 } 294 if !d.equivalentIPRuleNetworks(ipRule1.GetIp().GetSourceNetwork(), ipRule2.GetIp().GetSourceNetwork()) { 295 return false 296 } 297 if !d.equivalentIPRuleNetworks(ipRule1.GetIp().GetDestinationNetwork(), ipRule2.GetIp().GetDestinationNetwork()) { 298 return false 299 } 300 301 // handle undefined (AUTO) IP protocol 302 proto1 := ipRule1.GetIp().GetProtocol() 303 proto2 := ipRule2.GetIp().GetProtocol() 304 if proto1 != 0 && proto2 != 0 && proto1 != proto2 { 305 return false 306 } 307 return true 308 } 309 310 // equivalentIPRuleNetworks compares two IP networks, taking into account the fact 311 // that empty string is equivalent to address with all zeroes. 312 func (d *ACLDescriptor) equivalentIPRuleNetworks(net1, net2 string) bool { 313 var ( 314 ip1, ip2 net.IP 315 ipNet1, ipNet2 *net.IPNet 316 err1, err2 error 317 ) 318 if net1 != "" { 319 ip1, ipNet1, err1 = net.ParseCIDR(net1) 320 } 321 if net2 != "" { 322 ip2, ipNet2, err2 = net.ParseCIDR(net2) 323 } 324 if err1 != nil || err2 != nil { 325 return net1 == net2 326 } 327 if ipNet1 == nil { 328 return ipNet2 == nil || ip2.IsUnspecified() 329 } 330 if ipNet2 == nil { 331 return ip1.IsUnspecified() 332 } 333 return ip1.Equal(ip2) && bytes.Equal(ipNet1.Mask, ipNet2.Mask) 334 }