go.ligato.io/vpp-agent/v3@v3.5.0/plugins/linux/ifplugin/ifaceidx/ifaceidx.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 ifaceidx
    16  
    17  import (
    18  	"time"
    19  
    20  	"go.ligato.io/cn-infra/v2/idxmap"
    21  	"go.ligato.io/cn-infra/v2/idxmap/mem"
    22  	"go.ligato.io/cn-infra/v2/logging"
    23  	"google.golang.org/protobuf/proto"
    24  
    25  	linux_namespace "go.ligato.io/vpp-agent/v3/proto/ligato/linux/namespace"
    26  )
    27  
    28  // LinuxIfMetadataIndex provides read-only access to mapping with Linux interface
    29  // metadata. It extends from NameToIndex.
    30  type LinuxIfMetadataIndex interface {
    31  	// LookupByName retrieves a previously stored metadata of interface
    32  	// identified by logical <name>. If there is no interface associated with
    33  	// the given name in the mapping, the <exists> is returned as *false* and
    34  	// <metadata> as *nil*.
    35  	LookupByName(name string) (metadata *LinuxIfMetadata, exists bool)
    36  
    37  	// LookupByVPPTap retrieves a previously configured TAP_TO_VPP interface
    38  	// by the logical name of the associated VPP-side of the TAP.
    39  	// If there is no such interface, <exists> is returned as *false* with <name>
    40  	// and <metadata> both set to empty values.
    41  	LookupByVPPTap(vppTapName string) (name string, metadata *LinuxIfMetadata, exists bool)
    42  
    43  	// LookupByHostName retrieves a previously configured Linux interface
    44  	// by the host interface name inside the given namespace.
    45  	// If there is no such interface, <exists> is returned as *false* with <name>
    46  	// and <metadata> both set to empty values.
    47  	LookupByHostName(hostname string, ns *linux_namespace.NetNamespace) (name string,
    48  		metadata *LinuxIfMetadata, exists bool)
    49  
    50  	// ListAllInterfaces returns slice of names of all interfaces in the mapping.
    51  	ListAllInterfaces() (names []string)
    52  
    53  	// WatchInterfaces allows to subscribe to watch for changes in the mapping
    54  	// of interface metadata.
    55  	WatchInterfaces(subscriber string, channel chan<- LinuxIfMetadataIndexDto) error
    56  }
    57  
    58  // LinuxIfMetadataIndexRW provides read-write access to mapping with interface
    59  // metadata.
    60  type LinuxIfMetadataIndexRW interface {
    61  	LinuxIfMetadataIndex
    62  	idxmap.NamedMappingRW
    63  }
    64  
    65  // LinuxIfMetadata collects metadata for Linux interface used in secondary lookups.
    66  type LinuxIfMetadata struct {
    67  	LinuxIfIndex int
    68  	VPPTapName   string // empty for VETHs
    69  	Namespace    *linux_namespace.NetNamespace
    70  	HostIfName   string
    71  	VrfMasterIf  string
    72  	VrfDevRT     uint32 // only set for VRF_DEVICE
    73  }
    74  
    75  // LinuxIfMetadataIndexDto represents an item sent through watch channel in LinuxIfMetadataIndex.
    76  // In contrast to NamedMappingGenericEvent, it contains typed interface metadata.
    77  type LinuxIfMetadataIndexDto struct {
    78  	idxmap.NamedMappingEvent
    79  	Metadata *LinuxIfMetadata
    80  }
    81  
    82  // linuxIfMetadataIndex is type-safe implementation of mapping between interface
    83  // name and metadata of type *LinuxIfMetadata.
    84  type linuxIfMetadataIndex struct {
    85  	idxmap.NamedMappingRW /* embeds */
    86  	log                   logging.Logger
    87  }
    88  
    89  const (
    90  	// tapVPPNameIndexKey is used as a secondary key used to search TAP_TO_VPP
    91  	// interface by the logical name of the VPP-side of the TAP.
    92  	tapVPPNameIndexKey = "tap-vpp-name"
    93  
    94  	// hostNameIndexKey is used as a secondary key used to search Linux
    95  	// interfaces by the host interface name.
    96  	hostNameIndexKey = "host-iface-name"
    97  )
    98  
    99  // NewLinuxIfIndex creates a new instance implementing LinuxIfMetadataIndexRW.
   100  func NewLinuxIfIndex(logger logging.Logger, title string) LinuxIfMetadataIndexRW {
   101  	return &linuxIfMetadataIndex{
   102  		NamedMappingRW: mem.NewNamedMapping(logger, title, indexMetadata),
   103  	}
   104  }
   105  
   106  // LookupByName retrieves a previously stored metadata of interface
   107  // identified by logical <name>. If there is no interface associated with
   108  // the give/ name in the mapping, the <exists> is returned as *false* and
   109  // <metadata> as *nil*.
   110  func (ifmx *linuxIfMetadataIndex) LookupByName(name string) (metadata *LinuxIfMetadata, exists bool) {
   111  	meta, found := ifmx.GetValue(name)
   112  	if found {
   113  		if typedMeta, ok := meta.(*LinuxIfMetadata); ok {
   114  			return typedMeta, found
   115  		}
   116  	}
   117  	return nil, false
   118  }
   119  
   120  // LookupByVPPTap retrieves a previously configured TAP_TO_VPP interface
   121  // by the logical name of the associated VPP-side of the TAP.
   122  // If there is no such interface, <exists> is returned as *false* with <name>
   123  // and <metadata> both set to empty values.
   124  func (ifmx *linuxIfMetadataIndex) LookupByVPPTap(vppTapName string) (name string, metadata *LinuxIfMetadata, exists bool) {
   125  	res := ifmx.ListNames(tapVPPNameIndexKey, vppTapName)
   126  	if len(res) != 1 {
   127  		return
   128  	}
   129  	untypedMeta, found := ifmx.GetValue(res[0])
   130  	if found {
   131  		if ifMeta, ok := untypedMeta.(*LinuxIfMetadata); ok {
   132  			return res[0], ifMeta, found
   133  		}
   134  	}
   135  	return
   136  }
   137  
   138  // LookupByHostName retrieves a previously configured Linux interface
   139  // by the host interface name inside the given namespace.
   140  // If there is no such interface, <exists> is returned as *false* with <name>
   141  // and <metadata> both set to empty values.
   142  func (ifmx *linuxIfMetadataIndex) LookupByHostName(hostname string, ns *linux_namespace.NetNamespace) (
   143  	name string, metadata *LinuxIfMetadata, exists bool) {
   144  
   145  	res := ifmx.ListNames(hostNameIndexKey, hostname)
   146  	for _, iface := range res {
   147  		untypedMeta, found := ifmx.GetValue(iface)
   148  		if found {
   149  			if ifMeta, ok := untypedMeta.(*LinuxIfMetadata); ok {
   150  				if proto.Equal(ns, ifMeta.Namespace) {
   151  					return iface, ifMeta, true
   152  				}
   153  			}
   154  		}
   155  	}
   156  	return
   157  }
   158  
   159  // ListAllInterfaces returns slice of names of all interfaces in the mapping.
   160  func (ifmx *linuxIfMetadataIndex) ListAllInterfaces() (names []string) {
   161  	return ifmx.ListAllNames()
   162  }
   163  
   164  // WatchInterfaces allows to subscribe to watch for changes in the mapping
   165  // if interface metadata.
   166  func (ifmx *linuxIfMetadataIndex) WatchInterfaces(subscriber string, channel chan<- LinuxIfMetadataIndexDto) error {
   167  	watcher := func(dto idxmap.NamedMappingGenericEvent) {
   168  		typedMeta, ok := dto.Value.(*LinuxIfMetadata)
   169  		if !ok {
   170  			return
   171  		}
   172  		msg := LinuxIfMetadataIndexDto{
   173  			NamedMappingEvent: dto.NamedMappingEvent,
   174  			Metadata:          typedMeta,
   175  		}
   176  		select {
   177  		case channel <- msg:
   178  		case <-time.After(idxmap.DefaultNotifTimeout):
   179  			ifmx.log.Warn("Unable to deliver notification")
   180  		}
   181  	}
   182  	return ifmx.Watch(subscriber, watcher)
   183  }
   184  
   185  // indexMetadata is an index function used for interface metadata.
   186  func indexMetadata(metaData interface{}) map[string][]string {
   187  	indexes := make(map[string][]string)
   188  
   189  	ifMeta, ok := metaData.(*LinuxIfMetadata)
   190  	if !ok || ifMeta == nil {
   191  		return indexes
   192  	}
   193  
   194  	if ifMeta.VPPTapName != "" {
   195  		indexes[tapVPPNameIndexKey] = []string{ifMeta.VPPTapName}
   196  	}
   197  	indexes[hostNameIndexKey] = []string{ifMeta.HostIfName}
   198  
   199  	return indexes
   200  }