go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/srplugin/vppcalls/vpp2106/srv6.go (about) 1 // Copyright (c) 2021 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 vpp2106_379 contains wrappers over VPP (version 20.01) binary APIs to simplify their usage 16 package vpp2106 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/vpp2106/interface" 29 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/interface_types" 30 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/ip_types" 31 vpp_sr "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/sr" 32 "go.ligato.io/vpp-agent/v3/plugins/vpp/binapi/vpp2106/sr_types" 33 vpp2106 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/vppcalls/vpp2106" 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, _ = vpp2106.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 }