mirror of
https://github.com/vishvananda/netlink
synced 2025-01-28 10:23:42 +00:00
8d48f50f55
This commit added support to allow the operation of ip6tnl encapsulation. It is equivalent to the iproute2 command, e.g., ip route add 192.168.99.0/24 encap ip6 dst 2001:db8:: dev ip6-tunnel The limitation include that the options field defined in encap nl route attribute is not implenmented yet. Testcase is included.
2141 lines
46 KiB
Go
2141 lines
46 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
package netlink
|
|
|
|
import (
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/vishvananda/netlink/nl"
|
|
"github.com/vishvananda/netns"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func TestRouteAddDel(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
ip := net.IPv4(127, 1, 1, 1)
|
|
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err := RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not added properly")
|
|
}
|
|
|
|
routes, err = RouteList(nil, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not listed properly")
|
|
}
|
|
|
|
dstIP := net.IPv4(192, 168, 0, 42)
|
|
routeToDstIP, err := RouteGet(dstIP)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(routeToDstIP) == 0 {
|
|
t.Fatal("Default route not present")
|
|
}
|
|
if err := RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Route not removed properly")
|
|
}
|
|
|
|
// add default route test
|
|
// equiv: default dev lo
|
|
_, defaultDst, _ := net.ParseCIDR("0.0.0.0/0")
|
|
route = Route{Dst: defaultDst, LinkIndex: link.Attrs().Index}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Dev default route not listed properly")
|
|
}
|
|
if err := RouteDel(&routes[0]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Dev default route not removed properly")
|
|
}
|
|
|
|
// equiv: blackhole default
|
|
route = Route{Dst: defaultDst, Type: unix.RTN_BLACKHOLE, Family: FAMILY_V4}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(nil, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Logf("%+v", routes)
|
|
|
|
if len(routes) != 1 {
|
|
t.Fatal("Blackhole default route not listed properly")
|
|
}
|
|
|
|
if err := RouteDel(&routes[0]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(nil, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Blackhole default route not removed properly")
|
|
}
|
|
|
|
// equiv: prohibit default
|
|
route = Route{Dst: defaultDst, Type: unix.RTN_PROHIBIT}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(nil, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Prohibit default route not listed properly")
|
|
}
|
|
|
|
if err := RouteDel(&routes[0]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(nil, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Prohibit default route not removed properly")
|
|
}
|
|
}
|
|
|
|
func TestRoute6AddDel(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// create dummy interface
|
|
// IPv6 route added to loopback interface will be unreachable
|
|
la := NewLinkAttrs()
|
|
la.Name = "dummy_route6"
|
|
la.TxQLen = 1500
|
|
dummy := &Dummy{LinkAttrs: la}
|
|
if err := LinkAdd(dummy); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// get dummy interface
|
|
link, err := LinkByName("dummy_route6")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// remember number of routes before adding
|
|
// typically one route (fe80::/64) will be created when dummy_route6 is created
|
|
routes, err := RouteList(link, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
nroutes := len(routes)
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.ParseIP("2001:db8::0"),
|
|
Mask: net.CIDRMask(64, 128),
|
|
}
|
|
route := Route{LinkIndex: link.Attrs().Index, Dst: dst}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != nroutes+1 {
|
|
t.Fatal("Route not added properly")
|
|
}
|
|
|
|
dstIP := net.ParseIP("2001:db8::1")
|
|
routeToDstIP, err := RouteGet(dstIP)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// cleanup route
|
|
if len(routeToDstIP) == 0 {
|
|
t.Fatal("Route not present")
|
|
}
|
|
if err := RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != nroutes {
|
|
t.Fatal("Route not removed properly")
|
|
}
|
|
|
|
// add a default link route
|
|
_, defaultDst, _ := net.ParseCIDR("::/0")
|
|
route = Route{LinkIndex: link.Attrs().Index, Dst: defaultDst}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != nroutes+1 {
|
|
t.Fatal("Default route not added properly")
|
|
}
|
|
|
|
// add a default link route
|
|
for _, route := range routes {
|
|
if route.Dst.String() == defaultDst.String() {
|
|
if err := RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != nroutes {
|
|
t.Fatal("Default route not removed properly")
|
|
}
|
|
|
|
// add blackhole default link route
|
|
routes, err = RouteList(nil, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
nroutes = len(routes)
|
|
|
|
route = Route{Type: unix.RTN_BLACKHOLE, Dst: defaultDst}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(nil, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != nroutes+1 {
|
|
t.Fatal("Blackhole default route not added properly")
|
|
}
|
|
|
|
// add blackhole default link route
|
|
for _, route := range routes {
|
|
if ipNetEqual(route.Dst, defaultDst) {
|
|
if err := RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
routes, err = RouteList(nil, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != nroutes {
|
|
t.Fatal("Blackhole default route not removed properly")
|
|
}
|
|
|
|
// add prohibit default link route
|
|
routes, err = RouteList(nil, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
nroutes = len(routes)
|
|
|
|
route = Route{Type: unix.RTN_BLACKHOLE, Dst: defaultDst}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(nil, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != nroutes+1 {
|
|
t.Fatal("Prohibit default route not added properly")
|
|
}
|
|
|
|
// add prohibit default link route
|
|
for _, route := range routes {
|
|
if ipNetEqual(route.Dst, defaultDst) {
|
|
if err := RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
routes, err = RouteList(nil, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != nroutes {
|
|
t.Fatal("Prohibit default route not removed properly")
|
|
}
|
|
|
|
// cleanup dummy interface created for the test
|
|
if err := LinkDel(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRouteReplace(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
ip := net.IPv4(127, 1, 1, 1)
|
|
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err := RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not added properly")
|
|
}
|
|
|
|
ip = net.IPv4(127, 1, 1, 2)
|
|
route = Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
|
if err := RouteReplace(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(routes) != 1 || !routes[0].Src.Equal(ip) {
|
|
t.Fatal("Route not replaced properly")
|
|
}
|
|
|
|
if err := RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Route not removed properly")
|
|
}
|
|
|
|
}
|
|
|
|
func TestRouteAppend(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
ip := net.IPv4(127, 1, 1, 1)
|
|
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err := RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not added properly")
|
|
}
|
|
|
|
ip = net.IPv4(127, 1, 1, 2)
|
|
route = Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
|
if err := RouteAppend(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(routes) != 2 || !routes[1].Src.Equal(ip) {
|
|
t.Fatal("Route not append properly")
|
|
}
|
|
|
|
if err := RouteDel(&routes[0]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := RouteDel(&routes[1]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Route not removed properly")
|
|
}
|
|
}
|
|
|
|
func TestRouteAddIncomplete(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
route := Route{LinkIndex: link.Attrs().Index}
|
|
if err := RouteAdd(&route); err == nil {
|
|
t.Fatal("Adding incomplete route should fail")
|
|
}
|
|
}
|
|
|
|
// expectNeighUpdate returns whether the expected updated is received within one minute.
|
|
func expectRouteUpdate(ch <-chan RouteUpdate, t uint16, dst net.IP) bool {
|
|
for {
|
|
timeout := time.After(time.Minute)
|
|
select {
|
|
case update := <-ch:
|
|
if update.Type == t &&
|
|
update.Route.Dst != nil &&
|
|
update.Route.Dst.IP.Equal(dst) {
|
|
return true
|
|
}
|
|
case <-timeout:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRouteSubscribe(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
ch := make(chan RouteUpdate)
|
|
done := make(chan struct{})
|
|
defer close(done)
|
|
if err := RouteSubscribe(ch, done); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
ip := net.IPv4(127, 1, 1, 1)
|
|
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !expectRouteUpdate(ch, unix.RTM_NEWROUTE, dst.IP) {
|
|
t.Fatal("Add update not received as expected")
|
|
}
|
|
if err := RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !expectRouteUpdate(ch, unix.RTM_DELROUTE, dst.IP) {
|
|
t.Fatal("Del update not received as expected")
|
|
}
|
|
}
|
|
|
|
func TestRouteSubscribeWithOptions(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
ch := make(chan RouteUpdate)
|
|
done := make(chan struct{})
|
|
defer close(done)
|
|
var lastError error
|
|
defer func() {
|
|
if lastError != nil {
|
|
t.Fatalf("Fatal error received during subscription: %v", lastError)
|
|
}
|
|
}()
|
|
if err := RouteSubscribeWithOptions(ch, done, RouteSubscribeOptions{
|
|
ErrorCallback: func(err error) {
|
|
lastError = err
|
|
},
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
ip := net.IPv4(127, 1, 1, 1)
|
|
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !expectRouteUpdate(ch, unix.RTM_NEWROUTE, dst.IP) {
|
|
t.Fatal("Add update not received as expected")
|
|
}
|
|
}
|
|
|
|
func TestRouteSubscribeAt(t *testing.T) {
|
|
skipUnlessRoot(t)
|
|
|
|
// Create an handle on a custom netns
|
|
newNs, err := netns.New()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer newNs.Close()
|
|
|
|
nh, err := NewHandleAt(newNs)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer nh.Close()
|
|
|
|
// Subscribe for Route events on the custom netns
|
|
ch := make(chan RouteUpdate)
|
|
done := make(chan struct{})
|
|
defer close(done)
|
|
if err := RouteSubscribeAt(newNs, ch, done); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// get loopback interface
|
|
link, err := nh.LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err = nh.LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 169, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
ip := net.IPv4(127, 100, 1, 1)
|
|
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
|
if err := nh.RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !expectRouteUpdate(ch, unix.RTM_NEWROUTE, dst.IP) {
|
|
t.Fatal("Add update not received as expected")
|
|
}
|
|
if err := nh.RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !expectRouteUpdate(ch, unix.RTM_DELROUTE, dst.IP) {
|
|
t.Fatal("Del update not received as expected")
|
|
}
|
|
}
|
|
|
|
func TestRouteSubscribeListExisting(t *testing.T) {
|
|
skipUnlessRoot(t)
|
|
|
|
// Create an handle on a custom netns
|
|
newNs, err := netns.New()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer newNs.Close()
|
|
|
|
nh, err := NewHandleAt(newNs)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer nh.Close()
|
|
|
|
// get loopback interface
|
|
link, err := nh.LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err = nh.LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route before subscribing
|
|
dst10 := &net.IPNet{
|
|
IP: net.IPv4(10, 10, 10, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
ip := net.IPv4(127, 100, 1, 1)
|
|
route10 := Route{LinkIndex: link.Attrs().Index, Dst: dst10, Src: ip}
|
|
if err := nh.RouteAdd(&route10); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Subscribe for Route events including existing routes
|
|
ch := make(chan RouteUpdate)
|
|
done := make(chan struct{})
|
|
defer close(done)
|
|
if err := RouteSubscribeWithOptions(ch, done, RouteSubscribeOptions{
|
|
Namespace: &newNs,
|
|
ListExisting: true},
|
|
); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !expectRouteUpdate(ch, unix.RTM_NEWROUTE, dst10.IP) {
|
|
t.Fatal("Existing add update not received as expected")
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 169, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
|
|
if err := nh.RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !expectRouteUpdate(ch, unix.RTM_NEWROUTE, dst.IP) {
|
|
t.Fatal("Add update not received as expected")
|
|
}
|
|
if err := nh.RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !expectRouteUpdate(ch, unix.RTM_DELROUTE, dst.IP) {
|
|
t.Fatal("Del update not received as expected")
|
|
}
|
|
if err := nh.RouteDel(&route10); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !expectRouteUpdate(ch, unix.RTM_DELROUTE, dst10.IP) {
|
|
t.Fatal("Del update not received as expected")
|
|
}
|
|
}
|
|
|
|
func TestRouteFilterAllTables(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// bring the interface up
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(1, 1, 1, 1),
|
|
Mask: net.CIDRMask(32, 32),
|
|
}
|
|
|
|
tables := []int{1000, 1001, 1002}
|
|
src := net.IPv4(127, 3, 3, 3)
|
|
for _, table := range tables {
|
|
route := Route{
|
|
LinkIndex: link.Attrs().Index,
|
|
Dst: dst,
|
|
Src: src,
|
|
Scope: unix.RT_SCOPE_LINK,
|
|
Priority: 13,
|
|
Table: table,
|
|
Type: unix.RTN_UNICAST,
|
|
Tos: 12,
|
|
Hoplimit: 100,
|
|
Realm: 328,
|
|
}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
routes, err := RouteListFiltered(FAMILY_V4, &Route{
|
|
Dst: dst,
|
|
Src: src,
|
|
Scope: unix.RT_SCOPE_LINK,
|
|
Table: unix.RT_TABLE_UNSPEC,
|
|
Type: unix.RTN_UNICAST,
|
|
Tos: 12,
|
|
Hoplimit: 100,
|
|
Realm: 328,
|
|
}, RT_FILTER_DST|RT_FILTER_SRC|RT_FILTER_SCOPE|RT_FILTER_TABLE|RT_FILTER_TYPE|RT_FILTER_TOS|RT_FILTER_HOPLIMIT|RT_FILTER_REALM)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 3 {
|
|
t.Fatal("Routes not added properly")
|
|
}
|
|
|
|
for _, route := range routes {
|
|
if route.Scope != unix.RT_SCOPE_LINK {
|
|
t.Fatal("Invalid Scope. Route not added properly")
|
|
}
|
|
if route.Priority != 13 {
|
|
t.Fatal("Invalid Priority. Route not added properly")
|
|
}
|
|
if !tableIDIn(tables, route.Table) {
|
|
t.Fatalf("Invalid Table %d. Route not added properly", route.Table)
|
|
}
|
|
if route.Type != unix.RTN_UNICAST {
|
|
t.Fatal("Invalid Type. Route not added properly")
|
|
}
|
|
if route.Tos != 12 {
|
|
t.Fatal("Invalid Tos. Route not added properly")
|
|
}
|
|
if route.Hoplimit != 100 {
|
|
t.Fatal("Invalid Hoplimit. Route not added properly")
|
|
}
|
|
if route.Realm != 328 {
|
|
t.Fatal("Invalid Realm. Route not added properly")
|
|
}
|
|
}
|
|
}
|
|
|
|
func tableIDIn(ids []int, id int) bool {
|
|
for _, v := range ids {
|
|
if v == id {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TestRouteExtraFields(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// bring the interface up
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(1, 1, 1, 1),
|
|
Mask: net.CIDRMask(32, 32),
|
|
}
|
|
|
|
src := net.IPv4(127, 3, 3, 3)
|
|
route := Route{
|
|
LinkIndex: link.Attrs().Index,
|
|
Dst: dst,
|
|
Src: src,
|
|
Scope: unix.RT_SCOPE_LINK,
|
|
Priority: 13,
|
|
Table: unix.RT_TABLE_MAIN,
|
|
Type: unix.RTN_UNICAST,
|
|
Tos: 12,
|
|
Hoplimit: 100,
|
|
Realm: 239,
|
|
}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err := RouteListFiltered(FAMILY_V4, &Route{
|
|
Dst: dst,
|
|
Src: src,
|
|
Scope: unix.RT_SCOPE_LINK,
|
|
Table: unix.RT_TABLE_MAIN,
|
|
Type: unix.RTN_UNICAST,
|
|
Tos: 12,
|
|
Hoplimit: 100,
|
|
Realm: 239,
|
|
}, RT_FILTER_DST|RT_FILTER_SRC|RT_FILTER_SCOPE|RT_FILTER_TABLE|RT_FILTER_TYPE|RT_FILTER_TOS|RT_FILTER_HOPLIMIT|RT_FILTER_REALM)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not added properly")
|
|
}
|
|
|
|
if routes[0].Scope != unix.RT_SCOPE_LINK {
|
|
t.Fatal("Invalid Scope. Route not added properly")
|
|
}
|
|
if routes[0].Priority != 13 {
|
|
t.Fatal("Invalid Priority. Route not added properly")
|
|
}
|
|
if routes[0].Table != unix.RT_TABLE_MAIN {
|
|
t.Fatal("Invalid Scope. Route not added properly")
|
|
}
|
|
if routes[0].Type != unix.RTN_UNICAST {
|
|
t.Fatal("Invalid Type. Route not added properly")
|
|
}
|
|
if routes[0].Tos != 12 {
|
|
t.Fatal("Invalid Tos. Route not added properly")
|
|
}
|
|
if routes[0].Hoplimit != 100 {
|
|
t.Fatal("Invalid Hoplimit. Route not added properly")
|
|
}
|
|
if routes[0].Realm != 239 {
|
|
t.Fatal("Invalid Realm. Route not added properly")
|
|
}
|
|
}
|
|
|
|
func TestRouteMultiPath(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// bring the interface up
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
idx := link.Attrs().Index
|
|
route := Route{Dst: dst, MultiPath: []*NexthopInfo{{LinkIndex: idx}, {LinkIndex: idx}}}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err := RouteList(nil, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("MultiPath Route not added properly")
|
|
}
|
|
if len(routes[0].MultiPath) != 2 {
|
|
t.Fatal("MultiPath Route not added properly")
|
|
}
|
|
}
|
|
|
|
func TestRouteOifOption(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// setup two interfaces: eth0, eth1
|
|
err := LinkAdd(&Dummy{LinkAttrs{Name: "eth0"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
link1, err := LinkByName("eth0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err = LinkSetUp(link1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err = LinkAdd(&Dummy{LinkAttrs{Name: "eth1"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
link2, err := LinkByName("eth1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err = LinkSetUp(link2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// config ip addresses on interfaces
|
|
addr1 := &Addr{
|
|
IPNet: &net.IPNet{
|
|
IP: net.IPv4(192, 168, 1, 1),
|
|
Mask: net.CIDRMask(24, 32),
|
|
},
|
|
}
|
|
|
|
if err = AddrAdd(link1, addr1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
addr2 := &Addr{
|
|
IPNet: &net.IPNet{
|
|
IP: net.IPv4(192, 168, 2, 1),
|
|
Mask: net.CIDRMask(24, 32),
|
|
},
|
|
}
|
|
|
|
if err = AddrAdd(link2, addr2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add default multipath route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(0, 0, 0, 0),
|
|
Mask: net.CIDRMask(0, 32),
|
|
}
|
|
gw1 := net.IPv4(192, 168, 1, 254)
|
|
gw2 := net.IPv4(192, 168, 2, 254)
|
|
route := Route{Dst: dst, MultiPath: []*NexthopInfo{{LinkIndex: link1.Attrs().Index,
|
|
Gw: gw1}, {LinkIndex: link2.Attrs().Index, Gw: gw2}}}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// check getting route from specified Oif
|
|
dstIP := net.IPv4(10, 1, 1, 1)
|
|
routes, err := RouteGetWithOptions(dstIP, &RouteGetOptions{Oif: "eth0"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(routes) != 1 || routes[0].LinkIndex != link1.Attrs().Index ||
|
|
!routes[0].Gw.Equal(gw1) {
|
|
t.Fatal("Get route from unmatched interface")
|
|
}
|
|
|
|
routes, err = RouteGetWithOptions(dstIP, &RouteGetOptions{Oif: "eth1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(routes) != 1 || routes[0].LinkIndex != link2.Attrs().Index ||
|
|
!routes[0].Gw.Equal(gw2) {
|
|
t.Fatal("Get route from unmatched interface")
|
|
}
|
|
|
|
}
|
|
|
|
func TestFilterDefaultRoute(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// bring the interface up
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
address := &Addr{
|
|
IPNet: &net.IPNet{
|
|
IP: net.IPv4(127, 0, 0, 2),
|
|
Mask: net.CIDRMask(24, 32),
|
|
},
|
|
}
|
|
if err = AddrAdd(link, address); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add default route
|
|
gw := net.IPv4(127, 0, 0, 2)
|
|
|
|
defaultRoute := Route{
|
|
Dst: nil,
|
|
Gw: gw,
|
|
}
|
|
|
|
if err := RouteAdd(&defaultRoute); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add an extra route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
extraRoute := Route{
|
|
Dst: dst,
|
|
Gw: gw,
|
|
}
|
|
|
|
if err := RouteAdd(&extraRoute); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var filterTests = []struct {
|
|
filter *Route
|
|
mask uint64
|
|
expected net.IP
|
|
}{
|
|
{
|
|
&Route{Dst: nil},
|
|
RT_FILTER_DST,
|
|
gw,
|
|
},
|
|
{
|
|
&Route{Dst: dst},
|
|
RT_FILTER_DST,
|
|
gw,
|
|
},
|
|
}
|
|
|
|
for _, f := range filterTests {
|
|
routes, err := RouteListFiltered(FAMILY_V4, f.filter, f.mask)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not filtered properly")
|
|
}
|
|
if !routes[0].Gw.Equal(gw) {
|
|
t.Fatal("Unexpected Gateway")
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func TestMPLSRouteAddDel(t *testing.T) {
|
|
tearDown := setUpMPLSNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
mplsDst := 100
|
|
route := Route{
|
|
LinkIndex: link.Attrs().Index,
|
|
MPLSDst: &mplsDst,
|
|
NewDst: &MPLSDestination{
|
|
Labels: []int{200, 300},
|
|
},
|
|
}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err := RouteList(link, FAMILY_MPLS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not added properly")
|
|
}
|
|
|
|
if err := RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_MPLS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Route not removed properly")
|
|
}
|
|
|
|
}
|
|
|
|
func TestIP6tnlRouteAddDel(t *testing.T) {
|
|
_, err := RouteList(nil, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, dst, err := net.ParseCIDR("192.168.99.0/24")
|
|
if err != nil {
|
|
t.Fatalf("cannot parse destination prefix: %v", err)
|
|
}
|
|
|
|
encap := IP6tnlEncap{
|
|
Dst: net.ParseIP("2001:db8::"),
|
|
Src: net.ParseIP("::"),
|
|
}
|
|
|
|
route := &Route{
|
|
LinkIndex: link.Attrs().Index,
|
|
Dst: dst,
|
|
Encap: &encap,
|
|
}
|
|
|
|
if err := RouteAdd(route); err != nil {
|
|
t.Fatalf("Cannot add route: %v", err)
|
|
}
|
|
routes, err := RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not added properly")
|
|
}
|
|
|
|
if err := RouteDel(route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Route not removed properly")
|
|
}
|
|
|
|
}
|
|
|
|
func TestRouteEqual(t *testing.T) {
|
|
mplsDst := 100
|
|
seg6encap := &SEG6Encap{Mode: nl.SEG6_IPTUN_MODE_ENCAP}
|
|
seg6encap.Segments = []net.IP{net.ParseIP("fc00:a000::11")}
|
|
cases := []Route{
|
|
{
|
|
Dst: nil,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
ILinkIndex: 21,
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Protocol: 20,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Priority: 20,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Type: 20,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Table: 200,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Tos: 1,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Hoplimit: 1,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Realm: 29,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 20,
|
|
Dst: nil,
|
|
Flags: int(FLAG_ONLINK),
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 10,
|
|
Dst: &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
},
|
|
Src: net.IPv4(127, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 10,
|
|
Scope: unix.RT_SCOPE_LINK,
|
|
Dst: &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
},
|
|
Src: net.IPv4(127, 1, 1, 1),
|
|
},
|
|
{
|
|
LinkIndex: 3,
|
|
Dst: &net.IPNet{
|
|
IP: net.IPv4(1, 1, 1, 1),
|
|
Mask: net.CIDRMask(32, 32),
|
|
},
|
|
Src: net.IPv4(127, 3, 3, 3),
|
|
Scope: unix.RT_SCOPE_LINK,
|
|
Priority: 13,
|
|
Table: unix.RT_TABLE_MAIN,
|
|
Type: unix.RTN_UNICAST,
|
|
Tos: 12,
|
|
},
|
|
{
|
|
LinkIndex: 3,
|
|
Dst: &net.IPNet{
|
|
IP: net.IPv4(1, 1, 1, 1),
|
|
Mask: net.CIDRMask(32, 32),
|
|
},
|
|
Src: net.IPv4(127, 3, 3, 3),
|
|
Scope: unix.RT_SCOPE_LINK,
|
|
Priority: 13,
|
|
Table: unix.RT_TABLE_MAIN,
|
|
Type: unix.RTN_UNICAST,
|
|
Hoplimit: 100,
|
|
},
|
|
{
|
|
LinkIndex: 3,
|
|
Dst: &net.IPNet{
|
|
IP: net.IPv4(1, 1, 1, 1),
|
|
Mask: net.CIDRMask(32, 32),
|
|
},
|
|
Src: net.IPv4(127, 3, 3, 3),
|
|
Scope: unix.RT_SCOPE_LINK,
|
|
Priority: 13,
|
|
Table: unix.RT_TABLE_MAIN,
|
|
Type: unix.RTN_UNICAST,
|
|
Realm: 129,
|
|
},
|
|
{
|
|
LinkIndex: 10,
|
|
MPLSDst: &mplsDst,
|
|
NewDst: &MPLSDestination{
|
|
Labels: []int{200, 300},
|
|
},
|
|
},
|
|
{
|
|
Dst: nil,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
Encap: &MPLSEncap{
|
|
Labels: []int{100},
|
|
},
|
|
},
|
|
{
|
|
LinkIndex: 10,
|
|
Dst: &net.IPNet{
|
|
IP: net.IPv4(10, 0, 0, 102),
|
|
Mask: net.CIDRMask(32, 32),
|
|
},
|
|
Encap: seg6encap,
|
|
},
|
|
{
|
|
Dst: nil,
|
|
MultiPath: []*NexthopInfo{{LinkIndex: 10}, {LinkIndex: 20}},
|
|
},
|
|
{
|
|
Dst: nil,
|
|
MultiPath: []*NexthopInfo{{
|
|
LinkIndex: 10,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
}, {LinkIndex: 20}},
|
|
},
|
|
{
|
|
Dst: nil,
|
|
MultiPath: []*NexthopInfo{{
|
|
LinkIndex: 10,
|
|
Gw: net.IPv4(1, 1, 1, 1),
|
|
Encap: &MPLSEncap{
|
|
Labels: []int{100},
|
|
},
|
|
}, {LinkIndex: 20}},
|
|
},
|
|
{
|
|
Dst: nil,
|
|
MultiPath: []*NexthopInfo{{
|
|
LinkIndex: 10,
|
|
NewDst: &MPLSDestination{
|
|
Labels: []int{200, 300},
|
|
},
|
|
}, {LinkIndex: 20}},
|
|
},
|
|
{
|
|
Dst: nil,
|
|
MultiPath: []*NexthopInfo{{
|
|
LinkIndex: 10,
|
|
Encap: seg6encap,
|
|
}, {LinkIndex: 20}},
|
|
},
|
|
}
|
|
for i1 := range cases {
|
|
for i2 := range cases {
|
|
got := cases[i1].Equal(cases[i2])
|
|
expected := i1 == i2
|
|
if got != expected {
|
|
t.Errorf("Equal(%q,%q) == %s but expected %s",
|
|
cases[i1], cases[i2],
|
|
strconv.FormatBool(got),
|
|
strconv.FormatBool(expected))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIPNetEqual(t *testing.T) {
|
|
cases := []string{
|
|
"1.1.1.1/24", "1.1.1.0/24", "1.1.1.1/32",
|
|
"0.0.0.0/0", "0.0.0.0/14",
|
|
"2001:db8::/32", "2001:db8::/128",
|
|
"2001:db8::caff/32", "2001:db8::caff/128",
|
|
"",
|
|
}
|
|
for _, c1 := range cases {
|
|
var n1 *net.IPNet
|
|
if c1 != "" {
|
|
var i1 net.IP
|
|
var err1 error
|
|
i1, n1, err1 = net.ParseCIDR(c1)
|
|
if err1 != nil {
|
|
panic(err1)
|
|
}
|
|
n1.IP = i1
|
|
}
|
|
for _, c2 := range cases {
|
|
var n2 *net.IPNet
|
|
if c2 != "" {
|
|
var i2 net.IP
|
|
var err2 error
|
|
i2, n2, err2 = net.ParseCIDR(c2)
|
|
if err2 != nil {
|
|
panic(err2)
|
|
}
|
|
n2.IP = i2
|
|
}
|
|
|
|
got := ipNetEqual(n1, n2)
|
|
expected := c1 == c2
|
|
if got != expected {
|
|
t.Errorf("IPNetEqual(%q,%q) == %s but expected %s",
|
|
c1, c2,
|
|
strconv.FormatBool(got),
|
|
strconv.FormatBool(expected))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSEG6LocalEqual(t *testing.T) {
|
|
// Different attributes exists in different Actions. For example, Action
|
|
// SEG6_LOCAL_ACTION_END_X has In6Addr, SEG6_LOCAL_ACTION_END_T has Table etc.
|
|
segs := []net.IP{net.ParseIP("fc00:a000::11")}
|
|
// set flags for each actions.
|
|
var flags_end [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end[nl.SEG6_LOCAL_ACTION] = true
|
|
var flags_end_x [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_x[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_x[nl.SEG6_LOCAL_NH6] = true
|
|
var flags_end_t [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_t[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_t[nl.SEG6_LOCAL_TABLE] = true
|
|
var flags_end_dx2 [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_dx2[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_dx2[nl.SEG6_LOCAL_OIF] = true
|
|
var flags_end_dx6 [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_dx6[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_dx6[nl.SEG6_LOCAL_NH6] = true
|
|
var flags_end_dx4 [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_dx4[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_dx4[nl.SEG6_LOCAL_NH4] = true
|
|
var flags_end_dt6 [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_dt6[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_dt6[nl.SEG6_LOCAL_TABLE] = true
|
|
var flags_end_dt4 [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_dt4[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_dt4[nl.SEG6_LOCAL_TABLE] = true
|
|
var flags_end_b6 [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_b6[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_b6[nl.SEG6_LOCAL_SRH] = true
|
|
var flags_end_b6_encaps [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_b6_encaps[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_b6_encaps[nl.SEG6_LOCAL_SRH] = true
|
|
|
|
cases := []SEG6LocalEncap{
|
|
{
|
|
Flags: flags_end,
|
|
Action: nl.SEG6_LOCAL_ACTION_END,
|
|
},
|
|
{
|
|
Flags: flags_end_x,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_X,
|
|
In6Addr: net.ParseIP("2001:db8::1"),
|
|
},
|
|
{
|
|
Flags: flags_end_t,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_T,
|
|
Table: 10,
|
|
},
|
|
{
|
|
Flags: flags_end_dx2,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_DX2,
|
|
Oif: 20,
|
|
},
|
|
{
|
|
Flags: flags_end_dx6,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_DX6,
|
|
In6Addr: net.ParseIP("2001:db8::1"),
|
|
},
|
|
{
|
|
Flags: flags_end_dx4,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_DX4,
|
|
InAddr: net.IPv4(192, 168, 10, 10),
|
|
},
|
|
{
|
|
Flags: flags_end_dt6,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_DT6,
|
|
Table: 30,
|
|
},
|
|
{
|
|
Flags: flags_end_dt4,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_DT4,
|
|
Table: 40,
|
|
},
|
|
{
|
|
Flags: flags_end_b6,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_B6,
|
|
Segments: segs,
|
|
},
|
|
{
|
|
Flags: flags_end_b6_encaps,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_B6_ENCAPS,
|
|
Segments: segs,
|
|
},
|
|
}
|
|
for i1 := range cases {
|
|
for i2 := range cases {
|
|
got := cases[i1].Equal(&cases[i2])
|
|
expected := i1 == i2
|
|
if got != expected {
|
|
t.Errorf("Equal(%v,%v) == %s but expected %s",
|
|
cases[i1], cases[i2],
|
|
strconv.FormatBool(got),
|
|
strconv.FormatBool(expected))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
func TestSEG6RouteAddDel(t *testing.T) {
|
|
if os.Getenv("CI") == "true" {
|
|
t.Skipf("Fails in CI with: route_test.go:*: Invalid Type. SEG6_IPTUN_MODE_INLINE routes not added properly")
|
|
}
|
|
// add/del routes with LWTUNNEL_SEG6 to/from loopback interface.
|
|
// Test both seg6 modes: encap (IPv4) & inline (IPv6).
|
|
tearDown := setUpSEG6NetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface and bring it up
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
dst1 := &net.IPNet{ // INLINE mode must be IPv6 route
|
|
IP: net.ParseIP("2001:db8::1"),
|
|
Mask: net.CIDRMask(128, 128),
|
|
}
|
|
dst2 := &net.IPNet{
|
|
IP: net.IPv4(10, 0, 0, 102),
|
|
Mask: net.CIDRMask(32, 32),
|
|
}
|
|
var s1, s2 []net.IP
|
|
s1 = append(s1, net.ParseIP("::")) // inline requires "::"
|
|
s1 = append(s1, net.ParseIP("fc00:a000::12"))
|
|
s1 = append(s1, net.ParseIP("fc00:a000::11"))
|
|
s2 = append(s2, net.ParseIP("fc00:a000::22"))
|
|
s2 = append(s2, net.ParseIP("fc00:a000::21"))
|
|
e1 := &SEG6Encap{Mode: nl.SEG6_IPTUN_MODE_INLINE}
|
|
e2 := &SEG6Encap{Mode: nl.SEG6_IPTUN_MODE_ENCAP}
|
|
e1.Segments = s1
|
|
e2.Segments = s2
|
|
route1 := Route{LinkIndex: link.Attrs().Index, Dst: dst1, Encap: e1}
|
|
route2 := Route{LinkIndex: link.Attrs().Index, Dst: dst2, Encap: e2}
|
|
|
|
// Add SEG6 routes
|
|
if err := RouteAdd(&route1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := RouteAdd(&route2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// SEG6_IPTUN_MODE_INLINE
|
|
routes, err := RouteList(link, FAMILY_V6)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("SEG6 routes not added properly")
|
|
}
|
|
for _, route := range routes {
|
|
if route.Encap == nil || route.Encap.Type() != nl.LWTUNNEL_ENCAP_SEG6 {
|
|
t.Fatal("Invalid Type. SEG6_IPTUN_MODE_INLINE routes not added properly")
|
|
}
|
|
}
|
|
// SEG6_IPTUN_MODE_ENCAP
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("SEG6 routes not added properly")
|
|
}
|
|
for _, route := range routes {
|
|
if route.Encap.Type() != nl.LWTUNNEL_ENCAP_SEG6 {
|
|
t.Fatal("Invalid Type. SEG6_IPTUN_MODE_ENCAP routes not added properly")
|
|
}
|
|
}
|
|
|
|
// Del (remove) SEG6 routes
|
|
if err := RouteDel(&route1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := RouteDel(&route2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("SEG6 routes not removed properly")
|
|
}
|
|
}
|
|
|
|
// add/del routes with LWTUNNEL_ENCAP_SEG6_LOCAL to/from dummy interface.
|
|
func TestSEG6LocalRoute6AddDel(t *testing.T) {
|
|
minKernelRequired(t, 4, 14)
|
|
tearDown := setUpSEG6NetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// create dummy interface
|
|
// IPv6 route added to loopback interface will be unreachable
|
|
la := NewLinkAttrs()
|
|
la.Name = "dummy_route6"
|
|
la.TxQLen = 1500
|
|
dummy := &Dummy{LinkAttrs: la}
|
|
if err := LinkAdd(dummy); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// get dummy interface and bring it up
|
|
link, err := LinkByName("dummy_route6")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
dst1 := &net.IPNet{
|
|
IP: net.ParseIP("2001:db8::1"),
|
|
Mask: net.CIDRMask(128, 128),
|
|
}
|
|
|
|
// Create Route including Action SEG6_LOCAL_ACTION_END_B6.
|
|
// Could be any Action but thought better to have seg list.
|
|
var s1 []net.IP
|
|
s1 = append(s1, net.ParseIP("fc00:a000::12"))
|
|
s1 = append(s1, net.ParseIP("fc00:a000::11"))
|
|
var flags_end_b6_encaps [nl.SEG6_LOCAL_MAX]bool
|
|
flags_end_b6_encaps[nl.SEG6_LOCAL_ACTION] = true
|
|
flags_end_b6_encaps[nl.SEG6_LOCAL_SRH] = true
|
|
e1 := &SEG6LocalEncap{
|
|
Flags: flags_end_b6_encaps,
|
|
Action: nl.SEG6_LOCAL_ACTION_END_B6,
|
|
Segments: s1,
|
|
}
|
|
route1 := Route{LinkIndex: link.Attrs().Index, Dst: dst1, Encap: e1}
|
|
|
|
// Add SEG6Local routes
|
|
if err := RouteAdd(&route1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// typically one route (fe80::/64) will be created when dummy_route6 is created.
|
|
// Thus you cannot use RouteList() to find the route entry just added.
|
|
// Lookup route and confirm it's SEG6Local route just added.
|
|
routesFound, err := RouteGet(dst1.IP)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routesFound) != 1 { // should only find 1 route entry
|
|
t.Fatal("SEG6Local route not added correctly")
|
|
}
|
|
if !e1.Equal(routesFound[0].Encap) {
|
|
t.Fatal("Encap does not match the original SEG6LocalEncap")
|
|
}
|
|
|
|
// Del SEG6Local routes
|
|
if err := RouteDel(&route1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Confirm route is deleted.
|
|
if _, err = RouteGet(dst1.IP); err == nil {
|
|
t.Fatal("SEG6Local route still exists.")
|
|
}
|
|
|
|
// cleanup dummy interface created for the test
|
|
if err := LinkDel(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestBpfEncap(t *testing.T) {
|
|
tCase := &BpfEncap{}
|
|
if err := tCase.SetProg(nl.LWT_BPF_IN, 0, "test_in"); err == nil {
|
|
t.Fatal("BpfEncap: inserting invalid FD did not return error")
|
|
}
|
|
if err := tCase.SetProg(nl.LWT_BPF_XMIT_HEADROOM, 23, "test_nout"); err == nil {
|
|
t.Fatal("BpfEncap: inserting invalid mode did not return error")
|
|
}
|
|
if err := tCase.SetProg(nl.LWT_BPF_XMIT, 12, "test_xmit"); err != nil {
|
|
t.Fatal("BpfEncap: inserting valid program option returned error")
|
|
}
|
|
if err := tCase.SetXmitHeadroom(12); err != nil {
|
|
t.Fatal("BpfEncap: inserting valid headroom returned error")
|
|
}
|
|
if err := tCase.SetXmitHeadroom(nl.LWT_BPF_MAX_HEADROOM + 1); err == nil {
|
|
t.Fatal("BpfEncap: inserting invalid headroom did not return error")
|
|
}
|
|
tCase = &BpfEncap{}
|
|
|
|
expected := &BpfEncap{
|
|
progs: [nl.LWT_BPF_MAX]bpfObj{
|
|
1: {
|
|
progName: "test_in[fd:10]",
|
|
progFd: 10,
|
|
},
|
|
2: {
|
|
progName: "test_out[fd:11]",
|
|
progFd: 11,
|
|
},
|
|
3: {
|
|
progName: "test_xmit[fd:21]",
|
|
progFd: 21,
|
|
},
|
|
},
|
|
headroom: 128,
|
|
}
|
|
|
|
_ = tCase.SetProg(1, 10, "test_in")
|
|
_ = tCase.SetProg(2, 11, "test_out")
|
|
_ = tCase.SetProg(3, 21, "test_xmit")
|
|
_ = tCase.SetXmitHeadroom(128)
|
|
if !tCase.Equal(expected) {
|
|
t.Fatal("BpfEncap: equal comparison failed")
|
|
}
|
|
_ = tCase.SetProg(3, 21, "test2_xmit")
|
|
if tCase.Equal(expected) {
|
|
t.Fatal("BpfEncap: equal comparison succeeded when attributes differ")
|
|
}
|
|
}
|
|
|
|
func TestMTURouteAddDel(t *testing.T) {
|
|
_, err := RouteList(nil, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// get loopback interface
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// bring the interface up
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add a gateway route
|
|
dst := &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
}
|
|
|
|
route := Route{LinkIndex: link.Attrs().Index, Dst: dst, MTU: 500}
|
|
if err := RouteAdd(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err := RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not added properly")
|
|
}
|
|
|
|
if route.MTU != routes[0].MTU {
|
|
t.Fatal("Route mtu not set properly")
|
|
}
|
|
|
|
if err := RouteDel(&route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Route not removed properly")
|
|
}
|
|
}
|
|
|
|
func TestRouteViaAddDel(t *testing.T) {
|
|
minKernelRequired(t, 5, 4)
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
_, err := RouteList(nil, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
link, err := LinkByName("lo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
route := &Route{
|
|
LinkIndex: link.Attrs().Index,
|
|
Dst: &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
},
|
|
MultiPath: []*NexthopInfo{
|
|
{
|
|
LinkIndex: link.Attrs().Index,
|
|
Via: &Via{
|
|
AddrFamily: FAMILY_V6,
|
|
Addr: net.ParseIP("2001::1"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := RouteAdd(route); err != nil {
|
|
t.Fatalf("route: %v, err: %v", route, err)
|
|
}
|
|
|
|
routes, err := RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 {
|
|
t.Fatal("Route not added properly")
|
|
}
|
|
|
|
got := routes[0].Via
|
|
want := route.MultiPath[0].Via
|
|
if !want.Equal(got) {
|
|
t.Fatalf("Route Via attribute does not match; got: %s, want: %s", got, want)
|
|
}
|
|
|
|
if err := RouteDel(route); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routes, err = RouteList(link, FAMILY_V4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 0 {
|
|
t.Fatal("Route not removed properly")
|
|
}
|
|
}
|
|
|
|
func TestRouteUIDOption(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// setup eth0 so that network is reachable
|
|
err := LinkAdd(&Dummy{LinkAttrs{Name: "eth0"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
link, err := LinkByName("eth0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addr := &Addr{
|
|
IPNet: &net.IPNet{
|
|
IP: net.IPv4(192, 168, 1, 1),
|
|
Mask: net.CIDRMask(16, 32),
|
|
},
|
|
}
|
|
if err = AddrAdd(link, addr); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// a table different than unix.RT_TABLE_MAIN
|
|
testtable := 1000
|
|
|
|
gw1 := net.IPv4(192, 168, 1, 254)
|
|
gw2 := net.IPv4(192, 168, 2, 254)
|
|
|
|
// add default route via gw1 (in main route table by default)
|
|
defaultRouteMain := Route{
|
|
Dst: nil,
|
|
Gw: gw1,
|
|
}
|
|
if err := RouteAdd(&defaultRouteMain); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add default route via gw2 in test route table
|
|
defaultRouteTest := Route{
|
|
Dst: nil,
|
|
Gw: gw2,
|
|
Table: testtable,
|
|
}
|
|
if err := RouteAdd(&defaultRouteTest); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// check the routes are in different tables
|
|
routes, err := RouteListFiltered(FAMILY_V4, &Route{
|
|
Dst: nil,
|
|
Table: unix.RT_TABLE_UNSPEC,
|
|
}, RT_FILTER_DST|RT_FILTER_TABLE)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 2 || routes[0].Table == routes[1].Table {
|
|
t.Fatal("Routes not added properly")
|
|
}
|
|
|
|
// add a rule that uidrange match should result in route lookup of test table for uid other than current
|
|
// current uid is 0 due to skipUnlessRoot()
|
|
var uid uint32 = 1000
|
|
rule := NewRule()
|
|
rule.UIDRange = NewRuleUIDRange(uid, uid)
|
|
rule.Table = testtable
|
|
if err := RuleAdd(rule); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
dstIP := net.IPv4(10, 1, 1, 1)
|
|
|
|
// check getting route without UID option
|
|
routes, err = RouteGetWithOptions(dstIP, &RouteGetOptions{UID: nil})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// current uid is outside uidrange; rule does not apply; lookup main table
|
|
if len(routes) != 1 || !routes[0].Gw.Equal(gw1) {
|
|
t.Fatal(routes)
|
|
}
|
|
|
|
// check getting route with UID option
|
|
routes, err = RouteGetWithOptions(dstIP, &RouteGetOptions{UID: &uid})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// option uid is within uidrange; rule applies; lookup test table
|
|
if len(routes) != 1 || !routes[0].Gw.Equal(gw2) {
|
|
t.Fatal(routes)
|
|
}
|
|
}
|
|
|
|
func TestRouteFWMarkOption(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
// setup eth0 so that network is reachable
|
|
err := LinkAdd(&Dummy{LinkAttrs{Name: "eth0"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
link, err := LinkByName("eth0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addr := &Addr{
|
|
IPNet: &net.IPNet{
|
|
IP: net.IPv4(192, 168, 1, 1),
|
|
Mask: net.CIDRMask(16, 32),
|
|
},
|
|
}
|
|
if err = AddrAdd(link, addr); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// a table different than unix.RT_TABLE_MAIN
|
|
testtable := 1000
|
|
|
|
gw1 := net.IPv4(192, 168, 1, 254)
|
|
gw2 := net.IPv4(192, 168, 2, 254)
|
|
|
|
// add default route via gw1 (in main route table by default)
|
|
defaultRouteMain := Route{
|
|
Dst: nil,
|
|
Gw: gw1,
|
|
}
|
|
if err := RouteAdd(&defaultRouteMain); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// add default route via gw2 in test route table
|
|
defaultRouteTest := Route{
|
|
Dst: nil,
|
|
Gw: gw2,
|
|
Table: testtable,
|
|
}
|
|
if err := RouteAdd(&defaultRouteTest); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// check the routes are in different tables
|
|
routes, err := RouteListFiltered(FAMILY_V4, &Route{
|
|
Dst: nil,
|
|
Table: unix.RT_TABLE_UNSPEC,
|
|
}, RT_FILTER_DST|RT_FILTER_TABLE)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 2 || routes[0].Table == routes[1].Table {
|
|
t.Fatal("Routes not added properly")
|
|
}
|
|
|
|
// add a rule that fwmark match should result in route lookup of test table
|
|
fwmark := 1000
|
|
|
|
rule := NewRule()
|
|
rule.Mark = fwmark
|
|
rule.Mask = 0xFFFFFFFF
|
|
rule.Table = testtable
|
|
if err := RuleAdd(rule); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
dstIP := net.IPv4(10, 1, 1, 1)
|
|
|
|
// check getting route without FWMark option
|
|
routes, err = RouteGetWithOptions(dstIP, &RouteGetOptions{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 || !routes[0].Gw.Equal(gw1) {
|
|
t.Fatal(routes)
|
|
}
|
|
|
|
// check getting route with FWMark option
|
|
routes, err = RouteGetWithOptions(dstIP, &RouteGetOptions{Mark: fwmark})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(routes) != 1 || !routes[0].Gw.Equal(gw2) {
|
|
t.Fatal(routes)
|
|
}
|
|
}
|
|
|
|
func TestRouteGetFIBMatchOption(t *testing.T) {
|
|
tearDown := setUpNetlinkTest(t)
|
|
defer tearDown()
|
|
|
|
err := LinkAdd(&Dummy{LinkAttrs{Name: "eth0"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
link, err := LinkByName("eth0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = LinkSetUp(link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addr := &Addr{
|
|
IPNet: &net.IPNet{
|
|
IP: net.IPv4(192, 168, 0, 2),
|
|
Mask: net.CIDRMask(24, 32),
|
|
},
|
|
}
|
|
if err = AddrAdd(link, addr); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
route := &Route{
|
|
LinkIndex: link.Attrs().Index,
|
|
Gw: net.IPv4(192, 168, 1, 1),
|
|
Dst: &net.IPNet{
|
|
IP: net.IPv4(192, 168, 2, 0),
|
|
Mask: net.CIDRMask(24, 32),
|
|
},
|
|
Flags: int(FLAG_ONLINK),
|
|
}
|
|
|
|
err = RouteAdd(route)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
routes, err := RouteGetWithOptions(net.IPv4(192, 168, 2, 1), &RouteGetOptions{FIBMatch: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(routes) != 1 {
|
|
t.Fatalf("More than one route matched %v", routes)
|
|
}
|
|
|
|
if len(routes[0].ListFlags()) != 1 {
|
|
t.Fatalf("More than one route flag returned %v", routes[0].ListFlags())
|
|
}
|
|
|
|
flag := routes[0].ListFlags()[0]
|
|
if flag != "onlink" {
|
|
t.Fatalf("Unexpected flag %s returned", flag)
|
|
}
|
|
}
|