below-tc 0.9.0

TC crate for below
Documentation
// Copyright (c) Facebook, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::BTreeMap;

use netlink_packet_route::tc::TcAttribute;
use netlink_packet_route::tc::TcFqCodelQdStats;
use netlink_packet_route::tc::TcFqCodelXstats;
use netlink_packet_route::tc::TcHandle;
use netlink_packet_route::tc::TcHeader;
use netlink_packet_route::tc::TcMessage;
use netlink_packet_route::tc::TcOption;
use netlink_packet_route::tc::TcQdiscFqCodelOption;
use netlink_packet_route::tc::TcStats;
use netlink_packet_route::tc::TcStats2;
use netlink_packet_route::tc::TcStatsBasic;
use netlink_packet_route::tc::TcStatsQueue;
use netlink_packet_route::tc::TcXstats;

use crate::types::XStats;
use crate::FqCodelQDisc;
use crate::FqCodelQdStats;
use crate::FqCodelXStats;
use crate::QDisc;
use crate::Result;

fn fake_netlink_qdiscs() -> Result<Vec<TcMessage>> {
    let mut tc_msgs = Vec::new();

    let msg1 = TcMessage::from_parts(
        TcHeader {
            index: 2,
            handle: TcHandle::from(0),
            parent: TcHandle::from(2),
            ..Default::default()
        },
        vec![
            TcAttribute::Kind("fq_codel".to_string()),
            TcAttribute::Options(vec![
                TcOption::FqCodel(TcQdiscFqCodelOption::Target(4999u32)),
                TcOption::FqCodel(TcQdiscFqCodelOption::Limit(10240u32)),
                TcOption::FqCodel(TcQdiscFqCodelOption::Interval(99999u32)),
                TcOption::FqCodel(TcQdiscFqCodelOption::Ecn(1u32)),
                TcOption::FqCodel(TcQdiscFqCodelOption::Flows(1024u32)),
                TcOption::FqCodel(TcQdiscFqCodelOption::Quantum(1514u32)),
                TcOption::FqCodel(TcQdiscFqCodelOption::CeThreshold(0u32)),
                TcOption::FqCodel(TcQdiscFqCodelOption::DropBatchSize(64u32)),
                TcOption::FqCodel(TcQdiscFqCodelOption::MemoryLimit(33554432u32)),
            ]),
            TcAttribute::Stats({
                let mut stats = TcStats::default();
                stats.bytes = 39902796u64;
                stats.packets = 165687u32;
                stats.drops = 100u32;
                stats.overlimits = 200u32;
                stats.bps = 300u32;
                stats.pps = 400u32;
                stats.qlen = 500u32;
                stats.backlog = 10u32;
                stats
            }),
            TcAttribute::Stats2(vec![
                TcStats2::Basic({
                    let mut basic = TcStatsBasic::default();
                    basic.bytes = 39902796u64;
                    basic.packets = 165687u32;
                    basic
                }),
                TcStats2::Queue({
                    let mut queue = TcStatsQueue::default();
                    queue.qlen = 500u32;
                    queue.drops = 100u32;
                    queue.requeues = 50u32;
                    queue.overlimits = 20u32;
                    queue
                }),
            ]),
            TcAttribute::Xstats(TcXstats::FqCodel(TcFqCodelXstats::Qdisc({
                let mut fq_codel = TcFqCodelQdStats::default();
                fq_codel.maxpacket = 258;
                fq_codel.drop_overlimit = 0;
                fq_codel.ecn_mark = 0;
                fq_codel.new_flow_count = 91;
                fq_codel.new_flows_len = 0;
                fq_codel.old_flows_len = 0;
                fq_codel.ce_mark = 0;
                fq_codel.memory_usage = 0;
                fq_codel.drop_overmemory = 0;
                fq_codel
            }))),
        ],
    );
    tc_msgs.push(msg1);

    Ok(tc_msgs)
}

#[test]
fn test_tc_stats() {
    let ifaces = BTreeMap::from_iter(vec![(2, "eth0".to_string())]);
    let tc_map = crate::read_tc_stats(ifaces, &fake_netlink_qdiscs).unwrap();

    let tc = tc_map.first().unwrap();
    assert_eq!(tc.if_index, 2);
    assert_eq!(tc.handle, 0);
    assert_eq!(tc.parent, 2);

    assert_eq!(tc.kind, "fq_codel");
    assert_eq!(tc.stats.bytes, Some(39902796));
    assert_eq!(tc.stats.packets, Some(165687));
    assert_eq!(tc.stats.qlen, Some(500));
    assert_eq!(tc.stats.bps, Some(300));
    assert_eq!(tc.stats.pps, Some(400));

    // qdisc
    assert_eq!(
        tc.qdisc,
        Some(QDisc::FqCodel(FqCodelQDisc {
            target: 4999,
            limit: 10240,
            interval: 99999,
            ecn: 1,
            flows: 1024,
            quantum: 1514,
            ce_threshold: 0,
            drop_batch_size: 64,
            memory_limit: 33554432,
        }))
    );

    // xstats
    assert_eq!(
        tc.stats.xstats,
        Some(XStats::FqCodel(FqCodelXStats::FqCodelQdiscStats(
            FqCodelQdStats {
                maxpacket: 258,
                drop_overlimit: 0,
                ecn_mark: 0,
                new_flow_count: 91,
                new_flows_len: 0,
                old_flows_len: 0,
                ce_mark: 0,
                memory_usage: 0,
                drop_overmemory: 0,
            }
        )))
    );
}