#!/usr/bin/env python
# SPDX-License-Identifier: GPL-2.0-or-later
#
# test_bittwiste.py - bittwiste Linux test suite
# Copyright (C) 2006 - 2024 Addy Yeow <ayeowch@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import hashlib
import subprocess
import sys
from pathlib import Path

bin = Path(__file__).resolve().parent.parent / "bin" / "bittwiste"
if not bin.exists():
    sys.stderr.write(f"{bin} is missing")
    sys.exit(1)

out_pcap_file = Path(__file__).resolve().parent / "pcap" / "out.pcap"


def test_bittwiste_copy():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file}"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    in_checksum = hashlib.md5(open(in_pcap_file, "rb").read()).hexdigest()
    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert in_checksum == out_checksum


def test_bittwiste_templates():
    templates = [
        ("eth", "ef94560b8178c9c5d1e913dcc945ce54"),
        ("arp", "d5b379f8a20376e1b63bb8338dbf877c"),
        ("ip", "f29a94f834e7cc8ea668520136d50eea"),
        ("ip6", "959d8f64bd4db5c0c735f3d7b6e8f0cb"),
        ("icmp", "cd039cc28047192408bb2e37d0ece168"),
        ("icmp6", "dd5f5162c0666731e7389e4441a00d0b"),
        ("tcp", "47eb30890319537e780e597843345189"),
        ("ip6tcp", "b3795eb0b5b315f6d156bb2c48424062"),
        ("udp", "f3f26f6e1234741ff89d5205d20a7802"),
        ("ip6udp", "01f51a34b5cea651547437288ad3431a"),
    ]
    for template, expected_checksum in templates:
        command = f"{bin} -I {template} -O {out_pcap_file}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_template_udp_sport():
    command = f"{bin} -I udp -O {out_pcap_file} -T udp -s 0"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "c132a9cc34f8f951c6835655a061e5fc"


def test_bittwiste_template_repeat_tcp_dport_rand():
    command = f"{bin} -I ip6tcp -O {out_pcap_file} -N 10000 -P 1 -T tcp -d rand"
    output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
    output = output.decode("utf-8")
    assert "10001 packets (1020126 bytes) written" in output

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "38f4a09f4228898971269ee920ec0c12"


def test_bittwiste_max_eth_payload():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap"
    payload = "65535-65535:ff"  # 65535 bytes max payload.
    command = (
        f"{bin} -I {in_pcap_file} -O {out_pcap_file} -R 1 -L 2 -X {payload} -T eth"
    )
    output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
    output = output.decode("utf-8")
    assert "1 packets (65575 bytes) written" in output

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "8e637c6934ecc45a18868f14c3abc603"


def test_bittwiste_max_ip_payload():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap"
    payload = "65535-65535:ff"  # 65535 bytes max payload.
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -R 1 -L 3 -X {payload} -T ip"
    output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
    output = output.decode("utf-8")
    assert "1 packets (65575 bytes) written" in output

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "7507c9c1bb45c443c21fed328c872814"


def test_bittwiste_max_tcp_payload():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap"
    payload = "65535-65535:ff"  # 65535 bytes max payload.
    command = (
        f"{bin} -I {in_pcap_file} -O {out_pcap_file} -R 1 -L 4 -X {payload} -T tcp"
    )
    output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
    output = output.decode("utf-8")
    assert "1 packets (65575 bytes) written" in output

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "0e07b9f999a0c1ab5e301dd044288a41"


def test_bittwiste_icmp_echo():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp -t 0"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "decdb8f44253801bcfdc845387d6f6cb"


def test_bittwiste_layer_2():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -L 2"
    output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
    output = output.decode("utf-8")
    # 24 bytes (pcap file header)
    # 16 bytes (pcap packet header)
    # 14 bytes (Ethernet header)
    assert "1 packets (54 bytes) written" in output

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "284991bf006227abc978df0531206f83"


def test_bittwiste_layer_3():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -L 3"
    output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
    output = output.decode("utf-8")
    # 24 bytes (pcap file header)
    # 16 bytes (pcap packet header)
    # 14 bytes (Ethernet header)
    # 20 bytes (IP header)
    assert "1 packets (74 bytes) written" in output

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "911ad3bc2d34ce31a33f46eeedf0d003"


def test_bittwiste_layer_4():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -L 4"
    output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
    output = output.decode("utf-8")
    # 24 bytes (pcap file header)
    # 16 bytes (pcap packet header)
    # 14 bytes (Ethernet header)
    # 20 bytes (IP header)
    # 8 bytes (UDP header)
    assert "1 packets (82 bytes) written" in output

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "4859e9da19006513d8836686ad10b87a"


def test_bittwiste_no_checksum():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -L 4 -C"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "36858da4295e35c470b0de4955f3e44d"


def test_bittwiste_link_type():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -M 0"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "58018d4978c0ac9bc517d0686505553a"


def test_bittwiste_delete_offset():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -D 15-9999"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "284991bf006227abc978df0531206f83"


def test_bittwiste_range():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -R 2-3"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "011f483f5bf0a21039dfc345040c696c"


def test_bittwiste_timeframe():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -S 01/06/2023,14:56:32-01/06/2023,14:56:33"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "011f483f5bf0a21039dfc345040c696c"


def test_bittwiste_nsec_ts_pcap():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "nanosecond-ts.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -S 08/06/2023,09:09:29-08/06/2023,09:09:29"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "c621b20df8bc13975dbaefde03f17acc"


