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  }