diff --git a/qdisc.go b/qdisc.go index 067743d3..76100d63 100644 --- a/qdisc.go +++ b/qdisc.go @@ -208,12 +208,16 @@ func (qdisc *Netem) Type() string { // Tbf is a classless qdisc that rate limits based on tokens type Tbf struct { QdiscAttrs - Rate uint64 - Limit uint32 - Buffer uint32 - Peakrate uint64 - Minburst uint32 - // TODO: handle other settings + Limit uint32 /* byte */ + Rate uint64 /* bytes per second */ + Burst uint32 /* byte */ + BurstCell uint32 /* byte */ + Peakrate uint64 /* bytes per second */ + Minburst uint32 /* byte */ + MinburstCell uint32 /* byte */ + Mpu uint16 /* byte */ + Overhead uint16 /* byte */ + Linklayer int /* nl.LINKLAYER_UNSPEC / nl.LINKLAYER_ETHERNET / nl.LINKLAYER_ATM */ } func (qdisc *Tbf) Attrs() *QdiscAttrs { diff --git a/qdisc_linux.go b/qdisc_linux.go index e732ae3b..20cbb9f2 100644 --- a/qdisc_linux.go +++ b/qdisc_linux.go @@ -177,21 +177,47 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error { } options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize()) case *Tbf: + if qdisc.Linklayer == nl.LINKLAYER_UNSPEC { + qdisc.Linklayer = nl.LINKLAYER_ETHERNET + } opt := nl.TcTbfQopt{} - opt.Rate.Rate = uint32(qdisc.Rate) - opt.Peakrate.Rate = uint32(qdisc.Peakrate) opt.Limit = qdisc.Limit - opt.Buffer = qdisc.Buffer - options.AddRtAttr(nl.TCA_TBF_PARMS, opt.Serialize()) + if opt.Limit == 0 { + return fmt.Errorf("tbf: Limit is required") + } if qdisc.Rate >= uint64(1<<32) { + opt.Rate.Rate = ^uint32(0) options.AddRtAttr(nl.TCA_TBF_RATE64, nl.Uint64Attr(qdisc.Rate)) + } else { + opt.Rate.Rate = uint32(qdisc.Rate) } if qdisc.Peakrate >= uint64(1<<32) { + opt.Peakrate.Rate = ^uint32(0) options.AddRtAttr(nl.TCA_TBF_PRATE64, nl.Uint64Attr(qdisc.Peakrate)) + } else { + opt.Peakrate.Rate = uint32(qdisc.Peakrate) + } + options.AddRtAttr(nl.TCA_TBF_BURST, nl.Uint32Attr(qdisc.Burst)) + opt.Buffer = Xmittime(uint64(opt.Rate.Rate), qdisc.Burst) + opt.Rate.Mpu = qdisc.Mpu + opt.Rate.Overhead = qdisc.Overhead + rtab, err := calcTbfRtable(&opt.Rate, qdisc.Minburst, qdisc.BurstCell, qdisc.Linklayer) + if err != nil { + return err } - if qdisc.Peakrate > 0 { + options.AddRtAttr(nl.TCA_TBF_RTAB, rtab[0:]) + if opt.Peakrate.Rate > 0 { options.AddRtAttr(nl.TCA_TBF_PBURST, nl.Uint32Attr(qdisc.Minburst)) + opt.Mtu = Xmittime(uint64(opt.Peakrate.Rate), qdisc.Minburst) + opt.Peakrate.Mpu = qdisc.Mpu + opt.Peakrate.Overhead = qdisc.Overhead + ptab, err := calcTbfRtable(&opt.Peakrate, qdisc.Minburst, qdisc.MinburstCell, qdisc.Linklayer) + if err != nil { + return err + } + options.AddRtAttr(nl.TCA_TBF_PTAB, ptab[0:]) } + options.AddRtAttr(nl.TCA_TBF_PARMS, opt.Serialize()) case *Htb: opt := nl.TcHtbGlob{} opt.Version = qdisc.Version @@ -651,24 +677,105 @@ func parseNetemData(qdisc Qdisc, value []byte) error { return nil } +func calcTbfRtable(rate *nl.TcRateSpec, mtu uint32, cell uint32, linklayer int) ([1024]byte, error) { + bps := rate.Rate + mpu := rate.Mpu + var sz uint + var i uint32 + var token uint32 + var cellLog uint32 + var rtab [256]uint32 + var byteTab [1024]byte + var mtuToken uint32 + + if mtu == 0 { + mtu = 2047 + } + mtuToken = Xmittime(uint64(bps), mtu) + if cell > 0 { + _cellLog := -1 + for i = 0; i < 32; i++ { + if (1 << i) == cell { + _cellLog = int(i) + break + } + } + if _cellLog == -1 { + return byteTab, fmt.Errorf("invalid cell value %d", cell) + } + cellLog = uint32(_cellLog) + } else { + cellLog = 0 + for mtu>>cellLog > 255 { + cellLog++ + } + } + for { + for i = 0; i < 256; i++ { + sz = AdjustSize(uint((i+1)< 0 { + for i = 0; i < 256; i++ { + if rtab[i] > mtuToken { + break + } + } + maxSize := i<= mtuToken { + cellLog-- + } else { + break + } + } + } + rate.CellAlign = -1 + rate.CellLog = uint8(cellLog) + rate.Linklayer = uint8(linklayer & nl.TC_LINKLAYER_MASK) + return byteTab, nil +} + func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error { tbf := qdisc.(*Tbf) + rate64 := uint64(0) + prate64 := uint64(0) + var opt *nl.TcTbfQopt for _, datum := range data { switch datum.Attr.Type { case nl.TCA_TBF_PARMS: - opt := nl.DeserializeTcTbfQopt(datum.Value) - tbf.Rate = uint64(opt.Rate.Rate) - tbf.Peakrate = uint64(opt.Peakrate.Rate) - tbf.Limit = opt.Limit - tbf.Buffer = opt.Buffer + opt = nl.DeserializeTcTbfQopt(datum.Value) case nl.TCA_TBF_RATE64: - tbf.Rate = native.Uint64(datum.Value[0:8]) + rate64 = native.Uint64(datum.Value[0:8]) case nl.TCA_TBF_PRATE64: - tbf.Peakrate = native.Uint64(datum.Value[0:8]) - case nl.TCA_TBF_PBURST: - tbf.Minburst = native.Uint32(datum.Value[0:4]) + prate64 = native.Uint64(datum.Value[0:8]) } } + tbf.Limit = opt.Limit + if rate64 > 0 { + tbf.Rate = rate64 + } else { + tbf.Rate = uint64(opt.Rate.Rate) + } + if prate64 > 0 { + tbf.Peakrate = prate64 + } else { + tbf.Peakrate = uint64(opt.Peakrate.Rate) + } + tbf.Burst = Xmitsize(tbf.Rate, opt.Buffer) + tbf.BurstCell = 1 << opt.Rate.CellLog + if tbf.Peakrate > 0 { + tbf.Minburst = Xmitsize(tbf.Peakrate, opt.Mtu) + tbf.MinburstCell = 1 << opt.Peakrate.CellLog + } + tbf.Mpu = opt.Rate.Mpu + tbf.Overhead = opt.Rate.Overhead + tbf.Linklayer = int(opt.Rate.Linklayer & nl.TC_LINKLAYER_MASK) return nil } diff --git a/qdisc_test.go b/qdisc_test.go index 21c77862..d87b679d 100644 --- a/qdisc_test.go +++ b/qdisc_test.go @@ -26,9 +26,9 @@ func TestTbfAddDel(t *testing.T) { Handle: MakeHandle(1, 0), Parent: HANDLE_ROOT, }, - Rate: 131072, - Limit: 1220703, - Buffer: 16793, + Rate: 131072, + Limit: 1220703, + Burst: 4096, } if err := QdiscAdd(qdisc); err != nil { t.Fatal(err) @@ -50,7 +50,7 @@ func TestTbfAddDel(t *testing.T) { if tbf.Limit != qdisc.Limit { t.Fatal("Limit doesn't match") } - if tbf.Buffer != qdisc.Buffer { + if tbf.Burst != Xmitsize(qdisc.Rate, Xmittime(qdisc.Rate, qdisc.Burst)) { t.Fatal("Buffer doesn't match") } if err := QdiscDel(qdisc); err != nil { @@ -262,7 +262,7 @@ func TestTbfAddHtbReplaceDel(t *testing.T) { QdiscAttrs: attrs, Rate: 131072, Limit: 1220703, - Buffer: 16793, + Burst: 4096, } if err := QdiscAdd(qdisc); err != nil { t.Fatal(err) @@ -284,7 +284,7 @@ func TestTbfAddHtbReplaceDel(t *testing.T) { if tbf.Limit != qdisc.Limit { t.Fatal("Limit doesn't match") } - if tbf.Buffer != qdisc.Buffer { + if tbf.Burst != Xmitsize(qdisc.Rate, Xmittime(qdisc.Rate, qdisc.Burst)) { t.Fatal("Buffer doesn't match") } // Replace @@ -353,7 +353,7 @@ func TestTbfAddTbfChangeDel(t *testing.T) { QdiscAttrs: attrs, Rate: 131072, Limit: 1220703, - Buffer: 16793, + Burst: 4096, } if err := QdiscAdd(qdisc); err != nil { t.Fatal(err) @@ -375,7 +375,7 @@ func TestTbfAddTbfChangeDel(t *testing.T) { if tbf.Limit != qdisc.Limit { t.Fatal("Limit doesn't match") } - if tbf.Buffer != qdisc.Buffer { + if tbf.Burst != Xmitsize(qdisc.Rate, Xmittime(qdisc.Rate, qdisc.Burst)) { t.Fatal("Buffer doesn't match") } // Change @@ -402,7 +402,7 @@ func TestTbfAddTbfChangeDel(t *testing.T) { if tbf.Limit != qdisc.Limit { t.Fatal("Limit doesn't match") } - if tbf.Buffer != qdisc.Buffer { + if tbf.Burst != Xmitsize(qdisc.Rate, Xmittime(qdisc.Rate, qdisc.Burst)) { t.Fatal("Buffer doesn't match") }