go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/natplugin/descriptor/nat44_global.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 21 "github.com/pkg/errors" 22 "go.ligato.io/cn-infra/v2/logging" 23 "google.golang.org/protobuf/proto" 24 prototypes "google.golang.org/protobuf/types/known/emptypb" 25 26 "go.ligato.io/vpp-agent/v3/pkg/models" 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/natplugin/descriptor/adapter" 30 "go.ligato.io/vpp-agent/v3/plugins/vpp/natplugin/vppcalls" 31 nat "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/nat" 32 ) 33 34 const ( 35 // NAT44GlobalDescriptorName is the name of the descriptor for VPP NAT44 global 36 // configuration. 37 NAT44GlobalDescriptorName = "vpp-nat44-global" 38 ) 39 40 // A list of non-retriable errors: 41 var ( 42 // ErrNATInterfaceFeatureCollision is returned when VPP NAT features assigned 43 // to a single interface collide. 44 ErrNATInterfaceFeatureCollision = errors.New("VPP NAT interface feature collision") 45 46 // ErrDuplicateNATAddress is returned when VPP NAT address pool contains duplicate 47 // IP addresses. 48 ErrDuplicateNATAddress = errors.New("Duplicate VPP NAT address") 49 ) 50 51 // NAT44GlobalDescriptor teaches KVScheduler how to configure global options for 52 // VPP NAT44. 53 type NAT44GlobalDescriptor struct { 54 log logging.Logger 55 natHandler vppcalls.NatVppAPI 56 57 defaultGlobalCfg *nat.Nat44Global 58 59 // UseDeprecatedAPI tracks whether deprecated global API (NAT interfaces, addresses) is being used on NB. 60 // Used to orchestrate which data should be dumped from which descriptor on Retrieve. 61 UseDeprecatedAPI bool 62 } 63 64 // NewNAT44GlobalDescriptor creates a new instance of the NAT44Global descriptor. 65 func NewNAT44GlobalDescriptor(natHandler vppcalls.NatVppAPI, log logging.PluginLogger) (*NAT44GlobalDescriptor, *kvs.KVDescriptor) { 66 ctx := &NAT44GlobalDescriptor{ 67 natHandler: natHandler, 68 log: log.NewLogger("nat44-global-descriptor"), 69 defaultGlobalCfg: natHandler.DefaultNat44GlobalConfig(), 70 } 71 72 typedDescr := &adapter.NAT44GlobalDescriptor{ 73 Name: NAT44GlobalDescriptorName, 74 NBKeyPrefix: nat.ModelNat44Global.KeyPrefix(), 75 ValueTypeName: nat.ModelNat44Global.ProtoName(), 76 KeySelector: nat.ModelNat44Global.IsKeyValid, 77 ValueComparator: ctx.EquivalentNAT44Global, 78 Validate: ctx.Validate, 79 Create: ctx.Create, 80 Delete: ctx.Delete, 81 Update: ctx.Update, 82 UpdateWithRecreate: ctx.UpdateWithRecreate, 83 Retrieve: ctx.Retrieve, 84 DerivedValues: ctx.DerivedValues, 85 RetrieveDependencies: []string{vpp_ifdescriptor.InterfaceDescriptorName}, 86 } 87 return ctx, adapter.NewNAT44GlobalDescriptor(typedDescr) 88 } 89 90 // EquivalentNAT44Global compares two NAT44 global configs for equality. 91 func (d *NAT44GlobalDescriptor) EquivalentNAT44Global(key string, oldGlobalCfg, newGlobalCfg *nat.Nat44Global) bool { 92 if oldGlobalCfg.Forwarding != newGlobalCfg.Forwarding { 93 return false 94 } 95 if !d.natHandler.WithLegacyStartupConf() { 96 if oldGlobalCfg.EndpointIndependent != newGlobalCfg.EndpointIndependent { 97 return false 98 } 99 } 100 if !proto.Equal(d.getVirtualReassembly(oldGlobalCfg), d.getVirtualReassembly(newGlobalCfg)) { 101 return false 102 } 103 104 // Note: interfaces & addresses are not compared here as they are represented 105 // via derived kv-pairs 106 return true 107 } 108 109 // Validate validates VPP NAT44 global configuration. 110 func (d *NAT44GlobalDescriptor) Validate(key string, globalCfg *nat.Nat44Global) error { 111 if len(globalCfg.NatInterfaces) > 0 { 112 d.log.Warnf("NatInterfaces are deprecated in global NAT44 config, use separate Nat44Interface entries.") 113 } 114 if len(globalCfg.AddressPool) > 0 { 115 d.log.Warnf("AddressPool is deprecated in global NAT44 config, use separate Nat44AddressPool entries.") 116 } 117 // check NAT interface features for collisions 118 natIfaceMap := make(map[string]*natIface) 119 for _, iface := range globalCfg.NatInterfaces { 120 if _, hasEntry := natIfaceMap[iface.Name]; !hasEntry { 121 natIfaceMap[iface.Name] = &natIface{} 122 } 123 ifaceCfg := natIfaceMap[iface.Name] 124 if iface.IsInside { 125 ifaceCfg.in++ 126 } else { 127 ifaceCfg.out++ 128 } 129 if iface.OutputFeature { 130 ifaceCfg.output++ 131 } 132 } 133 natIfaceCollisionErr := kvs.NewInvalidValueError(ErrNATInterfaceFeatureCollision, "nat_interfaces") 134 for _, ifaceCfg := range natIfaceMap { 135 if ifaceCfg.in > 1 { 136 // duplicate IN 137 return natIfaceCollisionErr 138 } 139 if ifaceCfg.out > 1 { 140 // duplicate OUT 141 return natIfaceCollisionErr 142 } 143 if ifaceCfg.output == 1 && (ifaceCfg.in+ifaceCfg.out > 1) { 144 // OUTPUT interface cannot be both IN and OUT 145 return natIfaceCollisionErr 146 } 147 } 148 149 // check NAT address pool for duplicities 150 var snPool, tnPool []net.IP 151 for _, addr := range globalCfg.AddressPool { 152 ipAddr := net.ParseIP(addr.Address) 153 if ipAddr == nil { 154 // validated by NAT44Address descriptor 155 continue 156 } 157 var pool *[]net.IP 158 if addr.TwiceNat { 159 pool = &tnPool 160 } else { 161 pool = &snPool 162 } 163 for _, ipAddr2 := range *pool { 164 if ipAddr.Equal(ipAddr2) { 165 return kvs.NewInvalidValueError(ErrDuplicateNATAddress, 166 fmt.Sprintf("address_pool.address=%s", addr.Address)) 167 } 168 } 169 *pool = append(*pool, ipAddr) 170 } 171 return nil 172 } 173 174 // Create applies NAT44 global options. 175 func (d *NAT44GlobalDescriptor) Create(key string, globalCfg *nat.Nat44Global) (metadata interface{}, err error) { 176 if !d.natHandler.WithLegacyStartupConf() { 177 err = d.natHandler.EnableNAT44Plugin(vppcalls.Nat44InitOpts{ 178 EndpointDependent: !globalCfg.EndpointIndependent, 179 }) 180 if err != nil { 181 d.log.Error(err) 182 return nil, err 183 } 184 } 185 return d.Update(key, d.defaultGlobalCfg, globalCfg, nil) 186 } 187 188 // Delete sets NAT44 global options back to the defaults. 189 func (d *NAT44GlobalDescriptor) Delete(key string, globalCfg *nat.Nat44Global, metadata interface{}) error { 190 _, err := d.Update(key, globalCfg, d.defaultGlobalCfg, metadata) 191 if err != nil { 192 d.log.Error(err) 193 return err 194 } 195 if d.natHandler.WithLegacyStartupConf() { 196 return nil 197 } 198 return d.natHandler.DisableNAT44Plugin() 199 } 200 201 // Change in the endpoint-dependency mode requires NAT44 plugin to be disabled and re-enabled. 202 func (d *NAT44GlobalDescriptor) UpdateWithRecreate(key string, oldGlobalCfg, newGlobalCfg *nat.Nat44Global, metadata interface{}) bool { 203 return !d.natHandler.WithLegacyStartupConf() && 204 oldGlobalCfg.EndpointIndependent != newGlobalCfg.EndpointIndependent 205 } 206 207 // Update updates NAT44 global options. 208 func (d *NAT44GlobalDescriptor) Update(key string, oldGlobalCfg, newGlobalCfg *nat.Nat44Global, oldMetadata interface{}) (newMetadata interface{}, err error) { 209 // update forwarding 210 if oldGlobalCfg.Forwarding != newGlobalCfg.Forwarding { 211 if err = d.natHandler.SetNat44Forwarding(newGlobalCfg.Forwarding); err != nil { 212 err = errors.Errorf("failed to set NAT44 forwarding to %t: %v", newGlobalCfg.Forwarding, err) 213 d.log.Error(err) 214 return nil, err 215 } 216 } 217 218 // update virtual reassembly for IPv4 219 if !proto.Equal(d.getVirtualReassembly(oldGlobalCfg), d.getVirtualReassembly(newGlobalCfg)) { 220 if err = d.natHandler.SetVirtualReassemblyIPv4(d.getVirtualReassembly(newGlobalCfg)); err != nil { 221 err = errors.Errorf("failed to set NAT virtual reassembly for IPv4: %v", err) 222 d.log.Error(err) 223 return nil, err 224 } 225 } 226 227 return nil, nil 228 } 229 230 // Retrieve returns the current NAT44 global configuration. 231 func (d *NAT44GlobalDescriptor) Retrieve(correlate []adapter.NAT44GlobalKVWithMetadata) ([]adapter.NAT44GlobalKVWithMetadata, error) { 232 // Note: either this descriptor (deprecated) or separate interface / address pool descriptors 233 // can retrieve NAT interfaces / address pools, never both of them. Use correlate to decide. 234 d.UseDeprecatedAPI = false 235 for _, g := range correlate { 236 if len(g.Value.NatInterfaces) > 0 || len(g.Value.AddressPool) > 0 { 237 d.UseDeprecatedAPI = true 238 } 239 } 240 241 globalCfg, err := d.natHandler.Nat44GlobalConfigDump(d.UseDeprecatedAPI) 242 if err != nil { 243 d.log.Error(err) 244 return nil, err 245 } 246 247 origin := kvs.FromNB 248 if d.EquivalentNAT44Global(nat.GlobalNAT44Key(), globalCfg, d.defaultGlobalCfg) { 249 if !d.natHandler.WithLegacyStartupConf() { 250 if len(correlate) == 0 { 251 // It is not possible to find out if the NAT44 plugin is enabled or disabled. 252 // If it is not expected to be enabled then we will assume that to be the case. 253 return nil, nil 254 } 255 } else { 256 origin = kvs.FromSB 257 } 258 } 259 260 retrieved := []adapter.NAT44GlobalKVWithMetadata{{ 261 Key: models.Key(globalCfg), 262 Value: globalCfg, 263 Origin: origin, 264 }} 265 return retrieved, nil 266 } 267 268 // DerivedValues derives: 269 // - nat.NatAddress for every IP address to be added into the NAT address pool, 270 // - nat.NatInterface for every interface with assigned NAT configuration. 271 // - empty proto value if NAT44 runs in the endpoint-dependent mode 272 func (d *NAT44GlobalDescriptor) DerivedValues(key string, globalCfg *nat.Nat44Global) (derValues []kvs.KeyValuePair) { 273 // NAT addresses 274 for _, natAddr := range globalCfg.AddressPool { 275 derValues = append(derValues, kvs.KeyValuePair{ 276 Key: nat.DerivedAddressNAT44Key(natAddr.Address, natAddr.TwiceNat), 277 Value: natAddr, 278 }) 279 } 280 // NAT interfaces 281 for _, natIface := range globalCfg.NatInterfaces { 282 derValues = append(derValues, kvs.KeyValuePair{ 283 Key: nat.DerivedInterfaceNAT44Key(natIface.Name, natIface.IsInside), 284 Value: natIface, 285 }) 286 } 287 if !globalCfg.EndpointIndependent { 288 derValues = append(derValues, kvs.KeyValuePair{ 289 Key: nat.Nat44EndpointDepKey, 290 Value: &prototypes.Empty{}, 291 }) 292 } 293 return derValues 294 } 295 296 // natIface accumulates NAT interface configuration for validation purposes. 297 type natIface struct { 298 // feature assignment counters 299 in int 300 out int 301 output int 302 } 303 304 func (d *NAT44GlobalDescriptor) getVirtualReassembly(globalCfg *nat.Nat44Global) *nat.VirtualReassembly { 305 if globalCfg.VirtualReassembly == nil { 306 return d.defaultGlobalCfg.VirtualReassembly 307 } 308 return globalCfg.VirtualReassembly 309 }