/* packet-pcap_pktdata.c
 * Dissect packet data from a pcap or pcapng file or from a "remote pcap"
 * protocol.
 *
 * Copyright 2015, Michal Labedzki for Tieto Corporation
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <epan/packet.h>
#include <epan/expert.h>

#include <wiretap/wtap.h>
#include <wiretap/pcap-encap.h>

#include <epan/dissectors/packet-pcap_pktdata.h>

void proto_register_pcap_pktdata(void);
void proto_reg_handoff_pcap_pktdata(void);

static int proto_pcap_pktdata = -1;

static int hf_pcap_pktdata_pseudoheader = -1;
static int hf_pcap_pktdata_pseudoheader_bluetooth_direction = -1;
static int hf_pcap_pktdata_undecoded_data = -1;

static gint ett_pcap_pktdata_pseudoheader = -1;

static expert_field ei_pcap_pktdata_linktype_unknown = EI_INIT;
static expert_field ei_pcap_pktdata_cant_generate_phdr = EI_INIT;

static dissector_table_t wtap_encap_table;

/*
 * Link-layer header type values.
 *
 * Includes both the official documented values from
 *
 *    http://www.tcpdump.org/linktypes.html
 *
 * and values not listed there.  The names are, in most cases, the
 * LINKTYPE_ names with LINKTYPE_ stripped off.
 */
