diff mbox series

[ovs-dev,v3,3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()

Message ID 20180109185433.23945-4-zoltan.balogh@ericsson.com
State Changes Requested
Headers show
Series Fix tunnel neighbor cache population | expand

Commit Message

Zoltan Balogh Jan. 9, 2018, 6:54 p.m. UTC
Currenlty, OVS snoops any ARP or ND packets in any bridge and populates
the tunnel neighbor cache with the retreived data. For instance, when
ARP reply originated by a tenant is received in an overlay bridge, the
ARP message is snooped and tunnel neighbor cache is filled with tenant
data, however only tunnel neighbor data should be stored there.

This patch moves tunnel neighbor snooping from do_xlate_actions() to
terminate_native_tunnel() where native tunnel is terminated, in order to
keep ARP neighbor cache clean, i.e. only packets comming from a native
tunnel are snooped.

By applying the patch, only ARP and Neighbor Advertisement messages
addressing a tunnel endpoint (LOCAL port of underlay bridge) are
snooped. In order to achieve this, IP addresses of the bridge are
retrieved and then stored in xbridge by calling xlate_xbridge_set().
These data is then matched against the data extracted from an ARP or
Neighbor Advertisement message in is_neighbor_reply_correct() which is
invoked from terminate_native_tunnel().

Signed-off-by: Zoltan Balogh <zoltan.balogh@ericsson.com>
---
 include/sparse/netinet/in.h   |  10 +++
 ofproto/ofproto-dpif-xlate.c  | 147 ++++++++++++++++++++++++++++++++++++++++--
 tests/tunnel-push-pop-ipv6.at |  68 ++++++++++++++++++-
 tests/tunnel-push-pop.at      |  67 ++++++++++++++++++-
 4 files changed, 282 insertions(+), 10 deletions(-)

Comments

Ben Pfaff Jan. 23, 2018, 7:01 p.m. UTC | #1
On Tue, Jan 09, 2018 at 07:54:33PM +0100, Zoltan Balogh wrote:
> Currenlty, OVS snoops any ARP or ND packets in any bridge and populates
> the tunnel neighbor cache with the retreived data. For instance, when
> ARP reply originated by a tenant is received in an overlay bridge, the
> ARP message is snooped and tunnel neighbor cache is filled with tenant
> data, however only tunnel neighbor data should be stored there.
> 
> This patch moves tunnel neighbor snooping from do_xlate_actions() to
> terminate_native_tunnel() where native tunnel is terminated, in order to
> keep ARP neighbor cache clean, i.e. only packets comming from a native
> tunnel are snooped.
> 
> By applying the patch, only ARP and Neighbor Advertisement messages
> addressing a tunnel endpoint (LOCAL port of underlay bridge) are
> snooped. In order to achieve this, IP addresses of the bridge are
> retrieved and then stored in xbridge by calling xlate_xbridge_set().
> These data is then matched against the data extracted from an ARP or
> Neighbor Advertisement message in is_neighbor_reply_correct() which is
> invoked from terminate_native_tunnel().
> 
> Signed-off-by: Zoltan Balogh <zoltan.balogh@ericsson.com>

Thanks a lot!

It appears to me that this patch makes the following test consistently
fail.  When I revert the test, it passes for me again.  Can you take a
look?

Thanks,

Ben.

#                             -*- compilation -*-
2370. ovn.at:8136: testing ovn -- /32 router IP address ...
creating ovn-sb database
creating ovn-nb database
starting ovn-northd
starting backup ovn-northd
adding simulator 'main'
adding simulator 'hv1'
adding simulator 'hv2'
../../tests/ovn.at:8198: ovn_populate_arp__
stdout:
OK
OK

checking packets in hv2/vif1-tx.pcap against expected:
expected 1 packets, only received 0
../../tests/ovn.at:8230: sort $rcv_text
--- expout      2018-01-23 10:59:47.231797273 -0800
+++ /home/blp/nicira/ovs/_build/tests/testsuite.dir/at-groups/2370/stdout       2018-01-23 10:59:47.231797273 -0800
@@ -1 +0,0 @@
-f0000001020400000001020408004500001c000000003f110100c0a801020a0000020035111100080000
2370. ovn.at:8136: 2370. ovn -- /32 router IP address (ovn.at:8136): FAILED (ovn.at:8230)


> ---
>  include/sparse/netinet/in.h   |  10 +++
>  ofproto/ofproto-dpif-xlate.c  | 147 ++++++++++++++++++++++++++++++++++++++++--
>  tests/tunnel-push-pop-ipv6.at |  68 ++++++++++++++++++-
>  tests/tunnel-push-pop.at      |  67 ++++++++++++++++++-
>  4 files changed, 282 insertions(+), 10 deletions(-)
> 
> diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h
> index 6abdb2331..eea41bd7f 100644
> --- a/include/sparse/netinet/in.h
> +++ b/include/sparse/netinet/in.h
> @@ -123,6 +123,16 @@ struct sockaddr_in6 {
>       (X)->s6_addr[10] == 0xff &&                \
>       (X)->s6_addr[11] == 0xff)
>  
> +#define IN6_IS_ADDR_MC_LINKLOCAL(a)                 \
> +    (((const uint8_t *) (a))[0] == 0xff &&          \
> +     (((const uint8_t *) (a))[1] & 0xf) == 0x2)
> +
> +# define IN6_ARE_ADDR_EQUAL(a,b)                                          \
> +    ((((const uint32_t *) (a))[0] == ((const uint32_t *) (b))[0]) &&      \
> +     (((const uint32_t *) (a))[1] == ((const uint32_t *) (b))[1]) &&      \
> +     (((const uint32_t *) (a))[2] == ((const uint32_t *) (b))[2]) &&      \
> +     (((const uint32_t *) (a))[3] == ((const uint32_t *) (b))[3]))
> +
>  #define INET_ADDRSTRLEN 16
>  #define INET6_ADDRSTRLEN 46
>  
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 2328ec1da..16bc0cd96 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -90,6 +90,16 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
>   * recursive or not. */
>  #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH)
>  
> +/* The structure holds an array of IP addresses assigned to a bridge and the
> + * number of elements in the array. These data are mutable and are evaluated
> + * when ARP or Neighbor Advertisement packets received on a native tunnel
> + * port are xlated. So 'ref_cnt' and RCU are used for synchronization. */
> +struct xbridge_addr {
> +    struct in6_addr *addr;        /* Array of IP addresses of xbridge. */
> +    int n_addr;                   /* Number of IP addresses. */
> +    struct ovs_refcount ref_cnt;
> +};
> +
>  struct xbridge {
>      struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */
>      struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
> @@ -113,6 +123,8 @@ struct xbridge {
>  
>      /* Datapath feature support. */
>      struct dpif_backer_support support;
> +
> +    struct xbridge_addr *addr;
>  };
>  
>  struct xbundle {
> @@ -576,7 +588,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
>                                const struct dpif_ipfix *,
>                                const struct netflow *,
>                                bool forward_bpdu, bool has_in_band,
> -                              const struct dpif_backer_support *);
> +                              const struct dpif_backer_support *,
> +                              const struct xbridge_addr *);
>  static void xlate_xbundle_set(struct xbundle *xbundle,
>                                enum port_vlan_mode vlan_mode,
>                                uint16_t qinq_ethtype, int vlan,
> @@ -826,6 +839,56 @@ xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport)
>                  hash_ofp_port(xport->ofp_port));
>  }
>  
> +static struct xbridge_addr *
> +xbridge_addr_create(struct xbridge *xbridge)
> +{
> +    struct xbridge_addr *xbridge_addr = xbridge->addr;
> +    struct in6_addr *addr = NULL, *mask = NULL;
> +    struct netdev *dev;
> +    int err, n_addr = 0;
> +
> +    err = netdev_open(xbridge->name, NULL, &dev);
> +    if (!err) {
> +        err = netdev_get_addr_list(dev, &addr, &mask, &n_addr);
> +        if (!err) {
> +            if (!xbridge->addr ||
> +                n_addr != xbridge->addr->n_addr ||
> +                (xbridge->addr->addr && memcmp(addr, xbridge->addr->addr,
> +                                               sizeof(*addr) * n_addr))) {
> +                xbridge_addr = xzalloc(sizeof *xbridge_addr);
> +                xbridge_addr->addr = addr;
> +                xbridge_addr->n_addr = n_addr;
> +                ovs_refcount_init(&xbridge_addr->ref_cnt);
> +            } else {
> +                free(addr);
> +            }
> +            free(mask);
> +        }
> +        netdev_close(dev);
> +    }
> +
> +    return xbridge_addr;
> +}
> +
> +static struct xbridge_addr *
> +xbridge_addr_ref(const struct xbridge_addr *addr_)
> +{
> +    struct xbridge_addr *addr = CONST_CAST(struct xbridge_addr *, addr_);
> +    if (addr) {
> +        ovs_refcount_ref(&addr->ref_cnt);
> +    }
> +    return addr;
> +}
> +
> +static void
> +xbridge_addr_unref(struct xbridge_addr *addr)
> +{
> +    if (addr && ovs_refcount_unref_relaxed(&addr->ref_cnt) == 1) {
> +        free(addr->addr);
> +        free(addr);
> +    }
> +}
> +
>  static void
>  xlate_xbridge_set(struct xbridge *xbridge,
>                    struct dpif *dpif,
> @@ -836,7 +899,8 @@ xlate_xbridge_set(struct xbridge *xbridge,
>                    const struct dpif_ipfix *ipfix,
>                    const struct netflow *netflow,
>                    bool forward_bpdu, bool has_in_band,
> -                  const struct dpif_backer_support *support)
> +                  const struct dpif_backer_support *support,
> +                  const struct xbridge_addr *addr)
>  {
>      if (xbridge->ml != ml) {
>          mac_learning_unref(xbridge->ml);
> @@ -878,6 +942,11 @@ xlate_xbridge_set(struct xbridge *xbridge,
>          xbridge->netflow = netflow_ref(netflow);
>      }
>  
> +    if (xbridge->addr != addr) {
> +        xbridge_addr_unref(xbridge->addr);
> +        xbridge->addr = xbridge_addr_ref(addr);
> +    }
> +
>      xbridge->dpif = dpif;
>      xbridge->forward_bpdu = forward_bpdu;
>      xbridge->has_in_band = has_in_band;
> @@ -971,7 +1040,7 @@ xlate_xbridge_copy(struct xbridge *xbridge)
>                        xbridge->rstp, xbridge->ms, xbridge->mbridge,
>                        xbridge->sflow, xbridge->ipfix, xbridge->netflow,
>                        xbridge->forward_bpdu, xbridge->has_in_band,
> -                      &xbridge->support);
> +                      &xbridge->support, xbridge->addr);
>      LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
>          xlate_xbundle_copy(new_xbridge, xbundle);
>      }
> @@ -1126,6 +1195,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
>                    const struct dpif_backer_support *support)
>  {
>      struct xbridge *xbridge;
> +    struct xbridge_addr *xbridge_addr, *old_addr;
>  
>      ovs_assert(new_xcfg);
>  
> @@ -1140,8 +1210,16 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
>      free(xbridge->name);
>      xbridge->name = xstrdup(name);
>  
> +    xbridge_addr = xbridge_addr_create(xbridge);
> +    old_addr = xbridge->addr;
> +
>      xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,
> -                      netflow, forward_bpdu, has_in_band, support);
> +                      netflow, forward_bpdu, has_in_band, support,
> +                      xbridge_addr);
> +
> +    if (xbridge_addr != old_addr) {
> +        xbridge_addr_unref(xbridge_addr);
> +    }
>  }
>  
>  static void
> @@ -1171,6 +1249,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
>      netflow_unref(xbridge->netflow);
>      stp_unref(xbridge->stp);
>      rstp_unref(xbridge->rstp);
> +    xbridge_addr_unref(xbridge->addr);
>      hmap_destroy(&xbridge->xports);
>      free(xbridge->name);
>      free(xbridge);
> @@ -3655,6 +3734,54 @@ check_output_prerequisites(struct xlate_ctx *ctx,
>      return true;
>  }
>  
> +/* Function verifies if destination address of received Neighbor Advertisement
> + * message stored in 'flow' is correct. It should be either FF02::1:FFXX:XXXX
> + * where XX:XXXX stands for the last 24 bits of 'ipv6_addr' or it should match
> + * 'ipv6_addr'. */
> +static bool
> +is_nd_dst_correct(const struct flow *flow, const struct in6_addr *ipv6_addr)
> +{
> +    const uint8_t *flow_ipv6_addr = (uint8_t *) &flow->ipv6_dst;
> +    const uint8_t *addr = (uint8_t *) ipv6_addr;
> +
> +    return (IN6_IS_ADDR_MC_LINKLOCAL(flow_ipv6_addr) &&
> +            flow_ipv6_addr[11] == 0x01 &&
> +            flow_ipv6_addr[12] == 0xff &&
> +            flow_ipv6_addr[13] == addr[13] &&
> +            flow_ipv6_addr[14] == addr[14] &&
> +            flow_ipv6_addr[15] == addr[15]) ||
> +            IN6_ARE_ADDR_EQUAL(&flow->ipv6_dst, ipv6_addr);
> +}
> +
> +/* Function verifies if the ARP reply or Neighbor Advertisement represented by
> + * 'flow' addresses the 'xbridge' of 'ctx'. Returns true if the ARP TA or
> + * neighbor discovery destination is in the list of configured IP addresses of
> + * the bridge. Otherwise, it returns false. */
> +static bool
> +is_neighbor_reply_correct(const struct xlate_ctx *ctx, const struct flow *flow)
> +{
> +    bool ret = false;
> +    int i;
> +    struct xbridge_addr *xbridge_addr = xbridge_addr_ref(ctx->xbridge->addr);
> +
> +    /* Verify if 'nw_dst' of ARP or 'ipv6_dst' of ICMPV6 is in the list. */
> +    for (i = 0; xbridge_addr && i < xbridge_addr->n_addr; i++) {
> +        struct in6_addr *ip_addr = &xbridge_addr->addr[i];
> +        if ((IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> +             flow->dl_type == htons(ETH_TYPE_ARP) &&
> +             in6_addr_get_mapped_ipv4(ip_addr) == flow->nw_dst) ||
> +            (!IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> +              is_nd_dst_correct(flow, ip_addr))) {
> +            /* Found a match. */
> +            ret = true;
> +            break;
> +        }
> +    }
> +
> +    xbridge_addr_unref(xbridge_addr);
> +    return ret;
> +}
> +
>  static bool
>  terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
>                          struct flow *flow, struct flow_wildcards *wc,
> @@ -3667,6 +3794,15 @@ terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
>      if (ofp_port == OFPP_LOCAL &&
>          ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
>          *tnl_port = tnl_port_map_lookup(flow, wc);
> +
> +        /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,
> +         * do tunnel neighbor snooping. */
> +        if (*tnl_port == ODPP_NONE &&
> +            (flow->dl_type == htons(ETH_TYPE_ARP) ||
> +             flow->nw_proto == IPPROTO_ICMPV6) &&
> +             is_neighbor_reply_correct(ctx, flow)) {
> +            tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> +        }
>      }
>  
>      return *tnl_port != ODPP_NONE;
> @@ -6183,9 +6319,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>      struct flow *flow = &ctx->xin->flow;
>      const struct ofpact *a;
>  
> -    if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
> -        tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> -    }
>      /* dl_type already in the mask, not set below. */
>  
>      if (!ofpacts_len) {
> diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
> index 0b9d436db..b0f4a7e91 100644
> --- a/tests/tunnel-push-pop-ipv6.at
> +++ b/tests/tunnel-push-pop-ipv6.at
> @@ -55,9 +55,73 @@ AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | un
>  ])
>  
>  dnl Check ARP Snoop
> -AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)'])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:c8)'])
>  
> -AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b7)'])
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> +])
> +
> +dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> +])
> +
> +dnl Receiving Neighbot Advertisement with incorrect VLAN id should not alter tunnel neighbor cache
> +AT_CHECK([ovs-vsctl set port br0 tag=10])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> +])
> +
> +dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache
> +AT_CHECK([ovs-vsctl set port br0 tag=10])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> +])
> +
> +dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache
> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-config:hwaddr=aa:55:aa:55:00:99])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:c8)'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> +])
> +
> +dnl Receive Neighbor Advertisement without VLAN header
> +AT_CHECK([ovs-vsctl set port br0 tag=0])
> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> +])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)'])
> +
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff00:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b7)'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
>  
>  AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>  2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
> index b168f5f2f..4e259730c 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -70,9 +70,71 @@ ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101
>  ])
>  
>  dnl Check ARP Snoop
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> +])
> +
> +dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> +])
> +
> +dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache
> +AT_CHECK([ovs-vsctl set port br0 tag=10])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> +])
> +
> +dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> +])
> +
> +dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache
> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-config:hwaddr=aa:55:aa:55:00:99])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> +])
> +
> +dnl Receive ARP reply without VLAN header
> +AT_CHECK([ovs-vsctl set port br0 tag=0])
> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> +])
> +
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)'])
>  
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
>  AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
>  1.1.2.92                                      f8:bc:12:44:34:b6   br0
>  1.1.2.93                                      f8:bc:12:44:34:b7   br0
> @@ -190,9 +252,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl
>  dnl Check GREL3 only accepts non-fragmented packets?
>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>  
> +ovs-appctl time/warp 1000
> +ovs-appctl time/warp 1000
> +
>  AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
>    port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
> -  port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
> +  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?
>  ])
>  
>  dnl Check decapsulation of Geneve packet with options
> -- 
> 2.14.1
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Zoltan Balogh Jan. 26, 2018, 1:08 p.m. UTC | #2
Hi,

I've been investigating the failing unit test. I can confirm, it does fail
with my series on master. However, when I created the series and sent it to
the mailing list it did not. 

I've rebased my series to this commit before I sent it to the mailing list.
  commit f59cb331c481d08f9a851c07cf31e9d826650485
  Author: Yi Yang <yi.y.yang@intel.com>
  Date:   Sat Jan 6 13:47:51 2018 +0800

If I apply the series to the commit below, the test does pass.

So, I started to search for any commits between the one from Yi Yang and the 
Last one on master which could make my series fail on the OVN unit test 
"/32 router IP address".

I found, that reverting the series of two commits from Justin Pettit below,
makes my series pass the unit test again. Even rebased to master.

  commit 74c4530dca939109f3fb79776b60b8722e149738
  Author: Justin Pettit <jpettit@ovn.org>
  Date:   Wed Oct 18 23:16:22 2017 -0700

      ofproto-dpif: Don't slow-path controller actions with pause.
      
      A previous patch removed slow-pathing for controller actions with the
      exception of ones that specified "pause".  This commit removes that
      restriction so that no controller actions are slow-pathed.
      
      Signed-off-by: Justin Pettit <jpettit@ovn.org>
      Acked-by: Ben Pfaff <blp@ovn.org>

  commit d39ec23de38464ee35b3098b9f6c5f06d5191015
  Author: Justin Pettit <jpettit@ovn.org>
  Date:   Wed Jul 5 15:17:52 2017 -0700

      ofproto-dpif: Don't slow-path controller actions.
      
      Controller actions have become more commonly used for purposes other
      than just making forwarding decisions (e.g., packet logging).  A packet
      that needs to be copied to the controller and forwarded would always be
      sent to ovs-vswitchd to be handled, which could negatively affect
      performance and cause heavier CPU utilization in ovs-vswitchd.
      
      This commit changes the behavior so that OpenFlow controller actions
      become userspace datapath actions while continuing to let packet
      forwarding and manipulation continue to be handled by the datapath
      directly.
      
      This patch still slow-paths controller actions with the "pause" flag
      set.  A future patch will stop slow-pathing these pause actions as
      well.
      
      Signed-off-by: Justin Pettit <jpettit@ovn.org>
      Acked-by: Ben Pfaff <blp@ovn.org>

The main difference, my series does compared to master, is not putting the 
ARP entry of {10.0.0.2, f0:00:00:01:02:04, br-int} into tunnel neighbor 
cache. All the OF rules are almost the same, except loading different 
numbers into NXM_NX_REGx and using different metadata. 

How should this be related to Justin's series?

If I modify the unit test and populate the missing ARP entry then the test 
does pass.

Should this entry be present in the ARP neighbor cache in order to make the
test pass? If yes, then why? 

Best regards,
Zoltan

