- OpenFlow:
* OpenFlow 1.1+ OFPT_QUEUE_GET_CONFIG_REQUEST now supports OFPP_ANY.
* OpenFlow 1.4+ OFPMP_QUEUE_DESC is now supported.
- * New property-based packet-in message format NXT_PACKET_IN2.
+ * New property-based packet-in message format NXT_PACKET_IN2 with support
+ for arbitrary user-provided data.
- ovs-ofctl:
* queue-get-config command now allows a queue ID to be specified.
- DPDK:
/* Other. */
NXPINT_REASON, /* uint8_t, one of OFPR_*. */
NXPINT_METADATA, /* NXM or OXM for metadata fields. */
+ NXPINT_USERDATA, /* From NXAST_CONTROLLER2 userdata. */
};
/* Configures the "role" of the sending controller. The default role is:
#include "nx-match.h"
#include "odp-netlink.h"
#include "ofp-parse.h"
+#include "ofp-prop.h"
#include "ofp-util.h"
#include "ofpbuf.h"
#include "unaligned.h"
/* NX1.0+(20): struct nx_action_controller. */
NXAST_RAW_CONTROLLER,
+ /* NX1.0+(37): struct nx_action_controller2, ... */
+ NXAST_RAW_CONTROLLER2,
/* NX1.0+(22): struct nx_action_write_metadata. */
NXAST_RAW_WRITE_METADATA,
};
OFP_ASSERT(sizeof(struct nx_action_controller) == 16);
+/* Properties for NXAST_CONTROLLER2. */
+enum nx_action_controller2_prop_type {
+ NXAC2PT_MAX_LEN, /* ovs_be16 max bytes to send (default all). */
+ NXAC2PT_CONTROLLER_ID, /* ovs_be16 dest controller ID (default 0). */
+ NXAC2PT_REASON, /* uint8_t reason (OFPR_*), default 0. */
+ NXAC2PT_USERDATA, /* Data to copy into NXPINT_USERDATA. */
+};
+
+/* Action structure for NXAST_CONTROLLER2.
+ *
+ * This replacement for NXAST_CONTROLLER makes it extensible via properties. */
+struct nx_action_controller2 {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length is 16 or more. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_CONTROLLER2. */
+ uint8_t zeros[6]; /* Must be zero. */
+ /* Followed by NXAC2PT_* properties. */
+};
+OFP_ASSERT(sizeof(struct nx_action_controller2) == 16);
+
static enum ofperr
decode_NXAST_RAW_CONTROLLER(const struct nx_action_controller *nac,
enum ofp_version ofp_version OVS_UNUSED,
struct ofpact_controller *oc;
oc = ofpact_put_CONTROLLER(out);
+ oc->ofpact.raw = NXAST_RAW_CONTROLLER;
oc->max_len = ntohs(nac->max_len);
oc->controller_id = ntohs(nac->controller_id);
oc->reason = nac->reason;
+ ofpact_finish(out, &oc->ofpact);
+
+ return 0;
+}
+
+static enum ofperr
+decode_NXAST_RAW_CONTROLLER2(const struct nx_action_controller2 *nac2,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *out)
+{
+ if (!is_all_zeros(nac2->zeros, sizeof nac2->zeros)) {
+ return OFPERR_NXBRC_MUST_BE_ZERO;
+ }
+
+ size_t start_ofs = out->size;
+ struct ofpact_controller *oc = ofpact_put_CONTROLLER(out);
+ oc->ofpact.raw = NXAST_RAW_CONTROLLER2;
+ oc->max_len = UINT16_MAX;
+ oc->reason = OFPR_ACTION;
+
+ struct ofpbuf properties;
+ ofpbuf_use_const(&properties, nac2, ntohs(nac2->len));
+ ofpbuf_pull(&properties, sizeof *nac2);
+
+ while (properties.size > 0) {
+ struct ofpbuf payload;
+ uint64_t type;
+
+ enum ofperr error = ofpprop_pull(&properties, &payload, &type);
+ if (error) {
+ return error;
+ }
+
+ switch (type) {
+ case NXAC2PT_MAX_LEN:
+ error = ofpprop_parse_u16(&payload, &oc->max_len);
+ break;
+
+ case NXAC2PT_CONTROLLER_ID:
+ error = ofpprop_parse_u16(&payload, &oc->controller_id);
+ break;
+
+ case NXAC2PT_REASON: {
+ uint8_t u8;
+ error = ofpprop_parse_u8(&payload, &u8);
+ oc->reason = u8;
+ break;
+ }
+
+ case NXAC2PT_USERDATA:
+ out->size = start_ofs + OFPACT_CONTROLLER_SIZE;
+ ofpbuf_put(out, payload.msg, ofpbuf_msgsize(&payload));
+ oc = ofpbuf_at_assert(out, start_ofs, sizeof *oc);
+ oc->userdata_len = ofpbuf_msgsize(&payload);
+ break;
+
+ default:
+ error = OFPPROP_UNKNOWN(false, "NXAST_RAW_CONTROLLER2", type);
+ break;
+ }
+ if (error) {
+ return error;
+ }
+ }
+
+ ofpact_finish(out, &oc->ofpact);
+
return 0;
}
enum ofp_version ofp_version OVS_UNUSED,
struct ofpbuf *out)
{
- struct nx_action_controller *nac;
+ if (controller->userdata_len
+ || controller->ofpact.raw == NXAST_RAW_CONTROLLER2) {
+ size_t start_ofs = out->size;
+ put_NXAST_CONTROLLER2(out);
+ if (controller->max_len != UINT16_MAX) {
+ ofpprop_put_u16(out, NXAC2PT_MAX_LEN, controller->max_len);
+ }
+ if (controller->controller_id != 0) {
+ ofpprop_put_u16(out, NXAC2PT_CONTROLLER_ID,
+ controller->controller_id);
+ }
+ if (controller->reason != OFPR_ACTION) {
+ ofpprop_put_u8(out, NXAC2PT_REASON, controller->reason);
+ }
+ if (controller->userdata_len != 0) {
+ ofpprop_put(out, NXAC2PT_USERDATA, controller->userdata,
+ controller->userdata_len);
+ }
+ pad_ofpat(out, start_ofs);
+ } else {
+ struct nx_action_controller *nac;
- nac = put_NXAST_CONTROLLER(out);
- nac->max_len = htons(controller->max_len);
- nac->controller_id = htons(controller->controller_id);
- nac->reason = controller->reason;
+ nac = put_NXAST_CONTROLLER(out);
+ nac->max_len = htons(controller->max_len);
+ nac->controller_id = htons(controller->controller_id);
+ nac->reason = controller->reason;
+ }
}
static char * OVS_WARN_UNUSED_RESULT
enum ofp_packet_in_reason reason = OFPR_ACTION;
uint16_t controller_id = 0;
uint16_t max_len = UINT16_MAX;
+ const char *userdata = NULL;
if (!arg[0]) {
/* Use defaults. */
if (error) {
return error;
}
+ } else if (!strcmp(name, "userdata")) {
+ userdata = value;
} else {
return xasprintf("unknown key \"%s\" parsing controller "
"action", name);
}
}
- if (reason == OFPR_ACTION && controller_id == 0) {
+ if (reason == OFPR_ACTION && controller_id == 0 && !userdata) {
struct ofpact_output *output;
output = ofpact_put_OUTPUT(ofpacts);
controller->max_len = max_len;
controller->reason = reason;
controller->controller_id = controller_id;
+
+ if (userdata) {
+ size_t start_ofs = ofpacts->size;
+ const char *end = ofpbuf_put_hex(ofpacts, userdata, NULL);
+ if (*end) {
+ return xstrdup("bad hex digit in `controller' "
+ "action `userdata'");
+ }
+ size_t userdata_len = ofpacts->size - start_ofs;
+ controller = ofpacts->header;
+ controller->userdata_len = userdata_len;
+ }
+ ofpact_finish(ofpacts, &controller->ofpact);
}
return NULL;
}
+static void
+format_hex_arg(struct ds *s, const uint8_t *data, size_t len)
+{
+ for (size_t i = 0; i < len; i++) {
+ if (i) {
+ ds_put_char(s, '.');
+ }
+ ds_put_format(s, "%02"PRIx8, data[i]);
+ }
+}
+
static void
format_CONTROLLER(const struct ofpact_controller *a, struct ds *s)
{
- if (a->reason == OFPR_ACTION && a->controller_id == 0) {
+ if (a->reason == OFPR_ACTION && !a->controller_id && !a->userdata_len) {
ds_put_format(s, "CONTROLLER:%"PRIu16, a->max_len);
} else {
enum ofp_packet_in_reason reason = a->reason;
if (a->controller_id != 0) {
ds_put_format(s, "id=%"PRIu16",", a->controller_id);
}
+ if (a->userdata_len) {
+ ds_put_cstr(s, "userdata=");
+ format_hex_arg(s, a->userdata, a->userdata_len);
+ ds_put_char(s, ',');
+ }
ds_chomp(s, ',');
ds_put_char(s, ')');
}
static void
format_NOTE(const struct ofpact_note *a, struct ds *s)
{
- size_t i;
-
ds_put_cstr(s, "note:");
- for (i = 0; i < a->length; i++) {
- if (i) {
- ds_put_char(s, '.');
- }
- ds_put_format(s, "%02"PRIx8, a->data[i]);
- }
+ format_hex_arg(s, a->data, a->length);
}
\f
/* Exit action. */
/*
- * Copyright (c) 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Output. */ \
OFPACT(OUTPUT, ofpact_output, ofpact, "output") \
OFPACT(GROUP, ofpact_group, ofpact, "group") \
- OFPACT(CONTROLLER, ofpact_controller, ofpact, "controller") \
+ OFPACT(CONTROLLER, ofpact_controller, userdata, "controller") \
OFPACT(ENQUEUE, ofpact_enqueue, ofpact, "enqueue") \
OFPACT(OUTPUT_REG, ofpact_output_reg, ofpact, "output_reg") \
OFPACT(BUNDLE, ofpact_bundle, slaves, "bundle") \
uint16_t max_len; /* Maximum length to send to controller. */
uint16_t controller_id; /* Controller ID to send packet-in. */
enum ofp_packet_in_reason reason; /* Reason to put in packet-in. */
+
+ /* Arbitrary data to include in the packet-in message (currently, only in
+ * NXT_PACKET_IN2). */
+ uint16_t userdata_len;
+ uint8_t userdata[];
};
/* OFPACT_ENQUEUE.
return ds_cstr(&ds);
}
+static void
+format_hex_arg(struct ds *s, const uint8_t *data, size_t len)
+{
+ for (size_t i = 0; i < len; i++) {
+ if (i) {
+ ds_put_char(s, '.');
+ }
+ ds_put_format(s, "%02"PRIx8, data[i]);
+ }
+}
+
static void
ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
int verbosity)
}
ds_put_char(string, '\n');
+ if (pin.userdata_len) {
+ ds_put_cstr(string, " userdata=");
+ format_hex_arg(string, pin.userdata, pin.userdata_len);
+ ds_put_char(string, '\n');
+ }
+
if (verbosity > 0) {
char *packet = ofp_packet_to_string(pin.packet, pin.packet_len);
ds_put_cstr(string, packet);
&pin->flow_metadata);
break;
+ case NXPINT_USERDATA:
+ pin->userdata = payload.msg;
+ pin->userdata_len = ofpbuf_msgsize(&payload);
+ break;
+
default:
error = OFPPROP_UNKNOWN(true, "NX_PACKET_IN2", type);
break;
oxm_put_raw(msg, &pin->flow_metadata, version);
ofpprop_end(msg, start);
+ if (pin->userdata_len) {
+ ofpprop_put(msg, NXPINT_USERDATA, pin->userdata, pin->userdata_len);
+ }
+
ofpmsg_update_length(msg);
return msg;
}
* that case, 'cookie' is UINT64_MAX. */
uint8_t table_id; /* OpenFlow table ID. */
ovs_be64 cookie; /* Flow's cookie. */
+
+ /* Arbitrary user-provided data. */
+ uint8_t *userdata;
+ size_t userdata_len;
};
struct ofpbuf *ofputil_encode_packet_in(const struct ofputil_packet_in *,
ofproto_async_msg_free(struct ofproto_async_msg *am)
{
free(am->pin.up.packet);
+ free(am->pin.up.userdata);
free(am);
}
static void
execute_controller_action(struct xlate_ctx *ctx, int len,
enum ofp_packet_in_reason reason,
- uint16_t controller_id)
+ uint16_t controller_id,
+ const uint8_t *userdata, size_t userdata_len)
{
struct dp_packet *packet;
.reason = reason,
.table_id = ctx->table_id,
.cookie = ctx->rule_cookie,
+ .userdata = (userdata_len
+ ? xmemdup(userdata, userdata_len)
+ : NULL),
+ .userdata_len = userdata_len,
},
.max_len = len,
},
for (i = 0; i < ids->n_controllers; i++) {
execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
- ids->cnt_ids[i]);
+ ids->cnt_ids[i], NULL, 0);
}
/* Stop processing for current table. */
set_mpls_lse_ttl(&flow->mpls_lse[0], ttl);
return false;
} else {
- execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
+ execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0,
+ NULL, 0);
}
}
(ctx->in_group ? OFPR_GROUP
: ctx->in_action_set ? OFPR_ACTION_SET
: OFPR_ACTION),
- 0);
+ 0, NULL, 0);
break;
case OFPP_NONE:
break;
controller = ofpact_get_CONTROLLER(a);
execute_controller_action(ctx, controller->max_len,
controller->reason,
- controller->controller_id);
+ controller->controller_id,
+ controller->userdata,
+ controller->userdata_len);
break;
case OFPACT_ENQUEUE:
controller->max_len = UINT16_MAX;
controller->controller_id = 0;
controller->reason = OFPR_IMPLICIT_MISS;
+ ofpact_finish(&ofpacts, &controller->ofpact);
error = add_internal_miss_flow(ofproto, id++, &ofpacts,
&ofproto->miss_rule);
# actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
ffff 0010 00002320 0014 04d2 162e 02 00
+# actions=controller(reason=invalid_ttl,max_len=1234,id=5678,userdata=01.02.03.04.05)
+ffff 0038 00002320 0025 000000000000 dnl
+0000 0008 04d2 0000 dnl
+0001 0008 162e 0000 dnl
+0002 0005 02 000000 dnl
+0003 0009 0102030405 00000000000000
+
# actions=dec_ttl(32768,12345,90,765,1024)
ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
AT_SETUP([NX_PACKET_IN2])
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "
-01 04 0088 00000000 00002320 0000001e
+01 04 0098 00000000 00002320 0000001e
0000 0034
82 82 82 82 82 82 80 81 81 81 81 81 81 00 00 50
08 00 45 00 00 28 00 00 00 00 00 06 32 05 53 53
0003 0005 07 000000
0004 0010 00000000 fedcba9876543210
0005 0005 01 000000
-0006 0010 80000408 5a5a5a5a5a5a5a5a"
+0006 0010 80000408 5a5a5a5a5a5a5a5a
+0007 0009 0102030405 00000000000000
+"
], [0], [dnl
NXT_PACKET_IN2 (xid=0x0): table_id=7 cookie=0xfedcba9876543210 total_len=64 metadata=0x5a5a5a5a5a5a5a5a (via action) data_len=48 buffer=0x00000114
+ userdata=01.02.03.04.05
ip,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=0.0.0.0,nw_dst=0.0.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
])
AT_CLEANUP
OVS_VSWITCHD_STOP
AT_CLEANUP
-dnl This test checks that metadata is encoded in NXT_PACKET_IN2.
-AT_SETUP([ofproto - packet-out with metadata (NXT_PACKET_IN2)])
+dnl This test checks that metadata and userdata are encoded in NXT_PACKET_IN2.
+AT_SETUP([ofproto - packet-out with metadata and userdata (NXT_PACKET_IN2)])
OVS_VSWITCHD_START
# Start a monitor listening for packet-ins.
AT_CAPTURE_FILE([monitor.log])
# Send a packet-out with a load action to set some metadata, and forward to controller
-AT_CHECK([ovs-ofctl packet-out br0 controller 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), load(0xaa->NXM_NX_PKT_MARK[[]]), controller' '0001020304050010203040501234'])
+AT_CHECK([ovs-ofctl packet-out br0 controller 'load(0xfafafafa5a5a5a5a->OXM_OF_METADATA[[0..63]]), load(0xaa->NXM_NX_PKT_MARK[[]]), controller(userdata=01.02.03.04.05)' '0001020304050010203040501234'])
# Stop the monitor and check its output.
ovs-appctl -t ovs-ofctl ofctl/barrier
AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
NXT_PACKET_IN2: total_len=14 pkt_mark=0xaa,metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=14 (unbuffered)
+ userdata=01.02.03.04.05
vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
OFPT_BARRIER_REPLY:
])
controller connection will only have a nonzero connection ID if its
controller uses the \fBNXT_SET_CONTROLLER_ID\fR Nicira extension to
OpenFlow.
+.IP "\fBuserdata=\fIhh\fR...\fR"
+Supplies the bytes represented as hex digits \fIhh\fR as additional
+data to the controller in the packet-in message. Pairs of hex digits
+may be separated by periods for readability.
+.
.RE
.IP
-Any \fIreason\fR other than \fBaction\fR and any nonzero
-\fIcontroller-id\fR uses a Nicira vendor extension that, as of this
-writing, is only known to be implemented by Open vSwitch (version 1.6
-or later).
+If any \fIreason\fR other than \fBaction\fR or any nonzero
+\fIcontroller-id\fR is supplied, Open vSwitch extension
+\fBNXAST_CONTROLLER\fR, supported by Open vSwitch 1.6 and later, is
+used. If \fBuserdata\fR is supplied, then \fBNXAST_CONTROLLER2\fR,
+supported by Open vSwitch 2.6 and later, is used.
.
.IP \fBcontroller\fR
.IQ \fBcontroller\fR[\fB:\fInbytes\fR]
.
.IP "\fBnxt_packet_in2\fR"
This uses the \fBNXT_PACKET_IN2\fR message, which is extensible and
-should avoid the need to define new formats later. Open vSwitch 2.6
-and later support this format.
+should avoid the need to define new formats later. In particular,
+this format supports passing arbitrary user-provided data to a
+controller using the \fBuserdata\fB option on the \fBcontroller\fR
+action. Open vSwitch 2.6 and later support this format.
.
.RE
.IP