summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2012-09-08 21:25:33 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2012-09-08 22:49:39 +0200
commit92f6779f509ece6ac7a8659c90bc335677cc62ea (patch)
tree0b1ed0f72a54ec9567d480358ab10e2dcf126a23
parent46faeab56cf4117f41cb6f1f1c40a9c18a81372f (diff)
downloadconntrack-tools-dissector.tar.gz
tests: add dissector for conntrackd protocoldissector
Addd rudimentary utility to dissect conntrackd's synchronization protocol. This code should prepare the ground for some wireshark dissector in the near future. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--tests/conntrackd/dissect-sync/.gitignore14
-rw-r--r--tests/conntrackd/dissect-sync/Make_global.am7
-rw-r--r--tests/conntrackd/dissect-sync/Makefile.am13
-rw-r--r--tests/conntrackd/dissect-sync/configure.ac61
-rwxr-xr-xtests/conntrackd/dissect-sync/l3_ipv4.c34
-rwxr-xr-xtests/conntrackd/dissect-sync/l4_tcp.c28
-rwxr-xr-xtests/conntrackd/dissect-sync/l4_udp.c26
-rwxr-xr-xtests/conntrackd/dissect-sync/main.c299
-rw-r--r--tests/conntrackd/dissect-sync/network.h291
-rw-r--r--tests/conntrackd/dissect-sync/parse.c440
-rwxr-xr-xtests/conntrackd/dissect-sync/proto.c51
-rwxr-xr-xtests/conntrackd/dissect-sync/proto.h40
12 files changed, 1304 insertions, 0 deletions
diff --git a/tests/conntrackd/dissect-sync/.gitignore b/tests/conntrackd/dissect-sync/.gitignore
new file mode 100644
index 0000000..928e44b
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/.gitignore
@@ -0,0 +1,14 @@
+.deps/
+.libs/
+Makefile
+Makefile.in
+*.o
+*.la
+*.lo
+
+/aclocal.m4
+/autom4te.cache/
+/build-aux/
+/config.*
+/configure
+/libtool
diff --git a/tests/conntrackd/dissect-sync/Make_global.am b/tests/conntrackd/dissect-sync/Make_global.am
new file mode 100644
index 0000000..06785a1
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/Make_global.am
@@ -0,0 +1,7 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include -I../../../include
+
+AM_CFLAGS = -std=gnu99 -W -Wall \
+ -Wmissing-prototypes -Wwrite-strings -Wcast-qual -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \
+ -Wno-unused-parameter \
+ ${LIBNETFILTER_CONNTRACK_CFLAGS} \
+ ${LIBNETFILTER_CTTIMEOUT_CFLAGS}
diff --git a/tests/conntrackd/dissect-sync/Makefile.am b/tests/conntrackd/dissect-sync/Makefile.am
new file mode 100644
index 0000000..b5e00de
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/Makefile.am
@@ -0,0 +1,13 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = dissect
+
+dissect_SOURCES = proto.c \
+ l3_ipv4.c \
+ l4_tcp.c \
+ l4_udp.c \
+ parse.c \
+ main.c
+
+dissect_LDFLAGS = -dynamic \
+ -lpcap
diff --git a/tests/conntrackd/dissect-sync/configure.ac b/tests/conntrackd/dissect-sync/configure.ac
new file mode 100644
index 0000000..077a9c9
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/configure.ac
@@ -0,0 +1,61 @@
+AC_INIT(cthelper-test, 0.0.1, pablo@netfilter.org)
+AC_CONFIG_AUX_DIR([build-aux])
+
+AC_CANONICAL_HOST
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE([-Wall foreign subdir-objects
+ tar-pax no-dist-gzip dist-bzip2 1.6])
+
+dnl kernel style compile messages
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_SEARCH_LIBS([dlopen], [dl], [libdl_LIBS="$LIBS"; LIBS=""])
+AC_SUBST([libdl_LIBS])
+
+AC_PROG_CC
+AC_DISABLE_STATIC
+AM_PROG_LIBTOOL
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AM_PROG_LEX
+AC_PROG_YACC
+
+case "$host" in
+*-*-linux*) ;;
+*) AC_MSG_ERROR([Linux only, dude!]);;
+esac
+
+AC_CHECK_HEADERS(arpa/inet.h)
+dnl check for inet_pton
+AC_CHECK_FUNCS(inet_pton)
+dnl Some systems have it, but not IPv6
+if test "$ac_cv_func_inet_pton" = "yes" ; then
+AC_MSG_CHECKING(if inet_pton supports IPv6)
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+int main()
+ {
+ struct in6_addr addr6;
+ if (inet_pton(AF_INET6, "::1", &addr6) < 1)
+ exit(1);
+ else
+ exit(0);
+ }
+ ]])],[ AC_MSG_RESULT(yes)
+ AC_DEFINE_UNQUOTED(HAVE_INET_PTON_IPV6, 1, [Define to 1 if inet_pton supports IPv6.])
+ ],[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)])
+fi
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/tests/conntrackd/dissect-sync/l3_ipv4.c b/tests/conntrackd/dissect-sync/l3_ipv4.c
new file mode 100755
index 0000000..c5c285c
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/l3_ipv4.c
@@ -0,0 +1,34 @@
+#include <stdlib.h>
+#include <netinet/ip.h>
+#include <linux/if_ether.h>
+
+#include "proto.h"
+
+static int l3_ipv4_pkt_l4proto_num(const uint8_t *pkt)
+{
+ const struct iphdr *iph = (const struct iphdr *)pkt;
+
+ return iph->protocol;
+}
+
+static int l3_ipv4_pkt_l3hdr_len(const uint8_t *pkt, int *tot_len)
+{
+ const struct iphdr *iph = (const struct iphdr *)pkt;
+
+ *tot_len = ntohs(iph->tot_len);
+
+ return iph->ihl << 2;
+}
+
+static struct proto_l2l3_helper ipv4 = {
+ .l2protonum = ETH_P_IP,
+ .l3protonum = AF_INET,
+ .l2hdr_len = ETH_HLEN,
+ .l3pkt_hdr_len = l3_ipv4_pkt_l3hdr_len,
+ .l4pkt_proto = l3_ipv4_pkt_l4proto_num,
+};
+
+void l2l3_ipv4_init(void)
+{
+ proto_l2l3_helper_register(&ipv4);
+}
diff --git a/tests/conntrackd/dissect-sync/l4_tcp.c b/tests/conntrackd/dissect-sync/l4_tcp.c
new file mode 100755
index 0000000..48bc5cc
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/l4_tcp.c
@@ -0,0 +1,28 @@
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include "proto.h"
+
+static int l4_tcp_pkt_size(const uint8_t *pkt, uint32_t dataoff)
+{
+ const struct tcphdr *tcph = (const struct tcphdr *)(pkt + dataoff);
+
+ return tcph->doff << 2;
+}
+
+static int l4_tcp_pkt_no_data(const uint8_t *pkt)
+{
+ const struct tcphdr *tcph = (const struct tcphdr *)pkt;
+ return tcph->syn || tcph->fin || tcph->rst || !tcph->psh;
+}
+
+static struct proto_l4_helper tcp = {
+ .l4protonum = IPPROTO_TCP,
+ .l4pkt_size = l4_tcp_pkt_size,
+ .l4pkt_no_data = l4_tcp_pkt_no_data,
+};
+
+void l4_tcp_init(void)
+{
+ proto_l4_helper_register(&tcp);
+}
diff --git a/tests/conntrackd/dissect-sync/l4_udp.c b/tests/conntrackd/dissect-sync/l4_udp.c
new file mode 100755
index 0000000..6f16142
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/l4_udp.c
@@ -0,0 +1,26 @@
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "proto.h"
+
+static int l4_udp_pkt_size(const uint8_t *pkt, uint32_t dataoff)
+{
+ return sizeof(struct udphdr);
+}
+
+static int l4_udp_pkt_no_data(const uint8_t *pkt)
+{
+ /* UDP has no control packets. */
+ return 1;
+}
+
+static struct proto_l4_helper udp = {
+ .l4protonum = IPPROTO_UDP,
+ .l4pkt_size = l4_udp_pkt_size,
+ .l4pkt_no_data = l4_udp_pkt_no_data,
+};
+
+void l4_udp_init(void)
+{
+ proto_l4_helper_register(&udp);
+}
diff --git a/tests/conntrackd/dissect-sync/main.c b/tests/conntrackd/dissect-sync/main.c
new file mode 100755
index 0000000..3a38d2e
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/main.c
@@ -0,0 +1,299 @@
+#include <stdio.h>
+#include <pcap.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+
+#include "network.h"
+#include "proto.h"
+
+static struct sync_test_stats {
+ uint32_t pkts;
+ uint32_t errors;
+ uint32_t skip;
+
+ uint32_t l3_proto_unsupported;
+ uint32_t l4_proto_unsupported;
+ uint32_t l3_proto_malformed;
+ uint32_t l4_proto_malformed;
+ uint32_t sync_version_old;
+} sync_test_stats;
+
+static int bisect_message(struct nethdr *net, uint32_t remain)
+{
+ printf("v%u ", net->version);
+
+ if (net->version != CONNTRACKD_PROTOCOL_VERSION) {
+ printf("[warning: old version] ");
+ sync_test_stats.errors++;
+ sync_test_stats.sync_version_old++;
+ }
+
+ printf("seq:%u ", net->seq);
+
+ if (net->flags & NET_F_RESYNC)
+ printf("RESYNC");
+ if (net->flags & NET_F_NACK)
+ printf("NACK ");
+ if (net->flags & NET_F_ACK)
+ printf("ACK ");
+ if (net->flags & NET_F_ALIVE)
+ printf("ALIVE ");
+ if (net->flags & NET_F_HELLO)
+ printf("HELLO ");
+ if (net->flags & NET_F_HELLO_BACK)
+ printf("HELLO BACK ");
+
+ if (IS_ACK(net)) {
+ const struct nethdr_ack *h =
+ (const struct nethdr_ack *) net;
+
+ if (before(h->to, h->from))
+ printf("[warning: bad ACK message] ");
+
+ printf("from: %u to: %u ", h->from, h->to);
+
+ } else if (IS_NACK(net)) {
+ const struct nethdr_ack *h =
+ (const struct nethdr_ack *) net;
+
+ if (before(h->to, h->from))
+ printf("[warning: bad NACK message] ");
+
+ printf("from: %u to: %u ", h->from, h->to);
+ }
+
+ if (!IS_DATA(net))
+ return 0;
+
+ switch(net->type) {
+ case NET_T_STATE_CT_NEW:
+ printf("CT-NEW ");
+ if (msg2ct(net, remain) < 0)
+ printf("[warning: malformed payload] ");
+ break;
+ case NET_T_STATE_CT_UPD:
+ printf("CT-UPD ");
+ if (msg2ct(net, remain) < 0)
+ printf("[warning: malformed payload] ");
+ break;
+ case NET_T_STATE_CT_DEL:
+ printf("CT-DEL ");
+ if (msg2ct(net, remain) < 0)
+ printf("[warning: malformed payload] ");
+ break;
+ case NET_T_STATE_EXP_NEW:
+ printf("EXP-NEW ");
+ if (msg2exp(net, remain) < 0)
+ printf("[warning: malformed payload] ");
+ break;
+ case NET_T_STATE_EXP_UPD:
+ printf("EXP-UPD ");
+ if (msg2exp(net, remain) < 0)
+ printf("[warning: malformed payload] ");
+ break;
+ case NET_T_STATE_EXP_DEL:
+ printf("EXP-DEL ");
+ if (msg2exp(net, remain) < 0)
+ printf("[warning: malformed payload] ");
+ break;
+ default:
+ printf("? [warning: unknown type] ");
+ break;
+ }
+
+ return 0;
+}
+
+static int bisect(const uint8_t *pkt, int remain)
+{
+ int ret = 0;
+ struct nethdr *net;
+
+ while (remain > 0) {
+ int len;
+
+ net = (struct nethdr *)pkt;
+
+ if (remain < NETHDR_SIZ) {
+ printf("[warning: truncated header (%u)]\n", remain);
+ break;
+ }
+
+ len = ntohs(net->len);
+ if (len <= 0) {
+ printf("[warning: bad header length]\n");
+ break;
+ }
+
+ if (len > remain) {
+ printf("[warning: truncated packet]\n");
+ break;
+ }
+
+ if (IS_ACK(net) || IS_NACK(net) || IS_RESYNC(net)) {
+ if (remain < NETHDR_ACK_SIZ) {
+ printf("[warning: truncated ACK header]\n");
+ break;
+ }
+
+ if (len < NETHDR_ACK_SIZ) {
+ printf("[warning: too small ACK header]\n");
+ break;
+ }
+ } else {
+ if (len < NETHDR_SIZ) {
+ printf("[warning: truncated header]\n");
+ break;
+ }
+ }
+
+ HDR_NETWORK2HOST(net);
+
+ bisect_message(net, remain);
+
+ pkt += net->len;
+ remain -= net->len;
+
+ printf("\n");
+ }
+
+ return ret;
+}
+
+static int
+sync_process_packet(const uint8_t *pkt, int pktlen)
+{
+ struct proto_l2l3_helper *l3h;
+ struct proto_l4_helper *l4h;
+ int l3hdr_len, l4hdr_len, l4protonum, tot_len;
+
+ l3h = proto_l2l3_helper_find(pkt, &l4protonum, &l3hdr_len, &tot_len);
+ if (l3h == NULL) {
+ sync_test_stats.skip++;
+ sync_test_stats.l3_proto_unsupported++;
+ return -1;
+ }
+
+ l4h = proto_l4_helper_find(pkt, l4protonum);
+ if (l4h == NULL) {
+ sync_test_stats.skip++;
+ sync_test_stats.l4_proto_unsupported++;
+ return -1;
+ }
+
+ pkt += l3h->l2hdr_len;
+ pktlen -= l3h->l2hdr_len;
+
+ l3hdr_len = l3h->l3pkt_hdr_len(pkt, &tot_len);
+ if (l3hdr_len > pktlen) {
+ sync_test_stats.errors++;
+ sync_test_stats.l3_proto_malformed++;
+ return -1;
+ }
+
+ /* skip layer 3 header */
+ pkt += l3hdr_len;
+ pktlen -= l3hdr_len;
+
+ l4hdr_len = l4h->l4pkt_size(pkt, l3hdr_len);
+ if (l4hdr_len > pktlen) {
+ sync_test_stats.errors++;
+ sync_test_stats.l4_proto_malformed++;
+ return -1;
+ }
+
+ switch(l4protonum) {
+ case IPPROTO_UDP: {
+ struct udphdr *udph = (struct udphdr *)pkt;
+ if (ntohs(udph->dest) != 3780 &&
+ ntohs(udph->source) != 3780) {
+ sync_test_stats.skip++;
+ return -1;
+ }
+ break;
+ }
+ case IPPROTO_TCP: {
+ struct tcphdr *tcph = (struct tcphdr *)pkt;
+ if (ntohs(tcph->dest) != 3780 &&
+ ntohs(tcph->source) != 3780)
+ sync_test_stats.skip++;
+ return -1;
+ break;
+ }
+ }
+
+ /* skip layer 4 header */
+ pkt += l4hdr_len;
+ pktlen -= l4hdr_len;
+
+ /* Ethernet frames that are smaller than 64 bytes are padded. Note
+ * FCS is not included by PCAP files. Discard remaining bytes in the
+ * tail of the packets.
+ */
+ if (tot_len + l3h->l2hdr_len < 60)
+ pktlen -= (60 - (tot_len + l3h->l2hdr_len));
+
+ bisect(pkt, pktlen);
+
+ return 0;
+}
+
+static int
+sync_test(const char *pcapfile)
+{
+ struct pcap_pkthdr pcaph;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ const u_char *pkt;
+ pcap_t *handle;
+
+ handle = pcap_open_offline(pcapfile, errbuf);
+ if (handle == NULL) {
+ fprintf(stderr, "couldn't open pcap file %s: %s\n",
+ pcapfile, errbuf);
+ return -1;
+ }
+ while ((pkt = pcap_next(handle, &pcaph)) != NULL) {
+ sync_test_stats.pkts++;
+ sync_process_packet(pkt, pcaph.caplen);
+ }
+
+ pcap_close(handle);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc != 2) {
+ fprintf(stderr, "Wrong usage:\n");
+ fprintf(stderr, "%s <pcap_file>\n",
+ argv[0]);
+ fprintf(stderr, "example: %s file.pcap\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Initialization of supported layer 3 and 4 protocols here. */
+ l2l3_ipv4_init();
+ l4_tcp_init();
+ l4_udp_init();
+
+ if (sync_test(argv[1]) < 0)
+ ret = EXIT_FAILURE;
+ else
+ ret = EXIT_SUCCESS;
+
+ printf("\e[1;34mDone. packets=%d errors=%d skip=%d\e[0m\n",
+ sync_test_stats.pkts, sync_test_stats.errors,
+ sync_test_stats.skip);
+
+ return ret;
+}
diff --git a/tests/conntrackd/dissect-sync/network.h b/tests/conntrackd/dissect-sync/network.h
new file mode 100644
index 0000000..d6a933b
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/network.h
@@ -0,0 +1,291 @@
+#ifndef _NETWORK_H_
+#define _NETWORK_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#define CONNTRACKD_PROTOCOL_VERSION 1
+
+struct nethdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t type:4,
+ version:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t version:4,
+ type:4;
+#else
+#error "Unknown system endianess!"
+#endif
+ uint8_t flags;
+ uint16_t len;
+ uint32_t seq;
+};
+#define NETHDR_SIZ nethdr_align(sizeof(struct nethdr))
+
+#define NETHDR_ALIGNTO 4
+
+static inline int nethdr_align(int value)
+{
+ return (value + NETHDR_ALIGNTO - 1) & ~(NETHDR_ALIGNTO - 1);
+}
+
+static inline int nethdr_size(int len)
+{
+ return NETHDR_SIZ + len;
+}
+
+enum nethdr_type {
+ NET_T_STATE_CT_NEW = 0,
+ NET_T_STATE_CT_UPD,
+ NET_T_STATE_CT_DEL,
+ NET_T_STATE_EXP_NEW = 3,
+ NET_T_STATE_EXP_UPD,
+ NET_T_STATE_EXP_DEL,
+ NET_T_STATE_MAX = NET_T_STATE_EXP_DEL,
+ NET_T_CTL = 10,
+};
+
+void nethdr_set(struct nethdr *net, int type);
+void nethdr_set_ack(struct nethdr *net);
+void nethdr_set_ctl(struct nethdr *net);
+
+struct cache_object;
+int object_status_to_network_type(struct cache_object *obj);
+
+#define NETHDR_DATA(x) \
+ (struct netattr *)(((char *)x) + NETHDR_SIZ)
+#define NETHDR_TAIL(x) \
+ (struct netattr *)(((char *)x) + x->len)
+
+struct nethdr_ack {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t type:4,
+ version:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t version:4,
+ type:4;
+#else
+#error "Unknown system endianess!"
+#endif
+ uint8_t flags;
+ uint16_t len;
+ uint32_t seq;
+ uint32_t from;
+ uint32_t to;
+};
+#define NETHDR_ACK_SIZ nethdr_align(sizeof(struct nethdr_ack))
+
+enum {
+ NET_F_UNUSED = (1 << 0),
+ NET_F_RESYNC = (1 << 1),
+ NET_F_NACK = (1 << 2),
+ NET_F_ACK = (1 << 3),
+ NET_F_ALIVE = (1 << 4),
+ NET_F_HELLO = (1 << 5),
+ NET_F_HELLO_BACK= (1 << 6),
+};
+
+enum {
+ MSG_DATA,
+ MSG_CTL,
+ MSG_DROP,
+ MSG_BAD,
+};
+
+#define BUILD_NETMSG_FROM_CT(ct, query) \
+({ \
+ static char __net[4096]; \
+ struct nethdr *__hdr = (struct nethdr *) __net; \
+ memset(__hdr, 0, NETHDR_SIZ); \
+ nethdr_set(__hdr, query); \
+ ct2msg(ct, __hdr); \
+ HDR_HOST2NETWORK(__hdr); \
+ __hdr; \
+})
+
+#define BUILD_NETMSG_FROM_EXP(exp, query) \
+({ \
+ static char __net[4096]; \
+ struct nethdr *__hdr = (struct nethdr *) __net; \
+ memset(__hdr, 0, NETHDR_SIZ); \
+ nethdr_set(__hdr, query); \
+ exp2msg(exp, __hdr); \
+ HDR_HOST2NETWORK(__hdr); \
+ __hdr; \
+})
+
+struct mcast_sock_multi;
+
+enum {
+ SEQ_UNKNOWN,
+ SEQ_UNSET,
+ SEQ_IN_SYNC,
+ SEQ_AFTER,
+ SEQ_BEFORE,
+};
+
+int nethdr_track_seq(uint32_t seq, uint32_t *exp_seq);
+void nethdr_track_update_seq(uint32_t seq);
+int nethdr_track_is_seq_set(void);
+
+struct mcast_conf;
+
+#define IS_DATA(x) (x->type <= NET_T_STATE_MAX && \
+ (x->flags & ~(NET_F_HELLO | NET_F_HELLO_BACK)) == 0)
+#define IS_ACK(x) (x->type == NET_T_CTL && x->flags & NET_F_ACK)
+#define IS_NACK(x) (x->type == NET_T_CTL && x->flags & NET_F_NACK)
+#define IS_RESYNC(x) (x->type == NET_T_CTL && x->flags & NET_F_RESYNC)
+#define IS_ALIVE(x) (x->type == NET_T_CTL && x->flags & NET_F_ALIVE)
+#define IS_HELLO(x) (x->flags & NET_F_HELLO)
+#define IS_HELLO_BACK(x)(x->flags & NET_F_HELLO_BACK)
+
+#define HDR_NETWORK2HOST(x) \
+({ \
+ x->len = ntohs(x->len); \
+ x->seq = ntohl(x->seq); \
+ if (IS_ACK(x) || IS_NACK(x) || IS_RESYNC(x)) { \
+ struct nethdr_ack *__ack = (struct nethdr_ack *) x; \
+ __ack->from = ntohl(__ack->from); \
+ __ack->to = ntohl(__ack->to); \
+ } \
+})
+
+#define HDR_HOST2NETWORK(x) \
+({ \
+ if (IS_ACK(x) || IS_NACK(x) || IS_RESYNC(x)) { \
+ struct nethdr_ack *__ack = (struct nethdr_ack *) x; \
+ __ack->from = htonl(__ack->from); \
+ __ack->to = htonl(__ack->to); \
+ } \
+ x->len = htons(x->len); \
+ x->seq = htonl(x->seq); \
+})
+
+/* extracted from net/tcp.h */
+
+/*
+ * The next routines deal with comparing 32 bit unsigned ints
+ * and worry about wraparound (automatic with unsigned arithmetic).
+ */
+
+static inline int before(uint32_t seq1, uint32_t seq2)
+{
+ return (int32_t)(seq1-seq2) < 0;
+}
+#define after(seq2, seq1) before(seq1, seq2)
+
+/* is s2<=s1<=s3 ? */
+static inline int between(uint32_t seq1, uint32_t seq2, uint32_t seq3)
+{
+ return seq3 - seq2 >= seq1 - seq2;
+}
+
+#define PLD_NETWORK2HOST(x) \
+({ \
+ x->len = ntohs(x->len); \
+ x->query = ntohs(x->query); \
+})
+
+#define PLD_HOST2NETWORK(x) \
+({ \
+ x->len = htons(x->len); \
+ x->query = htons(x->query); \
+})
+
+struct netattr {
+ uint16_t nta_len;
+ uint16_t nta_attr;
+};
+
+#define ATTR_NETWORK2HOST(x) \
+({ \
+ x->nta_len = ntohs(x->nta_len); \
+ x->nta_attr = ntohs(x->nta_attr); \
+})
+
+#define NTA_SIZE(len) NTA_ALIGN(sizeof(struct netattr)) + len
+
+#define NTA_DATA(x) \
+ (void *)(((char *)x) + NTA_ALIGN(sizeof(struct netattr)))
+
+#define NTA_NEXT(x, len) \
+( \
+ len -= NTA_ALIGN(x->nta_len), \
+ (struct netattr *)(((char *)x) + NTA_ALIGN(x->nta_len)) \
+)
+
+#define NTA_ALIGNTO 4
+#define NTA_ALIGN(len) (((len) + NTA_ALIGNTO - 1) & ~(NTA_ALIGNTO - 1))
+#define NTA_LENGTH(len) (NTA_ALIGN(sizeof(struct netattr)) + (len))
+
+enum nta_attr {
+ NTA_IPV4 = 0, /* struct nfct_attr_grp_ipv4 */
+ NTA_IPV6, /* struct nfct_attr_grp_ipv6 */
+ NTA_L4PROTO, /* uint8_t */
+ NTA_PORT, /* struct nfct_attr_grp_port */
+ NTA_TCP_STATE = 4, /* uint8_t */
+ NTA_STATUS, /* uint32_t */
+ NTA_TIMEOUT, /* uint32_t */
+ NTA_MARK, /* uint32_t */
+ NTA_MASTER_IPV4 = 8, /* struct nfct_attr_grp_ipv4 */
+ NTA_MASTER_IPV6, /* struct nfct_attr_grp_ipv6 */
+ NTA_MASTER_L4PROTO, /* uint8_t */
+ NTA_MASTER_PORT, /* struct nfct_attr_grp_port */
+ NTA_SNAT_IPV4 = 12, /* uint32_t */
+ NTA_DNAT_IPV4, /* uint32_t */
+ NTA_SPAT_PORT, /* uint16_t */
+ NTA_DPAT_PORT, /* uint16_t */
+ NTA_NAT_SEQ_ADJ = 16, /* struct nta_attr_natseqadj */
+ NTA_SCTP_STATE, /* uint8_t */
+ NTA_SCTP_VTAG_ORIG, /* uint32_t */
+ NTA_SCTP_VTAG_REPL, /* uint32_t */
+ NTA_DCCP_STATE = 20, /* uint8_t */
+ NTA_DCCP_ROLE, /* uint8_t */
+ NTA_ICMP_TYPE, /* uint8_t */
+ NTA_ICMP_CODE, /* uint8_t */
+ NTA_ICMP_ID, /* uint16_t */
+ NTA_TCP_WSCALE_ORIG, /* uint8_t */
+ NTA_TCP_WSCALE_REPL, /* uint8_t */
+ NTA_HELPER_NAME, /* string (variable length) */
+ NTA_MAX
+};
+
+struct nta_attr_natseqadj {
+ uint32_t orig_seq_correction_pos;
+ uint32_t orig_seq_offset_before;
+ uint32_t orig_seq_offset_after;
+ uint32_t repl_seq_correction_pos;
+ uint32_t repl_seq_offset_before;
+ uint32_t repl_seq_offset_after;
+};
+
+int msg2ct(struct nethdr *n, size_t remain);
+
+enum nta_exp_attr {
+ NTA_EXP_MASTER_IPV4 = 0, /* struct nfct_attr_grp_ipv4 */
+ NTA_EXP_MASTER_IPV6, /* struct nfct_attr_grp_ipv6 */
+ NTA_EXP_MASTER_L4PROTO, /* uint8_t */
+ NTA_EXP_MASTER_PORT, /* struct nfct_attr_grp_port */
+ NTA_EXP_EXPECT_IPV4 = 4, /* struct nfct_attr_grp_ipv4 */
+ NTA_EXP_EXPECT_IPV6, /* struct nfct_attr_grp_ipv6 */
+ NTA_EXP_EXPECT_L4PROTO, /* uint8_t */
+ NTA_EXP_EXPECT_PORT, /* struct nfct_attr_grp_port */
+ NTA_EXP_MASK_IPV4 = 8, /* struct nfct_attr_grp_ipv4 */
+ NTA_EXP_MASK_IPV6, /* struct nfct_attr_grp_ipv6 */
+ NTA_EXP_MASK_L4PROTO, /* uint8_t */
+ NTA_EXP_MASK_PORT, /* struct nfct_attr_grp_port */
+ NTA_EXP_TIMEOUT, /* uint32_t */
+ NTA_EXP_FLAGS, /* uint32_t */
+ NTA_EXP_CLASS, /* uint32_t */
+ NTA_EXP_NAT_IPV4, /* struct nfct_attr_grp_ipv4 */
+ NTA_EXP_NAT_PORT, /* struct nfct_attr_grp_port */
+ NTA_EXP_NAT_L4PROTO, /* uint8_t */
+ NTA_EXP_NAT_DIR, /* uint32_t */
+ NTA_EXP_HELPER_NAME, /* string (variable length) */
+ NTA_EXP_FN, /* string (variable length) */
+ NTA_EXP_MAX
+};
+
+int msg2exp(struct nethdr *n, size_t remain);
+
+#endif
diff --git a/tests/conntrackd/dissect-sync/parse.c b/tests/conntrackd/dissect-sync/parse.c
new file mode 100644
index 0000000..d5ce183
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/parse.c
@@ -0,0 +1,440 @@
+/*
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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.
+ *
+ */
+
+#include "network.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+#ifndef ssizeof
+#define ssizeof(x) (int)sizeof(x)
+#endif
+
+#ifndef NFCT_HELPER_NAME_MAX
+#define NFCT_HELPER_NAME_MAX 16
+#endif
+
+struct nfct_attr_grp_ipv4 {
+ u_int32_t src, dst;
+};
+
+struct nfct_attr_grp_ipv6 {
+ u_int32_t src[4], dst[4];
+};
+
+struct nfct_attr_grp_port {
+ u_int16_t sport, dport;
+};
+
+static void ct_parse_u8(int attr, void *data);
+static void ct_parse_u16(int attr, void *data);
+static void ct_parse_u32(int attr, void *data);
+static void ct_parse_str(int attr, void *data);
+static void ct_parse_group(int attr, void *data);
+static void ct_parse_nat_seq_adj(int attr, void *data);
+
+struct ct_parser {
+ void (*parse)(int attr, void *data);
+ int size;
+ int max_size;
+};
+
+static struct ct_parser h[NTA_MAX] = {
+ [NTA_IPV4] = {
+ .parse = ct_parse_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
+ },
+ [NTA_IPV6] = {
+ .parse = ct_parse_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
+ },
+ [NTA_PORT] = {
+ .parse = ct_parse_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
+ },
+ [NTA_L4PROTO] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_TCP_STATE] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_STATUS] = {
+ .parse = ct_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_MARK] = {
+ .parse = ct_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_TIMEOUT] = {
+ .parse = ct_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_MASTER_IPV4] = {
+ .parse = ct_parse_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
+ },
+ [NTA_MASTER_IPV6] = {
+ .parse = ct_parse_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
+ },
+ [NTA_MASTER_L4PROTO] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_MASTER_PORT] = {
+ .parse = ct_parse_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
+ },
+ [NTA_SNAT_IPV4] = {
+ .parse = ct_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_DNAT_IPV4] = {
+ .parse = ct_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_SPAT_PORT] = {
+ .parse = ct_parse_u16,
+ .size = NTA_SIZE(sizeof(uint16_t)),
+ },
+ [NTA_DPAT_PORT] = {
+ .parse = ct_parse_u16,
+ .size = NTA_SIZE(sizeof(uint16_t)),
+ },
+ [NTA_NAT_SEQ_ADJ] = {
+ .parse = ct_parse_nat_seq_adj,
+ .size = NTA_SIZE(sizeof(struct nta_attr_natseqadj)),
+ },
+ [NTA_SCTP_STATE] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_SCTP_VTAG_ORIG] = {
+ .parse = ct_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_SCTP_VTAG_REPL] = {
+ .parse = ct_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_DCCP_STATE] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_DCCP_ROLE] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_ICMP_TYPE] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_ICMP_CODE] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_ICMP_ID] = {
+ .parse = ct_parse_u16,
+ .size = NTA_SIZE(sizeof(uint16_t)),
+ },
+ [NTA_TCP_WSCALE_ORIG] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_TCP_WSCALE_REPL] = {
+ .parse = ct_parse_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_HELPER_NAME] = {
+ .parse = ct_parse_str,
+ .max_size = NFCT_HELPER_NAME_MAX,
+ },
+};
+
+static void
+ct_parse_u8(int attr, void *data)
+{
+ uint8_t *value = (uint8_t *) data;
+ printf("%u ", *value);
+}
+
+static void
+ct_parse_u16(int attr, void *data)
+{
+ uint16_t *value = (uint16_t *) data;
+ printf("%u ", ntohs(*value));
+}
+
+static void
+ct_parse_u32(int attr, void *data)
+{
+ uint32_t *value = (uint32_t *) data;
+ printf("%u ", ntohl(*value));
+}
+
+static void
+ct_parse_str(int attr, void *data)
+{
+ printf("%s ", (char *)data);
+}
+
+static void
+ct_parse_group(int attr, void *data)
+{
+ /* XXX */
+ printf(" ");
+}
+
+static void
+ct_parse_nat_seq_adj(int attr, void *data)
+{
+ /* XXX */
+ printf(" ");
+}
+
+int msg2ct(struct nethdr *net, size_t remain)
+{
+ int len;
+ struct netattr *attr;
+
+ if (remain < net->len) {
+ printf("[warning: truncated payload (len=%d)\n", remain);
+ return -1;
+ }
+
+ len = net->len - NETHDR_SIZ;
+ attr = NETHDR_DATA(net);
+
+ printf("attrs=[ ");
+
+ while (len > ssizeof(struct netattr)) {
+ ATTR_NETWORK2HOST(attr);
+ if (attr->nta_len > len) {
+ printf("[warning: too small attribute length (attr=%u)\n",
+ attr->nta_attr);
+ return -1;
+ }
+ if (attr->nta_attr > NTA_MAX) {
+ printf("[warning: wrong attribute type (attr=%u)\n",
+ attr->nta_attr);
+ return -1;
+ }
+ if (h[attr->nta_attr].size &&
+ attr->nta_len != h[attr->nta_attr].size) {
+ printf("[warning: wrong attribute length (attr=%u)\n",
+ attr->nta_attr);
+ return -1;
+ }
+ if (h[attr->nta_attr].max_size &&
+ attr->nta_len > h[attr->nta_attr].max_size) {
+ printf("[warning: too big attribute length (attr=%u) "
+ "len=%u>max=%u]\n",
+ attr->nta_attr, attr->nta_len,
+ h[attr->nta_attr].max_size);
+ return -1;
+ }
+ if (h[attr->nta_attr].parse == NULL) {
+ printf("[warning: skipping unknown attribute (attr=%u)\n",
+ attr->nta_attr);
+ attr = NTA_NEXT(attr, len);
+ continue;
+ }
+ h[attr->nta_attr].parse(attr->nta_attr, NTA_DATA(attr));
+
+ printf("%u=", attr->nta_attr);
+
+ attr = NTA_NEXT(attr, len);
+ }
+ printf("] ");
+
+ return 0;
+}
+
+static void exp_parse_ct_group(int attr, void *data);
+static void exp_parse_ct_u8(int attr, void *data);
+static void exp_parse_u32(int attr, void *data);
+static void exp_parse_str(int attr, void *data);
+
+static struct exp_parser {
+ void (*parse)(int attr, void *data);
+ int size;
+ int max_size;
+} exp_h[NTA_EXP_MAX] = {
+ [NTA_EXP_MASTER_IPV4] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
+ },
+ [NTA_EXP_MASTER_IPV6] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
+ },
+ [NTA_EXP_MASTER_L4PROTO] = {
+ .parse = exp_parse_ct_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_EXP_MASTER_PORT] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
+ },
+ [NTA_EXP_EXPECT_IPV4] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
+ },
+ [NTA_EXP_EXPECT_IPV6] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
+ },
+ [NTA_EXP_EXPECT_L4PROTO] = {
+ .parse = exp_parse_ct_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_EXP_EXPECT_PORT] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
+ },
+ [NTA_EXP_MASK_IPV4] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
+ },
+ [NTA_EXP_MASK_IPV6] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
+ },
+ [NTA_EXP_MASK_L4PROTO] = {
+ .parse = exp_parse_ct_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_EXP_MASK_PORT] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
+ },
+ [NTA_EXP_TIMEOUT] = {
+ .parse = exp_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_EXP_FLAGS] = {
+ .parse = exp_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_EXP_CLASS] = {
+ .parse = exp_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_EXP_NAT_IPV4] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
+ },
+ [NTA_EXP_NAT_L4PROTO] = {
+ .parse = exp_parse_ct_u8,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_EXP_NAT_PORT] = {
+ .parse = exp_parse_ct_group,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
+ },
+ [NTA_EXP_NAT_DIR] = {
+ .parse = exp_parse_u32,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_EXP_HELPER_NAME] = {
+ .parse = exp_parse_str,
+ .max_size = NFCT_HELPER_NAME_MAX,
+ },
+ [NTA_EXP_FN] = {
+ .parse = exp_parse_str,
+ .max_size = 32, /* XXX: artificial limit */
+ },
+};
+
+static void exp_parse_ct_group(int attr, void *data)
+{
+ /* XXX */
+ printf(" ");
+}
+
+static void exp_parse_ct_u8(int attr, void *data)
+{
+ uint8_t *value = (uint8_t *) data;
+ printf("%u ", *value);
+}
+
+static void exp_parse_u32(int attr, void *data)
+{
+ uint32_t *value = (uint32_t *) data;
+ printf("%u ", ntohl(*value));
+}
+
+static void exp_parse_str(int attr, void *data)
+{
+ printf("%s ", (char *)data);
+}
+
+int msg2exp(struct nethdr *net, size_t remain)
+{
+ int len;
+ struct netattr *attr;
+
+ if (remain < net->len) {
+ printf("[warning: truncated payload (len=%d)\n", remain);
+ return -1;
+ }
+
+ len = net->len - NETHDR_SIZ;
+ attr = NETHDR_DATA(net);
+
+ printf("attrs=[ ");
+
+ while (len > ssizeof(struct netattr)) {
+ ATTR_NETWORK2HOST(attr);
+ if (attr->nta_len > len) {
+ printf("[warning: too small attribute length (attr=%u)\n",
+ attr->nta_attr);
+ goto err;
+ }
+ if (attr->nta_attr > NTA_MAX) {
+ printf("[warning: wrong attribute type (attr=%u)\n",
+ attr->nta_attr);
+ goto err;
+ }
+ if (exp_h[attr->nta_attr].size &&
+ attr->nta_len != exp_h[attr->nta_attr].size) {
+ printf("[warning: wrong attribute length (attr=%u)\n",
+ attr->nta_attr);
+ goto err;
+ }
+ if (exp_h[attr->nta_attr].max_size &&
+ attr->nta_len > exp_h[attr->nta_attr].max_size) {
+ printf("[warning: too big attribute length (attr=%u) "
+ "len=%u>max=%u]\n",
+ attr->nta_attr, attr->nta_len,
+ exp_h[attr->nta_attr].max_size);
+ goto err;
+ }
+ if (exp_h[attr->nta_attr].parse == NULL) {
+ printf("[warning: skipping unknown attribute (attr=%u)\n",
+ attr->nta_attr);
+ attr = NTA_NEXT(attr, len);
+ continue;
+ }
+ exp_h[attr->nta_attr].parse(attr->nta_attr, NTA_DATA(attr));
+ printf("%u=", attr->nta_attr);
+ attr = NTA_NEXT(attr, len);
+ }
+ printf("] ");
+
+ return 0;
+err:
+ return -1;
+}
diff --git a/tests/conntrackd/dissect-sync/proto.c b/tests/conntrackd/dissect-sync/proto.c
new file mode 100755
index 0000000..5a31fef
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/proto.c
@@ -0,0 +1,51 @@
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <linux/if_ether.h>
+
+#include "linux_list.h"
+#include "proto.h"
+
+static LIST_HEAD(l2l3_helper_list);
+static LIST_HEAD(l4_helper_list);
+
+struct proto_l2l3_helper *
+proto_l2l3_helper_find(const uint8_t *pkt,
+ unsigned int *l4protonum,
+ unsigned int *l3hdr_len,
+ unsigned int *l3hdr_tot_len)
+{
+ const struct ethhdr *eh = (const struct ethhdr *)pkt;
+ struct proto_l2l3_helper *cur;
+
+ list_for_each_entry(cur, &l2l3_helper_list, head) {
+ if (ntohs(cur->l2protonum) == eh->h_proto) {
+ *l4protonum = cur->l4pkt_proto(pkt + ETH_HLEN);
+ *l3hdr_len = cur->l3pkt_hdr_len(pkt + ETH_HLEN,
+ l3hdr_tot_len);
+ return cur;
+ }
+ }
+ return NULL;
+}
+
+void proto_l2l3_helper_register(struct proto_l2l3_helper *h)
+{
+ list_add(&h->head, &l2l3_helper_list);
+}
+
+struct proto_l4_helper *
+proto_l4_helper_find(const uint8_t *pkt, unsigned int l4protocol)
+{
+ struct proto_l4_helper *cur;
+
+ list_for_each_entry(cur, &l4_helper_list, head) {
+ if (cur->l4protonum == l4protocol)
+ return cur;
+ }
+ return NULL;
+}
+
+void proto_l4_helper_register(struct proto_l4_helper *h)
+{
+ list_add(&h->head, &l4_helper_list);
+}
diff --git a/tests/conntrackd/dissect-sync/proto.h b/tests/conntrackd/dissect-sync/proto.h
new file mode 100755
index 0000000..10b223c
--- /dev/null
+++ b/tests/conntrackd/dissect-sync/proto.h
@@ -0,0 +1,40 @@
+#ifndef _HELPER_H_
+#define _HELPER_H_
+
+#include <stdint.h>
+
+#include "../../../include/linux_list.h"
+
+struct proto_l4_helper {
+ struct list_head head;
+
+ unsigned int l4protonum;
+
+ int (*l4pkt_size)(const uint8_t *pkt, uint32_t dataoff);
+ int (*l4pkt_no_data)(const uint8_t *pkt);
+};
+
+struct proto_l2l3_helper {
+ struct list_head head;
+
+ unsigned int l2protonum;
+ unsigned int l2hdr_len;
+
+ unsigned int l3protonum;
+
+ int (*l3pkt_hdr_len)(const uint8_t *pkt, int *tot_len);
+ int (*l4pkt_proto)(const uint8_t *pkt);
+};
+
+struct proto_l2l3_helper *proto_l2l3_helper_find(const uint8_t *pkt, unsigned int *l4protonum, unsigned int *l3hdr_len, unsigned int *l3hdr_tot_len);
+void proto_l2l3_helper_register(struct proto_l2l3_helper *h);
+
+struct proto_l4_helper *proto_l4_helper_find(const uint8_t *pkt, unsigned int l4protonum);
+void proto_l4_helper_register(struct proto_l4_helper *h);
+
+/* Initialization of supported protocols here. */
+void l2l3_ipv4_init(void);
+void l4_tcp_init(void);
+void l4_udp_init(void);
+
+#endif