go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/srplugin/vppcalls/vpp2202/srv6.go (about)

     1  // Copyright (c) 2022 Bell Canada, Pantheon Technologies 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 vpp2202 contains wrappers over VPP binary APIs to simplify their usage
    16  package vpp2202
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"net"
    22  	"regexp"
    23  	"strconv"
    24  	"strings"
    25  
    26  	"go.ligato.io/cn-infra/v2/logging"
    27  
    28  	vpp_ifs "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/interface"
    29  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/interface_types"
    30  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/ip_types"
    31  	vpp_sr "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/sr"
    32  	"go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2202/sr_types"
    33  	vpp2202 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls/vpp2202"
    34  	ifs "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    35  	srv6 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/srv6"
    36  )
    37  
    38  // Constants for behavior function hardcoded into VPP (there can be also custom behavior functions implemented as VPP plugins)
    39  // Constants are taken from VPP's vnet/srv6/sr.h (names are modified to Golang from original C form in VPP code)
    40  const (
    41  	BehaviorEnd    = iota + 1 // Behavior of simple endpoint
    42  	BehaviorX                 // Behavior of endpoint with Layer-3 cross-connect
    43  	BehaviorT                 // Behavior of endpoint with specific IPv6 table lookup
    44  	BehaviorDfirst            // Unused. Separator in between regular and D
    45  	BehaviorDX2               // Behavior of endpoint with decapulation and Layer-2 cross-connect (or DX2 with egress VLAN rewrite when VLAN notzero - not supported this variant yet)
    46  	BehaviorDX6               // Behavior of endpoint with decapsulation and IPv6 cross-connect
    47  	BehaviorDX4               // Behavior of endpoint with decapsulation and IPv4 cross-connect
    48  	BehaviorDT6               // Behavior of endpoint with decapsulation and specific IPv6 table lookup
    49  	BehaviorDT4               // Behavior of endpoint with decapsulation and specific IPv4 table lookup
    50  	BehaviorLast              // seems unused, note in VPP: "Must always be the last one"
    51  )
    52  
    53  // Constants for steering type
    54  // Constants are taken from VPP's vnet/srv6/sr.h (names are modified to Golang from original C form in VPP code)
    55  const (
    56  	SteerTypeL2   = 2
    57  	SteerTypeIPv4 = 4
    58  	SteerTypeIPv6 = 6
    59  )
    60  
    61  // Constants for operation of SR policy modify binary API method
    62  const (
    63  	AddSRList            = iota + 1 // Add SR List to an existing SR policy
    64  	DeleteSRList                    // Delete SR List from an existing SR policy
    65  	ModifyWeightOfSRList            // Modify the weight of an existing SR List
    66  )
    67  
    68  // AddLocalSid adds local sid <localSID> into VPP
    69  func (h *SRv6VppHandler) AddLocalSid(localSID *srv6.LocalSID) error {
    70  	return h.addDelLocalSid(false, localSID)
    71  }
    72  
    73  // DeleteLocalSid deletes local sid <localSID> in VPP
    74  func (h *SRv6VppHandler) DeleteLocalSid(localSID *srv6.LocalSID) error {
    75  	return h.addDelLocalSid(true, localSID)
    76  }
    77  
    78  func (h *SRv6VppHandler) addDelLocalSid(deletion bool, localSID *srv6.LocalSID) error {
    79  	h.log.WithFields(logging.Fields{"localSID": localSID.GetSid(), "delete": deletion, "installationVrfID": h.installationVrfID(localSID), "end function": h.endFunction(localSID)}).
    80  		Debug("Adding/deleting Local SID", localSID.GetSid())
    81  	sidAddr, err := parseIPv6(localSID.GetSid()) // parsing to get some standard sid form
    82  	if err != nil {
    83  		return fmt.Errorf("sid address %s is not IPv6 address: %v", localSID.GetSid(), err) // calls from descriptor are already validated
    84  	}
    85  	if !deletion && localSID.GetEndFunctionAd() != nil {
    86  		return h.addSRProxy(sidAddr, localSID)
    87  	}
    88  	var localsid ip_types.IP6Address
    89  	copy(localsid[:], sidAddr.To16())
    90  
    91  	req := &vpp_sr.SrLocalsidAddDel{
    92  		IsDel:    deletion,
    93  		Localsid: localsid,
    94  	}
    95  	req.FibTable = localSID.InstallationVrfId // where to install localsid entry/from where to remove installed localsid entry
    96  	if !deletion {
    97  		if err := h.writeEndFunction(req, localSID); err != nil {
    98  			return err
    99  		}
   100  	}
   101  	reply := &vpp_sr.SrLocalsidAddDelReply{}
   102  
   103  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   104  		return err
   105  	}
   106  	if reply.Retval != 0 {
   107  		return fmt.Errorf("vpp call %q returned: %d", reply.GetMessageName(), reply.Retval)
   108  	}
   109  
   110  	h.log.WithFields(logging.Fields{"localSID": sidAddr, "delete": deletion, "installationVrfID": h.installationVrfID(localSID), "end function": h.endFunction(localSID)}).
   111  		Debug("Added/deleted Local SID ", sidAddr)
   112  
   113  	return nil
   114  }
   115  
   116  // addSRProxy adds local sid with SR-proxy end function (End.AD). This functionality has no binary API in VPP, therefore
   117  // CLI commands are used (VPE binary API that calls VPP's CLI).
   118  func (h *SRv6VppHandler) addSRProxy(sidAddr net.IP, localSID *srv6.LocalSID) error {
   119  	// get VPP-internal names of IN and OUT interfaces
   120  	names, err := h.interfaceNameMapping()
   121  	if err != nil {
   122  		return fmt.Errorf("can't convert interface names from etcd to VPP-internal interface names:%v", err)
   123  	}
   124  	outInterface, found := names[localSID.GetEndFunctionAd().OutgoingInterface]
   125  	if !found {
   126  		return fmt.Errorf("can't find VPP-internal name for interface %v (name in etcd)", localSID.GetEndFunctionAd().OutgoingInterface)
   127  	}
   128  	inInterface, found := names[localSID.GetEndFunctionAd().IncomingInterface]
   129  	if !found {
   130  		return fmt.Errorf("can't find VPP-internal name for interface %v (name in etcd)", localSID.GetEndFunctionAd().IncomingInterface)
   131  	}
   132  
   133  	// add SR-proxy using VPP CLI
   134  	var cmd string
   135  	if strings.TrimSpace(localSID.GetEndFunctionAd().L3ServiceAddress) == "" { // L2 service
   136  		cmd = fmt.Sprintf("sr localsid address %v fib-table %v behavior end.ad oif %v iif %v", sidAddr, localSID.InstallationVrfId, outInterface, inInterface)
   137  	} else { // L3 service
   138  		cmd = fmt.Sprintf("sr localsid address %v fib-table %v behavior end.ad nh %v oif %v iif %v", sidAddr, localSID.InstallationVrfId, localSID.GetEndFunctionAd().L3ServiceAddress, outInterface, inInterface)
   139  	}
   140  	data, err := h.RunCli(context.TODO(), cmd)
   141  	if err != nil {
   142  		return err
   143  	}
   144  	if len(strings.TrimSpace(string(data))) > 0 {
   145  		return fmt.Errorf("addition of dynamic segment routing proxy failed by returning nonblank space text in CLI: %v", string(data))
   146  	}
   147  	return nil
   148  }
   149  
   150  // interfaceNameMapping dumps from VPP internal names of interfaces and uses them to produce mapping from ligato interface names to vpp internal names.
   151  func (h *SRv6VppHandler) interfaceNameMapping() (map[string]string, error) {
   152  	mapping := make(map[string]string)
   153  	reqCtx := h.callsChannel.SendMultiRequest(&vpp_ifs.SwInterfaceDump{})
   154  
   155  	for {
   156  		// get next interface info
   157  		ifDetails := &vpp_ifs.SwInterfaceDetails{}
   158  		stop, err := reqCtx.ReceiveReply(ifDetails)
   159  		if stop {
   160  			break // Break from the loop.
   161  		}
   162  		if err != nil {
   163  			return nil, fmt.Errorf("failed to dump interface: %v", err)
   164  		}
   165  
   166  		// extract and compute names
   167  		ligatoName := strings.TrimRight(ifDetails.Tag, "\x00")
   168  		vppInternalName := strings.TrimRight(ifDetails.InterfaceName, "\x00")
   169  		if ifDetails.SupSwIfIndex == uint32(ifDetails.SwIfIndex) && // no subinterface (subinterface are not DPDK)
   170  			guessInterfaceType(strings.TrimRight(ifDetails.InterfaceName, "\x00")) == ifs.Interface_DPDK {
   171  			// fill name for physical interfaces (they are mostly without tag)
   172  			ligatoName = vppInternalName
   173  		}
   174  
   175  		mapping[ligatoName] = vppInternalName
   176  	}
   177  	return mapping, nil
   178  }
   179  
   180  func (h *SRv6VppHandler) installationVrfID(localSID *srv6.LocalSID) string {
   181  	if localSID != nil {
   182  		return fmt.Sprint(localSID.InstallationVrfId)
   183  	}
   184  	return "<nil>"
   185  }
   186  
   187  func (h *SRv6VppHandler) endFunction(localSID *srv6.LocalSID) string {
   188  	switch ef := localSID.GetEndFunction().(type) {
   189  	case *srv6.LocalSID_BaseEndFunction:
   190  		return fmt.Sprintf("End{psp: %v}", ef.BaseEndFunction.Psp)
   191  	case *srv6.LocalSID_EndFunctionX:
   192  		return fmt.Sprintf("X{psp: %v, OutgoingInterface: %v, NextHop: %v}", ef.EndFunctionX.Psp, ef.EndFunctionX.OutgoingInterface, ef.EndFunctionX.NextHop)
   193  	case *srv6.LocalSID_EndFunctionT:
   194  		return fmt.Sprintf("T{psp: %v, vrf: %v}", ef.EndFunctionT.Psp, ef.EndFunctionT.VrfId)
   195  	case *srv6.LocalSID_EndFunctionDx2:
   196  		return fmt.Sprintf("DX2{VlanTag: %v, OutgoingInterface: %v}", ef.EndFunctionDx2.VlanTag, ef.EndFunctionDx2.OutgoingInterface)
   197  	case *srv6.LocalSID_EndFunctionDx4:
   198  		return fmt.Sprintf("DX4{OutgoingInterface: %v, NextHop: %v}", ef.EndFunctionDx4.OutgoingInterface, ef.EndFunctionDx4.NextHop)
   199  	case *srv6.LocalSID_EndFunctionDx6:
   200  		return fmt.Sprintf("DX6{OutgoingInterface: %v, NextHop: %v}", ef.EndFunctionDx6.OutgoingInterface, ef.EndFunctionDx6.NextHop)
   201  	case *srv6.LocalSID_EndFunctionDt4:
   202  		return fmt.Sprintf("DT4{vrf: %v}", ef.EndFunctionDt4.VrfId)
   203  	case *srv6.LocalSID_EndFunctionDt6:
   204  		return fmt.Sprintf("DT6{vrf: %v}", ef.EndFunctionDt6.VrfId)
   205  	case *srv6.LocalSID_EndFunctionAd:
   206  		return fmt.Sprintf("AD{L3ServiceAddress: %v, OutgoingInterface: %v, IncomingInterface: %v}", ef.EndFunctionAd.L3ServiceAddress, ef.EndFunctionAd.OutgoingInterface, ef.EndFunctionAd.IncomingInterface)
   207  	case nil:
   208  		return "<nil>"
   209  	default:
   210  		return "unknown end function"
   211  	}
   212  }
   213  
   214  func (h *SRv6VppHandler) writeEndFunction(req *vpp_sr.SrLocalsidAddDel, localSID *srv6.LocalSID) error {
   215  	switch ef := localSID.EndFunction.(type) {
   216  	case *srv6.LocalSID_BaseEndFunction:
   217  		req.Behavior = BehaviorEnd
   218  		req.EndPsp = ef.BaseEndFunction.Psp
   219  	case *srv6.LocalSID_EndFunctionX:
   220  		req.Behavior = BehaviorX
   221  		req.EndPsp = ef.EndFunctionX.Psp
   222  		ifMeta, exists := h.ifIndexes.LookupByName(ef.EndFunctionX.OutgoingInterface)
   223  		if !exists {
   224  			return fmt.Errorf("for interface %v doesn't exist sw index", ef.EndFunctionX.OutgoingInterface)
   225  		}
   226  		req.SwIfIndex = interface_types.InterfaceIndex(ifMeta.SwIfIndex)
   227  
   228  		nhIP, err := parseIPv6(ef.EndFunctionX.NextHop) // parses also ipv4 addresses but into ipv6 address form
   229  		if err != nil {
   230  			return err
   231  		}
   232  		nhAddr, err := IPToAddress(nhIP.String())
   233  		if err != nil {
   234  			return err
   235  		}
   236  		req.NhAddr = nhAddr // ipv4 address in ipv6 address form?
   237  	case *srv6.LocalSID_EndFunctionT:
   238  		req.Behavior = BehaviorT
   239  		req.EndPsp = ef.EndFunctionT.Psp
   240  		req.SwIfIndex = interface_types.InterfaceIndex(ef.EndFunctionT.VrfId)
   241  	case *srv6.LocalSID_EndFunctionDx2:
   242  		req.Behavior = BehaviorDX2
   243  		req.VlanIndex = ef.EndFunctionDx2.VlanTag
   244  		ifMeta, exists := h.ifIndexes.LookupByName(ef.EndFunctionDx2.OutgoingInterface)
   245  		if !exists {
   246  			return fmt.Errorf("for interface %v doesn't exist sw index", ef.EndFunctionDx2.OutgoingInterface)
   247  		}
   248  		req.SwIfIndex = interface_types.InterfaceIndex(ifMeta.SwIfIndex)
   249  	case *srv6.LocalSID_EndFunctionDx4:
   250  		req.Behavior = BehaviorDX4
   251  		ifMeta, exists := h.ifIndexes.LookupByName(ef.EndFunctionDx4.OutgoingInterface)
   252  		if !exists {
   253  			return fmt.Errorf("for interface %v doesn't exist sw index", ef.EndFunctionDx4.OutgoingInterface)
   254  		}
   255  		req.SwIfIndex = interface_types.InterfaceIndex(ifMeta.SwIfIndex)
   256  		nhAddr, err := parseIPv6(ef.EndFunctionDx4.NextHop) // parses also IPv4
   257  		if err != nil {
   258  			return err
   259  		}
   260  		nhAddr4 := nhAddr.To4()
   261  		if nhAddr4 == nil {
   262  			return fmt.Errorf("next hop of DX4 end function (%v) is not valid IPv4 address", ef.EndFunctionDx4.NextHop)
   263  		}
   264  		var addr ip_types.IP4Address
   265  		copy(addr[:], nhAddr4)
   266  		req.NhAddr.Af = ip_types.ADDRESS_IP4
   267  		req.NhAddr.Un.SetIP4(addr)
   268  	case *srv6.LocalSID_EndFunctionDx6:
   269  		req.Behavior = BehaviorDX6
   270  		ifMeta, exists := h.ifIndexes.LookupByName(ef.EndFunctionDx6.OutgoingInterface)
   271  		if !exists {
   272  			return fmt.Errorf("for interface %v doesn't exist sw index", ef.EndFunctionDx6.OutgoingInterface)
   273  		}
   274  		req.SwIfIndex = interface_types.InterfaceIndex(ifMeta.SwIfIndex)
   275  		nhAddr6, err := parseIPv6(ef.EndFunctionDx6.NextHop)
   276  		if err != nil {
   277  			return err
   278  		}
   279  		var addr ip_types.IP6Address
   280  		copy(addr[:], nhAddr6)
   281  		req.NhAddr.Af = ip_types.ADDRESS_IP6
   282  		req.NhAddr.Un.SetIP6(addr)
   283  	case *srv6.LocalSID_EndFunctionDt4:
   284  		req.Behavior = BehaviorDT4
   285  		req.SwIfIndex = interface_types.InterfaceIndex(ef.EndFunctionDt4.VrfId)
   286  	case *srv6.LocalSID_EndFunctionDt6:
   287  		req.Behavior = BehaviorDT6
   288  		req.SwIfIndex = interface_types.InterfaceIndex(ef.EndFunctionDt6.VrfId)
   289  	case nil:
   290  		return fmt.Errorf("End function not set. Please configure end function for local SID %v ", localSID.GetSid())
   291  	default:
   292  		return fmt.Errorf("unknown end function (model link type %T)", ef) // EndFunction_AD is handled elsewhere
   293  	}
   294  
   295  	return nil
   296  }
   297  
   298  // SetEncapsSourceAddress sets for SRv6 in VPP the source address used for encapsulated packet
   299  func (h *SRv6VppHandler) SetEncapsSourceAddress(address string) error {
   300  	h.log.Debugf("Configuring encapsulation source address to address %v", address)
   301  	ipAddress, err := parseIPv6(address)
   302  	if err != nil {
   303  		return err
   304  	}
   305  	var encapSrc ip_types.IP6Address
   306  	copy(encapSrc[:], ipAddress.To16())
   307  	req := &vpp_sr.SrSetEncapSource{
   308  		EncapsSource: encapSrc,
   309  	}
   310  	reply := &vpp_sr.SrSetEncapSourceReply{}
   311  
   312  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   313  		return err
   314  	}
   315  	if reply.Retval != 0 {
   316  		return fmt.Errorf("vpp call %q returned: %d", reply.GetMessageName(), reply.Retval)
   317  	}
   318  
   319  	h.log.WithFields(logging.Fields{"Encapsulation source address": address}).
   320  		Debug("Encapsulation source address configured.")
   321  
   322  	return nil
   323  }
   324  
   325  // AddPolicy adds SRv6 policy <policy> into VPP (including all policy's segment lists).
   326  func (h *SRv6VppHandler) AddPolicy(policy *srv6.Policy) error {
   327  	if err := h.addBasePolicyWithFirstSegmentList(policy); err != nil {
   328  		return fmt.Errorf("can't create Policy with first segment list (Policy: %+v): %v", policy, err)
   329  	}
   330  	if err := h.addOtherSegmentLists(policy); err != nil {
   331  		return fmt.Errorf("can't add all segment lists to created policy %+v: %v", policy, err)
   332  	}
   333  	return nil
   334  }
   335  
   336  func (h *SRv6VppHandler) addBasePolicyWithFirstSegmentList(policy *srv6.Policy) error {
   337  	h.log.Debugf("Adding SR policy %+v", policy)
   338  	bindingSid, err := parseIPv6(policy.GetBsid()) // already validated
   339  	if err != nil {
   340  		return fmt.Errorf("binding sid address %s is not IPv6 address: %v", policy.GetBsid(), err) // calls from descriptor are already validated
   341  	}
   342  	if len(policy.SegmentLists) == 0 {
   343  		return fmt.Errorf("policy must have defined at least one segment list (Policy: %+v)", policy) // calls from descriptor are already validated
   344  	}
   345  	sids, err := h.convertPolicySegment(policy.SegmentLists[0])
   346  	if err != nil {
   347  		return err
   348  	}
   349  	var BsidAddr ip_types.IP6Address
   350  	copy(BsidAddr[:], bindingSid.To16())
   351  	// Note: Weight in sr.SrPolicyAdd is leftover from API changes that moved weight into sr.Srv6SidList (it is weight of sid list not of the whole policy)
   352  	req := &vpp_sr.SrPolicyAdd{
   353  		BsidAddr: BsidAddr,
   354  		Sids:     *sids,
   355  		IsEncap:  policy.SrhEncapsulation,
   356  		IsSpray:  policy.SprayBehaviour,
   357  		FibTable: policy.InstallationVrfId,
   358  	}
   359  	reply := &vpp_sr.SrPolicyAddReply{}
   360  
   361  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   362  		return err
   363  	}
   364  	if reply.Retval != 0 {
   365  		return fmt.Errorf("vpp call %q returned: %d", reply.GetMessageName(), reply.Retval)
   366  	}
   367  
   368  	h.log.WithFields(logging.Fields{"binding SID": bindingSid, "list of next SIDs": policy.SegmentLists[0].Segments}).
   369  		Debug("base SR policy (policy with just one segment list) added")
   370  
   371  	return nil
   372  }
   373  
   374  func (h *SRv6VppHandler) addOtherSegmentLists(policy *srv6.Policy) error {
   375  	for _, sl := range policy.SegmentLists[1:] {
   376  		if err := h.AddPolicySegmentList(sl, policy); err != nil {
   377  			return fmt.Errorf("failed to add policy segment %+v: %v", sl, err)
   378  		}
   379  	}
   380  	return nil
   381  }
   382  
   383  // DeletePolicy deletes SRv6 policy given by binding SID <bindingSid>
   384  func (h *SRv6VppHandler) DeletePolicy(bindingSid net.IP) error {
   385  	h.log.Debugf("Deleting SR policy with binding SID %v ", bindingSid)
   386  	var BsidAddr ip_types.IP6Address
   387  	copy(BsidAddr[:], bindingSid.To16())
   388  	req := &vpp_sr.SrPolicyDel{
   389  		BsidAddr: BsidAddr, // TODO add ability to define policy also by index (SrPolicyIndex)
   390  	}
   391  	reply := &vpp_sr.SrPolicyDelReply{}
   392  
   393  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   394  		return err
   395  	}
   396  	if reply.Retval != 0 {
   397  		return fmt.Errorf("vpp call %q returned: %d", reply.GetMessageName(), reply.Retval)
   398  	}
   399  
   400  	h.log.WithFields(logging.Fields{"binding SID": bindingSid}).Debug("SR policy deleted")
   401  
   402  	return nil
   403  }
   404  
   405  // AddPolicySegmentList adds segment list <segmentList> to SRv6 policy <policy> in VPP
   406  func (h *SRv6VppHandler) AddPolicySegmentList(segmentList *srv6.Policy_SegmentList, policy *srv6.Policy) error {
   407  	h.log.Debugf("Adding segment %+v to SR policy %+v", segmentList, policy)
   408  	err := h.modPolicy(AddSRList, policy, segmentList, 0)
   409  	if err == nil {
   410  		h.log.WithFields(logging.Fields{"binding SID": policy.Bsid, "list of next SIDs": segmentList.Segments}).
   411  			Debug("SR policy modified(added another segment list)")
   412  	}
   413  	return err
   414  }
   415  
   416  // DeletePolicySegmentList removes segment list <segmentList> (with VPP-internal index <segmentVPPIndex>) from SRv6 policy <policy> in VPP
   417  func (h *SRv6VppHandler) DeletePolicySegmentList(segmentList *srv6.Policy_SegmentList, segmentVPPIndex uint32, policy *srv6.Policy) error {
   418  	h.log.Debugf("Removing segment %+v (vpp-internal index %v) from SR policy %+v", segmentList, segmentVPPIndex, policy)
   419  	err := h.modPolicy(DeleteSRList, policy, segmentList, segmentVPPIndex)
   420  	if err == nil {
   421  		h.log.WithFields(logging.Fields{"binding SID": policy.Bsid, "list of next SIDs": segmentList.Segments, "segmentListIndex": segmentVPPIndex}).
   422  			Debug("SR policy modified(removed segment list)")
   423  	}
   424  	return err
   425  }
   426  
   427  func (h *SRv6VppHandler) modPolicy(operation sr_types.SrPolicyOp, policy *srv6.Policy, segmentList *srv6.Policy_SegmentList, segmentListIndex uint32) error {
   428  	bindingSid, err := parseIPv6(policy.GetBsid())
   429  	if err != nil {
   430  		return fmt.Errorf("binding sid address %s is not IPv6 address: %v", policy.GetBsid(), err) // calls from descriptor are already validated
   431  	}
   432  	sids, err := h.convertPolicySegment(segmentList)
   433  	if err != nil {
   434  		return err
   435  	}
   436  
   437  	var BsidAddr ip_types.IP6Address
   438  	copy(BsidAddr[:], bindingSid.To16())
   439  	// Note: Weight in sr.SrPolicyMod is leftover from API changes that moved weight into sr.Srv6SidList (it is weight of sid list not of the whole policy)
   440  	req := &vpp_sr.SrPolicyMod{
   441  		BsidAddr:  BsidAddr, // TODO add ability to define policy also by index (SrPolicyIndex)
   442  		Operation: operation,
   443  		Sids:      *sids,
   444  		FibTable:  policy.InstallationVrfId,
   445  	}
   446  	if operation == DeleteSRList || operation == ModifyWeightOfSRList {
   447  		req.SlIndex = segmentListIndex
   448  	}
   449  
   450  	reply := &vpp_sr.SrPolicyModReply{}
   451  
   452  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   453  		return err
   454  	}
   455  	if reply.Retval != 0 {
   456  		return fmt.Errorf("vpp call %q returned: %d", reply.GetMessageName(), reply.Retval)
   457  	}
   458  	return nil
   459  }
   460  
   461  func (h *SRv6VppHandler) convertPolicySegment(segmentList *srv6.Policy_SegmentList) (*vpp_sr.Srv6SidList, error) {
   462  	var segments []ip_types.IP6Address
   463  	for _, sid := range segmentList.Segments {
   464  		// parse to IPv6 address
   465  		parserSid, err := parseIPv6(sid)
   466  		if err != nil {
   467  			return nil, err
   468  		}
   469  		// add sid to segment list
   470  		var ipv6Segment ip_types.IP6Address
   471  		copy(ipv6Segment[:], parserSid)
   472  		segments = append(segments, ipv6Segment)
   473  	}
   474  	sidList := &vpp_sr.Srv6SidList{
   475  		NumSids: uint8(len(segments)),
   476  		Weight:  segmentList.Weight,
   477  	}
   478  	copy(sidList.Sids[:], segments)
   479  	return sidList, nil
   480  }
   481  
   482  // RetrievePolicyIndexInfo retrieves index of policy <policy> and its segment lists
   483  func (h *SRv6VppHandler) RetrievePolicyIndexInfo(policy *srv6.Policy) (policyIndex uint32, segmentListIndexes map[*srv6.Policy_SegmentList]uint32, err error) {
   484  	// dump sr policies using VPP CLI
   485  	data, err := h.RunCli(context.TODO(), "sh sr policies")
   486  	if err != nil {
   487  		return ^uint32(0), nil, fmt.Errorf("can't dump index data from VPP: %v", err)
   488  	}
   489  
   490  	// do necessary parsing to extract index of segment list
   491  	dumpStr := strings.ToLower(string(data))
   492  	segmentListIndexes = make(map[*srv6.Policy_SegmentList]uint32)
   493  
   494  	for _, policyStr := range strings.Split(dumpStr, "-----------") {
   495  		policyHeader := regexp.MustCompile(fmt.Sprintf("\\[(\\d+)\\]\\.-\\s+bsid:\\s*%s", strings.ToLower(strings.TrimSpace(policy.GetBsid()))))
   496  		if policyMatch := policyHeader.FindStringSubmatch(policyStr); policyMatch != nil {
   497  			parsed, err := strconv.ParseUint(policyMatch[1], 10, 32)
   498  			if err != nil {
   499  				return ^uint32(0), nil, fmt.Errorf("can't parse policy index %q (dump: %s)", policyMatch[1], dumpStr)
   500  			}
   501  			policyIndex = uint32(parsed)
   502  
   503  			for _, sl := range policy.SegmentLists {
   504  				slRE := regexp.MustCompile(fmt.Sprintf("\\[(\\d+)\\].- < %s,[^:>]*> weight: %d", strings.ToLower(strings.Join(sl.Segments, ", ")), sl.Weight))
   505  				if slMatch := slRE.FindStringSubmatch(policyStr); slMatch != nil {
   506  					parsed, err := strconv.ParseUint(slMatch[1], 10, 32)
   507  					if err != nil {
   508  						return ^uint32(0), nil, fmt.Errorf("can't parse segment policy index %q (dump: %s)", slMatch[1], dumpStr)
   509  					}
   510  					segmentListIndexes[sl] = uint32(parsed)
   511  					continue
   512  				}
   513  				return ^uint32(0), nil, fmt.Errorf("can't find index for segment list %+v (policy bsid %v) in dump %q", sl, policy.GetBsid(), dumpStr)
   514  			}
   515  			return policyIndex, segmentListIndexes, nil
   516  		}
   517  	}
   518  	return ^uint32(0), nil, fmt.Errorf("can't find index for policy with bsid %v in dump %q", policy.GetBsid(), dumpStr)
   519  }
   520  
   521  // AddSteering sets in VPP steering into SRv6 policy.
   522  func (h *SRv6VppHandler) AddSteering(steering *srv6.Steering) error {
   523  	return h.addDelSteering(false, steering)
   524  }
   525  
   526  // RemoveSteering removes in VPP steering into SRv6 policy.
   527  func (h *SRv6VppHandler) RemoveSteering(steering *srv6.Steering) error {
   528  	return h.addDelSteering(true, steering)
   529  }
   530  
   531  func (h *SRv6VppHandler) addDelSteering(delete bool, steering *srv6.Steering) error {
   532  	// defining operation strings for logging
   533  	operationProgressing, operationFinished := "Adding", "Added"
   534  	if delete {
   535  		operationProgressing, operationFinished = "Removing", "Removed"
   536  	}
   537  
   538  	// logging info about operation with steering
   539  	switch t := steering.Traffic.(type) {
   540  	case *srv6.Steering_L3Traffic_:
   541  		h.log.Debugf("%v steering for l3 traffic with destination %v to SR policy (binding SID %v, policy index %v)",
   542  			operationProgressing, t.L3Traffic.PrefixAddress, steering.GetPolicyBsid(), steering.GetPolicyIndex())
   543  	case *srv6.Steering_L2Traffic_:
   544  		h.log.Debugf("%v steering for l2 traffic from interface %v to SR policy (binding SID %v, policy index %v)",
   545  			operationProgressing, t.L2Traffic.InterfaceName, steering.GetPolicyBsid(), steering.GetPolicyIndex())
   546  	}
   547  
   548  	// converting policy reference
   549  	var policyBSIDAddr ip_types.IP6Address // undefined reference
   550  	var policyIndex = uint32(0)            // undefined reference
   551  	switch ref := steering.PolicyRef.(type) {
   552  	case *srv6.Steering_PolicyBsid:
   553  		bsid, err := parseIPv6(ref.PolicyBsid)
   554  		if err != nil {
   555  			return fmt.Errorf("can't parse binding SID %q to IP address: %v ", ref.PolicyBsid, err)
   556  		}
   557  		copy(policyBSIDAddr[:], bsid.To16())
   558  	case *srv6.Steering_PolicyIndex:
   559  		policyIndex = ref.PolicyIndex
   560  	case nil:
   561  		return fmt.Errorf("policy reference must be provided")
   562  	default:
   563  		return fmt.Errorf("unknown policy reference type (link type %+v)", ref)
   564  	}
   565  
   566  	// converting target traffic info
   567  	var prefix ip_types.Prefix
   568  	steerType := sr_types.SrSteer(SteerTypeIPv6)
   569  	tableID := uint32(0)
   570  	intIndex := uint32(0)
   571  	switch t := steering.Traffic.(type) {
   572  	case *srv6.Steering_L3Traffic_:
   573  		ip, ipnet, err := net.ParseCIDR(t.L3Traffic.PrefixAddress)
   574  		if err != nil {
   575  			return fmt.Errorf("can't parse ip prefix %q: %v", t.L3Traffic.PrefixAddress, err)
   576  		}
   577  		if ip.To4() != nil { // IPv4 address
   578  			steerType = SteerTypeIPv4
   579  		}
   580  		tableID = t.L3Traffic.InstallationVrfId
   581  		if ip.To16() != nil {
   582  			prefix.Address.Af = ip_types.ADDRESS_IP6
   583  		}
   584  		prefix.Address, _ = vpp2202.IPToAddress(ip.String())
   585  		maskWidth, _ := ipnet.Mask.Size()
   586  		prefix.Len = uint8(maskWidth)
   587  	case *srv6.Steering_L2Traffic_:
   588  		steerType = SteerTypeL2
   589  		ifMeta, exists := h.ifIndexes.LookupByName(t.L2Traffic.InterfaceName)
   590  		if !exists {
   591  			return fmt.Errorf("for interface %v doesn't exist sw index", t.L2Traffic.InterfaceName)
   592  		}
   593  		intIndex = ifMeta.SwIfIndex
   594  	case nil:
   595  		return fmt.Errorf("traffic type must be provided")
   596  	default:
   597  		return fmt.Errorf("unknown traffic type (link type %+v)", t)
   598  	}
   599  	req := &vpp_sr.SrSteeringAddDel{
   600  		IsDel:         delete,
   601  		TableID:       tableID,
   602  		BsidAddr:      policyBSIDAddr,                           // policy (to which we want to steer routing into) identified by policy binding sid (alternativelly it can be used policy index)
   603  		SrPolicyIndex: policyIndex,                              // policy (to which we want to steer routing into) identified by policy index (alternativelly it can be used policy binding sid)
   604  		TrafficType:   steerType,                                // type of traffic to steer
   605  		Prefix:        prefix,                                   // destination prefix address (L3 traffic type only)
   606  		SwIfIndex:     interface_types.InterfaceIndex(intIndex), // incoming interface (L2 traffic type only)
   607  	}
   608  	reply := &vpp_sr.SrSteeringAddDelReply{}
   609  
   610  	if err := h.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
   611  		return err
   612  	}
   613  	if reply.Retval != 0 {
   614  		return fmt.Errorf("vpp call %q returned: %d", reply.GetMessageName(), reply.Retval)
   615  	}
   616  
   617  	h.log.WithFields(logging.Fields{
   618  		"steer type":         steerType,
   619  		"L3 prefix":          prefix,
   620  		"L2 interface index": intIndex,
   621  		"policy binding SID": policyBSIDAddr,
   622  		"policy index":       policyIndex,
   623  	}).Debugf("%v steering to SR policy ", operationFinished)
   624  
   625  	return nil
   626  }
   627  
   628  // guessInterfaceType attempts to guess the correct interface type from its internal name (as given by VPP).
   629  // This is required mainly for those interface types, that do not provide dump binary API,
   630  // such as loopback of af_packet.
   631  func guessInterfaceType(ifName string) ifs.Interface_Type {
   632  	switch {
   633  	case strings.HasPrefix(ifName, "loop"),
   634  		strings.HasPrefix(ifName, "local"):
   635  		return ifs.Interface_SOFTWARE_LOOPBACK
   636  	case strings.HasPrefix(ifName, "memif"):
   637  		return ifs.Interface_MEMIF
   638  	case strings.HasPrefix(ifName, "tap"):
   639  		return ifs.Interface_TAP
   640  	case strings.HasPrefix(ifName, "host"):
   641  		return ifs.Interface_AF_PACKET
   642  	case strings.HasPrefix(ifName, "vxlan"):
   643  		return ifs.Interface_VXLAN_TUNNEL
   644  	case strings.HasPrefix(ifName, "ipsec"):
   645  		return ifs.Interface_IPSEC_TUNNEL
   646  	case strings.HasPrefix(ifName, "vmxnet3"):
   647  		return ifs.Interface_VMXNET3_INTERFACE
   648  	default:
   649  		return ifs.Interface_DPDK
   650  	}
   651  }