/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2008-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.
#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.
+ *
+ * For more information on the effect of NXAC2PT_PAUSE, see the large comment
+ * on NXT_PACKET_IN2 in nicira-ext.h */
+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. */
+ NXAC2PT_PAUSE, /* Flag to pause pipeline to resume later. */
+};
+
+/* 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;
+
+ case NXAC2PT_PAUSE:
+ oc->pause = true;
+ 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->pause
+ || 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);
+ }
+ if (controller->pause) {
+ ofpprop_put_flag(out, NXAC2PT_PAUSE);
+ }
+ 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;
+ bool pause = false;
if (!arg[0]) {
/* Use defaults. */
if (error) {
return error;
}
+ } else if (!strcmp(name, "userdata")) {
+ userdata = value;
+ } else if (!strcmp(name, "pause")) {
+ pause = true;
} 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 && !pause) {
struct ofpact_output *output;
output = ofpact_put_OUTPUT(ofpacts);
controller->max_len = max_len;
controller->reason = reason;
controller->controller_id = controller_id;
+ controller->pause = pause;
+
+ 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
+ && !a->pause) {
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, ',');
+ }
+ if (a->pause) {
+ ds_put_cstr(s, "pause,");
+ }
ds_chomp(s, ',');
ds_put_char(s, ')');
}
for (i = 0; i < bundle->n_slaves; i++) {
uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]);
ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port);
+ bundle = ofpacts->header;
}
- bundle = ofpacts->header;
ofpact_finish(ofpacts, &bundle->ofpact);
if (!error) {
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. */
void
ofpact_finish(struct ofpbuf *ofpacts, struct ofpact *ofpact)
{
+ ptrdiff_t len;
+
ovs_assert(ofpact == ofpacts->header);
- ofpact->len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact;
+ len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact;
+ ovs_assert(len <= UINT16_MAX);
+ ofpact->len = len;
ofpbuf_padto(ofpacts, OFPACT_ALIGN(ofpacts->size));
}
\f