Implement new OpenFlow "switch statistics" feature.
[openvswitch] / lib / ofp-print.c
index 01e49b78ce4606cf53ca5167fb7782bf00232240..d758680f8f38ff6f2a31b011d6979a7097c1826f 100644 (file)
 #include <stdlib.h>
 #include <ctype.h>
 
+#include "buffer.h"
 #include "compiler.h"
 #include "dynamic-string.h"
+#include "flow.h"
 #include "util.h"
 #include "openflow.h"
 #include "packets.h"
 
 static void ofp_print_port_name(struct ds *string, uint16_t port);
+static void ofp_print_match(struct ds *, const struct ofp_match *,
+                            int verbosity);
 
 /* Returns a string that represents the contents of the Ethernet frame in the
  * 'len' bytes starting at 'data' to 'stream' as output by tcpdump.
@@ -168,13 +172,35 @@ ofp_packet_in(struct ds *string, const void *oh, size_t len, int verbosity)
         if (ntohs(op->total_len) != data_len)
             ds_put_format(string, " (***total_len != data_len***)");
     } else {
-        ds_put_format(string, " buffer=%08"PRIx32, ntohl(op->buffer_id));
+        ds_put_format(string, " buffer=0x%08"PRIx32, ntohl(op->buffer_id));
         if (ntohs(op->total_len) < data_len)
             ds_put_format(string, " (***total_len < data_len***)");
     }
     ds_put_char(string, '\n');
 
     if (verbosity > 0) {
+        struct flow flow;
+        struct buffer packet;
+        struct ofp_match match;
+        packet.data = (void *) op->data;
+        packet.size = data_len;
+        flow_extract(&packet, ntohs(op->in_port), &flow);
+        match.wildcards = 0;
+        match.in_port = flow.in_port;
+        memcpy(match.dl_src, flow.dl_src, ETH_ADDR_LEN);
+        memcpy(match.dl_dst, flow.dl_dst, ETH_ADDR_LEN);
+        match.dl_vlan = flow.dl_vlan;
+        match.dl_type = flow.dl_type;
+        match.nw_proto = flow.nw_proto;
+        match.pad = 0;
+        match.nw_src = flow.nw_src;
+        match.nw_dst = flow.nw_dst;
+        match.tp_src = flow.tp_src;
+        match.tp_dst = flow.tp_dst;
+        ofp_print_match(string, &match, verbosity);
+        ds_put_char(string, '\n');
+    }
+    if (verbosity > 1) {
         char *packet = ofp_packet_to_string(op->data, data_len,
                                             ntohs(op->total_len)); 
         ds_put_cstr(string, packet);
@@ -334,7 +360,7 @@ static void ofp_packet_out(struct ds *string, const void *oh, size_t len,
             free(packet);
         }
     } else {
-        ds_put_format(string, " buffer=%08"PRIx32, ntohl(opo->buffer_id));
+        ds_put_format(string, " buffer=0x%08"PRIx32, ntohl(opo->buffer_id));
     }
     ds_put_char(string, '\n');
 }
@@ -420,7 +446,7 @@ ofp_print_switch_config(struct ds *string, const void *oh, size_t len,
         ds_put_format(string, " (sending flow expirations)");
     }
     if (flags) {
-        ds_put_format(string, " ***unknown flags %04"PRIx16"***", flags);
+        ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags);
     }
 
     ds_put_format(string, " miss_send_len=%"PRIu16"\n", ntohs(osc->miss_send_len));
@@ -449,28 +475,77 @@ static void print_wild(struct ds *string, const char *leader, int is_wild,
     ds_put_char(string, ',');
 }
 
+static void
+print_ip_netmask(struct ds *string, const char *leader, uint32_t ip,
+                 uint32_t wild_bits, int verbosity)
+{
+    if (wild_bits >= 32 && verbosity < 2) {
+        return;
+    }
+    ds_put_cstr(string, leader);
+    if (wild_bits < 32) {
+        ds_put_format(string, IP_FMT, IP_ARGS(&ip));
+        if (wild_bits) {
+            ds_put_format(string, "/%d", 32 - wild_bits);
+        }
+    } else {
+        ds_put_char(string, '*');
+    }
+    ds_put_char(string, ',');
+}
+
 /* Pretty-print the ofp_match structure */
 static void ofp_print_match(struct ds *f, const struct ofp_match *om, 
         int verbosity)
 {
-    uint16_t w = ntohs(om->wildcards);
-
+    uint32_t w = ntohl(om->wildcards);
+    bool skip_type = false;
+    bool skip_proto = false;
+
+    if (!(w & OFPFW_DL_TYPE)) {
+        skip_type = true;
+        if (om->dl_type == htons(ETH_TYPE_IP)) {
+            if (!(w & OFPFW_NW_PROTO)) {
+                skip_proto = true;
+                if (om->nw_proto == IP_TYPE_ICMP) {
+                    ds_put_cstr(f, "icmp,");
+                } else if (om->nw_proto == IP_TYPE_TCP) {
+                    ds_put_cstr(f, "tcp,");
+                } else if (om->nw_proto == IP_TYPE_UDP) {
+                    ds_put_cstr(f, "udp,");
+                } else {
+                    ds_put_cstr(f, "ip,");
+                    skip_proto = false;
+                }
+            } else {
+                ds_put_cstr(f, "ip,");
+            }
+        } else if (om->dl_type == htons(ETH_TYPE_ARP)) {
+            ds_put_cstr(f, "arp,");
+        } else {
+            skip_type = false;
+        }
+    }
     print_wild(f, "in_port=", w & OFPFW_IN_PORT, verbosity,
                "%d", ntohs(om->in_port));
     print_wild(f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
-               "%04x", ntohs(om->dl_vlan));
+               "0x%04x", ntohs(om->dl_vlan));
     print_wild(f, "dl_src=", w & OFPFW_DL_SRC, verbosity,
                ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
     print_wild(f, "dl_dst=", w & OFPFW_DL_DST, verbosity,
                ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
-    print_wild(f, "dl_type=", w & OFPFW_DL_TYPE, verbosity,
-               "%04x", ntohs(om->dl_type));
-    print_wild(f, "nw_src=", w & OFPFW_NW_SRC, verbosity,
-               IP_FMT, IP_ARGS(&om->nw_src));
-    print_wild(f, "nw_dst=", w & OFPFW_NW_DST, verbosity,
-               IP_FMT, IP_ARGS(&om->nw_dst));
-    print_wild(f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity,
-               "%u", om->nw_proto);
+    if (!skip_type) {
+        print_wild(f, "dl_type=", w & OFPFW_DL_TYPE, verbosity,
+                   "0x%04x", ntohs(om->dl_type));
+    }
+    print_ip_netmask(f, "nw_src=", om->nw_src,
+                     (w & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT, verbosity);
+    print_ip_netmask(f, "nw_dst=", om->nw_dst,
+                     (w & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT, verbosity);
+    if (!skip_proto) {
+        print_wild(f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity,
+                   "%u", om->nw_proto);
+    }
     print_wild(f, "tp_src=", w & OFPFW_TP_SRC, verbosity,
                "%d", ntohs(om->tp_src));
     print_wild(f, "tp_dst=", w & OFPFW_TP_DST, verbosity,
@@ -557,6 +632,17 @@ ofp_print_port_status(struct ds *string, const void *oh, size_t len,
     ofp_print_phy_port(string, &ops->desc);
 }
 
+static void
+ofp_version_stats_reply(struct ds *string, const void *body, size_t len,
+                     int verbosity)
+{
+    const struct ofp_version_stats *vs = body;
+
+    ds_put_format(string, "Manufacturer: %s\n", vs->mfr_desc);
+    ds_put_format(string, "Hardware: %s\n", vs->hw_desc);
+    ds_put_format(string, "Software: %s\n", vs->sw_desc);
+}
+
 static void
 ofp_flow_stats_request(struct ds *string, const void *oh, size_t len,
                       int verbosity) 
@@ -729,6 +815,22 @@ ofp_table_stats_reply(struct ds *string, const void *body, size_t len,
      }
 }
 
+static void
+switch_status_reply(struct ds *string, const void *body, size_t len,
+                    int verbosity UNUSED)
+{
+    char *save_ptr = NULL;
+    char *s, *line;
+
+    s = xmemdup0(body, len);
+    for (line = strtok_r(s, "\n\n", &save_ptr); line != NULL;
+         line = strtok_r(NULL, "\n\n", &save_ptr)) {
+        ds_put_printable(string, line, strlen(line));
+        ds_put_char(string, '\n');
+    }
+    free(s);
+}
+
 enum stats_direction {
     REQUEST,
     REPLY
@@ -750,6 +852,11 @@ print_stats(struct ds *string, int type, const void *body, size_t body_len,
     };
 
     static const struct stats_type stats_types[] = {
+        [OFPST_VERSION] = {
+            "version",
+            { 0, 0, NULL },
+            { 0, SIZE_MAX, ofp_version_stats_reply },
+        },
         [OFPST_FLOW] = {
             "flow",
             { sizeof(struct ofp_flow_stats_request),
@@ -776,6 +883,11 @@ print_stats(struct ds *string, int type, const void *body, size_t body_len,
             { 0, 0, NULL, },
             { 0, SIZE_MAX, ofp_port_stats_reply },
         },
+        [OFPST_SWITCH] = {
+            "switch status",
+            { 0, 0, NULL, },
+            { 0, SIZE_MAX, switch_status_reply },
+        },
     };
 
     const struct stats_type *s;
@@ -805,7 +917,7 @@ ofp_stats_request(struct ds *string, const void *oh, size_t len, int verbosity)
     const struct ofp_stats_request *srq = oh;
 
     if (srq->flags) {
-        ds_put_format(string, " ***unknown flags %04"PRIx16"***",
+        ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***",
                       ntohs(srq->flags));
     }
 
@@ -829,7 +941,7 @@ ofp_stats_reply(struct ds *string, const void *oh, size_t len, int verbosity)
             flags &= ~OFPSF_REPLY_MORE;
         }
         if (flags) {
-            ds_put_format(string, "[***unknown%04"PRIx16"***]", flags);
+            ds_put_format(string, "[***unknown flags 0x%04"PRIx16"***]", flags);
         }
     }
 
@@ -965,7 +1077,7 @@ ofp_to_string(const void *oh_, size_t len, int verbosity)
     }
 
     pkt = &packets[oh->type];
-    ds_put_format(&string, "%s (xid=%"PRIx32"):", pkt->name, oh->xid);
+    ds_put_format(&string, "%s (xid=0x%"PRIx32"):", pkt->name, oh->xid);
 
     if (ntohs(oh->length) > len)
         ds_put_format(&string, " (***truncated to %zu bytes from %"PRIu16"***)",