> -----Original Message-----
> From: Ben Pfaff [mailto:blp@ovn.org]
> Sent: Tuesday, January 23, 2018 8:02 PM
> To: Zoltán Balogh <zoltan.balogh@ericsson.com>
> Cc: dev@openvswitch.org; Jan Scheurich <jan.scheurich@ericsson.com>
> Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
> 
> On Tue, Jan 09, 2018 at 07:54:33PM +0100, Zoltan Balogh wrote:
> > Currenlty, OVS snoops any ARP or ND packets in any bridge and populates
> > the tunnel neighbor cache with the retreived data. For instance, when
> > ARP reply originated by a tenant is received in an overlay bridge, the
> > ARP message is snooped and tunnel neighbor cache is filled with tenant
> > data, however only tunnel neighbor data should be stored there.
> >
> > This patch moves tunnel neighbor snooping from do_xlate_actions() to
> > terminate_native_tunnel() where native tunnel is terminated, in order to
> > keep ARP neighbor cache clean, i.e. only packets comming from a native
> > tunnel are snooped.
> >
> > By applying the patch, only ARP and Neighbor Advertisement messages
> > addressing a tunnel endpoint (LOCAL port of underlay bridge) are
> > snooped. In order to achieve this, IP addresses of the bridge are
> > retrieved and then stored in xbridge by calling xlate_xbridge_set().
> > These data is then matched against the data extracted from an ARP or
> > Neighbor Advertisement message in is_neighbor_reply_correct() which is
> > invoked from terminate_native_tunnel().
> >
> > Signed-off-by: Zoltan Balogh <zoltan.balogh@ericsson.com>
> 
> Thanks a lot!
> 
> It appears to me that this patch makes the following test consistently
> fail.  When I revert the test, it passes for me again.  Can you take a
> look?
> 
> Thanks,
> 
> Ben.
> 
> #                             -*- compilation -*-
> 2370. ovn.at:8136: testing ovn -- /32 router IP address ...
> creating ovn-sb database
> creating ovn-nb database
> starting ovn-northd
> starting backup ovn-northd
> adding simulator 'main'
> adding simulator 'hv1'
> adding simulator 'hv2'
> ../../tests/ovn.at:8198: ovn_populate_arp__
> stdout:
> OK
> OK
> 
> checking packets in hv2/vif1-tx.pcap against expected:
> expected 1 packets, only received 0
> ../../tests/ovn.at:8230: sort $rcv_text
> --- expout      2018-01-23 10:59:47.231797273 -0800
> +++ /home/blp/nicira/ovs/_build/tests/testsuite.dir/at-groups/2370/stdout       2018-01-23 10:59:47.231797273 -0800
> @@ -1 +0,0 @@
> -f0000001020400000001020408004500001c000000003f110100c0a801020a0000020035111100080000
> 2370. ovn.at:8136: 2370. ovn -- /32 router IP address (ovn.at:8136): FAILED (ovn.at:8230)
> 
> 
> > ---
> >  include/sparse/netinet/in.h   |  10 +++
> >  ofproto/ofproto-dpif-xlate.c  | 147 ++++++++++++++++++++++++++++++++++++++++--
> >  tests/tunnel-push-pop-ipv6.at |  68 ++++++++++++++++++-
> >  tests/tunnel-push-pop.at      |  67 ++++++++++++++++++-
> >  4 files changed, 282 insertions(+), 10 deletions(-)
> >
> > diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h
> > index 6abdb2331..eea41bd7f 100644
> > --- a/include/sparse/netinet/in.h
> > +++ b/include/sparse/netinet/in.h
> > @@ -123,6 +123,16 @@ struct sockaddr_in6 {
> >       (X)->s6_addr[10] == 0xff &&                \
> >       (X)->s6_addr[11] == 0xff)
> >
> > +#define IN6_IS_ADDR_MC_LINKLOCAL(a)                 \
> > +    (((const uint8_t *) (a))[0] == 0xff &&          \
> > +     (((const uint8_t *) (a))[1] & 0xf) == 0x2)
> > +
> > +# define IN6_ARE_ADDR_EQUAL(a,b)                                          \
> > +    ((((const uint32_t *) (a))[0] == ((const uint32_t *) (b))[0]) &&      \
> > +     (((const uint32_t *) (a))[1] == ((const uint32_t *) (b))[1]) &&      \
> > +     (((const uint32_t *) (a))[2] == ((const uint32_t *) (b))[2]) &&      \
> > +     (((const uint32_t *) (a))[3] == ((const uint32_t *) (b))[3]))
> > +
> >  #define INET_ADDRSTRLEN 16
> >  #define INET6_ADDRSTRLEN 46
> >
> > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> > index 2328ec1da..16bc0cd96 100644
> > --- a/ofproto/ofproto-dpif-xlate.c
> > +++ b/ofproto/ofproto-dpif-xlate.c
> > @@ -90,6 +90,16 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
> >   * recursive or not. */
> >  #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH)
> >
> > +/* The structure holds an array of IP addresses assigned to a bridge and the
> > + * number of elements in the array. These data are mutable and are evaluated
> > + * when ARP or Neighbor Advertisement packets received on a native tunnel
> > + * port are xlated. So 'ref_cnt' and RCU are used for synchronization. */
> > +struct xbridge_addr {
> > +    struct in6_addr *addr;        /* Array of IP addresses of xbridge. */
> > +    int n_addr;                   /* Number of IP addresses. */
> > +    struct ovs_refcount ref_cnt;
> > +};
> > +
> >  struct xbridge {
> >      struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */
> >      struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
> > @@ -113,6 +123,8 @@ struct xbridge {
> >
> >      /* Datapath feature support. */
> >      struct dpif_backer_support support;
> > +
> > +    struct xbridge_addr *addr;
> >  };
> >
> >  struct xbundle {
> > @@ -576,7 +588,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
> >                                const struct dpif_ipfix *,
> >                                const struct netflow *,
> >                                bool forward_bpdu, bool has_in_band,
> > -                              const struct dpif_backer_support *);
> > +                              const struct dpif_backer_support *,
> > +                              const struct xbridge_addr *);
> >  static void xlate_xbundle_set(struct xbundle *xbundle,
> >                                enum port_vlan_mode vlan_mode,
> >                                uint16_t qinq_ethtype, int vlan,
> > @@ -826,6 +839,56 @@ xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport)
> >                  hash_ofp_port(xport->ofp_port));
> >  }
> >
> > +static struct xbridge_addr *
> > +xbridge_addr_create(struct xbridge *xbridge)
> > +{
> > +    struct xbridge_addr *xbridge_addr = xbridge->addr;
> > +    struct in6_addr *addr = NULL, *mask = NULL;
> > +    struct netdev *dev;
> > +    int err, n_addr = 0;
> > +
> > +    err = netdev_open(xbridge->name, NULL, &dev);
> > +    if (!err) {
> > +        err = netdev_get_addr_list(dev, &addr, &mask, &n_addr);
> > +        if (!err) {
> > +            if (!xbridge->addr ||
> > +                n_addr != xbridge->addr->n_addr ||
> > +                (xbridge->addr->addr && memcmp(addr, xbridge->addr->addr,
> > +                                               sizeof(*addr) * n_addr))) {
> > +                xbridge_addr = xzalloc(sizeof *xbridge_addr);
> > +                xbridge_addr->addr = addr;
> > +                xbridge_addr->n_addr = n_addr;
> > +                ovs_refcount_init(&xbridge_addr->ref_cnt);
> > +            } else {
> > +                free(addr);
> > +            }
> > +            free(mask);
> > +        }
> > +        netdev_close(dev);
> > +    }
> > +
> > +    return xbridge_addr;
> > +}
> > +
> > +static struct xbridge_addr *
> > +xbridge_addr_ref(const struct xbridge_addr *addr_)
> > +{
> > +    struct xbridge_addr *addr = CONST_CAST(struct xbridge_addr *, addr_);
> > +    if (addr) {
> > +        ovs_refcount_ref(&addr->ref_cnt);
> > +    }
> > +    return addr;
> > +}
> > +
> > +static void
> > +xbridge_addr_unref(struct xbridge_addr *addr)
> > +{
> > +    if (addr && ovs_refcount_unref_relaxed(&addr->ref_cnt) == 1) {
> > +        free(addr->addr);
> > +        free(addr);
> > +    }
> > +}
> > +
> >  static void
> >  xlate_xbridge_set(struct xbridge *xbridge,
> >                    struct dpif *dpif,
> > @@ -836,7 +899,8 @@ xlate_xbridge_set(struct xbridge *xbridge,
> >                    const struct dpif_ipfix *ipfix,
> >                    const struct netflow *netflow,
> >                    bool forward_bpdu, bool has_in_band,
> > -                  const struct dpif_backer_support *support)
> > +                  const struct dpif_backer_support *support,
> > +                  const struct xbridge_addr *addr)
> >  {
> >      if (xbridge->ml != ml) {
> >          mac_learning_unref(xbridge->ml);
> > @@ -878,6 +942,11 @@ xlate_xbridge_set(struct xbridge *xbridge,
> >          xbridge->netflow = netflow_ref(netflow);
> >      }
> >
> > +    if (xbridge->addr != addr) {
> > +        xbridge_addr_unref(xbridge->addr);
> > +        xbridge->addr = xbridge_addr_ref(addr);
> > +    }
> > +
> >      xbridge->dpif = dpif;
> >      xbridge->forward_bpdu = forward_bpdu;
> >      xbridge->has_in_band = has_in_band;
> > @@ -971,7 +1040,7 @@ xlate_xbridge_copy(struct xbridge *xbridge)
> >                        xbridge->rstp, xbridge->ms, xbridge->mbridge,
> >                        xbridge->sflow, xbridge->ipfix, xbridge->netflow,
> >                        xbridge->forward_bpdu, xbridge->has_in_band,
> > -                      &xbridge->support);
> > +                      &xbridge->support, xbridge->addr);
> >      LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
> >          xlate_xbundle_copy(new_xbridge, xbundle);
> >      }
> > @@ -1126,6 +1195,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
> >                    const struct dpif_backer_support *support)
> >  {
> >      struct xbridge *xbridge;
> > +    struct xbridge_addr *xbridge_addr, *old_addr;
> >
> >      ovs_assert(new_xcfg);
> >
> > @@ -1140,8 +1210,16 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
> >      free(xbridge->name);
> >      xbridge->name = xstrdup(name);
> >
> > +    xbridge_addr = xbridge_addr_create(xbridge);
> > +    old_addr = xbridge->addr;
> > +
> >      xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,
> > -                      netflow, forward_bpdu, has_in_band, support);
> > +                      netflow, forward_bpdu, has_in_band, support,
> > +                      xbridge_addr);
> > +
> > +    if (xbridge_addr != old_addr) {
> > +        xbridge_addr_unref(xbridge_addr);
> > +    }
> >  }
> >
> >  static void
> > @@ -1171,6 +1249,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
> >      netflow_unref(xbridge->netflow);
> >      stp_unref(xbridge->stp);
> >      rstp_unref(xbridge->rstp);
> > +    xbridge_addr_unref(xbridge->addr);
> >      hmap_destroy(&xbridge->xports);
> >      free(xbridge->name);
> >      free(xbridge);
> > @@ -3655,6 +3734,54 @@ check_output_prerequisites(struct xlate_ctx *ctx,
> >      return true;
> >  }
> >
> > +/* Function verifies if destination address of received Neighbor Advertisement
> > + * message stored in 'flow' is correct. It should be either FF02::1:FFXX:XXXX
> > + * where XX:XXXX stands for the last 24 bits of 'ipv6_addr' or it should match
> > + * 'ipv6_addr'. */
> > +static bool
> > +is_nd_dst_correct(const struct flow *flow, const struct in6_addr *ipv6_addr)
> > +{
> > +    const uint8_t *flow_ipv6_addr = (uint8_t *) &flow->ipv6_dst;
> > +    const uint8_t *addr = (uint8_t *) ipv6_addr;
> > +
> > +    return (IN6_IS_ADDR_MC_LINKLOCAL(flow_ipv6_addr) &&
> > +            flow_ipv6_addr[11] == 0x01 &&
> > +            flow_ipv6_addr[12] == 0xff &&
> > +            flow_ipv6_addr[13] == addr[13] &&
> > +            flow_ipv6_addr[14] == addr[14] &&
> > +            flow_ipv6_addr[15] == addr[15]) ||
> > +            IN6_ARE_ADDR_EQUAL(&flow->ipv6_dst, ipv6_addr);
> > +}
> > +
> > +/* Function verifies if the ARP reply or Neighbor Advertisement represented by
> > + * 'flow' addresses the 'xbridge' of 'ctx'. Returns true if the ARP TA or
> > + * neighbor discovery destination is in the list of configured IP addresses of
> > + * the bridge. Otherwise, it returns false. */
> > +static bool
> > +is_neighbor_reply_correct(const struct xlate_ctx *ctx, const struct flow *flow)
> > +{
> > +    bool ret = false;
> > +    int i;
> > +    struct xbridge_addr *xbridge_addr = xbridge_addr_ref(ctx->xbridge->addr);
> > +
> > +    /* Verify if 'nw_dst' of ARP or 'ipv6_dst' of ICMPV6 is in the list. */
> > +    for (i = 0; xbridge_addr && i < xbridge_addr->n_addr; i++) {
> > +        struct in6_addr *ip_addr = &xbridge_addr->addr[i];
> > +        if ((IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> > +             flow->dl_type == htons(ETH_TYPE_ARP) &&
> > +             in6_addr_get_mapped_ipv4(ip_addr) == flow->nw_dst) ||
> > +            (!IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> > +              is_nd_dst_correct(flow, ip_addr))) {
> > +            /* Found a match. */
> > +            ret = true;
> > +            break;
> > +        }
> > +    }
> > +
> > +    xbridge_addr_unref(xbridge_addr);
> > +    return ret;
> > +}
> > +
> >  static bool
> >  terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> >                          struct flow *flow, struct flow_wildcards *wc,
> > @@ -3667,6 +3794,15 @@ terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> >      if (ofp_port == OFPP_LOCAL &&
> >          ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
> >          *tnl_port = tnl_port_map_lookup(flow, wc);
> > +
> > +        /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,
> > +         * do tunnel neighbor snooping. */
> > +        if (*tnl_port == ODPP_NONE &&
> > +            (flow->dl_type == htons(ETH_TYPE_ARP) ||
> > +             flow->nw_proto == IPPROTO_ICMPV6) &&
> > +             is_neighbor_reply_correct(ctx, flow)) {
> > +            tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> > +        }
> >      }
> >
> >      return *tnl_port != ODPP_NONE;
> > @@ -6183,9 +6319,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
> >      struct flow *flow = &ctx->xin->flow;
> >      const struct ofpact *a;
> >
> > -    if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
> > -        tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> > -    }
> >      /* dl_type already in the mask, not set below. */
> >
> >      if (!ofpacts_len) {
> > diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
> > index 0b9d436db..b0f4a7e91 100644
> > --- a/tests/tunnel-push-pop-ipv6.at
> > +++ b/tests/tunnel-push-pop-ipv6.at
> > @@ -55,9 +55,73 @@ AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | un
> >  ])
> >
> >  dnl Check ARP Snoop
> > -AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> 00,tll=f8:bc:12:44:34:b6)'])
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> 00,tll=f8:bc:12:44:34:c8)'])
> >
> > -AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::
> 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:
> 00,tll=f8:bc:12:44:34:b7)'])
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > +])
> > +
> > +dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> 99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> 00,tll=f8:bc:12:44:34:b6)'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > +])
> > +
> > +dnl Receiving Neighbot Advertisement with incorrect VLAN id should not alter tunnel neighbor cache
> > +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86
> dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > +])
> > +
> > +dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache
> > +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86
> dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > +])
> > +
> > +dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache
> > +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
> config:hwaddr=aa:55:aa:55:00:99])
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p1
> 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe
> ::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:0
> 0:00,tll=f8:bc:12:44:34:c8)'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> > +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > +])
> > +
> > +dnl Receive Neighbor Advertisement without VLAN header
> > +AT_CHECK([ovs-vsctl set port br0 tag=0])
> > +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> > +])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> 00,tll=f8:bc:12:44:34:b6)'])
> > +
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff0
> 0:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00
> :00:00,tll=f8:bc:12:44:34:b7)'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> >
> >  AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> >  2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
> > index b168f5f2f..4e259730c 100644
> > --- a/tests/tunnel-push-pop.at
> > +++ b/tests/tunnel-push-pop.at
> > @@ -70,9 +70,71 @@ ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101
> >  ])
> >
> >  dnl Check ARP Snoop
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> 2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > +])
> > +
> > +dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> 2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > +])
> > +
> > +dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache
> > +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(
> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > +])
> > +
> > +dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(
> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > +])
> > +
> > +dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache
> > +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
> config:hwaddr=aa:55:aa:55:00:99])
> > +AT_CHECK([ovs-appctl netdev-dummy/receive p1
> 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.
> 1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> > +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > +])
> > +
> > +dnl Receive ARP reply without VLAN header
> > +AT_CHECK([ovs-vsctl set port br0 tag=0])
> > +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> > +])
> > +
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> >  AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> 2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
> >  AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.
> 2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)'])
> >
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> >  AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
> >  1.1.2.92                                      f8:bc:12:44:34:b6   br0
> >  1.1.2.93                                      f8:bc:12:44:34:b7   br0
> > @@ -190,9 +252,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl
> >  dnl Check GREL3 only accepts non-fragmented packets?
> >  AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a0800450
> 00054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
> f202122232425262728292a2b2c2d2e2f3031323334353637'])
> >
> > +ovs-appctl time/warp 1000
> > +ovs-appctl time/warp 1000
> > +
> >  AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
> >    port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
> > -  port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
> > +  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?
> >  ])
> >
> >  dnl Check decapsulation of Geneve packet with options
> > --
> > 2.14.1
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Ben Pfaff Feb. 1, 2018, 10:17 p.m. UTC | #3
Justin, I think this is mainly a question about your patches, can you
take a look?

On Fri, Jan 26, 2018 at 01:08:35PM +0000, Zoltán Balogh wrote:
> Hi,
> 
> I've been investigating the failing unit test. I can confirm, it does fail
> with my series on master. However, when I created the series and sent it to
> the mailing list it did not. 
> 
> I've rebased my series to this commit before I sent it to the mailing list.
>   commit f59cb331c481d08f9a851c07cf31e9d826650485
>   Author: Yi Yang <yi.y.yang@intel.com>
>   Date:   Sat Jan 6 13:47:51 2018 +0800
> 
> If I apply the series to the commit below, the test does pass.
> 
> So, I started to search for any commits between the one from Yi Yang and the 
> Last one on master which could make my series fail on the OVN unit test 
> "/32 router IP address".
> 
> I found, that reverting the series of two commits from Justin Pettit below,
> makes my series pass the unit test again. Even rebased to master.
> 
>   commit 74c4530dca939109f3fb79776b60b8722e149738
>   Author: Justin Pettit <jpettit@ovn.org>
>   Date:   Wed Oct 18 23:16:22 2017 -0700
> 
>       ofproto-dpif: Don't slow-path controller actions with pause.
>       
>       A previous patch removed slow-pathing for controller actions with the
>       exception of ones that specified "pause".  This commit removes that
>       restriction so that no controller actions are slow-pathed.
>       
>       Signed-off-by: Justin Pettit <jpettit@ovn.org>
>       Acked-by: Ben Pfaff <blp@ovn.org>
> 
>   commit d39ec23de38464ee35b3098b9f6c5f06d5191015
>   Author: Justin Pettit <jpettit@ovn.org>
>   Date:   Wed Jul 5 15:17:52 2017 -0700
> 
>       ofproto-dpif: Don't slow-path controller actions.
>       
>       Controller actions have become more commonly used for purposes other
>       than just making forwarding decisions (e.g., packet logging).  A packet
>       that needs to be copied to the controller and forwarded would always be
>       sent to ovs-vswitchd to be handled, which could negatively affect
>       performance and cause heavier CPU utilization in ovs-vswitchd.
>       
>       This commit changes the behavior so that OpenFlow controller actions
>       become userspace datapath actions while continuing to let packet
>       forwarding and manipulation continue to be handled by the datapath
>       directly.
>       
>       This patch still slow-paths controller actions with the "pause" flag
>       set.  A future patch will stop slow-pathing these pause actions as
>       well.
>       
>       Signed-off-by: Justin Pettit <jpettit@ovn.org>
>       Acked-by: Ben Pfaff <blp@ovn.org>
> 
> The main difference, my series does compared to master, is not putting the 
> ARP entry of {10.0.0.2, f0:00:00:01:02:04, br-int} into tunnel neighbor 
> cache. All the OF rules are almost the same, except loading different 
> numbers into NXM_NX_REGx and using different metadata. 
> 
> How should this be related to Justin's series?
> 
> If I modify the unit test and populate the missing ARP entry then the test 
> does pass.
> 
> Should this entry be present in the ARP neighbor cache in order to make the
> test pass? If yes, then why? 
> 
> Best regards,
> Zoltan
> 
> > -----Original Message-----
> > From: Ben Pfaff [mailto:blp@ovn.org]
> > Sent: Tuesday, January 23, 2018 8:02 PM
> > To: Zoltán Balogh <zoltan.balogh@ericsson.com>
> > Cc: dev@openvswitch.org; Jan Scheurich <jan.scheurich@ericsson.com>
> > Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
> > 
> > On Tue, Jan 09, 2018 at 07:54:33PM +0100, Zoltan Balogh wrote:
> > > Currenlty, OVS snoops any ARP or ND packets in any bridge and populates
> > > the tunnel neighbor cache with the retreived data. For instance, when
> > > ARP reply originated by a tenant is received in an overlay bridge, the
> > > ARP message is snooped and tunnel neighbor cache is filled with tenant
> > > data, however only tunnel neighbor data should be stored there.
> > >
> > > This patch moves tunnel neighbor snooping from do_xlate_actions() to
> > > terminate_native_tunnel() where native tunnel is terminated, in order to
> > > keep ARP neighbor cache clean, i.e. only packets comming from a native
> > > tunnel are snooped.
> > >
> > > By applying the patch, only ARP and Neighbor Advertisement messages
> > > addressing a tunnel endpoint (LOCAL port of underlay bridge) are
> > > snooped. In order to achieve this, IP addresses of the bridge are
> > > retrieved and then stored in xbridge by calling xlate_xbridge_set().
> > > These data is then matched against the data extracted from an ARP or
> > > Neighbor Advertisement message in is_neighbor_reply_correct() which is
> > > invoked from terminate_native_tunnel().
> > >
> > > Signed-off-by: Zoltan Balogh <zoltan.balogh@ericsson.com>
> > 
> > Thanks a lot!
> > 
> > It appears to me that this patch makes the following test consistently
> > fail.  When I revert the test, it passes for me again.  Can you take a
> > look?
> > 
> > Thanks,
> > 
> > Ben.
> > 
> > #                             -*- compilation -*-
> > 2370. ovn.at:8136: testing ovn -- /32 router IP address ...
> > creating ovn-sb database
> > creating ovn-nb database
> > starting ovn-northd
> > starting backup ovn-northd
> > adding simulator 'main'
> > adding simulator 'hv1'
> > adding simulator 'hv2'
> > ../../tests/ovn.at:8198: ovn_populate_arp__
> > stdout:
> > OK
> > OK
> > 
> > checking packets in hv2/vif1-tx.pcap against expected:
> > expected 1 packets, only received 0
> > ../../tests/ovn.at:8230: sort $rcv_text
> > --- expout      2018-01-23 10:59:47.231797273 -0800
> > +++ /home/blp/nicira/ovs/_build/tests/testsuite.dir/at-groups/2370/stdout       2018-01-23 10:59:47.231797273 -0800
> > @@ -1 +0,0 @@
> > -f0000001020400000001020408004500001c000000003f110100c0a801020a0000020035111100080000
> > 2370. ovn.at:8136: 2370. ovn -- /32 router IP address (ovn.at:8136): FAILED (ovn.at:8230)
> > 
> > 
> > > ---
> > >  include/sparse/netinet/in.h   |  10 +++
> > >  ofproto/ofproto-dpif-xlate.c  | 147 ++++++++++++++++++++++++++++++++++++++++--
> > >  tests/tunnel-push-pop-ipv6.at |  68 ++++++++++++++++++-
> > >  tests/tunnel-push-pop.at      |  67 ++++++++++++++++++-
> > >  4 files changed, 282 insertions(+), 10 deletions(-)
> > >
> > > diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h
> > > index 6abdb2331..eea41bd7f 100644
> > > --- a/include/sparse/netinet/in.h
> > > +++ b/include/sparse/netinet/in.h
> > > @@ -123,6 +123,16 @@ struct sockaddr_in6 {
> > >       (X)->s6_addr[10] == 0xff &&                \
> > >       (X)->s6_addr[11] == 0xff)
> > >
> > > +#define IN6_IS_ADDR_MC_LINKLOCAL(a)                 \
> > > +    (((const uint8_t *) (a))[0] == 0xff &&          \
> > > +     (((const uint8_t *) (a))[1] & 0xf) == 0x2)
> > > +
> > > +# define IN6_ARE_ADDR_EQUAL(a,b)                                          \
> > > +    ((((const uint32_t *) (a))[0] == ((const uint32_t *) (b))[0]) &&      \
> > > +     (((const uint32_t *) (a))[1] == ((const uint32_t *) (b))[1]) &&      \
> > > +     (((const uint32_t *) (a))[2] == ((const uint32_t *) (b))[2]) &&      \
> > > +     (((const uint32_t *) (a))[3] == ((const uint32_t *) (b))[3]))
> > > +
> > >  #define INET_ADDRSTRLEN 16
> > >  #define INET6_ADDRSTRLEN 46
> > >
> > > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> > > index 2328ec1da..16bc0cd96 100644
> > > --- a/ofproto/ofproto-dpif-xlate.c
> > > +++ b/ofproto/ofproto-dpif-xlate.c
> > > @@ -90,6 +90,16 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
> > >   * recursive or not. */
> > >  #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH)
> > >
> > > +/* The structure holds an array of IP addresses assigned to a bridge and the
> > > + * number of elements in the array. These data are mutable and are evaluated
> > > + * when ARP or Neighbor Advertisement packets received on a native tunnel
> > > + * port are xlated. So 'ref_cnt' and RCU are used for synchronization. */
> > > +struct xbridge_addr {
> > > +    struct in6_addr *addr;        /* Array of IP addresses of xbridge. */
> > > +    int n_addr;                   /* Number of IP addresses. */
> > > +    struct ovs_refcount ref_cnt;
> > > +};
> > > +
> > >  struct xbridge {
> > >      struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */
> > >      struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
> > > @@ -113,6 +123,8 @@ struct xbridge {
> > >
> > >      /* Datapath feature support. */
> > >      struct dpif_backer_support support;
> > > +
> > > +    struct xbridge_addr *addr;
> > >  };
> > >
> > >  struct xbundle {
> > > @@ -576,7 +588,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
> > >                                const struct dpif_ipfix *,
> > >                                const struct netflow *,
> > >                                bool forward_bpdu, bool has_in_band,
> > > -                              const struct dpif_backer_support *);
> > > +                              const struct dpif_backer_support *,
> > > +                              const struct xbridge_addr *);
> > >  static void xlate_xbundle_set(struct xbundle *xbundle,
> > >                                enum port_vlan_mode vlan_mode,
> > >                                uint16_t qinq_ethtype, int vlan,
> > > @@ -826,6 +839,56 @@ xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport)
> > >                  hash_ofp_port(xport->ofp_port));
> > >  }
> > >
> > > +static struct xbridge_addr *
> > > +xbridge_addr_create(struct xbridge *xbridge)
> > > +{
> > > +    struct xbridge_addr *xbridge_addr = xbridge->addr;
> > > +    struct in6_addr *addr = NULL, *mask = NULL;
> > > +    struct netdev *dev;
> > > +    int err, n_addr = 0;
> > > +
> > > +    err = netdev_open(xbridge->name, NULL, &dev);
> > > +    if (!err) {
> > > +        err = netdev_get_addr_list(dev, &addr, &mask, &n_addr);
> > > +        if (!err) {
> > > +            if (!xbridge->addr ||
> > > +                n_addr != xbridge->addr->n_addr ||
> > > +                (xbridge->addr->addr && memcmp(addr, xbridge->addr->addr,
> > > +                                               sizeof(*addr) * n_addr))) {
> > > +                xbridge_addr = xzalloc(sizeof *xbridge_addr);
> > > +                xbridge_addr->addr = addr;
> > > +                xbridge_addr->n_addr = n_addr;
> > > +                ovs_refcount_init(&xbridge_addr->ref_cnt);
> > > +            } else {
> > > +                free(addr);
> > > +            }
> > > +            free(mask);
> > > +        }
> > > +        netdev_close(dev);
> > > +    }
> > > +
> > > +    return xbridge_addr;
> > > +}
> > > +
> > > +static struct xbridge_addr *
> > > +xbridge_addr_ref(const struct xbridge_addr *addr_)
> > > +{
> > > +    struct xbridge_addr *addr = CONST_CAST(struct xbridge_addr *, addr_);
> > > +    if (addr) {
> > > +        ovs_refcount_ref(&addr->ref_cnt);
> > > +    }
> > > +    return addr;
> > > +}
> > > +
> > > +static void
> > > +xbridge_addr_unref(struct xbridge_addr *addr)
> > > +{
> > > +    if (addr && ovs_refcount_unref_relaxed(&addr->ref_cnt) == 1) {
> > > +        free(addr->addr);
> > > +        free(addr);
> > > +    }
> > > +}
> > > +
> > >  static void
> > >  xlate_xbridge_set(struct xbridge *xbridge,
> > >                    struct dpif *dpif,
> > > @@ -836,7 +899,8 @@ xlate_xbridge_set(struct xbridge *xbridge,
> > >                    const struct dpif_ipfix *ipfix,
> > >                    const struct netflow *netflow,
> > >                    bool forward_bpdu, bool has_in_band,
> > > -                  const struct dpif_backer_support *support)
> > > +                  const struct dpif_backer_support *support,
> > > +                  const struct xbridge_addr *addr)
> > >  {
> > >      if (xbridge->ml != ml) {
> > >          mac_learning_unref(xbridge->ml);
> > > @@ -878,6 +942,11 @@ xlate_xbridge_set(struct xbridge *xbridge,
> > >          xbridge->netflow = netflow_ref(netflow);
> > >      }
> > >
> > > +    if (xbridge->addr != addr) {
> > > +        xbridge_addr_unref(xbridge->addr);
> > > +        xbridge->addr = xbridge_addr_ref(addr);
> > > +    }
> > > +
> > >      xbridge->dpif = dpif;
> > >      xbridge->forward_bpdu = forward_bpdu;
> > >      xbridge->has_in_band = has_in_band;
> > > @@ -971,7 +1040,7 @@ xlate_xbridge_copy(struct xbridge *xbridge)
> > >                        xbridge->rstp, xbridge->ms, xbridge->mbridge,
> > >                        xbridge->sflow, xbridge->ipfix, xbridge->netflow,
> > >                        xbridge->forward_bpdu, xbridge->has_in_band,
> > > -                      &xbridge->support);
> > > +                      &xbridge->support, xbridge->addr);
> > >      LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
> > >          xlate_xbundle_copy(new_xbridge, xbundle);
> > >      }
> > > @@ -1126,6 +1195,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
> > >                    const struct dpif_backer_support *support)
> > >  {
> > >      struct xbridge *xbridge;
> > > +    struct xbridge_addr *xbridge_addr, *old_addr;
> > >
> > >      ovs_assert(new_xcfg);
> > >
> > > @@ -1140,8 +1210,16 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
> > >      free(xbridge->name);
> > >      xbridge->name = xstrdup(name);
> > >
> > > +    xbridge_addr = xbridge_addr_create(xbridge);
> > > +    old_addr = xbridge->addr;
> > > +
> > >      xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,
> > > -                      netflow, forward_bpdu, has_in_band, support);
> > > +                      netflow, forward_bpdu, has_in_band, support,
> > > +                      xbridge_addr);
> > > +
> > > +    if (xbridge_addr != old_addr) {
> > > +        xbridge_addr_unref(xbridge_addr);
> > > +    }
> > >  }
> > >
> > >  static void
> > > @@ -1171,6 +1249,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
> > >      netflow_unref(xbridge->netflow);
> > >      stp_unref(xbridge->stp);
> > >      rstp_unref(xbridge->rstp);
> > > +    xbridge_addr_unref(xbridge->addr);
> > >      hmap_destroy(&xbridge->xports);
> > >      free(xbridge->name);
> > >      free(xbridge);
> > > @@ -3655,6 +3734,54 @@ check_output_prerequisites(struct xlate_ctx *ctx,
> > >      return true;
> > >  }
> > >
> > > +/* Function verifies if destination address of received Neighbor Advertisement
> > > + * message stored in 'flow' is correct. It should be either FF02::1:FFXX:XXXX
> > > + * where XX:XXXX stands for the last 24 bits of 'ipv6_addr' or it should match
> > > + * 'ipv6_addr'. */
> > > +static bool
> > > +is_nd_dst_correct(const struct flow *flow, const struct in6_addr *ipv6_addr)
> > > +{
> > > +    const uint8_t *flow_ipv6_addr = (uint8_t *) &flow->ipv6_dst;
> > > +    const uint8_t *addr = (uint8_t *) ipv6_addr;
> > > +
> > > +    return (IN6_IS_ADDR_MC_LINKLOCAL(flow_ipv6_addr) &&
> > > +            flow_ipv6_addr[11] == 0x01 &&
> > > +            flow_ipv6_addr[12] == 0xff &&
> > > +            flow_ipv6_addr[13] == addr[13] &&
> > > +            flow_ipv6_addr[14] == addr[14] &&
> > > +            flow_ipv6_addr[15] == addr[15]) ||
> > > +            IN6_ARE_ADDR_EQUAL(&flow->ipv6_dst, ipv6_addr);
> > > +}
> > > +
> > > +/* Function verifies if the ARP reply or Neighbor Advertisement represented by
> > > + * 'flow' addresses the 'xbridge' of 'ctx'. Returns true if the ARP TA or
> > > + * neighbor discovery destination is in the list of configured IP addresses of
> > > + * the bridge. Otherwise, it returns false. */
> > > +static bool
> > > +is_neighbor_reply_correct(const struct xlate_ctx *ctx, const struct flow *flow)
> > > +{
> > > +    bool ret = false;
> > > +    int i;
> > > +    struct xbridge_addr *xbridge_addr = xbridge_addr_ref(ctx->xbridge->addr);
> > > +
> > > +    /* Verify if 'nw_dst' of ARP or 'ipv6_dst' of ICMPV6 is in the list. */
> > > +    for (i = 0; xbridge_addr && i < xbridge_addr->n_addr; i++) {
> > > +        struct in6_addr *ip_addr = &xbridge_addr->addr[i];
> > > +        if ((IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> > > +             flow->dl_type == htons(ETH_TYPE_ARP) &&
> > > +             in6_addr_get_mapped_ipv4(ip_addr) == flow->nw_dst) ||
> > > +            (!IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> > > +              is_nd_dst_correct(flow, ip_addr))) {
> > > +            /* Found a match. */
> > > +            ret = true;
> > > +            break;
> > > +        }
> > > +    }
> > > +
> > > +    xbridge_addr_unref(xbridge_addr);
> > > +    return ret;
> > > +}
> > > +
> > >  static bool
> > >  terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> > >                          struct flow *flow, struct flow_wildcards *wc,
> > > @@ -3667,6 +3794,15 @@ terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> > >      if (ofp_port == OFPP_LOCAL &&
> > >          ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
> > >          *tnl_port = tnl_port_map_lookup(flow, wc);
> > > +
> > > +        /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,
> > > +         * do tunnel neighbor snooping. */
> > > +        if (*tnl_port == ODPP_NONE &&
> > > +            (flow->dl_type == htons(ETH_TYPE_ARP) ||
> > > +             flow->nw_proto == IPPROTO_ICMPV6) &&
> > > +             is_neighbor_reply_correct(ctx, flow)) {
> > > +            tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> > > +        }
> > >      }
> > >
> > >      return *tnl_port != ODPP_NONE;
> > > @@ -6183,9 +6319,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
> > >      struct flow *flow = &ctx->xin->flow;
> > >      const struct ofpact *a;
> > >
> > > -    if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
> > > -        tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> > > -    }
> > >      /* dl_type already in the mask, not set below. */
> > >
> > >      if (!ofpacts_len) {
> > > diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
> > > index 0b9d436db..b0f4a7e91 100644
> > > --- a/tests/tunnel-push-pop-ipv6.at
> > > +++ b/tests/tunnel-push-pop-ipv6.at
> > > @@ -55,9 +55,73 @@ AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | un
> > >  ])
> > >
> > >  dnl Check ARP Snoop
> > > -AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > 00,tll=f8:bc:12:44:34:b6)'])
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > 00,tll=f8:bc:12:44:34:c8)'])
> > >
> > > -AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::
> > 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:
> > 00,tll=f8:bc:12:44:34:b7)'])
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > > +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > > +])
> > > +
> > > +dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > 99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > 00,tll=f8:bc:12:44:34:b6)'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > > +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > > +])
> > > +
> > > +dnl Receiving Neighbot Advertisement with incorrect VLAN id should not alter tunnel neighbor cache
> > > +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86
> > dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
> > d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > > +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > > +])
> > > +
> > > +dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache
> > > +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86
> > dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
> > d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > > +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > > +])
> > > +
> > > +dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache
> > > +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
> > config:hwaddr=aa:55:aa:55:00:99])
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1
> > 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe
> > ::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:0
> > 0:00,tll=f8:bc:12:44:34:c8)'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> > > +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > > +])
> > > +
> > > +dnl Receive Neighbor Advertisement without VLAN header
> > > +AT_CHECK([ovs-vsctl set port br0 tag=0])
> > > +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> > > +])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > 00,tll=f8:bc:12:44:34:b6)'])
> > > +
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff0
> > 0:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00
> > :00:00,tll=f8:bc:12:44:34:b7)'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > >
> > >  AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > >  2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > > diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
> > > index b168f5f2f..4e259730c 100644
> > > --- a/tests/tunnel-push-pop.at
> > > +++ b/tests/tunnel-push-pop.at
> > > @@ -70,9 +70,71 @@ ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101
> > >  ])
> > >
> > >  dnl Check ARP Snoop
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> > 2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > > +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > > +])
> > > +
> > > +dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> > 2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > > +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > > +])
> > > +
> > > +dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache
> > > +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(
> > eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > > +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > > +])
> > > +
> > > +dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(
> > eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > > +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > > +])
> > > +
> > > +dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache
> > > +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
> > config:hwaddr=aa:55:aa:55:00:99])
> > > +AT_CHECK([ovs-appctl netdev-dummy/receive p1
> > 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.
> > 1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > > +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> > > +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > > +])
> > > +
> > > +dnl Receive ARP reply without VLAN header
> > > +AT_CHECK([ovs-vsctl set port br0 tag=0])
> > > +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> > > +])
> > > +
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > >  AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> > 2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
> > >  AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.
> > 2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)'])
> > >
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > >  AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
> > >  1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > >  1.1.2.93                                      f8:bc:12:44:34:b7   br0
> > > @@ -190,9 +252,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl
> > >  dnl Check GREL3 only accepts non-fragmented packets?
> > >  AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a0800450
> > 00054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
> > f202122232425262728292a2b2c2d2e2f3031323334353637'])
> > >
> > > +ovs-appctl time/warp 1000
> > > +ovs-appctl time/warp 1000
> > > +
> > >  AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
> > >    port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
> > > -  port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
> > > +  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?
> > >  ])
> > >
> > >  dnl Check decapsulation of Geneve packet with options
> > > --
> > > 2.14.1
> > >
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Justin Pettit Feb. 1, 2018, 11 p.m. UTC | #4
I wasn't able to get this patch to apply to the tip of master.  Zoltan, can you rebase this patch and repost?  

