go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/l2plugin/descriptor/bridgedomain.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 "fmt" 19 "net" 20 "strings" 21 22 "github.com/pkg/errors" 23 "go.ligato.io/cn-infra/v2/idxmap" 24 "go.ligato.io/cn-infra/v2/logging" 25 26 "go.ligato.io/vpp-agent/v3/pkg/idxvpp" 27 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 28 vpp_ifdescriptor "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor" 29 "go.ligato.io/vpp-agent/v3/plugins/vpp/l2plugin/descriptor/adapter" 30 "go.ligato.io/vpp-agent/v3/plugins/vpp/l2plugin/vppcalls" 31 l2 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l2" 32 ) 33 34 const ( 35 // BridgeDomainDescriptorName is the name of the descriptor for VPP bridge domains. 36 BridgeDomainDescriptorName = "vpp-bridge-domain" 37 38 // prefix prepended to internal names of untagged bridge domains to construct 39 // unique logical names 40 untaggedBDPreffix = "UNTAGGED-" 41 ) 42 43 // A list of non-retriable errors: 44 var ( 45 // ErrBridgeDomainWithoutName is returned when VPP bridge domain configuration 46 // has undefined Name attribute. 47 ErrBridgeDomainWithoutName = errors.New("VPP bridge domain defined without logical name") 48 49 // ErrBridgeDomainWithMultipleBVI is returned when bridge domain is defined with 50 // multiple BVI interfaces. 51 ErrBridgeDomainWithMultipleBVI = errors.New("VPP bridge domain defined with mutliple BVIs") 52 ) 53 54 // BridgeDomainDescriptor teaches KVScheduler how to configure VPP bridge domains. 55 type BridgeDomainDescriptor struct { 56 // dependencies 57 log logging.Logger 58 bdHandler vppcalls.BridgeDomainVppAPI 59 60 // runtime 61 bdIDSeq uint32 62 } 63 64 // NewBridgeDomainDescriptor creates a new instance of the BridgeDomain descriptor. 65 func NewBridgeDomainDescriptor(bdHandler vppcalls.BridgeDomainVppAPI, log logging.PluginLogger) *BridgeDomainDescriptor { 66 67 return &BridgeDomainDescriptor{ 68 bdHandler: bdHandler, 69 log: log.NewLogger("bd-descriptor"), 70 bdIDSeq: 1, 71 } 72 } 73 74 // GetDescriptor returns descriptor suitable for registration (via adapter) with 75 // the KVScheduler. 76 func (d *BridgeDomainDescriptor) GetDescriptor() *adapter.BridgeDomainDescriptor { 77 return &adapter.BridgeDomainDescriptor{ 78 Name: BridgeDomainDescriptorName, 79 NBKeyPrefix: l2.ModelBridgeDomain.KeyPrefix(), 80 ValueTypeName: l2.ModelBridgeDomain.ProtoName(), 81 KeySelector: l2.ModelBridgeDomain.IsKeyValid, 82 KeyLabel: l2.ModelBridgeDomain.StripKeyPrefix, 83 ValueComparator: d.EquivalentBridgeDomains, 84 WithMetadata: true, 85 MetadataMapFactory: d.MetadataFactory, 86 Validate: d.Validate, 87 Create: d.Create, 88 Delete: d.Delete, 89 Update: d.Update, 90 UpdateWithRecreate: d.UpdateWithRecreate, 91 Retrieve: d.Retrieve, 92 DerivedValues: d.DerivedValues, 93 RetrieveDependencies: []string{vpp_ifdescriptor.InterfaceDescriptorName}, 94 } 95 } 96 97 // EquivalentBridgeDomains is case-insensitive comparison function for 98 // l2.BridgeDomain, also ignoring the order of assigned ARP termination entries. 99 func (d *BridgeDomainDescriptor) EquivalentBridgeDomains(key string, oldBD, newBD *l2.BridgeDomain) bool { 100 // BD parameters 101 if !equalBDParameters(oldBD, newBD) { 102 return false 103 } 104 105 // ARP termination entries 106 obsoleteARPs, newARPs := calculateARPDiff(oldBD.GetArpTerminationTable(), newBD.GetArpTerminationTable()) 107 return len(obsoleteARPs) == 0 && len(newARPs) == 0 108 } 109 110 // MetadataFactory is a factory for index-map customized for VPP bridge domains. 111 func (d *BridgeDomainDescriptor) MetadataFactory() idxmap.NamedMappingRW { 112 return idxvpp.NewNameToIndex(d.log, "vpp-bd-index", nil) 113 } 114 115 // Validate validates VPP bridge domain configuration. 116 func (d *BridgeDomainDescriptor) Validate(key string, bd *l2.BridgeDomain) error { 117 if bd.Name == "" { 118 return kvs.NewInvalidValueError(ErrBridgeDomainWithoutName, "name") 119 } 120 121 // check that BD has defined at most one BVI 122 var hasBVI bool 123 for _, bdIface := range bd.Interfaces { 124 if bdIface.BridgedVirtualInterface { 125 if hasBVI { 126 return kvs.NewInvalidValueError(ErrBridgeDomainWithMultipleBVI, 127 "interfaces.bridged_virtual_interface") 128 } 129 hasBVI = true 130 } 131 } 132 return nil 133 } 134 135 // Create adds new bridge domain. 136 func (d *BridgeDomainDescriptor) Create(key string, bd *l2.BridgeDomain) (metadata *idxvpp.OnlyIndex, err error) { 137 // allocate new bridge domain ID 138 bdIdx := d.bdIDSeq 139 d.bdIDSeq++ 140 141 // create the bridge domain 142 err = d.bdHandler.AddBridgeDomain(bdIdx, bd) 143 if err != nil { 144 // Note: d.bdIDSeq will be refreshed by Dump 145 d.log.Error(err) 146 return nil, err 147 } 148 149 // add ARP termination entries 150 for _, arp := range bd.ArpTerminationTable { 151 if err := d.bdHandler.AddArpTerminationTableEntry(bdIdx, arp.PhysAddress, arp.IpAddress); err != nil { 152 d.log.Error(err) 153 return nil, err 154 } 155 } 156 157 // fill the metadata 158 metadata = &idxvpp.OnlyIndex{ 159 Index: bdIdx, 160 } 161 return metadata, nil 162 } 163 164 // Delete removes VPP bridge domain. 165 func (d *BridgeDomainDescriptor) Delete(key string, bd *l2.BridgeDomain, metadata *idxvpp.OnlyIndex) error { 166 err := d.bdHandler.DeleteBridgeDomain(metadata.GetIndex()) 167 if err != nil { 168 d.log.Error(err) 169 } 170 return err 171 } 172 173 // UpdateWithRecreate returns true if bridge domain base parameters are different. 174 func (d *BridgeDomainDescriptor) UpdateWithRecreate(key string, oldBD, newBD *l2.BridgeDomain, metadata *idxvpp.OnlyIndex) bool { 175 return !equalBDParameters(oldBD, newBD) 176 } 177 178 // Update is able to change ARP termination entries. 179 func (d *BridgeDomainDescriptor) Update(key string, oldBD, newBD *l2.BridgeDomain, oldMetadata *idxvpp.OnlyIndex) (newMetadata *idxvpp.OnlyIndex, err error) { 180 // update ARP termination entries 181 bdIdx := oldMetadata.Index 182 obsoleteARPs, newARPs := calculateARPDiff(oldBD.GetArpTerminationTable(), newBD.GetArpTerminationTable()) 183 for _, arp := range obsoleteARPs { // remove obsolete first to avoid collisions 184 if err := d.bdHandler.RemoveArpTerminationTableEntry(bdIdx, arp.PhysAddress, arp.IpAddress); err != nil { 185 d.log.Error(err) 186 return oldMetadata, err 187 } 188 } 189 for _, arp := range newARPs { 190 if err := d.bdHandler.AddArpTerminationTableEntry(bdIdx, arp.PhysAddress, arp.IpAddress); err != nil { 191 d.log.Error(err) 192 return oldMetadata, err 193 } 194 } 195 196 return oldMetadata, nil 197 } 198 199 // Retrieve returns all configured VPP bridge domains. 200 func (d *BridgeDomainDescriptor) Retrieve(correlate []adapter.BridgeDomainKVWithMetadata) (retrieved []adapter.BridgeDomainKVWithMetadata, err error) { 201 // d.bdIDSeq will be refreshed 202 var bdIDSeq uint32 = 1 203 204 // sequence number for untagged interfaces 205 var untaggedSeq int 206 207 // dump bridge domains 208 bridgeDomains, err := d.bdHandler.DumpBridgeDomains() 209 if err != nil { 210 d.log.Error(err) 211 return retrieved, err 212 } 213 for _, bd := range bridgeDomains { 214 // make sure that bdIDSeq is larger than any of the existing indexes 215 if bd.Meta.BdID >= bdIDSeq { 216 bdIDSeq = bd.Meta.BdID + 1 217 } 218 219 // handle untagged bridge domain - construct name that is unlikely to 220 // collide with NB, thus the bridge domain will get removed by resync 221 if bd.Bd.Name == "" { 222 bd.Bd.Name = fmt.Sprintf("%s%d", untaggedBDPreffix, untaggedSeq) 223 untaggedSeq++ 224 } 225 226 retrieved = append(retrieved, adapter.BridgeDomainKVWithMetadata{ 227 Key: l2.BridgeDomainKey(bd.Bd.Name), 228 Value: bd.Bd, 229 Metadata: &idxvpp.OnlyIndex{Index: bd.Meta.BdID}, 230 Origin: kvs.FromNB, 231 }) 232 } 233 234 // update d.bdIDSeq 235 d.bdIDSeq = bdIDSeq 236 237 return retrieved, nil 238 } 239 240 // DerivedValues derives l2.BridgeDomain_Interface for every interface assigned 241 // to the bridge domain. 242 func (d *BridgeDomainDescriptor) DerivedValues(key string, bd *l2.BridgeDomain) (derValues []kvs.KeyValuePair) { 243 // BD interfaces 244 for _, bdIface := range bd.Interfaces { 245 derValues = append(derValues, kvs.KeyValuePair{ 246 Key: l2.BDInterfaceKey(bd.Name, bdIface.Name), 247 Value: bdIface, 248 }) 249 } 250 return derValues 251 } 252 253 // equalBDParameters compares all base bridge domain parameters for equality. 254 func equalBDParameters(bd1, bd2 *l2.BridgeDomain) bool { 255 return bd1.ArpTermination == bd2.ArpTermination && bd1.Flood == bd2.Flood && 256 bd1.Forward == bd2.Forward && bd1.Learn == bd2.Learn && bd1.MacAge == bd2.MacAge && 257 bd1.UnknownUnicastFlood == bd2.UnknownUnicastFlood 258 } 259 260 // calculateARPDiff compares two sets of ARP termination entries. 261 func calculateARPDiff(oldARPs, newARPs []*l2.BridgeDomain_ArpTerminationEntry) (toRemove, toAdd []*l2.BridgeDomain_ArpTerminationEntry) { 262 // Resolve ARPs to add 263 for _, newARP := range newARPs { 264 var exists bool 265 for _, oldARP := range oldARPs { 266 if equalTerminationARPs(oldARP, newARP) { 267 exists = true 268 break 269 } 270 } 271 if !exists { 272 toAdd = append(toAdd, newARP) 273 } 274 } 275 // Resolve ARPs to remove 276 for _, oldARP := range oldARPs { 277 var exists bool 278 for _, newARP := range newARPs { 279 if equalTerminationARPs(oldARP, newARP) { 280 exists = true 281 break 282 } 283 } 284 if !exists { 285 toRemove = append(toRemove, oldARP) 286 } 287 } 288 289 return toAdd, toRemove 290 } 291 292 // equalTerminationARPs compares two termination ARP entries for equality. 293 func equalTerminationARPs(arp1, arp2 *l2.BridgeDomain_ArpTerminationEntry) bool { 294 // compare MAC addresses 295 if !strings.EqualFold(arp1.PhysAddress, arp2.PhysAddress) { 296 return false 297 } 298 299 // compare IP addresses 300 ip1 := net.ParseIP(arp1.IpAddress) 301 ip2 := net.ParseIP(arp2.IpAddress) 302 if ip1 == nil || ip2 == nil { 303 // if parsing fails, compare as strings 304 return strings.EqualFold(arp1.IpAddress, arp2.IpAddress) 305 } 306 return ip1.Equal(ip2) 307 }