summaryrefslogtreecommitdiff
path: root/cesar
diff options
context:
space:
mode:
authorJérémy Dufour2011-07-13 16:18:50 +0200
committerJérémy Dufour2011-09-08 16:10:49 +0200
commit0fe91480bca31711e5edffd195eeeddc860857bc (patch)
treeeb90553392048d121f3765cf294377a5bd2a2fa6 /cesar
parent70c444ae54fe48dec17698bf62af1b8bb3bd7452 (diff)
cesar/lib: add library to check sequence number in data packets, refs #2644
By default, this library is disabled (at compilation) for performance reasons.
Diffstat (limited to 'cesar')
-rw-r--r--cesar/common/tests/tests4
-rw-r--r--cesar/lib/Config2
-rw-r--r--cesar/lib/Module3
-rw-r--r--cesar/lib/seq_check.h170
-rw-r--r--cesar/lib/src/seq_check.c154
-rw-r--r--cesar/lib/test/seq_check/Config2
-rw-r--r--cesar/lib/test/seq_check/Makefile8
-rw-r--r--cesar/lib/test/seq_check/src/seq_check.c250
-rw-r--r--cesar/station/src/station.c4
9 files changed, 597 insertions, 0 deletions
diff --git a/cesar/common/tests/tests b/cesar/common/tests/tests
index 3851e1e17d..b83bb8d1ff 100644
--- a/cesar/common/tests/tests
+++ b/cesar/common/tests/tests
@@ -134,6 +134,10 @@ lib/scenario/test:
make: make COV=y
cov test_scenario: ./obj/test_scenario
+lib/test/seq_check:
+make: make COV=y
+cov test_seq_check: ./obj/test_seq_check
+
maximus/stationtest:
make
diff --git a/cesar/lib/Config b/cesar/lib/Config
index f8dfbbd3a3..bb13fd5ab8 100644
--- a/cesar/lib/Config
+++ b/cesar/lib/Config
@@ -26,3 +26,5 @@ CONFIG_GPIO_FATAL_BLINK_DELAY = 10000000
CONFIG_SLAB_ALLOC_SCRAMBLE = n
CONFIG_RND_MT19937 = n
CONFIG_RND_TT800 = y
+CONFIG_SEQ_CHECK = n
+CONFIG_SEQ_CHECK_VLAN_MAX = 64
diff --git a/cesar/lib/Module b/cesar/lib/Module
index 69d03af8a3..e57586f636 100644
--- a/cesar/lib/Module
+++ b/cesar/lib/Module
@@ -26,3 +26,6 @@ endif
ifeq ($(CONFIG_RND_TT800),y)
SOURCES += tt800.c
endif
+ifeq ($(CONFIG_SEQ_CHECK),y)
+SOURCES += seq_check.c
+endif
diff --git a/cesar/lib/seq_check.h b/cesar/lib/seq_check.h
new file mode 100644
index 0000000000..5e0916ce29
--- /dev/null
+++ b/cesar/lib/seq_check.h
@@ -0,0 +1,170 @@
+#ifndef lib_seq_check_h
+#define lib_seq_check_h
+/* Cesar project {{{
+ *
+ * Copyright (C) 2011 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file lib/seq_check.h
+ * \brief Check sequence number in data packets
+ * \ingroup lib
+ *
+ * This lib is used to check sequence number in data packets generated by
+ * IPerf or Ixia. It supports VLAN if packets are tagged.
+ *
+ * De-sequence is stored in traces (no assert is done).
+ *
+ * You can configure:
+ * - position of the sequence number in the packet,
+ * - Ethernet type to inspect.
+ *
+ * This lib has some limitations:
+ * - you can not ignore some packets: for example during an IPerf traffic, if
+ * they are some pings, they will be inspected. If there is video traffic, it
+ * will be inspected too,
+ * - having different kind of throughput at the same time is not possible: an
+ * IPerf, an Ixia and video. You can only configure for one throughput,
+ * - you can not specify the first sequence number value,
+ * - you can not specify the size/length of the sequence number,
+ * - it can not support VLAN and non VLAN traffic at the same time (sequence
+ * position is the same for both).
+ *
+ * This lib should only be enabled when needed as it will probably impact
+ * performance (it needs to inspect packets).
+ */
+
+#include "config/seq/check.h"
+
+#if CONFIG_SEQ_CHECK
+
+/**
+ * Callback used when there is a de-sequencing in the packets.
+ * \param user an user pointer
+ * \param vlan the VLAN id which in the packet or 0 if there is none
+ * \param seq_expected the sequence number expected
+ * \param seq_actual the sequence actually in the packet
+ *
+ * This function is called by lib_seq_check_packet when there the sequence
+ * number is not the one expected.
+ */
+typedef void
+(*lib_seq_check_error_cb_t) (void *user, uint vlan, uint seq_expected,
+ uint seq_actual);
+
+/**
+ * Ethernet type IP.
+ */
+#define SEQ_CHECK_ET_IP 0x8000
+
+/**
+ * Sequence counters.
+ * This structure is used to store states of the sequence counter. There is
+ * one for each VLAN.
+ */
+typedef struct lib_seq_check_t
+{
+ /**
+ * Sequence counters, for each VLAN id.
+ * \note if no VLAN is present on frames, first index of the table is
+ * used. There should be no problem, as VLAN 0 is for management.
+ */
+ uint seq[CONFIG_SEQ_CHECK_VLAN_MAX];
+ /**
+ * Callback when sequence is wrong.
+ */
+ lib_seq_check_error_cb_t cb;
+ /**
+ * The user pointer used when calling the callback.
+ */
+ void *cb_user;
+} lib_seq_check_t;
+
+/**
+ * Configuration structure.
+ */
+typedef struct lib_seq_check_config_t
+{
+ /**
+ * Sequence number size.
+ * It only support 16 for the moment.
+ */
+ u8 seq_size;
+ /**
+ * Position of the sequence number in the packet.
+ * The position start at first bit of the Ethernet frame.
+ * This counter is expressed in byte.
+ */
+ uint seq_pos;
+ /**
+ * Ethernet type the packet has to have.
+ */
+ uint ethertype;
+} lib_seq_check_config_t;
+
+BEGIN_DECLS
+
+/**
+ * Configure lib sequencer check.
+ * \param config the new configuration structure to use.
+ *
+ * All fields are copied to the configuration. If you only want to setup one
+ * field, you need to use lib_seq_check_config_get before.
+ */
+void
+lib_seq_check_config_set (lib_seq_check_config_t *config);
+
+/**
+ * Get current configuration.
+ * \param config a configuration which will be set to the current one of the
+ * sequencer check of the lib.
+ */
+void
+lib_seq_check_config_get (lib_seq_check_config_t *config);
+
+/**
+ * Initialize lib sequencer check configuration.
+ * You need to do this only one time in the life of the station.
+ */
+void
+lib_seq_check_config_init (void);
+
+/**
+ * Initialize lib sequencer check.
+ * \param ctx the context to initialize
+ * \param cb the callback to use when there is an problem of sequence in the
+ * packet
+ * \param user an user pointer used when calling the callback
+ *
+ * This need to be done for each entry point to inspect.
+ */
+void
+lib_seq_check_init (lib_seq_check_t *ctx, lib_seq_check_error_cb_t cb,
+ void *user);
+
+/**
+ * Data packet to inspect.
+ * \param ctx the context of lib seq check
+ * \param buffer the packet buffer
+ * \param len the packet length (in byte)
+ * \return true if there is a de-sequencing in the packet and callback was
+ * called, false otherwise (this include packets which can not be inspected).
+ */
+bool
+lib_seq_check_packet (lib_seq_check_t *ctx, u8 *buffer, uint len);
+
+END_DECLS
+
+#else /* !CONFIG_SEQ_CHECK */
+
+# define lib_seq_check_config_get() ((void) 0)
+# define lib_seq_check_config_set() ((void) 0)
+# define lib_seq_check_config_init() ((void) 0)
+# define lib_seq_check_init(args...) ((void) 0)
+# define lib_seq_check_packet(args...) ((void) 0)
+
+#endif /* !CONFIG_SEQ_CHECK */
+
+#endif /* lib_seq_check_h */
diff --git a/cesar/lib/src/seq_check.c b/cesar/lib/src/seq_check.c
new file mode 100644
index 0000000000..5b7d0a1e01
--- /dev/null
+++ b/cesar/lib/src/seq_check.c
@@ -0,0 +1,154 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2011 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file lib/src/seq_check.c
+ * \brief Check sequence number in data packets
+ * \ingroup lib
+ *
+ *
+ */
+#include "common/std.h"
+#include "lib/bitstream.h"
+#include "lib/swap.h"
+#include "lib/stats.h"
+
+#include "config/seq/check/vlan/max.h"
+
+#include "lib/seq_check.h"
+
+/**
+ * Ethernet type VLAN ID.
+ */
+#define SEQ_CHECK_ET_VLAN 0x8100
+
+lib_seq_check_config_t lib_seq_check_config;
+
+void
+lib_seq_check_config_set (lib_seq_check_config_t *config)
+{
+ dbg_assert (config);
+ /* Set configuration. */
+ lib_seq_check_config = *config;
+}
+
+void
+lib_seq_check_config_get (lib_seq_check_config_t *config)
+{
+ dbg_assert (config);
+ /* Get configuration. */
+ *config = lib_seq_check_config;
+}
+
+void
+lib_seq_check_config_init (void)
+{
+ /* Set default configuration. */
+ lib_seq_check_config_t config =
+ {
+ .seq_size = 16,
+ .seq_pos = 44, /* For IPerf. */
+ .ethertype = SEQ_CHECK_ET_IP, /* No check for Ethernet type. */
+ };
+ lib_seq_check_config_set (&config);
+
+ /* Register our configuration item. */
+ lib_stats_set_stat_value_notype ("SEQ_CHECK_SEQ_POS",
+ &lib_seq_check_config.seq_pos,
+ LIB_STATS_ACCESS_READ_WRITE,
+ LIB_STATS_DEBUG);
+ lib_stats_set_stat_value_notype ("SEQ_CHECK_ETHERTYPE",
+ &lib_seq_check_config.ethertype,
+ LIB_STATS_ACCESS_READ_WRITE,
+ LIB_STATS_DEBUG);
+}
+
+void
+lib_seq_check_init (lib_seq_check_t *ctx, lib_seq_check_error_cb_t cb,
+ void *user)
+{
+ /* Check parameters. */
+ dbg_assert (ctx);
+ dbg_assert (cb);
+
+ uint i;
+ /* Initialize sequence number to 0. */
+ for (i = 0; i < CONFIG_SEQ_CHECK_VLAN_MAX; i++)
+ {
+ ctx->seq[i] = 0;
+ }
+ ctx->cb = cb;
+ ctx->cb_user = user;
+}
+
+bool
+lib_seq_check_packet (lib_seq_check_t *ctx, u8 *buffer, uint len)
+{
+ /* Check parameter. */
+ dbg_assert (ctx);
+
+ bool result = false;
+
+ /* Get Ethernet type (after SRC & DST MAC (6 bytes each)). */
+ uint ethertype = swap16 (bitstream_direct_read (buffer, 6 * 8 * 2, 2 *
+ 8));
+
+ /* Position of sequence counter index in the table. */
+ uint seq_index = 0;
+
+ /* VLAN in used? */
+ if (ethertype == SEQ_CHECK_ET_VLAN)
+ {
+ /* VLAN id on 12 bits. VLAN id is located after SRC & DST (6 bytes
+ * each) & VLAN/IEEE_802.1Q fields (TPID + PCP + CFI = 20 bits). But
+ * we can only get on 2 bytes. We need to get TCI field (PCP, CFI &
+ * VID). */
+ seq_index = swap16 (bitstream_direct_read (buffer, (6 * 2 + 2) * 8,
+ 16));
+ /* Remove PCP & CFI. */
+ seq_index &= (0xFFFF >> 4);
+ dbg_assert (seq_index < CONFIG_SEQ_CHECK_VLAN_MAX);
+
+ /* Get real Ethernet type, located after SRC & DST (6 bytes each) and
+ * VLAN/IEEE_802.1Q fields (4 bytes). */
+ ethertype = swap16 (bitstream_direct_read (buffer, (6 + 6 + 4) * 8,
+ 2 * 8));
+ }
+
+ /* If this is the expected ethertype. */
+ if (lib_seq_check_config.ethertype
+ && ethertype == lib_seq_check_config.ethertype)
+ {
+
+ /* Get sequence value. */
+ dbg_assert (lib_seq_check_config.seq_size == 16);
+ uint received_seq = swap16 (
+ bitstream_direct_read (buffer, lib_seq_check_config.seq_pos * 8,
+ lib_seq_check_config.seq_size));
+
+ /* Handle overflow. */
+ ctx->seq[seq_index] &= BITS_ONES (lib_seq_check_config.seq_size);
+
+ /* Check. */
+ if (ctx->seq[seq_index] != received_seq)
+ {
+ /* Call callback. */
+ dbg_assert (ctx->cb);
+ (*ctx->cb) (ctx->cb_user, seq_index, ctx->seq[seq_index],
+ received_seq);
+ /* Reset. */
+ ctx->seq[seq_index] = received_seq;
+ result = true;
+ }
+
+ /* Increment. */
+ ctx->seq[seq_index]++;
+ }
+
+ /* Return result. */
+ return result;
+}
diff --git a/cesar/lib/test/seq_check/Config b/cesar/lib/test/seq_check/Config
new file mode 100644
index 0000000000..aab2ba7dab
--- /dev/null
+++ b/cesar/lib/test/seq_check/Config
@@ -0,0 +1,2 @@
+CONFIG_SEQ_CHECK = y
+CONFIG_STATS = n
diff --git a/cesar/lib/test/seq_check/Makefile b/cesar/lib/test/seq_check/Makefile
new file mode 100644
index 0000000000..a74fa304d6
--- /dev/null
+++ b/cesar/lib/test/seq_check/Makefile
@@ -0,0 +1,8 @@
+BASE = ../../..
+
+HOST_PROGRAMS = test_seq_check
+
+test_seq_check_SOURCES = seq_check.c
+test_seq_check_MODULES = lib
+
+include $(BASE)/common/make/top.mk
diff --git a/cesar/lib/test/seq_check/src/seq_check.c b/cesar/lib/test/seq_check/src/seq_check.c
new file mode 100644
index 0000000000..574f98f017
--- /dev/null
+++ b/cesar/lib/test/seq_check/src/seq_check.c
@@ -0,0 +1,250 @@
+/* Cesar project {{{
+ *
+ * Copyright (C) 2011 Spidcom
+ *
+ * <<<Licence>>>
+ *
+ * }}} */
+/**
+ * \file lib/test/seq_check/src/seq_check.c
+ * \brief Sequence check test
+ * \ingroup test
+ *
+ * Test sequencer check.
+ */
+#include "common/std.h"
+
+#include "lib/test.h"
+#include "lib/seq_check.h"
+#include "common/defs/ethernet.h"
+#include "lib/rnd.h"
+#include "lib/bitstream.h"
+#include "lib/swap.h"
+
+/* Test structure to use with the callback. */
+typedef struct test_seq_check_callback_t
+{
+ struct test_t *test;
+ bool cb_called;
+ uint vlan;
+ uint seq_expected;
+ uint seq_actual;
+} test_seq_check_callback_t;
+
+void
+test_seq_check_write_ethertype (u8 *p, u16 ethertype, bool vlan)
+{
+ /* Store ethertype. */
+ uint pos = 6 * 2 * 8;
+ if (vlan)
+ {
+ bitstream_direct_write (p, pos, swap16 (0x8100), 16);
+ pos += 4 * 8;
+ }
+ bitstream_direct_write (p, pos, swap16 (ethertype), 16);
+}
+
+void
+test_seq_check_cb (void *user, uint vlan, uint seq_expected, uint seq_actual)
+{
+ dbg_assert (user);
+ test_seq_check_callback_t *test_seq_check_callback_values
+ = (test_seq_check_callback_t *) user;
+
+ test_within (test_seq_check_callback_values->test);
+ test_fail_if (test_seq_check_callback_values->vlan != vlan);
+ test_fail_if (test_seq_check_callback_values->seq_expected !=
+ seq_expected);
+ test_fail_if (test_seq_check_callback_values->seq_actual != seq_actual);
+ test_seq_check_callback_values->cb_called = true;
+}
+
+void
+test_seq_check_packet (test_t t, lib_rnd_t *rnd, uint pos, u16 ethertype)
+{
+ lib_seq_check_t s;
+ uint i;
+ u32 p[ETH_PACKET_MAX_SIZE];
+ bool res;
+ test_seq_check_callback_t test_seq_check_callback_values;
+ test_seq_check_callback_values.test = t;
+
+ /* Set configuration. */
+ lib_seq_check_config_t conf;
+ lib_seq_check_config_get (&conf);
+ conf.seq_pos = pos;
+ conf.ethertype = ethertype;
+ lib_seq_check_config_set (&conf);
+
+ test_begin (t, "config")
+ {
+ lib_seq_check_config_get (&conf);
+ test_fail_if (conf.seq_pos != pos);
+ test_fail_if (conf.ethertype != ethertype);
+ } test_end;
+
+ test_begin (t, "init")
+ {
+ lib_seq_check_init (&s, test_seq_check_cb,
+ &test_seq_check_callback_values);
+
+ for (i = 0; i < CONFIG_SEQ_CHECK_VLAN_MAX; i++)
+ {
+ test_fail_if (s.seq[i] != 0);
+ }
+ } test_end;
+
+ test_begin (t, "good sequence")
+ {
+ /* Store ethertype. */
+ test_seq_check_write_ethertype ((u8 *) p, ethertype, false);
+ for (i = 0; i < 1 << 16; i++)
+ {
+ bitstream_direct_write (p, pos * 8, swap16 (i), 16);
+ test_seq_check_callback_values.cb_called = false;
+ lib_seq_check_packet (&s, (u8 *) p, ETH_PACKET_MAX_SIZE);
+ test_fail_if (test_seq_check_callback_values.cb_called == true);
+ }
+ } test_end;
+
+ test_begin (t, "good and bad sequence")
+ {
+ u16 seq, prev;
+
+ /* Re-init. */
+ lib_seq_check_init (&s, test_seq_check_cb,
+ &test_seq_check_callback_values);
+ /* Store ethertype. */
+ test_seq_check_write_ethertype ((u8 *) p, ethertype, false);
+ for (seq = 0, i = 0; i < 1 << 16; i++, seq++)
+ {
+ if (lib_rnd_uniform (rnd, 2))
+ res = true;
+ else
+ {
+ prev = seq;
+ seq = lib_rnd_uniform (rnd, 1 << 16);
+ if (seq == prev)
+ seq++;
+ test_seq_check_callback_values.vlan = 0;
+ test_seq_check_callback_values.seq_expected = prev;
+ test_seq_check_callback_values.seq_actual = seq;
+ res = false;
+ }
+ test_seq_check_callback_values.cb_called = false;
+ bitstream_direct_write (p, pos * 8, swap16 (seq), 16);
+ lib_seq_check_packet (&s, (u8 *) p, ETH_PACKET_MAX_SIZE);
+ test_fail_if (test_seq_check_callback_values.cb_called == res);
+ }
+ } test_end;
+
+ test_begin (t, "good sequence with VLAN")
+ {
+ u16 seq[CONFIG_SEQ_CHECK_VLAN_MAX];
+
+ /* Re-init. */
+ lib_seq_check_init (&s, test_seq_check_cb,
+ &test_seq_check_callback_values);
+ /* Store ethertype. */
+ test_seq_check_write_ethertype ((u8 *) p, ethertype, true);
+
+ for (i = 0; i < CONFIG_SEQ_CHECK_VLAN_MAX; i ++)
+ {
+ seq[i] = 0;
+ }
+
+ for (i = 0; i < (1 << 16) * CONFIG_SEQ_CHECK_VLAN_MAX ; i++)
+ {
+ /* Get a random VLAN. */
+ uint vlan = lib_rnd_uniform (rnd, CONFIG_SEQ_CHECK_VLAN_MAX);
+ /* Write VLAN. */
+ bitstream_direct_write ((u8 *) p, 6 * 2 * 8 + 16, swap16 (vlan),
+ 16);
+
+ test_seq_check_callback_values.cb_called = false;
+ bitstream_direct_write (p, pos * 8, swap16 (seq[vlan]), 16);
+ lib_seq_check_packet (&s, (u8 *) p, ETH_PACKET_MAX_SIZE);
+ test_fail_if (test_seq_check_callback_values.cb_called == true);
+ seq[vlan]++;
+ }
+ } test_end;
+
+ test_begin (t, "good and bad sequence with VLAN")
+ {
+ u16 seq[CONFIG_SEQ_CHECK_VLAN_MAX], prev;
+
+ /* Re-init. */
+ lib_seq_check_init (&s, test_seq_check_cb,
+ &test_seq_check_callback_values);
+ /* Store ethertype. */
+ test_seq_check_write_ethertype ((u8 *) p, ethertype, true);
+
+ for (i = 0; i < CONFIG_SEQ_CHECK_VLAN_MAX; i ++)
+ {
+ seq[i] = 0;
+ }
+
+ for (i = 0; i < (1 << 16) * CONFIG_SEQ_CHECK_VLAN_MAX ; i++)
+ {
+ /* Get a random VLAN. */
+ uint vlan = lib_rnd_uniform (rnd, CONFIG_SEQ_CHECK_VLAN_MAX);
+ /* Write VLAN. */
+ bitstream_direct_write ((u8 *) p, 6 * 2 * 8 + 16, swap16 (vlan),
+ 16);
+
+ if (lib_rnd_uniform (rnd, 2))
+ res = true;
+ else
+ {
+ prev = seq[vlan];
+ seq[vlan] = lib_rnd_uniform (rnd, 1 << 16);
+ if (seq[vlan] == prev)
+ seq[vlan]++;
+ test_seq_check_callback_values.vlan = vlan;
+ test_seq_check_callback_values.seq_expected = prev;
+ test_seq_check_callback_values.seq_actual = seq[vlan];
+ res = false;
+ }
+ test_seq_check_callback_values.cb_called = false;
+ bitstream_direct_write (p, pos * 8, swap16 (seq[vlan]), 16);
+ lib_seq_check_packet (&s, (u8 *) p, ETH_PACKET_MAX_SIZE);
+ test_fail_if (test_seq_check_callback_values.cb_called == res);
+ seq[vlan]++;
+ }
+ } test_end;
+}
+
+int
+main (int argc, char **argv)
+{
+ lib_rnd_t rnd;
+ test_t test;
+
+ lib_rnd_init (&rnd, 0x42);
+ test_init (test, argc, argv);
+
+ test_suite_begin (test, "sequence check");
+
+ lib_seq_check_config_init ();
+
+ test_begin (test, "default config")
+ {
+ lib_seq_check_config_t conf;
+ lib_seq_check_config_get (&conf);
+ test_fail_if (conf.seq_size != 16);
+ test_fail_if (conf.seq_pos != 44);
+ test_fail_if (conf.ethertype != 0x8000);
+ } test_end;
+
+ test_case_begin (test, "IPerf");
+ /* Iperf position is at 44 and we check only IP packet. */
+ test_seq_check_packet (test, &rnd, 44, 0x8000);
+
+ test_case_begin (test, "Other");
+ /* Iperf position is at 44 and we check only IP packet. */
+ test_seq_check_packet (test, &rnd, 50, 0x8042);
+
+ test_result (test);
+ return test_nb_failed (test);
+}
+
diff --git a/cesar/station/src/station.c b/cesar/station/src/station.c
index b4f9de332d..699ce01c53 100644
--- a/cesar/station/src/station.c
+++ b/cesar/station/src/station.c
@@ -24,6 +24,8 @@
# include "hal/leon/fatal_button.h"
#endif
+#include "lib/seq_check.h"
+
#include "lib/init.h"
/** Static declaration. */
@@ -44,6 +46,8 @@ cesar_init (void)
lib_stats_init ();
+ lib_seq_check_config_init ();
+
/* Initialise the mac store. */
cesar.mac_store = mac_store_init ();