The main thing my patch series does is make it so that packets that have a controller action aren't processed entirely in userspace.  If, for example, the patches expect packets to be in userspace without an explicit slow-path request when generating the datapath flow, then that would be a problem.

--Justin


> On Feb 1, 2018, at 2:17 PM, Ben Pfaff <blp@ovn.org> wrote:
> 
> Justin, I think this is mainly a question about your patches, can you
> take a look?
> 
> On Fri, Jan 26, 2018 at 01:08:35PM +0000, Zoltán Balogh wrote:
>> Hi,
>> 
>> I've been investigating the failing unit test. I can confirm, it does fail
>> with my series on master. However, when I created the series and sent it to
>> the mailing list it did not. 
>> 
>> I've rebased my series to this commit before I sent it to the mailing list.
>>  commit f59cb331c481d08f9a851c07cf31e9d826650485
>>  Author: Yi Yang <yi.y.yang@intel.com>
>>  Date:   Sat Jan 6 13:47:51 2018 +0800
>> 
>> If I apply the series to the commit below, the test does pass.
>> 
>> So, I started to search for any commits between the one from Yi Yang and the 
>> Last one on master which could make my series fail on the OVN unit test 
>> "/32 router IP address".
>> 
>> I found, that reverting the series of two commits from Justin Pettit below,
>> makes my series pass the unit test again. Even rebased to master.
>> 
>>  commit 74c4530dca939109f3fb79776b60b8722e149738
>>  Author: Justin Pettit <jpettit@ovn.org>
>>  Date:   Wed Oct 18 23:16:22 2017 -0700
>> 
>>      ofproto-dpif: Don't slow-path controller actions with pause.
>> 
>>      A previous patch removed slow-pathing for controller actions with the
>>      exception of ones that specified "pause".  This commit removes that
>>      restriction so that no controller actions are slow-pathed.
>> 
>>      Signed-off-by: Justin Pettit <jpettit@ovn.org>
>>      Acked-by: Ben Pfaff <blp@ovn.org>
>> 
>>  commit d39ec23de38464ee35b3098b9f6c5f06d5191015
>>  Author: Justin Pettit <jpettit@ovn.org>
>>  Date:   Wed Jul 5 15:17:52 2017 -0700
>> 
>>      ofproto-dpif: Don't slow-path controller actions.
>> 
>>      Controller actions have become more commonly used for purposes other
>>      than just making forwarding decisions (e.g., packet logging).  A packet
>>      that needs to be copied to the controller and forwarded would always be
>>      sent to ovs-vswitchd to be handled, which could negatively affect
>>      performance and cause heavier CPU utilization in ovs-vswitchd.
>> 
>>      This commit changes the behavior so that OpenFlow controller actions
>>      become userspace datapath actions while continuing to let packet
>>      forwarding and manipulation continue to be handled by the datapath
>>      directly.
>> 
>>      This patch still slow-paths controller actions with the "pause" flag
>>      set.  A future patch will stop slow-pathing these pause actions as
>>      well.
>> 
>>      Signed-off-by: Justin Pettit <jpettit@ovn.org>
>>      Acked-by: Ben Pfaff <blp@ovn.org>
>> 
>> The main difference, my series does compared to master, is not putting the 
>> ARP entry of {10.0.0.2, f0:00:00:01:02:04, br-int} into tunnel neighbor 
>> cache. All the OF rules are almost the same, except loading different 
>> numbers into NXM_NX_REGx and using different metadata. 
>> 
>> How should this be related to Justin's series?
>> 
>> If I modify the unit test and populate the missing ARP entry then the test 
>> does pass.
>> 
>> Should this entry be present in the ARP neighbor cache in order to make the
>> test pass? If yes, then why? 
>> 
>> Best regards,
>> Zoltan
>> 
>>> -----Original Message-----
>>> From: Ben Pfaff [mailto:blp@ovn.org]
>>> Sent: Tuesday, January 23, 2018 8:02 PM
>>> To: Zoltán Balogh <zoltan.balogh@ericsson.com>
>>> Cc: dev@openvswitch.org; Jan Scheurich <jan.scheurich@ericsson.com>
>>> Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
>>> 
>>> On Tue, Jan 09, 2018 at 07:54:33PM +0100, Zoltan Balogh wrote:
>>>> Currenlty, OVS snoops any ARP or ND packets in any bridge and populates
>>>> the tunnel neighbor cache with the retreived data. For instance, when
>>>> ARP reply originated by a tenant is received in an overlay bridge, the
>>>> ARP message is snooped and tunnel neighbor cache is filled with tenant
>>>> data, however only tunnel neighbor data should be stored there.
>>>> 
>>>> This patch moves tunnel neighbor snooping from do_xlate_actions() to
>>>> terminate_native_tunnel() where native tunnel is terminated, in order to
>>>> keep ARP neighbor cache clean, i.e. only packets comming from a native
>>>> tunnel are snooped.
>>>> 
>>>> By applying the patch, only ARP and Neighbor Advertisement messages
>>>> addressing a tunnel endpoint (LOCAL port of underlay bridge) are
>>>> snooped. In order to achieve this, IP addresses of the bridge are
>>>> retrieved and then stored in xbridge by calling xlate_xbridge_set().
>>>> These data is then matched against the data extracted from an ARP or
>>>> Neighbor Advertisement message in is_neighbor_reply_correct() which is
>>>> invoked from terminate_native_tunnel().
>>>> 
>>>> Signed-off-by: Zoltan Balogh <zoltan.balogh@ericsson.com>
>>> 
>>> Thanks a lot!
>>> 
>>> It appears to me that this patch makes the following test consistently
>>> fail.  When I revert the test, it passes for me again.  Can you take a
>>> look?
>>> 
>>> Thanks,
>>> 
>>> Ben.
>>> 
>>> #                             -*- compilation -*-
>>> 2370. ovn.at:8136: testing ovn -- /32 router IP address ...
>>> creating ovn-sb database
>>> creating ovn-nb database
>>> starting ovn-northd
>>> starting backup ovn-northd
>>> adding simulator 'main'
>>> adding simulator 'hv1'
>>> adding simulator 'hv2'
>>> ../../tests/ovn.at:8198: ovn_populate_arp__
>>> stdout:
>>> OK
>>> OK
>>> 
>>> checking packets in hv2/vif1-tx.pcap against expected:
>>> expected 1 packets, only received 0
>>> ../../tests/ovn.at:8230: sort $rcv_text
>>> --- expout      2018-01-23 10:59:47.231797273 -0800
>>> +++ /home/blp/nicira/ovs/_build/tests/testsuite.dir/at-groups/2370/stdout       2018-01-23 10:59:47.231797273 -0800
>>> @@ -1 +0,0 @@
>>> -f0000001020400000001020408004500001c000000003f110100c0a801020a0000020035111100080000
>>> 2370. ovn.at:8136: 2370. ovn -- /32 router IP address (ovn.at:8136): FAILED (ovn.at:8230)
>>> 
>>> 
>>>> ---
>>>> include/sparse/netinet/in.h   |  10 +++
>>>> ofproto/ofproto-dpif-xlate.c  | 147 ++++++++++++++++++++++++++++++++++++++++--
>>>> tests/tunnel-push-pop-ipv6.at |  68 ++++++++++++++++++-
>>>> tests/tunnel-push-pop.at      |  67 ++++++++++++++++++-
>>>> 4 files changed, 282 insertions(+), 10 deletions(-)
>>>> 
>>>> diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h
>>>> index 6abdb2331..eea41bd7f 100644
>>>> --- a/include/sparse/netinet/in.h
>>>> +++ b/include/sparse/netinet/in.h
>>>> @@ -123,6 +123,16 @@ struct sockaddr_in6 {
>>>>      (X)->s6_addr[10] == 0xff &&                \
>>>>      (X)->s6_addr[11] == 0xff)
>>>> 
>>>> +#define IN6_IS_ADDR_MC_LINKLOCAL(a)                 \
>>>> +    (((const uint8_t *) (a))[0] == 0xff &&          \
>>>> +     (((const uint8_t *) (a))[1] & 0xf) == 0x2)
>>>> +
>>>> +# define IN6_ARE_ADDR_EQUAL(a,b)                                          \
>>>> +    ((((const uint32_t *) (a))[0] == ((const uint32_t *) (b))[0]) &&      \
>>>> +     (((const uint32_t *) (a))[1] == ((const uint32_t *) (b))[1]) &&      \
>>>> +     (((const uint32_t *) (a))[2] == ((const uint32_t *) (b))[2]) &&      \
>>>> +     (((const uint32_t *) (a))[3] == ((const uint32_t *) (b))[3]))
>>>> +
>>>> #define INET_ADDRSTRLEN 16
>>>> #define INET6_ADDRSTRLEN 46
>>>> 
>>>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>>>> index 2328ec1da..16bc0cd96 100644
>>>> --- a/ofproto/ofproto-dpif-xlate.c
>>>> +++ b/ofproto/ofproto-dpif-xlate.c
>>>> @@ -90,6 +90,16 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
>>>>  * recursive or not. */
>>>> #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH)
>>>> 
>>>> +/* The structure holds an array of IP addresses assigned to a bridge and the
>>>> + * number of elements in the array. These data are mutable and are evaluated
>>>> + * when ARP or Neighbor Advertisement packets received on a native tunnel
>>>> + * port are xlated. So 'ref_cnt' and RCU are used for synchronization. */
>>>> +struct xbridge_addr {
>>>> +    struct in6_addr *addr;        /* Array of IP addresses of xbridge. */
>>>> +    int n_addr;                   /* Number of IP addresses. */
>>>> +    struct ovs_refcount ref_cnt;
>>>> +};
>>>> +
>>>> struct xbridge {
>>>>     struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */
>>>>     struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
>>>> @@ -113,6 +123,8 @@ struct xbridge {
>>>> 
>>>>     /* Datapath feature support. */
>>>>     struct dpif_backer_support support;
>>>> +
>>>> +    struct xbridge_addr *addr;
>>>> };
>>>> 
>>>> struct xbundle {
>>>> @@ -576,7 +588,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
>>>>                               const struct dpif_ipfix *,
>>>>                               const struct netflow *,
>>>>                               bool forward_bpdu, bool has_in_band,
>>>> -                              const struct dpif_backer_support *);
>>>> +                              const struct dpif_backer_support *,
>>>> +                              const struct xbridge_addr *);
>>>> static void xlate_xbundle_set(struct xbundle *xbundle,
>>>>                               enum port_vlan_mode vlan_mode,
>>>>                               uint16_t qinq_ethtype, int vlan,
>>>> @@ -826,6 +839,56 @@ xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport)
>>>>                 hash_ofp_port(xport->ofp_port));
>>>> }
>>>> 
>>>> +static struct xbridge_addr *
>>>> +xbridge_addr_create(struct xbridge *xbridge)
>>>> +{
>>>> +    struct xbridge_addr *xbridge_addr = xbridge->addr;
>>>> +    struct in6_addr *addr = NULL, *mask = NULL;
>>>> +    struct netdev *dev;
>>>> +    int err, n_addr = 0;
>>>> +
>>>> +    err = netdev_open(xbridge->name, NULL, &dev);
>>>> +    if (!err) {
>>>> +        err = netdev_get_addr_list(dev, &addr, &mask, &n_addr);
>>>> +        if (!err) {
>>>> +            if (!xbridge->addr ||
>>>> +                n_addr != xbridge->addr->n_addr ||
>>>> +                (xbridge->addr->addr && memcmp(addr, xbridge->addr->addr,
>>>> +                                               sizeof(*addr) * n_addr))) {
>>>> +                xbridge_addr = xzalloc(sizeof *xbridge_addr);
>>>> +                xbridge_addr->addr = addr;
>>>> +                xbridge_addr->n_addr = n_addr;
>>>> +                ovs_refcount_init(&xbridge_addr->ref_cnt);
>>>> +            } else {
>>>> +                free(addr);
>>>> +            }
>>>> +            free(mask);
>>>> +        }
>>>> +        netdev_close(dev);
>>>> +    }
>>>> +
>>>> +    return xbridge_addr;
>>>> +}
>>>> +
>>>> +static struct xbridge_addr *
>>>> +xbridge_addr_ref(const struct xbridge_addr *addr_)
>>>> +{
>>>> +    struct xbridge_addr *addr = CONST_CAST(struct xbridge_addr *, addr_);
>>>> +    if (addr) {
>>>> +        ovs_refcount_ref(&addr->ref_cnt);
>>>> +    }
>>>> +    return addr;
>>>> +}
>>>> +
>>>> +static void
>>>> +xbridge_addr_unref(struct xbridge_addr *addr)
>>>> +{
>>>> +    if (addr && ovs_refcount_unref_relaxed(&addr->ref_cnt) == 1) {
>>>> +        free(addr->addr);
>>>> +        free(addr);
>>>> +    }
>>>> +}
>>>> +
>>>> static void
>>>> xlate_xbridge_set(struct xbridge *xbridge,
>>>>                   struct dpif *dpif,
>>>> @@ -836,7 +899,8 @@ xlate_xbridge_set(struct xbridge *xbridge,
>>>>                   const struct dpif_ipfix *ipfix,
>>>>                   const struct netflow *netflow,
>>>>                   bool forward_bpdu, bool has_in_band,
>>>> -                  const struct dpif_backer_support *support)
>>>> +                  const struct dpif_backer_support *support,
>>>> +                  const struct xbridge_addr *addr)
>>>> {
>>>>     if (xbridge->ml != ml) {
>>>>         mac_learning_unref(xbridge->ml);
>>>> @@ -878,6 +942,11 @@ xlate_xbridge_set(struct xbridge *xbridge,
>>>>         xbridge->netflow = netflow_ref(netflow);
>>>>     }
>>>> 
>>>> +    if (xbridge->addr != addr) {
>>>> +        xbridge_addr_unref(xbridge->addr);
>>>> +        xbridge->addr = xbridge_addr_ref(addr);
>>>> +    }
>>>> +
>>>>     xbridge->dpif = dpif;
>>>>     xbridge->forward_bpdu = forward_bpdu;
>>>>     xbridge->has_in_band = has_in_band;
>>>> @@ -971,7 +1040,7 @@ xlate_xbridge_copy(struct xbridge *xbridge)
>>>>                       xbridge->rstp, xbridge->ms, xbridge->mbridge,
>>>>                       xbridge->sflow, xbridge->ipfix, xbridge->netflow,
>>>>                       xbridge->forward_bpdu, xbridge->has_in_band,
>>>> -                      &xbridge->support);
>>>> +                      &xbridge->support, xbridge->addr);
>>>>     LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
>>>>         xlate_xbundle_copy(new_xbridge, xbundle);
>>>>     }
>>>> @@ -1126,6 +1195,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
>>>>                   const struct dpif_backer_support *support)
>>>> {
>>>>     struct xbridge *xbridge;
>>>> +    struct xbridge_addr *xbridge_addr, *old_addr;
>>>> 
>>>>     ovs_assert(new_xcfg);
>>>> 
>>>> @@ -1140,8 +1210,16 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
>>>>     free(xbridge->name);
>>>>     xbridge->name = xstrdup(name);
>>>> 
>>>> +    xbridge_addr = xbridge_addr_create(xbridge);
>>>> +    old_addr = xbridge->addr;
>>>> +
>>>>     xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,
>>>> -                      netflow, forward_bpdu, has_in_band, support);
>>>> +                      netflow, forward_bpdu, has_in_band, support,
>>>> +                      xbridge_addr);
>>>> +
>>>> +    if (xbridge_addr != old_addr) {
>>>> +        xbridge_addr_unref(xbridge_addr);
>>>> +    }
>>>> }
>>>> 
>>>> static void
>>>> @@ -1171,6 +1249,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
>>>>     netflow_unref(xbridge->netflow);
>>>>     stp_unref(xbridge->stp);
>>>>     rstp_unref(xbridge->rstp);
>>>> +    xbridge_addr_unref(xbridge->addr);
>>>>     hmap_destroy(&xbridge->xports);
>>>>     free(xbridge->name);
>>>>     free(xbridge);
>>>> @@ -3655,6 +3734,54 @@ check_output_prerequisites(struct xlate_ctx *ctx,
>>>>     return true;
>>>> }
>>>> 
>>>> +/* Function verifies if destination address of received Neighbor Advertisement
>>>> + * message stored in 'flow' is correct. It should be either FF02::1:FFXX:XXXX
>>>> + * where XX:XXXX stands for the last 24 bits of 'ipv6_addr' or it should match
>>>> + * 'ipv6_addr'. */
>>>> +static bool
>>>> +is_nd_dst_correct(const struct flow *flow, const struct in6_addr *ipv6_addr)
>>>> +{
>>>> +    const uint8_t *flow_ipv6_addr = (uint8_t *) &flow->ipv6_dst;
>>>> +    const uint8_t *addr = (uint8_t *) ipv6_addr;
>>>> +
>>>> +    return (IN6_IS_ADDR_MC_LINKLOCAL(flow_ipv6_addr) &&
>>>> +            flow_ipv6_addr[11] == 0x01 &&
>>>> +            flow_ipv6_addr[12] == 0xff &&
>>>> +            flow_ipv6_addr[13] == addr[13] &&
>>>> +            flow_ipv6_addr[14] == addr[14] &&
>>>> +            flow_ipv6_addr[15] == addr[15]) ||
>>>> +            IN6_ARE_ADDR_EQUAL(&flow->ipv6_dst, ipv6_addr);
>>>> +}
>>>> +
>>>> +/* Function verifies if the ARP reply or Neighbor Advertisement represented by
>>>> + * 'flow' addresses the 'xbridge' of 'ctx'. Returns true if the ARP TA or
>>>> + * neighbor discovery destination is in the list of configured IP addresses of
>>>> + * the bridge. Otherwise, it returns false. */
>>>> +static bool
>>>> +is_neighbor_reply_correct(const struct xlate_ctx *ctx, const struct flow *flow)
>>>> +{
>>>> +    bool ret = false;
>>>> +    int i;
>>>> +    struct xbridge_addr *xbridge_addr = xbridge_addr_ref(ctx->xbridge->addr);
>>>> +
>>>> +    /* Verify if 'nw_dst' of ARP or 'ipv6_dst' of ICMPV6 is in the list. */
>>>> +    for (i = 0; xbridge_addr && i < xbridge_addr->n_addr; i++) {
>>>> +        struct in6_addr *ip_addr = &xbridge_addr->addr[i];
>>>> +        if ((IN6_IS_ADDR_V4MAPPED(ip_addr) &&
>>>> +             flow->dl_type == htons(ETH_TYPE_ARP) &&
>>>> +             in6_addr_get_mapped_ipv4(ip_addr) == flow->nw_dst) ||
>>>> +            (!IN6_IS_ADDR_V4MAPPED(ip_addr) &&
>>>> +              is_nd_dst_correct(flow, ip_addr))) {
>>>> +            /* Found a match. */
>>>> +            ret = true;
>>>> +            break;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    xbridge_addr_unref(xbridge_addr);
>>>> +    return ret;
>>>> +}
>>>> +
>>>> static bool
>>>> terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
>>>>                         struct flow *flow, struct flow_wildcards *wc,
>>>> @@ -3667,6 +3794,15 @@ terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
>>>>     if (ofp_port == OFPP_LOCAL &&
>>>>         ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
>>>>         *tnl_port = tnl_port_map_lookup(flow, wc);
>>>> +
>>>> +        /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,
>>>> +         * do tunnel neighbor snooping. */
>>>> +        if (*tnl_port == ODPP_NONE &&
>>>> +            (flow->dl_type == htons(ETH_TYPE_ARP) ||
>>>> +             flow->nw_proto == IPPROTO_ICMPV6) &&
>>>> +             is_neighbor_reply_correct(ctx, flow)) {
>>>> +            tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
>>>> +        }
>>>>     }
>>>> 
>>>>     return *tnl_port != ODPP_NONE;
>>>> @@ -6183,9 +6319,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>>>>     struct flow *flow = &ctx->xin->flow;
>>>>     const struct ofpact *a;
>>>> 
>>>> -    if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
>>>> -        tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
>>>> -    }
>>>>     /* dl_type already in the mask, not set below. */
>>>> 
>>>>     if (!ofpacts_len) {
>>>> diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
>>>> index 0b9d436db..b0f4a7e91 100644
>>>> --- a/tests/tunnel-push-pop-ipv6.at
>>>> +++ b/tests/tunnel-push-pop-ipv6.at
>>>> @@ -55,9 +55,73 @@ AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | un
>>>> ])
>>>> 
>>>> dnl Check ARP Snoop
>>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
>>> 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
>>> 00,tll=f8:bc:12:44:34:b6)'])
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
>>> 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
>>> 00,tll=f8:bc:12:44:34:c8)'])
>>>> 
>>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::
>>> 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:
>>> 00,tll=f8:bc:12:44:34:b7)'])
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
>>>> +])
>>>> +
>>>> +dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
>>> 99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
>>> 00,tll=f8:bc:12:44:34:b6)'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
>>>> +])
>>>> +
>>>> +dnl Receiving Neighbot Advertisement with incorrect VLAN id should not alter tunnel neighbor cache
>>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86
>>> dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
>>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
>>>> +])
>>>> +
>>>> +dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache
>>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86
>>> dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
>>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
>>>> +])
>>>> +
>>>> +dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache
>>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
>>> config:hwaddr=aa:55:aa:55:00:99])
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1
>>> 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe
>>> ::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:0
>>> 0:00,tll=f8:bc:12:44:34:c8)'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
>>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
>>>> +])
>>>> +
>>>> +dnl Receive Neighbor Advertisement without VLAN header
>>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])
>>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
>>>> +])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
>>> 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
>>> 00,tll=f8:bc:12:44:34:b6)'])
>>>> +
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff0
>>> 0:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00
>>> :00:00,tll=f8:bc:12:44:34:b7)'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> 
>>>> AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>>>> 2001:cafe::92                                 f8:bc:12:44:34:b6   br0
>>>> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
>>>> index b168f5f2f..4e259730c 100644
>>>> --- a/tests/tunnel-push-pop.at
>>>> +++ b/tests/tunnel-push-pop.at
>>>> @@ -70,9 +70,71 @@ ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101
>>>> ])
>>>> 
>>>> dnl Check ARP Snoop
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
>>> 2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
>>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
>>>> +])
>>>> +
>>>> +dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
>>> 2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
>>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
>>>> +])
>>>> +
>>>> +dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache
>>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(
>>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
>>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
>>>> +])
>>>> +
>>>> +dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(
>>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
>>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
>>>> +])
>>>> +
>>>> +dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache
>>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
>>> config:hwaddr=aa:55:aa:55:00:99])
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1
>>> 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.
>>> 1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
>>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
>>>> +])
>>>> +
>>>> +dnl Receive ARP reply without VLAN header
>>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])
>>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
>>>> +])
>>>> +
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
>>> 2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
>>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.
>>> 2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)'])
>>>> 
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
>>>> 1.1.2.92                                      f8:bc:12:44:34:b6   br0
>>>> 1.1.2.93                                      f8:bc:12:44:34:b7   br0
>>>> @@ -190,9 +252,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl
>>>> dnl Check GREL3 only accepts non-fragmented packets?
>>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a0800450
>>> 00054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
>>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>>> 
>>>> +ovs-appctl time/warp 1000
>>>> +ovs-appctl time/warp 1000
>>>> +
>>>> AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
>>>>   port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
>>>> -  port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
>>>> +  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?
>>>> ])
>>>> 
>>>> dnl Check decapsulation of Geneve packet with options
>>>> --
>>>> 2.14.1
>>>> 
>>>> _______________________________________________
>>>> dev mailing list
>>>> dev@openvswitch.org
>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Zoltan Balogh Feb. 2, 2018, 2:42 p.m. UTC | #5
Hi Justin,

I rebased the patches to recent master. Please find them attached.

Best regards,
Zoltan

> -----Original Message-----

> From: Justin Pettit [mailto:jpettit@ovn.org]

> Sent: Friday, February 02, 2018 12:00 AM

> To: Ben Pfaff <blp@ovn.org>

> Cc: Zoltán Balogh <zoltan.balogh@ericsson.com>; guru@ovn.org; dev@openvswitch.org; Jan Scheurich

> <jan.scheurich@ericsson.com>

> Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()

> 

> I wasn't able to get this patch to apply to the tip of master.  Zoltan, can you rebase this patch and repost?

> 

> The main thing my patch series does is make it so that packets that have a controller action aren't processed

> entirely in userspace.  If, for example, the patches expect packets to be in userspace without an explicit slow-path

> request when generating the datapath flow, then that would be a problem.

> 

> --Justin

> 

> 

> > On Feb 1, 2018, at 2:17 PM, Ben Pfaff <blp@ovn.org> wrote:

> >

> > Justin, I think this is mainly a question about your patches, can you

> > take a look?

> >

> > On Fri, Jan 26, 2018 at 01:08:35PM +0000, Zoltán Balogh wrote:

> >> Hi,

> >>

> >> I've been investigating the failing unit test. I can confirm, it does fail

> >> with my series on master. However, when I created the series and sent it to

> >> the mailing list it did not.

> >>

> >> I've rebased my series to this commit before I sent it to the mailing list.

> >>  commit f59cb331c481d08f9a851c07cf31e9d826650485

> >>  Author: Yi Yang <yi.y.yang@intel.com>

> >>  Date:   Sat Jan 6 13:47:51 2018 +0800

> >>

> >> If I apply the series to the commit below, the test does pass.

> >>

> >> So, I started to search for any commits between the one from Yi Yang and the

> >> Last one on master which could make my series fail on the OVN unit test

> >> "/32 router IP address".

> >>

> >> I found, that reverting the series of two commits from Justin Pettit below,

> >> makes my series pass the unit test again. Even rebased to master.

> >>

> >>  commit 74c4530dca939109f3fb79776b60b8722e149738

> >>  Author: Justin Pettit <jpettit@ovn.org>

> >>  Date:   Wed Oct 18 23:16:22 2017 -0700

> >>

> >>      ofproto-dpif: Don't slow-path controller actions with pause.

> >>

> >>      A previous patch removed slow-pathing for controller actions with the

> >>      exception of ones that specified "pause".  This commit removes that

> >>      restriction so that no controller actions are slow-pathed.

> >>

> >>      Signed-off-by: Justin Pettit <jpettit@ovn.org>

> >>      Acked-by: Ben Pfaff <blp@ovn.org>

> >>

> >>  commit d39ec23de38464ee35b3098b9f6c5f06d5191015

> >>  Author: Justin Pettit <jpettit@ovn.org>

> >>  Date:   Wed Jul 5 15:17:52 2017 -0700

> >>

> >>      ofproto-dpif: Don't slow-path controller actions.

> >>

> >>      Controller actions have become more commonly used for purposes other

> >>      than just making forwarding decisions (e.g., packet logging).  A packet

> >>      that needs to be copied to the controller and forwarded would always be

> >>      sent to ovs-vswitchd to be handled, which could negatively affect

> >>      performance and cause heavier CPU utilization in ovs-vswitchd.

> >>

> >>      This commit changes the behavior so that OpenFlow controller actions

> >>      become userspace datapath actions while continuing to let packet

> >>      forwarding and manipulation continue to be handled by the datapath

> >>      directly.

> >>

> >>      This patch still slow-paths controller actions with the "pause" flag

> >>      set.  A future patch will stop slow-pathing these pause actions as

> >>      well.

> >>

> >>      Signed-off-by: Justin Pettit <jpettit@ovn.org>

> >>      Acked-by: Ben Pfaff <blp@ovn.org>

> >>

> >> The main difference, my series does compared to master, is not putting the

> >> ARP entry of {10.0.0.2, f0:00:00:01:02:04, br-int} into tunnel neighbor

> >> cache. All the OF rules are almost the same, except loading different

> >> numbers into NXM_NX_REGx and using different metadata.

> >>

> >> How should this be related to Justin's series?

> >>

> >> If I modify the unit test and populate the missing ARP entry then the test

> >> does pass.

> >>

> >> Should this entry be present in the ARP neighbor cache in order to make the

> >> test pass? If yes, then why?

> >>

> >> Best regards,

> >> Zoltan

> >>

> >>> -----Original Message-----

> >>> From: Ben Pfaff [mailto:blp@ovn.org]

> >>> Sent: Tuesday, January 23, 2018 8:02 PM

> >>> To: Zoltán Balogh <zoltan.balogh@ericsson.com>

> >>> Cc: dev@openvswitch.org; Jan Scheurich <jan.scheurich@ericsson.com>

> >>> Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()

> >>>

> >>> On Tue, Jan 09, 2018 at 07:54:33PM +0100, Zoltan Balogh wrote:

> >>>> Currenlty, OVS snoops any ARP or ND packets in any bridge and populates

> >>>> the tunnel neighbor cache with the retreived data. For instance, when

> >>>> ARP reply originated by a tenant is received in an overlay bridge, the

> >>>> ARP message is snooped and tunnel neighbor cache is filled with tenant

> >>>> data, however only tunnel neighbor data should be stored there.

> >>>>

> >>>> This patch moves tunnel neighbor snooping from do_xlate_actions() to

> >>>> terminate_native_tunnel() where native tunnel is terminated, in order to

> >>>> keep ARP neighbor cache clean, i.e. only packets comming from a native

> >>>> tunnel are snooped.

> >>>>

> >>>> By applying the patch, only ARP and Neighbor Advertisement messages

> >>>> addressing a tunnel endpoint (LOCAL port of underlay bridge) are

> >>>> snooped. In order to achieve this, IP addresses of the bridge are

> >>>> retrieved and then stored in xbridge by calling xlate_xbridge_set().

> >>>> These data is then matched against the data extracted from an ARP or

> >>>> Neighbor Advertisement message in is_neighbor_reply_correct() which is

> >>>> invoked from terminate_native_tunnel().

> >>>>

> >>>> Signed-off-by: Zoltan Balogh <zoltan.balogh@ericsson.com>

> >>>

> >>> Thanks a lot!

> >>>

> >>> It appears to me that this patch makes the following test consistently

> >>> fail.  When I revert the test, it passes for me again.  Can you take a

> >>> look?

> >>>

> >>> Thanks,

> >>>

> >>> Ben.

> >>>

> >>> #                             -*- compilation -*-

> >>> 2370. ovn.at:8136: testing ovn -- /32 router IP address ...

> >>> creating ovn-sb database

> >>> creating ovn-nb database

> >>> starting ovn-northd

> >>> starting backup ovn-northd

> >>> adding simulator 'main'

> >>> adding simulator 'hv1'

> >>> adding simulator 'hv2'

> >>> ../../tests/ovn.at:8198: ovn_populate_arp__

> >>> stdout:

> >>> OK

> >>> OK

> >>>

> >>> checking packets in hv2/vif1-tx.pcap against expected:

> >>> expected 1 packets, only received 0

> >>> ../../tests/ovn.at:8230: sort $rcv_text

> >>> --- expout      2018-01-23 10:59:47.231797273 -0800

> >>> +++ /home/blp/nicira/ovs/_build/tests/testsuite.dir/at-groups/2370/stdout       2018-01-23 10:59:47.231797273 -

> 0800

> >>> @@ -1 +0,0 @@

> >>> -f0000001020400000001020408004500001c000000003f110100c0a801020a0000020035111100080000

> >>> 2370. ovn.at:8136: 2370. ovn -- /32 router IP address (ovn.at:8136): FAILED (ovn.at:8230)

> >>>

> >>>

> >>>> ---

> >>>> include/sparse/netinet/in.h   |  10 +++

> >>>> ofproto/ofproto-dpif-xlate.c  | 147 ++++++++++++++++++++++++++++++++++++++++--

> >>>> tests/tunnel-push-pop-ipv6.at |  68 ++++++++++++++++++-

> >>>> tests/tunnel-push-pop.at      |  67 ++++++++++++++++++-

> >>>> 4 files changed, 282 insertions(+), 10 deletions(-)

> >>>>

> >>>> diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h

> >>>> index 6abdb2331..eea41bd7f 100644

> >>>> --- a/include/sparse/netinet/in.h

> >>>> +++ b/include/sparse/netinet/in.h

> >>>> @@ -123,6 +123,16 @@ struct sockaddr_in6 {

> >>>>      (X)->s6_addr[10] == 0xff &&                \

> >>>>      (X)->s6_addr[11] == 0xff)

> >>>>

> >>>> +#define IN6_IS_ADDR_MC_LINKLOCAL(a)                 \

> >>>> +    (((const uint8_t *) (a))[0] == 0xff &&          \

> >>>> +     (((const uint8_t *) (a))[1] & 0xf) == 0x2)

> >>>> +

> >>>> +# define IN6_ARE_ADDR_EQUAL(a,b)                                          \

> >>>> +    ((((const uint32_t *) (a))[0] == ((const uint32_t *) (b))[0]) &&      \

> >>>> +     (((const uint32_t *) (a))[1] == ((const uint32_t *) (b))[1]) &&      \

> >>>> +     (((const uint32_t *) (a))[2] == ((const uint32_t *) (b))[2]) &&      \

> >>>> +     (((const uint32_t *) (a))[3] == ((const uint32_t *) (b))[3]))

> >>>> +

> >>>> #define INET_ADDRSTRLEN 16

> >>>> #define INET6_ADDRSTRLEN 46

> >>>>

> >>>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c

> >>>> index 2328ec1da..16bc0cd96 100644

> >>>> --- a/ofproto/ofproto-dpif-xlate.c

> >>>> +++ b/ofproto/ofproto-dpif-xlate.c

> >>>> @@ -90,6 +90,16 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);

> >>>>  * recursive or not. */

> >>>> #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH)

> >>>>

> >>>> +/* The structure holds an array of IP addresses assigned to a bridge and the

> >>>> + * number of elements in the array. These data are mutable and are evaluated

> >>>> + * when ARP or Neighbor Advertisement packets received on a native tunnel

> >>>> + * port are xlated. So 'ref_cnt' and RCU are used for synchronization. */

> >>>> +struct xbridge_addr {

> >>>> +    struct in6_addr *addr;        /* Array of IP addresses of xbridge. */

> >>>> +    int n_addr;                   /* Number of IP addresses. */

> >>>> +    struct ovs_refcount ref_cnt;

> >>>> +};

> >>>> +

> >>>> struct xbridge {

> >>>>     struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */

> >>>>     struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */

> >>>> @@ -113,6 +123,8 @@ struct xbridge {

> >>>>

> >>>>     /* Datapath feature support. */

> >>>>     struct dpif_backer_support support;

> >>>> +

> >>>> +    struct xbridge_addr *addr;

> >>>> };

> >>>>

> >>>> struct xbundle {

> >>>> @@ -576,7 +588,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,

> >>>>                               const struct dpif_ipfix *,

> >>>>                               const struct netflow *,

> >>>>                               bool forward_bpdu, bool has_in_band,

> >>>> -                              const struct dpif_backer_support *);

> >>>> +                              const struct dpif_backer_support *,

> >>>> +                              const struct xbridge_addr *);

> >>>> static void xlate_xbundle_set(struct xbundle *xbundle,

> >>>>                               enum port_vlan_mode vlan_mode,

> >>>>                               uint16_t qinq_ethtype, int vlan,

> >>>> @@ -826,6 +839,56 @@ xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport)

> >>>>                 hash_ofp_port(xport->ofp_port));

> >>>> }

> >>>>

> >>>> +static struct xbridge_addr *

> >>>> +xbridge_addr_create(struct xbridge *xbridge)

> >>>> +{

> >>>> +    struct xbridge_addr *xbridge_addr = xbridge->addr;

> >>>> +    struct in6_addr *addr = NULL, *mask = NULL;

> >>>> +    struct netdev *dev;

> >>>> +    int err, n_addr = 0;

> >>>> +

> >>>> +    err = netdev_open(xbridge->name, NULL, &dev);

> >>>> +    if (!err) {

> >>>> +        err = netdev_get_addr_list(dev, &addr, &mask, &n_addr);

> >>>> +        if (!err) {

> >>>> +            if (!xbridge->addr ||

> >>>> +                n_addr != xbridge->addr->n_addr ||

> >>>> +                (xbridge->addr->addr && memcmp(addr, xbridge->addr->addr,

> >>>> +                                               sizeof(*addr) * n_addr))) {

> >>>> +                xbridge_addr = xzalloc(sizeof *xbridge_addr);

> >>>> +                xbridge_addr->addr = addr;

> >>>> +                xbridge_addr->n_addr = n_addr;

> >>>> +                ovs_refcount_init(&xbridge_addr->ref_cnt);

> >>>> +            } else {

> >>>> +                free(addr);

> >>>> +            }

> >>>> +            free(mask);

> >>>> +        }

> >>>> +        netdev_close(dev);

> >>>> +    }

> >>>> +

> >>>> +    return xbridge_addr;

> >>>> +}

> >>>> +

> >>>> +static struct xbridge_addr *

> >>>> +xbridge_addr_ref(const struct xbridge_addr *addr_)

> >>>> +{

> >>>> +    struct xbridge_addr *addr = CONST_CAST(struct xbridge_addr *, addr_);

> >>>> +    if (addr) {

> >>>> +        ovs_refcount_ref(&addr->ref_cnt);

> >>>> +    }

> >>>> +    return addr;

> >>>> +}

> >>>> +

> >>>> +static void

> >>>> +xbridge_addr_unref(struct xbridge_addr *addr)

> >>>> +{

> >>>> +    if (addr && ovs_refcount_unref_relaxed(&addr->ref_cnt) == 1) {

> >>>> +        free(addr->addr);

> >>>> +        free(addr);

> >>>> +    }

> >>>> +}

> >>>> +

> >>>> static void

> >>>> xlate_xbridge_set(struct xbridge *xbridge,

> >>>>                   struct dpif *dpif,

> >>>> @@ -836,7 +899,8 @@ xlate_xbridge_set(struct xbridge *xbridge,

> >>>>                   const struct dpif_ipfix *ipfix,

> >>>>                   const struct netflow *netflow,

> >>>>                   bool forward_bpdu, bool has_in_band,

> >>>> -                  const struct dpif_backer_support *support)

> >>>> +                  const struct dpif_backer_support *support,

> >>>> +                  const struct xbridge_addr *addr)

> >>>> {

> >>>>     if (xbridge->ml != ml) {

> >>>>         mac_learning_unref(xbridge->ml);

> >>>> @@ -878,6 +942,11 @@ xlate_xbridge_set(struct xbridge *xbridge,

> >>>>         xbridge->netflow = netflow_ref(netflow);

> >>>>     }

> >>>>

> >>>> +    if (xbridge->addr != addr) {

> >>>> +        xbridge_addr_unref(xbridge->addr);

> >>>> +        xbridge->addr = xbridge_addr_ref(addr);

> >>>> +    }

> >>>> +

> >>>>     xbridge->dpif = dpif;

> >>>>     xbridge->forward_bpdu = forward_bpdu;

> >>>>     xbridge->has_in_band = has_in_band;

> >>>> @@ -971,7 +1040,7 @@ xlate_xbridge_copy(struct xbridge *xbridge)

> >>>>                       xbridge->rstp, xbridge->ms, xbridge->mbridge,

> >>>>                       xbridge->sflow, xbridge->ipfix, xbridge->netflow,

> >>>>                       xbridge->forward_bpdu, xbridge->has_in_band,

> >>>> -                      &xbridge->support);

> >>>> +                      &xbridge->support, xbridge->addr);

> >>>>     LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {

> >>>>         xlate_xbundle_copy(new_xbridge, xbundle);

> >>>>     }

> >>>> @@ -1126,6 +1195,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,

> >>>>                   const struct dpif_backer_support *support)

> >>>> {

> >>>>     struct xbridge *xbridge;

> >>>> +    struct xbridge_addr *xbridge_addr, *old_addr;

> >>>>

> >>>>     ovs_assert(new_xcfg);

> >>>>

> >>>> @@ -1140,8 +1210,16 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,

> >>>>     free(xbridge->name);

> >>>>     xbridge->name = xstrdup(name);

> >>>>

> >>>> +    xbridge_addr = xbridge_addr_create(xbridge);

> >>>> +    old_addr = xbridge->addr;

> >>>> +

> >>>>     xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,

> >>>> -                      netflow, forward_bpdu, has_in_band, support);

> >>>> +                      netflow, forward_bpdu, has_in_band, support,

> >>>> +                      xbridge_addr);

> >>>> +

> >>>> +    if (xbridge_addr != old_addr) {

> >>>> +        xbridge_addr_unref(xbridge_addr);

> >>>> +    }

> >>>> }

> >>>>

> >>>> static void

> >>>> @@ -1171,6 +1249,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)

> >>>>     netflow_unref(xbridge->netflow);

> >>>>     stp_unref(xbridge->stp);

> >>>>     rstp_unref(xbridge->rstp);

> >>>> +    xbridge_addr_unref(xbridge->addr);

> >>>>     hmap_destroy(&xbridge->xports);

> >>>>     free(xbridge->name);

> >>>>     free(xbridge);

> >>>> @@ -3655,6 +3734,54 @@ check_output_prerequisites(struct xlate_ctx *ctx,

> >>>>     return true;

> >>>> }

> >>>>

> >>>> +/* Function verifies if destination address of received Neighbor Advertisement

> >>>> + * message stored in 'flow' is correct. It should be either FF02::1:FFXX:XXXX

> >>>> + * where XX:XXXX stands for the last 24 bits of 'ipv6_addr' or it should match

> >>>> + * 'ipv6_addr'. */

> >>>> +static bool

> >>>> +is_nd_dst_correct(const struct flow *flow, const struct in6_addr *ipv6_addr)

> >>>> +{

> >>>> +    const uint8_t *flow_ipv6_addr = (uint8_t *) &flow->ipv6_dst;

> >>>> +    const uint8_t *addr = (uint8_t *) ipv6_addr;

> >>>> +

> >>>> +    return (IN6_IS_ADDR_MC_LINKLOCAL(flow_ipv6_addr) &&

> >>>> +            flow_ipv6_addr[11] == 0x01 &&

> >>>> +            flow_ipv6_addr[12] == 0xff &&

> >>>> +            flow_ipv6_addr[13] == addr[13] &&

> >>>> +            flow_ipv6_addr[14] == addr[14] &&

> >>>> +            flow_ipv6_addr[15] == addr[15]) ||

> >>>> +            IN6_ARE_ADDR_EQUAL(&flow->ipv6_dst, ipv6_addr);

> >>>> +}

> >>>> +

> >>>> +/* Function verifies if the ARP reply or Neighbor Advertisement represented by

> >>>> + * 'flow' addresses the 'xbridge' of 'ctx'. Returns true if the ARP TA or

> >>>> + * neighbor discovery destination is in the list of configured IP addresses of

> >>>> + * the bridge. Otherwise, it returns false. */

> >>>> +static bool

> >>>> +is_neighbor_reply_correct(const struct xlate_ctx *ctx, const struct flow *flow)

> >>>> +{

> >>>> +    bool ret = false;

> >>>> +    int i;

> >>>> +    struct xbridge_addr *xbridge_addr = xbridge_addr_ref(ctx->xbridge->addr);

> >>>> +

> >>>> +    /* Verify if 'nw_dst' of ARP or 'ipv6_dst' of ICMPV6 is in the list. */

> >>>> +    for (i = 0; xbridge_addr && i < xbridge_addr->n_addr; i++) {

> >>>> +        struct in6_addr *ip_addr = &xbridge_addr->addr[i];

> >>>> +        if ((IN6_IS_ADDR_V4MAPPED(ip_addr) &&

> >>>> +             flow->dl_type == htons(ETH_TYPE_ARP) &&

> >>>> +             in6_addr_get_mapped_ipv4(ip_addr) == flow->nw_dst) ||

> >>>> +            (!IN6_IS_ADDR_V4MAPPED(ip_addr) &&

> >>>> +              is_nd_dst_correct(flow, ip_addr))) {

> >>>> +            /* Found a match. */

> >>>> +            ret = true;

> >>>> +            break;

> >>>> +        }

> >>>> +    }

> >>>> +

> >>>> +    xbridge_addr_unref(xbridge_addr);

> >>>> +    return ret;

> >>>> +}

> >>>> +

> >>>> static bool

> >>>> terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,

> >>>>                         struct flow *flow, struct flow_wildcards *wc,

> >>>> @@ -3667,6 +3794,15 @@ terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,

> >>>>     if (ofp_port == OFPP_LOCAL &&

> >>>>         ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {

> >>>>         *tnl_port = tnl_port_map_lookup(flow, wc);

> >>>> +

> >>>> +        /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,

> >>>> +         * do tunnel neighbor snooping. */

> >>>> +        if (*tnl_port == ODPP_NONE &&

> >>>> +            (flow->dl_type == htons(ETH_TYPE_ARP) ||

> >>>> +             flow->nw_proto == IPPROTO_ICMPV6) &&

> >>>> +             is_neighbor_reply_correct(ctx, flow)) {

> >>>> +            tnl_neigh_snoop(flow, wc, ctx->xbridge->name);

> >>>> +        }

> >>>>     }

> >>>>

> >>>>     return *tnl_port != ODPP_NONE;

> >>>> @@ -6183,9 +6319,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,

> >>>>     struct flow *flow = &ctx->xin->flow;

> >>>>     const struct ofpact *a;

> >>>>

> >>>> -    if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {

> >>>> -        tnl_neigh_snoop(flow, wc, ctx->xbridge->name);

> >>>> -    }

> >>>>     /* dl_type already in the mask, not set below. */

> >>>>

> >>>>     if (!ofpacts_len) {

> >>>> diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at

> >>>> index 0b9d436db..b0f4a7e91 100644

> >>>> --- a/tests/tunnel-push-pop-ipv6.at

> >>>> +++ b/tests/tunnel-push-pop-ipv6.at

> >>>> @@ -55,9 +55,73 @@ AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | un

> >>>> ])

> >>>>

> >>>> dnl Check ARP Snoop

> >>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::

> >>>

> 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:

> >>> 00,tll=f8:bc:12:44:34:b6)'])

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::

> >>>

> 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:

> >>> 00,tll=f8:bc:12:44:34:c8)'])

> >>>>

> >>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::

> >>>

> 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:

> >>> 00,tll=f8:bc:12:44:34:b7)'])

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl

> >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::

> >>>

> 99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:

> >>> 00,tll=f8:bc:12:44:34:b6)'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl

> >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receiving Neighbot Advertisement with incorrect VLAN id should not alter tunnel neighbor cache

> >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86

> >>>

> dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n

> >>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl

> >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache

> >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86

> >>>

> dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n

> >>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl

> >>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache

> >>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-

> >>> config:hwaddr=aa:55:aa:55:00:99])

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1

> >>>

> 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe

> >>>

> ::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:0

> >>> 0:00,tll=f8:bc:12:44:34:c8)'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl

> >>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receive Neighbor Advertisement without VLAN header

> >>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])

> >>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK

> >>>> +])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::

> >>>

> 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:

> >>> 00,tll=f8:bc:12:44:34:b6)'])

> >>>> +

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff0

> >>>

> 0:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00

> >>> :00:00,tll=f8:bc:12:44:34:b7)'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>>

> >>>> AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl

> >>>> 2001:cafe::92                                 f8:bc:12:44:34:b6   br0

> >>>> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at

> >>>> index b168f5f2f..4e259730c 100644

> >>>> --- a/tests/tunnel-push-pop.at

> >>>> +++ b/tests/tunnel-push-pop.at

> >>>> @@ -70,9 +70,71 @@ ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101

> >>>> ])

> >>>>

> >>>> dnl Check ARP Snoop

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.

> >>> 2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl

> >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.

> >>> 2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl

> >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache

> >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(

> >>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl

> >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(

> >>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl

> >>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache

> >>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-

> >>> config:hwaddr=aa:55:aa:55:00:99])

> >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1

> >>>

> 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.

> >>> 1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl

> >>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0

> >>>> +])

> >>>> +

> >>>> +dnl Receive ARP reply without VLAN header

> >>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])

> >>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK

> >>>> +])

> >>>> +

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.

> >>> 2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])

> >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.

> >>> 2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)'])

> >>>>

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl

> >>>> 1.1.2.92                                      f8:bc:12:44:34:b6   br0

> >>>> 1.1.2.93                                      f8:bc:12:44:34:b7   br0

> >>>> @@ -190,9 +252,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl

> >>>> dnl Check GREL3 only accepts non-fragmented packets?

> >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0

> >>>

> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a0800450

> >>>

> 00054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1

> >>> f202122232425262728292a2b2c2d2e2f3031323334353637'])

> >>>>

> >>>> +ovs-appctl time/warp 1000

> >>>> +ovs-appctl time/warp 1000

> >>>> +

> >>>> AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl

> >>>>   port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?

> >>>> -  port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?

> >>>> +  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?

> >>>> ])

> >>>>

> >>>> dnl Check decapsulation of Geneve packet with options

> >>>> --

> >>>> 2.14.1

> >>>>

> >>>> _______________________________________________

> >>>> dev mailing list

> >>>> dev@openvswitch.org

> >>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Jan Scheurich April 3, 2018, 11:52 p.m. UTC | #6
Hi,

I took this over from Zoltan and investigated the failing unit test a bit further.

The essential difference between master and Zoltan's (rebased) patches is that the dynamic "ARP cache" flow entry 

   table=66,priority=100,reg0=0xa000002,reg15=0x2,metadata=0x1 actions=set_field:f0:00:00:01:02:04->eth_dst

(which OVN installs in response to the ARP reply it receives from OVS in PACKET_IN after the first injected IP packet) triggers different behavior in the subsequent revalidation:

In the case of master the existing datapath flow entry
 
recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth(src=f0:00:00:01:02:03,dst=00:00:00:01:02:03),eth_type(0x0800),ipv4(src=192.168.1.2/255.255.255.254,dst=10.0.0.2/248.0.0.0,ttl=64,frag=no), actions:ct_clear,set(eth(src=00:00:00:01:02:04,dst=00:00:00:00:00:00)),set(ipv4(src=192.168.1.2/255.255.255.254,dst=8.0.0.0/248.0.0.0,ttl=63)),userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0x62066318,controller_id=0,max_len=65535))

is correctly deleted during revalidation due to the presence of the new rule in table 66, while with Zoltan's patch that same datapath flow entry remains untouched. It seems as if with the patch the revalidation of the datapath flow does not reach the new rule in table 66. 

Consequently the next IP packet injected by netdev-dummy/receive then matches the datapath flow and is sent up to OVN again, where it triggers the same ARP query procedure as before.

However, if I add a "sleep 11" before injecting the second IP packet to let the datapath flow entry time out, the test succeeds, proving that the OF pipeline behaves correctly for a real packet but not when revalidating the datapath flow entry with the userspace(controller(...)) action.

I still do not understand how the (passive) tunnel ARP snooping patch can possibly change the translation behavior for an IP packet in such a way that it affects the revalidation result.

Any idea is welcome.

Regards, Jan


BTW: The “pseudo” tunnel neighbor cache entry we get in this test on master for the tenant IP address 10.0.0.2

IP              	MAC                 	Bridge
======================================================
10.0.0.2		f0:00:00:01:02:04   	br-int
192.168.0.2	5e:97:6a:82:7d:41   	br-phys

is a good example why we need Zoltan's patch. Any IP address in an ARP reply is blindly inserted into the tunnel neighbor cache. Overlapping IP addresses among tenants can cause frequent overwriting of cache entries, in the worst case leading to continuous configuration changes and revalidation.


