/* Copies the 'n_bits' low-order bits of 'value' into the 'n_bits' bits * starting at bit 'dst_ofs' in 'dst', which is 'dst_len' bytes long. * * If you consider all of 'dst' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in dst[dst_len - 1], bit 1 is the bit with value 2, bit 2 is * the bit with value 4, ..., bit 8 is the bit with value 1 in dst[dst_len - * 2], and so on. * * Required invariants: * dst_ofs + n_bits <= dst_len * 8 * n_bits <= 64 */ void bitwise_put(uint64_t value, void *dst, unsigned int dst_len, unsigned int dst_ofs, unsigned int n_bits) { ovs_be64 n_value = htonll(value); bitwise_copy(&n_value, sizeof n_value, 0, dst, dst_len, dst_ofs, n_bits); }
/* Returns the value of the 'n_bits' bits starting at bit 'src_ofs' in 'src', * which is 'src_len' bytes long. * * If you consider all of 'src' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in src[src_len - 1], bit 1 is the bit with value 2, bit 2 is * the bit with value 4, ..., bit 8 is the bit with value 1 in src[src_len - * 2], and so on. * * Required invariants: * src_ofs + n_bits <= src_len * 8 * n_bits <= 64 */ uint64_t bitwise_get(const void *src, unsigned int src_len, unsigned int src_ofs, unsigned int n_bits) { ovs_be64 value = htonll(0); bitwise_copy(src, src_len, src_ofs, &value, sizeof value, 0, n_bits); return ntohll(value); }
static void check_bitwise_copy(void) { unsigned int n_loops; int src_ofs; int dst_ofs; int n_bits; n_loops = 0; for (n_bits = 0; n_bits <= 64; n_bits++) { for (src_ofs = 0; src_ofs < 64 - n_bits; src_ofs++) { for (dst_ofs = 0; dst_ofs < 64 - n_bits; dst_ofs++) { ovs_be64 src = htonll(random_uint64()); ovs_be64 dst = htonll(random_uint64()); ovs_be64 orig_dst = dst; ovs_be64 expect; if (n_bits == 64) { expect = dst; } else { uint64_t mask = (UINT64_C(1) << n_bits) - 1; expect = orig_dst & ~htonll(mask << dst_ofs); expect |= htonll(((ntohll(src) >> src_ofs) & mask) << dst_ofs); } bitwise_copy(&src, sizeof src, src_ofs, &dst, sizeof dst, dst_ofs, n_bits); if (expect != dst) { fprintf(stderr,"copy_bits(0x%016"PRIx64",8,%d, " "0x%016"PRIx64",8,%d, %d) yielded 0x%016"PRIx64" " "instead of the expected 0x%016"PRIx64"\n", ntohll(src), src_ofs, ntohll(orig_dst), dst_ofs, n_bits, ntohll(dst), ntohll(expect)); abort(); } n_loops++; } } } if (n_loops != sum_of_squares(64)) { abort(); } }
/* Converts 'learn' into a "struct nx_action_learn" and appends that action to * 'ofpacts'. */ void learn_to_nxast(const struct ofpact_learn *learn, struct ofpbuf *openflow) { const struct ofpact_learn_spec *spec; struct nx_action_learn *nal; size_t start_ofs; start_ofs = openflow->size; nal = ofputil_put_NXAST_LEARN(openflow); nal->idle_timeout = htons(learn->idle_timeout); nal->hard_timeout = htons(learn->hard_timeout); nal->fin_idle_timeout = htons(learn->fin_idle_timeout); nal->fin_hard_timeout = htons(learn->fin_hard_timeout); nal->priority = htons(learn->priority); nal->cookie = htonll(learn->cookie); nal->flags = htons(learn->flags); nal->table_id = learn->table_id; for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { put_u16(openflow, spec->n_bits | spec->dst_type | spec->src_type); if (spec->src_type == NX_LEARN_SRC_FIELD) { put_u32(openflow, spec->src.field->nxm_header); put_u16(openflow, spec->src.ofs); } else { size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); uint8_t *bits = ofpbuf_put_zeros(openflow, n_dst_bytes); bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0, bits, n_dst_bytes, 0, spec->n_bits); } if (spec->dst_type == NX_LEARN_DST_MATCH || spec->dst_type == NX_LEARN_DST_LOAD) { put_u32(openflow, spec->dst.field->nxm_header); put_u16(openflow, spec->dst.ofs); } } if ((openflow->size - start_ofs) % 8) { ofpbuf_put_zeros(openflow, 8 - (openflow->size - start_ofs) % 8); } nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal); nal->len = htons(openflow->size - start_ofs); }
/* 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); }
/* 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, ')'); }
/* 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; }
/* 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; }
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; }