### Manage traffic classes with (*Tc).Class().Add and Class().Get Source: https://context7.com/florianl/go-tc/llms.txt Use `Add` to create a child class under a classful qdisc, and `Get` to retrieve all classes on an interface. This example demonstrates adding an HTB leaf class with specified rate and ceil, then retrieving and printing class details. ```go package main import ( "fmt" "net" "os" "github.com/florianl/go-tc" "github.com/florianl/go-tc/core" "golang.org/x/sys/unix" ) func main() { devID, _ := net.InterfaceByName("eth0") rtnl, _ := tc.Open(&tc.Config{}) defer rtnl.Close() // Add an HTB leaf class with 10Mbit/s rate and 20Mbit/s ceil: // tc class add dev eth0 parent 1: classid 1:10 htb rate 10mbit ceil 20mbit ceil := uint64(20_000_000 / 8) // bytes/s rate := uint64(10_000_000 / 8) class := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: core.BuildHandle(0x1, 0x10), Parent: core.BuildHandle(0x1, 0x0), }, Attribute: tc.Attribute{ Kind: "htb", Htb: &tc.Htb{ Parms: &tc.HtbOpt{ Rate: tc.RateSpec{ Rate: uint32(rate), CellLog: 3, Linklayer: 1, CellAlign: 0xffff, }, Ceil: tc.RateSpec{ Rate: uint32(ceil), CellLog: 3, Linklayer: 1, CellAlign: 0xffff, }, Buffer: 1000, Cbuffer: 1000, }, }, }, } if err := rtnl.Class().Add(&class); err != nil { fmt.Fprintf(os.Stderr, "class add: %v\n", err) os.Exit(1) } // Retrieve all classes on the interface classes, err := rtnl.Class().Get(&tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), }) if err != nil { fmt.Fprintf(os.Stderr, "class get: %v\n", err) os.Exit(1) } for _, c := range classes { iface, _ := net.InterfaceByIndex(int(c.Ifindex)) fmt.Printf("%-" + "15s " + "kind=%-" + "6s " + "handle=0x%08x " + "parent=0x%08x\n", iface.Name, c.Kind, c.Handle, c.Parent) } } ``` -------------------------------- ### Manage Standalone Actions (Add, Get, Delete) Source: https://context7.com/florianl/go-tc/llms.txt Demonstrates how to create, retrieve, and delete standalone traffic control actions. Ensure the go-tc library is imported and a connection to the network namespace is established. ```go package main import ( "fmt" "os" "github.com/florianl/go-tc" ) func main() { rtnl, err := tc.Open(&tc.Config{}) if err != nil { fmt.Fprintf(os.Stderr, "open: %v\n", err) os.Exit(1) } defer rtnl.Close() // Create a standalone drop action (gact action drop) if err := rtnl.Actions().Add([]*tc.Action{ { Kind: "gact", Gact: &tc.Gact{ Parms: &tc.GactParms{Action: tc.ActShot}, }, }, }); err != nil { fmt.Fprintf(os.Stderr, "actions add: %v\n", err) os.Exit(1) } // List all gact actions actions, err := rtnl.Actions().Get("gact") if err != nil { fmt.Fprintf(os.Stderr, "actions get: %v\n", err) os.Exit(1) } for _, a := range actions { fmt.Printf("kind=%-8s index=%d\n", a.Kind, a.Index) if a.Gact != nil && a.Gact.Parms != nil { fmt.Printf(" action=%d\n", a.Gact.Parms.Action) } } // Delete the action at index 1 if err := rtnl.Actions().Delete([]*tc.Action{ {Kind: "gact", Index: 1}, }); err != nil { fmt.Fprintf(os.Stderr, "actions delete: %v\n", err) os.Exit(1) } fmt.Println("standalone gact action deleted") } ``` -------------------------------- ### Manage Filter Chains (Add, Get, Delete) Source: https://context7.com/florianl/go-tc/llms.txt Shows how to add, retrieve, and delete filter chains associated with a specific network interface and qdisc/class. Requires network interface name and appropriate permissions. ```go package main import ( "fmt" "net" "os" "github.com/florianl/go-tc" "golang.org/x/sys/unix" ) func main() { devID, _ := net.InterfaceByName("eth0") rtnl, _ := tc.Open(&tc.Config{}) defer rtnl.Close() chainNum := uint32(10) // tc chain add dev eth0 ingress chain 10 chain := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Parent: tc.HandleIngress + 1, }, Attribute: tc.Attribute{ Chain: &chainNum, }, } if err := rtnl.Chain().Add(&chain); err != nil { fmt.Fprintf(os.Stderr, "chain add: %v\n", err) os.Exit(1) } // Retrieve chains chains, err := rtnl.Chain().Get(&tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Parent: tc.HandleIngress + 1, }) if err != nil { fmt.Fprintf(os.Stderr, "chain get: %v\n", err) os.Exit(1) } for _, c := range chains { if c.Chain != nil { fmt.Printf("chain %d\n", *c.Chain) } } // tc chain del dev eth0 ingress chain 10 if err := rtnl.Chain().Delete(&chain); err != nil { fmt.Fprintf(os.Stderr, "chain delete: %v\n", err) os.Exit(1) } } ``` -------------------------------- ### List All Qdiscs with go-tc Source: https://context7.com/florianl/go-tc/llms.txt Retrieves all qdiscs installed on network interfaces. Displays interface name, qdisc kind, handle, parent, and statistics if available. ```go package main import ( "fmt" "net" "os" "github.com/florianl/go-tc" ) func main() { rtnl, err := tc.Open(&tc.Config{}) if err != nil { fmt.Fprintf(os.Stderr, "open: %v\n", err) os.Exit(1) } defer rtnl.Close() qdiscs, err := rtnl.Qdisc().Get() if err != nil { fmt.Fprintf(os.Stderr, "qdisc get: %v\n", err) os.Exit(1) } for _, qdisc := range qdiscs { iface, err := net.InterfaceByIndex(int(qdisc.Ifindex)) if err != nil { fmt.Fprintf(os.Stderr, "interface lookup %d: %v\n", qdisc.Ifindex, err) continue } fmt.Printf("%-20s kind=%-12s handle=0x%08x parent=0x%08x\n", iface.Name, qdisc.Kind, qdisc.Handle, qdisc.Parent) if qdisc.Stats != nil { fmt.Printf(" bytes=%-12d packets=%-10d drops=%d\n", qdisc.Stats.Bytes, qdisc.Stats.Packets, qdisc.Stats.Drops) } } } ``` -------------------------------- ### (*Tc).Qdisc().Get Source: https://context7.com/florianl/go-tc/llms.txt Retrieves every qdisc currently installed across all network interfaces from the kernel. Returns a slice of tc.Object, each containing a Msg (ifindex, handle, parent) and an Attribute (Kind plus qdisc-specific parameters and statistics). ```APIDOC ## List all qdiscs ### Description Retrieves every qdisc currently installed across all network interfaces from the kernel. Returns a slice of `tc.Object`, each containing a `Msg` (ifindex, handle, parent) and an `Attribute` (Kind plus qdisc-specific parameters and statistics). ### Method `(*Tc).Qdisc().Get()` ### Parameters None ### Returns - `[]tc.Object`: A slice of qdisc objects. - `error`: An error if the qdiscs could not be retrieved. ``` -------------------------------- ### Get All Qdiscs from Interfaces in Go Source: https://github.com/florianl/go-tc/blob/main/README.md Opens a rtnetlink socket, sets the ExtendedAcknowledge option for enhanced kernel error messages, and retrieves all queueing disciplines (qdiscs) from all network interfaces. Requires CAP_NET_ADMIN privileges. ```Go package main import ( "fmt" "net" "os" "github.com/mdlayher/netlink" "github.com/florianl/go-tc" ) 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) } }() // For enhanced error messages from the kernel, it is recommended to set // option `NETLINK_EXT_ACK`, which is supported since 4.12 kernel. // // If not supported, `unix.ENOPROTOOPT` is returned. err = rtnl.SetOption(netlink.ExtendedAcknowledge, true) if err != nil { fmt.Fprintf(os.Stderr, "could not set option ExtendedAcknowledge: %v\n", err) return } // get all the qdiscs from all interfaces qdiscs, err := rtnl.Qdisc().Get() if err != nil { fmt.Fprintf(os.Stderr, "could not get qdiscs: %v\n", err) return } 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 } fmt.Printf("%20s\t%s\n", iface.Name, qdisc.Kind) } } ``` -------------------------------- ### Set CAP_NET_ADMIN Capability Source: https://github.com/florianl/go-tc/blob/main/README.md Example command to set the CAP_NET_ADMIN capability for an executable, which is required for the go-tc package to interact with the kernel's traffic control system. ```shell setcap 'cap_net_admin=+ep' /your/executable ``` -------------------------------- ### Class().Add / Class().Get Source: https://context7.com/florianl/go-tc/llms.txt Manages traffic classes, which divide bandwidth among traffic streams under classful qdiscs. `Add` creates a child class, and `Get` retrieves all classes on an interface. ```APIDOC ## Class().Add / Class().Get ### Description Manages traffic classes. Classes belong to classful qdiscs (`htb`, `hfsc`, `drr`, `qfq`, `dsmark`) and divide bandwidth among traffic streams. `Add` creates a child class under a qdisc handle; `Get` retrieves all classes on an interface. ### Method `(*Tc) Class().Add(class *Object) error` `(*Tc) Class().Get(msg *Msg) ([]Object, error)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body (for Add) - **class** (*tc.Object) - Required - The class object to add, containing `Msg` and `Attribute` fields. ### Request Example (Add) ```go class := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: core.BuildHandle(0x1, 0x10), Parent: core.BuildHandle(0x1, 0x0), }, Attribute: tc.Attribute{ Kind: "htb", Htb: &tc.Htb{ Parms: &tc.HtbOpt{ Rate: tc.RateSpec{ Rate: uint32(rate) }, Ceil: tc.RateSpec{ Rate: uint32(ceil) }, }, }, }, } if err := rtnl.Class().Add(&class); err != nil { // Handle error } ``` ### Request Example (Get) ```go classes, err := rtnl.Class().Get(&tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), }) if err != nil { // Handle error } // Process classes ``` ### Response (Get) #### Success Response (200) - **classes** ([]tc.Object) - A slice of tc.Object representing the retrieved traffic classes. #### Response Example (Get) ```go // Example output format: // eth0 kind=htb handle=0x00000001 parent=0x00000000 ``` ``` -------------------------------- ### Add HTB Root Qdisc to Interface Source: https://context7.com/florianl/go-tc/llms.txt Creates a new HTB root queueing discipline on a specified network interface. Ensure the interface exists and the necessary permissions are available. ```go package main import ( "fmt" "net" "os" "github.com/florianl/go-tc" "github.com/florianl/go-tc/core" "golang.org/x/sys/unix" ) func main() { devID, err := net.InterfaceByName("eth0") if err != nil { fmt.Fprintf(os.Stderr, "interface: %v\n", err) os.Exit(1) } rtnl, err := tc.Open(&tc.Config{}) if err != nil { fmt.Fprintf(os.Stderr, "open: %v\n", err) os.Exit(1) } defer rtnl.Close() // Attach an HTB root qdisc: tc qdisc add dev eth0 root handle 1: htb qdisc := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: core.BuildHandle(0x1, 0x0), Parent: tc.HandleRoot, }, Attribute: tc.Attribute{ Kind: "htb", Htb: &tc.Htb{ Init: &tc.HtbGlob{ Version: 0x3, Rate2Quantum: 0xa, }, }, }, } if err := rtnl.Qdisc().Add(&qdisc); err != nil { fmt.Fprintf(os.Stderr, "qdisc add: %v\n", err) os.Exit(1) } fmt.Println("htb qdisc attached to eth0") } ``` -------------------------------- ### Clock and Timing Utilities for Qdiscs Source: https://context7.com/florianl/go-tc/llms.txt Provides timing conversions for qdiscs accepting time parameters. `InitializeClock` calibrates tick frequency. `Duration2TcTime` and `Time2Tick` convert between `time.Duration` and kernel ticks. `XmitTime` calculates transmission time. Must call `InitializeClock` first. Requires `github.com/florianl/go-tc/core`. ```go package main import ( "fmt" "os" "time" "github.com/florianl/go-tc/core" ) func main() { // Initialize once at startup if err := core.InitializeClock(); err != nil { fmt.Fprintf(os.Stderr, "init clock: %v\n", err) os.Exit(1) } fmt.Printf("Clock initialized: factor=%.4f tickInUSec=%.4f\n", core.GetClockFactor(), core.GetTickInUSec()) // Convert 50ms to ticks (for netem Latency field) tcTime, err := core.Duration2TcTime(50 * time.Millisecond) if err != nil { fmt.Fprintf(os.Stderr, "duration convert: %v\n", err) os.Exit(1) } ticks := core.Time2Tick(tcTime) fmt.Printf("50ms -> tcTime=%d ticks=%d\n", tcTime, ticks) // Round-trip back to microseconds usec := core.Tick2Time(ticks) fmt.Printf("ticks=%d -> %d µs (~%.1f ms)\n", ticks, usec, float64(usec)/1000) // Compute transmission time for a 1500-byte packet at 100Mbit/s rate := uint64(100_000_000 / 8) // bytes/s xmitTicks := core.XmitTime(rate, 1500) fmt.Printf("1500 bytes @ 100Mbit/s = %d ticks (~%d µs)\n", xmitTicks, core.Tick2Time(xmitTicks)) } // Example output: // Clock initialized: factor=1.0000 tickInUSec=1.0000 // 50ms -> tcTime=50000 ticks=50000 // ticks=50000 -> 50000 µs (~50.0 ms) // 1500 bytes @ 100Mbit/s = 120 ticks (~120 µs) ``` -------------------------------- ### Qdisc Handle Arithmetic with BuildHandle and SplitHandle Source: https://context7.com/florianl/go-tc/llms.txt Constructs and splits 32-bit qdisc handles from major and minor parts. Utilizes constants like `tc.HandleRoot` and `tc.HandleIngress` for common attachment points. Requires `github.com/florianl/go-tc` and `github.com/florianl/go-tc/core`. ```go package main import ( "fmt" "github.com/florianl/go-tc" "github.com/florianl/go-tc/core" ) func main() { // Build handle 1:10 (major=1, minor=10) h := core.BuildHandle(0x1, 0x10) fmt.Printf("handle 1:10 = 0x%08x\n", h) // 0x00010010 // Split it back maj, min := core.SplitHandle(h) fmt.Printf("major=%d minor=%d\n", maj, min) // major=1 minor=16 // Common special-purpose handles fmt.Printf("HandleRoot = 0x%08x\n", tc.HandleRoot) // 0xffffffff fmt.Printf("HandleIngress = 0x%08x\n", tc.HandleIngress) // 0xfffffff1 // Ingress and egress parents for clsact fmt.Printf("ingress qdisc parent = 0x%08x\n", core.BuildHandle(tc.HandleRoot, tc.HandleMinIngress)) } ``` -------------------------------- ### (*Tc).Qdisc().Add Source: https://context7.com/florianl/go-tc/llms.txt Attaches a new queueing discipline (qdisc) to a network interface. Requires specifying the interface index, handle, parent, and qdisc kind with its configuration. ```APIDOC ## (*Tc).Qdisc().Add — Attach a new qdisc to an interface ### Description Creates a new queueing discipline on a network interface. The `Object.Msg.Ifindex` must be set to the interface index, `Handle` and `Parent` control the qdisc hierarchy position, and `Attribute.Kind` selects the qdisc type with its configuration struct pointer set in `Attribute`. ### Method `Add` ### Parameters - `qdisc` (*tc.Object) - The qdisc object to add, containing message and attribute details. ### Request Example ```go // Example usage within a Go program qdisc := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: core.BuildHandle(0x1, 0x0), Parent: tc.HandleRoot, }, Attribute: tc.Attribute{ Kind: "htb", Htb: &tc.Htb{ Init: &tc.HtbGlob{ Version: 0x3, Rate2Quantum: 0xa, }, }, }, } if err := rtnl.Qdisc().Add(&qdisc); err != nil { // Handle error } ``` ### Response - **Error**: Returns an error if the qdisc cannot be added. ``` -------------------------------- ### Attach eBPF Program to Interface Ingress Source: https://context7.com/florianl/go-tc/llms.txt This snippet demonstrates attaching a minimal eBPF program to the ingress path of an interface using `clsact` and `bpf` filter. Ensure the interface `eth0` exists and the program has `CAP_NET_ADMIN` privileges. ```go package main import ( "fmt" "net" "os" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/florianl/go-tc" "github.com/florianl/go-tc/core" "github.com/mdlayher/netlink" "golang.org/x/sys/unix" ) func main() { devID, _ := net.InterfaceByName("eth0") rtnl, err := tc.Open(&tc.Config{}) if err != nil { fmt.Fprintf(os.Stderr, "open: %v\n", err) os.Exit(1) } defer rtnl.Close() rtnl.SetOption(netlink.ExtendedAcknowledge, true) // Attach clsact qdisc as ingress/egress anchor qdisc := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: core.BuildHandle(tc.HandleRoot, 0x0), Parent: tc.HandleIngress, }, Attribute: tc.Attribute{Kind: "clsact"}, } if err := rtnl.Qdisc().Add(&qdisc); err != nil { fmt.Fprintf(os.Stderr, "qdisc add: %v\n", err) os.Exit(1) } defer rtnl.Qdisc().Delete(&qdisc) // Load a minimal eBPF classifier (returns TC_ACT_OK = 0) prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ Name: "pass_all", Type: ebpf.SchedCLS, Instructions: asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.Return(), }, License: "GPL", }) if err != nil { fmt.Fprintf(os.Stderr, "ebpf load: %v\n", err) os.Exit(1) } defer prog.Close() fd := uint32(prog.FD()) flags := uint32(0x1) // BPF_TC_F_REPLACE // Attach eBPF program to ingress path filter := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: 0, Parent: core.BuildHandle(tc.HandleRoot, tc.HandleMinIngress), Info: core.FilterInfo(0, unix.ETH_P_ALL), }, Attribute: tc.Attribute{ Kind: "bpf", BPF: &tc.Bpf{ FD: &fd, Flags: &flags, }, }, } if err := rtnl.Filter().Add(&filter); err != nil { fmt.Fprintf(os.Stderr, "filter add: %v\n", err) os.Exit(1) } fmt.Println("eBPF program attached to eth0 ingress") } ``` -------------------------------- ### `core.InitializeClock` / `core.Duration2TcTime` / `core.Time2Tick` Source: https://context7.com/florianl/go-tc/llms.txt Provides clock and timing utilities for qdiscs that accept time parameters. `InitializeClock` calibrates the kernel's tick frequency. `Duration2TcTime` converts `time.Duration` to a TC time value, and `Time2Tick` converts that to kernel ticks. These must be called before using other timing functions. ```APIDOC ## `core.InitializeClock` / `core.Duration2TcTime` / `core.Time2Tick` — Clock and timing utilities ### Description The `core` package provides timing conversions needed for qdiscs that accept time parameters (e.g. `netem`, `tbf`). `InitializeClock` reads `/proc/net/psched` to calibrate the kernel's tick frequency. `Duration2TcTime` converts a `time.Duration` to a TC time value; `Time2Tick` converts that to kernel ticks. Must be called before using any timing functions. ### Method `InitializeClock() error` `Duration2TcTime(d time.Duration) (uint64, error)` `Time2Tick(tcTime uint64) uint64` `Tick2Time(ticks uint64) uint64` `XmitTime(rate uint64, bytes uint32) uint64` `GetClockFactor() float64` `GetTickInUSec() float64` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go if err := core.InitializeClock(); err != nil { // Handle error } tcTime, err := core.Duration2TcTime(50 * time.Millisecond) ticks := core.Time2Tick(tcTime) ``` ### Response #### Success Response (200) Returns the converted time values or ticks. `InitializeClock` returns `nil` on success. #### Response Example ```go // After InitializeClock(): // Clock initialized: factor=1.0000 tickInUSec=1.0000 // 50ms -> tcTime=50000 ticks=50000 // ticks=50000 -> 50000 µs (~50.0 ms) // 1500 bytes @ 100Mbit/s = 120 ticks (~120 µs) ``` ``` -------------------------------- ### `core.BuildHandle` / `core.SplitHandle` Source: https://context7.com/florianl/go-tc/llms.txt Provides functionality for qdisc handle arithmetic. `BuildHandle` constructs a 32-bit handle from major and minor parts, while `SplitHandle` performs the reverse operation. Constants like `tc.HandleRoot` and `tc.HandleIngress` are available for common attachment points. ```APIDOC ## `core.BuildHandle` / `core.SplitHandle` — Qdisc handle arithmetic ### Description `BuildHandle` constructs the 32-bit handle used in `Msg.Handle` and `Msg.Parent` from major and minor parts. `SplitHandle` reverses the operation. The constants `tc.HandleRoot`, `tc.HandleIngress`, and `tc.HandleMinIngress` cover common attachment points. ### Method `BuildHandle(major, minor uint8) uint32` `SplitHandle(handle uint32) (major, minor uint8)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go h := core.BuildHandle(0x1, 0x10) maj, min := core.SplitHandle(h) ``` ### Response #### Success Response (200) Returns the constructed handle (for `BuildHandle`) or major/minor parts (for `SplitHandle`). #### Response Example ```go // For BuildHandle(0x1, 0x10) // h = 0x00010010 // For SplitHandle(0x00010010) // maj = 1, min = 16 ``` ``` -------------------------------- ### Replace Netem Qdisc with Delay and Loss Source: https://context7.com/florianl/go-tc/llms.txt Atomically adds or replaces a Netem qdisc on a network interface, applying specified delay and loss. This is useful for idempotent configuration of classless qdiscs. Ensure the clock is initialized before use. ```go package main import ( "fmt" "net" "os" "time" "github.com/florianl/go-tc" "github.com/florianl/go-tc/core" "golang.org/x/sys/unix" ) func main() { if err := core.InitializeClock(); err != nil { fmt.Fprintf(os.Stderr, "init clock: %v\n", err) os.Exit(1) } devID, _ := net.InterfaceByName("eth0") rtnl, _ := tc.Open(&tc.Config{}) defer rtnl.Close() // Convert 100ms delay to kernel ticks tcTime, _ := core.Duration2TcTime(100 * time.Millisecond) ticks := core.Time2Tick(tcTime) // tc qdisc replace dev eth0 root handle 1: netem delay 100ms loss 1% var ecn uint32 = 1 qdisc := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: core.BuildHandle(0x1, 0x0), Parent: tc.HandleRoot, }, Attribute: tc.Attribute{ Kind: "netem", Netem: &tc.Netem{ Qopt: tc.NetemQopt{ Latency: ticks, Limit: 1000, Loss: 42949673, // ~1% (uint32 fraction of 2^32) }, Ecn: &ecn, }, }, } if err := rtnl.Qdisc().Replace(&qdisc); err != nil { fmt.Fprintf(os.Stderr, "qdisc replace: %v\n", err) os.Exit(1) } fmt.Println("netem qdisc with 100ms delay and 1% loss applied") } ``` -------------------------------- ### List filters on a qdisc/class using (*Tc).Filter().Get Source: https://context7.com/florianl/go-tc/llms.txt Retrieves all filters attached to a specific qdisc/class on an interface. The Msg parameter must set Ifindex and Parent to identify the attachment point. Info may be left zero to retrieve all priorities. This is useful for inspecting the current filter configuration. ```go package main import ( "fmt" "net" "os" "github.com/florianl/go-tc" "golang.org/x/sys/unix" ) func main() { devID, _ := net.InterfaceByName("eth0") rtnl, _ := tc.Open(&tc.Config{}) defer rtnl.Close() filters, err := rtnl.Filter().Get(&tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Parent: tc.HandleIngress + 1, // ingress }) if err != nil { fmt.Fprintf(os.Stderr, "filter get: %v\n", err) os.Exit(1) } for _, f := range filters { iface, _ := net.InterfaceByIndex(int(f.Ifindex)) fmt.Printf("iface=%-15s kind=%-12s handle=0x%08x\n", iface.Name, f.Kind, f.Handle) if f.Flower != nil && f.Flower.KeyEthSrc != nil { fmt.Printf(" flower src_mac=%s\n", f.Flower.KeyEthSrc) } } } ``` -------------------------------- ### Configure Netlink Socket Options with SetOption Source: https://context7.com/florianl/go-tc/llms.txt Enables or disables netlink socket-level options. Use ExtendedAcknowledge for kernel error strings (Linux 4.12+). Requires `github.com/florianl/go-tc` and `github.com/mdlayher/netlink`. ```go package main import ( "fmt" "os" "github.com/florianl/go-tc" "github.com/mdlayher/netlink" "golang.org/x/sys/unix" ) func main() { rtnl, err := tc.Open(&tc.Config{}) if err != nil { fmt.Fprintf(os.Stderr, "open: %v\n", err) os.Exit(1) } defer rtnl.Close() if err := rtnl.SetOption(netlink.ExtendedAcknowledge, true); err != nil { if err == unix.ENOPROTOOPT { fmt.Fprintln(os.Stderr, "kernel too old for ExtendedAcknowledge") } else { fmt.Fprintf(os.Stderr, "set option: %v\n", err) os.Exit(1) } } fmt.Println("ExtendedAcknowledge enabled — kernel error strings will be returned") } ``` -------------------------------- ### Attach a traffic filter using (*Tc).Filter().Add Source: https://context7.com/florianl/go-tc/llms.txt Adds a filter to a qdisc or class, classifying packets and potentially carrying embedded actions. The Msg.Parent identifies the qdisc/class to attach to, and Msg.Info encodes priority and protocol. Supported kinds include u32, flower, bpf, and others. Ensure the parent qdisc (e.g., clsact) is attached before adding filters. ```go package main import ( "fmt" "net" "os" "github.com/florianl/go-tc" "github.com/florianl/go-tc/core" "golang.org/x/sys/unix" ) func main() { devID, _ := net.InterfaceByName("eth0") rtnl, _ := tc.Open(&tc.Config{}) defer rtnl.Close() // First, attach a clsact qdisc as the ingress anchor clsact := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: core.BuildHandle(tc.HandleRoot, 0x0), Parent: tc.HandleIngress, }, Attribute: tc.Attribute{Kind: "clsact"}, } rtnl.Qdisc().Add(&clsact) // Drop packets from a specific source MAC: // tc filter add dev eth0 ingress protocol all prio 1 flower src_mac 00:00:5e:00:53:01 action gact drop srcMac, _ := net.ParseMAC("00:00:5e:00:53:01") actions := []*tc.Action{ { Kind: "gact", Gact: &tc.Gact{ Parms: &tc.GactParms{Action: tc.ActShot}, }, }, } filter := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: 0, Parent: tc.HandleIngress + 1, Info: core.FilterInfo(1, unix.ETH_P_ALL), }, Attribute: tc.Attribute{ Kind: "flower", Flower: &tc.Flower{ KeyEthSrc: &srcMac, Actions: &actions, }, }, } if err := rtnl.Filter().Add(&filter); err != nil { fmt.Fprintf(os.Stderr, "filter add: %v\n", err) os.Exit(1) } fmt.Println("flower filter installed: drop packets from 00:00:5e:00:53:01") } ``` -------------------------------- ### `(*Tc).SetOption` Source: https://context7.com/florianl/go-tc/llms.txt Configures netlink socket-level options on the underlying connection. The `netlink.ExtendedAcknowledge` option, when enabled, causes the kernel to include human-readable error descriptions with numeric error codes. ```APIDOC ## `(*Tc).SetOption` — Configure netlink socket options ### Description Enables or disables netlink socket-level options on the underlying connection. The most useful option is `netlink.ExtendedAcknowledge`, which causes the kernel (Linux 4.12+) to include a human-readable error description alongside numeric error codes. ### Method `SetOption(option int, enable bool)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go if err := rtnl.SetOption(netlink.ExtendedAcknowledge, true); err != nil { // Handle error } ``` ### Response #### Success Response (200) No explicit response body is defined, success is indicated by the absence of an error. #### Response Example None ``` -------------------------------- ### Encode Filter Priority and Protocol with FilterInfo Source: https://context7.com/florianl/go-tc/llms.txt Encodes priority and protocol into the `Msg.Info` field for `Filter.Add` and `Filter.Get`. Protocol values are from `unix.ETH_P_*` constants. Requires `github.com/florianl/go-tc/core` and `golang.org/x/sys/unix`. ```go package main import ( "fmt" "github.com/florianl/go-tc/core" "golang.org/x/sys/unix" ) func main() { // Priority 1, match all protocols info := core.FilterInfo(1, unix.ETH_P_ALL) fmt.Printf("prio=1 proto=all -> Info=0x%08x\n", info) // Priority 100, match IPv4 only info4 := core.FilterInfo(100, unix.ETH_P_IP) fmt.Printf("prio=100 proto=IP -> Info=0x%08x\n", info4) // Priority 50, match IPv6 only info6 := core.FilterInfo(50, unix.ETH_P_IPV6) fmt.Printf("prio=50 proto=IPv6 -> Info=0x%08x\n", info6) } ``` -------------------------------- ### Modify a qdisc in-place Source: https://context7.com/florianl/go-tc/llms.txt Updates parameters of an existing qdisc without creating or destroying it. Uses RTM_NEWQDISC without the Create or Replace flags so the kernel rejects the call if the qdisc does not already exist. ```APIDOC ## (*Tc).Qdisc().Change — Modify a qdisc in-place ### Description Updates parameters of an existing qdisc without creating or destroying it. Uses `RTM_NEWQDISC` without the `Create` or `Replace` flags so the kernel rejects the call if the qdisc does not already exist. ### Method `Change` ### Parameters - `qdisc` (*tc.Object) - The qdisc object with updated parameters. ``` -------------------------------- ### (*Tc).Qdisc().Replace Source: https://context7.com/florianl/go-tc/llms.txt Atomically adds or replaces a qdisc on a network interface. This operation is idempotent, meaning it will replace an existing qdisc if one is found at the specified location. ```APIDOC ## (*Tc).Qdisc().Replace — Add or replace a qdisc atomically ### Description Like `Add`, but idempotent: if a qdisc already exists at the given (ifindex, parent), it is replaced in place. Commonly used with classless qdiscs like `netem`, `fq`, and `tbf` where repeated configuration is expected. ### Method `Replace` ### Parameters - `qdisc` (*tc.Object) - The qdisc object to add or replace, containing message and attribute details. ### Request Example ```go // Example usage within a Go program var ecn uint32 = 1 qdisc := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: core.BuildHandle(0x1, 0x0), Parent: tc.HandleRoot, }, Attribute: tc.Attribute{ Kind: "netem", Netem: &tc.Netem{ Qopt: tc.NetemQopt{ Latency: ticks, Limit: 1000, Loss: 42949673, // ~1% (uint32 fraction of 2^32) }, Ecn: &ecn, }, }, } if err := rtnl.Qdisc().Replace(&qdisc); err != nil { // Handle error } ``` ### Response - **Error**: Returns an error if the qdisc cannot be replaced. ``` -------------------------------- ### Manage filter chains Source: https://context7.com/florianl/go-tc/llms.txt Chains allow multiple filter tables to be chained together via a `goto chain` action, enabling modular filter pipelines. They are associated with a specific qdisc/class through `Msg.Ifindex` and `Msg.Parent`. ```APIDOC ## Chain().Add / Chain().Get / Chain().Delete ### Description Manages filter chains, which enable chaining multiple filter tables together using a `goto chain` action for modular filter pipelines. Chains are associated with a specific qdisc or class. ### Method - Add: POST - Get: GET - Delete: DELETE ### Endpoint `/chain` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - `chain` (*tc.Object) - Required - The tc.Object representing the chain to add, get, or delete. - `Msg` (tc.Msg) - Message fields for the chain. - `Family` (uint8) - The address family (e.g., unix.AF_UNSPEC). - `Ifindex` (uint32) - The interface index. - `Parent` (tc.Handle) - The parent handle (e.g., tc.HandleIngress + 1). - `Attribute` (tc.Attribute) - Attribute fields for the chain. - `Chain` (*uint32) - Pointer to the chain number. ### Request Example ```go // Add chain chain := tc.Object{ Msg: tc.Msg{ Ifindex: uint32(devID.Index), Parent: tc.HandleIngress + 1, }, Attribute: tc.Attribute{ Chain: &chainNum, }, } rtnl.Chain().Add(&chain) // Get chains chains, err := rtnl.Chain().Get(&tc.Msg{ /* ... */ }) // Delete chain rtnl.Chain().Delete(&chain) ``` ### Response #### Success Response (200) - `chains` ([]*tc.Object) - A slice of tc.Object representing the retrieved chains. #### Response Example ```json [ { "Msg": { "Family": 0, "Ifindex": 1, "Parent": 16384 }, "Attribute": { "Chain": 10 } } ] ``` ``` -------------------------------- ### Modify qdisc in-place using (*Tc).Qdisc().Change Source: https://context7.com/florianl/go-tc/llms.txt Updates parameters of an existing qdisc without creating or destroying it. Uses RTM_NEWQDISC without the Create or Replace flags, so the kernel rejects the call if the qdisc does not already exist. Ensure the qdisc is already present before calling this method. ```go package main import ( "fmt" "net" "os" "github.com/florianl/go-tc" "github.com/florianl/go-tc/core" "golang.org/x/sys/unix" ) func main() { devID, _ := net.InterfaceByName("eth0") rtnl, _ := tc.Open(&tc.Config{}) defer rtnl.Close() // Adjust TBF rate in-place: tc qdisc change dev eth0 root tbf rate 64kbit burst 10240 limit 10240 linklayerEthernet := uint8(1) burst := uint32(0x280000) qdisc := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(devID.Index), Handle: core.BuildHandle(tc.HandleRoot, 0x0), Parent: tc.HandleRoot, }, Attribute: tc.Attribute{ Kind: "tbf", Tbf: &tc.Tbf{ Parms: &tc.TbfQopt{ Mtu: 1514, Limit: 0x2800, Rate: tc.RateSpec{ Rate: 0x2000, // 8192 bytes/s ~= 64kbit/s Linklayer: linklayerEthernet, CellLog: 0x3, }, }, Burst: &burst, }, }, } if err := rtnl.Qdisc().Change(&qdisc); err != nil { fmt.Fprintf(os.Stderr, "qdisc change: %v\n", err) os.Exit(1) } fmt.Println("tbf rate updated in-place") } ``` -------------------------------- ### List filters on a qdisc/class Source: https://context7.com/florianl/go-tc/llms.txt Retrieves all filters attached to a specific qdisc/class on an interface. The Msg parameter must set Ifindex and Parent to identify the attachment point. Info may be left zero to retrieve all priorities. ```APIDOC ## (*Tc).Filter().Get — List filters on a qdisc/class ### Description Retrieves all filters attached to a specific qdisc/class on an interface. The `Msg` parameter must set `Ifindex` and `Parent` to identify the attachment point. `Info` may be left zero to retrieve all priorities. ### Method `Get` ### Parameters - `msg` (*tc.Msg) - The message specifying the interface and attachment point. ```