/* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; struct match match; char *name, *value; learn = ofpact_put_LEARN(ofpacts); learn->idle_timeout = OFP_FLOW_PERMANENT; learn->hard_timeout = OFP_FLOW_PERMANENT; learn->priority = OFP_DEFAULT_PRIORITY; learn->table_id = 1; match_init_catchall(&match); while (ofputil_parse_key_value(&arg, &name, &value)) { if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { return xasprintf("%s: table id 255 not valid for `learn' " "action", orig); } } else if (!strcmp(name, "priority")) { learn->priority = atoi(value); } else if (!strcmp(name, "idle_timeout")) { learn->idle_timeout = atoi(value); } else if (!strcmp(name, "hard_timeout")) { learn->hard_timeout = atoi(value); } else if (!strcmp(name, "fin_idle_timeout")) { learn->fin_idle_timeout = atoi(value); } else if (!strcmp(name, "fin_hard_timeout")) { learn->fin_hard_timeout = atoi(value); } else if (!strcmp(name, "cookie")) { learn->cookie = htonll(strtoull(value, NULL, 0)); } else if (!strcmp(name, "send_flow_rem")) { learn->flags |= NX_LEARN_F_SEND_FLOW_REM; } else if (!strcmp(name, "delete_learned")) { learn->flags |= NX_LEARN_F_DELETE_LEARNED; } else { struct ofpact_learn_spec *spec; char *error; spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); error = learn_parse_spec(orig, name, value, spec, ofpacts, &match); if (error) { return error; } learn = ofpacts->header; } } ofpact_finish_LEARN(ofpacts, &learn); return NULL; }
/* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; struct match match; char *name, *value; learn = ofpact_put_LEARN(ofpacts); learn->idle_timeout = OFP_FLOW_PERMANENT; learn->hard_timeout = OFP_FLOW_PERMANENT; learn->priority = OFP_DEFAULT_PRIORITY; learn->table_id = 1; match_init_catchall(&match); while (ofputil_parse_key_value(&arg, &name, &value)) { if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { return xasprintf("%s: table id 255 not valid for `learn' " "action", orig); } } else if (!strcmp(name, "priority")) { learn->priority = atoi(value); } else if (!strcmp(name, "idle_timeout")) { learn->idle_timeout = atoi(value); } else if (!strcmp(name, "hard_timeout")) { learn->hard_timeout = atoi(value); } else if (!strcmp(name, "fin_idle_timeout")) { learn->fin_idle_timeout = atoi(value); } else if (!strcmp(name, "fin_hard_timeout")) { learn->fin_hard_timeout = atoi(value); } else if (!strcmp(name, "cookie")) { learn->cookie = htonll(strtoull(value, NULL, 0)); } else if (!strcmp(name, "send_flow_rem")) { learn->flags |= NX_LEARN_F_SEND_FLOW_REM; } else if (!strcmp(name, "delete_learned")) { learn->flags |= NX_LEARN_F_DELETE_LEARNED; } else { struct ofpact_learn_spec *spec; char *error; spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); learn = ofpacts->frame; learn->n_specs++; error = learn_parse_spec(orig, name, value, spec); if (error) { return error; } /* Update 'match' to allow for satisfying destination * prerequisites. */ if (spec->src_type == NX_LEARN_SRC_IMMEDIATE && spec->dst_type == NX_LEARN_DST_MATCH) { mf_write_subfield(&spec->dst, &spec->src_imm, &match); } } } ofpact_update_len(ofpacts, &learn->ofpact); return NULL; }
/* Converts 'nal' into a "struct ofpact_learn" and appends that struct to * 'ofpacts'. Returns 0 if successful, otherwise an OFPERR_*. */ enum ofperr learn_from_openflow(const struct nx_action_learn *nal, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; const void *p, *end; if (nal->pad) { return OFPERR_OFPBAC_BAD_ARGUMENT; } learn = ofpact_put_LEARN(ofpacts); learn->idle_timeout = ntohs(nal->idle_timeout); learn->hard_timeout = ntohs(nal->hard_timeout); learn->priority = ntohs(nal->priority); learn->cookie = ntohll(nal->cookie); learn->flags = ntohs(nal->flags); learn->table_id = nal->table_id; learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout); learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout); if (learn->flags & ~OFPFF_SEND_FLOW_REM || learn->table_id == 0xff) { return OFPERR_OFPBAC_BAD_ARGUMENT; } end = (char *) nal + ntohs(nal->len); for (p = nal + 1; p != end; ) { struct ofpact_learn_spec *spec; uint16_t header = ntohs(get_be16(&p)); if (!header) { break; } spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); learn = ofpacts->l2; learn->n_specs++; spec->src_type = header & NX_LEARN_SRC_MASK; spec->dst_type = header & NX_LEARN_DST_MASK; spec->n_bits = header & NX_LEARN_N_BITS_MASK; /* Check for valid src and dst type combination. */ if (spec->dst_type == NX_LEARN_DST_MATCH || spec->dst_type == NX_LEARN_DST_LOAD || (spec->dst_type == NX_LEARN_DST_OUTPUT && spec->src_type == NX_LEARN_SRC_FIELD)) { /* OK. */ } else { return OFPERR_OFPBAC_BAD_ARGUMENT; } /* Check that the arguments don't overrun the end of the action. */ if ((char *) end - (char *) p < learn_min_len(header)) { return OFPERR_OFPBAC_BAD_LEN; } /* Get the source. */ if (spec->src_type == NX_LEARN_SRC_FIELD) { get_subfield(spec->n_bits, &p, &spec->src); } else { int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); bitwise_copy(p, p_bytes, 0, &spec->src_imm, sizeof spec->src_imm, 0, spec->n_bits); p = (const uint8_t *) p + p_bytes; } /* Get the destination. */ if (spec->dst_type == NX_LEARN_DST_MATCH || spec->dst_type == NX_LEARN_DST_LOAD) { get_subfield(spec->n_bits, &p, &spec->dst); } } ofpact_update_len(ofpacts, &learn->ofpact); if (!is_all_zeros(p, (char *) end - (char *) p)) { return OFPERR_OFPBAC_BAD_ARGUMENT; } return 0; }