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

     1  //  Copyright (c) 2020 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  	"math"
    19  	"net"
    20  
    21  	"github.com/pkg/errors"
    22  	"google.golang.org/protobuf/proto"
    23  
    24  	"go.ligato.io/cn-infra/v2/logging"
    25  
    26  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    27  	ifdescriptor "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor"
    28  	"go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/descriptor/adapter"
    29  	"go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/vppcalls"
    30  	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    31  	l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3"
    32  )
    33  
    34  const (
    35  	// VrrpDescriptorName is the name of the descriptor.
    36  	VrrpDescriptorName = "vrrp"
    37  
    38  	// Dependency labels.
    39  	vrrpEntryInterfaceDep    = "interface-exists"
    40  	vrrpDescriptorLoggerName = "vrrp-descriptor"
    41  
    42  	// The minimum value in milliseconds that can be used as interval.
    43  	centisecondInMilliseconds = 10
    44  )
    45  
    46  // A list of validation errors.
    47  var (
    48  	ErrMissingInterface = errors.New("missing interface")
    49  	ErrInvalidAddrNum   = errors.New("addrs quantity should be > 0 && <= 255")
    50  	ErrInvalidVrID      = errors.New("vr_id should be > 0 && <= 255")
    51  	ErrInvalidPriority  = errors.New("priority should be > 0 && <= 255")
    52  	ErrInvalidInterval  = errors.New("interval should be > 0 && <= 65535")
    53  	ErrInvalidVrrpIP    = errors.New("invalid IP address")
    54  	ErrInvalidIPVer     = errors.New("ipv6_flag does not correspond to IP version of the provided address")
    55  	ErrInvalidInterface = errors.New("interface does not exist")
    56  )
    57  
    58  // VrrpDescriptor teaches KVScheduler how to configure VPP VRRPs.
    59  type VrrpDescriptor struct {
    60  	log         logging.Logger
    61  	vrrpHandler vppcalls.VrrpVppAPI
    62  }
    63  
    64  // NewVrrpDescriptor creates a new instance of the VrrpDescriptor.
    65  func NewVrrpDescriptor(vrrpHandler vppcalls.VrrpVppAPI,
    66  	log logging.PluginLogger) *kvs.KVDescriptor {
    67  	ctx := &VrrpDescriptor{
    68  		log:         log.NewLogger(vrrpDescriptorLoggerName),
    69  		vrrpHandler: vrrpHandler,
    70  	}
    71  
    72  	typedDescr := &adapter.VRRPEntryDescriptor{
    73  		Name:                 VrrpDescriptorName,
    74  		NBKeyPrefix:          l3.ModelVRRPEntry.KeyPrefix(),
    75  		ValueTypeName:        l3.ModelVRRPEntry.ProtoName(),
    76  		KeySelector:          l3.ModelVRRPEntry.IsKeyValid,
    77  		KeyLabel:             l3.ModelVRRPEntry.StripKeyPrefix,
    78  		ValueComparator:      ctx.EquivalentVRRPs,
    79  		Create:               ctx.Create,
    80  		Delete:               ctx.Delete,
    81  		Update:               ctx.Update,
    82  		UpdateWithRecreate:   ctx.UpdateWithRecreate,
    83  		Validate:             ctx.Validate,
    84  		Retrieve:             ctx.Retrieve,
    85  		Dependencies:         ctx.Dependencies,
    86  		RetrieveDependencies: []string{ifdescriptor.InterfaceDescriptorName},
    87  	}
    88  	return adapter.NewVRRPEntryDescriptor(typedDescr)
    89  }
    90  
    91  // Validate returns error if given VRRP is not valid.
    92  func (d *VrrpDescriptor) Validate(key string, vrrp *l3.VRRPEntry) error {
    93  	if vrrp.Interface == "" {
    94  		return kvs.NewInvalidValueError(ErrMissingInterface, "interface")
    95  	}
    96  
    97  	if len(vrrp.IpAddresses) > math.MaxUint8 || len(vrrp.IpAddresses) == 0 {
    98  		return kvs.NewInvalidValueError(ErrInvalidAddrNum, "ip_addresses")
    99  	}
   100  
   101  	if vrrp.GetVrId() > math.MaxUint8 || vrrp.GetVrId() == 0 {
   102  		return kvs.NewInvalidValueError(ErrInvalidVrID, "vr_id")
   103  	}
   104  
   105  	if vrrp.GetPriority() > math.MaxUint8 || vrrp.GetPriority() == 0 {
   106  		return kvs.NewInvalidValueError(ErrInvalidPriority, "priority")
   107  	}
   108  
   109  	if vrrp.GetInterval() > math.MaxUint16 || vrrp.GetInterval() < centisecondInMilliseconds {
   110  		return kvs.NewInvalidValueError(ErrInvalidInterval, "interval")
   111  	}
   112  
   113  	var ip net.IP
   114  	var isIpv6 bool
   115  	for idx, addr := range vrrp.IpAddresses {
   116  		ip = net.ParseIP(addr)
   117  		if ip == nil {
   118  			return kvs.NewInvalidValueError(ErrInvalidVrrpIP, "ip_addresses")
   119  		}
   120  
   121  		if idx == 0 && ip.To4() == nil {
   122  			isIpv6 = true
   123  			continue
   124  		}
   125  
   126  		if ip.To4() == nil && !isIpv6 || ip.To4() != nil && isIpv6 {
   127  			return kvs.NewInvalidValueError(ErrInvalidIPVer, "ip_addresses")
   128  		}
   129  	}
   130  	return nil
   131  }
   132  
   133  // Create adds VPP VRRP entry.
   134  func (d *VrrpDescriptor) Create(key string, vrrp *l3.VRRPEntry) (interface{}, error) {
   135  	if err := d.vrrpHandler.VppAddVrrp(vrrp); err != nil {
   136  		if errors.Is(vppcalls.ErrVRRPUnsupported, err) {
   137  			d.log.Debugf("Unsupported action: %v", err)
   138  		}
   139  		return nil, err
   140  	}
   141  
   142  	if vrrp.Enabled {
   143  		if err := d.vrrpHandler.VppStartVrrp(vrrp); err != nil {
   144  			return nil, err
   145  		}
   146  	}
   147  	return nil, nil
   148  }
   149  
   150  // Delete removes VPP VRRP entry.
   151  func (d *VrrpDescriptor) Delete(key string, vrrp *l3.VRRPEntry, metadata interface{}) error {
   152  	if vrrp.Enabled {
   153  		if err := d.vrrpHandler.VppStopVrrp(vrrp); err != nil {
   154  			if errors.Is(vppcalls.ErrVRRPUnsupported, err) {
   155  				d.log.Debugf("Unsupported action: %v", err)
   156  			}
   157  			return err
   158  		}
   159  	}
   160  	if err := d.vrrpHandler.VppDelVrrp(vrrp); err != nil {
   161  		return err
   162  	}
   163  	return nil
   164  }
   165  
   166  // UpdateWithRecreate returns true if a VRRP update needs to be performed via re-crate.
   167  func (d *VrrpDescriptor) UpdateWithRecreate(_ string, oldVRRPEntry, newVRRPEntry *l3.VRRPEntry, _ interface{}) bool {
   168  	if oldVRRPEntry.Enabled == newVRRPEntry.Enabled {
   169  		// Something has changed, but it is not about enabled/disabled.
   170  		return true
   171  	}
   172  
   173  	return !allFieldsWhithoutEnabledEquals(oldVRRPEntry, newVRRPEntry)
   174  }
   175  
   176  // Update updates VPP VRRP entry.
   177  func (d *VrrpDescriptor) Update(_ string, oldVRRPEntry, newVRRPEntry *l3.VRRPEntry, _ interface{}) (
   178  	_ interface{}, err error) {
   179  
   180  	if newVRRPEntry.Enabled {
   181  		err = d.vrrpHandler.VppStartVrrp(newVRRPEntry)
   182  	} else {
   183  		err = d.vrrpHandler.VppStopVrrp(newVRRPEntry)
   184  	}
   185  	return nil, err
   186  }
   187  
   188  // Dependencies lists dependencies for a VPP VRRP entry.
   189  func (d *VrrpDescriptor) Dependencies(key string, vrrp *l3.VRRPEntry) (deps []kvs.Dependency) {
   190  	// the interface must exist
   191  	if vrrp.Interface != "" {
   192  		deps = append(deps, kvs.Dependency{
   193  			Label: vrrpEntryInterfaceDep,
   194  			Key:   interfaces.InterfaceKey(vrrp.Interface),
   195  		})
   196  	}
   197  	return deps
   198  }
   199  
   200  // Retrieve returns all configured VPP VRRP entries.
   201  func (d *VrrpDescriptor) Retrieve(correlate []adapter.VRRPEntryKVWithMetadata) (
   202  	retrieved []adapter.VRRPEntryKVWithMetadata, err error,
   203  ) {
   204  	entries, err := d.vrrpHandler.DumpVrrpEntries()
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	for _, entry := range entries {
   210  		retrieved = append(retrieved, adapter.VRRPEntryKVWithMetadata{
   211  			Key:    l3.VrrpEntryKey(entry.Vrrp.Interface, entry.Vrrp.VrId),
   212  			Value:  entry.Vrrp,
   213  			Origin: kvs.FromNB,
   214  		})
   215  	}
   216  	return retrieved, nil
   217  }
   218  
   219  // EquivalentVRRPs is a comparison function for l3.VRRPEntry.
   220  func (d *VrrpDescriptor) EquivalentVRRPs(_ string, oldVRRPEntry, newVRRPEntry *l3.VRRPEntry) bool {
   221  	if proto.Equal(oldVRRPEntry, newVRRPEntry) {
   222  		return true
   223  	}
   224  	if oldVRRPEntry.Enabled != newVRRPEntry.Enabled {
   225  		return false
   226  	}
   227  	return allFieldsWhithoutEnabledEquals(oldVRRPEntry, newVRRPEntry)
   228  }
   229  
   230  // allFieldsWhithoutEnabledEquals returns true if all entrys' fields are equal,
   231  // without checking the Enabled field.
   232  func allFieldsWhithoutEnabledEquals(entry1, entry2 *l3.VRRPEntry) bool {
   233  	if entry1.Interface != entry2.Interface ||
   234  		!intervalEquals(entry1.Interval, entry2.Interval) ||
   235  		entry1.Priority != entry2.Priority ||
   236  		entry1.VrId != entry2.VrId ||
   237  		entry1.Accept != entry2.Accept ||
   238  		entry1.Preempt != entry2.Preempt ||
   239  		entry1.Unicast != entry2.Unicast ||
   240  		len(entry1.IpAddresses) != len(entry2.IpAddresses) {
   241  		return false
   242  	}
   243  
   244  	for i := 0; i < len(entry1.IpAddresses); i++ {
   245  		if entry1.IpAddresses[i] != entry2.IpAddresses[i] {
   246  			return false
   247  		}
   248  	}
   249  	return true
   250  }
   251  
   252  // intervalEquals returns true if i1 and i2 are equal in centisonds.
   253  func intervalEquals(i1, i2 uint32) bool {
   254  	return i1/centisecondInMilliseconds == i2/centisecondInMilliseconds
   255  }