static enum ofperr learn_check_header(uint16_t header, size_t len) { int src_type = header & NX_LEARN_SRC_MASK; int dst_type = header & NX_LEARN_DST_MASK; /* Check for valid src and dst type combination. */ if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD || (dst_type == NX_LEARN_DST_OUTPUT && 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 (len < learn_min_len(header)) { return OFPERR_OFPBAC_BAD_LEN; } return 0; }
/* 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; }
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, ')'); }