/*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 2012, 2013 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 <errno.h>
#include <stdlib.h>
-#include "autopath.h"
#include "bundle.h"
#include "byte-order.h"
#include "dynamic-string.h"
NO_RETURN;
static uint8_t
-str_to_table_id(const char *str)
+str_to_u8(const char *str, const char *name)
{
- int table_id;
+ int value;
- if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) {
- ovs_fatal(0, "invalid table \"%s\"", str);
+ if (!str_to_int(str, 10, &value) || value < 0 || value > 255) {
+ ovs_fatal(0, "invalid %s \"%s\"", name, str);
}
- return table_id;
+ return value;
}
static uint16_t
}
enqueue = ofpact_put_ENQUEUE(ofpacts);
- enqueue->port = str_to_u32(port);
+ enqueue->port = u16_to_ofp(str_to_u32(port));
enqueue->queue = str_to_u32(queue);
}
struct ofpact_output *output;
output = ofpact_put_OUTPUT(ofpacts);
- output->port = str_to_u32(arg);
+ output->port = u16_to_ofp(str_to_u32(arg));
output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
}
}
in_port_s = strsep(&arg, ",");
if (in_port_s && in_port_s[0]) {
if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) {
- resubmit->in_port = str_to_u32(in_port_s);
+ ovs_fatal(0, "%s: resubmit to unknown port", in_port_s);
}
} else {
resubmit->in_port = OFPP_IN_PORT;
}
static void
-parse_dec_ttl(struct ofpbuf *b, char *arg)
+parse_noargs_dec_ttl(struct ofpbuf *b)
{
struct ofpact_cnt_ids *ids;
+ uint16_t id = 0;
ids = ofpact_put_DEC_TTL(b);
+ ofpbuf_put(b, &id, sizeof id);
+ ids = b->l2;
+ ids->n_controllers++;
+ ofpact_update_len(b, &ids->ofpact);
+}
+static void
+parse_dec_ttl(struct ofpbuf *b, char *arg)
+{
if (*arg == '\0') {
- uint16_t id = 0;
-
- ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
- ofpbuf_put(b, &id, sizeof id);
- ids = b->l2;
- ids->n_controllers++;
+ parse_noargs_dec_ttl(b);
} else {
+ struct ofpact_cnt_ids *ids;
char *cntr;
+ ids = ofpact_put_DEC_TTL(b);
ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS;
for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL;
cntr = strtok_r(NULL, ", ", &arg)) {
ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller "
"id.");
}
+ ofpact_update_len(b, &ids->ofpact);
+ }
+}
+
+static void
+parse_set_mpls_ttl(struct ofpbuf *b, const char *arg)
+{
+ struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b);
+ if (*arg == '\0') {
+ ovs_fatal(0, "parse_set_mpls_ttl: expected ttl.");
}
- ofpact_update_len(b, &ids->ofpact);
+
+ mpls_ttl->ttl = atoi(arg);
}
static void
-parse_named_action(enum ofputil_action_code code, const struct flow *flow,
+set_field_parse(const char *arg, struct ofpbuf *ofpacts)
+{
+ char *orig = xstrdup(arg);
+ struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts);
+ char *value;
+ char *delim;
+ char *key;
+ const struct mf_field *mf;
+ const char *error;
+ union mf_value mf_value;
+
+ value = orig;
+ delim = strstr(orig, "->");
+ if (!delim) {
+ ovs_fatal(0, "%s: missing `->'", orig);
+ }
+ if (strlen(delim) <= strlen("->")) {
+ ovs_fatal(0, "%s: missing field name following `->'", orig);
+ }
+
+ key = delim + strlen("->");
+ mf = mf_from_name(key);
+ if (!mf) {
+ ovs_fatal(0, "%s is not valid oxm field name", key);
+ }
+ if (!mf->writable) {
+ ovs_fatal(0, "%s is not allowed to set", key);
+ }
+
+ delim[0] = '\0';
+ error = mf_parse_value(mf, value, &mf_value);
+ if (error) {
+ ovs_fatal(0, "%s", error);
+ }
+ if (!mf_is_value_valid(mf, &mf_value)) {
+ ovs_fatal(0, "%s is not valid valid for field %s", value, key);
+ }
+ ofpact_set_field_init(load, mf, &mf_value);
+ free(orig);
+}
+
+static void
+parse_metadata(struct ofpbuf *b, char *arg)
+{
+ struct ofpact_metadata *om;
+ char *mask = strchr(arg, '/');
+
+ om = ofpact_put_WRITE_METADATA(b);
+
+ if (mask) {
+ *mask = '\0';
+ om->mask = htonll(str_to_u64(mask + 1));
+ } else {
+ om->mask = htonll(UINT64_MAX);
+ }
+
+ om->metadata = htonll(str_to_u64(arg));
+}
+
+static void
+parse_sample(struct ofpbuf *b, char *arg)
+{
+ struct ofpact_sample *os = ofpact_put_SAMPLE(b);
+ char *key, *value;
+
+ while (ofputil_parse_key_value(&arg, &key, &value)) {
+ if (!strcmp(key, "probability")) {
+ os->probability = str_to_u16(value, "probability");
+ if (os->probability == 0) {
+ ovs_fatal(0, "invalid probability value \"%s\"", value);
+ }
+ } else if (!strcmp(key, "collector_set_id")) {
+ os->collector_set_id = str_to_u32(value);
+ } else if (!strcmp(key, "obs_domain_id")) {
+ os->obs_domain_id = str_to_u32(value);
+ } else if (!strcmp(key, "obs_point_id")) {
+ os->obs_point_id = str_to_u32(value);
+ } else {
+ ovs_fatal(0, "invalid key \"%s\" in \"sample\" argument",
+ key);
+ }
+ }
+ if (os->probability == 0) {
+ ovs_fatal(0, "non-zero \"probability\" must be specified on sample");
+ }
+}
+
+static void
+parse_named_action(enum ofputil_action_code code,
char *arg, struct ofpbuf *ofpacts)
{
struct ofpact_tunnel *tunnel;
uint16_t vid;
+ uint16_t ethertype;
ovs_be32 ip;
uint8_t pcp;
uint8_t tos;
ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
break;
+ case OFPUTIL_OFPAT12_SET_FIELD:
+ set_field_parse(arg, ofpacts);
+ break;
+
case OFPUTIL_OFPAT10_STRIP_VLAN:
+ case OFPUTIL_OFPAT11_POP_VLAN:
ofpact_put_STRIP_VLAN(ofpacts);
break;
+ case OFPUTIL_OFPAT11_PUSH_VLAN:
+ ethertype = str_to_u16(arg, "ethertype");
+ if (ethertype != ETH_TYPE_VLAN_8021Q) {
+ /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */
+ ovs_fatal(0, "%s: not a valid VLAN ethertype", arg);
+ }
+ ofpact_put_PUSH_VLAN(ofpacts);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_QUEUE:
+ ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
+ break;
+
+
case OFPUTIL_OFPAT10_SET_DL_SRC:
case OFPUTIL_OFPAT11_SET_DL_SRC:
str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos;
break;
+ case OFPUTIL_OFPAT11_DEC_NW_TTL:
+ NOT_REACHED();
+
case OFPUTIL_OFPAT10_SET_TP_SRC:
case OFPUTIL_OFPAT11_SET_TP_SRC:
ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg);
tunnel->tun_id = str_to_u64(arg);
break;
+ case OFPUTIL_NXAST_WRITE_METADATA:
+ parse_metadata(ofpacts, arg);
+ break;
+
case OFPUTIL_NXAST_SET_QUEUE:
ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
break;
multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
break;
- case OFPUTIL_NXAST_AUTOPATH:
- autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg);
- break;
-
case OFPUTIL_NXAST_BUNDLE:
bundle_parse(arg, ofpacts);
break;
NOT_REACHED();
case OFPUTIL_NXAST_LEARN:
- learn_parse(arg, flow, ofpacts);
+ learn_parse(arg, ofpacts);
break;
case OFPUTIL_NXAST_EXIT:
parse_dec_ttl(ofpacts, arg);
break;
+ case OFPUTIL_NXAST_SET_MPLS_TTL:
+ case OFPUTIL_OFPAT11_SET_MPLS_TTL:
+ parse_set_mpls_ttl(ofpacts, arg);
+ break;
+
+ case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
+ case OFPUTIL_NXAST_DEC_MPLS_TTL:
+ ofpact_put_DEC_MPLS_TTL(ofpacts);
+ break;
+
case OFPUTIL_NXAST_FIN_TIMEOUT:
parse_fin_timeout(ofpacts, arg);
break;
case OFPUTIL_NXAST_CONTROLLER:
parse_controller(ofpacts, arg);
break;
+
+ case OFPUTIL_OFPAT11_PUSH_MPLS:
+ case OFPUTIL_NXAST_PUSH_MPLS:
+ ofpact_put_PUSH_MPLS(ofpacts)->ethertype =
+ htons(str_to_u16(arg, "push_mpls"));
+ break;
+
+ case OFPUTIL_OFPAT11_POP_MPLS:
+ case OFPUTIL_NXAST_POP_MPLS:
+ ofpact_put_POP_MPLS(ofpacts)->ethertype =
+ htons(str_to_u16(arg, "pop_mpls"));
+ break;
+
+ case OFPUTIL_NXAST_STACK_PUSH:
+ nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
+ break;
+ case OFPUTIL_NXAST_STACK_POP:
+ nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
+ break;
+
+ case OFPUTIL_NXAST_SAMPLE:
+ parse_sample(ofpacts, arg);
+ break;
+ }
+}
+
+static bool
+str_to_ofpact__(char *pos, char *act, char *arg,
+ struct ofpbuf *ofpacts, int n_actions)
+{
+ int code = ofputil_action_code_from_name(act);
+ if (code >= 0) {
+ parse_named_action(code, arg, ofpacts);
+ } else if (!strcasecmp(act, "drop")) {
+ if (n_actions) {
+ ovs_fatal(0, "Drop actions must not be preceded by other "
+ "actions");
+ } else if (ofputil_parse_key_value(&pos, &act, &arg)) {
+ ovs_fatal(0, "Drop actions must not be followed by other "
+ "actions");
+ }
+ return false;
+ } else {
+ ofp_port_t port;
+ if (ofputil_port_from_string(act, &port)) {
+ ofpact_put_OUTPUT(ofpacts)->port = port;
+ } else {
+ ovs_fatal(0, "Unknown action: %s", act);
+ }
}
+
+ return true;
}
static void
-str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
+str_to_ofpacts(char *str, struct ofpbuf *ofpacts)
{
char *pos, *act, *arg;
+ enum ofperr error;
int n_actions;
pos = str;
n_actions = 0;
while (ofputil_parse_key_value(&pos, &act, &arg)) {
- uint16_t port;
- int code;
-
- code = ofputil_action_code_from_name(act);
- if (code >= 0) {
- parse_named_action(code, flow, arg, ofpacts);
- } else if (!strcasecmp(act, "drop")) {
- if (n_actions) {
- ovs_fatal(0, "Drop actions must not be preceded by other "
- "actions");
- } else if (ofputil_parse_key_value(&pos, &act, &arg)) {
- ovs_fatal(0, "Drop actions must not be followed by other "
- "actions");
- }
+ if (!str_to_ofpact__(pos, act, arg, ofpacts, n_actions)) {
break;
- } else if (ofputil_port_from_string(act, &port)) {
- ofpact_put_OUTPUT(ofpacts)->port = port;
+ }
+ n_actions++;
+ }
+
+ error = ofpacts_verify(ofpacts->data, ofpacts->size);
+ if (error) {
+ ovs_fatal(0, "Incorrect action ordering");
+ }
+
+ ofpact_pad(ofpacts);
+}
+
+static void
+parse_named_instruction(enum ovs_instruction_type type,
+ char *arg, struct ofpbuf *ofpacts)
+{
+ enum ofperr error;
+
+ switch (type) {
+ case OVSINST_OFPIT11_APPLY_ACTIONS:
+ NOT_REACHED(); /* This case is handled by str_to_inst_ofpacts() */
+ break;
+
+ case OVSINST_OFPIT11_WRITE_ACTIONS:
+ /* XXX */
+ ovs_fatal(0, "instruction write-actions is not supported yet");
+ break;
+
+ case OVSINST_OFPIT11_CLEAR_ACTIONS:
+ ofpact_put_CLEAR_ACTIONS(ofpacts);
+ break;
+
+ case OVSINST_OFPIT13_METER:
+ ofpact_put_METER(ofpacts)->meter_id = str_to_u32(arg);
+ break;
+
+ case OVSINST_OFPIT11_WRITE_METADATA:
+ parse_metadata(ofpacts, arg);
+ break;
+
+ case OVSINST_OFPIT11_GOTO_TABLE: {
+ struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts);
+ char *table_s = strsep(&arg, ",");
+ if (!table_s || !table_s[0]) {
+ ovs_fatal(0, "instruction goto-table needs table id");
+ }
+ ogt->table_id = str_to_u8(table_s, "table");
+ break;
+ }
+ }
+
+ /* If write_metadata is specified as an action AND an instruction, ofpacts
+ could be invalid. */
+ error = ofpacts_verify(ofpacts->data, ofpacts->size);
+ if (error) {
+ ovs_fatal(0, "Incorrect instruction ordering");
+ }
+}
+
+static void
+str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
+{
+ char *pos, *inst, *arg;
+ int type;
+ const char *prev_inst = NULL;
+ int prev_type = -1;
+ int n_actions = 0;
+
+ pos = str;
+ while (ofputil_parse_key_value(&pos, &inst, &arg)) {
+ type = ofpact_instruction_type_from_name(inst);
+ if (type < 0) {
+ if (!str_to_ofpact__(pos, inst, arg, ofpacts, n_actions)) {
+ break;
+ }
+
+ type = OVSINST_OFPIT11_APPLY_ACTIONS;
+ if (prev_type == type) {
+ n_actions++;
+ continue;
+ }
+ } else if (type == OVSINST_OFPIT11_APPLY_ACTIONS) {
+ ovs_fatal(0, "%s isn't supported. Just write actions then "
+ "it is interpreted as apply_actions", inst);
} else {
- ovs_fatal(0, "Unknown action: %s", act);
+ parse_named_instruction(type, arg, ofpacts);
+ }
+
+ if (type == prev_type) {
+ ovs_fatal(0, "instruction can be specified at most once: %s",
+ inst);
+ }
+ if (type <= prev_type) {
+ ovs_fatal(0, "Instruction %s must be specified before %s",
+ inst, prev_inst);
}
+
+ prev_inst = inst;
+ prev_type = type;
n_actions++;
}
ofpact_pad(ofpacts);
{ "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 },
{ "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
{ "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
+ { "rarp", ETH_TYPE_RARP, 0},
+ { "mpls", ETH_TYPE_MPLS, 0 },
+ { "mplsm", ETH_TYPE_MPLS_MCAST, 0 },
};
const struct protocol *p;
}
static void
-parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule)
+parse_field(const struct mf_field *mf, const char *s, struct match *match)
{
union mf_value value, mask;
char *error;
ovs_fatal(0, "%s", error);
}
- mf_set(mf, &value, &mask, rule);
+ mf_set(mf, &value, &mask, match);
}
/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
NOT_REACHED();
}
- cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
+ match_init_catchall(&fm->match);
+ fm->priority = OFP_DEFAULT_PRIORITY;
fm->cookie = htonll(0);
fm->cookie_mask = htonll(0);
if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) {
fm->idle_timeout = OFP_FLOW_PERMANENT;
fm->hard_timeout = OFP_FLOW_PERMANENT;
fm->buffer_id = UINT32_MAX;
- fm->out_port = OFPP_NONE;
+ fm->out_port = OFPP_ANY;
fm->flags = 0;
if (fields & F_ACTIONS) {
act_str = strstr(string, "action");
const struct protocol *p;
if (parse_protocol(name, &p)) {
- cls_rule_set_dl_type(&fm->cr, htons(p->dl_type));
+ match_set_dl_type(&fm->match, htons(p->dl_type));
if (p->nw_proto) {
- cls_rule_set_nw_proto(&fm->cr, p->nw_proto);
+ match_set_nw_proto(&fm->match, p->nw_proto);
}
} else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) {
fm->flags |= OFPFF_SEND_FLOW_REM;
} else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) {
fm->flags |= OFPFF_CHECK_OVERLAP;
+ } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) {
+ fm->flags |= OFPFF12_RESET_COUNTS;
+ } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) {
+ fm->flags |= OFPFF13_NO_PKT_COUNTS;
+ } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) {
+ fm->flags |= OFPFF13_NO_BYT_COUNTS;
} else {
char *value;
}
if (!strcmp(name, "table")) {
- fm->table_id = str_to_table_id(value);
+ fm->table_id = str_to_u8(value, name);
} else if (!strcmp(name, "out_port")) {
- fm->out_port = atoi(value);
+ if (!ofputil_port_from_string(value, &fm->out_port)) {
+ ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port",
+ name);
+ }
} else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
- fm->cr.priority = str_to_u16(value, name);
+ fm->priority = str_to_u16(value, name);
} else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) {
fm->idle_timeout = str_to_u16(value, name);
} else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
fm->new_cookie = htonll(str_to_u64(value));
}
} else if (mf_from_name(name)) {
- parse_field(mf_from_name(name), value, &fm->cr);
+ parse_field(mf_from_name(name), value, &fm->match);
} else if (!strcmp(name, "duration")
|| !strcmp(name, "n_packets")
- || !strcmp(name, "n_bytes")) {
+ || !strcmp(name, "n_bytes")
+ || !strcmp(name, "idle_age")
+ || !strcmp(name, "hard_age")) {
/* Ignore these, so that users can feed the output of
* "ovs-ofctl dump-flows" back into commands that parse
* flows. */
}
if (fields & F_ACTIONS) {
struct ofpbuf ofpacts;
+ enum ofperr err;
ofpbuf_init(&ofpacts, 32);
- str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts);
+ str_to_inst_ofpacts(act_str, &ofpacts);
fm->ofpacts_len = ofpacts.size;
fm->ofpacts = ofpbuf_steal_data(&ofpacts);
+
+ err = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
+ OFPP_MAX);
+ if (err) {
+ exit(EXIT_FAILURE);
+ }
+
} else {
fm->ofpacts_len = 0;
fm->ofpacts = NULL;
free(string);
}
+/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
+ * page) into 'mm' for sending the specified meter_mod 'command' to a switch.
+ */
+void
+parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
+ int command, bool verbose)
+{
+ enum {
+ F_METER = 1 << 0,
+ F_FLAGS = 1 << 1,
+ F_BANDS = 1 << 2,
+ } fields;
+ char *string = xstrdup(str_);
+ char *save_ptr = NULL;
+ char *band_str = NULL;
+ char *name;
+
+ switch (command) {
+ case -1:
+ fields = F_METER;
+ break;
+
+ case OFPMC13_ADD:
+ fields = F_METER | F_FLAGS | F_BANDS;
+ break;
+
+ case OFPMC13_DELETE:
+ fields = F_METER;
+ break;
+
+ case OFPMC13_MODIFY:
+ fields = F_METER | F_FLAGS | F_BANDS;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ mm->command = command;
+ mm->meter.meter_id = 0;
+ mm->meter.flags = 0;
+ if (fields & F_BANDS) {
+ band_str = strstr(string, "band");
+ if (!band_str) {
+ ofp_fatal(str_, verbose, "must specify bands");
+ }
+ *band_str = '\0';
+
+ band_str = strchr(band_str + 1, '=');
+ if (!band_str) {
+ ofp_fatal(str_, verbose, "must specify bands");
+ }
+
+ band_str++;
+ }
+ for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+ name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+ if (fields & F_FLAGS && !strcmp(name, "kbps")) {
+ mm->meter.flags |= OFPMF13_KBPS;
+ } else if (fields & F_FLAGS && !strcmp(name, "pktps")) {
+ mm->meter.flags |= OFPMF13_PKTPS;
+ } else if (fields & F_FLAGS && !strcmp(name, "burst")) {
+ mm->meter.flags |= OFPMF13_BURST;
+ } else if (fields & F_FLAGS && !strcmp(name, "stats")) {
+ mm->meter.flags |= OFPMF13_STATS;
+ } else {
+ char *value;
+
+ value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+ if (!value) {
+ ofp_fatal(str_, verbose, "field %s missing value", name);
+ }
+
+ if (!strcmp(name, "meter")) {
+ if (!strcmp(value, "all")) {
+ mm->meter.meter_id = OFPM13_ALL;
+ } else if (!strcmp(value, "controller")) {
+ mm->meter.meter_id = OFPM13_CONTROLLER;
+ } else if (!strcmp(value, "slowpath")) {
+ mm->meter.meter_id = OFPM13_SLOWPATH;
+ } else {
+ mm->meter.meter_id = str_to_u32(value);
+ if (mm->meter.meter_id > OFPM13_MAX) {
+ ofp_fatal(str_, verbose, "invalid value for %s", name);
+ }
+ }
+ } else {
+ ofp_fatal(str_, verbose, "unknown keyword %s", name);
+ }
+ }
+ }
+ if (fields & F_METER && !mm->meter.meter_id) {
+ ofp_fatal(str_, verbose, "must specify 'meter'");
+ }
+ if (fields & F_FLAGS && !mm->meter.flags) {
+ ofp_fatal(str_, verbose,
+ "meter must specify either 'kbps' or 'pktps'");
+ }
+
+ if (fields & F_BANDS) {
+ struct ofpbuf bands;
+ uint16_t n_bands = 0;
+ struct ofputil_meter_band *band = NULL;
+ int i;
+
+ ofpbuf_init(&bands, 64);
+
+ for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
+ name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+ char *value;
+
+ value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+ if (!value) {
+ ofp_fatal(str_, verbose, "field %s missing value", name);
+ }
+
+ if (!strcmp(name, "type")) {
+ /* Start a new band */
+ band = ofpbuf_put_zeros(&bands, sizeof *band);
+ n_bands++;
+
+ if (!strcmp(value, "drop")) {
+ band->type = OFPMBT13_DROP;
+ } else if (!strcmp(value, "dscp_remark")) {
+ band->type = OFPMBT13_DSCP_REMARK;
+ } else {
+ ofp_fatal(str_, verbose, "field %s unknown value %s", name,
+ value);
+ }
+ } else if (!band || !band->type) {
+ ofp_fatal(str_, verbose,
+ "band must start with the 'type' keyword");
+ } else if (!strcmp(name, "rate")) {
+ band->rate = str_to_u32(value);
+ } else if (!strcmp(name, "burst_size")) {
+ band->burst_size = str_to_u32(value);
+ } else if (!strcmp(name, "prec_level")) {
+ band->prec_level = str_to_u8(value, name);
+ } else {
+ ofp_fatal(str_, verbose, "unknown keyword %s", name);
+ }
+ }
+ /* validate bands */
+ if (!n_bands) {
+ ofp_fatal(str_, verbose, "meter must have bands");
+ }
+
+ mm->meter.n_bands = n_bands;
+ mm->meter.bands = ofpbuf_steal_data(&bands);
+
+ for (i = 0; i < n_bands; ++i) {
+ band = &mm->meter.bands[i];
+
+ if (!band->type) {
+ ofp_fatal(str_, verbose, "band must have 'type'");
+ }
+ if (band->type == OFPMBT13_DSCP_REMARK) {
+ if (!band->prec_level) {
+ ofp_fatal(str_, verbose, "'dscp_remark' band must have"
+ " 'prec_level'");
+ }
+ } else {
+ if (band->prec_level) {
+ ofp_fatal(str_, verbose, "Only 'dscp_remark' band may have"
+ " 'prec_level'");
+ }
+ }
+ if (!band->rate) {
+ ofp_fatal(str_, verbose, "band must have 'rate'");
+ }
+ if (mm->meter.flags & OFPMF13_BURST) {
+ if (!band->burst_size) {
+ ofp_fatal(str_, verbose, "band must have 'burst_size' "
+ "when 'burst' flag is set");
+ }
+ } else {
+ if (band->burst_size) {
+ ofp_fatal(str_, verbose, "band may have 'burst_size' only "
+ "when 'burst' flag is set");
+ }
+ }
+ }
+ } else {
+ mm->meter.n_bands = 0;
+ mm->meter.bands = NULL;
+ }
+
+ free(string);
+}
+
/* Convert 'str_' (as described in the documentation for the "monitor" command
* in the ovs-ofctl man page) into 'fmr'. */
void
| NXFMF_OWN | NXFMF_ACTIONS);
fmr->out_port = OFPP_NONE;
fmr->table_id = 0xff;
- cls_rule_init_catchall(&fmr->match, 0);
+ match_init_catchall(&fmr->match);
for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
} else if (!strcmp(name, "!own")) {
fmr->flags &= ~NXFMF_OWN;
} else if (parse_protocol(name, &p)) {
- cls_rule_set_dl_type(&fmr->match, htons(p->dl_type));
+ match_set_dl_type(&fmr->match, htons(p->dl_type));
if (p->nw_proto) {
- cls_rule_set_nw_proto(&fmr->match, p->nw_proto);
+ match_set_nw_proto(&fmr->match, p->nw_proto);
}
} else {
char *value;
}
if (!strcmp(name, "table")) {
- fmr->table_id = str_to_table_id(value);
+ fmr->table_id = str_to_u8(value, name);
} else if (!strcmp(name, "out_port")) {
- fmr->out_port = atoi(value);
+ fmr->out_port = u16_to_ofp(atoi(value));
} else if (mf_from_name(name)) {
parse_field(mf_from_name(name), value, &fmr->match);
} else {
parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
{
char *s = xstrdup(s_);
- str_to_ofpacts(NULL, s, ofpacts);
+ str_to_ofpacts(s, ofpacts);
free(s);
}
parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
uint16_t command, bool verbose)
{
- struct cls_rule rule_copy;
+ struct match match_copy;
parse_ofp_str(fm, command, string, verbose);
- /* Normalize a copy of the rule. This ensures that non-normalized flows
+ /* Normalize a copy of the match. This ensures that non-normalized flows
* get logged but doesn't affect what gets sent to the switch, so that the
* switch can do whatever it likes with the flow. */
- rule_copy = fm->cr;
- ofputil_normalize_rule(&rule_copy);
+ match_copy = fm->match;
+ ofputil_normalize_match(&match_copy);
}
void
fsr->aggregate = aggregate;
fsr->cookie = fm.cookie;
fsr->cookie_mask = fm.cookie_mask;
- fsr->match = fm.cr;
+ fsr->match = fm.match;
fsr->out_port = fm.out_port;
fsr->table_id = fm.table_id;
}
}
}
+ if (!flow->in_port.ofp_port) {
+ flow->in_port.ofp_port = OFPP_NONE;
+ }
+
exit:
free(copy);