Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't delete U32 filters #17

Closed
ianling opened this issue Oct 5, 2020 · 8 comments
Closed

Can't delete U32 filters #17

ianling opened this issue Oct 5, 2020 · 8 comments

Comments

@ianling
Copy link

ianling commented Oct 5, 2020

If I use ...Filter().Get(...) to retrieve a filter, I cannot then delete that filter: Filter().Delete(&filter)

From the command line, I am able to add and delete filters as expected:

$ tc filter add dev ens5 parent 0: protocol ip prio 1 u32 match ip dst 199.200.15.170 match ip dport 28145 0xffff action mirred egress redirect dev ifb0
$ tc filter del dev ens5 parent 0: handle 800::801 prio 1 u32

But I can't figure out how to accomplish the same thing in go-tc.

filterQuery := tc.Msg{
	Ifindex: uint32(2),
	Parent:  tc.HandleIngress,
}
filters, _ := tcgo.Filter().Get(&filterQuery)
filter := filters[0]
err := tcgo.Filter().Delete(&filter)  // error: "argument cannot be altered"

filter2 := tc.Object{
	tc.Msg{
		Handle:  filter.Handle,
		Parent:  filter.Parent,
		Ifindex: filter.Ifindex,
	},
	tc.Attribute{
		Kind: filter.Kind,
		U32: filter.U32,
	},
}
err = tcgo.Filter().Delete(&filter2)  // error: "netlink receive: no such file or directory"

Do you have any idea how I can delete a filter via go-tc?

@ianling
Copy link
Author

ianling commented Oct 5, 2020

I found that commenting out the "Handle:" and "U32:" lines in the example above allows the delete to succeed, but obviously that makes it match filters I wasn't intending to delete:

filter2 := tc.Object{
	tc.Msg{
		//Handle:  filter.Handle,
		Parent:  filter.Parent,
		Ifindex: filter.Ifindex,
	},
	tc.Attribute{
		Kind: filter.Kind,
		//U32: filter.U32,
	},
}
err = tcgo.Filter().Delete(&filter2)  // works! but deletes too many filters

@florianl
Copy link
Owner

florianl commented Oct 6, 2020

Hi!
And thanks for reporting this issue.
Can you provide a full working example that demonstrates the issue?
What kind of other filters are you applying and what differentiates them?

@sanfern
Copy link

sanfern commented May 23, 2023

Any progress in this issue, I am facing the same issue, while deleting filters netlink receive: no such file or directory

System details:

Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.5 LTS
Release:	20.04
Codename:	focal

Kernel Version: 5.15.0-051500-generic

main.go

package main

import (
	"fmt"
	"github.com/cilium/ebpf"
	tc "github.com/florianl/go-tc"
	"github.com/florianl/go-tc/core"
	"golang.org/x/sys/unix"
	"net"
	"os"
	"time"
)

func main() {
	tcIface := "enp0s3"
	devID, err := net.InterfaceByName(tcIface)
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not get interface ID: %v\n", err)
		return
	}

	tcgo, err := tc.Open(&tc.Config{})
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not open rtnetlink socket: %v\n", err)
		return
	}

	// get all the qdiscs from all interfaces
	qdiscs, err := tcgo.Qdisc().Get()
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not get qdiscs: %v\n", err)
		return
	}
	clsactFound := false
	for _, qdisc := range qdiscs {
		iface, err := net.InterfaceByIndex(int(qdisc.Ifindex))
		if err != nil {
			fmt.Fprintf(os.Stderr, "could not get interface from id %d: %v", qdisc.Ifindex, err)
			return
		}
		if iface.Name == tcIface && qdisc.Kind == "clsact" {
			clsactFound = true
		}
	}

	if !clsactFound {
		qdisc := tc.Object{
			Msg: tc.Msg{
				Family:  unix.AF_UNSPEC,
				Ifindex: uint32(devID.Index),
				Handle:  core.BuildHandle(tc.HandleRoot, 0x0000),
				Parent:  tc.HandleIngress,
				Info:    0,
			},
			Attribute: tc.Attribute{
				Kind: "clsact",
			},
		}

		if err := tcgo.Qdisc().Add(&qdisc); err != nil {
			fmt.Printf("could not assign clsact to %s: %v, its already exists", tcIface, err)
		}
	}

	kernFile := "tc_program_kern.o"

	prg, err := ebpf.LoadCollection(kernFile)
	if err != nil {
		fmt.Println("LoadCollection error : ", err)
		return
	}
	defer prg.Close()

	bpfProg := prg.Programs["tc_program"]
	fd := uint32(bpfProg.FD())
	flags := uint32(0x1)

	filter := tc.Object{
		tc.Msg{
			Family:  unix.AF_UNSPEC,
			Ifindex: uint32(devID.Index),
			Handle:  0,
			Parent:  core.BuildHandle(tc.HandleRoot, tc.HandleMinIngress),
			Info:    0x300,
		},
		tc.Attribute{
			Kind: "bpf",
			BPF: &tc.Bpf{
				FD:    &fd,
				Flags: &flags,
			},
		},
	}
	if err := tcgo.Filter().Add(&filter); err != nil {
		fmt.Fprintf(os.Stderr, "could not attach filter for eBPF program: %v\n", err)
		return
	}

	fmt.Println("Attaching tc program is successful, resting ...")
	time.Sleep(2 * time.Second)

	if err := tcgo.Filter().Delete(&filter); err != nil {
		fmt.Fprintf(os.Stderr, "could not del filter for eBPF program: %v\n", err)
		return
	}
}