def test_bittwiste_repeat():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "nanosecond-ts.pcap"
    opts = [
        ("-N 1", "243327564647893dda909185b545f72c"),
        ("-R 1 -N 1", "73a57e6dbc987767164b504ad70cd6e0"),
        ("-R 2-4 -N 1000", "77e31e7c751cc03dbe888ae7dcb33d71"),
    ]
    for opt, expected_checksum in opts:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_gaprange():
    ranges = [
        ("1", "0d088451053fca616c80a131d9559f81"),  # 1 microsecond
        (f"{2 ** 31 - 1}", "6263c28e0fb912a06a7e55e4045d856f"),
        ("1000-10000", "5b89572f552e0f9698757f5e0c03fa92"),
        ("1000", "9567e89a9ce877370f35519bcb3b6c5a"),
        ("100000", "e153fef92c6fc5fe31a764e6664edda3"),
        ("1000000", "97271dbc525fef039639f53a26942a07"),  # 1 second
    ]
    for range, expected_checksum in ranges:
        command = f"{bin} -I eth -O {out_pcap_file} -P 10000 -N 10 -G {range}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_nsec_gaprange():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "nanosecond-ts.pcap"
    ranges = [
        ("1", "877575bc86617acb3548edd36cc72ad2"),
        (f"{2 ** 31 - 1}", "6a2d01e9da3e102d5fa47c8e87c285c0"),
        ("1000-10000", "4ef4a66489e5efafa70c32e9c198a142"),
        ("1000", "78d1f93cac85aeca05893f178d08c55d"),
    ]
    for range, expected_checksum in ranges:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -G {range}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_eth_dst_mac():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    macs = [
        ("aa:bb:cc:dd:ee:ff", "fecc1048a00288d602bf7b84e023e8a2"),
        ("bb:bb:bb:bb:bb:bb,aa:bb:cc:dd:ee:ff", "fecc1048a00288d602bf7b84e023e8a2"),
        ("rand", "e9afb8b4268ae1ae6d071447da6c7246"),
        ("bb:bb:bb:bb:bb:bb,rand", "e9afb8b4268ae1ae6d071447da6c7246"),
    ]
    for mac, expected_checksum in macs:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T eth -d {mac}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_eth_src_mac():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    macs = [
        ("00:11:22:33:44:55", "933cb03f4ef0d2b57a1a0bd7763c20ec"),
        ("aa:aa:aa:aa:aa:aa,00:11:22:33:44:55", "933cb03f4ef0d2b57a1a0bd7763c20ec"),
        ("rand", "7f97cd336ae2090d26b97aaf988fdcb4"),
        ("aa:aa:aa:aa:aa:aa,rand", "7f97cd336ae2090d26b97aaf988fdcb4"),
    ]
    for mac, expected_checksum in macs:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T eth -s {mac}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_eth_type():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    types = [
        # keyword, hexadecimal, integer
        # VLAN
        ("vlan", "1e4b6e7e38df6b1371fb5bda4d3c14d3"),
        ("0x8100", "1e4b6e7e38df6b1371fb5bda4d3c14d3"),
        ("33024", "1e4b6e7e38df6b1371fb5bda4d3c14d3"),
        # IP
        ("ip", "2bedce3bb211e95b6b2f29978e6605e6"),
        ("0x0800", "2bedce3bb211e95b6b2f29978e6605e6"),
        ("2048", "2bedce3bb211e95b6b2f29978e6605e6"),
        # IPv6
        ("ip6", "13adf4829975c161e903d0adf8712cde"),
        ("0x86dd", "13adf4829975c161e903d0adf8712cde"),
        ("34525", "13adf4829975c161e903d0adf8712cde"),
        # ARP
        ("arp", "7cd8fe98e453ea62c8c70733e2eb6f38"),
        ("0x0806", "7cd8fe98e453ea62c8c70733e2eb6f38"),
        ("2054", "7cd8fe98e453ea62c8c70733e2eb6f38"),
        # upper and lower bound
        ("0x0000", "ca532cadf3d834b3f78fa1183bf6e28f"),
        ("0", "ca532cadf3d834b3f78fa1183bf6e28f"),
        ("0xffff", "ce48cca554c139c12761a945f8f5619b"),
        ("65535", "ce48cca554c139c12761a945f8f5619b"),
        # FIELD_REPLACE
        ("0x0800,0x8100", "1e4b6e7e38df6b1371fb5bda4d3c14d3"),
        # FIELD_SET_RAND
        ("rand", "4041bc8329806f6d419d12da6f406cdd"),
        # FIELD_REPLACE_RAND
        ("ip,rand", "4041bc8329806f6d419d12da6f406cdd"),
    ]
    for type, expected_checksum in types:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 1 -T eth -t {type}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_eth_payload():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    opts = [
        (
            "-N 10 -X 0302aad1 -L 2 -T eth -t 0x88b5",
            "2d90fb391c643ff13aa4d5142746ff0d",
        ),
        (
            "-P 10000 -N 10 -X 0-1000 -L 2 -T eth -t 0x88b5",
            "f30d2c3c6951a6cc3430295d3cd9e638",
        ),
        (
            "-P 10000 -N 10 -X 20-20 -L 2 -T eth -t 0x88b5",
            "b55fcf441004022558b87d997f704718",
        ),
        (
            "-P 10000 -N 10 -X 0-1000:00 -L 2 -T eth -t 0x88b5",
            "b271d51d0bd90b9a68cf05006d6b911c",
        ),
        (
            "-P 10000 -N 10 -X 0-1000:ff -L 2 -T eth -t 0x88b5",
            "0b85bee27ef4c0e1e3865c9bbc3eab87",
        ),
        (
            "-P 10000 -N 10 -X 0-1000:abcdef0123456789 -L 2 -T eth -t 0x88b5",
            "359e2c616b104b004e2e4c504479a10e",
        ),
        (
            "-P 4294967295 -N 100000 -X 1-1500 -L 2 -T eth -d rand -s rand",
            "453a45793274966bc98d159f443cba28",
        ),
    ]
    for opt, expected_checksum in opts:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_eth_type():
    types = [
        # FIELD_SET
        ("65535", "fe8cab3e872d552f6b7c81c84ca10ad1"),
        # # FIELD_REPLACE
        ("ip,arp", "8e6a3026e7c9a8135fbce47fa37231f2"),
        # # FIELD_SET_RAND
        ("rand", "d1fe64a1646727f45bd37c4bc1ed5135"),
        # # FIELD_REPLACE_RAND
        ("ip,rand", "d1fe64a1646727f45bd37c4bc1ed5135"),
    ]
    for type, expected_checksum in types:
        command = f"{bin} -I vlan -O {out_pcap_file} -P 1 -T vlan -t {type}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_id():
    ids = [
        # FIELD_SET
        ("0", "a8fcc5380cf87b3207474a37ce40a2e6"),
        ("1", "38167ed5739168dc69b681324116edfd"),
        ("2048", "d81bc51a04ada8209ca8334ce20f76fb"),
        ("4094", "e597b58134b6cf92fd3ba93897dba24c"),
        ("4095", "4dd412949fc3938ebda169d1d114a4c7"),
        # FIELD_REPLACE
        ("1024,1000", "75ef0675953c97b02ca7d44b5d1faf9f"),
        ("0,1000", "187de9344fa6857786cd0f27d3ce148d"),
        # FIELD_SET_RAND
        ("rand", "886ba7665f8945727d6435b7134aa560"),
        # FIELD_REPLACE_RAND
        ("1024,rand", "886ba7665f8945727d6435b7134aa560"),
        ("0,rand", "187de9344fa6857786cd0f27d3ce148d"),
    ]
    for id, expected_checksum in ids:
        command = f"{bin} -I vlan -O {out_pcap_file} -P 1 -T vlan -i {id}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_dei():
    deis = [
        # FIELD_SET
        ("0", "187de9344fa6857786cd0f27d3ce148d"),
        ("1", "520b7df671c40787ffabbfae5f115978"),
        # FIELD_REPLACE
        ("0,1", "520b7df671c40787ffabbfae5f115978"),
        ("1,0", "187de9344fa6857786cd0f27d3ce148d"),
        # FIELD_SET_RAND
        ("rand", "520b7df671c40787ffabbfae5f115978"),
        # FIELD_REPLACE_RAND
        ("0,rand", "520b7df671c40787ffabbfae5f115978"),
        ("1,rand", "187de9344fa6857786cd0f27d3ce148d"),
    ]
    for dei, expected_checksum in deis:
        command = f"{bin} -I vlan -O {out_pcap_file} -P 5 -T vlan -d {dei}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_pcp():
    pcps = [
        # FIELD_SET
        ("0", "187de9344fa6857786cd0f27d3ce148d"),
        ("1", "b5162a096f15c3acb68009a49f43d9b3"),
        ("2", "8aee181b9c399ef2d436d7421a18892e"),
        ("3", "28ed2f965d2b58aeab2bc3eda88541c6"),
        ("4", "ab345dcab219e9fb57e0b4da3330089e"),
        ("5", "8f20b623a55706f6bf41fd4e6865717f"),
        ("6", "f8b31ccac07f38c569029b09619adc5f"),
        ("7", "16b2837243021975762662645b7b4c3b"),
        # FIELD_REPLACE
        ("0,7", "16b2837243021975762662645b7b4c3b"),
        ("1,7", "187de9344fa6857786cd0f27d3ce148d"),
        # FIELD_SET_RAND
        ("rand", "8aee181b9c399ef2d436d7421a18892e"),
        # FIELD_REPLACE_RAND
        ("0,rand", "8aee181b9c399ef2d436d7421a18892e"),
        ("1,rand", "187de9344fa6857786cd0f27d3ce148d"),
    ]
    for pcp, expected_checksum in pcps:
        command = f"{bin} -I vlan -O {out_pcap_file} -P 1 -T vlan -p {pcp}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_tci():
    opts = [
        (
            "-P 1 -T vlan -i 0 -d 0 -p 0",
            "a8fcc5380cf87b3207474a37ce40a2e6",
        ),
        (
            "-P 1 -T vlan -i 4095 -d 1 -p 7",
            "c9a629c693674f55107304797b9f66f8",
        ),
        (
            "-P 1 -T vlan -i rand -d rand -p rand",
            "34646f1feaae839767db9ae9021e5e8f",
        ),
    ]
    for opt, expected_checksum in opts:
        command = f"{bin} -I vlan -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_q_in_q():
    opts = [
        (
            "vlan-2-tag.pcap",
            "-P 1 -N 10000 -L 2 -T vlan -i 2730 -i rand -d 0 -d 1 -p 7 -p rand -t stag -t rand",
            "1805a3ac9bad9c939c92f87808683302",
        ),
        (
            "vlan-3-tag.pcap",
            "-P 1 -N 10000 -L 2 -T vlan -p 7 -i 0 -i 1111 -i 4095 -t stag -t ctag -t rand",
            "79667c4e3df068d4bf299f1ce24a7e65",
        ),
        (
            "vlan-4-tag.pcap",
            "-P 1 -N 10000 -L 2 -T vlan -i 1000 -i 2000 -i 3000 -i 4000 -p 7 -p 6 -p 5 -t stag -t stag -t stag -t rand",
            "34c6c62b10546d9b49c2a20c86716d53",
        ),
    ]
    for pcap_file, opt, expected_checksum in opts:
        in_pcap_file = Path(__file__).resolve().parent / "pcap" / pcap_file
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_arp_opcode():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T arp -o 2"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "94318ecfee20fb3b402639d8f91093dc"


