diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2012-09-08 21:25:33 +0200 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2012-09-08 22:49:39 +0200 |
commit | 92f6779f509ece6ac7a8659c90bc335677cc62ea (patch) | |
tree | 0b1ed0f72a54ec9567d480358ab10e2dcf126a23 | |
parent | 46faeab56cf4117f41cb6f1f1c40a9c18a81372f (diff) | |
download | conntrack-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/.gitignore | 14 | ||||
-rw-r--r-- | tests/conntrackd/dissect-sync/Make_global.am | 7 | ||||
-rw-r--r-- | tests/conntrackd/dissect-sync/Makefile.am | 13 | ||||
-rw-r--r-- | tests/conntrackd/dissect-sync/configure.ac | 61 | ||||
-rwxr-xr-x | tests/conntrackd/dissect-sync/l3_ipv4.c | 34 | ||||
-rwxr-xr-x | tests/conntrackd/dissect-sync/l4_tcp.c | 28 | ||||
-rwxr-xr-x | tests/conntrackd/dissect-sync/l4_udp.c | 26 | ||||
-rwxr-xr-x | tests/conntrackd/dissect-sync/main.c | 299 | ||||
-rw-r--r-- | tests/conntrackd/dissect-sync/network.h | 291 | ||||
-rw-r--r-- | tests/conntrackd/dissect-sync/parse.c | 440 | ||||
-rwxr-xr-x | tests/conntrackd/dissect-sync/proto.c | 51 | ||||
-rwxr-xr-x | tests/conntrackd/dissect-sync/proto.h | 40 |
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 |