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  }