def test_bittwiste_arp_smac():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap"
    macs = [
        ("22:22:22:22:22:22", "0538da849487db578c53b1678222f6f1"),
        ("aa:aa:aa:aa:aa:aa,22:22:22:22:22:22", "0538da849487db578c53b1678222f6f1"),
        ("rand", "46251c409ad5729f430cad94429d3dc5"),
        ("aa:aa:aa:aa:aa:aa,rand", "46251c409ad5729f430cad94429d3dc5"),
    ]
    for mac, expected_checksum in macs:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T arp -s {mac}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_arp_sip():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T arp -p 192.168.0.1"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "c6747231d370b90a70dbc442dd4bdf6d"


def test_bittwiste_arp_tmac():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap"
    macs = [
        ("22:22:22:22:22:22", "bc45ddf0a6488d8ba0107ce0f16a7a97"),
        ("00:00:00:00:00:00,22:22:22:22:22:22", "bc45ddf0a6488d8ba0107ce0f16a7a97"),
        ("rand", "d67087305a597c4c68b8b3bd2436a4f7"),
        ("00:00:00:00:00:00,rand", "d67087305a597c4c68b8b3bd2436a4f7"),
    ]
    for mac, expected_checksum in macs:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T arp -t {mac}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_arp_tip():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T arp -q 192.168.0.1"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "0670268132833a757e42580584463fa2"