const value_string link_type_vals[] = {
    { 0,    "NULL" },
    { 1,    "ETHERNET" },
    { 2,    "EXP_ETHERNET" },            /* 3Mb experimental Ethernet */
    { 3,    "AX25" },
    { 4,    "PRONET" },                 /* Proteon PRONET */
    { 5,    "CHAOS" },                  /* MIT Chaosnet */
    { 6,    "IEEE802_5" },
    { 7,    "ARCNET_BSD" },
    { 8,    "SLIP" },
    { 9,    "PPP" },
    { 10,   "FDDI" },
    { 32,   "REDBACK" },                /* Redback SmartEdge 400/800 */
    { 50,   "PPP_HDLC" },
    { 51,   "PPP_ETHER" },
    { 99,   "SYMANTEC_FIREWALL" },
    { 100,  "ATM_RFC1483" },
    { 101,  "RAW" },
    { 102,  "BSD/OS SLIP" },
    { 103,  "BSD/OS PPP" },
    { 104,  "C_HDLC" },
    { 105,  "IEEE802_11" },
    { 106,  "LINUX_ATM_CLIP" },
    { 107,  "FRELAY" },
    { 108,  "LOOP" },
    { 109,  "ENC" },
    { 110,  "LANE8023" },               /* ATM LANE + 802.3 */
    { 111,  "HIPPI" },                  /* NetBSD HIPPI */
    { 112,  "HDLC" },                   /* NetBSD HDLC framing */
    { 113,  "LINUX_SLL" },
    { 114,  "LTALK" },
    { 115,  "ECONET" },                 /* Acorn Econet */
    { 116,  "IPFILTER" },               /* Reserved for use with OpenBSD ipfilter */
    { 117,  "PFLOG" },
    { 118,  "CISCO_IOS" },              /* for Cisco-internal use */
    { 119,  "IEEE802_11_PRISM" },
    { 120,  "IEEE802_11_AIRONET" },     /* 802.11 plus FreeBSD Aironet drive metadata header */
    { 121,  "HHDLC" },                  /* reserved for Siemens HiPath HDLC - never used */
    { 122,  "IP_OVER_FC" },
    { 123,  "SUNATM" },
    { 124,  "RIO" },                    /* Private use for RapidIO */
    { 125,  "PCI_EXP" },                /* Private use for PCI Express */
    { 126,  "AURORA" },                 /* Xilinx Aurora link layer */
    { 127,  "IEEE802_11_RADIOTAP" },
    { 128,  "TZSP" },                   /* reserved for TZSP encapsulation - never used */
    { 129,  "ARCNET_LINUX" },
    { 130,  "JUNIPER_MLPPP" },          /* Juniper-private, but handled by tcpdump and Wireshark */
    { 131,  "JUNIPER_MLFR" },           /* Juniper-private, but handled by tcpdump and Wireshark */
    { 132,  "JUNIPER_ES" },             /* Juniper-private, but handled by tcpdump */
    { 133,  "JUNIPER_GGSN" },           /* Juniper-private, but handled by tcpdump and Wireshark */
    { 134,  "JUNIPER_MFR" },            /* Juniper-private, but handled by tcpdump and Wireshark */
    { 135,  "JUNIPER_ATM2" },           /* Juniper-private, but handled by tcpdump and Wireshark */
    { 136,  "JUNIPER_SVCS" },           /* Juniper-private, but handled by tcpdump and Wireshark */
    { 137,  "JUNIPER_ATM1" },           /* Juniper-private, but handled by tcpdump and Wireshark */
    { 138,  "APPLE_IP_OVER_IEEE1394" },
    { 139,  "MTP2_WITH_PHDR" },
    { 140,  "MTP2" },
    { 141,  "MTP3" },
    { 142,  "SCCP" },
    { 143,  "DOCSIS" },
    { 144,  "LINUX_IRDA" },
    { 145,  "IBM_SP" },                 /* Reserved for IBM SP switch */
    { 146,  "IBM_SN" },                 /* Reserved for IBM Next Federation switch */
    { 147,  "USER_0" },
    { 148,  "USER_1" },
    { 149,  "USER_2" },
    { 150,  "USER_3" },
    { 151,  "USER_4" },
    { 152,  "USER_5" },
    { 153,  "USER_6" },
    { 154,  "USER_7" },
    { 155,  "USER_8" },
    { 156,  "USER_9" },
    { 157,  "USER_10" },
    { 158,  "USER_11" },
    { 159,  "USER_12" },
    { 160,  "USER_13" },
    { 161,  "USER_14" },
    { 162,  "USER_15" },
    { 163,  "IEEE802_11_AVS" },
    { 164,  "JUNIPER_MONITOR" },        /* Juniper-private, but handled by tcpdump */
    { 165,  "BACNET_MS_TP" },
    { 166,  "PPP_PPPD" },
    { 167,  "JUNIPER_PPPOE" },          /* Juniper-private, but handled by tcpdump and Wireshark */
    { 168,  "JUNIPER_PPPOE_ATM" },      /* Juniper-private, but handled by tcpdump */
    { 169,  "GPRS_LLC" },
    { 170,  "GPF_T" },                  /* GPF-T (ITU-T G.7041/Y.1303) */
    { 171,  "GPF_F" },                  /* GPF-F (ITU-T G.7041/Y.1303) */
    { 172,  "GCOM_TIE1" },              /* Reserved for Gcom's T1/E1 line monitoring equipment */
    { 173,  "GCOM_SERIAL" },            /* Reserved for Gcom's T1/E1 line monitoring equipment */
    { 174,  "JUNIPER_PIC_PEER" },       /* Juniper-private */
    { 175,  "ERF_ETH" },                /* ERF header followed by Ethernet */
    { 176,  "ERF_POS" },                /* ERF header followed by Packet-over-SONET */
    { 177,  "LINUX_LAPD" },
    { 178,  "JUNIPER_ETHER" },          /* Juniper-private, but handled by tcpdump and Wireshark */
    { 179,  "JUNIPER_PPP" },            /* Juniper-private, but handled by tcpdump and Wireshark */
    { 180,  "JUNIPER_FRELAY" },         /* Juniper-private, but handled by tcpdump and Wireshark */
    { 181,  "JUNIPER_CHDLC" },          /* Juniper-private, but handled by tcpdump and Wireshark */
    { 182,  "MFR" },                    /* Multi Link Frame Relay (FRF.16) */
    { 183,  "JUNIPER_VP" },             /* Juniper-private, but handled by tcpdump and Wireshark */
    { 184,  "A429" },                   /* Arinc 429 frames */
    { 185,  "A653_ICM" },               /* Aricn 653 Interpartition Communication messages */
    { 186,  "USB" },                    /* Older USB header */
    { 187,  "BLUETOOTH_HCI_H4" },
    { 188,  "IEEE802_16_MAC_CPS" },     /* IEEE 802.16 MAC Common Part Sublayer */
    { 189,  "USB_LINUX" },
    { 190,  "CAN20B" },                 /* CAN v2.0B packets */
    { 191,  "IEEE802_15_4_LINUX" },     /* IEEE 802.15.4, with address fields padded, as is done by Linux drivers */
    { 192,  "PPI" },
    { 193,  "IEEE802_16_MAC_CPS_RADIO" }, /* 802.16 MAC Common Part Sublayer plus a radiotap radio header */
    { 194,  "JUNIPER_ISM" },            /* Juniper-private */
    { 195,  "IEEE802_15_4" },
    { 196,  "SITA" },
    { 197,  "ERF" },
    { 198,  "RAIF1" },                  /* Special header prepended to Ethernet packets when capturing from a u10 Networks board */
    { 199,  "IPMB" },                   /* IPMB packet for IPMI */
    { 200,  "JUNIPER_ST" },             /* Juniper-private */
    { 201,  "BLUETOOTH_HCI_H4_WITH_PHDR" },
    { 202,  "AX25_KISS" },
    { 203,  "LAPD" },
    { 204,  "PPP_WITH_DIR" },
    { 205,  "C_HDLC_WITH_DIR" },
    { 206,  "FRELAY_WITH_DIR" },
    { 207,  "LAPB_WITH_DIR" },          /* LAPB with direction pseudo-header */
    { 209,  "IPMB_LINUX" },
    { 210,  "FLEXRAY" },                /* FlexRay automotive bus */
    { 211,  "MOST" },                   /* Media Oriented Systems Transport */
    { 212,  "LIN" },                    /* Local Interconnect Network */
    { 213,  "X2E_SERIAL" },             /* X2E-private for serial line capture */
    { 214,  "X2E_XORAYA" },             /* X2E-private for Xoraya data logger family */
    { 215,  "IEEE802_15_4_NONASK_PHY" },
    { 216,  "LINUX_EVDEV" },            /* Linux evdev messages */
    { 217,  "GSMTAP_UM" },              /* "gsmtap" header followed by GSM Um interface packets */
    { 218,  "GSMTAP_UM" },              /* "gsmtap" header followed by GSM Abis interface packets */
    { 219,  "MPLS" },                   /* MPLS label (stack?) as the link-layer header */
    { 220,  "USB_LINUX_MMAPPED" },
    { 221,  "DECT" },                   /* DECT packets, with a pseudo-header */
    { 222,  "AOS" },                    /* AOS Space Data Link Protocol */
    { 223,  "WIHART" },                 /* Wireless HART */
    { 224,  "FC_2" },
    { 225,  "FC_2_WITH_FRAME_DELIMS" },
    { 226,  "IPNET" },
    { 227,  "CAN_SOCKETCAN" },
    { 228,  "IPV4" },
    { 229,  "IPV6" },
    { 230,  "IEEE802_15_4_NOFCS" },
    { 231,  "DBUS" },
    { 232,  "JUNIPER_VS" },             /* Juniper-private */
    { 233,  "JUNIPER_SRX_E2E" },        /* Juniper-private */
    { 234,  "JUNIPER_FIBRECHANNEL" },   /* Juniper-private */
    { 235,  "DVB_CI" },
    { 236,  "MUX27010" },
    { 237,  "STANAG_5066_D_PDU" },
    { 238,  "JUNIPER_ATM_CEMIC" },      /* Juniper-private */
    { 239,  "NFLOG" },
    { 240,  "NETANALYZER" },
    { 241,  "NETANALYZER_TRANSPARENT" },
    { 242,  "IPOIB" },
    { 243,  "MPEG_2_TS" },
    { 244,  "NG40" },
    { 245,  "NFC_LLCP" },
    { 246,  "PFSYNC" },
    { 247,  "INFINIBAND" },
    { 248,  "SCTP" },
    { 249,  "USBPCAP" },
    { 250,  "RTAC_SERIAL" },
    { 251,  "BLUETOOTH_LE_LL" },
    { 252,  "WIRESHARK_UPPER_PDU" },    /* Upper-layer protocol saves from Wireshark */
    { 253,  "NETLINK" },
    { 254,  "BLUETOOTH_LINUX_MONITOR" },
    { 255,  "BLUETOOTH_BREDR_BB" },
    { 256,  "BLUETOOTH_LE_LL_WITH_PHDR" },
    { 257,  "PROFIBUS_DL" },
    { 258,  "PKTAP" },
    { 259,  "EPON" },
    { 260,  "IPMI_HPM_2" },
    { 261,  "ZWAVE_R1_R2" },
    { 262,  "ZWAVE_R3" },
    { 263,  "WATTSTOPPER_DLM" },
    { 264,  "ISO_14443" },
    { 265,  "RDS" },
    { 266,  "USB_DARWIN" },
    { 267,  "OPENFLOW" },
    { 268,  "SDLC" },
    { 269,  "TI_LLN_SNIFFER" },
    { 270,  "LORATAP" },
    { 271,  "VSOCK" },
    { 272,  "NORDIC_BLE" },
    { 273,  "DOCSIS31_XRA31" },
    { 274,  "ETHERNET_MPACKET" },
    { 275,  "DISPLAYPORT_AUX" },
    { 276,  "LINUX_SLL2" },
    { 277,  "SERCOS_MONITOR" },
    { 278,  "OPENVIZSLA" },
    { 279,  "EBHSCR" },
    { 280,  "VPP_DISPATCH" },
    { 281,  "DSA_TAG_BRCM" },
    { 282,  "DSA_TAG_BRCM_PREPEND" },
    { 283,  "IEEE802_15_4_TAP" },
    { 0, NULL }
};

static const value_string pseudoheader_bluetooth_direction_vals[] = {
    { 0,  "Sent" },
    { 1,  "Recv" },
    { 0, NULL }
};

static int
dissect_pcap_pktdata(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    gint         offset = 0;
    guint32     *link_type;
    tvbuff_t    *next_tvb;
    proto_item  *pseudoheader_item;
    proto_tree  *pseudoheader_tree = NULL;
    proto_item  *packet_item;
    struct eth_phdr eth;
    void        *phdr;

    DISSECTOR_ASSERT(data);

    link_type = (guint32 *) data;

    /*
     * We're passed a pointer to a LINKTYPE_ value.
     * Find the Wiretap encapsulation for that value.
     */
    pinfo->rec->rec_header.packet_header.pkt_encap = wtap_pcap_encap_to_wtap_encap(*link_type);

    /*
     * Do we know that type?
     */
    if (pinfo->rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_UNKNOWN) {
        /*
         * Nothing we know.
         * Just report that and give up.
         */
        packet_item = proto_tree_add_item(tree, hf_pcap_pktdata_undecoded_data, tvb, offset, tvb_reported_length(tvb), ENC_NA);
        expert_add_info_format(pinfo, packet_item,
                               &ei_pcap_pktdata_linktype_unknown,
                               "Link-layer header type %u is not supported",
                               *link_type);
        return tvb_captured_length(tvb);
    }

    /*
     * You can't just call an arbitrary subdissector based on a
     * WTAP_ENCAP_ value, because they may expect a particular
     * pseudo-header to be passed to them, and may not accept
     * a null pseudo-header pointer.
     *
     * First, check whether this WTAP_ENCAP_ value corresponds
     * to a link-layer header type where Wiretap generates a
     * pseudo-header from the bytes at the beginning of the
     * packet data.
     */
    if (wtap_encap_requires_phdr(pinfo->rec->rec_header.packet_header.pkt_encap)) {
        /*
         * It does.  Do we have code to do that?
         */
        switch (pinfo->rec->rec_header.packet_header.pkt_encap) {

        case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR:
            pseudoheader_item = proto_tree_add_item(tree, hf_pcap_pktdata_pseudoheader, tvb, offset, 4, ENC_NA);
            pseudoheader_tree = proto_item_add_subtree(pseudoheader_item, ett_pcap_pktdata_pseudoheader);
            proto_tree_add_item(pseudoheader_tree, hf_pcap_pktdata_pseudoheader_bluetooth_direction, tvb, offset, 4, ENC_BIG_ENDIAN);
            if (tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN) == 0)
                pinfo->p2p_dir = P2P_DIR_SENT;
            else if (tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN) == 1)
                pinfo->p2p_dir = P2P_DIR_RECV;
            else
                pinfo->p2p_dir = P2P_DIR_UNKNOWN;
            offset += 4;
            phdr = NULL;
            break;

        case WTAP_ENCAP_ATM_PDUS:
            /* TODO */
        case WTAP_ENCAP_IRDA:
            /* TODO */
        case WTAP_ENCAP_MTP2_WITH_PHDR:
            /* TODO no description for pseudoheader at http://www.tcpdump.org/linktypes.html */
        case WTAP_ENCAP_LINUX_LAPD:
            /* TODO */
        case WTAP_ENCAP_SITA:
            /* TODO */
        case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR:
            /* TODO */
	case WTAP_ENCAP_NFC_LLCP:
            /* TODO */
        case WTAP_ENCAP_PPP_WITH_PHDR:
            /* TODO */
        case WTAP_ENCAP_ERF:
            /* TODO no description for pseudoheader at http://www.tcpdump.org/linktypes.html */
        case WTAP_ENCAP_I2C:
            /* TODO */
        default:
            /*
             * No.  Give up.
             */
            packet_item = proto_tree_add_item(tree, hf_pcap_pktdata_undecoded_data, tvb, offset, tvb_reported_length(tvb), ENC_NA);
            expert_add_info_format(pinfo, packet_item,
                                   &ei_pcap_pktdata_cant_generate_phdr,
                                   "No pseudo-header can be generated for link-layer header type %u",
                                   *link_type);
            return tvb_captured_length(tvb);
        }
    } else {
        /*
         * These also require a pseudo-header, but it's not constructed
         * from packet data.
         */
        switch (pinfo->rec->rec_header.packet_header.pkt_encap) {

        case WTAP_ENCAP_ETHERNET:
            eth.fcs_len = -1;    /* Unknown whether we have an FCS */
            phdr = &eth;
            break;

        default:
            phdr = NULL;
            break;
        }
    }

    next_tvb = tvb_new_subset_remaining(tvb, offset);

    offset = dissector_try_uint_new(wtap_encap_table, pinfo->rec->rec_header.packet_header.pkt_encap, next_tvb, pinfo, tree, TRUE, phdr);

    return offset;
}

