Skip to content

Commit

Permalink
adding vxlan route manager to handle the route configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
alacuku committed Jun 8, 2021
1 parent 1a65af4 commit 34c5f0c
Show file tree
Hide file tree
Showing 7 changed files with 484 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ endif

# Run unit tests
unit: test-container
docker run --cap-add=NET_ADMIN --privileged=true --mount type=bind,src=$(shell pwd),dst=/go/src/liqo -w /go/src/liqo --rm liqo-test
docker run --privileged=true --mount type=bind,src=$(shell pwd),dst=/go/src/liqo -w /go/src/liqo --rm liqo-test

# Run e2e tests
e2e: gen
Expand Down
20 changes: 4 additions & 16 deletions pkg/liqonet/overlay/overlay_suite_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package overlay

import (
"fmt"
"net"
"testing"

"github.com/vishvananda/netlink"

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

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
Expand All @@ -23,27 +22,16 @@ func TestOverlay(t *testing.T) {

var _ = BeforeSuite(func() {
var err error
defaultIfaceIP, err = getIFaceIP("0.0.0.0")
defaultIfaceIP, err = getIFaceIP()
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) {
func getIFaceIP() (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
Expand All @@ -55,7 +43,7 @@ func getIFaceIP(ipAddress string) (net.IP, error) {
}
}
if ifaceIndex == 0 {
return nil, &liqonet.NoRouteFound{IPAddress: ipAddress}
return nil, fmt.Errorf("unable to get ip for default interface")
}
// Get link.
link, err := netlink.LinkByIndex(ifaceIndex)
Expand Down
2 changes: 1 addition & 1 deletion pkg/liqonet/overlay/vxlan.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (vxlan *VxlanDevice) AddFDB(n Neighbor) error {
IP: n.IP,
HardwareAddr: n.MAC,
})
if errors.Is(err, unix.ENOENT) {
if errors.Is(err, unix.EEXIST) {
return nil
}
if err != nil {
Expand Down
1 change: 0 additions & 1 deletion pkg/liqonet/overlay/vxlan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,5 +219,4 @@ var _ = Describe("Vxlan", func() {
})
})
})

})
28 changes: 21 additions & 7 deletions pkg/liqonet/routing/routing_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"reflect"
"testing"

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

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
Expand All @@ -17,13 +19,15 @@ import (
)

var (
ipAddress1 = "10.0.0.1/24"
ipAddress1NoSubnet = "10.0.0.1"
ipAddress2 = "10.0.0.2/24"
ipAddress2NoSubnet = "10.0.0.2"
dummylink1, dummyLink2 netlink.Link
iFacesNames = []string{"lioo-test-1", "liqo-test-2"}
drm Routing
ipAddress1 = "10.0.0.1/24"
ipAddress1NoSubnet = "10.0.0.1"
ipAddress2 = "10.0.0.2/24"
ipAddress2NoSubnet = "10.0.0.2"
// The value of ipAddress2NoSubnet when is mapped to the overlay network.
ipAddress2NoSubnetOverlay = "240.0.0.2"
dummylink1, dummyLink2 netlink.Link
iFacesNames = []string{"lioo-test-1", "liqo-test-2"}
drm, vrm Routing

tep netv1alpha1.TunnelEndpoint
)
Expand Down Expand Up @@ -53,10 +57,20 @@ var _ = BeforeSuite(func() {
VethIFaceIndex: 12345,
GatewayIP: ipAddress2NoSubnet,
}}

// Create vxlan device for Vxlan Routing manager tests.
link, err := setUpVxlanLink(vxlanConfig)
Expect(err).ShouldNot(HaveOccurred())
overlayDevice = overlay.VxlanDevice{Link: link.(*netlink.Vxlan)}
// Create Vxlan Routing Manager.
vrm, err = NewVxlanRoutingManager(routingTableIDVRM, ipAddress1NoSubnet, overlayNetPrexif, overlayDevice)
Expect(err).Should(BeNil())
Expect(vrm).NotTo(BeNil())
})

var _ = AfterSuite(func() {
tearDownInterfaces()
deleteLink(vxlanConfig.Name)
})

