Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dummy linux interface and existing IPs #1765

Merged
merged 1 commit into from
Dec 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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