aboutsummaryrefslogtreecommitdiffstats
path: root/tests/osmo-pcap-test
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@gnumonks.org>2012-06-28 12:53:45 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2012-06-28 16:42:22 +0200
commit75755dd7d948992d6c5d05f298559f01b511607d (patch)
treefa6276508c9ef6f1c8e5a16238b6d2149b3e9a52 /tests/osmo-pcap-test
parent729a0917ac17e2c5a70ab24cc884ff68a7df52af (diff)
test: add osmo-pcap-test infrastructure
This patch adds the osmo-pcap-test infrastructure that allows you to take packets stored in one pcap file, convert them to msgb and pass it to some function. The infrastructure also provides timing reconstruction based on the pcap file information. This is useful for easy protocol development, automated testing and fuzzying of the existing code to validate the code. Signed-off-by: Pablo Neira Ayuso <pablo@gnumonks.org>
Diffstat (limited to 'tests/osmo-pcap-test')
-rw-r--r--tests/osmo-pcap-test/.gitignore24
-rw-r--r--tests/osmo-pcap-test/Make_global.am6
-rw-r--r--tests/osmo-pcap-test/Makefile.am12
-rw-r--r--tests/osmo-pcap-test/configure.ac28
-rw-r--r--tests/osmo-pcap-test/l3_ipv4.c44
-rw-r--r--tests/osmo-pcap-test/l4_tcp.c38
-rw-r--r--tests/osmo-pcap-test/l4_udp.c38
-rw-r--r--tests/osmo-pcap-test/main.c215
-rw-r--r--tests/osmo-pcap-test/pcaps/rtp-nanobts-2-phones-amr-dtx.pcapbin0 -> 149092 bytes
-rw-r--r--tests/osmo-pcap-test/pcaps/rtp-nanobts-2-phones-amr.pcapbin0 -> 239416 bytes
-rw-r--r--tests/osmo-pcap-test/proto.c53
-rw-r--r--tests/osmo-pcap-test/proto.h40
12 files changed, 498 insertions, 0 deletions
diff --git a/tests/osmo-pcap-test/.gitignore b/tests/osmo-pcap-test/.gitignore
new file mode 100644
index 0000000..68f19b2
--- /dev/null
+++ b/tests/osmo-pcap-test/.gitignore
@@ -0,0 +1,24 @@
+Makefile
+Makefile.in
+.deps
+.libs
+*.o
+*.lo
+*.la
+aclocal.m4
+acinclude.m4
+aminclude.am
+m4/*.m4
+autom4te.cache
+config.h*
+config.sub
+config.log
+config.status
+config.guess
+configure
+depcomp
+missing
+ltmain.sh
+install-sh
+stamp-h1
+libtool
diff --git a/tests/osmo-pcap-test/Make_global.am b/tests/osmo-pcap-test/Make_global.am
new file mode 100644
index 0000000..b44fdfe
--- /dev/null
+++ b/tests/osmo-pcap-test/Make_global.am
@@ -0,0 +1,6 @@
+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 \
+ ${LIBOSMOCORE_CFLAGS}
diff --git a/tests/osmo-pcap-test/Makefile.am b/tests/osmo-pcap-test/Makefile.am
new file mode 100644
index 0000000..6420faf
--- /dev/null
+++ b/tests/osmo-pcap-test/Makefile.am
@@ -0,0 +1,12 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = osmo-pcap-test
+
+osmo_pcap_test_SOURCES = proto.c \
+ l3_ipv4.c \
+ l4_tcp.c \
+ l4_udp.c \
+ main.c
+
+osmo_pcap_test_LDFLAGS = -lpcap \
+ -losmocore
diff --git a/tests/osmo-pcap-test/configure.ac b/tests/osmo-pcap-test/configure.ac
new file mode 100644
index 0000000..0d496db
--- /dev/null
+++ b/tests/osmo-pcap-test/configure.ac
@@ -0,0 +1,28 @@
+AC_INIT(osmo-pcap-test, 0.0.1, pablo@gnumonks.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])])
+
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.0)
+
+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_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/tests/osmo-pcap-test/l3_ipv4.c b/tests/osmo-pcap-test/l3_ipv4.c
new file mode 100644
index 0000000..83e3479
--- /dev/null
+++ b/tests/osmo-pcap-test/l3_ipv4.c
@@ -0,0 +1,44 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@gnumonks.org>
+ * (C) 2012 by On Waves ehf <http://www.on-waves.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 vers
+ */
+
+#include <stdlib.h>
+#include <netinet/ip.h>
+#include <linux/if_ether.h>
+
+#include "proto.h"
+
+#define PRINT_CMP(...)
+
+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)
+{
+ const struct iphdr *iph = (const struct iphdr *)pkt;
+
+ return iph->ihl << 2;
+}
+
+static struct osmo_pcap_proto_l2l3 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)
+{
+ osmo_pcap_proto_l2l3_register(&ipv4);
+}
diff --git a/tests/osmo-pcap-test/l4_tcp.c b/tests/osmo-pcap-test/l4_tcp.c
new file mode 100644
index 0000000..1e024b5
--- /dev/null
+++ b/tests/osmo-pcap-test/l4_tcp.c
@@ -0,0 +1,38 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@gnumonks.org>
+ * (C) 2012 by On Waves ehf <http://www.on-waves.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 vers
+ */
+
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include "proto.h"
+
+static int l4_tcp_pkt_hdr_len(const uint8_t *pkt)
+{
+ const struct tcphdr *tcph = (const struct tcphdr *)pkt;
+
+ 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 osmo_pcap_proto_l4 tcp = {
+ .l4protonum = IPPROTO_TCP,
+ .l4pkt_hdr_len = l4_tcp_pkt_hdr_len,
+ .l4pkt_no_data = l4_tcp_pkt_no_data,
+};
+
+void l4_tcp_init(void)
+{
+ osmo_pcap_proto_l4_register(&tcp);
+}
diff --git a/tests/osmo-pcap-test/l4_udp.c b/tests/osmo-pcap-test/l4_udp.c
new file mode 100644
index 0000000..0a4266e
--- /dev/null
+++ b/tests/osmo-pcap-test/l4_udp.c
@@ -0,0 +1,38 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@gnumonks.org>
+ * (C) 2012 by On Waves ehf <http://www.on-waves.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 vers
+ */
+
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "proto.h"
+
+static int l4_udp_pkt_hdr_len(const uint8_t *pkt)
+{
+ const struct udphdr *udph = (const struct udphdr *)pkt;
+
+ return ntohs(udph->len);
+}
+
+static int l4_udp_pkt_no_data(const uint8_t *pkt)
+{
+ /* UDP has no control packets. */
+ return 0;
+}
+
+static struct osmo_pcap_proto_l4 udp = {
+ .l4protonum = IPPROTO_UDP,
+ .l4pkt_hdr_len = l4_udp_pkt_hdr_len,
+ .l4pkt_no_data = l4_udp_pkt_no_data,
+};
+
+void l4_udp_init(void)
+{
+ osmo_pcap_proto_l4_register(&udp);
+}
diff --git a/tests/osmo-pcap-test/main.c b/tests/osmo-pcap-test/main.c
new file mode 100644
index 0000000..3a62d95
--- /dev/null
+++ b/tests/osmo-pcap-test/main.c
@@ -0,0 +1,215 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@gnumonks.org>
+ * (C) 2012 by On Waves ehf <http://www.on-waves.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 vers
+ */
+
+#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 <unistd.h>
+
+#include <linux/if_ether.h>
+
+#include "proto.h"
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/select.h>
+
+struct osmo_pcap_test_stats {
+ uint32_t pkts;
+ uint32_t skip;
+ uint32_t processed;
+ uint32_t unsupported_l3;
+ uint32_t unsupported_l4;
+} osmo_pcap_test_stats;
+
+static int
+osmo_pcap_process_packet(const uint8_t *pkt, uint32_t pktlen,
+ struct osmo_pcap_proto_l2l3 *l3h,
+ struct osmo_pcap_proto_l4 *l4h,
+ int (*cb)(struct msgb *msgb))
+{
+ unsigned int l3hdr_len, skip_hdr_len;
+ struct msgb *msgb;
+ int ret;
+
+ /* skip layer 2, 3 and 4 headers */
+ l3hdr_len = l3h->l3pkt_hdr_len(pkt + ETH_HLEN);
+ skip_hdr_len = l3h->l2hdr_len + l3hdr_len +
+ l4h->l4pkt_hdr_len(pkt + ETH_HLEN + l3hdr_len);
+
+ /* This packet contains no data, skip it. */
+ if (l4h->l4pkt_no_data(pkt + l3hdr_len + ETH_HLEN)) {
+ osmo_pcap_test_stats.skip++;
+ return 0;
+ }
+
+ /* get application layer data. */
+ pkt += skip_hdr_len;
+ pktlen -= skip_hdr_len;
+
+ /* Create the fake network buffer. */
+ msgb = msgb_alloc(pktlen, "OSMO/PCAP test");
+ if (msgb == NULL) {
+ fprintf(stderr, "Not enough memory\n");
+ return -1;
+ }
+ memcpy(msgb->data, pkt, pktlen);
+ msgb_put(msgb, pktlen);
+
+ ret = cb(msgb);
+
+ msgb_free(msgb);
+
+ osmo_pcap_test_stats.processed++;
+
+ return ret;
+}
+
+static pcap_t *osmo_pcap_test_open(const char *pcapfile)
+{
+ pcap_t *handle;
+ char errbuf[PCAP_ERRBUF_SIZE];
+
+ handle = pcap_open_offline(pcapfile, errbuf);
+ if (handle == NULL) {
+ fprintf(stderr, "couldn't open pcap file %s: %s\n",
+ pcapfile, errbuf);
+ return NULL;
+ }
+
+ return handle;
+}
+
+static void osmo_pcap_test_close(pcap_t *handle)
+{
+ pcap_close(handle);
+}
+
+static struct osmo_pcap {
+ pcap_t *h;
+ struct osmo_timer_list timer;
+ struct timeval last;
+} osmo_pcap;
+
+static int
+osmo_pcap_test_run(pcap_t *handle, uint8_t pnum, int (*cb)(struct msgb *msgb))
+{
+ struct osmo_pcap_proto_l2l3 *l3h;
+ struct osmo_pcap_proto_l4 *l4h;
+ struct pcap_pkthdr pcaph;
+ const u_char *pkt;
+ struct timeval res;
+ uint8_t l4protonum;
+
+retry:
+ pkt = pcap_next(handle, &pcaph);
+ if (pkt == NULL)
+ return -1;
+
+ osmo_pcap_test_stats.pkts++;
+
+ l3h = osmo_pcap_proto_l2l3_find(pkt);
+ if (l3h == NULL) {
+ osmo_pcap_test_stats.unsupported_l3++;
+ goto retry;
+ }
+ l4protonum = l3h->l4pkt_proto(pkt + ETH_HLEN);
+
+ /* filter l4 protocols we are not interested in */
+ if (l4protonum != pnum) {
+ osmo_pcap_test_stats.skip++;
+ goto retry;
+ }
+
+ l4h = osmo_pcap_proto_l4_find(pkt, l4protonum);
+ if (l4h == NULL) {
+ osmo_pcap_test_stats.unsupported_l4++;
+ goto retry;
+ }
+
+ /* first packet that is going to be processed */
+ if (osmo_pcap_test_stats.processed == 0)
+ memcpy(&osmo_pcap.last, &pcaph.ts, sizeof(struct timeval));
+
+ /* retry with next packet if this has been skipped. */
+ if (osmo_pcap_process_packet(pkt, pcaph.caplen, l3h, l4h, cb) < 0)
+ goto retry;
+
+ /* calculate waiting time */
+ timersub(&pcaph.ts, &osmo_pcap.last, &res);
+ printf("waiting %lu.%.6lu seconds\n", res.tv_sec, res.tv_usec);
+ osmo_timer_schedule(&osmo_pcap.timer, res.tv_sec, res.tv_usec);
+
+ memcpy(&osmo_pcap.last, &pcaph.ts, sizeof(struct timeval));
+
+ return 0;
+}
+
+static int osmo_osmux_xfrm_encode(struct msgb *msgb)
+{
+ return 0;
+}
+
+static void osmo_pcap_pkt_timer_cb(void *data)
+{
+ if (osmo_pcap_test_run(osmo_pcap.h, IPPROTO_UDP,
+ osmo_osmux_xfrm_encode) < 0) {
+ printf("pkts=%d processed=%d skip=%d "
+ "unsupported_l3=%d unsupported_l4=%d\n",
+ osmo_pcap_test_stats.pkts,
+ osmo_pcap_test_stats.processed,
+ osmo_pcap_test_stats.skip,
+ osmo_pcap_test_stats.unsupported_l3,
+ osmo_pcap_test_stats.unsupported_l4);
+ printf("\e[1;34mDone.\e[0m\n");
+ osmo_pcap_test_close(osmo_pcap.h);
+ exit(EXIT_SUCCESS);
+ }
+}
+
+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();
+
+ printf("\e[1;34mStarting test...\e[0m\n");
+
+ osmo_pcap.h = osmo_pcap_test_open(argv[1]);
+ if (osmo_pcap.h == NULL)
+ exit(EXIT_FAILURE);
+
+ osmo_pcap.timer.cb = osmo_pcap_pkt_timer_cb;
+
+ /* first run */
+ osmo_pcap_pkt_timer_cb(NULL);
+
+ while(1) {
+ osmo_select_main(0);
+ }
+
+ return ret;
+}
diff --git a/tests/osmo-pcap-test/pcaps/rtp-nanobts-2-phones-amr-dtx.pcap b/tests/osmo-pcap-test/pcaps/rtp-nanobts-2-phones-amr-dtx.pcap
new file mode 100644
index 0000000..1b8a3e5
--- /dev/null
+++ b/tests/osmo-pcap-test/pcaps/rtp-nanobts-2-phones-amr-dtx.pcap
Binary files differ
diff --git a/tests/osmo-pcap-test/pcaps/rtp-nanobts-2-phones-amr.pcap b/tests/osmo-pcap-test/pcaps/rtp-nanobts-2-phones-amr.pcap
new file mode 100644
index 0000000..d2a9e45
--- /dev/null
+++ b/tests/osmo-pcap-test/pcaps/rtp-nanobts-2-phones-amr.pcap
Binary files differ
diff --git a/tests/osmo-pcap-test/proto.c b/tests/osmo-pcap-test/proto.c
new file mode 100644
index 0000000..f90f07c
--- /dev/null
+++ b/tests/osmo-pcap-test/proto.c
@@ -0,0 +1,53 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@gnumonks.org>
+ * (C) 2012 by On Waves ehf <http://www.on-waves.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 vers
+ */
+
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <linux/if_ether.h>
+
+#include <osmocom/core/linuxlist.h>
+#include "proto.h"
+
+static LLIST_HEAD(l2l3_proto_list);
+static LLIST_HEAD(l4_proto_list);
+
+struct osmo_pcap_proto_l2l3 *osmo_pcap_proto_l2l3_find(const uint8_t *pkt)
+{
+ const struct ethhdr *eh = (const struct ethhdr *)pkt;
+ struct osmo_pcap_proto_l2l3 *cur;
+
+ llist_for_each_entry(cur, &l2l3_proto_list, head) {
+ if (ntohs(cur->l2protonum) == eh->h_proto)
+ return cur;
+ }
+ return NULL;
+}
+
+void osmo_pcap_proto_l2l3_register(struct osmo_pcap_proto_l2l3 *h)
+{
+ llist_add(&h->head, &l2l3_proto_list);
+}
+
+struct osmo_pcap_proto_l4 *
+osmo_pcap_proto_l4_find(const uint8_t *pkt, unsigned int l4protocol)
+{
+ struct osmo_pcap_proto_l4 *cur;
+
+ llist_for_each_entry(cur, &l4_proto_list, head) {
+ if (cur->l4protonum == l4protocol)
+ return cur;
+ }
+ return NULL;
+}
+
+void osmo_pcap_proto_l4_register(struct osmo_pcap_proto_l4 *h)
+{
+ llist_add(&h->head, &l4_proto_list);
+}
diff --git a/tests/osmo-pcap-test/proto.h b/tests/osmo-pcap-test/proto.h
new file mode 100644
index 0000000..8cb41ed
--- /dev/null
+++ b/tests/osmo-pcap-test/proto.h
@@ -0,0 +1,40 @@
+#ifndef _OSMO_PCAP_PROTO_H_
+#define _OSMO_PCAP_PROTO_H_
+
+#include <stdint.h>
+
+#include <osmocom/core/linuxlist.h>
+
+struct osmo_pcap_proto_l4 {
+ struct llist_head head;
+
+ unsigned int l4protonum;
+
+ int (*l4pkt_hdr_len)(const uint8_t *pkt);
+ int (*l4pkt_no_data)(const uint8_t *pkt);
+};
+
+struct osmo_pcap_proto_l2l3 {
+ struct llist_head head;
+
+ unsigned int l2protonum;
+ unsigned int l2hdr_len;
+
+ unsigned int l3protonum;
+
+ int (*l3pkt_hdr_len)(const uint8_t *pkt);
+ int (*l4pkt_proto)(const uint8_t *pkt);
+};
+
+struct osmo_pcap_proto_l2l3 *osmo_pcap_proto_l2l3_find(const uint8_t *pkt);
+void osmo_pcap_proto_l2l3_register(struct osmo_pcap_proto_l2l3 *h);
+
+struct osmo_pcap_proto_l4 *osmo_pcap_proto_l4_find(const uint8_t *pkt, unsigned int l4protonum);
+void osmo_pcap_proto_l4_register(struct osmo_pcap_proto_l4 *h);
+
+/* Initialization of supported protocols here. */
+void l2l3_ipv4_init(void);
+void l4_tcp_init(void);
+void l4_udp_init(void);
+
+#endif