def test_bittwiste_arp_payload():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap"
    command = (
        f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 1 -X 0-1500 -L 3 -T arp -o 2"
    )
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "39b1d3138febf42611f2fca867445ad9"


def test_bittwiste_vlan_arp():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "vlan-arp.pcap"
    command = (
        f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T arp -o 2 -p 1.1.1.1 -q 2.2.2.2"
    )
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "5113e29e2972171bf700b4632ea1760b"


def test_bittwiste_ip_ds_field():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    # From RFC 4594
    # Some of the service class name mapping to DS field value:
    # ---------------------------------------------------------------
    # Service class name     DSCP name  DSCP value (binary, hex, int)
    # ---------------------------------------------------------------
    # Standard               CS0        000000, 0x00, 0
    # Low-priority data      CS1        001000, 0x08, 8
    # OAM                    CS2        010000, 0x10, 16
    # Broadcast video        CS3        011000, 0x18, 24
    # Real-time interactive  CS4        100000, 0x20, 32
    # ---------------------------------------------------------------
    values = [
        ("0", "f29a94f834e7cc8ea668520136d50eea"),
        ("0x00", "f29a94f834e7cc8ea668520136d50eea"),  # Standard
        ("8", "b0c9c5d4adf93e8e1749bd7844015da7"),
        ("0x08", "b0c9c5d4adf93e8e1749bd7844015da7"),  # Low-priority data
        ("16", "f247e8932c3b5405e82a9410d1250489"),
        ("0x10", "f247e8932c3b5405e82a9410d1250489"),  # OAM
        ("24", "c579867ac611fdadf43c9ec3a6a2eb72"),
        ("0x18", "c579867ac611fdadf43c9ec3a6a2eb72"),  # Broadcast video
        ("32", "d08af4ed967605d4a3e9c0ca24c4b886"),
        ("0x20", "d08af4ed967605d4a3e9c0ca24c4b886"),  # Real-time interactive
    ]
    for value, expected_checksum in values:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip -c {value}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip_ecn_field():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    values = [
        # From RFC 3168
        # ECN: Explicit Congestion Notification
        # ECT: ECN-Capable Transport
        # CE: Congestion Experienced
        # To set ECN field, choose one of the 4 codepoints below:
        # ----------------------------------
        #             ECN FIELD
        # ----------------------------------
        # ECT  CE  Hex value  Codepoint name
        # ----------------------------------
        # 0    0   0x00       Not-ECT
        # 0    1   0x01       ECT(1)
        # 1    0   0x02       ECT(0)
        # 1    1   0x03       CE
        # ----------------------------------
        ("0", "f29a94f834e7cc8ea668520136d50eea"),
        ("0x00", "f29a94f834e7cc8ea668520136d50eea"),  # 0b00000011 Not-ECT
        ("1", "c05a433d6fe82316368826af20fb57b3"),
        ("0x01", "c05a433d6fe82316368826af20fb57b3"),  # 0b00000001 ECT(1)
        ("2", "6f9cdfa72ad388f581c1ee3332fec340"),
        ("0x02", "6f9cdfa72ad388f581c1ee3332fec340"),  # 0b00000010 ECT(0)
        ("3", "8f738d20218243bfe371d01fe2134c3a"),
        ("0x03", "8f738d20218243bfe371d01fe2134c3a"),  # 0b00000011 CE
    ]
    for value, expected_checksum in values:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip -e {value}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip_id():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    ids = [
        ("0", "cce2b07dd766b216fc1393be020b1884"),
        ("65535", "3a856b4b58cb606fca2899fdb77de035"),
        ("12930,1", "9cc2bb65372c57a26811709caec42883"),
        ("1,2", "f29a94f834e7cc8ea668520136d50eea"),
        ("rand", "cc8d257889a156b6afe54e0c99a787b5"),
        ("0x3282,rand", "cc8d257889a156b6afe54e0c99a787b5"),
    ]
    for id, expected_checksum in ids:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -i {id}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip_flags():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    flags = [
        ("-", "5764a63494d331bae4ba6289583de7eb"),
        ("r", "273e0444f43862fd3a1adde85c259673"),
        ("d", "f29a94f834e7cc8ea668520136d50eea"),
        ("m", "a744c0b40197f83cbf08f48349346c07"),
        ("rdm", "027d1b001a781901aa1d3efabf938e9d"),
    ]
    for flag, expected_checksum in flags:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip -f {flag}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip_offset():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip -o 7770"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "8f6a26bb30dbc03fb10b7d39c8976fc8"


