go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/l2plugin/vppcalls/vpp2210/dump_vppcalls.go (about)

     1  //  Copyright (c) 2022 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 vpp2210
    16  
    17  import (
    18  	"net"
    19  	"strings"
    20  
    21  	"github.com/pkg/errors"
    22  
    23  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2210/interface_types"
    24  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2210/ip_types"
    25  	vpp_l2 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2210/l2"
    26  	"go.ligato.io/vpp-agent/v3/plugins/vpp/l2plugin/vppcalls"
    27  	l2 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l2"
    28  )
    29  
    30  // DumpBridgeDomains implements bridge domain handler.
    31  func (h *BridgeDomainVppHandler) DumpBridgeDomains() ([]*vppcalls.BridgeDomainDetails, error) {
    32  	// At first prepare bridge domain ARP termination table which needs to be dumped separately.
    33  	bdArpTab, err := h.dumpBridgeDomainMacTable()
    34  	if err != nil {
    35  		return nil, errors.Errorf("failed to dump arp termination table: %v", err)
    36  	}
    37  
    38  	// list of resulting BDs
    39  	var bds []*vppcalls.BridgeDomainDetails
    40  
    41  	// dump bridge domains
    42  	reqCtx := h.callsChannel.SendMultiRequest(&vpp_l2.BridgeDomainDump{
    43  		BdID:      ^uint32(0),
    44  		SwIfIndex: ^interface_types.InterfaceIndex(0),
    45  	})
    46  
    47  	for {
    48  		bdDetails := &vpp_l2.BridgeDomainDetails{}
    49  		stop, err := reqCtx.ReceiveReply(bdDetails)
    50  		if stop {
    51  			break
    52  		}
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  
    57  		// bridge domain metadata
    58  		bdData := &vppcalls.BridgeDomainDetails{
    59  			Bd: &l2.BridgeDomain{
    60  				Name:                strings.Trim(bdDetails.BdTag, "\x00"),
    61  				Flood:               bdDetails.Flood,
    62  				UnknownUnicastFlood: bdDetails.UuFlood,
    63  				Forward:             bdDetails.Forward,
    64  				Learn:               bdDetails.Learn,
    65  				ArpTermination:      bdDetails.ArpTerm,
    66  				MacAge:              uint32(bdDetails.MacAge),
    67  			},
    68  			Meta: &vppcalls.BridgeDomainMeta{
    69  				BdID: bdDetails.BdID,
    70  			},
    71  		}
    72  
    73  		// bridge domain interfaces
    74  		for _, iface := range bdDetails.SwIfDetails {
    75  			ifaceName, _, exists := h.ifIndexes.LookupBySwIfIndex(uint32(iface.SwIfIndex))
    76  			if !exists {
    77  				h.log.Warnf("Bridge domain dump: interface name for index %d not found", iface.SwIfIndex)
    78  				continue
    79  			}
    80  			// Bvi
    81  			var bvi bool
    82  			if iface.SwIfIndex == bdDetails.BviSwIfIndex {
    83  				bvi = true
    84  			}
    85  			// add interface entry
    86  			bdData.Bd.Interfaces = append(bdData.Bd.Interfaces, &l2.BridgeDomain_Interface{
    87  				Name:                    ifaceName,
    88  				BridgedVirtualInterface: bvi,
    89  				SplitHorizonGroup:       uint32(iface.Shg),
    90  			})
    91  		}
    92  
    93  		// Add ARP termination entries.
    94  		arpTable, ok := bdArpTab[bdDetails.BdID]
    95  		if ok {
    96  			bdData.Bd.ArpTerminationTable = arpTable
    97  		}
    98  
    99  		bds = append(bds, bdData)
   100  	}
   101  
   102  	return bds, nil
   103  }
   104  
   105  // Reads ARP termination table from all bridge domains. Result is then added to bridge domains.
   106  func (h *BridgeDomainVppHandler) dumpBridgeDomainMacTable() (map[uint32][]*l2.BridgeDomain_ArpTerminationEntry, error) {
   107  	bdArpTable := make(map[uint32][]*l2.BridgeDomain_ArpTerminationEntry)
   108  	req := &vpp_l2.BdIPMacDump{BdID: ^uint32(0)}
   109  
   110  	reqCtx := h.callsChannel.SendMultiRequest(req)
   111  	for {
   112  		msg := &vpp_l2.BdIPMacDetails{}
   113  		stop, err := reqCtx.ReceiveReply(msg)
   114  		if err != nil {
   115  			return nil, err
   116  		}
   117  		if stop {
   118  			break
   119  		}
   120  
   121  		// Prepare ARP entry
   122  		arpEntry := &l2.BridgeDomain_ArpTerminationEntry{}
   123  		arpEntry.IpAddress = parseAddressToString(msg.Entry.IP)
   124  		arpEntry.PhysAddress = net.HardwareAddr(msg.Entry.Mac[:]).String()
   125  
   126  		// Add ARP entry to result map
   127  		bdArpTable[msg.Entry.BdID] = append(bdArpTable[msg.Entry.BdID], arpEntry)
   128  	}
   129  
   130  	return bdArpTable, nil
   131  }
   132  
   133  // DumpL2FIBs dumps VPP L2 FIB table entries into the northbound API
   134  // data structure map indexed by destination MAC address.
   135  func (h *FIBVppHandler) DumpL2FIBs() (map[string]*vppcalls.FibTableDetails, error) {
   136  	// map for the resulting FIBs
   137  	fibs := make(map[string]*vppcalls.FibTableDetails)
   138  
   139  	reqCtx := h.callsChannel.SendMultiRequest(&vpp_l2.L2FibTableDump{BdID: ^uint32(0)})
   140  	for {
   141  		fibDetails := &vpp_l2.L2FibTableDetails{}
   142  		stop, err := reqCtx.ReceiveReply(fibDetails)
   143  		if stop {
   144  			break // Break from the loop.
   145  		}
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  
   150  		mac := net.HardwareAddr(fibDetails.Mac[:]).String()
   151  		var action l2.FIBEntry_Action
   152  		if fibDetails.FilterMac {
   153  			action = l2.FIBEntry_DROP
   154  		} else {
   155  			action = l2.FIBEntry_FORWARD
   156  		}
   157  
   158  		// Interface name (only for FORWARD entries)
   159  		var ifName string
   160  		if action == l2.FIBEntry_FORWARD {
   161  			var exists bool
   162  			ifName, _, exists = h.ifIndexes.LookupBySwIfIndex(uint32(fibDetails.SwIfIndex))
   163  			if !exists {
   164  				h.log.Warnf("FIB dump: interface name for index %d not found", fibDetails.SwIfIndex)
   165  				continue
   166  			}
   167  		}
   168  		// Bridge domain name
   169  		bdName, _, exists := h.bdIndexes.LookupByIndex(fibDetails.BdID)
   170  		if !exists {
   171  			h.log.Warnf("FIB dump: bridge domain name for index %d not found", fibDetails.BdID)
   172  			continue
   173  		}
   174  
   175  		fibs[mac] = &vppcalls.FibTableDetails{
   176  			Fib: &l2.FIBEntry{
   177  				PhysAddress:             mac,
   178  				BridgeDomain:            bdName,
   179  				Action:                  action,
   180  				OutgoingInterface:       ifName,
   181  				StaticConfig:            fibDetails.StaticMac,
   182  				BridgedVirtualInterface: fibDetails.BviMac,
   183  			},
   184  			Meta: &vppcalls.FibMeta{
   185  				BdID:  fibDetails.BdID,
   186  				IfIdx: uint32(fibDetails.SwIfIndex),
   187  			},
   188  		}
   189  	}
   190  
   191  	return fibs, nil
   192  }
   193  
   194  // DumpXConnectPairs implements xconnect handler.
   195  func (h *XConnectVppHandler) DumpXConnectPairs() (map[uint32]*vppcalls.XConnectDetails, error) {
   196  	// map for the resulting xconnect pairs
   197  	xpairs := make(map[uint32]*vppcalls.XConnectDetails)
   198  	reqCtx := h.callsChannel.SendMultiRequest(&vpp_l2.L2XconnectDump{})
   199  	for {
   200  		pairs := &vpp_l2.L2XconnectDetails{}
   201  		stop, err := reqCtx.ReceiveReply(pairs)
   202  		if stop {
   203  			break
   204  		}
   205  		if err != nil {
   206  			return nil, err
   207  		}
   208  
   209  		// Find interface names
   210  		rxIfaceName, _, exists := h.ifIndexes.LookupBySwIfIndex(uint32(pairs.RxSwIfIndex))
   211  		if !exists {
   212  			h.log.Warnf("XConnect dump: rx interface name for index %d not found", pairs.RxSwIfIndex)
   213  			continue
   214  		}
   215  		txIfaceName, _, exists := h.ifIndexes.LookupBySwIfIndex(uint32(pairs.TxSwIfIndex))
   216  		if !exists {
   217  			h.log.Warnf("XConnect dump: tx interface name for index %d not found", pairs.TxSwIfIndex)
   218  			continue
   219  		}
   220  
   221  		xpairs[uint32(pairs.RxSwIfIndex)] = &vppcalls.XConnectDetails{
   222  			Xc: &l2.XConnectPair{
   223  				ReceiveInterface:  rxIfaceName,
   224  				TransmitInterface: txIfaceName,
   225  			},
   226  			Meta: &vppcalls.XcMeta{
   227  				ReceiveInterfaceSwIfIdx:  uint32(pairs.RxSwIfIndex),
   228  				TransmitInterfaceSwIfIdx: uint32(pairs.TxSwIfIndex),
   229  			},
   230  		}
   231  	}
   232  	return xpairs, nil
   233  }
   234  
   235  func parseAddressToString(address ip_types.Address) string {
   236  	var nhIP net.IP = make([]byte, 16)
   237  	copy(nhIP[:], address.Un.XXX_UnionData[:])
   238  	if address.Af == ip_types.ADDRESS_IP4 {
   239  		return nhIP[:4].To4().String()
   240  	}
   241  	if address.Af == ip_types.ADDRESS_IP6 {
   242  		return nhIP.To16().String()
   243  	}
   244  	return ""
   245  }