Skip to content

Commit

Permalink
feat: dummy linux interface and existing IPs
Browse files Browse the repository at this point in the history
Signed-off-by: Milan Lenco <[email protected]>
  • Loading branch information
milanlenco committed Dec 1, 2020
1 parent 58571ed commit de9b348
Show file tree
Hide file tree
Showing 16 changed files with 880 additions and 103 deletions.
31 changes: 29 additions & 2 deletions plugins/linux/ifplugin/descriptor/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package descriptor

import (
"fmt"
"io/ioutil"
"net"
"path/filepath"
Expand Down Expand Up @@ -107,6 +108,10 @@ var (
// EXISTING interface.
ErrExistingWithNamespace = errors.New("EXISTING interface defined with namespace")

// ErrExistingIpWithNetalloc is returned when netalloc and EXISTING-IP features are combined,
// which is currently not supported.
ErrExistingIpWithNetalloc = errors.New("it is not supported to reference EXISTING-IP via netalloc")

// ErrInvalidIPWithMask is returned when address is invalid or mask is missing
ErrInvalidIPWithMask = errors.New("IP with mask is not valid")

Expand Down Expand Up @@ -281,6 +286,16 @@ func (d *InterfaceDescriptor) Validate(key string, linuxIf *interfaces.Interface
if linuxIf.GetNamespace() != nil {
return kvs.NewInvalidValueError(ErrExistingWithNamespace, "namespace")
}
// Currently it is not supported to combine netalloc with existing IP.
if linuxIf.GetLinkOnly() {
for i, ipAddr := range linuxIf.GetIpAddresses() {
_, hasAllocDep := d.addrAlloc.GetAddressAllocDep(ipAddr, linuxIf.Name, "")
if hasAllocDep {
return kvs.NewInvalidValueError(ErrExistingIpWithNetalloc,
"type", "link_only", fmt.Sprintf("ip_addresses[%d]", i))
}
}
}
case interfaces.Interface_LOOPBACK:
if linuxIf.GetLink() != nil {
return kvs.NewInvalidValueError(ErrInterfaceReferenceMismatch, "link")
Expand Down Expand Up @@ -357,6 +372,8 @@ func (d *InterfaceDescriptor) Create(key string, linuxIf *interfaces.Interface)
metadata, err = getMetadata(linuxIf)
case interfaces.Interface_VRF_DEVICE:
metadata, err = d.createVRF(nsCtx, linuxIf)
case interfaces.Interface_DUMMY:
metadata, err = d.createDummyIf(nsCtx, linuxIf)
default:
return nil, ErrUnsupportedLinuxInterfaceType
}
Expand Down Expand Up @@ -455,6 +472,8 @@ func (d *InterfaceDescriptor) Delete(key string, linuxIf *interfaces.Interface,
return nil
case interfaces.Interface_VRF_DEVICE:
return d.deleteVRF(linuxIf)
case interfaces.Interface_DUMMY:
return d.deleteDummyIf(linuxIf)
}

err = ErrUnsupportedLinuxInterfaceType
Expand Down Expand Up @@ -635,12 +654,20 @@ func (d *InterfaceDescriptor) DerivedValues(key string, linuxIf *interfaces.Inte
Value: &prototypes.Empty{},
})
}
if !linuxIf.GetLinkOnly() {
if !linuxIf.GetLinkOnly() || linuxIf.GetType() == interfaces.Interface_EXISTING {
var ipSource netalloc_api.IPAddressSource
var hostName string
if linuxIf.GetLinkOnly() { // interface type = EXISTING
ipSource = netalloc_api.IPAddressSource_EXISTING
hostName = getHostIfName(linuxIf)
} else {
ipSource = netalloc_api.IPAddressSource_STATIC
}
// IP addresses
for _, ipAddr := range linuxIf.IpAddresses {
derValues = append(derValues, kvs.KeyValuePair{
Key: interfaces.InterfaceAddressKey(
linuxIf.Name, ipAddr, linuxIf.VrfMasterInterface, netalloc_api.IPAddressSource_STATIC),
linuxIf.Name, ipAddr, linuxIf.VrfMasterInterface, hostName, ipSource),
Value: &prototypes.Empty{},
})
}
Expand Down
35 changes: 26 additions & 9 deletions plugins/linux/ifplugin/descriptor/interface_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ const (
DisableIPv6SysctlTemplate = "net.ipv6.conf.%s.disable_ipv6"

// dependency labels
interfaceVrfDep = "interface-assigned-to-vrf"
interfaceVrfDep = "interface-assigned-to-vrf"
interfaceAddrDep = "address-assigned-to-interface"
)

// InterfaceAddressDescriptor (un)assigns IP address to/from Linux interface.
Expand Down Expand Up @@ -81,17 +82,19 @@ func (d *InterfaceAddressDescriptor) SetInterfaceIndex(intfIndex ifaceidx.LinuxI
}

// IsInterfaceAddressKey returns true if the key represents assignment of an IP address
// to a Linux interface (that needs to be applied). KVs representing addresses
// already allocated from netalloc plugin are excluded.
// to a Linux interface (that needs to be applied or is expected to exist).
// KVs representing addresses already allocated from netalloc plugin are excluded.
func (d *InterfaceAddressDescriptor) IsInterfaceAddressKey(key string) bool {
_, _, _, source, _, isAddrKey := interfaces.ParseInterfaceAddressKey(key)
_, _, _, _, source, _, isAddrKey := interfaces.ParseInterfaceAddressKey(key)
return isAddrKey &&
(source == netalloc_api.IPAddressSource_STATIC || source == netalloc_api.IPAddressSource_ALLOC_REF)
(source == netalloc_api.IPAddressSource_STATIC ||
source == netalloc_api.IPAddressSource_ALLOC_REF ||
source == netalloc_api.IPAddressSource_EXISTING)
}

// Validate validates IP address to be assigned to an interface.
func (d *InterfaceAddressDescriptor) Validate(key string, emptyVal proto.Message) (err error) {
iface, addr, _, _, invalidKey, _ := interfaces.ParseInterfaceAddressKey(key)
iface, addr, _, _, _, invalidKey, _ := interfaces.ParseInterfaceAddressKey(key)
if invalidKey {
return errors.New("invalid key")
}
Expand All @@ -101,7 +104,11 @@ func (d *InterfaceAddressDescriptor) Validate(key string, emptyVal proto.Message

// Create assigns IP address to an interface.
func (d *InterfaceAddressDescriptor) Create(key string, emptyVal proto.Message) (metadata kvs.Metadata, err error) {
iface, addr, _, _, _, _ := interfaces.ParseInterfaceAddressKey(key)
iface, addr, _, _, source, _, _ := interfaces.ParseInterfaceAddressKey(key)
if source == netalloc_api.IPAddressSource_EXISTING {
// already exists, nothing to do
return nil, nil
}

ifMeta, found := d.intfIndex.LookupByName(iface)
if !found {
Expand Down Expand Up @@ -160,7 +167,11 @@ func (d *InterfaceAddressDescriptor) Create(key string, emptyVal proto.Message)

// Delete unassigns IP address from an interface.
func (d *InterfaceAddressDescriptor) Delete(key string, emptyVal proto.Message, metadata kvs.Metadata) (err error) {
iface, addr, _, _, _, _ := interfaces.ParseInterfaceAddressKey(key)
iface, addr, _, _, source, _, _ := interfaces.ParseInterfaceAddressKey(key)
if source == netalloc_api.IPAddressSource_EXISTING {
// already existed before Create, nothing to do
return nil
}

ifMeta, found := d.intfIndex.LookupByName(iface)
if !found {
Expand Down Expand Up @@ -197,13 +208,19 @@ func (d *InterfaceAddressDescriptor) Delete(key string, emptyVal proto.Message,

// Dependencies mentions (non-default) VRF and a potential allocation of the IP address as dependencies.
func (d *InterfaceAddressDescriptor) Dependencies(key string, emptyVal proto.Message) (deps []kvs.Dependency) {
iface, addr, vrf, _, _, _ := interfaces.ParseInterfaceAddressKey(key)
iface, addr, vrf, hostName, source, _, _ := interfaces.ParseInterfaceAddressKey(key)
if vrf != "" {
deps = append(deps, kvs.Dependency{
Label: interfaceVrfDep,
Key: interfaces.InterfaceVrfKey(iface, vrf),
})
}
if source == netalloc_api.IPAddressSource_EXISTING {
deps = append(deps, kvs.Dependency{
Label: interfaceAddrDep,
Key: interfaces.InterfaceHostNameWithAddrKey(hostName, addr),
})
}
allocDep, hasAllocDep := d.addrAlloc.GetAddressAllocDep(addr, iface, "")
if hasAllocDep {
deps = append(deps, allocDep)
Expand Down
77 changes: 77 additions & 0 deletions plugins/linux/ifplugin/descriptor/interface_dummy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) 2020 Pantheon.tech
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package descriptor

import (
"github.com/pkg/errors"
"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/linuxcalls"

"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/ifaceidx"
nslinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin/linuxcalls"
interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
)

// createDummyIf creates dummy interface.
func (d *InterfaceDescriptor) createDummyIf(
nsCtx nslinuxcalls.NamespaceMgmtCtx, linuxIf *interfaces.Interface,
) (md *ifaceidx.LinuxIfMetadata, err error) {
hostName := getHostIfName(linuxIf)
agentPrefix := d.serviceLabel.GetAgentPrefix()

// move to the namespace with the interface
revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, linuxIf.Namespace)
if err != nil {
d.log.Error("switch to namespace failed:", err)
return nil, err
}
defer revert()

// create a new Dummy interface
err = d.ifHandler.AddDummyInterface(hostName)
if err != nil {
return nil, errors.WithMessagef(err,
"failed to create dummy interface %s", hostName)
}

// add alias
err = d.ifHandler.SetInterfaceAlias(hostName, agentPrefix+linuxcalls.GetDummyIfAlias(linuxIf))
if err != nil {
return nil, errors.WithMessagef(err,
"error setting alias for Dummy interface %s", hostName)
}

// build metadata
link, err := d.ifHandler.GetLinkByName(hostName)
if err != nil {
return nil, errors.WithMessagef(err, "error getting link %s", hostName)
}

return &ifaceidx.LinuxIfMetadata{
Namespace: linuxIf.Namespace,
LinuxIfIndex: link.Attrs().Index,
HostIfName: hostName,
}, nil
}

// deleteDummyIf removes dummy interface.
func (d *InterfaceDescriptor) deleteDummyIf(linuxIf *interfaces.Interface) error {
hostName := getHostIfName(linuxIf)
err := d.ifHandler.DeleteInterface(hostName)
if err != nil {
d.log.Error(err)
return err
}
return nil
}
Loading

0 comments on commit de9b348

Please sign in to comment.