def test_bittwiste_ip_ttl():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    ttls = [
        ("0", "a02573984fc285b214a27ed81945c772"),
        ("rand", "f9677b333bd64e71a8f8fa2bc38ef52b"),
        ("64,rand", "f9677b333bd64e71a8f8fa2bc38ef52b"),
        ("255", "b184b553c697ae010c10e6e6d11d228c"),
    ]
    for ttl, expected_checksum in ttls:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -t {ttl}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip_proto():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    protos = [
        ("0", "5593499dc569b12f5eb35e866c770719"),
        ("rand", "94851677eb42797816674e20e3d482d4"),
        ("6,rand", "94851677eb42797816674e20e3d482d4"),
        ("255", "6518e609dd35cbbae72fa363236a1d18"),
    ]
    for proto, expected_checksum in protos:
        command = (
            f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -p {proto}"
        )
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip_sip():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    ips = [
        ("1.1.1.1", "2f4d5757b9b291930850e5bc5d312081"),
        ("127.0.0.1,1.1.1.1", "2f4d5757b9b291930850e5bc5d312081"),
        ("1.1.0.0/16", "72fa1c10e88753417ecca1b3f859da68"),
        ("127.0.0.1,1.1.0.0/16", "72fa1c10e88753417ecca1b3f859da68"),
        ("0.0.0.0/0", "870a06f0e16038bc57a28f3b95b3db06"),
        ("0.0.0.0/32", "151499a5f22cec02c0544f3010c96989"),
    ]
    for ip, expected_checksum in ips:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -s {ip}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip_dip():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    ips = [
        ("1.1.1.1", "19ab454b058b686ff297cabf962d91ad"),
        ("127.0.0.1,1.1.1.1", "19ab454b058b686ff297cabf962d91ad"),
        ("1.1.0.0/16", "78e2760d8880e1b0b6a06459d51eba24"),
        ("127.0.0.1,1.1.0.0/16", "78e2760d8880e1b0b6a06459d51eba24"),
        ("0.0.0.0/0", "8cb8aec22dc1a7b2e81e20b1fbe4f4de"),
        ("0.0.0.0/32", "2d48b2186a0dcbf612737e0e0ee49bc4"),
    ]
    for ip, expected_checksum in ips:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -d {ip}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip_payload():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap"
    opts = [
        # With checksum correction disabled to keep payload as is
        (
            "-C -N 10 -X 0302aad1 -L 3 -T ip",
            "4ba7c49bc088d5f7cab1b907fc6215f7",
        ),
        (
            "-C -P 10000 -N 10 -X 0-1000 -L 3 -T ip",
            "d6055fe2b5e9465e3edffafeda36f356",
        ),
        (
            "-C -P 10000 -N 10 -X 20-20 -L 3 -T ip",
            "4811383062406f5d6df27c172ae4f22f",
        ),
        (
            "-C -P 10000 -N 10 -X 0-1000:00 -L 3 -T ip",
            "5f961bf232a7155db4b005ecffd95c81",
        ),
        (
            "-C -P 10000 -N 10 -X 0-1000:ff -L 3 -T ip",
            "9a56d3cf8d5564081319aa799b88e4dd",
        ),
        (
            "-C -P 10000 -N 10 -X 0-1000:abcdef0123456789 -L 3 -T ip",
            "b0605a0100ac6eb61e9cb9da7534867b",
        ),
        # With checksum correction
        (
            "-P 4 -X 40-40 -L 3 -T ip",
            "ff241d8c0c65830c465467be44d907aa",
        ),
    ]

    for opt, expected_checksum in opts:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip_options_payload():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ipo.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -X {'a' * 40 * 2} -L 3 -T ip"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "676cfb6894c4c78bff9ab4ecd3e1cd10"


def test_bittwiste_vlan_ip():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "vlan-ip.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip -i 65535 -s 1.1.1.1 -d 2.2.2.2"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "cece155846940cb3005e5e271ccbc92f"


