Can't delete U32 filters
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?
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
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?
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
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 :pray:
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 https://github.com/florianl/go-tc/issues/17#issuecomment-704467961 are provided. Otherwise I will in the following months close this issue because of missing response.
Thanks @florianl. I created new issue to track
@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)
}
}
}
}
Thanks for reporting. I will try to reproduce it and investigate.
Filter argument checking was simplified in https://github.com/florianl/go-tc/pull/120. If you run in further issues, pelase open a new issue.