go.ligato.io/vpp-agent/v3@v3.5.0/plugins/linux/l3plugin/linuxcalls/dump_arp_linuxcalls.go (about)

     1  // Copyright (c) 2019 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  //go:build !windows && !darwin
    16  
    17  package linuxcalls
    18  
    19  import (
    20  	"github.com/pkg/errors"
    21  	"github.com/vishvananda/netlink"
    22  	"go.ligato.io/cn-infra/v2/logging"
    23  
    24  	"go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin/linuxcalls"
    25  	linux_l3 "go.ligato.io/vpp-agent/v3/proto/ligato/linux/l3"
    26  )
    27  
    28  // retrievedARPs is used as the return value sent via channel by retrieveARPs().
    29  type retrievedARPs struct {
    30  	arps []*ArpDetails
    31  	err  error
    32  }
    33  
    34  // GetARPEntries reads all configured static ARP entries for given interface.
    35  // <interfaceIdx> works as filter, if set to zero, all arp entries in the namespace
    36  // are returned
    37  func (h *NetLinkHandler) GetARPEntries(interfaceIdx int) ([]netlink.Neigh, error) {
    38  	return netlink.NeighList(interfaceIdx, 0)
    39  }
    40  
    41  // DumpARPEntries reads all ARP entries and returns them as details
    42  // with proto-modeled ARP data and additional metadata
    43  func (h *NetLinkHandler) DumpARPEntries() ([]*ArpDetails, error) {
    44  	interfaces := h.ifIndexes.ListAllInterfaces()
    45  	goRoutinesCnt := len(interfaces) / minWorkForGoRoutine
    46  	if goRoutinesCnt == 0 {
    47  		goRoutinesCnt = 1
    48  	}
    49  	if goRoutinesCnt > h.goRoutineCount {
    50  		goRoutinesCnt = h.goRoutineCount
    51  	}
    52  	ch := make(chan retrievedARPs, goRoutinesCnt)
    53  
    54  	// invoke multiple go routines for more efficient parallel ARP retrieval
    55  	for idx := 0; idx < goRoutinesCnt; idx++ {
    56  		if goRoutinesCnt > 1 {
    57  			go h.retrieveARPs(interfaces, idx, goRoutinesCnt, ch)
    58  		} else {
    59  			h.retrieveARPs(interfaces, idx, goRoutinesCnt, ch)
    60  		}
    61  	}
    62  
    63  	// collect results from the go routines
    64  	var arpDetails []*ArpDetails
    65  	for idx := 0; idx < goRoutinesCnt; idx++ {
    66  		retrieved := <-ch
    67  		if retrieved.err != nil {
    68  			return nil, retrieved.err
    69  		}
    70  		arpDetails = append(arpDetails, retrieved.arps...)
    71  	}
    72  
    73  	return arpDetails, nil
    74  }
    75  
    76  // retrieveARPs is run by a separate go routine to retrieve all ARP entries associated
    77  // with every <goRoutineIdx>-th interface.
    78  func (h *NetLinkHandler) retrieveARPs(interfaces []string, goRoutineIdx, goRoutinesCnt int, ch chan<- retrievedARPs) {
    79  	var retrieved retrievedARPs
    80  	nsCtx := linuxcalls.NewNamespaceMgmtCtx()
    81  
    82  	for i := goRoutineIdx; i < len(interfaces); i += goRoutinesCnt {
    83  		ifName := interfaces[i]
    84  		// get interface metadata
    85  		ifMeta, found := h.ifIndexes.LookupByName(ifName)
    86  		if !found || ifMeta == nil {
    87  			retrieved.err = errors.Errorf("failed to obtain metadata for interface %s", ifName)
    88  			h.log.Error(retrieved.err)
    89  			break
    90  		}
    91  
    92  		// switch to the namespace of the interface
    93  		revertNs, err := h.nsPlugin.SwitchToNamespace(nsCtx, ifMeta.Namespace)
    94  		if err != nil {
    95  			// namespace and all the ARPs it had contained no longer exist
    96  			h.log.WithFields(logging.Fields{
    97  				"err":       err,
    98  				"namespace": ifMeta.Namespace,
    99  			}).Warn("Failed to retrieve ARPs from the namespace")
   100  			continue
   101  		}
   102  
   103  		// get ARPs assigned to this interface
   104  		arps, err := h.GetARPEntries(ifMeta.LinuxIfIndex)
   105  		revertNs()
   106  		if err != nil {
   107  			retrieved.err = err
   108  			h.log.Error(retrieved.err)
   109  			break
   110  		}
   111  
   112  		// convert each ARP from Netlink representation to the ARP details
   113  		for _, arp := range arps {
   114  			if arp.IP.IsLinkLocalMulticast() {
   115  				// skip link-local multi-cast ARPs until there is a requirement to support them as well
   116  				continue
   117  			}
   118  			retrieved.arps = append(retrieved.arps, &ArpDetails{
   119  				ARP: &linux_l3.ARPEntry{
   120  					Interface: ifName,
   121  					IpAddress: arp.IP.String(),
   122  					HwAddress: arp.HardwareAddr.String(),
   123  				},
   124  				Meta: &ArpMeta{
   125  					InterfaceIndex: uint32(arp.LinkIndex),
   126  					IPFamily:       uint32(arp.Family),
   127  					VNI:            uint32(arp.VNI),
   128  				},
   129  			})
   130  		}
   131  	}
   132  
   133  	ch <- retrieved
   134  }