Console Output :

# go run main.go
Attaching tc program is successful, resting ...
could not del filter for eBPF program: netlink receive: no such file or directory

@florianl
Copy link
Owner

Thanks for reporting the issue @sanfern

As filters of type U32 are different from filters of type BPF, I would appreceate to handle this in a dedicated issue.
But thanks for reporting with a minimal reproducable program 🙏

Did you try to list attached filters with something alone the following lines?

tcgo.Filter().Get(&tc.Msg{
		Family:  unix.AF_UNSPEC,
		Ifindex: uint32(devID.Index),
		Handle:  0x0,
		Parent:  core.BuildHandle(tc.HandleRoot, tc.HandleMinIngress),
	})

If you compare the reported filters with your initial given input, you will notice differences. In particular pay attention to tc.Msg.Info.
As a consequence, if you take the reported filter and use the reported object as input to the Delete() function, you should be able to delete filter.

With regard to the inital request from @ianling: I would be happy to go forward if answers to the questions in #17 (comment) are provided. Otherwise I will in the following months close this issue because of missing response.

@sanfern
Copy link

sanfern commented May 25, 2023

Thanks @florianl. I created new issue to track

@RealFatCat
Copy link

RealFatCat commented Jun 5, 2023

@florianl HI, I have the same issue with Nat action, and the reason is this check.

Mirred action have the same check.

So for now, we can't just Filter().Get() U32 filters with NAT and Mirred (may be some others too) actions and pass them to Filter().Delete() as is.

To repoduce, add some filters with NAT action to dummy0:

sudo ip l add dev dummy0 type dummy
sudo tc qdisc  add dev dummy0 ingress
sudo tc filter add dev  dummy0 parent ffff: pref 10 u32 match ip dst 192.168.1.1/24 action nat ingress 127.1.1.1/16 127.0.0.1 # just as example

and run this code:


import (
	"fmt"
	"net"
	"os"
	"syscall"

	"github.com/mdlayher/netlink"

	"github.com/florianl/go-tc"
)

const (
	linkName = "dummy0"
)

func main() {
	// open a rtnetlink socket
	rtnl, err := tc.Open(&tc.Config{})
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not open rtnetlink socket: %v\n", err)
		return
	}
	defer func() {
		if err := rtnl.Close(); err != nil {
			fmt.Fprintf(os.Stderr, "could not close rtnetlink socket: %v\n", err)
		}
	}()

	err = rtnl.SetOption(netlink.ExtendedAcknowledge, true)
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not set option ExtendedAcknowledge: %v\n", err)
		return
	}

	l, err := net.InterfaceByName(linkName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not get interface: %v\n", err)
		return
	}

	msg := tc.Msg{
		Family:  syscall.AF_UNSPEC,
		Ifindex: uint32(l.Index),
		Parent:  tc.HandleRoot,
	}

	filters, err := rtnl.Filter().Get(&msg)
	if err != nil {
		fmt.Fprintf(os.Stderr, "could not get filters: %v\n", err)
		return
	}

	for _, f := range filters {
		if f.Attribute.U32 != nil && f.Attribute.U32.Actions != nil {
			err := rtnl.Filter().Delete(&f)
			if err != nil {
				fmt.Println(err)
			}
		}
	}
}

@florianl
Copy link
Owner

florianl commented Jun 5, 2023

Thanks for reporting. I will try to reproduce it and investigate.

@florianl
Copy link
Owner

Filter argument checking was simplified in #120. If you run in further issues, pelase open a new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants