256 lines
6.6 KiB
Go
256 lines
6.6 KiB
Go
|
package netlink
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"syscall"
|
||
|
|
||
|
"github.com/vishvananda/netlink/nl"
|
||
|
"golang.org/x/sys/unix"
|
||
|
)
|
||
|
|
||
|
// NOTE: function is in here because it uses other linux functions
|
||
|
func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
|
||
|
mtu := 1600
|
||
|
rate := cattrs.Rate / 8
|
||
|
ceil := cattrs.Ceil / 8
|
||
|
buffer := cattrs.Buffer
|
||
|
cbuffer := cattrs.Cbuffer
|
||
|
|
||
|
if ceil == 0 {
|
||
|
ceil = rate
|
||
|
}
|
||
|
|
||
|
if buffer == 0 {
|
||
|
buffer = uint32(float64(rate)/Hz() + float64(mtu))
|
||
|
}
|
||
|
buffer = uint32(Xmittime(rate, buffer))
|
||
|
|
||
|
if cbuffer == 0 {
|
||
|
cbuffer = uint32(float64(ceil)/Hz() + float64(mtu))
|
||
|
}
|
||
|
cbuffer = uint32(Xmittime(ceil, cbuffer))
|
||
|
|
||
|
return &HtbClass{
|
||
|
ClassAttrs: attrs,
|
||
|
Rate: rate,
|
||
|
Ceil: ceil,
|
||
|
Buffer: buffer,
|
||
|
Cbuffer: cbuffer,
|
||
|
Quantum: 10,
|
||
|
Level: 0,
|
||
|
Prio: 0,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ClassDel will delete a class from the system.
|
||
|
// Equivalent to: `tc class del $class`
|
||
|
func ClassDel(class Class) error {
|
||
|
return pkgHandle.ClassDel(class)
|
||
|
}
|
||
|
|
||
|
// ClassDel will delete a class from the system.
|
||
|
// Equivalent to: `tc class del $class`
|
||
|
func (h *Handle) ClassDel(class Class) error {
|
||
|
return h.classModify(unix.RTM_DELTCLASS, 0, class)
|
||
|
}
|
||
|
|
||
|
// ClassChange will change a class in place
|
||
|
// Equivalent to: `tc class change $class`
|
||
|
// The parent and handle MUST NOT be changed.
|
||
|
func ClassChange(class Class) error {
|
||
|
return pkgHandle.ClassChange(class)
|
||
|
}
|
||
|
|
||
|
// ClassChange will change a class in place
|
||
|
// Equivalent to: `tc class change $class`
|
||
|
// The parent and handle MUST NOT be changed.
|
||
|
func (h *Handle) ClassChange(class Class) error {
|
||
|
return h.classModify(unix.RTM_NEWTCLASS, 0, class)
|
||
|
}
|
||
|
|
||
|
// ClassReplace will replace a class to the system.
|
||
|
// quivalent to: `tc class replace $class`
|
||
|
// The handle MAY be changed.
|
||
|
// If a class already exist with this parent/handle pair, the class is changed.
|
||
|
// If a class does not already exist with this parent/handle, a new class is created.
|
||
|
func ClassReplace(class Class) error {
|
||
|
return pkgHandle.ClassReplace(class)
|
||
|
}
|
||
|
|
||
|
// ClassReplace will replace a class to the system.
|
||
|
// quivalent to: `tc class replace $class`
|
||
|
// The handle MAY be changed.
|
||
|
// If a class already exist with this parent/handle pair, the class is changed.
|
||
|
// If a class does not already exist with this parent/handle, a new class is created.
|
||
|
func (h *Handle) ClassReplace(class Class) error {
|
||
|
return h.classModify(unix.RTM_NEWTCLASS, unix.NLM_F_CREATE, class)
|
||
|
}
|
||
|
|
||
|
// ClassAdd will add a class to the system.
|
||
|
// Equivalent to: `tc class add $class`
|
||
|
func ClassAdd(class Class) error {
|
||
|
return pkgHandle.ClassAdd(class)
|
||
|
}
|
||
|
|
||
|
// ClassAdd will add a class to the system.
|
||
|
// Equivalent to: `tc class add $class`
|
||
|
func (h *Handle) ClassAdd(class Class) error {
|
||
|
return h.classModify(
|
||
|
unix.RTM_NEWTCLASS,
|
||
|
unix.NLM_F_CREATE|unix.NLM_F_EXCL,
|
||
|
class,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func (h *Handle) classModify(cmd, flags int, class Class) error {
|
||
|
req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
|
||
|
base := class.Attrs()
|
||
|
msg := &nl.TcMsg{
|
||
|
Family: nl.FAMILY_ALL,
|
||
|
Ifindex: int32(base.LinkIndex),
|
||
|
Handle: base.Handle,
|
||
|
Parent: base.Parent,
|
||
|
}
|
||
|
req.AddData(msg)
|
||
|
|
||
|
if cmd != unix.RTM_DELTCLASS {
|
||
|
if err := classPayload(req, class); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func classPayload(req *nl.NetlinkRequest, class Class) error {
|
||
|
req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
|
||
|
|
||
|
options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
|
||
|
if htb, ok := class.(*HtbClass); ok {
|
||
|
opt := nl.TcHtbCopt{}
|
||
|
opt.Buffer = htb.Buffer
|
||
|
opt.Cbuffer = htb.Cbuffer
|
||
|
opt.Quantum = htb.Quantum
|
||
|
opt.Level = htb.Level
|
||
|
opt.Prio = htb.Prio
|
||
|
// TODO: Handle Debug properly. For now default to 0
|
||
|
/* Calculate {R,C}Tab and set Rate and Ceil */
|
||
|
cellLog := -1
|
||
|
ccellLog := -1
|
||
|
linklayer := nl.LINKLAYER_ETHERNET
|
||
|
mtu := 1600
|
||
|
var rtab [256]uint32
|
||
|
var ctab [256]uint32
|
||
|
tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)}
|
||
|
if CalcRtable(&tcrate, rtab[:], cellLog, uint32(mtu), linklayer) < 0 {
|
||
|
return errors.New("HTB: failed to calculate rate table")
|
||
|
}
|
||
|
opt.Rate = tcrate
|
||
|
tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)}
|
||
|
if CalcRtable(&tcceil, ctab[:], ccellLog, uint32(mtu), linklayer) < 0 {
|
||
|
return errors.New("HTB: failed to calculate ceil rate table")
|
||
|
}
|
||
|
opt.Ceil = tcceil
|
||
|
nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize())
|
||
|
nl.NewRtAttrChild(options, nl.TCA_HTB_RTAB, SerializeRtab(rtab))
|
||
|
nl.NewRtAttrChild(options, nl.TCA_HTB_CTAB, SerializeRtab(ctab))
|
||
|
}
|
||
|
req.AddData(options)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ClassList gets a list of classes in the system.
|
||
|
// Equivalent to: `tc class show`.
|
||
|
// Generally returns nothing if link and parent are not specified.
|
||
|
func ClassList(link Link, parent uint32) ([]Class, error) {
|
||
|
return pkgHandle.ClassList(link, parent)
|
||
|
}
|
||
|
|
||
|
// ClassList gets a list of classes in the system.
|
||
|
// Equivalent to: `tc class show`.
|
||
|
// Generally returns nothing if link and parent are not specified.
|
||
|
func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
|
||
|
req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP)
|
||
|
msg := &nl.TcMsg{
|
||
|
Family: nl.FAMILY_ALL,
|
||
|
Parent: parent,
|
||
|
}
|
||
|
if link != nil {
|
||
|
base := link.Attrs()
|
||
|
h.ensureIndex(base)
|
||
|
msg.Ifindex = int32(base.Index)
|
||
|
}
|
||
|
req.AddData(msg)
|
||
|
|
||
|
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var res []Class
|
||
|
for _, m := range msgs {
|
||
|
msg := nl.DeserializeTcMsg(m)
|
||
|
|
||
|
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
base := ClassAttrs{
|
||
|
LinkIndex: int(msg.Ifindex),
|
||
|
Handle: msg.Handle,
|
||
|
Parent: msg.Parent,
|
||
|
}
|
||
|
|
||
|
var class Class
|
||
|
classType := ""
|
||
|
for _, attr := range attrs {
|
||
|
switch attr.Attr.Type {
|
||
|
case nl.TCA_KIND:
|
||
|
classType = string(attr.Value[:len(attr.Value)-1])
|
||
|
switch classType {
|
||
|
case "htb":
|
||
|
class = &HtbClass{}
|
||
|
default:
|
||
|
class = &GenericClass{ClassType: classType}
|
||
|
}
|
||
|
case nl.TCA_OPTIONS:
|
||
|
switch classType {
|
||
|
case "htb":
|
||
|
data, err := nl.ParseRouteAttr(attr.Value)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
_, err = parseHtbClassData(class, data)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*class.Attrs() = base
|
||
|
res = append(res, class)
|
||
|
}
|
||
|
|
||
|
return res, nil
|
||
|
}
|
||
|
|
||
|
func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
|
||
|
htb := class.(*HtbClass)
|
||
|
detailed := false
|
||
|
for _, datum := range data {
|
||
|
switch datum.Attr.Type {
|
||
|
case nl.TCA_HTB_PARMS:
|
||
|
opt := nl.DeserializeTcHtbCopt(datum.Value)
|
||
|
htb.Rate = uint64(opt.Rate.Rate)
|
||
|
htb.Ceil = uint64(opt.Ceil.Rate)
|
||
|
htb.Buffer = opt.Buffer
|
||
|
htb.Cbuffer = opt.Cbuffer
|
||
|
htb.Quantum = opt.Quantum
|
||
|
htb.Level = opt.Level
|
||
|
htb.Prio = opt.Prio
|
||
|
}
|
||
|
}
|
||
|
return detailed, nil
|
||
|
}
|