ovsdb-doc: Distinguish hyphens and minus signs in nroff output.
[sliver-openvswitch.git] / utilities / ovs-ofctl.c
index 2447ba2..d9ee607 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -48,6 +48,7 @@
 #include "timeval.h"
 #include "util.h"
 #include "vconn.h"
+#include "xtoxll.h"
 
 #include "vlog.h"
 #define THIS_MODULE VLM_ofctl
@@ -152,7 +153,7 @@ usage(void)
            "  dump-desc SWITCH            print switch description\n"
            "  dump-tables SWITCH          print table stats\n"
            "  mod-port SWITCH IFACE ACT   modify port behavior\n"
-           "  dump-ports SWITCH           print port statistics\n"
+           "  dump-ports SWITCH [PORT]    print port statistics\n"
            "  dump-flows SWITCH           print all flow entries\n"
            "  dump-flows SWITCH FLOW      print matching FLOWs\n"
            "  dump-aggregate SWITCH       print aggregate flow statistics\n"
@@ -339,7 +340,7 @@ dump_trivial_stats_transaction(const char *vconn_name, uint8_t stats_type)
 }
 
 static void
-do_show(int argc UNUSED, char *argv[])
+do_show(int argc OVS_UNUSED, char *argv[])
 {
     dump_trivial_transaction(argv[1], OFPT_FEATURES_REQUEST);
     dump_trivial_transaction(argv[1], OFPT_GET_CONFIG_REQUEST);
@@ -378,13 +379,13 @@ do_status(int argc, char *argv[])
 }
 
 static void
-do_dump_desc(int argc UNUSED, char *argv[])
+do_dump_desc(int argc OVS_UNUSED, char *argv[])
 {
     dump_trivial_stats_transaction(argv[1], OFPST_DESC);
 }
 
 static void
-do_dump_tables(int argc UNUSED, char *argv[])
+do_dump_tables(int argc OVS_UNUSED, char *argv[])
 {
     dump_trivial_stats_transaction(argv[1], OFPST_TABLE);
 }
@@ -404,6 +405,20 @@ str_to_u32(const char *str)
     return value;
 }
 
+static uint64_t
+str_to_u64(const char *str) 
+{
+    char *tail;
+    uint64_t value;
+
+    errno = 0;
+    value = strtoull(str, &tail, 0);
+    if (errno == EINVAL || errno == ERANGE || *tail) {
+        ovs_fatal(0, "invalid numeric format %s", str);
+    }
+    return value;
+}
+
 static void
 str_to_mac(const char *str, uint8_t mac[6]) 
 {
@@ -468,6 +483,48 @@ str_to_ip(const char *str_, uint32_t *ip)
     return n_wild;
 }
 
