Skip to content

Instantly share code, notes, and snippets.

@florianl
Created July 6, 2024 13:14
Show Gist options
  • Save florianl/f8917929ed9ebe7fa819663a853742be to your computer and use it in GitHub Desktop.
Save florianl/f8917929ed9ebe7fa819663a853742be to your computer and use it in GitHub Desktop.
HTB qdisc with two classes
package main
import (
"fmt"
"net"
"os"
"github.com/florianl/go-tc"
"github.com/florianl/go-tc/core"
"github.com/jsimonetti/rtnetlink"
"golang.org/x/sys/unix"
)
// setupDummyInterface installs a temporary dummy interface
func setupDummyInterface(iface string) (*rtnetlink.Conn, error) {
con, err := rtnetlink.Dial(nil)
if err != nil {
return &rtnetlink.Conn{}, err
}
if err := con.Link.New(&rtnetlink.LinkMessage{
Family: unix.AF_UNSPEC,
Type: unix.ARPHRD_NETROM,
Index: 0,
Flags: unix.IFF_UP,
Change: unix.IFF_UP,
Attributes: &rtnetlink.LinkAttributes{
Name: iface,
Info: &rtnetlink.LinkInfo{Kind: "dummy"},
},
}); err != nil {
return con, err
}
return con, err
}
func main() {
tcIface := "tcDummyIface"
rtnl, err := setupDummyInterface(tcIface)
if err != nil {
fmt.Fprintf(os.Stderr, "could not setup dummy interface: %v\n", err)
return
}
defer rtnl.Close()
devID, err := net.InterfaceByName(tcIface)
if err != nil {
fmt.Fprintf(os.Stderr, "could not get interface ID: %v\n", err)
return
}
defer func(devID uint32, rtnl *rtnetlink.Conn) {
if err := rtnl.Link.Delete(devID); err != nil {
fmt.Fprintf(os.Stderr, "could not delete interface: %v\n", err)
}
}(uint32(devID.Index), rtnl)
tcnl, 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 := tcnl.Close(); err != nil {
fmt.Fprintf(os.Stderr, "could not close rtnetlink socket: %v\n", err)
}
}()
qdisc := tc.Object{
Msg: tc.Msg{
Family: unix.AF_UNSPEC,
Ifindex: uint32(devID.Index),
Handle: core.BuildHandle(0x1, 0x0),
Parent: tc.HandleRoot,
Info: 0,
},
// configure a very basic hierarchy token bucket (htb) qdisc
Attribute: tc.Attribute{
Kind: "htb",
Htb: &tc.Htb{
Init: &tc.HtbGlob{
Version: 0x3,
Rate2Quantum: 0xa,
},
},
},
}
// tc qdisc add dev <iface> root handle 1: htb
if err := tcnl.Qdisc().Add(&qdisc); err != nil {
fmt.Fprintf(os.Stderr, "could not assign htb to lo: %v\n", err)
return
}
// delete the qdisc, if this program terminates
defer func() {
if err := tcnl.Qdisc().Delete(&qdisc); err != nil {
fmt.Fprintf(os.Stderr, "could not delete htb qdisc of lo: %v\n", err)
return
}
}()
qdiscs, err := tcnl.Qdisc().Get()
if err != nil {
fmt.Fprintf(os.Stderr, "could not get all qdiscs: %v\n", err)
}
// Parent Qdisc for HTB class
parentQdisc := uint32(0)
for _, qdisc := range qdiscs {
if qdisc.Ifindex != uint32(devID.Index) {
continue
}
parentQdisc = qdisc.Msg.Handle
}
if parentQdisc == 0 {
fmt.Fprintf(os.Stderr, "failed to get parent qdisc for class")
return
}
// tc class add dev <iface> parent 1: classid 10 htb rate 5mbit ceil 10mbit
if err := tcnl.Class().Add(&tc.Object{
Msg: tc.Msg{
Family: unix.AF_UNSPEC,
Ifindex: uint32(devID.Index),
Handle: core.BuildHandle(0, 0x10),
Parent: parentQdisc,
},
Attribute: tc.Attribute{
Kind: "htb",
Htb: &tc.Htb{
Parms: &tc.HtbOpt{
Rate: tc.RateSpec{CellLog: 0x3, Linklayer: 0x1, Overhead: 0x0, CellAlign: 0xffff, Mpu: 0x0, Rate: 0x98968},
Ceil: tc.RateSpec{CellLog: 0x3, Linklayer: 0x1, Overhead: 0x0, CellAlign: 0xffff, Mpu: 0x0, Rate: 0x1312d0},
},
},
},
}); err != nil {
fmt.Fprintf(os.Stderr, "failed to create htb class 0x10: %v\n", err)
return
}
// tc class add dev <iface> parent 1: classid 20 htb rate 2mbit ceil 3mbit
if err := tcnl.Class().Add(&tc.Object{
Msg: tc.Msg{
Family: unix.AF_UNSPEC,
Ifindex: uint32(devID.Index),
Handle: core.BuildHandle(0, 0x20),
Parent: parentQdisc,
},
Attribute: tc.Attribute{
Kind: "htb",
Htb: &tc.Htb{
Parms: &tc.HtbOpt{
Rate: tc.RateSpec{CellLog: 0x3, Linklayer: 0x1, Overhead: 0x0, CellAlign: 0xffff, Mpu: 0x0, Rate: 0x3d090},
Ceil: tc.RateSpec{CellLog: 0x3, Linklayer: 0x1, Overhead: 0x0, CellAlign: 0xffff, Mpu: 0x0, Rate: 0x5b8d8},
},
},
},
}); err != nil {
fmt.Fprintf(os.Stderr, "failed to create htb class 0x20: %v\n", err)
return
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment