Hi, Eelco,

Did this patch only fix the dummy-datapath?

since in the real world scenario, revalidator will only modify or delete
ukey, it will not add a megaflow into the datapath.
If we have a wider new key and narrowed old key, the reavlidator will
modify the action of the old key, and upcall_cb
will install the wider new key (without masking the key with the wider
mask, so in fact, we do not have the problem
you described here?)




Eelco Chaudron <echau...@redhat.com> 于2022年11月28日周一 16:53写道:

> The datapath supports installing wider flows, and OVS relies on
> this behavior. For example if ipv4(src=1.1.1.1/192.0.0.0,
> dst=1.1.1.2/192.0.0.0) exists, a wider flow (smaller mask) of
> ipv4(src=192.1.1.1/128.0.0.0,dst=192.1.1.2/128.0.0.0) is allowed
> to be added.
>
> However, if we try to add a wildcard rule, the installation fails:
>
> # ovs-appctl dpctl/add-flow system@myDP "in_port(1),eth_type(0x0800), \
>   ipv4(src=1.1.1.1/192.0.0.0,dst=1.1.1.2/192.0.0.0,frag=no)" 2
> # ovs-appctl dpctl/add-flow system@myDP "in_port(1),eth_type(0x0800), \
>   ipv4(src=192.1.1.1/0.0.0.0,dst=49.1.1.2/0.0.0.0,frag=no)" 2
> ovs-vswitchd: updating flow table (File exists)
>
> The reason is that the key used to determine if the flow is already
> present in the system uses the original key ANDed with the mask.
> This results in the IP address not being part of the (miniflow) key,
> i.e., being substituted with an all-zero value. When doing the actual
> lookup, this results in the key wrongfully matching the first flow,
> and therefore the flow does not get installed. The solution is to use
> the unmasked key for the existence check, the same way this is handled
> in the "slow" dpif_flow_put() case.
>
> OVS relies on the fact that overlapping flows can exist if one is a
> superset of the other. Note that this is only true when the same set
> of actions is applied. This is due to how the revalidator process
> works. During revalidation, OVS removes too generic flows from the
> datapath to avoid incorrect matches but allows too narrow flows to
> stay in the datapath to avoid the data plane disruption and also to
> avoid constant flow deletions if the datapath ignores wildcards on
> certain fields/bits.  See flow_wildcards_has_extra() check in the
> revalidate_ukey__() function.
>
> The problem here is that we have a too narrow flow installed, and now
> OpenFlow rules got changed, so the actual flow should be more generic.
> Revalidators will not remove the narrow flow, and we will eventually get
> an upcall on the packet that doesn't match the narrow flow, but we will
> not be able to install a more generic flow because after masking with
> the new wider mask, the key matches on the narrow flow, so we get EEXIST.
>
> Fixes: beb75a40fdc2 ("userspace: Switching of L3 packets in L2 pipeline")
> Signed-off-by: Eelco Chaudron <echau...@redhat.com>
>
> ---
> v3:
>   - Fixed comment text.
> v2:
>   - Updated commit message and added fixes tag.
>
>  lib/dpif-netdev.c    |   33 +++++++++++++++++++++++++++++----
>  tests/dpif-netdev.at |   14 ++++++++++++++
>  2 files changed, 43 insertions(+), 4 deletions(-)
>
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index a45b46014..be2041ce2 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -3321,6 +3321,28 @@ netdev_flow_key_init_masked(struct netdev_flow_key
> *dst,
>                              (dst_u64 - miniflow_get_values(&dst->mf)) *
> 8);
>  }
>
> +/* Initializes 'key' as a copy of 'flow'. */
> +static inline void
> +netdev_flow_key_init(struct netdev_flow_key *key,
> +                     const struct flow *flow)
> +{
> +    uint64_t *dst = miniflow_values(&key->mf);
> +    uint32_t hash = 0;
> +    uint64_t value;
> +
> +    miniflow_map_init(&key->mf, flow);
> +    miniflow_init(&key->mf, flow);
> +
> +    size_t n = dst - miniflow_get_values(&key->mf);
> +
> +    FLOW_FOR_EACH_IN_MAPS (value, flow, key->mf.map) {
> +        hash = hash_add64(hash, value);
> +    }
> +
> +    key->hash = hash_finish(hash, n * 8);
> +    key->len = netdev_flow_key_size(n);
> +}
> +
>  static inline void
>  emc_change_entry(struct emc_entry *ce, struct dp_netdev_flow *flow,
>                   const struct netdev_flow_key *key)
> @@ -4195,7 +4217,7 @@ static int
>  dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
>  {
>      struct dp_netdev *dp = get_dp_netdev(dpif);
> -    struct netdev_flow_key key, mask;
> +    struct netdev_flow_key key;
>      struct dp_netdev_pmd_thread *pmd;
>      struct match match;
>      ovs_u128 ufid;
> @@ -4244,9 +4266,12 @@ dpif_netdev_flow_put(struct dpif *dpif, const
> struct dpif_flow_put *put)
>
>      /* Must produce a netdev_flow_key for lookup.
>       * Use the same method as employed to create the key when adding
> -     * the flow to the dplcs to make sure they match. */
> -    netdev_flow_mask_init(&mask, &match);
> -    netdev_flow_key_init_masked(&key, &match.flow, &mask);
> +     * the flow to the dplcs to make sure they match.
> +     * We need to put in the unmasked key as flow_put_on_pmd() will first
> try
> +     * to see if an entry exists doing a packet type lookup. As masked-out
> +     * fields are interpreted as zeros, they could falsely match a wider
> IP
> +     * address mask. Installation of the flow will use the match
> variable. */
> +    netdev_flow_key_init(&key, &match.flow);
>
>      if (put->pmd_id == PMD_ID_NULL) {
>          if (cmap_count(&dp->poll_threads) == 0) {
> diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
> index 3179e1645..32054c52e 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -636,6 +636,20 @@ OVS_VSWITCHD_STOP(["/flow: in_port is not an exact
> match/d
>  /failed to put/d"])
>  AT_CLEANUP
>
> +AT_SETUP([dpif-netdev - check dpctl/add-flow wider ip match])
> +OVS_VSWITCHD_START(
> +  [add-port br0 p1 \
> +   -- set interface p1 type=dummy
> options:pstream=punix:$OVS_RUNDIR/p0.sock \
> +   -- set bridge br0 datapath-type=dummy])
> +
> +AT_CHECK([ovs-appctl revalidator/pause])
> +AT_CHECK([ovs-appctl dpctl/add-flow "in_port(1),eth_type(0x0800),ipv4(src=
> 0.0.0.0/192.0.0.0,dst=0.0.0.0/192.0.0.0,frag=no)" "3"])
> +AT_CHECK([ovs-appctl dpctl/add-flow "in_port(1),eth_type(0x0800),ipv4(src=
> 192.1.1.1/0.0.0.0,dst=49.1.1.1/0.0.0.0,frag=no)" "3"])
> +AT_CHECK([ovs-appctl revalidator/resume])
> +
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
>  # SEND_UDP_PKTS([p_name], [p_ofport])
>  #
>  # Sends 128 packets to port 'p_name' with different UDP destination ports.
>
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>


-- 
hepeng
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to