2 * Distributed under the terms of the GNU GPL version 2.
3 * Copyright (c) 2007, 2008 The Board of Trustees of The Leland
4 * Stanford Junior University
7 #include <linux/skbuff.h>
8 #include <linux/if_ether.h>
9 #include <linux/if_vlan.h>
12 #include <linux/random.h>
13 #include <linux/tcp.h>
14 #include <linux/udp.h>
17 #include "tests/forward_t.h"
23 * Tests execute_settings() in forward.c to check that actions are
24 * appropriately taken on packets, meaning:
26 * 1. Checksums are correct.
27 * 2. Actions are only taken on compatible packets (IP action not taken on
29 * 3. Other packet data remains untouched.
31 * forward_t.h contains static packet definitions. forward_t.h should be
32 * generated using gen_forward_t.c. This test is run on whatever packets are
33 * defined in forward_t.h.
35 * NOTE: Tests assume packets in forward_t.h are present in full and IP and
36 * transport checksums are correct. (Can prevent offloading of checksum
37 * computation using ethtool.
41 * Sets 'a->data'. If 'key' != NULL, sets 'data' to equal 'key's value for type
42 * specified by 'a->type'. If 'key' == NULL, sets data to a random value.
46 set_action_data(struct sk_buff *skb, struct sw_flow_key *key, struct ofp_action *a)
49 switch(ntohs(a->type)) {
50 case(OFPAT_SET_DL_SRC):
51 memcpy(a->arg.dl_addr, key->dl_src, sizeof key->dl_src);
53 case(OFPAT_SET_DL_DST):
54 memcpy(a->arg.dl_addr, key->dl_dst, sizeof key->dl_dst);
56 case(OFPAT_SET_NW_SRC):
57 if (key->dl_type == htons(ETH_P_IP))
58 a->arg.nw_addr = key->nw_src;
60 a->arg.nw_addr = random32();
62 case(OFPAT_SET_NW_DST):
63 if (key->dl_type == htons(ETH_P_IP))
64 a->arg.nw_addr = key->nw_dst;
66 a->arg.nw_addr = random32();
68 case(OFPAT_SET_TP_SRC):
69 if (key->nw_proto == IPPROTO_TCP || key->nw_proto == IPPROTO_UDP)
70 a->arg.tp = key->tp_src;
72 a->arg.tp = (uint16_t) random32();
74 case(OFPAT_SET_TP_DST):
75 if (key->nw_proto == IPPROTO_TCP || key->nw_proto == IPPROTO_UDP)
76 a->arg.tp = key->tp_dst;
78 a->arg.tp = (uint16_t) random32();
84 ((uint32_t*)a->arg.dl_addr)[0] = random32();
85 ((uint16_t*)a->arg.dl_addr)[2] = random32();
91 * Checks the IP sum of an IP packet. Returns 0 if correct, else -1.
95 check_IP_csum(struct iphdr *ih)
97 uint16_t check, *data;
98 uint32_t n_bytes, sum;
102 data = (uint16_t*) ih;
104 n_bytes = ih->ihl * 4;
106 while (n_bytes > 1) {
108 sum = (sum >> 16) + (uint16_t)sum;
114 sum += *(uint8_t*)data;
115 sum = (sum >> 16) + (uint16_t)sum;
118 ih->check = htons((uint16_t)(~sum));
119 if (ih->check != check) {
120 unit_fail("IP checksum %hu does not match %hu",
121 ntohs(ih->check), ntohs(check));
126 * Partially computes TCP checksum over 'n_bytes' pointed to by 'data'. Can be
127 * called multiple times if data csum is to be computed on is fragmented. If
128 * 'is_last' == 0, assumes will be called again on more data and returns the
129 * value that should be passed in as 'incr_sum' on the next call. Else if
130 * 'is_last' == 1, returns the final checksum. On the first call, 'incr_sum'
131 * should equal 0. If 'is_last' == 0, 'n_bytes' must be even. i.e. Should
132 * first be called on pseudo header fields that are multiples of two, and then
136 compute_transport_checksum(uint16_t *data, uint32_t n_bytes,
137 uint32_t incr_sum, uint8_t is_last)
141 if (n_bytes % 2 != 0 && is_last == 0)
144 while (n_bytes > 1) {
145 incr_sum += ntohs(*data);
146 incr_sum = (incr_sum >> 16) + (uint16_t)incr_sum;
155 arr[0] = *(uint8_t*)data;
157 incr_sum += ntohs(*((uint16_t*)arr));
158 incr_sum = (incr_sum >> 16) + (uint16_t)incr_sum;
165 * Checks the transport layer's checksum of a packet. Returns '0' if correct,
166 * else '1'. 'ih' should point to the IP header of the packet, if TCP, 'th'
167 * should point the TCP header, and if UDP, 'uh' should point to the UDP
171 check_transport_csum(struct iphdr *ih, struct tcphdr *th,
178 tmp = compute_transport_checksum((uint16_t*)(&ih->saddr),
179 2 * sizeof ih->saddr, 0, 0);
181 arr[1] = ih->protocol;
182 tmp = compute_transport_checksum((uint16_t*)arr, 2, tmp, 0);
183 len = ntohs(ih->tot_len) - (ih->ihl * 4);
184 *((uint16_t*)arr) = htons(len);
185 tmp = compute_transport_checksum((uint16_t*)arr, 2, tmp, 0);
190 th->check = htons((uint16_t)compute_transport_checksum((uint16_t*)th,
192 if (th->check != check) {
193 unit_fail("TCP checksum %hu does not match %hu",
194 ntohs(th->check), ntohs(check));
197 } else if (uh != NULL) {
200 uh->check = htons((uint16_t)compute_transport_checksum((uint16_t*)uh,
202 if (uh->check != check) {
203 unit_fail("UDP checksum %hu does not match %hu",
204 ntohs(uh->check), ntohs(check));
214 * Compares 'pkt_len' bytes of 'data' to 'pkt'. excl_start and excl_end point
215 * together delineate areas of 'data' that are not supposed to match 'pkt'.
216 * 'num_excl' specify how many such areas exist. An 'excl_start' entry is
217 * ignored if it equals NULL. See 'check_packet()' for usage.
221 compare(uint8_t *data, uint8_t *pkt, uint32_t pkt_len,
222 uint8_t **excl_start, uint8_t **excl_end, uint32_t num_excl)
225 uint8_t *d, *p, *end;
228 end = data + pkt_len;
233 for (i = 0; i < num_excl; i++) {
234 if(*excl_start != NULL) {
235 if ((ret = memcmp(d, p, *excl_start - d)) != 0)
237 p += (*excl_end - d);
245 ret = memcmp(d, p, end - d);
248 unit_fail("skb and packet comparison failed:");
249 for (i = 0; i < pkt_len; i++) {
250 if (data[i] != pkt[i]) {
251 unit_fail("skb[%u] = 0x%x != 0x%x",
260 * Checks that a packet's data has remained consistent after an action has been
261 * applied. 'skb' is the modified packet, 'a' is the action that was taken on
262 * the packet, 'p' is a copy of the packet's data before action 'a' was taken.
263 * Checks that the action was in fact taken, that the checksums of the packet
264 * are correct, and that no other data in the packet was altered.
268 check_packet(struct sk_buff *skb, struct ofp_action *a, struct pkt *p)
274 uint8_t *excl_start[5], *excl_end[5];
281 memset(excl_start, 0, sizeof excl_start);
282 memset(excl_end, 0, sizeof excl_end);
284 if (eh->h_proto == htons(ETH_P_IP)) {
286 excl_start[1] = (uint8_t*)&ih->check;
287 excl_end[1] = (uint8_t*)(&ih->check + 1);
288 if (ih->protocol == IPPROTO_TCP) {
290 excl_start[4] = (uint8_t*)&th->check;
291 excl_end[4] = (uint8_t*)(&th->check + 1);
292 } else if (ih->protocol == IPPROTO_UDP) {
294 excl_start[4] = (uint8_t*)&uh->check;
295 excl_end[4] = (uint8_t*)(&uh->check + 1);
300 switch(ntohs(a->type)) {
301 case(OFPAT_SET_DL_SRC):
302 if (memcmp(a->arg.dl_addr, eh->h_source, sizeof eh->h_source) != 0) {
303 unit_fail("Source eth addr has not been set");
306 excl_start[0] = (uint8_t*)(&eh->h_source);
307 excl_end[0] = (uint8_t*)(&eh->h_proto);
309 case(OFPAT_SET_DL_DST):
310 if (memcmp(a->arg.dl_addr, eh->h_dest, sizeof eh->h_dest) != 0) {
311 unit_fail("Dest eth addr has not been set");
314 excl_start[0] = (uint8_t*)(&eh->h_dest);
315 excl_end[0] = (uint8_t*)(&eh->h_source);
317 case(OFPAT_SET_NW_SRC):
319 if (a->arg.nw_addr != ih->saddr) {
320 unit_fail("Source IP addr has not been set");
323 excl_start[2] = (uint8_t*)(&ih->saddr);
324 excl_end[2] = (uint8_t*)(&ih->saddr + 1);
327 case(OFPAT_SET_NW_DST):
329 if (a->arg.nw_addr != ih->daddr) {
330 unit_fail("Dest IP addr has not been set");
333 excl_start[2] = (uint8_t*)(&ih->daddr);
334 excl_end[2] = (uint8_t*)(&ih->daddr + 1);
337 case(OFPAT_SET_TP_SRC):
339 if (a->arg.tp != th->source) {
340 unit_fail("Source port has not been set");
343 excl_start[3] = (uint8_t*)(&th->source);
344 excl_end[3] = (uint8_t*)(&th->source + 1);
345 } else if (uh != NULL) {
346 if (a->arg.tp != uh->source) {
347 unit_fail("Source port has not been set");
350 excl_start[3] = (uint8_t*)(&uh->source);
351 excl_end[3] = (uint8_t*)(&uh->source + 1);
354 case(OFPAT_SET_TP_DST):
356 if (a->arg.tp != th->dest) {
357 unit_fail("Dest port has not been set");
360 excl_start[3] = (uint8_t*)(&th->dest);
361 excl_end[3] = (uint8_t*)(&th->dest + 1);
362 } else if (uh != NULL) {
363 if (a->arg.tp != uh->dest) {
364 unit_fail("Dest port has not been set");
367 excl_start[3] = (uint8_t*)(&uh->dest);
368 excl_end[3] = (uint8_t*)(&uh->dest + 1);
376 compare(skb->data, p->data, p->len, excl_start, excl_end, 5);
387 if (th == NULL && uh == NULL)
390 check_transport_csum(ih, th, uh);
394 * Layers 3 & 4 Tests: Given packets in forward_t.h, executes all actions
395 * with random data, checking for consistency described in check_packet().
401 struct ofp_action action;
404 struct sw_flow_key key;
409 for (i = 0; i < num_packets; i++) {
410 skb = alloc_skb(packets[i].len, GFP_KERNEL);
412 unit_fail("Couldn't allocate %uth skb", i);
416 memcpy(skb_put(skb, packets[i].len), packets[i].data,
419 skb_set_mac_header(skb, 0);
420 flow_extract(skb, 0, &key);
421 eth_proto = ntohs(key.dl_type);
423 check_packet(skb, NULL, packets+i);
427 for (a_type = OFPAT_SET_DL_SRC;
428 a_type <= OFPAT_SET_TP_DST;
431 action.type = htons(a_type);
432 set_action_data(skb, NULL, &action);
433 for(j = 0; j < 2; j++) {
434 skb = execute_setter(skb, eth_proto, &key, &action);
435 check_packet(skb, &action, packets+i);
437 unit_fail("Packet %u inconsistent "
438 "after setter on action "
439 "type %d, iteration %u",
443 set_action_data(skb, &key, &action);
454 printk("\nL3/L4 actions test passed.\n");
460 struct ofp_action action;
462 struct sw_flow_key key;
466 struct vlan_ethhdr *vh;
468 struct net_device dev;
469 uint16_t new_id, orig_id;
472 memset((char *)&dev, '\0', sizeof(dev));
474 printk("Testing vlan\n");
475 for (i = 0; i < num_packets; i++) {
476 skb = alloc_skb(packets[i].len, GFP_KERNEL);
478 unit_fail("Couldn't allocate %uth skb", i);
482 memcpy(skb_put(skb, packets[i].len), packets[i].data,
486 skb_set_mac_header(skb, 0);
487 flow_extract(skb, 0, &key);
488 eth_proto = ntohs(key.dl_type);
491 if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
492 unit_fail("Packet %u has incorrect checksum unmodified",
499 orig_id = eh->h_proto;
501 action.type = htons(OFPAT_SET_VLAN_VID);
503 // Add a random vlan tag
504 new_id = (uint16_t) random32() & VLAN_VID_MASK;
505 action.arg.vlan_vid = new_id;
506 skb = execute_setter(skb, eth_proto, &key, &action);
507 vh = vlan_eth_hdr(skb);
508 if (ntohs(vh->h_vlan_TCI) != new_id) {
509 unit_fail("add: vlan id doesn't match: %#x != %#x",
510 ntohs(vh->h_vlan_TCI), new_id);
513 flow_extract(skb, 0, &key);
515 if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
516 unit_fail("Packet %u has incorrect checksum after adding vlan",
523 new_id = (uint16_t) random32() & VLAN_VID_MASK;
524 action.arg.vlan_vid = new_id;
525 skb = execute_setter(skb, eth_proto, &key, &action);
526 vh = vlan_eth_hdr(skb);
527 if (ntohs(vh->h_vlan_TCI) != new_id) {
528 unit_fail("mod: vlan id doesn't match: %#x != %#x",
529 ntohs(vh->h_vlan_TCI), new_id);
532 flow_extract(skb, 0, &key);
534 if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
535 unit_fail("Packet %u has incorrect checksum after modifying vlan",
542 action.type = htons(OFPAT_STRIP_VLAN);
543 skb = execute_setter(skb, eth_proto, &key, &action);
547 if (eh->h_proto != orig_id) {
548 unit_fail("del: vlan id doesn't match: %#x != %#x",
549 ntohs(eh->h_proto), ntohs(orig_id));
553 if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
554 unit_fail("Packet %u has incorrect checksum after removing vlan",
569 printk("\nVLAN actions test passed.\n");
575 * Actual test: Given packets in forward_t.h, executes all actions with random
576 * data, checking for consistency described in check_packet().