+static uint16_t
+str_to_port_no(const char *vconn_name, const char *str)
+{
+    struct ofpbuf *request, *reply;
+    struct ofp_switch_features *osf;
+    struct vconn *vconn;
+    int n_ports;
+    int port_idx;
+    unsigned int port_no;
+    
+
+    /* Check if the argument is a port index.  Otherwise, treat it as
+     * the port name. */
+    if (str_to_uint(str, 10, &port_no)) {
+        return port_no;
+    }
+
+    /* Send a "Features Request" to resolve the name into a number. */
+    make_openflow(sizeof(struct ofp_header), OFPT_FEATURES_REQUEST, &request);
+    open_vconn(vconn_name, &vconn);
+    run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
+
+    osf = reply->data;
+    n_ports = (reply->size - sizeof *osf) / sizeof *osf->ports;
+
+    for (port_idx = 0; port_idx < n_ports; port_idx++) {
+        /* Check argument as an interface name */
+        if (!strncmp((char *)osf->ports[port_idx].name, str,
+                    sizeof osf->ports[0].name)) {
+            break;
+        }
+    }
+    if (port_idx == n_ports) {
+        ovs_fatal(0, "couldn't find monitored port: %s", str);
+    }
+
+    ofpbuf_delete(reply);
+    vconn_close(vconn);
+
+    return port_idx;
+}
+
 static void *
 put_action(struct ofpbuf *b, size_t size, uint16_t type)
 {
@@ -580,6 +637,22 @@ str_to_action(char *str, struct ofpbuf *b)
             struct ofp_action_tp_port *ta;
             ta = put_action(b, sizeof *ta, OFPAT_SET_TP_DST);
             ta->tp_port = htons(str_to_u32(arg));
+        } else if (!strcasecmp(act, "mod_nw_tos")) {
+            struct ofp_action_nw_tos *nt;
+            nt = put_action(b, sizeof *nt, OFPAT_SET_NW_TOS);
+            nt->nw_tos = str_to_u32(arg);
+        } else if (!strcasecmp(act, "resubmit")) {
+            struct nx_action_resubmit *nar;
+            nar = put_action(b, sizeof *nar, OFPAT_VENDOR);
+            nar->vendor = htonl(NX_VENDOR_ID);
+            nar->subtype = htons(NXAST_RESUBMIT);
+            nar->in_port = htons(str_to_u32(arg));
+        } else if (!strcasecmp(act, "set_tunnel")) {
+            struct nx_action_set_tunnel *nast;
+            nast = put_action(b, sizeof *nast, OFPAT_VENDOR);
+            nast->vendor = htonl(NX_VENDOR_ID);
+            nast->subtype = htons(NXAST_SET_TUNNEL);
+            nast->tun_id = htonl(str_to_u32(arg));
         } else if (!strcasecmp(act, "output")) {
             put_output_action(b, str_to_u32(arg));
         } else if (!strcasecmp(act, "drop")) {
@@ -596,7 +669,7 @@ str_to_action(char *str, struct ofpbuf *b)
 
             /* Unless a numeric argument is specified, we send the whole
              * packet to the controller. */
-            if (arg && (strspn(act, "0123456789") == strlen(act))) {
+            if (arg && (strspn(arg, "0123456789") == strlen(arg))) {
                oao->max_len = htons(str_to_u32(arg));
             } else {
                 oao->max_len = htons(UINT16_MAX);
@@ -650,9 +723,10 @@ static bool
 parse_field(const char *name, const struct field **f_out) 
 {
 #define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
-    static const struct field fields[] = { 
+    static const struct field fields[] = {
         { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port), 0 },
         { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan), 0 },
+        { "dl_vlan_pcp", OFPFW_DL_VLAN_PCP, F_U8, F_OFS(dl_vlan_pcp), 0 },
         { "dl_src", OFPFW_DL_SRC, F_MAC, F_OFS(dl_src), 0 },
         { "dl_dst", OFPFW_DL_DST, F_MAC, F_OFS(dl_dst), 0 },
         { "dl_type", OFPFW_DL_TYPE, F_U16, F_OFS(dl_type), 0 },
@@ -661,6 +735,7 @@ parse_field(const char *name, const struct field **f_out)
         { "nw_dst", OFPFW_NW_DST_MASK, F_IP,
           F_OFS(nw_dst), OFPFW_NW_DST_SHIFT },
         { "nw_proto", OFPFW_NW_PROTO, F_U8, F_OFS(nw_proto), 0 },
+        { "nw_tos", OFPFW_NW_TOS, F_U8, F_OFS(nw_tos), 0 },
         { "tp_src", OFPFW_TP_SRC, F_U16, F_OFS(tp_src), 0 },
         { "tp_dst", OFPFW_TP_DST, F_U16, F_OFS(tp_dst), 0 },
         { "icmp_type", OFPFW_ICMP_TYPE, F_U16, F_OFS(icmp_type), 0 },
@@ -681,7 +756,8 @@ parse_field(const char *name, const struct field **f_out)
 static void
 str_to_flow(char *string, struct ofp_match *match, struct ofpbuf *actions,
             uint8_t *table_idx, uint16_t *out_port, uint16_t *priority, 
-            uint16_t *idle_timeout, uint16_t *hard_timeout)
+            uint16_t *idle_timeout, uint16_t *hard_timeout, 
+            uint64_t *cookie)
 {
     char *save_ptr = NULL;
     char *name;
@@ -702,14 +778,17 @@ str_to_flow(char *string, struct ofp_match *match, struct ofpbuf *actions,
     if (hard_timeout) {
         *hard_timeout = OFP_FLOW_PERMANENT;
     }
+    if (cookie) {
+        *cookie = 0;
+    }
     if (actions) {
         char *act_str = strstr(string, "action");
         if (!act_str) {
             ovs_fatal(0, "must specify an action");
         }
-        *(act_str-1) = '\0';
+        *act_str = '\0';
 
-        act_str = strchr(act_str, '=');
+        act_str = strchr(act_str + 1, '=');
         if (!act_str) {
             ovs_fatal(0, "must specify an action");
         }
@@ -750,6 +829,10 @@ str_to_flow(char *string, struct ofp_match *match, struct ofpbuf *actions,
                 *idle_timeout = atoi(value);
             } else if (hard_timeout && !strcmp(name, "hard_timeout")) {
                 *hard_timeout = atoi(value);
+            } else if (cookie && !strcmp(name, "cookie")) {
+                *cookie = str_to_u64(value);
+            } else if (!strcmp(name, "tun_id_wild")) {
+                wildcards |= NXFW_TUN_ID;
             } else if (parse_field(name, &f)) {
                 void *data = (char *) match + f->offset;
                 if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
@@ -788,7 +871,7 @@ do_dump_flows(int argc, char *argv[])
 
     req = alloc_stats_request(sizeof *req, OFPST_FLOW, &request);
     str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
-                &req->table_id, &out_port, NULL, NULL, NULL);
+                &req->table_id, &out_port, NULL, NULL, NULL, NULL);
     memset(&req->pad, 0, sizeof req->pad);
     req->out_port = htons(out_port);
 
@@ -804,7 +887,7 @@ do_dump_aggregate(int argc, char *argv[])
 
     req = alloc_stats_request(sizeof *req, OFPST_AGGREGATE, &request);
     str_to_flow(argc > 2 ? argv[2] : "", &req->match, NULL,
-                &req->table_id, &out_port, NULL, NULL, NULL);
+                &req->table_id, &out_port, NULL, NULL, NULL, NULL);
     memset(&req->pad, 0, sizeof req->pad);
     req->out_port = htons(out_port);
 
@@ -812,27 +895,29 @@ do_dump_aggregate(int argc, char *argv[])
 }
 
 static void
-do_add_flow(int argc UNUSED, char *argv[])
+do_add_flow(int argc OVS_UNUSED, char *argv[])
 {
     struct vconn *vconn;
     struct ofpbuf *buffer;
     struct ofp_flow_mod *ofm;
     uint16_t priority, idle_timeout, hard_timeout;
+    uint64_t cookie;
     struct ofp_match match;
 
     /* Parse and send.  str_to_flow() will expand and reallocate the data in
      * 'buffer', so we can't keep pointers to across the str_to_flow() call. */
     make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argv[2], &match, buffer,
-                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
+                NULL, NULL, &priority, &idle_timeout, &hard_timeout,
+                &cookie);
     ofm = buffer->data;
     ofm->match = match;
     ofm->command = htons(OFPFC_ADD);
+    ofm->cookie = htonll(cookie);
     ofm->idle_timeout = htons(idle_timeout);
     ofm->hard_timeout = htons(hard_timeout);
     ofm->buffer_id = htonl(UINT32_MAX);
     ofm->priority = htons(priority);
-    ofm->reserved = htonl(0);
 
     open_vconn(argv[1], &vconn);
     send_openflow_buffer(vconn, buffer);
@@ -840,7 +925,7 @@ do_add_flow(int argc UNUSED, char *argv[])
 }
 
 static void
-do_add_flows(int argc UNUSED, char *argv[])
+do_add_flows(int argc OVS_UNUSED, char *argv[])
 {
     struct vconn *vconn;
     FILE *file;
@@ -856,6 +941,7 @@ do_add_flows(int argc UNUSED, char *argv[])
         struct ofpbuf *buffer;
         struct ofp_flow_mod *ofm;
         uint16_t priority, idle_timeout, hard_timeout;
+        uint64_t cookie;
         struct ofp_match match;
 
         char *comment;
@@ -874,17 +960,18 @@ do_add_flows(int argc UNUSED, char *argv[])
         /* Parse and send.  str_to_flow() will expand and reallocate the data
          * in 'buffer', so we can't keep pointers to across the str_to_flow()
          * call. */
-        ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
+        make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
         str_to_flow(line, &match, buffer,
-                    NULL, NULL, &priority, &idle_timeout, &hard_timeout);
+                    NULL, NULL, &priority, &idle_timeout, &hard_timeout,
+                    &cookie);
         ofm = buffer->data;
         ofm->match = match;
         ofm->command = htons(OFPFC_ADD);
+        ofm->cookie = htonll(cookie);
         ofm->idle_timeout = htons(idle_timeout);
         ofm->hard_timeout = htons(hard_timeout);
         ofm->buffer_id = htonl(UINT32_MAX);
         ofm->priority = htons(priority);
-        ofm->reserved = htonl(0);
 
         send_openflow_buffer(vconn, buffer);
     }
@@ -893,9 +980,10 @@ do_add_flows(int argc UNUSED, char *argv[])
 }
 
 static void
