#include "openflow/openflow.h"
#include "ovs-thread.h"
#include "packets.h"
+#include "simap.h"
#include "socket-util.h"
#include "vconn.h"
-#include "vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(ofp_parse);
/* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
*
static char * WARN_UNUSED_RESULT
str_to_mac(const char *str, uint8_t mac[6])
{
- if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
- != ETH_ADDR_SCAN_COUNT) {
+ if (!ovs_scan(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
return xasprintf("invalid mac address %s", str);
}
return NULL;
parse_enqueue(char *arg, struct ofpbuf *ofpacts)
{
char *sp = NULL;
- char *port = strtok_r(arg, ":q", &sp);
+ char *port = strtok_r(arg, ":q,", &sp);
char *queue = strtok_r(NULL, "", &sp);
struct ofpact_enqueue *enqueue;
if (port == NULL || queue == NULL) {
- return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
+ return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\" or "
+ "\"enqueue(PORT,QUEUE)\"");
}
enqueue = ofpact_put_ENQUEUE(ofpacts);
struct ofpact_output *output;
output = ofpact_put_OUTPUT(ofpacts);
- output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
if (!ofputil_port_from_string(arg, &output->port)) {
return xasprintf("%s: output to unknown port", arg);
}
+ output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
return NULL;
}
}
return NULL;
}
+/* Parses 'arg' as the argument to a "set_mpls_label" action, and appends such
+ * an action to 'b'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+parse_set_mpls_label(struct ofpbuf *b, const char *arg)
+{
+ struct ofpact_mpls_label *mpls_label = ofpact_put_SET_MPLS_LABEL(b);
+
+ if (*arg == '\0') {
+ return xstrdup("parse_set_mpls_label: expected label.");
+ }
+
+ mpls_label->label = htonl(atoi(arg));
+ return NULL;
+}
+
+/* Parses 'arg' as the argument to a "set_mpls_tc" action, and appends such an
+ * action to 'b'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+parse_set_mpls_tc(struct ofpbuf *b, const char *arg)
+{
+ struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(b);
+
+ if (*arg == '\0') {
+ return xstrdup("parse_set_mpls_tc: expected tc.");
+ }
+
+ mpls_tc->tc = atoi(arg);
+ return NULL;
+}
+
/* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends such an
* action to 'ofpacts'.
*
set_field_parse__(char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols)
{
- struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts);
+ struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
char *value;
char *delim;
char *key;
const struct mf_field *mf;
char *error;
- union mf_value mf_value;
value = arg;
delim = strstr(arg, "->");
if (!mf->writable) {
return xasprintf("%s is read-only", key);
}
-
+ sf->field = mf;
delim[0] = '\0';
- error = mf_parse_value(mf, value, &mf_value);
+ error = mf_parse_value(mf, value, &sf->value);
if (error) {
return error;
}
- if (!mf_is_value_valid(mf, &mf_value)) {
+
+ if (!mf_is_value_valid(mf, &sf->value)) {
return xasprintf("%s is not a valid value for field %s", value, key);
}
- ofpact_set_field_init(load, mf, &mf_value);
*usable_protocols &= mf->usable_protocols;
return NULL;
return error;
}
} else {
- om->mask = htonll(UINT64_MAX);
+ om->mask = OVS_BE64_MAX;
}
return str_to_be64(arg, &om->metadata);
{
size_t orig_size = ofpacts->size;
struct ofpact_tunnel *tunnel;
+ struct ofpact_vlan_vid *vlan_vid;
+ struct ofpact_vlan_pcp *vlan_pcp;
char *error = NULL;
uint16_t ethertype = 0;
uint16_t vid = 0;
uint8_t tos = 0;
+ uint8_t ecn = 0;
+ uint8_t ttl = 0;
uint8_t pcp = 0;
switch (code) {
case OFPUTIL_ACTION_INVALID:
- NOT_REACHED();
+ OVS_NOT_REACHED();
case OFPUTIL_OFPAT10_OUTPUT:
case OFPUTIL_OFPAT11_OUTPUT:
if (vid & ~VLAN_VID_MASK) {
return xasprintf("%s: not a valid VLAN VID", arg);
}
- ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid;
+ vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts);
+ vlan_vid->vlan_vid = vid;
+ vlan_vid->ofpact.compat = code;
+ vlan_vid->push_vlan_if_needed = code == OFPUTIL_OFPAT10_SET_VLAN_VID;
break;
case OFPUTIL_OFPAT10_SET_VLAN_PCP:
if (pcp & ~7) {
return xasprintf("%s: not a valid VLAN PCP", arg);
}
- ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
+ vlan_pcp = ofpact_put_SET_VLAN_PCP(ofpacts);
+ vlan_pcp->vlan_pcp = pcp;
+ vlan_pcp->ofpact.compat = code;
+ vlan_pcp->push_vlan_if_needed = code == OFPUTIL_OFPAT10_SET_VLAN_PCP;
break;
case OFPUTIL_OFPAT12_SET_FIELD:
case OFPUTIL_OFPAT10_STRIP_VLAN:
case OFPUTIL_OFPAT11_POP_VLAN:
- ofpact_put_STRIP_VLAN(ofpacts);
+ ofpact_put_STRIP_VLAN(ofpacts)->ofpact.compat = code;
break;
case OFPUTIL_OFPAT11_PUSH_VLAN:
if (tos & ~IP_DSCP_MASK) {
return xasprintf("%s: not a valid TOS", arg);
}
- ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos;
+ ofpact_put_SET_IP_DSCP(ofpacts)->dscp = tos;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_ECN:
+ error = str_to_u8(arg, "ECN", &ecn);
+ if (error) {
+ return error;
+ }
+
+ if (ecn & ~IP_ECN_MASK) {
+ return xasprintf("%s: not a valid ECN", arg);
+ }
+ ofpact_put_SET_IP_ECN(ofpacts)->ecn = ecn;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_TTL:
+ error = str_to_u8(arg, "TTL", &ttl);
+ if (error) {
+ return error;
+ }
+
+ ofpact_put_SET_IP_TTL(ofpacts)->ttl = ttl;
break;
case OFPUTIL_OFPAT11_DEC_NW_TTL:
- NOT_REACHED();
+ OVS_NOT_REACHED();
case OFPUTIL_OFPAT10_SET_TP_SRC:
case OFPUTIL_OFPAT11_SET_TP_SRC:
case OFPUTIL_NXAST_RESUBMIT_TABLE:
case OFPUTIL_NXAST_OUTPUT_REG:
case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
- NOT_REACHED();
+ OVS_NOT_REACHED();
case OFPUTIL_NXAST_LEARN:
error = learn_parse(arg, ofpacts);
error = parse_dec_ttl(ofpacts, arg);
break;
+ case OFPUTIL_NXAST_SET_MPLS_LABEL:
+ case OFPUTIL_OFPAT11_SET_MPLS_LABEL:
+ error = parse_set_mpls_label(ofpacts, arg);
+ break;
+
+ case OFPUTIL_NXAST_SET_MPLS_TC:
+ case OFPUTIL_OFPAT11_SET_MPLS_TC:
+ error = parse_set_mpls_tc(ofpacts, arg);
+ break;
+
case OFPUTIL_NXAST_SET_MPLS_TTL:
case OFPUTIL_OFPAT11_SET_MPLS_TTL:
error = parse_set_mpls_ttl(ofpacts, arg);
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
static char * WARN_UNUSED_RESULT
-str_to_ofpacts(char *str, struct ofpbuf *ofpacts,
- enum ofputil_protocol *usable_protocols)
+str_to_ofpacts__(char *str, struct ofpbuf *ofpacts,
+ enum ofputil_protocol *usable_protocols)
{
size_t orig_size = ofpacts->size;
char *pos, *act, *arg;
- enum ofperr error;
int n_actions;
pos = str;
n_actions++;
}
+ ofpact_pad(ofpacts);
+ return NULL;
+}
+
+
+/* Parses 'str' as a series of actions, and appends them to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+str_to_ofpacts(char *str, struct ofpbuf *ofpacts,
+ enum ofputil_protocol *usable_protocols)
+{
+ size_t orig_size = ofpacts->size;
+ char *error_s;
+ enum ofperr error;
+
+ error_s = str_to_ofpacts__(str, ofpacts, usable_protocols);
+ if (error_s) {
+ return error_s;
+ }
+
error = ofpacts_verify(ofpacts->data, ofpacts->size);
if (error) {
ofpacts->size = orig_size;
return xstrdup("Incorrect action ordering");
}
- ofpact_pad(ofpacts);
return NULL;
}
switch (type) {
case OVSINST_OFPIT11_APPLY_ACTIONS:
- NOT_REACHED(); /* This case is handled by str_to_inst_ofpacts() */
+ OVS_NOT_REACHED(); /* This case is handled by str_to_inst_ofpacts() */
break;
- case OVSINST_OFPIT11_WRITE_ACTIONS:
- /* XXX */
- error_s = xstrdup("instruction write-actions is not supported yet");
+ case OVSINST_OFPIT11_WRITE_ACTIONS: {
+ struct ofpact_nest *on;
+ size_t ofs;
+
+ ofpact_pad(ofpacts);
+ ofs = ofpacts->size;
+ on = ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS,
+ offsetof(struct ofpact_nest, actions));
+ error_s = str_to_ofpacts__(arg, ofpacts, usable_protocols);
+
+ on = ofpbuf_at_assert(ofpacts, ofs, sizeof *on);
+ on->ofpact.len = ofpacts->size - ofs;
+
+ if (error_s) {
+ ofpacts->size = ofs;
+ }
break;
+ }
case OVSINST_OFPIT11_CLEAR_ACTIONS:
ofpact_put_CLEAR_ACTIONS(ofpacts);
break;
default:
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
match_init_catchall(&fm->match);
fm->cookie_mask = htonll(0);
if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) {
/* For modify, by default, don't update the cookie. */
- fm->new_cookie = htonll(UINT64_MAX);
+ fm->new_cookie = OVS_BE64_MAX;
} else{
fm->new_cookie = htonll(0);
}
*usable_protocols &= OFPUTIL_P_NXM_OXM_ANY;
}
}
- if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX)
+ if (!fm->cookie_mask && fm->new_cookie == OVS_BE64_MAX
&& (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) {
/* On modifies without a mask, we are supposed to add a flow if
* one does not exist. If a cookie wasn't been specified, use a
enum ofperr err;
err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow,
- OFPP_MAX, 0);
+ OFPP_MAX, fm->table_id, 255, usable_protocols);
+ if (!err && !usable_protocols) {
+ err = OFPERR_OFPBAC_MATCH_INCONSISTENT;
+ }
if (err) {
error = xasprintf("actions are invalid with specified match "
"(%s)", ofperr_to_string(err));
}
+
}
if (error) {
ofpbuf_uninit(&ofpacts);
char *name;
/* Meters require at least OF 1.3. */
- *usable_protocols &= OFPUTIL_P_OF13_UP;
+ *usable_protocols = OFPUTIL_P_OF13_UP;
switch (command) {
case -1:
break;
default:
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
mm->command = command;
return error;
}
+/* Convert 'table_id' and 'flow_miss_handling' (as described for the
+ * "mod-table" command in the ovs-ofctl man page) into 'tm' for sending the
+ * specified table_mod 'command' to a switch.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
+parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
+ const char *flow_miss_handling,
+ enum ofputil_protocol *usable_protocols)
+{
+ /* Table mod requires at least OF 1.1. */
+ *usable_protocols = OFPUTIL_P_OF11_UP;
+
+ if (!strcasecmp(table_id, "all")) {
+ tm->table_id = OFPTT_ALL;
+ } else {
+ char *error = str_to_u8(table_id, "table_id", &tm->table_id);
+ if (error) {
+ return error;
+ }
+ }
+
+ if (strcmp(flow_miss_handling, "controller") == 0) {
+ tm->config = OFPTC11_TABLE_MISS_CONTROLLER;
+ } else if (strcmp(flow_miss_handling, "continue") == 0) {
+ tm->config = OFPTC11_TABLE_MISS_CONTINUE;
+ } else if (strcmp(flow_miss_handling, "drop") == 0) {
+ tm->config = OFPTC11_TABLE_MISS_DROP;
+ } else {
+ return xasprintf("invalid flow_miss_handling %s", flow_miss_handling);
+ }
+
+ if (tm->table_id == 0xfe && tm->config == OFPTC11_TABLE_MISS_CONTINUE) {
+ return xstrdup("last table's flow miss handling can not be continue");
+ }
+
+ return NULL;
+}
+
+
/* Opens file 'file_name' and reads each line as a flow_mod of the specified
* type (one of OFPFC_*). Stores each flow_mod in '*fm', an array allocated
* on the caller's behalf, and the number of flow_mods in '*n_fms'.
/* Parses a specification of a flow from 's' into 'flow'. 's' must take the
* form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a
* mf_field. Fields must be specified in a natural order for satisfying
- * prerequisites.
+ * prerequisites. If 'mask' is specified, fills the mask field for each of the
+ * field specified in flow. If the map, 'names_portno' is specfied, converts
+ * the in_port name into port no while setting the 'flow'.
*
* Returns NULL on success, otherwise a malloc()'d string that explains the
* problem. */
char *
-parse_ofp_exact_flow(struct flow *flow, const char *s)
+parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s,
+ const struct simap *portno_names)
{
char *pos, *key, *value_s;
char *error = NULL;
char *copy;
memset(flow, 0, sizeof *flow);
+ if (mask) {
+ memset(mask, 0, sizeof *mask);
+ }
pos = copy = xstrdup(s);
while (ofputil_parse_key_value(&pos, &key, &value_s)) {
goto exit;
}
flow->dl_type = htons(p->dl_type);
+ if (mask) {
+ mask->dl_type = OVS_BE16_MAX;
+ }
if (p->nw_proto) {
if (flow->nw_proto) {
goto exit;
}
flow->nw_proto = p->nw_proto;
+ if (mask) {
+ mask->nw_proto = UINT8_MAX;
+ }
}
} else {
const struct mf_field *mf;
goto exit;
}
- field_error = mf_parse_value(mf, value_s, &value);
- if (field_error) {
- error = xasprintf("%s: bad value for %s (%s)",
- s, key, field_error);
- free(field_error);
- goto exit;
- }
+ if (!strcmp(key, "in_port")
+ && portno_names
+ && simap_contains(portno_names, value_s)) {
+ flow->in_port.ofp_port = u16_to_ofp(
+ simap_get(portno_names, value_s));
+ if (mask) {
+ mask->in_port.ofp_port = u16_to_ofp(ntohs(OVS_BE16_MAX));
+ }
+ } else {
+ field_error = mf_parse_value(mf, value_s, &value);
+ if (field_error) {
+ error = xasprintf("%s: bad value for %s (%s)",
+ s, key, field_error);
+ free(field_error);
+ goto exit;
+ }
- mf_set_flow_value(mf, &value, flow);
+ mf_set_flow_value(mf, &value, flow);
+ if (mask) {
+ mf_mask_field(mf, mask);
+ }
+ }
}
}
if (error) {
memset(flow, 0, sizeof *flow);
+ if (mask) {
+ memset(mask, 0, sizeof *mask);
+ }
}
return error;
}
break;
default:
- NOT_REACHED();
+ OVS_NOT_REACHED();
}
memset(gm, 0, sizeof *gm);
char *error;
if (*n_gms >= allocated_gms) {
+ size_t i;
+
*gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms);
+ for (i = 0; i < *n_gms; i++) {
+ list_moved(&(*gms)[i].buckets);
+ }
}
error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s),
&usable);