Skip to content

RuleListFiltered Doesn't Work for Src or Dst Default Routes #1080

@aauren

Description

@aauren

First off, thanks for putting together this library! kube-router has been benefiting from it for quite a while now!

While leaning deeper into using this library, I noticed that RuleListFiltered doesn't appear to work when attempting to filter by Src (RT_FILTER_SRC) or Dst (RT_FILTER_DST). After looking around a bit, it seems like when these are set to a default route (0.0.0.0/0 or ::/0), the netlink user-space returns a nil value for these attributes.

Additionally, I noticed that this appears to be at least a little expected by the library as the String() function actually has special handling for nil values and converts them to all: https://github.com/vishvananda/netlink/blob/main/rule.go#L35C1-L43C3

Anyway, I was working around this upstream, by applying special handling to filtered requests where the filter of rule.Src represents a default route and it occurred to me that I ought to check to see if there was interest from this project in just upstreaming the work and potentially fixing this for all users of the library.

So I guess my questions are as follows:

  • Is what I mentioned here expected functionality? Or should it be considered a bug with the way the library integrates with the netlink userspace?
  • If it is expected functionality, would there be interest in me submitting a PR that would attempt to fix filtering by checking to see if the Src or Dst filter represents a default route and then matching against a nil value?

Activity

aauren

aauren commented on May 6, 2025

@aauren
Author

If it helps, I've created a little test snippet that shows the problem:

package main

import (
	"net"

	"github.com/vishvananda/netlink"
	"k8s.io/klog/v2"
)

const (
	customDSRRouteTableID = 78
)

func testRule() {
	klog.Info("Rule test starting...")

	for _, route := range []string{"0.0.0.0/0", "127.0.0.99/32"} {
		klog.Infof("Testing rule for route: %s", route)
		nFamily := netlink.FAMILY_V4
		ipv4DefaultRoute := route
		_, defaultRouteCIDR, err := net.ParseCIDR(ipv4DefaultRoute)
		if err != nil {
			klog.Fatalf("failed to parse default route: %v", err)
		}

		nRule := netlink.NewRule()
		nRule.Family = nFamily
		nRule.Priority = 32765
		nRule.Src = defaultRouteCIDR
		nRule.Table = customDSRRouteTableID

		klog.Info("Listing rules...")
		rules, err := netlink.RuleListFiltered(nFamily, nRule, netlink.RT_FILTER_TABLE|netlink.RT_FILTER_PRIORITY|netlink.RT_FILTER_SRC)
		if err != nil {
			klog.Fatalf("failed to list rules: %v", err)
		}
		klog.Infof("rule found: %d", len(rules))
		for _, rule := range rules {
			klog.Infof("rule found: %+v", rule)
		}
		klog.Info("Finished listing rules.")

		klog.Info("Adding rule...")
		err = netlink.RuleAdd(nRule)
		if err != nil {
			klog.Fatalf("failed to add rule: %v", err)
		}

		klog.Info("Listing rules again...")
		rules, err = netlink.RuleListFiltered(nFamily, nRule, netlink.RT_FILTER_TABLE|netlink.RT_FILTER_PRIORITY|netlink.RT_FILTER_SRC)
		if err != nil {
			klog.Fatalf("failed to list rules: %v", err)
		}
		klog.Infof("rule found: %d", len(rules))
		for _, rule := range rules {
			klog.Infof("rule found: %+v", rule)
		}
		klog.Info("Finished listing rules again.")

		klog.Infof("Finished rule test for %s", route)
	}
	klog.Info("Rule test completed.")
}

func main() {
	testRule()
}

When run the output looks as follows:

I0506 18:39:53.673947 1932377 route-and-rule-test.go:15] Rule test starting...
I0506 18:39:53.674009 1932377 route-and-rule-test.go:18] Testing rule for route: 0.0.0.0/0
I0506 18:39:53.674014 1932377 route-and-rule-test.go:32] Listing rules...
I0506 18:39:53.674089 1932377 route-and-rule-test.go:37] rule found: 0
I0506 18:39:53.674097 1932377 route-and-rule-test.go:41] Finished listing rules.
I0506 18:39:53.674101 1932377 route-and-rule-test.go:43] Adding rule...
I0506 18:39:53.674138 1932377 route-and-rule-test.go:49] Listing rules again...
I0506 18:39:53.674166 1932377 route-and-rule-test.go:54] rule found: 0
I0506 18:39:53.674171 1932377 route-and-rule-test.go:58] Finished listing rules again.
I0506 18:39:53.674175 1932377 route-and-rule-test.go:60] Finished rule test for 0.0.0.0/0
I0506 18:39:53.674179 1932377 route-and-rule-test.go:18] Testing rule for route: 127.0.0.99/32
I0506 18:39:53.674184 1932377 route-and-rule-test.go:32] Listing rules...
I0506 18:39:53.674207 1932377 route-and-rule-test.go:37] rule found: 0
I0506 18:39:53.674212 1932377 route-and-rule-test.go:41] Finished listing rules.
I0506 18:39:53.674217 1932377 route-and-rule-test.go:43] Adding rule...
I0506 18:39:53.674242 1932377 route-and-rule-test.go:49] Listing rules again...
I0506 18:39:53.674275 1932377 route-and-rule-test.go:54] rule found: 1
I0506 18:39:53.674281 1932377 route-and-rule-test.go:56] rule found: ip rule 32765: from 127.0.0.99/32 to all table 78
I0506 18:39:53.674296 1932377 route-and-rule-test.go:58] Finished listing rules again.
I0506 18:39:53.674299 1932377 route-and-rule-test.go:60] Finished rule test for 127.0.0.99/32
I0506 18:39:53.674305 1932377 route-and-rule-test.go:62] Rule test completed.

The output of ip rule list after running the above test is:

$ ip rule list
0:      from all lookup local
32765:  from all lookup kube-router-external
32765:  from 127.0.0.99 lookup kube-router-external
32766:  from all lookup main
32767:  from all lookup default

As you can see, when the rule source is set to 0.0.0.0/0 it will get added, but afterwards is still not found when RuleListFiltered runs again. However, when the source is set to something other than the default route, then it finds the route as intended.

added a commit that references this issue on May 10, 2025
added a commit that references this issue on Jun 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @aauren

        Issue actions

          RuleListFiltered Doesn't Work for Src or Dst Default Routes · Issue #1080 · vishvananda/netlink