-do_mod_flows(int argc UNUSED, char *argv[])
+do_mod_flows(int argc OVS_UNUSED, char *argv[])
 {
     uint16_t priority, idle_timeout, hard_timeout;
+    uint64_t cookie;
     struct vconn *vconn;
     struct ofpbuf *buffer;
     struct ofp_flow_mod *ofm;
@@ -905,7 +993,8 @@ do_mod_flows(int argc UNUSED, char *argv[])
      * 'buffer', so we can't keep pointers to across the str_to_flow() call. */
     make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argv[2], &match, buffer,
-                NULL, NULL, &priority, &idle_timeout, &hard_timeout);
+                NULL, NULL, &priority, &idle_timeout, &hard_timeout,
+                &cookie);
     ofm = buffer->data;
     ofm->match = match;
     if (strict) {
@@ -915,9 +1004,9 @@ do_mod_flows(int argc UNUSED, char *argv[])
     }
     ofm->idle_timeout = htons(idle_timeout);
     ofm->hard_timeout = htons(hard_timeout);
+    ofm->cookie = htonll(cookie);
     ofm->buffer_id = htonl(UINT32_MAX);
     ofm->priority = htons(priority);
-    ofm->reserved = htonl(0);
 
     open_vconn(argv[1], &vconn);
     send_openflow_buffer(vconn, buffer);
