For SNAT, don't store the pre-fragment L2 header before actions are applied.
[sliver-openvswitch.git] / utilities / dpctl.c
index 8599a16..923e048 100644 (file)
 #ifdef HAVE_NETLINK
 #include "netdev.h"
 #include "netlink.h"
-#include "openflow-netlink.h"
+#include "openflow/openflow-netlink.h"
 #endif
 
 #include "command-line.h"
 #include "compiler.h"
 #include "dpif.h"
-#include "nicira-ext.h"
+#include "openflow/nicira-ext.h"
 #include "ofp-print.h"
 #include "ofpbuf.h"
-#include "openflow.h"
+#include "openflow/openflow.h"
 #include "packets.h"
 #include "random.h"
 #include "socket-util.h"
@@ -176,7 +176,8 @@ parse_options(int argc, char *argv[], struct settings *s)
             usage();
 
         case 'V':
-            printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
+            printf("%s %s compiled "__DATE__" "__TIME__"\n",
+                   program_name, VERSION BUILDNR);
             exit(EXIT_SUCCESS);
 
         case 'v':
@@ -222,13 +223,16 @@ usage(void)
            "  dump-flows SWITCH FLOW      print matching FLOWs\n"
            "  dump-aggregate SWITCH       print aggregate flow statistics\n"
            "  dump-aggregate SWITCH FLOW  print aggregate stats for FLOWs\n"
+#ifdef SUPPORT_SNAT
            "  add-snat SWITCH IFACE IP    add SNAT config to IFACE\n"
            "  del-snat SWITCH IFACE       delete SNAT config on IFACE\n"
+#endif
            "  add-flow SWITCH FLOW        add flow described by FLOW\n"
            "  add-flows SWITCH FILE       add flows from FILE\n"
            "  mod-flows SWITCH FLOW       modify actions of matching FLOWs\n"
            "  del-flows SWITCH [FLOW]     delete matching FLOWs\n"
            "  monitor SWITCH              print packets received from SWITCH\n"
+           "  execute SWITCH CMD [ARG...] execute CMD with ARGS on SWITCH\n"
            "\nFor local datapaths, remote switches, and controllers:\n"
            "  probe VCONN                 probe whether VCONN is up\n"
            "  ping VCONN [N]              latency of N-byte echos\n"
@@ -236,11 +240,10 @@ usage(void)
            "where each SWITCH is an active OpenFlow connection method.\n",
            program_name, program_name);
     vconn_usage(true, false, false);
-    printf("\nOptions:\n"
+    vlog_usage();
+    printf("\nOther options:\n"
            "  --strict                    use strict match for flow commands\n"
            "  -t, --timeout=SECS          give up after SECS seconds\n"
-           "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
-           "  -v, --verbose               set maximum verbosity level\n"
            "  -h, --help                  display this help message\n"
            "  -V, --version               display version information\n");
     exit(EXIT_SUCCESS);
@@ -617,6 +620,7 @@ str_to_action(char *str, struct ofp_action_header *actions,
             ah->type = htons(OFPAT_STRIP_VLAN);
         } else if (!strcasecmp(act, "output")) {
             port = str_to_int(arg);
+#ifdef SUPPORT_SNAT
         } else if (!strcasecmp(act, "nat")) {
             struct nx_action_snat *sa = (struct nx_action_snat *)ah;
 
@@ -633,6 +637,7 @@ str_to_action(char *str, struct ofp_action_header *actions,
             sa->vendor = htonl(NX_VENDOR_ID);
             sa->subtype = htons(NXAST_SNAT);
             sa->port = htons(str_to_int(arg));
+#endif
         } else if (!strcasecmp(act, "TABLE")) {
             port = OFPP_TABLE;
         } else if (!strcasecmp(act, "NORMAL")) {
@@ -737,6 +742,8 @@ parse_field(const char *name, const struct field **f_out)
         { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto) },
         { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src) },
         { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst) },
+        { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type) },
+        { "icmp_code", OFPFW_ICMP_CODE, F_U16, F_OFS(icmp_code) }
     };
     const struct field *f;
 
