// SPDX-License-Identifier: MIT

use std::net::{Ipv4Addr, Ipv6Addr};

use netlink_packet_utils::{Emitable, Parseable};

use crate::{
    tc::{
        TcAction, TcActionAttribute, TcActionGeneric, TcActionMirrorOption,
        TcActionOption, TcActionType, TcAttribute, TcFilterFlowerMplsLseOption,
        TcFilterFlowerMplsOption, TcFilterFlowerOption, TcHandle, TcHeader,
        TcMessage, TcMessageBuffer, TcMirror, TcMirrorActionType, TcOption,
        TcStats2, TcStatsBasic, TcStatsQueue, Tcf,
    },
    AddressFamily,
};

/*
 * struct tcmsg {}
TCA_KIND ("u32", "flower") defining type of options under TCA_OPTIONS
TCA_OPTIONS (container with ALL other options)
  * TCA_FLOWER_* (matchers)
  * TCA_<TYPE>_ACT
    * action#index
      * TCA_ACT_KIND ("mirred") defining type of options for this action
      * TCA_ACT_STATS
      * TCA_ACT_OPTIONS
        * TCA_<ACT_TYPE>_PARAMS { tc_mirred / tc_gen }
 *
 */

fn get_mirred_action(gen_idx: u32) -> TcAction {
    TcAction {
        tab: 1,
        attributes: vec![
            TcActionAttribute::Kind("mirred".to_string()),
            TcActionAttribute::Stats(vec![
                TcStats2::Basic(TcStatsBasic {
                    bytes: 0,
                    packets: 0,
                }),
                TcStats2::BasicHw(TcStatsBasic {
                    bytes: 0,
                    packets: 0,
                }),
                TcStats2::Queue(TcStatsQueue {
                    qlen: 0,
                    backlog: 0,
                    drops: 0,
                    requeues: 0,
                    overlimits: 0,
                }),
            ]),
            TcActionAttribute::Options(vec![
                TcActionOption::Mirror(TcActionMirrorOption::Parms(TcMirror {
                    generic: TcActionGeneric {
                        index: gen_idx,
                        capab: 0,
                        action: TcActionType::Stolen,
                        refcnt: 1,
                        bindcnt: 1,
                    },
                    eaction: TcMirrorActionType::EgressRedir,
                    ifindex: 7,
                })),
                TcActionOption::Mirror(TcActionMirrorOption::Tm(Tcf {
                    install: 4123,
                    lastuse: 4123,
                    expires: 0,
                    firstuse: 0,
                })),
            ]),
        ],
    }
}

// Common test setup:
//      ip link add veth1 type veth peer veth1.peer
//      ip link set veth1 up
//      ip link set veth1.peer up
//      tc qdisc add dev veth1 ingress
//      tc filter add dev veth1 ingress chain X handle 0x42 ...
//
// Capture:
//      tools/nl_dump.py dump_flower -i veth1 -c X
//