func setUpInterfaces() {
Expand Down
141 changes: 141 additions & 0 deletions pkg/liqonet/routing/vxlanRouting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package routing

import (
"net"
"strconv"
"strings"

"golang.org/x/sys/unix"
"k8s.io/klog/v2"

netv1alpha1 "github.com/liqotech/liqo/apis/net/v1alpha1"
"github.com/liqotech/liqo/pkg/liqonet"
"github.com/liqotech/liqo/pkg/liqonet/overlay"
)

// VxlanRoutingManager implements the routing manager interface.
// Used when nodes are not in the same network.
type VxlanRoutingManager struct {
routingTableID int
podIP string
vxlanNetPrefix string
vxlanDevice overlay.VxlanDevice
}

// NewVxlanRoutingManager returns a VxlanRoutingManager ready to be used or an error.
func NewVxlanRoutingManager(routingTableID int, podIP, vxlanNetPrefix string, vxlanDevice overlay.VxlanDevice) (Routing, error) {
// Check the validity of input parameters.
if routingTableID > unix.RT_TABLE_MAX {
return nil, &liqonet.WrongParameter{Parameter: "routingTableID", Reason: liqonet.MinorOrEqual + strconv.Itoa(unix.RT_TABLE_MAX)}
}
if routingTableID < 0 {
return nil, &liqonet.WrongParameter{Parameter: "routingTableID", Reason: liqonet.GreaterOrEqual + strconv.Itoa(0)}
}
if vxlanDevice.Link == nil {
return nil, &liqonet.WrongParameter{Parameter: "vxlanDevice.Link", Reason: liqonet.NotNil}
}
if vxlanNetPrefix == "" {
return nil, &liqonet.WrongParameter{Parameter: "vxlanNetPrefix", Reason: liqonet.StringNotEmpty}
}
ip := net.ParseIP(podIP)
if ip == nil {
return nil, &liqonet.ParseIPError{
IPToBeParsed: podIP,
}
}
klog.Infof("starting Vxlan Routing Manager with routing table ID %d, podIP %s, vxlanNetPrefix %s, vxlanDevice %s",
routingTableID, podIP, vxlanNetPrefix, vxlanDevice.Link.Name)
return &VxlanRoutingManager{
routingTableID: routingTableID,
podIP: podIP,
vxlanDevice: vxlanDevice,
vxlanNetPrefix: vxlanNetPrefix,
}, nil
}

// EnsureRoutesPerCluster accepts as input a netv1alpha.tunnelendpoint.
// It inserts the routes if they do not exist or updates them if they are outdated.
// Returns true if the routes have been configured, false if the routes are already configured.
// An error if something goes wrong and the routes can not be configured.
func (vrm *VxlanRoutingManager) EnsureRoutesPerCluster(tep *netv1alpha1.TunnelEndpoint) (bool, error) {
var routeAdd, policyRuleAdd, configured bool
var iFaceIndex int
var gatewayIP string
var err error
clusterID := tep.Spec.ClusterID
// Extract and save route information from the given tep.
_, dstNet := liqonet.GetPodCIDRS(tep)
if tep.Status.GatewayIP != vrm.podIP {
gatewayIP = vrm.getOverlayIP(tep.Status.GatewayIP)
iFaceIndex = vrm.vxlanDevice.Link.Index
} else {
iFaceIndex = tep.Status.VethIFaceIndex
}
// Add policy routing rule for the given cluster.
klog.Infof("%s -> adding policy routing rule for destination {%s} to lookup routing table with ID {%d}", clusterID, dstNet, vrm.routingTableID)
if policyRuleAdd, err = addPolicyRoutingRule("", dstNet, vrm.routingTableID); err != nil {
return policyRuleAdd, err
}
// Add route for the given cluster.
klog.Infof("%s -> adding route for destination {%s} with gateway {%s} in routing table with ID {%d}",
clusterID, dstNet, gatewayIP, vrm.routingTableID)
routeAdd, err = AddRoute(dstNet, gatewayIP, iFaceIndex, vrm.routingTableID)
if err != nil {
return routeAdd, err
}
if routeAdd || policyRuleAdd {
configured = true
}
return configured, nil
}

// RemoveRoutesPerCluster accepts as input a netv1alpha.tunnelendpoint.
// It deletes the routes if they do exist.
// Returns true if the routes exist and have been deleted, false if nothing is removed.
// An error if something goes wrong and the routes can not be removed.
func (vrm *VxlanRoutingManager) RemoveRoutesPerCluster(tep *netv1alpha1.TunnelEndpoint) (bool, error) {
var routeDel, policyRuleDel, configured bool
var iFaceIndex int
var gatewayIP string
var err error
clusterID := tep.Spec.ClusterID
// Extract and save route information from the given tep.
_, dstNet := liqonet.GetPodCIDRS(tep)
if tep.Status.GatewayIP != vrm.podIP {
gatewayIP = vrm.getOverlayIP(tep.Status.GatewayIP)
iFaceIndex = vrm.vxlanDevice.Link.Index
} else {
iFaceIndex = tep.Status.VethIFaceIndex
}
// Delete policy routing rule for the given cluster.
klog.Infof("%s -> deleting policy routing rule for destination {%s} to lookup routing table with ID {%d}", clusterID, dstNet, vrm.routingTableID)
if policyRuleDel, err = delPolicyRoutingRule("", dstNet, vrm.routingTableID); err != nil {
return policyRuleDel, err
}
// Delete route for the given cluster.
klog.Infof("%s -> deleting route for destination {%s} with gateway {%s} in routing table with ID {%d}",
clusterID, dstNet, gatewayIP, vrm.routingTableID)
routeDel, err = delRoute(dstNet, gatewayIP, iFaceIndex, vrm.routingTableID)
if err != nil {
return routeDel, err
}
if routeDel || policyRuleDel {
configured = true
}
return configured, nil
}

// CleanRoutingTable removes all the routes from the custom routing table used by the route manager.
func (vrm *VxlanRoutingManager) CleanRoutingTable() error {
return flushRoutesForRoutingTable(vrm.routingTableID)
}

// CleanPolicyRules removes all the policy rules pointing to the custom routing table used by the route manager.
func (vrm *VxlanRoutingManager) CleanPolicyRules() error {
return flushRulesForRoutingTable(vrm.routingTableID)
}

func (vrm *VxlanRoutingManager) getOverlayIP(ip string) string {
tokens := strings.Split(ip, ".")
return strings.Join([]string{vrm.vxlanNetPrefix, tokens[1], tokens[2], tokens[3]}, ".")
}
Loading

0 comments on commit 34c5f0c

Please sign in to comment.