/* 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 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 = strtoull(value, NULL, 0); } 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; }
/* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid, * otherwise an OFPERR_*. */ enum ofperr learn_check(const struct ofpact_learn *learn, const struct flow *flow) { const struct ofpact_learn_spec *spec; struct match match; match_init_catchall(&match); for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { enum ofperr error; /* Check the source. */ if (spec->src_type == NX_LEARN_SRC_FIELD) { error = mf_check_src(&spec->src, flow); if (error) { return error; } } /* Check the destination. */ switch (spec->dst_type) { case NX_LEARN_DST_MATCH: error = mf_check_src(&spec->dst, &match.flow); if (error) { return error; } mf_write_subfield(&spec->dst, &spec->src_imm, &match); break; case NX_LEARN_DST_LOAD: error = mf_check_dst(&spec->dst, &match.flow); if (error) { return error; } break; case NX_LEARN_DST_OUTPUT: /* Nothing to do. */ break; } } return 0; }
/* Composes 'fm' so that executing it will implement 'learn' given that the * packet being processed has 'flow' as its flow. * * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the * 'ofpacts' buffer. * * The caller has to actually execute 'fm'. */ void learn_execute(const struct ofpact_learn *learn, const struct flow *flow, struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts) { const struct ofpact_learn_spec *spec; match_init_catchall(&fm->match); fm->priority = learn->priority; fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = learn->cookie; fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX; fm->table_id = learn->table_id; fm->command = OFPFC_MODIFY_STRICT; fm->idle_timeout = learn->idle_timeout; fm->hard_timeout = learn->hard_timeout; fm->importance = 0; fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_NONE; fm->flags = 0; if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) { fm->flags |= OFPUTIL_FF_SEND_FLOW_REM; } fm->ofpacts = NULL; fm->ofpacts_len = 0; fm->delete_reason = OFPRR_DELETE; if (learn->fin_idle_timeout || learn->fin_hard_timeout) { struct ofpact_fin_timeout *oft; oft = ofpact_put_FIN_TIMEOUT(ofpacts); oft->fin_idle_timeout = learn->fin_idle_timeout; oft->fin_hard_timeout = learn->fin_hard_timeout; } for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { struct ofpact_set_field *sf; union mf_subvalue value; if (spec->src_type == NX_LEARN_SRC_FIELD) { mf_read_subfield(&spec->src, flow, &value); } else { value = spec->src_imm; } switch (spec->dst_type) { case NX_LEARN_DST_MATCH: mf_write_subfield(&spec->dst, &value, &fm->match); break; case NX_LEARN_DST_LOAD: sf = ofpact_put_reg_load(ofpacts); sf->field = spec->dst.field; bitwise_copy(&value, sizeof value, 0, &sf->value, spec->dst.field->n_bytes, spec->dst.ofs, spec->n_bits); bitwise_one(&sf->mask, spec->dst.field->n_bytes, spec->dst.ofs, spec->n_bits); break; case NX_LEARN_DST_OUTPUT: if (spec->n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) { ovs_be16 *last_be16 = &value.be16[ARRAY_SIZE(value.be16) - 1]; ofp_port_t port = u16_to_ofp(ntohs(*last_be16)); if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX) || port == OFPP_IN_PORT || port == OFPP_FLOOD || port == OFPP_LOCAL || port == OFPP_ALL) { ofpact_put_OUTPUT(ofpacts)->port = port; } } break; } } ofpact_pad(ofpacts); fm->ofpacts = ofpbuf_data(ofpacts); fm->ofpacts_len = ofpbuf_size(ofpacts); }
/* Composes 'fm' so that executing it will implement 'learn' given that the * packet being processed has 'flow' as its flow. * * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the * 'ofpacts' buffer. * * The caller has to actually execute 'fm'. */ void learn_execute(const struct ofpact_learn *learn, const struct flow *flow, struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts) { const struct ofpact_learn_spec *spec; match_init_catchall(&fm->match); fm->priority = learn->priority; fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = htonll(learn->cookie); fm->table_id = learn->table_id; fm->command = OFPFC_MODIFY_STRICT; fm->idle_timeout = learn->idle_timeout; fm->hard_timeout = learn->hard_timeout; fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_NONE; fm->flags = learn->flags; fm->ofpacts = NULL; fm->ofpacts_len = 0; if (learn->fin_idle_timeout || learn->fin_hard_timeout) { struct ofpact_fin_timeout *oft; oft = ofpact_put_FIN_TIMEOUT(ofpacts); oft->fin_idle_timeout = learn->fin_idle_timeout; oft->fin_hard_timeout = learn->fin_hard_timeout; } for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { union mf_subvalue value; int chunk, ofs; if (spec->src_type == NX_LEARN_SRC_FIELD) { mf_read_subfield(&spec->src, flow, &value); } else { value = spec->src_imm; } switch (spec->dst_type) { case NX_LEARN_DST_MATCH: mf_write_subfield(&spec->dst, &value, &fm->match); break; case NX_LEARN_DST_LOAD: for (ofs = 0; ofs < spec->n_bits; ofs += chunk) { struct ofpact_reg_load *load; chunk = MIN(spec->n_bits - ofs, 64); load = ofpact_put_REG_LOAD(ofpacts); load->dst.field = spec->dst.field; load->dst.ofs = spec->dst.ofs + ofs; load->dst.n_bits = chunk; bitwise_copy(&value, sizeof value, ofs, &load->subvalue, sizeof load->subvalue, 0, chunk); } break; case NX_LEARN_DST_OUTPUT: if (spec->n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) { ofp_port_t port = u16_to_ofp(ntohs(value.be16[7])); if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX) || port == OFPP_IN_PORT || port == OFPP_FLOOD || port == OFPP_LOCAL || port == OFPP_ALL) { ofpact_put_OUTPUT(ofpacts)->port = port; } } break; } } ofpact_pad(ofpacts); fm->ofpacts = ofpacts->data; fm->ofpacts_len = ofpacts->size; }
/* Parses 'arg' as a set of arguments to the "learn" action and appends a * matching NXAST_LEARN action to 'b'. The format parsed is described in * ovs-ofctl(8). * * Prints an error on stderr and aborts the program if 'arg' syntax is invalid. * * If 'flow' is nonnull, then it should be the flow from a cls_rule that is * the matching rule for the learning action. This helps to better validate * the action's arguments. * * Modifies 'arg'. */ void learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow) { char *orig = xstrdup(arg); char *name, *value; enum ofperr error; size_t learn_ofs; size_t len; struct nx_action_learn *learn; struct cls_rule rule; learn_ofs = b->size; learn = ofputil_put_NXAST_LEARN(b); learn->idle_timeout = htons(OFP_FLOW_PERMANENT); learn->hard_timeout = htons(OFP_FLOW_PERMANENT); learn->priority = htons(OFP_DEFAULT_PRIORITY); learn->cookie = htonll(0); learn->flags = htons(0); learn->table_id = 1; cls_rule_init_catchall(&rule, 0); while (ofputil_parse_key_value(&arg, &name, &value)) { learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn); if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { ovs_fatal(0, "%s: table id 255 not valid for `learn' action", orig); } } else if (!strcmp(name, "priority")) { learn->priority = htons(atoi(value)); } else if (!strcmp(name, "idle_timeout")) { learn->idle_timeout = htons(atoi(value)); } else if (!strcmp(name, "hard_timeout")) { learn->hard_timeout = htons(atoi(value)); } else if (!strcmp(name, "fin_idle_timeout")) { learn->fin_idle_timeout = htons(atoi(value)); } else if (!strcmp(name, "fin_hard_timeout")) { learn->fin_hard_timeout = htons(atoi(value)); } else if (!strcmp(name, "cookie")) { learn->cookie = htonll(strtoull(value, NULL, 0)); } else { struct learn_spec spec; learn_parse_spec(orig, name, value, &spec); /* Check prerequisites. */ if (spec.src_type == NX_LEARN_SRC_FIELD && flow && !mf_are_prereqs_ok(spec.src.field, flow)) { ovs_fatal(0, "%s: cannot specify source field %s because " "prerequisites are not satisfied", orig, spec.src.field->name); } if ((spec.dst_type == NX_LEARN_DST_MATCH || spec.dst_type == NX_LEARN_DST_LOAD) && !mf_are_prereqs_ok(spec.dst.field, &rule.flow)) { ovs_fatal(0, "%s: cannot specify destination field %s because " "prerequisites are not satisfied", orig, spec.dst.field->name); } /* Update 'rule' 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, &rule); } /* Output the flow_mod_spec. */ put_u16(b, spec.n_bits | spec.src_type | spec.dst_type); if (spec.src_type == NX_LEARN_SRC_IMMEDIATE) { int n_bytes = DIV_ROUND_UP(spec.n_bits, 16) * 2; int ofs = sizeof spec.src_imm - n_bytes; ofpbuf_put(b, &spec.src_imm.u8[ofs], n_bytes); } else { put_u32(b, spec.src.field->nxm_header); put_u16(b, spec.src.ofs); } if (spec.dst_type == NX_LEARN_DST_MATCH || spec.dst_type == NX_LEARN_DST_LOAD) { put_u32(b, spec.dst.field->nxm_header); put_u16(b, spec.dst.ofs); } else { assert(spec.dst_type == NX_LEARN_DST_OUTPUT); } } } put_u16(b, 0); len = b->size - learn_ofs; if (len % 8) { ofpbuf_put_zeros(b, 8 - len % 8); } learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn); learn->len = htons(b->size - learn_ofs); /* In theory the above should have caught any errors, but... */ if (flow) { error = learn_check(learn, flow); if (error) { ovs_fatal(0, "%s: %s", orig, ofperr_to_string(error)); } } free(orig); }
void learn_execute(const struct nx_action_learn *learn, const struct flow *flow, struct ofputil_flow_mod *fm) { const void *p, *end; struct ofpbuf actions; cls_rule_init_catchall(&fm->cr, ntohs(learn->priority)); fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = learn->cookie; fm->table_id = learn->table_id; fm->command = OFPFC_MODIFY_STRICT; fm->idle_timeout = ntohs(learn->idle_timeout); fm->hard_timeout = ntohs(learn->hard_timeout); fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_NONE; fm->flags = ntohs(learn->flags) & OFPFF_SEND_FLOW_REM; fm->actions = NULL; fm->n_actions = 0; ofpbuf_init(&actions, 64); if (learn->fin_idle_timeout || learn->fin_hard_timeout) { struct nx_action_fin_timeout *naft; naft = ofputil_put_NXAST_FIN_TIMEOUT(&actions); naft->fin_idle_timeout = learn->fin_idle_timeout; naft->fin_hard_timeout = learn->fin_hard_timeout; } for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) { uint16_t header = ntohs(get_be16(&p)); int n_bits = header & NX_LEARN_N_BITS_MASK; int src_type = header & NX_LEARN_SRC_MASK; int dst_type = header & NX_LEARN_DST_MASK; union mf_subvalue value; struct mf_subfield dst; int chunk, ofs; if (!header) { break; } if (src_type == NX_LEARN_SRC_FIELD) { struct mf_subfield src; get_subfield(n_bits, &p, &src); mf_read_subfield(&src, flow, &value); } else { int p_bytes = 2 * DIV_ROUND_UP(n_bits, 16); memset(&value, 0, sizeof value); bitwise_copy(p, p_bytes, 0, &value, sizeof value, 0, n_bits); p = (const uint8_t *) p + p_bytes; } switch (dst_type) { case NX_LEARN_DST_MATCH: get_subfield(n_bits, &p, &dst); mf_write_subfield(&dst, &value, &fm->cr); break; case NX_LEARN_DST_LOAD: get_subfield(n_bits, &p, &dst); for (ofs = 0; ofs < n_bits; ofs += chunk) { struct nx_action_reg_load *load; chunk = MIN(n_bits - ofs, 64); load = ofputil_put_NXAST_REG_LOAD(&actions); load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs + ofs, chunk); load->dst = htonl(dst.field->nxm_header); bitwise_copy(&value, sizeof value, ofs, &load->value, sizeof load->value, 0, chunk); } break; case NX_LEARN_DST_OUTPUT: if (n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) { ofputil_put_OFPAT10_OUTPUT(&actions)->port = value.be16[7]; } break; } } fm->actions = ofpbuf_steal_data(&actions); fm->n_actions = actions.size / sizeof(struct ofp_action_header); }
/* Composes 'fm' so that executing it will implement 'learn' given that the * packet being processed has 'flow' as its flow. * * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the * 'ofpacts' buffer. * * The caller has to actually execute 'fm'. */ void learn_execute(const struct ofpact_learn *learn, const struct flow *flow, struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts) { const struct ofpact_learn_spec *spec; match_init_catchall(&fm->match); fm->priority = learn->priority; fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = learn->cookie; fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX; fm->table_id = learn->table_id; fm->command = OFPFC_MODIFY_STRICT; fm->idle_timeout = learn->idle_timeout; fm->hard_timeout = learn->hard_timeout; fm->importance = 0; fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_NONE; fm->ofpacts_tlv_bitmap = 0; fm->flags = 0; if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) { fm->flags |= OFPUTIL_FF_SEND_FLOW_REM; } fm->ofpacts = NULL; fm->ofpacts_len = 0; if (learn->fin_idle_timeout || learn->fin_hard_timeout) { struct ofpact_fin_timeout *oft; oft = ofpact_put_FIN_TIMEOUT(ofpacts); oft->fin_idle_timeout = learn->fin_idle_timeout; oft->fin_hard_timeout = learn->fin_hard_timeout; } OFPACT_LEARN_SPEC_FOR_EACH (spec, learn) { struct ofpact_set_field *sf; union mf_subvalue value; if (spec->src_type == NX_LEARN_SRC_FIELD) { mf_read_subfield(&spec->src, flow, &value); } else { mf_subvalue_from_value(&spec->dst, &value, ofpact_learn_spec_imm(spec)); } switch (spec->dst_type) { case NX_LEARN_DST_MATCH: mf_write_subfield(&spec->dst, &value, &fm->match); match_add_ethernet_prereq(&fm->match, spec->dst.field); mf_vl_mff_set_tlv_bitmap( spec->dst.field, &fm->match.flow.tunnel.metadata.present.map); break; case NX_LEARN_DST_LOAD: sf = ofpact_put_reg_load(ofpacts, spec->dst.field, NULL, NULL); bitwise_copy(&value, sizeof value, 0, sf->value, spec->dst.field->n_bytes, spec->dst.ofs, spec->n_bits); bitwise_one(ofpact_set_field_mask(sf), spec->dst.field->n_bytes, spec->dst.ofs, spec->n_bits); mf_vl_mff_set_tlv_bitmap(spec->dst.field, &fm->ofpacts_tlv_bitmap); break; case NX_LEARN_DST_OUTPUT: if (spec->n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) { ofp_port_t port = u16_to_ofp(ntohll(value.integer)); if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX) || port == OFPP_IN_PORT || port == OFPP_FLOOD || port == OFPP_LOCAL || port == OFPP_ALL) { ofpact_put_OUTPUT(ofpacts)->port = port; } } break; } } fm->ofpacts = ofpacts->data; fm->ofpacts_len = ofpacts->size; }