/* * rdbx_driver.c * * driver for the rdbx implementation (replay database with extended range) * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include <stdio.h> /* for printf() */ #include "getopt_s.h" /* for local getopt() */ #include "rdbx.h" #ifdef ROC_TEST #error "rdbx_t won't work with ROC_TEST - bitmask same size as seq_median" #endif #include "ut_sim.h" err_status_t test_replay_dbx(int num_trials, unsigned long ws); double rdbx_check_adds_per_second(int num_trials, unsigned long ws); void usage(char *prog_name) { printf("usage: %s [ -t | -v ]\n", prog_name); exit(255); } int main (int argc, char *argv[]) { double rate; err_status_t status; char q; unsigned do_timing_test = 0; unsigned do_validation = 0; /* process input arguments */ while (1) { q = getopt_s(argc, argv, "tv"); if (q == -1) break; switch (q) { case 't': do_timing_test = 1; break; case 'v': do_validation = 1; break; default: usage(argv[0]); } } printf("rdbx (replay database w/ extended range) test driver\n" "David A. McGrew\n" "Cisco Systems, Inc.\n"); if (!do_validation && !do_timing_test) usage(argv[0]); if (do_validation) { printf("testing rdbx_t (ws=128)...\n"); status = test_replay_dbx(1 << 12, 128); if (status) { printf("failed\n"); exit(1); } printf("passed\n"); printf("testing rdbx_t (ws=1024)...\n"); status = test_replay_dbx(1 << 12, 1024); if (status) { printf("failed\n"); exit(1); } printf("passed\n"); } if (do_timing_test) { rate = rdbx_check_adds_per_second(1 << 18, 128); printf("rdbx_check/replay_adds per second (ws=128): %e\n", rate); rate = rdbx_check_adds_per_second(1 << 18, 1024); printf("rdbx_check/replay_adds per second (ws=1024): %e\n", rate); } return 0; } void print_rdbx(rdbx_t *rdbx) { char buf[2048]; printf("rdbx: {%llu, %s}\n", (unsigned long long)(rdbx->index), bitvector_bit_string(&rdbx->bitmask, buf, sizeof(buf)) ); } /* * rdbx_check_add(rdbx, idx) checks a known-to-be-good idx against * rdbx, then adds it. if a failure is detected (i.e., the check * indicates that the value is already in rdbx) then * err_status_algo_fail is returned. * */ err_status_t rdbx_check_add(rdbx_t *rdbx, uint32_t idx) { int delta; xtd_seq_num_t est; delta = index_guess(&rdbx->index, &est, idx); if (rdbx_check(rdbx, delta) != err_status_ok) { printf("replay_check failed at index %u\n", idx); return err_status_algo_fail; } /* * in practice, we'd authenticate the packet containing idx, using * the estimated value est, at this point */ if (rdbx_add_index(rdbx, delta) != err_status_ok) { printf("rdbx_add_index failed at index %u\n", idx); return err_status_algo_fail; } return err_status_ok; } /* * rdbx_check_expect_failure(rdbx_t *rdbx, uint32_t idx) * * checks that a sequence number idx is in the replay database * and thus will be rejected */ err_status_t rdbx_check_expect_failure(rdbx_t *rdbx, uint32_t idx) { int delta; xtd_seq_num_t est; err_status_t status; delta = index_guess(&rdbx->index, &est, idx); status = rdbx_check(rdbx, delta); if (status == err_status_ok) { printf("delta: %d ", delta); printf("replay_check failed at index %u (false positive)\n", idx); return err_status_algo_fail; } return err_status_ok; } err_status_t rdbx_check_unordered(rdbx_t *rdbx, uint32_t idx) { err_status_t rstat; rstat = rdbx_check(rdbx, idx); if ((rstat != err_status_ok) && (rstat != err_status_replay_old)) { printf("replay_check_unordered failed at index %u\n", idx); return err_status_algo_fail; } return err_status_ok; } err_status_t test_replay_dbx(int num_trials, unsigned long ws) { rdbx_t rdbx; uint32_t idx, ircvd; ut_connection utc; err_status_t status; int num_fp_trials; status = rdbx_init(&rdbx, ws); if (status) { printf("replay_init failed with error code %d\n", status); exit(1); } /* * test sequential insertion */ printf("\ttesting sequential insertion..."); for (idx=0; idx < (uint32_t)num_trials; idx++) { status = rdbx_check_add(&rdbx, idx); if (status) return status; } printf("passed\n"); /* * test for false positives by checking all of the index * values which we've just added * * note that we limit the number of trials here, since allowing the * rollover counter to roll over would defeat this test */ num_fp_trials = num_trials % 0x10000; if (num_fp_trials == 0) { printf("warning: no false positive tests performed\n"); } printf("\ttesting for false positives..."); for (idx=0; idx < (uint32_t)num_fp_trials; idx++) { status = rdbx_check_expect_failure(&rdbx, idx); if (status) return status; } printf("passed\n"); /* re-initialize */ rdbx_uninit(&rdbx); if (rdbx_init(&rdbx, ws) != err_status_ok) { printf("replay_init failed\n"); return err_status_init_fail; } /* * test non-sequential insertion * * this test covers only fase negatives, since the values returned * by ut_next_index(...) are distinct */ ut_init(&utc); printf("\ttesting non-sequential insertion..."); for (idx=0; idx < (uint32_t)num_trials; idx++) { ircvd = ut_next_index(&utc); status = rdbx_check_unordered(&rdbx, ircvd); if (status) return status; } printf("passed\n"); /* * test a replay condition close to zero. */ rdbx_uninit(&rdbx); if (rdbx_init(&rdbx, ws) != err_status_ok) { printf("replay_init failed\n"); return err_status_init_fail; } printf("\ttesting replay close to zero..."); status = rdbx_check_add(&rdbx, 1); if (status) return status; status = rdbx_check_expect_failure(&rdbx, 64500); if (status) return status; status = rdbx_check_add(&rdbx, 2); if (status) return status; printf("passed\n"); rdbx_uninit(&rdbx); return err_status_ok; } #include <time.h> /* for clock() */ #include <stdlib.h> /* for random() */ double rdbx_check_adds_per_second(int num_trials, unsigned long ws) { uint32_t i; int delta; rdbx_t rdbx; xtd_seq_num_t est; clock_t timer; int failures; /* count number of failures */ if (rdbx_init(&rdbx, ws) != err_status_ok) { printf("replay_init failed\n"); exit(1); } failures = 0; timer = clock(); for(i=0; i < (uint32_t)num_trials; i++) { delta = index_guess(&rdbx.index, &est, i); if (rdbx_check(&rdbx, delta) != err_status_ok) ++failures; else if (rdbx_add_index(&rdbx, delta) != err_status_ok) ++failures; } timer = clock() - timer; printf("number of failures: %d \n", failures); rdbx_uninit(&rdbx); return (double) CLOCKS_PER_SEC * num_trials / timer; }