/*
- * Copyright (c) 2008, 2009, 2010 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <stdlib.h>
#include <ctype.h>
+#include "bundle.h"
+#include "byte-order.h"
#include "compiler.h"
#include "dynamic-string.h"
#include "flow.h"
+#include "learn.h"
+#include "multipath.h"
+#include "meta-flow.h"
+#include "netdev.h"
+#include "nx-match.h"
+#include "ofp-actions.h"
+#include "ofp-errors.h"
+#include "ofp-msgs.h"
+#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "openflow/nicira-ext.h"
#include "packets.h"
#include "pcap.h"
+#include "type-props.h"
+#include "unaligned.h"
#include "util.h"
-#include "xtoxll.h"
-static void ofp_print_port_name(struct ds *string, uint16_t port);
static void ofp_print_queue_name(struct ds *string, uint32_t port);
+static void ofp_print_error(struct ds *, enum ofperr);
+
/* Returns a string that represents the contents of the Ethernet frame in the
- * 'len' bytes starting at 'data' to 'stream' as output by tcpdump.
- * 'total_len' specifies the full length of the Ethernet frame (of which 'len'
- * bytes were captured).
- *
- * The caller must free the returned string.
- *
- * This starts and kills a tcpdump subprocess so it's quite expensive. */
+ * 'len' bytes starting at 'data'. The caller must free the returned string.*/
char *
-ofp_packet_to_string(const void *data, size_t len, size_t total_len OVS_UNUSED)
+ofp_packet_to_string(const void *data, size_t len)
{
struct ds ds = DS_EMPTY_INITIALIZER;
struct ofpbuf buf;
+ struct flow flow;
+
+ ofpbuf_use_const(&buf, data, len);
+ flow_extract(&buf, 0, NULL, 0, &flow);
+ flow_format(&ds, &flow);
+
+ if (buf.l7) {
+ if (flow.nw_proto == IPPROTO_TCP) {
+ struct tcp_header *th = buf.l4;
+ ds_put_format(&ds, " tcp_csum:%"PRIx16,
+ ntohs(th->tcp_csum));
+ } else if (flow.nw_proto == IPPROTO_UDP) {
+ struct udp_header *uh = buf.l4;
+ ds_put_format(&ds, " udp_csum:%"PRIx16,
+ ntohs(uh->udp_csum));
+ }
+ }
- char command[128];
- FILE *pcap;
- FILE *tcpdump;
- int status;
- int c;
+ ds_put_char(&ds, '\n');
- buf.data = (void *) data;
- buf.size = len;
+ return ds_cstr(&ds);
+}
- pcap = tmpfile();
- if (!pcap) {
- ovs_error(errno, "tmpfile");
- return xstrdup("<error>");
- }
- pcap_write_header(pcap);
- pcap_write(pcap, &buf);
- fflush(pcap);
- if (ferror(pcap)) {
- ovs_error(errno, "error writing temporary file");
+static void
+ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
+{
+ struct ofputil_packet_in pin;
+ int error;
+ int i;
+
+ error = ofputil_decode_packet_in(&pin, oh);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
}
- rewind(pcap);
- snprintf(command, sizeof command, "/usr/sbin/tcpdump -e -n -r /dev/fd/%d 2>/dev/null",
- fileno(pcap));
- tcpdump = popen(command, "r");
- fclose(pcap);
- if (!tcpdump) {
- ovs_error(errno, "exec(\"%s\")", command);
- return xstrdup("<error>");
+ if (pin.table_id) {
+ ds_put_format(string, " table_id=%"PRIu8, pin.table_id);
}
- while ((c = getc(tcpdump)) != EOF) {
- ds_put_char(&ds, c);
+ if (pin.cookie) {
+ ds_put_format(string, " cookie=0x%"PRIx64, ntohll(pin.cookie));
}
- status = pclose(tcpdump);
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status))
- ovs_error(0, "tcpdump exited with status %d", WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- ovs_error(0, "tcpdump exited with signal %d", WTERMSIG(status));
+ ds_put_format(string, " total_len=%"PRIu16" in_port=", pin.total_len);
+ ofputil_format_port(pin.fmd.in_port, string);
+
+ if (pin.fmd.tun_id != htonll(0)) {
+ ds_put_format(string, " tun_id=0x%"PRIx64, ntohll(pin.fmd.tun_id));
}
- return ds_cstr(&ds);
-}
-/* Pretty-print the OFPT_PACKET_IN packet of 'len' bytes at 'oh' to 'stream'
- * at the given 'verbosity' level. */
-static void
-ofp_packet_in(struct ds *string, const void *oh, size_t len, int verbosity)
-{
- const struct ofp_packet_in *op = oh;
- size_t data_len;
+ if (pin.fmd.metadata != htonll(0)) {
+ ds_put_format(string, " metadata=0x%"PRIx64, ntohll(pin.fmd.metadata));
+ }
- ds_put_format(string, " total_len=%"PRIu16" in_port=",
- ntohs(op->total_len));
- ofp_print_port_name(string, ntohs(op->in_port));
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ if (pin.fmd.regs[i]) {
+ ds_put_format(string, " reg%d=0x%"PRIx32, i, pin.fmd.regs[i]);
+ }
+ }
- if (op->reason == OFPR_ACTION)
- ds_put_cstr(string, " (via action)");
- else if (op->reason != OFPR_NO_MATCH)
- ds_put_format(string, " (***reason %"PRIu8"***)", op->reason);
+ ds_put_format(string, " (via %s)",
+ ofputil_packet_in_reason_to_string(pin.reason));
- data_len = len - offsetof(struct ofp_packet_in, data);
- ds_put_format(string, " data_len=%zu", data_len);
- if (htonl(op->buffer_id) == UINT32_MAX) {
+ ds_put_format(string, " data_len=%zu", pin.packet_len);
+ if (pin.buffer_id == UINT32_MAX) {
ds_put_format(string, " (unbuffered)");
- if (ntohs(op->total_len) != data_len)
+ if (pin.total_len != pin.packet_len) {
ds_put_format(string, " (***total_len != data_len***)");
+ }
} else {
- ds_put_format(string, " buffer=0x%08"PRIx32, ntohl(op->buffer_id));
- if (ntohs(op->total_len) < data_len)
+ ds_put_format(string, " buffer=0x%08"PRIx32, pin.buffer_id);
+ if (pin.total_len < pin.packet_len) {
ds_put_format(string, " (***total_len < data_len***)");
+ }
}
ds_put_char(string, '\n');
if (verbosity > 0) {
- flow_t flow;
- struct ofpbuf packet;
- struct ofp_match match;
- packet.data = (void *) op->data;
- packet.size = data_len;
- flow_extract(&packet, 0, ntohs(op->in_port), &flow);
- flow_to_match(&flow, 0, false, &match);
- 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));
+ char *packet = ofp_packet_to_string(pin.packet, pin.packet_len);
ds_put_cstr(string, packet);
free(packet);
}
}
-static void ofp_print_port_name(struct ds *string, uint16_t port)
-{
- const char *name;
- switch (port) {
- case OFPP_IN_PORT:
- name = "IN_PORT";
- break;
- case OFPP_TABLE:
- name = "TABLE";
- break;
- case OFPP_NORMAL:
- name = "NORMAL";
- break;
- case OFPP_FLOOD:
- name = "FLOOD";
- break;
- case OFPP_ALL:
- name = "ALL";
- break;
- case OFPP_CONTROLLER:
- name = "CONTROLLER";
- break;
- case OFPP_LOCAL:
- name = "LOCAL";
- break;
- case OFPP_NONE:
- name = "NONE";
- break;
- default:
- ds_put_format(string, "%"PRIu16, port);
- return;
- }
- ds_put_cstr(string, name);
-}
-
static void
-ofp_print_nx_action(struct ds *string, const struct nx_action_header *nah)
+ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
{
- switch (ntohs(nah->subtype)) {
- case NXAST_RESUBMIT: {
- const struct nx_action_resubmit *nar = (struct nx_action_resubmit *)nah;
- ds_put_format(string, "resubmit:");
- ofp_print_port_name(string, ntohs(nar->in_port));
- break;
+ struct ofputil_packet_out po;
+ struct ofpbuf ofpacts;
+ enum ofperr error;
+
+ ofpbuf_init(&ofpacts, 64);
+ error = ofputil_decode_packet_out(&po, oh, &ofpacts);
+ if (error) {
+ ofpbuf_uninit(&ofpacts);
+ ofp_print_error(string, error);
+ return;
}
- case NXAST_SET_TUNNEL: {
- const struct nx_action_set_tunnel *nast =
- (struct nx_action_set_tunnel *)nah;
- ds_put_format(string, "set_tunnel:0x%08"PRIx32, ntohl(nast->tun_id));
- break;
- }
+ ds_put_cstr(string, " in_port=");
+ ofputil_format_port(po.in_port, string);
- case NXAST_DROP_SPOOFED_ARP:
- ds_put_cstr(string, "drop_spoofed_arp");
- break;
+ ds_put_char(string, ' ');
+ ofpacts_format(po.ofpacts, po.ofpacts_len, string);
- default:
- ds_put_format(string, "***unknown Nicira action:%d***",
- ntohs(nah->subtype));
+ if (po.buffer_id == UINT32_MAX) {
+ ds_put_format(string, " data_len=%zu", po.packet_len);
+ if (verbosity > 0 && po.packet_len > 0) {
+ char *packet = ofp_packet_to_string(po.packet, po.packet_len);
+ ds_put_char(string, '\n');
+ ds_put_cstr(string, packet);
+ free(packet);
+ }
+ } else {
+ ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id);
}
+ ds_put_char(string, '\n');
+
+ ofpbuf_uninit(&ofpacts);
}
+/* qsort comparison function. */
static int
-ofp_print_action(struct ds *string, const struct ofp_action_header *ah,
- size_t actions_len)
-{
- uint16_t type;
- size_t len;
-
- struct openflow_action {
- size_t min_size;
- size_t max_size;
- };
-
- const struct openflow_action of_actions[] = {
- [OFPAT_OUTPUT] = {
- sizeof(struct ofp_action_output),
- sizeof(struct ofp_action_output),
- },
- [OFPAT_SET_VLAN_VID] = {
- sizeof(struct ofp_action_vlan_vid),
- sizeof(struct ofp_action_vlan_vid),
- },
- [OFPAT_SET_VLAN_PCP] = {
- sizeof(struct ofp_action_vlan_pcp),
- sizeof(struct ofp_action_vlan_pcp),
- },
- [OFPAT_STRIP_VLAN] = {
- sizeof(struct ofp_action_header),
- sizeof(struct ofp_action_header),
- },
- [OFPAT_SET_DL_SRC] = {
- sizeof(struct ofp_action_dl_addr),
- sizeof(struct ofp_action_dl_addr),
- },
- [OFPAT_SET_DL_DST] = {
- sizeof(struct ofp_action_dl_addr),
- sizeof(struct ofp_action_dl_addr),
- },
- [OFPAT_SET_NW_SRC] = {
- sizeof(struct ofp_action_nw_addr),
- sizeof(struct ofp_action_nw_addr),
- },
- [OFPAT_SET_NW_DST] = {
- sizeof(struct ofp_action_nw_addr),
- sizeof(struct ofp_action_nw_addr),
- },
- [OFPAT_SET_NW_TOS] = {
- sizeof(struct ofp_action_nw_tos),
- sizeof(struct ofp_action_nw_tos),
- },
- [OFPAT_SET_TP_SRC] = {
- sizeof(struct ofp_action_tp_port),
- sizeof(struct ofp_action_tp_port),
- },
- [OFPAT_SET_TP_DST] = {
- sizeof(struct ofp_action_tp_port),
- sizeof(struct ofp_action_tp_port),
- }
- /* OFPAT_VENDOR is not here, since it would blow up the array size. */
- };
+compare_ports(const void *a_, const void *b_)
+{
+ const struct ofputil_phy_port *a = a_;
+ const struct ofputil_phy_port *b = b_;
+ uint16_t ap = a->port_no;
+ uint16_t bp = b->port_no;
- if (actions_len < sizeof *ah) {
- ds_put_format(string, "***action array too short for next action***\n");
- return -1;
- }
+ return ap < bp ? -1 : ap > bp;
+}
- type = ntohs(ah->type);
- len = ntohs(ah->len);
- if (actions_len < len) {
- ds_put_format(string, "***truncated action %"PRIu16"***\n", type);
- return -1;
- }
+static void
+ofp_print_bit_names(struct ds *string, uint32_t bits,
+ const char *(*bit_to_name)(uint32_t bit),
+ char separator)
+{
+ int n = 0;
+ int i;
- if ((len % 8) != 0) {
- ds_put_format(string,
- "***action %"PRIu16" length not a multiple of 8***\n",
- type);
- return -1;
+ if (!bits) {
+ ds_put_cstr(string, "0");
+ return;
}
- if (type < ARRAY_SIZE(of_actions)) {
- const struct openflow_action *act = &of_actions[type];
- if ((len < act->min_size) || (len > act->max_size)) {
- ds_put_format(string,
- "***action %"PRIu16" wrong length: %zu***\n", type, len);
- return -1;
- }
- }
+ for (i = 0; i < 32; i++) {
+ uint32_t bit = UINT32_C(1) << i;
- switch (type) {
- case OFPAT_OUTPUT: {
- struct ofp_action_output *oa = (struct ofp_action_output *)ah;
- uint16_t port = ntohs(oa->port);
- if (port < OFPP_MAX) {
- ds_put_format(string, "output:%"PRIu16, port);
- } else {
- ofp_print_port_name(string, port);
- if (port == OFPP_CONTROLLER) {
- if (oa->max_len) {
- ds_put_format(string, ":%"PRIu16, ntohs(oa->max_len));
- } else {
- ds_put_cstr(string, ":all");
+ if (bits & bit) {
+ const char *name = bit_to_name(bit);
+ if (name) {
+ if (n++) {
+ ds_put_char(string, separator);
}
+ ds_put_cstr(string, name);
+ bits &= ~bit;
}
}
- break;
}
- case OFPAT_ENQUEUE: {
- struct ofp_action_enqueue *ea = (struct ofp_action_enqueue *)ah;
- unsigned int port = ntohs(ea->port);
- unsigned int queue_id = ntohl(ea->queue_id);
- ds_put_format(string, "enqueue:");
- if (port != OFPP_IN_PORT) {
- ds_put_format(string, "%u", port);
- } else {
- ds_put_cstr(string, "IN_PORT");
+ if (bits) {
+ if (n) {
+ ds_put_char(string, separator);
}
- ds_put_format(string, "q%u", queue_id);
- break;
- }
-
- case OFPAT_SET_VLAN_VID: {
- struct ofp_action_vlan_vid *va = (struct ofp_action_vlan_vid *)ah;
- ds_put_format(string, "mod_vlan_vid:%"PRIu16, ntohs(va->vlan_vid));
- break;
- }
-
- case OFPAT_SET_VLAN_PCP: {
- struct ofp_action_vlan_pcp *va = (struct ofp_action_vlan_pcp *)ah;
- ds_put_format(string, "mod_vlan_pcp:%"PRIu8, va->vlan_pcp);
- break;
- }
-
- case OFPAT_STRIP_VLAN:
- ds_put_cstr(string, "strip_vlan");
- break;
-
- case OFPAT_SET_DL_SRC: {
- struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
- ds_put_format(string, "mod_dl_src:"ETH_ADDR_FMT,
- ETH_ADDR_ARGS(da->dl_addr));
- break;
- }
-
- case OFPAT_SET_DL_DST: {
- struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
- ds_put_format(string, "mod_dl_dst:"ETH_ADDR_FMT,
- ETH_ADDR_ARGS(da->dl_addr));
- break;
- }
-
- case OFPAT_SET_NW_SRC: {
- struct ofp_action_nw_addr *na = (struct ofp_action_nw_addr *)ah;
- ds_put_format(string, "mod_nw_src:"IP_FMT, IP_ARGS(&na->nw_addr));
- break;
- }
-
- case OFPAT_SET_NW_DST: {
- struct ofp_action_nw_addr *na = (struct ofp_action_nw_addr *)ah;
- ds_put_format(string, "mod_nw_dst:"IP_FMT, IP_ARGS(&na->nw_addr));
- break;
- }
-
- case OFPAT_SET_NW_TOS: {
- struct ofp_action_nw_tos *nt = (struct ofp_action_nw_tos *)ah;
- ds_put_format(string, "mod_nw_tos:%d", nt->nw_tos);
- break;
+ ds_put_format(string, "0x%"PRIx32, bits);
}
+}
- case OFPAT_SET_TP_SRC: {
- struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah;
- ds_put_format(string, "mod_tp_src:%d", ntohs(ta->tp_port));
- break;
- }
+static const char *
+netdev_feature_to_name(uint32_t bit)
+{
+ enum netdev_features f = bit;
+
+ switch (f) {
+ case NETDEV_F_10MB_HD: return "10MB-HD";
+ case NETDEV_F_10MB_FD: return "10MB-FD";
+ case NETDEV_F_100MB_HD: return "100MB-HD";
+ case NETDEV_F_100MB_FD: return "100MB-FD";
+ case NETDEV_F_1GB_HD: return "1GB-HD";
+ case NETDEV_F_1GB_FD: return "1GB-FD";
+ case NETDEV_F_10GB_FD: return "10GB-FD";
+ case NETDEV_F_40GB_FD: return "40GB-FD";
+ case NETDEV_F_100GB_FD: return "100GB-FD";
+ case NETDEV_F_1TB_FD: return "1TB-FD";
+ case NETDEV_F_OTHER: return "OTHER";
+ case NETDEV_F_COPPER: return "COPPER";
+ case NETDEV_F_FIBER: return "FIBER";
+ case NETDEV_F_AUTONEG: return "AUTO_NEG";
+ case NETDEV_F_PAUSE: return "AUTO_PAUSE";
+ case NETDEV_F_PAUSE_ASYM: return "AUTO_PAUSE_ASYM";
+ }
+
+ return NULL;
+}
- case OFPAT_SET_TP_DST: {
- struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah;
- ds_put_format(string, "mod_tp_dst:%d", ntohs(ta->tp_port));
- break;
- }
+static void
+ofp_print_port_features(struct ds *string, enum netdev_features features)
+{
+ ofp_print_bit_names(string, features, netdev_feature_to_name, ' ');
+ ds_put_char(string, '\n');
+}
- case OFPAT_VENDOR: {
- struct ofp_action_vendor_header *avh
- = (struct ofp_action_vendor_header *)ah;
- if (len < sizeof *avh) {
- ds_put_format(string, "***ofpat_vendor truncated***\n");
- return -1;
- }
- if (avh->vendor == htonl(NX_VENDOR_ID)) {
- ofp_print_nx_action(string, (struct nx_action_header *)avh);
- } else {
- ds_put_format(string, "vendor action:0x%x", ntohl(avh->vendor));
- }
- break;
- }
+static const char *
+ofputil_port_config_to_name(uint32_t bit)
+{
+ enum ofputil_port_config pc = bit;
- default:
- ds_put_format(string, "(decoder %"PRIu16" not implemented)", type);
- break;
+ switch (pc) {
+ case OFPUTIL_PC_PORT_DOWN: return "PORT_DOWN";
+ case OFPUTIL_PC_NO_STP: return "NO_STP";
+ case OFPUTIL_PC_NO_RECV: return "NO_RECV";
+ case OFPUTIL_PC_NO_RECV_STP: return "NO_RECV_STP";
+ case OFPUTIL_PC_NO_FLOOD: return "NO_FLOOD";
+ case OFPUTIL_PC_NO_FWD: return "NO_FWD";
+ case OFPUTIL_PC_NO_PACKET_IN: return "NO_PACKET_IN";
}
- return len;
+ return NULL;
}
-void
-ofp_print_actions(struct ds *string, const struct ofp_action_header *action,
- size_t actions_len)
+static void
+ofp_print_port_config(struct ds *string, enum ofputil_port_config config)
{
- uint8_t *p = (uint8_t *)action;
- int len = 0;
-
- ds_put_cstr(string, "actions=");
- if (!actions_len) {
- ds_put_cstr(string, "drop");
- }
- while (actions_len > 0) {
- if (len) {
- ds_put_cstr(string, ",");
- }
- len = ofp_print_action(string, (struct ofp_action_header *)p,
- actions_len);
- if (len < 0) {
- return;
- }
- p += len;
- actions_len -= len;
- }
+ ofp_print_bit_names(string, config, ofputil_port_config_to_name, ' ');
+ ds_put_char(string, '\n');
}
-/* Pretty-print the OFPT_PACKET_OUT packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
-static void ofp_packet_out(struct ds *string, const void *oh, size_t len,
- int verbosity)
+static const char *
+ofputil_port_state_to_name(uint32_t bit)
{
- const struct ofp_packet_out *opo = oh;
- size_t actions_len = ntohs(opo->actions_len);
-
- ds_put_cstr(string, " in_port=");
- ofp_print_port_name(string, ntohs(opo->in_port));
+ enum ofputil_port_state ps = bit;
- ds_put_format(string, " actions_len=%zu ", actions_len);
- if (actions_len > (ntohs(opo->header.length) - sizeof *opo)) {
- ds_put_format(string, "***packet too short for action length***\n");
- return;
- }
- ofp_print_actions(string, opo->actions, actions_len);
+ switch (ps) {
+ case OFPUTIL_PS_LINK_DOWN: return "LINK_DOWN";
+ case OFPUTIL_PS_BLOCKED: return "BLOCKED";
+ case OFPUTIL_PS_LIVE: return "LIVE";
- if (ntohl(opo->buffer_id) == UINT32_MAX) {
- int data_len = len - sizeof *opo - actions_len;
- ds_put_format(string, " data_len=%d", data_len);
- if (verbosity > 0 && len > sizeof *opo) {
- char *packet = ofp_packet_to_string(
- (uint8_t *)opo->actions + actions_len, data_len, data_len);
- ds_put_char(string, '\n');
- ds_put_cstr(string, packet);
- free(packet);
- }
- } else {
- ds_put_format(string, " buffer=0x%08"PRIx32, ntohl(opo->buffer_id));
+ case OFPUTIL_PS_STP_LISTEN:
+ case OFPUTIL_PS_STP_LEARN:
+ case OFPUTIL_PS_STP_FORWARD:
+ case OFPUTIL_PS_STP_BLOCK:
+ /* Handled elsewhere. */
+ return NULL;
}
- ds_put_char(string, '\n');
-}
-
-/* qsort comparison function. */
-static int
-compare_ports(const void *a_, const void *b_)
-{
- const struct ofp_phy_port *a = a_;
- const struct ofp_phy_port *b = b_;
- uint16_t ap = ntohs(a->port_no);
- uint16_t bp = ntohs(b->port_no);
- return ap < bp ? -1 : ap > bp;
+ return NULL;
}
-static void ofp_print_port_features(struct ds *string, uint32_t features)
+static void
+ofp_print_port_state(struct ds *string, enum ofputil_port_state state)
{
- if (features == 0) {
- ds_put_cstr(string, "Unsupported\n");
- return;
- }
- if (features & OFPPF_10MB_HD) {
- ds_put_cstr(string, "10MB-HD ");
- }
- if (features & OFPPF_10MB_FD) {
- ds_put_cstr(string, "10MB-FD ");
- }
- if (features & OFPPF_100MB_HD) {
- ds_put_cstr(string, "100MB-HD ");
- }
- if (features & OFPPF_100MB_FD) {
- ds_put_cstr(string, "100MB-FD ");
- }
- if (features & OFPPF_1GB_HD) {
- ds_put_cstr(string, "1GB-HD ");
- }
- if (features & OFPPF_1GB_FD) {
- ds_put_cstr(string, "1GB-FD ");
- }
- if (features & OFPPF_10GB_FD) {
- ds_put_cstr(string, "10GB-FD ");
- }
- if (features & OFPPF_COPPER) {
- ds_put_cstr(string, "COPPER ");
- }
- if (features & OFPPF_FIBER) {
- ds_put_cstr(string, "FIBER ");
- }
- if (features & OFPPF_AUTONEG) {
- ds_put_cstr(string, "AUTO_NEG ");
- }
- if (features & OFPPF_PAUSE) {
- ds_put_cstr(string, "AUTO_PAUSE ");
- }
- if (features & OFPPF_PAUSE_ASYM) {
- ds_put_cstr(string, "AUTO_PAUSE_ASYM ");
+ enum ofputil_port_state stp_state;
+
+ /* The STP state is a 2-bit field so it doesn't fit in with the bitmask
+ * pattern. We have to special case it.
+ *
+ * OVS doesn't support STP, so this field will always be 0 if we are
+ * talking to OVS, so we'd always print STP_LISTEN in that case.
+ * Therefore, we don't print anything at all if the value is STP_LISTEN, to
+ * avoid confusing users. */
+ stp_state = state & OFPUTIL_PS_STP_MASK;
+ if (stp_state) {
+ ds_put_cstr(string,
+ (stp_state == OFPUTIL_PS_STP_LEARN ? "STP_LEARN"
+ : stp_state == OFPUTIL_PS_STP_FORWARD ? "STP_FORWARD"
+ : "STP_BLOCK"));
+ state &= ~OFPUTIL_PS_STP_MASK;
+ if (state) {
+ ofp_print_bit_names(string, state, ofputil_port_state_to_name,
+ ' ');
+ }
+ } else {
+ ofp_print_bit_names(string, state, ofputil_port_state_to_name, ' ');
}
ds_put_char(string, '\n');
}
static void
-ofp_print_phy_port(struct ds *string, const struct ofp_phy_port *port)
+ofp_print_phy_port(struct ds *string, const struct ofputil_phy_port *port)
{
- uint8_t name[OFP_MAX_PORT_NAME_LEN];
+ char name[sizeof port->name];
int j;
memcpy(name, port->name, sizeof name);
for (j = 0; j < sizeof name - 1; j++) {
- if (!isprint(name[j])) {
+ if (!isprint((unsigned char) name[j])) {
break;
}
}
name[j] = '\0';
ds_put_char(string, ' ');
- ofp_print_port_name(string, ntohs(port->port_no));
- ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT", config: %#x, state:%#x\n",
- name, ETH_ADDR_ARGS(port->hw_addr), ntohl(port->config),
- ntohl(port->state));
+ ofputil_format_port(port->port_no, string);
+ ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT"\n",
+ name, ETH_ADDR_ARGS(port->hw_addr));
+
+ ds_put_cstr(string, " config: ");
+ ofp_print_port_config(string, port->config);
+
+ ds_put_cstr(string, " state: ");
+ ofp_print_port_state(string, port->state);
+
if (port->curr) {
ds_put_format(string, " current: ");
- ofp_print_port_features(string, ntohl(port->curr));
+ ofp_print_port_features(string, port->curr);
}
if (port->advertised) {
ds_put_format(string, " advertised: ");
- ofp_print_port_features(string, ntohl(port->advertised));
+ ofp_print_port_features(string, port->advertised);
}
if (port->supported) {
ds_put_format(string, " supported: ");
- ofp_print_port_features(string, ntohl(port->supported));
+ ofp_print_port_features(string, port->supported);
}
if (port->peer) {
ds_put_format(string, " peer: ");
- ofp_print_port_features(string, ntohl(port->peer));
+ ofp_print_port_features(string, port->peer);
}
+ ds_put_format(string, " speed: %"PRIu32" Mbps now, "
+ "%"PRIu32" Mbps max\n",
+ port->curr_speed / UINT32_C(1000),
+ port->max_speed / UINT32_C(1000));
}
-/* Pretty-print the struct ofp_switch_features of 'len' bytes at 'oh' to
- * 'string' at the given 'verbosity' level. */
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', writes a detailed description of each port into
+ * 'string'. */
static void
-ofp_print_switch_features(struct ds *string, const void *oh, size_t len,
- int verbosity OVS_UNUSED)
+ofp_print_phy_ports(struct ds *string, uint8_t ofp_version,
+ struct ofpbuf *b)
{
- const struct ofp_switch_features *osf = oh;
- struct ofp_phy_port *port_list;
- int n_ports;
- int i;
+ size_t n_ports;
+ struct ofputil_phy_port *ports;
+ enum ofperr error;
+ size_t i;
- ds_put_format(string, " ver:0x%x, dpid:%016"PRIx64"\n",
- osf->header.version, ntohll(osf->datapath_id));
- ds_put_format(string, "n_tables:%d, n_buffers:%d\n", osf->n_tables,
- ntohl(osf->n_buffers));
- ds_put_format(string, "features: capabilities:%#x, actions:%#x\n",
- ntohl(osf->capabilities), ntohl(osf->actions));
+ n_ports = ofputil_count_phy_ports(ofp_version, b);
- if (ntohs(osf->header.length) >= sizeof *osf) {
- len = MIN(len, ntohs(osf->header.length));
+ ports = xmalloc(n_ports * sizeof *ports);
+ for (i = 0; i < n_ports; i++) {
+ error = ofputil_pull_phy_port(ofp_version, b, &ports[i]);
+ if (error) {
+ ofp_print_error(string, error);
+ goto exit;
+ }
}
- n_ports = (len - sizeof *osf) / sizeof *osf->ports;
-
- port_list = xmemdup(osf->ports, len - sizeof *osf);
- qsort(port_list, n_ports, sizeof *port_list, compare_ports);
+ qsort(ports, n_ports, sizeof *ports, compare_ports);
for (i = 0; i < n_ports; i++) {
- ofp_print_phy_port(string, &port_list[i]);
+ ofp_print_phy_port(string, &ports[i]);
+ }
+
+exit:
+ free(ports);
+}
+
+static const char *
+ofputil_capabilities_to_name(uint32_t bit)
+{
+ enum ofputil_capabilities capabilities = bit;
+
+ switch (capabilities) {
+ case OFPUTIL_C_FLOW_STATS: return "FLOW_STATS";
+ case OFPUTIL_C_TABLE_STATS: return "TABLE_STATS";
+ case OFPUTIL_C_PORT_STATS: return "PORT_STATS";
+ case OFPUTIL_C_IP_REASM: return "IP_REASM";
+ case OFPUTIL_C_QUEUE_STATS: return "QUEUE_STATS";
+ case OFPUTIL_C_ARP_MATCH_IP: return "ARP_MATCH_IP";
+ case OFPUTIL_C_STP: return "STP";
+ case OFPUTIL_C_GROUP_STATS: return "GROUP_STATS";
+ case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED";
+ }
+
+ return NULL;
+}
+
+static const char *
+ofputil_action_bitmap_to_name(uint32_t bit)
+{
+ enum ofputil_action_bitmap action = bit;
+
+ switch (action) {
+ case OFPUTIL_A_OUTPUT: return "OUTPUT";
+ case OFPUTIL_A_SET_VLAN_VID: return "SET_VLAN_VID";
+ case OFPUTIL_A_SET_VLAN_PCP: return "SET_VLAN_PCP";
+ case OFPUTIL_A_STRIP_VLAN: return "STRIP_VLAN";
+ case OFPUTIL_A_SET_DL_SRC: return "SET_DL_SRC";
+ case OFPUTIL_A_SET_DL_DST: return "SET_DL_DST";
+ case OFPUTIL_A_SET_NW_SRC: return "SET_NW_SRC";
+ case OFPUTIL_A_SET_NW_DST: return "SET_NW_DST";
+ case OFPUTIL_A_SET_NW_ECN: return "SET_NW_ECN";
+ case OFPUTIL_A_SET_NW_TOS: return "SET_NW_TOS";
+ case OFPUTIL_A_SET_TP_SRC: return "SET_TP_SRC";
+ case OFPUTIL_A_SET_TP_DST: return "SET_TP_DST";
+ case OFPUTIL_A_SET_FIELD: return "SET_FIELD";
+ case OFPUTIL_A_ENQUEUE: return "ENQUEUE";
+ case OFPUTIL_A_COPY_TTL_OUT: return "COPY_TTL_OUT";
+ case OFPUTIL_A_COPY_TTL_IN: return "COPY_TTL_IN";
+ case OFPUTIL_A_SET_MPLS_LABEL: return "SET_MPLS_LABEL";
+ case OFPUTIL_A_SET_MPLS_TC: return "SET_MPLS_TC";
+ case OFPUTIL_A_SET_MPLS_TTL: return "SET_MPLS_TTL";
+ case OFPUTIL_A_DEC_MPLS_TTL: return "DEC_MPLS_TTL";
+ case OFPUTIL_A_PUSH_VLAN: return "PUSH_VLAN";
+ case OFPUTIL_A_POP_VLAN: return "POP_VLAN";
+ case OFPUTIL_A_PUSH_MPLS: return "PUSH_MPLS";
+ case OFPUTIL_A_POP_MPLS: return "POP_MPLS";
+ case OFPUTIL_A_SET_QUEUE: return "SET_QUEUE";
+ case OFPUTIL_A_GROUP: return "GROUP";
+ case OFPUTIL_A_SET_NW_TTL: return "SET_NW_TTL";
+ case OFPUTIL_A_DEC_NW_TTL: return "DEC_NW_TTL";
+ }
+
+ return NULL;
+}
+
+static void
+ofp_print_switch_features(struct ds *string, const struct ofp_header *oh)
+{
+ struct ofputil_switch_features features;
+ enum ofperr error;
+ struct ofpbuf b;
+
+ error = ofputil_decode_switch_features(oh, &features, &b);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
+ }
+
+ ds_put_format(string, " dpid:%016"PRIx64"\n", features.datapath_id);
+ ds_put_format(string, "n_tables:%"PRIu8", n_buffers:%"PRIu32"\n",
+ features.n_tables, features.n_buffers);
+
+ ds_put_cstr(string, "capabilities: ");
+ ofp_print_bit_names(string, features.capabilities,
+ ofputil_capabilities_to_name, ' ');
+ ds_put_char(string, '\n');
+
+ switch ((enum ofp_version)oh->version) {
+ case OFP10_VERSION:
+ ds_put_cstr(string, "actions: ");
+ ofp_print_bit_names(string, features.actions,
+ ofputil_action_bitmap_to_name, ' ');
+ ds_put_char(string, '\n');
+ break;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ break;
+ default:
+ NOT_REACHED();
}
- free(port_list);
+
+ ofp_print_phy_ports(string, oh->version, &b);
}
-/* Pretty-print the struct ofp_switch_config of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
static void
-ofp_print_switch_config(struct ds *string, const void *oh,
- size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
+ofp_print_switch_config(struct ds *string, const struct ofp_switch_config *osc)
{
- const struct ofp_switch_config *osc = oh;
- uint16_t flags;
+ enum ofp_config_flags flags;
flags = ntohs(osc->flags);
+
+ ds_put_format(string, " frags=%s", ofputil_frag_handling_to_string(flags));
+ flags &= ~OFPC_FRAG_MASK;
+
+ if (flags & OFPC_INVALID_TTL_TO_CONTROLLER) {
+ ds_put_format(string, " invalid_ttl_to_controller");
+ flags &= ~OFPC_INVALID_TTL_TO_CONTROLLER;
+ }
+
if (flags) {
ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags);
}
}
static void
-print_ip_netmask(struct ds *string, const char *leader, uint32_t ip,
+print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
uint32_t wild_bits, int verbosity)
{
if (wild_bits >= 32 && verbosity < 2) {
}
void
-ofp_print_match(struct ds *f, const struct ofp_match *om, int verbosity)
+ofp10_match_print(struct ds *f, const struct ofp10_match *om, int verbosity)
{
- char *s = ofp_match_to_string(om, verbosity);
+ char *s = ofp10_match_to_string(om, verbosity);
ds_put_cstr(f, s);
free(s);
}
char *
-ofp_match_to_string(const struct ofp_match *om, int verbosity)
+ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
{
struct ds f = DS_EMPTY_INITIALIZER;
uint32_t w = ntohl(om->wildcards);
bool skip_type = false;
bool skip_proto = false;
- if (!(w & OFPFW_DL_TYPE)) {
+ if (!(w & OFPFW10_DL_TYPE)) {
skip_type = true;
if (om->dl_type == htons(ETH_TYPE_IP)) {
- if (!(w & OFPFW_NW_PROTO)) {
+ if (!(w & OFPFW10_NW_PROTO)) {
skip_proto = true;
- if (om->nw_proto == IP_TYPE_ICMP) {
+ if (om->nw_proto == IPPROTO_ICMP) {
ds_put_cstr(&f, "icmp,");
- } else if (om->nw_proto == IP_TYPE_TCP) {
+ } else if (om->nw_proto == IPPROTO_TCP) {
ds_put_cstr(&f, "tcp,");
- } else if (om->nw_proto == IP_TYPE_UDP) {
+ } else if (om->nw_proto == IPPROTO_UDP) {
ds_put_cstr(&f, "udp,");
} else {
ds_put_cstr(&f, "ip,");
skip_type = false;
}
}
- if (w & NXFW_TUN_ID) {
- ds_put_cstr(&f, "tun_id_wild,");
- }
- print_wild(&f, "in_port=", w & OFPFW_IN_PORT, verbosity,
+ print_wild(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
"%d", ntohs(om->in_port));
- print_wild(&f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
+ print_wild(&f, "dl_vlan=", w & OFPFW10_DL_VLAN, verbosity,
"%d", ntohs(om->dl_vlan));
- print_wild(&f, "dl_vlan_pcp=", w & OFPFW_DL_VLAN_PCP, verbosity,
+ print_wild(&f, "dl_vlan_pcp=", w & OFPFW10_DL_VLAN_PCP, verbosity,
"%d", om->dl_vlan_pcp);
- print_wild(&f, "dl_src=", w & OFPFW_DL_SRC, verbosity,
+ print_wild(&f, "dl_src=", w & OFPFW10_DL_SRC, verbosity,
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
- print_wild(&f, "dl_dst=", w & OFPFW_DL_DST, verbosity,
+ print_wild(&f, "dl_dst=", w & OFPFW10_DL_DST, verbosity,
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
if (!skip_type) {
- print_wild(&f, "dl_type=", w & OFPFW_DL_TYPE, verbosity,
+ print_wild(&f, "dl_type=", w & OFPFW10_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);
+ (w & OFPFW10_NW_SRC_MASK) >> OFPFW10_NW_SRC_SHIFT,
+ verbosity);
print_ip_netmask(&f, "nw_dst=", om->nw_dst,
- (w & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT, verbosity);
+ (w & OFPFW10_NW_DST_MASK) >> OFPFW10_NW_DST_SHIFT,
+ verbosity);
if (!skip_proto) {
if (om->dl_type == htons(ETH_TYPE_ARP)) {
- print_wild(&f, "opcode=", w & OFPFW_NW_PROTO, verbosity,
+ print_wild(&f, "arp_op=", w & OFPFW10_NW_PROTO, verbosity,
"%u", om->nw_proto);
} else {
- print_wild(&f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity,
+ print_wild(&f, "nw_proto=", w & OFPFW10_NW_PROTO, verbosity,
"%u", om->nw_proto);
- print_wild(&f, "nw_tos=", w & OFPFW_NW_TOS, verbosity,
- "%u", om->nw_tos);
}
}
- if (om->nw_proto == IP_TYPE_ICMP) {
- print_wild(&f, "icmp_type=", w & OFPFW_ICMP_TYPE, verbosity,
- "%d", ntohs(om->icmp_type));
- print_wild(&f, "icmp_code=", w & OFPFW_ICMP_CODE, verbosity,
- "%d", ntohs(om->icmp_code));
+ print_wild(&f, "nw_tos=", w & OFPFW10_NW_TOS, verbosity,
+ "%u", om->nw_tos);
+ if (om->nw_proto == IPPROTO_ICMP) {
+ print_wild(&f, "icmp_type=", w & OFPFW10_ICMP_TYPE, verbosity,
+ "%d", ntohs(om->tp_src));
+ print_wild(&f, "icmp_code=", w & OFPFW10_ICMP_CODE, verbosity,
+ "%d", ntohs(om->tp_dst));
} else {
- print_wild(&f, "tp_src=", w & OFPFW_TP_SRC, verbosity,
+ print_wild(&f, "tp_src=", w & OFPFW10_TP_SRC, verbosity,
"%d", ntohs(om->tp_src));
- print_wild(&f, "tp_dst=", w & OFPFW_TP_DST, verbosity,
+ print_wild(&f, "tp_dst=", w & OFPFW10_TP_DST, verbosity,
"%d", ntohs(om->tp_dst));
}
+ if (ds_last(&f) == ',') {
+ f.length--;
+ }
return ds_cstr(&f);
}
-/* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
static void
-ofp_print_flow_mod(struct ds *string, const void *oh, size_t len,
- int verbosity)
+ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
{
- const struct ofp_flow_mod *ofm = oh;
-
- ds_put_char(string, ' ');
- ofp_print_match(string, &ofm->match, verbosity);
- if (ds_last(string) != ' ') {
- ds_put_char(string, ' ');
+ struct ofputil_flow_mod fm;
+ struct ofpbuf ofpacts;
+ bool need_priority;
+ enum ofperr error;
+ enum ofpraw raw;
+ enum ofputil_protocol protocol;
+
+ protocol = ofputil_protocol_from_ofp_version(oh->version);
+ protocol = ofputil_protocol_set_tid(protocol, true);
+
+ ofpbuf_init(&ofpacts, 64);
+ error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts);
+ if (error) {
+ ofpbuf_uninit(&ofpacts);
+ ofp_print_error(s, error);
+ return;
}
- switch (ntohs(ofm->command)) {
+ ds_put_char(s, ' ');
+ switch (fm.command) {
case OFPFC_ADD:
- ds_put_cstr(string, "ADD:");
+ ds_put_cstr(s, "ADD");
break;
case OFPFC_MODIFY:
- ds_put_cstr(string, "MOD:");
+ ds_put_cstr(s, "MOD");
break;
case OFPFC_MODIFY_STRICT:
- ds_put_cstr(string, "MOD_STRICT:");
+ ds_put_cstr(s, "MOD_STRICT");
break;
case OFPFC_DELETE:
- ds_put_cstr(string, "DEL:");
+ ds_put_cstr(s, "DEL");
break;
case OFPFC_DELETE_STRICT:
- ds_put_cstr(string, "DEL_STRICT:");
+ ds_put_cstr(s, "DEL_STRICT");
break;
default:
- ds_put_format(string, "cmd:%d", ntohs(ofm->command));
+ ds_put_format(s, "cmd:%d", fm.command);
}
- if (ofm->cookie != htonll(0)) {
- ds_put_format(string, " cookie:0x%"PRIx64, ntohll(ofm->cookie));
+ if (fm.table_id != 0) {
+ ds_put_format(s, " table:%d", fm.table_id);
}
- if (ofm->idle_timeout != htons(OFP_FLOW_PERMANENT)) {
- ds_put_format(string, " idle:%d", ntohs(ofm->idle_timeout));
+
+ ds_put_char(s, ' ');
+ ofpraw_decode(&raw, oh);
+ if (verbosity >= 3 && raw == OFPRAW_OFPT10_FLOW_MOD) {
+ const struct ofp10_flow_mod *ofm = ofpmsg_body(oh);
+ ofp10_match_print(s, &ofm->match, verbosity);
+
+ /* ofp_print_match() doesn't print priority. */
+ need_priority = true;
+ } else if (verbosity >= 3 && raw == OFPRAW_NXT_FLOW_MOD) {
+ const struct nx_flow_mod *nfm = ofpmsg_body(oh);
+ const void *nxm = nfm + 1;
+ char *nxm_s;
+
+ nxm_s = nx_match_to_string(nxm, ntohs(nfm->match_len));
+ ds_put_cstr(s, nxm_s);
+ free(nxm_s);
+
+ /* nx_match_to_string() doesn't print priority. */
+ need_priority = true;
+ } else {
+ match_format(&fm.match, s, fm.priority);
+
+ /* match_format() does print priority. */
+ need_priority = false;
}
- if (ofm->hard_timeout != htons(OFP_FLOW_PERMANENT)) {
- ds_put_format(string, " hard:%d", ntohs(ofm->hard_timeout));
+
+ if (ds_last(s) != ' ') {
+ ds_put_char(s, ' ');
}
- if (ofm->priority != htons(32768)) {
- ds_put_format(string, " pri:%"PRIu16, ntohs(ofm->priority));
+ if (fm.new_cookie != htonll(0) && fm.new_cookie != htonll(UINT64_MAX)) {
+ ds_put_format(s, "cookie:0x%"PRIx64" ", ntohll(fm.new_cookie));
}
- if (ofm->buffer_id != htonl(UINT32_MAX)) {
- ds_put_format(string, " buf:%#"PRIx32, ntohl(ofm->buffer_id));
+ if (fm.cookie_mask != htonll(0)) {
+ ds_put_format(s, "cookie:0x%"PRIx64"/0x%"PRIx64" ",
+ ntohll(fm.cookie), ntohll(fm.cookie_mask));
}
- if (ofm->flags != htons(0)) {
- ds_put_format(string, " flags:%"PRIx16, ntohs(ofm->flags));
+ if (fm.idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, "idle:%"PRIu16" ", fm.idle_timeout);
}
- ds_put_cstr(string, " ");
- ofp_print_actions(string, ofm->actions,
- len - offsetof(struct ofp_flow_mod, actions));
- ds_put_char(string, '\n');
-}
-
-/* Pretty-print the OFPT_FLOW_REMOVED packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
-static void
-ofp_print_flow_removed(struct ds *string, const void *oh,
- size_t len OVS_UNUSED, int verbosity)
-{
- const struct ofp_flow_removed *ofr = oh;
-
- ofp_print_match(string, &ofr->match, verbosity);
- ds_put_cstr(string, " reason=");
- switch (ofr->reason) {
- case OFPRR_IDLE_TIMEOUT:
- ds_put_cstr(string, "idle");
- break;
- case OFPRR_HARD_TIMEOUT:
- ds_put_cstr(string, "hard");
- break;
- case OFPRR_DELETE:
- ds_put_cstr(string, "delete");
- break;
- default:
- ds_put_format(string, "**%"PRIu8"**", ofr->reason);
- break;
+ if (fm.hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(s, "hard:%"PRIu16" ", fm.hard_timeout);
}
-
- if (ofr->cookie != htonll(0)) {
- ds_put_format(string, " cookie:0x%"PRIx64, ntohll(ofr->cookie));
+ if (fm.priority != OFP_DEFAULT_PRIORITY && need_priority) {
+ ds_put_format(s, "pri:%"PRIu16" ", fm.priority);
}
- if (ofr->priority != htons(32768)) {
- ds_put_format(string, " pri:%"PRIu16, ntohs(ofr->priority));
+ if (fm.buffer_id != UINT32_MAX) {
+ ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id);
}
- ds_put_format(string, " secs%"PRIu32" nsecs%"PRIu32
- " idle%"PRIu16" pkts%"PRIu64" bytes%"PRIu64"\n",
- ntohl(ofr->duration_sec), ntohl(ofr->duration_nsec),
- ntohs(ofr->idle_timeout), ntohll(ofr->packet_count),
- ntohll(ofr->byte_count));
-}
+ if (fm.out_port != OFPP_NONE) {
+ ds_put_format(s, "out_port:");
+ ofputil_format_port(fm.out_port, s);
+ ds_put_char(s, ' ');
+ }
+ if (fm.flags != 0) {
+ uint16_t flags = fm.flags;
-static void
-ofp_print_port_mod(struct ds *string, const void *oh, size_t len OVS_UNUSED,
- int verbosity OVS_UNUSED)
-{
- const struct ofp_port_mod *opm = oh;
+ if (flags & OFPFF_SEND_FLOW_REM) {
+ ds_put_cstr(s, "send_flow_rem ");
+ }
+ if (flags & OFPFF_CHECK_OVERLAP) {
+ ds_put_cstr(s, "check_overlap ");
+ }
+ if (flags & OFPFF10_EMERG) {
+ ds_put_cstr(s, "emerg ");
+ }
- ds_put_format(string, "port: %d: addr:"ETH_ADDR_FMT", config: %#x, mask:%#x\n",
- ntohs(opm->port_no), ETH_ADDR_ARGS(opm->hw_addr),
- ntohl(opm->config), ntohl(opm->mask));
- ds_put_format(string, " advertise: ");
- if (opm->advertise) {
- ofp_print_port_features(string, ntohl(opm->advertise));
- } else {
- ds_put_format(string, "UNCHANGED\n");
+ flags &= ~(OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF10_EMERG);
+ if (flags) {
+ ds_put_format(s, "flags:0x%"PRIx16" ", flags);
+ }
}
-}
-struct error_type {
- int type;
- int code;
- const char *name;
-};
-
-static const struct error_type error_types[] = {
-#define ERROR_TYPE(TYPE) {TYPE, -1, #TYPE}
-#define ERROR_CODE(TYPE, CODE) {TYPE, CODE, #CODE}
- ERROR_TYPE(OFPET_HELLO_FAILED),
- ERROR_CODE(OFPET_HELLO_FAILED, OFPHFC_INCOMPATIBLE),
- ERROR_CODE(OFPET_HELLO_FAILED, OFPHFC_EPERM),
-
- ERROR_TYPE(OFPET_BAD_REQUEST),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_EPERM),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BUFFER_EMPTY),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BUFFER_UNKNOWN),
-
- ERROR_TYPE(OFPET_BAD_ACTION),
- ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE),
- ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_LEN),
- ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR),
- ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE),
- ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT),
- ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT),
- ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_EPERM),
- ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_TOO_MANY),
-
- ERROR_TYPE(OFPET_FLOW_MOD_FAILED),
- ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL),
- ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP),
- ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_EPERM),
- ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_EMERG_TIMEOUT),
- ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND),
-
- ERROR_TYPE(OFPET_PORT_MOD_FAILED),
- ERROR_CODE(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT),
- ERROR_CODE(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR)
-};
-#define N_ERROR_TYPES ARRAY_SIZE(error_types)
+ ofpacts_format(fm.ofpacts, fm.ofpacts_len, s);
+ ofpbuf_uninit(&ofpacts);
+}
-static const char *
-lookup_error_type(int type)
+static void
+ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec)
{
- const struct error_type *t;
-
- for (t = error_types; t < &error_types[N_ERROR_TYPES]; t++) {
- if (t->type == type && t->code == -1) {
- return t->name;
+ ds_put_format(string, "%u", sec);
+ if (nsec > 0) {
+ ds_put_format(string, ".%09u", nsec);
+ while (string->string[string->length - 1] == '0') {
+ string->length--;
}
}
- return "?";
+ ds_put_char(string, 's');
}
static const char *
-lookup_error_code(int type, int code)
+ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason)
{
- const struct error_type *t;
+ static char s[32];
- for (t = error_types; t < &error_types[N_ERROR_TYPES]; t++) {
- if (t->type == type && t->code == code) {
- return t->name;
- }
+ switch (reason) {
+ case OFPRR_IDLE_TIMEOUT:
+ return "idle";
+ case OFPRR_HARD_TIMEOUT:
+ return "hard";
+ case OFPRR_DELETE:
+ return "delete";
+ case OFPRR_GROUP_DELETE:
+ return "group_delete";
+ case OFPRR_EVICTION:
+ return "eviction";
+ default:
+ sprintf(s, "%d", (int) reason);
+ return s;
}
- return "?";
}
-/* Pretty-print the OFPT_ERROR packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
static void
-ofp_print_error_msg(struct ds *string, const void *oh, size_t len,
- int verbosity OVS_UNUSED)
+ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
{
- const struct ofp_error_msg *oem = oh;
- int type = ntohs(oem->type);
- int code = ntohs(oem->code);
- char *s;
+ struct ofputil_flow_removed fr;
+ enum ofperr error;
- ds_put_format(string, " type%d(%s) code%d(%s) payload:\n",
- type, lookup_error_type(type),
- code, lookup_error_code(type, code));
+ error = ofputil_decode_flow_removed(&fr, oh);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
+ }
- switch (type) {
- case OFPET_HELLO_FAILED:
- ds_put_printable(string, (char *) oem->data, len - sizeof *oem);
- break;
+ ds_put_char(string, ' ');
+ match_format(&fr.match, string, fr.priority);
+
+ ds_put_format(string, " reason=%s",
+ ofp_flow_removed_reason_to_string(fr.reason));
+
+ if (fr.table_id != 255) {
+ ds_put_format(string, " table_id=%"PRIu8, fr.table_id);
+ }
+
+ if (fr.cookie != htonll(0)) {
+ ds_put_format(string, " cookie:0x%"PRIx64, ntohll(fr.cookie));
+ }
+ ds_put_cstr(string, " duration");
+ ofp_print_duration(string, fr.duration_sec, fr.duration_nsec);
+ ds_put_format(string, " idle%"PRIu16, fr.idle_timeout);
+ if (fr.hard_timeout) {
+ /* The hard timeout was only added in OF1.2, so only print it if it is
+ * actually in use to avoid gratuitous change to the formatting. */
+ ds_put_format(string, " hard%"PRIu16, fr.hard_timeout);
+ }
+ ds_put_format(string, " pkts%"PRIu64" bytes%"PRIu64"\n",
+ fr.packet_count, fr.byte_count);
+}
+
+static void
+ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
+{
+ struct ofputil_port_mod pm;
+ enum ofperr error;
+
+ error = ofputil_decode_port_mod(oh, &pm);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
+ }
+
+ ds_put_format(string, "port: %"PRIu16": addr:"ETH_ADDR_FMT"\n",
+ pm.port_no, ETH_ADDR_ARGS(pm.hw_addr));
+
+ ds_put_format(string, " config: ");
+ ofp_print_port_config(string, pm.config);
+
+ ds_put_format(string, " mask: ");
+ ofp_print_port_config(string, pm.mask);
+
+ ds_put_format(string, " advertise: ");
+ if (pm.advertise) {
+ ofp_print_port_features(string, pm.advertise);
+ } else {
+ ds_put_format(string, "UNCHANGED\n");
+ }
+}
- case OFPET_BAD_REQUEST:
- s = ofp_to_string(oem->data, len - sizeof *oem, 1);
+static void
+ofp_print_error(struct ds *string, enum ofperr error)
+{
+ if (string->length) {
+ ds_put_char(string, ' ');
+ }
+ ds_put_format(string, "***decode error: %s***\n", ofperr_get_name(error));
+}
+
+static void
+ofp_print_error_msg(struct ds *string, const struct ofp_header *oh)
+{
+ size_t len = ntohs(oh->length);
+ struct ofpbuf payload;
+ enum ofperr error;
+ char *s;
+
+ error = ofperr_decode_msg(oh, &payload);
+ if (!error) {
+ ds_put_cstr(string, "***decode error***");
+ ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true);
+ return;
+ }
+
+ ds_put_format(string, " %s\n", ofperr_get_name(error));
+
+ if (error == OFPERR_OFPHFC_INCOMPATIBLE || error == OFPERR_OFPHFC_EPERM) {
+ ds_put_printable(string, payload.data, payload.size);
+ } else {
+ s = ofp_to_string(payload.data, payload.size, 1);
ds_put_cstr(string, s);
free(s);
- break;
-
- default:
- ds_put_hex_dump(string, oem->data, len - sizeof *oem, 0, true);
- break;
}
}
-/* Pretty-print the OFPT_PORT_STATUS packet of 'len' bytes at 'oh' to 'string'
- * at the given 'verbosity' level. */
static void
-ofp_print_port_status(struct ds *string, const void *oh, size_t len OVS_UNUSED,
- int verbosity OVS_UNUSED)
+ofp_print_port_status(struct ds *string, const struct ofp_header *oh)
{
- const struct ofp_port_status *ops = oh;
+ struct ofputil_port_status ps;
+ enum ofperr error;
- if (ops->reason == OFPPR_ADD) {
+ error = ofputil_decode_port_status(oh, &ps);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
+ }
+
+ if (ps.reason == OFPPR_ADD) {
ds_put_format(string, " ADD:");
- } else if (ops->reason == OFPPR_DELETE) {
+ } else if (ps.reason == OFPPR_DELETE) {
ds_put_format(string, " DEL:");
- } else if (ops->reason == OFPPR_MODIFY) {
+ } else if (ps.reason == OFPPR_MODIFY) {
ds_put_format(string, " MOD:");
}
- ofp_print_phy_port(string, &ops->desc);
+ ofp_print_phy_port(string, &ps.desc);
}
static void
-ofp_desc_stats_reply(struct ds *string, const void *body,
- size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
+ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_header *oh)
{
- const struct ofp_desc_stats *ods = body;
+ const struct ofp_desc_stats *ods = ofpmsg_body(oh);
+ ds_put_char(string, '\n');
ds_put_format(string, "Manufacturer: %.*s\n",
(int) sizeof ods->mfr_desc, ods->mfr_desc);
ds_put_format(string, "Hardware: %.*s\n",
}
static void
-ofp_flow_stats_request(struct ds *string, const void *oh,
- size_t len OVS_UNUSED, int verbosity)
+ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh)
{
- const struct ofp_flow_stats_request *fsr = oh;
+ struct ofputil_flow_stats_request fsr;
+ enum ofperr error;
- if (fsr->table_id == 0xff) {
- ds_put_format(string, " table_id=any, ");
- } else {
- ds_put_format(string, " table_id=%"PRIu8", ", fsr->table_id);
+ error = ofputil_decode_flow_stats_request(&fsr, oh);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
+ }
+
+ if (fsr.table_id != 0xff) {
+ ds_put_format(string, " table=%"PRIu8, fsr.table_id);
+ }
+
+ if (fsr.out_port != OFPP_NONE) {
+ ds_put_cstr(string, " out_port=");
+ ofputil_format_port(fsr.out_port, string);
+ }
+
+ ds_put_char(string, ' ');
+ match_format(&fsr.match, string, OFP_DEFAULT_PRIORITY);
+}
+
+void
+ofp_print_flow_stats(struct ds *string, struct ofputil_flow_stats *fs)
+{
+ ds_put_format(string, " cookie=0x%"PRIx64", duration=",
+ ntohll(fs->cookie));
+
+ ofp_print_duration(string, fs->duration_sec, fs->duration_nsec);
+ ds_put_format(string, ", table=%"PRIu8", ", fs->table_id);
+ ds_put_format(string, "n_packets=%"PRIu64", ", fs->packet_count);
+ ds_put_format(string, "n_bytes=%"PRIu64", ", fs->byte_count);
+ if (fs->idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, "idle_timeout=%"PRIu16", ", fs->idle_timeout);
+ }
+ if (fs->hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, "hard_timeout=%"PRIu16", ", fs->hard_timeout);
+ }
+ if (fs->idle_age >= 0) {
+ ds_put_format(string, "idle_age=%d, ", fs->idle_age);
+ }
+ if (fs->hard_age >= 0 && fs->hard_age != fs->duration_sec) {
+ ds_put_format(string, "hard_age=%d, ", fs->hard_age);
+ }
+
+ match_format(&fs->match, string, fs->priority);
+ if (string->string[string->length - 1] != ' ') {
+ ds_put_char(string, ' ');
}
- ofp_print_match(string, &fsr->match, verbosity);
+ ofpacts_format(fs->ofpacts, fs->ofpacts_len, string);
}
static void
-ofp_flow_stats_reply(struct ds *string, const void *body_, size_t len,
- int verbosity)
+ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
{
- const char *body = body_;
- const char *pos = body;
+ struct ofpbuf ofpacts;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpbuf_init(&ofpacts, 64);
for (;;) {
- const struct ofp_flow_stats *fs;
- ptrdiff_t bytes_left = body + len - pos;
- size_t length;
-
- if (bytes_left < sizeof *fs) {
- if (bytes_left != 0) {
- ds_put_format(string, " ***%td leftover bytes at end***",
- bytes_left);
- }
- break;
- }
+ struct ofputil_flow_stats fs;
+ int retval;
- fs = (const void *) pos;
- length = ntohs(fs->length);
- if (length < sizeof *fs) {
- ds_put_format(string, " ***length=%zu shorter than minimum %zu***",
- length, sizeof *fs);
- break;
- } else if (length > bytes_left) {
- ds_put_format(string,
- " ***length=%zu but only %td bytes left***",
- length, bytes_left);
- break;
- } else if ((length - sizeof *fs) % sizeof fs->actions[0]) {
- ds_put_format(string,
- " ***length=%zu has %zu bytes leftover in "
- "final action***",
- length,
- (length - sizeof *fs) % sizeof fs->actions[0]);
+ retval = ofputil_decode_flow_stats_reply(&fs, &b, true, &ofpacts);
+ if (retval) {
+ if (retval != EOF) {
+ ds_put_cstr(string, " ***parse error***");
+ }
break;
}
-
- ds_put_format(string, " cookie=0x%"PRIx64", ", ntohll(fs->cookie));
- ds_put_format(string, "duration_sec=%"PRIu32"s, ",
- ntohl(fs->duration_sec));
- ds_put_format(string, "duration_nsec=%"PRIu32"ns, ",
- ntohl(fs->duration_nsec));
- ds_put_format(string, "table_id=%"PRIu8", ", fs->table_id);
- ds_put_format(string, "priority=%"PRIu16", ",
- fs->match.wildcards ? ntohs(fs->priority) : (uint16_t)-1);
- ds_put_format(string, "n_packets=%"PRIu64", ",
- ntohll(fs->packet_count));
- ds_put_format(string, "n_bytes=%"PRIu64", ", ntohll(fs->byte_count));
- if (fs->idle_timeout != htons(OFP_FLOW_PERMANENT)) {
- ds_put_format(string, "idle_timeout=%"PRIu16",",
- ntohs(fs->idle_timeout));
- }
- if (fs->hard_timeout != htons(OFP_FLOW_PERMANENT)) {
- ds_put_format(string, "hard_timeout=%"PRIu16",",
- ntohs(fs->hard_timeout));
- }
- ofp_print_match(string, &fs->match, verbosity);
- ofp_print_actions(string, fs->actions, length - sizeof *fs);
ds_put_char(string, '\n');
-
- pos += length;
+ ofp_print_flow_stats(string, &fs);
}
+ ofpbuf_uninit(&ofpacts);
}
static void
-ofp_aggregate_stats_request(struct ds *string, const void *oh,
- size_t len OVS_UNUSED, int verbosity)
+ofp_print_aggregate_stats_reply(struct ds *string, const struct ofp_header *oh)
{
- const struct ofp_aggregate_stats_request *asr = oh;
+ struct ofputil_aggregate_stats as;
+ enum ofperr error;
- if (asr->table_id == 0xff) {
- ds_put_format(string, " table_id=any, ");
- } else {
- ds_put_format(string, " table_id=%"PRIu8", ", asr->table_id);
+ error = ofputil_decode_aggregate_stats_reply(&as, oh);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
}
- ofp_print_match(string, &asr->match, verbosity);
+ ds_put_format(string, " packet_count=%"PRIu64, as.packet_count);
+ ds_put_format(string, " byte_count=%"PRIu64, as.byte_count);
+ ds_put_format(string, " flow_count=%"PRIu32, as.flow_count);
}
static void
-ofp_aggregate_stats_reply(struct ds *string, const void *body_,
- size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
-{
- const struct ofp_aggregate_stats_reply *asr = body_;
-
- ds_put_format(string, " packet_count=%"PRIu64, ntohll(asr->packet_count));
- ds_put_format(string, " byte_count=%"PRIu64, ntohll(asr->byte_count));
- ds_put_format(string, " flow_count=%"PRIu32, ntohl(asr->flow_count));
-}
-
-static void print_port_stat(struct ds *string, const char *leader,
- uint64_t stat, int more)
+print_port_stat(struct ds *string, const char *leader, uint64_t stat, int more)
{
ds_put_cstr(string, leader);
- if (stat != -1) {
+ if (stat != UINT64_MAX) {
ds_put_format(string, "%"PRIu64, stat);
} else {
ds_put_char(string, '?');
}
static void
-ofp_port_stats_request(struct ds *string, const void *body_,
- size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
+ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh)
{
- const struct ofp_port_stats_request *psr = body_;
- ds_put_format(string, "port_no=%"PRIu16, ntohs(psr->port_no));
+ uint16_t ofp10_port;
+ enum ofperr error;
+
+ error = ofputil_decode_port_stats_request(oh, &ofp10_port);
+ if (error) {
+ ofp_print_error(string, error);
+ return;
+ }
+
+ ds_put_format(string, " port_no=%2"PRIu16, ofp10_port);
}
static void
-ofp_port_stats_reply(struct ds *string, const void *body, size_t len,
- int verbosity)
+ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
{
- const struct ofp_port_stats *ps = body;
- size_t n = len / sizeof *ps;
- ds_put_format(string, " %zu ports\n", n);
+ struct ofpbuf b;
+
+ ds_put_format(string, " %zu ports\n", ofputil_count_port_stats(oh));
if (verbosity < 1) {
return;
}
- for (; n--; ps++) {
- ds_put_format(string, " port %2"PRIu16": ", ntohs(ps->port_no));
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ for (;;) {
+ struct ofputil_port_stats ps;
+ int retval;
- ds_put_cstr(string, "rx ");
- print_port_stat(string, "pkts=", ntohll(ps->rx_packets), 1);
- print_port_stat(string, "bytes=", ntohll(ps->rx_bytes), 1);
- print_port_stat(string, "drop=", ntohll(ps->rx_dropped), 1);
- print_port_stat(string, "errs=", ntohll(ps->rx_errors), 1);
- print_port_stat(string, "frame=", ntohll(ps->rx_frame_err), 1);
- print_port_stat(string, "over=", ntohll(ps->rx_over_err), 1);
- print_port_stat(string, "crc=", ntohll(ps->rx_crc_err), 0);
+ retval = ofputil_decode_port_stats(&ps, &b);
+ if (retval) {
+ if (retval != EOF) {
+ ds_put_cstr(string, " ***parse error***");
+ }
+ return;
+ }
+
+ ds_put_format(string, " port %2"PRIu16, ps.port_no);
+
+ ds_put_cstr(string, ": rx ");
+ print_port_stat(string, "pkts=", ps.stats.rx_packets, 1);
+ print_port_stat(string, "bytes=", ps.stats.rx_bytes, 1);
+ print_port_stat(string, "drop=", ps.stats.rx_dropped, 1);
+ print_port_stat(string, "errs=", ps.stats.rx_errors, 1);
+ print_port_stat(string, "frame=", ps.stats.rx_frame_errors, 1);
+ print_port_stat(string, "over=", ps.stats.rx_over_errors, 1);
+ print_port_stat(string, "crc=", ps.stats.rx_crc_errors, 0);
ds_put_cstr(string, " tx ");
- print_port_stat(string, "pkts=", ntohll(ps->tx_packets), 1);
- print_port_stat(string, "bytes=", ntohll(ps->tx_bytes), 1);
- print_port_stat(string, "drop=", ntohll(ps->tx_dropped), 1);
- print_port_stat(string, "errs=", ntohll(ps->tx_errors), 1);
- print_port_stat(string, "coll=", ntohll(ps->collisions), 0);
+ print_port_stat(string, "pkts=", ps.stats.tx_packets, 1);
+ print_port_stat(string, "bytes=", ps.stats.tx_bytes, 1);
+ print_port_stat(string, "drop=", ps.stats.tx_dropped, 1);
+ print_port_stat(string, "errs=", ps.stats.tx_errors, 1);
+ print_port_stat(string, "coll=", ps.stats.collisions, 0);
}
}
static void
-ofp_table_stats_reply(struct ds *string, const void *body, size_t len,
- int verbosity)
+ofp_print_one_ofpst_table_reply(struct ds *string, enum ofp_version ofp_version,
+ const char *name, struct ofp12_table_stats *ts)
+{
+ char name_[OFP_MAX_TABLE_NAME_LEN + 1];
+
+ ovs_strlcpy(name_, name, sizeof name_);
+
+ ds_put_format(string, " %d: %-8s: ", ts->table_id, name_);
+ ds_put_format(string, "wild=0x%05"PRIx64", ", ntohll(ts->wildcards));
+ ds_put_format(string, "max=%6"PRIu32", ", ntohl(ts->max_entries));
+ ds_put_format(string, "active=%"PRIu32"\n", ntohl(ts->active_count));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "lookup=%"PRIu64", ", ntohll(ts->lookup_count));
+ ds_put_format(string, "matched=%"PRIu64"\n", ntohll(ts->matched_count));
+
+ if (ofp_version < OFP11_VERSION) {
+ return;
+ }
+
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "match=0x%08"PRIx64", ", ntohll(ts->match));
+ ds_put_format(string, "instructions=0x%08"PRIx32", ",
+ ntohl(ts->instructions));
+ ds_put_format(string, "config=0x%08"PRIx32"\n", ntohl(ts->config));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "write_actions=0x%08"PRIx32", ",
+ ntohl(ts->write_actions));
+ ds_put_format(string, "apply_actions=0x%08"PRIx32"\n",
+ ntohl(ts->apply_actions));
+
+ if (ofp_version < OFP12_VERSION) {
+ return;
+ }
+
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "write_setfields=0x%016"PRIx64"\n",
+ ntohll(ts->write_setfields));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "apply_setfields=0x%016"PRIx64"\n",
+ ntohll(ts->apply_setfields));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "metadata_match=0x%016"PRIx64"\n",
+ ntohll(ts->metadata_match));
+ ds_put_cstr(string, " ");
+ ds_put_format(string, "metadata_write=0x%016"PRIx64"\n",
+ ntohll(ts->metadata_write));
+}
+
+static void
+ofp_print_ofpst_table_reply12(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
+{
+ struct ofp12_table_stats *ts;
+ struct ofpbuf b;
+ size_t n;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ n = b.size / sizeof *ts;
+ ds_put_format(string, " %zu tables\n", n);
+ if (verbosity < 1) {
+ return;
+ }
+
+ for (;;) {
+ ts = ofpbuf_try_pull(&b, sizeof *ts);
+ if (!ts) {
+ return;
+ }
+
+ ofp_print_one_ofpst_table_reply(string, OFP12_VERSION, ts->name, ts);
+ }
+}
+
+static void
+ofp_print_ofpst_table_reply11(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
+{
+ struct ofp11_table_stats *ts;
+ struct ofpbuf b;
+ size_t n;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ n = b.size / sizeof *ts;
+ ds_put_format(string, " %zu tables\n", n);
+ if (verbosity < 1) {
+ return;
+ }
+
+ for (;;) {
+ struct ofp12_table_stats ts12;
+
+ ts = ofpbuf_try_pull(&b, sizeof *ts);
+ if (!ts) {
+ return;
+ }
+
+ ts12.table_id = ts->table_id;
+ ts12.wildcards = htonll(ntohl(ts->wildcards));
+ ts12.max_entries = ts->max_entries;
+ ts12.active_count = ts->active_count;
+ ts12.lookup_count = ts->lookup_count;
+ ts12.matched_count = ts->matched_count;
+ ts12.match = htonll(ntohl(ts->match));
+ ts12.instructions = ts->instructions;
+ ts12.config = ts->config;
+ ts12.write_actions = ts->write_actions;
+ ts12.apply_actions = ts->apply_actions;
+ ofp_print_one_ofpst_table_reply(string, OFP11_VERSION, ts->name, &ts12);
+ }
+}
+
+static void
+ofp_print_ofpst_table_reply10(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
{
- const struct ofp_table_stats *ts = body;
- size_t n = len / sizeof *ts;
+ struct ofp10_table_stats *ts;
+ struct ofpbuf b;
+ size_t n;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ n = b.size / sizeof *ts;
ds_put_format(string, " %zu tables\n", n);
if (verbosity < 1) {
return;
}
- for (; n--; ts++) {
- char name[OFP_MAX_TABLE_NAME_LEN + 1];
- strncpy(name, ts->name, sizeof name);
- name[OFP_MAX_TABLE_NAME_LEN] = '\0';
-
- ds_put_format(string, " %d: %-8s: ", ts->table_id, name);
- ds_put_format(string, "wild=0x%05"PRIx32", ", ntohl(ts->wildcards));
- ds_put_format(string, "max=%6"PRIu32", ", ntohl(ts->max_entries));
- ds_put_format(string, "active=%"PRIu32"\n", ntohl(ts->active_count));
- ds_put_cstr(string, " ");
- ds_put_format(string, "lookup=%"PRIu64", ",
- ntohll(ts->lookup_count));
- ds_put_format(string, "matched=%"PRIu64"\n",
- ntohll(ts->matched_count));
+ for (;;) {
+ struct ofp12_table_stats ts12;
+
+ ts = ofpbuf_try_pull(&b, sizeof *ts);
+ if (!ts) {
+ return;
+ }
+
+ ts12.table_id = ts->table_id;
+ ts12.wildcards = htonll(ntohl(ts->wildcards));
+ ts12.max_entries = ts->max_entries;
+ ts12.active_count = ts->active_count;
+ ts12.lookup_count = get_32aligned_be64(&ts->lookup_count);
+ ts12.matched_count = get_32aligned_be64(&ts->matched_count);
+ ofp_print_one_ofpst_table_reply(string, OFP10_VERSION, ts->name, &ts12);
}
}
+static void
+ofp_print_ofpst_table_reply(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
+{
+ switch ((enum ofp_version)oh->version) {
+ case OFP12_VERSION:
+ ofp_print_ofpst_table_reply12(string, oh, verbosity);
+ break;
+
+ case OFP11_VERSION:
+ ofp_print_ofpst_table_reply11(string, oh, verbosity);
+ break;
+
+ case OFP10_VERSION:
+ ofp_print_ofpst_table_reply10(string, oh, verbosity);
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+}
+
static void
ofp_print_queue_name(struct ds *string, uint32_t queue_id)
{
}
static void
-ofp_queue_stats_request(struct ds *string, const void *body_,
- size_t len OVS_UNUSED, int verbosity OVS_UNUSED)
+ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh)
{
- const struct ofp_queue_stats_request *qsr = body_;
+ struct ofputil_queue_stats_request oqsr;
+ enum ofperr error;
+
+ error = ofputil_decode_queue_stats_request(oh, &oqsr);
+ if (error) {
+ ds_put_format(string, "***decode error: %s***\n", ofperr_get_name(error));
+ return;
+ }
ds_put_cstr(string, "port=");
- ofp_print_port_name(string, ntohs(qsr->port_no));
+ ofputil_format_port(oqsr.port_no, string);
ds_put_cstr(string, " queue=");
- ofp_print_queue_name(string, ntohl(qsr->queue_id));
+ ofp_print_queue_name(string, oqsr.queue_id);
}
static void
-ofp_queue_stats_reply(struct ds *string, const void *body, size_t len,
- int verbosity)
+ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh,
+ int verbosity)
{
- const struct ofp_queue_stats *qs = body;
- size_t n = len / sizeof *qs;
- ds_put_format(string, " %zu queues\n", n);
+ struct ofpbuf b;
+
+ ds_put_format(string, " %zu queues\n", ofputil_count_queue_stats(oh));
if (verbosity < 1) {
return;
}
- for (; n--; qs++) {
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ for (;;) {
+ struct ofputil_queue_stats qs;
+ int retval;
+
+ retval = ofputil_decode_queue_stats(&qs, &b);
+ if (retval) {
+ if (retval != EOF) {
+ ds_put_cstr(string, " ***parse error***");
+ }
+ return;
+ }
+
ds_put_cstr(string, " port ");
- ofp_print_port_name(string, ntohs(qs->port_no));
+ ofputil_format_port(qs.port_no, string);
ds_put_cstr(string, " queue ");
- ofp_print_queue_name(string, ntohl(qs->queue_id));
+ ofp_print_queue_name(string, qs.queue_id);
ds_put_cstr(string, ": ");
- print_port_stat(string, "bytes=", ntohll(qs->tx_bytes), 1);
- print_port_stat(string, "pkts=", ntohll(qs->tx_packets), 1);
- print_port_stat(string, "errors=", ntohll(qs->tx_errors), 0);
+ print_port_stat(string, "bytes=", qs.stats.tx_bytes, 1);
+ print_port_stat(string, "pkts=", qs.stats.tx_packets, 1);
+ print_port_stat(string, "errors=", qs.stats.tx_errors, 0);
}
}
static void
-vendor_stat(struct ds *string, const void *body, size_t len,
- int verbosity OVS_UNUSED)
+ofp_print_ofpst_port_desc_reply(struct ds *string,
+ const struct ofp_header *oh)
{
- ds_put_format(string, " vendor=%08"PRIx32, ntohl(*(uint32_t *) body));
- ds_put_format(string, " %zu bytes additional data",
- len - sizeof(uint32_t));
-}
+ struct ofpbuf b;
-enum stats_direction {
- REQUEST,
- REPLY
-};
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+ ds_put_char(string, '\n');
+ ofp_print_phy_ports(string, oh->version, &b);
+}
static void
-print_stats(struct ds *string, int type, const void *body, size_t body_len,
- int verbosity, enum stats_direction direction)
-{
- struct stats_msg {
- size_t min_body, max_body;
- void (*printer)(struct ds *, const void *, size_t len, int verbosity);
- };
-
- struct stats_type {
- int type;
- const char *name;
- struct stats_msg request;
- struct stats_msg reply;
- };
-
- static const struct stats_type stats_types[] = {
- {
- OFPST_DESC,
- "description",
- { 0, 0, NULL },
- { 0, SIZE_MAX, ofp_desc_stats_reply },
- },
- {
- OFPST_FLOW,
- "flow",
- { sizeof(struct ofp_flow_stats_request),
- sizeof(struct ofp_flow_stats_request),
- ofp_flow_stats_request },
- { 0, SIZE_MAX, ofp_flow_stats_reply },
- },
- {
- OFPST_AGGREGATE,
- "aggregate",
- { sizeof(struct ofp_aggregate_stats_request),
- sizeof(struct ofp_aggregate_stats_request),
- ofp_aggregate_stats_request },
- { sizeof(struct ofp_aggregate_stats_reply),
- sizeof(struct ofp_aggregate_stats_reply),
- ofp_aggregate_stats_reply },
- },
- {
- OFPST_TABLE,
- "table",
- { 0, 0, NULL },
- { 0, SIZE_MAX, ofp_table_stats_reply },
- },
- {
- OFPST_PORT,
- "port",
- { sizeof(struct ofp_port_stats_request),
- sizeof(struct ofp_port_stats_request),
- ofp_port_stats_request },
- { 0, SIZE_MAX, ofp_port_stats_reply },
- },
- {
- OFPST_QUEUE,
- "queue",
- { sizeof(struct ofp_queue_stats_request),
- sizeof(struct ofp_queue_stats_request),
- ofp_queue_stats_request },
- { 0, SIZE_MAX, ofp_queue_stats_reply },
- },
- {
- OFPST_VENDOR,
- "vendor-specific",
- { sizeof(uint32_t), SIZE_MAX, vendor_stat },
- { sizeof(uint32_t), SIZE_MAX, vendor_stat },
- },
- {
- -1,
- "unknown",
- { 0, 0, NULL, },
- { 0, 0, NULL, },
- },
- };
-
- const struct stats_type *s;
- const struct stats_msg *m;
-
- if (type >= ARRAY_SIZE(stats_types) || !stats_types[type].name) {
- ds_put_format(string, " ***unknown type %d***", type);
- return;
+ofp_print_stats_request(struct ds *string, const struct ofp_header *oh)
+{
+ uint16_t flags = ofpmp_flags(oh);
+
+ if (flags) {
+ ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags);
}
- for (s = stats_types; s->type >= 0; s++) {
- if (s->type == type) {
- break;
+}
+
+static void
+ofp_print_stats_reply(struct ds *string, const struct ofp_header *oh)
+{
+ uint16_t flags = ofpmp_flags(oh);
+
+ if (flags) {
+ ds_put_cstr(string, " flags=");
+ if (flags & OFPSF_REPLY_MORE) {
+ ds_put_cstr(string, "[more]");
+ flags &= ~OFPSF_REPLY_MORE;
+ }
+ if (flags) {
+ ds_put_format(string, "[***unknown flags 0x%04"PRIx16"***]",
+ flags);
}
}
- ds_put_format(string, " type=%d(%s)\n", type, s->name);
+}
- m = direction == REQUEST ? &s->request : &s->reply;
- if (body_len < m->min_body || body_len > m->max_body) {
- ds_put_format(string, " ***body_len=%zu not in %zu...%zu***",
- body_len, m->min_body, m->max_body);
- return;
+static void
+ofp_print_echo(struct ds *string, const struct ofp_header *oh, int verbosity)
+{
+ size_t len = ntohs(oh->length);
+
+ ds_put_format(string, " %zu bytes of payload\n", len - sizeof *oh);
+ if (verbosity > 1) {
+ ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true);
}
- if (m->printer) {
- m->printer(string, body, body_len, verbosity);
+}
+
+static void
+ofp_print_nxt_role_message(struct ds *string,
+ const struct nx_role_request *nrr)
+{
+ unsigned int role = ntohl(nrr->role);
+
+ ds_put_cstr(string, " role=");
+ if (role == NX_ROLE_OTHER) {
+ ds_put_cstr(string, "other");
+ } else if (role == NX_ROLE_MASTER) {
+ ds_put_cstr(string, "master");
+ } else if (role == NX_ROLE_SLAVE) {
+ ds_put_cstr(string, "slave");
+ } else {
+ ds_put_format(string, "%u", role);
}
}
static void
-ofp_stats_request(struct ds *string, const void *oh, size_t len, int verbosity)
+ofp_print_nxt_flow_mod_table_id(struct ds *string,
+ const struct nx_flow_mod_table_id *nfmti)
{
- const struct ofp_stats_request *srq = oh;
+ ds_put_format(string, " %s", nfmti->set ? "enable" : "disable");
+}
- if (srq->flags) {
- ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***",
- ntohs(srq->flags));
- }
+static void
+ofp_print_nxt_set_flow_format(struct ds *string,
+ const struct nx_set_flow_format *nsff)
+{
+ uint32_t format = ntohl(nsff->format);
- print_stats(string, ntohs(srq->type), srq->body,
- len - offsetof(struct ofp_stats_request, body),
- verbosity, REQUEST);
+ ds_put_cstr(string, " format=");
+ if (ofputil_nx_flow_format_is_valid(format)) {
+ ds_put_cstr(string, ofputil_nx_flow_format_to_string(format));
+ } else {
+ ds_put_format(string, "%"PRIu32, format);
+ }
}
static void
-ofp_stats_reply(struct ds *string, const void *oh, size_t len, int verbosity)
+ofp_print_nxt_set_packet_in_format(struct ds *string,
+ const struct nx_set_packet_in_format *nspf)
{
- const struct ofp_stats_reply *srp = oh;
+ uint32_t format = ntohl(nspf->format);
- ds_put_cstr(string, " flags=");
- if (!srp->flags) {
- ds_put_cstr(string, "none");
+ ds_put_cstr(string, " format=");
+ if (ofputil_packet_in_format_is_valid(format)) {
+ ds_put_cstr(string, ofputil_packet_in_format_to_string(format));
} else {
- uint16_t flags = ntohs(srp->flags);
- if (flags & OFPSF_REPLY_MORE) {
- ds_put_cstr(string, "[more]");
- flags &= ~OFPSF_REPLY_MORE;
+ ds_put_format(string, "%"PRIu32, format);
+ }
+}
+
+static const char *
+ofp_port_reason_to_string(enum ofp_port_reason reason)
+{
+ static char s[32];
+
+ switch (reason) {
+ case OFPPR_ADD:
+ return "add";
+
+ case OFPPR_DELETE:
+ return "delete";
+
+ case OFPPR_MODIFY:
+ return "modify";
+
+ default:
+ sprintf(s, "%d", (int) reason);
+ return s;
+ }
+}
+
+static void
+ofp_print_nxt_set_async_config(struct ds *string,
+ const struct nx_async_config *nac)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ int j;
+
+ ds_put_format(string, "\n %s:\n", i == 0 ? "master" : "slave");
+
+ ds_put_cstr(string, " PACKET_IN:");
+ for (j = 0; j < 32; j++) {
+ if (nac->packet_in_mask[i] & htonl(1u << j)) {
+ ds_put_format(string, " %s",
+ ofputil_packet_in_reason_to_string(j));
+ }
}
- if (flags) {
- ds_put_format(string, "[***unknown flags 0x%04"PRIx16"***]", flags);
+ if (!nac->packet_in_mask[i]) {
+ ds_put_cstr(string, " (off)");
+ }
+ ds_put_char(string, '\n');
+
+ ds_put_cstr(string, " PORT_STATUS:");
+ for (j = 0; j < 32; j++) {
+ if (nac->port_status_mask[i] & htonl(1u << j)) {
+ ds_put_format(string, " %s", ofp_port_reason_to_string(j));
+ }
+ }
+ if (!nac->port_status_mask[i]) {
+ ds_put_cstr(string, " (off)");
+ }
+ ds_put_char(string, '\n');
+
+ ds_put_cstr(string, " FLOW_REMOVED:");
+ for (j = 0; j < 32; j++) {
+ if (nac->flow_removed_mask[i] & htonl(1u << j)) {
+ ds_put_format(string, " %s",
+ ofp_flow_removed_reason_to_string(j));
+ }
+ }
+ if (!nac->flow_removed_mask[i]) {
+ ds_put_cstr(string, " (off)");
}
+ ds_put_char(string, '\n');
}
+}
- print_stats(string, ntohs(srp->type), srp->body,
- len - offsetof(struct ofp_stats_reply, body),
- verbosity, REPLY);
+static void
+ofp_print_nxt_set_controller_id(struct ds *string,
+ const struct nx_controller_id *nci)
+{
+ ds_put_format(string, " id=%"PRIu16, ntohs(nci->controller_id));
}
static void
-ofp_echo(struct ds *string, const void *oh, size_t len, int verbosity)
+ofp_print_nxt_flow_monitor_cancel(struct ds *string,
+ const struct ofp_header *oh)
{
- const struct ofp_header *hdr = oh;
+ ds_put_format(string, " id=%"PRIu32,
+ ofputil_decode_flow_monitor_cancel(oh));
+}
- ds_put_format(string, " %zu bytes of payload\n", len - sizeof *hdr);
- if (verbosity > 1) {
- ds_put_hex_dump(string, hdr, len - sizeof *hdr, 0, true);
- }
-}
-
-struct openflow_packet {
- uint8_t type;
- const char *name;
- size_t min_size;
- void (*printer)(struct ds *, const void *, size_t len, int verbosity);
-};
-
-static const struct openflow_packet packets[] = {
- {
- OFPT_HELLO,
- "hello",
- sizeof (struct ofp_header),
- NULL,
- },
- {
- OFPT_FEATURES_REQUEST,
- "features_request",
- sizeof (struct ofp_header),
- NULL,
- },
- {
- OFPT_FEATURES_REPLY,
- "features_reply",
- sizeof (struct ofp_switch_features),
- ofp_print_switch_features,
- },
- {
- OFPT_GET_CONFIG_REQUEST,
- "get_config_request",
- sizeof (struct ofp_header),
- NULL,
- },
- {
- OFPT_GET_CONFIG_REPLY,
- "get_config_reply",
- sizeof (struct ofp_switch_config),
- ofp_print_switch_config,
- },
- {
- OFPT_SET_CONFIG,
- "set_config",
- sizeof (struct ofp_switch_config),
- ofp_print_switch_config,
- },
- {
- OFPT_PACKET_IN,
- "packet_in",
- offsetof(struct ofp_packet_in, data),
- ofp_packet_in,
- },
- {
- OFPT_PACKET_OUT,
- "packet_out",
- sizeof (struct ofp_packet_out),
- ofp_packet_out,
- },
- {
- OFPT_FLOW_MOD,
- "flow_mod",
- sizeof (struct ofp_flow_mod),
- ofp_print_flow_mod,
- },
- {
- OFPT_FLOW_REMOVED,
- "flow_removed",
- sizeof (struct ofp_flow_removed),
- ofp_print_flow_removed,
- },
- {
- OFPT_PORT_MOD,
- "port_mod",
- sizeof (struct ofp_port_mod),
- ofp_print_port_mod,
- },
- {
- OFPT_PORT_STATUS,
- "port_status",
- sizeof (struct ofp_port_status),
- ofp_print_port_status
- },
- {
- OFPT_ERROR,
- "error_msg",
- sizeof (struct ofp_error_msg),
- ofp_print_error_msg,
- },
- {
- OFPT_STATS_REQUEST,
- "stats_request",
- sizeof (struct ofp_stats_request),
- ofp_stats_request,
- },
- {
- OFPT_STATS_REPLY,
- "stats_reply",
- sizeof (struct ofp_stats_reply),
- ofp_stats_reply,
- },
- {
- OFPT_ECHO_REQUEST,
- "echo_request",
- sizeof (struct ofp_header),
- ofp_echo,
- },
- {
- OFPT_ECHO_REPLY,
- "echo_reply",
- sizeof (struct ofp_header),
- ofp_echo,
- },
- {
- OFPT_VENDOR,
- "vendor",
- sizeof (struct ofp_vendor_header),
- NULL,
- },
- {
- OFPT_BARRIER_REQUEST,
- "barrier_request",
- sizeof (struct ofp_header),
- NULL,
- },
- {
- OFPT_BARRIER_REPLY,
- "barrier_reply",
- sizeof (struct ofp_header),
- NULL,
- }
-};
+static const char *
+nx_flow_monitor_flags_to_name(uint32_t bit)
+{
+ enum nx_flow_monitor_flags fmf = bit;
-/* Composes and returns a string representing the OpenFlow packet of 'len'
- * bytes at 'oh' at the given 'verbosity' level. 0 is a minimal amount of
- * verbosity and higher numbers increase verbosity. The caller is responsible
- * for freeing the string. */
-char *
-ofp_to_string(const void *oh_, size_t len, int verbosity)
+ switch (fmf) {
+ case NXFMF_INITIAL: return "initial";
+ case NXFMF_ADD: return "add";
+ case NXFMF_DELETE: return "delete";
+ case NXFMF_MODIFY: return "modify";
+ case NXFMF_ACTIONS: return "actions";
+ case NXFMF_OWN: return "own";
+ }
+
+ return NULL;
+}
+
+static void
+ofp_print_nxst_flow_monitor_request(struct ds *string,
+ const struct ofp_header *oh)
{
- struct ds string = DS_EMPTY_INITIALIZER;
- const struct ofp_header *oh = oh_;
- const struct openflow_packet *pkt;
-
- if (len < sizeof(struct ofp_header)) {
- ds_put_cstr(&string, "OpenFlow packet too short:\n");
- ds_put_hex_dump(&string, oh, len, 0, true);
- return ds_cstr(&string);
- } else if (oh->version != OFP_VERSION) {
- ds_put_format(&string, "Bad OpenFlow version %"PRIu8":\n", oh->version);
- ds_put_hex_dump(&string, oh, len, 0, true);
- return ds_cstr(&string);
- }
-
- for (pkt = packets; ; pkt++) {
- if (pkt >= &packets[ARRAY_SIZE(packets)]) {
- ds_put_format(&string, "Unknown OpenFlow packet type %"PRIu8":\n",
- oh->type);
- ds_put_hex_dump(&string, oh, len, 0, true);
- return ds_cstr(&string);
- } else if (oh->type == pkt->type) {
- break;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ for (;;) {
+ struct ofputil_flow_monitor_request request;
+ int retval;
+
+ retval = ofputil_decode_flow_monitor_request(&request, &b);
+ if (retval) {
+ if (retval != EOF) {
+ ofp_print_error(string, retval);
+ }
+ return;
}
- }
- ds_put_format(&string, "%s (xid=0x%"PRIx32"):", pkt->name, oh->xid);
+ ds_put_format(string, "\n id=%"PRIu32" flags=", request.id);
+ ofp_print_bit_names(string, request.flags,
+ nx_flow_monitor_flags_to_name, ',');
- if (ntohs(oh->length) > len)
- ds_put_format(&string, " (***truncated to %zu bytes from %"PRIu16"***)",
- len, ntohs(oh->length));
- else if (ntohs(oh->length) < len) {
- ds_put_format(&string, " (***only uses %"PRIu16" bytes out of %zu***)\n",
- ntohs(oh->length), len);
- len = ntohs(oh->length);
+ if (request.out_port != OFPP_NONE) {
+ ds_put_cstr(string, " out_port=");
+ ofputil_format_port(request.out_port, string);
+ }
+
+ if (request.table_id != 0xff) {
+ ds_put_format(string, " table=%"PRIu8, request.table_id);
+ }
+
+ ds_put_char(string, ' ');
+ match_format(&request.match, string, OFP_DEFAULT_PRIORITY);
+ ds_chomp(string, ' ');
}
+}
+
+static void
+ofp_print_nxst_flow_monitor_reply(struct ds *string,
+ const struct ofp_header *oh)
+{
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts;
+ struct ofpbuf b;
- if (len < pkt->min_size) {
- ds_put_format(&string, " (***length=%zu < min_size=%zu***)\n",
- len, pkt->min_size);
- } else if (!pkt->printer) {
- if (len > sizeof *oh) {
- ds_put_format(&string, " length=%"PRIu16" (decoder not implemented)\n",
- ntohs(oh->length));
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
+ for (;;) {
+ struct ofputil_flow_update update;
+ struct match match;
+ int retval;
+
+ update.match = &match;
+ retval = ofputil_decode_flow_update(&update, &b, &ofpacts);
+ if (retval) {
+ if (retval != EOF) {
+ ofp_print_error(string, retval);
+ }
+ ofpbuf_uninit(&ofpacts);
+ return;
+ }
+
+ ds_put_cstr(string, "\n event=");
+ switch (update.event) {
+ case NXFME_ADDED:
+ ds_put_cstr(string, "ADDED");
+ break;
+
+ case NXFME_DELETED:
+ ds_put_format(string, "DELETED reason=%s",
+ ofp_flow_removed_reason_to_string(update.reason));
+ break;
+
+ case NXFME_MODIFIED:
+ ds_put_cstr(string, "MODIFIED");
+ break;
+
+ case NXFME_ABBREV:
+ ds_put_format(string, "ABBREV xid=0x%"PRIx32, ntohl(update.xid));
+ continue;
+ }
+
+ ds_put_format(string, " table=%"PRIu8, update.table_id);
+ if (update.idle_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, " idle_timeout=%"PRIu16,
+ update.idle_timeout);
+ }
+ if (update.hard_timeout != OFP_FLOW_PERMANENT) {
+ ds_put_format(string, " hard_timeout=%"PRIu16,
+ update.hard_timeout);
+ }
+ ds_put_format(string, " cookie=%#"PRIx64, ntohll(update.cookie));
+
+ ds_put_char(string, ' ');
+ match_format(update.match, string, OFP_DEFAULT_PRIORITY);
+
+ if (update.ofpacts_len) {
+ if (string->string[string->length - 1] != ' ') {
+ ds_put_char(string, ' ');
+ }
+ ofpacts_format(update.ofpacts, update.ofpacts_len, string);
}
- } else {
- pkt->printer(&string, oh, len, verbosity);
}
- if (verbosity >= 3) {
- ds_put_hex_dump(&string, oh, len, 0, true);
+}
+
+void
+ofp_print_version(const struct ofp_header *oh,
+ struct ds *string)
+{
+ switch (oh->version) {
+ case OFP10_VERSION:
+ break;
+ case OFP11_VERSION:
+ ds_put_cstr(string, " (OF1.1)");
+ break;
+ case OFP12_VERSION:
+ ds_put_cstr(string, " (OF1.2)");
+ break;
+ default:
+ ds_put_format(string, " (OF 0x%02"PRIx8")", oh->version);
+ break;
}
- if (string.string[string.length - 1] != '\n') {
- ds_put_char(&string, '\n');
+ ds_put_format(string, " (xid=0x%"PRIx32"):", ntohl(oh->xid));
+}
+
+static void
+ofp_header_to_string__(const struct ofp_header *oh, enum ofpraw raw,
+ struct ds *string)
+{
+ ds_put_cstr(string, ofpraw_get_name(raw));
+ ofp_print_version(oh, string);
+}
+
+static void
+ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
+ struct ds *string, int verbosity)
+{
+ const void *msg = oh;
+
+ ofp_header_to_string__(oh, raw, string);
+ switch (ofptype_from_ofpraw(raw)) {
+ case OFPTYPE_HELLO:
+ ds_put_char(string, '\n');
+ ds_put_hex_dump(string, oh + 1, ntohs(oh->length) - sizeof *oh,
+ 0, true);
+ break;
+
+ case OFPTYPE_ERROR:
+ ofp_print_error_msg(string, oh);
+ break;
+
+ case OFPTYPE_ECHO_REQUEST:
+ case OFPTYPE_ECHO_REPLY:
+ ofp_print_echo(string, oh, verbosity);
+ break;
+
+ case OFPTYPE_FEATURES_REQUEST:
+ break;
+
+ case OFPTYPE_FEATURES_REPLY:
+ ofp_print_switch_features(string, oh);
+ break;
+
+ case OFPTYPE_GET_CONFIG_REQUEST:
+ break;
+
+ case OFPTYPE_GET_CONFIG_REPLY:
+ case OFPTYPE_SET_CONFIG:
+ ofp_print_switch_config(string, ofpmsg_body(oh));
+ break;
+
+ case OFPTYPE_PACKET_IN:
+ ofp_print_packet_in(string, oh, verbosity);
+ break;
+
+ case OFPTYPE_FLOW_REMOVED:
+ ofp_print_flow_removed(string, oh);
+ break;
+
+ case OFPTYPE_PORT_STATUS:
+ ofp_print_port_status(string, oh);
+ break;
+
+ case OFPTYPE_PACKET_OUT:
+ ofp_print_packet_out(string, oh, verbosity);
+ break;
+
+ case OFPTYPE_FLOW_MOD:
+ ofp_print_flow_mod(string, oh, verbosity);
+ break;
+
+ case OFPTYPE_PORT_MOD:
+ ofp_print_port_mod(string, oh);
+ break;
+
+ case OFPTYPE_BARRIER_REQUEST:
+ case OFPTYPE_BARRIER_REPLY:
+ break;
+
+ case OFPTYPE_DESC_STATS_REQUEST:
+ case OFPTYPE_PORT_DESC_STATS_REQUEST:
+ ofp_print_stats_request(string, oh);
+ break;
+
+ case OFPTYPE_FLOW_STATS_REQUEST:
+ case OFPTYPE_AGGREGATE_STATS_REQUEST:
+ ofp_print_stats_request(string, oh);
+ ofp_print_flow_stats_request(string, oh);
+ break;
+
+ case OFPTYPE_TABLE_STATS_REQUEST:
+ ofp_print_stats_request(string, oh);
+ break;
+
+ case OFPTYPE_PORT_STATS_REQUEST:
+ ofp_print_stats_request(string, oh);
+ ofp_print_ofpst_port_request(string, oh);
+ break;
+
+ case OFPTYPE_QUEUE_STATS_REQUEST:
+ ofp_print_stats_request(string, oh);
+ ofp_print_ofpst_queue_request(string, oh);
+ break;
+
+ case OFPTYPE_DESC_STATS_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_ofpst_desc_reply(string, oh);
+ break;
+
+ case OFPTYPE_FLOW_STATS_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_flow_stats_reply(string, oh);
+ break;
+
+ case OFPTYPE_QUEUE_STATS_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_ofpst_queue_reply(string, oh, verbosity);
+ break;
+
+ case OFPTYPE_PORT_STATS_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_ofpst_port_reply(string, oh, verbosity);
+ break;
+
+ case OFPTYPE_TABLE_STATS_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_ofpst_table_reply(string, oh, verbosity);
+ break;
+
+ case OFPTYPE_AGGREGATE_STATS_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_aggregate_stats_reply(string, oh);
+ break;
+
+ case OFPTYPE_PORT_DESC_STATS_REPLY:
+ ofp_print_stats_reply(string, oh);
+ ofp_print_ofpst_port_desc_reply(string, oh);
+ break;
+
+ case OFPTYPE_ROLE_REQUEST:
+ case OFPTYPE_ROLE_REPLY:
+ ofp_print_nxt_role_message(string, ofpmsg_body(oh));
+ break;
+
+ case OFPTYPE_FLOW_MOD_TABLE_ID:
+ ofp_print_nxt_flow_mod_table_id(string, ofpmsg_body(oh));
+ break;
+
+ case OFPTYPE_SET_FLOW_FORMAT:
+ ofp_print_nxt_set_flow_format(string, ofpmsg_body(oh));
+ break;
+
+ case OFPTYPE_SET_PACKET_IN_FORMAT:
+ ofp_print_nxt_set_packet_in_format(string, ofpmsg_body(oh));
+ break;
+
+ case OFPTYPE_FLOW_AGE:
+ break;
+
+ case OFPTYPE_SET_CONTROLLER_ID:
+ ofp_print_nxt_set_controller_id(string, ofpmsg_body(oh));
+ break;
+
+ case OFPTYPE_SET_ASYNC_CONFIG:
+ ofp_print_nxt_set_async_config(string, ofpmsg_body(oh));
+ break;
+
+ case OFPTYPE_FLOW_MONITOR_CANCEL:
+ ofp_print_nxt_flow_monitor_cancel(string, msg);
+ break;
+
+ case OFPTYPE_FLOW_MONITOR_PAUSED:
+ case OFPTYPE_FLOW_MONITOR_RESUMED:
+ break;
+
+ case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+ ofp_print_nxst_flow_monitor_request(string, msg);
+ break;
+
+ case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
+ ofp_print_nxst_flow_monitor_reply(string, msg);
+ break;
}
- return ds_cstr(&string);
}
-/* Returns the name for the specified OpenFlow message type as a string,
- * e.g. "OFPT_FEATURES_REPLY". If no name is known, the string returned is a
- * hex number, e.g. "0x55".
- *
- * The caller must free the returned string when it is no longer needed. */
+/* Composes and returns a string representing the OpenFlow packet of 'len'
+ * bytes at 'oh' at the given 'verbosity' level. 0 is a minimal amount of
+ * verbosity and higher numbers increase verbosity. The caller is responsible
+ * for freeing the string. */
char *
-ofp_message_type_to_string(uint8_t type)
+ofp_to_string(const void *oh_, size_t len, int verbosity)
{
- struct ds s = DS_EMPTY_INITIALIZER;
- const struct openflow_packet *pkt;
- for (pkt = packets; ; pkt++) {
- if (pkt >= &packets[ARRAY_SIZE(packets)]) {
- ds_put_format(&s, "0x%02"PRIx8, type);
- break;
- } else if (type == pkt->type) {
- const char *p;
+ struct ds string = DS_EMPTY_INITIALIZER;
+ const struct ofp_header *oh = oh_;
+
+ if (!len) {
+ ds_put_cstr(&string, "OpenFlow message is empty\n");
+ } else if (len < sizeof(struct ofp_header)) {
+ ds_put_format(&string, "OpenFlow packet too short (only %zu bytes):\n",
+ len);
+ } else if (ntohs(oh->length) > len) {
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = ofpraw_decode_partial(&raw, oh, len);
+ if (!error) {
+ ofp_header_to_string__(oh, raw, &string);
+ ds_put_char(&string, '\n');
+ }
- ds_put_cstr(&s, "OFPT_");
- for (p = pkt->name; *p; p++) {
- ds_put_char(&s, toupper((unsigned char) *p));
+ ds_put_format(&string,
+ "(***truncated to %zu bytes from %"PRIu16"***)\n",
+ len, ntohs(oh->length));
+ } else if (ntohs(oh->length) < len) {
+ ds_put_format(&string,
+ "(***only uses %"PRIu16" bytes out of %zu***)\n",
+ ntohs(oh->length), len);
+ } else {
+ enum ofperr error;
+ enum ofpraw raw;
+
+ error = ofpraw_decode(&raw, oh);
+ if (!error) {
+ ofp_to_string__(oh, raw, &string, verbosity);
+ if (verbosity >= 5) {
+ if (ds_last(&string) != '\n') {
+ ds_put_char(&string, '\n');
+ }
+ ds_put_hex_dump(&string, oh, len, 0, true);
}
- break;
+ if (ds_last(&string) != '\n') {
+ ds_put_char(&string, '\n');
+ }
+ return ds_steal_cstr(&string);
}
+
+ ofp_print_error(&string, error);
}
- return ds_cstr(&s);
+ ds_put_hex_dump(&string, oh, len, 0, true);
+ return ds_steal_cstr(&string);
}
\f
static void
}
/* Dumps the contents of the Ethernet frame in the 'len' bytes starting at
- * 'data' to 'stream' using tcpdump. 'total_len' specifies the full length of
- * the Ethernet frame (of which 'len' bytes were captured).
- *
- * This starts and kills a tcpdump subprocess so it's quite expensive. */
+ * 'data' to 'stream'. */
void
-ofp_print_packet(FILE *stream, const void *data, size_t len, size_t total_len)
+ofp_print_packet(FILE *stream, const void *data, size_t len)
{
- print_and_free(stream, ofp_packet_to_string(data, len, total_len));
+ print_and_free(stream, ofp_packet_to_string(data, len));
}