// TC rule:
//      tc filter add dev veth1 ingress handle 0x42 \
//        proto ip flower src_mac 01:02:03:04:05:06 \
//        dst_mac 01:02:03:04:05:06 ip_proto tcp \
//        dst_ip 10.0.0.0/8 src_ip 11.0.0.0/8 \
//        action mirred egress redirect dev veth1.peer
#[test]
fn test_get_filter_flower_mac_ipv4() {
    let raw = vec![
        0x00, // tcm_family (AF_UNSPEC)
        0x00, // pad1
        0x00, 0x00, // pad2
        0x08, 0x00, 0x00, 0x00, // ifindex(LE): 8
        0x42, 0x00, 0x00, 0x00, // tcm_handle(LE) (rule handle): 0x42
        0xf2, 0xff, 0xff, 0xff, // tcm_parent
        0x08, 0x00, 0x00, 0xc0, // tcm_info
        0x0b, 0x00, // NLA length: 11
        0x01, 0x00, // NLA type: TCA_KIND
        0x66, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x00, // NLA data: "flower"
        0x00, // pad
        0x08, 0x00, // NLA length: 8
        0x0b, 0x00, // NLA type: 11 / TCA_CHAIN
        0x00, 0x00, 0x00, 0x00, // NLA data: 0
        0x14, 0x01, // NLA length: 276
        0x02, 0x00, // NLA type: 2 / TCA_OPTIONS
        0x0a, 0x00, // NLA length: 10
        0x04, 0x00, // NLA type: 4 / TCA_FLOWER_KEY_ETH_DST
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // NLA data: 01:02:03:04:05:06
        0x00, 0x00, // pad
        0x0a, 0x00, // NLA length: 10
        0x05, 0x00, // NLA type: 5 / TCA_FLOWER_KEY_ETH_DST_MASK
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // NLA data: ff:ff:ff:ff:ff:ff
        0x00, 0x00, // pad
        0x0a, 0x00, // NLA length: 10,
        0x06, 0x00, // NLA type: 6 / TCA_FLOWER_KEY_ETH_SRC,
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // NLA data: 01:02:03:04:05:06
        0x00, 0x00, // pad
        0x0a, 0x00, // NLA length: 10,
        0x07, 0x00, // NLA type: 7 / TCA_FLOWER_KEY_ETH_SRC_MASK,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // NLA data: ff:ff:ff:ff:ff:ff
        0x00, 0x00, // pad
        0x06, 0x00, // NLA length: 6
        0x08, 0x00, // NLA type: 8 / TCA_FLOWER_KEY_ETH_TYPE
        0x08, 0x00, // NLA data(BE16): 0x0800 / IPv4
        0x00, 0x00, // pad
        0x05, 0x00, // NLA length: 5
        0x09, 0x00, // NLA type: 9 / TCA_FLOWER_KEY_IP_PROTO
        0x06, // NLA data: 6 (IPPROTO_TCP)
        0x00, 0x00, 0x00, // pad
        0x08, 0x00, // NLA length: 8
        0x0a, 0x00, // NLA type: 10 / TCA_FLOWER_KEY_IPV4_SRC
        0x0b, 0x00, 0x00, 0x00, // NLA data: 11.0.0.0
        0x08, 0x00, // NLA length: 8
        0x0b, 0x00, // NLA type: 11 / TCA_FLOWER_KEY_IPV4_SRC_MASK
        0xff, 0x00, 0x00, 0x00, // NLA data: 255.0.0.0
        0x08, 0x00, // NLA length: 8
        0x0c, 0x00, // NLA type: 12 / TCA_FLOWER_KEY_IPV4_DST
        0x0a, 0x00, 0x00, 0x00, // NLA data: 10.0.0.0
        0x08, 0x00, // NLA length: 8
        0x0d, 0x00, // NLA type: 13 / TCA_FLOWER_KEY_IPV4_DST_MASK
        0xff, 0x00, 0x00, 0x00, // NLA data: 255.0.0.0
        0x08, 0x00, // NLA length: 8
        0x16, 0x00, // NLA type: 22 / TCA_FLOWER_FLAGS
        0x08, 0x00, 0x00, 0x00, // NLA data: 8 / TCA_CLS_FLAGS_NOT_IN_HW
        0x08, 0x00, // NLA length: 8
        0x56, 0x00, // NLA type: 86 / TCA_FLOWER_IN_HW_COUNT
        0x00, 0x00, 0x00, 0x00, // NLA data: 0
        0xa0, 0x00, // NLA length: 160
        0x03, 0x00, // TCA_FLOWER_ACT
        0x9c, 0x00, // NLA length: 156
        0x01, 0x00, // NLA type: 1 /
        0x0b, 0x00, // NLA length: 11
        0x01, 0, // NLA type: 1 / TCA_ACT_KIND
        0x6d, 0x69, 0x72, 0x72, 0x65, 0x64, 0x00, // NLA data: mirred
        0x00, //
        0x44, 0x00, // NLA length: 68
        0x04, 0x00, // NLA type: 4 / TCA_ACT_STATS
        0x14, 0x00, // NLA length: 20
        0x01, 0x00, // NLA type: 1 / TCA_STATS_BASIC
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // bytes: 0
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // packets: 0
        0x14, 0x00, // NLA length: 20
        0x07, 0x00, // NLA type: 7 / TCA_STATS_BASIC_HW
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // bytes: 0
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // paclets: 0
        0x18, 0x00, // NLA length: 24
        0x03, 0x00, // NLA type: 3 / TCA_STATS_QUEUE
        0x00, 0x00, 0x00, 0x00, // qlen: 0
        0x00, 0x00, 0x00, 0x00, // backlog: 0
        0x00, 0x00, 0x00, 0x00, // drops: 0
        0x00, 0x00, 0x00, 0x00, // requests: 0
        0x00, 0x00, 0x00, 0x00, // overlimits: 0
        0x48, 0x00, // NLA length: 72
        0x02, 0x80, // NLA type: 2 / TCA_ACT_OPTIONS,
        0x20, 0x00, // NLA length: 32
        0x02, 0x00, // NLA type: 2 / TCA_MIRRED_PARMS (struct tc_mirred)
        0x02, 0x00, 0x00, 0x00, // tc_mirred::tc_gen::index // 2
        0x00, 0x00, 0x00, 0x00, // tc_mirred::tc_gen::capab 0
        0x04, 0x00, 0x00,
        0x00, // tc_mirred::tc_gen::action 4 / TC_ACT_STOLEN
        0x01, 0x00, 0x00, 0x00, // tc_mirred::tc_gen::refctnt 1
        0x01, 0x00, 0x00, 0x00, // tc_mirred::tc_gen::bindctnt 1
        0x01, 0x00, 0x00, 0x00, // tc_mirred::eaction 1 / TCA_EGRESS_REDIR
        0x07, 0x00, 0x00, 0x00, // tc_mirred:ifindex 7
        0x24, 0x00, // NLA length: 36
        0x01, 0x00, // NLA type: 1 / TCA_MIRRED_TM (struct tcf_t)
        0x1b, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, // tcf_t::install 4123
        0x1b, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, // tcf_t::lastuse 4123
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tcf_t::expires
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tcf_t::firstuse
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x42,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221225480,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(0),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthDst([
                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                ])),
                TcOption::Flower(TcFilterFlowerOption::EthDstMask([
                    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                ])),
                TcOption::Flower(TcFilterFlowerOption::EthSrc([
                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                ])),
                TcOption::Flower(TcFilterFlowerOption::EthSrcMask([
                    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                ])),
                TcOption::Flower(TcFilterFlowerOption::EthType(0x0800)),
                TcOption::Flower(TcFilterFlowerOption::IpProto(6)),
                TcOption::Flower(TcFilterFlowerOption::Ipv4Src(
                    "11.0.0.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::Ipv4SrcMask(
                    "255.0.0.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::Ipv4Dst(
                    "10.0.0.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::Ipv4DstMask(
                    "255.0.0.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::Flags(1 << 3)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
                TcOption::Flower(TcFilterFlowerOption::Actions(vec![
                    get_mirred_action(2),
                ])),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress handle 0x43 \
//        proto ipv6 flower src_ip 2a00:1::/64 \
//        dst_ip 2a01:2::/32 ip_ttl 63 ip_proto udp \
//        dst_port 137 src_port 121 \
//        action mirred egress redirect dev veth1.peer
#[test]
fn test_get_filter_flower_ipv6_ports() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
        0xf2, 0xff, 0xff, 0xff, 0x86, 0xdd, 0xff, 0xbf, 0x0b, 0x00, 0x01, 0x00,
        0x66, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0b, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x44, 0x01, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x86, 0xdd, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x11, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x4c, 0x00,
        0xff, 0x00, 0x00, 0x00, 0x14, 0x00, 0x0e, 0x00, 0x2a, 0x00, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x14, 0x00, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x10, 0x00,
        0x2a, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x11, 0x00, 0xff, 0xff, 0xff, 0xff,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x06, 0x00, 0x14, 0x00, 0x00, 0x79, 0x00, 0x00, 0x06, 0x00, 0x25, 0x00,
        0xff, 0xff, 0x00, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, 0x89, 0x00, 0x00,
        0x06, 0x00, 0x26, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x16, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xa0, 0x00, 0x03, 0x00, 0x9c, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x01, 0x00,
        0x6d, 0x69, 0x72, 0x72, 0x65, 0x64, 0x00, 0x00, 0x44, 0x00, 0x04, 0x00,
        0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x80, 0x20, 0x00, 0x02, 0x00,
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x07, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x1b, 0x10, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x43,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221216646,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(0),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x86DD)),
                TcOption::Flower(TcFilterFlowerOption::IpProto(17)),
                TcOption::Flower(TcFilterFlowerOption::IpTtl(63)),
                TcOption::Flower(TcFilterFlowerOption::IpTtlMask(0xFF)),
                TcOption::Flower(TcFilterFlowerOption::Ipv6Src(
                    "2a00:1::".parse::<Ipv6Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::Ipv6SrcMask(
                    "ffff:ffff:ffff:ffff::".parse::<Ipv6Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::Ipv6Dst(
                    "2a01:2::".parse::<Ipv6Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::Ipv6DstMask(
                    "ffff:ffff::".parse::<Ipv6Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::UdpSrc(121)),
                TcOption::Flower(TcFilterFlowerOption::UdpSrcMask(65535)),
                TcOption::Flower(TcFilterFlowerOption::UdpDst(137)),
                TcOption::Flower(TcFilterFlowerOption::UdpDstMask(65535)),
                TcOption::Flower(TcFilterFlowerOption::Flags(1 << 3)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
                TcOption::Flower(TcFilterFlowerOption::Actions(vec![
                    get_mirred_action(3),
                ])),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress handle 0x1 \
//        proto ip flower ip_proto tcp src_port 11-2222 \
//        dst_port 22-3333 tcp_flags 0x01/0xF
#[test]
fn test_get_filter_flower_ipv4_tcp_portrange() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x08, 0x00, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x54, 0x00, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x06, 0x00, 0x00, 0x00,
        0x06, 0x00, 0x47, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x48, 0x00,
        0x00, 0x0F, 0x00, 0x00, 0x06, 0x00, 0x59, 0x00, 0x00, 0x16, 0x00, 0x00,
        0x06, 0x00, 0x5A, 0x00, 0x0D, 0x05, 0x00, 0x00, 0x06, 0x00, 0x57, 0x00,
        0x00, 0x0B, 0x00, 0x00, 0x06, 0x00, 0x58, 0x00, 0x08, 0xAE, 0x00, 0x00,
        0x08, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x56, 0x00,
        0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x1,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221225480,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x0800)),
                TcOption::Flower(TcFilterFlowerOption::IpProto(6)),
                TcOption::Flower(TcFilterFlowerOption::TcpFlags(0x01)),
                TcOption::Flower(TcFilterFlowerOption::TcpFlagsMask(0x0F)),
                TcOption::Flower(TcFilterFlowerOption::PortDstMin(22)),
                TcOption::Flower(TcFilterFlowerOption::PortDstMax(3333)),
                TcOption::Flower(TcFilterFlowerOption::PortSrcMin(11)),
                TcOption::Flower(TcFilterFlowerOption::PortSrcMax(2222)),
                TcOption::Flower(TcFilterFlowerOption::Flags(8)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress handle 0x1 \
//        proto ip flower ip_proto icmp \
//        ip_proto icmp code 8/254 type 6/252
#[test]
fn test_get_filter_flower_ipv4_icmp() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x08, 0x00, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x44, 0x00, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x33, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x34, 0x00,
        0xFC, 0x00, 0x00, 0x00, 0x05, 0x00, 0x31, 0x00, 0x08, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x32, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x08, 0x00, 0x16, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x1,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221225480,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x0800)),
                TcOption::Flower(TcFilterFlowerOption::IpProto(1)),
                TcOption::Flower(TcFilterFlowerOption::Icmpv4Type(6)),
                TcOption::Flower(TcFilterFlowerOption::Icmpv4TypeMask(252)),
                TcOption::Flower(TcFilterFlowerOption::Icmpv4Code(8)),
                TcOption::Flower(TcFilterFlowerOption::Icmpv4CodeMask(254)),
                TcOption::Flower(TcFilterFlowerOption::Flags(8)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress handle 0x1 \
//        proto ipv6 flower ip_proto icmpv6 \
//        ip_proto icmp code 8/254 type 6/252
#[test]
fn test_get_filter_flower_ipv6_icmp() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x86, 0xDD, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x44, 0x00, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x86, 0xDD, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x3A, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x37, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x38, 0x00,
        0xFC, 0x00, 0x00, 0x00, 0x05, 0x00, 0x35, 0x00, 0x08, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x36, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x08, 0x00, 0x16, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x1,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221282182,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x86DD)),
                TcOption::Flower(TcFilterFlowerOption::IpProto(58)),
                TcOption::Flower(TcFilterFlowerOption::Icmpv6Type(6)),
                TcOption::Flower(TcFilterFlowerOption::Icmpv6TypeMask(252)),
                TcOption::Flower(TcFilterFlowerOption::Icmpv6Code(8)),
                TcOption::Flower(TcFilterFlowerOption::Icmpv6CodeMask(254)),
                TcOption::Flower(TcFilterFlowerOption::Flags(8)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress handle 0x1 \
//        proto ip flower ip_proto tcp ct_state +trk \
//        ct_zone 11 ct_mark 3456/65535 ct_label 12345678
#[test]
fn test_get_filter_flower_ct() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x08, 0x00, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x06, 0x00, 0x00, 0x00,
        0x06, 0x00, 0x5B, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x5C, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x5D, 0x00, 0x0B, 0x00, 0x00, 0x00,
        0x06, 0x00, 0x5E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x08, 0x00, 0x5F, 0x00,
        0x80, 0x0D, 0x00, 0x00, 0x08, 0x00, 0x60, 0x00, 0xFF, 0xFF, 0x00, 0x00,
        0x14, 0x00, 0x61, 0x00, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x62, 0x00,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x00,
        0x08, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x1,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221225480,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x0800)),
                TcOption::Flower(TcFilterFlowerOption::IpProto(6)),
                TcOption::Flower(TcFilterFlowerOption::CtState(8)),
                TcOption::Flower(TcFilterFlowerOption::CtStateMask(8)),
                TcOption::Flower(TcFilterFlowerOption::CtZone(11)),
                TcOption::Flower(TcFilterFlowerOption::CtZoneMask(0xFFFF)),
                TcOption::Flower(TcFilterFlowerOption::CtMark(3456)),
                TcOption::Flower(TcFilterFlowerOption::CtMarkMask(0xFFFF)),
                TcOption::Flower(TcFilterFlowerOption::CtLabels([
                    0x12, 0x34, 0x56, 0x78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                ])),
                TcOption::Flower(TcFilterFlowerOption::CtLabelsMask([
                    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
                    255, 255, 255, 255,
                ])),
                TcOption::Flower(TcFilterFlowerOption::Flags(8)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress handle 0x1 \
//        proto arp flower arp_sip 10.0.0.0/23 arp_tip 11.0.0.0/24 \
//        arp_op request arp_tha 01:02:03:04:05:06/FF:FF:FF:FF:FF:0F \
//        arp_sha 02:03:04:05:06:07/FF:FF:FF:FF:0F:0F
#[test]
fn test_get_filter_flower_arp() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x08, 0x06, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x08, 0x06, 0x00, 0x00, 0x08, 0x00, 0x39, 0x00, 0x0A, 0x00, 0x00, 0x00,
        0x08, 0x00, 0x3A, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x08, 0x00, 0x3B, 0x00,
        0x0B, 0x00, 0x00, 0x00, 0x08, 0x00, 0x3C, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
        0x05, 0x00, 0x3D, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x3E, 0x00,
        0xFF, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x3F, 0x00, 0x02, 0x03, 0x04, 0x05,
        0x06, 0x07, 0x00, 0x00, 0x0A, 0x00, 0x40, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
        0x0F, 0x0F, 0x00, 0x00, 0x0A, 0x00, 0x41, 0x00, 0x01, 0x02, 0x03, 0x04,
        0x05, 0x06, 0x00, 0x00, 0x0A, 0x00, 0x42, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF, 0x0F, 0x00, 0x00, 0x08, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x00,
        0x08, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x1,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221227016,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x0806)),
                TcOption::Flower(TcFilterFlowerOption::ArpSip(
                    "10.0.0.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::ArpSipMask(
                    "255.255.254.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::ArpTip(
                    "11.0.0.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::ArpTipMask(
                    "255.255.255.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::ArpOp(1)),
                TcOption::Flower(TcFilterFlowerOption::ArpOpMask(255)),
                TcOption::Flower(TcFilterFlowerOption::ArpSha([
                    0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                ])),
                TcOption::Flower(TcFilterFlowerOption::ArpShaMask([
                    0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F,
                ])),
                TcOption::Flower(TcFilterFlowerOption::ArpTha([
                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                ])),
                TcOption::Flower(TcFilterFlowerOption::ArpThaMask([
                    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F,
                ])),
                TcOption::Flower(TcFilterFlowerOption::Flags(8)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress handle 0x1 \
//        proto 802.1AD flower vlan_ethtype 802.1Q \
//        vlan 11 vlan_prio 1 cvlan_ethtype 0x0800 cvlan 22 cvlan_prio 3
#[test]
fn test_get_filter_flower_vlan() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x88, 0xA8, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x17, 0x00, 0x0B, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x18, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4D, 0x00,
        0x16, 0x00, 0x00, 0x00, 0x05, 0x00, 0x4E, 0x00, 0x03, 0x00, 0x00, 0x00,
        0x06, 0x00, 0x19, 0x00, 0x81, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4F, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x00,
        0x08, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x1,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221268616,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x0800)),
                TcOption::Flower(TcFilterFlowerOption::VlanId(11)),
                TcOption::Flower(TcFilterFlowerOption::VlanPrio(1)),
                TcOption::Flower(TcFilterFlowerOption::CvlanId(22)),
                TcOption::Flower(TcFilterFlowerOption::CvlanPrio(3)),
                TcOption::Flower(TcFilterFlowerOption::VlanEthType(0x8100)),
                TcOption::Flower(TcFilterFlowerOption::CvlanEthType(0x0800)),
                TcOption::Flower(TcFilterFlowerOption::Flags(8)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress handle 0x1 \
//        proto mpls_uc flower mpls_label 132 mpls_bos 1 mpls_ttl 11 mpls_tc 5
#[test]
fn test_get_filter_flower_mpls_uc_single() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x88, 0x47, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x88, 0x47, 0x00, 0x00, 0x05, 0x00, 0x43, 0x00, 0x0B, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x45, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x46, 0x00,
        0x84, 0x00, 0x00, 0x00, 0x05, 0x00, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x08, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x56, 0x00,
        0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x1,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221243784,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x08847)),
                TcOption::Flower(TcFilterFlowerOption::MplsTtl(11)),
                TcOption::Flower(TcFilterFlowerOption::MplsTc(5)),
                TcOption::Flower(TcFilterFlowerOption::MplsLabel(132)),
                TcOption::Flower(TcFilterFlowerOption::MplsBos(1)),
                TcOption::Flower(TcFilterFlowerOption::Flags(8)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress handle 0x1 \
//        proto mpls_uc flower mpls lse depth 1 label 33 \
//        tc 3 bos 0 ttl 48 lse depth 2 label 44 tc 4 bos 1 ttl 47
#[test]
fn test_get_filter_flower_mpls_uc_lse() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x88, 0x47, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x78, 0x00, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x88, 0x47, 0x00, 0x00, 0x5C, 0x00, 0x63, 0x80, 0x2C, 0x00, 0x01, 0x80,
        0x05, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00,
        0x30, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00,
        0x21, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x01, 0x80, 0x05, 0x00, 0x01, 0x00,
        0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
        0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x05, 0x00, 0x2C, 0x00, 0x00, 0x00,
        0x08, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x56, 0x00,
        0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x1,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221243784,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x08847)),
                TcOption::Flower(TcFilterFlowerOption::MplsOpts(vec![
                    TcFilterFlowerMplsOption::Lse(vec![
                        TcFilterFlowerMplsLseOption::LseDepth(1),
                        TcFilterFlowerMplsLseOption::LseTtl(48),
                        TcFilterFlowerMplsLseOption::LseBos(0),
                        TcFilterFlowerMplsLseOption::LseTc(3),
                        TcFilterFlowerMplsLseOption::LseLabel(33),
                    ]),
                    TcFilterFlowerMplsOption::Lse(vec![
                        TcFilterFlowerMplsLseOption::LseDepth(2),
                        TcFilterFlowerMplsLseOption::LseTtl(47),
                        TcFilterFlowerMplsLseOption::LseBos(1),
                        TcFilterFlowerMplsLseOption::LseTc(4),
                        TcFilterFlowerMplsLseOption::LseLabel(44),
                    ]),
                ])),
                TcOption::Flower(TcFilterFlowerOption::Flags(8)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress chain 5 handle 0x44 \
//        proto ip flower enc_key_id 33 enc_dst_port 4789 \
//        enc_dst_ip 2a00:1::/32 enc_src_ip 2a01:2::/64 \
//        enc_ttl 62 enc_tos 1 action mirred egress redirect dev veth1.peer
#[test]
fn test_get_filter_flower_ipv4_enc() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x08, 0x00, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x14, 0x01, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1B, 0x00, 0x0B, 0x00, 0x00, 0x00,
        0x08, 0x00, 0x1C, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x08, 0x00, 0x1D, 0x00,
        0x0A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1E, 0x00, 0xFF, 0xFF, 0xFF, 0x00,
        0x08, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x21, 0x06, 0x00, 0x2D, 0x00,
        0x12, 0xB5, 0x00, 0x00, 0x06, 0x00, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00,
        0x05, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x51, 0x00,
        0xFF, 0x00, 0x00, 0x00, 0x05, 0x00, 0x52, 0x00, 0x3E, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x53, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x16, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xA0, 0x00, 0x03, 0x00, 0x9C, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x01, 0x00,
        0x6D, 0x69, 0x72, 0x72, 0x65, 0x64, 0x00, 0x00, 0x44, 0x00, 0x04, 0x00,
        0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x80, 0x20, 0x00, 0x02, 0x00,
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x07, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x1B, 0x10, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x1B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x44,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221225480,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x0800)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpv4Src(
                    "11.0.0.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpv4SrcMask(
                    "255.255.0.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpv4Dst(
                    "10.0.0.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpv4DstMask(
                    "255.255.255.0".parse::<Ipv4Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyId(33)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyUdpDstPort(4789)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyUdpDstPortMask(
                    65535,
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpTos(1)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpTosMask(255)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpTtl(62)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpTtlMask(255)),
                TcOption::Flower(TcFilterFlowerOption::Flags(1 << 3)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
                TcOption::Flower(TcFilterFlowerOption::Actions(vec![
                    get_mirred_action(3),
                ])),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}

// TC rule:
//      tc filter add dev veth1 ingress chain 5 handle 0x44 \
//        proto ip flower enc_key_id 33 enc_dst_port 4789 \
//        enc_dst_ip 2a00:1::/32 enc_src_ip 2a01:2::/64 \
//        enc_ttl 62 enc_tos 1 action mirred egress redirect dev veth1.peer
#[test]
fn test_get_filter_flower_ipv6_enc() {
    let raw = vec![
        0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
        0xF2, 0xFF, 0xFF, 0xFF, 0x08, 0x00, 0x00, 0xC0, 0x0B, 0x00, 0x01, 0x00,
        0x66, 0x6C, 0x6F, 0x77, 0x65, 0x72, 0x00, 0x00, 0x08, 0x00, 0x0B, 0x00,
        0x05, 0x00, 0x00, 0x00, 0x44, 0x01, 0x02, 0x00, 0x06, 0x00, 0x08, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x14, 0x00, 0x1F, 0x00, 0x2A, 0x01, 0x00, 0x02,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x14, 0x00, 0x20, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x21, 0x00,
        0x2A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x22, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x08, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x21, 0x06, 0x00, 0x2D, 0x00,
        0x12, 0xB5, 0x00, 0x00, 0x06, 0x00, 0x2E, 0x00, 0xFF, 0xFF, 0x00, 0x00,
        0x05, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x51, 0x00,
        0xFF, 0x00, 0x00, 0x00, 0x05, 0x00, 0x52, 0x00, 0x3E, 0x00, 0x00, 0x00,
        0x05, 0x00, 0x53, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x16, 0x00,
        0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xA0, 0x00, 0x03, 0x00, 0x9C, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x01, 0x00,
        0x6D, 0x69, 0x72, 0x72, 0x65, 0x64, 0x00, 0x00, 0x44, 0x00, 0x04, 0x00,
        0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x80, 0x20, 0x00, 0x02, 0x00,
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x07, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x1B, 0x10, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x1B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
    ];

    let expected = TcMessage {
        header: TcHeader {
            family: AddressFamily::Unspec,
            index: 8,
            handle: TcHandle {
                major: 0,
                minor: 0x44,
            },
            parent: TcHandle {
                major: 65535,
                minor: 65522,
            },
            info: 3221225480,
        },
        attributes: vec![
            TcAttribute::Kind("flower".to_string()),
            TcAttribute::Chain(5),
            TcAttribute::Options(vec![
                TcOption::Flower(TcFilterFlowerOption::EthType(0x0800)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpv6Src(
                    "2a01:2::".parse::<Ipv6Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpv6SrcMask(
                    "ffff:ffff:ffff:ffff::".parse::<Ipv6Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpv6Dst(
                    "2a00:1::".parse::<Ipv6Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpv6DstMask(
                    "ffff:ffff::".parse::<Ipv6Addr>().unwrap(),
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyId(33)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyUdpDstPort(4789)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyUdpDstPortMask(
                    65535,
                )),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpTos(1)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpTosMask(255)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpTtl(62)),
                TcOption::Flower(TcFilterFlowerOption::EncKeyIpTtlMask(255)),
                TcOption::Flower(TcFilterFlowerOption::Flags(1 << 3)),
                TcOption::Flower(TcFilterFlowerOption::InHwCount(0)),
                TcOption::Flower(TcFilterFlowerOption::Actions(vec![
                    get_mirred_action(3),
                ])),
            ]),
        ],
    };

    assert_eq!(
        expected,
        TcMessage::parse(&TcMessageBuffer::new(&raw)).unwrap()
    );

    let mut buf = vec![0; expected.buffer_len()];

    expected.emit(&mut buf);

    assert_eq!(buf, raw);
}
