<commit_before>package descriptor

import (
	"net"

	"github.com/gogo/protobuf/proto"
	"github.com/pkg/errors"

	"github.com/ligato/cn-infra/utils/addrs"

	scheduler "github.com/ligato/vpp-agent/plugins/kvscheduler/api"
	"github.com/ligato/vpp-agent/plugins/vppv2/ifplugin/descriptor/adapter"
	"github.com/ligato/vpp-agent/plugins/vppv2/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/vppv2/model/interfaces"
)

// Add creates a VPP interface.
func (d *InterfaceDescriptor) Add(key string, intf *interfaces.Interface) (metadata *ifaceidx.IfaceMetadata, err error) {
	var ifIdx uint32
	var tapHostIfName string

	// validate the configuration first
	err = d.validateInterfaceConfig(intf)
	if err != nil {
		d.log.Error(err)
		return nil, err
	}

	// create the interface of the given type
	switch intf.Type {
	case interfaces.Interface_TAP:
		tapCfg := getTapConfig(intf)
		tapHostIfName = tapCfg.HostIfName
		ifIdx, err = d.ifHandler.AddTapInterface(intf.Name, tapCfg)
		if err != nil {
			d.log.Error(err)
			return nil, err
		}

		// TAP hardening: verify that the Linux side was created
		if d.linuxIfHandler != nil {
			exists, err := d.linuxIfHandler.InterfaceExists(tapHostIfName)
			if err != nil {
				d.log.Error(err)
				return nil, err
			}
			if !exists {
				err = errors.Errorf("failed to create the Linux side (%s) of the TAP interface %s", tapHostIfName, intf.Name)
				d.log.Error(err)
				return nil, err
			}
		}

	case interfaces.Interface_MEMIF:
		var socketID uint32
		if socketID, err = d.resolveMemifSocketFilename(intf.GetMemif()); err != nil {
			d.log.Error(err)
			return nil, err
		}
		ifIdx, err = d.ifHandler.AddMemifInterface(intf.Name, intf.GetMemif(), socketID)
		if err != nil {
			d.log.Error(err)
			return nil, err
		}

	case interfaces.Interface_VXLAN_TUNNEL:
		var multicastIfIdx uint32
		multicastIf := intf.GetVxlan().GetMulticast()
		if multicastIf != "" {
			multicastMeta, found := d.intfIndex.LookupByName(multicastIf)
			if !found {
				err = errors.Errorf("failed to find multicast interface %s referenced by VXLAN %s",
					multicastIf, intf.Name)
				d.log.Error(err)
				return nil, err
			}
			multicastIfIdx = multicastMeta.SwIfIndex
		}
		ifIdx, err = d.ifHandler.AddVxLanTunnel(intf.Name, intf.GetVrf(), multicastIfIdx, intf.GetVxlan())
		if err != nil {
			d.log.Error(err)
			return nil, err
		}

	case interfaces.Interface_SOFTWARE_LOOPBACK:
		ifIdx, err = d.ifHandler.AddLoopbackInterface(intf.Name)
		if err != nil {
			d.log.Error(err)
			return nil, err
		}

	case interfaces.Interface_DPDK:
		var found bool
		ifIdx, found = d.ethernetIfs[intf.Name]
		if !found {
			err = errors.Errorf("failed to find physical interface %s", intf.Name)
			d.log.Error(err)
			return nil, err
		}

	case interfaces.Interface_AF_PACKET:
		ifIdx, err = d.ifHandler.AddAfPacketInterface(intf.Name, intf.GetPhysAddress(), intf.GetAfpacket())
		if err != nil {
			d.log.Error(err)
			return nil, err
		}
	case interfaces.Interface_IPSEC_TUNNEL:
		ifIdx, err = d.ifHandler.AddIPSecTunnelInterface(intf.Name, intf.GetIpsec())
		if err != nil {
			d.log.Error(err)
			return nil, err
		}
	case interfaces.Interface_SUB_INTERFACE:
		sub := intf.GetSub()
		parentMeta, found := d.intfIndex.LookupByName(sub.GetParentName())
		if !found {
			err = errors.Errorf("unable to find parent interface %s referenced by sub interface %s",
				sub.GetParentName(), intf.Name)
			d.log.Error(err)
			return nil, err
		}
		ifIdx, err = d.ifHandler.CreateSubif(parentMeta.SwIfIndex, sub.GetSubId())
		if err != nil {
			d.log.Error(err)
			return nil, err
		}
		err = d.ifHandler.SetInterfaceTag(intf.Name, ifIdx)
		if err != nil {
			d.log.Error(err)
			return nil, err
		}
	}

	/*
		Rx-mode

		Legend:
		P - polling
		I - interrupt
		A - adaptive

		Interfaces - supported modes:
			* tap interface - PIA
			* memory interface - PIA
			* vxlan tunnel - PIA
			* software loopback - PIA
			* ethernet csmad - P
			* af packet - PIA
	*/
	if intf.RxModeSettings != nil {
		rxMode := getRxMode(intf)
		err = d.ifHandler.SetRxMode(ifIdx, rxMode)
		if err != nil {
			err = errors.Errorf("failed to set Rx-mode for interface %s: %v", intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// rx-placement
	if intf.GetRxPlacementSettings() != nil {
		if err = d.ifHandler.SetRxPlacement(ifIdx, intf.GetRxPlacementSettings()); err != nil {
			err = errors.Errorf("failed to set rx-placement for interface %s: %v", intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// MAC address. Note: af-packet uses HwAddr from the host and physical interfaces cannot have the MAC address changed
	if intf.GetPhysAddress() != "" &&
		intf.GetType() != interfaces.Interface_AF_PACKET &&
		intf.GetType() != interfaces.Interface_DPDK {
		if err = d.ifHandler.SetInterfaceMac(ifIdx, intf.GetPhysAddress()); err != nil {
			err = errors.Errorf("failed to set MAC address %s to interface %s: %v",
				intf.GetPhysAddress(), intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// convert IP addresses to net.IPNet
	ipAddresses, err := addrs.StrAddrsToStruct(intf.IpAddresses)
	if err != nil {
		err = errors.Errorf("failed to convert %s IP address list to IPNet structures: %v", intf.Name, err)
		d.log.Error(err)
		return nil, err
	}

	// VRF (optional, unavailable for VxLAN interfaces), should be done before IP addresses are configured
	if intf.GetType() != interfaces.Interface_VXLAN_TUNNEL {
		// Configured separately for IPv4/IPv6
		isIPv4, isIPv6 := getIPAddressVersions(ipAddresses)
		if isIPv4 {
			if err = d.ifHandler.SetInterfaceVrf(ifIdx, intf.Vrf); err != nil {
				err = errors.Errorf("failed to set interface %s as IPv4 VRF %d: %v", intf.Name, intf.Vrf, err)
				d.log.Error(err)
				return nil, err
			}
		}
		if isIPv6 {
			if err := d.ifHandler.SetInterfaceVrfIPv6(ifIdx, intf.Vrf); err != nil {
				err = errors.Errorf("failed to set interface %s as IPv6 VRF %d: %v", intf.Name, intf.Vrf, err)
				d.log.Error(err)
				return nil, err
			}
		}
	}

	// configure IP addresses
	for _, address := range ipAddresses {
		if err := d.ifHandler.AddInterfaceIP(ifIdx, address); err != nil {
			err = errors.Errorf("adding IP address %v to interface %v failed: %v", address, intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// configure MTU. Prefer value in the interface config, otherwise set the plugin-wide
	// default value if provided.
	if intf.Type != interfaces.Interface_VXLAN_TUNNEL && intf.Type != interfaces.Interface_IPSEC_TUNNEL {
		mtuToConfigure := intf.Mtu
		if mtuToConfigure == 0 && d.defaultMtu != 0 {
			mtuToConfigure = d.defaultMtu
		}
		if mtuToConfigure != 0 {
			if err = d.ifHandler.SetInterfaceMtu(ifIdx, mtuToConfigure); err != nil {
				err = errors.Errorf("failed to set MTU %d to interface %s: %v", mtuToConfigure, intf.Name, err)
				d.log.Error(err)
				return nil, err
			}
		}
	}

	// set interface up if enabled
	if intf.Enabled {
		if err = d.ifHandler.InterfaceAdminUp(ifIdx); err != nil {
			err = errors.Errorf("failed to set interface %s up: %v", intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// fill the metadata
	metadata = &ifaceidx.IfaceMetadata{
		SwIfIndex:     ifIdx,
		IPAddresses:   intf.GetIpAddresses(),
		TAPHostIfName: tapHostIfName,
	}
	return metadata, nil
}

// Delete removes VPP interface.
func (d *InterfaceDescriptor) Delete(key string, intf *interfaces.Interface, metadata *ifaceidx.IfaceMetadata) error {
	ifIdx := metadata.SwIfIndex

	// set interface to ADMIN_DOWN unless the type is AF_PACKET_INTERFACE
	if intf.Type != interfaces.Interface_AF_PACKET {
		if err := d.ifHandler.InterfaceAdminDown(ifIdx); err != nil {
			err = errors.Errorf("failed to set interface %s down: %v", intf.Name, err)
			d.log.Error(err)
			return err
		}
	}

	// remove IP addresses from the interface
	var nonLocalIPs []string
	for _, ipAddr := range intf.IpAddresses {
		ip := net.ParseIP(ipAddr)
		if !ip.IsLinkLocalUnicast() {
			nonLocalIPs = append(nonLocalIPs, ipAddr)
		}
	}
	ipAddrs, err := addrs.StrAddrsToStruct(nonLocalIPs)
	if err != nil {
		err = errors.Errorf("failed to convert %s IP address list to IPNet structures: %v", intf.Name, err)
		d.log.Error(err)
		return err
	}
	for _, ipAddr := range ipAddrs {
		if err = d.ifHandler.DelInterfaceIP(ifIdx, ipAddr); err != nil {
			err = errors.Errorf("failed to remove IP address %s from interface %s: %v",
				ipAddr, intf.Name, err)
			d.log.Error(err)
			return err
		}
	}

	// remove the interface
	switch intf.Type {
	case interfaces.Interface_TAP:
		err = d.ifHandler.DeleteTapInterface(intf.Name, ifIdx, intf.GetTap().GetVersion())
	case interfaces.Interface_MEMIF:
		err = d.ifHandler.DeleteMemifInterface(intf.Name, ifIdx)
	case interfaces.Interface_VXLAN_TUNNEL:
		err = d.ifHandler.DeleteVxLanTunnel(intf.Name, ifIdx, intf.Vrf, intf.GetVxlan())
	case interfaces.Interface_SOFTWARE_LOOPBACK:
		err = d.ifHandler.DeleteLoopbackInterface(intf.Name, ifIdx)
	case interfaces.Interface_DPDK:
		d.log.Debugf("Interface %s removal skipped: cannot remove (blacklist) physical interface", intf.Name) // Not an error
		return nil
	case interfaces.Interface_AF_PACKET:
		err = d.ifHandler.DeleteAfPacketInterface(intf.Name, ifIdx, intf.GetAfpacket())
	case interfaces.Interface_IPSEC_TUNNEL:
		err = d.ifHandler.DeleteIPSecTunnelInterface(intf.Name, intf.GetIpsec())
	case interfaces.Interface_SUB_INTERFACE:
		err = d.ifHandler.DeleteSubif(ifIdx)
	}
	if err != nil {
		err = errors.Errorf("failed to remove interface %s, index %d: %v", intf.Name, ifIdx, err)
		d.log.Error(err)
		return err
	}

	return nil
}

// Modify is able to change Type-unspecific attributes.
func (d *InterfaceDescriptor) Modify(key string, oldIntf, newIntf *interfaces.Interface, oldMetadata *ifaceidx.IfaceMetadata) (newMetadata *ifaceidx.IfaceMetadata, err error) {
	// validate the new configuration first
	err = d.validateInterfaceConfig(newIntf)
	if err != nil {
		d.log.Error(err)
		return nil, err
	}

	ifIdx := oldMetadata.SwIfIndex

	// rx-mode
	oldRx := getRxMode(oldIntf)
	newRx := getRxMode(newIntf)
	if !proto.Equal(oldRx, newRx) {
		err = d.ifHandler.SetRxMode(ifIdx, newRx)
		if err != nil {
			err = errors.Errorf("failed to modify rx-mode for interface %s: %v", newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// rx-placement
	if !proto.Equal(getRxPlacement(oldIntf), getRxPlacement(newIntf)) {
		if err = d.ifHandler.SetRxPlacement(ifIdx, newIntf.GetRxPlacementSettings()); err != nil {
			err = errors.Errorf("failed to modify rx-placement for interface %s: %v", newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// admin status
	if newIntf.Enabled != oldIntf.Enabled {
		if newIntf.Enabled {
			if err = d.ifHandler.InterfaceAdminUp(ifIdx); err != nil {
				err = errors.Errorf("failed to set interface %s up: %v", newIntf.Name, err)
				d.log.Error(err)
				return oldMetadata, err
			}
		} else {
			if err = d.ifHandler.InterfaceAdminDown(ifIdx); err != nil {
				err = errors.Errorf("failed to set interface %s down: %v", newIntf.Name, err)
				d.log.Error(err)
				return oldMetadata, err
			}
		}
	}

	// configure new MAC address if set (and only if it was changed and only for supported interface type)
	if newIntf.PhysAddress != "" &&
		newIntf.PhysAddress != oldIntf.PhysAddress &&
		oldIntf.Type != interfaces.Interface_AF_PACKET &&
		oldIntf.Type != interfaces.Interface_DPDK {
		if err := d.ifHandler.SetInterfaceMac(ifIdx, newIntf.PhysAddress); err != nil {
			err = errors.Errorf("setting interface %s MAC address %s failed: %v",
				newIntf.Name, newIntf.PhysAddress, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// calculate diff of IP addresses
	newIPAddresses, err := addrs.StrAddrsToStruct(newIntf.IpAddresses)
	if err != nil {
		err = errors.Errorf("failed to convert %s IP address list to IPNet structures: %v", newIntf.Name, err)
		d.log.Error(err)
		return oldMetadata, err
	}
	oldIPAddresses, err := addrs.StrAddrsToStruct(oldIntf.IpAddresses)
	if err != nil {
		err = errors.Errorf("failed to convert %s IP address list to IPNet structures: %v", oldIntf.Name, err)
		d.log.Error(err)
		return oldMetadata, err
	}
	del, add := addrs.DiffAddr(newIPAddresses, oldIPAddresses)

	// delete obsolete IP addresses
	for _, address := range del {
		err := d.ifHandler.DelInterfaceIP(ifIdx, address)
		if nil != err {
			err = errors.Errorf("failed to remove obsolete IP address %v from interface %s: %v",
				address, newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// add new IP addresses
	for _, address := range add {
		err := d.ifHandler.AddInterfaceIP(ifIdx, address)
		if nil != err {
			err = errors.Errorf("failed to add new IP addresses %v to interface %s: %v",
				address, newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// update IP addresses in the metadata
	oldMetadata.IPAddresses = newIntf.IpAddresses

	// update MTU
	if newIntf.Mtu != 0 && newIntf.Mtu != oldIntf.Mtu {
		if err := d.ifHandler.SetInterfaceMtu(ifIdx, newIntf.Mtu); err != nil {
			err = errors.Errorf("failed to set MTU to interface %s: %v", newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	} else if newIntf.Mtu == 0 && d.defaultMtu != 0 {
		if err := d.ifHandler.SetInterfaceMtu(ifIdx, d.defaultMtu); err != nil {
			err = errors.Errorf("failed to set MTU to interface %s: %v", newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	return oldMetadata, nil
}

// Dump returns all configured VPP interfaces.
func (d *InterfaceDescriptor) Dump(correlate []adapter.InterfaceKVWithMetadata) (dump []adapter.InterfaceKVWithMetadata, err error) {
	// convert interfaces for correlation into a map
	ifCfg := make(map[string]*interfaces.Interface) // interface logical name -> interface config (as expected by correlate)
	for _, kv := range correlate {
		ifCfg[kv.Value.Name] = kv.Value
	}

	// refresh the map of memif socket IDs
	d.memifSocketToID, err = d.ifHandler.DumpMemifSocketDetails()
	if err != nil {
		err = errors.Errorf("failed to dump memif socket details: %v", err)
		d.log.Error(err)
		return dump, err
	}
	for socketPath, socketID := range d.memifSocketToID {
		if socketID == 0 {
			d.defaultMemifSocketPath = socketPath
		}
	}

	// clear the map of ethernet interfaces
	d.ethernetIfs = make(map[string]uint32)

	// dump current state of VPP interfaces
	vppIfs, err := d.ifHandler.DumpInterfaces()
	if err != nil {
		err = errors.Errorf("failed to dump interfaces: %v", err)
		d.log.Error(err)
		return dump, err
	}

	for ifIdx, intf := range vppIfs {
		origin := scheduler.FromNB
		if ifIdx == 0 {
			// local0 is created automatically
			origin = scheduler.FromSB
		}
		if intf.Interface.Type == interfaces.Interface_DPDK {
			d.ethernetIfs[intf.Interface.Name] = ifIdx
			if !intf.Interface.Enabled && len(intf.Interface.IpAddresses) == 0 {
				// unconfigured physical interface => skip (but add entry to d.ethernetIfs)
				continue
			}
		}
		if intf.Interface.Name == "" {
			// untagged interface - generate a logical name for it
			// (apart from local0 it will get removed by resync)
			intf.Interface.Name = untaggedIfPreffix + intf.Meta.InternalName
		}

		// get TAP host interface name
		var tapHostIfName string
		if intf.Interface.Type == interfaces.Interface_TAP {
			tapHostIfName = intf.Interface.GetTap().GetHostIfName()
			if generateTAPHostName(intf.Interface.Name) == tapHostIfName {
				// interface host name was unset
				intf.Interface.GetTap().HostIfName = ""
			}
		}

		// correlate attributes that cannot be dumped
		if expCfg, hasExpCfg := ifCfg[intf.Interface.Name]; hasExpCfg {
			if expCfg.Type == interfaces.Interface_TAP && intf.Interface.GetTap() != nil {
				intf.Interface.GetTap().ToMicroservice = expCfg.GetTap().GetToMicroservice()
				intf.Interface.GetTap().RxRingSize = expCfg.GetTap().GetRxRingSize()
				intf.Interface.GetTap().TxRingSize = expCfg.GetTap().GetTxRingSize()
			}<commit_msg>Temporary workaround for TAPv2 hostIfName dump issue.<commit_after>package descriptor

import (
	"net"

	"github.com/gogo/protobuf/proto"
	"github.com/pkg/errors"

	"github.com/ligato/cn-infra/utils/addrs"

	scheduler "github.com/ligato/vpp-agent/plugins/kvscheduler/api"
	"github.com/ligato/vpp-agent/plugins/vppv2/ifplugin/descriptor/adapter"
	"github.com/ligato/vpp-agent/plugins/vppv2/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/vppv2/model/interfaces"
)

// Add creates a VPP interface.
func (d *InterfaceDescriptor) Add(key string, intf *interfaces.Interface) (metadata *ifaceidx.IfaceMetadata, err error) {
	var ifIdx uint32
	var tapHostIfName string

	// validate the configuration first
	err = d.validateInterfaceConfig(intf)
	if err != nil {
		d.log.Error(err)
		return nil, err
	}

	// create the interface of the given type
	switch intf.Type {
	case interfaces.Interface_TAP:
		tapCfg := getTapConfig(intf)
		tapHostIfName = tapCfg.HostIfName
		ifIdx, err = d.ifHandler.AddTapInterface(intf.Name, tapCfg)
		if err != nil {
			d.log.Error(err)
			return nil, err
		}

		// TAP hardening: verify that the Linux side was created
		if d.linuxIfHandler != nil {
			exists, err := d.linuxIfHandler.InterfaceExists(tapHostIfName)
			if err != nil {
				d.log.Error(err)
				return nil, err
			}
			if !exists {
				err = errors.Errorf("failed to create the Linux side (%s) of the TAP interface %s", tapHostIfName, intf.Name)
				d.log.Error(err)
				return nil, err
			}
		}

	case interfaces.Interface_MEMIF:
		var socketID uint32
		if socketID, err = d.resolveMemifSocketFilename(intf.GetMemif()); err != nil {
			d.log.Error(err)
			return nil, err
		}
		ifIdx, err = d.ifHandler.AddMemifInterface(intf.Name, intf.GetMemif(), socketID)
		if err != nil {
			d.log.Error(err)
			return nil, err
		}

	case interfaces.Interface_VXLAN_TUNNEL:
		var multicastIfIdx uint32
		multicastIf := intf.GetVxlan().GetMulticast()
		if multicastIf != "" {
			multicastMeta, found := d.intfIndex.LookupByName(multicastIf)
			if !found {
				err = errors.Errorf("failed to find multicast interface %s referenced by VXLAN %s",
					multicastIf, intf.Name)
				d.log.Error(err)
				return nil, err
			}
			multicastIfIdx = multicastMeta.SwIfIndex
		}
		ifIdx, err = d.ifHandler.AddVxLanTunnel(intf.Name, intf.GetVrf(), multicastIfIdx, intf.GetVxlan())
		if err != nil {
			d.log.Error(err)
			return nil, err
		}

	case interfaces.Interface_SOFTWARE_LOOPBACK:
		ifIdx, err = d.ifHandler.AddLoopbackInterface(intf.Name)
		if err != nil {
			d.log.Error(err)
			return nil, err
		}

	case interfaces.Interface_DPDK:
		var found bool
		ifIdx, found = d.ethernetIfs[intf.Name]
		if !found {
			err = errors.Errorf("failed to find physical interface %s", intf.Name)
			d.log.Error(err)
			return nil, err
		}

	case interfaces.Interface_AF_PACKET:
		ifIdx, err = d.ifHandler.AddAfPacketInterface(intf.Name, intf.GetPhysAddress(), intf.GetAfpacket())
		if err != nil {
			d.log.Error(err)
			return nil, err
		}
	case interfaces.Interface_IPSEC_TUNNEL:
		ifIdx, err = d.ifHandler.AddIPSecTunnelInterface(intf.Name, intf.GetIpsec())
		if err != nil {
			d.log.Error(err)
			return nil, err
		}
	case interfaces.Interface_SUB_INTERFACE:
		sub := intf.GetSub()
		parentMeta, found := d.intfIndex.LookupByName(sub.GetParentName())
		if !found {
			err = errors.Errorf("unable to find parent interface %s referenced by sub interface %s",
				sub.GetParentName(), intf.Name)
			d.log.Error(err)
			return nil, err
		}
		ifIdx, err = d.ifHandler.CreateSubif(parentMeta.SwIfIndex, sub.GetSubId())
		if err != nil {
			d.log.Error(err)
			return nil, err
		}
		err = d.ifHandler.SetInterfaceTag(intf.Name, ifIdx)
		if err != nil {
			d.log.Error(err)
			return nil, err
		}
	}

	/*
		Rx-mode

		Legend:
		P - polling
		I - interrupt
		A - adaptive

		Interfaces - supported modes:
			* tap interface - PIA
			* memory interface - PIA
			* vxlan tunnel - PIA
			* software loopback - PIA
			* ethernet csmad - P
			* af packet - PIA
	*/
	if intf.RxModeSettings != nil {
		rxMode := getRxMode(intf)
		err = d.ifHandler.SetRxMode(ifIdx, rxMode)
		if err != nil {
			err = errors.Errorf("failed to set Rx-mode for interface %s: %v", intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// rx-placement
	if intf.GetRxPlacementSettings() != nil {
		if err = d.ifHandler.SetRxPlacement(ifIdx, intf.GetRxPlacementSettings()); err != nil {
			err = errors.Errorf("failed to set rx-placement for interface %s: %v", intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// MAC address. Note: af-packet uses HwAddr from the host and physical interfaces cannot have the MAC address changed
	if intf.GetPhysAddress() != "" &&
		intf.GetType() != interfaces.Interface_AF_PACKET &&
		intf.GetType() != interfaces.Interface_DPDK {
		if err = d.ifHandler.SetInterfaceMac(ifIdx, intf.GetPhysAddress()); err != nil {
			err = errors.Errorf("failed to set MAC address %s to interface %s: %v",
				intf.GetPhysAddress(), intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// convert IP addresses to net.IPNet
	ipAddresses, err := addrs.StrAddrsToStruct(intf.IpAddresses)
	if err != nil {
		err = errors.Errorf("failed to convert %s IP address list to IPNet structures: %v", intf.Name, err)
		d.log.Error(err)
		return nil, err
	}

	// VRF (optional, unavailable for VxLAN interfaces), should be done before IP addresses are configured
	if intf.GetType() != interfaces.Interface_VXLAN_TUNNEL {
		// Configured separately for IPv4/IPv6
		isIPv4, isIPv6 := getIPAddressVersions(ipAddresses)
		if isIPv4 {
			if err = d.ifHandler.SetInterfaceVrf(ifIdx, intf.Vrf); err != nil {
				err = errors.Errorf("failed to set interface %s as IPv4 VRF %d: %v", intf.Name, intf.Vrf, err)
				d.log.Error(err)
				return nil, err
			}
		}
		if isIPv6 {
			if err := d.ifHandler.SetInterfaceVrfIPv6(ifIdx, intf.Vrf); err != nil {
				err = errors.Errorf("failed to set interface %s as IPv6 VRF %d: %v", intf.Name, intf.Vrf, err)
				d.log.Error(err)
				return nil, err
			}
		}
	}

	// configure IP addresses
	for _, address := range ipAddresses {
		if err := d.ifHandler.AddInterfaceIP(ifIdx, address); err != nil {
			err = errors.Errorf("adding IP address %v to interface %v failed: %v", address, intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// configure MTU. Prefer value in the interface config, otherwise set the plugin-wide
	// default value if provided.
	if intf.Type != interfaces.Interface_VXLAN_TUNNEL && intf.Type != interfaces.Interface_IPSEC_TUNNEL {
		mtuToConfigure := intf.Mtu
		if mtuToConfigure == 0 && d.defaultMtu != 0 {
			mtuToConfigure = d.defaultMtu
		}
		if mtuToConfigure != 0 {
			if err = d.ifHandler.SetInterfaceMtu(ifIdx, mtuToConfigure); err != nil {
				err = errors.Errorf("failed to set MTU %d to interface %s: %v", mtuToConfigure, intf.Name, err)
				d.log.Error(err)
				return nil, err
			}
		}
	}

	// set interface up if enabled
	if intf.Enabled {
		if err = d.ifHandler.InterfaceAdminUp(ifIdx); err != nil {
			err = errors.Errorf("failed to set interface %s up: %v", intf.Name, err)
			d.log.Error(err)
			return nil, err
		}
	}

	// fill the metadata
	metadata = &ifaceidx.IfaceMetadata{
		SwIfIndex:     ifIdx,
		IPAddresses:   intf.GetIpAddresses(),
		TAPHostIfName: tapHostIfName,
	}
	return metadata, nil
}

// Delete removes VPP interface.
func (d *InterfaceDescriptor) Delete(key string, intf *interfaces.Interface, metadata *ifaceidx.IfaceMetadata) error {
	ifIdx := metadata.SwIfIndex

	// set interface to ADMIN_DOWN unless the type is AF_PACKET_INTERFACE
	if intf.Type != interfaces.Interface_AF_PACKET {
		if err := d.ifHandler.InterfaceAdminDown(ifIdx); err != nil {
			err = errors.Errorf("failed to set interface %s down: %v", intf.Name, err)
			d.log.Error(err)
			return err
		}
	}

	// remove IP addresses from the interface
	var nonLocalIPs []string
	for _, ipAddr := range intf.IpAddresses {
		ip := net.ParseIP(ipAddr)
		if !ip.IsLinkLocalUnicast() {
			nonLocalIPs = append(nonLocalIPs, ipAddr)
		}
	}
	ipAddrs, err := addrs.StrAddrsToStruct(nonLocalIPs)
	if err != nil {
		err = errors.Errorf("failed to convert %s IP address list to IPNet structures: %v", intf.Name, err)
		d.log.Error(err)
		return err
	}
	for _, ipAddr := range ipAddrs {
		if err = d.ifHandler.DelInterfaceIP(ifIdx, ipAddr); err != nil {
			err = errors.Errorf("failed to remove IP address %s from interface %s: %v",
				ipAddr, intf.Name, err)
			d.log.Error(err)
			return err
		}
	}

	// remove the interface
	switch intf.Type {
	case interfaces.Interface_TAP:
		err = d.ifHandler.DeleteTapInterface(intf.Name, ifIdx, intf.GetTap().GetVersion())
	case interfaces.Interface_MEMIF:
		err = d.ifHandler.DeleteMemifInterface(intf.Name, ifIdx)
	case interfaces.Interface_VXLAN_TUNNEL:
		err = d.ifHandler.DeleteVxLanTunnel(intf.Name, ifIdx, intf.Vrf, intf.GetVxlan())
	case interfaces.Interface_SOFTWARE_LOOPBACK:
		err = d.ifHandler.DeleteLoopbackInterface(intf.Name, ifIdx)
	case interfaces.Interface_DPDK:
		d.log.Debugf("Interface %s removal skipped: cannot remove (blacklist) physical interface", intf.Name) // Not an error
		return nil
	case interfaces.Interface_AF_PACKET:
		err = d.ifHandler.DeleteAfPacketInterface(intf.Name, ifIdx, intf.GetAfpacket())
	case interfaces.Interface_IPSEC_TUNNEL:
		err = d.ifHandler.DeleteIPSecTunnelInterface(intf.Name, intf.GetIpsec())
	case interfaces.Interface_SUB_INTERFACE:
		err = d.ifHandler.DeleteSubif(ifIdx)
	}
	if err != nil {
		err = errors.Errorf("failed to remove interface %s, index %d: %v", intf.Name, ifIdx, err)
		d.log.Error(err)
		return err
	}

	return nil
}

// Modify is able to change Type-unspecific attributes.
func (d *InterfaceDescriptor) Modify(key string, oldIntf, newIntf *interfaces.Interface, oldMetadata *ifaceidx.IfaceMetadata) (newMetadata *ifaceidx.IfaceMetadata, err error) {
	// validate the new configuration first
	err = d.validateInterfaceConfig(newIntf)
	if err != nil {
		d.log.Error(err)
		return nil, err
	}

	ifIdx := oldMetadata.SwIfIndex

	// rx-mode
	oldRx := getRxMode(oldIntf)
	newRx := getRxMode(newIntf)
	if !proto.Equal(oldRx, newRx) {
		err = d.ifHandler.SetRxMode(ifIdx, newRx)
		if err != nil {
			err = errors.Errorf("failed to modify rx-mode for interface %s: %v", newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// rx-placement
	if !proto.Equal(getRxPlacement(oldIntf), getRxPlacement(newIntf)) {
		if err = d.ifHandler.SetRxPlacement(ifIdx, newIntf.GetRxPlacementSettings()); err != nil {
			err = errors.Errorf("failed to modify rx-placement for interface %s: %v", newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// admin status
	if newIntf.Enabled != oldIntf.Enabled {
		if newIntf.Enabled {
			if err = d.ifHandler.InterfaceAdminUp(ifIdx); err != nil {
				err = errors.Errorf("failed to set interface %s up: %v", newIntf.Name, err)
				d.log.Error(err)
				return oldMetadata, err
			}
		} else {
			if err = d.ifHandler.InterfaceAdminDown(ifIdx); err != nil {
				err = errors.Errorf("failed to set interface %s down: %v", newIntf.Name, err)
				d.log.Error(err)
				return oldMetadata, err
			}
		}
	}

	// configure new MAC address if set (and only if it was changed and only for supported interface type)
	if newIntf.PhysAddress != "" &&
		newIntf.PhysAddress != oldIntf.PhysAddress &&
		oldIntf.Type != interfaces.Interface_AF_PACKET &&
		oldIntf.Type != interfaces.Interface_DPDK {
		if err := d.ifHandler.SetInterfaceMac(ifIdx, newIntf.PhysAddress); err != nil {
			err = errors.Errorf("setting interface %s MAC address %s failed: %v",
				newIntf.Name, newIntf.PhysAddress, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// calculate diff of IP addresses
	newIPAddresses, err := addrs.StrAddrsToStruct(newIntf.IpAddresses)
	if err != nil {
		err = errors.Errorf("failed to convert %s IP address list to IPNet structures: %v", newIntf.Name, err)
		d.log.Error(err)
		return oldMetadata, err
	}
	oldIPAddresses, err := addrs.StrAddrsToStruct(oldIntf.IpAddresses)
	if err != nil {
		err = errors.Errorf("failed to convert %s IP address list to IPNet structures: %v", oldIntf.Name, err)
		d.log.Error(err)
		return oldMetadata, err
	}
	del, add := addrs.DiffAddr(newIPAddresses, oldIPAddresses)

	// delete obsolete IP addresses
	for _, address := range del {
		err := d.ifHandler.DelInterfaceIP(ifIdx, address)
		if nil != err {
			err = errors.Errorf("failed to remove obsolete IP address %v from interface %s: %v",
				address, newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// add new IP addresses
	for _, address := range add {
		err := d.ifHandler.AddInterfaceIP(ifIdx, address)
		if nil != err {
			err = errors.Errorf("failed to add new IP addresses %v to interface %s: %v",
				address, newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	// update IP addresses in the metadata
	oldMetadata.IPAddresses = newIntf.IpAddresses

	// update MTU
	if newIntf.Mtu != 0 && newIntf.Mtu != oldIntf.Mtu {
		if err := d.ifHandler.SetInterfaceMtu(ifIdx, newIntf.Mtu); err != nil {
			err = errors.Errorf("failed to set MTU to interface %s: %v", newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	} else if newIntf.Mtu == 0 && d.defaultMtu != 0 {
		if err := d.ifHandler.SetInterfaceMtu(ifIdx, d.defaultMtu); err != nil {
			err = errors.Errorf("failed to set MTU to interface %s: %v", newIntf.Name, err)
			d.log.Error(err)
			return oldMetadata, err
		}
	}

	return oldMetadata, nil
}

// Dump returns all configured VPP interfaces.
func (d *InterfaceDescriptor) Dump(correlate []adapter.InterfaceKVWithMetadata) (dump []adapter.InterfaceKVWithMetadata, err error) {
	// convert interfaces for correlation into a map
	ifCfg := make(map[string]*interfaces.Interface) // interface logical name -> interface config (as expected by correlate)
	for _, kv := range correlate {
		ifCfg[kv.Value.Name] = kv.Value
	}

	// refresh the map of memif socket IDs
	d.memifSocketToID, err = d.ifHandler.DumpMemifSocketDetails()
	if err != nil {
		err = errors.Errorf("failed to dump memif socket details: %v", err)
		d.log.Error(err)
		return dump, err
	}
	for socketPath, socketID := range d.memifSocketToID {
		if socketID == 0 {
			d.defaultMemifSocketPath = socketPath
		}
	}

	// clear the map of ethernet interfaces
	d.ethernetIfs = make(map[string]uint32)

	// dump current state of VPP interfaces
	vppIfs, err := d.ifHandler.DumpInterfaces()
	if err != nil {
		err = errors.Errorf("failed to dump interfaces: %v", err)
		d.log.Error(err)
		return dump, err
	}

	for ifIdx, intf := range vppIfs {
		origin := scheduler.FromNB
		if ifIdx == 0 {
			// local0 is created automatically
			origin = scheduler.FromSB
		}
		if intf.Interface.Type == interfaces.Interface_DPDK {
			d.ethernetIfs[intf.Interface.Name] = ifIdx
			if !intf.Interface.Enabled && len(intf.Interface.IpAddresses) == 0 {
				// unconfigured physical interface => skip (but add entry to d.ethernetIfs)
				continue
			}
		}
		if intf.Interface.Name == "" {
			// untagged interface - generate a logical name for it
			// (apart from local0 it will get removed by resync)
			intf.Interface.Name = untaggedIfPreffix + intf.Meta.InternalName
		}

		// get TAP host interface name
		var tapHostIfName string
		if intf.Interface.Type == interfaces.Interface_TAP {
			tapHostIfName = intf.Interface.GetTap().GetHostIfName()
			if generateTAPHostName(intf.Interface.Name) == tapHostIfName {
				// interface host name was unset
				intf.Interface.GetTap().HostIfName = ""
			}
		}

		// correlate attributes that cannot be dumped
		if expCfg, hasExpCfg := ifCfg[intf.Interface.Name]; hasExpCfg {
			if expCfg.Type == interfaces.Interface_TAP && intf.Interface.GetTap() != nil {
				intf.Interface.GetTap().ToMicroservice = expCfg.GetTap().GetToMicroservice()
				intf.Interface.GetTap().RxRingSize = expCfg.GetTap().GetRxRingSize()
				intf.Interface.GetTap().TxRingSize = expCfg.GetTap().GetTxRingSize()
				// FIXME: VPP BUG - TAPv2 host name is sometimes not properly dumped
				// (seemingly uninitialized section of memory is returned)
				if intf.Interface.GetTap().GetVersion() == 2 {
					intf.Interface.GetTap().HostIfName = expCfg.GetTap().GetHostIfName()
				}

			}
{'fraction_non_alphanumeric': 0.12064376354069947, 'fraction_numerical': 0.0020117610646858556, 'mean_word_length': 3.6457225017972683, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6434178', 'n_tokens_mistral': 12611, 'n_tokens_neox': 11326, 'n_words': 2903}