c596abb59a1e5e2d8b1a1e21cd013376b67effe4
[sliver-openvswitch.git] / datapath / forward_t.c
1 /*
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
5  */
6
7 #include <linux/skbuff.h>
8 #include <linux/if_ether.h>
9 #include <linux/if_vlan.h>
10 #include <linux/in.h>
11 #include <linux/ip.h>
12 #include <linux/random.h>
13 #include <linux/tcp.h>
14 #include <linux/udp.h>
15
16 #include "forward.h"
17 #include "tests/forward_t.h"
18 #include "openflow.h"
19 #include "unit.h"
20 #include "flow.h"
21
22 /*
23  * Tests execute_settings() in forward.c to check that actions are
24  * appropriately taken on packets, meaning:
25  *
26  * 1. Checksums are correct.
27  * 2. Actions are only taken on compatible packets (IP action not taken on
28  * non-IP packet)
29  * 3. Other packet data remains untouched.
30
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.
34  *
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.
38  */
39
40 /*
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.
43  */
44
45 static void
46 set_action_data(struct sk_buff *skb, struct sw_flow_key *key, struct ofp_action *a)
47 {
48         if (key != NULL) {
49                 switch(ntohs(a->type)) {
50                 case(OFPAT_SET_DL_SRC):
51                         memcpy(a->arg.dl_addr, key->dl_src, sizeof key->dl_src);
52                         break;
53                 case(OFPAT_SET_DL_DST):
54                         memcpy(a->arg.dl_addr, key->dl_dst, sizeof key->dl_dst);
55                         break;
56                 case(OFPAT_SET_NW_SRC):
57                         if (key->dl_type == htons(ETH_P_IP))
58                                 a->arg.nw_addr = key->nw_src;
59                         else
60                                 a->arg.nw_addr = random32();
61                         break;
62                 case(OFPAT_SET_NW_DST):
63                         if (key->dl_type == htons(ETH_P_IP))
64                                 a->arg.nw_addr = key->nw_dst;
65                         else
66                                 a->arg.nw_addr = random32();
67                         break;
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;
71                         else
72                                 a->arg.tp = (uint16_t) random32();
73                         break;
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;
77                         else
78                                 a->arg.tp = (uint16_t) random32();
79                         break;
80                 default:
81                         BUG();
82                 }
83         } else {
84                 ((uint32_t*)a->arg.dl_addr)[0] = random32();
85                 ((uint16_t*)a->arg.dl_addr)[2] = random32();
86         }
87 }
88
89
90 /*
91  * Checks the IP sum of an IP packet.  Returns 0 if correct, else -1.
92  */
93
94 static void
95 check_IP_csum(struct iphdr *ih)
96 {
97         uint16_t check, *data;
98         uint32_t n_bytes, sum;
99
100         check = ih->check;
101         ih->check = 0;
102         data = (uint16_t*) ih;
103         sum = 0;
104         n_bytes = ih->ihl * 4;
105
106         while (n_bytes > 1) {
107                 sum += ntohs(*data);
108                 sum = (sum >> 16) + (uint16_t)sum;
109                 data++;
110                 n_bytes -= 2;
111         }
112
113         if (n_bytes == 1) {
114                 sum += *(uint8_t*)data;
115                 sum = (sum >> 16) + (uint16_t)sum;
116         }
117
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));
122         }
123 }
124
125 /*
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
133  * on the TCP packet.
134  */
135 static uint32_t
136 compute_transport_checksum(uint16_t *data, uint32_t n_bytes,
137                         uint32_t incr_sum, uint8_t is_last)
138 {
139         uint8_t arr[2];
140
141         if (n_bytes % 2 != 0 && is_last == 0)
142                 BUG();
143
144         while (n_bytes > 1) {
145                 incr_sum += ntohs(*data);
146                 incr_sum = (incr_sum >> 16) + (uint16_t)incr_sum;
147                 data++;
148                 n_bytes -= 2;
149         }
150
151         if (is_last == 0)
152                 return incr_sum;
153
154         if(n_bytes == 1) {
155                 arr[0] = *(uint8_t*)data;
156                 arr[1] = 0;
157                 incr_sum += ntohs(*((uint16_t*)arr));
158                 incr_sum = (incr_sum >> 16) + (uint16_t)incr_sum;
159         }
160
161         return ~incr_sum;
162 }
163
164 /*
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
168  * header.
169  */
170 static int
171 check_transport_csum(struct iphdr *ih, struct tcphdr *th,
172                          struct udphdr *uh)
173 {
174         uint32_t tmp;
175         uint16_t len, check;
176         uint8_t arr[2];
177
178         tmp = compute_transport_checksum((uint16_t*)(&ih->saddr),
179                                          2 * sizeof ih->saddr, 0, 0);
180         arr[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);
186
187         if (th != NULL) {
188                 check = th->check;
189                 th->check = 0;
190                 th->check = htons((uint16_t)compute_transport_checksum((uint16_t*)th,
191                                                                         len, tmp, 1));
192                 if (th->check != check) {
193                         unit_fail("TCP checksum %hu does not match %hu",
194                                   ntohs(th->check), ntohs(check));
195                         return -1;
196                 }
197         } else if (uh != NULL) {
198                 check = uh->check;
199                 uh->check = 0;
200                 uh->check = htons((uint16_t)compute_transport_checksum((uint16_t*)uh,
201                                                                         len, tmp, 1));
202                 if (uh->check != check) {
203                         unit_fail("UDP checksum %hu does not match %hu",
204                                   ntohs(uh->check), ntohs(check));
205                         return -1;
206                 }
207         }
208
209         return 0;
210 }
211
212
213 /*
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.
218  */
219
220 static void
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)
223 {
224         uint32_t i;
225         uint8_t *d, *p, *end;
226         int ret;
227
228         end = data + pkt_len;
229         d = data;
230         p = pkt;
231         ret = 0;
232
233         for (i = 0; i < num_excl; i++) {
234                 if(*excl_start != NULL) {
235                         if ((ret = memcmp(d, p, *excl_start - d)) != 0)
236                                 break;
237                         p += (*excl_end - d);
238                         d = *excl_end;
239                 }
240                 excl_start++;
241                 excl_end++;
242         }
243
244         if (ret == 0)
245                 ret = memcmp(d, p, end - d);
246
247         if (ret != 0) {
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",
252                                           i, data[i], pkt[i]);
253                         }
254                 }
255         }
256 }
257
258
259 /*
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.
265  */
266
267 static void
268 check_packet(struct sk_buff *skb, struct ofp_action *a, struct pkt *p)
269 {
270         struct ethhdr *eh;
271         struct iphdr *ih;
272         struct tcphdr *th;
273         struct udphdr *uh;
274         uint8_t *excl_start[5], *excl_end[5];
275
276         eh = eth_hdr(skb);
277         ih = NULL;
278         th = NULL;
279         uh = NULL;
280
281         memset(excl_start, 0, sizeof excl_start);
282         memset(excl_end, 0, sizeof excl_end);
283
284         if (eh->h_proto == htons(ETH_P_IP)) {
285                 ih = ip_hdr(skb);
286                 excl_start[1] = (uint8_t*)&ih->check;
287                 excl_end[1] = (uint8_t*)(&ih->check + 1);
288                 if (ih->protocol == IPPROTO_TCP) {
289                         th = tcp_hdr(skb);
290                         excl_start[4] = (uint8_t*)&th->check;
291                         excl_end[4] = (uint8_t*)(&th->check + 1);
292                 } else if (ih->protocol == IPPROTO_UDP) {
293                         uh = udp_hdr(skb);
294                         excl_start[4] = (uint8_t*)&uh->check;
295                         excl_end[4] = (uint8_t*)(&uh->check + 1);
296                 }
297         }
298
299         if (a != NULL) {
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");
304                                 return;
305                         }
306                         excl_start[0] = (uint8_t*)(&eh->h_source);
307                         excl_end[0] = (uint8_t*)(&eh->h_proto);
308                         break;
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");
312                                 return;
313                         }
314                         excl_start[0] = (uint8_t*)(&eh->h_dest);
315                         excl_end[0] = (uint8_t*)(&eh->h_source);
316                         break;
317                 case(OFPAT_SET_NW_SRC):
318                         if (ih != NULL) {
319                                 if (a->arg.nw_addr != ih->saddr) {
320                                         unit_fail("Source IP addr has not been set");
321                                         return;
322                                 }
323                                 excl_start[2] = (uint8_t*)(&ih->saddr);
324                                 excl_end[2] = (uint8_t*)(&ih->saddr + 1);
325                         }
326                         break;
327                 case(OFPAT_SET_NW_DST):
328                         if (ih != NULL) {
329                                 if (a->arg.nw_addr != ih->daddr) {
330                                         unit_fail("Dest IP addr has not been set");
331                                         return;
332                                 }
333                                 excl_start[2] = (uint8_t*)(&ih->daddr);
334                                 excl_end[2] = (uint8_t*)(&ih->daddr + 1);
335                         }
336                         break;
337                 case(OFPAT_SET_TP_SRC):
338                         if (th != NULL) {
339                                 if (a->arg.tp != th->source) {
340                                         unit_fail("Source port has not been set");
341                                         return;
342                                 }
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");
348                                         return;
349                                 }
350                                 excl_start[3] = (uint8_t*)(&uh->source);
351                                 excl_end[3] = (uint8_t*)(&uh->source + 1);
352                         }
353                         break;
354                 case(OFPAT_SET_TP_DST):
355                         if (th != NULL) {
356                                 if (a->arg.tp != th->dest) {
357                                         unit_fail("Dest port has not been set");
358                                         return;
359                                 }
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");
365                                         return;
366                                 }
367                                 excl_start[3] = (uint8_t*)(&uh->dest);
368                                 excl_end[3] = (uint8_t*)(&uh->dest + 1);
369                         }
370                         break;
371                 default:
372                         BUG();
373                 }
374         }
375
376         compare(skb->data, p->data, p->len, excl_start, excl_end, 5);
377         if (unit_failed())
378                 return;
379
380         if (ih == NULL)
381                 return;
382
383         check_IP_csum(ih);
384         if (unit_failed())
385                 return;
386
387         if (th == NULL && uh == NULL)
388                 return;
389
390         check_transport_csum(ih, th, uh);
391 }
392
393 /*
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().
396  */
397
398 void
399 test_l3_l4(void)
400 {
401         struct ofp_action action;
402         uint16_t a_type;
403         struct sk_buff *skb;
404         struct sw_flow_key key;
405         unsigned int i, j;
406         uint16_t eth_proto;
407         int ret = 0;
408
409         for (i = 0; i < num_packets; i++) {
410                 skb = alloc_skb(packets[i].len, GFP_KERNEL);
411                 if (!skb) {
412                         unit_fail("Couldn't allocate %uth skb", i);
413                         return;
414                 }
415
416                 memcpy(skb_put(skb, packets[i].len), packets[i].data,
417                                         packets[i].len);
418
419                 skb_set_mac_header(skb, 0);
420                 flow_extract(skb, 0, &key);
421                 eth_proto = ntohs(key.dl_type);
422
423                 check_packet(skb, NULL, packets+i);
424                 if (unit_failed())
425                         return;
426
427                 for (a_type = OFPAT_SET_DL_SRC;
428                          a_type <= OFPAT_SET_TP_DST;
429                          a_type++)
430                 {
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);
436                                 if (unit_failed()) {
437                                         unit_fail("Packet %u inconsistent "
438                                                   "after setter on action "
439                                                   "type %d, iteration %u",
440                                                   i, action.type, j);
441                                         return;
442                                 }
443                                 set_action_data(skb, &key, &action);
444                         }
445                 }
446
447                 kfree_skb(skb);
448
449                 if (ret != 0)
450                         break;
451         }
452
453         if (ret == 0)
454                 printk("\nL3/L4 actions test passed.\n");
455 }
456
457 int
458 test_vlan(void)
459 {
460         struct ofp_action action;
461         struct sk_buff *skb;
462         struct sw_flow_key key;
463         unsigned int i;
464         uint16_t eth_proto;
465         int ret = 0;
466         struct vlan_ethhdr *vh;
467         struct ethhdr *eh;
468         struct net_device dev;
469         uint16_t new_id, orig_id;
470
471
472         memset((char *)&dev, '\0', sizeof(dev));
473
474         printk("Testing vlan\n");
475         for (i = 0; i < num_packets; i++) {
476                 skb = alloc_skb(packets[i].len, GFP_KERNEL);
477                 if (!skb) {
478                         unit_fail("Couldn't allocate %uth skb", i);
479                         return -ENOMEM;
480                 }
481
482                 memcpy(skb_put(skb, packets[i].len), packets[i].data,
483                                         packets[i].len);
484                 skb->dev = &dev;
485
486                 skb_set_mac_header(skb, 0);
487                 flow_extract(skb, 0, &key);
488                 eth_proto = ntohs(key.dl_type);
489
490 #if 0
491                 if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
492                         unit_fail("Packet %u has incorrect checksum unmodified",
493                                         i);
494                         goto free_skb;
495                 }
496 #endif
497
498                 eh = eth_hdr(skb);
499                 orig_id = eh->h_proto;
500
501                 action.type = htons(OFPAT_SET_DL_VLAN);
502
503                 // Add a random vlan tag
504                 new_id = (uint16_t) random32() & VLAN_VID_MASK;
505                 action.arg.vlan_id = 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);
511                         return -1;
512                 }
513                 flow_extract(skb, 0, &key);
514 #if 0
515                 if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
516                         unit_fail("Packet %u has incorrect checksum after adding vlan",
517                                   i);
518                         goto free_skb;
519                 }
520 #endif
521
522                 // Modify the tag
523                 new_id = (uint16_t) random32() & VLAN_VID_MASK;
524                 action.arg.vlan_id = 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);
530                         return -1;
531                 }
532                 flow_extract(skb, 0, &key);
533 #if 0
534                 if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
535                         unit_fail("Packet %u has incorrect checksum after modifying vlan",
536                                   i);
537                         goto free_skb;
538                 }
539 #endif
540
541                 // Remove the tag
542                 action.arg.vlan_id = OFP_VLAN_NONE;
543                 skb = execute_setter(skb, eth_proto, &key, &action);
544
545                 eh = eth_hdr(skb);
546
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));
550                         return -1;
551                 }
552 #if 0
553                 if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
554                         unit_fail("Packet %u has incorrect checksum after removing vlan",
555                                   i);
556                         goto free_skb;
557                 }
558
559         free_skb:
560 #endif
561
562                 kfree_skb(skb);
563
564                 if (ret != 0)
565                         break;
566         }
567
568         if (ret == 0)
569                 printk("\nVLAN actions test passed.\n");
570
571         return ret;
572 }
573
574 /*
575  * Actual test:  Given packets in forward_t.h, executes all actions with random
576  * data, checking for consistency described in check_packet().
577  */
578
579 void
580 run_forward_t(void)
581 {
582         test_vlan();
583         test_l3_l4();
584 }