From 59ee150134bda858cbe26407e7006b250539441a Mon Sep 17 00:00:00 2001 From: Shaun Crampton Date: Wed, 3 Jan 2024 11:13:02 +0000 Subject: [PATCH] Add banchmarks for RouteListFiltered variants. --- netlink_test.go | 4 +- route_test.go | 171 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 2 deletions(-) diff --git a/netlink_test.go b/netlink_test.go index 822cced6..d73c246c 100644 --- a/netlink_test.go +++ b/netlink_test.go @@ -23,7 +23,7 @@ import ( type tearDownNetlinkTest func() -func skipUnlessRoot(t *testing.T) { +func skipUnlessRoot(t testing.TB) { t.Helper() if os.Getuid() != 0 { @@ -31,7 +31,7 @@ func skipUnlessRoot(t *testing.T) { } } -func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest { +func setUpNetlinkTest(t testing.TB) tearDownNetlinkTest { skipUnlessRoot(t) // new temporary namespace so we don't pollute the host diff --git a/route_test.go b/route_test.go index 0e685e72..2fbc0e0d 100644 --- a/route_test.go +++ b/route_test.go @@ -6,6 +6,7 @@ package netlink import ( "net" "os" + "runtime" "strconv" "testing" "time" @@ -875,6 +876,176 @@ func TestRouteFilterIterCanStop(t *testing.T) { } } +func BenchmarkRouteListFilteredOld(b *testing.B) { + tearDown := setUpNetlinkTest(b) + defer tearDown() + + link, err := setUpRoutesBench(b) + + b.ResetTimer() + b.ReportAllocs() + var routes []Route + for i := 0; i < b.N; i++ { + routes, err = pkgHandle.RouteListFilteredOld(FAMILY_V4, &Route{ + LinkIndex: link.Attrs().Index, + }, RT_FILTER_OIF) + if err != nil { + b.Fatal(err) + } + if len(routes) != 65535 { + b.Fatal("Incorrect number of routes.", len(routes)) + } + } + runtime.KeepAlive(routes) +} + +func BenchmarkRouteListFilteredNew(b *testing.B) { + tearDown := setUpNetlinkTest(b) + defer tearDown() + + link, err := setUpRoutesBench(b) + + b.ResetTimer() + b.ReportAllocs() + var routes []Route + for i := 0; i < b.N; i++ { + routes, err = pkgHandle.RouteListFiltered(FAMILY_V4, &Route{ + LinkIndex: link.Attrs().Index, + }, RT_FILTER_OIF) + if err != nil { + b.Fatal(err) + } + if len(routes) != 65535 { + b.Fatal("Incorrect number of routes.", len(routes)) + } + } + runtime.KeepAlive(routes) +} + +// RouteListFiltered gets a list of routes in the system filtered with specified rules. +// All rules must be defined in RouteFilter struct +func (h *Handle) RouteListFilteredOld(family int, filter *Route, filterMask uint64) ([]Route, error) { + req := h.newNetlinkRequest(unix.RTM_GETROUTE, unix.NLM_F_DUMP) + rtmsg := &nl.RtMsg{} + rtmsg.Family = uint8(family) + msgs, err := h.routeHandle(filter, req, rtmsg) + if err != nil { + return nil, err + } + + var res []Route + for _, m := range msgs { + msg := nl.DeserializeRtMsg(m) + if msg.Flags&unix.RTM_F_CLONED != 0 { + // Ignore cloned routes + continue + } + if msg.Table != unix.RT_TABLE_MAIN { + if filter == nil || filter != nil && filterMask&RT_FILTER_TABLE == 0 { + // Ignore non-main tables + continue + } + } + route, err := deserializeRoute(m) + if err != nil { + return nil, err + } + if filter != nil { + switch { + case filterMask&RT_FILTER_TABLE != 0 && filter.Table != unix.RT_TABLE_UNSPEC && route.Table != filter.Table: + continue + case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol: + continue + case filterMask&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope: + continue + case filterMask&RT_FILTER_TYPE != 0 && route.Type != filter.Type: + continue + case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos: + continue + case filterMask&RT_FILTER_REALM != 0 && route.Realm != filter.Realm: + continue + case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex: + continue + case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex: + continue + case filterMask&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw): + continue + case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src): + continue + case filterMask&RT_FILTER_DST != 0: + if filter.MPLSDst == nil || route.MPLSDst == nil || (*filter.MPLSDst) != (*route.MPLSDst) { + if filter.Dst == nil { + filter.Dst = genZeroIPNet(family) + } + if !ipNetEqual(route.Dst, filter.Dst) { + continue + } + } + case filterMask&RT_FILTER_HOPLIMIT != 0 && route.Hoplimit != filter.Hoplimit: + continue + } + } + res = append(res, route) + } + return res, nil +} + +func BenchmarkRouteListIter(b *testing.B) { + tearDown := setUpNetlinkTest(b) + defer tearDown() + + link, err := setUpRoutesBench(b) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + var routes int + err = RouteListFilteredIter(FAMILY_V4, &Route{ + LinkIndex: link.Attrs().Index, + }, RT_FILTER_OIF, func(route Route) (cont bool) { + routes++ + return true + }) + if err != nil { + b.Fatal(err) + } + if routes != 65535 { + b.Fatal("Incorrect number of routes.", routes) + } + } +} + +func setUpRoutesBench(b *testing.B) (Link, error) { + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + b.Fatal(err) + } + // bring the interface up + if err = LinkSetUp(link); err != nil { + b.Fatal(err) + } + + // add a gateway route + for i := 0; i < 65535; i++ { + dst := &net.IPNet{ + IP: net.IPv4(1, 1, byte(i>>8), byte(i&0xff)), + Mask: net.CIDRMask(32, 32), + } + route := Route{ + LinkIndex: link.Attrs().Index, + Dst: dst, + Scope: unix.RT_SCOPE_LINK, + Priority: 10, + Type: unix.RTN_UNICAST, + } + if err := RouteAdd(&route); err != nil { + b.Fatal(err) + } + } + return link, err +} + func tableIDIn(ids []int, id int) bool { for _, v := range ids { if v == id {