go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/l3plugin/descriptor/l3xc.go (about) 1 // Copyright (c) 2018 Cisco 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 "context" 19 "fmt" 20 "net" 21 "strconv" 22 "strings" 23 24 "github.com/pkg/errors" 25 "go.ligato.io/cn-infra/v2/logging" 26 "google.golang.org/protobuf/proto" 27 28 "go.ligato.io/vpp-agent/v3/pkg/models" 29 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 30 ifdescriptor "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor" 31 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" 32 "go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/descriptor/adapter" 33 "go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/vppcalls" 34 interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces" 35 l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3" 36 ) 37 38 const ( 39 // L3XCDescriptorName is the name of the descriptor. 40 L3XCDescriptorName = "vpp-l3xc" 41 42 // dependency labels 43 l3xcTargetInterfaceDep = "target-interface-exists" 44 l3xcPathInterfaceDep = "outgoing-interface-exists" 45 ) 46 47 // L3XCDescriptor teaches KVScheduler how to configure VPP L3XCs. 48 type L3XCDescriptor struct { 49 log logging.Logger 50 l3xcHandler vppcalls.L3XCVppAPI 51 ifIndexes ifaceidx.IfaceMetadataIndex 52 } 53 54 // NewL3XCDescriptor creates a new instance of the L3XCDescriptor. 55 func NewL3XCDescriptor(l3xcHandler vppcalls.L3XCVppAPI, ifIndexes ifaceidx.IfaceMetadataIndex, 56 log logging.PluginLogger, 57 ) *kvs.KVDescriptor { 58 59 ctx := &L3XCDescriptor{ 60 ifIndexes: ifIndexes, 61 l3xcHandler: l3xcHandler, 62 log: log.NewLogger("l3xc-descriptor"), 63 } 64 65 typedDescr := &adapter.L3XCDescriptor{ 66 Name: L3XCDescriptorName, 67 NBKeyPrefix: l3.ModelL3XC.KeyPrefix(), 68 ValueTypeName: l3.ModelL3XC.ProtoName(), 69 KeySelector: l3.ModelL3XC.IsKeyValid, 70 KeyLabel: l3.ModelL3XC.StripKeyPrefix, 71 ValueComparator: ctx.EquivalentL3XCs, 72 Validate: ctx.Validate, 73 Create: ctx.Create, 74 Update: ctx.Update, 75 Delete: ctx.Delete, 76 Retrieve: ctx.Retrieve, 77 Dependencies: ctx.Dependencies, 78 RetrieveDependencies: []string{ifdescriptor.InterfaceDescriptorName}, 79 } 80 return adapter.NewL3XCDescriptor(typedDescr) 81 } 82 83 // EquivalentL3XCs is comparison function for L3XC entries. 84 func (d *L3XCDescriptor) EquivalentL3XCs(key string, oldL3XC, newL3XC *l3.L3XConnect) bool { 85 return proto.Equal(oldL3XC, newL3XC) 86 } 87 88 // Validate returns if given l3xc is valid. 89 func (d *L3XCDescriptor) Validate(key string, l3xc *l3.L3XConnect) error { 90 if l3xc.Interface == "" { 91 return errors.Errorf("no interface defined") 92 } 93 if len(l3xc.Paths) == 0 { 94 return errors.Errorf("no paths defined") 95 } 96 return nil 97 } 98 99 // Dependencies lists dependencies for a VPP L3XC entry. 100 func (d *L3XCDescriptor) Dependencies(key string, l3xc *l3.L3XConnect) (deps []kvs.Dependency) { 101 // the outgoing interface must exist 102 if l3xc.Interface != "" { 103 deps = append(deps, kvs.Dependency{ 104 Label: l3xcTargetInterfaceDep, 105 Key: interfaces.InterfaceKey(l3xc.Interface), 106 }) 107 } 108 for _, path := range l3xc.Paths { 109 deps = append(deps, kvs.Dependency{ 110 Label: l3xcPathInterfaceDep, 111 Key: interfaces.InterfaceKey(path.OutgoingInterface), 112 }) 113 } 114 return deps 115 } 116 117 // Create adds VPP L3XC entry. 118 func (d *L3XCDescriptor) Create(key string, l3xc *l3.L3XConnect) (interface{}, error) { 119 return d.update(key, l3xc) 120 } 121 122 // Update updates VPP L3XC entry. 123 func (d *L3XCDescriptor) Update(key string, oldL3XC, newL3XC *l3.L3XConnect, oldMeta interface{}) (interface{}, error) { 124 return d.update(key, newL3XC) 125 } 126 127 func (d *L3XCDescriptor) update(key string, l3xc *l3.L3XConnect) (interface{}, error) { 128 ctx := context.TODO() 129 130 var swIfIndex uint32 131 if strings.HasPrefix(l3xc.Interface, "MISSING-") { 132 idx := strings.TrimPrefix(l3xc.Interface, "MISSING-") 133 x, _ := strconv.ParseUint(idx, 10, 32) 134 swIfIndex = uint32(x) 135 } else { 136 meta, found := d.ifIndexes.LookupByName(l3xc.Interface) 137 if !found { 138 return nil, errors.Errorf("interface %s not found", l3xc.Interface) 139 } 140 swIfIndex = meta.SwIfIndex 141 } 142 143 paths := make([]vppcalls.Path, len(l3xc.Paths)) 144 for i, p := range l3xc.Paths { 145 pmeta, found := d.ifIndexes.LookupByName(p.OutgoingInterface) 146 if !found { 147 return nil, errors.Errorf("interface %s from path #%d not found", p.OutgoingInterface, i) 148 } 149 paths[i] = vppcalls.Path{ 150 SwIfIndex: pmeta.SwIfIndex, 151 Weight: uint8(p.Weight), 152 Preference: uint8(p.Preference), 153 NextHop: net.ParseIP(p.NextHopAddr), 154 } 155 } 156 157 if err := d.l3xcHandler.UpdateL3XC(ctx, &vppcalls.L3XC{ 158 SwIfIndex: swIfIndex, 159 IsIPv6: l3xc.Protocol == l3.L3XConnect_IPV6, 160 Paths: paths, 161 }); err != nil { 162 return nil, err 163 } 164 165 return nil, nil 166 } 167 168 // Delete removes VPP L3XC entry. 169 func (d *L3XCDescriptor) Delete(key string, l3xc *l3.L3XConnect, metadata interface{}) error { 170 ctx := context.TODO() 171 172 var swIfIndex uint32 173 if strings.HasPrefix(l3xc.Interface, "MISSING-") { 174 idx := strings.TrimPrefix(l3xc.Interface, "MISSING-") 175 x, _ := strconv.ParseUint(idx, 10, 32) 176 swIfIndex = uint32(x) 177 } else { 178 meta, found := d.ifIndexes.LookupByName(l3xc.Interface) 179 if !found { 180 return errors.Errorf("interface %s not found", l3xc.Interface) 181 } 182 swIfIndex = meta.SwIfIndex 183 } 184 isIPv6 := l3xc.Protocol == l3.L3XConnect_IPV6 185 186 if err := d.l3xcHandler.DeleteL3XC(ctx, swIfIndex, isIPv6); err != nil { 187 return err 188 } 189 190 return nil 191 } 192 193 // Retrieve returns all L3XC entries associated with interfaces managed by this agent. 194 func (d *L3XCDescriptor) Retrieve(correlate []adapter.L3XCKVWithMetadata) ( 195 retrieved []adapter.L3XCKVWithMetadata, err error, 196 ) { 197 ctx := context.TODO() 198 199 l3xcEntries, err := d.l3xcHandler.DumpAllL3XC(ctx) 200 if err != nil { 201 return nil, errors.Errorf("dumping VPP L3XCs failed: %v", err) 202 } 203 204 for _, l3xc := range l3xcEntries { 205 ifName, _, exists := d.ifIndexes.LookupBySwIfIndex(l3xc.SwIfIndex) 206 if !exists { 207 ifName = fmt.Sprintf("MISSING-%d", l3xc.SwIfIndex) 208 d.log.Warnf("L3XC dump: interface index %d not found", l3xc.SwIfIndex) 209 210 } 211 ipProto := l3.L3XConnect_IPV4 212 if l3xc.IsIPv6 { 213 ipProto = l3.L3XConnect_IPV6 214 } 215 paths := make([]*l3.L3XConnect_Path, len(l3xc.Paths)) 216 for i, p := range l3xc.Paths { 217 ifNamePath, _, exists := d.ifIndexes.LookupBySwIfIndex(p.SwIfIndex) 218 if !exists { 219 ifNamePath = fmt.Sprintf("MISSING-%d", p.SwIfIndex) 220 d.log.Warnf("L3XC dump: interface index %d for path #%d not found", p.SwIfIndex, i) 221 } 222 paths[i] = &l3.L3XConnect_Path{ 223 OutgoingInterface: ifNamePath, 224 NextHopAddr: p.NextHop.String(), 225 Weight: uint32(p.Weight), 226 Preference: uint32(p.Preference), 227 } 228 } 229 value := &l3.L3XConnect{ 230 Interface: ifName, 231 Protocol: ipProto, 232 Paths: paths, 233 } 234 retrieved = append(retrieved, adapter.L3XCKVWithMetadata{ 235 Key: models.Key(value), 236 Value: value, 237 Origin: kvs.FromNB, 238 }) 239 } 240 241 return retrieved, nil 242 }