go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/ipfixplugin/descriptor/ipfix.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  	"errors"
    19  
    20  	"go.ligato.io/cn-infra/v2/logging"
    21  
    22  	"go.ligato.io/vpp-agent/v3/pkg/models"
    23  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    24  	"go.ligato.io/vpp-agent/v3/plugins/vpp/ipfixplugin/descriptor/adapter"
    25  	"go.ligato.io/vpp-agent/v3/plugins/vpp/ipfixplugin/vppcalls"
    26  	ipfix "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/ipfix"
    27  )
    28  
    29  const (
    30  	// IPFIXDescriptorName is the name of the descriptor for
    31  	// VPP IP Flow Information eXport (IPFIX) configuration.
    32  	IPFIXDescriptorName = "vpp-ipfix"
    33  )
    34  
    35  // Validation errors:
    36  var (
    37  	// ErrColAddrNotDefined returned when collector address in confiugration is empty string.
    38  	ErrColAddrNotDefined = errors.New("address of a collector was not provided")
    39  	// ErrSrcAddrNotDefined returned when source address in confiugration is empty string.
    40  	ErrSrcAddrNotDefined = errors.New("address of a source was not provided")
    41  	// ErrTooBigMTU informs about the maximum value for Path MTU.
    42  	ErrTooBigMTU = errors.New("too big value, maximum is 1450")
    43  	// ErrTooSmlMTU informs about the minimum value for Path MTU.
    44  	ErrTooSmlMTU = errors.New("too small value, minimum is 68")
    45  )
    46  
    47  // IPFIXDescriptor configures IPFIX for VPP.
    48  type IPFIXDescriptor struct {
    49  	ipfixHandler vppcalls.IpfixVppAPI
    50  	log          logging.Logger
    51  }
    52  
    53  // NewIPFIXDescriptor creates a new instance of IPFIXDescriptor.
    54  func NewIPFIXDescriptor(ipfixHandler vppcalls.IpfixVppAPI, log logging.PluginLogger) *kvs.KVDescriptor {
    55  	ctx := &IPFIXDescriptor{
    56  		ipfixHandler: ipfixHandler,
    57  		log:          log.NewLogger("ipfix-descriptor"),
    58  	}
    59  	typedDescr := &adapter.IPFIXDescriptor{
    60  		Name:            IPFIXDescriptorName,
    61  		NBKeyPrefix:     ipfix.ModelIPFIX.KeyPrefix(),
    62  		ValueTypeName:   ipfix.ModelIPFIX.ProtoName(),
    63  		KeySelector:     ipfix.ModelIPFIX.IsKeyValid,
    64  		KeyLabel:        ipfix.ModelIPFIX.StripKeyPrefix,
    65  		ValueComparator: ctx.EquivalentIPFIX,
    66  		Validate:        ctx.Validate,
    67  		Create:          ctx.Create,
    68  		Delete:          ctx.Delete,
    69  		Retrieve:        ctx.Retrieve,
    70  		Update:          ctx.Update,
    71  	}
    72  	return adapter.NewIPFIXDescriptor(typedDescr)
    73  }
    74  
    75  // EquivalentIPFIX returns true if two IPFIX configurations are equal.
    76  func (d *IPFIXDescriptor) EquivalentIPFIX(key string, oldValue, newValue *ipfix.IPFIX) bool {
    77  	if oldValue.GetCollector().GetAddress() != newValue.GetCollector().GetAddress() {
    78  		return false
    79  	}
    80  
    81  	if oldValue.GetSourceAddress() != newValue.GetSourceAddress() {
    82  		return false
    83  	}
    84  
    85  	oldPort := oldValue.GetCollector().GetPort()
    86  	newPort := newValue.GetCollector().GetPort()
    87  	if oldPort != newPort {
    88  		defaultPort := uint32(4739)
    89  		oldIsNotDefault := oldPort != 0 && oldPort != defaultPort
    90  		newIsNotDefault := newPort != 0 && newPort != defaultPort
    91  
    92  		if oldIsNotDefault || newIsNotDefault {
    93  			return false
    94  		}
    95  	}
    96  
    97  	if oldValue.GetVrfId() != newValue.GetVrfId() {
    98  		return false
    99  	}
   100  
   101  	oldMTU := oldValue.GetPathMtu()
   102  	newMTU := newValue.GetPathMtu()
   103  	if oldMTU != newMTU {
   104  		defaultMTU := uint32(512)
   105  		oldIsNotDefault := oldMTU != 0 && oldMTU != defaultMTU
   106  		newIsNotDefault := newMTU != 0 && newMTU != defaultMTU
   107  
   108  		if oldIsNotDefault || newIsNotDefault {
   109  			return false
   110  		}
   111  	}
   112  
   113  	oldInterval := oldValue.GetTemplateInterval()
   114  	newInterval := newValue.GetTemplateInterval()
   115  	if oldInterval != newInterval {
   116  		defaultInterval := uint32(20)
   117  		oldIsNotDefault := oldInterval != 0 && oldInterval != defaultInterval
   118  		newIsNotDefault := newInterval != 0 && newInterval != defaultInterval
   119  
   120  		if oldIsNotDefault || newIsNotDefault {
   121  			return false
   122  		}
   123  	}
   124  
   125  	return true
   126  }
   127  
   128  // Validate does basic check of VPP IPFIX configuration.
   129  func (d *IPFIXDescriptor) Validate(key string, value *ipfix.IPFIX) error {
   130  	if value.GetCollector().GetAddress() == "" {
   131  		return kvs.NewInvalidValueError(ErrColAddrNotDefined, "collector.address")
   132  	}
   133  
   134  	if value.GetSourceAddress() == "" {
   135  		return kvs.NewInvalidValueError(ErrSrcAddrNotDefined, "source_address")
   136  	}
   137  
   138  	if mtu := value.GetPathMtu(); mtu == 0 {
   139  		// That's okay. No worries. VPP will use the default Path MTU value.
   140  	} else if mtu > vppcalls.MaxPathMTU {
   141  		return kvs.NewInvalidValueError(ErrTooBigMTU, "path_mtu")
   142  	} else if mtu < vppcalls.MinPathMTU {
   143  		return kvs.NewInvalidValueError(ErrTooSmlMTU, "path_mtu")
   144  	}
   145  
   146  	return nil
   147  }
   148  
   149  // Create calls Update method, because IPFIX configuration is always there and can not be created.
   150  func (d *IPFIXDescriptor) Create(key string, val *ipfix.IPFIX) (metadata interface{}, err error) {
   151  	_, err = d.Update(key, nil, val, nil)
   152  	return
   153  }
   154  
   155  // Update sets VPP IPFIX configuration.
   156  func (d *IPFIXDescriptor) Update(key string, oldVal, newVal *ipfix.IPFIX, oldMetadata interface{}) (newMetadata interface{}, err error) {
   157  	err = d.ipfixHandler.SetExporter(newVal)
   158  	return
   159  }
   160  
   161  // Delete does nothing, because there are neither ability
   162  // nor reasons to delete VPP IPFIX configuration.
   163  // You can only configure exporting in a way you want to.
   164  func (d *IPFIXDescriptor) Delete(key string, val *ipfix.IPFIX, metadata interface{}) (err error) {
   165  	return nil
   166  }
   167  
   168  // Retrieve returns configuration of IP Flow Infromation eXporter.
   169  func (d *IPFIXDescriptor) Retrieve(correlate []adapter.IPFIXKVWithMetadata) (retrieved []adapter.IPFIXKVWithMetadata, err error) {
   170  	ipfixes, err := d.ipfixHandler.DumpExporters()
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	for _, e := range ipfixes {
   176  		retrieved = append(retrieved, adapter.IPFIXKVWithMetadata{
   177  			Key:    models.Key(e),
   178  			Value:  e,
   179  			Origin: kvs.FromSB,
   180  		})
   181  	}
   182  
   183  	return retrieved, nil
   184  }