+ * 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)
+{
+ size_t orig_size = ofpacts->size;
+ char *pos, *act, *arg;
+ enum ofperr error;
+ int n_actions;
+
+ pos = str;
+ n_actions = 0;
+ while (ofputil_parse_key_value(&pos, &act, &arg)) {
+ char *error = str_to_ofpact__(pos, act, arg, ofpacts, n_actions);
+ if (error) {
+ ofpacts->size = orig_size;
+ return error;
+ }
+ n_actions++;
+ }
+
+ error = ofpacts_verify(ofpacts->data, ofpacts->size);
+ if (error) {
+ ofpacts->size = orig_size;
+ return xstrdup("Incorrect action ordering");
+ }
+
+ ofpact_pad(ofpacts);
+ return NULL;
+}
+
+/* Parses 'arg' as the argument to instruction 'type', and appends such an
+ * instruction 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
+parse_named_instruction(enum ovs_instruction_type type,
+ char *arg, struct ofpbuf *ofpacts)
+{
+ char *error_s = NULL;
+ 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 */
+ error_s = xstrdup("instruction write-actions is not supported yet");
+ break;
+
+ case OVSINST_OFPIT11_CLEAR_ACTIONS:
+ ofpact_put_CLEAR_ACTIONS(ofpacts);
+ break;
+
+ case OVSINST_OFPIT13_METER:
+ error_s = str_to_u32(arg, &ofpact_put_METER(ofpacts)->meter_id);
+ break;
+
+ case OVSINST_OFPIT11_WRITE_METADATA:
+ error_s = 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]) {
+ return xstrdup("instruction goto-table needs table id");
+ }
+ error_s = str_to_u8(table_s, "table", &ogt->table_id);
+ break;
+ }
+ }
+
+ if (error_s) {
+ return error_s;
+ }
+
+ /* If write_metadata is specified as an action AND an instruction, ofpacts
+ could be invalid. */
+ error = ofpacts_verify(ofpacts->data, ofpacts->size);
+ if (error) {
+ return xstrdup("Incorrect instruction ordering");
+ }
+ return NULL;
+}
+
+/* Parses 'str' as a series of instructions, 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_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
+{
+ size_t orig_size = ofpacts->size;
+ 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 = ovs_instruction_type_from_name(inst);
+ if (type < 0) {
+ char *error = str_to_ofpact__(pos, inst, arg, ofpacts, n_actions);
+ if (error) {
+ ofpacts->size = orig_size;
+ return error;
+ }
+
+ type = OVSINST_OFPIT11_APPLY_ACTIONS;
+ if (prev_type == type) {
+ n_actions++;
+ continue;
+ }
+ } else if (type == OVSINST_OFPIT11_APPLY_ACTIONS) {
+ ofpacts->size = orig_size;
+ return xasprintf("%s isn't supported. Just write actions then "
+ "it is interpreted as apply_actions", inst);
+ } else {
+ char *error = parse_named_instruction(type, arg, ofpacts);
+ if (error) {
+ ofpacts->size = orig_size;
+ return error;
+ }
+ }
+
+ if (type <= prev_type) {
+ ofpacts->size = orig_size;
+ if (type == prev_type) {
+ return xasprintf("instruction %s may be specified only once",
+ inst);
+ } else {
+ return xasprintf("instruction %s must be specified before %s",
+ inst, prev_inst);
+ }
+ }
+
+ prev_inst = inst;
+ prev_type = type;
+ n_actions++;
+ }
+ ofpact_pad(ofpacts);
+
+ return NULL;
+}
+
+struct protocol {
+ const char *name;
+ uint16_t dl_type;
+ uint8_t nw_proto;
+};
+
+static bool
+parse_protocol(const char *name, const struct protocol **p_out)
+{
+ static const struct protocol protocols[] = {
+ { "ip", ETH_TYPE_IP, 0 },
+ { "arp", ETH_TYPE_ARP, 0 },
+ { "icmp", ETH_TYPE_IP, IPPROTO_ICMP },
+ { "tcp", ETH_TYPE_IP, IPPROTO_TCP },
+ { "udp", ETH_TYPE_IP, IPPROTO_UDP },
+ { "ipv6", ETH_TYPE_IPV6, 0 },
+ { "ip6", ETH_TYPE_IPV6, 0 },
+ { "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;
+
+ for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
+ if (!strcmp(p->name, name)) {
+ *p_out = p;
+ return true;
+ }
+ }
+ *p_out = NULL;
+ return false;
+}
+
+/* Parses 's' as the (possibly masked) value of field 'mf', and updates
+ * 'match' appropriately.
+ *
+ * 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_field(const struct mf_field *mf, const char *s, struct match *match)
+{
+ union mf_value value, mask;
+ char *error;
+
+ error = mf_parse(mf, s, &value, &mask);
+ if (!error) {
+ mf_set(mf, &value, &mask, match);
+ }
+ return error;
+}
+
+static char * WARN_UNUSED_RESULT
+parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)