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 }