> -----Original Message-----
> From: Zoltán Balogh
> Sent: Friday, 02 February, 2018 15:42
> To: Justin Pettit <jpettit@ovn.org>; Ben Pfaff <blp@ovn.org>
> Cc: guru@ovn.org; dev@openvswitch.org; Jan Scheurich <jan.scheurich@ericsson.com>
> Subject: RE: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
> 
> Hi Justin,
> 
> I rebased the patches to recent master. Please find them attached.
> 
> Best regards,
> Zoltan
> 
> > -----Original Message-----
> > From: Justin Pettit [mailto:jpettit@ovn.org]
> > Sent: Friday, February 02, 2018 12:00 AM
> > To: Ben Pfaff <mailto:blp@ovn.org>
> > Cc: Zoltán Balogh <mailto:zoltan.balogh@ericsson.com>; mailto:guru@ovn.org; mailto:dev@openvswitch.org; Jan Scheurich
> > <mailto:jan.scheurich@ericsson.com>
> > Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
> >
> > I wasn't able to get this patch to apply to the tip of master.  Zoltan, can you rebase this patch and repost?
> >
> > The main thing my patch series does is make it so that packets that have a controller action aren't processed
> > entirely in userspace.  If, for example, the patches expect packets to be in userspace without an explicit slow-path
> > request when generating the datapath flow, then that would be a problem.
> >
> > --Justin
> >
> >
> > > On Feb 1, 2018, at 2:17 PM, Ben Pfaff <mailto:blp@ovn.org> wrote:
> > >
> > > Justin, I think this is mainly a question about your patches, can you
> > > take a look?
> > >
> > > On Fri, Jan 26, 2018 at 01:08:35PM +0000, Zoltán Balogh wrote:
> > >> Hi,
> > >>
> > >> I've been investigating the failing unit test. I can confirm, it does fail
> > >> with my series on master. However, when I created the series and sent it to
> > >> the mailing list it did not.
> > >>
> > >> I've rebased my series to this commit before I sent it to the mailing list.
> > >>  commit f59cb331c481d08f9a851c07cf31e9d826650485
> > >>  Author: Yi Yang <mailto:yi.y.yang@intel.com>
> > >>  Date:   Sat Jan 6 13:47:51 2018 +0800
> > >>
> > >> If I apply the series to the commit below, the test does pass.
> > >>
> > >> So, I started to search for any commits between the one from Yi Yang and the
> > >> Last one on master which could make my series fail on the OVN unit test
> > >> "/32 router IP address".
> > >>
> > >> I found, that reverting the series of two commits from Justin Pettit below,
> > >> makes my series pass the unit test again. Even rebased to master.
> > >>
> > >>  commit 74c4530dca939109f3fb79776b60b8722e149738
> > >>  Author: Justin Pettit <mailto:jpettit@ovn.org>
> > >>  Date:   Wed Oct 18 23:16:22 2017 -0700
> > >>
> > >>      ofproto-dpif: Don't slow-path controller actions with pause.
> > >>
> > >>      A previous patch removed slow-pathing for controller actions with the
> > >>      exception of ones that specified "pause".  This commit removes that
> > >>      restriction so that no controller actions are slow-pathed.
> > >>
> > >>      Signed-off-by: Justin Pettit <mailto:jpettit@ovn.org>
> > >>      Acked-by: Ben Pfaff <mailto:blp@ovn.org>
> > >>
> > >>  commit d39ec23de38464ee35b3098b9f6c5f06d5191015
> > >>  Author: Justin Pettit <mailto:jpettit@ovn.org>
> > >>  Date:   Wed Jul 5 15:17:52 2017 -0700
> > >>
> > >>      ofproto-dpif: Don't slow-path controller actions.
> > >>
> > >>      Controller actions have become more commonly used for purposes other
> > >>      than just making forwarding decisions (e.g., packet logging).  A packet
> > >>      that needs to be copied to the controller and forwarded would always be
> > >>      sent to ovs-vswitchd to be handled, which could negatively affect
> > >>      performance and cause heavier CPU utilization in ovs-vswitchd.
> > >>
> > >>      This commit changes the behavior so that OpenFlow controller actions
> > >>      become userspace datapath actions while continuing to let packet
> > >>      forwarding and manipulation continue to be handled by the datapath
> > >>      directly.
> > >>
> > >>      This patch still slow-paths controller actions with the "pause" flag
> > >>      set.  A future patch will stop slow-pathing these pause actions as
> > >>      well.
> > >>
> > >>      Signed-off-by: Justin Pettit <mailto:jpettit@ovn.org>
> > >>      Acked-by: Ben Pfaff <mailto:blp@ovn.org>
> > >>
> > >> The main difference, my series does compared to master, is not putting the
> > >> ARP entry of {10.0.0.2, f0:00:00:01:02:04, br-int} into tunnel neighbor
> > >> cache. All the OF rules are almost the same, except loading different
> > >> numbers into NXM_NX_REGx and using different metadata.
> > >>
> > >> How should this be related to Justin's series?
> > >>
> > >> If I modify the unit test and populate the missing ARP entry then the test
> > >> does pass.
> > >>
> > >> Should this entry be present in the ARP neighbor cache in order to make the
> > >> test pass? If yes, then why?
> > >>
> > >> Best regards,
> > >> Zoltan
> > >>
> > >>> -----Original Message-----
> > >>> From: Ben Pfaff [mailto:blp@ovn.org]
> > >>> Sent: Tuesday, January 23, 2018 8:02 PM
> > >>> To: Zoltán Balogh <mailto:zoltan.balogh@ericsson.com>
> > >>> Cc: mailto:dev@openvswitch.org; Jan Scheurich <mailto:jan.scheurich@ericsson.com>
> > >>> Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
> > >>>
> > >>> On Tue, Jan 09, 2018 at 07:54:33PM +0100, Zoltan Balogh wrote:
> > >>>> Currenlty, OVS snoops any ARP or ND packets in any bridge and populates
> > >>>> the tunnel neighbor cache with the retreived data. For instance, when
> > >>>> ARP reply originated by a tenant is received in an overlay bridge, the
> > >>>> ARP message is snooped and tunnel neighbor cache is filled with tenant
> > >>>> data, however only tunnel neighbor data should be stored there.
> > >>>>
> > >>>> This patch moves tunnel neighbor snooping from do_xlate_actions() to
> > >>>> terminate_native_tunnel() where native tunnel is terminated, in order to
> > >>>> keep ARP neighbor cache clean, i.e. only packets comming from a native
> > >>>> tunnel are snooped.
> > >>>>
> > >>>> By applying the patch, only ARP and Neighbor Advertisement messages
> > >>>> addressing a tunnel endpoint (LOCAL port of underlay bridge) are
> > >>>> snooped. In order to achieve this, IP addresses of the bridge are
> > >>>> retrieved and then stored in xbridge by calling xlate_xbridge_set().
> > >>>> These data is then matched against the data extracted from an ARP or
> > >>>> Neighbor Advertisement message in is_neighbor_reply_correct() which is
> > >>>> invoked from terminate_native_tunnel().
> > >>>>
> > >>>> Signed-off-by: Zoltan Balogh <mailto:zoltan.balogh@ericsson.com>
> > >>>
> > >>> Thanks a lot!
> > >>>
> > >>> It appears to me that this patch makes the following test consistently
> > >>> fail.  When I revert the test, it passes for me again.  Can you take a
> > >>> look?
> > >>>
> > >>> Thanks,
> > >>>
> > >>> Ben.
> > >>>
> > >>> #                             -*- compilation -*-
> > >>> 2370. ovn.at:8136: testing ovn -- /32 router IP address ...
> > >>> creating ovn-sb database
> > >>> creating ovn-nb database
> > >>> starting ovn-northd
> > >>> starting backup ovn-northd
> > >>> adding simulator 'main'
> > >>> adding simulator 'hv1'
> > >>> adding simulator 'hv2'
> > >>> ../../tests/ovn.at:8198: ovn_populate_arp__
> > >>> stdout:
> > >>> OK
> > >>> OK
> > >>>
> > >>> checking packets in hv2/vif1-tx.pcap against expected:
> > >>> expected 1 packets, only received 0
> > >>> ../../tests/ovn.at:8230: sort $rcv_text
> > >>> --- expout      2018-01-23 10:59:47.231797273 -0800
> > >>> +++ /home/blp/nicira/ovs/_build/tests/testsuite.dir/at-groups/2370/stdout       2018-01-23 10:59:47.231797273 -
> > 0800
> > >>> @@ -1 +0,0 @@
> > >>> -f0000001020400000001020408004500001c000000003f110100c0a801020a0000020035111100080000
> > >>> 2370. ovn.at:8136: 2370. ovn -- /32 router IP address (ovn.at:8136): FAILED (ovn.at:8230)
> > >>>
> > >>>
> > >>>> ---
> > >>>> include/sparse/netinet/in.h   |  10 +++
> > >>>> ofproto/ofproto-dpif-xlate.c  | 147 ++++++++++++++++++++++++++++++++++++++++--
> > >>>> tests/tunnel-push-pop-ipv6.at |  68 ++++++++++++++++++-
> > >>>> tests/tunnel-push-pop.at      |  67 ++++++++++++++++++-
> > >>>> 4 files changed, 282 insertions(+), 10 deletions(-)
> > >>>>
> > >>>> diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h
> > >>>> index 6abdb2331..eea41bd7f 100644
> > >>>> --- a/include/sparse/netinet/in.h
> > >>>> +++ b/include/sparse/netinet/in.h
> > >>>> @@ -123,6 +123,16 @@ struct sockaddr_in6 {
> > >>>>      (X)->s6_addr[10] == 0xff &&                \
> > >>>>      (X)->s6_addr[11] == 0xff)
> > >>>>
> > >>>> +#define IN6_IS_ADDR_MC_LINKLOCAL(a)                 \
> > >>>> +    (((const uint8_t *) (a))[0] == 0xff &&          \
> > >>>> +     (((const uint8_t *) (a))[1] & 0xf) == 0x2)
> > >>>> +
> > >>>> +# define IN6_ARE_ADDR_EQUAL(a,b)                                          \
> > >>>> +    ((((const uint32_t *) (a))[0] == ((const uint32_t *) (b))[0]) &&      \
> > >>>> +     (((const uint32_t *) (a))[1] == ((const uint32_t *) (b))[1]) &&      \
> > >>>> +     (((const uint32_t *) (a))[2] == ((const uint32_t *) (b))[2]) &&      \
> > >>>> +     (((const uint32_t *) (a))[3] == ((const uint32_t *) (b))[3]))
> > >>>> +
> > >>>> #define INET_ADDRSTRLEN 16
> > >>>> #define INET6_ADDRSTRLEN 46
> > >>>>
> > >>>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> > >>>> index 2328ec1da..16bc0cd96 100644
> > >>>> --- a/ofproto/ofproto-dpif-xlate.c
> > >>>> +++ b/ofproto/ofproto-dpif-xlate.c
> > >>>> @@ -90,6 +90,16 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
> > >>>>  * recursive or not. */
> > >>>> #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH)
> > >>>>
> > >>>> +/* The structure holds an array of IP addresses assigned to a bridge and the
> > >>>> + * number of elements in the array. These data are mutable and are evaluated
> > >>>> + * when ARP or Neighbor Advertisement packets received on a native tunnel
> > >>>> + * port are xlated. So 'ref_cnt' and RCU are used for synchronization. */
> > >>>> +struct xbridge_addr {
> > >>>> +    struct in6_addr *addr;        /* Array of IP addresses of xbridge. */
> > >>>> +    int n_addr;                   /* Number of IP addresses. */
> > >>>> +    struct ovs_refcount ref_cnt;
> > >>>> +};
> > >>>> +
> > >>>> struct xbridge {
> > >>>>     struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */
> > >>>>     struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
> > >>>> @@ -113,6 +123,8 @@ struct xbridge {
> > >>>>
> > >>>>     /* Datapath feature support. */
> > >>>>     struct dpif_backer_support support;
> > >>>> +
> > >>>> +    struct xbridge_addr *addr;
> > >>>> };
> > >>>>
> > >>>> struct xbundle {
> > >>>> @@ -576,7 +588,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
> > >>>>                               const struct dpif_ipfix *,
> > >>>>                               const struct netflow *,
> > >>>>                               bool forward_bpdu, bool has_in_band,
> > >>>> -                              const struct dpif_backer_support *);
> > >>>> +                              const struct dpif_backer_support *,
> > >>>> +                              const struct xbridge_addr *);
> > >>>> static void xlate_xbundle_set(struct xbundle *xbundle,
> > >>>>                               enum port_vlan_mode vlan_mode,
> > >>>>                               uint16_t qinq_ethtype, int vlan,
> > >>>> @@ -826,6 +839,56 @@ xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport)
> > >>>>                 hash_ofp_port(xport->ofp_port));
> > >>>> }
> > >>>>
> > >>>> +static struct xbridge_addr *
> > >>>> +xbridge_addr_create(struct xbridge *xbridge)
> > >>>> +{
> > >>>> +    struct xbridge_addr *xbridge_addr = xbridge->addr;
> > >>>> +    struct in6_addr *addr = NULL, *mask = NULL;
> > >>>> +    struct netdev *dev;
> > >>>> +    int err, n_addr = 0;
> > >>>> +
> > >>>> +    err = netdev_open(xbridge->name, NULL, &dev);
> > >>>> +    if (!err) {
> > >>>> +        err = netdev_get_addr_list(dev, &addr, &mask, &n_addr);
> > >>>> +        if (!err) {
> > >>>> +            if (!xbridge->addr ||
> > >>>> +                n_addr != xbridge->addr->n_addr ||
> > >>>> +                (xbridge->addr->addr && memcmp(addr, xbridge->addr->addr,
> > >>>> +                                               sizeof(*addr) * n_addr))) {
> > >>>> +                xbridge_addr = xzalloc(sizeof *xbridge_addr);
> > >>>> +                xbridge_addr->addr = addr;
> > >>>> +                xbridge_addr->n_addr = n_addr;
> > >>>> +                ovs_refcount_init(&xbridge_addr->ref_cnt);
> > >>>> +            } else {
> > >>>> +                free(addr);
> > >>>> +            }
> > >>>> +            free(mask);
> > >>>> +        }
> > >>>> +        netdev_close(dev);
> > >>>> +    }
> > >>>> +
> > >>>> +    return xbridge_addr;
> > >>>> +}
> > >>>> +
> > >>>> +static struct xbridge_addr *
> > >>>> +xbridge_addr_ref(const struct xbridge_addr *addr_)
> > >>>> +{
> > >>>> +    struct xbridge_addr *addr = CONST_CAST(struct xbridge_addr *, addr_);
> > >>>> +    if (addr) {
> > >>>> +        ovs_refcount_ref(&addr->ref_cnt);
> > >>>> +    }
> > >>>> +    return addr;
> > >>>> +}
> > >>>> +
> > >>>> +static void
> > >>>> +xbridge_addr_unref(struct xbridge_addr *addr)
> > >>>> +{
> > >>>> +    if (addr && ovs_refcount_unref_relaxed(&addr->ref_cnt) == 1) {
> > >>>> +        free(addr->addr);
> > >>>> +        free(addr);
> > >>>> +    }
> > >>>> +}
> > >>>> +
> > >>>> static void
> > >>>> xlate_xbridge_set(struct xbridge *xbridge,
> > >>>>                   struct dpif *dpif,
> > >>>> @@ -836,7 +899,8 @@ xlate_xbridge_set(struct xbridge *xbridge,
> > >>>>                   const struct dpif_ipfix *ipfix,
> > >>>>                   const struct netflow *netflow,
> > >>>>                   bool forward_bpdu, bool has_in_band,
> > >>>> -                  const struct dpif_backer_support *support)
> > >>>> +                  const struct dpif_backer_support *support,
> > >>>> +                  const struct xbridge_addr *addr)
> > >>>> {
> > >>>>     if (xbridge->ml != ml) {
> > >>>>         mac_learning_unref(xbridge->ml);
> > >>>> @@ -878,6 +942,11 @@ xlate_xbridge_set(struct xbridge *xbridge,
> > >>>>         xbridge->netflow = netflow_ref(netflow);
> > >>>>     }
> > >>>>
> > >>>> +    if (xbridge->addr != addr) {
> > >>>> +        xbridge_addr_unref(xbridge->addr);
> > >>>> +        xbridge->addr = xbridge_addr_ref(addr);
> > >>>> +    }
> > >>>> +
> > >>>>     xbridge->dpif = dpif;
> > >>>>     xbridge->forward_bpdu = forward_bpdu;
> > >>>>     xbridge->has_in_band = has_in_band;
> > >>>> @@ -971,7 +1040,7 @@ xlate_xbridge_copy(struct xbridge *xbridge)
> > >>>>                       xbridge->rstp, xbridge->ms, xbridge->mbridge,
> > >>>>                       xbridge->sflow, xbridge->ipfix, xbridge->netflow,
> > >>>>                       xbridge->forward_bpdu, xbridge->has_in_band,
> > >>>> -                      &xbridge->support);
> > >>>> +                      &xbridge->support, xbridge->addr);
> > >>>>     LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
> > >>>>         xlate_xbundle_copy(new_xbridge, xbundle);
> > >>>>     }
> > >>>> @@ -1126,6 +1195,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
> > >>>>                   const struct dpif_backer_support *support)
> > >>>> {
> > >>>>     struct xbridge *xbridge;
> > >>>> +    struct xbridge_addr *xbridge_addr, *old_addr;
> > >>>>
> > >>>>     ovs_assert(new_xcfg);
> > >>>>
> > >>>> @@ -1140,8 +1210,16 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
> > >>>>     free(xbridge->name);
> > >>>>     xbridge->name = xstrdup(name);
> > >>>>
> > >>>> +    xbridge_addr = xbridge_addr_create(xbridge);
> > >>>> +    old_addr = xbridge->addr;
> > >>>> +
> > >>>>     xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,
> > >>>> -                      netflow, forward_bpdu, has_in_band, support);
> > >>>> +                      netflow, forward_bpdu, has_in_band, support,
> > >>>> +                      xbridge_addr);
> > >>>> +
> > >>>> +    if (xbridge_addr != old_addr) {
> > >>>> +        xbridge_addr_unref(xbridge_addr);
> > >>>> +    }
> > >>>> }
> > >>>>
> > >>>> static void
> > >>>> @@ -1171,6 +1249,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
> > >>>>     netflow_unref(xbridge->netflow);
> > >>>>     stp_unref(xbridge->stp);
> > >>>>     rstp_unref(xbridge->rstp);
> > >>>> +    xbridge_addr_unref(xbridge->addr);
> > >>>>     hmap_destroy(&xbridge->xports);
> > >>>>     free(xbridge->name);
> > >>>>     free(xbridge);
> > >>>> @@ -3655,6 +3734,54 @@ check_output_prerequisites(struct xlate_ctx *ctx,
> > >>>>     return true;
> > >>>> }
> > >>>>
> > >>>> +/* Function verifies if destination address of received Neighbor Advertisement
> > >>>> + * message stored in 'flow' is correct. It should be either FF02::1:FFXX:XXXX
> > >>>> + * where XX:XXXX stands for the last 24 bits of 'ipv6_addr' or it should match
> > >>>> + * 'ipv6_addr'. */
> > >>>> +static bool
> > >>>> +is_nd_dst_correct(const struct flow *flow, const struct in6_addr *ipv6_addr)
> > >>>> +{
> > >>>> +    const uint8_t *flow_ipv6_addr = (uint8_t *) &flow->ipv6_dst;
> > >>>> +    const uint8_t *addr = (uint8_t *) ipv6_addr;
> > >>>> +
> > >>>> +    return (IN6_IS_ADDR_MC_LINKLOCAL(flow_ipv6_addr) &&
> > >>>> +            flow_ipv6_addr[11] == 0x01 &&
> > >>>> +            flow_ipv6_addr[12] == 0xff &&
> > >>>> +            flow_ipv6_addr[13] == addr[13] &&
> > >>>> +            flow_ipv6_addr[14] == addr[14] &&
> > >>>> +            flow_ipv6_addr[15] == addr[15]) ||
> > >>>> +            IN6_ARE_ADDR_EQUAL(&flow->ipv6_dst, ipv6_addr);
> > >>>> +}
> > >>>> +
> > >>>> +/* Function verifies if the ARP reply or Neighbor Advertisement represented by
> > >>>> + * 'flow' addresses the 'xbridge' of 'ctx'. Returns true if the ARP TA or
> > >>>> + * neighbor discovery destination is in the list of configured IP addresses of
> > >>>> + * the bridge. Otherwise, it returns false. */
> > >>>> +static bool
> > >>>> +is_neighbor_reply_correct(const struct xlate_ctx *ctx, const struct flow *flow)
> > >>>> +{
> > >>>> +    bool ret = false;
> > >>>> +    int i;
> > >>>> +    struct xbridge_addr *xbridge_addr = xbridge_addr_ref(ctx->xbridge->addr);
> > >>>> +
> > >>>> +    /* Verify if 'nw_dst' of ARP or 'ipv6_dst' of ICMPV6 is in the list. */
> > >>>> +    for (i = 0; xbridge_addr && i < xbridge_addr->n_addr; i++) {
> > >>>> +        struct in6_addr *ip_addr = &xbridge_addr->addr[i];
> > >>>> +        if ((IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> > >>>> +             flow->dl_type == htons(ETH_TYPE_ARP) &&
> > >>>> +             in6_addr_get_mapped_ipv4(ip_addr) == flow->nw_dst) ||
> > >>>> +            (!IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> > >>>> +              is_nd_dst_correct(flow, ip_addr))) {
> > >>>> +            /* Found a match. */
> > >>>> +            ret = true;
> > >>>> +            break;
> > >>>> +        }
> > >>>> +    }
> > >>>> +
> > >>>> +    xbridge_addr_unref(xbridge_addr);
> > >>>> +    return ret;
> > >>>> +}
> > >>>> +
> > >>>> static bool
> > >>>> terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> > >>>>                         struct flow *flow, struct flow_wildcards *wc,
> > >>>> @@ -3667,6 +3794,15 @@ terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> > >>>>     if (ofp_port == OFPP_LOCAL &&
> > >>>>         ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
> > >>>>         *tnl_port = tnl_port_map_lookup(flow, wc);
> > >>>> +
> > >>>> +        /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,
> > >>>> +         * do tunnel neighbor snooping. */
> > >>>> +        if (*tnl_port == ODPP_NONE &&
> > >>>> +            (flow->dl_type == htons(ETH_TYPE_ARP) ||
> > >>>> +             flow->nw_proto == IPPROTO_ICMPV6) &&
> > >>>> +             is_neighbor_reply_correct(ctx, flow)) {
> > >>>> +            tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> > >>>> +        }
> > >>>>     }
> > >>>>
> > >>>>     return *tnl_port != ODPP_NONE;
> > >>>> @@ -6183,9 +6319,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
> > >>>>     struct flow *flow = &ctx->xin->flow;
> > >>>>     const struct ofpact *a;
> > >>>>
> > >>>> -    if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
> > >>>> -        tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> > >>>> -    }
> > >>>>     /* dl_type already in the mask, not set below. */
> > >>>>
> > >>>>     if (!ofpacts_len) {
> > >>>> diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
> > >>>> index 0b9d436db..b0f4a7e91 100644
> > >>>> --- a/tests/tunnel-push-pop-ipv6.at
> > >>>> +++ b/tests/tunnel-push-pop-ipv6.at
> > >>>> @@ -55,9 +55,73 @@ AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | un
> > >>>> ])
> > >>>>
> > >>>> dnl Check ARP Snoop
> > >>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > >>>
> > 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > >>> 00,tll=f8:bc:12:44:34:b6)'])
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > >>>
> > 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > >>> 00,tll=f8:bc:12:44:34:c8)'])
> > >>>>
> > >>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::
> > >>>
> > 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:
> > >>> 00,tll=f8:bc:12:44:34:b7)'])
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > >>>
> > 99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > >>> 00,tll=f8:bc:12:44:34:b6)'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receiving Neighbot Advertisement with incorrect VLAN id should not alter tunnel neighbor cache
> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86
> > >>>
> > dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
> > >>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache
> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86
> > >>>
> > dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
> > >>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache
> > >>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
> > >>> config:hwaddr=aa:55:aa:55:00:99])
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1
> > >>>
> > 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe
> > >>>
> > ::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:0
> > >>> 0:00,tll=f8:bc:12:44:34:c8)'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receive Neighbor Advertisement without VLAN header
> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])
> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> > >>>> +])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > >>>
> > 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > >>> 00,tll=f8:bc:12:44:34:b6)'])
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff0
> > >>>
> > 0:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00
> > >>> :00:00,tll=f8:bc:12:44:34:b7)'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>>
> > >>>> AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > >>>> 2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > >>>> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
> > >>>> index b168f5f2f..4e259730c 100644
> > >>>> --- a/tests/tunnel-push-pop.at
> > >>>> +++ b/tests/tunnel-push-pop.at
> > >>>> @@ -70,9 +70,71 @@ ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101
> > >>>> ])
> > >>>>
> > >>>> dnl Check ARP Snoop
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> > >>> 2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> > >>> 2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache
> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(
> > >>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(
> > >>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > >>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache
> > >>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
> > >>> config:hwaddr=aa:55:aa:55:00:99])
> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1
> > >>>
> > 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.
> > >>> 1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> > >>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > >>>> +])
> > >>>> +
> > >>>> +dnl Receive ARP reply without VLAN header
> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])
> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> > >>>> +])
> > >>>> +
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> > >>> 2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
> > >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.
> > >>> 2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)'])
> > >>>>
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
> > >>>> 1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > >>>> 1.1.2.93                                      f8:bc:12:44:34:b7   br0
> > >>>> @@ -190,9 +252,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl
> > >>>> dnl Check GREL3 only accepts non-fragmented packets?
> > >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > >>>
> >
> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a0800450
> > >>>
> >
> 00054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
> > >>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
> > >>>>
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +ovs-appctl time/warp 1000
> > >>>> +
> > >>>> AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
> > >>>>   port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
> > >>>> -  port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
> > >>>> +  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?
> > >>>> ])
> > >>>>
> > >>>> dnl Check decapsulation of Geneve packet with options
> > >>>> --
> > >>>> 2.14.1
> > >>>>
> > >>>> _______________________________________________
> > >>>> dev mailing list
> > >>>> mailto:dev@openvswitch.org
> > >>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Jan Scheurich April 4, 2018, 10:11 a.m. UTC | #7
Hi again,

At closer inspection it turns out the immediate revalidation triggered by the Flow Mod Add for the table 66 flow is skipped for the datapath flow entry with the userspace(controller(...)) action because the corresponding ukey's dump_seq matches the current udpif->dump_seq and it is considered as already dumped.

This is obviously wrong as the full debug log with added VLOG_WARN proves (see below). We seem to have a race here, which can hit if the first revalidation is triggered in the same dump_seq period as when the flow and ukey were installed.

This is something I have observed earlier when trying to check the flow stats in unit tests shortly after datapath flow creation. The culprit is the initialization of the ukey's dump_seq with the current udp_if->dump_seq at flow creation in upcall_xlate(). The following change would fix the issue reliably. 

+++ b/ofproto/ofproto-dpif-upcall.c
@@ -1159,7 +1159,7 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
          * with pushing its stats eventually. */
     }

-    upcall->dump_seq = seq_read(udpif->dump_seq);
+    upcall->dump_seq = 0;
     upcall->reval_seq = seq_read(udpif->reval_seq);

     xerr = xlate_actions(&xin, &upcall->xout);

Should I submit a patch for this?

BR, Jan

--------

2018-03-28T18:41:24.964Z|00315|unixctl|DBG|received request netdev-dummy/receive["hv1-vif1","000000010203f0000001020308004500001c0000000040110000c0a801020a0000020035111100080000"], id=0
2018-03-28T18:41:24.964Z|00316|unixctl|DBG|replying with success, id=0: ""
2018-03-28T18:41:24.964Z|00317|jsonrpc|DBG|unix#6: send reply, result="", id=0
2018-03-28T18:41:24.964Z|00318|poll_loop|DBG|wakeup due to 0-ms timeout at lib/netdev-dummy.c:1052
2018-03-28T18:41:24.964Z|00319|dpif_netdev|DBG|ovs-system: miss upcall:
skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(3),packet_type(ns=0,id=0),eth(src=f0:00:00:01:02:03,dst=00:00:00:01:02:03),eth_type(0x0800),ipv4(src=192.168.1.2,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=no),udp(src=53,dst=4369)
udp,vlan_tci=0x0000,dl_src=f0:00:00:01:02:03,dl_dst=00:00:00:01:02:03,nw_src=192.168.1.2,nw_dst=10.0.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=53,tp_dst=4369 udp_csum:0
2018-03-28T18:41:24.964Z|00320|ofproto_dpif_xlate|DBG|/proc/sys/net/core/netdev_max_backlog: using 1000 max_backlog
2018-03-28T18:41:24.965Z|00321|dpif_netdev|DBG|ovs-system: action upcall:
skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(3),packet_type(ns=0,id=0),eth(src=00:00:00:01:02:04,dst=00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.1.2,dst=10.0.0.2,proto=17,tos=0,ttl=63,frag=no),udp(src=53,dst=4369)
udp,vlan_tci=0x0000,dl_src=00:00:00:01:02:04,dl_dst=00:00:00:00:00:00,nw_src=192.168.1.2,nw_dst=10.0.0.2,nw_tos=0,nw_ecn=0,nw_ttl=63,tp_src=53,tp_dst=4369 udp_csum:0
2018-03-28T18:41:24.965Z|00322|dpif_netdev|DBG|Creating dpcls 0x2bde370 for in_port 3
2018-03-28T18:41:24.965Z|00323|dpif_netdev|DBG|Creating 1. subtable 0x2bdf460 for in_port 3
2018-03-28T18:41:24.965Z|00324|dpif_netdev|DBG|flow_add: ufid:de19ea9b-110b-4476-b8f7-df5fac3d07c5 recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth(src=f0:00:00:01:02:03,dst=00:00:00:01:02:03),eth_type(0x0800),ipv4(src=192.168.1.2/255.255.255.254,dst=10.0.0.2/248.0.0.0,ttl=64,frag=no), actions:ct_clear,set(eth(src=00:00:00:01:02:04,dst=00:00:00:00:00:00)),set(ipv4(src=192.168.1.2/255.255.255.254,dst=8.0.0.0/248.0.0.0,ttl=63)),userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0x9817d67d,controller_id=0,max_len=65535))
2018-03-28T18:41:24.965Z|00325|dpif_netdev|DBG|flow match: recirc_id=0,eth,ip,vlan_tci=0x0000/0x1000,dl_src=f0:00:00:01:02:03,dl_dst=00:00:00:01:02:03,nw_src=192.168.1.2/31,nw_dst=8.0.0.0/5,nw_ttl=64,nw_frag=no
2018-03-28T18:41:24.965Z|00011|poll_loop(urcu2)|DBG|wakeup due to [POLLIN] on fd 24 (FIFO pipe:[16413512]) at lib/ovs-rcu.c:358
2018-03-28T18:41:24.965Z|00326|vconn|DBG|unix#3: sent (Success): NXT_PACKET_IN2 (OF1.3) (xid=0x0): table_id=18 cookie=0x9817d67d total_len=42 reg0=0xa000002,reg1=0xac100101,reg10=0x1,reg11=0x5,reg12=0x7,reg14=0x1,reg15=0x2,metadata=0x1,in_port=0 (via action) data_len=42 (unbuffered)
 userdata=00.00.00.00.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.40.00.00.00.01.de.10.00.00.20.04.ff.ff.00.18.00.00.23.20.00.06.00.20.00.60.00.00.00.01.de.10.00.00.22.04.00.19.00.10.80.00.2a.02.00.01.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.20.00.00.00
udp,vlan_tci=0x0000,dl_src=00:00:00:01:02:04,dl_dst=00:00:00:00:00:00,nw_src=192.168.1.2,nw_dst=10.0.0.2,nw_tos=0,nw_ecn=0,nw_ttl=63,tp_src=53,tp_dst=4369 udp_csum:0
2018-03-28T18:41:24.965Z|00327|poll_loop|DBG|wakeup due to [POLLIN][POLLHUP] on fd 50 (/opt/ovs/tests/testsuite.dir/2487/hv1/ovs-vswitchd.27587.ctl<->) at lib/stream-fd.c:157
2018-03-28T18:41:24.965Z|00012|poll_loop(urcu2)|DBG|wakeup due to [POLLIN] on fd 24 (FIFO pipe:[16413512]) at lib/ovs-rcu.c:235
2018-03-28T18:41:24.982Z|00328|poll_loop|DBG|wakeup due to [POLLIN] on fd 46 (/opt/ovs/tests/testsuite.dir/2487/hv1/br-int.mgmt<->) at lib/stream-fd.c:157
2018-03-28T18:41:24.983Z|00329|vconn|DBG|unix#3: received: OFPT_PACKET_OUT (OF1.3) (xid=0xd3): in_port=CONTROLLER actions=set_field:0xa000002->reg0,set_field:0xac100101->reg1,set_field:0x1->reg10,set_field:0x5->reg11,set_field:0x7->reg12,set_field:0x1->reg14,set_field:0x2->reg15,set_field:0x1->metadata,set_field:ff:ff:ff:ff:ff:ff->eth_dst,move:NXM_NX_XXREG0[64..95]->NXM_OF_ARP_SPA[],move:NXM_NX_XXREG0[96..127]->NXM_OF_ARP_TPA[],set_field:1->arp_op,resubmit(,32) data_len=42
arp,vlan_tci=0x0000,dl_src=00:00:00:01:02:04,dl_dst=00:00:00:00:00:00,arp_spa=192.168.1.2,arp_tpa=10.0.0.2,arp_op=1,arp_sha=00:00:00:01:02:04,arp_tha=00:00:00:00:00:00
2018-03-28T18:41:24.983Z|00330|dpif_netdev|DBG|ovs-system: action upcall:
skb_priority(0),skb_mark(0),ct_state(0),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),packet_type(ns=0,id=0),eth(src=f0:00:00:01:02:04,dst=00:00:00:01:02:04),eth_type(0x0806),arp(sip=10.0.0.2,tip=172.16.1.1,op=2,sha=f0:00:00:01:02:04,tha=00:00:00:01:02:04)
arp,vlan_tci=0x0000,dl_src=f0:00:00:01:02:04,dl_dst=00:00:00:01:02:04,arp_spa=10.0.0.2,arp_tpa=172.16.1.1,arp_op=2,arp_sha=f0:00:00:01:02:04,arp_tha=00:00:00:01:02:04
2018-03-28T18:41:24.983Z|00331|dpif|DBG|system@ovs-system: execute userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0xd8bfa54f,controller_id=0,max_len=65535)) on packet arp,vlan_tci=0x0000,dl_src=f0:00:00:01:02:04,dl_dst=00:00:00:01:02:04,arp_spa=10.0.0.2,arp_tpa=172.16.1.1,arp_op=2,arp_sha=f0:00:00:01:02:04,arp_tha=00:00:00:01:02:04
 with metadata skb_priority(0),skb_mark(0) mtu 0
2018-03-28T18:41:24.983Z|00332|dpif|DBG|system@ovs-system: sub-execute userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0xd8bfa54f,controller_id=0,max_len=65535)) on packet arp,vlan_tci=0x0000,dl_src=f0:00:00:01:02:04,dl_dst=00:00:00:01:02:04,arp_spa=10.0.0.2,arp_tpa=172.16.1.1,arp_op=2,arp_sha=f0:00:00:01:02:04,arp_tha=00:00:00:01:02:04
 with metadata skb_priority(0),skb_mark(0) mtu 0
2018-03-28T18:41:24.983Z|00333|poll_loop|DBG|wakeup due to 0-ms timeout at ofproto/ofproto-dpif.c:1713
2018-03-28T18:41:24.984Z|00334|vconn|DBG|unix#3: sent (Success): NXT_PACKET_IN2 (OF1.3) (xid=0x0): table_id=9 cookie=0xd8bfa54f total_len=42 reg0=0xa000002,reg11=0x5,reg12=0x7,reg14=0x2,metadata=0x1,in_port=0 (via action) data_len=42 (unbuffered)
 userdata=00.00.00.01.00.00.00.00
arp,vlan_tci=0x0000,dl_src=f0:00:00:01:02:04,dl_dst=00:00:00:01:02:04,arp_spa=10.0.0.2,arp_tpa=172.16.1.1,arp_op=2,arp_sha=f0:00:00:01:02:04,arp_tha=00:00:00:01:02:04
2018-03-28T18:41:24.996Z|00335|poll_loop|DBG|wakeup due to [POLLIN] on fd 45 (/opt/ovs/tests/testsuite.dir/2487/hv1/br-int.mgmt<->) at lib/stream-fd.c:157
2018-03-28T18:41:24.996Z|00336|vconn|DBG|unix#2: received: OFPT_FLOW_MOD (OF1.3) (xid=0xd4): ADD table:66 priority=100,reg0=0xa000002,reg15=0x2,metadata=0x1 actions=set_field:f0:00:00:01:02:04->eth_dst
2018-03-28T18:41:24.996Z|00337|poll_loop|DBG|wakeup due to 0-ms timeout at ofproto/ofproto-dpif.c:1709
2018-03-28T18:41:24.996Z|00013|poll_loop(urcu2)|DBG|wakeup due to [POLLIN] on fd 24 (FIFO pipe:[16413512]) at lib/ovs-rcu.c:358
2018-03-28T18:41:24.996Z|00024|poll_loop(revalidator6)|DBG|wakeup due to [POLLIN] on fd 35 (FIFO pipe:[16413516]) at ofproto/ofproto-dpif-upcall.c:948
2018-03-28T18:41:24.996Z|00025|dpif(revalidator6)|DBG|system@ovs-system: flow_dump ufid:de19ea9b-110b-4476-b8f7-df5fac3d07c5 <empty>, packets:0, bytes:0, used:never
2018-03-28T18:41:24.996Z|00026|ofproto_dpif_upcall(revalidator6)|WARN|Flow already dumped
2018-03-28T18:41:24.996Z|00027|dpif(revalidator6)|DBG|system@ovs-system: dumped all flows
2018-03-28T18:41:24.997Z|00028|dpif(revalidator6)|DBG|system@ovs-system: flow_dump_destroy success



> -----Original Message-----
> From: Jan Scheurich
> Sent: Wednesday, 04 April, 2018 01:52
> To: Zoltán Balogh <zoltan.balogh@ericsson.com>; Justin Pettit <jpettit@ovn.org>; Ben Pfaff <blp@ovn.org>
> Cc: guru@ovn.org; dev@openvswitch.org
> Subject: RE: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
> 
> Hi,
> 
> I took this over from Zoltan and investigated the failing unit test a bit further.
> 
> The essential difference between master and Zoltan's (rebased) patches is that the dynamic "ARP cache" flow entry
> 
>    table=66,priority=100,reg0=0xa000002,reg15=0x2,metadata=0x1 actions=set_field:f0:00:00:01:02:04->eth_dst
> 
> (which OVN installs in response to the ARP reply it receives from OVS in PACKET_IN after the first injected IP packet) triggers different
> behavior in the subsequent revalidation:
> 
> In the case of master the existing datapath flow entry
> 
> recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth(src=f0:00:00:01:02:03,dst=00:00:00:01:02:03),eth_type(0x0800),ipv4(src=192.168.1.
> 2/255.255.255.254,dst=10.0.0.2/248.0.0.0,ttl=64,frag=no),
> actions:ct_clear,set(eth(src=00:00:00:01:02:04,dst=00:00:00:00:00:00)),set(ipv4(src=192.168.1.2/255.255.255.254,dst=8.0.0.0/248.0.0.0,t
> tl=63)),userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0x62066318,controller_id=0,max_l
> en=65535))
> 
> is correctly deleted during revalidation due to the presence of the new rule in table 66, while with Zoltan's patch that same datapath
> flow entry remains untouched. It seems as if with the patch the revalidation of the datapath flow does not reach the new rule in table
> 66.
> 
> Consequently the next IP packet injected by netdev-dummy/receive then matches the datapath flow and is sent up to OVN again,
> where it triggers the same ARP query procedure as before.
> 
> However, if I add a "sleep 11" before injecting the second IP packet to let the datapath flow entry time out, the test succeeds, proving
> that the OF pipeline behaves correctly for a real packet but not when revalidating the datapath flow entry with the
> userspace(controller(...)) action.
> 
> I still do not understand how the (passive) tunnel ARP snooping patch can possibly change the translation behavior for an IP packet in
> such a way that it affects the revalidation result.
> 
> Any idea is welcome.
> 
> Regards, Jan
> 
> 
> BTW: The “pseudo” tunnel neighbor cache entry we get in this test on master for the tenant IP address 10.0.0.2
> 
> IP              	MAC                 	Bridge
> ======================================================
> 10.0.0.2		f0:00:00:01:02:04   	br-int
> 192.168.0.2	5e:97:6a:82:7d:41   	br-phys
> 
> is a good example why we need Zoltan's patch. Any IP address in an ARP reply is blindly inserted into the tunnel neighbor cache.
> Overlapping IP addresses among tenants can cause frequent overwriting of cache entries, in the worst case leading to continuous
> configuration changes and revalidation.
> 
> 
> > -----Original Message-----
> > From: Zoltán Balogh
> > Sent: Friday, 02 February, 2018 15:42
> > To: Justin Pettit <jpettit@ovn.org>; Ben Pfaff <blp@ovn.org>
> > Cc: guru@ovn.org; dev@openvswitch.org; Jan Scheurich <jan.scheurich@ericsson.com>
> > Subject: RE: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
> >
> > Hi Justin,
> >
> > I rebased the patches to recent master. Please find them attached.
> >
> > Best regards,
> > Zoltan
> >
> > > -----Original Message-----
> > > From: Justin Pettit [mailto:jpettit@ovn.org]
> > > Sent: Friday, February 02, 2018 12:00 AM
> > > To: Ben Pfaff <mailto:blp@ovn.org>
> > > Cc: Zoltán Balogh <mailto:zoltan.balogh@ericsson.com>; mailto:guru@ovn.org; mailto:dev@openvswitch.org; Jan Scheurich
> > > <mailto:jan.scheurich@ericsson.com>
> > > Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
> > >
> > > I wasn't able to get this patch to apply to the tip of master.  Zoltan, can you rebase this patch and repost?
> > >
> > > The main thing my patch series does is make it so that packets that have a controller action aren't processed
> > > entirely in userspace.  If, for example, the patches expect packets to be in userspace without an explicit slow-path
> > > request when generating the datapath flow, then that would be a problem.
> > >
> > > --Justin
> > >
> > >
> > > > On Feb 1, 2018, at 2:17 PM, Ben Pfaff <mailto:blp@ovn.org> wrote:
> > > >
> > > > Justin, I think this is mainly a question about your patches, can you
> > > > take a look?
> > > >
> > > > On Fri, Jan 26, 2018 at 01:08:35PM +0000, Zoltán Balogh wrote:
> > > >> Hi,
> > > >>
> > > >> I've been investigating the failing unit test. I can confirm, it does fail
> > > >> with my series on master. However, when I created the series and sent it to
> > > >> the mailing list it did not.
> > > >>
> > > >> I've rebased my series to this commit before I sent it to the mailing list.
> > > >>  commit f59cb331c481d08f9a851c07cf31e9d826650485
> > > >>  Author: Yi Yang <mailto:yi.y.yang@intel.com>
> > > >>  Date:   Sat Jan 6 13:47:51 2018 +0800
> > > >>
> > > >> If I apply the series to the commit below, the test does pass.
> > > >>
> > > >> So, I started to search for any commits between the one from Yi Yang and the
> > > >> Last one on master which could make my series fail on the OVN unit test
> > > >> "/32 router IP address".
> > > >>
> > > >> I found, that reverting the series of two commits from Justin Pettit below,
> > > >> makes my series pass the unit test again. Even rebased to master.
> > > >>
> > > >>  commit 74c4530dca939109f3fb79776b60b8722e149738
> > > >>  Author: Justin Pettit <mailto:jpettit@ovn.org>
> > > >>  Date:   Wed Oct 18 23:16:22 2017 -0700
> > > >>
> > > >>      ofproto-dpif: Don't slow-path controller actions with pause.
> > > >>
> > > >>      A previous patch removed slow-pathing for controller actions with the
> > > >>      exception of ones that specified "pause".  This commit removes that
> > > >>      restriction so that no controller actions are slow-pathed.
> > > >>
> > > >>      Signed-off-by: Justin Pettit <mailto:jpettit@ovn.org>
> > > >>      Acked-by: Ben Pfaff <mailto:blp@ovn.org>
> > > >>
> > > >>  commit d39ec23de38464ee35b3098b9f6c5f06d5191015
> > > >>  Author: Justin Pettit <mailto:jpettit@ovn.org>
> > > >>  Date:   Wed Jul 5 15:17:52 2017 -0700
> > > >>
> > > >>      ofproto-dpif: Don't slow-path controller actions.
> > > >>
> > > >>      Controller actions have become more commonly used for purposes other
> > > >>      than just making forwarding decisions (e.g., packet logging).  A packet
> > > >>      that needs to be copied to the controller and forwarded would always be
> > > >>      sent to ovs-vswitchd to be handled, which could negatively affect
> > > >>      performance and cause heavier CPU utilization in ovs-vswitchd.
> > > >>
> > > >>      This commit changes the behavior so that OpenFlow controller actions
> > > >>      become userspace datapath actions while continuing to let packet
> > > >>      forwarding and manipulation continue to be handled by the datapath
> > > >>      directly.
> > > >>
> > > >>      This patch still slow-paths controller actions with the "pause" flag
> > > >>      set.  A future patch will stop slow-pathing these pause actions as
> > > >>      well.
> > > >>
> > > >>      Signed-off-by: Justin Pettit <mailto:jpettit@ovn.org>
> > > >>      Acked-by: Ben Pfaff <mailto:blp@ovn.org>
> > > >>
> > > >> The main difference, my series does compared to master, is not putting the
> > > >> ARP entry of {10.0.0.2, f0:00:00:01:02:04, br-int} into tunnel neighbor
> > > >> cache. All the OF rules are almost the same, except loading different
> > > >> numbers into NXM_NX_REGx and using different metadata.
> > > >>
> > > >> How should this be related to Justin's series?
> > > >>
> > > >> If I modify the unit test and populate the missing ARP entry then the test
> > > >> does pass.
> > > >>
> > > >> Should this entry be present in the ARP neighbor cache in order to make the
> > > >> test pass? If yes, then why?
> > > >>
> > > >> Best regards,
> > > >> Zoltan
> > > >>
> > > >>> -----Original Message-----
> > > >>> From: Ben Pfaff [mailto:blp@ovn.org]
> > > >>> Sent: Tuesday, January 23, 2018 8:02 PM
> > > >>> To: Zoltán Balogh <mailto:zoltan.balogh@ericsson.com>
> > > >>> Cc: mailto:dev@openvswitch.org; Jan Scheurich <mailto:jan.scheurich@ericsson.com>
> > > >>> Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
> > > >>>
> > > >>> On Tue, Jan 09, 2018 at 07:54:33PM +0100, Zoltan Balogh wrote:
> > > >>>> Currenlty, OVS snoops any ARP or ND packets in any bridge and populates
> > > >>>> the tunnel neighbor cache with the retreived data. For instance, when
> > > >>>> ARP reply originated by a tenant is received in an overlay bridge, the
> > > >>>> ARP message is snooped and tunnel neighbor cache is filled with tenant
> > > >>>> data, however only tunnel neighbor data should be stored there.
> > > >>>>
> > > >>>> This patch moves tunnel neighbor snooping from do_xlate_actions() to
> > > >>>> terminate_native_tunnel() where native tunnel is terminated, in order to
> > > >>>> keep ARP neighbor cache clean, i.e. only packets comming from a native
> > > >>>> tunnel are snooped.
> > > >>>>
> > > >>>> By applying the patch, only ARP and Neighbor Advertisement messages
> > > >>>> addressing a tunnel endpoint (LOCAL port of underlay bridge) are
> > > >>>> snooped. In order to achieve this, IP addresses of the bridge are
> > > >>>> retrieved and then stored in xbridge by calling xlate_xbridge_set().
> > > >>>> These data is then matched against the data extracted from an ARP or
> > > >>>> Neighbor Advertisement message in is_neighbor_reply_correct() which is
> > > >>>> invoked from terminate_native_tunnel().
> > > >>>>
> > > >>>> Signed-off-by: Zoltan Balogh <mailto:zoltan.balogh@ericsson.com>
> > > >>>
> > > >>> Thanks a lot!
> > > >>>
> > > >>> It appears to me that this patch makes the following test consistently
> > > >>> fail.  When I revert the test, it passes for me again.  Can you take a
> > > >>> look?
> > > >>>
> > > >>> Thanks,
> > > >>>
> > > >>> Ben.
> > > >>>
> > > >>> #                             -*- compilation -*-
> > > >>> 2370. ovn.at:8136: testing ovn -- /32 router IP address ...
> > > >>> creating ovn-sb database
> > > >>> creating ovn-nb database
> > > >>> starting ovn-northd
> > > >>> starting backup ovn-northd
> > > >>> adding simulator 'main'
> > > >>> adding simulator 'hv1'
> > > >>> adding simulator 'hv2'
> > > >>> ../../tests/ovn.at:8198: ovn_populate_arp__
> > > >>> stdout:
> > > >>> OK
> > > >>> OK
> > > >>>
> > > >>> checking packets in hv2/vif1-tx.pcap against expected:
> > > >>> expected 1 packets, only received 0
> > > >>> ../../tests/ovn.at:8230: sort $rcv_text
> > > >>> --- expout      2018-01-23 10:59:47.231797273 -0800
> > > >>> +++ /home/blp/nicira/ovs/_build/tests/testsuite.dir/at-groups/2370/stdout       2018-01-23 10:59:47.231797273 -
> > > 0800
> > > >>> @@ -1 +0,0 @@
> > > >>> -f0000001020400000001020408004500001c000000003f110100c0a801020a0000020035111100080000
> > > >>> 2370. ovn.at:8136: 2370. ovn -- /32 router IP address (ovn.at:8136): FAILED (ovn.at:8230)
> > > >>>
> > > >>>
> > > >>>> ---
> > > >>>> include/sparse/netinet/in.h   |  10 +++
> > > >>>> ofproto/ofproto-dpif-xlate.c  | 147 ++++++++++++++++++++++++++++++++++++++++--
> > > >>>> tests/tunnel-push-pop-ipv6.at |  68 ++++++++++++++++++-
> > > >>>> tests/tunnel-push-pop.at      |  67 ++++++++++++++++++-
> > > >>>> 4 files changed, 282 insertions(+), 10 deletions(-)
> > > >>>>
> > > >>>> diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h
> > > >>>> index 6abdb2331..eea41bd7f 100644
> > > >>>> --- a/include/sparse/netinet/in.h
> > > >>>> +++ b/include/sparse/netinet/in.h
> > > >>>> @@ -123,6 +123,16 @@ struct sockaddr_in6 {
> > > >>>>      (X)->s6_addr[10] == 0xff &&                \
> > > >>>>      (X)->s6_addr[11] == 0xff)
> > > >>>>
> > > >>>> +#define IN6_IS_ADDR_MC_LINKLOCAL(a)                 \
> > > >>>> +    (((const uint8_t *) (a))[0] == 0xff &&          \
> > > >>>> +     (((const uint8_t *) (a))[1] & 0xf) == 0x2)
> > > >>>> +
> > > >>>> +# define IN6_ARE_ADDR_EQUAL(a,b)                                          \
> > > >>>> +    ((((const uint32_t *) (a))[0] == ((const uint32_t *) (b))[0]) &&      \
> > > >>>> +     (((const uint32_t *) (a))[1] == ((const uint32_t *) (b))[1]) &&      \
> > > >>>> +     (((const uint32_t *) (a))[2] == ((const uint32_t *) (b))[2]) &&      \
> > > >>>> +     (((const uint32_t *) (a))[3] == ((const uint32_t *) (b))[3]))
> > > >>>> +
> > > >>>> #define INET_ADDRSTRLEN 16
> > > >>>> #define INET6_ADDRSTRLEN 46
> > > >>>>
> > > >>>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> > > >>>> index 2328ec1da..16bc0cd96 100644
> > > >>>> --- a/ofproto/ofproto-dpif-xlate.c
> > > >>>> +++ b/ofproto/ofproto-dpif-xlate.c
> > > >>>> @@ -90,6 +90,16 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
> > > >>>>  * recursive or not. */
> > > >>>> #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH)
> > > >>>>
> > > >>>> +/* The structure holds an array of IP addresses assigned to a bridge and the
> > > >>>> + * number of elements in the array. These data are mutable and are evaluated
> > > >>>> + * when ARP or Neighbor Advertisement packets received on a native tunnel
> > > >>>> + * port are xlated. So 'ref_cnt' and RCU are used for synchronization. */
> > > >>>> +struct xbridge_addr {
> > > >>>> +    struct in6_addr *addr;        /* Array of IP addresses of xbridge. */
> > > >>>> +    int n_addr;                   /* Number of IP addresses. */
> > > >>>> +    struct ovs_refcount ref_cnt;
> > > >>>> +};
> > > >>>> +
> > > >>>> struct xbridge {
> > > >>>>     struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */
> > > >>>>     struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
> > > >>>> @@ -113,6 +123,8 @@ struct xbridge {
> > > >>>>
> > > >>>>     /* Datapath feature support. */
> > > >>>>     struct dpif_backer_support support;
> > > >>>> +
> > > >>>> +    struct xbridge_addr *addr;
> > > >>>> };
> > > >>>>
> > > >>>> struct xbundle {
> > > >>>> @@ -576,7 +588,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
> > > >>>>                               const struct dpif_ipfix *,
> > > >>>>                               const struct netflow *,
> > > >>>>                               bool forward_bpdu, bool has_in_band,
> > > >>>> -                              const struct dpif_backer_support *);
> > > >>>> +                              const struct dpif_backer_support *,
> > > >>>> +                              const struct xbridge_addr *);
> > > >>>> static void xlate_xbundle_set(struct xbundle *xbundle,
> > > >>>>                               enum port_vlan_mode vlan_mode,
> > > >>>>                               uint16_t qinq_ethtype, int vlan,
> > > >>>> @@ -826,6 +839,56 @@ xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport)
> > > >>>>                 hash_ofp_port(xport->ofp_port));
> > > >>>> }
> > > >>>>
> > > >>>> +static struct xbridge_addr *
> > > >>>> +xbridge_addr_create(struct xbridge *xbridge)
> > > >>>> +{
> > > >>>> +    struct xbridge_addr *xbridge_addr = xbridge->addr;
> > > >>>> +    struct in6_addr *addr = NULL, *mask = NULL;
> > > >>>> +    struct netdev *dev;
> > > >>>> +    int err, n_addr = 0;
> > > >>>> +
> > > >>>> +    err = netdev_open(xbridge->name, NULL, &dev);
> > > >>>> +    if (!err) {
> > > >>>> +        err = netdev_get_addr_list(dev, &addr, &mask, &n_addr);
> > > >>>> +        if (!err) {
> > > >>>> +            if (!xbridge->addr ||
> > > >>>> +                n_addr != xbridge->addr->n_addr ||
> > > >>>> +                (xbridge->addr->addr && memcmp(addr, xbridge->addr->addr,
> > > >>>> +                                               sizeof(*addr) * n_addr))) {
> > > >>>> +                xbridge_addr = xzalloc(sizeof *xbridge_addr);
> > > >>>> +                xbridge_addr->addr = addr;
> > > >>>> +                xbridge_addr->n_addr = n_addr;
> > > >>>> +                ovs_refcount_init(&xbridge_addr->ref_cnt);
> > > >>>> +            } else {
> > > >>>> +                free(addr);
> > > >>>> +            }
> > > >>>> +            free(mask);
> > > >>>> +        }
> > > >>>> +        netdev_close(dev);
> > > >>>> +    }
> > > >>>> +
> > > >>>> +    return xbridge_addr;
> > > >>>> +}
> > > >>>> +
> > > >>>> +static struct xbridge_addr *
> > > >>>> +xbridge_addr_ref(const struct xbridge_addr *addr_)
> > > >>>> +{
> > > >>>> +    struct xbridge_addr *addr = CONST_CAST(struct xbridge_addr *, addr_);
> > > >>>> +    if (addr) {
> > > >>>> +        ovs_refcount_ref(&addr->ref_cnt);
> > > >>>> +    }
> > > >>>> +    return addr;
> > > >>>> +}
> > > >>>> +
> > > >>>> +static void
> > > >>>> +xbridge_addr_unref(struct xbridge_addr *addr)
> > > >>>> +{
> > > >>>> +    if (addr && ovs_refcount_unref_relaxed(&addr->ref_cnt) == 1) {
> > > >>>> +        free(addr->addr);
> > > >>>> +        free(addr);
> > > >>>> +    }
> > > >>>> +}
> > > >>>> +
> > > >>>> static void
> > > >>>> xlate_xbridge_set(struct xbridge *xbridge,
> > > >>>>                   struct dpif *dpif,
> > > >>>> @@ -836,7 +899,8 @@ xlate_xbridge_set(struct xbridge *xbridge,
> > > >>>>                   const struct dpif_ipfix *ipfix,
> > > >>>>                   const struct netflow *netflow,
> > > >>>>                   bool forward_bpdu, bool has_in_band,
> > > >>>> -                  const struct dpif_backer_support *support)
> > > >>>> +                  const struct dpif_backer_support *support,
> > > >>>> +                  const struct xbridge_addr *addr)
> > > >>>> {
> > > >>>>     if (xbridge->ml != ml) {
> > > >>>>         mac_learning_unref(xbridge->ml);
> > > >>>> @@ -878,6 +942,11 @@ xlate_xbridge_set(struct xbridge *xbridge,
> > > >>>>         xbridge->netflow = netflow_ref(netflow);
> > > >>>>     }
> > > >>>>
> > > >>>> +    if (xbridge->addr != addr) {
> > > >>>> +        xbridge_addr_unref(xbridge->addr);
> > > >>>> +        xbridge->addr = xbridge_addr_ref(addr);
> > > >>>> +    }
> > > >>>> +
> > > >>>>     xbridge->dpif = dpif;
> > > >>>>     xbridge->forward_bpdu = forward_bpdu;
> > > >>>>     xbridge->has_in_band = has_in_band;
> > > >>>> @@ -971,7 +1040,7 @@ xlate_xbridge_copy(struct xbridge *xbridge)
> > > >>>>                       xbridge->rstp, xbridge->ms, xbridge->mbridge,
> > > >>>>                       xbridge->sflow, xbridge->ipfix, xbridge->netflow,
> > > >>>>                       xbridge->forward_bpdu, xbridge->has_in_band,
> > > >>>> -                      &xbridge->support);
> > > >>>> +                      &xbridge->support, xbridge->addr);
> > > >>>>     LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
> > > >>>>         xlate_xbundle_copy(new_xbridge, xbundle);
> > > >>>>     }
> > > >>>> @@ -1126,6 +1195,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
> > > >>>>                   const struct dpif_backer_support *support)
> > > >>>> {
> > > >>>>     struct xbridge *xbridge;
> > > >>>> +    struct xbridge_addr *xbridge_addr, *old_addr;
> > > >>>>
> > > >>>>     ovs_assert(new_xcfg);
> > > >>>>
> > > >>>> @@ -1140,8 +1210,16 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
> > > >>>>     free(xbridge->name);
> > > >>>>     xbridge->name = xstrdup(name);
> > > >>>>
> > > >>>> +    xbridge_addr = xbridge_addr_create(xbridge);
> > > >>>> +    old_addr = xbridge->addr;
> > > >>>> +
> > > >>>>     xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,
> > > >>>> -                      netflow, forward_bpdu, has_in_band, support);
> > > >>>> +                      netflow, forward_bpdu, has_in_band, support,
> > > >>>> +                      xbridge_addr);
> > > >>>> +
> > > >>>> +    if (xbridge_addr != old_addr) {
> > > >>>> +        xbridge_addr_unref(xbridge_addr);
> > > >>>> +    }
> > > >>>> }
> > > >>>>
> > > >>>> static void
> > > >>>> @@ -1171,6 +1249,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
> > > >>>>     netflow_unref(xbridge->netflow);
> > > >>>>     stp_unref(xbridge->stp);
> > > >>>>     rstp_unref(xbridge->rstp);
> > > >>>> +    xbridge_addr_unref(xbridge->addr);
> > > >>>>     hmap_destroy(&xbridge->xports);
> > > >>>>     free(xbridge->name);
> > > >>>>     free(xbridge);
> > > >>>> @@ -3655,6 +3734,54 @@ check_output_prerequisites(struct xlate_ctx *ctx,
> > > >>>>     return true;
> > > >>>> }
> > > >>>>
> > > >>>> +/* Function verifies if destination address of received Neighbor Advertisement
> > > >>>> + * message stored in 'flow' is correct. It should be either FF02::1:FFXX:XXXX
> > > >>>> + * where XX:XXXX stands for the last 24 bits of 'ipv6_addr' or it should match
> > > >>>> + * 'ipv6_addr'. */
> > > >>>> +static bool
> > > >>>> +is_nd_dst_correct(const struct flow *flow, const struct in6_addr *ipv6_addr)
> > > >>>> +{
> > > >>>> +    const uint8_t *flow_ipv6_addr = (uint8_t *) &flow->ipv6_dst;
> > > >>>> +    const uint8_t *addr = (uint8_t *) ipv6_addr;
> > > >>>> +
> > > >>>> +    return (IN6_IS_ADDR_MC_LINKLOCAL(flow_ipv6_addr) &&
> > > >>>> +            flow_ipv6_addr[11] == 0x01 &&
> > > >>>> +            flow_ipv6_addr[12] == 0xff &&
> > > >>>> +            flow_ipv6_addr[13] == addr[13] &&
> > > >>>> +            flow_ipv6_addr[14] == addr[14] &&
> > > >>>> +            flow_ipv6_addr[15] == addr[15]) ||
> > > >>>> +            IN6_ARE_ADDR_EQUAL(&flow->ipv6_dst, ipv6_addr);
> > > >>>> +}
> > > >>>> +
> > > >>>> +/* Function verifies if the ARP reply or Neighbor Advertisement represented by
> > > >>>> + * 'flow' addresses the 'xbridge' of 'ctx'. Returns true if the ARP TA or
> > > >>>> + * neighbor discovery destination is in the list of configured IP addresses of
> > > >>>> + * the bridge. Otherwise, it returns false. */
> > > >>>> +static bool
> > > >>>> +is_neighbor_reply_correct(const struct xlate_ctx *ctx, const struct flow *flow)
> > > >>>> +{
> > > >>>> +    bool ret = false;
> > > >>>> +    int i;
> > > >>>> +    struct xbridge_addr *xbridge_addr = xbridge_addr_ref(ctx->xbridge->addr);
> > > >>>> +
> > > >>>> +    /* Verify if 'nw_dst' of ARP or 'ipv6_dst' of ICMPV6 is in the list. */
> > > >>>> +    for (i = 0; xbridge_addr && i < xbridge_addr->n_addr; i++) {
> > > >>>> +        struct in6_addr *ip_addr = &xbridge_addr->addr[i];
> > > >>>> +        if ((IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> > > >>>> +             flow->dl_type == htons(ETH_TYPE_ARP) &&
> > > >>>> +             in6_addr_get_mapped_ipv4(ip_addr) == flow->nw_dst) ||
> > > >>>> +            (!IN6_IS_ADDR_V4MAPPED(ip_addr) &&
> > > >>>> +              is_nd_dst_correct(flow, ip_addr))) {
> > > >>>> +            /* Found a match. */
> > > >>>> +            ret = true;
> > > >>>> +            break;
> > > >>>> +        }
> > > >>>> +    }
> > > >>>> +
> > > >>>> +    xbridge_addr_unref(xbridge_addr);
> > > >>>> +    return ret;
> > > >>>> +}
> > > >>>> +
> > > >>>> static bool
> > > >>>> terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> > > >>>>                         struct flow *flow, struct flow_wildcards *wc,
> > > >>>> @@ -3667,6 +3794,15 @@ terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
> > > >>>>     if (ofp_port == OFPP_LOCAL &&
> > > >>>>         ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
> > > >>>>         *tnl_port = tnl_port_map_lookup(flow, wc);
> > > >>>> +
> > > >>>> +        /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,
> > > >>>> +         * do tunnel neighbor snooping. */
> > > >>>> +        if (*tnl_port == ODPP_NONE &&
> > > >>>> +            (flow->dl_type == htons(ETH_TYPE_ARP) ||
> > > >>>> +             flow->nw_proto == IPPROTO_ICMPV6) &&
> > > >>>> +             is_neighbor_reply_correct(ctx, flow)) {
> > > >>>> +            tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> > > >>>> +        }
> > > >>>>     }
> > > >>>>
> > > >>>>     return *tnl_port != ODPP_NONE;
> > > >>>> @@ -6183,9 +6319,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
> > > >>>>     struct flow *flow = &ctx->xin->flow;
> > > >>>>     const struct ofpact *a;
> > > >>>>
> > > >>>> -    if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
> > > >>>> -        tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
> > > >>>> -    }
> > > >>>>     /* dl_type already in the mask, not set below. */
> > > >>>>
> > > >>>>     if (!ofpacts_len) {
> > > >>>> diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
> > > >>>> index 0b9d436db..b0f4a7e91 100644
> > > >>>> --- a/tests/tunnel-push-pop-ipv6.at
> > > >>>> +++ b/tests/tunnel-push-pop-ipv6.at
> > > >>>> @@ -55,9 +55,73 @@ AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | un
> > > >>>> ])
> > > >>>>
> > > >>>> dnl Check ARP Snoop
> > > >>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > > >>>
> > > 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > > >>> 00,tll=f8:bc:12:44:34:b6)'])
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > > >>>
> > > 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > > >>> 00,tll=f8:bc:12:44:34:c8)'])
> > > >>>>
> > > >>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::
> > > >>>
> > > 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:
> > > >>> 00,tll=f8:bc:12:44:34:b7)'])
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > > >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > > >>>
> > > 99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > > >>> 00,tll=f8:bc:12:44:34:b6)'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > > >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receiving Neighbot Advertisement with incorrect VLAN id should not alter tunnel neighbor cache
> > > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86
> > > >>>
> > > dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
> > > >>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > > >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache
> > > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86
> > > >>>
> > > dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
> > > >>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > > >>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache
> > > >>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
> > > >>> config:hwaddr=aa:55:aa:55:00:99])
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1
> > > >>>
> > > 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe
> > > >>>
> > > ::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:0
> > > >>> 0:00,tll=f8:bc:12:44:34:c8)'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> > > >>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receive Neighbor Advertisement without VLAN header
> > > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])
> > > >>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> > > >>>> +])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
> > > >>>
> > > 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
> > > >>> 00,tll=f8:bc:12:44:34:b6)'])
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff0
> > > >>>
> > > 0:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00
> > > >>> :00:00,tll=f8:bc:12:44:34:b7)'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>>
> > > >>>> AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
> > > >>>> 2001:cafe::92                                 f8:bc:12:44:34:b6   br0
> > > >>>> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
> > > >>>> index b168f5f2f..4e259730c 100644
> > > >>>> --- a/tests/tunnel-push-pop.at
> > > >>>> +++ b/tests/tunnel-push-pop.at
> > > >>>> @@ -70,9 +70,71 @@ ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101
> > > >>>> ])
> > > >>>>
> > > >>>> dnl Check ARP Snoop
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> > > >>> 2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > > >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> > > >>> 2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > > >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache
> > > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(
> > > >>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > > >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(
> > > >>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
> > > >>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache
> > > >>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
> > > >>> config:hwaddr=aa:55:aa:55:00:99])
> > > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1
> > > >>>
> > > 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.
> > > >>> 1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
> > > >>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > > >>>> +])
> > > >>>> +
> > > >>>> +dnl Receive ARP reply without VLAN header
> > > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])
> > > >>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
> > > >>>> +])
> > > >>>> +
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
> > > >>> 2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
> > > >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.
> > > >>> 2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)'])
> > > >>>>
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
> > > >>>> 1.1.2.92                                      f8:bc:12:44:34:b6   br0
> > > >>>> 1.1.2.93                                      f8:bc:12:44:34:b7   br0
> > > >>>> @@ -190,9 +252,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl
> > > >>>> dnl Check GREL3 only accepts non-fragmented packets?
> > > >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
> > > >>>
> > >
> >
> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a0800450
> > > >>>
> > >
> >
> 00054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
> > > >>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
> > > >>>>
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +ovs-appctl time/warp 1000
> > > >>>> +
> > > >>>> AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
> > > >>>>   port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
> > > >>>> -  port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
> > > >>>> +  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?
> > > >>>> ])
> > > >>>>
> > > >>>> dnl Check decapsulation of Geneve packet with options
> > > >>>> --
> > > >>>> 2.14.1
> > > >>>>
> > > >>>> _______________________________________________
> > > >>>> dev mailing list
> > > >>>> mailto:dev@openvswitch.org
> > > >>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Zoltán Balogh April 5, 2018, 8:03 a.m. UTC | #8
Hi,

