go.ligato.io/vpp-agent/v3@v3.5.0/plugins/netalloc/netalloc_plugin.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  //go:generate descriptor-adapter --descriptor-name IPAlloc --value-type *netalloc.IPAllocation --meta-type *netalloc.IPAllocMetadata --import "go.ligato.io/vpp-agent/v3/proto/ligato/netalloc" --output-dir "descriptor"
    16  
    17  package netalloc
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"net"
    24  
    25  	"go.ligato.io/cn-infra/v2/infra"
    26  
    27  	"go.ligato.io/cn-infra/v2/idxmap"
    28  
    29  	"go.ligato.io/vpp-agent/v3/pkg/models"
    30  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    31  	"go.ligato.io/vpp-agent/v3/plugins/netalloc/descriptor"
    32  	"go.ligato.io/vpp-agent/v3/plugins/netalloc/utils"
    33  	"go.ligato.io/vpp-agent/v3/proto/ligato/netalloc"
    34  )
    35  
    36  // Plugin implements network allocation features.
    37  // For more information, please refer to the netalloc proto model: proto/ligato/netalloc/netalloc.proto
    38  type Plugin struct {
    39  	Deps
    40  
    41  	// IP address allocation
    42  	ipAllocDescriptor *kvs.KVDescriptor
    43  	ipIndex           idxmap.NamedMapping
    44  }
    45  
    46  // Deps lists dependencies of the netalloc plugin.
    47  type Deps struct {
    48  	infra.PluginDeps
    49  	KVScheduler kvs.KVScheduler
    50  }
    51  
    52  // Init initializes netalloc descriptors.
    53  func (p *Plugin) Init() error {
    54  	// init & register descriptors
    55  	p.ipAllocDescriptor = descriptor.NewAddrAllocDescriptor(p.Log)
    56  	err := p.Deps.KVScheduler.RegisterKVDescriptor(p.ipAllocDescriptor)
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	// obtain map with metadata of allocated addresses
    62  	p.ipIndex = p.KVScheduler.GetMetadataMap(descriptor.IPAllocDescriptorName)
    63  	if p.ipIndex == nil {
    64  		return errors.New("missing index with metadata of allocated addresses")
    65  	}
    66  	return nil
    67  }
    68  
    69  // Close does nothing.
    70  func (p *Plugin) Close() error {
    71  	return nil
    72  }
    73  
    74  // CreateAddressAllocRef creates reference to an allocated IP address.
    75  func (p *Plugin) CreateAddressAllocRef(network, iface string, getGW bool) string {
    76  	ref := netalloc.AllocRefPrefix + network
    77  	if iface != "" {
    78  		ref += "/" + iface
    79  	}
    80  	if getGW {
    81  		ref += netalloc.AllocRefGWSuffix
    82  	}
    83  	return ref
    84  }
    85  
    86  // ParseAddressAllocRef parses reference to an allocated IP address.
    87  func (p *Plugin) ParseAddressAllocRef(addrAllocRef, expIface string) (
    88  	network, iface string, isGW, isRef bool, err error) {
    89  	return utils.ParseAddrAllocRef(addrAllocRef, expIface)
    90  }
    91  
    92  // GetAddressAllocDep reads what can be potentially a reference to an allocated
    93  // IP address. If <allocRef> is indeed a reference, the function returns
    94  // the corresponding dependency to be passed further into KVScheduler
    95  // from the descriptor. Otherwise <hasAllocDep> is returned as false, and
    96  // <allocRef> should be an actual address and not a reference.
    97  func (p *Plugin) GetAddressAllocDep(addrOrAllocRef, ifaceName, depLabelPrefix string) (
    98  	dep kvs.Dependency, hasAllocDep bool) {
    99  
   100  	network, iface, _, isRef, err := utils.ParseAddrAllocRef(addrOrAllocRef, ifaceName)
   101  	if !isRef || err != nil {
   102  		return kvs.Dependency{}, false
   103  	}
   104  
   105  	return kvs.Dependency{
   106  		Label: depLabelPrefix + addrOrAllocRef,
   107  		Key: models.Key(&netalloc.IPAllocation{
   108  			NetworkName:   network,
   109  			InterfaceName: iface,
   110  		}),
   111  	}, true
   112  }
   113  
   114  // ValidateIPAddress checks validity of address reference or, if <addrOrAllocRef>
   115  // already contains an actual IP address, it tries to parse it.
   116  func (p *Plugin) ValidateIPAddress(addrOrAllocRef, ifaceName, fieldName string, gwCheck GwValidityCheck) error {
   117  	_, _, isGW, isRef, err := utils.ParseAddrAllocRef(addrOrAllocRef, ifaceName)
   118  	if !isRef {
   119  		_, _, err = utils.ParseIPAddr(addrOrAllocRef, nil)
   120  	} else if err == nil {
   121  		switch gwCheck {
   122  		case GWRefRequired:
   123  			if !isGW {
   124  				err = errors.New("expected GW address reference")
   125  			}
   126  		case GwRefUnexpected:
   127  			if isGW {
   128  				err = errors.New("expected non-GW address reference")
   129  			}
   130  		}
   131  	}
   132  	if err != nil {
   133  		if fieldName != "" {
   134  			return kvs.NewInvalidValueError(err, fieldName)
   135  		} else {
   136  			return kvs.NewInvalidValueError(err)
   137  		}
   138  	}
   139  	return nil
   140  }
   141  
   142  // GetOrParseIPAddress tries to get allocated interface (or GW) IP address
   143  // referenced by <addrOrAllocRef> in the requested form. But if the string
   144  // contains/ an actual IP address instead of a reference, the address is parsed
   145  // using methods from the net package and returned in the requested form.
   146  // For ADDR_ONLY address form, the returned <addr> will have the mask unset
   147  // and the IP address should be accessed as <addr>.IP
   148  func (p *Plugin) GetOrParseIPAddress(addrOrAllocRef string, ifaceName string,
   149  	addrForm netalloc.IPAddressForm) (addr *net.IPNet, err error) {
   150  
   151  	network, iface, getGW, isRef, err := utils.ParseAddrAllocRef(addrOrAllocRef, ifaceName)
   152  	if isRef && err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	if isRef {
   157  		// reference to allocated IP address
   158  		allocName := models.Name(&netalloc.IPAllocation{
   159  			NetworkName:   network,
   160  			InterfaceName: iface,
   161  		})
   162  		allocVal, found := p.ipIndex.GetValue(allocName)
   163  		if !found {
   164  			return nil, fmt.Errorf("failed to find metadata for IP address allocation '%s'",
   165  				allocName)
   166  		}
   167  		allocMeta, ok := allocVal.(*netalloc.IPAllocMetadata)
   168  		if !ok {
   169  			return nil, fmt.Errorf("invalid type of metadata stored for IP address allocation '%s'",
   170  				allocName)
   171  		}
   172  		if getGW {
   173  			if allocMeta.GwAddr == nil {
   174  				return nil, fmt.Errorf("gw address is not defined for IP address allocation '%s'",
   175  					allocName)
   176  			}
   177  			return utils.GetIPAddrInGivenForm(allocMeta.GwAddr, addrForm), nil
   178  		}
   179  		return utils.GetIPAddrInGivenForm(allocMeta.IfaceAddr, addrForm), nil
   180  	}
   181  
   182  	// not a reference - try to parse the address
   183  	ipAddr, _, err := utils.ParseIPAddr(addrOrAllocRef, nil)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	return utils.GetIPAddrInGivenForm(ipAddr, addrForm), nil
   188  }
   189  
   190  // CorrelateRetrievedIPs should be used in Retrieve to correlate one or group
   191  // of (model-wise indistinguishable) retrieved interface or GW IP addresses
   192  // with the expected configuration. The method will replace retrieved addresses
   193  // with the corresponding allocation references from the expected configuration
   194  // if there are any.
   195  // The method returns one IP address or address-allocation reference for every
   196  // address from <retrievedAddrs>.
   197  func (p *Plugin) CorrelateRetrievedIPs(expAddrsOrRefs []string, retrievedAddrs []string,
   198  	ifaceName string, addrForm netalloc.IPAddressForm) (correlated []string) {
   199  
   200  	expParsed := make([]*net.IPNet, len(expAddrsOrRefs))
   201  	for i, addr := range expAddrsOrRefs {
   202  		expParsed[i], _ = p.GetOrParseIPAddress(addr, ifaceName, addrForm)
   203  	}
   204  
   205  	for _, addr := range retrievedAddrs {
   206  		ipAddr, _, err := utils.ParseIPAddr(addr, nil)
   207  		if err != nil {
   208  			// invalid - do not try to correlate, just return as is
   209  			correlated = append(correlated, addr)
   210  			continue
   211  		}
   212  		var addrCorrelated bool
   213  		for i, expAddr := range expParsed {
   214  			if expAddr == nil {
   215  				continue
   216  			}
   217  			if bytes.Equal(ipAddr.IP, expAddr.IP) {
   218  				if addrForm == netalloc.IPAddressForm_ADDR_ONLY ||
   219  					bytes.Equal(ipAddr.Mask, expAddr.Mask) {
   220  					// found match in the expected configuration
   221  					correlated = append(correlated, expAddrsOrRefs[i])
   222  					addrCorrelated = true
   223  					break
   224  				}
   225  			}
   226  		}
   227  		if !addrCorrelated {
   228  			// couldn't find match in the expected configuration, just return as is
   229  			correlated = append(correlated, addr)
   230  		}
   231  	}
   232  	return correlated
   233  }