go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/srplugin/descriptor/localsid.go (about) 1 // Copyright (c) 2019 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 descriptor 16 17 import ( 18 "net" 19 "reflect" 20 "strings" 21 22 "github.com/pkg/errors" 23 "go.ligato.io/cn-infra/v2/logging" 24 25 scheduler "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 26 "go.ligato.io/vpp-agent/v3/plugins/vpp/srplugin/descriptor/adapter" 27 "go.ligato.io/vpp-agent/v3/plugins/vpp/srplugin/vppcalls" 28 interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces" 29 vpp_l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3" 30 srv6 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/srv6" 31 ) 32 33 const ( 34 // LocalSIDDescriptorName is the name of the descriptor for VPP LocalSIDs 35 LocalSIDDescriptorName = "vpp-sr-localsid" 36 37 // dependency labels 38 localsidOutgoingInterfaceDep = "sr-localsid-outgoing-interface-exists" 39 localsidIncomingInterfaceDep = "sr-localsid-incoming-interface-exists" 40 localsidInstallationVRFDep = "sr-localsid-installation-vrf-table-exists" 41 localsidLookupVRFDep = "sr-localsid-routing-lookup-vrf-table-exists" 42 ) 43 44 // LocalSIDDescriptor teaches KVScheduler how to configure VPP LocalSIDs. 45 type LocalSIDDescriptor struct { 46 // dependencies 47 log logging.Logger 48 srHandler vppcalls.SRv6VppAPI 49 } 50 51 // NewLocalSIDDescriptor creates a new instance of the LocalSID descriptor. 52 func NewLocalSIDDescriptor(srHandler vppcalls.SRv6VppAPI, log logging.PluginLogger) *scheduler.KVDescriptor { 53 ctx := &LocalSIDDescriptor{ 54 log: log.NewLogger("localsid-descriptor"), 55 srHandler: srHandler, 56 } 57 58 typedDescr := &adapter.LocalSIDDescriptor{ 59 Name: LocalSIDDescriptorName, 60 NBKeyPrefix: srv6.ModelLocalSID.KeyPrefix(), 61 ValueTypeName: srv6.ModelLocalSID.ProtoName(), 62 KeySelector: srv6.ModelLocalSID.IsKeyValid, 63 KeyLabel: srv6.ModelLocalSID.StripKeyPrefix, 64 ValueComparator: ctx.EquivalentLocalSIDs, 65 Validate: ctx.Validate, 66 Create: ctx.Create, 67 Delete: ctx.Delete, 68 Dependencies: ctx.Dependencies, 69 } 70 return adapter.NewLocalSIDDescriptor(typedDescr) 71 } 72 73 // EquivalentLocalSIDs determines whether 2 localSIDs are logically equal. This comparison takes into consideration also 74 // semantics that couldn't be modeled into proto models (i.e. SID is IPv6 address and not only string) 75 func (d *LocalSIDDescriptor) EquivalentLocalSIDs(key string, oldLocalSID, newLocalSID *srv6.LocalSID) bool { 76 return oldLocalSID.InstallationVrfId == newLocalSID.InstallationVrfId && 77 equivalentSIDs(oldLocalSID.Sid, newLocalSID.Sid) && 78 d.equivalentEndFunctions(oldLocalSID.EndFunction, newLocalSID.EndFunction) 79 } 80 81 func (d *LocalSIDDescriptor) equivalentEndFunctions(ef1, ef2 interface{}) bool { 82 if ef1 == nil || ef2 == nil { 83 return ef1 == ef2 84 } 85 if reflect.TypeOf(ef1) != reflect.TypeOf(ef2) { 86 return false 87 } 88 switch ef1typed := ef1.(type) { 89 case *srv6.LocalSID_BaseEndFunction: 90 return true 91 case *srv6.LocalSID_EndFunctionX: 92 return ef1typed.EndFunctionX.Psp == ef2.(*srv6.LocalSID_EndFunctionX).EndFunctionX.Psp && 93 equivalentIPv6(ef1typed.EndFunctionX.NextHop, ef2.(*srv6.LocalSID_EndFunctionX).EndFunctionX.NextHop) && 94 equivalentTrimmedLowered(ef1typed.EndFunctionX.OutgoingInterface, ef2.(*srv6.LocalSID_EndFunctionX).EndFunctionX.OutgoingInterface) 95 case *srv6.LocalSID_EndFunctionT: 96 return ef1typed.EndFunctionT.Psp == ef2.(*srv6.LocalSID_EndFunctionT).EndFunctionT.Psp && 97 ef1typed.EndFunctionT.VrfId == ef2.(*srv6.LocalSID_EndFunctionT).EndFunctionT.VrfId 98 case *srv6.LocalSID_EndFunctionDx2: 99 return ef1typed.EndFunctionDx2.VlanTag == ef2.(*srv6.LocalSID_EndFunctionDx2).EndFunctionDx2.VlanTag && 100 equivalentTrimmedLowered(ef1typed.EndFunctionDx2.OutgoingInterface, ef2.(*srv6.LocalSID_EndFunctionDx2).EndFunctionDx2.OutgoingInterface) 101 case *srv6.LocalSID_EndFunctionDx4: 102 return equivalentIPv4(ef1typed.EndFunctionDx4.NextHop, ef2.(*srv6.LocalSID_EndFunctionDx4).EndFunctionDx4.NextHop) && 103 equivalentTrimmedLowered(ef1typed.EndFunctionDx4.OutgoingInterface, ef2.(*srv6.LocalSID_EndFunctionDx4).EndFunctionDx4.OutgoingInterface) 104 case *srv6.LocalSID_EndFunctionDx6: 105 return equivalentIPv4(ef1typed.EndFunctionDx6.NextHop, ef2.(*srv6.LocalSID_EndFunctionDx6).EndFunctionDx6.NextHop) && 106 equivalentTrimmedLowered(ef1typed.EndFunctionDx6.OutgoingInterface, ef2.(*srv6.LocalSID_EndFunctionDx6).EndFunctionDx6.OutgoingInterface) 107 case *srv6.LocalSID_EndFunctionDt4: 108 return ef1typed.EndFunctionDt4.VrfId == ef2.(*srv6.LocalSID_EndFunctionDt4).EndFunctionDt4.VrfId 109 case *srv6.LocalSID_EndFunctionDt6: 110 return ef1typed.EndFunctionDt6.VrfId == ef2.(*srv6.LocalSID_EndFunctionDt6).EndFunctionDt6.VrfId 111 case *srv6.LocalSID_EndFunctionAd: 112 return equivalentTrimmedLowered(ef1typed.EndFunctionAd.OutgoingInterface, ef2.(*srv6.LocalSID_EndFunctionAd).EndFunctionAd.OutgoingInterface) && 113 equivalentTrimmedLowered(ef1typed.EndFunctionAd.IncomingInterface, ef2.(*srv6.LocalSID_EndFunctionAd).EndFunctionAd.IncomingInterface) && 114 (equivalentIPv4(ef1typed.EndFunctionAd.L3ServiceAddress, ef2.(*srv6.LocalSID_EndFunctionAd).EndFunctionAd.L3ServiceAddress) || // l3 ipv4 service 115 equivalentIPv6(ef1typed.EndFunctionAd.L3ServiceAddress, ef2.(*srv6.LocalSID_EndFunctionAd).EndFunctionAd.L3ServiceAddress) || // l3 ipv6 service 116 (strings.TrimSpace(ef1typed.EndFunctionAd.L3ServiceAddress) == "" && strings.TrimSpace(ef2.(*srv6.LocalSID_EndFunctionAd).EndFunctionAd.L3ServiceAddress) == "")) // l2 service 117 default: 118 d.log.Warn("EquivalentSteering found unknown end function type (%T). Using general reflect.DeepEqual for it.", ef1) 119 return reflect.DeepEqual(ef1, ef2) // unknown end function type 120 } 121 } 122 123 // Validate validates VPP LocalSIDs. 124 func (d *LocalSIDDescriptor) Validate(key string, localSID *srv6.LocalSID) error { 125 // checking basic attributes 126 _, err := ParseIPv6(localSID.GetSid()) 127 if err != nil { 128 return scheduler.NewInvalidValueError(errors.Errorf("failed to parse local sid %s, should be a valid ipv6 address: %v", localSID.GetSid(), err), "sid") 129 } 130 131 // checking end functions 132 switch ef := localSID.EndFunction.(type) { 133 case *srv6.LocalSID_BaseEndFunction: 134 case *srv6.LocalSID_EndFunctionX: 135 _, err := ParseIPv6(ef.EndFunctionX.NextHop) 136 if err != nil { 137 return scheduler.NewInvalidValueError(errors.Errorf("failed to parse next hop %s, should be a valid ipv6 address: %v", ef.EndFunctionX.NextHop, err), "EndFunctionX.NextHop") 138 } 139 case *srv6.LocalSID_EndFunctionT: 140 case *srv6.LocalSID_EndFunctionDx2: 141 case *srv6.LocalSID_EndFunctionDx4: 142 _, err := ParseIPv4(ef.EndFunctionDx4.NextHop) 143 if err != nil { 144 return scheduler.NewInvalidValueError(errors.Errorf("failed to parse next hop %s, should be a valid ipv4 address: %v", ef.EndFunctionDx4.NextHop, err), "EndFunctionDX4.NextHop") 145 } 146 case *srv6.LocalSID_EndFunctionDx6: 147 _, err := ParseIPv6(ef.EndFunctionDx6.NextHop) 148 if err != nil { 149 return scheduler.NewInvalidValueError(errors.Errorf("failed to parse next hop %s, should be a valid ipv6 address: %v", ef.EndFunctionDx6.NextHop, err), "EndFunctionDX6.NextHop") 150 } 151 case *srv6.LocalSID_EndFunctionDt4: 152 case *srv6.LocalSID_EndFunctionDt6: 153 case *srv6.LocalSID_EndFunctionAd: 154 if strings.TrimSpace(ef.EndFunctionAd.L3ServiceAddress) == "" { 155 return nil // l2 service 156 } 157 // l3 service 158 ip := net.ParseIP(ef.EndFunctionAd.L3ServiceAddress) 159 if ip == nil { 160 return scheduler.NewInvalidValueError(errors.Errorf("failed to parse service address %s, should be a valid ip address(ipv4 or ipv6) or empty(case of l2 service): %v", ef.EndFunctionAd.L3ServiceAddress, err), "EndFunctionAD.L3ServiceAddress") 161 } 162 case nil: 163 return scheduler.NewInvalidValueError(errors.New("end function must be provided"), "endfunction") 164 default: 165 return scheduler.NewInvalidValueError(errors.Errorf("end function has unexpected model link type %T", ef), "endfunction") 166 } 167 168 return nil 169 } 170 171 // Create creates new Local SID into VPP using VPP's binary api 172 func (d *LocalSIDDescriptor) Create(key string, value *srv6.LocalSID) (metadata interface{}, err error) { 173 if err := d.srHandler.AddLocalSid(value); err != nil { 174 return nil, errors.Errorf("failed to add local sid %s: %v", value.GetSid(), err) 175 } 176 return nil, nil 177 } 178 179 // Delete removes Local SID from VPP using VPP's binary api 180 func (d *LocalSIDDescriptor) Delete(key string, value *srv6.LocalSID, metadata interface{}) error { 181 if err := d.srHandler.DeleteLocalSid(value); err != nil { 182 return errors.Errorf("failed to delete local sid %s: %v", value.GetSid(), err) 183 } 184 return nil 185 } 186 187 // Dependencies for LocalSIDs are represented by interface (interface in up state) 188 func (d *LocalSIDDescriptor) Dependencies(key string, localSID *srv6.LocalSID) (dependencies []scheduler.Dependency) { 189 dependencies = append(dependencies, scheduler.Dependency{ 190 Label: localsidInstallationVRFDep, 191 Key: vpp_l3.VrfTableKey(localSID.InstallationVrfId, vpp_l3.VrfTable_IPV6), 192 }) 193 194 switch ef := localSID.EndFunction.(type) { 195 case *srv6.LocalSID_EndFunctionT: 196 if ef.EndFunctionT.VrfId != 0 { // VRF 0 is in VPP by default 197 dependencies = append(dependencies, scheduler.Dependency{ 198 Label: localsidLookupVRFDep, 199 Key: vpp_l3.VrfTableKey(ef.EndFunctionT.VrfId, vpp_l3.VrfTable_IPV6), // T refers to IPv6 VRF table 200 }) 201 } 202 case *srv6.LocalSID_EndFunctionX: 203 dependencies = append(dependencies, scheduler.Dependency{ 204 Label: localsidOutgoingInterfaceDep, 205 Key: interfaces.InterfaceKey(ef.EndFunctionX.OutgoingInterface), 206 }) 207 case *srv6.LocalSID_EndFunctionDx2: 208 dependencies = append(dependencies, scheduler.Dependency{ 209 Label: localsidOutgoingInterfaceDep, 210 Key: interfaces.InterfaceKey(ef.EndFunctionDx2.OutgoingInterface), 211 }) 212 case *srv6.LocalSID_EndFunctionDx4: 213 dependencies = append(dependencies, scheduler.Dependency{ 214 Label: localsidOutgoingInterfaceDep, 215 Key: interfaces.InterfaceKey(ef.EndFunctionDx4.OutgoingInterface), 216 }) 217 case *srv6.LocalSID_EndFunctionDx6: 218 dependencies = append(dependencies, scheduler.Dependency{ 219 Label: localsidOutgoingInterfaceDep, 220 Key: interfaces.InterfaceKey(ef.EndFunctionDx6.OutgoingInterface), 221 }) 222 case *srv6.LocalSID_EndFunctionDt4: 223 if ef.EndFunctionDt4.VrfId != 0 { // VRF 0 is in VPP by default 224 dependencies = append(dependencies, scheduler.Dependency{ 225 Label: localsidLookupVRFDep, 226 Key: vpp_l3.VrfTableKey(ef.EndFunctionDt4.VrfId, vpp_l3.VrfTable_IPV4), // we want ipv4 VRF because DT4 227 }) 228 } 229 case *srv6.LocalSID_EndFunctionDt6: 230 if ef.EndFunctionDt6.VrfId != 0 { // VRF 0 is in VPP by default 231 dependencies = append(dependencies, scheduler.Dependency{ 232 Label: localsidLookupVRFDep, 233 Key: vpp_l3.VrfTableKey(ef.EndFunctionDt6.VrfId, vpp_l3.VrfTable_IPV6), // we want ipv6 VRF because DT6 234 }) 235 } 236 case *srv6.LocalSID_EndFunctionAd: 237 dependencies = append(dependencies, scheduler.Dependency{ 238 Label: localsidOutgoingInterfaceDep, 239 Key: interfaces.InterfaceKey(ef.EndFunctionAd.OutgoingInterface), 240 }) 241 dependencies = append(dependencies, scheduler.Dependency{ 242 Label: localsidIncomingInterfaceDep, 243 Key: interfaces.InterfaceKey(ef.EndFunctionAd.IncomingInterface), 244 }) 245 } 246 247 return dependencies 248 } 249 250 // ParseIPv6 parses string <str> to IPv6 address (including IPv4 address converted to IPv6 address) 251 func ParseIPv6(str string) (net.IP, error) { 252 ip := net.ParseIP(str) 253 if ip == nil { 254 return nil, errors.Errorf(" %q is not ip address", str) 255 } 256 ipv6 := ip.To16() 257 if ipv6 == nil { 258 return nil, errors.Errorf(" %q is not ipv6 address", str) 259 } 260 return ipv6, nil 261 } 262 263 // ParseIPv4 parses string <str> to IPv4 address 264 func ParseIPv4(str string) (net.IP, error) { 265 ip := net.ParseIP(str) 266 if ip == nil { 267 return nil, errors.Errorf(" %q is not ip address", str) 268 } 269 ipv4 := ip.To4() 270 if ipv4 == nil { 271 return nil, errors.Errorf(" %q is not ipv4 address", str) 272 } 273 return ipv4, nil 274 } 275 276 func equivalentSIDs(sid1, sid2 string) bool { 277 return equivalentIPv6(sid1, sid2) 278 } 279 280 func equivalentIPv6(ip1Str, ip2str string) bool { 281 ip1, err1 := ParseIPv6(ip1Str) 282 ip2, err2 := ParseIPv6(ip2str) 283 if err1 != nil || err2 != nil { // one of values is invalid, but that will handle validator -> compare by strings 284 return equivalentTrimmedLowered(ip1Str, ip2str) 285 } 286 return ip1.Equal(ip2) // form doesn't matter, are they representig the same IP value ? 287 } 288 289 func equivalentIPv4(ip1str, ip2str string) bool { 290 ip1, err1 := ParseIPv4(ip1str) 291 ip2, err2 := ParseIPv4(ip2str) 292 if err1 != nil || err2 != nil { // one of values is invalid, but that will handle validator -> compare by strings 293 return equivalentTrimmedLowered(ip1str, ip2str) 294 } 295 return ip1.Equal(ip2) // form doesn't matter, are they representig the same IP value ? 296 } 297 298 func equivalentTrimmedLowered(str1, str2 string) bool { 299 return strings.TrimSpace(strings.ToLower(str1)) == strings.TrimSpace(strings.ToLower(str2)) 300 }