/* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a * valid action on 'flow'. */ enum ofperr learn_check(const struct nx_action_learn *learn, const struct flow *flow) { struct cls_rule rule; const void *p, *end; cls_rule_init_catchall(&rule, 0); if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM) || learn->pad || learn->table_id == 0xff) { return OFPERR_OFPBAC_BAD_ARGUMENT; } end = (char *) learn + ntohs(learn->len); for (p = learn + 1; 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; enum ofperr error; uint64_t value; if (!header) { break; } error = learn_check_header(header, (char *) end - (char *) p); if (error) { return error; } /* Check the source. */ if (src_type == NX_LEARN_SRC_FIELD) { struct mf_subfield src; get_subfield(n_bits, &p, &src); error = mf_check_src(&src, flow); if (error) { return error; } value = 0; } else { value = get_bits(n_bits, &p); } /* Check the destination. */ if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) { struct mf_subfield dst; get_subfield(n_bits, &p, &dst); error = (dst_type == NX_LEARN_DST_LOAD ? mf_check_dst(&dst, &rule.flow) : mf_check_src(&dst, &rule.flow)); if (error) { return error; } if (dst_type == NX_LEARN_DST_MATCH && src_type == NX_LEARN_SRC_IMMEDIATE) { if (n_bits <= 64) { mf_set_subfield(&dst, value, &rule); } else { /* We're only setting subfields to allow us to check * prerequisites. No prerequisite depends on the value of * a field that is wider than 64 bits. So just skip * setting it entirely. */ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); } } } } if (!is_all_zeros(p, (char *) end - (char *) p)) { return OFPERR_OFPBAC_BAD_ARGUMENT; } return 0; }
void learn_format(const struct nx_action_learn *learn, struct ds *s) { struct cls_rule rule; const void *p, *end; cls_rule_init_catchall(&rule, 0); ds_put_format(s, "learn(table=%"PRIu8, learn->table_id); if (learn->idle_timeout != htons(OFP_FLOW_PERMANENT)) { ds_put_format(s, ",idle_timeout=%"PRIu16, ntohs(learn->idle_timeout)); } if (learn->hard_timeout != htons(OFP_FLOW_PERMANENT)) { ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout)); } if (learn->fin_idle_timeout) { ds_put_format(s, ",fin_idle_timeout=%"PRIu16, ntohs(learn->fin_idle_timeout)); } if (learn->fin_hard_timeout) { ds_put_format(s, ",fin_hard_timeout=%"PRIu16, ntohs(learn->fin_hard_timeout)); } if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) { ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority)); } if (learn->flags & htons(OFPFF_SEND_FLOW_REM)) { ds_put_cstr(s, ",OFPFF_SEND_FLOW_REM"); } if (learn->flags & htons(~OFPFF_SEND_FLOW_REM)) { ds_put_format(s, ",***flags=%"PRIu16"***", ntohs(learn->flags) & ~OFPFF_SEND_FLOW_REM); } if (learn->cookie != htonll(0)) { ds_put_format(s, ",cookie=0x%"PRIx64, ntohll(learn->cookie)); } if (learn->pad != 0) { ds_put_cstr(s, ",***nonzero pad***"); } end = (char *) learn + ntohs(learn->len); for (p = learn + 1; 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; struct mf_subfield src; const uint8_t *src_value; int src_value_bytes; int dst_type = header & NX_LEARN_DST_MASK; struct mf_subfield dst; enum ofperr error; int i; if (!header) { break; } error = learn_check_header(header, (char *) end - (char *) p); if (error == OFPERR_OFPBAC_BAD_ARGUMENT) { ds_put_format(s, ",***bad flow_mod_spec header %"PRIx16"***)", header); return; } else if (error == OFPERR_OFPBAC_BAD_LEN) { ds_put_format(s, ",***flow_mod_spec at offset %td is %u bytes " "long but only %td bytes are left***)", (char *) p - (char *) (learn + 1) - 2, learn_min_len(header) + 2, (char *) end - (char *) p + 2); return; } assert(!error); /* Get the source. */ if (src_type == NX_LEARN_SRC_FIELD) { get_subfield(n_bits, &p, &src); src_value_bytes = 0; src_value = NULL; } else { src.field = NULL; src.ofs = 0; src.n_bits = 0; src_value_bytes = 2 * DIV_ROUND_UP(n_bits, 16); src_value = p; p = (const void *) ((const uint8_t *) p + src_value_bytes); } /* Get the destination. */ if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) { get_subfield(n_bits, &p, &dst); } else { dst.field = NULL; dst.ofs = 0; dst.n_bits = 0; } ds_put_char(s, ','); switch (src_type | dst_type) { case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH: if (dst.field && dst.ofs == 0 && n_bits == dst.field->n_bits) { union mf_value value; uint8_t *bytes = (uint8_t *) &value; if (src_value_bytes > dst.field->n_bytes) { /* The destination field is an odd number of bytes, which * got rounded up to a multiple of 2 to be put into the * learning action. Skip over the leading byte, which * should be zero anyway. Otherwise the memcpy() below * will overrun the start of 'value'. */ int diff = src_value_bytes - dst.field->n_bytes; src_value += diff; src_value_bytes -= diff; } memset(&value, 0, sizeof value); memcpy(&bytes[dst.field->n_bytes - src_value_bytes], src_value, src_value_bytes); ds_put_format(s, "%s=", dst.field->name); mf_format(dst.field, &value, NULL, s); } else { mf_format_subfield(&dst, s); ds_put_cstr(s, "=0x"); for (i = 0; i < src_value_bytes; i++) { ds_put_format(s, "%02"PRIx8, src_value[i]); } } break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH: mf_format_subfield(&dst, s); if (src.field != dst.field || src.ofs != dst.ofs) { ds_put_char(s, '='); mf_format_subfield(&src, s); } break; case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD: ds_put_cstr(s, "load:0x"); for (i = 0; i < src_value_bytes; i++) { ds_put_format(s, "%02"PRIx8, src_value[i]); } ds_put_cstr(s, "->"); mf_format_subfield(&dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD: ds_put_cstr(s, "load:"); mf_format_subfield(&src, s); ds_put_cstr(s, "->"); mf_format_subfield(&dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT: ds_put_cstr(s, "output:"); mf_format_subfield(&src, s); break; } } if (!is_all_zeros(p, (char *) end - (char *) p)) { ds_put_cstr(s, ",***nonzero trailer***"); } ds_put_char(s, ')'); }
/* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a * valid action on 'flow'. */ int learn_check(const struct nx_action_learn *learn, const struct flow *flow) { struct cls_rule rule; const void *p, *end; cls_rule_init_catchall(&rule, 0); if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM) || !is_all_zeros(learn->pad, sizeof learn->pad) || learn->table_id == 0xff) { return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); } end = (char *) learn + ntohs(learn->len); for (p = learn + 1; 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; uint64_t value; int error; if (!header) { break; } error = learn_check_header(header, (char *) end - (char *) p); if (error) { return error; } /* Check the source. */ if (src_type == NX_LEARN_SRC_FIELD) { ovs_be32 src_field = get_be32(&p); int src_ofs = ntohs(get_be16(&p)); error = nxm_src_check(src_field, src_ofs, n_bits, flow); if (error) { return error; } value = 0; } else { value = get_bits(n_bits, &p); } /* Check the destination. */ if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) { ovs_be32 dst_field = get_be32(&p); int dst_ofs = ntohs(get_be16(&p)); int error; error = (dst_type == NX_LEARN_DST_LOAD ? nxm_dst_check(dst_field, dst_ofs, n_bits, &rule.flow) : nxm_src_check(dst_field, dst_ofs, n_bits, &rule.flow)); if (error) { return error; } if (dst_type == NX_LEARN_DST_MATCH && src_type == NX_LEARN_SRC_IMMEDIATE) { mf_set_subfield(mf_from_nxm_header(ntohl(dst_field)), value, dst_ofs, n_bits, &rule); } } } if (!is_all_zeros(p, (char *) end - (char *) p)) { return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); } return 0; }