@@ -935,7 +1024,7 @@ static void do_del_flows(int argc, char *argv[])
     /* Parse and send. */
     ofm = make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
     str_to_flow(argc > 2 ? argv[2] : "", &ofm->match, NULL, NULL, 
-                &out_port, &priority, NULL, NULL);
+                &out_port, &priority, NULL, NULL, NULL);
     if (strict) {
         ofm->command = htons(OFPFC_DELETE_STRICT);
     } else {
@@ -946,7 +1035,6 @@ static void do_del_flows(int argc, char *argv[])
     ofm->buffer_id = htonl(UINT32_MAX);
     ofm->out_port = htons(out_port);
     ofm->priority = htons(priority);
-    ofm->reserved = htonl(0);
 
     open_vconn(argv[1], &vconn);
     send_openflow_buffer(vconn, buffer);
@@ -954,19 +1042,35 @@ static void do_del_flows(int argc, char *argv[])
 }
 
 static void
-do_monitor(int argc UNUSED, char *argv[])
+do_tun_cookie(int argc OVS_UNUSED, char *argv[])
+{
+    struct nxt_tun_id_cookie *tun_id_cookie;
+    struct ofpbuf *buffer;
+    struct vconn *vconn;
+
+    tun_id_cookie = make_openflow(sizeof *tun_id_cookie, OFPT_VENDOR, &buffer);
+
+    tun_id_cookie->vendor = htonl(NX_VENDOR_ID);
+    tun_id_cookie->subtype = htonl(NXT_TUN_ID_FROM_COOKIE);
+    tun_id_cookie->set = !strcmp(argv[2], "true");
+
+    open_vconn(argv[1], &vconn);
+    send_openflow_buffer(vconn, buffer);
+    vconn_close(vconn);
+}
+
+static void
+do_monitor(int argc OVS_UNUSED, char *argv[])
 {
     struct vconn *vconn;
 
     open_vconn(argv[1], &vconn);
     if (argc > 2) {
         int miss_send_len = atoi(argv[2]);
-        int send_flow_exp = argc > 3 ? atoi(argv[3]) : 0;
         struct ofp_switch_config *osc;
         struct ofpbuf *buf;
 
         osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &buf);
-        osc->flags = htons(send_flow_exp ? OFPC_SEND_FLOW_EXP : 0);
         osc->miss_send_len = htons(miss_send_len);
         send_openflow_buffer(vconn, buf);
     }