Could you use the e-mail address zoltan.balogh.eth@gmail, please?

Thanks,
Zoltan

On 4 April 2018 at 01:52, Jan Scheurich <jan.scheurich@ericsson.com> wrote:
> Hi,
>
> I took this over from Zoltan and investigated the failing unit test a bit further.
>
> The essential difference between master and Zoltan's (rebased) patches is that the dynamic "ARP cache" flow entry
>
>    table=66,priority=100,reg0=0xa000002,reg15=0x2,metadata=0x1 actions=set_field:f0:00:00:01:02:04->eth_dst
>
> (which OVN installs in response to the ARP reply it receives from OVS in PACKET_IN after the first injected IP packet) triggers different behavior in the subsequent revalidation:
>
> In the case of master the existing datapath flow entry
>
> recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth(src=f0:00:00:01:02:03,dst=00:00:00:01:02:03),eth_type(0x0800),ipv4(src=192.168.1.2/255.255.255.254,dst=10.0.0.2/248.0.0.0,ttl=64,frag=no), actions:ct_clear,set(eth(src=00:00:00:01:02:04,dst=00:00:00:00:00:00)),set(ipv4(src=192.168.1.2/255.255.255.254,dst=8.0.0.0/248.0.0.0,ttl=63)),userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=1,rule_cookie=0x62066318,controller_id=0,max_len=65535))
>
> is correctly deleted during revalidation due to the presence of the new rule in table 66, while with Zoltan's patch that same datapath flow entry remains untouched. It seems as if with the patch the revalidation of the datapath flow does not reach the new rule in table 66.
>
> Consequently the next IP packet injected by netdev-dummy/receive then matches the datapath flow and is sent up to OVN again, where it triggers the same ARP query procedure as before.
>
> However, if I add a "sleep 11" before injecting the second IP packet to let the datapath flow entry time out, the test succeeds, proving that the OF pipeline behaves correctly for a real packet but not when revalidating the datapath flow entry with the userspace(controller(...)) action.
>
> I still do not understand how the (passive) tunnel ARP snooping patch can possibly change the translation behavior for an IP packet in such a way that it affects the revalidation result.
>
> Any idea is welcome.
>
> Regards, Jan
>
>
> BTW: The “pseudo” tunnel neighbor cache entry we get in this test on master for the tenant IP address 10.0.0.2
>
> IP                      MAC                     Bridge
> ======================================================
> 10.0.0.2                f0:00:00:01:02:04       br-int
> 192.168.0.2     5e:97:6a:82:7d:41       br-phys
>
> is a good example why we need Zoltan's patch. Any IP address in an ARP reply is blindly inserted into the tunnel neighbor cache. Overlapping IP addresses among tenants can cause frequent overwriting of cache entries, in the worst case leading to continuous configuration changes and revalidation.
>
>
>> -----Original Message-----
>> From: Zoltán Balogh
>> Sent: Friday, 02 February, 2018 15:42
>> To: Justin Pettit <jpettit@ovn.org>; Ben Pfaff <blp@ovn.org>
>> Cc: guru@ovn.org; dev@openvswitch.org; Jan Scheurich <jan.scheurich@ericsson.com>
>> Subject: RE: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
>>
>> Hi Justin,
>>
>> I rebased the patches to recent master. Please find them attached.
>>
>> Best regards,
>> Zoltan
>>
>> > -----Original Message-----
>> > From: Justin Pettit [mailto:jpettit@ovn.org]
>> > Sent: Friday, February 02, 2018 12:00 AM
>> > To: Ben Pfaff <mailto:blp@ovn.org>
>> > Cc: Zoltán Balogh <mailto:zoltan.balogh@ericsson.com>; mailto:guru@ovn.org; mailto:dev@openvswitch.org; Jan Scheurich
>> > <mailto:jan.scheurich@ericsson.com>
>> > Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
>> >
>> > I wasn't able to get this patch to apply to the tip of master.  Zoltan, can you rebase this patch and repost?
>> >
>> > The main thing my patch series does is make it so that packets that have a controller action aren't processed
>> > entirely in userspace.  If, for example, the patches expect packets to be in userspace without an explicit slow-path
>> > request when generating the datapath flow, then that would be a problem.
>> >
>> > --Justin
>> >
>> >
>> > > On Feb 1, 2018, at 2:17 PM, Ben Pfaff <mailto:blp@ovn.org> wrote:
>> > >
>> > > Justin, I think this is mainly a question about your patches, can you
>> > > take a look?
>> > >
>> > > On Fri, Jan 26, 2018 at 01:08:35PM +0000, Zoltán Balogh wrote:
>> > >> Hi,
>> > >>
>> > >> I've been investigating the failing unit test. I can confirm, it does fail
>> > >> with my series on master. However, when I created the series and sent it to
>> > >> the mailing list it did not.
>> > >>
>> > >> I've rebased my series to this commit before I sent it to the mailing list.
>> > >>  commit f59cb331c481d08f9a851c07cf31e9d826650485
>> > >>  Author: Yi Yang <mailto:yi.y.yang@intel.com>
>> > >>  Date:   Sat Jan 6 13:47:51 2018 +0800
>> > >>
>> > >> If I apply the series to the commit below, the test does pass.
>> > >>
>> > >> So, I started to search for any commits between the one from Yi Yang and the
>> > >> Last one on master which could make my series fail on the OVN unit test
>> > >> "/32 router IP address".
>> > >>
>> > >> I found, that reverting the series of two commits from Justin Pettit below,
>> > >> makes my series pass the unit test again. Even rebased to master.
>> > >>
>> > >>  commit 74c4530dca939109f3fb79776b60b8722e149738
>> > >>  Author: Justin Pettit <mailto:jpettit@ovn.org>
>> > >>  Date:   Wed Oct 18 23:16:22 2017 -0700
>> > >>
>> > >>      ofproto-dpif: Don't slow-path controller actions with pause.
>> > >>
>> > >>      A previous patch removed slow-pathing for controller actions with the
>> > >>      exception of ones that specified "pause".  This commit removes that
>> > >>      restriction so that no controller actions are slow-pathed.
>> > >>
>> > >>      Signed-off-by: Justin Pettit <mailto:jpettit@ovn.org>
>> > >>      Acked-by: Ben Pfaff <mailto:blp@ovn.org>
>> > >>
>> > >>  commit d39ec23de38464ee35b3098b9f6c5f06d5191015
>> > >>  Author: Justin Pettit <mailto:jpettit@ovn.org>
>> > >>  Date:   Wed Jul 5 15:17:52 2017 -0700
>> > >>
>> > >>      ofproto-dpif: Don't slow-path controller actions.
>> > >>
>> > >>      Controller actions have become more commonly used for purposes other
>> > >>      than just making forwarding decisions (e.g., packet logging).  A packet
>> > >>      that needs to be copied to the controller and forwarded would always be
>> > >>      sent to ovs-vswitchd to be handled, which could negatively affect
>> > >>      performance and cause heavier CPU utilization in ovs-vswitchd.
>> > >>
>> > >>      This commit changes the behavior so that OpenFlow controller actions
>> > >>      become userspace datapath actions while continuing to let packet
>> > >>      forwarding and manipulation continue to be handled by the datapath
>> > >>      directly.
>> > >>
>> > >>      This patch still slow-paths controller actions with the "pause" flag
>> > >>      set.  A future patch will stop slow-pathing these pause actions as
>> > >>      well.
>> > >>
>> > >>      Signed-off-by: Justin Pettit <mailto:jpettit@ovn.org>
>> > >>      Acked-by: Ben Pfaff <mailto:blp@ovn.org>
>> > >>
>> > >> The main difference, my series does compared to master, is not putting the
>> > >> ARP entry of {10.0.0.2, f0:00:00:01:02:04, br-int} into tunnel neighbor
>> > >> cache. All the OF rules are almost the same, except loading different
>> > >> numbers into NXM_NX_REGx and using different metadata.
>> > >>
>> > >> How should this be related to Justin's series?
>> > >>
>> > >> If I modify the unit test and populate the missing ARP entry then the test
>> > >> does pass.
>> > >>
>> > >> Should this entry be present in the ARP neighbor cache in order to make the
>> > >> test pass? If yes, then why?
>> > >>
>> > >> Best regards,
>> > >> Zoltan
>> > >>
>> > >>> -----Original Message-----
>> > >>> From: Ben Pfaff [mailto:blp@ovn.org]
>> > >>> Sent: Tuesday, January 23, 2018 8:02 PM
>> > >>> To: Zoltán Balogh <mailto:zoltan.balogh@ericsson.com>
>> > >>> Cc: mailto:dev@openvswitch.org; Jan Scheurich <mailto:jan.scheurich@ericsson.com>
>> > >>> Subject: Re: [ovs-dev] [PATCH v3 3/3] xlate: call tnl_neigh_snoop() from terminate_native_tunnel()
>> > >>>
>> > >>> On Tue, Jan 09, 2018 at 07:54:33PM +0100, Zoltan Balogh wrote:
>> > >>>> Currenlty, OVS snoops any ARP or ND packets in any bridge and populates
>> > >>>> the tunnel neighbor cache with the retreived data. For instance, when
>> > >>>> ARP reply originated by a tenant is received in an overlay bridge, the
>> > >>>> ARP message is snooped and tunnel neighbor cache is filled with tenant
>> > >>>> data, however only tunnel neighbor data should be stored there.
>> > >>>>
>> > >>>> This patch moves tunnel neighbor snooping from do_xlate_actions() to
>> > >>>> terminate_native_tunnel() where native tunnel is terminated, in order to
>> > >>>> keep ARP neighbor cache clean, i.e. only packets comming from a native
>> > >>>> tunnel are snooped.
>> > >>>>
>> > >>>> By applying the patch, only ARP and Neighbor Advertisement messages
>> > >>>> addressing a tunnel endpoint (LOCAL port of underlay bridge) are
>> > >>>> snooped. In order to achieve this, IP addresses of the bridge are
>> > >>>> retrieved and then stored in xbridge by calling xlate_xbridge_set().
>> > >>>> These data is then matched against the data extracted from an ARP or
>> > >>>> Neighbor Advertisement message in is_neighbor_reply_correct() which is
>> > >>>> invoked from terminate_native_tunnel().
>> > >>>>
>> > >>>> Signed-off-by: Zoltan Balogh <mailto:zoltan.balogh@ericsson.com>
>> > >>>
>> > >>> Thanks a lot!
>> > >>>
>> > >>> It appears to me that this patch makes the following test consistently
>> > >>> fail.  When I revert the test, it passes for me again.  Can you take a
>> > >>> look?
>> > >>>
>> > >>> Thanks,
>> > >>>
>> > >>> Ben.
>> > >>>
>> > >>> #                             -*- compilation -*-
>> > >>> 2370. ovn.at:8136: testing ovn -- /32 router IP address ...
>> > >>> creating ovn-sb database
>> > >>> creating ovn-nb database
>> > >>> starting ovn-northd
>> > >>> starting backup ovn-northd
>> > >>> adding simulator 'main'
>> > >>> adding simulator 'hv1'
>> > >>> adding simulator 'hv2'
>> > >>> ../../tests/ovn.at:8198: ovn_populate_arp__
>> > >>> stdout:
>> > >>> OK
>> > >>> OK
>> > >>>
>> > >>> checking packets in hv2/vif1-tx.pcap against expected:
>> > >>> expected 1 packets, only received 0
>> > >>> ../../tests/ovn.at:8230: sort $rcv_text
>> > >>> --- expout      2018-01-23 10:59:47.231797273 -0800
>> > >>> +++ /home/blp/nicira/ovs/_build/tests/testsuite.dir/at-groups/2370/stdout       2018-01-23 10:59:47.231797273 -
>> > 0800
>> > >>> @@ -1 +0,0 @@
>> > >>> -f0000001020400000001020408004500001c000000003f110100c0a801020a0000020035111100080000
>> > >>> 2370. ovn.at:8136: 2370. ovn -- /32 router IP address (ovn.at:8136): FAILED (ovn.at:8230)
>> > >>>
>> > >>>
>> > >>>> ---
>> > >>>> include/sparse/netinet/in.h   |  10 +++
>> > >>>> ofproto/ofproto-dpif-xlate.c  | 147 ++++++++++++++++++++++++++++++++++++++++--
>> > >>>> tests/tunnel-push-pop-ipv6.at |  68 ++++++++++++++++++-
>> > >>>> tests/tunnel-push-pop.at      |  67 ++++++++++++++++++-
>> > >>>> 4 files changed, 282 insertions(+), 10 deletions(-)
>> > >>>>
>> > >>>> diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h
>> > >>>> index 6abdb2331..eea41bd7f 100644
>> > >>>> --- a/include/sparse/netinet/in.h
>> > >>>> +++ b/include/sparse/netinet/in.h
>> > >>>> @@ -123,6 +123,16 @@ struct sockaddr_in6 {
>> > >>>>      (X)->s6_addr[10] == 0xff &&                \
>> > >>>>      (X)->s6_addr[11] == 0xff)
>> > >>>>
>> > >>>> +#define IN6_IS_ADDR_MC_LINKLOCAL(a)                 \
>> > >>>> +    (((const uint8_t *) (a))[0] == 0xff &&          \
>> > >>>> +     (((const uint8_t *) (a))[1] & 0xf) == 0x2)
>> > >>>> +
>> > >>>> +# define IN6_ARE_ADDR_EQUAL(a,b)                                          \
>> > >>>> +    ((((const uint32_t *) (a))[0] == ((const uint32_t *) (b))[0]) &&      \
>> > >>>> +     (((const uint32_t *) (a))[1] == ((const uint32_t *) (b))[1]) &&      \
>> > >>>> +     (((const uint32_t *) (a))[2] == ((const uint32_t *) (b))[2]) &&      \
>> > >>>> +     (((const uint32_t *) (a))[3] == ((const uint32_t *) (b))[3]))
>> > >>>> +
>> > >>>> #define INET_ADDRSTRLEN 16
>> > >>>> #define INET6_ADDRSTRLEN 46
>> > >>>>
>> > >>>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>> > >>>> index 2328ec1da..16bc0cd96 100644
>> > >>>> --- a/ofproto/ofproto-dpif-xlate.c
>> > >>>> +++ b/ofproto/ofproto-dpif-xlate.c
>> > >>>> @@ -90,6 +90,16 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
>> > >>>>  * recursive or not. */
>> > >>>> #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH)
>> > >>>>
>> > >>>> +/* The structure holds an array of IP addresses assigned to a bridge and the
>> > >>>> + * number of elements in the array. These data are mutable and are evaluated
>> > >>>> + * when ARP or Neighbor Advertisement packets received on a native tunnel
>> > >>>> + * port are xlated. So 'ref_cnt' and RCU are used for synchronization. */
>> > >>>> +struct xbridge_addr {
>> > >>>> +    struct in6_addr *addr;        /* Array of IP addresses of xbridge. */
>> > >>>> +    int n_addr;                   /* Number of IP addresses. */
>> > >>>> +    struct ovs_refcount ref_cnt;
>> > >>>> +};
>> > >>>> +
>> > >>>> struct xbridge {
>> > >>>>     struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */
>> > >>>>     struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
>> > >>>> @@ -113,6 +123,8 @@ struct xbridge {
>> > >>>>
>> > >>>>     /* Datapath feature support. */
>> > >>>>     struct dpif_backer_support support;
>> > >>>> +
>> > >>>> +    struct xbridge_addr *addr;
>> > >>>> };
>> > >>>>
>> > >>>> struct xbundle {
>> > >>>> @@ -576,7 +588,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
>> > >>>>                               const struct dpif_ipfix *,
>> > >>>>                               const struct netflow *,
>> > >>>>                               bool forward_bpdu, bool has_in_band,
>> > >>>> -                              const struct dpif_backer_support *);
>> > >>>> +                              const struct dpif_backer_support *,
>> > >>>> +                              const struct xbridge_addr *);
>> > >>>> static void xlate_xbundle_set(struct xbundle *xbundle,
>> > >>>>                               enum port_vlan_mode vlan_mode,
>> > >>>>                               uint16_t qinq_ethtype, int vlan,
>> > >>>> @@ -826,6 +839,56 @@ xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport)
>> > >>>>                 hash_ofp_port(xport->ofp_port));
>> > >>>> }
>> > >>>>
>> > >>>> +static struct xbridge_addr *
>> > >>>> +xbridge_addr_create(struct xbridge *xbridge)
>> > >>>> +{
>> > >>>> +    struct xbridge_addr *xbridge_addr = xbridge->addr;
>> > >>>> +    struct in6_addr *addr = NULL, *mask = NULL;
>> > >>>> +    struct netdev *dev;
>> > >>>> +    int err, n_addr = 0;
>> > >>>> +
>> > >>>> +    err = netdev_open(xbridge->name, NULL, &dev);
>> > >>>> +    if (!err) {
>> > >>>> +        err = netdev_get_addr_list(dev, &addr, &mask, &n_addr);
>> > >>>> +        if (!err) {
>> > >>>> +            if (!xbridge->addr ||
>> > >>>> +                n_addr != xbridge->addr->n_addr ||
>> > >>>> +                (xbridge->addr->addr && memcmp(addr, xbridge->addr->addr,
>> > >>>> +                                               sizeof(*addr) * n_addr))) {
>> > >>>> +                xbridge_addr = xzalloc(sizeof *xbridge_addr);
>> > >>>> +                xbridge_addr->addr = addr;
>> > >>>> +                xbridge_addr->n_addr = n_addr;
>> > >>>> +                ovs_refcount_init(&xbridge_addr->ref_cnt);
>> > >>>> +            } else {
>> > >>>> +                free(addr);
>> > >>>> +            }
>> > >>>> +            free(mask);
>> > >>>> +        }
>> > >>>> +        netdev_close(dev);
>> > >>>> +    }
>> > >>>> +
>> > >>>> +    return xbridge_addr;
>> > >>>> +}
>> > >>>> +
>> > >>>> +static struct xbridge_addr *
>> > >>>> +xbridge_addr_ref(const struct xbridge_addr *addr_)
>> > >>>> +{
>> > >>>> +    struct xbridge_addr *addr = CONST_CAST(struct xbridge_addr *, addr_);
>> > >>>> +    if (addr) {
>> > >>>> +        ovs_refcount_ref(&addr->ref_cnt);
>> > >>>> +    }
>> > >>>> +    return addr;
>> > >>>> +}
>> > >>>> +
>> > >>>> +static void
>> > >>>> +xbridge_addr_unref(struct xbridge_addr *addr)
>> > >>>> +{
>> > >>>> +    if (addr && ovs_refcount_unref_relaxed(&addr->ref_cnt) == 1) {
>> > >>>> +        free(addr->addr);
>> > >>>> +        free(addr);
>> > >>>> +    }
>> > >>>> +}
>> > >>>> +
>> > >>>> static void
>> > >>>> xlate_xbridge_set(struct xbridge *xbridge,
>> > >>>>                   struct dpif *dpif,
>> > >>>> @@ -836,7 +899,8 @@ xlate_xbridge_set(struct xbridge *xbridge,
>> > >>>>                   const struct dpif_ipfix *ipfix,
>> > >>>>                   const struct netflow *netflow,
>> > >>>>                   bool forward_bpdu, bool has_in_band,
>> > >>>> -                  const struct dpif_backer_support *support)
>> > >>>> +                  const struct dpif_backer_support *support,
>> > >>>> +                  const struct xbridge_addr *addr)
>> > >>>> {
>> > >>>>     if (xbridge->ml != ml) {
>> > >>>>         mac_learning_unref(xbridge->ml);
>> > >>>> @@ -878,6 +942,11 @@ xlate_xbridge_set(struct xbridge *xbridge,
>> > >>>>         xbridge->netflow = netflow_ref(netflow);
>> > >>>>     }
>> > >>>>
>> > >>>> +    if (xbridge->addr != addr) {
>> > >>>> +        xbridge_addr_unref(xbridge->addr);
>> > >>>> +        xbridge->addr = xbridge_addr_ref(addr);
>> > >>>> +    }
>> > >>>> +
>> > >>>>     xbridge->dpif = dpif;
>> > >>>>     xbridge->forward_bpdu = forward_bpdu;
>> > >>>>     xbridge->has_in_band = has_in_band;
>> > >>>> @@ -971,7 +1040,7 @@ xlate_xbridge_copy(struct xbridge *xbridge)
>> > >>>>                       xbridge->rstp, xbridge->ms, xbridge->mbridge,
>> > >>>>                       xbridge->sflow, xbridge->ipfix, xbridge->netflow,
>> > >>>>                       xbridge->forward_bpdu, xbridge->has_in_band,
>> > >>>> -                      &xbridge->support);
>> > >>>> +                      &xbridge->support, xbridge->addr);
>> > >>>>     LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
>> > >>>>         xlate_xbundle_copy(new_xbridge, xbundle);
>> > >>>>     }
>> > >>>> @@ -1126,6 +1195,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
>> > >>>>                   const struct dpif_backer_support *support)
>> > >>>> {
>> > >>>>     struct xbridge *xbridge;
>> > >>>> +    struct xbridge_addr *xbridge_addr, *old_addr;
>> > >>>>
>> > >>>>     ovs_assert(new_xcfg);
>> > >>>>
>> > >>>> @@ -1140,8 +1210,16 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
>> > >>>>     free(xbridge->name);
>> > >>>>     xbridge->name = xstrdup(name);
>> > >>>>
>> > >>>> +    xbridge_addr = xbridge_addr_create(xbridge);
>> > >>>> +    old_addr = xbridge->addr;
>> > >>>> +
>> > >>>>     xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,
>> > >>>> -                      netflow, forward_bpdu, has_in_band, support);
>> > >>>> +                      netflow, forward_bpdu, has_in_band, support,
>> > >>>> +                      xbridge_addr);
>> > >>>> +
>> > >>>> +    if (xbridge_addr != old_addr) {
>> > >>>> +        xbridge_addr_unref(xbridge_addr);
>> > >>>> +    }
>> > >>>> }
>> > >>>>
>> > >>>> static void
>> > >>>> @@ -1171,6 +1249,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
>> > >>>>     netflow_unref(xbridge->netflow);
>> > >>>>     stp_unref(xbridge->stp);
>> > >>>>     rstp_unref(xbridge->rstp);
>> > >>>> +    xbridge_addr_unref(xbridge->addr);
>> > >>>>     hmap_destroy(&xbridge->xports);
>> > >>>>     free(xbridge->name);
>> > >>>>     free(xbridge);
>> > >>>> @@ -3655,6 +3734,54 @@ check_output_prerequisites(struct xlate_ctx *ctx,
>> > >>>>     return true;
>> > >>>> }
>> > >>>>
>> > >>>> +/* Function verifies if destination address of received Neighbor Advertisement
>> > >>>> + * message stored in 'flow' is correct. It should be either FF02::1:FFXX:XXXX
>> > >>>> + * where XX:XXXX stands for the last 24 bits of 'ipv6_addr' or it should match
>> > >>>> + * 'ipv6_addr'. */
>> > >>>> +static bool
>> > >>>> +is_nd_dst_correct(const struct flow *flow, const struct in6_addr *ipv6_addr)
>> > >>>> +{
>> > >>>> +    const uint8_t *flow_ipv6_addr = (uint8_t *) &flow->ipv6_dst;
>> > >>>> +    const uint8_t *addr = (uint8_t *) ipv6_addr;
>> > >>>> +
>> > >>>> +    return (IN6_IS_ADDR_MC_LINKLOCAL(flow_ipv6_addr) &&
>> > >>>> +            flow_ipv6_addr[11] == 0x01 &&
>> > >>>> +            flow_ipv6_addr[12] == 0xff &&
>> > >>>> +            flow_ipv6_addr[13] == addr[13] &&
>> > >>>> +            flow_ipv6_addr[14] == addr[14] &&
>> > >>>> +            flow_ipv6_addr[15] == addr[15]) ||
>> > >>>> +            IN6_ARE_ADDR_EQUAL(&flow->ipv6_dst, ipv6_addr);
>> > >>>> +}
>> > >>>> +
>> > >>>> +/* Function verifies if the ARP reply or Neighbor Advertisement represented by
>> > >>>> + * 'flow' addresses the 'xbridge' of 'ctx'. Returns true if the ARP TA or
>> > >>>> + * neighbor discovery destination is in the list of configured IP addresses of
>> > >>>> + * the bridge. Otherwise, it returns false. */
>> > >>>> +static bool
>> > >>>> +is_neighbor_reply_correct(const struct xlate_ctx *ctx, const struct flow *flow)
>> > >>>> +{
>> > >>>> +    bool ret = false;
>> > >>>> +    int i;
>> > >>>> +    struct xbridge_addr *xbridge_addr = xbridge_addr_ref(ctx->xbridge->addr);
>> > >>>> +
>> > >>>> +    /* Verify if 'nw_dst' of ARP or 'ipv6_dst' of ICMPV6 is in the list. */
>> > >>>> +    for (i = 0; xbridge_addr && i < xbridge_addr->n_addr; i++) {
>> > >>>> +        struct in6_addr *ip_addr = &xbridge_addr->addr[i];
>> > >>>> +        if ((IN6_IS_ADDR_V4MAPPED(ip_addr) &&
>> > >>>> +             flow->dl_type == htons(ETH_TYPE_ARP) &&
>> > >>>> +             in6_addr_get_mapped_ipv4(ip_addr) == flow->nw_dst) ||
>> > >>>> +            (!IN6_IS_ADDR_V4MAPPED(ip_addr) &&
>> > >>>> +              is_nd_dst_correct(flow, ip_addr))) {
>> > >>>> +            /* Found a match. */
>> > >>>> +            ret = true;
>> > >>>> +            break;
>> > >>>> +        }
>> > >>>> +    }
>> > >>>> +
>> > >>>> +    xbridge_addr_unref(xbridge_addr);
>> > >>>> +    return ret;
>> > >>>> +}
>> > >>>> +
>> > >>>> static bool
>> > >>>> terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
>> > >>>>                         struct flow *flow, struct flow_wildcards *wc,
>> > >>>> @@ -3667,6 +3794,15 @@ terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
>> > >>>>     if (ofp_port == OFPP_LOCAL &&
>> > >>>>         ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
>> > >>>>         *tnl_port = tnl_port_map_lookup(flow, wc);
>> > >>>> +
>> > >>>> +        /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,
>> > >>>> +         * do tunnel neighbor snooping. */
>> > >>>> +        if (*tnl_port == ODPP_NONE &&
>> > >>>> +            (flow->dl_type == htons(ETH_TYPE_ARP) ||
>> > >>>> +             flow->nw_proto == IPPROTO_ICMPV6) &&
>> > >>>> +             is_neighbor_reply_correct(ctx, flow)) {
>> > >>>> +            tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
>> > >>>> +        }
>> > >>>>     }
>> > >>>>
>> > >>>>     return *tnl_port != ODPP_NONE;
>> > >>>> @@ -6183,9 +6319,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
>> > >>>>     struct flow *flow = &ctx->xin->flow;
>> > >>>>     const struct ofpact *a;
>> > >>>>
>> > >>>> -    if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
>> > >>>> -        tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
>> > >>>> -    }
>> > >>>>     /* dl_type already in the mask, not set below. */
>> > >>>>
>> > >>>>     if (!ofpacts_len) {
>> > >>>> diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
>> > >>>> index 0b9d436db..b0f4a7e91 100644
>> > >>>> --- a/tests/tunnel-push-pop-ipv6.at
>> > >>>> +++ b/tests/tunnel-push-pop-ipv6.at
>> > >>>> @@ -55,9 +55,73 @@ AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | un
>> > >>>> ])
>> > >>>>
>> > >>>> dnl Check ARP Snoop
>> > >>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
>> > >>>
>> > 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
>> > >>> 00,tll=f8:bc:12:44:34:b6)'])
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
>> > >>>
>> > 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
>> > >>> 00,tll=f8:bc:12:44:34:c8)'])
>> > >>>>
>> > >>>> -AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::
>> > >>>
>> > 94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:
>> > >>> 00,tll=f8:bc:12:44:34:b7)'])
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
>> > >>>
>> > 99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
>> > >>> 00,tll=f8:bc:12:44:34:b6)'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receiving Neighbot Advertisement with incorrect VLAN id should not alter tunnel neighbor cache
>> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86
>> > >>>
>> > dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
>> > >>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:c8   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache
>> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86
>> > >>>
>> > dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),n
>> > >>> d(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache
>> > >>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
>> > >>> config:hwaddr=aa:55:aa:55:00:99])
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1
>> > >>>
>> > 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe
>> > >>>
>> > ::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:0
>> > >>> 0:00,tll=f8:bc:12:44:34:c8)'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
>> > >>>> +2001:cafe::92                                 f8:bc:12:44:34:b6   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receive Neighbor Advertisement without VLAN header
>> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])
>> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
>> > >>>> +])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::
>> > >>>
>> > 88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:
>> > >>> 00,tll=f8:bc:12:44:34:b6)'])
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff0
>> > >>>
>> > 0:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00
>> > >>> :00:00,tll=f8:bc:12:44:34:b7)'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>>
>> > >>>> AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
>> > >>>> 2001:cafe::92                                 f8:bc:12:44:34:b6   br0
>> > >>>> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
>> > >>>> index b168f5f2f..4e259730c 100644
>> > >>>> --- a/tests/tunnel-push-pop.at
>> > >>>> +++ b/tests/tunnel-push-pop.at
>> > >>>> @@ -70,9 +70,71 @@ ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101
>> > >>>> ])
>> > >>>>
>> > >>>> dnl Check ARP Snoop
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
>> > >>> 2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
>> > >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
>> > >>> 2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
>> > >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache
>> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=10])
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(
>> > >>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
>> > >>>> +1.1.2.92                                      f8:bc:12:44:34:c8   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(
>> > >>> eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
>> > >>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache
>> > >>>> +AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-
>> > >>> config:hwaddr=aa:55:aa:55:00:99])
>> > >>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p1
>> > >>>
>> > 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.
>> > >>> 1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
>> > >>>> +1.1.2.92                                      f8:bc:12:44:34:b6   br0
>> > >>>> +])
>> > >>>> +
>> > >>>> +dnl Receive ARP reply without VLAN header
>> > >>>> +AT_CHECK([ovs-vsctl set port br0 tag=0])
>> > >>>> +AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
>> > >>>> +])
>> > >>>> +
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.
>> > >>> 2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
>> > >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> > 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.
>> > >>> 2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)'])
>> > >>>>
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
>> > >>>> 1.1.2.92                                      f8:bc:12:44:34:b6   br0
>> > >>>> 1.1.2.93                                      f8:bc:12:44:34:b7   br0
>> > >>>> @@ -190,9 +252,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl
>> > >>>> dnl Check GREL3 only accepts non-fragmented packets?
>> > >>>> AT_CHECK([ovs-appctl netdev-dummy/receive p0
>> > >>>
>> >
>> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a0800450
>> > >>>
>> >
>> 00054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
>> > >>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
>> > >>>>
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +ovs-appctl time/warp 1000
>> > >>>> +
>> > >>>> AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
>> > >>>>   port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
>> > >>>> -  port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
>> > >>>> +  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?
>> > >>>> ])
>> > >>>>
>> > >>>> dnl Check decapsulation of Geneve packet with options
>> > >>>> --
>> > >>>> 2.14.1
>> > >>>>
>> > >>>> _______________________________________________
>> > >>>> dev mailing list
>> > >>>> mailto:dev@openvswitch.org
>> > >>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
diff mbox series

