+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);
+}
+
+/* Parses 'arg' as the argument to a "dec_ttl" action, and appends such an
+ * action 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_dec_ttl(struct ofpbuf *b, char *arg)
+{
+ if (*arg == '\0') {
+ 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)) {
+ uint16_t id = atoi(cntr);
+
+ ofpbuf_put(b, &id, sizeof id);
+ ids = b->l2;
+ ids->n_controllers++;
+ }
+ if (!ids->n_controllers) {
+ return xstrdup("dec_ttl_cnt_ids: expected at least one controller "
+ "id.");
+ }
+ ofpact_update_len(b, &ids->ofpact);
+ }
+ return NULL;
+}
+
+/* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends such an
+ * action 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_set_mpls_ttl(struct ofpbuf *b, const char *arg)
+{
+ struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b);
+
+ if (*arg == '\0') {
+ return xstrdup("parse_set_mpls_ttl: expected ttl.");
+ }
+
+ mpls_ttl->ttl = atoi(arg);
+ return NULL;
+}
+
+/* Parses a "set_field" action with argument 'arg', appending the parsed
+ * action 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
+set_field_parse__(char *arg, struct ofpbuf *ofpacts)
+{
+ struct ofpact_reg_load *load = ofpact_put_REG_LOAD(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 (!delim) {
+ return xasprintf("%s: missing `->'", arg);
+ }
+ if (strlen(delim) <= strlen("->")) {
+ return xasprintf("%s: missing field name following `->'", arg);
+ }
+
+ key = delim + strlen("->");
+ mf = mf_from_name(key);
+ if (!mf) {
+ return xasprintf("%s is not a valid OXM field name", key);
+ }
+ if (!mf->writable) {
+ return xasprintf("%s is read-only", key);
+ }
+
+ delim[0] = '\0';
+ error = mf_parse_value(mf, value, &mf_value);
+ if (error) {
+ return error;
+ }
+ if (!mf_is_value_valid(mf, &mf_value)) {
+ return xasprintf("%s is not a valid value for field %s", value, key);
+ }
+ ofpact_set_field_init(load, mf, &mf_value);
+
+ return NULL;
+}
+
+/* Parses 'arg' as the argument to a "set_field" action, and appends such an
+ * action 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
+set_field_parse(const char *arg, struct ofpbuf *ofpacts)
+{
+ char *copy = xstrdup(arg);
+ char *error = set_field_parse__(copy, ofpacts);
+ free(copy);
+ return error;
+}
+
+/* Parses 'arg' as the argument to a "write_metadata" instruction, and appends
+ * such an action 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_metadata(struct ofpbuf *b, char *arg)