@@ -753,7 +760,7 @@ parse_field(const char *name, const struct field **f_out)
 static void
 str_to_flow(char *string, struct ofp_match *match, 
             struct ofp_action_header *actions, size_t *actions_len, 
-            uint8_t *table_idx, uint16_t *priority, 
+            uint8_t *table_idx, uint16_t *out_port, uint16_t *priority, 
             uint16_t *idle_timeout, uint16_t *hard_timeout)
 {
 
@@ -763,6 +770,9 @@ str_to_flow(char *string, struct ofp_match *match,
     if (table_idx) {
         *table_idx = 0xff;
     }
+    if (out_port) {
+        *out_port = OFPP_NONE;
+    }
     if (priority) {
         *priority = OFP_DEFAULT_PRIORITY;
     }
@@ -812,6 +822,8 @@ str_to_flow(char *string, struct ofp_match *match,
         
             if (table_idx && !strcmp(name, "table")) {
                 *table_idx = atoi(value);
+            } else if (out_port && !strcmp(name, "out_port")) {
+                *out_port = atoi(value);
             } else if (priority && !strcmp(name, "priority")) {
                 *priority = atoi(value);
             } else if (idle_timeout && !strcmp(name, "idle_timeout")) {
@@ -847,12 +859,14 @@ str_to_flow(char *string, struct ofp_match *match,
 static void do_dump_flows(const struct settings *s, int argc, char *argv[])
 {
     struct ofp_flow_stats_request *req;
+    uint16_t out_port;
     struct ofpbuf *request;
 
     req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
     str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0, 
-                &req->table_id, NULL, NULL, NULL);
-    memset(req->pad, 0, sizeof req->pad);
+                &req->table_id, &out_port, NULL, NULL, NULL);
+    memset(&req->pad, 0, sizeof req->pad);
+    req->out_port = htons(out_port);
 
     dump_stats_transaction(argv[1], request);
 }
@@ -862,15 +876,18 @@ static void do_dump_aggregate(const struct settings *s, int argc,
 {
     struct ofp_aggregate_stats_request *req;
     struct ofpbuf *request;
+    uint16_t out_port;
 
     req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
     str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL, 0,
-                &req->table_id, NULL, NULL, NULL);
-    memset(req->pad, 0, sizeof req->pad);
+                &req->table_id, &out_port, NULL, NULL, NULL);
+    memset(&req->pad, 0, sizeof req->pad);
+    req->out_port = htons(out_port);
 
     dump_stats_transaction(argv[1], request);
 }
 
+#ifdef SUPPORT_SNAT
 static void do_add_snat(const struct settings *s, int argc, char *argv[])
 {
     struct vconn *vconn;
@@ -920,6 +937,7 @@ static void do_del_snat(const struct settings *s, int argc, char *argv[])
     send_openflow_buffer(vconn, buffer);
     vconn_close(vconn);
 }
+#endif /* SUPPORT_SNAT */
 
 static void do_add_flow(const struct settings *s, int argc, char *argv[])
 {
@@ -934,7 +952,7 @@ static void do_add_flow(const struct settings *s, int argc, char *argv[])
     size = sizeof *ofm + actions_len;
     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len, 
-                NULL, &priority, &idle_timeout, &hard_timeout);
+                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
     ofm->command = htons(OFPFC_ADD);
     ofm->idle_timeout = htons(idle_timeout);
     ofm->hard_timeout = htons(hard_timeout);
@@ -986,7 +1004,7 @@ static void do_add_flows(const struct settings *s, int argc, char *argv[])
         size = sizeof *ofm + actions_len;
         ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
         str_to_flow(line, &ofm->match, &ofm->actions[0], &actions_len, 
-                    NULL, &priority, &idle_timeout, &hard_timeout);
+                    NULL, NULL, &priority, &idle_timeout, &hard_timeout);
         ofm->command = htons(OFPFC_ADD);
         ofm->idle_timeout = htons(idle_timeout);
         ofm->hard_timeout = htons(hard_timeout);
@@ -1016,7 +1034,7 @@ static void do_mod_flows(const struct settings *s, int argc, char *argv[])
     size = sizeof *ofm + actions_len;
     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argv[2], &ofm->match, &ofm->actions[0], &actions_len, 
-                NULL, &priority, &idle_timeout, &hard_timeout);
+                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
     if (s->strict) {
         ofm->command = htons(OFPFC_MODIFY_STRICT);
     } else {
@@ -1040,6 +1058,7 @@ static void do_del_flows(const struct settings *s, int argc, char *argv[])
 {
     struct vconn *vconn;
     uint16_t priority;
+    uint16_t out_port;
     struct ofpbuf *buffer;
     struct ofp_flow_mod *ofm;
     size_t size;
@@ -1048,7 +1067,7 @@ static void do_del_flows(const struct settings *s, int argc, char *argv[])
     size = sizeof *ofm;
     ofm = make_openflow(size, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, 0, NULL, 
-                &priority, NULL, NULL);
+                &out_port, &priority, NULL, NULL);
     if (s->strict) {
         ofm->command = htons(OFPFC_DELETE_STRICT);
     } else {
@@ -1057,6 +1076,7 @@ static void do_del_flows(const struct settings *s, int argc, char *argv[])
     ofm->idle_timeout = htons(0);
     ofm->hard_timeout = htons(0);
     ofm->buffer_id = htonl(UINT32_MAX);
+    ofm->out_port = htons(out_port);
     ofm->priority = htons(priority);
     ofm->reserved = htonl(0);
 
@@ -1104,7 +1124,7 @@ do_probe(const struct settings *s, int argc, char *argv[])
     make_openflow(sizeof(struct ofp_header), OFPT_ECHO_REQUEST, &request);
     open_vconn(argv[1], &vconn);
     run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
-    if (reply->size != request->size) {
+    if (reply->size != sizeof(struct ofp_header)) {
         ofp_fatal(0, "reply does not match request");
     }
     ofpbuf_delete(reply);
@@ -1281,6 +1301,71 @@ do_benchmark(const struct settings *s, int argc, char *argv[])
            count * message_size / (duration / 1000.0));
 }
 
+static void
+do_execute(const struct settings *s, int argc, char *argv[])
+{
+    struct vconn *vconn;
+    struct ofpbuf *request;
+    struct nicira_header *nicira;
+    struct nx_command_reply *ncr;
+    uint32_t xid;
+    int i;
+
+    nicira = make_openflow(sizeof *nicira, OFPT_VENDOR, &request);
+    xid = nicira->header.xid;
+    nicira->vendor = htonl(NX_VENDOR_ID);
+    nicira->subtype = htonl(NXT_COMMAND_REQUEST);
+    ofpbuf_put(request, argv[2], strlen(argv[2]));
+    for (i = 3; i < argc; i++) {
+        ofpbuf_put_zeros(request, 1);
+        ofpbuf_put(request, argv[i], strlen(argv[i]));
+    }
+    update_openflow_length(request);
+
+    open_vconn(argv[1], &vconn);
+    run(vconn_send_block(vconn, request), "send");
+
+    for (;;) {
+        struct ofpbuf *reply;
+        uint32_t status;
+
+        run(vconn_recv_xid(vconn, xid, &reply), "recv_xid");
+        if (reply->size < sizeof *ncr) {
+            ofp_fatal(0, "reply is too short (%zu bytes < %zu bytes)",
+                      reply->size, sizeof *ncr);
+        }
+        ncr = reply->data;
+        if (ncr->nxh.header.type != OFPT_VENDOR
+            || ncr->nxh.vendor != htonl(NX_VENDOR_ID)
+            || ncr->nxh.subtype != htonl(NXT_COMMAND_REPLY)) {
+            ofp_fatal(0, "reply is invalid");
+        }
+
+        status = ntohl(ncr->status);
+        if (status & NXT_STATUS_STARTED) {
+            /* Wait for a second reply. */
+            continue;
+        } else if (status & NXT_STATUS_EXITED) {
+            fprintf(stderr, "process terminated normally with exit code %d",
+                    status & NXT_STATUS_EXITSTATUS);
+        } else if (status & NXT_STATUS_SIGNALED) {
+            fprintf(stderr, "process terminated by signal %d",
+                    status & NXT_STATUS_TERMSIG);
+        } else if (status & NXT_STATUS_ERROR) {
+            fprintf(stderr, "error executing command");
+        } else {
+            fprintf(stderr, "process terminated for unknown reason");
+        }
+        if (status & NXT_STATUS_COREDUMP) {
+            fprintf(stderr, " (core dumped)");
+        }
+        putc('\n', stderr);
+
+        fwrite(ncr + 1, reply->size - sizeof *ncr, 1, stdout);
+        break;
+    }
+}
+
 static void do_help(const struct settings *s, int argc UNUSED, 
         char *argv[] UNUSED)
 {
@@ -1304,8 +1389,10 @@ static struct command all_commands[] = {
     { "dump-tables", 1, 1, do_dump_tables },
     { "dump-flows", 1, 2, do_dump_flows },
     { "dump-aggregate", 1, 2, do_dump_aggregate },
+#ifdef SUPPORT_SNAT
     { "add-snat", 3, 3, do_add_snat },
     { "del-snat", 2, 2, do_del_snat },
+#endif
     { "add-flow", 2, 2, do_add_flow },
     { "add-flows", 2, 2, do_add_flows },
     { "mod-flows", 2, 2, do_mod_flows },
@@ -1315,5 +1402,6 @@ static struct command all_commands[] = {
     { "probe", 1, 1, do_probe },
     { "ping", 1, 2, do_ping },
     { "benchmark", 3, 3, do_benchmark },
+    { "execute", 2, INT_MAX, do_execute },
     { NULL, 0, 0, NULL },
 };