Patch

diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h
index 6abdb2331..eea41bd7f 100644
--- a/include/sparse/netinet/in.h
+++ b/include/sparse/netinet/in.h
@@ -123,6 +123,16 @@  struct sockaddr_in6 {
      (X)->s6_addr[10] == 0xff &&                \
      (X)->s6_addr[11] == 0xff)
 
+#define IN6_IS_ADDR_MC_LINKLOCAL(a)                 \
+    (((const uint8_t *) (a))[0] == 0xff &&          \
+     (((const uint8_t *) (a))[1] & 0xf) == 0x2)
+
+# define IN6_ARE_ADDR_EQUAL(a,b)                                          \
+    ((((const uint32_t *) (a))[0] == ((const uint32_t *) (b))[0]) &&      \
+     (((const uint32_t *) (a))[1] == ((const uint32_t *) (b))[1]) &&      \
+     (((const uint32_t *) (a))[2] == ((const uint32_t *) (b))[2]) &&      \
+     (((const uint32_t *) (a))[3] == ((const uint32_t *) (b))[3]))
+
 #define INET_ADDRSTRLEN 16
 #define INET6_ADDRSTRLEN 46
 
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 2328ec1da..16bc0cd96 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -90,6 +90,16 @@  VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
  * recursive or not. */
 #define MAX_RESUBMITS (MAX_DEPTH * MAX_DEPTH)
 
+/* The structure holds an array of IP addresses assigned to a bridge and the
+ * number of elements in the array. These data are mutable and are evaluated
+ * when ARP or Neighbor Advertisement packets received on a native tunnel
+ * port are xlated. So 'ref_cnt' and RCU are used for synchronization. */
+struct xbridge_addr {
+    struct in6_addr *addr;        /* Array of IP addresses of xbridge. */
+    int n_addr;                   /* Number of IP addresses. */
+    struct ovs_refcount ref_cnt;
+};
+
 struct xbridge {
     struct hmap_node hmap_node;   /* Node in global 'xbridges' map. */
     struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */
@@ -113,6 +123,8 @@  struct xbridge {
 
     /* Datapath feature support. */
     struct dpif_backer_support support;
+
+    struct xbridge_addr *addr;
 };
 
 struct xbundle {
@@ -576,7 +588,8 @@  static void xlate_xbridge_set(struct xbridge *, struct dpif *,
                               const struct dpif_ipfix *,
                               const struct netflow *,
                               bool forward_bpdu, bool has_in_band,
-                              const struct dpif_backer_support *);
+                              const struct dpif_backer_support *,
+                              const struct xbridge_addr *);
 static void xlate_xbundle_set(struct xbundle *xbundle,
                               enum port_vlan_mode vlan_mode,
                               uint16_t qinq_ethtype, int vlan,
@@ -826,6 +839,56 @@  xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport)
                 hash_ofp_port(xport->ofp_port));
 }
 
+static struct xbridge_addr *
+xbridge_addr_create(struct xbridge *xbridge)
+{
+    struct xbridge_addr *xbridge_addr = xbridge->addr;
+    struct in6_addr *addr = NULL, *mask = NULL;
+    struct netdev *dev;
+    int err, n_addr = 0;
+
+    err = netdev_open(xbridge->name, NULL, &dev);
+    if (!err) {
+        err = netdev_get_addr_list(dev, &addr, &mask, &n_addr);
+        if (!err) {
+            if (!xbridge->addr ||
+                n_addr != xbridge->addr->n_addr ||
+                (xbridge->addr->addr && memcmp(addr, xbridge->addr->addr,
+                                               sizeof(*addr) * n_addr))) {
+                xbridge_addr = xzalloc(sizeof *xbridge_addr);
+                xbridge_addr->addr = addr;
+                xbridge_addr->n_addr = n_addr;
+                ovs_refcount_init(&xbridge_addr->ref_cnt);
+            } else {
+                free(addr);
+            }
+            free(mask);
+        }
+        netdev_close(dev);
+    }
+
+    return xbridge_addr;
+}
+
+static struct xbridge_addr *
+xbridge_addr_ref(const struct xbridge_addr *addr_)
+{
+    struct xbridge_addr *addr = CONST_CAST(struct xbridge_addr *, addr_);
+    if (addr) {
+        ovs_refcount_ref(&addr->ref_cnt);
+    }
+    return addr;
+}
+
+static void
+xbridge_addr_unref(struct xbridge_addr *addr)
+{
+    if (addr && ovs_refcount_unref_relaxed(&addr->ref_cnt) == 1) {
+        free(addr->addr);
+        free(addr);
+    }
+}
+
 static void
 xlate_xbridge_set(struct xbridge *xbridge,
                   struct dpif *dpif,
@@ -836,7 +899,8 @@  xlate_xbridge_set(struct xbridge *xbridge,
                   const struct dpif_ipfix *ipfix,
                   const struct netflow *netflow,
                   bool forward_bpdu, bool has_in_band,
-                  const struct dpif_backer_support *support)
+                  const struct dpif_backer_support *support,
+                  const struct xbridge_addr *addr)
 {
     if (xbridge->ml != ml) {
         mac_learning_unref(xbridge->ml);
@@ -878,6 +942,11 @@  xlate_xbridge_set(struct xbridge *xbridge,
         xbridge->netflow = netflow_ref(netflow);
     }
 
+    if (xbridge->addr != addr) {
+        xbridge_addr_unref(xbridge->addr);
+        xbridge->addr = xbridge_addr_ref(addr);
+    }
+
     xbridge->dpif = dpif;
     xbridge->forward_bpdu = forward_bpdu;
     xbridge->has_in_band = has_in_band;
@@ -971,7 +1040,7 @@  xlate_xbridge_copy(struct xbridge *xbridge)
                       xbridge->rstp, xbridge->ms, xbridge->mbridge,
                       xbridge->sflow, xbridge->ipfix, xbridge->netflow,
                       xbridge->forward_bpdu, xbridge->has_in_band,
-                      &xbridge->support);
+                      &xbridge->support, xbridge->addr);
     LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) {
         xlate_xbundle_copy(new_xbridge, xbundle);
     }
@@ -1126,6 +1195,7 @@  xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
                   const struct dpif_backer_support *support)
 {
     struct xbridge *xbridge;
+    struct xbridge_addr *xbridge_addr, *old_addr;
 
     ovs_assert(new_xcfg);
 
@@ -1140,8 +1210,16 @@  xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
     free(xbridge->name);
     xbridge->name = xstrdup(name);
 
+    xbridge_addr = xbridge_addr_create(xbridge);
+    old_addr = xbridge->addr;
+
     xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix,
-                      netflow, forward_bpdu, has_in_band, support);
+                      netflow, forward_bpdu, has_in_band, support,
+                      xbridge_addr);
+
+    if (xbridge_addr != old_addr) {
+        xbridge_addr_unref(xbridge_addr);
+    }
 }
 
 static void
@@ -1171,6 +1249,7 @@  xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge)
     netflow_unref(xbridge->netflow);
     stp_unref(xbridge->stp);
     rstp_unref(xbridge->rstp);
+    xbridge_addr_unref(xbridge->addr);
     hmap_destroy(&xbridge->xports);
     free(xbridge->name);
     free(xbridge);
@@ -3655,6 +3734,54 @@  check_output_prerequisites(struct xlate_ctx *ctx,
     return true;
 }
 
+/* Function verifies if destination address of received Neighbor Advertisement
+ * message stored in 'flow' is correct. It should be either FF02::1:FFXX:XXXX
+ * where XX:XXXX stands for the last 24 bits of 'ipv6_addr' or it should match
+ * 'ipv6_addr'. */
+static bool
+is_nd_dst_correct(const struct flow *flow, const struct in6_addr *ipv6_addr)
+{
+    const uint8_t *flow_ipv6_addr = (uint8_t *) &flow->ipv6_dst;
+    const uint8_t *addr = (uint8_t *) ipv6_addr;
+
+    return (IN6_IS_ADDR_MC_LINKLOCAL(flow_ipv6_addr) &&
+            flow_ipv6_addr[11] == 0x01 &&
+            flow_ipv6_addr[12] == 0xff &&
+            flow_ipv6_addr[13] == addr[13] &&
+            flow_ipv6_addr[14] == addr[14] &&
+            flow_ipv6_addr[15] == addr[15]) ||
+            IN6_ARE_ADDR_EQUAL(&flow->ipv6_dst, ipv6_addr);
+}
+
+/* Function verifies if the ARP reply or Neighbor Advertisement represented by
+ * 'flow' addresses the 'xbridge' of 'ctx'. Returns true if the ARP TA or
+ * neighbor discovery destination is in the list of configured IP addresses of
+ * the bridge. Otherwise, it returns false. */
+static bool
+is_neighbor_reply_correct(const struct xlate_ctx *ctx, const struct flow *flow)
+{
+    bool ret = false;
+    int i;
+    struct xbridge_addr *xbridge_addr = xbridge_addr_ref(ctx->xbridge->addr);
+
+    /* Verify if 'nw_dst' of ARP or 'ipv6_dst' of ICMPV6 is in the list. */
+    for (i = 0; xbridge_addr && i < xbridge_addr->n_addr; i++) {
+        struct in6_addr *ip_addr = &xbridge_addr->addr[i];
+        if ((IN6_IS_ADDR_V4MAPPED(ip_addr) &&
+             flow->dl_type == htons(ETH_TYPE_ARP) &&
+             in6_addr_get_mapped_ipv4(ip_addr) == flow->nw_dst) ||
+            (!IN6_IS_ADDR_V4MAPPED(ip_addr) &&
+              is_nd_dst_correct(flow, ip_addr))) {
+            /* Found a match. */
+            ret = true;
+            break;
+        }
+    }
+
+    xbridge_addr_unref(xbridge_addr);
+    return ret;
+}
+
 static bool
 terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
                         struct flow *flow, struct flow_wildcards *wc,
@@ -3667,6 +3794,15 @@  terminate_native_tunnel(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     if (ofp_port == OFPP_LOCAL &&
         ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
         *tnl_port = tnl_port_map_lookup(flow, wc);
+
+        /* If no tunnel port was found and it's about an ARP or ICMPv6 packet,
+         * do tunnel neighbor snooping. */
+        if (*tnl_port == ODPP_NONE &&
+            (flow->dl_type == htons(ETH_TYPE_ARP) ||
+             flow->nw_proto == IPPROTO_ICMPV6) &&
+             is_neighbor_reply_correct(ctx, flow)) {
+            tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
+        }
     }
 
     return *tnl_port != ODPP_NONE;
@@ -6183,9 +6319,6 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
     struct flow *flow = &ctx->xin->flow;
     const struct ofpact *a;
 
-    if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
-        tnl_neigh_snoop(flow, wc, ctx->xbridge->name);
-    }
     /* dl_type already in the mask, not set below. */
 
     if (!ofpacts_len) {
diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at
index 0b9d436db..b0f4a7e91 100644
--- a/tests/tunnel-push-pop-ipv6.at
+++ b/tests/tunnel-push-pop-ipv6.at
@@ -55,9 +55,73 @@  AT_CHECK([cat p0.pcap.txt | grep 93aa55aa55000086dd6000000000203aff2001cafe | un
 ])
 
 dnl Check ARP Snoop
-AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:c8)'])
 
-AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=2001:cafe::94,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b7)'])
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
+2001:cafe::92                                 f8:bc:12:44:34:c8   br0
+])
+
+dnl Receiving Neighbor Advertisement with incorrect 'nw_dst' should not alter tunnel neighbor cache
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
+2001:cafe::92                                 f8:bc:12:44:34:c8   br0
+])
+
+dnl Receiving Neighbot Advertisement with incorrect VLAN id should not alter tunnel neighbor cache
+AT_CHECK([ovs-vsctl set port br0 tag=10])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
+2001:cafe::92                                 f8:bc:12:44:34:c8   br0
+])
+
+dnl Receiving Neighbor Advertisement with correct VLAN id should alter tunnel neighbor cache
+AT_CHECK([ovs-vsctl set port br0 tag=10])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6))'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
+2001:cafe::92                                 f8:bc:12:44:34:b6   br0
+])
+
+dnl Receiving Neighbor Advertisement in overlay bridge should not alter tunnel neighbor cache
+AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-config:hwaddr=aa:55:aa:55:00:99])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(200),eth(src=f8:bc:12:44:34:c8,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::99,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:c8)'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
+2001:cafe::92                                 f8:bc:12:44:34:b6   br0
+])
+
+dnl Receive Neighbor Advertisement without VLAN header
+AT_CHECK([ovs-vsctl set port br0 tag=0])
+AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
+])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::92,dst=2001:cafe::88,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::92,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b6)'])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'in_port(1),eth(src=f8:bc:12:44:34:b7,dst=aa:55:aa:55:00:00),eth_type(0x86dd),ipv6(src=2001:cafe::93,dst=ff02::1:ff00:0088,label=0,proto=58,tclass=0,hlimit=255,frag=no),icmpv6(type=136,code=0),nd(target=2001:cafe::93,sll=00:00:00:00:00:00,tll=f8:bc:12:44:34:b7)'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
 
 AT_CHECK([ovs-appctl tnl/arp/show | tail -n+3 | sort], [0], [dnl
 2001:cafe::92                                 f8:bc:12:44:34:b6   br0
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index b168f5f2f..4e259730c 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -70,9 +70,71 @@  ffffffffffffaa55aa55000008060001080006040001aa55aa550000010102580000000000000101
 ])
 
 dnl Check ARP Snoop
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
+1.1.2.92                                      f8:bc:12:44:34:c8   br0
+])
+
+dnl Receiving ARP reply with incorrect 'tip' should not alter tunnel neighbor cache
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.90,op=2,sha=f8:bc:12:44:34:b8,tha=00:00:00:00:00:00)'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
+1.1.2.92                                      f8:bc:12:44:34:c8   br0
+])
+
+dnl Receiving ARP reply with incorrect VLAN id should not alter tunnel neighbor cache
+AT_CHECK([ovs-vsctl set port br0 tag=10])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
+1.1.2.92                                      f8:bc:12:44:34:c8   br0
+])
+
+dnl Receiving ARP reply with correct VLAN id should alter tunnel neighbor cache
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=10,pcp=7),encap(eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00))'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br0 | sort], [0], [dnl
+1.1.2.92                                      f8:bc:12:44:34:b6   br0
+])
+
+dnl Receiving ARP reply in overlay bridge should not alter tunnel neighbor cache
+AT_CHECK([ovs-vsctl add-port int-br p1 -- set interface p1 type=dummy ofport_request=200 other-config:hwaddr=aa:55:aa:55:00:99])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'recirc_id(0),in_port(200),eth(src=f8:bc:12:44:34:c8,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:c8,tha=00:00:00:00:00:00)'])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-appctl tnl/neigh/show | grep br | sort], [0], [dnl
+1.1.2.92                                      f8:bc:12:44:34:b6   br0
+])
+
+dnl Receive ARP reply without VLAN header
+AT_CHECK([ovs-vsctl set port br0 tag=0])
+AT_CHECK([ovs-appctl tnl/neigh/flush], [0], [OK
+])
+
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(1),eth(src=f8:bc:12:44:34:b7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=1.1.2.93,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b7,tha=00:00:00:00:00:00)'])
 
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
 AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
 1.1.2.92                                      f8:bc:12:44:34:b6   br0
 1.1.2.93                                      f8:bc:12:44:34:b7   br0
@@ -190,9 +252,12 @@  AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl
 dnl Check GREL3 only accepts non-fragmented packets?
 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
 
+ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
+
 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
   port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
-  port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
+  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?
 ])
 
 dnl Check decapsulation of Geneve packet with options