go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/l2plugin/descriptor/bridgedomain.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  	"fmt"
    19  	"net"
    20  	"strings"
    21  
    22  	"github.com/pkg/errors"
    23  	"go.ligato.io/cn-infra/v2/idxmap"
    24  	"go.ligato.io/cn-infra/v2/logging"
    25  
    26  	"go.ligato.io/vpp-agent/v3/pkg/idxvpp"
    27  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    28  	vpp_ifdescriptor "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor"
    29  	"go.ligato.io/vpp-agent/v3/plugins/vpp/l2plugin/descriptor/adapter"
    30  	"go.ligato.io/vpp-agent/v3/plugins/vpp/l2plugin/vppcalls"
    31  	l2 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l2"
    32  )
    33  
    34  const (
    35  	// BridgeDomainDescriptorName is the name of the descriptor for VPP bridge domains.
    36  	BridgeDomainDescriptorName = "vpp-bridge-domain"
    37  
    38  	// prefix prepended to internal names of untagged bridge domains to construct
    39  	// unique logical names
    40  	untaggedBDPreffix = "UNTAGGED-"
    41  )
    42  
    43  // A list of non-retriable errors:
    44  var (
    45  	// ErrBridgeDomainWithoutName is returned when VPP bridge domain configuration
    46  	// has undefined Name attribute.
    47  	ErrBridgeDomainWithoutName = errors.New("VPP bridge domain defined without logical name")
    48  
    49  	// ErrBridgeDomainWithMultipleBVI is returned when bridge domain is defined with
    50  	// multiple BVI interfaces.
    51  	ErrBridgeDomainWithMultipleBVI = errors.New("VPP bridge domain defined with mutliple BVIs")
    52  )
    53  
    54  // BridgeDomainDescriptor teaches KVScheduler how to configure VPP bridge domains.
    55  type BridgeDomainDescriptor struct {
    56  	// dependencies
    57  	log       logging.Logger
    58  	bdHandler vppcalls.BridgeDomainVppAPI
    59  
    60  	// runtime
    61  	bdIDSeq uint32
    62  }
    63  
    64  // NewBridgeDomainDescriptor creates a new instance of the BridgeDomain descriptor.
    65  func NewBridgeDomainDescriptor(bdHandler vppcalls.BridgeDomainVppAPI, log logging.PluginLogger) *BridgeDomainDescriptor {
    66  
    67  	return &BridgeDomainDescriptor{
    68  		bdHandler: bdHandler,
    69  		log:       log.NewLogger("bd-descriptor"),
    70  		bdIDSeq:   1,
    71  	}
    72  }
    73  
    74  // GetDescriptor returns descriptor suitable for registration (via adapter) with
    75  // the KVScheduler.
    76  func (d *BridgeDomainDescriptor) GetDescriptor() *adapter.BridgeDomainDescriptor {
    77  	return &adapter.BridgeDomainDescriptor{
    78  		Name:                 BridgeDomainDescriptorName,
    79  		NBKeyPrefix:          l2.ModelBridgeDomain.KeyPrefix(),
    80  		ValueTypeName:        l2.ModelBridgeDomain.ProtoName(),
    81  		KeySelector:          l2.ModelBridgeDomain.IsKeyValid,
    82  		KeyLabel:             l2.ModelBridgeDomain.StripKeyPrefix,
    83  		ValueComparator:      d.EquivalentBridgeDomains,
    84  		WithMetadata:         true,
    85  		MetadataMapFactory:   d.MetadataFactory,
    86  		Validate:             d.Validate,
    87  		Create:               d.Create,
    88  		Delete:               d.Delete,
    89  		Update:               d.Update,
    90  		UpdateWithRecreate:   d.UpdateWithRecreate,
    91  		Retrieve:             d.Retrieve,
    92  		DerivedValues:        d.DerivedValues,
    93  		RetrieveDependencies: []string{vpp_ifdescriptor.InterfaceDescriptorName},
    94  	}
    95  }
    96  
    97  // EquivalentBridgeDomains is case-insensitive comparison function for
    98  // l2.BridgeDomain, also ignoring the order of assigned ARP termination entries.
    99  func (d *BridgeDomainDescriptor) EquivalentBridgeDomains(key string, oldBD, newBD *l2.BridgeDomain) bool {
   100  	// BD parameters
   101  	if !equalBDParameters(oldBD, newBD) {
   102  		return false
   103  	}
   104  
   105  	// ARP termination entries
   106  	obsoleteARPs, newARPs := calculateARPDiff(oldBD.GetArpTerminationTable(), newBD.GetArpTerminationTable())
   107  	return len(obsoleteARPs) == 0 && len(newARPs) == 0
   108  }
   109  
   110  // MetadataFactory is a factory for index-map customized for VPP bridge domains.
   111  func (d *BridgeDomainDescriptor) MetadataFactory() idxmap.NamedMappingRW {
   112  	return idxvpp.NewNameToIndex(d.log, "vpp-bd-index", nil)
   113  }
   114  
   115  // Validate validates VPP bridge domain configuration.
   116  func (d *BridgeDomainDescriptor) Validate(key string, bd *l2.BridgeDomain) error {
   117  	if bd.Name == "" {
   118  		return kvs.NewInvalidValueError(ErrBridgeDomainWithoutName, "name")
   119  	}
   120  
   121  	// check that BD has defined at most one BVI
   122  	var hasBVI bool
   123  	for _, bdIface := range bd.Interfaces {
   124  		if bdIface.BridgedVirtualInterface {
   125  			if hasBVI {
   126  				return kvs.NewInvalidValueError(ErrBridgeDomainWithMultipleBVI,
   127  					"interfaces.bridged_virtual_interface")
   128  			}
   129  			hasBVI = true
   130  		}
   131  	}
   132  	return nil
   133  }
   134  
   135  // Create adds new bridge domain.
   136  func (d *BridgeDomainDescriptor) Create(key string, bd *l2.BridgeDomain) (metadata *idxvpp.OnlyIndex, err error) {
   137  	// allocate new bridge domain ID
   138  	bdIdx := d.bdIDSeq
   139  	d.bdIDSeq++
   140  
   141  	// create the bridge domain
   142  	err = d.bdHandler.AddBridgeDomain(bdIdx, bd)
   143  	if err != nil {
   144  		// Note: d.bdIDSeq will be refreshed by Dump
   145  		d.log.Error(err)
   146  		return nil, err
   147  	}
   148  
   149  	// add ARP termination entries
   150  	for _, arp := range bd.ArpTerminationTable {
   151  		if err := d.bdHandler.AddArpTerminationTableEntry(bdIdx, arp.PhysAddress, arp.IpAddress); err != nil {
   152  			d.log.Error(err)
   153  			return nil, err
   154  		}
   155  	}
   156  
   157  	// fill the metadata
   158  	metadata = &idxvpp.OnlyIndex{
   159  		Index: bdIdx,
   160  	}
   161  	return metadata, nil
   162  }
   163  
   164  // Delete removes VPP bridge domain.
   165  func (d *BridgeDomainDescriptor) Delete(key string, bd *l2.BridgeDomain, metadata *idxvpp.OnlyIndex) error {
   166  	err := d.bdHandler.DeleteBridgeDomain(metadata.GetIndex())
   167  	if err != nil {
   168  		d.log.Error(err)
   169  	}
   170  	return err
   171  }
   172  
   173  // UpdateWithRecreate returns true if bridge domain base parameters are different.
   174  func (d *BridgeDomainDescriptor) UpdateWithRecreate(key string, oldBD, newBD *l2.BridgeDomain, metadata *idxvpp.OnlyIndex) bool {
   175  	return !equalBDParameters(oldBD, newBD)
   176  }
   177  
   178  // Update is able to change ARP termination entries.
   179  func (d *BridgeDomainDescriptor) Update(key string, oldBD, newBD *l2.BridgeDomain, oldMetadata *idxvpp.OnlyIndex) (newMetadata *idxvpp.OnlyIndex, err error) {
   180  	// update ARP termination entries
   181  	bdIdx := oldMetadata.Index
   182  	obsoleteARPs, newARPs := calculateARPDiff(oldBD.GetArpTerminationTable(), newBD.GetArpTerminationTable())
   183  	for _, arp := range obsoleteARPs { // remove obsolete first to avoid collisions
   184  		if err := d.bdHandler.RemoveArpTerminationTableEntry(bdIdx, arp.PhysAddress, arp.IpAddress); err != nil {
   185  			d.log.Error(err)
   186  			return oldMetadata, err
   187  		}
   188  	}
   189  	for _, arp := range newARPs {
   190  		if err := d.bdHandler.AddArpTerminationTableEntry(bdIdx, arp.PhysAddress, arp.IpAddress); err != nil {
   191  			d.log.Error(err)
   192  			return oldMetadata, err
   193  		}
   194  	}
   195  
   196  	return oldMetadata, nil
   197  }
   198  
   199  // Retrieve returns all configured VPP bridge domains.
   200  func (d *BridgeDomainDescriptor) Retrieve(correlate []adapter.BridgeDomainKVWithMetadata) (retrieved []adapter.BridgeDomainKVWithMetadata, err error) {
   201  	// d.bdIDSeq will be refreshed
   202  	var bdIDSeq uint32 = 1
   203  
   204  	// sequence number for untagged interfaces
   205  	var untaggedSeq int
   206  
   207  	// dump bridge domains
   208  	bridgeDomains, err := d.bdHandler.DumpBridgeDomains()
   209  	if err != nil {
   210  		d.log.Error(err)
   211  		return retrieved, err
   212  	}
   213  	for _, bd := range bridgeDomains {
   214  		// make sure that bdIDSeq is larger than any of the existing indexes
   215  		if bd.Meta.BdID >= bdIDSeq {
   216  			bdIDSeq = bd.Meta.BdID + 1
   217  		}
   218  
   219  		// handle untagged bridge domain - construct name that is unlikely to
   220  		// collide with NB, thus the bridge domain will get removed by resync
   221  		if bd.Bd.Name == "" {
   222  			bd.Bd.Name = fmt.Sprintf("%s%d", untaggedBDPreffix, untaggedSeq)
   223  			untaggedSeq++
   224  		}
   225  
   226  		retrieved = append(retrieved, adapter.BridgeDomainKVWithMetadata{
   227  			Key:      l2.BridgeDomainKey(bd.Bd.Name),
   228  			Value:    bd.Bd,
   229  			Metadata: &idxvpp.OnlyIndex{Index: bd.Meta.BdID},
   230  			Origin:   kvs.FromNB,
   231  		})
   232  	}
   233  
   234  	// update d.bdIDSeq
   235  	d.bdIDSeq = bdIDSeq
   236  
   237  	return retrieved, nil
   238  }
   239  
   240  // DerivedValues derives l2.BridgeDomain_Interface for every interface assigned
   241  // to the bridge domain.
   242  func (d *BridgeDomainDescriptor) DerivedValues(key string, bd *l2.BridgeDomain) (derValues []kvs.KeyValuePair) {
   243  	// BD interfaces
   244  	for _, bdIface := range bd.Interfaces {
   245  		derValues = append(derValues, kvs.KeyValuePair{
   246  			Key:   l2.BDInterfaceKey(bd.Name, bdIface.Name),
   247  			Value: bdIface,
   248  		})
   249  	}
   250  	return derValues
   251  }
   252  
   253  // equalBDParameters compares all base bridge domain parameters for equality.
   254  func equalBDParameters(bd1, bd2 *l2.BridgeDomain) bool {
   255  	return bd1.ArpTermination == bd2.ArpTermination && bd1.Flood == bd2.Flood &&
   256  		bd1.Forward == bd2.Forward && bd1.Learn == bd2.Learn && bd1.MacAge == bd2.MacAge &&
   257  		bd1.UnknownUnicastFlood == bd2.UnknownUnicastFlood
   258  }
   259  
   260  // calculateARPDiff compares two sets of ARP termination entries.
   261  func calculateARPDiff(oldARPs, newARPs []*l2.BridgeDomain_ArpTerminationEntry) (toRemove, toAdd []*l2.BridgeDomain_ArpTerminationEntry) {
   262  	// Resolve ARPs to add
   263  	for _, newARP := range newARPs {
   264  		var exists bool
   265  		for _, oldARP := range oldARPs {
   266  			if equalTerminationARPs(oldARP, newARP) {
   267  				exists = true
   268  				break
   269  			}
   270  		}
   271  		if !exists {
   272  			toAdd = append(toAdd, newARP)
   273  		}
   274  	}
   275  	// Resolve ARPs to remove
   276  	for _, oldARP := range oldARPs {
   277  		var exists bool
   278  		for _, newARP := range newARPs {
   279  			if equalTerminationARPs(oldARP, newARP) {
   280  				exists = true
   281  				break
   282  			}
   283  		}
   284  		if !exists {
   285  			toRemove = append(toRemove, oldARP)
   286  		}
   287  	}
   288  
   289  	return toAdd, toRemove
   290  }
   291  
   292  // equalTerminationARPs compares two termination ARP entries for equality.
   293  func equalTerminationARPs(arp1, arp2 *l2.BridgeDomain_ArpTerminationEntry) bool {
   294  	// compare MAC addresses
   295  	if !strings.EqualFold(arp1.PhysAddress, arp2.PhysAddress) {
   296  		return false
   297  	}
   298  
   299  	// compare IP addresses
   300  	ip1 := net.ParseIP(arp1.IpAddress)
   301  	ip2 := net.ParseIP(arp2.IpAddress)
   302  	if ip1 == nil || ip2 == nil {
   303  		// if parsing fails, compare as strings
   304  		return strings.EqualFold(arp1.IpAddress, arp2.IpAddress)
   305  	}
   306  	return ip1.Equal(ip2)
   307  }