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  }