Thanks! I applied this to master.
On Mon, Oct 06, 2014 at 01:24:17PM -0700, Jarno Rajahalme wrote: > LGTM, > > Acked-by: Jarno Rajahalme <jrajaha...@nicira.com> > > On Sep 30, 2014, at 5:47 PM, Ben Pfaff <b...@nicira.com> wrote: > > > Some of the Nicira extension actions include fixed-size 32-bit members that > > designate NXM fields. These actions can't accommodate 64-bit experimenter > > OXMs, so we need to figure out some kind of solution. This commit does > > that, in different ways for different actions. > > > > For some actions, I did not think it was worthwhile to worry about > > experimenter OXM, so I just disabled use of them. This is what I did for > > bundle, learn, and multipath actions. > > > > Other actions could be gracefully reinterpreted to support experimenter > > OXM. This is true of reg_move, which use NXM headers only at the end of > > the action and such that using an experimenter OXM would make the action > > longer (which unambigously signals to older OVS that the action is an > > error, which is desired behavior since older OVS cannot interpret this > > action). The stack push and pop actions are also in this category. > > > > reg_load was the most frustrating case. In OpenFlow 1.5 we had already > > eliminated this action in favor of OF1.5+ set_field. In other OpenFlow > > versions, though, reg_load is more powerful than set_field because it > > can modify partial fields. This commit therefore adds a new variant of > > reg_load, called reg_load2, which is simply OF1.5+ set_field with a Nicira > > extension header on it. > > > > Signed-off-by: Ben Pfaff <b...@nicira.com> > > --- > > lib/bundle.c | 5 + > > lib/learn.c | 8 ++ > > lib/multipath.c | 4 + > > lib/ofp-actions.c | 263 > > ++++++++++++++++++++++++++++++++++++++++++++--------- > > 4 files changed, 236 insertions(+), 44 deletions(-) > > > > diff --git a/lib/bundle.c b/lib/bundle.c > > index a514ebb..793eb82 100644 > > --- a/lib/bundle.c > > +++ b/lib/bundle.c > > @@ -207,6 +207,11 @@ bundle_parse__(const char *s, char **save_ptr, > > if (error) { > > return error; > > } > > + > > + if (!mf_nxm_header(bundle->dst.field->id)) { > > + return xasprintf("%s: experimenter OXM field '%s' not > > supported", > > + s, dst); > > + } > > } > > > > return NULL; > > diff --git a/lib/learn.c b/lib/learn.c > > index c04e1f3..e93015c 100644 > > --- a/lib/learn.c > > +++ b/lib/learn.c > > @@ -221,6 +221,10 @@ learn_parse_load_immediate(const char *s, struct > > ofpact_learn_spec *spec) > > if (error) { > > return error; > > } > > + if (!mf_nxm_header(dst.field->id)) { > > + return xasprintf("%s: experimenter OXM field '%s' not supported", > > + full_s, s); > > + } > > > > if (!bitwise_is_all_zeros(&imm, sizeof imm, dst.n_bits, > > (8 * sizeof imm) - dst.n_bits)) { > > @@ -269,6 +273,10 @@ learn_parse_spec(const char *orig, char *name, char > > *value, > > if (error) { > > return error; > > } > > + if (!mf_nxm_header(spec->dst.field->id)) { > > + return xasprintf("%s: experimenter OXM field '%s' not > > supported", > > + orig, name); > > + } > > > > /* Parse source and check prerequisites. */ > > if (value[0] != '\0') { > > diff --git a/lib/multipath.c b/lib/multipath.c > > index 8983c6a..edd295f 100644 > > --- a/lib/multipath.c > > +++ b/lib/multipath.c > > @@ -189,6 +189,10 @@ multipath_parse__(struct ofpact_multipath *mp, const > > char *s_, char *s) > > if (error) { > > return error; > > } > > + if (!mf_nxm_header(mp->dst.field->id)) { > > + return xasprintf("%s: experimenter OXM field '%s' not supported", > > + s, dst); > > + } > > if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) { > > return xasprintf("%s: %d-bit destination field has %u possible " > > "values, less than specified n_links %d", > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c > > index a02dd22..4424fe2 100644 > > --- a/lib/ofp-actions.c > > +++ b/lib/ofp-actions.c > > @@ -210,10 +210,15 @@ enum ofp_raw_action_type { > > * [In OpenFlow 1.5, set_field is a superset of reg_load functionality, > > so > > * we drop reg_load.] */ > > NXAST_RAW_REG_LOAD, > > + /* NX1.0-1.4(33): struct nx_action_reg_load2, ... > > + * > > + * [In OpenFlow 1.5, set_field is a superset of reg_load2 > > functionality, so > > + * we drop reg_load2.] */ > > + NXAST_RAW_REG_LOAD2, > > > > /* OF1.5+(28): struct ofp15_action_copy_field, ... */ > > OFPAT_RAW15_COPY_FIELD, > > - /* NX1.0-1.4(6): struct nx_action_reg_move. */ > > + /* NX1.0-1.4(6): struct nx_action_reg_move, ... */ > > NXAST_RAW_REG_MOVE, > > > > /* ## ------------------------- ## */ > > @@ -248,6 +253,8 @@ enum ofp_raw_action_type { > > > > /* NX1.0+(15): struct nx_action_output_reg. */ > > NXAST_RAW_OUTPUT_REG, > > + /* NX1.0+(32): struct nx_action_output_reg2. */ > > + NXAST_RAW_OUTPUT_REG2, > > > > /* NX1.0+(16): struct nx_action_learn, ... */ > > NXAST_RAW_LEARN, > > @@ -708,6 +715,27 @@ struct nx_action_output_reg { > > }; > > OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24); > > > > +/* Action structure for NXAST_OUTPUT_REG2. > > + * > > + * Like the NXAST_OUTPUT_REG but organized so that there is room for a > > 64-bit > > + * experimenter OXM as 'src'. > > + */ > > +struct nx_action_output_reg2 { > > + ovs_be16 type; /* OFPAT_VENDOR. */ > > + ovs_be16 len; /* 24. */ > > + ovs_be32 vendor; /* NX_VENDOR_ID. */ > > + ovs_be16 subtype; /* NXAST_OUTPUT_REG2. */ > > + > > + ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ > > + ovs_be16 max_len; /* Max length to send to controller. */ > > + > > + /* Followed by: > > + * - 'src', as an OXM/NXM header (either 4 or 8 bytes). > > + * - Enough 0-bytes to pad the action out to 24 bytes. */ > > + uint8_t pad[10]; > > +}; > > +OFP_ASSERT(sizeof(struct nx_action_output_reg2) == 24); > > + > > static enum ofperr > > decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor, > > struct ofpbuf *out) > > @@ -719,6 +747,7 @@ decode_NXAST_RAW_OUTPUT_REG(const struct > > nx_action_output_reg *naor, > > } > > > > output_reg = ofpact_put_OUTPUT_REG(out); > > + output_reg->ofpact.raw = NXAST_RAW_OUTPUT_REG; > > output_reg->src.field = mf_from_nxm_header(ntohl(naor->src)); > > output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits); > > output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits); > > @@ -727,17 +756,61 @@ decode_NXAST_RAW_OUTPUT_REG(const struct > > nx_action_output_reg *naor, > > return mf_check_src(&output_reg->src, NULL); > > } > > > > +static enum ofperr > > +decode_NXAST_RAW_OUTPUT_REG2(const struct nx_action_output_reg2 *naor, > > + struct ofpbuf *out) > > +{ > > + struct ofpact_output_reg *output_reg; > > + enum ofperr error; > > + struct ofpbuf b; > > + > > + output_reg = ofpact_put_OUTPUT_REG(out); > > + output_reg->ofpact.raw = NXAST_RAW_OUTPUT_REG2; > > + output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits); > > + output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits); > > + output_reg->max_len = ntohs(naor->max_len); > > + > > + ofpbuf_use_const(&b, naor, ntohs(naor->len)); > > + ofpbuf_pull(&b, OBJECT_OFFSETOF(naor, pad)); > > + error = nx_pull_header(&b, &output_reg->src.field, NULL); > > + if (error) { > > + return error; > > + } > > + if (!is_all_zeros(ofpbuf_data(&b), ofpbuf_size(&b))) { > > + return OFPERR_NXBRC_MUST_BE_ZERO; > > + } > > + > > + return mf_check_src(&output_reg->src, NULL); > > +} > > + > > static void > > encode_OUTPUT_REG(const struct ofpact_output_reg *output_reg, > > enum ofp_version ofp_version OVS_UNUSED, > > struct ofpbuf *out) > > { > > - struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out); > > + /* If 'output_reg' came in as an NXAST_RAW_OUTPUT_REG2 action, or if it > > + * cannot be encoded in the older form, encode it as > > + * NXAST_RAW_OUTPUT_REG2. */ > > + if (output_reg->ofpact.raw == NXAST_RAW_OUTPUT_REG2 > > + || !mf_nxm_header(output_reg->src.field->id)) { > > + struct nx_action_output_reg2 *naor = put_NXAST_OUTPUT_REG2(out); > > + size_t size = ofpbuf_size(out); > > + > > + naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, > > + output_reg->src.n_bits); > > + naor->max_len = htons(output_reg->max_len); > > + > > + ofpbuf_set_size(out, size - sizeof naor->pad); > > + nx_put_header(out, output_reg->src.field->id, 0, false); > > + ofpbuf_set_size(out, size); > > + } else { > > + struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out); > > > > - naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, > > - output_reg->src.n_bits); > > - naor->src = htonl(mf_nxm_header(output_reg->src.field->id)); > > - naor->max_len = htons(output_reg->max_len); > > + naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, > > + output_reg->src.n_bits); > > + naor->src = htonl(mf_nxm_header(output_reg->src.field->id)); > > + naor->max_len = htons(output_reg->max_len); > > + } > > } > > > > static char * WARN_UNUSED_RESULT > > @@ -1747,10 +1820,12 @@ struct nx_action_reg_move { > > ovs_be16 n_bits; /* Number of bits. */ > > ovs_be16 src_ofs; /* Starting bit offset in source. */ > > ovs_be16 dst_ofs; /* Starting bit offset in destination. > > */ > > - ovs_be32 src; /* Source register. */ > > - ovs_be32 dst; /* Destination register. */ > > + /* Followed by: > > + * - OXM/NXM header for source field (4 or 8 bytes). > > + * - OXM/NXM header for destination field (4 or 8 bytes). > > + * - Padding with 0-bytes to a multiple of 8 bytes, if necessary. */ > > }; > > -OFP_ASSERT(sizeof(struct nx_action_reg_move) == 24); > > +OFP_ASSERT(sizeof(struct nx_action_reg_move) == 16); > > > > static enum ofperr > > decode_OFPAT_RAW15_COPY_FIELD(const struct ofp15_action_copy_field *oacf, > > @@ -1794,15 +1869,29 @@ decode_NXAST_RAW_REG_MOVE(const struct > > nx_action_reg_move *narm, > > struct ofpbuf *ofpacts) > > { > > struct ofpact_reg_move *move; > > + enum ofperr error; > > + struct ofpbuf b; > > > > move = ofpact_put_REG_MOVE(ofpacts); > > - move->src.field = mf_from_nxm_header(ntohl(narm->src)); > > move->src.ofs = ntohs(narm->src_ofs); > > move->src.n_bits = ntohs(narm->n_bits); > > - move->dst.field = mf_from_nxm_header(ntohl(narm->dst)); > > move->dst.ofs = ntohs(narm->dst_ofs); > > move->dst.n_bits = ntohs(narm->n_bits); > > > > + ofpbuf_use_const(&b, narm, ntohs(narm->len)); > > + ofpbuf_pull(&b, sizeof *narm); > > + error = nx_pull_header(&b, &move->src.field, NULL); > > + if (error) { > > + return error; > > + } > > + error = nx_pull_header(&b, &move->dst.field, NULL); > > + if (error) { > > + return error; > > + } > > + if (!is_all_zeros(ofpbuf_data(&b), ofpbuf_size(&b))) { > > + return OFPERR_NXBRC_MUST_BE_ZERO; > > + } > > + > > return nxm_reg_move_check(move, NULL); > > } > > > > @@ -1810,11 +1899,9 @@ static void > > encode_REG_MOVE(const struct ofpact_reg_move *move, > > enum ofp_version ofp_version, struct ofpbuf *out) > > { > > + size_t start_ofs = ofpbuf_size(out); > > if (ofp_version >= OFP15_VERSION) { > > - struct ofp15_action_copy_field *copy; > > - size_t start_ofs = ofpbuf_size(out); > > - > > - copy = put_OFPAT15_COPY_FIELD(out); > > + struct ofp15_action_copy_field *copy = put_OFPAT15_COPY_FIELD(out); > > copy->n_bits = htons(move->dst.n_bits); > > copy->src_offset = htons(move->src.ofs); > > copy->dst_offset = htons(move->dst.ofs); > > @@ -1822,17 +1909,15 @@ encode_REG_MOVE(const struct ofpact_reg_move *move, > > ofpbuf_set_size(out, ofpbuf_size(out) - sizeof copy->pad); > > nx_put_header(out, move->src.field->id, ofp_version, false); > > nx_put_header(out, move->dst.field->id, ofp_version, false); > > - pad_ofpat(out, start_ofs); > > } else { > > - struct nx_action_reg_move *narm; > > - > > - narm = put_NXAST_REG_MOVE(out); > > + struct nx_action_reg_move *narm = put_NXAST_REG_MOVE(out); > > narm->n_bits = htons(move->dst.n_bits); > > narm->src_ofs = htons(move->src.ofs); > > narm->dst_ofs = htons(move->dst.ofs); > > - narm->src = htonl(mf_nxm_header(move->src.field->id)); > > - narm->dst = htonl(mf_nxm_header(move->dst.field->id)); > > + nx_put_header(out, move->src.field->id, 0, false); > > + nx_put_header(out, move->dst.field->id, 0, false); > > } > > + pad_ofpat(out, start_ofs); > > } > > > > static char * WARN_UNUSED_RESULT > > @@ -1921,6 +2006,26 @@ struct nx_action_reg_load { > > }; > > OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24); > > > > +/* Action structure for NXAST_REG_LOAD2. > > + * > > + * Compared to OFPAT_SET_FIELD, we can use this to set whole or partial > > fields > > + * in any OpenFlow version. Compared to NXAST_REG_LOAD, we can use this > > to set > > + * OXM experimenter fields. */ > > +struct nx_action_reg_load2 { > > + ovs_be16 type; /* OFPAT_VENDOR. */ > > + ovs_be16 len; /* At least 16. */ > > + ovs_be32 vendor; /* NX_VENDOR_ID. */ > > + ovs_be16 subtype; /* NXAST_SET_FIELD. */ > > + > > + /* Followed by: > > + * - An NXM/OXM header, value, and optionally a mask. > > + * - Enough 0-bytes to pad out to a multiple of 64 bits. > > + * > > + * The "pad" member is the beginning of the above. */ > > + uint8_t pad[6]; > > +}; > > +OFP_ASSERT(sizeof(struct nx_action_reg_load2) == 16); > > + > > static enum ofperr > > decode_ofpat_set_field(const struct ofp12_action_set_field *oasf, > > bool may_mask, struct ofpbuf *ofpacts) > > @@ -2027,6 +2132,35 @@ decode_NXAST_RAW_REG_LOAD(const struct > > nx_action_reg_load *narl, > > return 0; > > } > > > > +static enum ofperr > > +decode_NXAST_RAW_REG_LOAD2(const struct nx_action_reg_load2 *narl, > > + struct ofpbuf *out) > > +{ > > + struct ofpact_set_field *sf; > > + enum ofperr error; > > + struct ofpbuf b; > > + > > + sf = ofpact_put_SET_FIELD(out); > > + sf->ofpact.raw = NXAST_RAW_REG_LOAD2; > > + > > + ofpbuf_use_const(&b, narl, ntohs(narl->len)); > > + ofpbuf_pull(&b, OBJECT_OFFSETOF(narl, pad)); > > + error = nx_pull_entry(&b, &sf->field, &sf->value, &sf->mask); > > + if (error) { > > + return error; > > + } > > + if (!is_all_zeros(ofpbuf_data(&b), ofpbuf_size(&b))) { > > + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; > > + } > > + > > + if (!sf->field->writable) { > > + VLOG_WARN_RL(&rl, "destination field %s is not writable", > > + sf->field->name); > > + return OFPERR_OFPBAC_BAD_SET_ARGUMENT; > > + } > > + return 0; > > +} > > + > > static void > > ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version ofp_version, > > enum mf_field_id field, uint64_t value_) > > @@ -2068,15 +2202,30 @@ next_load_segment(const struct ofpact_set_field *sf, > > static void > > set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf > > *openflow) > > { > > - struct mf_subfield dst; > > - uint64_t value; > > + /* If 'sf' cannot be encoded as NXAST_REG_LOAD because it requires an > > + * experimenter OXM (or if it came in as NXAST_REG_LOAD2), encode as > > + * NXAST_REG_LOAD2. Otherwise use NXAST_REG_LOAD, which is backward > > + * compatible. */ > > + if (sf->ofpact.raw == NXAST_RAW_REG_LOAD2 > > + || !mf_nxm_header(sf->field->id)) { > > + struct nx_action_reg_load2 *narl OVS_UNUSED; > > + size_t start_ofs = ofpbuf_size(openflow); > > + > > + narl = put_NXAST_REG_LOAD2(openflow); > > + ofpbuf_set_size(openflow, ofpbuf_size(openflow) - sizeof > > narl->pad); > > + nx_put_entry(openflow, sf->field->id, 0, &sf->value, &sf->mask); > > + pad_ofpat(openflow, start_ofs); > > + } else { > > + struct mf_subfield dst; > > + uint64_t value; > > > > - dst.ofs = dst.n_bits = 0; > > - while (next_load_segment(sf, &dst, &value)) { > > - struct nx_action_reg_load *narl = put_NXAST_REG_LOAD(openflow); > > - narl->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits); > > - narl->dst = htonl(mf_nxm_header(dst.field->id)); > > - narl->value = htonll(value); > > + dst.ofs = dst.n_bits = 0; > > + while (next_load_segment(sf, &dst, &value)) { > > + struct nx_action_reg_load *narl = put_NXAST_REG_LOAD(openflow); > > + narl->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits); > > + narl->dst = htonl(mf_nxm_header(dst.field->id)); > > + narl->value = htonll(value); > > + } > > } > > } > > > > @@ -2219,10 +2368,12 @@ encode_SET_FIELD(const struct ofpact_set_field *sf, > > enum ofp_version ofp_version, struct ofpbuf *out) > > { > > if (ofp_version >= OFP15_VERSION) { > > - /* OF1.5+ only has Set-Field (we drop NXAST_REG_LOAD entirely). */ > > + /* OF1.5+ only has Set-Field (reg_load is redundant so we drop it > > + * entirely). */ > > set_field_to_set_field(sf, ofp_version, out); > > - } else if (sf->ofpact.raw == NXAST_RAW_REG_LOAD) { > > - /* It came in as NXAST_REG_LOAD, send it out the same way. */ > > + } else if (sf->ofpact.raw == NXAST_RAW_REG_LOAD || > > + sf->ofpact.raw == NXAST_RAW_REG_LOAD2) { > > + /* It came in as reg_load, send it out the same way. */ > > set_field_to_nxast(sf, out); > > } else if (ofp_version < OFP12_VERSION) { > > /* OpenFlow 1.0 and 1.1 don't have Set-Field. */ > > @@ -2378,19 +2529,36 @@ struct nx_action_stack { > > ovs_be32 vendor; /* NX_VENDOR_ID. */ > > ovs_be16 subtype; /* NXAST_STACK_PUSH or NXAST_STACK_POP. > > */ > > ovs_be16 offset; /* Bit offset into the field. */ > > - ovs_be32 field; /* The field used for push or pop. */ > > - ovs_be16 n_bits; /* (n_bits + 1) bits of the field. */ > > - uint8_t zero[6]; /* Reserved, must be zero. */ > > + /* Followed by: > > + * - OXM/NXM header for field to push or pop (4 or 8 bytes). > > + * - ovs_be16 'n_bits', the number of bits to extract from the field. > > + * - Enough 0-bytes to pad out the action to 24 bytes. */ > > + uint8_t pad[12]; /* See above. */ > > }; > > OFP_ASSERT(sizeof(struct nx_action_stack) == 24); > > > > -static void > > +static enum ofperr > > decode_stack_action(const struct nx_action_stack *nasp, > > struct ofpact_stack *stack_action) > > { > > - stack_action->subfield.field = mf_from_nxm_header(ntohl(nasp->field)); > > + enum ofperr error; > > + struct ofpbuf b; > > + > > stack_action->subfield.ofs = ntohs(nasp->offset); > > - stack_action->subfield.n_bits = ntohs(nasp->n_bits); > > + > > + ofpbuf_use_const(&b, nasp, sizeof *nasp); > > + ofpbuf_pull(&b, OBJECT_OFFSETOF(nasp, pad)); > > + error = nx_pull_header(&b, &stack_action->subfield.field, NULL); > > + if (error) { > > + return error; > > + } > > + stack_action->subfield.n_bits = ntohs(*(const ovs_be16 *) > > ofpbuf_data(&b)); > > + ofpbuf_pull(&b, 2); > > + if (!is_all_zeros(ofpbuf_data(&b), ofpbuf_size(&b))) { > > + return OFPERR_NXBRC_MUST_BE_ZERO; > > + } > > + > > + return 0; > > } > > > > static enum ofperr > > @@ -2398,8 +2566,8 @@ decode_NXAST_RAW_STACK_PUSH(const struct > > nx_action_stack *nasp, > > struct ofpbuf *ofpacts) > > { > > struct ofpact_stack *push = ofpact_put_STACK_PUSH(ofpacts); > > - decode_stack_action(nasp, push); > > - return nxm_stack_push_check(push, NULL); > > + enum ofperr error = decode_stack_action(nasp, push); > > + return error ? error : nxm_stack_push_check(push, NULL); > > } > > > > static enum ofperr > > @@ -2407,17 +2575,24 @@ decode_NXAST_RAW_STACK_POP(const struct > > nx_action_stack *nasp, > > struct ofpbuf *ofpacts) > > { > > struct ofpact_stack *pop = ofpact_put_STACK_POP(ofpacts); > > - decode_stack_action(nasp, pop); > > - return nxm_stack_pop_check(pop, NULL); > > + enum ofperr error = decode_stack_action(nasp, pop); > > + return error ? error : nxm_stack_pop_check(pop, NULL); > > } > > > > static void > > encode_STACK_op(const struct ofpact_stack *stack_action, > > struct nx_action_stack *nasp) > > { > > + struct ofpbuf b; > > + ovs_be16 n_bits; > > + > > nasp->offset = htons(stack_action->subfield.ofs); > > - nasp->n_bits = htons(stack_action->subfield.n_bits); > > - nasp->field = htonl(mf_nxm_header(stack_action->subfield.field->id)); > > + > > + ofpbuf_use_stack(&b, nasp, ntohs(nasp->len)); > > + ofpbuf_put_uninit(&b, OBJECT_OFFSETOF(nasp, pad)); > > + nx_put_header(&b, stack_action->subfield.field->id, 0, false); > > + n_bits = htons(stack_action->subfield.n_bits); > > + ofpbuf_put(&b, &n_bits, sizeof n_bits); > > } > > > > static void > > -- > > 1.7.10.4 > > > > _______________________________________________ > > dev mailing list > > dev@openvswitch.org > > http://openvswitch.org/mailman/listinfo/dev > _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev