text: #include <net/ethernet.h>
  #include <netinet/in.h>
  #include <netinet/ip.h>
  #include <netinet/ip6.h>
  #include <netinet/tcp.h>
  #include <netinet/udp.h>
  #include <net/ethernet.h>
  // #include <netinet/sctp_header.h>
  #include <linux/if_ether.h>
  
  #include "protos.hpp"
  
  #define GTPU_MIN_HDR_LEN 8
  
  typedef struct sctphdr {
    uint16_t source;
    uint16_t dest;
    uint32_t vtag;
    uint32_t checksum;
  } sctp_sctphdr_t;
  
  typedef struct sctp_chunkhdr {
    uint8_t type;
    uint8_t flags;
    uint16_t length;
  } sctp_chunkhdr_t;
  
  typedef struct sctp_datahdr {
    uint32_t tsn;
    uint16_t stream;
    uint16_t ssn;
    uint32_t ppid;
    uint8_t  payload[0];
  } sctp_datahdr_t;
  
  typedef struct sctp_data_chunk {
    sctp_chunkhdr_t chunk_hdr;
    sctp_datahdr_t  data_hdr;
  } sctp_data_chunk_t;
  
  int process_sip(uint8_t *byte, int payload_size);
  
  int process_sctp(uint8_t *&payload, int& size) {
    struct sctphdr *sctph = (struct sctphdr *) payload;
    if ((ntohs(sctph->source) == 5060 ) || (ntohs(sctph->dest) == 5060 )) {
      // sip
      payload += sizeof(struct sctphdr) + sizeof(sctp_data_chunk);
      size -= sizeof(struct sctphdr) + sizeof(sctp_data_chunk);
      return process_sip(payload, size);
    }
    return PROTO_SCTP;
  }
  
  
  int process_gtpu(uint8_t **data, int *datalen) {
      int hdr_len;
  
      int gtp_msg_type = *((uint8_t*) (*data + 1));  //GTP: Message type 0xff is GTP U
      if (0xff != gtp_msg_type) {
        // printf("%ld:error msg type\n",g_stats.total_pkts_);
        // thread_data->stats.err_pkts_++;
        return PROTO_ERROR;
      }
  
      if (*datalen < GTPU_MIN_HDR_LEN) {
        // printf("%ld:<min hdr len\n",g_stats.total_pkts_);
        // thread_data->stats.err_pkts_++;
        return PROTO_ERROR;
      }
  
      char cNextExtHdr = *((uint8_t*) (*data)) & 0x04;   // E
      char cSeqNum = *((uint8_t*) (*data)) & 0x02;   // S
      char cNPDUNum = *((uint8_t*) (*data)) & 0x01;   // PN
  
      if (0 != cNextExtHdr || 0 != cSeqNum || 0 != cNPDUNum) {
        hdr_len = 4 + 8;
      } else {
        hdr_len = 8;
      }
      if (hdr_len > *datalen)
        hdr_len = *datalen;
  
      // move payload to next protocol layer
      (*data) += hdr_len;
      *datalen -= hdr_len;
  
      //g_stats.curr_pkt_size_ =  *datalen;
  
      if (*(*data) >> 4 == 4) {
        struct iphdr *iph = (struct iphdr *) (*data);
        (void)iph;
        // thread_data->stats.curr_pkt_size_ = ntohs(iph->tot_len);
        // navl_classify(g_navl, NAVL_ENCAP_IP, *data, ntohs(iph->tot_len), NULL, 0, classify_callback, thread_data);
        // thread_data->stats.processed_pkts_++;      
      } else if (*(*data) >> 4 == 6) {
        struct ip6_hdr *ip6h = (struct ip6_hdr *) (*data);
        (void)ip6h;
        // thread_data->stats.curr_pkt_size_ = ntohs(ip6h->ip6_plen);
        // navl_classify(g_navl, NAVL_ENCAP_IP6, *data, ntohs(ip6h->ip6_plen), NULL, 0, classify_callback, thread_data);
        // thread_data->stats.processed_pkts_++;
      } else {
        // printf("%ld:neither ip4 or ip6\n",g_stats.total_pkts_);
        // thread_data->stats.err_pkts_++;
        return PROTO_UNKNOWN;
      }
  
      return PROTO_GTPU;
  }
  
  int process_sip(uint8_t *byte, int payload_size) {
    return PROTO_SIP;
  }
  
  int process_gtpc(uint8_t **byte, int& payload_size) {
    return PROTO_GTPC;
  }
  
  int process_packet(uint8_t *&payload, int &payload_size) {
      uint16_t proto = 0;
      // 1: ethernet
      struct ethhdr *eh = (struct ethhdr *) payload;
      payload += sizeof(struct ethhdr);
      payload_size -= sizeof(struct ethhdr);
  
      uint32_t eType = ntohs(eh->h_proto);
  
      do {
        if (eType == ETHERTYPE_VLAN) {
          eType = ntohs(*((u_int16_t*) (payload + 2)));  // VLAN_proto
          payload += 4;  // size of VLAN header
          payload_size -= 4;
        } else {
          break;
        }
      } while (true);
  
      if (eType != ETHERTYPE_IP && eType != ETHERTYPE_IPV6)
        return PROTO_UNKNOWN;
  
      // ip layer
      if (eType == ETHERTYPE_IP) {
        struct iphdr *iph = (struct iphdr *) payload;
        proto = iph->protocol;
        payload += iph->ihl * 4;  // skip IP header
        payload_size -= iph->ihl * 4;
      } else if (eType == ETHERTYPE_IPV6) {
        struct ip6_hdr *ip6h = (struct ip6_hdr *) payload;
        payload += 40;  // skip IPv6 header
        payload_size -= 40;
        proto = ip6h->ip6_nxt;  // ip6_un1.ip6_un1_nxt;
      } 
  
      // transport layer
      if (proto == 6 && (size_t) payload_size >= sizeof(struct tcphdr)) {
        struct tcphdr *tcph = (struct tcphdr *) payload;
        payload += sizeof(struct tcphdr);
        payload_size -= sizeof(struct tcphdr);
        if ((ntohs(tcph->source) == 5060) || (ntohs(tcph->dest == 5060))) {
          return process_sip(payload, payload_size);
        } else {
          return PROTO_TCP;
        }
      } else if (proto == 17 && (size_t) payload_size >= sizeof(struct udphdr)) {
        struct udphdr *udph = (struct udphdr*) payload;
        payload += sizeof(struct udphdr);
        payload_size -= sizeof(struct udphdr);
        if ((ntohs(udph->source) == 2123) || (ntohs(udph->dest) == 2123)) {       
          return process_gtpc(&payload, payload_size);
        } else if ((ntohs(udph->source) == 2152) || (ntohs(udph->dest) == 2152)) {       
          return process_gtpu(&payload, &payload_size);
        } else if ((ntohs(udph->source) == 5060) || (ntohs(udph->dest) == 5060)) {
          return process_sip(payload, payload_size);
        } else {
          return PROTO_UDP;
        }
      } else if (proto == 132 && (size_t) payload_size >= sizeof(struct sctphdr)) {
        return process_sctp(payload, payload_size);
      }
      return PROTO_IP;
  }
  