def test_bittwiste_ip6_ds_field():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    # From RFC 4594
    # Some of the service class name mapping to DS field value:
    # ---------------------------------------------------------------
    # Service class name     DSCP name  DSCP value (binary, hex, int)
    # ---------------------------------------------------------------
    # Standard               CS0        000000, 0x00, 0
    # Low-priority data      CS1        001000, 0x08, 8
    # OAM                    CS2        010000, 0x10, 16
    # Broadcast video        CS3        011000, 0x18, 24
    # Real-time interactive  CS4        100000, 0x20, 32
    # ---------------------------------------------------------------
    values = [
        ("0", "fd907f31094c4937012285acf200e28f"),
        ("0x00", "fd907f31094c4937012285acf200e28f"),  # Standard
        ("8", "d4c72d18d6f83f7010ff3caccaa976b6"),
        ("0x08", "d4c72d18d6f83f7010ff3caccaa976b6"),  # Low-priority data
        ("16", "c6bd6e916e953525253b003000a2a7ba"),
        ("0x10", "c6bd6e916e953525253b003000a2a7ba"),  # OAM
        ("24", "2f4a2716507d916e0fcdca596c96163a"),
        ("0x18", "2f4a2716507d916e0fcdca596c96163a"),  # Broadcast video
        ("32", "a80bb13607492a8c5d2108b23db532ba"),
        ("0x20", "a80bb13607492a8c5d2108b23db532ba"),  # Real-time interactive
    ]
    for value, expected_checksum in values:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip6 -c {value}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip6_ecn_field():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    values = [
        # From RFC 3168
        # ECN: Explicit Congestion Notification
        # ECT: ECN-Capable Transport
        # CE: Congestion Experienced
        # To set ECN field, choose one of the 4 codepoints below:
        # ----------------------------------
        #             ECN FIELD
        # ----------------------------------
        # ECT  CE  Hex value  Codepoint name
        # ----------------------------------
        # 0    0   0x00       Not-ECT
        # 0    1   0x01       ECT(1)
        # 1    0   0x02       ECT(0)
        # 1    1   0x03       CE
        # ----------------------------------
        ("0", "fd907f31094c4937012285acf200e28f"),
        ("0x00", "fd907f31094c4937012285acf200e28f"),  # 0b00000011 Not-ECT
        ("1", "30f782ab4c0d12ad7ab5e9cc1b00de8e"),
        ("0x01", "30f782ab4c0d12ad7ab5e9cc1b00de8e"),  # 0b00000001 ECT(1)
        ("2", "f4d7480a83ddd35de40a3224be44b07a"),
        ("0x02", "f4d7480a83ddd35de40a3224be44b07a"),  # 0b00000010 ECT(0)
        ("3", "e6aa1806cb7f3b7df354570e5ebd4d81"),
        ("0x03", "e6aa1806cb7f3b7df354570e5ebd4d81"),  # 0b00000011 CE
    ]
    for value, expected_checksum in values:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip6 -e {value}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip6_flow_label():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    in_checksum = hashlib.md5(open(in_pcap_file, "rb").read()).hexdigest()
    flow_labels = [
        # 217523, 0x351b3, 0x0351b3, and 0650663 are all equal but specified in
        # different notation.
        ("217523", in_checksum),  # integer input
        ("0x351b3", in_checksum),  # hexadecimal input, leading 0x
        ("0x0351b3", in_checksum),  # hexadecimal input, leading 0x
        ("0650663", in_checksum),  # octal input, leading 0
        ("0x00000", "e426a6627f85ec9e81c92aba38c82809"),
        ("0", "e426a6627f85ec9e81c92aba38c82809"),
        ("0xfffff", "d46873fcd066f82302169e52bd235247"),
        ("1048575", "d46873fcd066f82302169e52bd235247"),
    ]
    for flow_label, expected_checksum in flow_labels:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip6 -f {flow_label}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip6_next_header():
    next_headers = [
        ("0", "1c96458741482f6ae270b2162c2525ae"),
        ("6", "959d8f64bd4db5c0c735f3d7b6e8f0cb"),
        ("17", "4fcb7c47888cfd15d64805eadc595fc4"),
        ("50", "ca9a470a6a59fb6d100280c4d3777efe"),
        ("51", "fd2942e491ea9641d43be4fd93c80abc"),
        ("58", "3d74df02c928143f3a519f989cce1f1e"),
        ("255", "812c88133f034b831c732c5ad2bf2a3c"),
        ("rand", "9715fb341e3daa197da5de27744dc874"),
        ("6,rand", "9715fb341e3daa197da5de27744dc874"),
    ]
    for next_header, expected_checksum in next_headers:
        command = f"{bin} -I ip6 -O {out_pcap_file} -P 10000 -T ip6 -n {next_header}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip6_hop_limit():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    hop_limits = [
        ("0", "e3b0d298d4f3f994674b50dd69e9fdc5"),
        ("255", "e18371eabf1e0eff127feed105f256af"),
        ("rand", "a6041eb2f7b9341258362800102ca155"),
        ("53,rand", "a6041eb2f7b9341258362800102ca155"),
    ]
    for hop_limit, expected_checksum in hop_limits:
        command = (
            f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip6 -h {hop_limit}"
        )
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip6_sip():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    ips = [
        ("fd00::1", "2945d7bf35e49e9d1370f43387b9e74e"),
        ("2606:4700:4700::64,::1", "4162c7086a1b07c1bcfed847bc561762"),
        ("2001:db8::/64", "e70dc9a77460bd7da04c43702f283c39"),
        ("2606:4700:4700::64,2001:db8::/64", "e70dc9a77460bd7da04c43702f283c39"),
        ("::/0", "2214a6eaf456a8e77bdbd835a8e635ba"),
        ("::/128", "66414ba3c39e8d41b34dea1822e9da60"),
    ]
    for ip, expected_checksum in ips:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip6 -s {ip}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_ip6_dip():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    ips = [
        ("fd00::2", "a47dd221b2a8a9c20433d80b20a32a97"),
        ("2606:4700:4700::6400,::2", "7d7996cee18bbcd17cc3541da84b372c"),
        ("2001:db8::/64", "64b7b89f5b9221cc9f63aae4361b7bf6"),
        ("2606:4700:4700::6400,2001:db8::/64", "64b7b89f5b9221cc9f63aae4361b7bf6"),
        ("::/0", "ccd2ee2417f409883229a716b54beaef"),
        ("::/128", "ed7310991e521868b871d429e3d73932"),
    ]
    for ip, expected_checksum in ips:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip6 -d {ip}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_ip6():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "vlan-ip6.pcap"
    command = (
        f"{bin} -I {in_pcap_file} -O {out_pcap_file} "
        f"-P 1 -T ip6 -c 32 -e 3 -f 0xfffff -n 17 -h 255 -s ::/0 -d ::/0"
    )
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "46fd1b5a2b17b10b740456bcec1907d8"


