Created
July 6, 2024 13:14
-
-
Save florianl/f8917929ed9ebe7fa819663a853742be to your computer and use it in GitHub Desktop.
HTB qdisc with two classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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