summaryrefslogtreecommitdiff
path: root/common/src/packet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/packet.cpp')
-rw-r--r--common/src/packet.cpp1475
1 files changed, 1475 insertions, 0 deletions
diff --git a/common/src/packet.cpp b/common/src/packet.cpp
new file mode 100644
index 0000000..b96edcc
--- /dev/null
+++ b/common/src/packet.cpp
@@ -0,0 +1,1475 @@
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#define __FAVOR_BSD 1
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ether.h>
+#include <linux/ppp_defs.h>
+
+#include "uthash.h"
+#include "packet.h"
+
+#define likely(expr) __builtin_expect((expr), 1)
+#define unlikely(expr) __builtin_expect((expr), 0)
+
+#define PACKET_LOG_DATA_INSUFFICIENCY(type) \
+ { \
+ PACKET_LOG_ERROR("layer: %s, data insufficiency", \
+ layer_type_tostring((type))); \
+ }
+
+#define PACKET_LOG_UNSUPPORT_PROTO(tag, next_proto) \
+ { \
+ PACKET_LOG_ERROR("%s: unsupport next proto %d", \
+ (tag), (next_proto)); \
+ }
+
+#define PACKET_LOG_UNSUPPORT_ETHPROTO(tag, next_proto) \
+ { \
+ PACKET_LOG_ERROR("%s: unsupport next proto %d: %s", \
+ (tag), (next_proto), ethproto_tostring(next_proto)); \
+ }
+
+#define PACKET_LOG_UNSUPPORT_IPPROTO(tag, next_proto) \
+ { \
+ PACKET_LOG_ERROR("%s: unsupport next proto %d: %s", \
+ (tag), (next_proto), ipproto_tostring(next_proto)); \
+ }
+
+/******************************************************************************
+ * Static API
+ ******************************************************************************/
+
+static const char *ethproto_tostring(uint16_t proto);
+static const char *ipproto_tostring(uint16_t proto);
+static inline const char *ldbc_method_tostring(enum ldbc_method method);
+static inline const char *layer_type_tostring(enum layer_type type);
+
+static inline void set_tuple2(const char *data, enum layer_type type, struct tuple2 *tuple);
+static inline void set_tuple4(const char *data, enum layer_type type, struct tuple4 *tuple);
+static inline void set_tuple6(const char *data, enum layer_type type, struct tuple6 *tuple, uint64_t zone_id);
+
+static inline struct layer_record *get_free_layer(struct packet *handler);
+
+static inline uint16_t get_gtp_hdr_len(const char *data, uint16_t len);
+static inline uint16_t get_gre_hdr_len(const char *data, uint16_t len);
+
+// 数据链路层
+static inline const char *parse_ether(struct packet *handler, const char *data, uint16_t len);
+static inline const char *parse_ppp(struct packet *handler, const char *data, uint16_t len);
+// 数据链路层 -- 隧道
+static inline const char *parse_vlan(struct packet *handler, const char *data, uint16_t len);
+static inline const char *parse_pppoe_ses(struct packet *handler, const char *data, uint16_t len);
+static inline const char *parse_mpls(struct packet *handler, const char *data, uint16_t len);
+// 网络层
+static inline const char *parse_ipv4(struct packet *handler, const char *data, uint16_t len);
+static inline const char *parse_ipv6(struct packet *handler, const char *data, uint16_t len);
+// 网络层 -- 隧道
+static inline const char *parse_gre(struct packet *handler, const char *data, uint16_t len);
+// 传输层
+static inline const char *parse_udp(struct packet *handler, const char *data, uint16_t len);
+static inline const char *parse_tcp(struct packet *handler, const char *data, uint16_t len);
+// 传输层 -- 隧道
+static inline const char *parse_vxlan(struct packet *handler, const char *data, uint16_t len);
+static inline const char *parse_gtpv1_u(struct packet *handler, const char *data, uint16_t len);
+// L3/L4
+static inline const char *parse_l3(struct packet *handler, uint16_t next_proto, const char *data, uint16_t len);
+static inline const char *parse_l4(struct packet *handler, uint8_t next_proto, const char *data, uint16_t len);
+
+/******************************************************************************
+ * Private API -- Utils
+ ******************************************************************************/
+
+static const char *ethproto_tostring(uint16_t proto)
+{
+ switch (proto)
+ {
+ case ETH_P_LOOP:
+ return "LOOP";
+ case ETH_P_PUP:
+ return "PUP";
+ case ETH_P_PUPAT:
+ return "PUPAT";
+ case ETH_P_IP:
+ return "IP";
+ case ETH_P_X25:
+ return "X25";
+ case ETH_P_ARP:
+ return "ARP";
+ case ETH_P_BPQ:
+ return "BPQ";
+ case ETH_P_IEEEPUP:
+ return "IEEEPUP";
+ case ETH_P_IEEEPUPAT:
+ return "IEEEPUPAT";
+ case ETH_P_DEC:
+ return "DEC";
+ case ETH_P_DNA_DL:
+ return "DNA_DL";
+ case ETH_P_DNA_RC:
+ return "DNA_RC";
+ case ETH_P_DNA_RT:
+ return "DNA_RT";
+ case ETH_P_LAT:
+ return "LAT";
+ case ETH_P_DIAG:
+ return "DIAG";
+ case ETH_P_CUST:
+ return "CUST";
+ case ETH_P_SCA:
+ return "SCA";
+ case ETH_P_TEB:
+ return "TEB";
+ case ETH_P_RARP:
+ return "RARP";
+ case ETH_P_ATALK:
+ return "ATALK";
+ case ETH_P_AARP:
+ return "AARP";
+ case ETH_P_8021Q:
+ return "8021Q";
+ case ETH_P_IPX:
+ return "IPX";
+ case ETH_P_IPV6:
+ return "IPV6";
+ case ETH_P_PAUSE:
+ return "PAUSE";
+ case ETH_P_SLOW:
+ return "SLOW";
+ case ETH_P_WCCP:
+ return "WCCP";
+ case ETH_P_PPP_DISC:
+ return "PPP_DISC";
+ case ETH_P_PPP_SES:
+ return "PPP_SES";
+ case ETH_P_MPLS_UC:
+ return "MPLS_UC";
+ case ETH_P_MPLS_MC:
+ return "MPLS_MC";
+ case ETH_P_ATMMPOA:
+ return "ATMMPOA";
+ case ETH_P_LINK_CTL:
+ return "LINK_CTL";
+ case ETH_P_ATMFATE:
+ return "ATMFATE";
+ case ETH_P_PAE:
+ return "PAE";
+ case ETH_P_AOE:
+ return "AOE";
+ case ETH_P_8021AD:
+ return "8021AD";
+ case ETH_P_802_EX1:
+ return "802_EX1";
+ case ETH_P_TIPC:
+ return "TIPC";
+ case ETH_P_8021AH:
+ return "8021AH";
+ case ETH_P_1588:
+ return "1588";
+ case ETH_P_FCOE:
+ return "FCOE";
+ case ETH_P_TDLS:
+ return "TDLS";
+ case ETH_P_FIP:
+ return "FIP";
+ case ETH_P_QINQ1:
+ return "QINQ1";
+ case ETH_P_QINQ2:
+ return "QINQ2";
+ case ETH_P_QINQ3:
+ return "QINQ3";
+ case ETH_P_EDSA:
+ return "EDSA";
+ case ETH_P_AF_IUCV:
+ return "AF_IUCV";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static const char *ipproto_tostring(uint16_t proto)
+{
+ switch (proto)
+ {
+ case IPPROTO_IP:
+ return "IP";
+ case IPPROTO_ICMP:
+ return "ICMP";
+ case IPPROTO_IGMP:
+ return "IGMP";
+ case IPPROTO_IPIP:
+ return "IPIP";
+ case IPPROTO_TCP:
+ return "TCP";
+ case IPPROTO_EGP:
+ return "EGP";
+ case IPPROTO_PUP:
+ return "PUP";
+ case IPPROTO_UDP:
+ return "UDP";
+ case IPPROTO_IDP:
+ return "IDP";
+ case IPPROTO_TP:
+ return "TP";
+ case IPPROTO_DCCP:
+ return "DCCP";
+ case IPPROTO_IPV6:
+ return "IPV6";
+ case IPPROTO_ROUTING:
+ return "ROUTING";
+ case IPPROTO_FRAGMENT:
+ return "FRAGMENT";
+ case IPPROTO_RSVP:
+ return "RSVP";
+ case IPPROTO_GRE:
+ return "GRE";
+ case IPPROTO_ESP:
+ return "ESP";
+ case IPPROTO_AH:
+ return "AH";
+ case IPPROTO_ICMPV6:
+ return "ICMPV6";
+ case IPPROTO_NONE:
+ return "NONE";
+ case IPPROTO_DSTOPTS:
+ return "DSTOPTS";
+ case IPPROTO_MTP:
+ return "MTP";
+ case IPPROTO_ENCAP:
+ return "ENCAP";
+ case IPPROTO_PIM:
+ return "PIM";
+ case IPPROTO_COMP:
+ return "COMP";
+ case IPPROTO_SCTP:
+ return "SCTP";
+ case IPPROTO_UDPLITE:
+ return "UDPLITE";
+ case IPPROTO_RAW:
+ return "RAW";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline const char *ldbc_method_tostring(enum ldbc_method method)
+{
+ switch (method)
+ {
+ case LDBC_METHOD_HASH_INT_IP:
+ return "outter_internal_ip";
+ case LDBC_METHOD_HASH_EXT_IP:
+ return "outter_external_ip";
+ case LDBC_METHOD_HASH_INT_IP_AND_EXT_IP:
+ return "outter_internal_ip_and_external_ip";
+ case LDBC_METHOD_HASH_INNERMOST_INT_IP:
+ return "inner_internal_ip";
+ case LDBC_METHOD_HASH_INNERMOST_EXT_IP:
+ return "inner_external_ip";
+ default:
+ return "unknown";
+ }
+}
+
+static inline const char *layer_type_tostring(enum layer_type type)
+{
+ switch (type)
+ {
+ case LAYER_TYPE_ETHER:
+ return "ETH";
+ case LAYER_TYPE_PPP:
+ return "PPP";
+ case LAYER_TYPE_HDLC:
+ return "HDLC";
+ case LAYER_TYPE_VLAN:
+ return "VLAN";
+ case LAYER_TYPE_PPPOE:
+ return "PPPOE";
+ case LAYER_TYPE_MPLS:
+ return "MPLS";
+ case LAYER_TYPE_IPV4:
+ return "IPV4";
+ case LAYER_TYPE_IPV6:
+ return "IPV6";
+ case LAYER_TYPE_GRE:
+ return "GRE";
+ case LAYER_TYPE_UDP:
+ return "UDP";
+ case LAYER_TYPE_TCP:
+ return "TCP";
+ case LAYER_TYPE_VXLAN:
+ return "VXLAN";
+ case LAYER_TYPE_GTPV1_U:
+ return "GTPV1";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline void set_tuple2(const char *data, enum layer_type type, struct tuple2 *tuple)
+{
+ const struct ip *ipv4 = NULL;
+ const struct ip6_hdr *ipv6 = NULL;
+
+ switch (type)
+ {
+ case LAYER_TYPE_IPV4:
+ ipv4 = (const struct ip *)data;
+ tuple->ip_type = IP_TYPE_V4;
+ tuple->src_addr.v4.s_addr = ipv4->ip_src.s_addr;
+ tuple->dst_addr.v4.s_addr = ipv4->ip_dst.s_addr;
+ break;
+ case LAYER_TYPE_IPV6:
+ ipv6 = (const struct ip6_hdr *)data;
+ tuple->ip_type = IP_TYPE_V6;
+ tuple->src_addr.v6 = ipv6->ip6_src;
+ tuple->dst_addr.v6 = ipv6->ip6_dst;
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void set_tuple4(const char *data, enum layer_type type, struct tuple4 *tuple)
+{
+ const struct ip *ipv4 = NULL;
+ const struct ip6_hdr *ipv6 = NULL;
+ const struct tcphdr *tcp = NULL;
+ const struct udphdr *udp = NULL;
+
+ switch (type)
+ {
+ case LAYER_TYPE_TCP:
+ tcp = (const struct tcphdr *)data;
+ tuple->src_port = tcp->th_sport;
+ tuple->dst_port = tcp->th_dport;
+ break;
+ case LAYER_TYPE_UDP:
+ udp = (const struct udphdr *)data;
+ tuple->src_port = udp->uh_sport;
+ tuple->dst_port = udp->uh_dport;
+ break;
+ case LAYER_TYPE_IPV4:
+ ipv4 = (const struct ip *)data;
+ tuple->ip_type = IP_TYPE_V4;
+ tuple->src_addr.v4.s_addr = ipv4->ip_src.s_addr;
+ tuple->dst_addr.v4.s_addr = ipv4->ip_dst.s_addr;
+ break;
+ case LAYER_TYPE_IPV6:
+ ipv6 = (const struct ip6_hdr *)data;
+ tuple->ip_type = IP_TYPE_V6;
+ tuple->src_addr.v6 = ipv6->ip6_src;
+ tuple->dst_addr.v6 = ipv6->ip6_dst;
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void set_tuple6(const char *data, enum layer_type type, struct tuple6 *tuple, uint64_t zone_id)
+{
+ const struct ip *ipv4 = NULL;
+ const struct ip6_hdr *ipv6 = NULL;
+ const struct tcphdr *tcp = NULL;
+ const struct udphdr *udp = NULL;
+
+ tuple->security_zone = zone_id;
+
+ switch (type)
+ {
+ case LAYER_TYPE_TCP:
+ tcp = (const struct tcphdr *)data;
+ tuple->ip_proto = IPPROTO_TCP;
+ tuple->src_port = tcp->th_sport;
+ tuple->dst_port = tcp->th_dport;
+ break;
+ case LAYER_TYPE_UDP:
+ udp = (const struct udphdr *)data;
+ tuple->ip_proto = IPPROTO_UDP;
+ tuple->src_port = udp->uh_sport;
+ tuple->dst_port = udp->uh_dport;
+ break;
+ case LAYER_TYPE_IPV4:
+ ipv4 = (const struct ip *)data;
+ tuple->ip_type = IP_TYPE_V4;
+ tuple->src_addr.v4.s_addr = ipv4->ip_src.s_addr;
+ tuple->dst_addr.v4.s_addr = ipv4->ip_dst.s_addr;
+ break;
+ case LAYER_TYPE_IPV6:
+ ipv6 = (const struct ip6_hdr *)data;
+ tuple->ip_type = IP_TYPE_V6;
+ tuple->src_addr.v6 = ipv6->ip6_src;
+ tuple->dst_addr.v6 = ipv6->ip6_dst;
+ break;
+ default:
+ break;
+ }
+}
+
+static inline struct layer_record *get_free_layer(struct packet *handler)
+{
+ if (handler->layers_used >= handler->layers_size)
+ {
+ return NULL;
+ }
+
+ return &handler->layers[handler->layers_used];
+}
+
+#define SET_LAYER(_handler, _layer, _type, _hdr_len, _data, _len) \
+ { \
+ (_layer)->type = (_type); \
+ (_layer)->hdr_offset = (_handler)->data_len - (_len); \
+ (_layer)->hdr_ptr = (_data); \
+ (_layer)->hdr_len = (_hdr_len); \
+ (_layer)->pld_ptr = (_data) + (_hdr_len); \
+ (_layer)->pld_len = (_len) - (_hdr_len); \
+ (_handler)->layers_used++; \
+ PACKET_LOG_DEBUG("layer[%d/%d]: %s, hdr_offset: %d, hdr_ptr: %p, hdr_len: %d, pld_ptr: %p, pld_len: %d", \
+ (_handler)->layers_used - 1, (_handler)->layers_size, layer_type_tostring((_type)), \
+ (_layer)->hdr_offset, (_layer)->hdr_ptr, (_layer)->hdr_len, (_layer)->pld_ptr, (_layer)->pld_len); \
+ }
+
+/******************************************************************************
+ * Private API -- Parses
+ ******************************************************************************/
+
+static inline uint16_t get_gtp_hdr_len(const char *data, uint16_t len)
+{
+#define GTP_HDR_VER (0xE0)
+#define GTP_HDR_FLAG_N_PDU (0x01)
+#define GTP_HDR_FLAG_SEQ_NUM (0x02)
+#define GTP_HDR_FLAG_EXT_HDR (0x04)
+
+ struct gtp_hdr
+ {
+ uint8_t flags;
+ uint8_t msg_type;
+ uint16_t msg_len;
+ uint32_t teid;
+ } __attribute__((__packed__));
+
+ struct gtp_opt
+ {
+ uint16_t seq_num;
+ uint8_t npdu;
+ uint8_t next_ext_hdr;
+ } __attribute__((__packed__));
+
+ uint16_t hdr_offset = 0;
+ if (len < sizeof(struct gtp_hdr))
+ {
+ return 0;
+ }
+ const struct gtp_hdr *gtp = (const struct gtp_hdr *)data;
+ hdr_offset += sizeof(struct gtp_hdr); // skip gre hdr
+
+ // GTPv0 Not Supported
+ if (((gtp->flags & GTP_HDR_VER) >> 5) != 1)
+ {
+ return 0;
+ }
+
+ if (gtp->flags & (GTP_HDR_FLAG_SEQ_NUM | GTP_HDR_FLAG_N_PDU | GTP_HDR_FLAG_EXT_HDR))
+ {
+ if (hdr_offset + sizeof(struct gtp_opt) > len)
+ {
+ return 0;
+ }
+ struct gtp_opt *opt_hdr = (struct gtp_opt *)((char *)data + hdr_offset);
+ uint8_t next_ext_hdr = opt_hdr->next_ext_hdr;
+ hdr_offset += sizeof(struct gtp_opt); // skip gre opt
+
+ while (next_ext_hdr)
+ {
+ if (hdr_offset + 1 > len)
+ {
+ return 0;
+ }
+ uint8_t length = *((char *)data + hdr_offset) * 4 - 2;
+ hdr_offset += 1; // skip length field
+
+ if (hdr_offset + length + 1 > len)
+ {
+ return 0;
+ }
+ hdr_offset += length; // skip data field
+ next_ext_hdr = *((char *)data + hdr_offset);
+ hdr_offset += 1; // skip next ext hdr field
+ }
+ }
+
+ return hdr_offset;
+}
+
+static inline uint16_t get_gre_hdr_len(const char *data, uint16_t len)
+{
+ /*
+ * GRE Header Format (Version 0)
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |C|R|K|S|s|Recur| Flags | Ver | Protocol Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Checksum (optional) | Offset (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Routing (optional)
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Address Family | SRE Offset | SRE Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Routing Information ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * https://datatracker.ietf.org/doc/html/rfc1701
+ * https://datatracker.ietf.org/doc/html/rfc2890
+ */
+
+ /*
+ * Enhanced GRE header (Version 1)
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |C|R|K|S|s|Recur|A| Flags | Ver | Protocol Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key (HW) Payload Length | Key (LW) Call ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number (Optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Acknowledgment Number (Optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * https://datatracker.ietf.org/doc/html/rfc2637
+ */
+
+/* bit positions for flags in header */
+#define GRE_CHECKSUM 0x8000
+#define GRE_ROUTING 0x4000
+#define GRE_KEY 0x2000
+#define GRE_SEQUENCE 0x1000
+#define GRE_STRICTSOURCE 0x0800
+#define GRE_RECURSION 0x0700
+#define GRE_ACK 0x0080 /* only in special PPTPized GRE header */
+#define GRE_RESERVED_PPP 0x0078 /* only in special PPTPized GRE header */
+#define GRE_RESERVED 0x00F8
+#define GRE_VERSION 0x0007
+
+ if (len < 4)
+ {
+ return 0;
+ }
+
+ struct SRE
+ {
+ uint16_t address_family;
+ uint8_t sre_offset;
+ uint8_t sre_length;
+ } __attribute__((__packed__));
+
+ uint16_t sre_size = sizeof(struct SRE);
+ const uint16_t *gre = (const uint16_t *)data;
+ uint16_t flags = ntohs(gre[0]);
+ uint16_t version = flags & GRE_VERSION;
+ uint16_t hdr_offset = 0;
+
+ if (version == 0)
+ {
+ hdr_offset = 4;
+
+ if ((flags & GRE_CHECKSUM) || (flags & GRE_ROUTING))
+ {
+ hdr_offset += 4;
+ }
+ if (flags & GRE_KEY)
+ {
+ hdr_offset += 4;
+ }
+ if (flags & GRE_SEQUENCE)
+ {
+ hdr_offset += 4;
+ }
+ if (flags & GRE_ROUTING)
+ {
+ while (hdr_offset + sre_size <= len)
+ {
+ struct SRE *sre = (struct SRE *)((char *)data + hdr_offset);
+ if (sre->sre_length == 0)
+ {
+ hdr_offset += sre_size;
+ break;
+ }
+ else
+ {
+ hdr_offset += sre_size + sre->sre_length;
+ }
+ }
+ }
+ }
+
+ if (version == 1)
+ {
+ hdr_offset = 8;
+ if (flags & GRE_SEQUENCE)
+ {
+ hdr_offset += 4;
+ }
+ if (flags & GRE_ACK)
+ {
+ hdr_offset += 4;
+ }
+ }
+
+ if (hdr_offset > len)
+ {
+ return 0;
+ }
+
+ return hdr_offset;
+}
+
+static inline const char *parse_ether(struct packet *handler, const char *data, uint16_t len)
+{
+ if (unlikely(len < sizeof(struct ethhdr)))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_ETHER);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ uint16_t next_proto = ntohs(((struct ethhdr *)data)->h_proto);
+ SET_LAYER(handler, layer, LAYER_TYPE_ETHER, sizeof(struct ethhdr), data, len);
+
+ // TESTED
+ return parse_l3(handler, next_proto, layer->pld_ptr, layer->pld_len);
+}
+
+static inline const char *parse_ppp(struct packet *handler, const char *data, uint16_t len)
+{
+ if (unlikely(len < 4))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_PPP);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ uint16_t next_proto = ntohs(*((uint16_t *)data + 1));
+ SET_LAYER(handler, layer, LAYER_TYPE_PPP, 4, data, len);
+
+ switch (next_proto)
+ {
+ // TESTED
+ case PPP_IP:
+ return parse_ipv4(handler, layer->pld_ptr, layer->pld_len);
+ case PPP_IPV6:
+ return parse_ipv6(handler, layer->pld_ptr, layer->pld_len);
+ default:
+ PACKET_LOG_UNSUPPORT_PROTO("ppp", next_proto);
+ return layer->pld_ptr;
+ }
+}
+
+static inline const char *parse_vlan(struct packet *handler, const char *data, uint16_t len)
+{
+ struct vlan_hdr
+ {
+ uint16_t vlan_cfi;
+ uint16_t protocol;
+ } __attribute__((__packed__));
+
+ if (unlikely(len < sizeof(struct vlan_hdr)))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_VLAN);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ uint16_t next_proto = ntohs(((struct vlan_hdr *)data)->protocol);
+ SET_LAYER(handler, layer, LAYER_TYPE_VLAN, sizeof(struct vlan_hdr), data, len);
+
+ // TESTED
+ return parse_l3(handler, next_proto, layer->pld_ptr, layer->pld_len);
+}
+
+static inline const char *parse_pppoe_ses(struct packet *handler, const char *data, uint16_t len)
+{
+#define PPPOE_TYPE_IPV4 0x2100
+#define PPPOE_TYPE_IPV6 0x5700
+
+ if (unlikely(len < 8))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_PPPOE);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ uint16_t next_proto = *((uint16_t *)data + 3);
+ SET_LAYER(handler, layer, LAYER_TYPE_PPPOE, 8, data, len);
+
+ switch (next_proto)
+ {
+ // TESTED
+ case PPPOE_TYPE_IPV4:
+ return parse_ipv4(handler, layer->pld_ptr, layer->pld_len);
+ case PPPOE_TYPE_IPV6:
+ return parse_ipv6(handler, layer->pld_ptr, layer->pld_len);
+ default:
+ PACKET_LOG_UNSUPPORT_PROTO("pppoe", next_proto);
+ return layer->pld_ptr;
+ }
+}
+
+static inline const char *parse_mpls(struct packet *handler, const char *data, uint16_t len)
+{
+ /*
+ * MPLS Format
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Label | Exp |S| TTL |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Label : Label Value 20 bits
+ * Exp : Experimental Use 3 bits
+ * S : Bottom of Stack 1 bit
+ * TTL : Time to Live 8 bits
+ */
+
+#define MPLS_LABEL_MASK (0xFFFFF000)
+#define MPLS_EXP_MASK (0x00000E00)
+#define MPLS_BLS_MASK (0x00000100)
+#define MPLS_TTL_MASK (0x000000FF)
+
+ enum mpls_next_proto
+ {
+ MPLS_NEXT_PROTO_ETHER = 0x0,
+ MPLS_NEXT_PROTO_MPLS = 0x1,
+ MPLS_NEXT_PROTO_IPV4 = 0x4,
+ MPLS_NEXT_PROTO_IPV6 = 0x6,
+ };
+
+ // 4 + 1
+ if (unlikely(len < 5))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_MPLS);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+
+ uint16_t hdr_len = 4;
+ uint32_t *hdr = (uint32_t *)data;
+ unsigned int mpls_bls = (ntohl(*hdr) & MPLS_BLS_MASK) >> 8;
+ enum mpls_next_proto next_proto;
+ if (mpls_bls == 1)
+ {
+ switch ((((uint8_t *)(data + 4))[0]) >> 4)
+ {
+ case 0:
+ /*
+ * PW Ethernet Control Word
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0| Reserved | Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Reference: https://tools.ietf.org/html/rfc4448
+ */
+ hdr_len += 4; // skip PW Ethernet Control Word
+ next_proto = MPLS_NEXT_PROTO_ETHER;
+ break;
+ case 4:
+ next_proto = MPLS_NEXT_PROTO_IPV4;
+ break;
+ case 6:
+ next_proto = MPLS_NEXT_PROTO_IPV6;
+ break;
+ default:
+ next_proto = MPLS_NEXT_PROTO_ETHER;
+ break;
+ }
+ }
+ else
+ {
+ next_proto = MPLS_NEXT_PROTO_MPLS;
+ }
+ SET_LAYER(handler, layer, LAYER_TYPE_MPLS, hdr_len, data, len);
+
+ switch (next_proto)
+ {
+ // TESTED
+ case MPLS_NEXT_PROTO_IPV4:
+ return parse_ipv4(handler, layer->pld_ptr, layer->pld_len);
+ case MPLS_NEXT_PROTO_IPV6:
+ return parse_ipv6(handler, layer->pld_ptr, layer->pld_len);
+ // TESTED
+ case MPLS_NEXT_PROTO_ETHER:
+ return parse_ether(handler, layer->pld_ptr, layer->pld_len);
+ // TESTED
+ case MPLS_NEXT_PROTO_MPLS:
+ return parse_mpls(handler, layer->pld_ptr, layer->pld_len);
+ default:
+ // unreachable
+ return layer->pld_ptr;
+ }
+}
+
+static inline const char *parse_ipv4(struct packet *handler, const char *data, uint16_t len)
+{
+ if (unlikely(len < sizeof(struct ip)))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_IPV4);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ struct ip *hdr = (struct ip *)data;
+ uint8_t next_proto = hdr->ip_p;
+ uint16_t hdr_len = (hdr->ip_hl & 0xf) * 4u;
+ SET_LAYER(handler, layer, LAYER_TYPE_IPV4, hdr_len, data, len);
+
+ // ip fragmented
+ if ((ntohs(hdr->ip_off) & IP_MF) || (ntohs(hdr->ip_off) & IP_OFFMASK))
+ {
+ PACKET_LOG_DEBUG("ip is fragmented");
+ return layer->pld_ptr;
+ }
+
+ // TESTED
+ return parse_l4(handler, next_proto, layer->pld_ptr, layer->pld_len);
+}
+
+static inline const char *parse_ipv6(struct packet *handler, const char *data, uint16_t len)
+{
+ if (unlikely(len < sizeof(struct ip6_hdr)))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_IPV6);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ uint8_t next_proto = ((struct ip6_hdr *)data)->ip6_nxt;
+ SET_LAYER(handler, layer, LAYER_TYPE_IPV6, sizeof(struct ip6_hdr), data, len);
+
+ // TESTED
+ return parse_l4(handler, next_proto, layer->pld_ptr, layer->pld_len);
+}
+
+static inline const char *parse_gre(struct packet *handler, const char *data, uint16_t len)
+{
+#define GRE_PRO_IPV4 (0x0800)
+#define GRE_PRO_IPV6 (0x86DD)
+#define GRE_PRO_ARP (0x0806)
+#define GRE_PRO_PPP (0x880B)
+
+ uint16_t hdr_len = get_gre_hdr_len(data, len);
+ if (unlikely(hdr_len == 0))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_GRE);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ uint16_t next_proto = ntohs(*((uint16_t *)data + 1));
+ SET_LAYER(handler, layer, LAYER_TYPE_GRE, hdr_len, data, len);
+
+ switch (next_proto)
+ {
+ case GRE_PRO_IPV4:
+ return parse_ipv4(handler, layer->pld_ptr, layer->pld_len);
+ case GRE_PRO_IPV6:
+ return parse_ipv6(handler, layer->pld_ptr, layer->pld_len);
+ // TESTED
+ case GRE_PRO_PPP:
+ return parse_ppp(handler, layer->pld_ptr, layer->pld_len);
+ default:
+ PACKET_LOG_UNSUPPORT_PROTO("gre", next_proto);
+ return layer->pld_ptr;
+ }
+}
+
+static inline const char *parse_udp(struct packet *handler, const char *data, uint16_t len)
+{
+ if (unlikely(len < sizeof(struct udphdr)))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_UDP);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ struct udphdr *hdr = (struct udphdr *)data;
+ SET_LAYER(handler, layer, LAYER_TYPE_UDP, sizeof(struct udphdr), data, len);
+
+ switch (ntohs(hdr->uh_dport))
+ {
+ // TESTED
+ // VXLAN_DPORT
+ case 4789:
+ return parse_vxlan(handler, layer->pld_ptr, layer->pld_len);
+ // TESTED
+ // GTP1U_PORT
+ case 2152:
+ return parse_gtpv1_u(handler, layer->pld_ptr, layer->pld_len);
+ default:
+ return layer->pld_ptr;
+ }
+}
+
+static inline const char *parse_tcp(struct packet *handler, const char *data, uint16_t len)
+{
+ if (unlikely(len < sizeof(struct tcphdr)))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_TCP);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ uint16_t hdr_len = ((struct tcphdr *)data)->th_off << 2;
+ SET_LAYER(handler, layer, LAYER_TYPE_TCP, hdr_len, data, len);
+
+ return layer->pld_ptr;
+}
+
+static inline const char *parse_vxlan(struct packet *handler, const char *data, uint16_t len)
+{
+ struct vxlan_hdr
+ {
+ uint8_t flags[2];
+ uint16_t gdp;
+ uint8_t vni[3];
+ uint8_t reserved;
+ } __attribute__((__packed__));
+
+ if (unlikely(len < sizeof(struct vxlan_hdr)))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_VXLAN);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ SET_LAYER(handler, layer, LAYER_TYPE_VXLAN, sizeof(struct vxlan_hdr), data, len);
+
+ // TESTED
+ return parse_ether(handler, layer->pld_ptr, layer->pld_len);
+}
+
+static inline const char *parse_gtpv1_u(struct packet *handler, const char *data, uint16_t len)
+{
+ uint16_t hdr_len = get_gtp_hdr_len(data, len);
+ if (unlikely(hdr_len == 0))
+ {
+ PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_GTPV1_U);
+ return data;
+ }
+
+ struct layer_record *layer = get_free_layer(handler);
+ if (unlikely(layer == NULL))
+ {
+ return data;
+ }
+ uint8_t next_proto = (((const uint8_t *)(data + hdr_len))[0]) >> 4;
+ SET_LAYER(handler, layer, LAYER_TYPE_GTPV1_U, hdr_len, data, len);
+
+ switch (next_proto)
+ {
+ // TESTED
+ case 4:
+ return parse_ipv4(handler, layer->pld_ptr, layer->pld_len);
+ // TESTED
+ case 6:
+ return parse_ipv6(handler, layer->pld_ptr, layer->pld_len);
+ default:
+ PACKET_LOG_UNSUPPORT_PROTO("gtp", next_proto);
+ return layer->pld_ptr;
+ }
+}
+
+static inline const char *parse_l3(struct packet *handler, uint16_t next_proto, const char *data, uint16_t len)
+{
+ switch (next_proto)
+ {
+ // TESTED
+ case ETH_P_8021Q:
+ case ETH_P_8021AD:
+ return parse_vlan(handler, data, len);
+ // TESTED
+ case ETH_P_IP:
+ return parse_ipv4(handler, data, len);
+ // TESTED
+ case ETH_P_IPV6:
+ return parse_ipv6(handler, data, len);
+ // TESTED
+ case ETH_P_PPP_SES:
+ return parse_pppoe_ses(handler, data, len);
+ // TESTED
+ case ETH_P_MPLS_UC:
+ return parse_mpls(handler, data, len);
+ default:
+ // TESTED ARP
+ PACKET_LOG_UNSUPPORT_ETHPROTO("l3", next_proto);
+ return data;
+ }
+}
+
+static inline const char *parse_l4(struct packet *handler, uint8_t next_proto, const char *data, uint16_t len)
+{
+ switch (next_proto)
+ {
+ // TESTED
+ case IPPROTO_TCP:
+ return parse_tcp(handler, data, len);
+ // TESTED
+ case IPPROTO_UDP:
+ return parse_udp(handler, data, len);
+ // TESTED
+ case IPPROTO_IPIP:
+ return parse_ipv4(handler, data, len);
+ // TESTED
+ case IPPROTO_IPV6:
+ return parse_ipv6(handler, data, len);
+ // TESTED
+ case IPPROTO_GRE:
+ return parse_gre(handler, data, len);
+ default:
+ PACKET_LOG_UNSUPPORT_IPPROTO("l4", next_proto);
+ return data;
+ }
+}
+
+/******************************************************************************
+ * Public API
+ ******************************************************************************/
+
+// return innermost payload
+const char *packet_parse(struct packet *handler, const char *data, uint16_t len)
+{
+ handler->layers_used = 0;
+ handler->layers_size = PACKET_MAX_LAYERS;
+ handler->data_ptr = data;
+ handler->data_len = len;
+ handler->zone_id = 0;
+ handler->user_data = NULL;
+
+ // TESTED
+ return parse_ether(handler, data, len);
+}
+
+void packet_print(const struct packet *handler)
+{
+ if (handler == NULL)
+ {
+ return;
+ }
+
+ printf("packet: %p, data_ptr: %p, data_len: %u, layers_used: %u, layers_size: %u\n",
+ handler, handler->data_ptr, handler->data_len,
+ handler->layers_used, handler->layers_size);
+ for (uint8_t i = 0; i < handler->layers_used; i++)
+ {
+ const struct layer_record *layer = &handler->layers[i];
+ printf(" layer[%u]: %p, type: %s, hdr_offset: %u, hdr_ptr: %p, hdr_len: %u, pld_ptr: %p, pld_len: %u\n",
+ i, layer, layer_type_tostring(layer->type), layer->hdr_offset,
+ layer->hdr_ptr, layer->hdr_len, layer->pld_ptr, layer->pld_len);
+ }
+}
+
+// return 0 : found
+// return -1 : not found
+int packet_get_innermost_tuple2(const struct packet *handler, struct tuple2 *tuple)
+{
+ const struct layer_record *layer = NULL;
+
+ for (int8_t i = handler->layers_used - 1; i >= 0; i--)
+ {
+ layer = &handler->layers[i];
+
+ if (layer->type & LAYER_TYPE_L3)
+ {
+ set_tuple2((const char *)handler->data_ptr + layer->hdr_offset, layer->type, tuple);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+// return 0 : found
+// return -1 : not found
+int packet_get_outermost_tuple2(const struct packet *handler, struct tuple2 *tuple)
+{
+ const struct layer_record *layer = NULL;
+
+ for (int8_t i = 0; i < handler->layers_used; i++)
+ {
+ layer = &handler->layers[i];
+
+ if (layer->type & LAYER_TYPE_L3)
+ {
+ set_tuple2((const char *)handler->data_ptr + layer->hdr_offset, layer->type, tuple);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+// return 0 : found
+// return -1 : not found
+int packet_get_innermost_tuple4(const struct packet *handler, struct tuple4 *tuple)
+{
+ const struct layer_record *layer_l3 = NULL;
+ const struct layer_record *layer_l4 = NULL;
+ const struct layer_record *layer = NULL;
+
+ for (int8_t i = handler->layers_used - 1; i >= 0; i--)
+ {
+ layer = &handler->layers[i];
+
+ // first get L4 layer
+ if (layer->type & LAYER_TYPE_L4)
+ {
+ layer_l4 = layer;
+ continue;
+ }
+
+ // second get L3 layer
+ if (layer->type & LAYER_TYPE_L3)
+ {
+ layer_l3 = layer;
+ break;
+ }
+ }
+
+ if (layer_l3 && layer_l4)
+ {
+ set_tuple4((const char *)handler->data_ptr + layer_l3->hdr_offset, layer_l3->type, tuple);
+ set_tuple4((const char *)handler->data_ptr + layer_l4->hdr_offset, layer_l4->type, tuple);
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+// return 0 : found
+// return -1 : not found
+int packet_get_outermost_tuple4(const struct packet *handler, struct tuple4 *tuple)
+{
+ const struct layer_record *layer_l3 = NULL;
+ const struct layer_record *layer_l4 = NULL;
+ const struct layer_record *layer = NULL;
+
+ for (int8_t i = 0; i < handler->layers_used; i++)
+ {
+ layer = &handler->layers[i];
+
+ // first get L3 layer
+ if (layer->type & LAYER_TYPE_L3)
+ {
+ layer_l3 = layer;
+ continue;
+ }
+
+ // second get L4 layer
+ if (layer->type & LAYER_TYPE_L4)
+ {
+ layer_l4 = layer;
+ break;
+ }
+ }
+
+ if (layer_l3 && layer_l4)
+ {
+ set_tuple4((const char *)handler->data_ptr + layer_l3->hdr_offset, layer_l3->type, tuple);
+ set_tuple4((const char *)handler->data_ptr + layer_l4->hdr_offset, layer_l4->type, tuple);
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+// return 0 : found
+// return -1 : not found
+int packet_get_innermost_tuple6(const struct packet *handler, struct tuple6 *tuple)
+{
+ const struct layer_record *layer_l3 = NULL;
+ const struct layer_record *layer_l4 = NULL;
+ const struct layer_record *layer = NULL;
+
+ for (int8_t i = handler->layers_used - 1; i >= 0; i--)
+ {
+ layer = &handler->layers[i];
+
+ // first get L4 layer
+ if (layer->type & LAYER_TYPE_L4)
+ {
+ layer_l4 = layer;
+ continue;
+ }
+
+ // second get L3 layer
+ if (layer->type & LAYER_TYPE_L3)
+ {
+ layer_l3 = layer;
+ break;
+ }
+ }
+
+ if (layer_l3 && layer_l4)
+ {
+ set_tuple6((const char *)handler->data_ptr + layer_l3->hdr_offset, layer_l3->type, tuple, handler->zone_id);
+ set_tuple6((const char *)handler->data_ptr + layer_l4->hdr_offset, layer_l4->type, tuple, handler->zone_id);
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+// return 0 : found
+// return -1 : not found
+int packet_get_outermost_tuple6(const struct packet *handler, struct tuple6 *tuple)
+{
+ const struct layer_record *layer_l3 = NULL;
+ const struct layer_record *layer_l4 = NULL;
+ const struct layer_record *layer = NULL;
+
+ for (int8_t i = 0; i < handler->layers_used; i++)
+ {
+ layer = &handler->layers[i];
+
+ // first get L3 layer
+ if (layer->type & LAYER_TYPE_L3)
+ {
+ layer_l3 = layer;
+ continue;
+ }
+
+ // second get L4 layer
+ if (layer->type & LAYER_TYPE_L4)
+ {
+ layer_l4 = layer;
+ break;
+ }
+ }
+
+ if (layer_l3 && layer_l4)
+ {
+ set_tuple6((const char *)handler->data_ptr + layer_l3->hdr_offset, layer_l3->type, tuple, handler->zone_id);
+ set_tuple6((const char *)handler->data_ptr + layer_l4->hdr_offset, layer_l4->type, tuple, handler->zone_id);
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+const struct layer_record *packet_get_innermost_layer(const struct packet *handler, enum layer_type type)
+{
+ const struct layer_record *layer = NULL;
+
+ for (int8_t i = handler->layers_used - 1; i >= 0; i--)
+ {
+ layer = &handler->layers[i];
+ if (layer->type & type)
+ {
+ return layer;
+ }
+ }
+
+ return NULL;
+}
+
+const struct layer_record *packet_get_outermost_layer(const struct packet *handler, enum layer_type type)
+{
+ const struct layer_record *layer = NULL;
+
+ for (int8_t i = 0; i < handler->layers_used; i++)
+ {
+ layer = &handler->layers[i];
+ if (layer->type & type)
+ {
+ return layer;
+ }
+ }
+
+ return NULL;
+}
+
+// direction 1: E2I
+// direction 0: I2E
+uint64_t packet_get_hash(const struct packet *handler, enum ldbc_method method, int direction)
+{
+ uint64_t temp = 0;
+ uint64_t hash_value = 1;
+
+ int inner_addr_len = 0;
+ int outer_addr_len = 0;
+ const char *inner_src_addr = NULL;
+ const char *inner_dst_addr = NULL;
+ const char *outer_src_addr = NULL;
+ const char *outer_dst_addr = NULL;
+
+ struct tuple2 inner_addr;
+ struct tuple2 outer_addr;
+
+ if (handler == NULL)
+ {
+ return hash_value;
+ }
+
+ if (packet_get_innermost_tuple2(handler, &inner_addr) == -1)
+ {
+ return hash_value;
+ }
+
+ if (packet_get_outermost_tuple2(handler, &outer_addr) == -1)
+ {
+ return hash_value;
+ }
+
+ if (inner_addr.ip_type == IP_TYPE_V4)
+ {
+ inner_src_addr = (const char *)&inner_addr.src_addr.v4;
+ inner_dst_addr = (const char *)&inner_addr.dst_addr.v4;
+ inner_addr_len = sizeof(struct in_addr);
+ }
+ else
+ {
+ inner_src_addr = (const char *)&inner_addr.src_addr.v6;
+ inner_dst_addr = (const char *)&inner_addr.dst_addr.v6;
+ inner_addr_len = sizeof(struct in6_addr);
+ }
+
+ if (outer_addr.ip_type == IP_TYPE_V4)
+ {
+ outer_src_addr = (const char *)&outer_addr.src_addr.v4;
+ outer_dst_addr = (const char *)&outer_addr.dst_addr.v4;
+ outer_addr_len = sizeof(struct in_addr);
+ }
+ else
+ {
+ outer_src_addr = (const char *)&outer_addr.src_addr.v6;
+ outer_dst_addr = (const char *)&outer_addr.dst_addr.v6;
+ outer_addr_len = sizeof(struct in6_addr);
+ }
+
+ switch (method)
+ {
+ case LDBC_METHOD_HASH_INT_IP:
+ if (direction)
+ {
+ // direction 1: E2I
+ HASH_VALUE(outer_dst_addr, outer_addr_len, hash_value);
+ }
+ else
+ {
+ // direction 0: I2E
+ HASH_VALUE(outer_src_addr, outer_addr_len, hash_value);
+ }
+ break;
+ case LDBC_METHOD_HASH_EXT_IP:
+ if (direction)
+ {
+ // direction 1: E2I
+ HASH_VALUE(outer_src_addr, outer_addr_len, hash_value);
+ }
+ else
+ {
+ // direction 0: I2E
+ HASH_VALUE(outer_dst_addr, outer_addr_len, hash_value);
+ }
+ break;
+ case LDBC_METHOD_HASH_INT_IP_AND_EXT_IP:
+ HASH_VALUE(outer_src_addr, outer_addr_len, hash_value);
+ HASH_VALUE(outer_dst_addr, outer_addr_len, temp);
+ hash_value = hash_value ^ temp;
+ break;
+ case LDBC_METHOD_HASH_INNERMOST_INT_IP:
+ if (direction)
+ {
+ // direction 1: E2I
+ HASH_VALUE(inner_dst_addr, inner_addr_len, hash_value);
+ }
+ else
+ {
+ // direction 0: I2E
+ HASH_VALUE(inner_src_addr, inner_addr_len, hash_value);
+ }
+ break;
+ case LDBC_METHOD_HASH_INNERMOST_EXT_IP:
+ if (direction)
+ {
+ // direction 1: E2I
+ HASH_VALUE(inner_src_addr, inner_addr_len, hash_value);
+ }
+ else
+ {
+ // direction 0: I2E
+ HASH_VALUE(inner_dst_addr, inner_addr_len, hash_value);
+ }
+ break;
+ default:
+ return hash_value;
+ }
+
+ return hash_value;
+}