Skip to content

Commit

Permalink
added vxlan overlay network and test suite
Browse files Browse the repository at this point in the history
  • Loading branch information
alacuku committed Jun 4, 2021
1 parent 93383d9 commit 3eaede8
Show file tree
Hide file tree
Showing 3 changed files with 446 additions and 0 deletions.
70 changes: 70 additions & 0 deletions pkg/liqonet/overlay/overlay_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package overlay

import (
"net"
"testing"

"github.com/vishvananda/netlink"

"github.com/liqotech/liqo/pkg/liqonet"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var (
defaultIfaceIP net.IP
)

func TestOverlay(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Overlay Suite")
}

var _ = BeforeSuite(func() {
var err error
defaultIfaceIP, err = getIFaceIP("0.0.0.0")
Expect(err).ShouldNot(HaveOccurred())
Expect(defaultIfaceIP).ShouldNot(BeNil())
// Create dummy link
err = netlink.LinkAdd(&netlink.Dummy{LinkAttrs: netlink.LinkAttrs{Name: "dummy-link"}})
Expect(err).ShouldNot(HaveOccurred())
})

/*var _ = AfterSuite(func() {
})*/

func getIFaceIP(ipAddress string) (net.IP, error) {
var ifaceIndex int
// Convert the given IP address from string to net.IP format
ip := net.ParseIP(ipAddress)
if ip == nil {
return nil, &liqonet.ParseIPError{
IPToBeParsed: ipAddress,
}
}
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
if err != nil {
return nil, err
}
// Find the route whose destination contains our IP address.
for i := range routes {
if routes[i].Scope == netlink.SCOPE_UNIVERSE {
ifaceIndex = routes[i].LinkIndex
}
}
if ifaceIndex == 0 {
return nil, &liqonet.NoRouteFound{IPAddress: ipAddress}
}
// Get link.
link, err := netlink.LinkByIndex(ifaceIndex)
if err != nil {
return nil, err
}
ips, err := netlink.AddrList(link, netlink.FAMILY_V4)
if err != nil {
return nil, err
}
return ips[0].IP, nil
}
153 changes: 153 additions & 0 deletions pkg/liqonet/overlay/vxlan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package overlay

import (
"fmt"
"net"
"reflect"
"syscall"

"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
"k8s.io/klog/v2"
)

// VxlanDeviceAttrs configuration for a new vxlan device.
type VxlanDeviceAttrs struct {
Vni int
Name string
VtepPort int
VtepAddr net.IP
Mtu int
}

// VxlanDevice struct that holds a vxlan link.
type VxlanDevice struct {
Link *netlink.Vxlan
}

// Neighbor struct that holds information for an fdb entry.
type Neighbor struct {
MAC net.HardwareAddr
IP net.IP
}

// NewVxlanDevice takes as argument a struct of type VxlanDeviceAttrs and returns a VxlanDevice or error.
func NewVxlanDevice(devAttrs *VxlanDeviceAttrs) (*VxlanDevice, error) {
link := &netlink.Vxlan{
LinkAttrs: netlink.LinkAttrs{
Name: devAttrs.Name,
MTU: devAttrs.Mtu,
Flags: net.FlagUp,
},
VxlanId: devAttrs.Vni,
SrcAddr: devAttrs.VtepAddr,
Port: devAttrs.VtepPort,
Learning: true,
}

link, err := createVxLanLink(link)
if err != nil {
return nil, err
}
return &VxlanDevice{
Link: link,
}, nil
}

func createVxLanLink(link *netlink.Vxlan) (*netlink.Vxlan, error) {
err := netlink.LinkAdd(link)
if err == syscall.EEXIST {
existing, err := netlink.LinkByName(link.Name)
if err != nil {
return nil, fmt.Errorf("failed to retrieve Link with name %s: %v", link.Name, err)
}
if isVxlanConfigTheSame(link, existing) {
klog.V(4).Infof("vxlan device with the same configuration already exists")
link = existing.(*netlink.Vxlan)
return link, nil
}
// If we come here it means that the config of the existing vxlan device does not match with the new one.
// We delete it and then recreate.
if err = netlink.LinkDel(existing); err != nil {
return nil, fmt.Errorf("failed to delete the existing vxlan device with name %s: %v", existing.Attrs().Name, err)
}
if err = netlink.LinkAdd(link); err != nil {
return nil, fmt.Errorf("failed to re-create the vxlan interface: %v", err)
}
} else if err != nil {
return nil, fmt.Errorf("failed to create the vxlan interface: %v", err)
}
ifindex := link.Index
vxlan, err := netlink.LinkByIndex(ifindex)
if err != nil {
return nil, fmt.Errorf("can't locate created vxlan device with index %v, %v", ifindex, err)
}
return vxlan.(*netlink.Vxlan), nil
}

// If a vxlan exists with the same name then we use this function to check if the configuration is the same.
func isVxlanConfigTheSame(new, current netlink.Link) bool {
if new.Type() != current.Type() {
klog.V(4).Infof("different types for the interfaces: new -> %v, current -> %v", new.Type(), current.Type())
return false
}
newNetlinkVxlan := new.(*netlink.Vxlan)
currentNetlinkVxlan := current.(*netlink.Vxlan)

if newNetlinkVxlan.VxlanId != currentNetlinkVxlan.VxlanId {
klog.V(4).Infof("different vxlan ID for the interfaces: new -> %d, current -> %d", newNetlinkVxlan.VxlanId, currentNetlinkVxlan.VxlanId)
return false
}
if !reflect.DeepEqual(newNetlinkVxlan.SrcAddr, currentNetlinkVxlan.SrcAddr) {
klog.V(4).Infof("different Source Addresses for the interfaces: new -> %v, current -> %v", newNetlinkVxlan.SrcAddr, currentNetlinkVxlan.SrcAddr)
return false
}
if newNetlinkVxlan.Port != currentNetlinkVxlan.Port {
klog.V(4).Infof("different Vxlan Port for the interfaces: new -> %d, current -> %d", newNetlinkVxlan.Port, currentNetlinkVxlan.Port)
return false
}
klog.V(4).Infof("the existing interface is already configured")
return true
}

// AddFDB adds a fdb entry for the given neighbour into the current vxlan device.
func (vxlan *VxlanDevice) AddFDB(n Neighbor) error {
klog.V(4).Infof("calling AppendFDB: %v, %v", n.IP, n.MAC)
err := netlink.NeighAdd(&netlink.Neigh{
LinkIndex: vxlan.Link.Index,
State: netlink.NUD_PERMANENT | netlink.NUD_NOARP,
Family: syscall.AF_BRIDGE,
Flags: netlink.NTF_SELF,
Type: netlink.NDA_DST,
IP: n.IP,
HardwareAddr: n.MAC,
})
if err == unix.EEXIST {
return nil
}
if err != nil {
return err
}
return nil
}

// DelFDB deletes a fdb entry for the given neighbour from the current vxlan device.
func (vxlan *VxlanDevice) DelFDB(n Neighbor) error {
klog.V(4).Infof("calling DelFDB: %v, %v", n.IP, n.MAC)
err := netlink.NeighDel(&netlink.Neigh{
LinkIndex: vxlan.Link.Index,
State: netlink.NUD_PERMANENT | netlink.NUD_NOARP,
Family: syscall.AF_BRIDGE,
Flags: netlink.NTF_SELF,
Type: netlink.NDA_DST,
IP: n.IP,
HardwareAddr: n.MAC,
})
if err == unix.ENOENT {
return nil
}
if err != nil {
return err
}
return nil
}
Loading

0 comments on commit 3eaede8

Please sign in to comment.