func (v *Validator) validateRemoteNodeRoutes(node *telemetrymodel.Node, vrfMap VrfMap, routeMap RouteMap) int {
	numErrs := 0
	_, err := findInterface(ipnet.DefaultVxlanBVIInterfaceName, node.NodeInterfaces)
	if err != nil {
		numErrs++
		errString := fmt.Sprintf("local vxlanBVI lookup failed, error %s; "+
			"unable to validate routes to remote nodes", err)
		v.Report.AppendToNodeReport(node.Name, errString)
		return numErrs
	}
	nodeList := v.VppCache.RetrieveAllNodes()
	for _, othNode := range nodeList {
		if othNode.NodeIPam == nil {
			numErrs++
			errString := fmt.Sprintf("missing IPAM for node %s - routes to the node not validated",
				othNode.Name)
			v.Report.AppendToNodeReport(node.Name, errString)
			continue
		}
		if othNode.Name == node.Name {
			numErrs += v.validateRoute(othNode.NodeIPam.VppHostNetwork, 1, vrfMap, routeMap, node.Name,
				"", "0.0.0.0", 0, vpp_l3.Route_INTER_VRF)
			continue
		}
		ifc, err := findInterface(ipnet.DefaultVxlanBVIInterfaceName, othNode.NodeInterfaces)
		if err != nil {
			numErrs++
			errString := fmt.Sprintf("failed to validate route %s VRF%d - "+
				"failed lookup for vxlanBVI for node %s, error %s", othNode.ManIPAddr+"/32", 0, othNode.Name, err)
			v.Report.AppendToNodeReport(node.Name, errString)
			continue
		}
		bviAddr, err := checkIfRouteNextHopPointsToInterface(othNode.NodeIPam.VppHostNetwork, 1, vrfMap,
			ifc, othNode.Name)
		if err == nil {
			numErrs += v.validateRoute(othNode.NodeIPam.VppHostNetwork, 1, vrfMap, routeMap, node.Name,
				ipnet.DefaultVxlanBVIInterfaceName, bviAddr, 0, vpp_l3.Route_INTRA_VRF)
			numErrs += v.validateRoute(othNode.ManIPAddr+"/32", 0, vrfMap, routeMap, node.Name,
				"", "0.0.0.0", 1, vpp_l3.Route_INTER_VRF)
			numErrs += v.validateRoute(othNode.ManIPAddr+"/32", 1, vrfMap, routeMap, node.Name,
				ipnet.DefaultVxlanBVIInterfaceName, bviAddr, 0, vpp_l3.Route_INTRA_VRF)
		} else {
			numErrs++
			v.Report.AppendToNodeReport(node.Name, err.Error())
		}
		bviAddr, err = checkIfRouteNextHopPointsToInterface(othNode.NodeIPam.PodSubnetThisNode, 1, vrfMap,
			ifc, othNode.Name)
		if err == nil {
			numErrs += v.validateRoute(othNode.NodeIPam.PodSubnetThisNode, 1, vrfMap, routeMap, node.Name,
				ipnet.DefaultVxlanBVIInterfaceName, bviAddr, 0, vpp_l3.Route_INTRA_VRF)
		} else {
			numErrs++
			v.Report.AppendToNodeReport(node.Name, err.Error())
		}
	}
	return numErrs
}
validateRemoteNodeRoutes validates routes to Host subnets on both the local
node and the remote nodes and toPod subnets on remote nodes.
package l3
import (
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"
	"text/tabwriter"
	"go.ligato.io/cn-infra/v2/logging"
	vpp_l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3"
	"github.com/contiv/vpp/plugins/crd/api"
	"github.com/contiv/vpp/plugins/crd/cache/telemetrymodel"
	"github.com/contiv/vpp/plugins/crd/validator/utils"
	"github.com/contiv/vpp/plugins/ipnet"
)
const (
	routeNotValidated = iota
	routeInvalid      = iota
	routeValid        = iota
	gigENameMatch = `GigabitEthernet[[:alnum:]]*/[[:alnum:]]*/[[:alnum:]]*`
	tap2HostName  = "tap-vpp2"
	hostToVppIfName = "vpp1"
	vppToHostIfName = "tap-vpp2"
)
var (
	v4CidrRe = regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).*`)
)
type Validator struct {
	Log logging.Logger
	VppCache api.VppCache
	K8sCache api.K8sCache
	Report   api.Report
}
type Vrf map[string]telemetrymodel.NodeIPRoute
type VrfMap map[uint32]Vrf
type RouteMap map[uint32]map[string]int
type LinuxIfMap map[string]string
func (v *Validator) Validate() {
	nodeList := v.VppCache.RetrieveAllNodes()
	numErrs := 0
	v.Report.SetPrefix("L3-FIB")
	for _, node := range nodeList {
		if node.NodeIPam == nil {
			v.Log.Infof("No IPAM data for node %s", node.Name)
			v.Report.AppendToNodeReport(node.Name, "validation skipped - no IPAM data available")
			continue
		}
		if node.NodeInterfaces == nil {
			v.Log.Infof("validation aborted - no interfaces data available")
			v.Report.AppendToNodeReport(node.Name, "validation skipped - no interface data available")
			continue
		}
		if node.NodeStaticRoutes == nil {
			v.Log.Infof("validation aborted - no route data available")
			v.Report.AppendToNodeReport(node.Name, "validation skipped - no route data available")
			continue
		}
		vrfMap, err := v.createVrfMap(node)
		if err != nil {
			v.Report.AppendToNodeReport(node.Name, err.Error())
		}
		routeMap := v.createValidationMap(vrfMap)
		numErrs += v.validateVrf1PodRoutes(node, vrfMap, routeMap)
		numErrs += v.validateRouteToLocalVxlanBVI(node, vrfMap, routeMap)
		numErrs += v.validateVrf0GigERoutes(node, vrfMap, routeMap)
		numErrs += v.validateVrf0LocalHostRoute(node, vrfMap, routeMap)
		numErrs += v.validateDefaultRoutes(node, vrfMap, routeMap)
		numErrs += v.validateRemoteNodeRoutes(node, vrfMap, routeMap)
		numErrs += v.validatePodSubnetCidrRoutes(node, vrfMap, routeMap)
		numErrs += v.validateVppHostNetworkRoutes(node, vrfMap, routeMap)
		numErrs += v.validateRoutesToLinuxInterfaces(node, vrfMap, routeMap)
		numErrs += v.checkUnvalidatedRoutes(routeMap, node.Name)
	}
	if numErrs == 0 {
		v.Report.AppendToNodeReport(api.GlobalMsg, "validation OK")
	} else {
		errString := fmt.Sprintf("%d error%s found", numErrs, printS(numErrs))
		v.Report.AppendToNodeReport(api.GlobalMsg, errString)
	}
}
func (v *Validator) createVrfMap(node *telemetrymodel.Node) (VrfMap, error) {
	vrfMap := make(VrfMap, 0)
	for _, route := range node.NodeStaticRoutes {
		vrf, ok := vrfMap[route.Value.VrfId]
		if !ok {
			vrfMap[route.Value.VrfId] = make(Vrf, 0)
			vrf = vrfMap[route.Value.VrfId]
		}
		vrf[route.Value.DstNetwork] = route
	}
	return vrfMap, nil
}
func (v *Validator) createValidationMap(vm map[uint32]Vrf) RouteMap {
	valMap := make(RouteMap, 0)
	for vIdx, vrf := range vm {
		vrfRoutes := make(map[string]int, 0)
		for _, rte := range vrf {
			vrfRoutes[rte.Value.DstNetwork] = routeNotValidated
		}
		valMap[vIdx] = vrfRoutes
	}
	return valMap
}
func (v *Validator) validateVrf1PodRoutes(node *telemetrymodel.Node, vrfMap VrfMap, routeMap RouteMap) int {
	numErrs := 0
	for _, pod := range node.PodMap {
		if pod.IPAddress == node.ManIPAddr {
			continue
		}
		if pod.IPAddress != "" {
			numErrs += v.validateRoute(pod.IPAddress+"/32", 1, vrfMap, routeMap, node.Name,
				pod.VppIfName, pod.IPAddress, 0, vpp_l3.Route_INTRA_VRF)
		} else {
			numErrs++
			errString := fmt.Sprintf("pod '%s': IP Address empty, pod route validation skipped", pod.Name)
			v.Report.AppendToNodeReport(node.Name, errString)
		}
	}
	return numErrs
}
func (v *Validator) validateVrf0GigERoutes(node *telemetrymodel.Node, vrfMap VrfMap, routeMap RouteMap) int {
	numErrs := 0
	ifc, err := findInterface(gigENameMatch, node.NodeInterfaces)
	if err != nil {
		numErrs++
		errString := fmt.Sprintf("local GigE interface not found, error %s", err)
		v.Report.AppendToNodeReport(node.Name, errString)
		return numErrs
	}
	numErrs += v.validateRoute(node.IPAddr, 0, vrfMap, routeMap, node.Name, ifc.Value.Name,
		"0.0.0.0", 0, vpp_l3.Route_INTRA_VRF)
	for _, ipAddr := range ifc.Value.IpAddresses {
		if ipAddr == node.IPAddr {
			numErrs += v.validatePhyNextHopRoutes(ipAddr, 0, vrfMap, routeMap, node.Name,
				ifc, 0, vpp_l3.Route_DROP)
			break
		}
	}
	nodeList := v.VppCache.RetrieveAllNodes()
	for _, remoteNode := range nodeList {
		if remoteNode == node {
			continue
		}
		dstIP := strings.Split(remoteNode.IPAddr, "/")
		numErrs += v.validateRoute(dstIP[0]+"/32", 0, vrfMap, routeMap, node.Name, ifc.Value.Name,
			dstIP[0], 0, vpp_l3.Route_INTRA_VRF)
	}
	return numErrs
}