void
proto_register_pcap_pktdata(void)
{
    static hf_register_info hf[] = {
        { &hf_pcap_pktdata_pseudoheader,
            { "Pseudoheader",                              "pcap_pktdata.data.pseudoheader",
            FT_NONE, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_pcap_pktdata_pseudoheader_bluetooth_direction,
            { "Direction",                                 "pcap_pktdata.pseudoheader.bluetooth.direction",
            FT_UINT32, BASE_HEX, VALS(pseudoheader_bluetooth_direction_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_pcap_pktdata_undecoded_data,
            { "Undecoded data",                            "pcap_pktdata.undecoded_data",
            FT_NONE, BASE_NONE, NULL, 0x00,
            NULL, HFILL }
        },
    };

    static gint *ett[] = {
        &ett_pcap_pktdata_pseudoheader,
    };

    static ei_register_info ei[] = {
        { &ei_pcap_pktdata_linktype_unknown, { "pcap_pktdata.linktype_unknown", PI_UNDECODED, PI_NOTE, "That link-layer header type is not supported", EXPFILL }},
        { &ei_pcap_pktdata_cant_generate_phdr, { "pcap_pktdata.cant_generate_phdr", PI_UNDECODED, PI_NOTE, "No pseudo-header can be generated for that link-layer header type", EXPFILL }},
    };

    expert_module_t *expert_pcap_pktdata;

    proto_pcap_pktdata = proto_register_protocol("pcap/pcapng packet data", "pcap_pktdata", "pcap_pktdata");
    proto_register_field_array(proto_pcap_pktdata, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
    expert_pcap_pktdata = expert_register_protocol(proto_pcap_pktdata);
    expert_register_field_array(expert_pcap_pktdata, ei, array_length(ei));

    register_dissector("pcap_pktdata", dissect_pcap_pktdata, proto_pcap_pktdata);
}

void
proto_reg_handoff_pcap_pktdata(void)
{
    wtap_encap_table = find_dissector_table("wtap_encap");
}
