> On Dec 9, 2015, at 6:27 PM, Daniele Di Proietto <diproiet...@vmware.com> > wrote: > > During translation we need to unwildcard each member of the flow that we > look at. When setting or moving a field, we need to look at (and > consequently unwildcard) the field itself an all the prerequisites. > > The current code unwildcards the field and all the prerequisites and > then reads them. This behavior is wrong, because if prerequisites are > not met we might end up unwildcarding more fields than necessary, > generating masks that don't make sense. >
Good catch! > The bug can be triggered with an action that sets the tcp src port and > a fragmented IP packet. The translation will not generate a set action, > but it will match on the target field (tcp src port), even though the > prerequisites for that are not met. > > The wrong mask causes the flow to get deleted by revalidation. > > Fix this by introducing mf_check_prereqs_and_set_mask(), which checks > the prerequisites while masking the field. It is based on the behavior > of mf_are_prereqs_ok(). > > mf_mask_field_and_prereqs() is not used anymore, so it can be removed. > mf_are_prereqs_ok() can be rewritten using > mf_check_prereqs_and_set_mask(), avoiding code duplication > > Signed-off-by: Daniele Di Proietto <diproiet...@vmware.com> > --- > lib/flow.h | 38 +++++++++++++-- > lib/meta-flow.c | 113 +++++++++++++++++++------------------------ > lib/meta-flow.h | 5 +- > lib/nx-match.c | 10 ++-- > ofproto/ofproto-dpif-xlate.c | 4 +- > 5 files changed, 92 insertions(+), 78 deletions(-) > > diff --git a/lib/flow.h b/lib/flow.h > index 5d78615..6f99297 100644 > --- a/lib/flow.h > +++ b/lib/flow.h > @@ -999,21 +999,49 @@ pkt_metadata_from_flow(struct pkt_metadata *md, const > struct flow *flow) > md->ct_label = flow->ct_label; > } > > +/* Often, during translation we need to read a value from a flow and > + * unwildcard the corresponding bits in the wildcards. */ > + > +#define FLOW_WC_GET_AND_MASK(FLOW, WC, FIELD) \ > + (((WC) ? WC_MASK_FIELD(WC, FIELD) : NULL), (FLOW)->FIELD) > + > +#define FLOW_WC_GET_AND_MASK_MASKED(FLOW, WC, FIELD, MASK) \ > + (((WC) ? WC_MASK_FIELD_MASK(WC, FIELD, MASK) : 0), \ > + (((FLOW)->FIELD) & (MASK))) > + These would be better named as FLOW_GET_AND_MASK_WC{_MASKED}(). Now the names see like they would return a field from the WC. > +static inline bool check_and_mask_ip_any(const struct flow *flow, > + struct flow_wildcards *wc) > +{ > + return dl_type_is_ip_any(FLOW_WC_GET_AND_MASK(flow, wc, dl_type)); > +} While correct, masking here unnecessary, as dl_type is always unwildcarded in the beginning of xlate_wc_init(). We also rely on that everywhere, so we might do it here as well. The reasoning for this is that dl_type is always read and in so many places that it is better to mask it unconditionally. Given this the callers can keep using dl_type_is_ip_any() directly. > + > +static inline bool check_and_mask_icmpv4(const struct flow *flow, > + struct flow_wildcards *wc) > +{ > + return (FLOW_WC_GET_AND_MASK(flow, wc, dl_type) == htons(ETH_TYPE_IP) ditto. > + && FLOW_WC_GET_AND_MASK(flow, wc, nw_proto) == IPPROTO_ICMP); > +} > + > +static inline bool check_and_mask_icmpv6(const struct flow *flow, > + struct flow_wildcards *wc) > +{ > + return (FLOW_WC_GET_AND_MASK(flow, wc, dl_type) == htons(ETH_TYPE_IPV6) ditto. > + && FLOW_WC_GET_AND_MASK(flow, wc, nw_proto) == IPPROTO_ICMPV6); > +} > + > static inline bool is_ip_any(const struct flow *flow) > { > - return dl_type_is_ip_any(flow->dl_type); > + return check_and_mask_ip_any(flow, NULL); Given the above this change is not needed. > } > > static inline bool is_icmpv4(const struct flow *flow) > { > - return (flow->dl_type == htons(ETH_TYPE_IP) > - && flow->nw_proto == IPPROTO_ICMP); > + return check_and_mask_icmpv4(flow, NULL); Given that there is no mask I would not change this either. > } > > static inline bool is_icmpv6(const struct flow *flow) > { > - return (flow->dl_type == htons(ETH_TYPE_IPV6) > - && flow->nw_proto == IPPROTO_ICMPV6); > + return check_and_mask_icmpv6(flow, NULL); Nor this. > } > > static inline bool is_igmp(const struct flow *flow) > diff --git a/lib/meta-flow.c b/lib/meta-flow.c > index 6bd0b99..398b2f2 100644 > --- a/lib/meta-flow.c > +++ b/lib/meta-flow.c > @@ -356,94 +356,79 @@ mf_is_mask_valid(const struct mf_field *mf, const union > mf_value *mask) > bool > mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow) > { > + return mf_check_prereqs_and_set_mask(mf, flow, NULL); > +} > + > +/* Returns true if 'flow' meets the prerequisites for 'mf', false otherwise. > + * If an attribute is read from 'flow', the corresponding attribute in 'wc' > + * will be unwildcarded. */ > +bool > +mf_check_prereqs_and_set_mask(const struct mf_field *mf, > + const struct flow *flow, > + struct flow_wildcards *wc) > +{ > switch (mf->prereqs) { > case MFP_NONE: > return true; > > case MFP_ARP: > - return (flow->dl_type == htons(ETH_TYPE_ARP) || > - flow->dl_type == htons(ETH_TYPE_RARP)); > + return FLOW_WC_GET_AND_MASK(flow, wc, dl_type) == htons(ETH_TYPE_ARP) > + || FLOW_WC_GET_AND_MASK(flow, wc, dl_type) == > htons(ETH_TYPE_RARP); No change needed. > case MFP_IPV4: > - return flow->dl_type == htons(ETH_TYPE_IP); > + return FLOW_WC_GET_AND_MASK(flow, wc, dl_type) == htons(ETH_TYPE_IP); No change needed. > case MFP_IPV6: > - return flow->dl_type == htons(ETH_TYPE_IPV6); > + return FLOW_WC_GET_AND_MASK(flow, wc, dl_type) == > htons(ETH_TYPE_IPV6); No change needed. > case MFP_VLAN_VID: > - return (flow->vlan_tci & htons(VLAN_CFI)) != 0; > + return FLOW_WC_GET_AND_MASK_MASKED(flow, wc, > + vlan_tci, htons(VLAN_CFI)) != 0; > case MFP_MPLS: > - return eth_type_mpls(flow->dl_type); > + return eth_type_mpls(FLOW_WC_GET_AND_MASK(flow, wc, dl_type)); No change needed. > case MFP_IP_ANY: > - return is_ip_any(flow); > - > + return check_and_mask_ip_any(flow, wc); No change needed here. > case MFP_TCP: > - return is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP > - && !(flow->nw_frag & FLOW_NW_FRAG_LATER); > + return check_and_mask_ip_any(flow, wc) This need not be changed. > + && FLOW_WC_GET_AND_MASK(flow, wc, nw_proto) == IPPROTO_TCP > + && !FLOW_WC_GET_AND_MASK_MASKED(flow, wc, nw_frag, > + FLOW_NW_FRAG_LATER); Is the order between these now significant? > case MFP_UDP: > - return is_ip_any(flow) && flow->nw_proto == IPPROTO_UDP > - && !(flow->nw_frag & FLOW_NW_FRAG_LATER); > + return check_and_mask_ip_any(flow, wc) > + && FLOW_WC_GET_AND_MASK(flow, wc, nw_proto) == IPPROTO_UDP > + && !FLOW_WC_GET_AND_MASK_MASKED(flow, wc, nw_frag, > + FLOW_NW_FRAG_LATER); > case MFP_SCTP: > - return is_ip_any(flow) && flow->nw_proto == IPPROTO_SCTP > - && !(flow->nw_frag & FLOW_NW_FRAG_LATER); > + return check_and_mask_ip_any(flow, wc) > + && FLOW_WC_GET_AND_MASK(flow, wc, nw_proto) == IPPROTO_SCTP > + && !FLOW_WC_GET_AND_MASK_MASKED(flow, wc, nw_frag, > + FLOW_NW_FRAG_LATER); > case MFP_ICMPV4: > - return is_icmpv4(flow); > + return check_and_mask_icmpv4(flow, wc); > case MFP_ICMPV6: > - return is_icmpv6(flow); > + return check_and_mask_icmpv6(flow, wc); > > case MFP_ND: > - return (is_icmpv6(flow) > - && flow->tp_dst == htons(0) > - && (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) || > - flow->tp_src == htons(ND_NEIGHBOR_ADVERT))); > + return check_and_mask_icmpv6(flow, wc) > + && FLOW_WC_GET_AND_MASK(flow, wc, tp_dst) == htons(0) > + && (FLOW_WC_GET_AND_MASK(flow, wc, tp_src) > + == htons(ND_NEIGHBOR_SOLICIT) > + || FLOW_WC_GET_AND_MASK(flow, wc, tp_src) > + == htons(ND_NEIGHBOR_ADVERT)); Same here, is the order for checking and masking tp_dst and tp_src significant? > case MFP_ND_SOLICIT: > - return (is_icmpv6(flow) > - && flow->tp_dst == htons(0) > - && (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT))); > + return check_and_mask_icmpv6(flow, wc) > + && FLOW_WC_GET_AND_MASK(flow, wc, tp_dst) > + == htons(0) > + && FLOW_WC_GET_AND_MASK(flow, wc, tp_src) > + == htons(ND_NEIGHBOR_SOLICIT); > case MFP_ND_ADVERT: > - return (is_icmpv6(flow) > - && flow->tp_dst == htons(0) > - && (flow->tp_src == htons(ND_NEIGHBOR_ADVERT))); > + return check_and_mask_icmpv6(flow, wc) > + && FLOW_WC_GET_AND_MASK(flow, wc, tp_dst) > + == htons(0) > + && FLOW_WC_GET_AND_MASK(flow, wc, tp_src) > + == htons(ND_NEIGHBOR_ADVERT); > } > > OVS_NOT_REACHED(); > } > > -/* Set field and it's prerequisities in the mask. > - * This is only ever called for writeable 'mf's, but we do not make the > - * distinction here. */ > -void > -mf_mask_field_and_prereqs(const struct mf_field *mf, struct flow_wildcards > *wc) > -{ > - mf_set_flow_value(mf, &exact_match_mask, &wc->masks); > - > - switch (mf->prereqs) { > - case MFP_ND: > - case MFP_ND_SOLICIT: > - case MFP_ND_ADVERT: > - WC_MASK_FIELD(wc, tp_src); > - WC_MASK_FIELD(wc, tp_dst); > - /* Fall through. */ > - case MFP_TCP: > - case MFP_UDP: > - case MFP_SCTP: > - case MFP_ICMPV4: > - case MFP_ICMPV6: > - /* nw_frag always unwildcarded. */ > - WC_MASK_FIELD(wc, nw_proto); > - /* Fall through. */ > - case MFP_ARP: > - case MFP_IPV4: > - case MFP_IPV6: > - case MFP_MPLS: > - case MFP_IP_ANY: > - /* dl_type always unwildcarded. */ > - break; > - case MFP_VLAN_VID: > - WC_MASK_FIELD_MASK(wc, vlan_tci, htons(VLAN_CFI)); > - break; > - case MFP_NONE: > - break; > - } > -} > - > /* Set bits of 'bm' corresponding to the field 'mf' and it's prerequisities. > */ > void > mf_bitmap_set_field_and_prereqs(const struct mf_field *mf, struct mf_bitmap > *bm) > diff --git a/lib/meta-flow.h b/lib/meta-flow.h > index 71c238d..1e6055b 100644 > --- a/lib/meta-flow.h > +++ b/lib/meta-flow.h > @@ -1967,8 +1967,9 @@ void mf_get_mask(const struct mf_field *, const struct > flow_wildcards *, > > /* Prerequisites. */ > bool mf_are_prereqs_ok(const struct mf_field *, const struct flow *); > -void mf_mask_field_and_prereqs(const struct mf_field *, > - struct flow_wildcards *); > +bool mf_check_prereqs_and_set_mask(const struct mf_field *mf, > + const struct flow *flow, > + struct flow_wildcards *wc); > void mf_bitmap_set_field_and_prereqs(const struct mf_field *mf, struct > mf_bitmap *bm); > > diff --git a/lib/nx-match.c b/lib/nx-match.c > index 11bcd95..f56c9e6 100644 > --- a/lib/nx-match.c > +++ b/lib/nx-match.c > @@ -1605,13 +1605,13 @@ nxm_execute_reg_move(const struct ofpact_reg_move > *move, > union mf_value src_value; > union mf_value dst_value; > > - mf_mask_field_and_prereqs(move->dst.field, wc); > - mf_mask_field_and_prereqs(move->src.field, wc); > - > /* A flow may wildcard nw_frag. Do nothing if setting a transport > * header field on a packet that does not have them. */ > - if (mf_are_prereqs_ok(move->dst.field, flow) > - && mf_are_prereqs_ok(move->src.field, flow)) { > + if (mf_check_prereqs_and_set_mask(move->dst.field, flow, wc) > + && mf_check_prereqs_and_set_mask(move->src.field, flow, wc)) { Would checking and masking in the opposite order make a difference? > + > + mf_mask_field(move->dst.field, &wc->masks); > + mf_mask_field(move->src.field, &wc->masks); > > mf_get_value(move->dst.field, flow, &dst_value); > mf_get_value(move->src.field, flow, &src_value); > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > index dab64b9..08807bc 100644 > --- a/ofproto/ofproto-dpif-xlate.c > +++ b/ofproto/ofproto-dpif-xlate.c > @@ -4594,8 +4594,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t > ofpacts_len, > } > /* A flow may wildcard nw_frag. Do nothing if setting a transport > * header field on a packet that does not have them. */ > - mf_mask_field_and_prereqs(mf, wc); > - if (mf_are_prereqs_ok(mf, flow)) { > + if (mf_check_prereqs_and_set_mask(mf, flow, wc)) { > + mf_mask_field(mf, &wc->masks); > mf_set_flow_value_masked(mf, &set_field->value, > &set_field->mask, flow); > } > -- > 2.1.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