def test_bittwiste_icmp_type():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp -t 0"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "decdb8f44253801bcfdc845387d6f6cb"


def test_bittwiste_icmp_code():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp -c 255"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "aef78c21afb648a5c554bd2256dc6389"


def test_bittwiste_icmp6_type():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp6.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp6 -t 129"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "2458130f660da7acf73e984bfc2d858b"


def test_bittwiste_icmp6_code():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp6.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp6 -c 255"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "5690afe231cd89d2a0b6dde31d1b6441"


def test_bittwiste_icmp_payload():
    opts = [
        (
            f"{bin} -I ip -O {out_pcap_file} -X 0800f58a012b014a -L 3 -T ip -p 1",
            "6d824fb13ac013c2ef3fd8b08a9c0e01",
        ),
        (
            f"{bin} -I icmp -O {out_pcap_file} -X aaaabbbb -L 4 -T icmp",
            "833a04596bd08703e681b481d6b4919d",
        ),
        (
            f"{bin} -I ip6 -O {out_pcap_file} -X 800000004c010013 -L 3 -T ip6 -n 58",
            "0c233663995bc1361ab3a824bb41daa9",
        ),
        (
            f"{bin} -I icmp6 -O {out_pcap_file} -X 11223344556677 -L 4 -T icmp6 -c 255 -t 255",
            "3258671c6f23cf27a129f74048a14986",
        ),
    ]
    for command, expected_checksum in opts:
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_icmp():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "vlan-icmp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp -t 255 -c 255"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "9bce76b7c5a740e96519c7e1d9b2e604"


def test_bittwiste_vlan_icmp6():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "vlan-icmp6.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp6 -t 255 -c 255"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "87453664eb986d0b3198c2bf5d1d5d96"


def test_bittwiste_tcp_sport():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    opts = [
        ("-T tcp -s 1000", "b064417c556ef7a068f7cda4c1aa0865"),
        ("-T tcp -s 30000,1000", "b064417c556ef7a068f7cda4c1aa0865"),
        ("-P 10000 -T tcp -s rand", "4ff6f9931fc2e0b7065eb613e5bd7a2a"),
        ("-P 10000 -T tcp -s 30000,rand", "4ff6f9931fc2e0b7065eb613e5bd7a2a"),
    ]
    for opt, expected_checksum in opts:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_tcp_dport():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    opts = [
        ("-T tcp -d 1000", "f6f4d2431303b49c47d7e3a63178e2ff"),
        ("-T tcp -d 60000,1000", "f6f4d2431303b49c47d7e3a63178e2ff"),
        ("-P 10000 -T tcp -d rand", "737aae1e41f76e323da89ebb3df86dd7"),
        ("-P 10000 -T tcp -d 60000,rand", "737aae1e41f76e323da89ebb3df86dd7"),
    ]
    for opt, expected_checksum in opts:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_tcp_seq():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap"
    opts = [
        ("-T tcp -q 0", "eab1ff9e93ada77cd598b1173fbd1e36"),
        ("-T tcp -q 4294967295", "c7dfbf484ecc6cabe6e2d7aa7db502fc"),
        ("-T tcp -q 2053058830,100", "e46cc50cb4c4159692b62deb09be511e"),
        ("-P 10000 -T tcp -q rand", "a21d1ae3d7dd4b5fae9c8dac9dd75c77"),
        ("-P 10000 -T tcp -q 2053058831,rand", "857cc97740470d7995c2fb7e9bb80374"),
    ]
    for opt, expected_checksum in opts:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_tcp_ack():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap"
    opts = [
        ("-T tcp -a 0", "5b57c547f994a80dd284c176dfaade3e"),
        ("-T tcp -a 4294967295", "0b7f0b0b502f93e818cab70f147de02c"),
        ("-T tcp -a 0,100", "13e5873d42f2115a2a5dae4b2a78bf4a"),
        ("-P 10000 -T tcp -a rand", "ebbf176a8ec144e8e84b6ee0aa54561f"),
        ("-P 10000 -T tcp -a 143840249,rand", "e304ecb1942c875a8d73db1da7aeb9be"),
    ]
    for opt, expected_checksum in opts:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_tcp_flags():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap"
    flags = [
        ("-", "5a62552112078b48e578909c8829a0bd"),
        ("c", "e512b62c0fd8ddb0084afddc0a1df163"),
        ("e", "a9e39e786441ddfb894437e4ca31cd03"),
        ("u", "90148eeee21504b0452fc074b033e828"),
        ("a", "9c171b01791a089c6d8896462a86a313"),
        ("p", "eb86309a48eeb86695cbc7ab124d50b3"),
        ("r", "74edf982a1b3bd504f2c7fb808040398"),
        ("s", "bffdb416fa9f142d5a5ab73668d051f5"),
        ("f", "58da6f385414ce1fdb7e9c0cfa1becf1"),
        ("ceuaprsf", "bd002c3a6fa2bf65f612c6c65063ba80"),
    ]
    for flag, expected_checksum in flags:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T tcp -f {flag}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_tcp_win():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T tcp -w 65535"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "77e7eccd00e2e58915d9ce66771c5892"


def test_bittwiste_tcp_urg():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T tcp -u 65535"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "f7c30cad1c6eca1a949e2a02bbeb3fb7"


