+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * 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);
- }
-}
-