/* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8) * describes. */ void learn_format(const struct ofpact_learn *learn, struct ds *s) { const struct ofpact_learn_spec *spec; struct match match; match_init_catchall(&match); ds_put_format(s, "learn(table=%"PRIu8, learn->table_id); if (learn->idle_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, ",idle_timeout=%"PRIu16, learn->idle_timeout); } if (learn->hard_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, ",hard_timeout=%"PRIu16, learn->hard_timeout); } if (learn->fin_idle_timeout) { ds_put_format(s, ",fin_idle_timeout=%"PRIu16, learn->fin_idle_timeout); } if (learn->fin_hard_timeout) { ds_put_format(s, ",fin_hard_timeout=%"PRIu16, learn->fin_hard_timeout); } if (learn->priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, ",priority=%"PRIu16, learn->priority); } if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) { ds_put_cstr(s, ",send_flow_rem"); } if (learn->flags & NX_LEARN_F_DELETE_LEARNED) { ds_put_cstr(s, ",delete_learned"); } if (learn->cookie != 0) { ds_put_format(s, ",cookie=%#"PRIx64, ntohll(learn->cookie)); } for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { ds_put_char(s, ','); switch (spec->src_type | spec->dst_type) { case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH: if (spec->dst.ofs == 0 && spec->dst.n_bits == spec->dst.field->n_bits) { union mf_value value; memset(&value, 0, sizeof value); bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0, &value, spec->dst.field->n_bytes, 0, spec->dst.field->n_bits); ds_put_format(s, "%s=", spec->dst.field->name); mf_format(spec->dst.field, &value, NULL, s); } else { mf_format_subfield(&spec->dst, s); ds_put_char(s, '='); mf_format_subvalue(&spec->src_imm, s); } break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH: mf_format_subfield(&spec->dst, s); if (spec->src.field != spec->dst.field || spec->src.ofs != spec->dst.ofs) { ds_put_char(s, '='); mf_format_subfield(&spec->src, s); } break; case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD: ds_put_format(s, "load:"); mf_format_subvalue(&spec->src_imm, s); ds_put_cstr(s, "->"); mf_format_subfield(&spec->dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD: ds_put_cstr(s, "load:"); mf_format_subfield(&spec->src, s); ds_put_cstr(s, "->"); mf_format_subfield(&spec->dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT: ds_put_cstr(s, "output:"); mf_format_subfield(&spec->src, s); break; } } ds_put_char(s, ')'); }
/* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8) * describes. */ void learn_format(const struct ofpact_learn *learn, const struct ofputil_port_map *port_map, struct ds *s) { const struct ofpact_learn_spec *spec; struct match match; match_init_catchall(&match); ds_put_format(s, "%slearn(%s%stable=%s%"PRIu8, colors.learn, colors.end, colors.special, colors.end, learn->table_id); if (learn->idle_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, ",%sidle_timeout=%s%"PRIu16, colors.param, colors.end, learn->idle_timeout); } if (learn->hard_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, ",%shard_timeout=%s%"PRIu16, colors.param, colors.end, learn->hard_timeout); } if (learn->fin_idle_timeout) { ds_put_format(s, ",%sfin_idle_timeout=%s%"PRIu16, colors.param, colors.end, learn->fin_idle_timeout); } if (learn->fin_hard_timeout) { ds_put_format(s, "%s,fin_hard_timeout=%s%"PRIu16, colors.param, colors.end, learn->fin_hard_timeout); } if (learn->priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "%s,priority=%s%"PRIu16, colors.special, colors.end, learn->priority); } if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) { ds_put_format(s, ",%ssend_flow_rem%s", colors.value, colors.end); } if (learn->flags & NX_LEARN_F_DELETE_LEARNED) { ds_put_format(s, ",%sdelete_learned%s", colors.value, colors.end); } if (learn->cookie != 0) { ds_put_format(s, ",%scookie=%s%#"PRIx64, colors.param, colors.end, ntohll(learn->cookie)); } if (learn->limit != 0) { ds_put_format(s, ",%slimit=%s%"PRIu32, colors.param, colors.end, learn->limit); } if (learn->flags & NX_LEARN_F_WRITE_RESULT) { ds_put_format(s, ",%sresult_dst=%s", colors.param, colors.end); mf_format_subfield(&learn->result_dst, s); } OFPACT_LEARN_SPEC_FOR_EACH (spec, learn) { unsigned int n_bytes = DIV_ROUND_UP(spec->n_bits, 8); ds_put_char(s, ','); switch (spec->src_type | spec->dst_type) { case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH: { if (spec->dst.ofs == 0 && spec->dst.n_bits == spec->dst.field->n_bits) { union mf_value value; memset(&value, 0, sizeof value); memcpy(&value.b[spec->dst.field->n_bytes - n_bytes], ofpact_learn_spec_imm(spec), n_bytes); ds_put_format(s, "%s%s=%s", colors.param, spec->dst.field->name, colors.end); mf_format(spec->dst.field, &value, NULL, port_map, s); } else { ds_put_format(s, "%s", colors.param); mf_format_subfield(&spec->dst, s); ds_put_format(s, "=%s", colors.end); ds_put_hex(s, ofpact_learn_spec_imm(spec), n_bytes); } break; } case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH: ds_put_format(s, "%s", colors.param); mf_format_subfield(&spec->dst, s); ds_put_format(s, "%s", colors.end); if (spec->src.field != spec->dst.field || spec->src.ofs != spec->dst.ofs) { ds_put_format(s, "%s=%s", colors.param, colors.end); mf_format_subfield(&spec->src, s); } break; case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD: ds_put_format(s, "%sload:%s", colors.special, colors.end); ds_put_hex(s, ofpact_learn_spec_imm(spec), n_bytes); ds_put_format(s, "%s->%s", colors.special, colors.end); mf_format_subfield(&spec->dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD: ds_put_format(s, "%sload:%s", colors.special, colors.end); mf_format_subfield(&spec->src, s); ds_put_format(s, "%s->%s", colors.special, colors.end); mf_format_subfield(&spec->dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT: ds_put_format(s, "%soutput:%s", colors.special, colors.end); mf_format_subfield(&spec->src, s); break; } }
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, ')'); }
/* 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_spec(const char *orig, char *name, char *value, const struct ofputil_port_map *port_map, struct ofpact_learn_spec *spec, struct ofpbuf *ofpacts, struct match *match) { /* Parse destination and check prerequisites. */ struct mf_subfield dst; char *error = mf_parse_subfield(&dst, name); bool parse_error = error != NULL; free(error); if (!parse_error) { if (!mf_nxm_header(dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", orig, name); } spec->dst = dst; spec->n_bits = dst.n_bits; spec->dst_type = NX_LEARN_DST_MATCH; /* Parse source and check prerequisites. */ if (value[0] != '\0') { struct mf_subfield src; error = mf_parse_subfield(&src, value); if (error) { union mf_value imm; char *imm_error = NULL; /* Try an immediate value. */ if (dst.ofs == 0 && dst.n_bits == dst.field->n_bits) { /* Full field value. */ imm_error = mf_parse_value(dst.field, value, port_map, &imm); } else { char *tail; /* Partial field value. */ if (parse_int_string(value, (uint8_t *)&imm, dst.field->n_bytes, &tail) || *tail != 0) { imm_error = xasprintf("%s: cannot parse integer value", orig); } if (!imm_error && !bitwise_is_all_zeros(&imm, dst.field->n_bytes, dst.n_bits, dst.field->n_bytes * 8 - dst.n_bits)) { struct ds ds; ds_init(&ds); mf_format(dst.field, &imm, NULL, NULL, &ds); imm_error = xasprintf("%s: value %s does not fit into %d bits", orig, ds_cstr(&ds), dst.n_bits); ds_destroy(&ds); } } if (imm_error) { char *err = xasprintf("%s: %s value %s cannot be parsed as a subfield (%s) or an immediate value (%s)", orig, name, value, error, imm_error); free(error); free(imm_error); return err; } spec->src_type = NX_LEARN_SRC_IMMEDIATE; /* Update 'match' to allow for satisfying destination * prerequisites. */ mf_write_subfield_value(&dst, &imm, match); /* Push value last, as this may reallocate 'spec'! */ unsigned int imm_bytes = DIV_ROUND_UP(dst.n_bits, 8); uint8_t *src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(imm_bytes)); memcpy(src_imm, &imm, imm_bytes); free(error); return NULL; } spec->src = src; if (spec->src.n_bits != spec->dst.n_bits) { return xasprintf("%s: bit widths of %s (%u) and %s (%u) " "differ", orig, name, spec->src.n_bits, value, spec->dst.n_bits); } } else { spec->src = spec->dst; } spec->src_type = NX_LEARN_SRC_FIELD; } else if (!strcmp(name, "load")) { union mf_subvalue imm; char *tail; char *dst_value = strstr(value, "->"); if (dst_value == value) { return xasprintf("%s: missing source before `->' in `%s'", name, value); } if (!dst_value) { return xasprintf("%s: missing `->' in `%s'", name, value); } if (!parse_int_string(value, imm.u8, sizeof imm.u8, (char **) &tail) && tail != value) { if (tail != dst_value) { return xasprintf("%s: garbage before `->' in `%s'", name, value); } error = learn_parse_load_immediate(&imm, dst_value + 2, value, spec, ofpacts); if (error) { return error; } } else { struct ofpact_reg_move move; error = nxm_parse_reg_move(&move, value); if (error) { return error; } spec->n_bits = move.src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->src = move.src; spec->dst_type = NX_LEARN_DST_LOAD; spec->dst = move.dst; } } else if (!strcmp(name, "output")) { error = mf_parse_subfield(&spec->src, value); if (error) { return error; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_OUTPUT; } else { return xasprintf("%s: unknown keyword %s", orig, name); } return NULL; }