go.ligato.io/vpp-agent/v3@v3.5.0/plugins/vpp/natplugin/descriptor/nat44_address_pool.go (about) 1 // Copyright (c) 2019 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 "bytes" 19 "net" 20 "strings" 21 22 "github.com/pkg/errors" 23 "go.ligato.io/cn-infra/v2/logging" 24 "go.ligato.io/vpp-agent/v3/pkg/models" 25 kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api" 26 "go.ligato.io/vpp-agent/v3/plugins/vpp/natplugin/descriptor/adapter" 27 "go.ligato.io/vpp-agent/v3/plugins/vpp/natplugin/vppcalls" 28 l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3" 29 nat "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/nat" 30 ) 31 32 const ( 33 // NAT44AddressPoolDescriptorName is the name of the descriptor for NAT44 IP address pools. 34 NAT44AddressPoolDescriptorName = "vpp-nat44-address-pool" 35 ) 36 37 // A list of non-retriable errors: 38 var ( 39 // errInvalidIPAddress is returned when IP address from NAT address pool cannot be parsed. 40 errInvalidIPAddress = errors.New("invalid IP address") 41 // errInvalidLastPoolAddress is returned when last IP of the pool is not higher than first IP of the pool, or empty. 42 errInvalidLastPoolAddress = errors.New("last IP should be higher than first IP, or empty") 43 ) 44 45 // NAT44AddressPoolDescriptor teaches KVScheduler how to add/remove VPP NAT44 IP addresses pools. 46 type NAT44AddressPoolDescriptor struct { 47 log logging.Logger 48 natHandler vppcalls.NatVppAPI 49 nat44GlobalDesc *NAT44GlobalDescriptor 50 } 51 52 // NewNAT44AddressPoolDescriptor creates a new instance of the NAT44AddressPoolDescriptor. 53 func NewNAT44AddressPoolDescriptor(nat44GlobalDesc *NAT44GlobalDescriptor, 54 natHandler vppcalls.NatVppAPI, log logging.PluginLogger) *kvs.KVDescriptor { 55 ctx := &NAT44AddressPoolDescriptor{ 56 nat44GlobalDesc: nat44GlobalDesc, 57 natHandler: natHandler, 58 log: log.NewLogger("nat44-address-pool-descriptor"), 59 } 60 typedDescr := &adapter.NAT44AddressPoolDescriptor{ 61 Name: NAT44AddressPoolDescriptorName, 62 NBKeyPrefix: nat.ModelNat44AddressPool.KeyPrefix(), 63 ValueTypeName: nat.ModelNat44AddressPool.ProtoName(), 64 KeySelector: nat.ModelNat44AddressPool.IsKeyValid, 65 KeyLabel: nat.ModelNat44AddressPool.StripKeyPrefix, 66 Validate: ctx.Validate, 67 Create: ctx.Create, 68 Delete: ctx.Delete, 69 Retrieve: ctx.Retrieve, 70 Dependencies: ctx.Dependencies, 71 DerivedValues: ctx.DerivedValues, 72 // retrieve global NAT config first (required for deprecated global NAT interface & address API) 73 RetrieveDependencies: []string{NAT44GlobalDescriptorName}, 74 } 75 return adapter.NewNAT44AddressPoolDescriptor(typedDescr) 76 } 77 78 // Validate validates configuration for NAT44 IP addresses pool. 79 func (d *NAT44AddressPoolDescriptor) Validate(key string, natAddr *nat.Nat44AddressPool) error { 80 firstIP := net.ParseIP(natAddr.FirstIp) 81 if firstIP == nil { 82 return kvs.NewInvalidValueError(errInvalidIPAddress, "first_ip") 83 } 84 if natAddr.LastIp != "" { 85 lastIP := net.ParseIP(natAddr.LastIp) 86 if lastIP == nil { 87 return kvs.NewInvalidValueError(errInvalidIPAddress, "last_ip") 88 } 89 if bytes.Compare(firstIP, lastIP) > 0 { 90 // last IP should be empty or higher than first IP 91 return kvs.NewInvalidValueError(errInvalidLastPoolAddress, "last_ip") 92 } 93 } 94 return nil 95 } 96 97 // Create adds IP address pool into VPP NAT44 address pools. 98 func (d *NAT44AddressPoolDescriptor) Create(key string, natAddr *nat.Nat44AddressPool) (metadata interface{}, err error) { 99 return nil, 100 d.natHandler.AddNat44AddressPool(natAddr.VrfId, natAddr.FirstIp, natAddr.LastIp, natAddr.TwiceNat) 101 } 102 103 // Delete removes IP address pool from VPP NAT44 address pools. 104 func (d *NAT44AddressPoolDescriptor) Delete(key string, natAddr *nat.Nat44AddressPool, metadata interface{}) error { 105 return d.natHandler.DelNat44AddressPool(natAddr.VrfId, natAddr.FirstIp, natAddr.LastIp, natAddr.TwiceNat) 106 } 107 108 // Retrieve returns VPP IP address pools configured on VPP. 109 func (d *NAT44AddressPoolDescriptor) Retrieve(correlate []adapter.NAT44AddressPoolKVWithMetadata) ( 110 retrieved []adapter.NAT44AddressPoolKVWithMetadata, err error) { 111 if d.nat44GlobalDesc.UseDeprecatedAPI { 112 return nil, nil // NAT IP addresses already dumped by global descriptor (deprecated API is in use) 113 } 114 115 // dumping pools 116 natPools, err := d.natHandler.Nat44AddressPoolsDump() 117 if err != nil { 118 return nil, err 119 } 120 121 // processing the pool dump 122 for _, sbPool := range natPools { 123 // try to find NB Pool corresponding to SB Pool (for named pools we link name to SB pools) 124 pool := sbPool 125 for _, nbPool := range correlate { 126 if d.equalNamelessPool(nbPool.Value, sbPool) { 127 pool = nbPool.Value // NB pool found 128 break 129 } 130 } 131 132 // creating SB view result 133 retrieved = append(retrieved, adapter.NAT44AddressPoolKVWithMetadata{ 134 Key: models.Key(pool), 135 Value: pool, 136 Origin: kvs.FromNB, 137 }) 138 } 139 return 140 } 141 142 // Dependencies lists endpoint-dependent mode and non-zero/non-all-ones VRF as dependencies. 143 func (d *NAT44AddressPoolDescriptor) Dependencies(key string, natAddr *nat.Nat44AddressPool) (deps []kvs.Dependency) { 144 if natAddr.VrfId != 0 && natAddr.VrfId != ^uint32(0) { 145 deps = append(deps, kvs.Dependency{ 146 Label: addressVrfDep, 147 Key: l3.VrfTableKey(natAddr.VrfId, l3.VrfTable_IPV4), 148 }) 149 } 150 if !d.natHandler.WithLegacyStartupConf() { 151 deps = append(deps, kvs.Dependency{ 152 Label: addressEpModeDep, 153 Key: nat.Nat44EndpointDepKey, 154 }) 155 } 156 return deps 157 } 158 159 // DerivedValues derives: 160 // - for twiceNAT address pool the pool itself with exposed IP addresses and VRF in derived key 161 func (d *NAT44AddressPoolDescriptor) DerivedValues(key string, addrPool *nat.Nat44AddressPool) (derValues []kvs.KeyValuePair) { 162 if addrPool.TwiceNat { 163 // this derived value may seem as copy of nat44-pool, but nat44-pool key can have 2 forms and in form 164 // where nat44-pool key is only pool name, there can't be made dependency based on IP address and 165 // twiceNAT bool => this derived key is needed 166 derValues = append(derValues, kvs.KeyValuePair{ 167 Key: nat.DerivedTwiceNATAddressPoolKey(addrPool.FirstIp, addrPool.LastIp, addrPool.VrfId), 168 Value: addrPool, 169 }) 170 } 171 return derValues 172 } 173 174 // equalNamelessPool determine equality between 2 Nat44AddressPools ignoring Name field 175 func (d *NAT44AddressPoolDescriptor) equalNamelessPool(pool1, pool2 *nat.Nat44AddressPool) bool { 176 return pool1.VrfId == pool2.VrfId && 177 pool1.TwiceNat == pool2.TwiceNat && 178 equivalentIPv4(pool1.FirstIp, pool2.FirstIp) && 179 equivalentIPv4(pool1.LastIp, pool2.LastIp) 180 } 181 182 func equivalentIPv4(ip1str, ip2str string) bool { 183 ip1, err1 := ParseIPv4(ip1str) 184 ip2, err2 := ParseIPv4(ip2str) 185 if err1 != nil || err2 != nil { // one of values is invalid, but that will handle validator -> compare by strings 186 return equivalentTrimmedLowered(ip1str, ip2str) 187 } 188 return ip1.Equal(ip2) // form doesn't matter, are they representing the same IP value ? 189 } 190 191 func equivalentTrimmedLowered(str1, str2 string) bool { 192 return strings.TrimSpace(strings.ToLower(str1)) == strings.TrimSpace(strings.ToLower(str2)) 193 } 194 195 // ParseIPv4 parses string <str> to IPv4 address 196 func ParseIPv4(str string) (net.IP, error) { 197 ip := net.ParseIP(str) 198 if ip == nil { 199 return nil, errors.Errorf(" %q is not ip address", str) 200 } 201 ipv4 := ip.To4() 202 if ipv4 == nil { 203 return nil, errors.Errorf(" %q is not ipv4 address", str) 204 } 205 return ipv4, nil 206 }