Initial import
[sliver-openvswitch.git] / utilities / dpctl.c
1 /* Copyright (C) 2007 Board of Trustees, Leland Stanford Jr. University.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21
22 #include <errno.h>
23 #include <getopt.h>
24 #include <inttypes.h>
25 #include <netinet/in.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/time.h>
30
31 #include "command-line.h"
32 #include "compiler.h"
33 #include "buffer.h"
34 #include "dpif.h"
35 #ifdef HAVE_NETLINK
36 #include "netlink.h"
37 #include "openflow-netlink.h"
38 #endif
39 #include "util.h"
40 #include "socket-util.h"
41 #include "openflow.h"
42 #include "ofp-print.h"
43 #include "vconn.h"
44
45 #include "vlog.h"
46 #define THIS_MODULE VLM_DPCTL
47
48 static const char* ifconfigbin = "/sbin/ifconfig";
49
50 struct command {
51     const char *name;
52     int min_args;
53     int max_args;
54     void (*handler)(int argc, char *argv[]);
55 };
56
57 static struct command all_commands[];
58
59 static void usage(void) NO_RETURN;
60 static void parse_options(int argc, char *argv[]);
61
62 int main(int argc, char *argv[])
63 {
64     struct command *p;
65
66     set_program_name(argv[0]);
67     vlog_init();
68     parse_options(argc, argv);
69
70     argc -= optind;
71     argv += optind;
72     if (argc < 1)
73         fatal(0, "missing command name; use --help for help");
74
75     for (p = all_commands; p->name != NULL; p++) {
76         if (!strcmp(p->name, argv[0])) {
77             int n_arg = argc - 1;
78             if (n_arg < p->min_args)
79                 fatal(0, "'%s' command requires at least %d arguments",
80                       p->name, p->min_args);
81             else if (n_arg > p->max_args)
82                 fatal(0, "'%s' command takes at most %d arguments",
83                       p->name, p->max_args);
84             else {
85                 p->handler(argc, argv);
86                 exit(0);
87             }
88         }
89     }
90     fatal(0, "unknown command '%s'; use --help for help", argv[0]);
91
92     return 0;
93 }
94
95 static void
96 parse_options(int argc, char *argv[])
97 {
98     static struct option long_options[] = {
99         {"verbose", optional_argument, 0, 'v'},
100         {"help", no_argument, 0, 'h'},
101         {"version", no_argument, 0, 'V'},
102         {0, 0, 0, 0},
103     };
104     char *short_options = long_options_to_short_options(long_options);
105
106     for (;;) {
107         int indexptr;
108         int c;
109
110         c = getopt_long(argc, argv, short_options, long_options, &indexptr);
111         if (c == -1) {
112             break;
113         }
114
115         switch (c) {
116         case 'h':
117             usage();
118
119         case 'V':
120             printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
121             exit(EXIT_SUCCESS);
122
123         case 'v':
124             vlog_set_verbosity(optarg);
125             break;
126
127         case '?':
128             exit(EXIT_FAILURE);
129
130         default:
131             abort();
132         }
133     }
134     free(short_options);
135 }
136
137 static void
138 usage(void)
139 {
140     printf("%s: Datapath Utility\n"
141            "usage: %s [OPTIONS] COMMAND [ARG...]\n"
142            "\nAvailable commands:\n"
143            "  adddp DP_ID                 add a new datapath with ID DP_ID\n"
144            "  deldp DP_ID                 delete datapath DP_ID\n"
145            "  show DP                     show information about DP\n"
146            "  addif DP_ID IFACE           add IFACE as a port on DP_ID\n"
147            "  delif DP_ID IFACE           delete IFACE as a port on DP_ID\n"
148            "  monitor DP_ID               print packets received on DP_ID\n"
149            "  dump-tables DP_ID           print stats for all tables in DP_ID\n"
150            "  dump-flows DP_ID T_ID       print all flow entries in table T_ID of DP_ID\n"
151            "  dump-flows DP_ID T_ID FLOW  print matching FLOWs in table T_ID of DP_ID\n"
152            "  add-flows DP FILE           add flows from FILE to DP\n"
153            "  benchmark-nl DP_ID N SIZE   send N packets of SIZE bytes up netlink\n"
154            "\nOptions:\n"
155            "  -v, --verbose               set maximum verbosity level\n"
156            "  -h, --help                  display this help message\n"
157            "  -V, --version               display version information\n",
158            program_name, program_name);
159     exit(EXIT_SUCCESS);
160 }
161
162 static void run(int retval, const char *name) 
163 {
164     if (retval) {
165         fatal(retval, "%s", name);
166     }
167 }
168
169 static int  if_up(const char* intf)
170 {
171     char command[256];
172     snprintf(command, sizeof command, "%s %s up &> /dev/null",
173             ifconfigbin, intf);
174     return system(command);
175 }
176
177 static void do_add_dp(int argc UNUSED, char *argv[])
178 {
179     struct dpif dp;
180     run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
181     run(dpif_add_dp(&dp), "add_dp");
182     dpif_close(&dp);
183 }
184
185 static void do_del_dp(int argc UNUSED, char *argv[])
186 {
187     struct dpif dp;
188     run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
189     run(dpif_del_dp(&dp), "del_dp");
190     dpif_close(&dp);
191 }
192
193 static void do_show(int argc UNUSED, char *argv[])
194 {
195     struct dpif dp;
196     run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
197     run(dpif_show(&dp), "show");
198     dpif_close(&dp);
199 }
200
201 static void do_add_port(int argc UNUSED, char *argv[])
202 {
203     struct dpif dp;
204     if_up(argv[2]);
205     run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
206     run(dpif_add_port(&dp, argv[2]), "add_port");
207     dpif_close(&dp);
208 }
209
210 static void do_del_port(int argc UNUSED, char *argv[])
211 {
212     struct dpif dp;
213     run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
214     run(dpif_del_port(&dp, argv[2]), "del_port");
215     dpif_close(&dp);
216 }
217
218 #define BENCHMARK_INCR   100
219
220 static void do_benchmark_nl(int argc UNUSED, char *argv[])
221 {
222     struct dpif dp;
223     uint32_t num_packets, i, milestone;
224     struct timeval start, end;
225
226     run(dpif_open(atoi(argv[1]), true, &dp), "dpif_open");
227     num_packets = atoi(argv[2]);
228     milestone = BENCHMARK_INCR;
229     run(dpif_benchmark_nl(&dp, num_packets, atoi(argv[3])), "benchmark_nl");
230     if (gettimeofday(&start, NULL) == -1) {
231         run(errno, "gettimeofday");
232     }
233     for (i = 0; i < num_packets;i++) {
234         struct buffer *b;
235         run(dpif_recv_openflow(&dp, &b, true), "dpif_recv_openflow");
236         if (i == milestone) {
237             gettimeofday(&end, NULL);
238             printf("%u packets received in %f ms\n",
239                    BENCHMARK_INCR,
240                    (1000*(double)(end.tv_sec - start.tv_sec))
241                    + (.001*(end.tv_usec - start.tv_usec)));
242             milestone += BENCHMARK_INCR;
243             start = end;
244         }
245         buffer_delete(b);
246     }
247     gettimeofday(&end, NULL);
248     printf("%u packets received in %f ms\n",
249            i - (milestone - BENCHMARK_INCR),
250            (1000*(double)(end.tv_sec - start.tv_sec))
251            + (.001*(end.tv_usec - start.tv_usec)));
252
253     dpif_close(&dp);
254 }
255
256 static void do_monitor(int argc UNUSED, char *argv[])
257 {
258     struct dpif dp;
259     run(dpif_open(atoi(argv[1]), true, &dp), "dpif_open");
260     for (;;) {
261         struct buffer *b;
262         run(dpif_recv_openflow(&dp, &b, true), "dpif_recv_openflow");
263         ofp_print(stderr, b->data, b->size, 2);
264         buffer_delete(b);
265     }
266 }
267
268 static void do_dump_tables(int argc, char *argv[])
269 {
270     struct dpif dp;
271     run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
272     run(dpif_dump_tables(&dp), "dump_tables");
273     dpif_close(&dp);
274 }
275
276
277 static uint32_t
278 str_to_int(const char *str) 
279 {
280     uint32_t value;
281     if (sscanf(str, "%"SCNu32, &value) != 1) {
282         fatal(0, "invalid numeric format %s", str);
283     }
284     return value;
285 }
286
287 static void
288 str_to_mac(const char *str, uint8_t mac[6]) 
289 {
290     if (sscanf(str, "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8,
291                &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) {
292         fatal(0, "invalid mac address %s", str);
293     }
294 }
295
296 static void
297 str_to_ip(const char *str, uint32_t *ip) 
298 {
299     struct in_addr in_addr;
300     int retval;
301
302     retval = lookup_ip(str, &in_addr);
303     if (retval) {
304         fatal(0, "%s: could not convert to IP address", str);
305     }
306     *ip = in_addr.s_addr;
307 }
308
309 static void
310 str_to_action(const char *str, struct ofp_action *action) 
311 {
312     uint16_t port;
313
314     if (!strcasecmp(str, "flood")) {
315         port = OFPP_FLOOD;
316     } else if (!strcasecmp(str, "controller")) {
317         port = OFPP_CONTROLLER;
318     } else {
319         port = str_to_int(str);
320     }
321
322     memset(action, 0, sizeof *action);
323     action->type = OFPAT_OUTPUT;
324     action->arg.output.port = htons(port);
325 }
326
327 static void
328 str_to_flow(char *string, struct ofp_match *match, struct ofp_action *action)
329 {
330     struct field {
331         const char *name;
332         uint32_t wildcard;
333         enum { F_U8, F_U16, F_MAC, F_IP } type;
334         size_t offset;
335     };
336
337 #define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
338     static const struct field fields[] = { 
339         { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port) },
340         { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan) },
341         { "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src) },
342         { "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst) },
343         { "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type) },
344         { "nw_src", OFPFW_NW_SRC, F_IP, F_OFS(nw_src) },
345         { "nw_dst", OFPFW_NW_DST, F_IP, F_OFS(nw_dst) },
346         { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto) },
347         { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src) },
348         { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst) },
349     };
350
351     char *name, *value;
352     uint32_t wildcards;
353     bool got_action = false;
354
355     memset(match, 0, sizeof *match);
356     wildcards = OFPFW_ALL;
357     for (name = strtok(string, "="), value = strtok(NULL, " \t\n");
358          name && value;
359          name = strtok(NULL, "="), value = strtok(NULL, " \t\n"))
360     {
361         const struct field *f;
362         void *data;
363
364         if (action && !strcmp(name, "action")) {
365             got_action = true;
366             str_to_action(value, action);
367             continue;
368         }
369
370         for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
371             if (!strcmp(f->name, name)) {
372                 goto found;
373             }
374         }
375         fprintf(stderr, "%s: unknown field %s (fields are",
376                 program_name, name);
377         for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
378             if (f != fields) {
379                 putc(',', stderr);
380             }
381             fprintf(stderr, " %s", f->name);
382         }
383         fprintf(stderr, ")\n");
384         exit(1);
385
386     found:
387         data = (char *) match + f->offset;
388         if (!strcmp(value, "*")) {
389             wildcards |= f->wildcard;
390         } else {
391             wildcards &= ~f->wildcard;
392             if (f->type == F_U8) {
393                 *(uint8_t *) data = str_to_int(value);
394             } else if (f->type == F_U16) {
395                 *(uint16_t *) data = htons(str_to_int(value));
396             } else if (f->type == F_MAC) {
397                 str_to_mac(value, data);
398             } else if (f->type == F_IP) {
399                 str_to_ip(value, data);
400             } else {
401                 NOT_REACHED();
402             }
403         }
404     }
405     if (name && !value) {
406         fatal(0, "field %s missing value", name);
407     }
408     if (action && !got_action) {
409         fatal(0, "must specify an action");
410     }
411     match->wildcards = htons(wildcards);
412 }
413
414 static void do_dump_flows(int argc, char *argv[])
415 {
416     struct dpif dp;
417     struct ofp_match match, *matchp;
418     run(dpif_open(atoi(argv[1]), false, &dp), "dpif_open");
419     if (argc == 4) {
420         str_to_flow(argv[3], &match, NULL);
421         matchp = &match;
422     } else {
423         matchp = NULL;
424     }
425     run(dpif_dump_flows(&dp, atoi(argv[2]), matchp), "dump_flows");
426     dpif_close(&dp);
427 }
428
429 static void do_add_flows(int argc, char *argv[])
430 {
431     struct vconn *vconn;
432     char vconn_name[16];
433
434     FILE *file;
435     char line[1024];
436
437     int retval;
438
439     file = fopen(argv[2], "r");
440     if (file == NULL) {
441         fatal(errno, "%s: open", argv[2]);
442     }
443
444     sprintf(vconn_name, "nl:%d", atoi(argv[1]));
445     retval = vconn_open(vconn_name, &vconn);
446     if (retval) {
447         fatal(retval, "opening datapath");
448     }
449
450     while (fgets(line, sizeof line, file)) {
451         struct buffer *buffer;
452         struct ofp_flow_mod *ofm;
453         size_t size;
454
455         char *comment;
456
457         /* Delete comments. */
458         comment = strchr(line, '#');
459         if (comment) {
460             *comment = '\0';
461         }
462
463         /* Drop empty lines. */
464         if (line[strspn(line, " \t\n")] == '\0') {
465             continue;
466         }
467
468         size = sizeof *ofm + sizeof ofm->actions[0];
469         buffer = buffer_new(size);
470         ofm = buffer_put_uninit(buffer, size);
471
472         /* Parse. */
473         memset(ofm, 0, size);
474         ofm->header.type = OFPT_FLOW_MOD;
475         ofm->header.version = OFP_VERSION;
476         ofm->header.length = htons(size);
477         ofm->command = htons(OFPFC_ADD);
478         ofm->max_idle = htons(50);
479         ofm->buffer_id = htonl(UINT32_MAX);
480         ofm->group_id = htonl(0);
481         str_to_flow(line, &ofm->match, &ofm->actions[0]);
482
483         retval = vconn_send_wait(vconn, buffer);
484         if (retval) {
485             fatal(retval, "sending to datapath");
486         }
487     }
488     vconn_close(vconn);
489     fclose(file);
490 }
491
492 static void do_help(int argc UNUSED, char *argv[] UNUSED)
493 {
494     usage();
495 }
496
497 static struct command all_commands[] = {
498     { "add-dp", 1, 1, do_add_dp },
499     { "adddp", 1, 1, do_add_dp },
500
501     { "del-dp", 1, 1, do_del_dp },
502     { "deldp", 1, 1, do_del_dp },
503
504     { "show", 1, 1, do_show },
505
506     { "add-port", 2, 2, do_add_port },
507     { "addif", 2, 2, do_add_port },
508
509     { "del-port", 2, 2, do_del_port },
510     { "delif", 2, 2, do_del_port },
511
512     { "help", 0, INT_MAX, do_help },
513     { "monitor", 1, 1, do_monitor },
514     { "dump-tables", 1, 1, do_dump_tables },
515     { "dump-flows", 2, 3, do_dump_flows },
516     { "add-flows", 2, 2, do_add_flows },
517
518     { "benchmark-nl", 3, 3, do_benchmark_nl },
519 };