// 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) { req := nl.NewNetlinkRequest(syscall.RTM_GETTCLASS, syscall.NLM_F_DUMP) msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Parent: parent, } if link != nil { base := link.Attrs() ensureIndex(base) msg.Ifindex = int32(base.Index) } req.AddData(msg) msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.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 }
// FilterList gets a list of filters in the system. // Equivalent to: `tc filter show`. // Generally retunrs nothing if link and parent are not specified. func FilterList(link Link, parent uint32) ([]Filter, error) { req := nl.NewNetlinkRequest(syscall.RTM_GETTFILTER, syscall.NLM_F_DUMP) msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Parent: parent, } if link != nil { base := link.Attrs() ensureIndex(base) msg.Ifindex = int32(base.Index) } req.AddData(msg) msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWTFILTER) if err != nil { return nil, err } var res []Filter for _, m := range msgs { msg := nl.DeserializeTcMsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } base := FilterAttrs{ LinkIndex: int(msg.Ifindex), Handle: msg.Handle, Parent: msg.Parent, } base.Priority, base.Protocol = MajorMinor(msg.Info) base.Protocol = nl.Swap16(base.Protocol) var filter Filter filterType := "" detailed := false for _, attr := range attrs { switch attr.Attr.Type { case nl.TCA_KIND: filterType = string(attr.Value[:len(attr.Value)-1]) switch filterType { case "u32": filter = &U32{} case "fw": filter = &Fw{} default: filter = &GenericFilter{FilterType: filterType} } case nl.TCA_OPTIONS: switch filterType { case "u32": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } detailed, err = parseU32Data(filter, data) if err != nil { return nil, err } case "fw": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } detailed, err = parseFwData(filter, data) if err != nil { return nil, err } } } } // only return the detailed version of the filter if detailed { *filter.Attrs() = base res = append(res, filter) } } return res, nil }
// QdiscList gets a list of qdiscs in the system. // Equivalent to: `tc qdisc show`. // The list can be filtered by link. func QdiscList(link Link) ([]Qdisc, error) { req := nl.NewNetlinkRequest(syscall.RTM_GETQDISC, syscall.NLM_F_DUMP) index := int32(0) if link != nil { base := link.Attrs() ensureIndex(base) index = int32(base.Index) } msg := &nl.TcMsg{ Family: nl.FAMILY_ALL, Ifindex: index, } req.AddData(msg) msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWQDISC) if err != nil { return nil, err } var res []Qdisc for _, m := range msgs { msg := nl.DeserializeTcMsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } // skip qdiscs from other interfaces if link != nil && msg.Ifindex != index { continue } base := QdiscAttrs{ LinkIndex: int(msg.Ifindex), Handle: msg.Handle, Parent: msg.Parent, Refcnt: msg.Info, } var qdisc Qdisc qdiscType := "" for _, attr := range attrs { switch attr.Attr.Type { case nl.TCA_KIND: qdiscType = string(attr.Value[:len(attr.Value)-1]) switch qdiscType { case "pfifo_fast": qdisc = &PfifoFast{} case "prio": qdisc = &Prio{} case "tbf": qdisc = &Tbf{} case "ingress": qdisc = &Ingress{} case "htb": qdisc = &Htb{} case "netem": qdisc = &Netem{} default: qdisc = &GenericQdisc{QdiscType: qdiscType} } case nl.TCA_OPTIONS: switch qdiscType { case "pfifo_fast": // pfifo returns TcPrioMap directly without wrapping it in rtattr if err := parsePfifoFastData(qdisc, attr.Value); err != nil { return nil, err } case "prio": // prio returns TcPrioMap directly without wrapping it in rtattr if err := parsePrioData(qdisc, attr.Value); err != nil { return nil, err } case "tbf": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } if err := parseTbfData(qdisc, data); err != nil { return nil, err } case "htb": data, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } if err := parseHtbData(qdisc, data); err != nil { return nil, err } case "netem": if err := parseNetemData(qdisc, attr.Value); err != nil { return nil, err } // no options for ingress } } } *qdisc.Attrs() = base res = append(res, qdisc) } return res, nil }