Skip to content

Commit

Permalink
added gateway routing manager implementation and test suite for the g…
Browse files Browse the repository at this point in the history
…ateway operator.
  • Loading branch information
alacuku authored and palexster committed Jun 15, 2021
1 parent 0790c14 commit 66f03bc
Show file tree
Hide file tree
Showing 3 changed files with 319 additions and 2 deletions.
84 changes: 84 additions & 0 deletions pkg/liqonet/routing/gatewayRouting.go
@@ -0,0 +1,84 @@
package routing

import (
"strconv"

"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"

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

// GatewayRoutingManager implements the routing manager interface.
// Used by the gateway operator to configure the routes for remote clusters.
type GatewayRoutingManager struct {
routingTableID int
tunnelDevice netlink.Link
}

// NewGatewayRoutingManager returns a GatewayRoutingManager ready to be used or an error.
func NewGatewayRoutingManager(routingTableID int, tunnelDevice netlink.Link) (Routing, error) {
// Check the validity of input parameters.
if routingTableID > unix.RT_TABLE_MAX {
return nil, &liqoerrors.WrongParameter{Parameter: "routingTableID", Reason: liqoerrors.MinorOrEqual + strconv.Itoa(unix.RT_TABLE_MAX)}
}
if routingTableID < 0 {
return nil, &liqoerrors.WrongParameter{Parameter: "routingTableID", Reason: liqoerrors.GreaterOrEqual + strconv.Itoa(0)}
}
if tunnelDevice == nil {
return nil, &liqoerrors.WrongParameter{
Reason: liqoerrors.NotNil,
Parameter: "tunnelDevice",
}
}
return &GatewayRoutingManager{
routingTableID: routingTableID,
tunnelDevice: tunnelDevice,
}, 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 (grm *GatewayRoutingManager) EnsureRoutesPerCluster(tep *netv1alpha1.TunnelEndpoint) (bool, error) {
var routeAdd bool
var err error
// Extract and save route information from the given tep.
_, dstNet := liqonet.GetPodCIDRS(tep)
// Add route for the given cluster.
routeAdd, err = AddRoute(dstNet, "", grm.tunnelDevice.Attrs().Index, grm.routingTableID)
if err != nil {
return routeAdd, err
}
return routeAdd, 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 (grm *GatewayRoutingManager) RemoveRoutesPerCluster(tep *netv1alpha1.TunnelEndpoint) (bool, error) {
var routeDel bool
var err error
// Extract and save route information from the given tep.
_, dstNet := liqonet.GetPodCIDRS(tep)
// Delete route for the given cluster.
routeDel, err = delRoute(dstNet, "", grm.tunnelDevice.Attrs().Index, grm.routingTableID)
if err != nil {
return routeDel, err
}
return routeDel, nil
}

// CleanRoutingTable stub function, as the gateway only operates in custom network namespace.
func (grm *GatewayRoutingManager) CleanRoutingTable() error {
return flushRoutesForRoutingTable(grm.routingTableID)
}

// CleanPolicyRules stub function, as the gateway only operates in custom network namespace.
func (grm *GatewayRoutingManager) CleanPolicyRules() error {
return flushRulesForRoutingTable(grm.routingTableID)
}
219 changes: 219 additions & 0 deletions pkg/liqonet/routing/gatewayRouting_test.go
@@ -0,0 +1,219 @@
package routing

import (
"net"
"strconv"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"

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

var (
routingTableIDGRM = 19773
routesGRM = []routingInfo{
{
destinationNet: "10.120.0.0/16",
routingTableID: routingTableIDGRM,
},
{
destinationNet: "10.121.0.0/16",
routingTableID: routingTableIDGRM,
}}
existingRoutesGRM []*netlink.Route
tepGRM netv1alpha1.TunnelEndpoint
tunnelDevice netlink.Link
)

var _ = Describe("GatewayRouting", func() {
BeforeEach(func() {
// Populate the index of the routes with the correct one.
for i := range routesGRM {
routesGRM[i].iFaceIndex = tunnelDevice.Attrs().Index
}
})

Describe("creating new Gateway Route Manager", func() {

Context("when parameters are not valid", func() {
It("routingTableID parameter out of range: a negative number", func() {
grm, err := NewGatewayRoutingManager(-244, tunnelDevice)
Expect(grm).Should(BeNil())
Expect(err).Should(Equal(&liqoerrors.WrongParameter{Parameter: "routingTableID", Reason: liqoerrors.GreaterOrEqual + strconv.Itoa(0)}))
})

It("routingTableID parameter out of range: superior to max value ", func() {
grm, err := NewGatewayRoutingManager(unix.RT_TABLE_MAX+1, tunnelDevice)
Expect(grm).Should(BeNil())
Expect(err).Should(Equal(&liqoerrors.WrongParameter{Parameter: "routingTableID", Reason: liqoerrors.MinorOrEqual + strconv.Itoa(unix.RT_TABLE_MAX)}))
})
It("tunnelDevice is nil", func() {
grm, err := NewGatewayRoutingManager(routingTableIDGRM, nil)
Expect(grm).Should(BeNil())
Expect(err).Should(Equal(&liqoerrors.WrongParameter{Parameter: "tunnelDevice", Reason: liqoerrors.NotNil}))
})
})

Context("when parameters are correct", func() {
It("right parameters", func() {
grm, err := NewGatewayRoutingManager(routingTableIDGRM, tunnelDevice)
Expect(grm).ShouldNot(BeNil())
Expect(err).ShouldNot(HaveOccurred())
})
})
})

Describe("configuring routes for a remote peering cluster", func() {
JustBeforeEach(func() {
tepGRM = netv1alpha1.TunnelEndpoint{
Status: netv1alpha1.TunnelEndpointStatus{
LocalNATPodCIDR: "10.150.0.0/16",
RemoteNATPodCIDR: "10.250.0.0/16",
VethIFaceIndex: 12345,
GatewayIP: ipAddress2NoSubnet,
}}
})
Context("when tep holds malformed parameters", func() {
It("route configuration fails while adding route", func() {
tepGRM.Status.RemoteNATPodCIDR = "10.150.000/16"
added, err := grm.EnsureRoutesPerCluster(&tepGRM)
Expect(err).Should(Equal(&net.ParseError{
Type: "CIDR address",
Text: "10.150.000/16",
}))
Expect(added).Should(BeFalse())
Expect(err).NotTo(BeNil())
})
})

Context("when tep holds correct parameters", func() {
JustBeforeEach(func() {
existingRoutesGRM = setUpRoutes(routesGRM)
})

JustAfterEach(func() {
tearDownRoutes(routingTableIDGRM)
})

It("route configuration should be correctly inserted", func() {
added, err := grm.EnsureRoutesPerCluster(&tep)
Expect(err).ShouldNot(HaveOccurred())
Expect(added).Should(BeTrue())
// Get the inserted route
_, dstNet, err := net.ParseCIDR(tep.Status.RemoteNATPodCIDR)
Expect(err).ShouldNot(HaveOccurred())
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, &netlink.Route{Dst: dstNet, Table: routingTableIDGRM}, netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE)
Expect(err).ShouldNot(HaveOccurred())
Expect(routes[0].Dst.String()).Should(Equal(tep.Status.RemoteNATPodCIDR))
Expect(routes[0].Gw).Should(BeNil())
})

It("route already exists, should return false and nil", func() {
tepGRM.Status.RemoteNATPodCIDR = existingRoutesGRM[0].Dst.String()
tepGRM.Status.GatewayIP = existingRoutesGRM[0].Gw.String()
added, err := grm.EnsureRoutesPerCluster(&tepGRM)
Expect(err).ShouldNot(HaveOccurred())
Expect(added).Should(BeFalse())
})
})
})

Describe("removing route configuration for a remote peering cluster", func() {
Context("when tep holds malformed parameters", func() {
It("fails to remove route configuration while removing the route", func() {
tepGRM.Status.RemoteNATPodCIDR = ""
added, err := grm.RemoveRoutesPerCluster(&tepGRM)
Expect(err).Should(Equal(&net.ParseError{
Type: "CIDR address",
Text: "",
}))
Expect(added).Should(BeFalse())
Expect(err).NotTo(BeNil())
})
})

Context("when tep holds correct parameters", func() {
JustBeforeEach(func() {
existingRoutesGRM = setUpRoutes(routesGRM)
})

JustAfterEach(func() {
tearDownRoutes(routingTableIDGRM)
})

It("route configuration should be correctly removed", func() {
tepGRM.Status.RemoteNATPodCIDR = existingRoutesGRM[0].Dst.String()
deleted, err := grm.RemoveRoutesPerCluster(&tepGRM)
Expect(err).ShouldNot(HaveOccurred())
Expect(deleted).Should(BeTrue())
// Try to get the remove route.
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, existingRoutesGRM[0], netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE)
Expect(err).ShouldNot(HaveOccurred())
Expect(len(routes)).Should(BeZero())
})

It("route configuration should be correctly removed from veth pair", func() {
tepGRM.Status.RemoteNATPodCIDR = existingRoutesGRM[1].Dst.String()
tepGRM.Status.GatewayIP = ipAddress1NoSubnet
tepGRM.Status.VethIFaceIndex = overlayDevice.Link.Index
added, err := grm.RemoveRoutesPerCluster(&tepGRM)
Expect(err).ShouldNot(HaveOccurred())
Expect(added).Should(BeTrue())
// Try to get the remove route.
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, existingRoutesGRM[1], netlink.RT_FILTER_DST|netlink.RT_FILTER_TABLE)
Expect(err).ShouldNot(HaveOccurred())
Expect(len(routes)).Should(BeZero())
})

It("route does not exist, should return false and nil", func() {
added, err := grm.RemoveRoutesPerCluster(&tep)
Expect(err).ShouldNot(HaveOccurred())
Expect(added).Should(BeFalse())
})
})
})

Describe("removing all routes configurations managed by the gateway route manager", func() {
Context("removing routes, should return nil", func() {
JustBeforeEach(func() {
existingRoutesGRM = setUpRoutes(routesGRM)
})

JustAfterEach(func() {
tearDownRoutes(routingTableIDGRM)
})

It("routes should be correctly removed", func() {
err := grm.CleanRoutingTable()
Expect(err).ShouldNot(HaveOccurred())
// Try to list rules
routes, err := netlink.RouteListFiltered(netlink.FAMILY_V4, existingRoutesGRM[0], netlink.RT_FILTER_TABLE)
Expect(err).ShouldNot(HaveOccurred())
Expect(len(routes)).Should(BeZero())
})
})

Context("removing policy routing rules, should return nil", func() {
JustBeforeEach(func() {
existingRoutesGRM = setUpRoutes(routesGRM)
})

JustAfterEach(func() {
tearDownRoutes(routingTableIDGRM)
})

It("policy routing rules should be correctly removed", func() {
err := grm.CleanPolicyRules()
Expect(err).ShouldNot(HaveOccurred())
// Try to list rules
exists, err := existsRuleForRoutingTable(routingTableIDGRM)
Expect(err).ShouldNot(HaveOccurred())
Expect(exists).Should(BeFalse())
})
})
})
})
18 changes: 16 additions & 2 deletions pkg/liqonet/routing/routing_suite_test.go
Expand Up @@ -26,7 +26,7 @@ var (
ipAddress2NoSubnetOverlay = "240.0.0.2"
dummylink1, dummyLink2 netlink.Link
iFacesNames = []string{"liqo-test-1", "liqo-test-2"}
drm, vrm Routing
drm, vrm, grm Routing

tep netv1alpha1.TunnelEndpoint
)
Expand Down Expand Up @@ -65,11 +65,25 @@ var _ = BeforeSuite(func() {
vrm, err = NewVxlanRoutingManager(routingTableIDVRM, ipAddress1NoSubnet, overlayNetPrexif, overlayDevice)
Expect(err).Should(BeNil())
Expect(vrm).NotTo(BeNil())

//*** Gateway Route Manager Configuration ***/
// Create a dummy interface used as tunnel device.
link = &netlink.Dummy{netlink.LinkAttrs{Name: "dummy-tunnel"}}
Expect(netlink.LinkAdd(link)).To(BeNil())
tunnelDevice, err = netlink.LinkByName("dummy-tunnel")
Expect(err).To(BeNil())
Expect(tunnelDevice).NotTo(BeNil())
// Set up dummy tunnel device
Expect(netlink.LinkSetUp(tunnelDevice)).To(BeNil())
grm, err = NewGatewayRoutingManager(routingTableIDGRM, tunnelDevice)
Expect(err).Should(BeNil())
Expect(grm).NotTo(BeNil())
})

var _ = AfterSuite(func() {
tearDownInterfaces()
deleteLink(vxlanConfig.Name)
Expect(deleteLink(vxlanConfig.Name)).To(BeNil())
Expect(deleteLink(tunnelDevice.Attrs().Name)).To(BeNil())
})

func setUpInterfaces() {
Expand Down

0 comments on commit 66f03bc

Please sign in to comment.