#include "flow.h"
#include "ofp-print.h"
#include "ofpbuf.h"
+#include "openflow/nicira-ext.h"
#include "openflow/openflow.h"
+#include "packets.h"
#include "poll-loop.h"
#include "random.h"
#include "util.h"
return fs;
}
+/* Alignment of ofp_actions. */
+#define ACTION_ALIGNMENT 8
+
+static int
+check_action_exact_len(const union ofp_action *a, unsigned int len,
+ unsigned int required_len)
+{
+ if (len != required_len) {
+ VLOG_DBG_RL(&bad_ofmsg_rl,
+ "action %u has invalid length %"PRIu16" (must be %u)\n",
+ a->type, ntohs(a->header.len), required_len);
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+ }
+ return 0;
+}
+
+static int
+check_action_port(int port)
+{
+ switch (port) {
+ case OFPP_IN_PORT:
+ case OFPP_FLOOD:
+ case OFPP_ALL:
+ case OFPP_CONTROLLER:
+ case OFPP_LOCAL:
+ return 0;
+
+ case OFPP_TABLE:
+ case OFPP_NORMAL:
+ VLOG_WARN_RL(&bad_ofmsg_rl, "port %x not yet implemented", port);
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
+
+ default:
+ if (port >= 0 && port < OFPP_MAX) {
+ return 0;
+ }
+ VLOG_WARN_RL(&bad_ofmsg_rl, "unknown output port %x", port);
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT);
+ }
+}
+
+static int
+check_nicira_action(const union ofp_action *a, unsigned int len)
+{
+ const struct nx_action_header *nah;
+ int error;
+
+ if (len < 16) {
+ VLOG_DBG_RL(&bad_ofmsg_rl,
+ "Nicira vendor action only %u bytes", len);
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+ }
+ nah = (const struct nx_action_header *) a;
+
+ switch (ntohs(nah->subtype)) {
+ case NXAST_SNAT:
+ error = check_action_exact_len(a, len, 16);
+ if (error) {
+ return error;
+ }
+ return check_action_port(ntohs(((struct nx_action_snat *) nah)->port));
+ default:
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
+ }
+}
+
+static int
+check_action(const union ofp_action *a, unsigned int len)
+{
+ int error;
+
+ switch (a->type) {
+ case OFPAT_OUTPUT:
+ error = check_action_port(ntohs(a->output.port));
+ if (error) {
+ return error;
+ }
+ return check_action_exact_len(a, len, 8);
+
+ case OFPAT_SET_VLAN_VID:
+ case OFPAT_SET_VLAN_PCP:
+ case OFPAT_STRIP_VLAN:
+ case OFPAT_SET_NW_SRC:
+ case OFPAT_SET_NW_DST:
+ case OFPAT_SET_TP_SRC:
+ case OFPAT_SET_TP_DST:
+ return check_action_exact_len(a, len, 8);
+
+ case OFPAT_SET_DL_SRC:
+ case OFPAT_SET_DL_DST:
+ return check_action_exact_len(a, len, 16);
+
+ case OFPAT_VENDOR:
+ if (a->vendor.vendor == htonl(NX_VENDOR_ID)) {
+ return check_nicira_action(a, len);
+ } else {
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR);
+ }
+ break;
+
+ default:
+ VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16, a->type);
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE);
+ }
+
+ if (!len) {
+ VLOG_DBG_RL(&bad_ofmsg_rl, "action has invalid length 0");
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+ }
+ if (len % ACTION_ALIGNMENT) {
+ VLOG_DBG_RL(&bad_ofmsg_rl, "action length %u is not a multiple of %d",
+ len, ACTION_ALIGNMENT);
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+ }
+ return 0;
+}
+
+int
+validate_actions(const union ofp_action *actions, size_t n_actions)
+{
+ const union ofp_action *a;
+
+ for (a = actions; a < &actions[n_actions]; ) {
+ unsigned int len = ntohs(a->header.len);
+ unsigned int n_slots = len / ACTION_ALIGNMENT;
+ unsigned int slots_left = &actions[n_actions] - a;
+ int error;
+
+ if (n_slots > slots_left) {
+ VLOG_DBG_RL(&bad_ofmsg_rl,
+ "action requires %u slots but only %td remain",
+ n_slots, slots_left);
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
+ }
+ error = check_action(a, len);
+ if (error) {
+ return error;
+ }
+ a += n_slots;
+ }
+ return 0;
+}
+
+/* The set of actions must either come from a trusted source or have been
+ * previously validated with validate_actions(). */
+const union ofp_action *
+actions_first(struct actions_iterator *iter,
+ const union ofp_action *oa, size_t n_actions)
+{
+ iter->pos = oa;
+ iter->end = oa + n_actions;
+ return actions_next(iter);
+}
+
+const union ofp_action *
+actions_next(struct actions_iterator *iter)
+{
+ if (iter->pos < iter->end) {
+ const union ofp_action *a = iter->pos;
+ unsigned int len = ntohs(a->header.len);
+ iter->pos += len / ACTION_ALIGNMENT;
+ return a;
+ } else {
+ return NULL;
+ }
+}
+
void
vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
uint32_t ip, const char *name, bool reconnectable)
flow_extract(buffer, ntohs(opo->in_port), &key.flow);
- v_code = validate_actions(dp, &key, opo->actions, actions_len);
+ v_code = dp_validate_actions(dp, &key, opo->actions, actions_len);
if (v_code != ACT_VALIDATION_OK) {
dp_send_error_msg(dp, sender, OFPET_BAD_ACTION, v_code,
msg, ntohs(opo->header.length));
flow_extract_match(&flow->key, &ofm->match);
- v_code = validate_actions(dp, &flow->key, ofm->actions, actions_len);
+ v_code = dp_validate_actions(dp, &flow->key, ofm->actions, actions_len);
if (v_code != ACT_VALIDATION_OK) {
dp_send_error_msg(dp, sender, OFPET_BAD_ACTION, v_code,
ofm, ntohs(ofm->header.length));
actions_len = ntohs(ofm->header.length) - sizeof *ofm;
- v_code = validate_actions(dp, &key, ofm->actions, actions_len);
+ v_code = dp_validate_actions(dp, &key, ofm->actions, actions_len);
if (v_code != ACT_VALIDATION_OK) {
dp_send_error_msg(dp, sender, OFPET_BAD_ACTION, v_code,
ofm, ntohs(ofm->header.length));