def test_bittwiste_tcp_payload():
    opts = [
        (
            "tcp.pcap",
            "-P 10000 -N 10 -X 0-1000:abcdef0123456789 -L 4 -T tcp",
            "e51e47daa048470fcf145bb36e464b92",
        ),
        (
            "tcp6.pcap",
            "-N 10 -X 0302aad1 -L 4 -T tcp",
            "27f171088d419c733224a3f4fd040569",
        ),
        (
            "tcp6.pcap",
            "-P 10000 -N 10 -X 0-1000 -L 4 -T tcp",
            "55d04546877e183e28ec033a7451de78",
        ),
        (
            "tcp6.pcap",
            "-P 10000 -N 10 -X 20-20 -L 4 -T tcp",
            "1de267bfc75db3a681ecdf7bd606d401",
        ),
        (
            "tcp6.pcap",
            "-P 10000 -N 10 -X 0-1000:00 -L 4 -T tcp",
            "c717bac50c143d25070e33747eeeaf9c",
        ),
        (
            "tcp6.pcap",
            "-P 10000 -N 10 -X 0-1000:ff -L 4 -T tcp",
            "989a4b812cca8932d0ca627bea6007e0",
        ),
        (
            "tcp6.pcap",
            "-P 10000 -N 10 -X 0-1000:abcdef0123456789 -L 4 -T tcp",
            "fcec4e1efb7a23261dc24740be99322d",
        ),
    ]
    for pcap_file, opt, expected_checksum in opts:
        in_pcap_file = Path(__file__).resolve().parent / "pcap" / pcap_file
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_tcp():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "vlan-tcp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T tcp -s 65535 -d 65535"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "77096d06a76ec4e8b7b5f83fb29fb2db"


def test_bittwiste_vlan_tcp6():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "vlan-tcp6.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T tcp -s 65535 -d 65535"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "53d0f27e2d613d136c2d1bc4f77274c5"


def test_bittwiste_udp_sport():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap"
    opts = [
        ("-T udp -s 0", "f40f2330d6c7a5c6b912820fc75c520f"),
        ("-T udp -s 60935,0", "f40f2330d6c7a5c6b912820fc75c520f"),
        ("-P 10000 -T udp -s rand", "00235742347dc989f273bef864c15413"),
        ("-P 10000 -T udp -s 60935,rand", "00235742347dc989f273bef864c15413"),
    ]
    for opt, expected_checksum in opts:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_udp_dport():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp6.pcap"
    opts = [
        ("-T udp -d 65535", "e9875fa4b2f6f14f21335c1b3e84d7ec"),
        ("-T udp -d 53,65535", "e9875fa4b2f6f14f21335c1b3e84d7ec"),
        ("-P 10000 -T udp -d rand", "66d4781d2f88aadbb12416c398f34886"),
        ("-P 10000 -T udp -d 53,rand", "66d4781d2f88aadbb12416c398f34886"),
    ]
    for opt, expected_checksum in opts:
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_udp_payload():
    opts = [
        (
            "udp.pcap",
            "-P 10000 -N 10 -X 0-1000:abcdef0123456789 -L 4 -T udp",
            "932a8b03b0bfeb62c4642caa2cd5f0ad",
        ),
        (
            "udp6.pcap",
            "-N 10 -X 0302aad1 -L 4 -T udp",
            "baac6faa64072a02a4fcfcff2c7d9627",
        ),
        (
            "udp6.pcap",
            "-P 10000 -N 10 -X 0-1000 -L 4 -T udp",
            "8d7edfe9164f29b8b917fdbd6b4d8aab",
        ),
        (
            "udp6.pcap",
            "-P 10000 -N 10 -X 20-20 -L 4 -T udp",
            "9f68cdb0e385ee8a867e424c0125b724",
        ),
        (
            "udp6.pcap",
            "-P 10000 -N 10 -X 0-1000:00 -L 4 -T udp",
            "984e7bcd6bd27dfc50a064a8d1967fab",
        ),
        (
            "udp6.pcap",
            "-P 10000 -N 10 -X 0-1000:ff -L 4 -T udp",
            "b5a05d2ae0bb46abac249edb3784c367",
        ),
        (
            "udp6.pcap",
            "-P 10000 -N 10 -X 0-1000:abcdef0123456789 -L 4 -T udp",
            "ba69ac1ef3b2912882f25c93016597b1",
        ),
    ]
    for pcap_file, opt, expected_checksum in opts:
        in_pcap_file = Path(__file__).resolve().parent / "pcap" / pcap_file
        command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_udp_payload_malformed():
    opts = [
        # Valid UDP + DNS payload
        (
            "ip",
            "-X "
            "ee0700350039716c97b50100000100000000000101310131013101310"
            "7696e2d61646472046172706100000c000100002905c0000000000000"
            " -L 3 -T ip -p 17",
            "673865897802a157f1568edb34d0774f",
        ),
        # Malformed UDP (truncated bytes) from above
        (
            "ip",
            "-N 1000 -X ee070035 -L 3 -T ip -p 17",
            "807afe83dfba1d95b5f58301c2390e93",
        ),
    ]
    for pcap_file, opt, expected_checksum in opts:
        command = f"{bin} -I {pcap_file} -O {out_pcap_file} {opt}"
        subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

        out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
        assert out_checksum == expected_checksum


def test_bittwiste_vlan_udp():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "vlan-udp.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T udp -s 65535 -d 65535"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "a217d4a3d64e52990dedaa60f59c03c3"


def test_bittwiste_vlan_udp6():
    in_pcap_file = Path(__file__).resolve().parent / "pcap" / "vlan-udp6.pcap"
    command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T udp -s 65535 -d 65535"
    subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)

    out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest()
    assert out_checksum == "bcdf0684b56cb8ee13bd7a5dca3b01d8"
