go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/srplugin/descriptor/policy.go (about)

     1  // Copyright (c) 2019 Bell Canada, Pantheon Technologies 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  	"github.com/pkg/errors"
    19  	"google.golang.org/protobuf/encoding/prototext"
    20  
    21  	"go.ligato.io/cn-infra/v2/logging"
    22  
    23  	scheduler "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    24  	"go.ligato.io/vpp-agent/v3/plugins/vpp/srplugin/descriptor/adapter"
    25  	"go.ligato.io/vpp-agent/v3/plugins/vpp/srplugin/vppcalls"
    26  	vpp_l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3"
    27  	srv6 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/srv6"
    28  )
    29  
    30  const (
    31  	// PolicyDescriptorName is the name of the descriptor for VPP SRv6 policies
    32  	PolicyDescriptorName = "vpp-sr-policy"
    33  
    34  	// dependency labels
    35  	policyVRFDep = "sr-policy-vrf-table-exists"
    36  )
    37  
    38  // PolicyDescriptor teaches KVScheduler how to configure VPP SRv6 policies.
    39  //
    40  // Implementation note: according to https://tools.ietf.org/html/draft-ietf-spring-segment-routing-policy-02#section-2.6,
    41  // (weight,segments) tuple is not unique identification of candidate path(segment list) in policy (VPP also allows to
    42  // have multiple segment lists that have the same (weight,segments) but differs in index (in ietf material they call it
    43  // "Discriminator of a Candidate Path")). However, in validation and equivalency stage of vpp-agent there is no way how
    44  // to get from VPP the index (validation precedes creation call and VPP doesn't have it). As result, restriction must be
    45  // made, (weight,segments) is unique within given Policy. The drawback is that you can't create multiple segment lists
    46  // with the same (weight,segments), but that is not issue if you don't want to explicitly rely on special attributes
    47  // used by tie-breaking rules from https://tools.ietf.org/html/draft-ietf-spring-segment-routing-policy-02#section-2.9
    48  // (i.e. VPP-internal index). These special attributes can't be explicitly set in VPP, so can't rely on them anyway. So
    49  // we should not miss any client use case for current VPP implementation (by removing duplicated segment list we don't
    50  // break any use case).
    51  type PolicyDescriptor struct {
    52  	// dependencies
    53  	log       logging.Logger
    54  	srHandler vppcalls.SRv6VppAPI
    55  }
    56  
    57  // PolicyMetadata are Policy-related metadata that KVscheduler bundles with Policy data. They are served by KVScheduler in Create/Update descriptor methods.
    58  type PolicyMetadata struct {
    59  	segmentListIndexes map[string]uint32 // map[marshalled segment list]index in policy
    60  }
    61  
    62  // NewPolicyDescriptor creates a new instance of the Srv6 policy descriptor.
    63  func NewPolicyDescriptor(srHandler vppcalls.SRv6VppAPI, log logging.PluginLogger) *scheduler.KVDescriptor {
    64  	ctx := &PolicyDescriptor{
    65  		log:       log.NewLogger("policy-descriptor"),
    66  		srHandler: srHandler,
    67  	}
    68  
    69  	typedDescr := &adapter.PolicyDescriptor{
    70  		Name:               PolicyDescriptorName,
    71  		NBKeyPrefix:        srv6.ModelPolicy.KeyPrefix(),
    72  		ValueTypeName:      srv6.ModelPolicy.ProtoName(),
    73  		KeySelector:        srv6.ModelPolicy.IsKeyValid,
    74  		KeyLabel:           srv6.ModelPolicy.StripKeyPrefix,
    75  		ValueComparator:    ctx.EquivalentPolicies,
    76  		Validate:           ctx.Validate,
    77  		Create:             ctx.Create,
    78  		Delete:             ctx.Delete,
    79  		Update:             ctx.Update,
    80  		UpdateWithRecreate: ctx.UpdateWithRecreate,
    81  		Dependencies:       ctx.Dependencies,
    82  		WithMetadata:       true,
    83  	}
    84  	return adapter.NewPolicyDescriptor(typedDescr)
    85  }
    86  
    87  // Validate validates VPP policies.
    88  func (d *PolicyDescriptor) Validate(key string, policy *srv6.Policy) error {
    89  	_, err := ParseIPv6(policy.GetBsid())
    90  	if err != nil {
    91  		return scheduler.NewInvalidValueError(errors.Errorf("failed to parse binding sid %s, should be a valid ipv6 address: %v", policy.GetBsid(), err), "bsid")
    92  	}
    93  	if len(policy.SegmentLists) == 0 { // includes nil check
    94  		return scheduler.NewInvalidValueError(errors.New("there must be defined at least one segment list"), "SegmentLists")
    95  	}
    96  
    97  	for i, sl := range policy.SegmentLists {
    98  		for _, segment := range sl.Segments {
    99  			_, err := ParseIPv6(segment)
   100  			if err != nil {
   101  				return scheduler.NewInvalidValueError(errors.Errorf("failed to parse segment %s in segments %v, should be a valid ipv6 address: %v", segment, sl.Segments, err), "SegmentLists.segments")
   102  			}
   103  		}
   104  
   105  		// checking for segment list duplicity (existence of this check is used in equivalency check too), see PolicyDescriptor's godoc implementation note for more info
   106  		for j, previousSL := range policy.SegmentLists[:i] {
   107  			if d.equivalentSegmentList(previousSL, sl) {
   108  				return scheduler.NewInvalidValueError(errors.Errorf("found duplicated segment list: %+v (list index %v) and %+v (list index %v) ", sl, i, previousSL, j), "SegmentLists")
   109  			}
   110  		}
   111  	}
   112  	return nil
   113  }
   114  
   115  // Create creates new Policy into VPP using VPP's binary api
   116  func (d *PolicyDescriptor) Create(key string, policy *srv6.Policy) (metadata interface{}, err error) {
   117  	// add policy (including segment lists)
   118  	err = d.srHandler.AddPolicy(policy) // there exist first segment (validation checked it)
   119  	if err != nil {
   120  		return nil, errors.Errorf("failed to write policy %s with first segment %s: %v", policy.GetBsid(), policy.SegmentLists[0], err)
   121  	}
   122  
   123  	// retrieve from VPP indexes of just added Policy/Segment Lists and store it as metadata
   124  	slIndexes, err := d.policyIndexInfo(policy)
   125  	if err != nil {
   126  		return nil, errors.Wrapf(err, "can't retrieve indexes of created srv6 policy with bsid %v", policy.GetBsid())
   127  	}
   128  	metadata = &PolicyMetadata{
   129  		segmentListIndexes: slIndexes,
   130  	}
   131  	return metadata, nil
   132  }
   133  
   134  // Delete removes Policy from VPP using VPP's binary api
   135  func (d *PolicyDescriptor) Delete(key string, policy *srv6.Policy, metadata interface{}) error {
   136  	bsid, _ := ParseIPv6(policy.GetBsid())                 // already validated
   137  	if err := d.srHandler.DeletePolicy(bsid); err != nil { // expecting that delete of SB defined policy will also delete policy segments in vpp
   138  		return errors.Errorf("failed to delete policy %s: %v", bsid.String(), err)
   139  	}
   140  	return nil
   141  }
   142  
   143  // Update updates Policy in VPP using VPP's binary api. Only changes of segment list handled here. Other updates are
   144  // handle by recreation (see function UpdateWithRecreate)
   145  func (d *PolicyDescriptor) Update(key string, oldPolicy, newPolicy *srv6.Policy, oldMetadata interface{}) (newMetadata interface{}, err error) {
   146  	// compute segment lists for delete (removePool) and segment lists to add (addPool)
   147  	removePool := make([]*srv6.Policy_SegmentList, len(oldPolicy.SegmentLists))
   148  	addPool := make([]*srv6.Policy_SegmentList, 0, len(newPolicy.SegmentLists))
   149  	copy(removePool, oldPolicy.SegmentLists)
   150  	for _, newSL := range newPolicy.SegmentLists {
   151  		found := false
   152  		for i, oldSL := range removePool {
   153  			if d.equivalentSegmentList(oldSL, newSL) {
   154  				removePool = append(removePool[:i], removePool[i+1:]...)
   155  				found = true
   156  				break
   157  			}
   158  		}
   159  		if !found {
   160  			addPool = append(addPool, newSL)
   161  		}
   162  	}
   163  
   164  	// add new segment lists not present in oldPolicy
   165  	for _, sl := range addPool {
   166  		if err := d.srHandler.AddPolicySegmentList(sl, newPolicy); err != nil {
   167  			return nil, errors.Errorf("failed update policy: failed to add policy segment %s: %v", newPolicy.GetBsid(), err)
   168  		}
   169  	}
   170  
   171  	// remove old segment lists not present in newPolicy
   172  	slIndexes := oldMetadata.(*PolicyMetadata).segmentListIndexes
   173  	for _, sl := range removePool {
   174  		index, exists := slIndexes[prototext.MarshalOptions{}.Format(sl)]
   175  		if !exists {
   176  			return nil, errors.Errorf("failed update policy: failed to find index for segment list "+
   177  				"%+v in policy with bsid %v (metadata segment list indexes: %+v)", sl, oldPolicy.GetBsid(), slIndexes)
   178  		}
   179  		if err := d.srHandler.DeletePolicySegmentList(sl, index, newPolicy); err != nil {
   180  			return nil, errors.Errorf("failed update policy: failed to delete policy segment %s: %v", oldPolicy.GetBsid(), err)
   181  		}
   182  	}
   183  
   184  	// update metadata be recreation it from scratch
   185  	slIndexes, err = d.policyIndexInfo(newPolicy)
   186  	if err != nil {
   187  		return nil, errors.Wrapf(err, "can't retrieve indexes of updated srv6 policy with bsid %v", newPolicy.GetBsid())
   188  	}
   189  	newMetadata = &PolicyMetadata{
   190  		segmentListIndexes: slIndexes,
   191  	}
   192  
   193  	return newMetadata, nil
   194  }
   195  
   196  // policyIndexInfo retrieves indexes for segment lists of given policy
   197  func (d *PolicyDescriptor) policyIndexInfo(policy *srv6.Policy) (map[string]uint32, error) {
   198  	_, indexes, err := d.srHandler.RetrievePolicyIndexInfo(policy)
   199  	if err != nil {
   200  		return nil, errors.Wrapf(err, "can't retrieve indexes of srv6 policy with bsid %v from vpp", policy.GetBsid())
   201  	}
   202  	result := make(map[string]uint32)
   203  	for slist, index := range indexes {
   204  		result[prototext.MarshalOptions{}.Format(slist)] = index
   205  	}
   206  	return result, nil
   207  }
   208  
   209  // UpdateWithRecreate define whether update case should be handled by complete policy recreation
   210  func (d *PolicyDescriptor) UpdateWithRecreate(key string, oldPolicy, newPolicy *srv6.Policy, oldMetadata interface{}) bool {
   211  	return !d.equivalentPolicyAttributes(oldPolicy, newPolicy) // update with recreate only when policy attributes changes because segment list change can be handled more efficiently
   212  }
   213  
   214  func (d *PolicyDescriptor) Dependencies(key string, policy *srv6.Policy) (dependencies []scheduler.Dependency) {
   215  	if policy.InstallationVrfId != 0 {
   216  		dependencies = append(dependencies, scheduler.Dependency{
   217  			Label: policyVRFDep,
   218  			Key:   vpp_l3.VrfTableKey(policy.InstallationVrfId, vpp_l3.VrfTable_IPV6),
   219  		})
   220  	}
   221  	return dependencies
   222  }
   223  
   224  // EquivalentPolicies determines whether 2 policies are logically equal. This comparison takes into consideration also
   225  // semantics that couldn't be modeled into proto models (i.e. SID is IPv6 address and not only string)
   226  func (d *PolicyDescriptor) EquivalentPolicies(key string, oldPolicy, newPolicy *srv6.Policy) bool {
   227  	return d.equivalentPolicyAttributes(oldPolicy, newPolicy) &&
   228  		d.equivalentSegmentLists(oldPolicy.SegmentLists, newPolicy.SegmentLists)
   229  }
   230  
   231  func (d *PolicyDescriptor) equivalentPolicyAttributes(oldPolicy, newPolicy *srv6.Policy) bool {
   232  	return oldPolicy.InstallationVrfId == newPolicy.InstallationVrfId &&
   233  		equivalentSIDs(oldPolicy.Bsid, newPolicy.Bsid) &&
   234  		oldPolicy.SprayBehaviour == newPolicy.SprayBehaviour &&
   235  		oldPolicy.SrhEncapsulation == newPolicy.SrhEncapsulation
   236  }
   237  
   238  func (d *PolicyDescriptor) equivalentSegmentLists(oldSLs, newSLs []*srv6.Policy_SegmentList) bool {
   239  	if oldSLs == nil || newSLs == nil {
   240  		return oldSLs == nil && newSLs == nil
   241  	}
   242  	if len(oldSLs) != len(newSLs) {
   243  		return false
   244  	}
   245  
   246  	// checking segment lists equality (segment lists with just reordered segment list items are considered equal)
   247  	// we know that:
   248  	// 1. lists have the same length (due to previous checks),
   249  	// 2. there are no segment list equivalence duplicates (due to validation check)
   250  	// => hence if we check that each segment list from newSLs has its equivalent in oldSLs then we got equivalent bijection
   251  	// and that means that they are equal (items maybe reordered but equal). If we find one segment list from newSLs that
   252  	// is not in oldSLs, the SLs are obviously not equal.
   253  	for _, newSL := range newSLs {
   254  		found := false
   255  		for _, oldSL := range oldSLs {
   256  			if d.equivalentSegmentList(oldSL, newSL) {
   257  				found = true
   258  				break
   259  			}
   260  		}
   261  		if !found {
   262  			return false
   263  		}
   264  	}
   265  	return true
   266  }
   267  
   268  func (d *PolicyDescriptor) equivalentSegmentList(oldSL, newSL *srv6.Policy_SegmentList) bool {
   269  	return oldSL.Weight == newSL.Weight && d.equivalentSegments(oldSL.Segments, newSL.Segments)
   270  }
   271  
   272  func (d *PolicyDescriptor) equivalentSegments(segments1, segments2 []string) bool {
   273  	if segments1 == nil || segments2 == nil {
   274  		return segments1 == nil && segments2 == nil
   275  	}
   276  	if len(segments1) != len(segments2) {
   277  		return false
   278  	}
   279  	for i := range segments1 {
   280  		if !equivalentSIDs(segments1[i], segments2[i]) {
   281  			return false
   282  		}
   283  	}
   284  	return true
   285  }