Remove kernel datapath unit tests.
The tests haven't been seeing much love and are suffering from bit-rot at this point. We're removing them, since we have other ways to test at this point.
This commit is contained in:
1 parent
a3f9fd8581
commit
0fab7c69ae
15 files changed
+4
-1920
No files matched your search
@@ -72,7 +72,6 @@ utilities/Makefile
|
||||
secchan/Makefile
|
||||
switch/Makefile
|
||||
tests/Makefile
|
||||
datapath/tests/Makefile
|
||||
third-party/Makefile
|
||||
debian/Makefile
|
||||
datapath/linux-2.6/Kbuild
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = tests
|
||||
SUBDIRS =
|
||||
if L26_ENABLED
|
||||
SUBDIRS += linux-2.6
|
||||
endif
|
||||
|
||||
+3
-11
@@ -1,5 +1,5 @@
|
||||
all_modules = $(dist_modules)
|
||||
dist_modules = openflow unit
|
||||
dist_modules = openflow
|
||||
|
||||
openflow_sources = \
|
||||
chain.c \
|
||||
@@ -10,8 +10,7 @@ openflow_sources = \
|
||||
flow.c \
|
||||
forward.c \
|
||||
table-hash.c \
|
||||
table-linear.c \
|
||||
unit-exports.c
|
||||
table-linear.c
|
||||
|
||||
openflow_headers = \
|
||||
chain.h \
|
||||
@@ -22,14 +21,7 @@ openflow_headers = \
|
||||
flow.h \
|
||||
forward.h \
|
||||
snap.h \
|
||||
table.h \
|
||||
unit.h
|
||||
|
||||
unit_sources = \
|
||||
crc_t.c \
|
||||
forward_t.c \
|
||||
table_t.c \
|
||||
unit.c
|
||||
table.h
|
||||
|
||||
dist_sources = $(foreach module,$(dist_modules),$($(module)_sources))
|
||||
dist_headers = $(foreach module,$(dist_modules),$($(module)_headers))
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Distributed under the terms of the GNU GPL version 2.
|
||||
* Copyright (c) 2007, 2008 The Board of Trustees of The Leland
|
||||
* Stanford Junior University
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "crc32.h"
|
||||
#include "unit.h"
|
||||
|
||||
|
||||
static void
|
||||
print_error(unsigned int poly, char *data,
|
||||
unsigned int expected, unsigned int calculated)
|
||||
{
|
||||
unit_fail("crc error: poly=%x data=%s expected=%x calculated=%x\n",
|
||||
poly, data, expected, calculated);
|
||||
}
|
||||
|
||||
void
|
||||
run_crc_t(void)
|
||||
{
|
||||
struct crc32 crc;
|
||||
unsigned int val, i, j;
|
||||
|
||||
char *data[3] = { "h3rei$@neX@mp13da7@sTr117G0fCH@r$",
|
||||
"1324lkqasdf0-[LKJD0;asd,.cv;/asd0:\"'~`co29",
|
||||
"6" };
|
||||
|
||||
unsigned int polys[2] = { 0x04C11DB7,
|
||||
0x1EDC6F41 };
|
||||
|
||||
unsigned int crc_values[2][3] = {
|
||||
{ 0xDE1040C3, 0x65343A0B, 0xCEB42022 },
|
||||
{ 0x6C149FAE, 0x470A6B73, 0x4D3AA134 } };
|
||||
for (i = 0; i < 2; i++) {
|
||||
crc32_init(&crc, polys[i]);
|
||||
for (j = 0; j < 3; j++) {
|
||||
val = crc32_calculate(&crc, data[j], strlen(data[j]));
|
||||
if (val != crc_values[i][j]) {
|
||||
print_error(polys[i], data[j], crc_values[i][j], val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,584 +0,0 @@
|
||||
/*
|
||||
* Distributed under the terms of the GNU GPL version 2.
|
||||
* Copyright (c) 2007, 2008 The Board of Trustees of The Leland
|
||||
* Stanford Junior University
|
||||
*/
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
|
||||
#include "forward.h"
|
||||
#include "tests/forward_t.h"
|
||||
#include "openflow.h"
|
||||
#include "unit.h"
|
||||
#include "flow.h"
|
||||
|
||||
/*
|
||||
* Tests execute_settings() in forward.c to check that actions are
|
||||
* appropriately taken on packets, meaning:
|
||||
*
|
||||
* 1. Checksums are correct.
|
||||
* 2. Actions are only taken on compatible packets (IP action not taken on
|
||||
* non-IP packet)
|
||||
* 3. Other packet data remains untouched.
|
||||
|
||||
* forward_t.h contains static packet definitions. forward_t.h should be
|
||||
* generated using gen_forward_t.c. This test is run on whatever packets are
|
||||
* defined in forward_t.h.
|
||||
*
|
||||
* NOTE: Tests assume packets in forward_t.h are present in full and IP and
|
||||
* transport checksums are correct. (Can prevent offloading of checksum
|
||||
* computation using ethtool.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sets 'a->data'. If 'key' != NULL, sets 'data' to equal 'key's value for type
|
||||
* specified by 'a->type'. If 'key' == NULL, sets data to a random value.
|
||||
*/
|
||||
|
||||
static void
|
||||
set_action_data(struct sk_buff *skb, struct sw_flow_key *key, struct ofp_action *a)
|
||||
{
|
||||
if (key != NULL) {
|
||||
switch(ntohs(a->type)) {
|
||||
case(OFPAT_SET_DL_SRC):
|
||||
memcpy(a->arg.dl_addr, key->dl_src, sizeof key->dl_src);
|
||||
break;
|
||||
case(OFPAT_SET_DL_DST):
|
||||
memcpy(a->arg.dl_addr, key->dl_dst, sizeof key->dl_dst);
|
||||
break;
|
||||
case(OFPAT_SET_NW_SRC):
|
||||
if (key->dl_type == htons(ETH_P_IP))
|
||||
a->arg.nw_addr = key->nw_src;
|
||||
else
|
||||
a->arg.nw_addr = random32();
|
||||
break;
|
||||
case(OFPAT_SET_NW_DST):
|
||||
if (key->dl_type == htons(ETH_P_IP))
|
||||
a->arg.nw_addr = key->nw_dst;
|
||||
else
|
||||
a->arg.nw_addr = random32();
|
||||
break;
|
||||
case(OFPAT_SET_TP_SRC):
|
||||
if (key->nw_proto == IPPROTO_TCP || key->nw_proto == IPPROTO_UDP)
|
||||
a->arg.tp = key->tp_src;
|
||||
else
|
||||
a->arg.tp = (uint16_t) random32();
|
||||
break;
|
||||
case(OFPAT_SET_TP_DST):
|
||||
if (key->nw_proto == IPPROTO_TCP || key->nw_proto == IPPROTO_UDP)
|
||||
a->arg.tp = key->tp_dst;
|
||||
else
|
||||
a->arg.tp = (uint16_t) random32();
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
} else {
|
||||
((uint32_t*)a->arg.dl_addr)[0] = random32();
|
||||
((uint16_t*)a->arg.dl_addr)[2] = random32();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Checks the IP sum of an IP packet. Returns 0 if correct, else -1.
|
||||
*/
|
||||
|
||||
static void
|
||||
check_IP_csum(struct iphdr *ih)
|
||||
{
|
||||
uint16_t check, *data;
|
||||
uint32_t n_bytes, sum;
|
||||
|
||||
check = ih->check;
|
||||
ih->check = 0;
|
||||
data = (uint16_t*) ih;
|
||||
sum = 0;
|
||||
n_bytes = ih->ihl * 4;
|
||||
|
||||
while (n_bytes > 1) {
|
||||
sum += ntohs(*data);
|
||||
sum = (sum >> 16) + (uint16_t)sum;
|
||||
data++;
|
||||
n_bytes -= 2;
|
||||
}
|
||||
|
||||
if (n_bytes == 1) {
|
||||
sum += *(uint8_t*)data;
|
||||
sum = (sum >> 16) + (uint16_t)sum;
|
||||
}
|
||||
|
||||
ih->check = htons((uint16_t)(~sum));
|
||||
if (ih->check != check) {
|
||||
unit_fail("IP checksum %hu does not match %hu",
|
||||
ntohs(ih->check), ntohs(check));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Partially computes TCP checksum over 'n_bytes' pointed to by 'data'. Can be
|
||||
* called multiple times if data csum is to be computed on is fragmented. If
|
||||
* 'is_last' == 0, assumes will be called again on more data and returns the
|
||||
* value that should be passed in as 'incr_sum' on the next call. Else if
|
||||
* 'is_last' == 1, returns the final checksum. On the first call, 'incr_sum'
|
||||
* should equal 0. If 'is_last' == 0, 'n_bytes' must be even. i.e. Should
|
||||
* first be called on pseudo header fields that are multiples of two, and then
|
||||
* on the TCP packet.
|
||||
*/
|
||||
static uint32_t
|
||||
compute_transport_checksum(uint16_t *data, uint32_t n_bytes,
|
||||
uint32_t incr_sum, uint8_t is_last)
|
||||
{
|
||||
uint8_t arr[2];
|
||||
|
||||
if (n_bytes % 2 != 0 && is_last == 0)
|
||||
BUG();
|
||||
|
||||
while (n_bytes > 1) {
|
||||
incr_sum += ntohs(*data);
|
||||
incr_sum = (incr_sum >> 16) + (uint16_t)incr_sum;
|
||||
data++;
|
||||
n_bytes -= 2;
|
||||
}
|
||||
|
||||
if (is_last == 0)
|
||||
return incr_sum;
|
||||
|
||||
if(n_bytes == 1) {
|
||||
arr[0] = *(uint8_t*)data;
|
||||
arr[1] = 0;
|
||||
incr_sum += ntohs(*((uint16_t*)arr));
|
||||
incr_sum = (incr_sum >> 16) + (uint16_t)incr_sum;
|
||||
}
|
||||
|
||||
return ~incr_sum;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks the transport layer's checksum of a packet. Returns '0' if correct,
|
||||
* else '1'. 'ih' should point to the IP header of the packet, if TCP, 'th'
|
||||
* should point the TCP header, and if UDP, 'uh' should point to the UDP
|
||||
* header.
|
||||
*/
|
||||
static int
|
||||
check_transport_csum(struct iphdr *ih, struct tcphdr *th,
|
||||
struct udphdr *uh)
|
||||
{
|
||||
uint32_t tmp;
|
||||
uint16_t len, check;
|
||||
uint8_t arr[2];
|
||||
|
||||
tmp = compute_transport_checksum((uint16_t*)(&ih->saddr),
|
||||
2 * sizeof ih->saddr, 0, 0);
|
||||
arr[0] = 0;
|
||||
arr[1] = ih->protocol;
|
||||
tmp = compute_transport_checksum((uint16_t*)arr, 2, tmp, 0);
|
||||
len = ntohs(ih->tot_len) - (ih->ihl * 4);
|
||||
*((uint16_t*)arr) = htons(len);
|
||||
tmp = compute_transport_checksum((uint16_t*)arr, 2, tmp, 0);
|
||||
|
||||
if (th != NULL) {
|
||||
check = th->check;
|
||||
th->check = 0;
|
||||
th->check = htons((uint16_t)compute_transport_checksum((uint16_t*)th,
|
||||
len, tmp, 1));
|
||||
if (th->check != check) {
|
||||
unit_fail("TCP checksum %hu does not match %hu",
|
||||
ntohs(th->check), ntohs(check));
|
||||
return -1;
|
||||
}
|
||||
} else if (uh != NULL) {
|
||||
check = uh->check;
|
||||
uh->check = 0;
|
||||
uh->check = htons((uint16_t)compute_transport_checksum((uint16_t*)uh,
|
||||
len, tmp, 1));
|
||||
if (uh->check != check) {
|
||||
unit_fail("UDP checksum %hu does not match %hu",
|
||||
ntohs(uh->check), ntohs(check));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compares 'pkt_len' bytes of 'data' to 'pkt'. excl_start and excl_end point
|
||||
* together delineate areas of 'data' that are not supposed to match 'pkt'.
|
||||
* 'num_excl' specify how many such areas exist. An 'excl_start' entry is
|
||||
* ignored if it equals NULL. See 'check_packet()' for usage.
|
||||
*/
|
||||
|
||||
static void
|
||||
compare(uint8_t *data, uint8_t *pkt, uint32_t pkt_len,
|
||||
uint8_t **excl_start, uint8_t **excl_end, uint32_t num_excl)
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t *d, *p, *end;
|
||||
int ret;
|
||||
|
||||
end = data + pkt_len;
|
||||
d = data;
|
||||
p = pkt;
|
||||
ret = 0;
|
||||
|
||||
for (i = 0; i < num_excl; i++) {
|
||||
if(*excl_start != NULL) {
|
||||
if ((ret = memcmp(d, p, *excl_start - d)) != 0)
|
||||
break;
|
||||
p += (*excl_end - d);
|
||||
d = *excl_end;
|
||||
}
|
||||
excl_start++;
|
||||
excl_end++;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
ret = memcmp(d, p, end - d);
|
||||
|
||||
if (ret != 0) {
|
||||
unit_fail("skb and packet comparison failed:");
|
||||
for (i = 0; i < pkt_len; i++) {
|
||||
if (data[i] != pkt[i]) {
|
||||
unit_fail("skb[%u] = 0x%x != 0x%x",
|
||||
i, data[i], pkt[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Checks that a packet's data has remained consistent after an action has been
|
||||
* applied. 'skb' is the modified packet, 'a' is the action that was taken on
|
||||
* the packet, 'p' is a copy of the packet's data before action 'a' was taken.
|
||||
* Checks that the action was in fact taken, that the checksums of the packet
|
||||
* are correct, and that no other data in the packet was altered.
|
||||
*/
|
||||
|
||||
static void
|
||||
check_packet(struct sk_buff *skb, struct ofp_action *a, struct pkt *p)
|
||||
{
|
||||
struct ethhdr *eh;
|
||||
struct iphdr *ih;
|
||||
struct tcphdr *th;
|
||||
struct udphdr *uh;
|
||||
uint8_t *excl_start[5], *excl_end[5];
|
||||
|
||||
eh = eth_hdr(skb);
|
||||
ih = NULL;
|
||||
th = NULL;
|
||||
uh = NULL;
|
||||
|
||||
memset(excl_start, 0, sizeof excl_start);
|
||||
memset(excl_end, 0, sizeof excl_end);
|
||||
|
||||
if (eh->h_proto == htons(ETH_P_IP)) {
|
||||
ih = ip_hdr(skb);
|
||||
excl_start[1] = (uint8_t*)&ih->check;
|
||||
excl_end[1] = (uint8_t*)(&ih->check + 1);
|
||||
if (ih->protocol == IPPROTO_TCP) {
|
||||
th = tcp_hdr(skb);
|
||||
excl_start[4] = (uint8_t*)&th->check;
|
||||
excl_end[4] = (uint8_t*)(&th->check + 1);
|
||||
} else if (ih->protocol == IPPROTO_UDP) {
|
||||
uh = udp_hdr(skb);
|
||||
excl_start[4] = (uint8_t*)&uh->check;
|
||||
excl_end[4] = (uint8_t*)(&uh->check + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (a != NULL) {
|
||||
switch(ntohs(a->type)) {
|
||||
case(OFPAT_SET_DL_SRC):
|
||||
if (memcmp(a->arg.dl_addr, eh->h_source, sizeof eh->h_source) != 0) {
|
||||
unit_fail("Source eth addr has not been set");
|
||||
return;
|
||||
}
|
||||
excl_start[0] = (uint8_t*)(&eh->h_source);
|
||||
excl_end[0] = (uint8_t*)(&eh->h_proto);
|
||||
break;
|
||||
case(OFPAT_SET_DL_DST):
|
||||
if (memcmp(a->arg.dl_addr, eh->h_dest, sizeof eh->h_dest) != 0) {
|
||||
unit_fail("Dest eth addr has not been set");
|
||||
return;
|
||||
}
|
||||
excl_start[0] = (uint8_t*)(&eh->h_dest);
|
||||
excl_end[0] = (uint8_t*)(&eh->h_source);
|
||||
break;
|
||||
case(OFPAT_SET_NW_SRC):
|
||||
if (ih != NULL) {
|
||||
if (a->arg.nw_addr != ih->saddr) {
|
||||
unit_fail("Source IP addr has not been set");
|
||||
return;
|
||||
}
|
||||
excl_start[2] = (uint8_t*)(&ih->saddr);
|
||||
excl_end[2] = (uint8_t*)(&ih->saddr + 1);
|
||||
}
|
||||
break;
|
||||
case(OFPAT_SET_NW_DST):
|
||||
if (ih != NULL) {
|
||||
if (a->arg.nw_addr != ih->daddr) {
|
||||
unit_fail("Dest IP addr has not been set");
|
||||
return;
|
||||
}
|
||||
excl_start[2] = (uint8_t*)(&ih->daddr);
|
||||
excl_end[2] = (uint8_t*)(&ih->daddr + 1);
|
||||
}
|
||||
break;
|
||||
case(OFPAT_SET_TP_SRC):
|
||||
if (th != NULL) {
|
||||
if (a->arg.tp != th->source) {
|
||||
unit_fail("Source port has not been set");
|
||||
return;
|
||||
}
|
||||
excl_start[3] = (uint8_t*)(&th->source);
|
||||
excl_end[3] = (uint8_t*)(&th->source + 1);
|
||||
} else if (uh != NULL) {
|
||||
if (a->arg.tp != uh->source) {
|
||||
unit_fail("Source port has not been set");
|
||||
return;
|
||||
}
|
||||
excl_start[3] = (uint8_t*)(&uh->source);
|
||||
excl_end[3] = (uint8_t*)(&uh->source + 1);
|
||||
}
|
||||
break;
|
||||
case(OFPAT_SET_TP_DST):
|
||||
if (th != NULL) {
|
||||
if (a->arg.tp != th->dest) {
|
||||
unit_fail("Dest port has not been set");
|
||||
return;
|
||||
}
|
||||
excl_start[3] = (uint8_t*)(&th->dest);
|
||||
excl_end[3] = (uint8_t*)(&th->dest + 1);
|
||||
} else if (uh != NULL) {
|
||||
if (a->arg.tp != uh->dest) {
|
||||
unit_fail("Dest port has not been set");
|
||||
return;
|
||||
}
|
||||
excl_start[3] = (uint8_t*)(&uh->dest);
|
||||
excl_end[3] = (uint8_t*)(&uh->dest + 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
compare(skb->data, p->data, p->len, excl_start, excl_end, 5);
|
||||
if (unit_failed())
|
||||
return;
|
||||
|
||||
if (ih == NULL)
|
||||
return;
|
||||
|
||||
check_IP_csum(ih);
|
||||
if (unit_failed())
|
||||
return;
|
||||
|
||||
if (th == NULL && uh == NULL)
|
||||
return;
|
||||
|
||||
check_transport_csum(ih, th, uh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Layers 3 & 4 Tests: Given packets in forward_t.h, executes all actions
|
||||
* with random data, checking for consistency described in check_packet().
|
||||
*/
|
||||
|
||||
void
|
||||
test_l3_l4(void)
|
||||
{
|
||||
struct ofp_action action;
|
||||
uint16_t a_type;
|
||||
struct sk_buff *skb;
|
||||
struct sw_flow_key key;
|
||||
unsigned int i, j;
|
||||
uint16_t eth_proto;
|
||||
int ret = 0;
|
||||
|
||||
for (i = 0; i < num_packets; i++) {
|
||||
skb = alloc_skb(packets[i].len, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
unit_fail("Couldn't allocate %uth skb", i);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(skb_put(skb, packets[i].len), packets[i].data,
|
||||
packets[i].len);
|
||||
|
||||
skb_set_mac_header(skb, 0);
|
||||
flow_extract(skb, 0, &key);
|
||||
eth_proto = ntohs(key.dl_type);
|
||||
|
||||
check_packet(skb, NULL, packets+i);
|
||||
if (unit_failed())
|
||||
return;
|
||||
|
||||
for (a_type = OFPAT_SET_DL_SRC;
|
||||
a_type <= OFPAT_SET_TP_DST;
|
||||
a_type++)
|
||||
{
|
||||
action.type = htons(a_type);
|
||||
set_action_data(skb, NULL, &action);
|
||||
for(j = 0; j < 2; j++) {
|
||||
skb = execute_setter(skb, eth_proto, &key, &action);
|
||||
check_packet(skb, &action, packets+i);
|
||||
if (unit_failed()) {
|
||||
unit_fail("Packet %u inconsistent "
|
||||
"after setter on action "
|
||||
"type %d, iteration %u",
|
||||
i, action.type, j);
|
||||
return;
|
||||
}
|
||||
set_action_data(skb, &key, &action);
|
||||
}
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
if (ret != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
printk("\nL3/L4 actions test passed.\n");
|
||||
}
|
||||
|
||||
int
|
||||
test_vlan(void)
|
||||
{
|
||||
struct ofp_action action;
|
||||
struct sk_buff *skb;
|
||||
struct sw_flow_key key;
|
||||
unsigned int i;
|
||||
uint16_t eth_proto;
|
||||
int ret = 0;
|
||||
struct vlan_ethhdr *vh;
|
||||
struct ethhdr *eh;
|
||||
struct net_device dev;
|
||||
uint16_t new_id, orig_id;
|
||||
|
||||
|
||||
memset((char *)&dev, '\0', sizeof(dev));
|
||||
|
||||
printk("Testing vlan\n");
|
||||
for (i = 0; i < num_packets; i++) {
|
||||
skb = alloc_skb(packets[i].len, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
unit_fail("Couldn't allocate %uth skb", i);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(skb_put(skb, packets[i].len), packets[i].data,
|
||||
packets[i].len);
|
||||
skb->dev = &dev;
|
||||
|
||||
skb_set_mac_header(skb, 0);
|
||||
flow_extract(skb, 0, &key);
|
||||
eth_proto = ntohs(key.dl_type);
|
||||
|
||||
#if 0
|
||||
if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
|
||||
unit_fail("Packet %u has incorrect checksum unmodified",
|
||||
i);
|
||||
goto free_skb;
|
||||
}
|
||||
#endif
|
||||
|
||||
eh = eth_hdr(skb);
|
||||
orig_id = eh->h_proto;
|
||||
|
||||
action.type = htons(OFPAT_SET_VLAN_VID);
|
||||
|
||||
// Add a random vlan tag
|
||||
new_id = (uint16_t) random32() & VLAN_VID_MASK;
|
||||
action.arg.vlan_vid = new_id;
|
||||
skb = execute_setter(skb, eth_proto, &key, &action);
|
||||
vh = vlan_eth_hdr(skb);
|
||||
if (ntohs(vh->h_vlan_TCI) != new_id) {
|
||||
unit_fail("add: vlan id doesn't match: %#x != %#x",
|
||||
ntohs(vh->h_vlan_TCI), new_id);
|
||||
return -1;
|
||||
}
|
||||
flow_extract(skb, 0, &key);
|
||||
#if 0
|
||||
if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
|
||||
unit_fail("Packet %u has incorrect checksum after adding vlan",
|
||||
i);
|
||||
goto free_skb;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Modify the tag
|
||||
new_id = (uint16_t) random32() & VLAN_VID_MASK;
|
||||
action.arg.vlan_vid = new_id;
|
||||
skb = execute_setter(skb, eth_proto, &key, &action);
|
||||
vh = vlan_eth_hdr(skb);
|
||||
if (ntohs(vh->h_vlan_TCI) != new_id) {
|
||||
unit_fail("mod: vlan id doesn't match: %#x != %#x",
|
||||
ntohs(vh->h_vlan_TCI), new_id);
|
||||
return -1;
|
||||
}
|
||||
flow_extract(skb, 0, &key);
|
||||
#if 0
|
||||
if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
|
||||
unit_fail("Packet %u has incorrect checksum after modifying vlan",
|
||||
i);
|
||||
goto free_skb;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Remove the tag
|
||||
action.type = htons(OFPAT_STRIP_VLAN);
|
||||
skb = execute_setter(skb, eth_proto, &key, &action);
|
||||
|
||||
eh = eth_hdr(skb);
|
||||
|
||||
if (eh->h_proto != orig_id) {
|
||||
unit_fail("del: vlan id doesn't match: %#x != %#x",
|
||||
ntohs(eh->h_proto), ntohs(orig_id));
|
||||
return -1;
|
||||
}
|
||||
#if 0
|
||||
if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
|
||||
unit_fail("Packet %u has incorrect checksum after removing vlan",
|
||||
i);
|
||||
goto free_skb;
|
||||
}
|
||||
|
||||
free_skb:
|
||||
#endif
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
if (ret != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
printk("\nVLAN actions test passed.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Actual test: Given packets in forward_t.h, executes all actions with random
|
||||
* data, checking for consistency described in check_packet().
|
||||
*/
|
||||
|
||||
void
|
||||
run_forward_t(void)
|
||||
{
|
||||
test_vlan();
|
||||
test_l3_l4();
|
||||
}
|
||||
@@ -1,874 +0,0 @@
|
||||
/*
|
||||
* Distributed under the terms of the GNU GPL version 2.
|
||||
* Copyright (c) 2007, 2008 The Board of Trustees of The Leland
|
||||
* Stanford Junior University
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
#include "flow.h"
|
||||
#include "table.h"
|
||||
#include "openflow.h"
|
||||
#include "unit.h"
|
||||
|
||||
static const char *
|
||||
table_name(struct sw_table *table)
|
||||
{
|
||||
struct sw_table_stats stats;
|
||||
table->stats(table, &stats);
|
||||
return stats.name;
|
||||
}
|
||||
|
||||
static unsigned long int
|
||||
table_max_flows(struct sw_table *table)
|
||||
{
|
||||
struct sw_table_stats stats;
|
||||
table->stats(table, &stats);
|
||||
return stats.max_flows;
|
||||
}
|
||||
|
||||
static struct sw_flow *flow_zalloc(int n_actions, gfp_t flags)
|
||||
{
|
||||
struct sw_flow *flow = flow_alloc(n_actions, flags);
|
||||
if (flow) {
|
||||
struct sw_flow_actions *sfa = flow->sf_acts;
|
||||
memset(flow, 0, sizeof *flow);
|
||||
flow->sf_acts = sfa;
|
||||
}
|
||||
return flow;
|
||||
}
|
||||
|
||||
static void
|
||||
simple_insert_delete(struct sw_table *swt, uint32_t wildcards)
|
||||
{
|
||||
struct sw_flow *a_flow = flow_zalloc(0, GFP_KERNEL);
|
||||
struct sw_flow *b_flow = flow_zalloc(0, GFP_KERNEL);
|
||||
struct sw_flow *found;
|
||||
|
||||
if (!swt) {
|
||||
unit_fail("table creation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
printk("simple_insert_delete: testing %s table\n", table_name(swt));
|
||||
*((uint32_t*)a_flow->key.dl_src) = 0x12345678;
|
||||
*((uint32_t*)b_flow->key.dl_src) = 0x87654321;
|
||||
|
||||
a_flow->key.nw_src = 0xdeadbeef;
|
||||
b_flow->key.nw_src = 0x001dd0d0;
|
||||
|
||||
a_flow->key.wildcards = wildcards;
|
||||
b_flow->key.wildcards = wildcards;
|
||||
|
||||
if (!(swt->insert(swt, a_flow)))
|
||||
unit_fail("insert failed");
|
||||
found = swt->lookup(swt, &a_flow->key);
|
||||
if(found != a_flow)
|
||||
unit_fail("%p != %p", found, a_flow);
|
||||
if (swt->lookup(swt, &b_flow->key))
|
||||
unit_fail("lookup should not succeed (1)");
|
||||
|
||||
swt->delete(swt, &a_flow->key, 0, 0);
|
||||
if (swt->lookup(swt, &a_flow->key))
|
||||
unit_fail("lookup should not succeed (3)");
|
||||
|
||||
flow_free(b_flow);
|
||||
swt->destroy(swt);
|
||||
}
|
||||
|
||||
static void
|
||||
multiple_insert_destroy(struct sw_table *swt, int inserts, uint32_t wildcards,
|
||||
int min_collisions, int max_collisions)
|
||||
{
|
||||
int i;
|
||||
int col = 0;
|
||||
|
||||
if (!swt) {
|
||||
unit_fail("table creation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
printk("inserting %d flows into %s table with max %lu flows: ",
|
||||
inserts, table_name(swt), table_max_flows(swt));
|
||||
for(i = 0; i < inserts; ++i){
|
||||
struct sw_flow *a_flow = flow_zalloc(0, GFP_KERNEL);
|
||||
*((uint32_t*)&(a_flow->key.dl_src[0])) = random32();
|
||||
a_flow->key.nw_src = random32();
|
||||
a_flow->key.wildcards = wildcards;
|
||||
|
||||
if(!swt->insert(swt, a_flow)) {
|
||||
col++;
|
||||
flow_free(a_flow);
|
||||
}
|
||||
}
|
||||
printk("%d failures\n", col);
|
||||
if (min_collisions <= col && col <= max_collisions)
|
||||
printk("\tmin = %d <= %d <= %d = max, OK.\n",
|
||||
min_collisions, col, max_collisions);
|
||||
else {
|
||||
if (col < min_collisions)
|
||||
unit_fail("too few collisions (%d < %d)",
|
||||
col, min_collisions);
|
||||
else if (col > max_collisions)
|
||||
unit_fail("too many collisions (%d > %d)",
|
||||
col, max_collisions);
|
||||
printk("(This is statistically possible "
|
||||
"but should not occur often.)\n");
|
||||
}
|
||||
|
||||
swt->destroy(swt);
|
||||
}
|
||||
|
||||
static void
|
||||
set_random_key(struct sw_flow_key *key, uint32_t wildcards)
|
||||
{
|
||||
key->nw_src = random32();
|
||||
key->nw_dst = random32();
|
||||
key->in_port = (uint16_t) random32();
|
||||
key->dl_vlan = (uint16_t) random32();
|
||||
key->dl_type = (uint16_t) random32();
|
||||
key->tp_src = (uint16_t) random32();
|
||||
key->tp_dst = (uint16_t) random32();
|
||||
key->wildcards = wildcards;
|
||||
*((uint32_t*)key->dl_src) = random32();
|
||||
*(((uint32_t*)key->dl_src) + 1) = random32();
|
||||
*((uint32_t*)key->dl_dst) = random32();
|
||||
*(((uint32_t*)key->dl_dst) + 1) = random32();
|
||||
key->nw_proto = (uint8_t) random32();
|
||||
}
|
||||
|
||||
struct flow_key_entry {
|
||||
struct sw_flow_key key;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
/*
|
||||
* Allocates memory for 'n_keys' flow_key_entrys. Initializes the allocated
|
||||
* keys with random values, setting their wildcard values to 'wildcards', and
|
||||
* places them all in a list. Returns a pointer to a flow_key_entry that
|
||||
* serves solely as the list's head (its key has not been set). If allocation
|
||||
* fails, returns NULL. Returned pointer should be freed with vfree (which
|
||||
* frees the memory associated with the keys as well.)
|
||||
*/
|
||||
|
||||
static struct flow_key_entry *
|
||||
allocate_random_keys(int n_keys, uint32_t wildcards)
|
||||
{
|
||||
struct flow_key_entry *entries, *pos;
|
||||
struct list_head *keys;
|
||||
|
||||
if (n_keys < 0)
|
||||
return NULL;
|
||||
|
||||
entries = vmalloc((n_keys+1) * sizeof *entries);
|
||||
if (entries == NULL) {
|
||||
unit_fail("cannot allocate memory for %u keys",
|
||||
n_keys);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
keys = &entries->node;
|
||||
INIT_LIST_HEAD(keys);
|
||||
|
||||
for(pos = entries+1; pos < (entries + n_keys + 1); pos++) {
|
||||
set_random_key(&pos->key, wildcards);
|
||||
list_add(&pos->node, keys);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to insert the first 'n_flows' flow keys in list 'keys' into table
|
||||
* 'swt', where 'keys' is a list of flow_key_entrys. key_entrys that are
|
||||
* inserted into the table are removed from the 'keys' list and placed in
|
||||
* 'added' list. Returns -1 if flow memory allocation fails, else returns the
|
||||
* number of flows that were actually inserted (some attempts might fail due to
|
||||
* collisions).
|
||||
*/
|
||||
|
||||
static int
|
||||
insert_flows(struct sw_table *swt, struct list_head *keys, struct list_head *added, int n_flows)
|
||||
{
|
||||
struct flow_key_entry *pos, *next;
|
||||
int cnt;
|
||||
|
||||
cnt = 0;
|
||||
|
||||
|
||||
list_for_each_entry_safe (pos, next, keys, node) {
|
||||
struct sw_flow *flow = flow_zalloc(0, GFP_KERNEL);
|
||||
if (flow == NULL) {
|
||||
unit_fail("Could only allocate %u flows", cnt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
flow->key = pos->key;
|
||||
|
||||
if (!swt->insert(swt, flow)) {
|
||||
flow_free(flow);
|
||||
list_del(&pos->node);
|
||||
} else {
|
||||
list_del(&pos->node);
|
||||
list_add(&pos->node, added);
|
||||
cnt++;
|
||||
if (n_flows != -1 && cnt == n_flows)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds and returns the flow_key_entry in list 'keys' matching the passed in
|
||||
* flow's key. If not found, returns NULL.
|
||||
*/
|
||||
|
||||
static struct flow_key_entry *
|
||||
find_flow(struct list_head *keys, struct sw_flow *flow)
|
||||
{
|
||||
struct flow_key_entry *pos;
|
||||
|
||||
list_for_each_entry(pos, keys, node) {
|
||||
if(!memcmp(&pos->key, &flow->key, sizeof(struct sw_flow_key)))
|
||||
return pos;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that all flow_key_entrys in list 'keys' return successful lookups on
|
||||
* the table 'swt'.
|
||||
*/
|
||||
|
||||
static int
|
||||
check_lookup(struct sw_table *swt, struct list_head *keys)
|
||||
{
|
||||
struct flow_key_entry *pos;
|
||||
|
||||
list_for_each_entry(pos, keys, node) {
|
||||
if(swt->lookup(swt, &pos->key) == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that all flow_key_entrys in list 'keys' DO NOT return successful
|
||||
* lookups in the 'swt' table.
|
||||
*/
|
||||
|
||||
static int
|
||||
check_no_lookup(struct sw_table *swt, struct list_head *keys)
|
||||
{
|
||||
struct flow_key_entry *pos;
|
||||
|
||||
list_for_each_entry(pos, keys, node) {
|
||||
if(swt->lookup(swt, &pos->key) != NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct check_iteration_state
|
||||
{
|
||||
int n_found;
|
||||
struct list_head *to_find;
|
||||
struct list_head *found;
|
||||
};
|
||||
|
||||
static int
|
||||
check_iteration_callback(struct sw_flow *flow, void *private)
|
||||
{
|
||||
struct check_iteration_state *s = private;
|
||||
struct flow_key_entry *entry;
|
||||
|
||||
entry = find_flow(s->to_find, flow);
|
||||
if (entry == NULL) {
|
||||
unit_fail("UNKNOWN ITERATOR FLOW %p", flow);
|
||||
rcu_read_unlock();
|
||||
return 1;
|
||||
}
|
||||
s->n_found++;
|
||||
list_del(&entry->node);
|
||||
list_add(&entry->node, s->found);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares an iterator's view of the 'swt' table to the list of
|
||||
* flow_key_entrys in 'to_find'. flow_key_entrys that are matched are removed
|
||||
* from the 'to_find' list and placed in the 'found' list. Returns -1 if the
|
||||
* iterator cannot be initialized or it encounters a flow with a key not in
|
||||
* 'to_find'. Else returns the number of flows found by the iterator
|
||||
* (i.e. there might still be flow keys in the 'to_find' list that were not
|
||||
* encountered by the iterator. it is up to the caller to determine if that is
|
||||
* acceptable behavior)
|
||||
*/
|
||||
|
||||
static int
|
||||
check_iteration(struct sw_table *swt, struct list_head *to_find, struct list_head *found)
|
||||
{
|
||||
struct sw_flow_key key;
|
||||
struct sw_table_position position;
|
||||
struct check_iteration_state state;
|
||||
|
||||
memset(&key, 0, sizeof key);
|
||||
key.wildcards = -1;
|
||||
|
||||
memset(&position, 0, sizeof position);
|
||||
|
||||
state.n_found = 0;
|
||||
state.to_find = to_find;
|
||||
state.found = found;
|
||||
|
||||
rcu_read_lock();
|
||||
swt->iterate(swt, &key, &position, check_iteration_callback, &state);
|
||||
rcu_read_unlock();
|
||||
|
||||
return state.n_found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes from table 'swt' keys from the list of flow_key_entrys 'keys'.
|
||||
* Removes flow_key_entrys of deleted flows from 'keys' and places them in the
|
||||
* 'deleted' list. If 'del_all' == 1, all flows in 'keys' will be deleted,
|
||||
* else only every third key will be deleted. Returns the number flows deleted
|
||||
* from the table.
|
||||
*/
|
||||
|
||||
static int
|
||||
delete_flows(struct sw_table *swt, struct list_head *keys,
|
||||
struct list_head *deleted, uint8_t del_all)
|
||||
{
|
||||
struct flow_key_entry *pos, *next;
|
||||
int i, n_del, total_del;
|
||||
|
||||
total_del = 0;
|
||||
i = 0;
|
||||
|
||||
list_for_each_entry_safe (pos, next, keys, node) {
|
||||
if (del_all == 1 || i % 3 == 0) {
|
||||
n_del = swt->delete(swt, &pos->key, 0, 0);
|
||||
if (n_del > 1) {
|
||||
unit_fail("%d flows deleted for one entry", n_del);
|
||||
unit_fail("\tfuture 'errors' could just be product duplicate flow_key_entries");
|
||||
unit_fail("THIS IS VERY UNLIKELY...SHOULDN'T HAPPEN OFTEN");
|
||||
}
|
||||
total_del += n_del;
|
||||
list_del(&pos->node);
|
||||
list_add(&pos->node, deleted);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return total_del;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that both iteration and lookups are consistent with the caller's view
|
||||
* of the table. In particular, checks that all keys in flow_key_entry list
|
||||
* 'deleted' do not show up in lookup or iteration, and keys in flow_key_entry
|
||||
* list 'added' do show up. 'tmp' should be an empty list that can be used for
|
||||
* iteration. References to list_head pointers are needed for 'added' and 'tmp'
|
||||
* because iteration will cause the list_heads to change. Function thus
|
||||
* switches 'added' to point to the list of added keys after the iteration.
|
||||
*/
|
||||
|
||||
static int
|
||||
check_lookup_and_iter(struct sw_table *swt, struct list_head *deleted,
|
||||
struct list_head **added, struct list_head **tmp)
|
||||
{
|
||||
struct list_head *tmp2;
|
||||
int ret;
|
||||
|
||||
if (check_no_lookup(swt, deleted) < 0) {
|
||||
unit_fail("Uninserted flows returning lookup");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (check_lookup(swt, *added) < 0) {
|
||||
unit_fail("Inserted flows not returning lookup");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = check_iteration(swt, *added, *tmp);
|
||||
|
||||
tmp2 = *added;
|
||||
*added = *tmp;
|
||||
*tmp = tmp2;
|
||||
|
||||
if ((*tmp)->next != *tmp) {
|
||||
unit_fail("WARNING: not all flows in 'added' found by iterator");
|
||||
unit_fail("\tcould be a product of duplicate flow_key_entrys, though should be VERY rare.");
|
||||
/* To avoid reoccurence */
|
||||
(*tmp)->next = (*tmp)->prev = *tmp;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies iteration and lookup after inserting 'n_flows', then after deleting
|
||||
* some flows, and once again after deleting all flows in table 'swt'.
|
||||
*/
|
||||
|
||||
static int
|
||||
iterator_test(struct sw_table *swt, int n_flows, uint32_t wildcards)
|
||||
{
|
||||
struct flow_key_entry *allocated, h1, h2;
|
||||
struct list_head *added, *deleted, *tmp;
|
||||
int ret, n_del, success;
|
||||
|
||||
INIT_LIST_HEAD(&h1.node);
|
||||
INIT_LIST_HEAD(&h2.node);
|
||||
|
||||
success = -1;
|
||||
|
||||
allocated = allocate_random_keys(n_flows, wildcards);
|
||||
if(allocated == NULL)
|
||||
return success;
|
||||
|
||||
deleted = &allocated->node;
|
||||
added = &h1.node;
|
||||
tmp = &h2.node;
|
||||
|
||||
ret = insert_flows(swt, deleted, added, -1);
|
||||
if (ret < 0)
|
||||
goto iterator_test_destr;
|
||||
|
||||
n_flows = ret;
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup after insertion");
|
||||
goto iterator_test_destr;
|
||||
} else if (ret != n_flows) {
|
||||
unit_fail("Iterator only found %d of %d flows",
|
||||
ret, n_flows);
|
||||
goto iterator_test_destr;
|
||||
}
|
||||
|
||||
n_del = delete_flows(swt, added, deleted, 0);
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup after some deletion");
|
||||
goto iterator_test_destr;
|
||||
} else if (ret + n_del != n_flows) {
|
||||
unit_fail("iterator after deletion inconsistent");
|
||||
unit_fail("\tn_del = %d, n_found = %d, n_flows = %d",
|
||||
n_del, ret, n_flows);
|
||||
goto iterator_test_destr;
|
||||
}
|
||||
|
||||
n_flows -= n_del;
|
||||
|
||||
n_del = delete_flows(swt, added, deleted, 1);
|
||||
if (n_del != n_flows) {
|
||||
unit_fail("Not all flows deleted - only %d of %d",
|
||||
n_del, n_flows);
|
||||
goto iterator_test_destr;
|
||||
}
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup after all deletion");
|
||||
goto iterator_test_destr;
|
||||
} else if (ret != 0) {
|
||||
unit_fail("Empty table iterator failed. %d flows found",
|
||||
ret);
|
||||
goto iterator_test_destr;
|
||||
}
|
||||
|
||||
success = 0;
|
||||
|
||||
iterator_test_destr:
|
||||
allocated->key.wildcards = OFPFW_ALL;
|
||||
swt->delete(swt, &allocated->key, 0, 0);
|
||||
vfree(allocated);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Checks lookup and iteration consistency after adding one flow, adding the
|
||||
* flow again, and then deleting the flow from table 'swt'.
|
||||
*/
|
||||
|
||||
static int
|
||||
add_test(struct sw_table *swt, uint32_t wildcards)
|
||||
{
|
||||
struct flow_key_entry *allocated, h1, h2;
|
||||
struct list_head *added, *deleted, *tmp, *tmp2;
|
||||
int ret, success = -1;
|
||||
|
||||
INIT_LIST_HEAD(&h1.node);
|
||||
INIT_LIST_HEAD(&h2.node);
|
||||
|
||||
allocated = allocate_random_keys(2, wildcards);
|
||||
if (allocated == NULL)
|
||||
return success;
|
||||
|
||||
deleted = &allocated->node;
|
||||
added = &h1.node;
|
||||
tmp = &h2.node;
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup before table modification");
|
||||
goto add_test_destr;
|
||||
} else if (ret != 0) {
|
||||
unit_fail("Iterator on empty table found %d flows",
|
||||
ret);
|
||||
goto add_test_destr;
|
||||
}
|
||||
|
||||
if (insert_flows(swt, deleted, added, 1) != 1) {
|
||||
unit_fail("Cannot add one flow to table");
|
||||
goto add_test_destr;
|
||||
}
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup after single add");
|
||||
goto add_test_destr;
|
||||
} else if (ret != 1) {
|
||||
unit_fail("Iterator on single add found %d flows",
|
||||
ret);
|
||||
goto add_test_destr;
|
||||
}
|
||||
|
||||
/* Re-adding flow */
|
||||
if (insert_flows(swt, added, tmp, 1) != 1) {
|
||||
unit_fail("Cannot insert same flow twice");
|
||||
goto add_test_destr;
|
||||
}
|
||||
|
||||
tmp2 = added;
|
||||
added = tmp;
|
||||
tmp = tmp2;
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup after double add");
|
||||
goto add_test_destr;
|
||||
} else if (ret != 1) {
|
||||
unit_fail("Iterator on double add found %d flows",
|
||||
ret);
|
||||
goto add_test_destr;
|
||||
}
|
||||
|
||||
ret = delete_flows(swt, added, deleted, 1);
|
||||
if (ret != 1) {
|
||||
unit_fail("Unexpected %d flows deleted", ret);
|
||||
goto add_test_destr;
|
||||
}
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup after delete.");
|
||||
goto add_test_destr;
|
||||
} else if (ret != 0) {
|
||||
unit_fail("unexpected %d flows found delete", ret);
|
||||
goto add_test_destr;
|
||||
}
|
||||
|
||||
success = 0;
|
||||
|
||||
add_test_destr:
|
||||
allocated->key.wildcards = OFPFW_ALL;
|
||||
swt->delete(swt, &allocated->key, 0, 0);
|
||||
vfree(allocated);
|
||||
return success;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks lookup and iteration consistency after each deleting a non-existent
|
||||
* flow, adding and then deleting a flow, adding the flow again, and then
|
||||
* deleting the flow twice in table 'swt'.
|
||||
*/
|
||||
|
||||
static int
|
||||
delete_test(struct sw_table *swt, uint32_t wildcards)
|
||||
{
|
||||
struct flow_key_entry *allocated, h1, h2;
|
||||
struct list_head *added, *deleted, *tmp, *tmp2;
|
||||
int i, ret, success = -1;
|
||||
|
||||
INIT_LIST_HEAD(&h1.node);
|
||||
INIT_LIST_HEAD(&h2.node);
|
||||
|
||||
allocated = allocate_random_keys(2, wildcards);
|
||||
if (allocated == NULL)
|
||||
return success;
|
||||
|
||||
/* Not really added...*/
|
||||
|
||||
added = &allocated->node;
|
||||
deleted = &h1.node;
|
||||
tmp = &h2.node;
|
||||
|
||||
ret = delete_flows(swt, added, deleted, 1);
|
||||
if (ret != 0) {
|
||||
unit_fail("Deleting non-existent keys from table returned unexpected value %d",
|
||||
ret);
|
||||
goto delete_test_destr;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
if (i == 0)
|
||||
unit_fail("Loop %d. Bad lookup before modification.", i);
|
||||
else
|
||||
unit_fail("Loop %d. Bad lookup after delete.", i);
|
||||
goto delete_test_destr;
|
||||
} else if (ret != 0) {
|
||||
if(i == 0)
|
||||
unit_fail("Loop %d. Unexpected %d flows found before modification",
|
||||
i, ret);
|
||||
else
|
||||
unit_fail("Loop %d. Unexpected %d flows found after delete",
|
||||
i, ret);
|
||||
goto delete_test_destr;
|
||||
}
|
||||
|
||||
if(i == 2)
|
||||
break;
|
||||
|
||||
if (insert_flows(swt, deleted, added, 1) != 1) {
|
||||
unit_fail("loop %d: cannot add flow to table", i);
|
||||
goto delete_test_destr;
|
||||
}
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("loop %d: bad lookup after single add.", i);
|
||||
goto delete_test_destr;
|
||||
} else if (ret != 1) {
|
||||
unit_fail("loop %d: unexpected %d flows found after single add",
|
||||
i, ret);
|
||||
goto delete_test_destr;
|
||||
}
|
||||
|
||||
ret = delete_flows(swt, added, deleted, 1);
|
||||
if (ret != 1) {
|
||||
unit_fail("loop %d: deleting inserted key from table returned unexpected value %d",
|
||||
i, ret);
|
||||
goto delete_test_destr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ret = delete_flows(swt, deleted, tmp, 1);
|
||||
|
||||
tmp2 = deleted;
|
||||
deleted = tmp2;
|
||||
tmp = tmp2;
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup after double delete.");
|
||||
goto delete_test_destr;
|
||||
} else if (ret != 0) {
|
||||
unit_fail("Unexpected %d flows found after double delete", ret);
|
||||
goto delete_test_destr;
|
||||
}
|
||||
|
||||
success = 0;
|
||||
|
||||
delete_test_destr:
|
||||
allocated->key.wildcards = OFPFW_ALL;
|
||||
swt->delete(swt, &allocated->key, 0, 0);
|
||||
vfree(allocated);
|
||||
return success;
|
||||
}
|
||||
|
||||
/*
|
||||
* Randomly adds and deletes from a set of size 'n_flows', looping for 'i'
|
||||
* iterations.
|
||||
*/
|
||||
|
||||
static int
|
||||
complex_add_delete_test(struct sw_table *swt, int n_flows, int i, uint32_t wildcards)
|
||||
{
|
||||
struct flow_key_entry *allocated, h1, h2;
|
||||
struct list_head *added, *deleted, *tmp;
|
||||
int cnt, ret, n_added, n_deleted, success = -1;
|
||||
uint8_t del_all;
|
||||
|
||||
INIT_LIST_HEAD(&h1.node);
|
||||
INIT_LIST_HEAD(&h2.node);
|
||||
|
||||
allocated = allocate_random_keys(n_flows, wildcards);
|
||||
if (allocated == NULL)
|
||||
return success;
|
||||
|
||||
deleted = &allocated->node;
|
||||
added = &h1.node;
|
||||
tmp = &h2.node;
|
||||
|
||||
n_deleted = n_flows;
|
||||
n_added = 0;
|
||||
|
||||
for (;i > 0; i--) {
|
||||
if (n_deleted != 0 && random32() % 2 == 0) {
|
||||
cnt = random32() % n_deleted;
|
||||
cnt = insert_flows(swt, deleted, added, cnt);
|
||||
if (cnt < 0)
|
||||
goto complex_test_destr;
|
||||
n_deleted -= cnt;
|
||||
n_added += cnt;
|
||||
} else {
|
||||
if (random32() % 7 == 0)
|
||||
del_all = 1;
|
||||
else
|
||||
del_all = 0;
|
||||
cnt = delete_flows(swt, added, deleted, del_all);
|
||||
n_deleted += cnt;
|
||||
n_added -= cnt;
|
||||
}
|
||||
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup on iteration %d.", i);
|
||||
goto complex_test_destr;
|
||||
}
|
||||
}
|
||||
|
||||
delete_flows(swt, added, deleted, 1);
|
||||
ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
|
||||
if (ret < 0) {
|
||||
unit_fail("Bad lookup on end deletion.");
|
||||
goto complex_test_destr;
|
||||
} else if (ret != 0) {
|
||||
unit_fail("Unexpected %d flows found on end deletion", ret);
|
||||
goto complex_test_destr;
|
||||
}
|
||||
|
||||
success = 0;
|
||||
|
||||
complex_test_destr:
|
||||
allocated->key.wildcards = OFPFW_ALL;
|
||||
swt->delete(swt, &allocated->key, 0, 0);
|
||||
vfree(allocated);
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
void run_table_t(void)
|
||||
{
|
||||
int linear_max, hash_buckets, hash2_buckets1;
|
||||
int hash2_buckets2, num_flows, num_iterations;
|
||||
int i;
|
||||
|
||||
struct sw_table *swt;
|
||||
|
||||
/* Most basic operations. */
|
||||
simple_insert_delete(table_linear_create(2048), 0);
|
||||
simple_insert_delete(table_hash_create(0x04C11DB7, 2048), 0);
|
||||
simple_insert_delete(table_hash2_create(0x04C11DB7, 2048,
|
||||
0x1EDC6F41, 2048), 0);
|
||||
|
||||
/* Linear table operations. */
|
||||
multiple_insert_destroy(table_linear_create(2048), 1024, 0, 0, 0);
|
||||
multiple_insert_destroy(table_linear_create(2048), 2048, 0, 0, 0);
|
||||
multiple_insert_destroy(table_linear_create(2048), 8192, 0,
|
||||
8192 - 2048, 8192 - 2048);
|
||||
|
||||
/* Hash table operations. */
|
||||
multiple_insert_destroy(table_hash_create(0x04C11DB7, 2048), 1024, 0,
|
||||
100, 300);
|
||||
multiple_insert_destroy(table_hash_create(0x04C11DB7, 2048), 2048, 0,
|
||||
500, 1000);
|
||||
multiple_insert_destroy(table_hash_create(0x04C11DB7, 1 << 20), 8192, 0,
|
||||
0, 50);
|
||||
multiple_insert_destroy(table_hash_create(0x04C11DB7, 1 << 20), 65536, 0,
|
||||
1500, 3000);
|
||||
|
||||
/* Hash table 2, two hash functions. */
|
||||
multiple_insert_destroy(table_hash2_create(0x04C11DB7, 2048,
|
||||
0x1EDC6F41, 2048), 1024, 0, 0, 20);
|
||||
multiple_insert_destroy(table_hash2_create(0x04C11DB7, 2048,
|
||||
0x1EDC6F41, 2048), 2048, 0, 50, 200);
|
||||
multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
|
||||
0x1EDC6F41, 1<<20), 8192, 0, 0, 20);
|
||||
multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
|
||||
0x1EDC6F41, 1<<20), 65536, 0, 0, 20);
|
||||
|
||||
/* Hash table 2, one hash function. */
|
||||
multiple_insert_destroy(table_hash2_create(0x04C11DB7, 2048,
|
||||
0x04C11DB7, 2048), 1024, 0, 0, 50);
|
||||
multiple_insert_destroy(table_hash2_create(0x04C11DB7, 2048,
|
||||
0x04C11DB7, 2048), 2048, 0, 100, 300);
|
||||
multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
|
||||
0x04C11DB7, 1<<20), 8192, 0, 0, 20);
|
||||
multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
|
||||
0x04C11DB7, 1<<20), 65536, 0, 0, 100);
|
||||
multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
|
||||
0x04C11DB7, 1<<20), 1<<16, 0, 0, 100);
|
||||
|
||||
linear_max = 2048;
|
||||
hash_buckets = 2048;
|
||||
hash2_buckets1 = 1024;
|
||||
hash2_buckets2 = 1024;
|
||||
|
||||
num_flows = 2300;
|
||||
num_iterations = 100;
|
||||
|
||||
printk("\nTesting on each table type:\n");
|
||||
printk(" iteration_test on 0 flows\n");
|
||||
printk(" iteration_test on %d flows\n", num_flows);
|
||||
printk(" add_test\n");
|
||||
printk(" delete_test\n");
|
||||
printk(" complex_add_delete_test with %d flows and %d iterations\n\n",
|
||||
num_flows, num_iterations);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
unsigned int mask = i == 0 ? : 0;
|
||||
|
||||
if (unit_failed())
|
||||
return;
|
||||
|
||||
mask = 0;
|
||||
switch (i) {
|
||||
case 0:
|
||||
swt = table_linear_create(linear_max);
|
||||
break;
|
||||
case 1:
|
||||
swt = table_hash_create (0x04C11DB7, hash_buckets);
|
||||
break;
|
||||
case 2:
|
||||
swt = table_hash2_create(0x04C11DB7, hash2_buckets1,
|
||||
0x1EDC6F41, hash2_buckets2);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
if (swt == NULL) {
|
||||
unit_fail("failed to allocate table %d", i);
|
||||
return;
|
||||
}
|
||||
printk("Testing %s table with %d buckets and %d max flows...\n",
|
||||
table_name(swt), hash_buckets, num_flows);
|
||||
iterator_test(swt, 0, mask);
|
||||
iterator_test(swt, num_flows, mask);
|
||||
add_test(swt, mask);
|
||||
delete_test(swt, mask);
|
||||
complex_add_delete_test(swt, num_flows, num_iterations, mask);
|
||||
swt->destroy(swt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
/forward_t.h
|
||||
/fwdhgen
|
||||
@@ -1,7 +0,0 @@
|
||||
noinst_HEADERS = forward_t.h
|
||||
|
||||
forward_t.h: gen_forward_t.pl example.pcap
|
||||
perl $(srcdir)/gen_forward_t.pl $(srcdir)/example.pcap > forward_t.h.tmp
|
||||
mv forward_t.h.tmp forward_t.h
|
||||
|
||||
EXTRA_DIST = gen_forward_t.pl example.pcap
|
||||
Binary file not shown.
@@ -1,80 +0,0 @@
|
||||
#! /usr/bin/perl
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
if (@ARGV != 1) {
|
||||
print "usage: $0 input.pcap > output.h\n";
|
||||
print "where input.pcap is a packet capture in pcap format\n";
|
||||
print "and output.c is a C header file containing the packets\n";
|
||||
exit(1);
|
||||
}
|
||||
my ($in_file_name) = $ARGV[0];
|
||||
open(INPUT, '<', $in_file_name) or die "$in_file_name: open: $!\n";
|
||||
|
||||
my ($file_header);
|
||||
if (read(INPUT, $file_header, 24) != 24) {
|
||||
die "$in_file_name: could not read pcap header\n";
|
||||
}
|
||||
|
||||
my ($s, $l);
|
||||
if (substr($file_header, 0, 4) eq pack('V', 0xa1b2c3d4)) {
|
||||
($s, $l) = ('v', 'V');
|
||||
} elsif (substr($file_header, 0, 4) eq pack('N', 0xa1b2c3d4)) {
|
||||
($s, $l) = ('n', 'N');
|
||||
} else {
|
||||
die "$in_file_name: not a pcap file\n";
|
||||
}
|
||||
|
||||
print <<'EOF';
|
||||
#ifndef DP_TEST_PACKETS_H
|
||||
#define DP_TEST_PACKETS_H 1
|
||||
|
||||
struct pkt {
|
||||
unsigned char *data;
|
||||
unsigned int len;
|
||||
};
|
||||
EOF
|
||||
|
||||
my ($n_packets) = 0;
|
||||
for (;;) {
|
||||
my ($pkt_hdr) = must_read(16);
|
||||
last if $pkt_hdr eq '';
|
||||
|
||||
my ($ts_sec, $ts_usec, $incl_len, $orig_len) = unpack("${l}4", $pkt_hdr);
|
||||
print STDERR "warning: captured less than len %u\n"
|
||||
if $incl_len < $orig_len;
|
||||
|
||||
my ($pkt) = must_read($incl_len);
|
||||
die "$in_file_name: unexpected end of file\n" if !$pkt;
|
||||
|
||||
print "\nstatic unsigned char p${n_packets}[] = {";
|
||||
my ($line_bytes) = 0;
|
||||
for my $c (map(ord($_), split(//, $pkt))) {
|
||||
if ($line_bytes++ % 13 == 0) {
|
||||
print "\n";
|
||||
}
|
||||
printf " 0x%02x,", $c;
|
||||
}
|
||||
print "\n};\n";
|
||||
$n_packets++;
|
||||
}
|
||||
|
||||
print "\nstatic int num_packets = $n_packets;\n";
|
||||
print "\nstatic struct pkt packets[] = {\n";
|
||||
for my $i (0..$n_packets - 1) {
|
||||
print " { p$i, sizeof p$i },\n";
|
||||
}
|
||||
print "};\n";
|
||||
|
||||
print "\n#endif\n";
|
||||
|
||||
sub must_read {
|
||||
my ($rq_bytes) = @_;
|
||||
my ($data);
|
||||
my ($nbytes) = read(INPUT, $data, $rq_bytes);
|
||||
die "$in_file_name: read: $!\n" if !defined $nbytes;
|
||||
die "$in_file_name: unexpected end of file\n"
|
||||
if $nbytes && $nbytes != $rq_bytes;
|
||||
return $data;
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/* A cheap knock-off of the pcap library to remove that dependency. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "ofp_pcap.h"
|
||||
|
||||
int
|
||||
ofp_pcap_open(struct ofp_pcap *p, const char *fname, char *errbuf)
|
||||
{
|
||||
FILE *fp;
|
||||
struct pcap_file_header hdr;
|
||||
size_t amt_read;
|
||||
|
||||
fp = fopen(fname, "r");
|
||||
|
||||
memset((char *)p, 0, sizeof(*p));
|
||||
|
||||
amt_read = fread((char *)&hdr, 1, sizeof(hdr), fp);
|
||||
if (amt_read != sizeof(hdr)) {
|
||||
snprintf(errbuf, OFP_PCAP_ERRBUF_SIZE, "error reading dump file");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (hdr.magic != TCPDUMP_MAGIC) {
|
||||
hdr.magic = SWAPLONG(hdr.magic);
|
||||
hdr.version_major = SWAPSHORT(hdr.version_major);
|
||||
hdr.version_minor = SWAPSHORT(hdr.version_minor);
|
||||
hdr.thiszone = SWAPLONG(hdr.thiszone);
|
||||
hdr.sigfigs = SWAPLONG(hdr.sigfigs);
|
||||
hdr.snaplen = SWAPLONG(hdr.snaplen);
|
||||
hdr.linktype = SWAPLONG(hdr.linktype);
|
||||
|
||||
p->swapped = 1;
|
||||
}
|
||||
|
||||
p->fp = fp;
|
||||
p->errbuf = errbuf;
|
||||
p->bufsize = hdr.snaplen+sizeof(struct pcap_pkthdr);
|
||||
p->buf = malloc(p->bufsize);
|
||||
if (!p->buf) {
|
||||
snprintf(errbuf, OFP_PCAP_ERRBUF_SIZE, "error allocating buffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (hdr.version_major < OFP_PCAP_VERSION_MAJOR) {
|
||||
snprintf(errbuf, OFP_PCAP_ERRBUF_SIZE, "archaic file format");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (p->buf)
|
||||
free(p->buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *
|
||||
ofp_pcap_next(struct ofp_pcap *p, struct pcap_pkthdr *hdr)
|
||||
{
|
||||
size_t amt_read;
|
||||
|
||||
amt_read = fread(hdr, 1, sizeof(*hdr), p->fp);
|
||||
if (amt_read != sizeof(*hdr)) {
|
||||
snprintf(p->errbuf, OFP_PCAP_ERRBUF_SIZE, "error reading dump file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (p->swapped) {
|
||||
hdr->caplen = SWAPLONG(hdr->caplen);
|
||||
hdr->len = SWAPLONG(hdr->len);
|
||||
hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
|
||||
hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
|
||||
}
|
||||
|
||||
if (hdr->caplen > p->bufsize) {
|
||||
snprintf(p->errbuf, OFP_PCAP_ERRBUF_SIZE, "error reading dump file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
amt_read = fread((char *)p->buf, 1, hdr->caplen, p->fp);
|
||||
if (amt_read != hdr->caplen){
|
||||
snprintf(p->errbuf, OFP_PCAP_ERRBUF_SIZE, "error reading dump file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p->buf;
|
||||
}
|
||||
|
||||
void
|
||||
ofp_pcap_close(struct ofp_pcap *p)
|
||||
{
|
||||
fclose(p->fp);
|
||||
free(p->buf);
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
#ifndef OFP_PCAP_H
|
||||
#define OFP_PCAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define OFP_PCAP_VERSION_MAJOR 2
|
||||
#define OFP_PCAP_VERSION_MINOR 4
|
||||
|
||||
#define TCPDUMP_MAGIC 0xa1b2c3d4
|
||||
|
||||
#define OFP_LINKTYPE_ETHERNET 1
|
||||
|
||||
#define OFP_PCAP_ERRBUF_SIZE 256
|
||||
|
||||
/* Swap the byte order regardless of the architecture */
|
||||
#define SWAPLONG(x) \
|
||||
((((x)&0xff)<<24) | (((x)&0xff00)<<8) | (((x)&0xff0000)>>8) | (((x)&0xff000000)>>24))
|
||||
#define SWAPSHORT(x) \
|
||||
((((x)&0xff)<<8) | (((x)&0xff00)>>8))
|
||||
|
||||
struct ofp_pcap {
|
||||
FILE *fp; /* File pointer to currently processed file */
|
||||
int swapped; /* Indicate whether endian-ness needs to change */
|
||||
char *buf; /* Buffer to hold packet data */
|
||||
size_t bufsize; /* Size of buffer */
|
||||
char *errbuf; /* Pointer to buffer to hold error message */
|
||||
};
|
||||
|
||||
struct pcap_file_header {
|
||||
uint32_t magic; /* Magic number */
|
||||
uint16_t version_major; /* Version number major */
|
||||
uint16_t version_minor; /* Version number minor */
|
||||
int32_t thiszone; /* Gmt to local correction */
|
||||
uint32_t sigfigs; /* Accuracy of timestamps */
|
||||
uint32_t snaplen; /* Max length saved portion of each pkt */
|
||||
uint32_t linktype; /* Data link type (LINKTYPE_*) */
|
||||
};
|
||||
|
||||
/*
|
||||
* This is a timeval as stored in disk in a dumpfile.
|
||||
* It has to use the same types everywhere, independent of the actual
|
||||
* `struct timeval'
|
||||
*/
|
||||
struct pcap_timeval {
|
||||
int32_t tv_sec; /* Seconds */
|
||||
int32_t tv_usec; /* Microseconds */
|
||||
};
|
||||
|
||||
/*
|
||||
* How a `pcap_pkthdr' is actually stored in the dumpfile.
|
||||
*/
|
||||
struct pcap_pkthdr {
|
||||
struct pcap_timeval ts; /* Time stamp */
|
||||
uint32_t caplen; /* Length of portion present */
|
||||
uint32_t len; /* Length this packet (off wire) */
|
||||
};
|
||||
|
||||
int ofp_pcap_open(struct ofp_pcap *p, const char *fname, char *errbuf);
|
||||
char *ofp_pcap_next(struct ofp_pcap *p, struct pcap_pkthdr *hdr);
|
||||
void ofp_pcap_close(struct ofp_pcap *p);
|
||||
|
||||
#endif /* ofp_pcap.h */
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Distributed under the terms of the GNU GPL version 2.
|
||||
* Copyright (c) 2007, 2008 The Board of Trustees of The Leland
|
||||
* Stanford Junior University
|
||||
*/
|
||||
|
||||
#include "table.h"
|
||||
#include "flow.h"
|
||||
#include "crc32.h"
|
||||
#include "forward.h"
|
||||
#include <linux/module.h>
|
||||
|
||||
EXPORT_SYMBOL(flow_alloc);
|
||||
EXPORT_SYMBOL(flow_cache);
|
||||
|
||||
EXPORT_SYMBOL(table_hash_create);
|
||||
EXPORT_SYMBOL(table_hash2_create);
|
||||
EXPORT_SYMBOL(table_linear_create);
|
||||
|
||||
EXPORT_SYMBOL(crc32_init);
|
||||
EXPORT_SYMBOL(crc32_calculate);
|
||||
|
||||
EXPORT_SYMBOL(flow_extract);
|
||||
EXPORT_SYMBOL(execute_setter);
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Distributed under the terms of the GNU GPL version 2.
|
||||
* Copyright (c) 2007, 2008 The Board of Trustees of The Leland
|
||||
* Stanford Junior University
|
||||
*/
|
||||
|
||||
#include <linux/autoconf.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
||||
static char run[1024];
|
||||
module_param_string(run, run, sizeof run, 0);
|
||||
MODULE_PARM_DESC(run_tests, "run=\"test1,[test2,...]\"\n");
|
||||
#else
|
||||
static char *run;
|
||||
MODULE_PARM(run, "s");
|
||||
#endif
|
||||
|
||||
static int test_failed;
|
||||
static const char *test_name;
|
||||
|
||||
void unit_fail_function(const char *function, const char *msg, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
printk("%s: FAIL: %s: ", test_name, function);
|
||||
va_start(args, msg);
|
||||
vprintk(msg, args);
|
||||
va_end(args);
|
||||
printk("\n");
|
||||
test_failed = 1;
|
||||
}
|
||||
|
||||
int unit_failed(void)
|
||||
{
|
||||
return test_failed;
|
||||
}
|
||||
|
||||
static int run_test(const char *name, size_t len)
|
||||
{
|
||||
static const struct test {
|
||||
const char *name;
|
||||
void (*func)(void);
|
||||
} tests[] = {
|
||||
#define UNIT_TEST(NAME) {#NAME, run_##NAME},
|
||||
UNIT_TESTS
|
||||
#undef UNIT_TEST
|
||||
};
|
||||
|
||||
const struct test *p;
|
||||
|
||||
for (p = tests; p < &tests[ARRAY_SIZE(tests)]; p++)
|
||||
if (len == strlen(p->name)
|
||||
&& !memcmp(name, p->name, len)) {
|
||||
test_name = p->name;
|
||||
test_failed = 0;
|
||||
p->func();
|
||||
printk("%s: %s\n", test_name,
|
||||
test_failed ? "FAIL" : "PASS");
|
||||
return !test_failed;
|
||||
}
|
||||
printk("unknown unit test %.*s\n", (int) len, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unit_init(void)
|
||||
{
|
||||
int n_pass = 0, n_fail = 0;
|
||||
char *p = run;
|
||||
|
||||
if (p == NULL) {
|
||||
p = "";
|
||||
}
|
||||
for (;;) {
|
||||
static const char white_space[] = " \t\r\n\v,";
|
||||
int len;
|
||||
|
||||
p += strspn(p, white_space);
|
||||
if (!*p)
|
||||
break;
|
||||
|
||||
len = strcspn(p, white_space);
|
||||
if (run_test(p, len))
|
||||
n_pass++;
|
||||
else
|
||||
n_fail++;
|
||||
p += len;
|
||||
}
|
||||
|
||||
if (n_pass + n_fail == 0)
|
||||
printk("no tests specified (use run=\"test1 [test2...]\")\n");
|
||||
else
|
||||
printk("%d tests passed, %d failed\n", n_pass, n_fail);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
module_init(unit_init);
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef UNIT_H
|
||||
#define UNIT_H 1
|
||||
|
||||
/* List of unit tests. */
|
||||
#define UNIT_TESTS \
|
||||
UNIT_TEST(table_t) \
|
||||
UNIT_TEST(crc_t) \
|
||||
UNIT_TEST(forward_t)
|
||||
|
||||
/* Prototype a function run_<NAME> for each of the unit tests. */
|
||||
#define UNIT_TEST(NAME) void run_##NAME(void);
|
||||
UNIT_TESTS
|
||||
#undef UNIT_TEST
|
||||
|
||||
void unit_fail_function(const char *function, const char *msg, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
#define unit_fail(...) unit_fail_function(__func__, __VA_ARGS__)
|
||||
|
||||
int unit_failed(void);
|
||||
|
||||
#endif /* unit.h */
|
||||
Reference in new issue
Block a user