class_linux.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. package netlink
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "encoding/hex"
  6. "errors"
  7. "fmt"
  8. "syscall"
  9. "github.com/vishvananda/netlink/nl"
  10. "golang.org/x/sys/unix"
  11. )
  12. // Internal tc_stats representation in Go struct.
  13. // This is for internal uses only to deserialize the payload of rtattr.
  14. // After the deserialization, this should be converted into the canonical stats
  15. // struct, ClassStatistics, in case of statistics of a class.
  16. // Ref: struct tc_stats { ... }
  17. type tcStats struct {
  18. Bytes uint64 // Number of enqueued bytes
  19. Packets uint32 // Number of enqueued packets
  20. Drops uint32 // Packets dropped because of lack of resources
  21. Overlimits uint32 // Number of throttle events when this flow goes out of allocated bandwidth
  22. Bps uint32 // Current flow byte rate
  23. Pps uint32 // Current flow packet rate
  24. Qlen uint32
  25. Backlog uint32
  26. }
  27. // NewHtbClass NOTE: function is in here because it uses other linux functions
  28. func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
  29. mtu := 1600
  30. rate := cattrs.Rate / 8
  31. ceil := cattrs.Ceil / 8
  32. buffer := cattrs.Buffer
  33. cbuffer := cattrs.Cbuffer
  34. if ceil == 0 {
  35. ceil = rate
  36. }
  37. if buffer == 0 {
  38. buffer = uint32(float64(rate)/Hz() + float64(mtu))
  39. }
  40. buffer = Xmittime(rate, buffer)
  41. if cbuffer == 0 {
  42. cbuffer = uint32(float64(ceil)/Hz() + float64(mtu))
  43. }
  44. cbuffer = Xmittime(ceil, cbuffer)
  45. return &HtbClass{
  46. ClassAttrs: attrs,
  47. Rate: rate,
  48. Ceil: ceil,
  49. Buffer: buffer,
  50. Cbuffer: cbuffer,
  51. Level: 0,
  52. Prio: cattrs.Prio,
  53. Quantum: cattrs.Quantum,
  54. }
  55. }
  56. // ClassDel will delete a class from the system.
  57. // Equivalent to: `tc class del $class`
  58. func ClassDel(class Class) error {
  59. return pkgHandle.ClassDel(class)
  60. }
  61. // ClassDel will delete a class from the system.
  62. // Equivalent to: `tc class del $class`
  63. func (h *Handle) ClassDel(class Class) error {
  64. return h.classModify(unix.RTM_DELTCLASS, 0, class)
  65. }
  66. // ClassChange will change a class in place
  67. // Equivalent to: `tc class change $class`
  68. // The parent and handle MUST NOT be changed.
  69. func ClassChange(class Class) error {
  70. return pkgHandle.ClassChange(class)
  71. }
  72. // ClassChange will change a class in place
  73. // Equivalent to: `tc class change $class`
  74. // The parent and handle MUST NOT be changed.
  75. func (h *Handle) ClassChange(class Class) error {
  76. return h.classModify(unix.RTM_NEWTCLASS, 0, class)
  77. }
  78. // ClassReplace will replace a class to the system.
  79. // quivalent to: `tc class replace $class`
  80. // The handle MAY be changed.
  81. // If a class already exist with this parent/handle pair, the class is changed.
  82. // If a class does not already exist with this parent/handle, a new class is created.
  83. func ClassReplace(class Class) error {
  84. return pkgHandle.ClassReplace(class)
  85. }
  86. // ClassReplace will replace a class to the system.
  87. // quivalent to: `tc class replace $class`
  88. // The handle MAY be changed.
  89. // If a class already exist with this parent/handle pair, the class is changed.
  90. // If a class does not already exist with this parent/handle, a new class is created.
  91. func (h *Handle) ClassReplace(class Class) error {
  92. return h.classModify(unix.RTM_NEWTCLASS, unix.NLM_F_CREATE, class)
  93. }
  94. // ClassAdd will add a class to the system.
  95. // Equivalent to: `tc class add $class`
  96. func ClassAdd(class Class) error {
  97. return pkgHandle.ClassAdd(class)
  98. }
  99. // ClassAdd will add a class to the system.
  100. // Equivalent to: `tc class add $class`
  101. func (h *Handle) ClassAdd(class Class) error {
  102. return h.classModify(
  103. unix.RTM_NEWTCLASS,
  104. unix.NLM_F_CREATE|unix.NLM_F_EXCL,
  105. class,
  106. )
  107. }
  108. func (h *Handle) classModify(cmd, flags int, class Class) error {
  109. req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
  110. base := class.Attrs()
  111. msg := &nl.TcMsg{
  112. Family: nl.FAMILY_ALL,
  113. Ifindex: int32(base.LinkIndex),
  114. Handle: base.Handle,
  115. Parent: base.Parent,
  116. }
  117. req.AddData(msg)
  118. if cmd != unix.RTM_DELTCLASS {
  119. if err := classPayload(req, class); err != nil {
  120. return err
  121. }
  122. }
  123. _, err := req.Execute(unix.NETLINK_ROUTE, 0)
  124. return err
  125. }
  126. func classPayload(req *nl.NetlinkRequest, class Class) error {
  127. req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
  128. options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
  129. switch class.Type() {
  130. case "htb":
  131. htb := class.(*HtbClass)
  132. opt := nl.TcHtbCopt{}
  133. opt.Buffer = htb.Buffer
  134. opt.Cbuffer = htb.Cbuffer
  135. opt.Quantum = htb.Quantum
  136. opt.Level = htb.Level
  137. opt.Prio = htb.Prio
  138. // TODO: Handle Debug properly. For now default to 0
  139. /* Calculate {R,C}Tab and set Rate and Ceil */
  140. cellLog := -1
  141. ccellLog := -1
  142. linklayer := nl.LINKLAYER_ETHERNET
  143. mtu := 1600
  144. var rtab [256]uint32
  145. var ctab [256]uint32
  146. tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)}
  147. if CalcRtable(&tcrate, rtab[:], cellLog, uint32(mtu), linklayer) < 0 {
  148. return errors.New("HTB: failed to calculate rate table")
  149. }
  150. opt.Rate = tcrate
  151. tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)}
  152. if CalcRtable(&tcceil, ctab[:], ccellLog, uint32(mtu), linklayer) < 0 {
  153. return errors.New("HTB: failed to calculate ceil rate table")
  154. }
  155. opt.Ceil = tcceil
  156. options.AddRtAttr(nl.TCA_HTB_PARMS, opt.Serialize())
  157. options.AddRtAttr(nl.TCA_HTB_RTAB, SerializeRtab(rtab))
  158. options.AddRtAttr(nl.TCA_HTB_CTAB, SerializeRtab(ctab))
  159. if htb.Rate >= uint64(1<<32) {
  160. options.AddRtAttr(nl.TCA_HTB_RATE64, nl.Uint64Attr(htb.Rate))
  161. }
  162. if htb.Ceil >= uint64(1<<32) {
  163. options.AddRtAttr(nl.TCA_HTB_CEIL64, nl.Uint64Attr(htb.Ceil))
  164. }
  165. case "hfsc":
  166. hfsc := class.(*HfscClass)
  167. opt := nl.HfscCopt{}
  168. rm1, rd, rm2 := hfsc.Rsc.Attrs()
  169. opt.Rsc.Set(rm1/8, rd, rm2/8)
  170. fm1, fd, fm2 := hfsc.Fsc.Attrs()
  171. opt.Fsc.Set(fm1/8, fd, fm2/8)
  172. um1, ud, um2 := hfsc.Usc.Attrs()
  173. opt.Usc.Set(um1/8, ud, um2/8)
  174. options.AddRtAttr(nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc))
  175. options.AddRtAttr(nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc))
  176. options.AddRtAttr(nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc))
  177. }
  178. req.AddData(options)
  179. return nil
  180. }
  181. // ClassList gets a list of classes in the system.
  182. // Equivalent to: `tc class show`.
  183. //
  184. // Generally returns nothing if link and parent are not specified.
  185. // If the returned error is [ErrDumpInterrupted], results may be inconsistent
  186. // or incomplete.
  187. func ClassList(link Link, parent uint32) ([]Class, error) {
  188. return pkgHandle.ClassList(link, parent)
  189. }
  190. // ClassList gets a list of classes in the system.
  191. // Equivalent to: `tc class show`.
  192. //
  193. // Generally returns nothing if link and parent are not specified.
  194. // If the returned error is [ErrDumpInterrupted], results may be inconsistent
  195. // or incomplete.
  196. func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
  197. req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP)
  198. msg := &nl.TcMsg{
  199. Family: nl.FAMILY_ALL,
  200. Parent: parent,
  201. }
  202. if link != nil {
  203. base := link.Attrs()
  204. h.ensureIndex(base)
  205. msg.Ifindex = int32(base.Index)
  206. }
  207. req.AddData(msg)
  208. msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
  209. if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
  210. return nil, executeErr
  211. }
  212. var res []Class
  213. for _, m := range msgs {
  214. msg := nl.DeserializeTcMsg(m)
  215. attrs, err := nl.ParseRouteAttr(m[msg.Len():])
  216. if err != nil {
  217. return nil, err
  218. }
  219. base := ClassAttrs{
  220. LinkIndex: int(msg.Ifindex),
  221. Handle: msg.Handle,
  222. Parent: msg.Parent,
  223. Statistics: nil,
  224. }
  225. var class Class
  226. classType := ""
  227. for _, attr := range attrs {
  228. switch attr.Attr.Type {
  229. case nl.TCA_KIND:
  230. classType = string(attr.Value[:len(attr.Value)-1])
  231. switch classType {
  232. case "htb":
  233. class = &HtbClass{}
  234. case "hfsc":
  235. class = &HfscClass{}
  236. default:
  237. class = &GenericClass{ClassType: classType}
  238. }
  239. case nl.TCA_OPTIONS:
  240. switch classType {
  241. case "htb":
  242. data, err := nl.ParseRouteAttr(attr.Value)
  243. if err != nil {
  244. return nil, err
  245. }
  246. _, err = parseHtbClassData(class, data)
  247. if err != nil {
  248. return nil, err
  249. }
  250. case "hfsc":
  251. data, err := nl.ParseRouteAttr(attr.Value)
  252. if err != nil {
  253. return nil, err
  254. }
  255. _, err = parseHfscClassData(class, data)
  256. if err != nil {
  257. return nil, err
  258. }
  259. }
  260. // For backward compatibility.
  261. case nl.TCA_STATS:
  262. base.Statistics, err = parseTcStats(attr.Value)
  263. if err != nil {
  264. return nil, err
  265. }
  266. case nl.TCA_STATS2:
  267. base.Statistics, err = parseTcStats2(attr.Value)
  268. if err != nil {
  269. return nil, err
  270. }
  271. }
  272. }
  273. *class.Attrs() = base
  274. res = append(res, class)
  275. }
  276. return res, executeErr
  277. }
  278. func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
  279. htb := class.(*HtbClass)
  280. detailed := false
  281. for _, datum := range data {
  282. switch datum.Attr.Type {
  283. case nl.TCA_HTB_PARMS:
  284. opt := nl.DeserializeTcHtbCopt(datum.Value)
  285. htb.Rate = uint64(opt.Rate.Rate)
  286. htb.Ceil = uint64(opt.Ceil.Rate)
  287. htb.Buffer = opt.Buffer
  288. htb.Cbuffer = opt.Cbuffer
  289. htb.Quantum = opt.Quantum
  290. htb.Level = opt.Level
  291. htb.Prio = opt.Prio
  292. case nl.TCA_HTB_RATE64:
  293. htb.Rate = native.Uint64(datum.Value[0:8])
  294. case nl.TCA_HTB_CEIL64:
  295. htb.Ceil = native.Uint64(datum.Value[0:8])
  296. }
  297. }
  298. return detailed, nil
  299. }
  300. func parseHfscClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
  301. hfsc := class.(*HfscClass)
  302. detailed := false
  303. for _, datum := range data {
  304. m1, d, m2 := nl.DeserializeHfscCurve(datum.Value).Attrs()
  305. switch datum.Attr.Type {
  306. case nl.TCA_HFSC_RSC:
  307. hfsc.Rsc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8}
  308. case nl.TCA_HFSC_FSC:
  309. hfsc.Fsc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8}
  310. case nl.TCA_HFSC_USC:
  311. hfsc.Usc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8}
  312. }
  313. }
  314. return detailed, nil
  315. }
  316. func parseTcStats(data []byte) (*ClassStatistics, error) {
  317. buf := &bytes.Buffer{}
  318. buf.Write(data)
  319. tcStats := &tcStats{}
  320. if err := binary.Read(buf, native, tcStats); err != nil {
  321. return nil, err
  322. }
  323. stats := NewClassStatistics()
  324. stats.Basic.Bytes = tcStats.Bytes
  325. stats.Basic.Packets = tcStats.Packets
  326. stats.Queue.Qlen = tcStats.Qlen
  327. stats.Queue.Backlog = tcStats.Backlog
  328. stats.Queue.Drops = tcStats.Drops
  329. stats.Queue.Overlimits = tcStats.Overlimits
  330. stats.RateEst.Bps = tcStats.Bps
  331. stats.RateEst.Pps = tcStats.Pps
  332. return stats, nil
  333. }
  334. func parseGnetStats(data []byte, gnetStats interface{}) error {
  335. buf := &bytes.Buffer{}
  336. buf.Write(data)
  337. return binary.Read(buf, native, gnetStats)
  338. }
  339. func parseTcStats2(data []byte) (*ClassStatistics, error) {
  340. rtAttrs, err := nl.ParseRouteAttr(data)
  341. if err != nil {
  342. return nil, err
  343. }
  344. stats := NewClassStatistics()
  345. for _, datum := range rtAttrs {
  346. switch datum.Attr.Type {
  347. case nl.TCA_STATS_BASIC:
  348. if err := parseGnetStats(datum.Value, stats.Basic); err != nil {
  349. return nil, fmt.Errorf("Failed to parse ClassStatistics.Basic with: %v\n%s",
  350. err, hex.Dump(datum.Value))
  351. }
  352. case nl.TCA_STATS_QUEUE:
  353. if err := parseGnetStats(datum.Value, stats.Queue); err != nil {
  354. return nil, fmt.Errorf("Failed to parse ClassStatistics.Queue with: %v\n%s",
  355. err, hex.Dump(datum.Value))
  356. }
  357. case nl.TCA_STATS_RATE_EST:
  358. if err := parseGnetStats(datum.Value, stats.RateEst); err != nil {
  359. return nil, fmt.Errorf("Failed to parse ClassStatistics.RateEst with: %v\n%s",
  360. err, hex.Dump(datum.Value))
  361. }
  362. case nl.TCA_STATS_BASIC_HW:
  363. if err := parseGnetStats(datum.Value, stats.BasicHw); err != nil {
  364. return nil, fmt.Errorf("Failed to parse ClassStatistics.BasicHw with: %v\n%s",
  365. err, hex.Dump(datum.Value))
  366. }
  367. }
  368. }
  369. return stats, nil
  370. }