2009-07-08 13:19:16 -07:00
|
|
|
|
/*
|
2012-05-02 15:21:36 -07:00
|
|
|
|
* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at:
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include "ofp-print.h"
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <inttypes.h>
|
2010-02-12 12:51:36 -08:00
|
|
|
|
#include <sys/types.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
2011-06-10 17:45:45 -07:00
|
|
|
|
#include "bundle.h"
|
2010-10-28 17:13:18 -07:00
|
|
|
|
#include "byte-order.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "compiler.h"
|
|
|
|
|
#include "dynamic-string.h"
|
|
|
|
|
#include "flow.h"
|
2011-09-12 16:19:57 -07:00
|
|
|
|
#include "learn.h"
|
2010-12-17 14:38:50 -08:00
|
|
|
|
#include "multipath.h"
|
2011-12-28 12:42:14 -08:00
|
|
|
|
#include "meta-flow.h"
|
2012-02-15 16:33:04 -08:00
|
|
|
|
#include "netdev.h"
|
2010-12-07 13:22:46 -08:00
|
|
|
|
#include "nx-match.h"
|
2012-07-03 22:17:14 -07:00
|
|
|
|
#include "ofp-actions.h"
|
2012-01-12 15:48:19 -08:00
|
|
|
|
#include "ofp-errors.h"
|
2012-07-19 23:23:17 -07:00
|
|
|
|
#include "ofp-msgs.h"
|
2010-11-10 16:22:18 -08:00
|
|
|
|
#include "ofp-util.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "ofpbuf.h"
|
|
|
|
|
#include "openflow/openflow.h"
|
|
|
|
|
#include "openflow/nicira-ext.h"
|
|
|
|
|
#include "packets.h"
|
|
|
|
|
#include "pcap.h"
|
2010-12-09 10:41:32 -08:00
|
|
|
|
#include "type-props.h"
|
2011-01-18 11:50:56 -08:00
|
|
|
|
#include "unaligned.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "util.h"
|
|
|
|
|
|
2010-09-16 15:36:57 -07:00
|
|
|
|
static void ofp_print_queue_name(struct ds *string, uint32_t port);
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static void ofp_print_error(struct ds *, enum ofperr);
|
2010-12-07 13:22:46 -08:00
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
/* Returns a string that represents the contents of the Ethernet frame in the
|
2011-12-21 12:59:28 -08:00
|
|
|
|
* 'len' bytes starting at 'data'. The caller must free the returned string.*/
|
2009-07-08 13:19:16 -07:00
|
|
|
|
char *
|
2011-12-22 17:47:15 -08:00
|
|
|
|
ofp_packet_to_string(const void *data, size_t len)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
struct ds ds = DS_EMPTY_INITIALIZER;
|
|
|
|
|
struct ofpbuf buf;
|
2011-12-21 12:59:28 -08:00
|
|
|
|
struct flow flow;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-12-02 14:53:12 -08:00
|
|
|
|
ofpbuf_use_const(&buf, data, len);
|
2012-09-13 20:11:08 -07:00
|
|
|
|
flow_extract(&buf, 0, NULL, 0, &flow);
|
2011-12-21 12:59:28 -08:00
|
|
|
|
flow_format(&ds, &flow);
|
2011-12-20 19:56:43 -08:00
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-21 12:59:28 -08:00
|
|
|
|
ds_put_char(&ds, '\n');
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
return ds_cstr(&ds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2011-12-22 16:35:23 -08:00
|
|
|
|
ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
int verbosity)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2011-12-22 16:35:23 -08:00
|
|
|
|
struct ofputil_packet_in pin;
|
|
|
|
|
int error;
|
2012-01-04 16:40:13 -08:00
|
|
|
|
int i;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2011-12-22 16:35:23 -08:00
|
|
|
|
error = ofputil_decode_packet_in(&pin, oh);
|
|
|
|
|
if (error) {
|
|
|
|
|
ofp_print_error(string, error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-09 15:48:26 -08:00
|
|
|
|
if (pin.table_id) {
|
|
|
|
|
ds_put_format(string, " table_id=%"PRIu8, pin.table_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin.cookie) {
|
|
|
|
|
ds_put_format(string, " cookie=0x%"PRIx64, ntohll(pin.cookie));
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-22 16:35:23 -08:00
|
|
|
|
ds_put_format(string, " total_len=%"PRIu16" in_port=", pin.total_len);
|
2012-01-04 16:40:13 -08:00
|
|
|
|
ofputil_format_port(pin.fmd.in_port, string);
|
|
|
|
|
|
Do not include zeroed metadata fields in NXM/OXM packet-in messages.
NXM and OpenFlow 1.2+ allow including the values of arbitrary flow metadata
in "packet-in" messages. Open vSwitch has until now always included all
the values of the metadata fields that it implements in NXT_PACKET_IN
messages.
However, this has at least two disadvantages:
- Most of the metadata fields tend to be zero most of the time, which
wastes space in the message.
- It means that controllers must be very liberal about accepting
fields that they know nothing about in packet-in messages, since any
switch upgrade could cause new fields to appear even if the
controller does nothing to give them nonzero values. (Controllers
have to be prepared to tolerate unknown fields in any case, but this
property makes unknown fields more likely to appear than otherwise.)
This commit changes Open vSwitch so that metadata fields whose values are
zero are not reported in packet-ins, fixing both problems. (This is
explicitly allowed by OpenFlow 1.2+.)
This commit mainly fixes a sort of internal conceptual dissonance centering
around struct flow_metadata. This structure is supposed to report the
metadata for a given flow. If you look at a flow, it has particular
metadata values; it doesn't have masks, and the idea of a mask for a
particular flow doesn't really make sense. However, struct flow_metadata
did have masks. This led to internal confusion; one can see this in, for
example, the following code removed by this commit in ofproto-dpif.c to
handle misses in the OpenFlow flow table:
/* Registers aren't meaningful on a miss. */
memset(pin.fmd.reg_masks, 0, sizeof pin.fmd.reg_masks);
What this code was really trying to say is that on a flow miss, the
registers are zero, so they shouldn't be included in the packet-in message.
It did manage to omit the registers, by marking them as "wild", but it is
conceptually more correct to simply omit them because they are zero (and
that's one effect of this commit).
Bug #12968.
Reported-by: Igor Ganichev <iganichev@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2012-08-15 10:16:49 -07:00
|
|
|
|
if (pin.fmd.tun_id != htonll(0)) {
|
2012-01-04 16:40:13 -08:00
|
|
|
|
ds_put_format(string, " tun_id=0x%"PRIx64, ntohll(pin.fmd.tun_id));
|
|
|
|
|
}
|
|
|
|
|
|
Do not include zeroed metadata fields in NXM/OXM packet-in messages.
NXM and OpenFlow 1.2+ allow including the values of arbitrary flow metadata
in "packet-in" messages. Open vSwitch has until now always included all
the values of the metadata fields that it implements in NXT_PACKET_IN
messages.
However, this has at least two disadvantages:
- Most of the metadata fields tend to be zero most of the time, which
wastes space in the message.
- It means that controllers must be very liberal about accepting
fields that they know nothing about in packet-in messages, since any
switch upgrade could cause new fields to appear even if the
controller does nothing to give them nonzero values. (Controllers
have to be prepared to tolerate unknown fields in any case, but this
property makes unknown fields more likely to appear than otherwise.)
This commit changes Open vSwitch so that metadata fields whose values are
zero are not reported in packet-ins, fixing both problems. (This is
explicitly allowed by OpenFlow 1.2+.)
This commit mainly fixes a sort of internal conceptual dissonance centering
around struct flow_metadata. This structure is supposed to report the
metadata for a given flow. If you look at a flow, it has particular
metadata values; it doesn't have masks, and the idea of a mask for a
particular flow doesn't really make sense. However, struct flow_metadata
did have masks. This led to internal confusion; one can see this in, for
example, the following code removed by this commit in ofproto-dpif.c to
handle misses in the OpenFlow flow table:
/* Registers aren't meaningful on a miss. */
memset(pin.fmd.reg_masks, 0, sizeof pin.fmd.reg_masks);
What this code was really trying to say is that on a flow miss, the
registers are zero, so they shouldn't be included in the packet-in message.
It did manage to omit the registers, by marking them as "wild", but it is
conceptually more correct to simply omit them because they are zero (and
that's one effect of this commit).
Bug #12968.
Reported-by: Igor Ganichev <iganichev@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2012-08-15 10:16:49 -07:00
|
|
|
|
if (pin.fmd.metadata != htonll(0)) {
|
2012-06-27 01:09:44 +12:00
|
|
|
|
ds_put_format(string, " metadata=0x%"PRIx64, ntohll(pin.fmd.metadata));
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-04 16:40:13 -08:00
|
|
|
|
for (i = 0; i < FLOW_N_REGS; i++) {
|
Do not include zeroed metadata fields in NXM/OXM packet-in messages.
NXM and OpenFlow 1.2+ allow including the values of arbitrary flow metadata
in "packet-in" messages. Open vSwitch has until now always included all
the values of the metadata fields that it implements in NXT_PACKET_IN
messages.
However, this has at least two disadvantages:
- Most of the metadata fields tend to be zero most of the time, which
wastes space in the message.
- It means that controllers must be very liberal about accepting
fields that they know nothing about in packet-in messages, since any
switch upgrade could cause new fields to appear even if the
controller does nothing to give them nonzero values. (Controllers
have to be prepared to tolerate unknown fields in any case, but this
property makes unknown fields more likely to appear than otherwise.)
This commit changes Open vSwitch so that metadata fields whose values are
zero are not reported in packet-ins, fixing both problems. (This is
explicitly allowed by OpenFlow 1.2+.)
This commit mainly fixes a sort of internal conceptual dissonance centering
around struct flow_metadata. This structure is supposed to report the
metadata for a given flow. If you look at a flow, it has particular
metadata values; it doesn't have masks, and the idea of a mask for a
particular flow doesn't really make sense. However, struct flow_metadata
did have masks. This led to internal confusion; one can see this in, for
example, the following code removed by this commit in ofproto-dpif.c to
handle misses in the OpenFlow flow table:
/* Registers aren't meaningful on a miss. */
memset(pin.fmd.reg_masks, 0, sizeof pin.fmd.reg_masks);
What this code was really trying to say is that on a flow miss, the
registers are zero, so they shouldn't be included in the packet-in message.
It did manage to omit the registers, by marking them as "wild", but it is
conceptually more correct to simply omit them because they are zero (and
that's one effect of this commit).
Bug #12968.
Reported-by: Igor Ganichev <iganichev@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2012-08-15 10:16:49 -07:00
|
|
|
|
if (pin.fmd.regs[i]) {
|
2012-01-04 16:40:13 -08:00
|
|
|
|
ds_put_format(string, " reg%d=0x%"PRIx32, i, pin.fmd.regs[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-02-09 14:06:35 -08:00
|
|
|
|
ds_put_format(string, " (via %s)",
|
2012-02-07 14:46:34 -08:00
|
|
|
|
ofputil_packet_in_reason_to_string(pin.reason));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2011-12-22 16:35:23 -08:00
|
|
|
|
ds_put_format(string, " data_len=%zu", pin.packet_len);
|
|
|
|
|
if (pin.buffer_id == UINT32_MAX) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " (unbuffered)");
|
2011-12-22 16:35:23 -08:00
|
|
|
|
if (pin.total_len != pin.packet_len) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " (***total_len != data_len***)");
|
2011-12-22 16:35:23 -08:00
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
} else {
|
2011-12-22 16:35:23 -08:00
|
|
|
|
ds_put_format(string, " buffer=0x%08"PRIx32, pin.buffer_id);
|
|
|
|
|
if (pin.total_len < pin.packet_len) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " (***total_len < data_len***)");
|
2011-12-22 16:35:23 -08:00
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
ds_put_char(string, '\n');
|
|
|
|
|
|
|
|
|
|
if (verbosity > 0) {
|
2011-12-22 16:35:23 -08:00
|
|
|
|
char *packet = ofp_packet_to_string(pin.packet, pin.packet_len);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_cstr(string, packet);
|
|
|
|
|
free(packet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-06 10:20:20 -08:00
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
int verbosity)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-02-06 14:17:49 -08:00
|
|
|
|
struct ofputil_packet_out po;
|
2012-07-03 22:17:14 -07:00
|
|
|
|
struct ofpbuf ofpacts;
|
2012-02-06 14:17:49 -08:00
|
|
|
|
enum ofperr error;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpbuf_init(&ofpacts, 64);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
error = ofputil_decode_packet_out(&po, oh, &ofpacts);
|
2012-02-06 14:17:49 -08:00
|
|
|
|
if (error) {
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
2012-02-06 14:17:49 -08:00
|
|
|
|
ofp_print_error(string, error);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return;
|
|
|
|
|
}
|
2012-02-06 14:17:49 -08:00
|
|
|
|
|
|
|
|
|
ds_put_cstr(string, " in_port=");
|
|
|
|
|
ofputil_format_port(po.in_port, string);
|
|
|
|
|
|
|
|
|
|
ds_put_char(string, ' ');
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpacts_format(po.ofpacts, po.ofpacts_len, string);
|
2012-02-06 14:17:49 -08:00
|
|
|
|
|
|
|
|
|
if (po.buffer_id == UINT32_MAX) {
|
2012-02-09 01:39:49 -08:00
|
|
|
|
ds_put_format(string, " data_len=%zu", po.packet_len);
|
2012-02-06 14:17:49 -08:00
|
|
|
|
if (verbosity > 0 && po.packet_len > 0) {
|
|
|
|
|
char *packet = ofp_packet_to_string(po.packet, po.packet_len);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_char(string, '\n');
|
|
|
|
|
ds_put_cstr(string, packet);
|
|
|
|
|
free(packet);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2012-02-06 14:17:49 -08:00
|
|
|
|
ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
ds_put_char(string, '\n');
|
2012-07-03 22:17:14 -07:00
|
|
|
|
|
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* qsort comparison function. */
|
|
|
|
|
static int
|
|
|
|
|
compare_ports(const void *a_, const void *b_)
|
|
|
|
|
{
|
2012-02-15 16:33:04 -08:00
|
|
|
|
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;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
return ap < bp ? -1 : ap > bp;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-14 15:17:33 -07:00
|
|
|
|
static void
|
|
|
|
|
ofp_print_bit_names(struct ds *string, uint32_t bits,
|
2012-06-29 16:23:43 -07:00
|
|
|
|
const char *(*bit_to_name)(uint32_t bit),
|
|
|
|
|
char separator)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2011-07-14 15:17:33 -07:00
|
|
|
|
int n = 0;
|
2012-02-15 16:33:04 -08:00
|
|
|
|
int i;
|
2011-07-14 15:17:33 -07:00
|
|
|
|
|
|
|
|
|
if (!bits) {
|
|
|
|
|
ds_put_cstr(string, "0");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return;
|
|
|
|
|
}
|
2011-07-14 15:17:33 -07:00
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
|
uint32_t bit = UINT32_C(1) << i;
|
|
|
|
|
|
|
|
|
|
if (bits & bit) {
|
|
|
|
|
const char *name = bit_to_name(bit);
|
|
|
|
|
if (name) {
|
|
|
|
|
if (n++) {
|
2012-06-29 16:23:43 -07:00
|
|
|
|
ds_put_char(string, separator);
|
2012-02-15 16:33:04 -08:00
|
|
|
|
}
|
|
|
|
|
ds_put_cstr(string, name);
|
|
|
|
|
bits &= ~bit;
|
2011-07-14 15:17:33 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2011-07-14 15:17:33 -07:00
|
|
|
|
|
|
|
|
|
if (bits) {
|
2012-05-13 16:34:49 -07:00
|
|
|
|
if (n) {
|
2012-06-29 16:23:43 -07:00
|
|
|
|
ds_put_char(string, separator);
|
2011-07-14 15:17:33 -07:00
|
|
|
|
}
|
|
|
|
|
ds_put_format(string, "0x%"PRIx32, bits);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2011-07-14 15:17:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-14 15:17:33 -07:00
|
|
|
|
static void
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_port_features(struct ds *string, enum netdev_features features)
|
2011-07-14 15:17:33 -07:00
|
|
|
|
{
|
2012-06-29 16:23:43 -07:00
|
|
|
|
ofp_print_bit_names(string, features, netdev_feature_to_name, ' ');
|
2011-07-14 15:17:33 -07:00
|
|
|
|
ds_put_char(string, '\n');
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
static const char *
|
|
|
|
|
ofputil_port_config_to_name(uint32_t bit)
|
|
|
|
|
{
|
|
|
|
|
enum ofputil_port_config pc = bit;
|
|
|
|
|
|
|
|
|
|
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 NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-14 15:17:33 -07:00
|
|
|
|
static void
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_port_config(struct ds *string, enum ofputil_port_config config)
|
2011-07-14 15:17:33 -07:00
|
|
|
|
{
|
2012-06-29 16:23:43 -07:00
|
|
|
|
ofp_print_bit_names(string, config, ofputil_port_config_to_name, ' ');
|
2011-07-14 15:17:33 -07:00
|
|
|
|
ds_put_char(string, '\n');
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
static const char *
|
|
|
|
|
ofputil_port_state_to_name(uint32_t bit)
|
|
|
|
|
{
|
|
|
|
|
enum ofputil_port_state ps = bit;
|
|
|
|
|
|
|
|
|
|
switch (ps) {
|
|
|
|
|
case OFPUTIL_PS_LINK_DOWN: return "LINK_DOWN";
|
|
|
|
|
case OFPUTIL_PS_BLOCKED: return "BLOCKED";
|
|
|
|
|
case OFPUTIL_PS_LIVE: return "LIVE";
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_PS_STP_LISTEN:
|
|
|
|
|
case OFPUTIL_PS_STP_LEARN:
|
|
|
|
|
case OFPUTIL_PS_STP_FORWARD:
|
|
|
|
|
case OFPUTIL_PS_STP_BLOCK:
|
|
|
|
|
/* Handled elsewhere. */
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-14 15:17:33 -07:00
|
|
|
|
static void
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_port_state(struct ds *string, enum ofputil_port_state state)
|
2011-07-14 15:17:33 -07:00
|
|
|
|
{
|
2012-02-15 16:33:04 -08:00
|
|
|
|
enum ofputil_port_state stp_state;
|
2011-07-14 15:17:33 -07:00
|
|
|
|
|
|
|
|
|
/* 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. */
|
2012-02-15 16:33:04 -08:00
|
|
|
|
stp_state = state & OFPUTIL_PS_STP_MASK;
|
2011-07-14 15:17:33 -07:00
|
|
|
|
if (stp_state) {
|
2012-02-15 16:33:04 -08:00
|
|
|
|
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;
|
2011-07-14 15:17:33 -07:00
|
|
|
|
if (state) {
|
2012-06-29 16:23:43 -07:00
|
|
|
|
ofp_print_bit_names(string, state, ofputil_port_state_to_name,
|
|
|
|
|
' ');
|
2011-07-14 15:17:33 -07:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2012-06-29 16:23:43 -07:00
|
|
|
|
ofp_print_bit_names(string, state, ofputil_port_state_to_name, ' ');
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
ds_put_char(string, '\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_phy_port(struct ds *string, const struct ofputil_phy_port *port)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-02-15 16:33:04 -08:00
|
|
|
|
char name[sizeof port->name];
|
2009-07-08 13:19:16 -07:00
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
memcpy(name, port->name, sizeof name);
|
|
|
|
|
for (j = 0; j < sizeof name - 1; j++) {
|
2011-04-15 09:31:36 -07:00
|
|
|
|
if (!isprint((unsigned char) name[j])) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
name[j] = '\0';
|
|
|
|
|
|
|
|
|
|
ds_put_char(string, ' ');
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofputil_format_port(port->port_no, string);
|
2011-07-14 15:17:33 -07:00
|
|
|
|
ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT"\n",
|
|
|
|
|
name, ETH_ADDR_ARGS(port->hw_addr));
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(string, " config: ");
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_port_config(string, port->config);
|
2011-07-14 15:17:33 -07:00
|
|
|
|
|
|
|
|
|
ds_put_cstr(string, " state: ");
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_port_state(string, port->state);
|
2011-07-14 15:17:33 -07:00
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (port->curr) {
|
|
|
|
|
ds_put_format(string, " current: ");
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_port_features(string, port->curr);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
if (port->advertised) {
|
|
|
|
|
ds_put_format(string, " advertised: ");
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_port_features(string, port->advertised);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
if (port->supported) {
|
|
|
|
|
ds_put_format(string, " supported: ");
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_port_features(string, port->supported);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
if (port->peer) {
|
|
|
|
|
ds_put_format(string, " peer: ");
|
2012-02-15 16:33:04 -08:00
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-04 14:42:04 -07:00
|
|
|
|
/* 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_phy_ports(struct ds *string, uint8_t ofp_version,
|
|
|
|
|
struct ofpbuf *b)
|
|
|
|
|
{
|
|
|
|
|
size_t n_ports;
|
|
|
|
|
struct ofputil_phy_port *ports;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
n_ports = ofputil_count_phy_ports(ofp_version, b);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
qsort(ports, n_ports, sizeof *ports, compare_ports);
|
|
|
|
|
for (i = 0; i < n_ports; i++) {
|
|
|
|
|
ofp_print_phy_port(string, &ports[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
free(ports);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
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";
|
2012-07-30 11:03:03 +09:00
|
|
|
|
case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED";
|
2012-02-15 16:33:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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";
|
2012-09-04 10:32:48 +09:00
|
|
|
|
case OFPUTIL_A_SET_FIELD: return "SET_FIELD";
|
2012-02-15 16:33:04 -08:00
|
|
|
|
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";
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2012-02-15 16:33:04 -08:00
|
|
|
|
|
|
|
|
|
return NULL;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_switch_features(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-02-15 16:33:04 -08:00
|
|
|
|
struct ofputil_switch_features features;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
struct ofpbuf b;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
error = ofputil_decode_switch_features(oh, &features, &b);
|
2012-02-15 16:33:04 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
ofp_print_error(string, error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
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);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ds_put_cstr(string, "capabilities: ");
|
|
|
|
|
ofp_print_bit_names(string, features.capabilities,
|
2012-06-29 16:23:43 -07:00
|
|
|
|
ofputil_capabilities_to_name, ' ');
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ds_put_char(string, '\n');
|
|
|
|
|
|
2012-07-30 11:03:00 +09:00
|
|
|
|
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();
|
|
|
|
|
}
|
2012-02-15 16:33:04 -08:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_phy_ports(string, oh->version, &b);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_switch_config(struct ds *string, const struct ofp_switch_config *osc)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-01-13 17:54:04 -08:00
|
|
|
|
enum ofp_config_flags flags;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
flags = ntohs(osc->flags);
|
2010-12-14 12:08:10 -08:00
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
ds_put_format(string, " frags=%s", ofputil_frag_handling_to_string(flags));
|
|
|
|
|
flags &= ~OFPC_FRAG_MASK;
|
|
|
|
|
|
2012-01-13 17:54:04 -08:00
|
|
|
|
if (flags & OFPC_INVALID_TTL_TO_CONTROLLER) {
|
|
|
|
|
ds_put_format(string, " invalid_ttl_to_controller");
|
|
|
|
|
flags &= ~OFPC_INVALID_TTL_TO_CONTROLLER;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void print_wild(struct ds *string, const char *leader, int is_wild,
|
2010-08-30 00:24:53 -07:00
|
|
|
|
int verbosity, const char *format, ...)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
__attribute__((format(printf, 5, 6)));
|
|
|
|
|
|
|
|
|
|
static void print_wild(struct ds *string, const char *leader, int is_wild,
|
2010-08-30 00:24:53 -07:00
|
|
|
|
int verbosity, const char *format, ...)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
if (is_wild && verbosity < 2) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ds_put_cstr(string, leader);
|
|
|
|
|
if (!is_wild) {
|
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
|
ds_put_format_valist(string, format, args);
|
|
|
|
|
va_end(args);
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_char(string, '*');
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(string, ',');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2011-03-29 14:42:20 -07:00
|
|
|
|
print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
|
2009-07-08 13:19:16 -07:00
|
|
|
|
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, ',');
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-14 13:03:57 -07:00
|
|
|
|
void
|
2012-05-21 21:51:03 -07:00
|
|
|
|
ofp10_match_print(struct ds *f, const struct ofp10_match *om, int verbosity)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-05-21 21:51:03 -07:00
|
|
|
|
char *s = ofp10_match_to_string(om, verbosity);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_cstr(f, s);
|
|
|
|
|
free(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
2012-05-21 21:51:03 -07:00
|
|
|
|
ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
struct ds f = DS_EMPTY_INITIALIZER;
|
|
|
|
|
uint32_t w = ntohl(om->wildcards);
|
|
|
|
|
bool skip_type = false;
|
|
|
|
|
bool skip_proto = false;
|
|
|
|
|
|
2012-05-21 21:51:03 -07:00
|
|
|
|
if (!(w & OFPFW10_DL_TYPE)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
skip_type = true;
|
|
|
|
|
if (om->dl_type == htons(ETH_TYPE_IP)) {
|
2012-05-21 21:51:03 -07:00
|
|
|
|
if (!(w & OFPFW10_NW_PROTO)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
skip_proto = true;
|
2011-02-02 11:33:20 -08:00
|
|
|
|
if (om->nw_proto == IPPROTO_ICMP) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_cstr(&f, "icmp,");
|
2011-02-02 11:33:20 -08:00
|
|
|
|
} else if (om->nw_proto == IPPROTO_TCP) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_cstr(&f, "tcp,");
|
2011-02-02 11:33:20 -08:00
|
|
|
|
} else if (om->nw_proto == IPPROTO_UDP) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
|
2009-07-08 13:19:16 -07:00
|
|
|
|
"%d", ntohs(om->in_port));
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "dl_vlan=", w & OFPFW10_DL_VLAN, verbosity,
|
2010-04-10 01:20:23 -07:00
|
|
|
|
"%d", ntohs(om->dl_vlan));
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "dl_vlan_pcp=", w & OFPFW10_DL_VLAN_PCP, verbosity,
|
2009-11-11 14:59:49 -08:00
|
|
|
|
"%d", om->dl_vlan_pcp);
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "dl_src=", w & OFPFW10_DL_SRC, verbosity,
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "dl_dst=", w & OFPFW10_DL_DST, verbosity,
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
|
|
|
|
|
if (!skip_type) {
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "dl_type=", w & OFPFW10_DL_TYPE, verbosity,
|
2009-07-08 13:19:16 -07:00
|
|
|
|
"0x%04x", ntohs(om->dl_type));
|
|
|
|
|
}
|
|
|
|
|
print_ip_netmask(&f, "nw_src=", om->nw_src,
|
2012-05-21 21:51:03 -07:00
|
|
|
|
(w & OFPFW10_NW_SRC_MASK) >> OFPFW10_NW_SRC_SHIFT,
|
|
|
|
|
verbosity);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
print_ip_netmask(&f, "nw_dst=", om->nw_dst,
|
2012-05-21 21:51:03 -07:00
|
|
|
|
(w & OFPFW10_NW_DST_MASK) >> OFPFW10_NW_DST_SHIFT,
|
|
|
|
|
verbosity);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (!skip_proto) {
|
2009-11-09 16:43:47 -08:00
|
|
|
|
if (om->dl_type == htons(ETH_TYPE_ARP)) {
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "arp_op=", w & OFPFW10_NW_PROTO, verbosity,
|
2009-11-09 16:43:47 -08:00
|
|
|
|
"%u", om->nw_proto);
|
|
|
|
|
} else {
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "nw_proto=", w & OFPFW10_NW_PROTO, verbosity,
|
2009-11-09 16:43:47 -08:00
|
|
|
|
"%u", om->nw_proto);
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "nw_tos=", w & OFPFW10_NW_TOS, verbosity,
|
2010-11-05 10:47:50 +09:00
|
|
|
|
"%u", om->nw_tos);
|
2011-02-02 11:33:20 -08:00
|
|
|
|
if (om->nw_proto == IPPROTO_ICMP) {
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "icmp_type=", w & OFPFW10_ICMP_TYPE, verbosity,
|
2011-10-05 11:06:12 -07:00
|
|
|
|
"%d", ntohs(om->tp_src));
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "icmp_code=", w & OFPFW10_ICMP_CODE, verbosity,
|
2011-10-05 11:06:12 -07:00
|
|
|
|
"%d", ntohs(om->tp_dst));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
} else {
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "tp_src=", w & OFPFW10_TP_SRC, verbosity,
|
2009-07-08 13:19:16 -07:00
|
|
|
|
"%d", ntohs(om->tp_src));
|
2012-05-21 21:51:03 -07:00
|
|
|
|
print_wild(&f, "tp_dst=", w & OFPFW10_TP_DST, verbosity,
|
2009-07-08 13:19:16 -07:00
|
|
|
|
"%d", ntohs(om->tp_dst));
|
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
if (ds_last(&f) == ',') {
|
|
|
|
|
f.length--;
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return ds_cstr(&f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2011-08-08 14:46:38 -07:00
|
|
|
|
struct ofputil_flow_mod fm;
|
2012-07-03 22:17:14 -07:00
|
|
|
|
struct ofpbuf ofpacts;
|
2010-12-14 11:36:04 -08:00
|
|
|
|
bool need_priority;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
enum ofpraw raw;
|
2012-08-01 16:01:51 +09:00
|
|
|
|
enum ofputil_protocol protocol;
|
|
|
|
|
|
|
|
|
|
protocol = ofputil_protocol_from_ofp_version(oh->version);
|
|
|
|
|
protocol = ofputil_protocol_set_tid(protocol, true);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpbuf_init(&ofpacts, 64);
|
2012-08-01 16:01:51 +09:00
|
|
|
|
error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts);
|
2010-12-07 13:22:46 -08:00
|
|
|
|
if (error) {
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ofp_print_error(s, error);
|
|
|
|
|
return;
|
2010-10-01 13:05:59 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ds_put_char(s, ' ');
|
|
|
|
|
switch (fm.command) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
case OFPFC_ADD:
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ds_put_cstr(s, "ADD");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
case OFPFC_MODIFY:
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ds_put_cstr(s, "MOD");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
case OFPFC_MODIFY_STRICT:
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ds_put_cstr(s, "MOD_STRICT");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
case OFPFC_DELETE:
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ds_put_cstr(s, "DEL");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
case OFPFC_DELETE_STRICT:
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ds_put_cstr(s, "DEL_STRICT");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ds_put_format(s, "cmd:%d", fm.command);
|
|
|
|
|
}
|
2011-05-12 09:58:01 -07:00
|
|
|
|
if (fm.table_id != 0) {
|
2011-06-08 12:36:53 -07:00
|
|
|
|
ds_put_format(s, " table:%d", fm.table_id);
|
2011-05-12 09:58:01 -07:00
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
|
|
|
|
|
ds_put_char(s, ' ');
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofpraw_decode(&raw, oh);
|
|
|
|
|
if (verbosity >= 3 && raw == OFPRAW_OFPT10_FLOW_MOD) {
|
2012-07-03 23:01:59 -07:00
|
|
|
|
const struct ofp10_flow_mod *ofm = ofpmsg_body(oh);
|
2012-05-21 21:51:03 -07:00
|
|
|
|
ofp10_match_print(s, &ofm->match, verbosity);
|
2010-12-14 11:36:04 -08:00
|
|
|
|
|
|
|
|
|
/* ofp_print_match() doesn't print priority. */
|
|
|
|
|
need_priority = true;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
} else if (verbosity >= 3 && raw == OFPRAW_NXT_FLOW_MOD) {
|
|
|
|
|
const struct nx_flow_mod *nfm = ofpmsg_body(oh);
|
2010-12-07 13:22:46 -08:00
|
|
|
|
const void *nxm = nfm + 1;
|
2010-12-14 11:36:04 -08:00
|
|
|
|
char *nxm_s;
|
|
|
|
|
|
|
|
|
|
nxm_s = nx_match_to_string(nxm, ntohs(nfm->match_len));
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ds_put_cstr(s, nxm_s);
|
|
|
|
|
free(nxm_s);
|
2010-12-14 11:36:04 -08:00
|
|
|
|
|
|
|
|
|
/* nx_match_to_string() doesn't print priority. */
|
|
|
|
|
need_priority = true;
|
2010-12-07 13:22:46 -08:00
|
|
|
|
} else {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_format(&fm.match, s, fm.priority);
|
2010-12-14 11:36:04 -08:00
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
/* match_format() does print priority. */
|
2010-12-14 11:36:04 -08:00
|
|
|
|
need_priority = false;
|
2010-10-01 13:05:59 -07:00
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
|
|
|
|
|
if (ds_last(s) != ' ') {
|
|
|
|
|
ds_put_char(s, ' ');
|
2010-10-01 13:05:59 -07:00
|
|
|
|
}
|
2012-03-24 01:02:26 -07:00
|
|
|
|
if (fm.new_cookie != htonll(0)) {
|
|
|
|
|
ds_put_format(s, "cookie:0x%"PRIx64" ", ntohll(fm.new_cookie));
|
|
|
|
|
}
|
|
|
|
|
if (fm.cookie_mask != htonll(0)) {
|
|
|
|
|
ds_put_format(s, "cookie:0x%"PRIx64"/0x%"PRIx64" ",
|
|
|
|
|
ntohll(fm.cookie), ntohll(fm.cookie_mask));
|
2010-10-01 13:05:59 -07:00
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
if (fm.idle_timeout != OFP_FLOW_PERMANENT) {
|
|
|
|
|
ds_put_format(s, "idle:%"PRIu16" ", fm.idle_timeout);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
if (fm.hard_timeout != OFP_FLOW_PERMANENT) {
|
|
|
|
|
ds_put_format(s, "hard:%"PRIu16" ", fm.hard_timeout);
|
2010-10-01 13:05:59 -07:00
|
|
|
|
}
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (fm.priority != OFP_DEFAULT_PRIORITY && need_priority) {
|
|
|
|
|
ds_put_format(s, "pri:%"PRIu16" ", fm.priority);
|
2010-10-01 13:05:59 -07:00
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
if (fm.buffer_id != UINT32_MAX) {
|
|
|
|
|
ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id);
|
2010-10-01 13:05:59 -07:00
|
|
|
|
}
|
2012-07-06 13:05:38 -07:00
|
|
|
|
if (fm.out_port != OFPP_NONE) {
|
|
|
|
|
ds_put_format(s, "out_port:");
|
|
|
|
|
ofputil_format_port(fm.out_port, s);
|
|
|
|
|
ds_put_char(s, ' ');
|
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
if (fm.flags != 0) {
|
2012-01-25 13:54:15 -08:00
|
|
|
|
uint16_t flags = fm.flags;
|
|
|
|
|
|
|
|
|
|
if (flags & OFPFF_SEND_FLOW_REM) {
|
|
|
|
|
ds_put_cstr(s, "send_flow_rem ");
|
|
|
|
|
}
|
|
|
|
|
if (flags & OFPFF_CHECK_OVERLAP) {
|
|
|
|
|
ds_put_cstr(s, "check_overlap ");
|
|
|
|
|
}
|
2012-07-03 23:01:59 -07:00
|
|
|
|
if (flags & OFPFF10_EMERG) {
|
2012-01-25 13:54:15 -08:00
|
|
|
|
ds_put_cstr(s, "emerg ");
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-03 23:01:59 -07:00
|
|
|
|
flags &= ~(OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF10_EMERG);
|
2012-01-25 13:54:15 -08:00
|
|
|
|
if (flags) {
|
|
|
|
|
ds_put_format(s, "flags:0x%"PRIx16" ", flags);
|
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpacts_format(fm.ofpacts, fm.ofpacts_len, s);
|
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 15:45:10 -08:00
|
|
|
|
static void
|
|
|
|
|
ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec)
|
|
|
|
|
{
|
|
|
|
|
ds_put_format(string, "%u", sec);
|
|
|
|
|
if (nsec > 0) {
|
|
|
|
|
ds_put_format(string, ".%09u", nsec);
|
|
|
|
|
while (string->string[string->length - 1] == '0') {
|
|
|
|
|
string->length--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(string, 's');
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-09 14:06:35 -08:00
|
|
|
|
static const char *
|
|
|
|
|
ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason)
|
|
|
|
|
{
|
|
|
|
|
static char s[32];
|
|
|
|
|
|
|
|
|
|
switch (reason) {
|
|
|
|
|
case OFPRR_IDLE_TIMEOUT:
|
|
|
|
|
return "idle";
|
|
|
|
|
case OFPRR_HARD_TIMEOUT:
|
|
|
|
|
return "hard";
|
|
|
|
|
case OFPRR_DELETE:
|
|
|
|
|
return "delete";
|
2012-03-30 11:30:00 +09:00
|
|
|
|
case OFPRR_GROUP_DELETE:
|
|
|
|
|
return "group_delete";
|
2012-07-03 14:23:10 -07:00
|
|
|
|
case OFPRR_EVICTION:
|
|
|
|
|
return "eviction";
|
2012-02-09 14:06:35 -08:00
|
|
|
|
default:
|
|
|
|
|
sprintf(s, "%d", (int) reason);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2010-12-09 10:31:49 -08:00
|
|
|
|
ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-12-09 10:31:49 -08:00
|
|
|
|
struct ofputil_flow_removed fr;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-12-09 10:31:49 -08:00
|
|
|
|
|
2011-04-26 09:42:18 -07:00
|
|
|
|
error = ofputil_decode_flow_removed(&fr, oh);
|
2010-12-09 10:31:49 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
ofp_print_error(string, error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-13 16:19:19 -08:00
|
|
|
|
ds_put_char(string, ' ');
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_format(&fr.match, string, fr.priority);
|
2010-12-09 10:31:49 -08:00
|
|
|
|
|
2012-02-09 14:06:35 -08:00
|
|
|
|
ds_put_format(string, " reason=%s",
|
|
|
|
|
ofp_flow_removed_reason_to_string(fr.reason));
|
2010-10-01 13:05:59 -07:00
|
|
|
|
|
2012-10-01 16:51:49 +09:00
|
|
|
|
if (fr.table_id != 255) {
|
|
|
|
|
ds_put_format(string, " table_id=%"PRIu8, fr.table_id);
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-09 10:31:49 -08:00
|
|
|
|
if (fr.cookie != htonll(0)) {
|
|
|
|
|
ds_put_format(string, " cookie:0x%"PRIx64, ntohll(fr.cookie));
|
2010-10-01 13:05:59 -07:00
|
|
|
|
}
|
2010-12-07 15:45:10 -08:00
|
|
|
|
ds_put_cstr(string, " duration");
|
2010-12-09 10:31:49 -08:00
|
|
|
|
ofp_print_duration(string, fr.duration_sec, fr.duration_nsec);
|
2012-08-09 11:01:48 -07:00
|
|
|
|
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);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-02-15 16:33:04 -08:00
|
|
|
|
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);
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " advertise: ");
|
2012-02-15 16:33:04 -08:00
|
|
|
|
if (pm.advertise) {
|
|
|
|
|
ofp_print_port_features(string, pm.advertise);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(string, "UNCHANGED\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:22:46 -08:00
|
|
|
|
static void
|
2012-01-12 15:48:19 -08:00
|
|
|
|
ofp_print_error(struct ds *string, enum ofperr error)
|
2010-12-07 13:22:46 -08:00
|
|
|
|
{
|
|
|
|
|
if (string->length) {
|
|
|
|
|
ds_put_char(string, ' ');
|
|
|
|
|
}
|
2012-01-12 15:48:19 -08:00
|
|
|
|
ds_put_format(string, "***decode error: %s***\n", ofperr_get_name(error));
|
2010-12-07 21:57:09 -08:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_error_msg(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-19 23:23:17 -07:00
|
|
|
|
size_t len = ntohs(oh->length);
|
|
|
|
|
struct ofpbuf payload;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
char *s;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
error = ofperr_decode_msg(oh, &payload);
|
2012-01-12 15:48:19 -08:00
|
|
|
|
if (!error) {
|
|
|
|
|
ds_put_cstr(string, "***decode error***");
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true);
|
2011-01-12 13:42:50 -08:00
|
|
|
|
return;
|
2010-12-07 21:57:09 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
ds_put_format(string, " %s\n", ofperr_get_name(error));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
if (error == OFPERR_OFPHFC_INCOMPATIBLE || error == OFPERR_OFPHFC_EPERM) {
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ds_put_printable(string, payload.data, payload.size);
|
2012-01-12 15:48:19 -08:00
|
|
|
|
} else {
|
2012-07-19 23:23:17 -07:00
|
|
|
|
s = ofp_to_string(payload.data, payload.size, 1);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_cstr(string, s);
|
|
|
|
|
free(s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_port_status(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-02-15 16:33:04 -08:00
|
|
|
|
struct ofputil_port_status ps;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
error = ofputil_decode_port_status(oh, &ps);
|
2012-02-15 16:33:04 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
ofp_print_error(string, error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ps.reason == OFPPR_ADD) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " ADD:");
|
2012-02-15 16:33:04 -08:00
|
|
|
|
} else if (ps.reason == OFPPR_DELETE) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " DEL:");
|
2012-02-15 16:33:04 -08:00
|
|
|
|
} else if (ps.reason == OFPPR_MODIFY) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " MOD:");
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
ofp_print_phy_port(string, &ps.desc);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-19 23:23:17 -07:00
|
|
|
|
const struct ofp_desc_stats *ods = ofpmsg_body(oh);
|
|
|
|
|
|
2010-12-13 16:19:19 -08:00
|
|
|
|
ds_put_char(string, '\n');
|
2010-08-30 00:24:53 -07:00
|
|
|
|
ds_put_format(string, "Manufacturer: %.*s\n",
|
2010-01-22 15:12:34 -08:00
|
|
|
|
(int) sizeof ods->mfr_desc, ods->mfr_desc);
|
|
|
|
|
ds_put_format(string, "Hardware: %.*s\n",
|
|
|
|
|
(int) sizeof ods->hw_desc, ods->hw_desc);
|
|
|
|
|
ds_put_format(string, "Software: %.*s\n",
|
|
|
|
|
(int) sizeof ods->sw_desc, ods->sw_desc);
|
|
|
|
|
ds_put_format(string, "Serial Num: %.*s\n",
|
|
|
|
|
(int) sizeof ods->serial_num, ods->serial_num);
|
|
|
|
|
ds_put_format(string, "DP Description: %.*s\n",
|
|
|
|
|
(int) sizeof ods->dp_desc, ods->dp_desc);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2011-08-08 14:48:48 -07:00
|
|
|
|
struct ofputil_flow_stats_request fsr;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
error = ofputil_decode_flow_stats_request(&fsr, oh);
|
2010-12-07 14:52:26 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
ofp_print_error(string, error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fsr.table_id != 0xff) {
|
2011-06-08 12:36:53 -07:00
|
|
|
|
ds_put_format(string, " table=%"PRIu8, fsr.table_id);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 14:52:26 -08:00
|
|
|
|
if (fsr.out_port != OFPP_NONE) {
|
|
|
|
|
ds_put_cstr(string, " out_port=");
|
2011-08-16 15:26:18 -07:00
|
|
|
|
ofputil_format_port(fsr.out_port, string);
|
2010-12-07 14:52:26 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_put_char(string, ' ');
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_format(&fsr.match, string, OFP_DEFAULT_PRIORITY);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-12 13:32:47 -07:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_format(&fs->match, string, fs->priority);
|
2012-07-12 13:32:47 -07:00
|
|
|
|
if (string->string[string->length - 1] != ' ') {
|
|
|
|
|
ds_put_char(string, ' ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpacts_format(fs->ofpacts, fs->ofpacts_len, string);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2011-03-10 15:02:05 -08:00
|
|
|
|
ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-03 22:17:14 -07:00
|
|
|
|
struct ofpbuf ofpacts;
|
2011-03-10 15:02:05 -08:00
|
|
|
|
struct ofpbuf b;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2011-03-10 15:02:05 -08:00
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpbuf_init(&ofpacts, 64);
|
2011-03-10 15:02:05 -08:00
|
|
|
|
for (;;) {
|
|
|
|
|
struct ofputil_flow_stats fs;
|
|
|
|
|
int retval;
|
2010-12-08 12:05:20 -08:00
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
retval = ofputil_decode_flow_stats_reply(&fs, &b, true, &ofpacts);
|
2011-03-10 15:02:05 -08:00
|
|
|
|
if (retval) {
|
|
|
|
|
if (retval != EOF) {
|
|
|
|
|
ds_put_cstr(string, " ***parse error***");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-12-09 12:31:31 -08:00
|
|
|
|
ds_put_char(string, '\n');
|
2012-07-12 13:32:47 -07:00
|
|
|
|
ofp_print_flow_stats(string, &fs);
|
|
|
|
|
}
|
2012-08-17 13:59:15 -07:00
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
2010-12-07 14:21:38 -08:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_aggregate_stats_reply(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-19 23:23:17 -07:00
|
|
|
|
struct ofputil_aggregate_stats as;
|
|
|
|
|
enum ofperr error;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
error = ofputil_decode_aggregate_stats_reply(&as, oh);
|
|
|
|
|
if (error) {
|
|
|
|
|
ofp_print_error(string, error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2010-12-07 15:07:54 -08:00
|
|
|
|
}
|
|
|
|
|
|
2010-08-30 00:24:53 -07:00
|
|
|
|
static void print_port_stat(struct ds *string, const char *leader,
|
2011-01-18 11:50:56 -08:00
|
|
|
|
const ovs_32aligned_be64 *statp, int more)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2011-01-18 11:50:56 -08:00
|
|
|
|
uint64_t stat = ntohll(get_32aligned_be64(statp));
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_cstr(string, leader);
|
2011-01-18 11:50:56 -08:00
|
|
|
|
if (stat != UINT64_MAX) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, "%"PRIu64, stat);
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_char(string, '?');
|
|
|
|
|
}
|
|
|
|
|
if (more) {
|
|
|
|
|
ds_put_cstr(string, ", ");
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_cstr(string, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-19 22:35:18 -08:00
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh)
|
2010-01-19 22:35:18 -08:00
|
|
|
|
{
|
2012-06-27 21:13:13 -07:00
|
|
|
|
const struct ofp10_port_stats_request *psr = ofpmsg_body(oh);
|
2010-12-13 16:19:19 -08:00
|
|
|
|
ds_put_format(string, " port_no=%"PRIu16, ntohs(psr->port_no));
|
2010-01-19 22:35:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
|
|
|
|
|
int verbosity)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-06-27 21:13:13 -07:00
|
|
|
|
struct ofp10_port_stats *ps;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
|
|
|
|
ofpraw_pull_assert(&b);
|
|
|
|
|
|
|
|
|
|
n = b.size / sizeof *ps;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " %zu ports\n", n);
|
|
|
|
|
if (verbosity < 1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
for (;;) {
|
|
|
|
|
ps = ofpbuf_try_pull(&b, sizeof *ps);
|
|
|
|
|
if (!ps) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " port %2"PRIu16": ", ntohs(ps->port_no));
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(string, "rx ");
|
2011-01-18 11:50:56 -08:00
|
|
|
|
print_port_stat(string, "pkts=", &ps->rx_packets, 1);
|
|
|
|
|
print_port_stat(string, "bytes=", &ps->rx_bytes, 1);
|
|
|
|
|
print_port_stat(string, "drop=", &ps->rx_dropped, 1);
|
|
|
|
|
print_port_stat(string, "errs=", &ps->rx_errors, 1);
|
|
|
|
|
print_port_stat(string, "frame=", &ps->rx_frame_err, 1);
|
|
|
|
|
print_port_stat(string, "over=", &ps->rx_over_err, 1);
|
|
|
|
|
print_port_stat(string, "crc=", &ps->rx_crc_err, 0);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
ds_put_cstr(string, " tx ");
|
2011-01-18 11:50:56 -08:00
|
|
|
|
print_port_stat(string, "pkts=", &ps->tx_packets, 1);
|
|
|
|
|
print_port_stat(string, "bytes=", &ps->tx_bytes, 1);
|
|
|
|
|
print_port_stat(string, "drop=", &ps->tx_dropped, 1);
|
|
|
|
|
print_port_stat(string, "errs=", &ps->tx_errors, 1);
|
|
|
|
|
print_port_stat(string, "coll=", &ps->collisions, 0);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-08-09 17:49:34 +09:00
|
|
|
|
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)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-06-27 21:13:13 -07:00
|
|
|
|
struct ofp10_table_stats *ts;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
|
|
|
|
ofpraw_pull_assert(&b);
|
|
|
|
|
|
|
|
|
|
n = b.size / sizeof *ts;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_format(string, " %zu tables\n", n);
|
|
|
|
|
if (verbosity < 1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
for (;;) {
|
2012-08-09 17:49:34 +09:00
|
|
|
|
struct ofp12_table_stats ts12;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
|
|
|
|
|
ts = ofpbuf_try_pull(&b, sizeof *ts);
|
|
|
|
|
if (!ts) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-09 17:49:34 +09:00
|
|
|
|
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);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-09 17:49:34 +09:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-16 15:36:57 -07:00
|
|
|
|
static void
|
|
|
|
|
ofp_print_queue_name(struct ds *string, uint32_t queue_id)
|
|
|
|
|
{
|
|
|
|
|
if (queue_id == OFPQ_ALL) {
|
|
|
|
|
ds_put_cstr(string, "ALL");
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(string, "%"PRIu32, queue_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh)
|
2010-09-16 15:36:57 -07:00
|
|
|
|
{
|
2012-06-27 21:13:13 -07:00
|
|
|
|
const struct ofp10_queue_stats_request *qsr = ofpmsg_body(oh);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
|
2010-09-16 15:36:57 -07:00
|
|
|
|
ds_put_cstr(string, "port=");
|
2011-08-16 15:26:18 -07:00
|
|
|
|
ofputil_format_port(ntohs(qsr->port_no), string);
|
2010-09-16 15:36:57 -07:00
|
|
|
|
|
|
|
|
|
ds_put_cstr(string, " queue=");
|
|
|
|
|
ofp_print_queue_name(string, ntohl(qsr->queue_id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh,
|
|
|
|
|
int verbosity)
|
2010-09-16 15:36:57 -07:00
|
|
|
|
{
|
2012-06-27 21:13:13 -07:00
|
|
|
|
struct ofp10_queue_stats *qs;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
|
|
|
|
ofpraw_pull_assert(&b);
|
|
|
|
|
|
|
|
|
|
n = b.size / sizeof *qs;
|
2010-09-16 15:36:57 -07:00
|
|
|
|
ds_put_format(string, " %zu queues\n", n);
|
|
|
|
|
if (verbosity < 1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
for (;;) {
|
|
|
|
|
qs = ofpbuf_try_pull(&b, sizeof *qs);
|
|
|
|
|
if (!qs) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-16 15:36:57 -07:00
|
|
|
|
ds_put_cstr(string, " port ");
|
2011-08-16 15:26:18 -07:00
|
|
|
|
ofputil_format_port(ntohs(qs->port_no), string);
|
2010-09-16 15:36:57 -07:00
|
|
|
|
ds_put_cstr(string, " queue ");
|
|
|
|
|
ofp_print_queue_name(string, ntohl(qs->queue_id));
|
|
|
|
|
ds_put_cstr(string, ": ");
|
|
|
|
|
|
2011-01-18 11:50:56 -08:00
|
|
|
|
print_port_stat(string, "bytes=", &qs->tx_bytes, 1);
|
|
|
|
|
print_port_stat(string, "pkts=", &qs->tx_packets, 1);
|
|
|
|
|
print_port_stat(string, "errors=", &qs->tx_errors, 0);
|
2010-09-16 15:36:57 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-04 14:42:04 -07:00
|
|
|
|
static void
|
|
|
|
|
ofp_print_ofpst_port_desc_reply(struct ds *string,
|
|
|
|
|
const struct ofp_header *oh)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofpraw_pull_assert(&b);
|
2012-05-04 14:42:04 -07:00
|
|
|
|
ds_put_char(string, '\n');
|
|
|
|
|
ofp_print_phy_ports(string, oh->version, &b);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_request(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-19 23:23:17 -07:00
|
|
|
|
uint16_t flags = ofpmp_flags(oh);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
if (flags) {
|
|
|
|
|
ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_reply(struct ds *string, const struct ofp_header *oh)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-19 23:23:17 -07:00
|
|
|
|
uint16_t flags = ofpmp_flags(oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
if (flags) {
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ds_put_cstr(string, " flags=");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (flags & OFPSF_REPLY_MORE) {
|
|
|
|
|
ds_put_cstr(string, "[more]");
|
|
|
|
|
flags &= ~OFPSF_REPLY_MORE;
|
|
|
|
|
}
|
|
|
|
|
if (flags) {
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ds_put_format(string, "[***unknown flags 0x%04"PRIx16"***]",
|
|
|
|
|
flags);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_echo(struct ds *string, const struct ofp_header *oh, int verbosity)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-12-06 10:20:20 -08:00
|
|
|
|
size_t len = ntohs(oh->length);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ds_put_format(string, " %zu bytes of payload\n", len - sizeof *oh);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (verbosity > 1) {
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 14:36:18 -08:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-12 09:58:01 -07:00
|
|
|
|
static void
|
|
|
|
|
ofp_print_nxt_flow_mod_table_id(struct ds *string,
|
2012-01-12 11:35:50 -08:00
|
|
|
|
const struct nx_flow_mod_table_id *nfmti)
|
2011-05-12 09:58:01 -07:00
|
|
|
|
{
|
|
|
|
|
ds_put_format(string, " %s", nfmti->set ? "enable" : "disable");
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:22:46 -08:00
|
|
|
|
static void
|
|
|
|
|
ofp_print_nxt_set_flow_format(struct ds *string,
|
2012-01-12 11:35:50 -08:00
|
|
|
|
const struct nx_set_flow_format *nsff)
|
2010-12-07 13:22:46 -08:00
|
|
|
|
{
|
|
|
|
|
uint32_t format = ntohl(nsff->format);
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(string, " format=");
|
2012-02-10 13:30:23 -08:00
|
|
|
|
if (ofputil_nx_flow_format_is_valid(format)) {
|
|
|
|
|
ds_put_cstr(string, ofputil_nx_flow_format_to_string(format));
|
2010-12-07 13:22:46 -08:00
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(string, "%"PRIu32, format);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-09 15:48:26 -08:00
|
|
|
|
static void
|
|
|
|
|
ofp_print_nxt_set_packet_in_format(struct ds *string,
|
2012-01-12 11:35:50 -08:00
|
|
|
|
const struct nx_set_packet_in_format *nspf)
|
2011-12-09 15:48:26 -08:00
|
|
|
|
{
|
|
|
|
|
uint32_t format = ntohl(nspf->format);
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
ds_put_format(string, "%"PRIu32, format);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-09 14:06:35 -08:00
|
|
|
|
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",
|
2012-02-07 14:46:34 -08:00
|
|
|
|
ofputil_packet_in_reason_to_string(j));
|
2012-02-09 14:06:35 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-09 14:17:33 -08:00
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-12 14:18:05 -07:00
|
|
|
|
static void
|
|
|
|
|
ofp_print_nxt_flow_monitor_cancel(struct ds *string,
|
|
|
|
|
const struct ofp_header *oh)
|
|
|
|
|
{
|
|
|
|
|
ds_put_format(string, " id=%"PRIu32,
|
|
|
|
|
ofputil_decode_flow_monitor_cancel(oh));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
nx_flow_monitor_flags_to_name(uint32_t bit)
|
|
|
|
|
{
|
|
|
|
|
enum nx_flow_monitor_flags fmf = bit;
|
|
|
|
|
|
|
|
|
|
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 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, "\n id=%"PRIu32" flags=", request.id);
|
|
|
|
|
ofp_print_bit_names(string, request.flags,
|
|
|
|
|
nx_flow_monitor_flags_to_name, ',');
|
|
|
|
|
|
|
|
|
|
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, ' ');
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_format(&request.match, string, OFP_DEFAULT_PRIORITY);
|
2012-07-12 14:18:05 -07:00
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
|
|
|
|
ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
|
|
|
|
|
for (;;) {
|
|
|
|
|
struct ofputil_flow_update update;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match match;
|
2012-07-12 14:18:05 -07:00
|
|
|
|
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, ' ');
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_format(update.match, string, OFP_DEFAULT_PRIORITY);
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
|
|
|
|
if (update.ofpacts_len) {
|
|
|
|
|
if (string->string[string->length - 1] != ' ') {
|
|
|
|
|
ds_put_char(string, ' ');
|
|
|
|
|
}
|
|
|
|
|
ofpacts_format(update.ofpacts, update.ofpacts_len, string);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-12 13:32:47 -07:00
|
|
|
|
void
|
|
|
|
|
ofp_print_version(const struct ofp_header *oh,
|
|
|
|
|
struct ds *string)
|
2010-12-06 10:20:20 -08:00
|
|
|
|
{
|
2012-02-15 15:28:25 -08:00
|
|
|
|
switch (oh->version) {
|
|
|
|
|
case OFP10_VERSION:
|
|
|
|
|
break;
|
|
|
|
|
case OFP11_VERSION:
|
|
|
|
|
ds_put_cstr(string, " (OF1.1)");
|
2012-06-27 17:19:52 +09:00
|
|
|
|
break;
|
|
|
|
|
case OFP12_VERSION:
|
|
|
|
|
ds_put_cstr(string, " (OF1.2)");
|
2012-02-15 15:28:25 -08:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ds_put_format(string, " (OF 0x%02"PRIx8")", oh->version);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ds_put_format(string, " (xid=0x%"PRIx32"):", ntohl(oh->xid));
|
2012-07-12 13:32:47 -07:00
|
|
|
|
}
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-05-23 09:33:22 -07:00
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_header_to_string__(const struct ofp_header *oh, enum ofpraw raw,
|
|
|
|
|
struct ds *string)
|
2012-05-23 09:33:22 -07:00
|
|
|
|
{
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ds_put_cstr(string, ofpraw_get_name(raw));
|
2012-05-23 09:33:22 -07:00
|
|
|
|
ofp_print_version(oh, string);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-12 13:32:47 -07:00
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
|
|
|
|
|
struct ds *string, int verbosity)
|
2012-07-12 13:32:47 -07:00
|
|
|
|
{
|
|
|
|
|
const void *msg = oh;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_header_to_string__(oh, raw, string);
|
|
|
|
|
switch (ofptype_from_ofpraw(raw)) {
|
|
|
|
|
case OFPTYPE_HELLO:
|
2011-01-12 13:42:50 -08:00
|
|
|
|
ds_put_char(string, '\n');
|
|
|
|
|
ds_put_hex_dump(string, oh + 1, ntohs(oh->length) - sizeof *oh,
|
|
|
|
|
0, true);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_ERROR:
|
|
|
|
|
ofp_print_error_msg(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_ECHO_REQUEST:
|
|
|
|
|
case OFPTYPE_ECHO_REPLY:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_echo(string, oh, verbosity);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FEATURES_REQUEST:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FEATURES_REPLY:
|
|
|
|
|
ofp_print_switch_features(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_GET_CONFIG_REQUEST:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_GET_CONFIG_REPLY:
|
|
|
|
|
case OFPTYPE_SET_CONFIG:
|
|
|
|
|
ofp_print_switch_config(string, ofpmsg_body(oh));
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_PACKET_IN:
|
|
|
|
|
ofp_print_packet_in(string, oh, verbosity);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_REMOVED:
|
|
|
|
|
ofp_print_flow_removed(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_PORT_STATUS:
|
|
|
|
|
ofp_print_port_status(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_PACKET_OUT:
|
|
|
|
|
ofp_print_packet_out(string, oh, verbosity);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_MOD:
|
|
|
|
|
ofp_print_flow_mod(string, oh, verbosity);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_PORT_MOD:
|
|
|
|
|
ofp_print_port_mod(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_BARRIER_REQUEST:
|
|
|
|
|
case OFPTYPE_BARRIER_REPLY:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_DESC_STATS_REQUEST:
|
|
|
|
|
case OFPTYPE_PORT_DESC_STATS_REQUEST:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_request(string, oh);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_STATS_REQUEST:
|
|
|
|
|
case OFPTYPE_AGGREGATE_STATS_REQUEST:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_request(string, oh);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_flow_stats_request(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_TABLE_STATS_REQUEST:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_request(string, oh);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_PORT_STATS_REQUEST:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_request(string, oh);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_ofpst_port_request(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_QUEUE_STATS_REQUEST:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_request(string, oh);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_ofpst_queue_request(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_DESC_STATS_REPLY:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_reply(string, oh);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_ofpst_desc_reply(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_STATS_REPLY:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_reply(string, oh);
|
2011-03-10 15:02:05 -08:00
|
|
|
|
ofp_print_flow_stats_reply(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_QUEUE_STATS_REPLY:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_reply(string, oh);
|
|
|
|
|
ofp_print_ofpst_queue_reply(string, oh, verbosity);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_PORT_STATS_REPLY:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_reply(string, oh);
|
|
|
|
|
ofp_print_ofpst_port_reply(string, oh, verbosity);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_TABLE_STATS_REPLY:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_reply(string, oh);
|
|
|
|
|
ofp_print_ofpst_table_reply(string, oh, verbosity);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_AGGREGATE_STATS_REPLY:
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofp_print_stats_reply(string, oh);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_print_aggregate_stats_reply(string, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_PORT_DESC_STATS_REPLY:
|
2012-05-04 14:42:04 -07:00
|
|
|
|
ofp_print_stats_reply(string, oh);
|
|
|
|
|
ofp_print_ofpst_port_desc_reply(string, oh);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_ROLE_REQUEST:
|
|
|
|
|
case OFPTYPE_ROLE_REPLY:
|
|
|
|
|
ofp_print_nxt_role_message(string, ofpmsg_body(oh));
|
2010-12-07 13:22:46 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_MOD_TABLE_ID:
|
|
|
|
|
ofp_print_nxt_flow_mod_table_id(string, ofpmsg_body(oh));
|
2011-05-12 09:58:01 -07:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_SET_FLOW_FORMAT:
|
|
|
|
|
ofp_print_nxt_set_flow_format(string, ofpmsg_body(oh));
|
2010-12-07 13:22:46 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_SET_PACKET_IN_FORMAT:
|
|
|
|
|
ofp_print_nxt_set_packet_in_format(string, ofpmsg_body(oh));
|
2011-12-09 15:48:26 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_AGE:
|
2012-02-07 10:13:52 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_SET_CONTROLLER_ID:
|
|
|
|
|
ofp_print_nxt_set_controller_id(string, ofpmsg_body(oh));
|
2012-02-09 14:17:33 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_SET_ASYNC_CONFIG:
|
|
|
|
|
ofp_print_nxt_set_async_config(string, ofpmsg_body(oh));
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_MONITOR_CANCEL:
|
2012-07-12 14:18:05 -07:00
|
|
|
|
ofp_print_nxt_flow_monitor_cancel(string, msg);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_MONITOR_PAUSED:
|
|
|
|
|
case OFPTYPE_FLOW_MONITOR_RESUMED:
|
2012-07-12 14:18:05 -07:00
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
|
2012-07-12 14:18:05 -07:00
|
|
|
|
ofp_print_nxst_flow_monitor_request(string, msg);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
|
2012-07-12 14:18:05 -07:00
|
|
|
|
ofp_print_nxst_flow_monitor_reply(string, msg);
|
|
|
|
|
break;
|
2009-11-13 12:41:57 -08:00
|
|
|
|
}
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
{
|
|
|
|
|
struct ds string = DS_EMPTY_INITIALIZER;
|
|
|
|
|
const struct ofp_header *oh = oh_;
|
|
|
|
|
|
2010-12-13 16:27:20 -08:00
|
|
|
|
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);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
} else if (ntohs(oh->length) > len) {
|
2012-05-23 09:33:22 -07:00
|
|
|
|
enum ofperr error;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
enum ofpraw raw;
|
2012-05-23 09:33:22 -07:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
error = ofpraw_decode_partial(&raw, oh, len);
|
2012-05-23 09:33:22 -07:00
|
|
|
|
if (!error) {
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_header_to_string__(oh, raw, &string);
|
2012-05-23 09:33:22 -07:00
|
|
|
|
ds_put_char(&string, '\n');
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ds_put_format(&string,
|
2010-12-13 16:27:20 -08:00
|
|
|
|
"(***truncated to %zu bytes from %"PRIu16"***)\n",
|
2010-12-06 10:20:20 -08:00
|
|
|
|
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 {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
enum ofpraw raw;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
error = ofpraw_decode(&raw, oh);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
if (!error) {
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofp_to_string__(oh, raw, &string, verbosity);
|
2010-12-07 13:22:46 -08:00
|
|
|
|
if (verbosity >= 5) {
|
|
|
|
|
if (ds_last(&string) != '\n') {
|
|
|
|
|
ds_put_char(&string, '\n');
|
|
|
|
|
}
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ds_put_hex_dump(&string, oh, len, 0, true);
|
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
if (ds_last(&string) != '\n') {
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ds_put_char(&string, '\n');
|
|
|
|
|
}
|
|
|
|
|
return ds_steal_cstr(&string);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:22:46 -08:00
|
|
|
|
ofp_print_error(&string, error);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ds_put_hex_dump(&string, oh, len, 0, true);
|
|
|
|
|
return ds_steal_cstr(&string);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-08-30 00:24:53 -07:00
|
|
|
|
print_and_free(FILE *stream, char *string)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
fputs(string, stream);
|
|
|
|
|
free(string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pretty-print the OpenFlow packet of 'len' bytes at 'oh' to 'stream' at the
|
|
|
|
|
* given 'verbosity' level. 0 is a minimal amount of verbosity and higher
|
|
|
|
|
* numbers increase verbosity. */
|
|
|
|
|
void
|
|
|
|
|
ofp_print(FILE *stream, const void *oh, size_t len, int verbosity)
|
|
|
|
|
{
|
|
|
|
|
print_and_free(stream, ofp_to_string(oh, len, verbosity));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dumps the contents of the Ethernet frame in the 'len' bytes starting at
|
2011-12-21 12:59:28 -08:00
|
|
|
|
* 'data' to 'stream'. */
|
2009-07-08 13:19:16 -07:00
|
|
|
|
void
|
2011-12-22 17:47:15 -08:00
|
|
|
|
ofp_print_packet(FILE *stream, const void *data, size_t len)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2011-12-22 17:47:15 -08:00
|
|
|
|
print_and_free(stream, ofp_packet_to_string(data, len));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|