go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/l3plugin/descriptor/vrrp.go (about) 1 // Copyright (c) 2020 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 "math" 19 "net" 20 21 "github.com/pkg/errors" 22 "google.golang.org/protobuf/proto" 23 24 "go.ligato.io/cn-infra/v2/logging" 25 26 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 27 ifdescriptor "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/descriptor" 28 "go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/descriptor/adapter" 29 "go.ligato.io/vpp-agent/v3/plugins/vpp/l3plugin/vppcalls" 30 interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces" 31 l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3" 32 ) 33 34 const ( 35 // VrrpDescriptorName is the name of the descriptor. 36 VrrpDescriptorName = "vrrp" 37 38 // Dependency labels. 39 vrrpEntryInterfaceDep = "interface-exists" 40 vrrpDescriptorLoggerName = "vrrp-descriptor" 41 42 // The minimum value in milliseconds that can be used as interval. 43 centisecondInMilliseconds = 10 44 ) 45 46 // A list of validation errors. 47 var ( 48 ErrMissingInterface = errors.New("missing interface") 49 ErrInvalidAddrNum = errors.New("addrs quantity should be > 0 && <= 255") 50 ErrInvalidVrID = errors.New("vr_id should be > 0 && <= 255") 51 ErrInvalidPriority = errors.New("priority should be > 0 && <= 255") 52 ErrInvalidInterval = errors.New("interval should be > 0 && <= 65535") 53 ErrInvalidVrrpIP = errors.New("invalid IP address") 54 ErrInvalidIPVer = errors.New("ipv6_flag does not correspond to IP version of the provided address") 55 ErrInvalidInterface = errors.New("interface does not exist") 56 ) 57 58 // VrrpDescriptor teaches KVScheduler how to configure VPP VRRPs. 59 type VrrpDescriptor struct { 60 log logging.Logger 61 vrrpHandler vppcalls.VrrpVppAPI 62 } 63 64 // NewVrrpDescriptor creates a new instance of the VrrpDescriptor. 65 func NewVrrpDescriptor(vrrpHandler vppcalls.VrrpVppAPI, 66 log logging.PluginLogger) *kvs.KVDescriptor { 67 ctx := &VrrpDescriptor{ 68 log: log.NewLogger(vrrpDescriptorLoggerName), 69 vrrpHandler: vrrpHandler, 70 } 71 72 typedDescr := &adapter.VRRPEntryDescriptor{ 73 Name: VrrpDescriptorName, 74 NBKeyPrefix: l3.ModelVRRPEntry.KeyPrefix(), 75 ValueTypeName: l3.ModelVRRPEntry.ProtoName(), 76 KeySelector: l3.ModelVRRPEntry.IsKeyValid, 77 KeyLabel: l3.ModelVRRPEntry.StripKeyPrefix, 78 ValueComparator: ctx.EquivalentVRRPs, 79 Create: ctx.Create, 80 Delete: ctx.Delete, 81 Update: ctx.Update, 82 UpdateWithRecreate: ctx.UpdateWithRecreate, 83 Validate: ctx.Validate, 84 Retrieve: ctx.Retrieve, 85 Dependencies: ctx.Dependencies, 86 RetrieveDependencies: []string{ifdescriptor.InterfaceDescriptorName}, 87 } 88 return adapter.NewVRRPEntryDescriptor(typedDescr) 89 } 90 91 // Validate returns error if given VRRP is not valid. 92 func (d *VrrpDescriptor) Validate(key string, vrrp *l3.VRRPEntry) error { 93 if vrrp.Interface == "" { 94 return kvs.NewInvalidValueError(ErrMissingInterface, "interface") 95 } 96 97 if len(vrrp.IpAddresses) > math.MaxUint8 || len(vrrp.IpAddresses) == 0 { 98 return kvs.NewInvalidValueError(ErrInvalidAddrNum, "ip_addresses") 99 } 100 101 if vrrp.GetVrId() > math.MaxUint8 || vrrp.GetVrId() == 0 { 102 return kvs.NewInvalidValueError(ErrInvalidVrID, "vr_id") 103 } 104 105 if vrrp.GetPriority() > math.MaxUint8 || vrrp.GetPriority() == 0 { 106 return kvs.NewInvalidValueError(ErrInvalidPriority, "priority") 107 } 108 109 if vrrp.GetInterval() > math.MaxUint16 || vrrp.GetInterval() < centisecondInMilliseconds { 110 return kvs.NewInvalidValueError(ErrInvalidInterval, "interval") 111 } 112 113 var ip net.IP 114 var isIpv6 bool 115 for idx, addr := range vrrp.IpAddresses { 116 ip = net.ParseIP(addr) 117 if ip == nil { 118 return kvs.NewInvalidValueError(ErrInvalidVrrpIP, "ip_addresses") 119 } 120 121 if idx == 0 && ip.To4() == nil { 122 isIpv6 = true 123 continue 124 } 125 126 if ip.To4() == nil && !isIpv6 || ip.To4() != nil && isIpv6 { 127 return kvs.NewInvalidValueError(ErrInvalidIPVer, "ip_addresses") 128 } 129 } 130 return nil 131 } 132 133 // Create adds VPP VRRP entry. 134 func (d *VrrpDescriptor) Create(key string, vrrp *l3.VRRPEntry) (interface{}, error) { 135 if err := d.vrrpHandler.VppAddVrrp(vrrp); err != nil { 136 if errors.Is(vppcalls.ErrVRRPUnsupported, err) { 137 d.log.Debugf("Unsupported action: %v", err) 138 } 139 return nil, err 140 } 141 142 if vrrp.Enabled { 143 if err := d.vrrpHandler.VppStartVrrp(vrrp); err != nil { 144 return nil, err 145 } 146 } 147 return nil, nil 148 } 149 150 // Delete removes VPP VRRP entry. 151 func (d *VrrpDescriptor) Delete(key string, vrrp *l3.VRRPEntry, metadata interface{}) error { 152 if vrrp.Enabled { 153 if err := d.vrrpHandler.VppStopVrrp(vrrp); err != nil { 154 if errors.Is(vppcalls.ErrVRRPUnsupported, err) { 155 d.log.Debugf("Unsupported action: %v", err) 156 } 157 return err 158 } 159 } 160 if err := d.vrrpHandler.VppDelVrrp(vrrp); err != nil { 161 return err 162 } 163 return nil 164 } 165 166 // UpdateWithRecreate returns true if a VRRP update needs to be performed via re-crate. 167 func (d *VrrpDescriptor) UpdateWithRecreate(_ string, oldVRRPEntry, newVRRPEntry *l3.VRRPEntry, _ interface{}) bool { 168 if oldVRRPEntry.Enabled == newVRRPEntry.Enabled { 169 // Something has changed, but it is not about enabled/disabled. 170 return true 171 } 172 173 return !allFieldsWhithoutEnabledEquals(oldVRRPEntry, newVRRPEntry) 174 } 175 176 // Update updates VPP VRRP entry. 177 func (d *VrrpDescriptor) Update(_ string, oldVRRPEntry, newVRRPEntry *l3.VRRPEntry, _ interface{}) ( 178 _ interface{}, err error) { 179 180 if newVRRPEntry.Enabled { 181 err = d.vrrpHandler.VppStartVrrp(newVRRPEntry) 182 } else { 183 err = d.vrrpHandler.VppStopVrrp(newVRRPEntry) 184 } 185 return nil, err 186 } 187 188 // Dependencies lists dependencies for a VPP VRRP entry. 189 func (d *VrrpDescriptor) Dependencies(key string, vrrp *l3.VRRPEntry) (deps []kvs.Dependency) { 190 // the interface must exist 191 if vrrp.Interface != "" { 192 deps = append(deps, kvs.Dependency{ 193 Label: vrrpEntryInterfaceDep, 194 Key: interfaces.InterfaceKey(vrrp.Interface), 195 }) 196 } 197 return deps 198 } 199 200 // Retrieve returns all configured VPP VRRP entries. 201 func (d *VrrpDescriptor) Retrieve(correlate []adapter.VRRPEntryKVWithMetadata) ( 202 retrieved []adapter.VRRPEntryKVWithMetadata, err error, 203 ) { 204 entries, err := d.vrrpHandler.DumpVrrpEntries() 205 if err != nil { 206 return nil, err 207 } 208 209 for _, entry := range entries { 210 retrieved = append(retrieved, adapter.VRRPEntryKVWithMetadata{ 211 Key: l3.VrrpEntryKey(entry.Vrrp.Interface, entry.Vrrp.VrId), 212 Value: entry.Vrrp, 213 Origin: kvs.FromNB, 214 }) 215 } 216 return retrieved, nil 217 } 218 219 // EquivalentVRRPs is a comparison function for l3.VRRPEntry. 220 func (d *VrrpDescriptor) EquivalentVRRPs(_ string, oldVRRPEntry, newVRRPEntry *l3.VRRPEntry) bool { 221 if proto.Equal(oldVRRPEntry, newVRRPEntry) { 222 return true 223 } 224 if oldVRRPEntry.Enabled != newVRRPEntry.Enabled { 225 return false 226 } 227 return allFieldsWhithoutEnabledEquals(oldVRRPEntry, newVRRPEntry) 228 } 229 230 // allFieldsWhithoutEnabledEquals returns true if all entrys' fields are equal, 231 // without checking the Enabled field. 232 func allFieldsWhithoutEnabledEquals(entry1, entry2 *l3.VRRPEntry) bool { 233 if entry1.Interface != entry2.Interface || 234 !intervalEquals(entry1.Interval, entry2.Interval) || 235 entry1.Priority != entry2.Priority || 236 entry1.VrId != entry2.VrId || 237 entry1.Accept != entry2.Accept || 238 entry1.Preempt != entry2.Preempt || 239 entry1.Unicast != entry2.Unicast || 240 len(entry1.IpAddresses) != len(entry2.IpAddresses) { 241 return false 242 } 243 244 for i := 0; i < len(entry1.IpAddresses); i++ { 245 if entry1.IpAddresses[i] != entry2.IpAddresses[i] { 246 return false 247 } 248 } 249 return true 250 } 251 252 // intervalEquals returns true if i1 and i2 are equal in centisonds. 253 func intervalEquals(i1, i2 uint32) bool { 254 return i1/centisecondInMilliseconds == i2/centisecondInMilliseconds 255 }