@@ -979,13 +1083,20 @@ do_monitor(int argc UNUSED, char *argv[])
 }
 
 static void
-do_dump_ports(int argc UNUSED, char *argv[])
+do_dump_ports(int argc, char *argv[])
 {
-    dump_trivial_stats_transaction(argv[1], OFPST_PORT);
+    struct ofp_port_stats_request *req;
+    struct ofpbuf *request;
+    uint16_t port;
+
+    req = alloc_stats_request(sizeof *req, OFPST_PORT, &request);
+    port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_NONE;
+    req->port_no = htons(port);
+    dump_stats_transaction(argv[1], request);
 }
 
 static void
-do_probe(int argc UNUSED, char *argv[])
+do_probe(int argc OVS_UNUSED, char *argv[])
 {
     struct ofpbuf *request;
     struct vconn *vconn;
@@ -1002,7 +1113,7 @@ do_probe(int argc UNUSED, char *argv[])
 }
 
 static void
-do_mod_port(int argc UNUSED, char *argv[])
+do_mod_port(int argc OVS_UNUSED, char *argv[])
 {
     struct ofpbuf *request, *reply;
     struct ofp_switch_features *osf;
@@ -1129,7 +1240,7 @@ do_ping(int argc, char *argv[])
 }
 
 static void
-do_benchmark(int argc UNUSED, char *argv[])
+do_benchmark(int argc OVS_UNUSED, char *argv[])
 {
     size_t max_payload = 65535 - sizeof(struct ofp_header);
     struct timeval start, end;
@@ -1172,7 +1283,7 @@ do_benchmark(int argc UNUSED, char *argv[])
 }
 
 static void
-do_help(int argc UNUSED, char *argv[] UNUSED)
+do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     usage();
 }
@@ -1189,7 +1300,8 @@ static const struct command all_commands[] = {
     { "add-flows", 2, 2, do_add_flows },
     { "mod-flows", 2, 2, do_mod_flows },
     { "del-flows", 1, 2, do_del_flows },
-    { "dump-ports", 1, 1, do_dump_ports },
+    { "tun-cookie", 2, 2, do_tun_cookie },
+    { "dump-ports", 1, 2, do_dump_ports },
     { "mod-port", 3, 3, do_mod_port },
     { "probe", 1, 1, do_probe },
     { "ping", 1, 2, do_ping },