Skip to content

Commit e5edff9

Browse files
Milan Lenčomilanlenco
Milan Lenčo
andauthored
feat: dummy linux interface and existing IPs (#1765)
Signed-off-by: Milan Lenco <[email protected]> Co-authored-by: Milan Lenco <[email protected]>
1 parent effbcad commit e5edff9

File tree

16 files changed

+880
-103
lines changed

16 files changed

+880
-103
lines changed

plugins/linux/ifplugin/descriptor/interface.go

+29-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package descriptor
1616

1717
import (
18+
"fmt"
1819
"io/ioutil"
1920
"net"
2021
"path/filepath"
@@ -107,6 +108,10 @@ var (
107108
// EXISTING interface.
108109
ErrExistingWithNamespace = errors.New("EXISTING interface defined with namespace")
109110

111+
// ErrExistingIpWithNetalloc is returned when netalloc and EXISTING-IP features are combined,
112+
// which is currently not supported.
113+
ErrExistingIpWithNetalloc = errors.New("it is not supported to reference EXISTING-IP via netalloc")
114+
110115
// ErrInvalidIPWithMask is returned when address is invalid or mask is missing
111116
ErrInvalidIPWithMask = errors.New("IP with mask is not valid")
112117

@@ -281,6 +286,16 @@ func (d *InterfaceDescriptor) Validate(key string, linuxIf *interfaces.Interface
281286
if linuxIf.GetNamespace() != nil {
282287
return kvs.NewInvalidValueError(ErrExistingWithNamespace, "namespace")
283288
}
289+
// Currently it is not supported to combine netalloc with existing IP.
290+
if linuxIf.GetLinkOnly() {
291+
for i, ipAddr := range linuxIf.GetIpAddresses() {
292+
_, hasAllocDep := d.addrAlloc.GetAddressAllocDep(ipAddr, linuxIf.Name, "")
293+
if hasAllocDep {
294+
return kvs.NewInvalidValueError(ErrExistingIpWithNetalloc,
295+
"type", "link_only", fmt.Sprintf("ip_addresses[%d]", i))
296+
}
297+
}
298+
}
284299
case interfaces.Interface_LOOPBACK:
285300
if linuxIf.GetLink() != nil {
286301
return kvs.NewInvalidValueError(ErrInterfaceReferenceMismatch, "link")
@@ -357,6 +372,8 @@ func (d *InterfaceDescriptor) Create(key string, linuxIf *interfaces.Interface)
357372
metadata, err = getMetadata(linuxIf)
358373
case interfaces.Interface_VRF_DEVICE:
359374
metadata, err = d.createVRF(nsCtx, linuxIf)
375+
case interfaces.Interface_DUMMY:
376+
metadata, err = d.createDummyIf(nsCtx, linuxIf)
360377
default:
361378
return nil, ErrUnsupportedLinuxInterfaceType
362379
}
@@ -455,6 +472,8 @@ func (d *InterfaceDescriptor) Delete(key string, linuxIf *interfaces.Interface,
455472
return nil
456473
case interfaces.Interface_VRF_DEVICE:
457474
return d.deleteVRF(linuxIf)
475+
case interfaces.Interface_DUMMY:
476+
return d.deleteDummyIf(linuxIf)
458477
}
459478

460479
err = ErrUnsupportedLinuxInterfaceType
@@ -635,12 +654,20 @@ func (d *InterfaceDescriptor) DerivedValues(key string, linuxIf *interfaces.Inte
635654
Value: &prototypes.Empty{},
636655
})
637656
}
638-
if !linuxIf.GetLinkOnly() {
657+
if !linuxIf.GetLinkOnly() || linuxIf.GetType() == interfaces.Interface_EXISTING {
658+
var ipSource netalloc_api.IPAddressSource
659+
var hostName string
660+
if linuxIf.GetLinkOnly() { // interface type = EXISTING
661+
ipSource = netalloc_api.IPAddressSource_EXISTING
662+
hostName = getHostIfName(linuxIf)
663+
} else {
664+
ipSource = netalloc_api.IPAddressSource_STATIC
665+
}
639666
// IP addresses
640667
for _, ipAddr := range linuxIf.IpAddresses {
641668
derValues = append(derValues, kvs.KeyValuePair{
642669
Key: interfaces.InterfaceAddressKey(
643-
linuxIf.Name, ipAddr, linuxIf.VrfMasterInterface, netalloc_api.IPAddressSource_STATIC),
670+
linuxIf.Name, ipAddr, linuxIf.VrfMasterInterface, hostName, ipSource),
644671
Value: &prototypes.Empty{},
645672
})
646673
}

plugins/linux/ifplugin/descriptor/interface_address.go

+26-9
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ const (
4141
DisableIPv6SysctlTemplate = "net.ipv6.conf.%s.disable_ipv6"
4242

4343
// dependency labels
44-
interfaceVrfDep = "interface-assigned-to-vrf"
44+
interfaceVrfDep = "interface-assigned-to-vrf"
45+
interfaceAddrDep = "address-assigned-to-interface"
4546
)
4647

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

8384
// IsInterfaceAddressKey returns true if the key represents assignment of an IP address
84-
// to a Linux interface (that needs to be applied). KVs representing addresses
85-
// already allocated from netalloc plugin are excluded.
85+
// to a Linux interface (that needs to be applied or is expected to exist).
86+
// KVs representing addresses already allocated from netalloc plugin are excluded.
8687
func (d *InterfaceAddressDescriptor) IsInterfaceAddressKey(key string) bool {
87-
_, _, _, source, _, isAddrKey := interfaces.ParseInterfaceAddressKey(key)
88+
_, _, _, _, source, _, isAddrKey := interfaces.ParseInterfaceAddressKey(key)
8889
return isAddrKey &&
89-
(source == netalloc_api.IPAddressSource_STATIC || source == netalloc_api.IPAddressSource_ALLOC_REF)
90+
(source == netalloc_api.IPAddressSource_STATIC ||
91+
source == netalloc_api.IPAddressSource_ALLOC_REF ||
92+
source == netalloc_api.IPAddressSource_EXISTING)
9093
}
9194

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

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

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

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

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

198209
// Dependencies mentions (non-default) VRF and a potential allocation of the IP address as dependencies.
199210
func (d *InterfaceAddressDescriptor) Dependencies(key string, emptyVal proto.Message) (deps []kvs.Dependency) {
200-
iface, addr, vrf, _, _, _ := interfaces.ParseInterfaceAddressKey(key)
211+
iface, addr, vrf, hostName, source, _, _ := interfaces.ParseInterfaceAddressKey(key)
201212
if vrf != "" {
202213
deps = append(deps, kvs.Dependency{
203214
Label: interfaceVrfDep,
204215
Key: interfaces.InterfaceVrfKey(iface, vrf),
205216
})
206217
}
218+
if source == netalloc_api.IPAddressSource_EXISTING {
219+
deps = append(deps, kvs.Dependency{
220+
Label: interfaceAddrDep,
221+
Key: interfaces.InterfaceHostNameWithAddrKey(hostName, addr),
222+
})
223+
}
207224
allocDep, hasAllocDep := d.addrAlloc.GetAddressAllocDep(addr, iface, "")
208225
if hasAllocDep {
209226
deps = append(deps, allocDep)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) 2020 Pantheon.tech
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+
"github.com/pkg/errors"
19+
"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/linuxcalls"
20+
21+
"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/ifaceidx"
22+
nslinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin/linuxcalls"
23+
interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
24+
)
25+
26+
// createDummyIf creates dummy interface.
27+
func (d *InterfaceDescriptor) createDummyIf(
28+
nsCtx nslinuxcalls.NamespaceMgmtCtx, linuxIf *interfaces.Interface,
29+
) (md *ifaceidx.LinuxIfMetadata, err error) {
30+
hostName := getHostIfName(linuxIf)
31+
agentPrefix := d.serviceLabel.GetAgentPrefix()
32+
33+
// move to the namespace with the interface
34+
revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, linuxIf.Namespace)
35+
if err != nil {
36+
d.log.Error("switch to namespace failed:", err)
37+
return nil, err
38+
}
39+
defer revert()
40+
41+
// create a new Dummy interface
42+
err = d.ifHandler.AddDummyInterface(hostName)
43+
if err != nil {
44+
return nil, errors.WithMessagef(err,
45+
"failed to create dummy interface %s", hostName)
46+
}
47+
48+
// add alias
49+
err = d.ifHandler.SetInterfaceAlias(hostName, agentPrefix+linuxcalls.GetDummyIfAlias(linuxIf))
50+
if err != nil {
51+
return nil, errors.WithMessagef(err,
52+
"error setting alias for Dummy interface %s", hostName)
53+
}
54+
55+
// build metadata
56+
link, err := d.ifHandler.GetLinkByName(hostName)
57+
if err != nil {
58+
return nil, errors.WithMessagef(err, "error getting link %s", hostName)
59+
}
60+
61+
return &ifaceidx.LinuxIfMetadata{
62+
Namespace: linuxIf.Namespace,
63+
LinuxIfIndex: link.Attrs().Index,
64+
HostIfName: hostName,
65+
}, nil
66+
}
67+
68+
// deleteDummyIf removes dummy interface.
69+
func (d *InterfaceDescriptor) deleteDummyIf(linuxIf *interfaces.Interface) error {
70+
hostName := getHostIfName(linuxIf)
71+
err := d.ifHandler.DeleteInterface(hostName)
72+
if err != nil {
73+
d.log.Error(err)
74+
return err
75+
}
76+
return nil
77+
}

0 commit comments

Comments
 (0)