class_linux.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package netlink
  2. import (
  3. "errors"
  4. "syscall"
  5. "github.com/vishvananda/netlink/nl"
  6. )
  7. // ClassDel will delete a class from the system.
  8. // Equivalent to: `tc class del $class`
  9. func ClassDel(class Class) error {
  10. return classModify(syscall.RTM_DELTCLASS, 0, class)
  11. }
  12. // ClassChange will change a class in place
  13. // Equivalent to: `tc class change $class`
  14. // The parent and handle MUST NOT be changed.
  15. func ClassChange(class Class) error {
  16. return classModify(syscall.RTM_NEWTCLASS, 0, class)
  17. }
  18. // ClassReplace will replace a class to the system.
  19. // quivalent to: `tc class replace $class`
  20. // The handle MAY be changed.
  21. // If a class already exist with this parent/handle pair, the class is changed.
  22. // If a class does not already exist with this parent/handle, a new class is created.
  23. func ClassReplace(class Class) error {
  24. return classModify(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE, class)
  25. }
  26. // ClassAdd will add a class to the system.
  27. // Equivalent to: `tc class add $class`
  28. func ClassAdd(class Class) error {
  29. return classModify(
  30. syscall.RTM_NEWTCLASS,
  31. syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
  32. class,
  33. )
  34. }
  35. func classModify(cmd, flags int, class Class) error {
  36. req := nl.NewNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
  37. base := class.Attrs()
  38. msg := &nl.TcMsg{
  39. Family: nl.FAMILY_ALL,
  40. Ifindex: int32(base.LinkIndex),
  41. Handle: base.Handle,
  42. Parent: base.Parent,
  43. }
  44. req.AddData(msg)
  45. if cmd != syscall.RTM_DELTCLASS {
  46. if err := classPayload(req, class); err != nil {
  47. return err
  48. }
  49. }
  50. _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
  51. return err
  52. }
  53. func classPayload(req *nl.NetlinkRequest, class Class) error {
  54. req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
  55. options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
  56. if htb, ok := class.(*HtbClass); ok {
  57. opt := nl.TcHtbCopt{}
  58. opt.Buffer = htb.Buffer
  59. opt.Cbuffer = htb.Cbuffer
  60. opt.Quantum = htb.Quantum
  61. opt.Level = htb.Level
  62. opt.Prio = htb.Prio
  63. // TODO: Handle Debug properly. For now default to 0
  64. /* Calculate {R,C}Tab and set Rate and Ceil */
  65. cell_log := -1
  66. ccell_log := -1
  67. linklayer := nl.LINKLAYER_ETHERNET
  68. mtu := 1600
  69. var rtab [256]uint32
  70. var ctab [256]uint32
  71. tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)}
  72. if CalcRtable(&tcrate, rtab, cell_log, uint32(mtu), linklayer) < 0 {
  73. return errors.New("HTB: failed to calculate rate table.")
  74. }
  75. opt.Rate = tcrate
  76. tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)}
  77. if CalcRtable(&tcceil, ctab, ccell_log, uint32(mtu), linklayer) < 0 {
  78. return errors.New("HTB: failed to calculate ceil rate table.")
  79. }
  80. opt.Ceil = tcceil
  81. nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize())
  82. nl.NewRtAttrChild(options, nl.TCA_HTB_RTAB, SerializeRtab(rtab))
  83. nl.NewRtAttrChild(options, nl.TCA_HTB_CTAB, SerializeRtab(ctab))
  84. }
  85. req.AddData(options)
  86. return nil
  87. }
  88. // ClassList gets a list of classes in the system.
  89. // Equivalent to: `tc class show`.
  90. // Generally returns nothing if link and parent are not specified.
  91. func ClassList(link Link, parent uint32) ([]Class, error) {
  92. req := nl.NewNetlinkRequest(syscall.RTM_GETTCLASS, syscall.NLM_F_DUMP)
  93. msg := &nl.TcMsg{
  94. Family: nl.FAMILY_ALL,
  95. Parent: parent,
  96. }
  97. if link != nil {
  98. base := link.Attrs()
  99. ensureIndex(base)
  100. msg.Ifindex = int32(base.Index)
  101. }
  102. req.AddData(msg)
  103. msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWTCLASS)
  104. if err != nil {
  105. return nil, err
  106. }
  107. var res []Class
  108. for _, m := range msgs {
  109. msg := nl.DeserializeTcMsg(m)
  110. attrs, err := nl.ParseRouteAttr(m[msg.Len():])
  111. if err != nil {
  112. return nil, err
  113. }
  114. base := ClassAttrs{
  115. LinkIndex: int(msg.Ifindex),
  116. Handle: msg.Handle,
  117. Parent: msg.Parent,
  118. }
  119. var class Class
  120. classType := ""
  121. for _, attr := range attrs {
  122. switch attr.Attr.Type {
  123. case nl.TCA_KIND:
  124. classType = string(attr.Value[:len(attr.Value)-1])
  125. switch classType {
  126. case "htb":
  127. class = &HtbClass{}
  128. default:
  129. class = &GenericClass{ClassType: classType}
  130. }
  131. case nl.TCA_OPTIONS:
  132. switch classType {
  133. case "htb":
  134. data, err := nl.ParseRouteAttr(attr.Value)
  135. if err != nil {
  136. return nil, err
  137. }
  138. _, err = parseHtbClassData(class, data)
  139. if err != nil {
  140. return nil, err
  141. }
  142. }
  143. }
  144. }
  145. *class.Attrs() = base
  146. res = append(res, class)
  147. }
  148. return res, nil
  149. }
  150. func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
  151. htb := class.(*HtbClass)
  152. detailed := false
  153. for _, datum := range data {
  154. switch datum.Attr.Type {
  155. case nl.TCA_HTB_PARMS:
  156. opt := nl.DeserializeTcHtbCopt(datum.Value)
  157. htb.Rate = uint64(opt.Rate.Rate)
  158. htb.Ceil = uint64(opt.Ceil.Rate)
  159. htb.Buffer = opt.Buffer
  160. htb.Cbuffer = opt.Cbuffer
  161. htb.Quantum = opt.Quantum
  162. htb.Level = opt.Level
  163. htb.Prio = opt.Prio
  164. }
  165. }
  166. return detailed, nil
  167. }