go-tc

repository·main·Indexed 19 days ago

https://github.com/florianl/go-tc

A Go interface for interacting with Linux Traffic Control (TC) that maps kernel-level C structures to Go types. It provides programmatic management of network queuing disciplines, rate limiting, and traffic shaping via netlink, including APIs for managing TC actions, chains, and classes (supporting hfsc, qfq, htb, dsmark, and drr), as well as various match disciplines such as u32, ematch, and ipset.

Tokens
35.7K
Snippets
140
Records
178
Agent score
68%

What's inside go-tc

  1. Understand the Ematch data model and logical composition

    main

    The ematch discipline uses an array of interconnected match sequences to implement complex logical expressions (AND, OR, etc.).

    Logical Composition:

    • Precedence: Implemented via a special EmatchContainer kind. A container references a sequence beyond the current marker, causing the current position to be pushed onto a stack. Matching continues in the new sequence until a marker is reached, at which point the position is restored from the stack.
    • Flags: Logical combinations are managed using flags like EmatchRelAnd and EmatchRelOr.
    • Order of Operations: In userspace, the array is encapsulated such that logical combinations update flags and use containers. For example, an expression like A AND (B1 OR B2) AND C AND D is transformed into a flat array where the container handles the jump to the B1 OR B2 sequence and returns to the main sequence after completion.

    Mental Model: Think of an Ematch as a tree of matches flattened into a linear array where EmatchContainer acts as a jump/subroutine mechanism to handle nested logic.

  2. Understand the GenStats structure for TC statistics

    main

    The GenStats type is used to represent Traffic Control (TC) statistics retrieved from the Linux kernel. It is a container for several specialized statistic sub-structures. Because these statistics are often optional or provided by different hardware/drivers, the fields in GenStats are pointers; a nil field indicates that specific statistic type was not provided.

    Available sub-structures:

    • Basic (*GenBasic): Standard byte and packet counts.
    • RateEst (*GenRateEst): Estimated rates (32-bit).
    • Queue (*GenQueue): Queue-specific metrics like length, backlog, drops, and requeues.
    • RateEst64 (*GenRateEst64): Estimated rates (64-bit).
    • BasicHw (*GenBasic): Hardware-specific basic statistics.
    type GenStats struct {
    	Basic     *GenBasic
    	RateEst   *GenRateEst
    	Queue     *GenQueue
    	RateEst64 *GenRateEst64
    	BasicHw   *GenBasic
    }
  3. Supported Action Kinds

    main

    The Action struct supports various specialized configurations depending on the Kind string provided. When setting the Kind, you must also provide the corresponding configuration struct in the Action object. Supported kinds include:

    • bpf: BPF action
    • connmark: Connection marking
    • csum: Checksum modification
    • ct: Connection tracking
    • ctinfo: Connection tracking info
    • defact: Default action
    • gact: Generic action
    • gate: Gate action
    • ife: Interface action
    • ipt: IP tables action
    • mirred: Mirror/Redirect action
    • nat: Network Address Translation
    • sample: Sampling action
    • vlan: VLAN action
    • police: Policing action
    • tunnel_key: Tunnel key action
    • mpls: MPLS action
    • skbedit: Skb edit action
    • skbmod: Skb modification action
  4. Configure Checksum (csum) action attributes

    main

    The Csum struct is used to define the configuration attributes for a checksum discipline in the Traffic Control (tc) subsystem. It allows specifying parameters for checksum calculation and timing-related settings.

    Attributes include:

    • Parms: A pointer to a CsumParms struct containing capability and action details.
    • Tm: A pointer to a Tcft struct (timing configuration).

    Note: The current implementation of marshalCsum does not support providing both Tm and Parms simultaneously; providing both will result in an ErrNoArgAlter error.

    import "github.com/florianl/florianl/go-tc/tc"
    
    // Example configuration
    config := &tc.Csum{
        Parms: &tc.CsumParms{
            Index: 1,
            // ... other fields
        },
    }
  5. How to use synchronous calls and monitoring together

    main

    A Tc instance wraps a single netlink socket. Because synchronous calls (like Qdisc().Get()) and the Monitor loop read from the same underlying connection, they cannot be used simultaneously on the same Tc instance. If you attempt a synchronous call while a monitor is active, it will return ErrMonitorActive.

    Best Practice:

    • Use one Tc instance for synchronous operations (adding, deleting, or getting qdiscs/filters/classes).
    • Open a dedicated Tc instance specifically for Monitor() or MonitorWithErrorFunc().
  6. Configure Netem qdisc attributes

    main

    The Netem struct is used to represent and configure the attributes of the Netem (Network Emulator) discipline. It contains a base NetemQopt configuration and several optional pointers to more specific attribute structures like correlation, delay distribution, reordering, corruption, rate limiting, and more.

    To configure Netem, populate the desired fields in a Netem instance. Fields that are not set (left as nil) will not be included in the resulting netlink message.

    import "github.com/florianl/florianl/go-tc"
    
    // Example: Configuring Netem with latency and reordering
    netemCfg := tc.Netem{
    	Qopt: tc.NetemQopt{
    		Latency: 10000, // 10ms in microseconds
    	},
    	Reorder: &tc.NetemReorder{
    		Probability: 5,
    		Correlation: 25,
    	},
    }
  7. Marshal and unmarshal Ets configuration

    main

    To convert an Ets struct to binary data for netlink communication, use marshalEts. To convert binary data received from the kernel back into an Ets struct, use unmarshalEts.

    Note that marshalEts and unmarshalEts handle nested attributes for Quanta and PrioMap automatically.

    // Example: Marshaling an Ets configuration
    nbands := uint8(8)
    strict := uint8(2)
    quanta := []uint32{1000, 2000}
    
    etsConfig := &tc.Ets{
    	NBands:  &nbands,
    	NStrict: &strict,
    	Quanta:  &quanta,
    }
    
    data, err := tc.MarshalEts(etsConfig)
    
    // Example: Unmarshaling Ets configuration
    newEts := &tc.Ets{}
    err := tc.UnmarshalEts(data, newEts)
  8. Manage TC chains with the Chain API

    main

    The Chain type provides methods to create, delete, and retrieve Traffic Control (TC) chains. You access the chain management interface by calling the Chain() method on a Tc instance.

    • Add(info *Object): Creates a new chain using the provided Object. It uses RTM_NEWCHAIN and fails if the chain already exists (netlink.Excl).
    • Delete(info *Object): Removes an existing chain using the provided Object via RTM_DELCHAIN.
    • Get(i *Msg): Fetches a list of existing chains based on the provided message i using RTM_GETCHAIN.
    // Assuming tc is an initialized *tc.Tc instance
    chain := tc.Chain()
    
    // To add a chain
    err := chain.Add(&tc.Object{ /* ... configuration ... */ })
    
    // To delete a chain
    err := chain.Delete(&tc.Object{ /* ... configuration ... */ })
    
    // To get chains
    chains, err := chain.Get(&tc.Msg{ /* ... query ... */ })
  9. Configure Stab qdisc using the Stab struct

    main

    The Stab struct is used to configure a stab queuing discipline (qdisc) in Linux. It allows you to define size specifications via the Base field and provide additional data via the Data field.

    To use this in a configuration context, you populate a Stab instance and pass it to the library's marshalling logic (which converts the struct into netlink attributes).

    import "github.com/florianl/florianl/go-tc/tc"
    
    // Example of initializing a Stab configuration
    stabConfig := &tc.Stab{
    	Base: &tc.SizeSpec{
    		CellLog:   8,
    		SizeLog:   10,
    		CellAlign: 64,
    		MTU:       1500,
    	},
    	Data: &[]byte{0x01, 0x02, 0x03},
    }
  10. Configure IPSet matching with IPSetMatch

    main

    The IPSetMatch struct is used to define attributes for the ipset match discipline in Netfilter/conntrack. It allows you to specify an IP set ID and the packet directions to be matched against that set.

    IPSetDir

    IPSetDir defines the packet direction. You can use the following constants:

    • IPSetSrc: Source direction.
    • IPSetDst: Destination direction.

    IPSetMatch Fields

    • IPSetID: A uint16 representing the ID of the IP set.
    • Dir: A slice of IPSetDir values specifying which directions to match.
    package tc
    
    type IPSetMatch struct {
    	IPSetID uint16
    	Dir     []IPSetDir
    }
    
    const (
    	IPSetSrc IPSetDir = 1
    	IPSetDst IPSetDir = 2
    )
  11. Configure HHF qdisc attributes using the Hhf struct

    main

    The Hhf struct is used to define the configuration attributes for the Hierarchical Fair Hashing (HHF) queuing discipline (qdisc). Each field in the struct is a pointer to a uint32, allowing you to specify only the attributes you wish to set; fields left as nil will not be included in the marshaled binary output.

    Attributes

    FieldTypeDescription
    BacklogLimit*uint32The maximum backlog limit
    Quantum*uint32The quantum value
    HHFlowsLimit*uint32The limit for HH flows
    ResetTimeout*uint32The reset timeout
    AdmitBytes*uint32The number of bytes to admit
    EVICTTimeout*uint32The eviction timeout
    NonHHWeight*uint32The weight for non-HH flows
    // Example of configuring HHF attributes
    // Note: You must use pointers to uint32 values
    backlogLimit := uint32(1000)
    quantum := uint32(10)
    
    hhfConfig := &tc.Hhf{
        BacklogLimit: &backlogLimit,
        Quantum:      &quantum,
    }