Merge branch 'mainstream'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Thu, 31 Jan 2013 14:09:26 +0000 (15:09 +0100)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Thu, 31 Jan 2013 14:09:26 +0000 (15:09 +0100)
Conflicts:
lib/dpif-netdev.c

60 files changed:
AUTHORS
FAQ
NEWS
datapath/Modules.mk
datapath/datapath.c
datapath/flow.h
datapath/tunnel.c
datapath/tunnel.h
datapath/vport-capwap.c
datapath/vport-gre.c
datapath/vport-internal_dev.c
datapath/vport-netdev.c
datapath/vport-netdev.h
datapath/vport-patch.c [deleted file]
datapath/vport-vxlan.c
datapath/vport.c
datapath/vport.h
include/linux/openvswitch.h
include/openvswitch/tunnel.h
lib/automake.mk
lib/dpif-linux.c
lib/dpif-linux.h
lib/dpif-netdev.c
lib/dpif.h
lib/match.c
lib/meta-flow.c
lib/netdev-dummy.c
lib/netdev-linux.c
lib/netdev-vport.c
lib/netdev-vport.h
lib/netdev.c
lib/netdev.h
lib/odp-util.c
lib/odp-util.h
lib/ofp-parse.c
lib/packets.c
lib/packets.h
lib/rconn.c
lib/route-table-stub.c
lib/vconn-provider.h
lib/vconn.c
lib/vconn.h
lib/worker.c
ofproto/automake.mk
ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/tunnel.c [new file with mode: 0644]
ofproto/tunnel.h [new file with mode: 0644]
ovsdb/jsonrpc-server.c
rhel/etc_sysconfig_network-scripts_ifdown-ovs
rhel/etc_sysconfig_network-scripts_ifup-ovs
tests/automake.mk
tests/lacp.at
tests/learn.at
tests/ofproto-dpif.at
tests/testsuite.at
tests/tunnel.at [new file with mode: 0644]
utilities/ovs-ctl.8
utilities/ovs-ofctl.c

diff --git a/AUTHORS b/AUTHORS
index 28825d7..2e55522 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -72,6 +72,7 @@ SUGYO Kazushi           sugyo.org@gmail.com
 Tadaaki Nagao           nagao@stratosphere.co.jp
 Tetsuo NAKAGAWA         nakagawa@mxc.nes.nec.co.jp
 Thomas Goirand          zigo@debian.org
+Thomas Graf             tgraf@redhat.com
 Thomas Lacroix          thomas.lacroix@citrix.com
 Todd Deshane            deshantm@gmail.com
 Tom Everman             teverman@google.com
diff --git a/FAQ b/FAQ
index 72a1479..1a2c4f8 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -154,32 +154,37 @@ A: The following table lists the Linux kernel versions against which the
    It should build against almost any kernel, certainly against 2.6.18
    and later.
 
+Q: Should userspace or kernel be upgraded first to minimize downtime?
+
+   In general, the Open vSwitch userspace should be used with the
+   kernel version included in the same release or with the version
+   from upstream Linux.  However, when upgrading between two releases
+   of Open vSwitch it is best to migrate userspace first to reduce
+   the possbility of incompatibilities.
+
 Q: What features are not available in the Open vSwitch kernel datapath
    that ships as part of the upstream Linux kernel?
 
 A: The kernel module in upstream Linux 3.3 and later does not include
-   the following features:
-
-       - Tunnel virtual ports, that is, interfaces with type "gre",
-         "ipsec_gre", "capwap".  It is possible to create tunnels in
-         Linux and attach them to Open vSwitch as system devices.
-         However, they cannot be dynamically created through the OVSDB
-         protocol or set the tunnel ids as a flow action.
-
-         Work is in progress in adding these features to the upstream
-         Linux version of the Open vSwitch kernel module.  For now, if
-         you need these features, use the kernel module from the Open
-         vSwitch distribution instead of the upstream Linux kernel
-         module.
-
-       - Patch virtual ports, that is, interfaces with type "patch".
-         You can use Linux "veth" devices as a substitute.
-
-         We don't have any plans to add patch ports upstream.
+   tunnel virtual ports, that is, interfaces with type "gre",
+   "ipsec_gre", "gre64", "ipsec_gre64", "vxlan", or "capwap".  It is
+   possible to create tunnels in Linux and attach them to Open vSwitch
+   as system devices.  However, they cannot be dynamically created
+   through the OVSDB protocol or set the tunnel ids as a flow action.
+
+   Work is in progress in adding tunnel virtual ports to the upstream
+   Linux version of the Open vSwitch kernel module.  For now, if you
+   need these features, use the kernel module from the Open vSwitch
+   distribution instead of the upstream Linux kernel module.
+
+   The upstream kernel module does not include patch ports, but this
+   only matters for Open vSwitch 1.9 and earlier, because Open vSwitch
+   1.10 and later implement patch ports without using this kernel
+   feature.
 
 Q: What features are not available when using the userspace datapath?
 
-A: Tunnel and patch virtual ports are not supported, as described in the
+A: Tunnel virtual ports are not supported, as described in the
    previous answer.  It is also not possible to use queue-related
    actions.  On Linux kernels before 2.6.39, maximum-sized VLAN packets
    may not be transmitted.
diff --git a/NEWS b/NEWS
index 6cf09ba..1164962 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,9 @@ post-v1.9.0
         retire that meaning of ANY in favor of the OpenFlow 1.1 meaning.
     - Inheritance of the Don't Fragment bit in IP tunnels (df_inherit) is
       no longer supported.
+    - Patch ports are implemented in userspace.
+    - Tunneling requires the version of the kernel module paired with Open
+      vSwitch 1.9.0 or later.
 
 
 v1.9.0 - xx xxx xxxx
index 281408b..54c6f13 100644 (file)
@@ -20,7 +20,6 @@ openvswitch_sources = \
        vport-gre.c \
        vport-internal_dev.c \
        vport-netdev.c \
-       vport-patch.c \
        vport-vxlan.c
 
 openvswitch_headers = \
index b731c20..04a5e7f 100644 (file)
@@ -1787,10 +1787,8 @@ static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
 #ifdef HAVE_NLA_NUL_STRING
        [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
        [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
-       [OVS_VPORT_ATTR_ADDRESS] = { .len = ETH_ALEN },
 #else
        [OVS_VPORT_ATTR_STATS] = { .minlen = sizeof(struct ovs_vport_stats) },
-       [OVS_VPORT_ATTR_ADDRESS] = { .minlen = ETH_ALEN },
 #endif
        [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
        [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
@@ -1837,10 +1835,6 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
                    &vport_stats))
                goto nla_put_failure;
 
-       if (nla_put(skb, OVS_VPORT_ATTR_ADDRESS, ETH_ALEN,
-                   vport->ops->get_addr(vport)))
-               goto nla_put_failure;
-
        err = ovs_vport_get_options(vport, skb);
        if (err == -EMSGSIZE)
                goto error;
@@ -1912,21 +1906,6 @@ static struct vport *lookup_vport(struct net *net,
                return ERR_PTR(-EINVAL);
 }
 
-/* Called with RTNL lock. */
-static int change_vport(struct vport *vport,
-                       struct nlattr *a[OVS_VPORT_ATTR_MAX + 1])
-{
-       int err = 0;
-
-       if (a[OVS_VPORT_ATTR_STATS])
-               ovs_vport_set_stats(vport, nla_data(a[OVS_VPORT_ATTR_STATS]));
-
-       if (a[OVS_VPORT_ATTR_ADDRESS])
-               err = ovs_vport_set_addr(vport, nla_data(a[OVS_VPORT_ATTR_ADDRESS]));
-
-       return err;
-}
-
 static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr **a = info->attrs;
@@ -1988,15 +1967,13 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(vport))
                goto exit_unlock;
 
-       err = change_vport(vport, a);
-       if (!err) {
-               reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
-                                                info->snd_seq,
-                                                OVS_VPORT_CMD_NEW);
-               if (IS_ERR(reply))
-                       err = PTR_ERR(reply);
-       }
-       if (err) {
+       if (a[OVS_VPORT_ATTR_STATS])
+               ovs_vport_set_stats(vport, nla_data(a[OVS_VPORT_ATTR_STATS]));
+
+       reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq,
+                                        OVS_VPORT_CMD_NEW);
+       if (IS_ERR(reply)) {
+               err = PTR_ERR(reply);
                ovs_dp_detach_port(vport);
                goto exit_unlock;
        }
@@ -2033,11 +2010,13 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
 
        if (!err && a[OVS_VPORT_ATTR_OPTIONS])
                err = ovs_vport_set_options(vport, a[OVS_VPORT_ATTR_OPTIONS]);
-       if (!err)
-               err = change_vport(vport, a);
-       else
+       if (err)
                goto exit_unlock;
-       if (!err && a[OVS_VPORT_ATTR_UPCALL_PID])
+
+       if (a[OVS_VPORT_ATTR_STATS])
+               ovs_vport_set_stats(vport, nla_data(a[OVS_VPORT_ATTR_STATS]));
+
+       if (a[OVS_VPORT_ATTR_UPCALL_PID])
                vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
 
        reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
index dab6980..6949640 100644 (file)
@@ -45,6 +45,11 @@ struct sw_flow_actions {
 #define OVS_TNL_F_CSUM                 (1 << 1)
 #define OVS_TNL_F_KEY                  (1 << 2)
 
+/* Used to memset ovs_key_ipv4_tunnel padding. */
+#define OVS_TUNNEL_KEY_SIZE                                    \
+        (offsetof(struct ovs_key_ipv4_tunnel, ipv4_ttl) +      \
+         FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, ipv4_ttl))
+
 struct ovs_key_ipv4_tunnel {
        __be64 tun_id;
        __be32 ipv4_src;
index d03b708..6193891 100644 (file)
@@ -358,349 +358,6 @@ void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb)
        ovs_vport_receive(vport, skb);
 }
 
-static bool check_ipv4_address(__be32 addr)
-{
-       if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr)
-           || ipv4_is_loopback(addr) || ipv4_is_zeronet(addr))
-               return false;
-
-       return true;
-}
-
-static bool ipv4_should_icmp(struct sk_buff *skb)
-{
-       struct iphdr *old_iph = ip_hdr(skb);
-
-       /* Don't respond to L2 broadcast. */
-       if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
-               return false;
-
-       /* Don't respond to L3 broadcast or invalid addresses. */
-       if (!check_ipv4_address(old_iph->daddr) ||
-           !check_ipv4_address(old_iph->saddr))
-               return false;
-
-       /* Only respond to the first fragment. */
-       if (old_iph->frag_off & htons(IP_OFFSET))
-               return false;
-
-       /* Don't respond to ICMP error messages. */
-       if (old_iph->protocol == IPPROTO_ICMP) {
-               u8 icmp_type, *icmp_typep;
-
-               icmp_typep = skb_header_pointer(skb, (u8 *)old_iph +
-                                               (old_iph->ihl << 2) +
-                                               offsetof(struct icmphdr, type) -
-                                               skb->data, sizeof(icmp_type),
-                                               &icmp_type);
-
-               if (!icmp_typep)
-                       return false;
-
-               if (*icmp_typep > NR_ICMP_TYPES
-                       || (*icmp_typep <= ICMP_PARAMETERPROB
-                               && *icmp_typep != ICMP_ECHOREPLY
-                               && *icmp_typep != ICMP_ECHO))
-                       return false;
-       }
-
-       return true;
-}
-
-static void ipv4_build_icmp(struct sk_buff *skb, struct sk_buff *nskb,
-                           unsigned int mtu, unsigned int payload_length)
-{
-       struct iphdr *iph, *old_iph = ip_hdr(skb);
-       struct icmphdr *icmph;
-       u8 *payload;
-
-       iph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
-       icmph = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));
-       payload = skb_put(nskb, payload_length);
-
-       /* IP */
-       iph->version            =       4;
-       iph->ihl                =       sizeof(struct iphdr) >> 2;
-       iph->tos                =       (old_iph->tos & IPTOS_TOS_MASK) |
-                                       IPTOS_PREC_INTERNETCONTROL;
-       iph->tot_len            =       htons(sizeof(struct iphdr)
-                                             + sizeof(struct icmphdr)
-                                             + payload_length);
-       get_random_bytes(&iph->id, sizeof(iph->id));
-       iph->frag_off           =       0;
-       iph->ttl                =       IPDEFTTL;
-       iph->protocol           =       IPPROTO_ICMP;
-       iph->daddr              =       old_iph->saddr;
-       iph->saddr              =       old_iph->daddr;
-
-       ip_send_check(iph);
-
-       /* ICMP */
-       icmph->type             =       ICMP_DEST_UNREACH;
-       icmph->code             =       ICMP_FRAG_NEEDED;
-       icmph->un.gateway       =       htonl(mtu);
-       icmph->checksum         =       0;
-
-       nskb->csum = csum_partial((u8 *)icmph, sizeof(struct icmphdr), 0);
-       nskb->csum = skb_copy_and_csum_bits(skb, (u8 *)old_iph - skb->data,
-                                           payload, payload_length,
-                                           nskb->csum);
-       icmph->checksum = csum_fold(nskb->csum);
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static bool ipv6_should_icmp(struct sk_buff *skb)
-{
-       struct ipv6hdr *old_ipv6h = ipv6_hdr(skb);
-       int addr_type;
-       int payload_off = (u8 *)(old_ipv6h + 1) - skb->data;
-       u8 nexthdr = ipv6_hdr(skb)->nexthdr;
-       __be16 frag_off;
-
-       /* Check source address is valid. */
-       addr_type = ipv6_addr_type(&old_ipv6h->saddr);
-       if (addr_type & IPV6_ADDR_MULTICAST || addr_type == IPV6_ADDR_ANY)
-               return false;
-
-       /* Don't reply to unspecified addresses. */
-       if (ipv6_addr_type(&old_ipv6h->daddr) == IPV6_ADDR_ANY)
-               return false;
-
-       /* Don't respond to ICMP error messages. */
-       payload_off = ipv6_skip_exthdr(skb, payload_off, &nexthdr, &frag_off);
-       if (payload_off < 0)
-               return false;
-
-       if (nexthdr == NEXTHDR_ICMP) {
-               u8 icmp_type, *icmp_typep;
-
-               icmp_typep = skb_header_pointer(skb, payload_off +
-                                               offsetof(struct icmp6hdr,
-                                                       icmp6_type),
-                                               sizeof(icmp_type), &icmp_type);
-
-               if (!icmp_typep || !(*icmp_typep & ICMPV6_INFOMSG_MASK))
-                       return false;
-       }
-
-       return true;
-}
-
-static void ipv6_build_icmp(struct sk_buff *skb, struct sk_buff *nskb,
-                           unsigned int mtu, unsigned int payload_length)
-{
-       struct ipv6hdr *ipv6h, *old_ipv6h = ipv6_hdr(skb);
-       struct icmp6hdr *icmp6h;
-       u8 *payload;
-
-       ipv6h = (struct ipv6hdr *)skb_put(nskb, sizeof(struct ipv6hdr));
-       icmp6h = (struct icmp6hdr *)skb_put(nskb, sizeof(struct icmp6hdr));
-       payload = skb_put(nskb, payload_length);
-
-       /* IPv6 */
-       ipv6h->version          =       6;
-       ipv6h->priority         =       0;
-       memset(&ipv6h->flow_lbl, 0, sizeof(ipv6h->flow_lbl));
-       ipv6h->payload_len      =       htons(sizeof(struct icmp6hdr)
-                                             + payload_length);
-       ipv6h->nexthdr          =       NEXTHDR_ICMP;
-       ipv6h->hop_limit        =       IPV6_DEFAULT_HOPLIMIT;
-       ipv6h->daddr            =       old_ipv6h->saddr;
-       ipv6h->saddr            =       old_ipv6h->daddr;
-
-       /* ICMPv6 */
-       icmp6h->icmp6_type      =       ICMPV6_PKT_TOOBIG;
-       icmp6h->icmp6_code      =       0;
-       icmp6h->icmp6_cksum     =       0;
-       icmp6h->icmp6_mtu       =       htonl(mtu);
-
-       nskb->csum = csum_partial((u8 *)icmp6h, sizeof(struct icmp6hdr), 0);
-       nskb->csum = skb_copy_and_csum_bits(skb, (u8 *)old_ipv6h - skb->data,
-                                           payload, payload_length,
-                                           nskb->csum);
-       icmp6h->icmp6_cksum = csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
-                                               sizeof(struct icmp6hdr)
-                                               + payload_length,
-                                               ipv6h->nexthdr, nskb->csum);
-}
-#endif /* IPv6 */
-
-bool ovs_tnl_frag_needed(struct vport *vport,
-                        const struct tnl_mutable_config *mutable,
-                        struct sk_buff *skb, unsigned int mtu)
-{
-       unsigned int eth_hdr_len = ETH_HLEN;
-       unsigned int total_length = 0, header_length = 0, payload_length;
-       struct ethhdr *eh, *old_eh = eth_hdr(skb);
-       struct sk_buff *nskb;
-
-       /* Sanity check */
-       if (skb->protocol == htons(ETH_P_IP)) {
-               if (mtu < IP_MIN_MTU)
-                       return false;
-
-               if (!ipv4_should_icmp(skb))
-                       return true;
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (skb->protocol == htons(ETH_P_IPV6)) {
-               if (mtu < IPV6_MIN_MTU)
-                       return false;
-
-               /*
-                * In theory we should do PMTUD on IPv6 multicast messages but
-                * we don't have an address to send from so just fragment.
-                */
-               if (ipv6_addr_type(&ipv6_hdr(skb)->daddr) & IPV6_ADDR_MULTICAST)
-                       return false;
-
-               if (!ipv6_should_icmp(skb))
-                       return true;
-       }
-#endif
-       else
-               return false;
-
-       /* Allocate */
-       if (old_eh->h_proto == htons(ETH_P_8021Q))
-               eth_hdr_len = VLAN_ETH_HLEN;
-
-       payload_length = skb->len - eth_hdr_len;
-       if (skb->protocol == htons(ETH_P_IP)) {
-               header_length = sizeof(struct iphdr) + sizeof(struct icmphdr);
-               total_length = min_t(unsigned int, header_length +
-                                                  payload_length, 576);
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else {
-               header_length = sizeof(struct ipv6hdr) +
-                               sizeof(struct icmp6hdr);
-               total_length = min_t(unsigned int, header_length +
-                                                 payload_length, IPV6_MIN_MTU);
-       }
-#endif
-
-       payload_length = total_length - header_length;
-
-       nskb = dev_alloc_skb(NET_IP_ALIGN + eth_hdr_len + header_length +
-                            payload_length);
-       if (!nskb)
-               return false;
-
-       skb_reserve(nskb, NET_IP_ALIGN);
-
-       /* Ethernet / VLAN */
-       eh = (struct ethhdr *)skb_put(nskb, eth_hdr_len);
-       memcpy(eh->h_dest, old_eh->h_source, ETH_ALEN);
-       memcpy(eh->h_source, mutable->eth_addr, ETH_ALEN);
-       nskb->protocol = eh->h_proto = old_eh->h_proto;
-       if (old_eh->h_proto == htons(ETH_P_8021Q)) {
-               struct vlan_ethhdr *vh = (struct vlan_ethhdr *)eh;
-
-               vh->h_vlan_TCI = vlan_eth_hdr(skb)->h_vlan_TCI;
-               vh->h_vlan_encapsulated_proto = skb->protocol;
-       } else
-               vlan_set_tci(nskb, vlan_get_tci(skb));
-       skb_reset_mac_header(nskb);
-
-       /* Protocol */
-       if (skb->protocol == htons(ETH_P_IP))
-               ipv4_build_icmp(skb, nskb, mtu, payload_length);
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else
-               ipv6_build_icmp(skb, nskb, mtu, payload_length);
-#endif
-
-       if (unlikely(compute_ip_summed(nskb, false))) {
-               kfree_skb(nskb);
-               return false;
-       }
-
-       ovs_vport_receive(vport, nskb);
-
-       return true;
-}
-
-static bool check_mtu(struct sk_buff *skb,
-                     struct vport *vport,
-                     const struct tnl_mutable_config *mutable,
-                     const struct rtable *rt, __be16 *frag_offp,
-                     int tunnel_hlen)
-{
-       bool df_inherit;
-       bool pmtud;
-       __be16 frag_off;
-       int mtu = 0;
-       unsigned int packet_length = skb->len - ETH_HLEN;
-
-       if (OVS_CB(skb)->tun_key->ipv4_dst) {
-               df_inherit = false;
-               pmtud = false;
-               frag_off = OVS_CB(skb)->tun_key->tun_flags & OVS_TNL_F_DONT_FRAGMENT ?
-                                 htons(IP_DF) : 0;
-       } else {
-               df_inherit = mutable->flags & TNL_F_DF_INHERIT;
-               pmtud = mutable->flags & TNL_F_PMTUD;
-               frag_off = mutable->flags & TNL_F_DF_DEFAULT ? htons(IP_DF) : 0;
-       }
-
-       /* Allow for one level of tagging in the packet length. */
-       if (!vlan_tx_tag_present(skb) &&
-           eth_hdr(skb)->h_proto == htons(ETH_P_8021Q))
-               packet_length -= VLAN_HLEN;
-
-       if (pmtud) {
-               int vlan_header = 0;
-
-               /* The tag needs to go in packet regardless of where it
-                * currently is, so subtract it from the MTU.
-                */
-               if (vlan_tx_tag_present(skb) ||
-                   eth_hdr(skb)->h_proto == htons(ETH_P_8021Q))
-                       vlan_header = VLAN_HLEN;
-
-               mtu = dst_mtu(&rt_dst(rt))
-                       - ETH_HLEN
-                       - tunnel_hlen
-                       - vlan_header;
-       }
-
-       if (skb->protocol == htons(ETH_P_IP)) {
-               struct iphdr *iph = ip_hdr(skb);
-
-               if (df_inherit)
-                       frag_off = iph->frag_off & htons(IP_DF);
-
-               if (pmtud && iph->frag_off & htons(IP_DF)) {
-                       mtu = max(mtu, IP_MIN_MTU);
-
-                       if (packet_length > mtu &&
-                           ovs_tnl_frag_needed(vport, mutable, skb, mtu))
-                               return false;
-               }
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (skb->protocol == htons(ETH_P_IPV6)) {
-               /* IPv6 requires end hosts to do fragmentation
-                * if the packet is above the minimum MTU.
-                */
-               if (df_inherit && packet_length > IPV6_MIN_MTU)
-                       frag_off = htons(IP_DF);
-
-               if (pmtud) {
-                       mtu = max(mtu, IPV6_MIN_MTU);
-
-                       if (packet_length > mtu &&
-                           ovs_tnl_frag_needed(vport, mutable, skb, mtu))
-                               return false;
-               }
-       }
-#endif
-
-       *frag_offp = frag_off;
-       return true;
-}
-
 static struct rtable *find_route(struct net *net,
                __be32 *saddr, __be32 daddr, u8 ipproto,
                u8 tos)
@@ -856,7 +513,7 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
        struct ovs_key_ipv4_tunnel tun_key;
        int sent_len = 0;
        int tunnel_hlen;
-       __be16 frag_off = 0;
+       __be16 frag_off;
        __be32 daddr;
        __be32 saddr;
        u8 ttl;
@@ -905,6 +562,8 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
                saddr = OVS_CB(skb)->tun_key->ipv4_src;
                tos = OVS_CB(skb)->tun_key->ipv4_tos;
                ttl = OVS_CB(skb)->tun_key->ipv4_ttl;
+                frag_off = OVS_CB(skb)->tun_key->tun_flags &
+                               OVS_TNL_F_DONT_FRAGMENT ?  htons(IP_DF) : 0;
        } else {
                u8 inner_tos;
                daddr = mutable->key.daddr;
@@ -945,6 +604,7 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
 #endif
                }
 
+               frag_off = mutable->flags & TNL_F_DF_DEFAULT ? htons(IP_DF) : 0;
        }
 
        /* Route lookup */
@@ -966,12 +626,6 @@ int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
                goto err_free_rt;
        }
 
-       /* MTU */
-       if (unlikely(!check_mtu(skb, vport, mutable, rt, &frag_off, tunnel_hlen))) {
-               err = VPORT_E_TX_DROPPED;
-               goto err_free_rt;
-       }
-
        /* TTL Fixup. */
        if (!OVS_CB(skb)->tun_key->ipv4_dst) {
                if (!(mutable->flags & TNL_F_TTL_INHERIT)) {
@@ -1165,8 +819,6 @@ struct vport *ovs_tnl_create(const struct vport_parms *parms,
                goto error_free_vport;
        }
 
-       random_ether_addr(mutable->eth_addr);
-
        get_random_bytes(&initial_frag_id, sizeof(int));
        atomic_set(&tnl_vport->frag_id, initial_frag_id);
 
@@ -1206,10 +858,6 @@ int ovs_tnl_set_options(struct vport *vport, struct nlattr *options)
                goto error;
        }
 
-       /* Copy fields whose values should be retained. */
-       mutable->seq = old_mutable->seq + 1;
-       memcpy(mutable->eth_addr, old_mutable->eth_addr, ETH_ALEN);
-
        /* Parse the others configured by userspace. */
        err = tnl_set_config(ovs_dp_get_net(vport->dp), options, tnl_vport->tnl_ops,
                             vport, mutable);
@@ -1288,36 +936,12 @@ void ovs_tnl_destroy(struct vport *vport)
        call_rcu(&tnl_vport->rcu, free_port_rcu);
 }
 
-int ovs_tnl_set_addr(struct vport *vport, const unsigned char *addr)
-{
-       struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       struct tnl_mutable_config *old_mutable, *mutable;
-
-       old_mutable = rtnl_dereference(tnl_vport->mutable);
-       mutable = kmemdup(old_mutable, sizeof(struct tnl_mutable_config), GFP_KERNEL);
-       if (!mutable)
-               return -ENOMEM;
-
-       old_mutable->mlink = 0;
-
-       memcpy(mutable->eth_addr, addr, ETH_ALEN);
-       assign_config_rcu(vport, mutable);
-
-       return 0;
-}
-
 const char *ovs_tnl_get_name(const struct vport *vport)
 {
        const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
        return tnl_vport->name;
 }
 
-const unsigned char *ovs_tnl_get_addr(const struct vport *vport)
-{
-       const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-       return rcu_dereference_rtnl(tnl_vport->mutable)->eth_addr;
-}
-
 void ovs_tnl_free_linked_skbs(struct sk_buff *skb)
 {
        while (skb) {
index b7de7a9..7e4d1a6 100644 (file)
@@ -56,8 +56,7 @@
 
 /* All public tunnel flags. */
 #define TNL_F_PUBLIC (TNL_F_CSUM | TNL_F_TOS_INHERIT | TNL_F_TTL_INHERIT | \
-                     TNL_F_DF_INHERIT | TNL_F_DF_DEFAULT | TNL_F_PMTUD | \
-                     TNL_F_IPSEC)
+                     TNL_F_DF_DEFAULT | TNL_F_IPSEC)
 
 /**
  * struct port_lookup_key - Tunnel port key, used as hash table key.
@@ -95,10 +94,7 @@ static inline void port_key_set_net(struct port_lookup_key *key, struct net *net
  * @key: Used as key for tunnel port.  Configured via OVS_TUNNEL_ATTR_*
  * attributes.
  * @rcu: RCU callback head for deferred destruction.
- * @seq: Sequence number for distinguishing configuration versions.
  * @tunnel_hlen: Tunnel header length.
- * @eth_addr: Source address for packets generated by tunnel itself
- * (e.g. ICMP fragmentation needed messages).
  * @out_key: Key to use on output, 0 if this tunnel has no fixed output key.
  * @flags: TNL_F_* flags.
  * @tos: IPv4 TOS value to use for tunnel, 0 if no fixed TOS.
@@ -108,10 +104,6 @@ struct tnl_mutable_config {
        struct port_lookup_key key;
        struct rcu_head rcu;
 
-       unsigned seq;
-
-       unsigned char eth_addr[ETH_ALEN];
-
        /* Configured via OVS_TUNNEL_ATTR_* attributes. */
        __be64  out_key;
        u32     flags;
@@ -171,9 +163,7 @@ void ovs_tnl_destroy(struct vport *);
 int ovs_tnl_set_options(struct vport *, struct nlattr *);
 int ovs_tnl_get_options(const struct vport *, struct sk_buff *);
 
-int ovs_tnl_set_addr(struct vport *vport, const unsigned char *addr);
 const char *ovs_tnl_get_name(const struct vport *vport);
-const unsigned char *ovs_tnl_get_addr(const struct vport *vport);
 int ovs_tnl_send(struct vport *vport, struct sk_buff *skb);
 void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb);
 
@@ -201,6 +191,10 @@ static inline void tnl_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key,
        tun_key->ipv4_tos = iph->tos;
        tun_key->ipv4_ttl = iph->ttl;
        tun_key->tun_flags = tun_flags;
+
+       /* clear struct padding. */
+       memset((unsigned char*) tun_key + OVS_TUNNEL_KEY_SIZE, 0,
+              sizeof(*tun_key) - OVS_TUNNEL_KEY_SIZE);
 }
 
 static inline void tnl_get_param(const struct tnl_mutable_config *mutable,
index f45d349..56e6394 100644 (file)
@@ -841,9 +841,7 @@ const struct vport_ops ovs_capwap_vport_ops = {
        .exit           = capwap_exit,
        .create         = capwap_create,
        .destroy        = capwap_destroy,
-       .set_addr       = ovs_tnl_set_addr,
        .get_name       = ovs_tnl_get_name,
-       .get_addr       = ovs_tnl_get_addr,
        .get_options    = ovs_tnl_get_options,
        .set_options    = ovs_tnl_set_options,
        .send           = ovs_tnl_send,
index 8ce8a35..14c5ba3 100644 (file)
@@ -201,130 +201,6 @@ static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *tun_id,
        return hdr_len;
 }
 
-/* Called with rcu_read_lock and BH disabled. */
-static void gre_err(struct sk_buff *skb, u32 info)
-{
-       struct vport *vport;
-       const struct tnl_mutable_config *mutable;
-       const int type = icmp_hdr(skb)->type;
-       const int code = icmp_hdr(skb)->code;
-       int mtu = ntohs(icmp_hdr(skb)->un.frag.mtu);
-       u32 tunnel_type;
-
-       struct iphdr *iph;
-       __be16 flags;
-       __be64 key;
-       int tunnel_hdr_len, tot_hdr_len;
-       unsigned int orig_mac_header;
-       unsigned int orig_nw_header;
-
-       if (type != ICMP_DEST_UNREACH || code != ICMP_FRAG_NEEDED)
-               return;
-
-       /*
-        * The mimimum size packet that we would actually be able to process:
-        * encapsulating IP header, minimum GRE header, Ethernet header,
-        * inner IPv4 header.
-        */
-       if (!pskb_may_pull(skb, sizeof(struct iphdr) + GRE_HEADER_SECTION +
-                               ETH_HLEN + sizeof(struct iphdr)))
-               return;
-
-       iph = (struct iphdr *)skb->data;
-       if (ipv4_is_multicast(iph->daddr))
-               return;
-
-       tunnel_hdr_len = parse_header(iph, &flags, &key, &tunnel_type);
-       if (tunnel_hdr_len < 0)
-               return;
-
-       vport = ovs_tnl_find_port(dev_net(skb->dev), iph->saddr, iph->daddr, key,
-                                 tunnel_type, &mutable);
-       if (!vport)
-               return;
-
-       /*
-        * Packets received by this function were previously sent by us, so
-        * any comparisons should be to the output values, not the input.
-        * However, it's not really worth it to have a hash table based on
-        * output keys (especially since ICMP error handling of tunneled packets
-        * isn't that reliable anyways).  Therefore, we do a lookup based on the
-        * out key as if it were the in key and then check to see if the input
-        * and output keys are the same.
-        */
-       if (mutable->key.in_key != mutable->out_key)
-               return;
-
-       if (!!(mutable->flags & TNL_F_IN_KEY_MATCH) !=
-           !!(mutable->flags & TNL_F_OUT_KEY_ACTION))
-               return;
-
-       if ((mutable->flags & TNL_F_CSUM) && !(flags & GRE_CSUM))
-               return;
-
-       tunnel_hdr_len += iph->ihl << 2;
-
-       orig_mac_header = skb_mac_header(skb) - skb->data;
-       orig_nw_header = skb_network_header(skb) - skb->data;
-       skb_set_mac_header(skb, tunnel_hdr_len);
-
-       tot_hdr_len = tunnel_hdr_len + ETH_HLEN;
-
-       skb->protocol = eth_hdr(skb)->h_proto;
-       if (skb->protocol == htons(ETH_P_8021Q)) {
-               tot_hdr_len += VLAN_HLEN;
-               skb->protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
-       }
-
-       skb_set_network_header(skb, tot_hdr_len);
-       mtu -= tot_hdr_len;
-
-       if (skb->protocol == htons(ETH_P_IP))
-               tot_hdr_len += sizeof(struct iphdr);
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (skb->protocol == htons(ETH_P_IPV6))
-               tot_hdr_len += sizeof(struct ipv6hdr);
-#endif
-       else
-               goto out;
-
-       if (!pskb_may_pull(skb, tot_hdr_len))
-               goto out;
-
-       if (skb->protocol == htons(ETH_P_IP)) {
-               if (mtu < IP_MIN_MTU) {
-                       if (ntohs(ip_hdr(skb)->tot_len) >= IP_MIN_MTU)
-                               mtu = IP_MIN_MTU;
-                       else
-                               goto out;
-               }
-
-       }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (skb->protocol == htons(ETH_P_IPV6)) {
-               if (mtu < IPV6_MIN_MTU) {
-                       unsigned int packet_length = sizeof(struct ipv6hdr) +
-                                             ntohs(ipv6_hdr(skb)->payload_len);
-
-                       if (packet_length >= IPV6_MIN_MTU
-                           || ntohs(ipv6_hdr(skb)->payload_len) == 0)
-                               mtu = IPV6_MIN_MTU;
-                       else
-                               goto out;
-               }
-       }
-#endif
-
-       __skb_pull(skb, tunnel_hdr_len);
-       ovs_tnl_frag_needed(vport, mutable, skb, mtu);
-       __skb_push(skb, tunnel_hdr_len);
-
-out:
-       skb_set_mac_header(skb, orig_mac_header);
-       skb_set_network_header(skb, orig_nw_header);
-       skb->protocol = htons(ETH_P_IP);
-}
-
 static bool check_checksum(struct sk_buff *skb)
 {
        struct iphdr *iph = ip_hdr(skb);
@@ -430,11 +306,6 @@ static struct vport *gre_create(const struct vport_parms *parms)
        return ovs_tnl_create(parms, &ovs_gre_vport_ops, &gre_tnl_ops);
 }
 
-static struct vport *gre_create_ft(const struct vport_parms *parms)
-{
-       return ovs_tnl_create(parms, &ovs_gre_ft_vport_ops, &gre_tnl_ops);
-}
-
 static const struct tnl_ops gre64_tnl_ops = {
        .tunnel_type    = TNL_T_PROTO_GRE64,
        .ipproto        = IPPROTO_GRE,
@@ -449,7 +320,6 @@ static struct vport *gre_create64(const struct vport_parms *parms)
 
 static const struct net_protocol gre_protocol_handlers = {
        .handler        =       gre_rcv,
-       .err_handler    =       gre_err,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
        .netns_ok       =       1,
 #endif
@@ -482,21 +352,6 @@ static void gre_exit(void)
        inet_del_protocol(&gre_protocol_handlers, IPPROTO_GRE);
 }
 
-const struct vport_ops ovs_gre_ft_vport_ops = {
-       .type           = OVS_VPORT_TYPE_FT_GRE,
-       .flags          = VPORT_F_TUN_ID,
-       .init           = gre_init,
-       .exit           = gre_exit,
-       .create         = gre_create_ft,
-       .destroy        = ovs_tnl_destroy,
-       .set_addr       = ovs_tnl_set_addr,
-       .get_name       = ovs_tnl_get_name,
-       .get_addr       = ovs_tnl_get_addr,
-       .get_options    = ovs_tnl_get_options,
-       .set_options    = ovs_tnl_set_options,
-       .send           = ovs_tnl_send,
-};
-
 const struct vport_ops ovs_gre_vport_ops = {
        .type           = OVS_VPORT_TYPE_GRE,
        .flags          = VPORT_F_TUN_ID,
@@ -504,9 +359,7 @@ const struct vport_ops ovs_gre_vport_ops = {
        .exit           = gre_exit,
        .create         = gre_create,
        .destroy        = ovs_tnl_destroy,
-       .set_addr       = ovs_tnl_set_addr,
        .get_name       = ovs_tnl_get_name,
-       .get_addr       = ovs_tnl_get_addr,
        .get_options    = ovs_tnl_get_options,
        .set_options    = ovs_tnl_set_options,
        .send           = ovs_tnl_send,
@@ -519,9 +372,7 @@ const struct vport_ops ovs_gre64_vport_ops = {
        .exit           = gre_exit,
        .create         = gre_create64,
        .destroy        = ovs_tnl_destroy,
-       .set_addr       = ovs_tnl_set_addr,
        .get_name       = ovs_tnl_get_name,
-       .get_addr       = ovs_tnl_get_addr,
        .get_options    = ovs_tnl_get_options,
        .set_options    = ovs_tnl_set_options,
        .send           = ovs_tnl_send,
index 9e2e788..a5f2d75 100644 (file)
@@ -292,9 +292,7 @@ const struct vport_ops ovs_internal_vport_ops = {
        .flags          = VPORT_F_REQUIRED | VPORT_F_FLOW,
        .create         = internal_dev_create,
        .destroy        = internal_dev_destroy,
-       .set_addr       = ovs_netdev_set_addr,
        .get_name       = ovs_netdev_get_name,
-       .get_addr       = ovs_netdev_get_addr,
        .get_ifindex    = ovs_netdev_get_ifindex,
        .send           = internal_dev_recv,
 };
index fb64fe0..727194e 100644 (file)
@@ -193,29 +193,12 @@ static void netdev_destroy(struct vport *vport)
        call_rcu(&netdev_vport->rcu, free_port_rcu);
 }
 
-int ovs_netdev_set_addr(struct vport *vport, const unsigned char *addr)
-{
-       struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
-       struct sockaddr sa;
-
-       sa.sa_family = ARPHRD_ETHER;
-       memcpy(sa.sa_data, addr, ETH_ALEN);
-
-       return dev_set_mac_address(netdev_vport->dev, &sa);
-}
-
 const char *ovs_netdev_get_name(const struct vport *vport)
 {
        const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
        return netdev_vport->dev->name;
 }
 
-const unsigned char *ovs_netdev_get_addr(const struct vport *vport)
-{
-       const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
-       return netdev_vport->dev->dev_addr;
-}
-
 int ovs_netdev_get_ifindex(const struct vport *vport)
 {
        const struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
@@ -383,9 +366,7 @@ const struct vport_ops ovs_netdev_vport_ops = {
        .exit           = netdev_exit,
        .create         = netdev_create,
        .destroy        = netdev_destroy,
-       .set_addr       = ovs_netdev_set_addr,
        .get_name       = ovs_netdev_get_name,
-       .get_addr       = ovs_netdev_get_addr,
        .get_ifindex    = ovs_netdev_get_ifindex,
        .send           = netdev_send,
 };
index a387b8c..6478079 100644 (file)
@@ -38,9 +38,7 @@ netdev_vport_priv(const struct vport *vport)
        return vport_priv(vport);
 }
 
-int ovs_netdev_set_addr(struct vport *, const unsigned char *addr);
 const char *ovs_netdev_get_name(const struct vport *);
-const unsigned char *ovs_netdev_get_addr(const struct vport *);
 const char *ovs_netdev_get_config(const struct vport *);
 int ovs_netdev_get_ifindex(const struct vport *);
 
diff --git a/datapath/vport-patch.c b/datapath/vport-patch.c
deleted file mode 100644 (file)
index 501eb7a..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (c) 2007-2012 Nicira, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#include <linux/kernel.h>
-#include <linux/jhash.h>
-#include <linux/list.h>
-#include <linux/rtnetlink.h>
-#include <net/net_namespace.h>
-
-#include "compat.h"
-#include "datapath.h"
-#include "vport.h"
-
-struct patch_config {
-       struct rcu_head rcu;
-
-       char peer_name[IFNAMSIZ];
-       unsigned char eth_addr[ETH_ALEN];
-};
-
-struct patch_vport {
-       struct rcu_head rcu;
-
-       char name[IFNAMSIZ];
-
-       /* Protected by RTNL lock. */
-       struct hlist_node hash_node;
-
-       struct vport __rcu *peer;
-       struct patch_config __rcu *patchconf;
-};
-
-/* Protected by RTNL lock. */
-static struct hlist_head *peer_table;
-#define PEER_HASH_BUCKETS 256
-
-static void update_peers(struct net *, const char *name, struct vport *);
-
-static struct patch_vport *patch_vport_priv(const struct vport *vport)
-{
-       return vport_priv(vport);
-}
-
-/* RCU callback. */
-static void free_config(struct rcu_head *rcu)
-{
-       struct patch_config *c = container_of(rcu, struct patch_config, rcu);
-       kfree(c);
-}
-
-static void assign_config_rcu(struct vport *vport,
-                             struct patch_config *new_config)
-{
-       struct patch_vport *patch_vport = patch_vport_priv(vport);
-       struct patch_config *old_config;
-
-       old_config = rtnl_dereference(patch_vport->patchconf);
-       rcu_assign_pointer(patch_vport->patchconf, new_config);
-       call_rcu(&old_config->rcu, free_config);
-}
-
-static struct hlist_head *hash_bucket(struct net *net, const char *name)
-{
-       unsigned int hash = jhash(name, strlen(name), (unsigned long) net);
-       return &peer_table[hash & (PEER_HASH_BUCKETS - 1)];
-}
-
-static int patch_init(void)
-{
-       peer_table = kzalloc(PEER_HASH_BUCKETS * sizeof(struct hlist_head),
-                           GFP_KERNEL);
-       if (!peer_table)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static void patch_exit(void)
-{
-       kfree(peer_table);
-}
-
-static const struct nla_policy patch_policy[OVS_PATCH_ATTR_MAX + 1] = {
-#ifdef HAVE_NLA_NUL_STRING
-       [OVS_PATCH_ATTR_PEER] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-#endif
-};
-
-static int patch_set_config(struct vport *vport, const struct nlattr *options,
-                           struct patch_config *patchconf)
-{
-       struct patch_vport *patch_vport = patch_vport_priv(vport);
-       struct nlattr *a[OVS_PATCH_ATTR_MAX + 1];
-       const char *peer_name;
-       int err;
-
-       if (!options)
-               return -EINVAL;
-
-       err = nla_parse_nested(a, OVS_PATCH_ATTR_MAX, options, patch_policy);
-       if (err)
-               return err;
-
-       if (!a[OVS_PATCH_ATTR_PEER] ||
-           CHECK_NUL_STRING(a[OVS_PATCH_ATTR_PEER], IFNAMSIZ - 1))
-               return -EINVAL;
-
-       peer_name = nla_data(a[OVS_PATCH_ATTR_PEER]);
-       if (!strcmp(patch_vport->name, peer_name))
-               return -EINVAL;
-
-       strcpy(patchconf->peer_name, peer_name);
-
-       return 0;
-}
-
-static struct vport *patch_create(const struct vport_parms *parms)
-{
-       struct vport *vport;
-       struct patch_vport *patch_vport;
-       const char *peer_name;
-       struct patch_config *patchconf;
-       struct net *net = ovs_dp_get_net(parms->dp);
-       int err;
-
-       vport = ovs_vport_alloc(sizeof(struct patch_vport),
-                               &ovs_patch_vport_ops, parms);
-       if (IS_ERR(vport)) {
-               err = PTR_ERR(vport);
-               goto error;
-       }
-
-       patch_vport = patch_vport_priv(vport);
-
-       strcpy(patch_vport->name, parms->name);
-
-       patchconf = kmalloc(sizeof(struct patch_config), GFP_KERNEL);
-       if (!patchconf) {
-               err = -ENOMEM;
-               goto error_free_vport;
-       }
-
-       err = patch_set_config(vport, parms->options, patchconf);
-       if (err)
-               goto error_free_patchconf;
-
-       random_ether_addr(patchconf->eth_addr);
-
-       rcu_assign_pointer(patch_vport->patchconf, patchconf);
-
-       peer_name = patchconf->peer_name;
-       hlist_add_head(&patch_vport->hash_node, hash_bucket(net, peer_name));
-       rcu_assign_pointer(patch_vport->peer, ovs_vport_locate(net, peer_name));
-       update_peers(net, patch_vport->name, vport);
-
-       return vport;
-
-error_free_patchconf:
-       kfree(patchconf);
-error_free_vport:
-       ovs_vport_free(vport);
-error:
-       return ERR_PTR(err);
-}
-
-static void free_port_rcu(struct rcu_head *rcu)
-{
-       struct patch_vport *patch_vport = container_of(rcu,
-                                         struct patch_vport, rcu);
-
-       kfree((struct patch_config __force *)patch_vport->patchconf);
-       ovs_vport_free(vport_from_priv(patch_vport));
-}
-
-static void patch_destroy(struct vport *vport)
-{
-       struct patch_vport *patch_vport = patch_vport_priv(vport);
-
-       update_peers(ovs_dp_get_net(vport->dp), patch_vport->name, NULL);
-       hlist_del(&patch_vport->hash_node);
-       call_rcu(&patch_vport->rcu, free_port_rcu);
-}
-
-static int patch_set_options(struct vport *vport, struct nlattr *options)
-{
-       struct patch_vport *patch_vport = patch_vport_priv(vport);
-       struct patch_config *patchconf;
-       int err;
-
-       patchconf = kmemdup(rtnl_dereference(patch_vport->patchconf),
-                         sizeof(struct patch_config), GFP_KERNEL);
-       if (!patchconf) {
-               err = -ENOMEM;
-               goto error;
-       }
-
-       err = patch_set_config(vport, options, patchconf);
-       if (err)
-               goto error_free;
-
-       assign_config_rcu(vport, patchconf);
-
-       hlist_del(&patch_vport->hash_node);
-
-       rcu_assign_pointer(patch_vport->peer,
-               ovs_vport_locate(ovs_dp_get_net(vport->dp), patchconf->peer_name));
-
-       hlist_add_head(&patch_vport->hash_node,
-                      hash_bucket(ovs_dp_get_net(vport->dp), patchconf->peer_name));
-
-       return 0;
-error_free:
-       kfree(patchconf);
-error:
-       return err;
-}
-
-static void update_peers(struct net *net, const char *name, struct vport *vport)
-{
-       struct hlist_head *bucket = hash_bucket(net, name);
-       struct patch_vport *peer_vport;
-       struct hlist_node *node;
-
-       hlist_for_each_entry(peer_vport, node, bucket, hash_node) {
-               struct vport *curr_vport = vport_from_priv(peer_vport);
-               const char *peer_name;
-
-               peer_name = rtnl_dereference(peer_vport->patchconf)->peer_name;
-               if (!strcmp(peer_name, name) && net_eq(ovs_dp_get_net(curr_vport->dp), net))
-                       rcu_assign_pointer(peer_vport->peer, vport);
-       }
-}
-
-static int patch_set_addr(struct vport *vport, const unsigned char *addr)
-{
-       struct patch_vport *patch_vport = patch_vport_priv(vport);
-       struct patch_config *patchconf;
-
-       patchconf = kmemdup(rtnl_dereference(patch_vport->patchconf),
-                         sizeof(struct patch_config), GFP_KERNEL);
-       if (!patchconf)
-               return -ENOMEM;
-
-       memcpy(patchconf->eth_addr, addr, ETH_ALEN);
-       assign_config_rcu(vport, patchconf);
-
-       return 0;
-}
-
-
-static const char *patch_get_name(const struct vport *vport)
-{
-       const struct patch_vport *patch_vport = patch_vport_priv(vport);
-       return patch_vport->name;
-}
-
-static const unsigned char *patch_get_addr(const struct vport *vport)
-{
-       const struct patch_vport *patch_vport = patch_vport_priv(vport);
-       return rcu_dereference_rtnl(patch_vport->patchconf)->eth_addr;
-}
-
-static int patch_get_options(const struct vport *vport, struct sk_buff *skb)
-{
-       struct patch_vport *patch_vport = patch_vport_priv(vport);
-       struct patch_config *patchconf = rcu_dereference_rtnl(patch_vport->patchconf);
-
-       return nla_put_string(skb, OVS_PATCH_ATTR_PEER, patchconf->peer_name);
-}
-
-static int patch_send(struct vport *vport, struct sk_buff *skb)
-{
-       struct patch_vport *patch_vport = patch_vport_priv(vport);
-       struct vport *peer = rcu_dereference(patch_vport->peer);
-       int skb_len = skb->len;
-
-       if (!peer) {
-               kfree_skb(skb);
-               ovs_vport_record_error(vport, VPORT_E_TX_DROPPED);
-
-               return 0;
-       }
-
-       ovs_vport_receive(peer, skb);
-       return skb_len;
-}
-
-const struct vport_ops ovs_patch_vport_ops = {
-       .type           = OVS_VPORT_TYPE_PATCH,
-       .init           = patch_init,
-       .exit           = patch_exit,
-       .create         = patch_create,
-       .destroy        = patch_destroy,
-       .set_addr       = patch_set_addr,
-       .get_name       = patch_get_name,
-       .get_addr       = patch_get_addr,
-       .get_options    = patch_get_options,
-       .set_options    = patch_set_options,
-       .send           = patch_send,
-};
index f72b95f..4f9f339 100644 (file)
@@ -372,9 +372,7 @@ const struct vport_ops ovs_vxlan_vport_ops = {
        .flags          = VPORT_F_TUN_ID,
        .create         = vxlan_tnl_create,
        .destroy        = vxlan_tnl_destroy,
-       .set_addr       = ovs_tnl_set_addr,
        .get_name       = ovs_tnl_get_name,
-       .get_addr       = ovs_tnl_get_addr,
        .get_options    = ovs_tnl_get_options,
        .set_options    = vxlan_set_options,
        .send           = ovs_tnl_send,
index a78ebfa..9c0942b 100644 (file)
@@ -39,9 +39,7 @@
 static const struct vport_ops *base_vport_ops_list[] = {
        &ovs_netdev_vport_ops,
        &ovs_internal_vport_ops,
-       &ovs_patch_vport_ops,
        &ovs_gre_vport_ops,
-       &ovs_gre_ft_vport_ops,
        &ovs_gre64_vport_ops,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
        &ovs_capwap_vport_ops,
@@ -285,29 +283,6 @@ void ovs_vport_del(struct vport *vport)
        vport->ops->destroy(vport);
 }
 
-/**
- *     ovs_vport_set_addr - set device Ethernet address (for kernel callers)
- *
- * @vport: vport on which to set Ethernet address.
- * @addr: New address.
- *
- * Sets the Ethernet address of the given device.  Some devices may not support
- * setting the Ethernet address, in which case the result will always be
- * -EOPNOTSUPP.  RTNL lock must be held.
- */
-int ovs_vport_set_addr(struct vport *vport, const unsigned char *addr)
-{
-       ASSERT_RTNL();
-
-       if (!is_valid_ether_addr(addr))
-               return -EADDRNOTAVAIL;
-
-       if (vport->ops->set_addr)
-               return vport->ops->set_addr(vport, addr);
-       else
-               return -EOPNOTSUPP;
-}
-
 /**
  *     ovs_vport_set_stats - sets offset device stats
  *
index 91f8836..ed089a9 100644 (file)
@@ -45,7 +45,6 @@ void ovs_vport_del(struct vport *);
 
 struct vport *ovs_vport_locate(struct net *net, const char *name);
 
-int ovs_vport_set_addr(struct vport *, const unsigned char *);
 void ovs_vport_set_stats(struct vport *, struct ovs_vport_stats *);
 void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *);
 
@@ -152,9 +151,7 @@ struct vport_parms {
  * @get_options: Appends vport-specific attributes for the configuration of an
  * existing vport to a &struct sk_buff.  May be %NULL for a vport that does not
  * have any configuration.
- * @set_addr: Set the device's MAC address.  May be null if not supported.
  * @get_name: Get the device's name.
- * @get_addr: Get the device's MAC address.
  * @get_config: Get the device's configuration.
  * @get_ifindex: Get the system interface index associated with the device.
  * May be null if the device does not have an ifindex.
@@ -175,11 +172,8 @@ struct vport_ops {
        int (*set_options)(struct vport *, struct nlattr *);
        int (*get_options)(const struct vport *, struct sk_buff *);
 
-       int (*set_addr)(struct vport *, const unsigned char *);
-
        /* Called with rcu_read_lock or RTNL lock. */
        const char *(*get_name)(const struct vport *);
-       const unsigned char *(*get_addr)(const struct vport *);
        void (*get_config)(const struct vport *, void *);
        int (*get_ifindex)(const struct vport *);
        int (*send)(struct vport *, struct sk_buff *);
@@ -234,9 +228,7 @@ void ovs_vport_record_error(struct vport *, enum vport_err_type err_type);
  * add yours to the list at the top of vport.c. */
 extern const struct vport_ops ovs_netdev_vport_ops;
 extern const struct vport_ops ovs_internal_vport_ops;
-extern const struct vport_ops ovs_patch_vport_ops;
 extern const struct vport_ops ovs_gre_vport_ops;
-extern const struct vport_ops ovs_gre_ft_vport_ops;
 extern const struct vport_ops ovs_gre64_vport_ops;
 extern const struct vport_ops ovs_capwap_vport_ops;
 extern const struct vport_ops ovs_vxlan_vport_ops;
index f471fbc..b12bf0c 100644 (file)
@@ -182,11 +182,9 @@ enum ovs_vport_type {
        OVS_VPORT_TYPE_UNSPEC,
        OVS_VPORT_TYPE_NETDEV,   /* network device */
        OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */
-       OVS_VPORT_TYPE_FT_GRE,   /* Flow based GRE tunnel. */
+       OVS_VPORT_TYPE_GRE,      /* GRE tunnel. */
        OVS_VPORT_TYPE_VXLAN,    /* VXLAN tunnel */
-       OVS_VPORT_TYPE_PATCH = 100, /* virtual tunnel connecting two vports */
-       OVS_VPORT_TYPE_GRE,      /* GRE tunnel */
-       OVS_VPORT_TYPE_CAPWAP,   /* CAPWAP tunnel */
+       OVS_VPORT_TYPE_CAPWAP = 102,  /* CAPWAP tunnel */
        OVS_VPORT_TYPE_GRE64 = 104, /* GRE tunnel with 64-bit keys */
        __OVS_VPORT_TYPE_MAX
 };
@@ -207,7 +205,6 @@ enum ovs_vport_type {
  * this port.  A value of zero indicates that upcalls should not be sent.
  * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for
  * packets sent or received through the vport.
- * @OVS_VPORT_ATTR_ADDRESS: A 6-byte Ethernet address for the vport.
  *
  * These attributes follow the &struct ovs_header within the Generic Netlink
  * payload for %OVS_VPORT_* commands.
@@ -216,8 +213,8 @@ enum ovs_vport_type {
  * %OVS_VPORT_ATTR_NAME attributes are required.  %OVS_VPORT_ATTR_PORT_NO is
  * optional; if not specified a free port number is automatically selected.
  * Whether %OVS_VPORT_ATTR_OPTIONS is required or optional depends on the type
- * of vport.  %OVS_VPORT_ATTR_STATS and %OVS_VPORT_ATTR_ADDRESS are optional,
- * and other attributes are ignored.
+ * of vport.  %OVS_VPORT_ATTR_STATS is optional and other attributes are
+ * ignored.
  *
  * For other requests, if %OVS_VPORT_ATTR_NAME is specified then it is used to
  * look up the vport to operate on; otherwise dp_idx from the &struct
@@ -231,7 +228,6 @@ enum ovs_vport_attr {
        OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */
        OVS_VPORT_ATTR_UPCALL_PID, /* u32 Netlink PID to receive upcalls */
        OVS_VPORT_ATTR_STATS,   /* struct ovs_vport_stats */
-       OVS_VPORT_ATTR_ADDRESS = 100, /* hardware address */
        __OVS_VPORT_ATTR_MAX
 };
 
index 8a53ef8..d9f92d5 100644 (file)
@@ -67,11 +67,11 @@ enum {
 #define TNL_F_CSUM             (1 << 0) /* Checksum packets. */
 #define TNL_F_TOS_INHERIT      (1 << 1) /* Inherit ToS from inner packet. */
 #define TNL_F_TTL_INHERIT      (1 << 2) /* Inherit TTL from inner packet. */
-#define TNL_F_DF_INHERIT       (1 << 3) /* Inherit DF bit from inner packet. */
+/* Bit 3 was previously used for Don't Fragment inheritance. " */
 #define TNL_F_DF_DEFAULT       (1 << 4) /* Set DF bit if inherit off or
                                          * not IP. */
+/* Bit 5 was previously used for path MTU discovery. " */
 /* Bit 6 is reserved since it was previously used for Tunnel header caching. */
-#define TNL_F_PMTUD            (1 << 5) /* Enable path MTU discovery. */
 #define TNL_F_IPSEC            (1 << 7) /* Traffic is IPsec encrypted. */
 
 #endif /* openvswitch/tunnel.h */
index 185f092..58d8da2 100644 (file)
@@ -93,6 +93,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/netdev-tunnel.c \
        lib/netdev-pltap.c \
        lib/netdev-provider.h \
+       lib/netdev-vport.c \
+       lib/netdev-vport.h \
        lib/netdev.c \
        lib/netdev.h \
        lib/netflow.h \
@@ -241,8 +243,6 @@ lib_libopenvswitch_a_SOURCES += \
        lib/dpif-linux.h \
        lib/netdev-linux.c \
        lib/netdev-linux.h \
-       lib/netdev-vport.c \
-       lib/netdev-vport.h \
        lib/netlink-notifier.c \
        lib/netlink-notifier.h \
        lib/netlink-protocol.h \
index 4425f6f..b6eba39 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -421,15 +421,70 @@ dpif_linux_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats)
     return error;
 }
 
+static const char *
+get_vport_type(const struct dpif_linux_vport *vport)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+
+    switch (vport->type) {
+    case OVS_VPORT_TYPE_NETDEV:
+        return "system";
+
+    case OVS_VPORT_TYPE_INTERNAL:
+        return "internal";
+
+    case OVS_VPORT_TYPE_GRE:
+        return "gre";
+
+    case OVS_VPORT_TYPE_GRE64:
+        return "gre64";
+
+    case OVS_VPORT_TYPE_CAPWAP:
+        return "capwap";
+
+    case OVS_VPORT_TYPE_VXLAN:
+        return "vxlan";
+
+    case OVS_VPORT_TYPE_UNSPEC:
+    case __OVS_VPORT_TYPE_MAX:
+        break;
+    }
+
+    VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u",
+                 vport->dp_ifindex, vport->name, (unsigned int) vport->type);
+    return "unknown";
+}
+
+static enum ovs_vport_type
+netdev_to_ovs_vport_type(const struct netdev *netdev)
+{
+    const char *type = netdev_get_type(netdev);
+
+    if (!strcmp(type, "tap") || !strcmp(type, "system")) {
+        return OVS_VPORT_TYPE_NETDEV;
+    } else if (!strcmp(type, "internal")) {
+        return OVS_VPORT_TYPE_INTERNAL;
+    } else if (strstr(type, "gre64")) {
+        return OVS_VPORT_TYPE_GRE64;
+    } else if (strstr(type, "gre")) {
+        return OVS_VPORT_TYPE_GRE;
+    } else if (!strcmp(type, "capwap")) {
+        return OVS_VPORT_TYPE_CAPWAP;
+    } else if (!strcmp(type, "vxlan")) {
+        return OVS_VPORT_TYPE_VXLAN;
+    } else {
+        return OVS_VPORT_TYPE_UNSPEC;
+    }
+}
+
 static int
 dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
                     uint32_t *port_nop)
 {
     struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-    const char *name = netdev_get_name(netdev);
+    const char *name = netdev_vport_get_dpif_port(netdev);
     const char *type = netdev_get_type(netdev);
     struct dpif_linux_vport request, reply;
-    const struct ofpbuf *options;
     struct nl_sock *sock = NULL;
     uint32_t upcall_pid;
     struct ofpbuf *buf;
@@ -445,7 +500,7 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
     dpif_linux_vport_init(&request);
     request.cmd = OVS_VPORT_CMD_NEW;
     request.dp_ifindex = dpif->dp_ifindex;
-    request.type = netdev_vport_get_vport_type(netdev);
+    request.type = netdev_to_ovs_vport_type(netdev);
     if (request.type == OVS_VPORT_TYPE_UNSPEC) {
         VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has "
                      "unsupported type `%s'",
@@ -455,12 +510,6 @@ dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,
     }
     request.name = name;
 
-    options = netdev_vport_get_options(netdev);
-    if (options && options->size) {
-        request.options = options->data;
-        request.options_len = options->size;
-    }
-
     if (request.type == OVS_VPORT_TYPE_NETDEV) {
         netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false);
     }
@@ -547,7 +596,7 @@ dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no,
             error = ENODEV;
         } else if (dpif_port) {
             dpif_port->name = xstrdup(reply.name);
-            dpif_port->type = xstrdup(netdev_vport_get_netdev_type(&reply));
+            dpif_port->type = xstrdup(get_vport_type(&reply));
             dpif_port->port_no = reply.port_no;
         }
         ofpbuf_delete(buf);
@@ -647,7 +696,7 @@ dpif_linux_port_dump_next(const struct dpif *dpif OVS_UNUSED, void *state_,
     }
 
     dpif_port->name = CONST_CAST(char *, vport.name);
-    dpif_port->type = CONST_CAST(char *, netdev_vport_get_netdev_type(&vport));
+    dpif_port->type = CONST_CAST(char *, get_vport_type(&vport));
     dpif_port->port_no = vport.port_no;
     return 0;
 }
@@ -1465,10 +1514,6 @@ dpif_linux_vport_from_ofpbuf(struct dpif_linux_vport *vport,
         [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_U32 },
         [OVS_VPORT_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_vport_stats),
                                    .optional = true },
-        [OVS_VPORT_ATTR_ADDRESS] = { .type = NL_A_UNSPEC,
-                                     .min_len = ETH_ADDR_LEN,
-                                     .max_len = ETH_ADDR_LEN,
-                                     .optional = true },
         [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = true },
     };
 
@@ -1502,9 +1547,6 @@ dpif_linux_vport_from_ofpbuf(struct dpif_linux_vport *vport,
     if (a[OVS_VPORT_ATTR_STATS]) {
         vport->stats = nl_attr_get(a[OVS_VPORT_ATTR_STATS]);
     }
-    if (a[OVS_VPORT_ATTR_ADDRESS]) {
-        vport->address = nl_attr_get(a[OVS_VPORT_ATTR_ADDRESS]);
-    }
     if (a[OVS_VPORT_ATTR_OPTIONS]) {
         vport->options = nl_attr_get(a[OVS_VPORT_ATTR_OPTIONS]);
         vport->options_len = nl_attr_get_size(a[OVS_VPORT_ATTR_OPTIONS]);
@@ -1547,11 +1589,6 @@ dpif_linux_vport_to_ofpbuf(const struct dpif_linux_vport *vport,
                           vport->stats, sizeof *vport->stats);
     }
 
-    if (vport->address) {
-        nl_msg_put_unspec(buf, OVS_VPORT_ATTR_ADDRESS,
-                          vport->address, ETH_ADDR_LEN);
-    }
-
     if (vport->options) {
         nl_msg_put_nested(buf, OVS_VPORT_ATTR_OPTIONS,
                           vport->options, vport->options_len);
@@ -1929,7 +1966,7 @@ report_loss(struct dpif *dpif_, struct dpif_channel *ch)
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
     struct ds s;
 
-    if (VLOG_DROP_ERR(&rl)) {
+    if (VLOG_DROP_WARN(&rl)) {
         return;
     }
 
index 966abc1..81062aa 100644 (file)
@@ -41,7 +41,6 @@ struct dpif_linux_vport {
     const char *name;                      /* OVS_VPORT_ATTR_NAME. */
     const uint32_t *upcall_pid;            /* OVS_VPORT_ATTR_UPCALL_PID. */
     const struct ovs_vport_stats *stats;   /* OVS_VPORT_ATTR_STATS. */
-    const uint8_t *address;                /* OVS_VPORT_ATTR_ADDRESS. */
     const struct nlattr *options;          /* OVS_VPORT_ATTR_OPTIONS. */
     size_t options_len;
 };
index 13f5715..129839c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@
 #include "hmap.h"
 #include "list.h"
 #include "netdev.h"
+#include "netdev-vport.h"
 #include "netlink.h"
 #include "odp-util.h"
 #include "ofp-print.h"
@@ -180,13 +181,25 @@ dpif_netdev_enumerate(struct sset *all_dps)
     return 0;
 }
 
+static bool
+dpif_netdev_class_is_dummy(const struct dpif_class *class)
+{
+    return class != &dpif_netdev_class;
+}
+
+static bool
+dpif_netdev_class_is_planetlab(const struct dpif_class *class)
+{
+    return class == &dpif_planetlab_class;
+}
+
 static const char *
 dpif_netdev_port_open_type(const struct dpif_class *class, const char *type)
 {
     return strcmp(type, "internal") ? type
-                  : class == &dpif_netdev_class ? "tap"
-                  : class == &dpif_planetlab_class ? "pltap"
-                  : "dummy";
+                  : dpif_netdev_class_is_planetlab(class) ? "pltap"
+                  : dpif_netdev_class_is_dummy(class) ? "dummy"
+                  : "tap";
 }
 
 static struct dpif *
@@ -387,7 +400,8 @@ do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
     /* XXX reject non-Ethernet devices */
 
     error = netdev_listen(netdev);
-    if (error) {
+    if (error
+        && !(error == EOPNOTSUPP && dpif_netdev_class_is_dummy(dp->class))) {
         VLOG_ERR("%s: cannot receive packets on this network device (%s)",
                  devname, strerror(errno));
         netdev_close(netdev);
@@ -432,11 +446,11 @@ dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
         }
         port_no = *port_nop;
     } else {
-        port_no = choose_port(dp, netdev_get_name(netdev));
+        port_no = choose_port(dp, netdev_vport_get_dpif_port(netdev));
     }
     if (port_no >= 0) {
         *port_nop = port_no;
-        return do_add_port(dp, netdev_get_name(netdev),
+        return do_add_port(dp, netdev_vport_get_dpif_port(netdev),
                            netdev_get_type(netdev), port_no);
     }
     return EFBIG;
@@ -475,7 +489,7 @@ get_port_by_name(struct dp_netdev *dp,
     struct dp_netdev_port *port;
 
     LIST_FOR_EACH (port, node, &dp->port_list) {
-        if (!strcmp(netdev_get_name(port->netdev), devname)) {
+        if (!strcmp(netdev_vport_get_dpif_port(port->netdev), devname)) {
             *portp = port;
             return 0;
         }
@@ -499,7 +513,7 @@ do_del_port(struct dp_netdev *dp, uint32_t port_no)
     dp->ports[port->port_no] = NULL;
     dp->serial++;
 
-    name = xstrdup(netdev_get_name(port->netdev));
+    name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
     netdev_close(port->netdev);
     free(port->type);
 
@@ -513,7 +527,7 @@ static void
 answer_port_query(const struct dp_netdev_port *port,
                   struct dpif_port *dpif_port)
 {
-    dpif_port->name = xstrdup(netdev_get_name(port->netdev));
+    dpif_port->name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
     dpif_port->type = xstrdup(port->type);
     dpif_port->port_no = port->port_no;
 }
@@ -604,7 +618,7 @@ dpif_netdev_port_dump_next(const struct dpif *dpif, void *state_,
         struct dp_netdev_port *port = dp->ports[port_no];
         if (port) {
             free(state->name);
-            state->name = xstrdup(netdev_get_name(port->netdev));
+            state->name = xstrdup(netdev_vport_get_dpif_port(port->netdev));
             dpif_port->name = state->name;
             dpif_port->type = port->type;
             dpif_port->port_no = port->port_no;
@@ -1063,7 +1077,8 @@ dpif_netdev_run(struct dpif *dpif)
         } else if (error != EAGAIN && error != EOPNOTSUPP) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
             VLOG_ERR_RL(&rl, "error receiving data from %s: %s",
-                        netdev_get_name(port->netdev), strerror(error));
+                        netdev_vport_get_dpif_port(port->netdev),
+                        strerror(error));
         }
     }
     ofpbuf_uninit(&packet);
index a478db2..c5e3fc8 100644 (file)
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
-#include <linux/openvswitch.h>
 #include "openflow/openflow.h"
 #include "netdev.h"
 #include "util.h"
index f1bf63c..bedb1a1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -106,8 +106,7 @@ match_wc_init(struct match *match, const struct flow *flow)
         memset(&wc->masks.arp_tha, 0xff, sizeof wc->masks.arp_tha);
     }
 
-    if (flow->dl_type == htons(ETH_TYPE_IPV6) ||
-        flow->dl_type == htons(ETH_TYPE_IP)) {
+    if (is_ip_any(flow)) {
         memset(&wc->masks.nw_tos, 0xff, sizeof wc->masks.nw_tos);
         memset(&wc->masks.nw_ttl, 0xff, sizeof wc->masks.nw_ttl);
     }
index 87887a8..e25103d 100644 (file)
@@ -760,13 +760,6 @@ mf_is_mask_valid(const struct mf_field *mf, const union mf_value *mask)
     NOT_REACHED();
 }
 
-static bool
-is_ip_any(const struct flow *flow)
-{
-    return (flow->dl_type == htons(ETH_TYPE_IP) ||
-            flow->dl_type == htons(ETH_TYPE_IPV6));
-}
-
 static bool
 is_icmpv4(const struct flow *flow)
 {
index ab43772..f81b68e 100644 (file)
@@ -23,6 +23,7 @@
 #include "flow.h"
 #include "list.h"
 #include "netdev-provider.h"
+#include "netdev-vport.h"
 #include "odp-util.h"
 #include "ofp-print.h"
 #include "ofpbuf.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_dummy);
 
+#ifdef __FreeBSD__
+#define FREE_BSD 1
+#else
+#define FREE_BSD 0
+#endif
+
 struct netdev_dev_dummy {
     struct netdev_dev netdev_dev;
     uint8_t hwaddr[ETH_ADDR_LEN];
@@ -530,4 +537,8 @@ netdev_dummy_register(bool override)
         sset_destroy(&types);
     }
     netdev_register_provider(&dummy_class);
+
+    if (FREE_BSD) {
+        netdev_vport_tunnel_register();
+    }
 }
index 433d168..5de4fa2 100644 (file)
 #include "hmap.h"
 #include "netdev-provider.h"
 #include "netdev-vport.h"
-#include "netlink.h"
 #include "netlink-notifier.h"
 #include "netlink-socket.h"
+#include "netlink.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "rtnetlink-link.h"
-#include "socket-util.h"
 #include "shash.h"
+#include "socket-util.h"
 #include "sset.h"
 #include "timer.h"
+#include "unaligned.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_linux);
@@ -1309,6 +1310,58 @@ swap_uint64(uint64_t *a, uint64_t *b)
     *b = tmp;
 }
 
+/* Copies 'src' into 'dst', performing format conversion in the process.
+ *
+ * 'src' is allowed to be misaligned. */
+static void
+netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst,
+                                  const struct ovs_vport_stats *src)
+{
+    dst->rx_packets = get_unaligned_u64(&src->rx_packets);
+    dst->tx_packets = get_unaligned_u64(&src->tx_packets);
+    dst->rx_bytes = get_unaligned_u64(&src->rx_bytes);
+    dst->tx_bytes = get_unaligned_u64(&src->tx_bytes);
+    dst->rx_errors = get_unaligned_u64(&src->rx_errors);
+    dst->tx_errors = get_unaligned_u64(&src->tx_errors);
+    dst->rx_dropped = get_unaligned_u64(&src->rx_dropped);
+    dst->tx_dropped = get_unaligned_u64(&src->tx_dropped);
+    dst->multicast = 0;
+    dst->collisions = 0;
+    dst->rx_length_errors = 0;
+    dst->rx_over_errors = 0;
+    dst->rx_crc_errors = 0;
+    dst->rx_frame_errors = 0;
+    dst->rx_fifo_errors = 0;
+    dst->rx_missed_errors = 0;
+    dst->tx_aborted_errors = 0;
+    dst->tx_carrier_errors = 0;
+    dst->tx_fifo_errors = 0;
+    dst->tx_heartbeat_errors = 0;
+    dst->tx_window_errors = 0;
+}
+
+static int
+get_stats_via_vport__(const struct netdev *netdev, struct netdev_stats *stats)
+{
+    struct dpif_linux_vport reply;
+    struct ofpbuf *buf;
+    int error;
+
+    error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
+    if (error) {
+        return error;
+    } else if (!reply.stats) {
+        ofpbuf_delete(buf);
+        return EOPNOTSUPP;
+    }
+
+    netdev_stats_from_ovs_vport_stats(stats, reply.stats);
+
+    ofpbuf_delete(buf);
+
+    return 0;
+}
+
 static void
 get_stats_via_vport(const struct netdev *netdev_,
                     struct netdev_stats *stats)
@@ -1320,7 +1373,7 @@ get_stats_via_vport(const struct netdev *netdev_,
         !(netdev_dev->cache_valid & VALID_VPORT_STAT_ERROR)) {
         int error;
 
-        error = netdev_vport_get_stats(netdev_, stats);
+        error = get_stats_via_vport__(netdev_, stats);
         if (error && error != ENOENT) {
             VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed "
                          "(%s)", netdev_get_name(netdev_), strerror(error));
@@ -3730,7 +3783,7 @@ tc_add_policer(struct netdev *netdev, int kbits_rate, int kbits_burst)
     memset(&tc_police, 0, sizeof tc_police);
     tc_police.action = TC_POLICE_SHOT;
     tc_police.mtu = mtu;
-    tc_fill_rate(&tc_police.rate, kbits_rate/8 * 1000, mtu);
+    tc_fill_rate(&tc_police.rate, (kbits_rate * 1000)/8, mtu);
     tc_police.burst = tc_bytes_to_ticks(tc_police.rate.rate,
                                         kbits_burst * 1024);
 
index 60437b9..b892f8f 100644 (file)
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/socket.h>
-#include <linux/openvswitch.h>
-#include <linux/rtnetlink.h>
 #include <net/if.h>
 #include <sys/ioctl.h>
 
 #include "byte-order.h"
 #include "daemon.h"
 #include "dirs.h"
-#include "dpif-linux.h"
+#include "dpif.h"
 #include "hash.h"
 #include "hmap.h"
 #include "list.h"
-#include "netdev-linux.h"
 #include "netdev-provider.h"
-#include "netlink.h"
-#include "netlink-notifier.h"
-#include "netlink-socket.h"
 #include "ofpbuf.h"
-#include "openvswitch/tunnel.h"
 #include "packets.h"
 #include "route-table.h"
 #include "shash.h"
 #include "socket-util.h"
-#include "unaligned.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_vport);
@@ -56,31 +48,26 @@ VLOG_DEFINE_THIS_MODULE(netdev_vport);
 
 struct netdev_dev_vport {
     struct netdev_dev netdev_dev;
-    struct ofpbuf *options;
     unsigned int change_seq;
     uint8_t etheraddr[ETH_ADDR_LEN];
+    struct netdev_stats stats;
+
+    /* Tunnels. */
     struct netdev_tunnel_config tnl_cfg;
+
+    /* Patch Ports. */
+    char *peer;
 };
 
 struct vport_class {
-    enum ovs_vport_type type;
+    const char *dpif_port;
     struct netdev_class netdev_class;
-    int (*parse_config)(const char *name, const char *type,
-                        const struct smap *args, struct ofpbuf *options,
-                        struct netdev_tunnel_config *tnl_cfg);
-    int (*unparse_config)(const char *name, const char *type,
-                          const struct nlattr *options, size_t options_len,
-                          struct smap *args);
 };
 
-static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
-
 static int netdev_vport_create(const struct netdev_class *, const char *,
                                struct netdev_dev **);
-static void netdev_vport_poll_notify(const struct netdev *);
-static int tnl_port_config_from_nlattr(const struct nlattr *options,
-                                       size_t options_len,
-                                       struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1]);
+static int get_patch_config(struct netdev_dev *, struct smap *args);
+static void netdev_vport_poll_notify(struct netdev_dev_vport *);
 
 static bool
 is_vport_class(const struct netdev_class *class)
@@ -114,86 +101,26 @@ get_netdev_tunnel_config(const struct netdev_dev *netdev_dev)
     return &netdev_dev_vport_cast(netdev_dev)->tnl_cfg;
 }
 
-/* If 'netdev' is a vport netdev, returns an ofpbuf that contains Netlink
- * options to include in OVS_VPORT_ATTR_OPTIONS for configuring that vport.
- * Otherwise returns NULL. */
-const struct ofpbuf *
-netdev_vport_get_options(const struct netdev *netdev)
-{
-    const struct netdev_dev *dev = netdev_get_dev(netdev);
-
-    return (is_vport_class(netdev_dev_get_class(dev))
-            ? netdev_dev_vport_cast(dev)->options
-            : NULL);
-}
-
-enum ovs_vport_type
-netdev_vport_get_vport_type(const struct netdev *netdev)
+bool
+netdev_vport_is_patch(const struct netdev *netdev)
 {
     const struct netdev_dev *dev = netdev_get_dev(netdev);
     const struct netdev_class *class = netdev_dev_get_class(dev);
 
-    return (is_vport_class(class) ? vport_class_cast(class)->type
-            : class == &netdev_internal_class ? OVS_VPORT_TYPE_INTERNAL
-            : (class == &netdev_linux_class ||
-               class == &netdev_tap_class) ? OVS_VPORT_TYPE_NETDEV
-            : OVS_VPORT_TYPE_UNSPEC);
-}
-
-static uint32_t
-get_u32_or_zero(const struct nlattr *a)
-{
-    return a ? nl_attr_get_u32(a) : 0;
+    return class->get_config == get_patch_config;
 }
 
 const char *
-netdev_vport_get_netdev_type(const struct dpif_linux_vport *vport)
+netdev_vport_get_dpif_port(const struct netdev *netdev)
 {
-    struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
-
-    switch (vport->type) {
-    case OVS_VPORT_TYPE_UNSPEC:
-        break;
-
-    case OVS_VPORT_TYPE_NETDEV:
-        return "system";
-
-    case OVS_VPORT_TYPE_INTERNAL:
-        return "internal";
-
-    case OVS_VPORT_TYPE_PATCH:
-        return "patch";
-
-    case OVS_VPORT_TYPE_GRE:
-        if (tnl_port_config_from_nlattr(vport->options, vport->options_len,
-                                        a)) {
-            break;
-        }
-        return (get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
-                ? "ipsec_gre" : "gre");
-
-    case OVS_VPORT_TYPE_GRE64:
-        if (tnl_port_config_from_nlattr(vport->options, vport->options_len,
-                                        a)) {
-            break;
-        }
-        return (get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_IPSEC
-                ? "ipsec_gre64" : "gre64");
-
-    case OVS_VPORT_TYPE_CAPWAP:
-        return "capwap";
-
-    case OVS_VPORT_TYPE_VXLAN:
-        return "vxlan";
-
-    case OVS_VPORT_TYPE_FT_GRE:
-    case __OVS_VPORT_TYPE_MAX:
-        break;
-    }
+    const struct netdev_dev *dev = netdev_get_dev(netdev);
+    const struct netdev_class *class = netdev_dev_get_class(dev);
+    const char *dpif_port;
 
-    VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u",
-                 vport->dp_ifindex, vport->name, (unsigned int) vport->type);
-    return "unknown";
+    dpif_port = (is_vport_class(class)
+                 ? vport_class_cast(class)->dpif_port
+                 : NULL);
+    return dpif_port ? dpif_port : netdev_get_name(netdev);
 }
 
 static int
@@ -218,8 +145,8 @@ netdev_vport_destroy(struct netdev_dev *netdev_dev_)
 {
     struct netdev_dev_vport *netdev_dev = netdev_dev_vport_cast(netdev_dev_);
 
-    ofpbuf_delete(netdev_dev->options);
     route_table_unregister();
+    free(netdev_dev->peer);
     free(netdev_dev);
 }
 
@@ -237,89 +164,13 @@ netdev_vport_close(struct netdev *netdev)
     free(netdev);
 }
 
-static int
-netdev_vport_get_config(struct netdev_dev *dev_, struct smap *args)
-{
-    const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
-    const struct vport_class *vport_class = vport_class_cast(netdev_class);
-    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
-    const char *name = netdev_dev_get_name(dev_);
-    int error;
-
-    if (!dev->options) {
-        struct dpif_linux_vport reply;
-        struct ofpbuf *buf;
-
-        error = dpif_linux_vport_get(name, &reply, &buf);
-        if (error) {
-            VLOG_ERR_RL(&rl, "%s: vport query failed (%s)",
-                        name, strerror(error));
-            return error;
-        }
-
-        dev->options = ofpbuf_clone_data(reply.options, reply.options_len);
-        ofpbuf_delete(buf);
-    }
-
-    error = vport_class->unparse_config(name, netdev_class->type,
-                                        dev->options->data,
-                                        dev->options->size,
-                                        args);
-    if (error) {
-        VLOG_ERR_RL(&rl, "%s: failed to parse kernel config (%s)",
-                    name, strerror(error));
-    }
-    return error;
-}
-
-static int
-netdev_vport_set_config(struct netdev_dev *dev_, const struct smap *args)
-{
-    const struct netdev_class *netdev_class = netdev_dev_get_class(dev_);
-    const struct vport_class *vport_class = vport_class_cast(netdev_class);
-    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
-    const char *name = netdev_dev_get_name(dev_);
-    struct netdev_tunnel_config tnl_cfg;
-    struct ofpbuf *options;
-    int error;
-
-    options = ofpbuf_new(64);
-    error = vport_class->parse_config(name, netdev_dev_get_type(dev_),
-                                      args, options, &tnl_cfg);
-    if (!error
-        && (!dev->options
-            || options->size != dev->options->size
-            || memcmp(options->data, dev->options->data, options->size))) {
-        struct dpif_linux_vport vport;
-
-        dpif_linux_vport_init(&vport);
-        vport.cmd = OVS_VPORT_CMD_SET;
-        vport.name = name;
-        vport.options = options->data;
-        vport.options_len = options->size;
-        error = dpif_linux_vport_transact(&vport, NULL, NULL);
-        if (!error || error == ENODEV) {
-            /* Either reconfiguration succeeded or this vport is not installed
-             * in the kernel (e.g. it hasn't been added to a dpif yet with
-             * dpif_port_add()). */
-            ofpbuf_delete(dev->options);
-            dev->options = options;
-            dev->tnl_cfg = tnl_cfg;
-            options = NULL;
-            error = 0;
-        }
-    }
-    ofpbuf_delete(options);
-
-    return error;
-}
-
 static int
 netdev_vport_set_etheraddr(struct netdev *netdev,
                            const uint8_t mac[ETH_ADDR_LEN])
 {
-    memcpy(netdev_vport_get_dev(netdev)->etheraddr, mac, ETH_ADDR_LEN);
-    netdev_vport_poll_notify(netdev);
+    struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
+    memcpy(dev->etheraddr, mac, ETH_ADDR_LEN);
+    netdev_vport_poll_notify(dev);
     return 0;
 }
 
@@ -331,58 +182,6 @@ netdev_vport_get_etheraddr(const struct netdev *netdev,
     return 0;
 }
 
-/* Copies 'src' into 'dst', performing format conversion in the process.
- *
- * 'src' is allowed to be misaligned. */
-static void
-netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst,
-                                  const struct ovs_vport_stats *src)
-{
-    dst->rx_packets = get_unaligned_u64(&src->rx_packets);
-    dst->tx_packets = get_unaligned_u64(&src->tx_packets);
-    dst->rx_bytes = get_unaligned_u64(&src->rx_bytes);
-    dst->tx_bytes = get_unaligned_u64(&src->tx_bytes);
-    dst->rx_errors = get_unaligned_u64(&src->rx_errors);
-    dst->tx_errors = get_unaligned_u64(&src->tx_errors);
-    dst->rx_dropped = get_unaligned_u64(&src->rx_dropped);
-    dst->tx_dropped = get_unaligned_u64(&src->tx_dropped);
-    dst->multicast = 0;
-    dst->collisions = 0;
-    dst->rx_length_errors = 0;
-    dst->rx_over_errors = 0;
-    dst->rx_crc_errors = 0;
-    dst->rx_frame_errors = 0;
-    dst->rx_fifo_errors = 0;
-    dst->rx_missed_errors = 0;
-    dst->tx_aborted_errors = 0;
-    dst->tx_carrier_errors = 0;
-    dst->tx_fifo_errors = 0;
-    dst->tx_heartbeat_errors = 0;
-    dst->tx_window_errors = 0;
-}
-
-int
-netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
-{
-    struct dpif_linux_vport reply;
-    struct ofpbuf *buf;
-    int error;
-
-    error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf);
-    if (error) {
-        return error;
-    } else if (!reply.stats) {
-        ofpbuf_delete(buf);
-        return EOPNOTSUPP;
-    }
-
-    netdev_stats_from_ovs_vport_stats(stats, reply.stats);
-
-    ofpbuf_delete(buf);
-
-    return 0;
-}
-
 static int
 tunnel_get_status(const struct netdev *netdev, struct smap *smap)
 {
@@ -439,17 +238,15 @@ netdev_vport_wait(void)
 /* Helper functions. */
 
 static void
-netdev_vport_poll_notify(const struct netdev *netdev)
+netdev_vport_poll_notify(struct netdev_dev_vport *ndv)
 {
-    struct netdev_dev_vport *ndv = netdev_vport_get_dev(netdev);
-
     ndv->change_seq++;
     if (!ndv->change_seq) {
         ndv->change_seq++;
     }
 }
 \f
-/* Code specific to individual vport types. */
+/* Code specific to tunnel types. */
 
 static ovs_be64
 parse_key(const struct smap *args, const char *name,
@@ -479,16 +276,15 @@ parse_key(const struct smap *args, const char *name,
 }
 
 static int
-parse_tunnel_config(const char *name, const char *type,
-                    const struct smap *args, struct ofpbuf *options,
-                    struct netdev_tunnel_config *tnl_cfg_)
+set_tunnel_config(struct netdev_dev *dev_, const struct smap *args)
 {
+    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
+    const char *name = netdev_dev_get_name(dev_);
+    const char *type = netdev_dev_get_type(dev_);
     bool ipsec_mech_set, needs_dst_port, has_csum;
     struct netdev_tunnel_config tnl_cfg;
     struct smap_node *node;
-    uint8_t flags;
 
-    flags = TNL_F_DF_DEFAULT;
     has_csum = strstr(type, "gre");
     ipsec_mech_set = false;
     memset(&tnl_cfg, 0, sizeof tnl_cfg);
@@ -499,9 +295,6 @@ parse_tunnel_config(const char *name, const char *type,
 
     needs_dst_port = !strcmp(type, "vxlan");
     tnl_cfg.ipsec = strstr(type, "ipsec");
-    if (tnl_cfg.ipsec) {
-        flags |= TNL_F_IPSEC;
-    }
     tnl_cfg.dont_fragment = true;
 
     SMAP_FOR_EACH (node, args) {
@@ -521,14 +314,12 @@ parse_tunnel_config(const char *name, const char *type,
             }
         } else if (!strcmp(node->key, "tos")) {
             if (!strcmp(node->value, "inherit")) {
-                flags |= TNL_F_TOS_INHERIT;
                 tnl_cfg.tos_inherit = true;
             } else {
                 char *endptr;
                 int tos;
                 tos = strtol(node->value, &endptr, 0);
                 if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) {
-                    nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TOS, tos);
                     tnl_cfg.tos = tos;
                 } else {
                     VLOG_WARN("%s: invalid TOS %s", name, node->value);
@@ -536,24 +327,18 @@ parse_tunnel_config(const char *name, const char *type,
             }
         } else if (!strcmp(node->key, "ttl")) {
             if (!strcmp(node->value, "inherit")) {
-                flags |= TNL_F_TTL_INHERIT;
                 tnl_cfg.ttl_inherit = true;
             } else {
-                nl_msg_put_u8(options, OVS_TUNNEL_ATTR_TTL, atoi(node->value));
                 tnl_cfg.ttl = atoi(node->value);
             }
         } else if (!strcmp(node->key, "dst_port") && needs_dst_port) {
             tnl_cfg.dst_port = htons(atoi(node->value));
-            nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT,
-                           atoi(node->value));
         } else if (!strcmp(node->key, "csum") && has_csum) {
             if (!strcmp(node->value, "true")) {
-                flags |= TNL_F_CSUM;
                 tnl_cfg.csum = true;
             }
         } else if (!strcmp(node->key, "df_default")) {
             if (!strcmp(node->value, "false")) {
-                flags &= ~TNL_F_DF_DEFAULT;
                 tnl_cfg.dont_fragment = false;
             }
         } else if (!strcmp(node->key, "peer_cert") && tnl_cfg.ipsec) {
@@ -594,7 +379,6 @@ parse_tunnel_config(const char *name, const char *type,
 
     /* Add a default destination port for VXLAN if none specified. */
     if (needs_dst_port && !tnl_cfg.dst_port) {
-        nl_msg_put_u16(options, OVS_TUNNEL_ATTR_DST_PORT, VXLAN_DST_PORT);
         tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
     }
 
@@ -630,14 +414,11 @@ parse_tunnel_config(const char *name, const char *type,
                  name, type);
         return EINVAL;
     }
-    nl_msg_put_be32(options, OVS_TUNNEL_ATTR_DST_IPV4, tnl_cfg.ip_dst);
 
     if (tnl_cfg.ip_src) {
         if (ip_is_multicast(tnl_cfg.ip_dst)) {
             VLOG_WARN("%s: remote_ip is multicast, ignoring local_ip", name);
             tnl_cfg.ip_src = 0;
-        } else {
-            nl_msg_put_be32(options, OVS_TUNNEL_ATTR_SRC_IPV4, tnl_cfg.ip_src);
         }
     }
 
@@ -648,146 +429,131 @@ parse_tunnel_config(const char *name, const char *type,
     tnl_cfg.in_key = parse_key(args, "in_key",
                                &tnl_cfg.in_key_present,
                                &tnl_cfg.in_key_flow);
-    if (tnl_cfg.in_key_present && !tnl_cfg.in_key_flow) {
-        nl_msg_put_be64(options, OVS_TUNNEL_ATTR_IN_KEY, tnl_cfg.in_key);
-    }
 
     tnl_cfg.out_key = parse_key(args, "out_key",
                                &tnl_cfg.out_key_present,
                                &tnl_cfg.out_key_flow);
-    if (tnl_cfg.out_key_present && !tnl_cfg.out_key_flow) {
-        nl_msg_put_be64(options, OVS_TUNNEL_ATTR_OUT_KEY, tnl_cfg.out_key);
-    }
-    nl_msg_put_u32(options, OVS_TUNNEL_ATTR_FLAGS, flags);
 
-    *tnl_cfg_ = tnl_cfg;
+    dev->tnl_cfg = tnl_cfg;
+    netdev_vport_poll_notify(dev);
 
     return 0;
 }
 
 static int
-tnl_port_config_from_nlattr(const struct nlattr *options, size_t options_len,
-                            struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1])
-{
-    static const struct nl_policy ovs_tunnel_policy[] = {
-        [OVS_TUNNEL_ATTR_FLAGS] = { .type = NL_A_U32, .optional = true },
-        [OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NL_A_BE32, .optional = true },
-        [OVS_TUNNEL_ATTR_SRC_IPV4] = { .type = NL_A_BE32, .optional = true },
-        [OVS_TUNNEL_ATTR_IN_KEY] = { .type = NL_A_BE64, .optional = true },
-        [OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NL_A_BE64, .optional = true },
-        [OVS_TUNNEL_ATTR_TOS] = { .type = NL_A_U8, .optional = true },
-        [OVS_TUNNEL_ATTR_TTL] = { .type = NL_A_U8, .optional = true },
-        [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NL_A_U16, .optional = true },
-    };
-    struct ofpbuf buf;
-
-    ofpbuf_use_const(&buf, options, options_len);
-    if (!nl_policy_parse(&buf, 0, ovs_tunnel_policy,
-                         a, ARRAY_SIZE(ovs_tunnel_policy))) {
-        return EINVAL;
-    }
-    return 0;
-}
-
-static uint64_t
-get_be64_or_zero(const struct nlattr *a)
-{
-    return a ? ntohll(nl_attr_get_be64(a)) : 0;
-}
-
-static int
-unparse_tunnel_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
-                      const struct nlattr *options, size_t options_len,
-                      struct smap *args)
+get_tunnel_config(struct netdev_dev *dev, struct smap *args)
 {
-    struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
-    uint32_t flags;
-    int error;
+    const struct netdev_tunnel_config *tnl_cfg =
+        &netdev_dev_vport_cast(dev)->tnl_cfg;
 
-    error = tnl_port_config_from_nlattr(options, options_len, a);
-    if (error) {
-        return error;
+    if (tnl_cfg->ip_dst) {
+        smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_dst));
     }
 
-    if (a[OVS_TUNNEL_ATTR_DST_IPV4]) {
-        ovs_be32 daddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
-        smap_add_format(args, "remote_ip", IP_FMT, IP_ARGS(daddr));
+    if (tnl_cfg->ip_src) {
+        smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(tnl_cfg->ip_src));
     }
 
-    if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
-        ovs_be32 saddr = nl_attr_get_be32(a[OVS_TUNNEL_ATTR_SRC_IPV4]);
-        smap_add_format(args, "local_ip", IP_FMT, IP_ARGS(saddr));
-    }
-
-    if (!a[OVS_TUNNEL_ATTR_IN_KEY] && !a[OVS_TUNNEL_ATTR_OUT_KEY]) {
+    if (tnl_cfg->in_key_flow && tnl_cfg->out_key_flow) {
         smap_add(args, "key", "flow");
+    } else if (tnl_cfg->in_key_present && tnl_cfg->out_key_present
+               && tnl_cfg->in_key == tnl_cfg->out_key) {
+        smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg->in_key));
     } else {
-        uint64_t in_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_IN_KEY]);
-        uint64_t out_key = get_be64_or_zero(a[OVS_TUNNEL_ATTR_OUT_KEY]);
-
-        if (in_key && in_key == out_key) {
-            smap_add_format(args, "key", "%"PRIu64, in_key);
-        } else {
-            if (!a[OVS_TUNNEL_ATTR_IN_KEY]) {
-                smap_add(args, "in_key", "flow");
-            } else if (in_key) {
-                smap_add_format(args, "in_key", "%"PRIu64, in_key);
-            }
+        if (tnl_cfg->in_key_flow) {
+            smap_add(args, "in_key", "flow");
+        } else if (tnl_cfg->in_key_present) {
+            smap_add_format(args, "in_key", "%"PRIu64,
+                            ntohll(tnl_cfg->in_key));
+        }
 
-            if (!a[OVS_TUNNEL_ATTR_OUT_KEY]) {
-                smap_add(args, "out_key", "flow");
-            } else if (out_key) {
-                smap_add_format(args, "out_key", "%"PRIu64, out_key);
-            }
+        if (tnl_cfg->out_key_flow) {
+            smap_add(args, "out_key", "flow");
+        } else if (tnl_cfg->out_key_present) {
+            smap_add_format(args, "out_key", "%"PRIu64,
+                            ntohll(tnl_cfg->out_key));
         }
     }
 
-    flags = get_u32_or_zero(a[OVS_TUNNEL_ATTR_FLAGS]);
-
-    if (flags & TNL_F_TTL_INHERIT) {
+    if (tnl_cfg->ttl_inherit) {
         smap_add(args, "ttl", "inherit");
-    } else if (a[OVS_TUNNEL_ATTR_TTL]) {
-        int ttl = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TTL]);
-        smap_add_format(args, "ttl", "%d", ttl);
+    } else if (tnl_cfg->ttl != DEFAULT_TTL) {
+        smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg->ttl);
     }
 
-    if (flags & TNL_F_TOS_INHERIT) {
+    if (tnl_cfg->tos_inherit) {
         smap_add(args, "tos", "inherit");
-    } else if (a[OVS_TUNNEL_ATTR_TOS]) {
-        int tos = nl_attr_get_u8(a[OVS_TUNNEL_ATTR_TOS]);
-        smap_add_format(args, "tos", "0x%x", tos);
+    } else if (tnl_cfg->tos) {
+        smap_add_format(args, "tos", "0x%x", tnl_cfg->tos);
     }
 
-    if (a[OVS_TUNNEL_ATTR_DST_PORT]) {
-        uint16_t dst_port = nl_attr_get_u16(a[OVS_TUNNEL_ATTR_DST_PORT]);
+    if (tnl_cfg->dst_port) {
+        uint16_t dst_port = ntohs(tnl_cfg->dst_port);
         if (dst_port != VXLAN_DST_PORT) {
             smap_add_format(args, "dst_port", "%d", dst_port);
         }
     }
 
-    if (flags & TNL_F_CSUM) {
+    if (tnl_cfg->csum) {
         smap_add(args, "csum", "true");
     }
-    if (flags & TNL_F_DF_INHERIT) {
-        /* Shouldn't happen as "df_inherit" is no longer supported.  However,
-         * for completeness we report it if it's there. */
-        smap_add(args, "df_inherit", "true");
-    }
-    if (!(flags & TNL_F_DF_DEFAULT)) {
+
+    if (!tnl_cfg->dont_fragment) {
         smap_add(args, "df_default", "false");
     }
 
     return 0;
 }
+\f
+/* Code specific to patch ports. */
+
+const char *
+netdev_vport_patch_peer(const struct netdev *netdev)
+{
+    return netdev_vport_is_patch(netdev)
+        ? netdev_vport_get_dev(netdev)->peer
+        : NULL;
+}
+
+void
+netdev_vport_inc_rx(const struct netdev *netdev,
+                          const struct dpif_flow_stats *stats)
+{
+    if (is_vport_class(netdev_dev_get_class(netdev_get_dev(netdev)))) {
+        struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
+        dev->stats.rx_packets += stats->n_packets;
+        dev->stats.rx_bytes += stats->n_bytes;
+    }
+}
+
+void
+netdev_vport_inc_tx(const struct netdev *netdev,
+                    const struct dpif_flow_stats *stats)
+{
+    if (is_vport_class(netdev_dev_get_class(netdev_get_dev(netdev)))) {
+        struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
+        dev->stats.tx_packets += stats->n_packets;
+        dev->stats.tx_bytes += stats->n_bytes;
+    }
+}
 
 static int
-parse_patch_config(const char *name, const char *type OVS_UNUSED,
-                   const struct smap *args, struct ofpbuf *options,
-                   struct netdev_tunnel_config *tnl_cfg)
+get_patch_config(struct netdev_dev *dev_, struct smap *args)
 {
-    const char *peer;
+    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
+
+    if (dev->peer) {
+        smap_add(args, "peer", dev->peer);
+    }
+    return 0;
+}
 
-    memset(tnl_cfg, 0, sizeof *tnl_cfg);
+static int
+set_patch_config(struct netdev_dev *dev_, const struct smap *args)
+{
+    struct netdev_dev_vport *dev = netdev_dev_vport_cast(dev_);
+    const char *name = netdev_dev_get_name(dev_);
+    const char *peer;
 
     peer = smap_get(args, "peer");
     if (!peer) {
@@ -800,54 +566,35 @@ parse_patch_config(const char *name, const char *type OVS_UNUSED,
         return EINVAL;
     }
 
-    if (strlen(peer) >= IFNAMSIZ) {
-        VLOG_ERR("%s: patch 'peer' arg too long", name);
-        return EINVAL;
-    }
-
     if (!strcmp(name, peer)) {
         VLOG_ERR("%s: patch peer must not be self", name);
         return EINVAL;
     }
 
-    nl_msg_put_string(options, OVS_PATCH_ATTR_PEER, peer);
+    free(dev->peer);
+    dev->peer = xstrdup(peer);
 
     return 0;
 }
 
 static int
-unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
-                     const struct nlattr *options, size_t options_len,
-                     struct smap *args)
+get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 {
-    static const struct nl_policy ovs_patch_policy[] = {
-        [OVS_PATCH_ATTR_PEER] = { .type = NL_A_STRING,
-                               .max_len = IFNAMSIZ,
-                               .optional = false }
-    };
-
-    struct nlattr *a[ARRAY_SIZE(ovs_patch_policy)];
-    struct ofpbuf buf;
-
-    ofpbuf_use_const(&buf, options, options_len);
-    if (!nl_policy_parse(&buf, 0, ovs_patch_policy,
-                         a, ARRAY_SIZE(ovs_patch_policy))) {
-        return EINVAL;
-    }
-
-    smap_add(args, "peer", nl_attr_get_string(a[OVS_PATCH_ATTR_PEER]));
+    struct netdev_dev_vport *dev = netdev_vport_get_dev(netdev);
+    memcpy(stats, &dev->stats, sizeof *stats);
     return 0;
 }
 \f
-#define VPORT_FUNCTIONS(GET_TUNNEL_CONFIG, GET_STATUS)      \
+#define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG,             \
+                        GET_TUNNEL_CONFIG, GET_STATUS)      \
     NULL,                                                   \
     netdev_vport_run,                                       \
     netdev_vport_wait,                                      \
                                                             \
     netdev_vport_create,                                    \
     netdev_vport_destroy,                                   \
-    netdev_vport_get_config,                                \
-    netdev_vport_set_config,                                \
+    GET_CONFIG,                                             \
+    SET_CONFIG,                                             \
     GET_TUNNEL_CONFIG,                                      \
                                                             \
     netdev_vport_open,                                      \
@@ -869,7 +616,7 @@ unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
     NULL,                       /* get_carrier */           \
     NULL,                       /* get_carrier_resets */    \
     NULL,                       /* get_miimon */            \
-    netdev_vport_get_stats,                                 \
+    get_stats,                                              \
     NULL,                       /* set_stats */             \
                                                             \
     NULL,                       /* get_features */          \
@@ -899,26 +646,23 @@ unparse_patch_config(const char *name OVS_UNUSED, const char *type OVS_UNUSED,
                                                             \
     netdev_vport_change_seq
 
-#define TUNNEL_CLASS(NAME, VPORT_TYPE)                      \
-    { VPORT_TYPE,                                           \
-        { NAME, VPORT_FUNCTIONS(get_netdev_tunnel_config,   \
-                                tunnel_get_status) },       \
-            parse_tunnel_config, unparse_tunnel_config }
+#define TUNNEL_CLASS(NAME, DPIF_PORT)                       \
+    { DPIF_PORT,                                            \
+        { NAME, VPORT_FUNCTIONS(get_tunnel_config,          \
+                                set_tunnel_config,          \
+                                get_netdev_tunnel_config,   \
+                                tunnel_get_status) }}
 
 void
-netdev_vport_register(void)
+netdev_vport_tunnel_register(void)
 {
     static const struct vport_class vport_classes[] = {
-        TUNNEL_CLASS("gre", OVS_VPORT_TYPE_GRE),
-        TUNNEL_CLASS("ipsec_gre", OVS_VPORT_TYPE_GRE),
-        TUNNEL_CLASS("gre64", OVS_VPORT_TYPE_GRE64),
-        TUNNEL_CLASS("ipsec_gre64", OVS_VPORT_TYPE_GRE64),
-        TUNNEL_CLASS("capwap", OVS_VPORT_TYPE_CAPWAP),
-        TUNNEL_CLASS("vxlan", OVS_VPORT_TYPE_VXLAN),
-
-        { OVS_VPORT_TYPE_PATCH,
-          { "patch", VPORT_FUNCTIONS(NULL, NULL) },
-          parse_patch_config, unparse_patch_config }
+        TUNNEL_CLASS("gre", "gre_system"),
+        TUNNEL_CLASS("ipsec_gre", "gre_system"),
+        TUNNEL_CLASS("gre64", "gre64_system"),
+        TUNNEL_CLASS("ipsec_gre64", "gre64_system"),
+        TUNNEL_CLASS("capwap", "capwap_system"),
+        TUNNEL_CLASS("vxlan", "vxlan_system")
     };
 
     int i;
@@ -927,3 +671,15 @@ netdev_vport_register(void)
         netdev_register_provider(&vport_classes[i].netdev_class);
     }
 }
+
+void
+netdev_vport_patch_register(void)
+{
+    static const struct vport_class patch_class =
+        { NULL,
+            { "patch", VPORT_FUNCTIONS(get_patch_config,
+                                       set_patch_config,
+                                       NULL,
+                                       NULL) }};
+    netdev_register_provider(&patch_class.netdev_class);
+}
index 31c1198..c907b0c 100644 (file)
 #define NETDEV_VPORT_H 1
 
 #include <stdbool.h>
-#include "openvswitch/types.h"
 
 struct dpif_linux_vport;
+struct dpif_flow_stats;
 struct netdev;
 struct netdev_stats;
 
-void netdev_vport_register(void);
+void netdev_vport_tunnel_register(void);
+void netdev_vport_patch_register(void);
 
-const struct ofpbuf *netdev_vport_get_options(const struct netdev *);
+bool netdev_vport_is_patch(const struct netdev *);
 
-enum ovs_vport_type netdev_vport_get_vport_type(const struct netdev *);
-const char *netdev_vport_get_netdev_type(const struct dpif_linux_vport *);
+const char *netdev_vport_patch_peer(const struct netdev *netdev);
 
-int netdev_vport_get_stats(const struct netdev *, struct netdev_stats *);
+void netdev_vport_inc_rx(const struct netdev *,
+                         const struct dpif_flow_stats *);
+void netdev_vport_inc_tx(const struct netdev *,
+                         const struct dpif_flow_stats *);
+
+const char *netdev_vport_get_dpif_port(const struct netdev *);
 
 #endif /* netdev-vport.h */
index 3c528a8..d358e40 100644 (file)
@@ -73,12 +73,13 @@ netdev_initialize(void)
         inited = true;
 
         fatal_signal_add_hook(close_all_netdevs, NULL, NULL, true);
+        netdev_vport_patch_register();
 
 #ifdef LINUX_DATAPATH
         netdev_register_provider(&netdev_linux_class);
         netdev_register_provider(&netdev_internal_class);
         netdev_register_provider(&netdev_tap_class);
-        netdev_vport_register();
+        netdev_vport_tunnel_register();
 #endif
 #ifdef __FreeBSD__
         netdev_register_provider(&netdev_tap_class);
@@ -1443,6 +1444,14 @@ netdev_get_type(const struct netdev *netdev)
     return netdev_get_dev(netdev)->netdev_class->type;
 }
 
+
+const char *
+netdev_get_type_from_name(const char *name)
+{
+    const struct netdev_dev *dev = netdev_dev_from_name(name);
+    return dev ? netdev_dev_get_type(dev) : NULL;
+}
+
 struct netdev_dev *
 netdev_get_dev(const struct netdev *netdev)
 {
index a544131..a691d70 100644 (file)
@@ -127,6 +127,7 @@ const struct netdev_tunnel_config *
 /* Basic properties. */
 const char *netdev_get_name(const struct netdev *);
 const char *netdev_get_type(const struct netdev *);
+const char *netdev_get_type_from_name(const char *);
 int netdev_get_mtu(const struct netdev *, int *mtup);
 int netdev_set_mtu(const struct netdev *, int mtu);
 int netdev_get_ifindex(const struct netdev *);
index 96a9523..7e48981 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1526,10 +1526,7 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
         memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
     }
 
-    if ((flow->dl_type == htons(ETH_TYPE_IP)
-         || flow->dl_type == htons(ETH_TYPE_IPV6))
-        && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
-
+    if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
         if (flow->nw_proto == IPPROTO_TCP) {
             struct ovs_key_tcp *tcp_key;
 
@@ -1790,8 +1787,7 @@ parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     }
 
     if (flow->nw_proto == IPPROTO_TCP
-        && (flow->dl_type == htons(ETH_TYPE_IP) ||
-            flow->dl_type == htons(ETH_TYPE_IPV6))
+        && is_ip_any(flow)
         && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
@@ -1802,8 +1798,7 @@ parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             flow->tp_dst = tcp_key->tcp_dst;
         }
     } else if (flow->nw_proto == IPPROTO_UDP
-               && (flow->dl_type == htons(ETH_TYPE_IP) ||
-                   flow->dl_type == htons(ETH_TYPE_IPV6))
+               && is_ip_any(flow)
                && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
@@ -2045,6 +2040,15 @@ odp_put_userspace_action(uint32_t pid, const union user_action_cookie *cookie,
 
     return cookie ? odp_actions->size - NLA_ALIGN(sizeof *cookie) : 0;
 }
+
+void
+odp_put_tunnel_action(const struct flow_tnl *tunnel,
+                      struct ofpbuf *odp_actions)
+{
+    size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
+    tun_key_to_attr(odp_actions, tunnel);
+    nl_msg_end_nested(odp_actions, offset);
+}
 \f
 /* The commit_odp_actions() function and its helpers. */
 
@@ -2057,8 +2061,14 @@ commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
     nl_msg_end_nested(odp_actions, offset);
 }
 
-static void
-commit_set_tunnel_action(const struct flow *flow, struct flow *base,
+/* If any of the flow key data that ODP actions can modify are different in
+ * 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
+ * 'odp_actions' that change the flow tunneling information in key from
+ * 'base->tunnel' into 'flow->tunnel', and then changes 'base->tunnel' in the
+ * same way.  In other words, operates the same as commit_odp_actions(), but
+ * only on tunneling information. */
+void
+commit_odp_tunnel_action(const struct flow *flow, struct flow *base,
                          struct ofpbuf *odp_actions)
 {
     if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
@@ -2068,11 +2078,7 @@ commit_set_tunnel_action(const struct flow *flow, struct flow *base,
 
     /* A valid IPV4_TUNNEL must have non-zero ip_dst. */
     if (flow->tunnel.ip_dst) {
-        size_t offset;
-
-        offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
-        tun_key_to_attr(odp_actions, &base->tunnel);
-        nl_msg_end_nested(odp_actions, offset);
+        odp_put_tunnel_action(&base->tunnel, odp_actions);
     } else {
         commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
                           &base->tunnel.tun_id, sizeof base->tunnel.tun_id);
@@ -2254,12 +2260,13 @@ commit_set_skb_mark_action(const struct flow *flow, struct flow *base,
 }
 /* If any of the flow key data that ODP actions can modify are different in
  * 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
- * key from 'base' into 'flow', and then changes 'base' the same way. */
+ * key from 'base' into 'flow', and then changes 'base' the same way.  Does not
+ * commit set_tunnel actions.  Users should call commit_odp_tunnel_action()
+ * in addition to this function if needed. */
 void
 commit_odp_actions(const struct flow *flow, struct flow *base,
                    struct ofpbuf *odp_actions)
 {
-    commit_set_tunnel_action(flow, base, odp_actions);
     commit_set_ether_addr_action(flow, base, odp_actions);
     commit_vlan_action(flow, base, odp_actions);
     commit_set_nw_action(flow, base, odp_actions);
index 9d0cc86..ccf6c2a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
 
 struct ds;
 struct flow;
+struct flow_tnl;
 struct nlattr;
 struct ofpbuf;
 struct simap;
@@ -113,6 +114,8 @@ enum odp_key_fitness odp_flow_key_to_flow(const struct nlattr *, size_t,
                                           struct flow *);
 const char *odp_key_fitness_to_string(enum odp_key_fitness);
 
+void commit_odp_tunnel_action(const struct flow *, struct flow *base,
+                              struct ofpbuf *odp_actions);
 void commit_odp_actions(const struct flow *, struct flow *base,
                         struct ofpbuf *odp_actions);
 \f
@@ -151,6 +154,8 @@ BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 8);
 size_t odp_put_userspace_action(uint32_t pid,
                                 const union user_action_cookie *,
                                 struct ofpbuf *odp_actions);
+void odp_put_tunnel_action(const struct flow_tnl *tunnel,
+                           struct ofpbuf *odp_actions);
 
 /* Reasons why a subfacet might not be fast-pathable. */
 enum slow_path_reason {
index 1d0ab85..6f35c12 100644 (file)
@@ -1149,6 +1149,10 @@ parse_ofp_exact_flow(struct flow *flow, const char *s)
         }
     }
 
+    if (!flow->in_port) {
+        flow->in_port = OFPP_NONE;
+    }
+
 exit:
     free(copy);
 
index 73dfcdc..fa73282 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -710,9 +710,7 @@ packet_set_udp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst)
 uint8_t
 packet_get_tcp_flags(const struct ofpbuf *packet, const struct flow *flow)
 {
-    if ((flow->dl_type == htons(ETH_TYPE_IP) ||
-         flow->dl_type == htons(ETH_TYPE_IPV6)) &&
-        flow->nw_proto == IPPROTO_TCP && packet->l7) {
+    if (is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP && packet->l7) {
         const struct tcp_header *tcp = packet->l4;
         return TCP_FLAGS(tcp->tcp_ctl);
     } else {
index 7e2d4e9..8dd3ebf 100644 (file)
 #include <stdint.h>
 #include <string.h>
 #include "compiler.h"
+#include "flow.h"
 #include "openvswitch/types.h"
 #include "random.h"
 #include "util.h"
 
 struct ofpbuf;
 struct ds;
-struct flow;
 
 bool dpid_from_string(const char *s, uint64_t *dpidp);
 
@@ -468,6 +468,12 @@ static inline bool ipv6_mask_is_exact(const struct in6_addr *mask) {
     return ipv6_addr_equals(mask, &in6addr_exact);
 }
 
+static inline bool is_ip_any(const struct flow *flow)
+{
+    return flow->dl_type == htons(ETH_TYPE_IP)
+        || flow->dl_type == htons(ETH_TYPE_IPV6);
+}
+
 void format_ipv6_addr(char *addr_str, const struct in6_addr *addr);
 void print_ipv6_addr(struct ds *string, const struct in6_addr *addr);
 void print_ipv6_masked(struct ds *string, const struct in6_addr *addr,
index 67ea86c..9b6cd86 100644 (file)
@@ -672,14 +672,6 @@ void
 rconn_add_monitor(struct rconn *rc, struct vconn *vconn)
 {
     if (rc->n_monitors < ARRAY_SIZE(rc->monitors)) {
-        int version = vconn_get_version(rc->vconn);
-
-        /* Override the allowed versions of the snoop vconn so that
-         * only the version of the controller connection is allowed.
-         * This is because the snoop will see the same messages as the
-         * controller */
-        vconn_set_allowed_versions(vconn, 1u << version);
-
         VLOG_INFO("new monitor connection from %s", vconn_get_name(vconn));
         rc->monitors[rc->n_monitors++] = vconn;
     } else {
index fafad28..a4a0991 100644 (file)
@@ -45,3 +45,8 @@ void
 route_table_run(void)
 {
 }
+
+void
+route_table_wait(void)
+{
+}
index f4e9a9e..e6058f3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -33,13 +33,18 @@ struct vconn {
     struct vconn_class *class;
     int state;
     int error;
-    uint32_t allowed_versions;
-    uint32_t peer_versions;
-    enum ofp_version version;
+
+    /* OpenFlow versions. */
+    uint32_t allowed_versions;  /* Bitmap of versions we will accept. */
+    uint32_t peer_versions;     /* Peer's bitmap of versions it will accept. */
+    enum ofp_version version;   /* Negotiated version (or 0). */
+    bool recv_any_version;      /* True to receive a message of any version. */
+
     ovs_be32 remote_ip;
     ovs_be16 remote_port;
     ovs_be32 local_ip;
     ovs_be16 local_port;
+
     char *name;
 };
 
index e0223cd..bf40b1b 100644 (file)
@@ -394,6 +394,27 @@ vconn_get_version(const struct vconn *vconn)
     return vconn->version ? vconn->version : -1;
 }
 
+/* By default, a vconn accepts only OpenFlow messages whose version matches the
+ * one negotiated for the connection.  A message received with a different
+ * version is an error that causes the vconn to drop the connection.
+ *
+ * This functions allows 'vconn' to accept messages with any OpenFlow version.
+ * This is useful in the special case where 'vconn' is used as an rconn
+ * "monitor" connection (see rconn_add_monitor()), that is, where 'vconn' is
+ * used as a target for mirroring OpenFlow messages for debugging and
+ * troubleshooting.
+ *
+ * This function should be called after a successful vconn_open() or
+ * pvconn_accept() but before the connection completes, that is, before
+ * vconn_connect() returns success.  Otherwise, messages that arrive on 'vconn'
+ * beforehand with an unexpected version will the vconn to drop the
+ * connection. */
+void
+vconn_set_recv_any_version(struct vconn *vconn)
+{
+    vconn->recv_any_version = true;
+}
+
 static void
 vcs_connecting(struct vconn *vconn)
 {
@@ -600,7 +621,7 @@ vconn_recv(struct vconn *vconn, struct ofpbuf **msgp)
     if (!retval) {
         retval = do_recv(vconn, &msg);
     }
-    if (!retval) {
+    if (!retval && !vconn->recv_any_version) {
         const struct ofp_header *oh = msg->data;
         if (oh->version != vconn->version) {
             enum ofptype type;
@@ -1091,17 +1112,13 @@ void
 vconn_init(struct vconn *vconn, struct vconn_class *class, int connect_status,
            const char *name, uint32_t allowed_versions)
 {
+    memset(vconn, 0, sizeof *vconn);
     vconn->class = class;
     vconn->state = (connect_status == EAGAIN ? VCS_CONNECTING
                     : !connect_status ? VCS_SEND_HELLO
                     : VCS_DISCONNECTED);
     vconn->error = connect_status;
-    vconn->version = 0;
     vconn->allowed_versions = allowed_versions;
-    vconn->remote_ip = 0;
-    vconn->remote_port = 0;
-    vconn->local_ip = 0;
-    vconn->local_port = 0;
     vconn->name = xstrdup(name);
     ovs_assert(vconn->state != VCS_CONNECTING || class->connect);
 }
index 81bdcc9..b15388c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -38,14 +38,18 @@ int vconn_open(const char *name, uint32_t allowed_versions, uint8_t dscp,
                struct vconn **vconnp);
 void vconn_close(struct vconn *);
 const char *vconn_get_name(const struct vconn *);
+
 uint32_t vconn_get_allowed_versions(const struct vconn *vconn);
 void vconn_set_allowed_versions(struct vconn *vconn,
                                 uint32_t allowed_versions);
+int vconn_get_version(const struct vconn *);
+void vconn_set_recv_any_version(struct vconn *);
+
 ovs_be32 vconn_get_remote_ip(const struct vconn *);
 ovs_be16 vconn_get_remote_port(const struct vconn *);
 ovs_be32 vconn_get_local_ip(const struct vconn *);
 ovs_be16 vconn_get_local_port(const struct vconn *);
-int vconn_get_version(const struct vconn *);
+
 int vconn_connect(struct vconn *);
 int vconn_recv(struct vconn *, struct ofpbuf **);
 int vconn_send(struct vconn *, struct ofpbuf *);
index 6ca05cd..b0f1a82 100644 (file)
@@ -345,7 +345,7 @@ worker_reply_iovec(const struct iovec *iovs, size_t n_iovs,
          * to avoid missing log messages. */
         VLOG_INFO("send failed (%s)", strerror(error));
     } else if (error) {
-        VLOG_ABORT("send failed (%s)", strerror(error));
+        VLOG_FATAL("send failed (%s)", strerror(error));
     }
 
     free(all_iovs);
@@ -379,7 +379,7 @@ worker_main(int fd)
             /* Main process closed the IPC socket.  Exit cleanly. */
             break;
         } else if (error != EAGAIN) {
-            VLOG_ABORT("RPC receive failed (%s)", strerror(error));
+            VLOG_FATAL("RPC receive failed (%s)", strerror(error));
         }
 
         poll_fd_wait(server_sock, POLLIN);
index 9088292..69f014f 100644 (file)
@@ -29,6 +29,8 @@ ofproto_libofproto_a_SOURCES = \
        ofproto/pktbuf.c \
        ofproto/pktbuf.h \
        ofproto/pinsched.c \
-       ofproto/pinsched.h
+       ofproto/pinsched.h \
+       ofproto/tunnel.c \
+       ofproto/tunnel.h
 
 MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man
index 2c216fe..efd47b3 100644 (file)
@@ -36,6 +36,7 @@
 #include "mac-learning.h"
 #include "meta-flow.h"
 #include "multipath.h"
+#include "netdev-vport.h"
 #include "netdev.h"
 #include "netlink.h"
 #include "nx-match.h"
@@ -51,6 +52,7 @@
 #include "simap.h"
 #include "smap.h"
 #include "timer.h"
+#include "tunnel.h"
 #include "unaligned.h"
 #include "unixctl.h"
 #include "vlan-bitmap.h"
@@ -295,6 +297,8 @@ static void xlate_actions(struct action_xlate_ctx *,
 static void xlate_actions_for_side_effects(struct action_xlate_ctx *,
                                            const struct ofpact *ofpacts,
                                            size_t ofpacts_len);
+static void xlate_table_action(struct action_xlate_ctx *, uint16_t in_port,
+                               uint8_t table_id, bool may_packet_in);
 
 static size_t put_userspace_action(const struct ofproto_dpif *,
                                    struct ofpbuf *odp_actions,
@@ -509,6 +513,7 @@ struct ofport_dpif {
     uint32_t bond_stable_id;    /* stable_id to use as bond slave, or 0. */
     bool may_enable;            /* May be enabled in bonds. */
     long long int carrier_seq;  /* Carrier status changes. */
+    struct tnl_port *tnl_port;  /* Tunnel handle, or null. */
 
     /* Spanning tree. */
     struct stp_port *stp_port;  /* Spanning Tree Protocol, if any. */
@@ -612,6 +617,15 @@ COVERAGE_DEFINE(rev_port_toggled);
 COVERAGE_DEFINE(rev_flow_table);
 COVERAGE_DEFINE(rev_inconsistency);
 
+/* Drop keys are odp flow keys which have drop flows installed in the kernel.
+ * These are datapath flows which have no associated ofproto, if they did we
+ * would use facets. */
+struct drop_key {
+    struct hmap_node hmap_node;
+    struct nlattr *key;
+    size_t key_len;
+};
+
 /* All datapaths of a given type share a single dpif backer instance. */
 struct dpif_backer {
     char *type;
@@ -619,11 +633,20 @@ struct dpif_backer {
     struct dpif *dpif;
     struct timer next_expiration;
     struct hmap odp_to_ofport_map; /* ODP port to ofport mapping. */
+
+    struct sset tnl_backers;       /* Set of dpif ports backing tunnels. */
+
+    /* Facet revalidation flags applying to facets which use this backer. */
+    enum revalidate_reason need_revalidate; /* Revalidate every facet. */
+    struct tag_set revalidate_set; /* Revalidate only matching facets. */
+
+    struct hmap drop_keys; /* Set of dropped odp keys. */
 };
 
 /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
 static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
 
+static void drop_key_clear(struct dpif_backer *);
 static struct ofport_dpif *
 odp_port_to_ofport(const struct dpif_backer *, uint32_t odp_port);
 
@@ -655,8 +678,6 @@ struct ofproto_dpif {
 
     /* Revalidation. */
     struct table_dpif tables[N_TABLES];
-    enum revalidate_reason need_revalidate;
-    struct tag_set revalidate_set;
 
     /* Support for debugging async flow mods. */
     struct list completions;
@@ -674,7 +695,8 @@ struct ofproto_dpif {
     struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
 
     /* Ports. */
-    struct sset ports;             /* Set of port names. */
+    struct sset ports;             /* Set of standard port names. */
+    struct sset ghost_ports;       /* Ports with no datapath port. */
     struct sset port_poll_set;     /* Queued names for port_poll() reply. */
     int port_poll_errno;           /* Last errno for port_poll() reply. */
 };
@@ -702,6 +724,7 @@ static struct ofport_dpif *get_odp_port(const struct ofproto_dpif *,
 static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
                           const struct ofpbuf *, ovs_be16 initial_tci,
                           struct ds *);
+static bool may_dpif_port_del(struct ofport_dpif *);
 
 /* Packet processing. */
 static void update_learning_table(struct ofproto_dpif *,
@@ -824,6 +847,46 @@ type_run(const char *type)
 
     dpif_run(backer->dpif);
 
+    if (backer->need_revalidate
+        || !tag_set_is_empty(&backer->revalidate_set)) {
+        struct tag_set revalidate_set = backer->revalidate_set;
+        bool need_revalidate = backer->need_revalidate;
+        struct ofproto_dpif *ofproto;
+
+        switch (backer->need_revalidate) {
+        case REV_RECONFIGURE:   COVERAGE_INC(rev_reconfigure);   break;
+        case REV_STP:           COVERAGE_INC(rev_stp);           break;
+        case REV_PORT_TOGGLED:  COVERAGE_INC(rev_port_toggled);  break;
+        case REV_FLOW_TABLE:    COVERAGE_INC(rev_flow_table);    break;
+        case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
+        }
+
+        if (backer->need_revalidate) {
+            /* Clear the drop_keys in case we should now be accepting some
+             * formerly dropped flows. */
+            drop_key_clear(backer);
+        }
+
+        HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+            struct facet *facet;
+
+            if (ofproto->backer != backer) {
+                continue;
+            }
+
+            /* Clear the revalidation flags. */
+            tag_set_init(&backer->revalidate_set);
+            backer->need_revalidate = 0;
+
+            HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+                if (need_revalidate
+                    || tag_set_intersects(&revalidate_set, facet->tags)) {
+                    facet_revalidate(facet);
+                }
+            }
+        }
+    }
+
     if (timer_expired(&backer->next_expiration)) {
         int delay = expire(backer);
         timer_set_duration(&backer->next_expiration, delay);
@@ -839,6 +902,13 @@ type_run(const char *type)
             goto next;
         }
 
+        HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node,
+                       &all_ofproto_dpifs) {
+            if (sset_contains(&ofproto->backer->tnl_backers, devname)) {
+                goto next;
+            }
+        }
+
         ofproto = lookup_ofproto_dpif_by_port_name(devname);
         if (dpif_port_query_by_name(backer->dpif, devname, &port)) {
             /* The port was removed.  If we know the datapath,
@@ -956,6 +1026,10 @@ close_dpif_backer(struct dpif_backer *backer)
         return;
     }
 
+    drop_key_clear(backer);
+    hmap_destroy(&backer->drop_keys);
+
+    sset_destroy(&backer->tnl_backers);
     hmap_destroy(&backer->odp_to_ofport_map);
     node = shash_find(&all_dpif_backers, backer->type);
     free(backer->type);
@@ -1029,7 +1103,11 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp)
     backer->type = xstrdup(type);
     backer->refcount = 1;
     hmap_init(&backer->odp_to_ofport_map);
+    hmap_init(&backer->drop_keys);
     timer_set_duration(&backer->next_expiration, 1000);
+    backer->need_revalidate = 0;
+    sset_init(&backer->tnl_backers);
+    tag_set_init(&backer->revalidate_set);
     *backerp = backer;
 
     dpif_flow_flush(backer->dpif);
@@ -1107,8 +1185,6 @@ construct(struct ofproto *ofproto_)
         table->other_table = NULL;
         table->basis = random_uint32();
     }
-    ofproto->need_revalidate = 0;
-    tag_set_init(&ofproto->revalidate_set);
 
     list_init(&ofproto->completions);
 
@@ -1121,6 +1197,7 @@ construct(struct ofproto *ofproto_)
     hmap_init(&ofproto->realdev_vid_map);
 
     sset_init(&ofproto->ports);
+    sset_init(&ofproto->ghost_ports);
     sset_init(&ofproto->port_poll_set);
     ofproto->port_poll_errno = 0;
 
@@ -1265,6 +1342,7 @@ destruct(struct ofproto *ofproto_)
     hmap_destroy(&ofproto->realdev_vid_map);
 
     sset_destroy(&ofproto->ports);
+    sset_destroy(&ofproto->ghost_ports);
     sset_destroy(&ofproto->port_poll_set);
 
     close_dpif_backer(ofproto->backer);
@@ -1317,44 +1395,19 @@ run(struct ofproto *ofproto_)
     }
 
     stp_run(ofproto);
-    mac_learning_run(ofproto->ml, &ofproto->revalidate_set);
-
-    /* Now revalidate if there's anything to do. */
-    if (ofproto->need_revalidate
-        || !tag_set_is_empty(&ofproto->revalidate_set)) {
-        struct tag_set revalidate_set = ofproto->revalidate_set;
-        bool revalidate_all = ofproto->need_revalidate;
-        struct facet *facet;
-
-        switch (ofproto->need_revalidate) {
-        case REV_RECONFIGURE:   COVERAGE_INC(rev_reconfigure);   break;
-        case REV_STP:           COVERAGE_INC(rev_stp);           break;
-        case REV_PORT_TOGGLED:  COVERAGE_INC(rev_port_toggled);  break;
-        case REV_FLOW_TABLE:    COVERAGE_INC(rev_flow_table);    break;
-        case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
-        }
-
-        /* Clear the revalidation flags. */
-        tag_set_init(&ofproto->revalidate_set);
-        ofproto->need_revalidate = 0;
-
-        HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
-            if (revalidate_all
-                || tag_set_intersects(&revalidate_set, facet->tags)) {
-                facet_revalidate(facet);
-            }
-        }
-    }
+    mac_learning_run(ofproto->ml, &ofproto->backer->revalidate_set);
 
     /* Check the consistency of a random facet, to aid debugging. */
-    if (!hmap_is_empty(&ofproto->facets) && !ofproto->need_revalidate) {
+    if (!hmap_is_empty(&ofproto->facets)
+        && !ofproto->backer->need_revalidate) {
         struct facet *facet;
 
         facet = CONTAINER_OF(hmap_random_node(&ofproto->facets),
                              struct facet, hmap_node);
-        if (!tag_set_intersects(&ofproto->revalidate_set, facet->tags)) {
+        if (!tag_set_intersects(&ofproto->backer->revalidate_set,
+                                facet->tags)) {
             if (!facet_check_consistency(facet)) {
-                ofproto->need_revalidate = REV_INCONSISTENCY;
+                ofproto->backer->need_revalidate = REV_INCONSISTENCY;
             }
         }
     }
@@ -1396,7 +1449,7 @@ wait(struct ofproto *ofproto_)
     if (ofproto->sflow) {
         dpif_sflow_wait(ofproto->sflow);
     }
-    if (!tag_set_is_empty(&ofproto->revalidate_set)) {
+    if (!tag_set_is_empty(&ofproto->backer->revalidate_set)) {
         poll_immediate_wake();
     }
     HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
@@ -1410,7 +1463,7 @@ wait(struct ofproto *ofproto_)
     }
     mac_learning_wait(ofproto->ml);
     stp_wait(ofproto);
-    if (ofproto->need_revalidate) {
+    if (ofproto->backer->need_revalidate) {
         /* Shouldn't happen, but if it does just go around again. */
         VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
         poll_immediate_wake();
@@ -1508,23 +1561,31 @@ port_construct(struct ofport *port_)
 {
     struct ofport_dpif *port = ofport_dpif_cast(port_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+    const struct netdev *netdev = port->up.netdev;
     struct dpif_port dpif_port;
     int error;
 
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     port->bundle = NULL;
     port->cfm = NULL;
     port->tag = tag_create_random();
     port->may_enable = true;
     port->stp_port = NULL;
     port->stp_state = STP_DISABLED;
+    port->tnl_port = NULL;
     hmap_init(&port->priorities);
     port->realdev_ofp_port = 0;
     port->vlandev_vid = 0;
-    port->carrier_seq = netdev_get_carrier_resets(port->up.netdev);
+    port->carrier_seq = netdev_get_carrier_resets(netdev);
+
+    if (netdev_vport_is_patch(netdev)) {
+        /* XXX By bailing out here, we don't do required sFlow work. */
+        port->odp_port = OVSP_NONE;
+        return 0;
+    }
 
     error = dpif_port_query_by_name(ofproto->backer->dpif,
-                                    netdev_get_name(port->up.netdev),
+                                    netdev_vport_get_dpif_port(netdev),
                                     &dpif_port);
     if (error) {
         return error;
@@ -1532,16 +1593,20 @@ port_construct(struct ofport *port_)
 
     port->odp_port = dpif_port.port_no;
 
-    /* Sanity-check that a mapping doesn't already exist.  This
-     * shouldn't happen. */
-    if (odp_port_to_ofp_port(ofproto, port->odp_port) != OFPP_NONE) {
-        VLOG_ERR("port %s already has an OpenFlow port number\n",
-                 dpif_port.name);
-        return EBUSY;
-    }
+    if (netdev_get_tunnel_config(netdev)) {
+        port->tnl_port = tnl_port_add(&port->up, port->odp_port);
+    } else {
+        /* Sanity-check that a mapping doesn't already exist.  This
+         * shouldn't happen for non-tunnel ports. */
+        if (odp_port_to_ofp_port(ofproto, port->odp_port) != OFPP_NONE) {
+            VLOG_ERR("port %s already has an OpenFlow port number",
+                     dpif_port.name);
+            return EBUSY;
+        }
 
-    hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
-                hash_int(port->odp_port, 0));
+        hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node,
+                    hash_int(port->odp_port, 0));
+    }
 
     if (ofproto->sflow) {
         dpif_sflow_add_port(ofproto->sflow, port_, port->odp_port);
@@ -1555,19 +1620,27 @@ port_destruct(struct ofport *port_)
 {
     struct ofport_dpif *port = ofport_dpif_cast(port_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+    const char *dp_port_name = netdev_vport_get_dpif_port(port->up.netdev);
     const char *devname = netdev_get_name(port->up.netdev);
 
-    if (dpif_port_exists(ofproto->backer->dpif, devname)) {
+    if (dpif_port_exists(ofproto->backer->dpif, dp_port_name)
+        && may_dpif_port_del(port)) {
         /* The underlying device is still there, so delete it.  This
          * happens when the ofproto is being destroyed, since the caller
          * assumes that removal of attached ports will happen as part of
          * destruction. */
         dpif_port_del(ofproto->backer->dpif, port->odp_port);
+        sset_find_and_delete(&ofproto->backer->tnl_backers, dp_port_name);
     }
 
+    if (port->odp_port != OVSP_NONE && !port->tnl_port) {
+        hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
+    }
+
+    tnl_port_del(port->tnl_port);
     sset_find_and_delete(&ofproto->ports, devname);
-    hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    sset_find_and_delete(&ofproto->ghost_ports, devname);
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     bundle_remove(port_);
     set_cfm(port_, NULL);
     if (ofproto->sflow) {
@@ -1598,7 +1671,7 @@ port_reconfigured(struct ofport *port_, enum ofputil_port_config old_config)
     if (changed & (OFPUTIL_PC_NO_RECV | OFPUTIL_PC_NO_RECV_STP |
                    OFPUTIL_PC_NO_FWD | OFPUTIL_PC_NO_FLOOD |
                    OFPUTIL_PC_NO_PACKET_IN)) {
-        ofproto->need_revalidate = REV_RECONFIGURE;
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
 
         if (changed & OFPUTIL_PC_NO_FLOOD && port->bundle) {
             bundle_update(port->bundle);
@@ -1621,13 +1694,13 @@ set_sflow(struct ofproto *ofproto_,
             HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
                 dpif_sflow_add_port(ds, &ofport->up, ofport->odp_port);
             }
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
         }
         dpif_sflow_set_options(ds, sflow_options);
     } else {
         if (ds) {
             dpif_sflow_destroy(ds);
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
             ofproto->sflow = NULL;
         }
     }
@@ -1647,7 +1720,7 @@ set_cfm(struct ofport *ofport_, const struct cfm_settings *s)
             struct ofproto_dpif *ofproto;
 
             ofproto = ofproto_dpif_cast(ofport->up.ofproto);
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
             ofport->cfm = cfm_create(netdev_get_name(ofport->up.netdev));
         }
 
@@ -1735,7 +1808,7 @@ set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s)
 
     /* Only revalidate flows if the configuration changed. */
     if (!s != !ofproto->stp) {
-        ofproto->need_revalidate = REV_RECONFIGURE;
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
     }
 
     if (s) {
@@ -1803,12 +1876,13 @@ update_stp_port_state(struct ofport_dpif *ofport)
         if (stp_learn_in_state(ofport->stp_state)
                 != stp_learn_in_state(state)) {
             /* xxx Learning action flows should also be flushed. */
-            mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+            mac_learning_flush(ofproto->ml,
+                               &ofproto->backer->revalidate_set);
         }
         fwd_change = stp_forward_in_state(ofport->stp_state)
                         != stp_forward_in_state(state);
 
-        ofproto->need_revalidate = REV_STP;
+        ofproto->backer->need_revalidate = REV_STP;
         ofport->stp_state = state;
         ofport->stp_state_entered = time_msec();
 
@@ -1908,7 +1982,7 @@ stp_run(struct ofproto_dpif *ofproto)
         }
 
         if (stp_check_and_reset_fdb_flush(ofproto->stp)) {
-            mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+            mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
         }
     }
 }
@@ -2006,12 +2080,12 @@ set_queues(struct ofport *ofport_,
             pdscp = xmalloc(sizeof *pdscp);
             pdscp->priority = priority;
             pdscp->dscp = dscp;
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
         }
 
         if (pdscp->dscp != dscp) {
             pdscp->dscp = dscp;
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
         }
 
         hmap_insert(&new, &pdscp->hmap_node, hash_int(pdscp->priority, 0));
@@ -2019,7 +2093,7 @@ set_queues(struct ofport *ofport_,
 
     if (!hmap_is_empty(&ofport->priorities)) {
         ofport_clear_priorities(ofport);
-        ofproto->need_revalidate = REV_RECONFIGURE;
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
     }
 
     hmap_swap(&new, &ofport->priorities);
@@ -2046,7 +2120,7 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos)
     struct mac_learning *ml = ofproto->ml;
     struct mac_entry *mac, *next_mac;
 
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
         if (mac->port.p == bundle) {
             if (all_ofprotos) {
@@ -2059,7 +2133,6 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos)
                         e = mac_learning_lookup(o->ml, mac->mac, mac->vlan,
                                                 NULL);
                         if (e) {
-                            tag_set_add(&o->revalidate_set, e->tag);
                             mac_learning_expire(o->ml, e);
                         }
                     }
@@ -2123,7 +2196,7 @@ bundle_del_port(struct ofport_dpif *port)
 {
     struct ofbundle *bundle = port->bundle;
 
-    bundle->ofproto->need_revalidate = REV_RECONFIGURE;
+    bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE;
 
     list_remove(&port->bundle_node);
     port->bundle = NULL;
@@ -2151,7 +2224,7 @@ bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
     }
 
     if (port->bundle != bundle) {
-        bundle->ofproto->need_revalidate = REV_RECONFIGURE;
+        bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE;
         if (port->bundle) {
             bundle_del_port(port);
         }
@@ -2164,7 +2237,7 @@ bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
         }
     }
     if (lacp) {
-        port->bundle->ofproto->need_revalidate = REV_RECONFIGURE;
+        bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE;
         lacp_slave_register(bundle->lacp, port, lacp);
     }
 
@@ -2192,7 +2265,7 @@ bundle_destroy(struct ofbundle *bundle)
                 mirror_destroy(m);
             } else if (hmapx_find_and_delete(&m->srcs, bundle)
                        || hmapx_find_and_delete(&m->dsts, bundle)) {
-                ofproto->need_revalidate = REV_RECONFIGURE;
+                ofproto->backer->need_revalidate = REV_RECONFIGURE;
             }
         }
     }
@@ -2264,7 +2337,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
     /* LACP. */
     if (s->lacp) {
         if (!bundle->lacp) {
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
             bundle->lacp = lacp_create();
         }
         lacp_configure(bundle->lacp, s->lacp);
@@ -2370,11 +2443,11 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         bundle->ofproto->has_bonded_bundles = true;
         if (bundle->bond) {
             if (bond_reconfigure(bundle->bond, s->bond)) {
-                ofproto->need_revalidate = REV_RECONFIGURE;
+                ofproto->backer->need_revalidate = REV_RECONFIGURE;
             }
         } else {
             bundle->bond = bond_create(s->bond);
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
         }
 
         LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
@@ -2494,7 +2567,7 @@ bundle_run(struct ofbundle *bundle)
             bond_slave_set_may_enable(bundle->bond, port, port->may_enable);
         }
 
-        bond_run(bundle->bond, &bundle->ofproto->revalidate_set,
+        bond_run(bundle->bond, &bundle->ofproto->backer->revalidate_set,
                  lacp_status(bundle->lacp));
         if (bond_should_send_learning_packets(bundle->bond)) {
             bundle_send_learning_packets(bundle);
@@ -2679,9 +2752,10 @@ mirror_set(struct ofproto *ofproto_, void *aux,
         }
     }
 
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     ofproto->has_mirrors = true;
-    mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+    mac_learning_flush(ofproto->ml,
+                       &ofproto->backer->revalidate_set);
     mirror_update_dups(ofproto);
 
     return 0;
@@ -2700,8 +2774,8 @@ mirror_destroy(struct ofmirror *mirror)
     }
 
     ofproto = mirror->ofproto;
-    ofproto->need_revalidate = REV_RECONFIGURE;
-    mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
+    mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
 
     mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
     HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
@@ -2752,7 +2826,7 @@ set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     if (mac_learning_set_flood_vlans(ofproto->ml, flood_vlans)) {
-        mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+        mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
     }
     return 0;
 }
@@ -2769,7 +2843,7 @@ static void
 forward_bpdu_changed(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
 }
 
 static void
@@ -2807,6 +2881,28 @@ ofproto_port_from_dpif_port(struct ofproto_dpif *ofproto,
     ofproto_port->ofp_port = odp_port_to_ofp_port(ofproto, dpif_port->port_no);
 }
 
+static struct ofport_dpif *
+ofport_get_peer(const struct ofport_dpif *ofport_dpif)
+{
+    const struct ofproto_dpif *ofproto;
+    const char *peer;
+
+    peer = netdev_vport_patch_peer(ofport_dpif->up.netdev);
+    if (!peer) {
+        return NULL;
+    }
+
+    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        struct ofport *ofport;
+
+        ofport = shash_find_data(&ofproto->up.port_by_name, peer);
+        if (ofport && ofport->ofproto->ofproto_class == &ofproto_dpif_class) {
+            return ofport_dpif_cast(ofport);
+        }
+    }
+    return NULL;
+}
+
 static void
 port_run_fast(struct ofport_dpif *ofport)
 {
@@ -2830,6 +2926,13 @@ port_run(struct ofport_dpif *ofport)
     ofport->carrier_seq = carrier_seq;
 
     port_run_fast(ofport);
+
+    if (ofport->tnl_port
+        && tnl_port_reconfigure(&ofport->up, ofport->odp_port,
+                                &ofport->tnl_port)) {
+        ofproto_dpif_cast(ofport->up.ofproto)->backer->need_revalidate = true;
+    }
+
     if (ofport->cfm) {
         int cfm_opup = cfm_get_opup(ofport->cfm);
 
@@ -2852,7 +2955,7 @@ port_run(struct ofport_dpif *ofport)
         struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
 
         if (ofproto->has_bundle_action) {
-            ofproto->need_revalidate = REV_PORT_TOGGLED;
+            ofproto->backer->need_revalidate = REV_PORT_TOGGLED;
         }
     }
 
@@ -2875,6 +2978,24 @@ port_query_by_name(const struct ofproto *ofproto_, const char *devname,
     struct dpif_port dpif_port;
     int error;
 
+    if (sset_contains(&ofproto->ghost_ports, devname)) {
+        const char *type = netdev_get_type_from_name(devname);
+
+        /* We may be called before ofproto->up.port_by_name is populated with
+         * the appropriate ofport.  For this reason, we must get the name and
+         * type from the netdev layer directly. */
+        if (type) {
+            const struct ofport *ofport;
+
+            ofport = shash_find_data(&ofproto->up.port_by_name, devname);
+            ofproto_port->ofp_port = ofport ? ofport->ofp_port : OFPP_NONE;
+            ofproto_port->name = xstrdup(devname);
+            ofproto_port->type = xstrdup(type);
+            return 0;
+        }
+        return ENODEV;
+    }
+
     if (!sset_contains(&ofproto->ports, devname)) {
         return ENODEV;
     }
@@ -2890,33 +3011,91 @@ static int
 port_add(struct ofproto *ofproto_, struct netdev *netdev)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    uint32_t odp_port = UINT32_MAX;
-    int error;
+    const char *dp_port_name = netdev_vport_get_dpif_port(netdev);
+    const char *devname = netdev_get_name(netdev);
 
-    error = dpif_port_add(ofproto->backer->dpif, netdev, &odp_port);
-    if (!error) {
-        sset_add(&ofproto->ports, netdev_get_name(netdev));
+    if (netdev_vport_is_patch(netdev)) {
+        sset_add(&ofproto->ghost_ports, netdev_get_name(netdev));
+        return 0;
     }
-    return error;
+
+    if (!dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
+        int error = dpif_port_add(ofproto->backer->dpif, netdev, NULL);
+        if (error) {
+            return error;
+        }
+    }
+
+    if (netdev_get_tunnel_config(netdev)) {
+        sset_add(&ofproto->ghost_ports, devname);
+        sset_add(&ofproto->backer->tnl_backers, dp_port_name);
+    } else {
+        sset_add(&ofproto->ports, devname);
+    }
+    return 0;
+}
+
+/* Returns true if the odp_port backing 'ofport' may be deleted from the
+ * datapath. In most cases, this function simply returns true. However, for
+ * tunnels it's possible that multiple ofports use the same odp_port, in which
+ * case we need to keep the odp_port backer around until the last ofport is
+ * deleted. */
+static bool
+may_dpif_port_del(struct ofport_dpif *ofport)
+{
+    struct dpif_backer *backer = ofproto_dpif_cast(ofport->up.ofproto)->backer;
+    struct ofproto_dpif *ofproto_iter;
+
+    if (!ofport->tnl_port) {
+        return true;
+    }
+
+    HMAP_FOR_EACH (ofproto_iter, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        struct ofport_dpif *iter;
+
+        if (backer != ofproto_iter->backer) {
+            continue;
+        }
+
+        HMAP_FOR_EACH (iter, up.hmap_node, &ofproto_iter->up.ports) {
+            if (ofport == iter) {
+                continue;
+            }
+
+            if (!strcmp(netdev_vport_get_dpif_port(ofport->up.netdev),
+                        netdev_vport_get_dpif_port(iter->up.netdev))) {
+                return false;
+            }
+        }
+    }
+
+    return true;
 }
 
 static int
 port_del(struct ofproto *ofproto_, uint16_t ofp_port)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    uint32_t odp_port = ofp_port_to_odp_port(ofproto, ofp_port);
+    struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
     int error = 0;
 
-    if (odp_port != OFPP_NONE) {
-        error = dpif_port_del(ofproto->backer->dpif, odp_port);
+    if (!ofport) {
+        return 0;
     }
-    if (!error) {
-        struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
-        if (ofport) {
+
+    sset_find_and_delete(&ofproto->ghost_ports,
+                         netdev_get_name(ofport->up.netdev));
+    if (may_dpif_port_del(ofport)) {
+        error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port);
+        if (!error) {
+            const char *dpif_port;
+
             /* The caller is going to close ofport->up.netdev.  If this is a
              * bonded port, then the bond is using that netdev, so remove it
              * from the bond.  The client will need to reconfigure everything
              * after deleting ports, so then the slave will get re-added. */
+            dpif_port = netdev_vport_get_dpif_port(ofport->up.netdev);
+            sset_find_and_delete(&ofproto->backer->tnl_backers, dpif_port);
             bundle_remove(&ofport->up);
         }
     }
@@ -2983,29 +3162,27 @@ ofproto_update_local_port_stats(const struct ofproto *ofproto_,
 struct port_dump_state {
     uint32_t bucket;
     uint32_t offset;
+    bool ghost;
 };
 
 static int
 port_dump_start(const struct ofproto *ofproto_ OVS_UNUSED, void **statep)
 {
-    struct port_dump_state *state;
-
-    *statep = state = xmalloc(sizeof *state);
-    state->bucket = 0;
-    state->offset = 0;
+    *statep = xzalloc(sizeof(struct port_dump_state));
     return 0;
 }
 
 static int
-port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
+port_dump_next(const struct ofproto *ofproto_, void *state_,
                struct ofproto_port *port)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct port_dump_state *state = state_;
+    const struct sset *sset;
     struct sset_node *node;
 
-    while ((node = sset_at_position(&ofproto->ports, &state->bucket,
-                               &state->offset))) {
+    sset = state->ghost ? &ofproto->ghost_ports : &ofproto->ports;
+    while ((node = sset_at_position(sset, &state->bucket, &state->offset))) {
         int error;
 
         error = port_query_by_name(ofproto_, node->name, port);
@@ -3014,6 +3191,13 @@ port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
         }
     }
 
+    if (!state->ghost) {
+        state->ghost = true;
+        state->bucket = 0;
+        state->offset = 0;
+        return port_dump_next(ofproto_, state_, port);
+    }
+
     return EOF;
 }
 
@@ -3395,6 +3579,47 @@ handle_flow_miss(struct flow_miss *miss, struct flow_miss_op *ops,
     handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
 }
 
+static struct drop_key *
+drop_key_lookup(const struct dpif_backer *backer, const struct nlattr *key,
+                size_t key_len)
+{
+    struct drop_key *drop_key;
+
+    HMAP_FOR_EACH_WITH_HASH (drop_key, hmap_node, hash_bytes(key, key_len, 0),
+                             &backer->drop_keys) {
+        if (drop_key->key_len == key_len
+            && !memcmp(drop_key->key, key, key_len)) {
+            return drop_key;
+        }
+    }
+    return NULL;
+}
+
+static void
+drop_key_clear(struct dpif_backer *backer)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 15);
+    struct drop_key *drop_key, *next;
+
+    HMAP_FOR_EACH_SAFE (drop_key, next, hmap_node, &backer->drop_keys) {
+        int error;
+
+        error = dpif_flow_del(backer->dpif, drop_key->key, drop_key->key_len,
+                              NULL);
+        if (error && !VLOG_DROP_WARN(&rl)) {
+            struct ds ds = DS_EMPTY_INITIALIZER;
+            odp_flow_key_format(drop_key->key, drop_key->key_len, &ds);
+            VLOG_WARN("Failed to delete drop key (%s) (%s)", strerror(error),
+                      ds_cstr(&ds));
+            ds_destroy(&ds);
+        }
+
+        hmap_remove(&backer->drop_keys, &drop_key->hmap_node);
+        free(drop_key->key);
+        free(drop_key);
+    }
+}
+
 /* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key'
  * respectively), populates 'flow' with the result of odp_flow_key_to_flow().
  * Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as
@@ -3417,6 +3642,10 @@ handle_flow_miss(struct flow_miss *miss, struct flow_miss_op *ops,
  * odp_flow_key_to_flow().  (This differs from the value returned in
  * flow->vlan_tci only for packets received on VLAN splinters.)
  *
+ * Similarly, this function also includes some logic to help with tunnels.  It
+ * may modify 'flow' as necessary to make the tunneling implementation
+ * transparent to the upcall processing logic.
+ *
  * Returns 0 if successful, ENODEV if the parsed flow has no associated ofport,
  * or some other positive errno if there are other problems. */
 static int
@@ -3428,7 +3657,7 @@ ofproto_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
 {
     const struct ofport_dpif *port;
     enum odp_key_fitness fitness;
-    int error;
+    int error = ENODEV;
 
     fitness = odp_flow_key_to_flow(key, key_len, flow);
     if (fitness == ODP_FIT_ERROR) {
@@ -3444,44 +3673,60 @@ ofproto_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
         *odp_in_port = flow->in_port;
     }
 
-    port = odp_port_to_ofport(backer, flow->in_port);
-    if (!port) {
-        flow->in_port = OFPP_NONE;
-        error = ofproto ? ENODEV : 0;
-        goto exit;
-    }
+    if (tnl_port_should_receive(flow)) {
+        const struct ofport *ofport = tnl_port_receive(flow);
+        if (!ofport) {
+            flow->in_port = OFPP_NONE;
+            goto exit;
+        }
+        port = ofport_dpif_cast(ofport);
 
-    if (ofproto) {
-        *ofproto = ofproto_dpif_cast(port->up.ofproto);
-    }
+        /* We can't reproduce 'key' from 'flow'. */
+        fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
 
-    flow->in_port = port->up.ofp_port;
-    if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
-        if (packet) {
-            /* Make the packet resemble the flow, so that it gets sent to an
-             * OpenFlow controller properly, so that it looks correct for
-             * sFlow, and so that flow_extract() will get the correct vlan_tci
-             * if it is called on 'packet'.
-             *
-             * The allocated space inside 'packet' probably also contains
-             * 'key', that is, both 'packet' and 'key' are probably part of a
-             * struct dpif_upcall (see the large comment on that structure
-             * definition), so pushing data on 'packet' is in general not a
-             * good idea since it could overwrite 'key' or free it as a side
-             * effect.  However, it's OK in this special case because we know
-             * that 'packet' is inside a Netlink attribute: pushing 4 bytes
-             * will just overwrite the 4-byte "struct nlattr", which is fine
-             * since we don't need that header anymore. */
-            eth_push_vlan(packet, flow->vlan_tci);
-        }
-
-        /* Let the caller know that we can't reproduce 'key' from 'flow'. */
-        if (fitness == ODP_FIT_PERFECT) {
-            fitness = ODP_FIT_TOO_MUCH;
+        /* XXX: Since the tunnel module is not scoped per backer, it's
+         * theoretically possible that we'll receive an ofport belonging to an
+         * entirely different datapath.  In practice, this can't happen because
+         * no platforms has two separate datapaths which each support
+         * tunneling. */
+        ovs_assert(ofproto_dpif_cast(port->up.ofproto)->backer == backer);
+    } else {
+        port = odp_port_to_ofport(backer, flow->in_port);
+        if (!port) {
+            flow->in_port = OFPP_NONE;
+            goto exit;
+        }
+
+        flow->in_port = port->up.ofp_port;
+        if (vsp_adjust_flow(ofproto_dpif_cast(port->up.ofproto), flow)) {
+            if (packet) {
+                /* Make the packet resemble the flow, so that it gets sent to
+                 * an OpenFlow controller properly, so that it looks correct
+                 * for sFlow, and so that flow_extract() will get the correct
+                 * vlan_tci if it is called on 'packet'.
+                 *
+                 * The allocated space inside 'packet' probably also contains
+                 * 'key', that is, both 'packet' and 'key' are probably part of
+                 * a struct dpif_upcall (see the large comment on that
+                 * structure definition), so pushing data on 'packet' is in
+                 * general not a good idea since it could overwrite 'key' or
+                 * free it as a side effect.  However, it's OK in this special
+                 * case because we know that 'packet' is inside a Netlink
+                 * attribute: pushing 4 bytes will just overwrite the 4-byte
+                 * "struct nlattr", which is fine since we don't need that
+                 * header anymore. */
+                eth_push_vlan(packet, flow->vlan_tci);
+            }
+            /* We can't reproduce 'key' from 'flow'. */
+            fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness;
         }
     }
     error = 0;
 
+    if (ofproto) {
+        *ofproto = ofproto_dpif_cast(port->up.ofproto);
+    }
+
 exit:
     if (fitnessp) {
         *fitnessp = fitness;
@@ -3527,12 +3772,29 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
                                 upcall->key_len, &flow, &miss->key_fitness,
                                 &ofproto, &odp_in_port, &miss->initial_tci);
         if (error == ENODEV) {
+            struct drop_key *drop_key;
+
             /* Received packet on port for which we couldn't associate
              * an ofproto.  This can happen if a port is removed while
              * traffic is being received.  Print a rate-limited message
-             * in case it happens frequently. */
+             * in case it happens frequently.  Install a drop flow so
+             * that future packets of the flow are inexpensively dropped
+             * in the kernel. */
             VLOG_INFO_RL(&rl, "received packet on unassociated port %"PRIu32,
                          flow.in_port);
+
+            drop_key = drop_key_lookup(backer, upcall->key, upcall->key_len);
+            if (!drop_key) {
+                drop_key = xmalloc(sizeof *drop_key);
+                drop_key->key = xmemdup(upcall->key, upcall->key_len);
+                drop_key->key_len = upcall->key_len;
+
+                hmap_insert(&backer->drop_keys, &drop_key->hmap_node,
+                            hash_bytes(drop_key->key, drop_key->key_len, 0));
+                dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+                              drop_key->key, drop_key->key_len, NULL, 0, NULL);
+            }
+            continue;
         }
         if (error) {
             continue;
@@ -3722,12 +3984,15 @@ expire(struct dpif_backer *backer)
     struct ofproto_dpif *ofproto;
     int max_idle = INT32_MAX;
 
+    /* Periodically clear out the drop keys in an effort to keep them
+     * relatively few. */
+    drop_key_clear(backer);
+
     /* Update stats for each flow in the backer. */
     update_stats(backer);
 
     HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-        struct rule_dpif *rule, *next_rule;
-        struct oftable *table;
+        struct rule *rule, *next_rule;
         int dp_max_idle;
 
         if (ofproto->backer != backer) {
@@ -3742,13 +4007,9 @@ expire(struct dpif_backer *backer)
 
         /* Expire OpenFlow flows whose idle_timeout or hard_timeout
          * has passed. */
-        OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
-            struct cls_cursor cursor;
-
-            cls_cursor_init(&cursor, &table->cls, NULL);
-            CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
-                rule_expire(rule);
-            }
+        LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
+                            &ofproto->up.expirable) {
+            rule_expire(rule_dpif_cast(rule));
         }
 
         /* All outstanding data in existing flows has been accounted, so it's a
@@ -3758,7 +4019,7 @@ expire(struct dpif_backer *backer)
 
             HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
                 if (bundle->bond) {
-                    bond_rebalance(bundle->bond, &ofproto->revalidate_set);
+                    bond_rebalance(bundle->bond, &backer->revalidate_set);
                 }
             }
         }
@@ -3844,29 +4105,21 @@ update_stats(struct dpif_backer *backer)
     while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
         struct flow flow;
         struct subfacet *subfacet;
-        enum odp_key_fitness fitness;
         struct ofproto_dpif *ofproto;
-        struct ofport_dpif *port;
+        struct ofport_dpif *ofport;
         uint32_t key_hash;
 
-        fitness = odp_flow_key_to_flow(key, key_len, &flow);
-        if (fitness == ODP_FIT_ERROR) {
+        if (ofproto_receive(backer, NULL, key, key_len, &flow, NULL, &ofproto,
+                            NULL, NULL)) {
             continue;
         }
 
-        port = odp_port_to_ofport(backer, flow.in_port);
-        if (!port) {
-            /* This flow is for a port for which we couldn't associate an
-             * ofproto.  This can happen if a port is removed while
-             * traffic is being received.  Ignore this flow, since it
-             * will get timed out. */
-            continue;
+        ofport = get_ofp_port(ofproto, flow.in_port);
+        if (ofport && ofport->tnl_port) {
+            netdev_vport_inc_rx(ofport->up.netdev, stats);
         }
 
-        ofproto = ofproto_dpif_cast(port->up.ofproto);
-        flow.in_port = port->up.ofp_port;
         key_hash = odp_flow_key_hash(key, key_len);
-
         subfacet = subfacet_find(ofproto, key, key_len, key_hash, &flow);
         switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
         case SF_FAST_PATH:
@@ -4090,9 +4343,7 @@ facet_free(struct facet *facet)
 }
 
 /* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
- * 'packet', which arrived on 'in_port'.
- *
- * Takes ownership of 'packet'. */
+ * 'packet', which arrived on 'in_port'. */
 static bool
 execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
                     const struct nlattr *odp_actions, size_t actions_len,
@@ -4108,8 +4359,6 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
 
     error = dpif_execute(ofproto->backer->dpif, key.data, key.size,
                          odp_actions, actions_len, packet);
-
-    ofpbuf_delete(packet);
     return !error;
 }
 
@@ -4322,8 +4571,9 @@ facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow,
 
     facet = facet_find(ofproto, flow, hash);
     if (facet
-        && (ofproto->need_revalidate
-            || tag_set_intersects(&ofproto->revalidate_set, facet->tags))) {
+        && (ofproto->backer->need_revalidate
+            || tag_set_intersects(&ofproto->backer->revalidate_set,
+                                  facet->tags))) {
         facet_revalidate(facet);
     }
 
@@ -5160,11 +5410,10 @@ rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes)
     }
 }
 
-static enum ofperr
-rule_execute(struct rule *rule_, const struct flow *flow,
-             struct ofpbuf *packet)
+static void
+rule_dpif_execute(struct rule_dpif *rule, const struct flow *flow,
+                  struct ofpbuf *packet)
 {
-    struct rule_dpif *rule = rule_dpif_cast(rule_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 
     struct dpif_flow_stats stats;
@@ -5186,7 +5435,14 @@ rule_execute(struct rule *rule_, const struct flow *flow,
                         odp_actions.size, packet);
 
     ofpbuf_uninit(&odp_actions);
+}
 
+static enum ofperr
+rule_execute(struct rule *rule, const struct flow *flow,
+             struct ofpbuf *packet)
+{
+    rule_dpif_execute(rule_dpif_cast(rule), flow, packet);
+    ofpbuf_delete(packet);
     return 0;
 }
 
@@ -5205,6 +5461,7 @@ static int
 send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
 {
     const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+    uint64_t odp_actions_stub[1024 / 8];
     struct ofpbuf key, odp_actions;
     struct odputil_keybuf keybuf;
     uint32_t odp_port;
@@ -5212,18 +5469,55 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
     int error;
 
     flow_extract(packet, 0, 0, NULL, OFPP_LOCAL, &flow);
-    odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
-                                      flow.vlan_tci);
-    if (odp_port != ofport->odp_port) {
-        eth_pop_vlan(packet);
-        flow.vlan_tci = htons(0);
+    if (netdev_vport_is_patch(ofport->up.netdev)) {
+        struct ofproto_dpif *peer_ofproto;
+        struct dpif_flow_stats stats;
+        struct ofport_dpif *peer;
+        struct rule_dpif *rule;
+
+        peer = ofport_get_peer(ofport);
+        if (!peer) {
+            return ENODEV;
+        }
+
+        dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
+        netdev_vport_inc_tx(ofport->up.netdev, &stats);
+        netdev_vport_inc_rx(peer->up.netdev, &stats);
+
+        flow.in_port = peer->up.ofp_port;
+        peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
+        rule = rule_dpif_lookup(peer_ofproto, &flow);
+        rule_dpif_execute(rule, &flow, packet);
+
+        return 0;
+    }
+
+    ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
+
+    if (ofport->tnl_port) {
+        struct dpif_flow_stats stats;
+
+        odp_port = tnl_port_send(ofport->tnl_port, &flow);
+        if (odp_port == OVSP_NONE) {
+            return ENODEV;
+        }
+
+        dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
+        netdev_vport_inc_tx(ofport->up.netdev, &stats);
+        odp_put_tunnel_action(&flow.tunnel, &odp_actions);
+    } else {
+        odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
+                                          flow.vlan_tci);
+        if (odp_port != ofport->odp_port) {
+            eth_pop_vlan(packet);
+            flow.vlan_tci = htons(0);
+        }
     }
 
     ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
     odp_flow_key_from_flow(&key, &flow,
                            ofp_port_to_odp_port(ofproto, flow.in_port));
 
-    ofpbuf_init(&odp_actions, 32);
     compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
 
     nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
@@ -5398,11 +5692,15 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
                         bool check_stp)
 {
     const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
-    uint32_t odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
     ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
+    ovs_be64 flow_tun_id = ctx->flow.tunnel.tun_id;
     uint8_t flow_nw_tos = ctx->flow.nw_tos;
     struct priority_to_dscp *pdscp;
-    uint32_t out_port;
+    uint32_t out_port, odp_port;
+
+    /* If 'struct flow' gets additional metadata, we'll need to zero it out
+     * before traversing a patch port. */
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
 
     if (!ofport) {
         xlate_report(ctx, "Nonexistent output port");
@@ -5415,16 +5713,65 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
         return;
     }
 
+    if (netdev_vport_is_patch(ofport->up.netdev)) {
+        struct ofport_dpif *peer = ofport_get_peer(ofport);
+        struct flow old_flow = ctx->flow;
+        const struct ofproto_dpif *peer_ofproto;
+
+        if (!peer) {
+            xlate_report(ctx, "Nonexistent patch port peer");
+            return;
+        }
+
+        peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
+        if (peer_ofproto->backer != ctx->ofproto->backer) {
+            xlate_report(ctx, "Patch port peer on a different datapath");
+            return;
+        }
+
+        ctx->ofproto = ofproto_dpif_cast(peer->up.ofproto);
+        ctx->flow.in_port = peer->up.ofp_port;
+        ctx->flow.metadata = htonll(0);
+        memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
+        memset(ctx->flow.regs, 0, sizeof ctx->flow.regs);
+        xlate_table_action(ctx, ctx->flow.in_port, 0, true);
+        ctx->flow = old_flow;
+        ctx->ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+
+        if (ctx->resubmit_stats) {
+            netdev_vport_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
+            netdev_vport_inc_rx(peer->up.netdev, ctx->resubmit_stats);
+        }
+
+        return;
+    }
+
     pdscp = get_priority(ofport, ctx->flow.skb_priority);
     if (pdscp) {
         ctx->flow.nw_tos &= ~IP_DSCP_MASK;
         ctx->flow.nw_tos |= pdscp->dscp;
     }
 
-    out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
-                                      ctx->flow.vlan_tci);
-    if (out_port != odp_port) {
-        ctx->flow.vlan_tci = htons(0);
+    odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
+    if (ofport->tnl_port) {
+        odp_port = tnl_port_send(ofport->tnl_port, &ctx->flow);
+        if (odp_port == OVSP_NONE) {
+            xlate_report(ctx, "Tunneling decided against output");
+            return;
+        }
+
+        if (ctx->resubmit_stats) {
+            netdev_vport_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
+        }
+        out_port = odp_port;
+        commit_odp_tunnel_action(&ctx->flow, &ctx->base_flow,
+                                 ctx->odp_actions);
+    } else {
+        out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
+                                          ctx->flow.vlan_tci);
+        if (out_port != odp_port) {
+            ctx->flow.vlan_tci = htons(0);
+        }
     }
     commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
     nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);
@@ -5432,6 +5779,7 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
     ctx->sflow_odp_port = odp_port;
     ctx->sflow_n_outputs++;
     ctx->nf_output_iface = ofp_port;
+    ctx->flow.tunnel.tun_id = flow_tun_id;
     ctx->flow.vlan_tci = flow_vlan_tci;
     ctx->flow.nw_tos = flow_nw_tos;
 }
@@ -6615,7 +6963,7 @@ update_learning_table(struct ofproto_dpif *ofproto,
                     in_bundle->name, vlan);
 
         mac->port.p = in_bundle;
-        tag_set_add(&ofproto->revalidate_set,
+        tag_set_add(&ofproto->backer->revalidate_set,
                     mac_learning_changed(ofproto->ml, mac));
     }
 }
@@ -6891,7 +7239,7 @@ table_update_taggable(struct ofproto_dpif *ofproto, uint8_t table_id)
     if (table->catchall_table != catchall || table->other_table != other) {
         table->catchall_table = catchall;
         table->other_table = other;
-        ofproto->need_revalidate = REV_FLOW_TABLE;
+        ofproto->backer->need_revalidate = REV_FLOW_TABLE;
     }
 }
 
@@ -6909,13 +7257,13 @@ rule_invalidate(const struct rule_dpif *rule)
 
     table_update_taggable(ofproto, rule->up.table_id);
 
-    if (!ofproto->need_revalidate) {
+    if (!ofproto->backer->need_revalidate) {
         struct table_dpif *table = &ofproto->tables[rule->up.table_id];
 
         if (table->other_table && rule->tag) {
-            tag_set_add(&ofproto->revalidate_set, rule->tag);
+            tag_set_add(&ofproto->backer->revalidate_set, rule->tag);
         } else {
-            ofproto->need_revalidate = REV_FLOW_TABLE;
+            ofproto->backer->need_revalidate = REV_FLOW_TABLE;
         }
     }
 }
@@ -6925,9 +7273,8 @@ set_frag_handling(struct ofproto *ofproto_,
                   enum ofp_config_flags frag_handling)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-
     if (frag_handling != OFPC_FRAG_REASM) {
-        ofproto->need_revalidate = REV_RECONFIGURE;
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
         return true;
     } else {
         return false;
@@ -7059,10 +7406,10 @@ ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, int argc,
             unixctl_command_reply_error(conn, "no such bridge");
             return;
         }
-        mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+        mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
     } else {
         HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-            mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+            mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
         }
     }
 
@@ -7430,7 +7777,7 @@ ofproto_dpif_self_check__(struct ofproto_dpif *ofproto, struct ds *reply)
         }
     }
     if (errors) {
-        ofproto->need_revalidate = REV_INCONSISTENCY;
+        ofproto->backer->need_revalidate = REV_INCONSISTENCY;
     }
 
     if (errors) {
@@ -7531,9 +7878,17 @@ show_dp_format(const struct ofproto_dpif *ofproto, struct ds *ds)
         struct ofport *ofport = node->data;
         const char *name = netdev_get_name(ofport->netdev);
         const char *type = netdev_get_type(ofport->netdev);
+        uint32_t odp_port;
+
+        ds_put_format(ds, "\t%s %u/", name, ofport->ofp_port);
+
+        odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port);
+        if (odp_port != OVSP_NONE) {
+            ds_put_format(ds, "%"PRIu32":", odp_port);
+        } else {
+            ds_put_cstr(ds, "none:");
+        }
 
-        ds_put_format(ds, "\t%s %u/%u:", name, ofport->ofp_port,
-                      ofp_port_to_odp_port(ofproto, ofport->ofp_port));
         if (strcmp(type, "system")) {
             struct netdev *netdev;
             int error;
@@ -7623,6 +7978,8 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
         return;
     }
 
+    update_stats(ofproto->backer);
+
     HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
         struct odputil_keybuf keybuf;
         struct ofpbuf key;
@@ -7723,7 +8080,7 @@ set_realdev(struct ofport *ofport_, uint16_t realdev_ofp_port, int vid)
         return 0;
     }
 
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
 
     if (ofport->realdev_ofp_port) {
         vsp_remove(ofport);
index f2274ef..95bda33 100644 (file)
@@ -71,6 +71,10 @@ struct ofproto {
     struct oftable *tables;
     int n_tables;
 
+    /* Optimisation for flow expiry.
+     * These flows should all be present in tables. */
+    struct list expirable;      /* Expirable 'struct rule"s in all tables. */
+
     /* OpenFlow connections. */
     struct connmgr *connmgr;
 
@@ -221,6 +225,10 @@ struct rule {
     enum nx_flow_monitor_flags monitor_flags;
     uint64_t add_seqno;         /* Sequence number when added. */
     uint64_t modify_seqno;      /* Sequence number when changed. */
+
+    /* Optimisation for flow expiry. */
+    struct list expirable;      /* In ofproto's 'expirable' list if this rule
+                                 * is expirable, otherwise empty. */
 };
 
 static inline struct rule *
index 9bae971..c0d94f7 100644 (file)
@@ -419,6 +419,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ofproto->max_ports = OFPP_MAX;
     ofproto->tables = NULL;
     ofproto->n_tables = 0;
+    list_init(&ofproto->expirable);
     ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
     ofproto->state = S_OPENFLOW;
     list_init(&ofproto->pending);
@@ -1644,7 +1645,9 @@ alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name)
 static void
 dealloc_ofp_port(const struct ofproto *ofproto, uint16_t ofp_port)
 {
-    bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+    if (ofp_port < ofproto->max_ports) {
+        bitmap_set0(ofproto->ofp_port_ids, ofp_port);
+    }
 }
 
 /* Opens and returns a netdev for 'ofproto_port' in 'ofproto', or a null
@@ -3162,6 +3165,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     rule->ofpacts_len = fm->ofpacts_len;
     rule->evictable = true;
     rule->eviction_group = NULL;
+    list_init(&rule->expirable);
     rule->monitor_flags = 0;
     rule->add_seqno = 0;
     rule->modify_seqno = 0;
@@ -3257,10 +3261,6 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
         new_cookie = (fm->new_cookie != htonll(UINT64_MAX)
                       ? fm->new_cookie
                       : rule->flow_cookie);
-        if (!actions_changed && new_cookie == rule->flow_cookie) {
-            /* No change at all. */
-            continue;
-        }
 
         op = ofoperation_create(group, rule, OFOPERATION_MODIFY, 0);
         rule->flow_cookie = new_cookie;
@@ -4243,7 +4243,19 @@ ofopgroup_complete(struct ofopgroup *group)
     LIST_FOR_EACH_SAFE (op, next_op, group_node, &group->ops) {
         struct rule *rule = op->rule;
 
-        if (!op->error && !ofproto_rule_is_hidden(rule)) {
+        /* We generally want to report the change to active OpenFlow flow
+           monitors (e.g. NXST_FLOW_MONITOR).  There are three exceptions:
+
+              - The operation failed.
+
+              - The affected rule is not visible to controllers.
+
+              - The operation's only effect was to update rule->modified. */
+        if (!(op->error
+              || ofproto_rule_is_hidden(rule)
+              || (op->type == OFOPERATION_MODIFY
+                  && op->ofpacts
+                  && rule->flow_cookie == op->flow_cookie))) {
             /* Check that we can just cast from ofoperation_type to
              * nx_flow_update_event. */
             BUILD_ASSERT_DECL((enum nx_flow_update_event) OFOPERATION_ADD
@@ -4824,6 +4836,9 @@ oftable_remove_rule(struct rule *rule)
 
     classifier_remove(&table->cls, &rule->cr);
     eviction_group_remove_rule(rule);
+    if (!list_is_empty(&rule->expirable)) {
+        list_remove(&rule->expirable);
+    }
 }
 
 /* Inserts 'rule' into its oftable.  Removes any existing rule from 'rule''s
@@ -4835,9 +4850,17 @@ oftable_replace_rule(struct rule *rule)
     struct ofproto *ofproto = rule->ofproto;
     struct oftable *table = &ofproto->tables[rule->table_id];
     struct rule *victim;
+    bool may_expire = rule->hard_timeout || rule->idle_timeout;
+
+    if (may_expire) {
+        list_insert(&ofproto->expirable, &rule->expirable);
+    }
 
     victim = rule_from_cls_rule(classifier_replace(&table->cls, &rule->cr));
     if (victim) {
+        if (!list_is_empty(&victim->expirable)) {
+            list_remove(&victim->expirable);
+        }
         eviction_group_remove_rule(victim);
     }
     eviction_group_add_rule(rule);
diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
new file mode 100644 (file)
index 0000000..ddfeeda
--- /dev/null
@@ -0,0 +1,470 @@
+/* Copyright (c) 2013 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#include <config.h>
+#include "tunnel.h"
+
+#include <errno.h>
+
+#include "ofproto/ofproto-provider.h"
+#include "byte-order.h"
+#include "dynamic-string.h"
+#include "hash.h"
+#include "hmap.h"
+#include "netdev-vport.h"
+#include "odp-util.h"
+#include "packets.h"
+#include "smap.h"
+#include "socket-util.h"
+#include "tunnel.h"
+#include "vlog.h"
+
+/* XXX:
+ *
+ * Ability to generate actions on input for ECN
+ * Ability to generate metadata for packet-outs
+ * IPsec using skb mark.
+ * VXLAN.
+ * Multicast group management (possibly).
+ * Disallow netdevs with names like "gre64_system" to prevent collisions. */
+
+VLOG_DEFINE_THIS_MODULE(tunnel);
+
+struct tnl_match {
+    ovs_be64 in_key;
+    ovs_be32 ip_src;
+    ovs_be32 ip_dst;
+    uint32_t odp_port;
+    bool in_key_present;
+    bool in_key_flow;
+};
+
+struct tnl_port {
+    struct hmap_node match_node;
+
+    const struct ofport *ofport;
+    unsigned int netdev_seq;
+    struct tnl_match match;
+};
+
+static struct hmap tnl_match_map = HMAP_INITIALIZER(&tnl_match_map);
+
+/* Returned to callers when their ofport will never be used to receive or send
+ * tunnel traffic. Alternatively, we could ask the caller to delete their
+ * ofport, but this would be unclean in the reconfguration case.  For the first
+ * time, an ofproto provider would have to call ofproto_port_del() on itself.*/
+static struct tnl_port void_tnl_port;
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+static struct vlog_rate_limit dbg_rl = VLOG_RATE_LIMIT_INIT(60, 60);
+
+static struct tnl_port *tnl_find(struct tnl_match *);
+static struct tnl_port *tnl_find_exact(struct tnl_match *);
+static uint32_t tnl_hash(struct tnl_match *);
+static void tnl_match_fmt(const struct tnl_match *, struct ds *);
+static char *tnl_port_fmt(const struct tnl_port *);
+static void tnl_port_mod_log(const struct tnl_port *, const char *action);
+static const char *tnl_port_get_name(const struct tnl_port *);
+
+static struct tnl_port *
+tnl_port_add__(const struct ofport *ofport, uint32_t odp_port,
+               bool warn)
+{
+    const struct netdev_tunnel_config *cfg;
+    struct tnl_port *existing_port;
+    struct tnl_port *tnl_port;
+
+    cfg = netdev_get_tunnel_config(ofport->netdev);
+    ovs_assert(cfg);
+
+    tnl_port = xzalloc(sizeof *tnl_port);
+    tnl_port->ofport = ofport;
+    tnl_port->netdev_seq = netdev_change_seq(tnl_port->ofport->netdev);
+
+    tnl_port->match.in_key = cfg->in_key;
+    tnl_port->match.ip_src = cfg->ip_src;
+    tnl_port->match.ip_dst = cfg->ip_dst;
+    tnl_port->match.in_key_present = cfg->in_key_present;
+    tnl_port->match.in_key_flow = cfg->in_key_flow;
+    tnl_port->match.odp_port = odp_port;
+
+    existing_port = tnl_find_exact(&tnl_port->match);
+    if (existing_port) {
+        if (warn) {
+            struct ds ds = DS_EMPTY_INITIALIZER;
+            tnl_match_fmt(&tnl_port->match, &ds);
+            VLOG_WARN("%s: attempting to add tunnel port with same config as "
+                      "port '%s' (%s)", tnl_port_get_name(tnl_port),
+                      tnl_port_get_name(existing_port), ds_cstr(&ds));
+            ds_destroy(&ds);
+            free(tnl_port);
+        }
+        return &void_tnl_port;
+    }
+
+    hmap_insert(&tnl_match_map, &tnl_port->match_node,
+                tnl_hash(&tnl_port->match));
+    tnl_port_mod_log(tnl_port, "adding");
+    return tnl_port;
+}
+
+/* Adds 'ofport' to the module with datapath port number 'odp_port'. 'ofport's
+ * must be added before they can be used by the module. 'ofport' must be a
+ * tunnel. */
+struct tnl_port *
+tnl_port_add(const struct ofport *ofport, uint32_t odp_port)
+{
+    return tnl_port_add__(ofport, odp_port, true);
+}
+
+/* Checks if the tnl_port pointed to by 'tnl_portp' needs reconfiguration due
+ * to changes in its netdev_tunnel_config.  If it does, updates 'tnl_portp' to
+ * point to a new tnl_port and returns true.  Otherwise, returns false.
+ * 'ofport' and 'odp_port' should be the same as would be passed to
+ * tnl_port_add(). */
+bool
+tnl_port_reconfigure(const struct ofport *ofport, uint32_t odp_port,
+                     struct tnl_port **tnl_portp)
+{
+    struct tnl_port *tnl_port = *tnl_portp;
+
+    if (tnl_port == &void_tnl_port) {
+        *tnl_portp = tnl_port_add__(ofport, odp_port, false);
+        return *tnl_portp != &void_tnl_port;
+    } else if (tnl_port->ofport != ofport
+               || tnl_port->match.odp_port != odp_port
+               || tnl_port->netdev_seq != netdev_change_seq(ofport->netdev)) {
+        VLOG_DBG("reconfiguring %s", tnl_port_get_name(tnl_port));
+        tnl_port_del(tnl_port);
+        *tnl_portp = tnl_port_add(ofport, odp_port);
+        return true;
+    }
+    return false;
+}
+
+/* Removes 'tnl_port' from the module. */
+void
+tnl_port_del(struct tnl_port *tnl_port)
+{
+    if (tnl_port && tnl_port != &void_tnl_port) {
+        tnl_port_mod_log(tnl_port, "removing");
+        hmap_remove(&tnl_match_map, &tnl_port->match_node);
+        free(tnl_port);
+    }
+}
+
+/* Transforms 'flow' so that it appears to have been received by a tunnel
+ * OpenFlow port controlled by this module instead of the datapath port it
+ * actually came in on.  Sets 'flow''s in_port to the appropriate OpenFlow port
+ * number.  Returns the 'ofport' corresponding to the new in_port.
+ *
+ * Callers should verify that 'flow' needs to be received by calling
+ * tnl_port_should_receive() before this function.
+ *
+ * Leaves 'flow' untouched and returns null if unsuccessful. */
+const struct ofport *
+tnl_port_receive(struct flow *flow)
+{
+    char *pre_flow_str = NULL;
+    struct tnl_port *tnl_port;
+    struct tnl_match match;
+
+    memset(&match, 0, sizeof match);
+    match.odp_port = flow->in_port;
+    match.ip_src = flow->tunnel.ip_dst;
+    match.ip_dst = flow->tunnel.ip_src;
+    match.in_key = flow->tunnel.tun_id;
+    match.in_key_present = flow->tunnel.flags & FLOW_TNL_F_KEY;
+
+    tnl_port = tnl_find(&match);
+    if (!tnl_port) {
+        struct ds ds = DS_EMPTY_INITIALIZER;
+
+        tnl_match_fmt(&match, &ds);
+        VLOG_WARN_RL(&rl, "receive tunnel port not found (%s)", ds_cstr(&ds));
+        ds_destroy(&ds);
+        return NULL;
+    }
+
+    if (is_ip_any(flow)
+        && ((flow->tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE)
+        && (flow->nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) {
+        VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE but is not ECN"
+                     " capable");
+        return NULL;
+    }
+
+    if (!VLOG_DROP_DBG(&dbg_rl)) {
+        pre_flow_str = flow_to_string(flow);
+    }
+
+    flow->in_port = tnl_port->ofport->ofp_port;
+    memset(&flow->tunnel, 0, sizeof flow->tunnel);
+    flow->tunnel.tun_id = match.in_key;
+
+    if (pre_flow_str) {
+        char *post_flow_str = flow_to_string(flow);
+        char *tnl_str = tnl_port_fmt(tnl_port);
+        VLOG_DBG("flow received\n"
+                 "%s"
+                 " pre: %s\n"
+                 "post: %s",
+                 tnl_str, pre_flow_str, post_flow_str);
+        free(tnl_str);
+        free(pre_flow_str);
+        free(post_flow_str);
+    }
+    return tnl_port->ofport;
+}
+
+/* Given that 'flow' should be output to the ofport corresponding to
+ * 'tnl_port', updates 'flow''s tunnel headers and returns the actual datapath
+ * port that the output should happen on.  May return OVSP_NONE if the output
+ * shouldn't occur. */
+uint32_t
+tnl_port_send(const struct tnl_port *tnl_port, struct flow *flow)
+{
+    const struct netdev_tunnel_config *cfg;
+    char *pre_flow_str = NULL;
+
+    if (tnl_port == &void_tnl_port) {
+        return OVSP_NONE;
+    }
+
+    cfg = netdev_get_tunnel_config(tnl_port->ofport->netdev);
+    ovs_assert(cfg);
+
+    if (!VLOG_DROP_DBG(&dbg_rl)) {
+        pre_flow_str = flow_to_string(flow);
+    }
+
+    flow->tunnel.ip_src = tnl_port->match.ip_src;
+    flow->tunnel.ip_dst = tnl_port->match.ip_dst;
+
+    if (!cfg->out_key_flow) {
+        flow->tunnel.tun_id = cfg->out_key;
+    }
+
+    if (cfg->ttl_inherit && is_ip_any(flow)) {
+        flow->tunnel.ip_ttl = flow->nw_ttl;
+    } else {
+        flow->tunnel.ip_ttl = cfg->ttl;
+    }
+
+    if (cfg->tos_inherit && is_ip_any(flow)) {
+        flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK;
+    } else {
+        flow->tunnel.ip_tos = cfg->tos;
+    }
+
+    if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_CE) {
+        flow->tunnel.ip_tos |= IP_ECN_ECT_0;
+    } else {
+        flow->tunnel.ip_tos |= flow->nw_tos & IP_ECN_MASK;
+    }
+
+    flow->tunnel.flags = (cfg->dont_fragment ? FLOW_TNL_F_DONT_FRAGMENT : 0)
+        | (cfg->csum ? FLOW_TNL_F_CSUM : 0)
+        | (cfg->out_key_present ? FLOW_TNL_F_KEY : 0);
+
+    if (pre_flow_str) {
+        char *post_flow_str = flow_to_string(flow);
+        char *tnl_str = tnl_port_fmt(tnl_port);
+        VLOG_DBG("flow sent\n"
+                 "%s"
+                 " pre: %s\n"
+                 "post: %s",
+                 tnl_str, pre_flow_str, post_flow_str);
+        free(tnl_str);
+        free(pre_flow_str);
+        free(post_flow_str);
+    }
+
+    return tnl_port->match.odp_port;
+}
+
+static uint32_t
+tnl_hash(struct tnl_match *match)
+{
+    BUILD_ASSERT_DECL(sizeof *match % sizeof(uint32_t) == 0);
+    return hash_words((uint32_t *) match, sizeof *match / sizeof(uint32_t), 0);
+}
+
+static struct tnl_port *
+tnl_find_exact(struct tnl_match *match)
+{
+    struct tnl_port *tnl_port;
+
+    HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match),
+                             &tnl_match_map) {
+        if (!memcmp(match, &tnl_port->match, sizeof *match)) {
+            return tnl_port;
+        }
+    }
+    return NULL;
+}
+
+static struct tnl_port *
+tnl_find(struct tnl_match *match_)
+{
+    struct tnl_match match = *match_;
+    bool is_multicast = ip_is_multicast(match.ip_src);
+    struct tnl_port *tnl_port;
+
+    /* remote_ip, local_ip, in_key */
+    if (!is_multicast) {
+        tnl_port = tnl_find_exact(&match);
+        if (tnl_port) {
+            return tnl_port;
+        }
+    }
+
+    /* remote_ip, in_key */
+    match.ip_src = 0;
+    tnl_port = tnl_find_exact(&match);
+    if (tnl_port) {
+        return tnl_port;
+    }
+    match.ip_src = match_->ip_src;
+
+    /* remote_ip, local_ip */
+    if (!is_multicast) {
+        match.in_key = 0;
+        match.in_key_flow = true;
+        tnl_port = tnl_find_exact(&match);
+        if (tnl_port) {
+            return tnl_port;
+        }
+        match.in_key = match_->in_key;
+        match.in_key_flow = false;
+    }
+
+    /* remote_ip */
+    match.ip_src = 0;
+    match.in_key = 0;
+    match.in_key_flow = true;
+    tnl_port = tnl_find_exact(&match);
+    if (tnl_port) {
+        return tnl_port;
+    }
+    match.ip_src = match_->ip_src;
+    match.in_key = match_->in_key;
+    match.in_key_flow = false;
+
+    if (is_multicast) {
+        match.ip_src = 0;
+        match.ip_dst = match_->ip_src;
+
+        /* multicast remote_ip, in_key */
+        tnl_port = tnl_find_exact(&match);
+        if (tnl_port) {
+            return tnl_port;
+        }
+
+        /* multicast remote_ip */
+        match.in_key = 0;
+        match.in_key_flow = true;
+        tnl_port = tnl_find_exact(&match);
+        if (tnl_port) {
+            return tnl_port;
+        }
+    }
+    return NULL;
+}
+
+static void
+tnl_match_fmt(const struct tnl_match *match, struct ds *ds)
+{
+    ds_put_format(ds, IP_FMT"->"IP_FMT, IP_ARGS(match->ip_src),
+                  IP_ARGS(match->ip_dst));
+
+    if (match->in_key_present) {
+        if (match->in_key_flow) {
+            ds_put_cstr(ds, ", key=flow");
+        } else {
+            ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key));
+        }
+    }
+
+    ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port);
+}
+
+static void
+tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action)
+{
+    if (VLOG_IS_DBG_ENABLED()) {
+        struct ds ds = DS_EMPTY_INITIALIZER;
+
+        tnl_match_fmt(&tnl_port->match, &ds);
+        VLOG_DBG("%s tunnel port %s (%s)", action, tnl_port_get_name(tnl_port),
+                 ds_cstr(&ds));
+        ds_destroy(&ds);
+    }
+}
+
+static char *
+tnl_port_fmt(const struct tnl_port *tnl_port)
+{
+    const struct netdev_tunnel_config *cfg =
+        netdev_get_tunnel_config(tnl_port->ofport->netdev);
+    struct ds ds = DS_EMPTY_INITIALIZER;
+
+    ds_put_format(&ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port,
+                  tnl_port_get_name(tnl_port),
+                  netdev_get_type(tnl_port->ofport->netdev));
+    tnl_match_fmt(&tnl_port->match, &ds);
+
+    if (cfg->out_key != cfg->in_key ||
+        cfg->out_key_present != cfg->in_key_present ||
+        cfg->out_key_flow != cfg->in_key_flow) {
+        ds_put_cstr(&ds, ", out_key=");
+        if (!cfg->out_key_present) {
+            ds_put_cstr(&ds, "none");
+        } else if (cfg->out_key_flow) {
+            ds_put_cstr(&ds, "flow");
+        } else {
+            ds_put_format(&ds, "%#"PRIx64, ntohll(cfg->out_key));
+        }
+    }
+
+    if (cfg->ttl_inherit) {
+        ds_put_cstr(&ds, ", ttl=inherit");
+    } else {
+        ds_put_format(&ds, ", ttl=%"PRIu8, cfg->ttl);
+    }
+
+    if (cfg->tos_inherit) {
+        ds_put_cstr(&ds, ", tos=inherit");
+    } else if (cfg->tos) {
+        ds_put_format(&ds, ", tos=%#"PRIx8, cfg->tos);
+    }
+
+    if (!cfg->dont_fragment) {
+        ds_put_cstr(&ds, ", df=false");
+    }
+
+    if (cfg->csum) {
+        ds_put_cstr(&ds, ", csum=true");
+    }
+
+    ds_put_cstr(&ds, ")\n");
+
+    return ds_steal_cstr(&ds);
+}
+
+static const char *
+tnl_port_get_name(const struct tnl_port *tnl_port)
+{
+    return netdev_get_name(tnl_port->ofport->netdev);
+}
diff --git a/ofproto/tunnel.h b/ofproto/tunnel.h
new file mode 100644 (file)
index 0000000..acb69a8
--- /dev/null
@@ -0,0 +1,47 @@
+/* Copyright (c) 2013 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TUNNEL_H
+#define TUNNEL_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "flow.h"
+
+/* Tunnel port emulation layer.
+ *
+ * These functions emulate tunnel virtual ports based on the outer
+ * header information from the kernel. */
+
+struct ofport;
+struct tnl_port;
+
+bool tnl_port_reconfigure(const struct ofport *, uint32_t odp_port,
+                          struct tnl_port **);
+
+struct tnl_port *tnl_port_add(const struct ofport *, uint32_t odp_port);
+void tnl_port_del(struct tnl_port *);
+
+const struct ofport *tnl_port_receive(struct flow *);
+uint32_t tnl_port_send(const struct tnl_port *, struct flow *);
+
+/* Returns true if 'flow' should be submitted to tnl_port_receive(). */
+static inline bool
+tnl_port_should_receive(const struct flow *flow)
+{
+    return flow->tunnel.ip_dst != 0;
+}
+
+#endif /* tunnel.h */
index 1d0b0e3..6d07c45 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -386,7 +386,6 @@ ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s)
     ovsdb_jsonrpc_session_unlock_all(s);
     jsonrpc_session_close(s->js);
     list_remove(&s->node);
-    ovsdb_session_destroy(&s->up);
     s->remote->server->n_sessions--;
     ovsdb_session_destroy(&s->up);
     free(s);
index 19047d8..d2a2f4b 100755 (executable)
@@ -34,6 +34,8 @@ if [ ! -x ${OTHERSCRIPT} ]; then
     OTHERSCRIPT="/etc/sysconfig/network-scripts/ifdown-eth"
 fi
 
+[ -f /var/lock/subsys/openvswitch ] || /sbin/service openvswitch start
+
 case "$TYPE" in
        OVSBridge)
                ${OTHERSCRIPT} ${CONFIG} $2
index b6ccf56..3538721 100755 (executable)
@@ -34,6 +34,27 @@ if [ ! -x ${OTHERSCRIPT} ]; then
        OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-eth"
 fi
 
+check_recursion()
+{
+       [ -n "${UPPEDSTACK}" ] && for _r in ${UPPEDSTACK}; do
+               [ "$_r" = "$1" ] && return 1
+       done
+
+       return 0
+}
+
+if [ -z "${UPPEDSTACK}" ]; then
+       UPPEDSTACK="${DEVICE}"
+fi
+
+[ -n "${OVSREQUIRES}" ] && for _i in ${OVSREQUIRES}; do
+       if ( check_recursion "$_i" ); then
+               UPPEDSTACK="${UPPEDSTACK} $_i" /sbin/ifup "$_i"
+       fi
+done
+
+[ -f /var/lock/subsys/openvswitch ] || /sbin/service openvswitch start
+
 case "$TYPE" in
        OVSBridge)
                ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
index 23c36ef..1ebdf85 100644 (file)
@@ -33,6 +33,7 @@ TESTSUITE_AT = \
        tests/jsonrpc.at \
        tests/jsonrpc-py.at \
        tests/timeval.at \
+       tests/tunnel.at \
        tests/lockfile.at \
        tests/reconnect.at \
        tests/ofproto-dpif.at \
index 7d96143..408b1ec 100644 (file)
@@ -117,3 +117,433 @@ slave p2: disabled
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([lacp - negotiation])
+# Create bond0 on br0 with interfaces p0 and p1
+#    and bond1 on br1 with interfaces p2 and p3
+# with p0 patched to p2 and p1 patched to p3.
+OVS_VSWITCHD_START(
+  [add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \
+                            other-config:lacp-time=fast \
+                            other-config:bond-rebalance-interval=0 -- \
+   set interface p0 type=patch options:peer=p2 ofport_request=1 -- \
+   set interface p1 type=patch options:peer=p3 ofport_request=2 -- \
+   add-br br1 -- \
+   set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \
+   set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \
+                  fail-mode=secure -- \
+   add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \
+                            other-config:lacp-time=fast \
+                            other-config:bond-rebalance-interval=0 -- \
+   set interface p2 type=patch options:peer=p0 ofport_request=3 -- \
+   set interface p3 type=patch options:peer=p1 ofport_request=4 --])
+
+AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
+])
+
+# Wait for up to 5 (simulated) seconds, until LACP negotiation finishes.
+i=0
+while :; do
+    ovs-appctl lacp/show bond0 > bond0
+    AT_CAPTURE_FILE([bond0])
+    ovs-appctl lacp/show bond1 > bond1
+    AT_CAPTURE_FILE([bond1])
+    if grep negotiated bond0 && grep negotiated bond1; then
+        if grep expired bond0 || grep expired bond1; then
+            :
+        else
+            break
+        fi
+    fi
+    i=`expr $i + 1`
+    if test $i = 50; then
+        AT_FAIL_IF([:])
+    fi
+    ovs-appctl time/warp 100
+done
+
+# Now check the correctly negotiated configuration.
+AT_CHECK(
+  [ovs-appctl lacp/show bond0
+ovs-appctl lacp/show bond1
+ovs-appctl bond/show bond0
+ovs-appctl bond/show bond1], [0], [stdout])
+AT_CHECK([sed '/active slave/d' stdout], [0], [dnl
+---- bond0 ----
+       status: active negotiated
+       sys_id: aa:55:aa:55:00:00
+       sys_priority: 65534
+       aggregation key: 2
+       lacp_time: fast
+
+slave: p0: current attached
+       port_id: 1
+       port_priority: 65535
+       may_enable: true
+
+       actor sys_id: aa:55:aa:55:00:00
+       actor sys_priority: 65534
+       actor port_id: 1
+       actor port_priority: 65535
+       actor key: 2
+       actor state: activity timeout aggregation synchronized collecting distributing
+
+       partner sys_id: aa:66:aa:66:00:00
+       partner sys_priority: 65534
+       partner port_id: 3
+       partner port_priority: 65535
+       partner key: 4
+       partner state: activity timeout aggregation synchronized collecting distributing
+
+slave: p1: current attached
+       port_id: 2
+       port_priority: 65535
+       may_enable: true
+
+       actor sys_id: aa:55:aa:55:00:00
+       actor sys_priority: 65534
+       actor port_id: 2
+       actor port_priority: 65535
+       actor key: 2
+       actor state: activity timeout aggregation synchronized collecting distributing
+
+       partner sys_id: aa:66:aa:66:00:00
+       partner sys_priority: 65534
+       partner port_id: 4
+       partner port_priority: 65535
+       partner key: 4
+       partner state: activity timeout aggregation synchronized collecting distributing
+---- bond1 ----
+       status: active negotiated
+       sys_id: aa:66:aa:66:00:00
+       sys_priority: 65534
+       aggregation key: 4
+       lacp_time: fast
+
+slave: p2: current attached
+       port_id: 3
+       port_priority: 65535
+       may_enable: true
+
+       actor sys_id: aa:66:aa:66:00:00
+       actor sys_priority: 65534
+       actor port_id: 3
+       actor port_priority: 65535
+       actor key: 4
+       actor state: activity timeout aggregation synchronized collecting distributing
+
+       partner sys_id: aa:55:aa:55:00:00
+       partner sys_priority: 65534
+       partner port_id: 1
+       partner port_priority: 65535
+       partner key: 2
+       partner state: activity timeout aggregation synchronized collecting distributing
+
+slave: p3: current attached
+       port_id: 4
+       port_priority: 65535
+       may_enable: true
+
+       actor sys_id: aa:66:aa:66:00:00
+       actor sys_priority: 65534
+       actor port_id: 4
+       actor port_priority: 65535
+       actor key: 4
+       actor state: activity timeout aggregation synchronized collecting distributing
+
+       partner sys_id: aa:55:aa:55:00:00
+       partner sys_priority: 65534
+       partner port_id: 2
+       partner port_priority: 65535
+       partner key: 2
+       partner state: activity timeout aggregation synchronized collecting distributing
+---- bond0 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p0: enabled
+       may_enable: true
+
+slave p1: enabled
+       may_enable: true
+
+---- bond1 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p2: enabled
+       may_enable: true
+
+slave p3: enabled
+       may_enable: true
+
+])
+AT_CHECK([grep 'active slave' stdout], [0], [dnl
+       active slave
+       active slave
+])
+
+# Redirect the patch link between p0 and p2 so that no packets get
+# back and forth across them anymore.  Then wait 4 simulated
+# seconds.  The LACP state should become "expired" for p0 and p2.
+AT_CHECK([ovs-vsctl \
+-- add-port br0 null0 -- set int null0 type=patch options:peer=p2 -- set int p2 options:peer=null0 \
+-- add-port br1 null1 -- set int null1 type=patch options:peer=p0 -- set int p0 options:peer=null1])
+
+for i in `seq 0 40`; do ovs-appctl time/warp 100; done
+AT_CHECK(
+  [ovs-appctl lacp/show bond0
+ovs-appctl lacp/show bond1
+ovs-appctl bond/show bond0
+ovs-appctl bond/show bond1], [0], [dnl
+---- bond0 ----
+       status: active negotiated
+       sys_id: aa:55:aa:55:00:00
+       sys_priority: 65534
+       aggregation key: 2
+       lacp_time: fast
+
+slave: p0: expired attached
+       port_id: 1
+       port_priority: 65535
+       may_enable: false
+
+       actor sys_id: aa:55:aa:55:00:00
+       actor sys_priority: 65534
+       actor port_id: 1
+       actor port_priority: 65535
+       actor key: 2
+       actor state: activity timeout aggregation synchronized collecting distributing expired
+
+       partner sys_id: aa:66:aa:66:00:00
+       partner sys_priority: 65534
+       partner port_id: 3
+       partner port_priority: 65535
+       partner key: 4
+       partner state: activity timeout aggregation collecting distributing
+
+slave: p1: current attached
+       port_id: 2
+       port_priority: 65535
+       may_enable: true
+
+       actor sys_id: aa:55:aa:55:00:00
+       actor sys_priority: 65534
+       actor port_id: 2
+       actor port_priority: 65535
+       actor key: 2
+       actor state: activity timeout aggregation synchronized collecting distributing
+
+       partner sys_id: aa:66:aa:66:00:00
+       partner sys_priority: 65534
+       partner port_id: 4
+       partner port_priority: 65535
+       partner key: 4
+       partner state: activity timeout aggregation synchronized collecting distributing
+---- bond1 ----
+       status: active negotiated
+       sys_id: aa:66:aa:66:00:00
+       sys_priority: 65534
+       aggregation key: 4
+       lacp_time: fast
+
+slave: p2: expired attached
+       port_id: 3
+       port_priority: 65535
+       may_enable: false
+
+       actor sys_id: aa:66:aa:66:00:00
+       actor sys_priority: 65534
+       actor port_id: 3
+       actor port_priority: 65535
+       actor key: 4
+       actor state: activity timeout aggregation synchronized collecting distributing expired
+
+       partner sys_id: aa:55:aa:55:00:00
+       partner sys_priority: 65534
+       partner port_id: 1
+       partner port_priority: 65535
+       partner key: 2
+       partner state: activity timeout aggregation collecting distributing
+
+slave: p3: current attached
+       port_id: 4
+       port_priority: 65535
+       may_enable: true
+
+       actor sys_id: aa:66:aa:66:00:00
+       actor sys_priority: 65534
+       actor port_id: 4
+       actor port_priority: 65535
+       actor key: 4
+       actor state: activity timeout aggregation synchronized collecting distributing
+
+       partner sys_id: aa:55:aa:55:00:00
+       partner sys_priority: 65534
+       partner port_id: 2
+       partner port_priority: 65535
+       partner key: 2
+       partner state: activity timeout aggregation synchronized collecting distributing
+---- bond0 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p0: disabled
+       may_enable: false
+
+slave p1: enabled
+       active slave
+       may_enable: true
+
+---- bond1 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p2: disabled
+       may_enable: false
+
+slave p3: enabled
+       active slave
+       may_enable: true
+
+])
+
+# Wait 4 more simulated seconds.  The LACP state should become
+# "defaulted" for p0 and p2.
+for i in `seq 0 40`; do ovs-appctl time/warp 100; done
+AT_CHECK(
+  [ovs-appctl lacp/show bond0
+ovs-appctl lacp/show bond1
+ovs-appctl bond/show bond0
+ovs-appctl bond/show bond1], [0], [dnl
+---- bond0 ----
+       status: active negotiated
+       sys_id: aa:55:aa:55:00:00
+       sys_priority: 65534
+       aggregation key: 2
+       lacp_time: fast
+
+slave: p0: defaulted detached
+       port_id: 1
+       port_priority: 65535
+       may_enable: false
+
+       actor sys_id: aa:55:aa:55:00:00
+       actor sys_priority: 65534
+       actor port_id: 1
+       actor port_priority: 65535
+       actor key: 2
+       actor state: activity timeout aggregation defaulted
+
+       partner sys_id: 00:00:00:00:00:00
+       partner sys_priority: 0
+       partner port_id: 0
+       partner port_priority: 0
+       partner key: 0
+       partner state:
+
+slave: p1: current attached
+       port_id: 2
+       port_priority: 65535
+       may_enable: true
+
+       actor sys_id: aa:55:aa:55:00:00
+       actor sys_priority: 65534
+       actor port_id: 2
+       actor port_priority: 65535
+       actor key: 2
+       actor state: activity timeout aggregation synchronized collecting distributing
+
+       partner sys_id: aa:66:aa:66:00:00
+       partner sys_priority: 65534
+       partner port_id: 4
+       partner port_priority: 65535
+       partner key: 4
+       partner state: activity timeout aggregation synchronized collecting distributing
+---- bond1 ----
+       status: active negotiated
+       sys_id: aa:66:aa:66:00:00
+       sys_priority: 65534
+       aggregation key: 4
+       lacp_time: fast
+
+slave: p2: defaulted detached
+       port_id: 3
+       port_priority: 65535
+       may_enable: false
+
+       actor sys_id: aa:66:aa:66:00:00
+       actor sys_priority: 65534
+       actor port_id: 3
+       actor port_priority: 65535
+       actor key: 4
+       actor state: activity timeout aggregation defaulted
+
+       partner sys_id: 00:00:00:00:00:00
+       partner sys_priority: 0
+       partner port_id: 0
+       partner port_priority: 0
+       partner key: 0
+       partner state:
+
+slave: p3: current attached
+       port_id: 4
+       port_priority: 65535
+       may_enable: true
+
+       actor sys_id: aa:66:aa:66:00:00
+       actor sys_priority: 65534
+       actor port_id: 4
+       actor port_priority: 65535
+       actor key: 4
+       actor state: activity timeout aggregation synchronized collecting distributing
+
+       partner sys_id: aa:55:aa:55:00:00
+       partner sys_priority: 65534
+       partner port_id: 2
+       partner port_priority: 65535
+       partner key: 2
+       partner state: activity timeout aggregation synchronized collecting distributing
+---- bond0 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p0: disabled
+       may_enable: false
+
+slave p1: enabled
+       active slave
+       may_enable: true
+
+---- bond1 ----
+bond_mode: balance-tcp
+bond-hash-basis: 0
+updelay: 0 ms
+downdelay: 0 ms
+lacp_status: negotiated
+
+slave p2: disabled
+       may_enable: false
+
+slave p3: enabled
+       active slave
+       may_enable: true
+
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 47c1d32..800dc14 100644 (file)
@@ -122,6 +122,81 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+dnl This test checks that repeated uses of a "learn" action cause the
+dnl modified time of the learned flow to advance.  Otherwise, the
+dnl learned flow will expire after its hard timeout even though it's
+dnl supposed to be refreshed.  (The expiration can be hard to see since
+dnl it gets re-learned again the next time a packet appears, but
+dnl sometimes the expiration can cause temporary flooding etc.)
+AT_SETUP([learning action - learn refreshes hard_age])
+OVS_VSWITCHD_START(
+  [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
+   add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2 -- \
+   add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3])
+
+ovs-appctl time/stop
+
+# Set up flow table for MAC learning.
+AT_DATA([flows.txt], [[
+table=0 actions=learn(table=1, hard_timeout=10, NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[]), resubmit(,1)
+table=1 priority=0 actions=flood
+]])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+# Trace an ICMP packet arriving on port 3, to create a MAC learning entry.
+flow="in_port(3),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)"
+AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+actual=`tail -1 stdout | sed 's/Datapath actions: //'`
+
+expected="1,2,100"
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
+mv stdout expout
+AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
+
+# Check that the MAC learning entry appeared.
+AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
+ table=1, hard_timeout=10, dl_dst=50:54:00:00:00:07 actions=output:3
+ table=1, priority=0 actions=FLOOD
+NXST_FLOW reply:
+])
+
+# For 25 seconds, make sure that the MAC learning entry doesn't
+# disappear as long as we refresh it every second.
+for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25; do
+    ovs-appctl time/warp 1000
+    AT_CHECK([ovs-appctl ofproto/trace br0 "$flow" -generate], [0], [stdout])
+
+    # Check that the entry is there.
+    AT_CHECK([ovs-ofctl dump-flows br0 table=1], [0], [stdout])
+    AT_CHECK([ofctl_strip < stdout | sort], [0], [dnl
+ table=1, hard_timeout=10, dl_dst=50:54:00:00:00:07 actions=output:3
+ table=1, priority=0 actions=FLOOD
+NXST_FLOW reply:
+])
+
+    if test $i != 1; then
+        # Check that hard_age has appeared.  We need to do this separately
+        # from the above check because ofctl_strip removes it.  dump-flows
+        # only prints hard_age when it is different from the flow's duration
+        # (that is, the number of seconds from the time it was created),
+        # so we only check for it after we've refreshed the flow once.
+        AT_CHECK([grep dl_dst=50:54:00:00:00:07 stdout | grep -c hard_age],
+                 [0], [1
+])
+    fi
+done
+
+# Make sure that 15 seconds without refreshing makes the flow time out.
+ovs-appctl time/warp 5000
+ovs-appctl time/warp 5000
+ovs-appctl time/warp 5000
+    AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip | sort], [0], [dnl
+ table=1, priority=0 actions=FLOOD
+NXST_FLOW reply:
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([learning action - TCPv4 port learning])
 OVS_VSWITCHD_START(
   [add-port br0 p1 -- set Interface p1 type=dummy -- \
index a14c412..3eec947 100644 (file)
@@ -116,20 +116,20 @@ ADD_OF_PORTS([br0], [1], [2])
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
 
 # "in_port" defaults to OFPP_NONE if it's not specified.
-flow="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
+flow="icmp,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,icmp_type=8,icmp_code=0"
 AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
-actual=`tail -1 stdout | sed 's/Datapath actions: //'`
-
-expected="1,2,100"
-AT_CHECK([ovs-dpctl normalize-actions "$flow" "$expected"], [0], [stdout])
-mv stdout expout
-AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual"], [0], [expout])
+AT_CHECK([tail -1 stdout | sed 's/Datapath actions: //' | tr "," "\n" | sort -n], [0], [dnl
+1
+2
+100
+])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - DSCP])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
+ADD_OF_PORTS([br0], [9])
 AT_DATA([flows.txt], [dnl
 actions=output:65534,enqueue:1:1,enqueue:1:2,enqueue:1:2,enqueue:1:1,output:1,mod_nw_tos:0,output:1,output:65534
 ])
@@ -212,25 +212,6 @@ AT_CHECK([tail -1 stdout], [0],
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
-AT_SETUP([ofproto-dpif - set_tunnel])
-OVS_VSWITCHD_START
-ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [90])
-AT_DATA([flows.txt], [dnl
-in_port=90 actions=resubmit:1,resubmit:2,resubmit:3,resubmit:4,resubmit:5
-in_port=1 actions=set_tunnel:1,output:1
-in_port=2 actions=set_tunnel:1,output:2
-in_port=3 actions=set_tunnel:2,set_tunnel:3,output:3
-in_port=4 actions=set_tunnel:4,set_tunnel:3,output:4
-in_port=5 actions=set_tunnel:5
-])
-AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'tun_id(0x1),in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
-AT_CHECK([tail -1 stdout], [0],
-  [Datapath actions: set(tun_id(0x1)),1,2,set(tun_id(0x3)),3,4
-])
-OVS_VSWITCHD_STOP
-AT_CLEANUP
-
 AT_SETUP([ofproto-dpif - controller])
 OVS_VSWITCHD_START([dnl
    add-port br0 p1 -- set Interface p1 type=dummy
@@ -536,7 +517,7 @@ AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - fragment handling])
 OVS_VSWITCHD_START
-ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6])
+ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6], [90])
 AT_DATA([flows.txt], [dnl
 priority=75 tcp ip_frag=no    tp_dst=80 actions=output:1
 priority=75 tcp ip_frag=first tp_dst=80 actions=output:2
@@ -669,7 +650,7 @@ ovs-vsctl \
 AT_CHECK([ovs-ofctl add-flow br0 action=output:1])
 
 # "in_port" defaults to OFPP_NONE if it's not specified.
-flow="eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)"
+flow="icmp,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_ttl=128,icmp_type=8,icmp_code=0"
 AT_CHECK([ovs-appctl ofproto/trace br0 "$flow"], [0], [stdout])
 AT_CHECK_UNQUOTED([tail -1 stdout], [0],
   [Datapath actions: 1,2
@@ -1324,3 +1305,60 @@ in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - patch ports])
+OVS_VSWITCHD_START([add-br br1 \
+-- set bridge br1 datapath-type=dummy fail-mode=secure \
+-- add-port br1 pbr1 -- set int pbr1 type=patch options:peer=pbr0 \
+-- add-port br0 pbr0 -- set int pbr0 type=patch options:peer=pbr1])
+
+ADD_OF_PORTS([br0], [2])
+ADD_OF_PORTS([br1], [3])
+
+AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL,output:1,output:2])
+AT_CHECK([ovs-ofctl add-flow br1 actions=LOCAL,output:1,output:3])
+
+for i in $(seq 1 10); do
+    ovs-appctl netdev-dummy/receive br0 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+done
+
+for i in $(seq 1 5); do
+    ovs-appctl netdev-dummy/receive br1 'in_port(101),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+done
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:13 missed:2 lost:0
+       flows: 1
+       br0 65534/100: (dummy)
+       p2 2/2: (dummy)
+       pbr0 1/none: (patch: peer=pbr1)
+br1 (dummy@ovs-dummy):
+       lookups: hit:13 missed:2 lost:0
+       flows: 1
+       br1 65534/101: (dummy)
+       p3 3/3: (dummy)
+       pbr1 1/none: (patch: peer=pbr0)
+])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_USED], [0], [dnl
+in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:9, bytes:540, used:0.0s, actions:101,3,2
+]),
+AT_CHECK([ovs-appctl dpif/dump-flows br1 | STRIP_USED], [0], [dnl
+in_port(101),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:4, bytes:240, used:0.0s, actions:100,2,3
+])
+
+AT_CHECK([ovs-ofctl dump-ports br0 pbr0], [0], [dnl
+OFPST_PORT reply (xid=0x4): 1 ports
+  port  1: rx pkts=5, bytes=300, drop=0, errs=0, frame=0, over=0, crc=0
+           tx pkts=10, bytes=600, drop=0, errs=0, coll=0
+])
+
+AT_CHECK([ovs-ofctl dump-ports br1 pbr1], [0], [dnl
+OFPST_PORT reply (xid=0x4): 1 ports
+  port  1: rx pkts=10, bytes=600, drop=0, errs=0, frame=0, over=0, crc=0
+           tx pkts=5, bytes=300, drop=0, errs=0, coll=0
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index c330f2c..fa80c4d 100644 (file)
@@ -93,6 +93,7 @@ m4_include([tests/json.at])
 m4_include([tests/jsonrpc.at])
 m4_include([tests/jsonrpc-py.at])
 m4_include([tests/timeval.at])
+m4_include([tests/tunnel.at])
 m4_include([tests/lockfile.at])
 m4_include([tests/reconnect.at])
 m4_include([tests/ofproto.at])
diff --git a/tests/tunnel.at b/tests/tunnel.at
new file mode 100644 (file)
index 0000000..c708b30
--- /dev/null
@@ -0,0 +1,312 @@
+AT_BANNER([tunnel])
+
+AT_SETUP([tunnel - input])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+                    options:remote_ip=1.1.1.1 ofport_request=1\
+                    -- add-port br0 p2 -- set Interface p2 type=gre \
+                    options:local_ip=2.2.2.2 options:remote_ip=1.1.1.1 \
+                    ofport_request=2 \
+                    -- add-port br0 p3 -- set Interface p3 type=gre \
+                    options:remote_ip=2.2.2.2 ofport_request=3])
+AT_DATA([flows.txt], [dnl
+actions=IN_PORT
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (gre: remote_ip=1.1.1.1)
+       p2 2/1: (gre: local_ip=2.2.2.2, remote_ip=1.1.1.1)
+       p3 3/1: (gre: remote_ip=2.2.2.2)
+])
+
+dnl remote_ip
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=1.2.3.4,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
+])
+
+dnl local_ip, remote_ip
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,src=2.2.2.2,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
+])
+
+dnl reconfigure, local_ip, remote_ip
+AT_CHECK([ovs-vsctl set Interface p2 type=gre options:local_ip=2.2.2.3 \
+          options:df_default=false options:ttl=1 options:csum=true \
+          -- set Interface p3 type=gre64])
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (gre: remote_ip=1.1.1.1)
+       p2 2/1: (gre: csum=true, df_default=false, local_ip=2.2.2.3, remote_ip=1.1.1.1, ttl=1)
+       p3 3/64: (gre64: remote_ip=2.2.2.2)
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.3,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,src=2.2.2.3,dst=1.1.1.1,tos=0x0,ttl=1,flags(csum))),1
+])
+
+dnl nonexistent tunnel
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=5.5.5.5,dst=6.6.6.6,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl
+Invalid flow
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - ECN decapsulation])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+                    options:remote_ip=1.1.1.1 ofport_request=1 \
+                    -- add-port br0 p2 -- set Interface p2 type=dummy \
+                    ofport_request=2])
+AT_DATA([flows.txt], [dnl
+actions=2
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (gre: remote_ip=1.1.1.1)
+       p2 2/2: (dummy)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 2
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - output])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+                    options:remote_ip=1.1.1.1 options:local_ip=2.2.2.2 \
+                    options:key=5 ofport_request=1\
+                    -- add-port br0 p2 -- set Interface p2 type=dummy \
+                    ofport_request=2 ofport_request=2])
+AT_DATA([flows.txt], [dnl
+actions=output:1
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1)
+       p2 2/2: (dummy)
+])
+
+dnl Basic
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
+])
+
+dnl ECN
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,tos=0x1,ttl=64,flags(df,key))),1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - ToS and TTL inheritance])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gre \
+                    options:remote_ip=1.1.1.1 options:tos=inherit \
+                    options:ttl=inherit ofport_request=1 \
+                    -- add-port br0 p2 -- set Interface p2 type=dummy \
+                    ofport_request=2 ofport_request=2])
+AT_DATA([flows.txt], [dnl
+actions=output:1
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (gre: remote_ip=1.1.1.1, tos=inherit, ttl=inherit)
+       p2 2/2: (dummy)
+])
+
+dnl Basic
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=4,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x4,ttl=128,flags(df))),1
+])
+
+dnl ECN
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=5,ttl=128,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x5,ttl=128,flags(df))),1
+])
+
+dnl non-IP
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x0,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df))),1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - set_tunnel])
+OVS_VSWITCHD_START([dnl
+    add-port br0 p1 -- set Interface p1 type=gre options:key=flow \
+        options:remote_ip=1.1.1.1 ofport_request=1 \
+    -- add-port br0 p2 -- set Interface p2 type=gre options:key=flow \
+        options:remote_ip=2.2.2.2 ofport_request=2 \
+    -- add-port br0 p3 -- set Interface p3 type=gre options:key=flow \
+        options:remote_ip=3.3.3.3 ofport_request=3 \
+    -- add-port br0 p4 -- set Interface p4 type=gre options:key=flow \
+        options:remote_ip=4.4.4.4 ofport_request=4])
+AT_DATA([flows.txt], [dnl
+actions=set_tunnel:1,output:1,set_tunnel:2,output:2,set_tunnel:3,output:3,set_tunnel:5,output:4
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
+       p2 2/1: (gre: key=flow, remote_ip=2.2.2.2)
+       p3 3/1: (gre: key=flow, remote_ip=3.3.3.3)
+       p4 4/1: (gre: key=flow, remote_ip=4.4.4.4)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
+set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x2,src=0.0.0.0,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x3,src=0.0.0.0,dst=3.3.3.3,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x5,src=0.0.0.0,dst=4.4.4.4,tos=0x0,ttl=64,flags(df,key))),1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - key])
+OVS_VSWITCHD_START([dnl
+    add-port br0 p1 -- set Interface p1 type=gre options:key=1 \
+        options:remote_ip=1.1.1.1 ofport_request=1 \
+    -- add-port br0 p2 -- set Interface p2 type=gre options:in_key=2 \
+        options:out_key=3 options:remote_ip=1.1.1.1 ofport_request=2 \
+    -- add-port br0 p3 -- set Interface p3 type=gre options:out_key=5 \
+        options:remote_ip=1.1.1.1 ofport_request=3])
+AT_DATA([flows.txt], [dnl
+actions=IN_PORT,output:1,output:2,output:3
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (gre: key=1, remote_ip=1.1.1.1)
+       p2 2/1: (gre: in_key=2, out_key=3, remote_ip=1.1.1.1)
+       p3 3/1: (gre: out_key=5, remote_ip=1.1.1.1)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
+set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x3,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x5,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
+set(tunnel(tun_id=0x3,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x5,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [Datapath actions: dnl
+set(tunnel(tun_id=0x5,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x1,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1,dnl
+set(tunnel(tun_id=0x3,src=0.0.0.0,dst=1.1.1.1,tos=0x0,ttl=64,flags(df,key))),1
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0xf,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [2], [ignore], [dnl
+Invalid flow
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - key match])
+OVS_VSWITCHD_START([dnl
+    add-port br0 p1 -- set Interface p1 type=gre options:key=flow \
+        options:remote_ip=1.1.1.1 ofport_request=1 \
+    -- add-port br0 p2 -- set Interface p2 type=gre options:key=3 \
+        options:remote_ip=3.3.3.3 ofport_request=2 \
+    -- add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3 \
+    -- add-port br0 p4 -- set Interface p4 type=dummy ofport_request=4 \
+    -- add-port br0 p5 -- set Interface p5 type=dummy ofport_request=5])
+AT_DATA([flows.txt], [dnl
+tun_id=2,actions=output:3
+tun_id=3,actions=output:4,set_tunnel:2,resubmit:99,set_tunnel:4,output:2,resubmit:99
+tun_id=4,actions=output:5
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+       lookups: hit:0 missed:0 lost:0
+       flows: 0
+       br0 65534/100: (dummy)
+       p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
+       p2 2/1: (gre: key=3, remote_ip=3.3.3.3)
+       p3 3/3: (dummy)
+       p4 4/4: (dummy)
+       p5 5/5: (dummy)
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x2,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 3
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x3,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 4,3,set(tunnel(tun_id=0x3,src=0.0.0.0,dst=3.3.3.3,tos=0x0,ttl=64,flags(df,key))),1,5
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x3,src=3.3.3.3,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: 4,3,5
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+       - Sends "packet-in" messages to the OpenFlow controller.
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 7e1057a..97294af 100644 (file)
@@ -195,7 +195,7 @@ By default, no wrapper is used.
 .
 .IP
 Each of the wrappers can expose bugs in Open vSwitch that lead to
-incorrect operation, including crashes.  The \fBvalgring\fR and
+incorrect operation, including crashes.  The \fBvalgrind\fR and
 \fBstrace\fR wrappers greatly slow daemon operations so they should
 not be used in production.  They also produce voluminous logs that can
 quickly fill small disk partitions.  The \fBglibc\fR wrapper is less
@@ -400,7 +400,7 @@ for the other Open vSwitch programs that it runs.
 .
 \fBovs\-ctl\fR uses the following files:
 .
-.IP "\fBovs\-lib.sh"
+.IP "\fBovs\-lib"
 Shell function library used internally by \fBovs\-ctl\fR.  It must be
 installed in the same directory as \fBovs\-ctl\fR.
 .
index 2ffe3d5..4d8c39f 100644 (file)
@@ -368,27 +368,28 @@ open_vconn_socket(const char *name, struct vconn **vconnp)
     return error;
 }
 
+enum open_target { MGMT, SNOOP };
+
 static enum ofputil_protocol
-open_vconn__(const char *name, const char *default_suffix,
+open_vconn__(const char *name, enum open_target target,
              struct vconn **vconnp)
 {
+    const char *suffix = target == MGMT ? "mgmt" : "snoop";
     char *datapath_name, *datapath_type, *socket_name;
     enum ofputil_protocol protocol;
     char *bridge_path;
     int ofp_version;
     int error;
 
-    bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, default_suffix);
+    bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, suffix);
 
     ofproto_parse_name(name, &datapath_name, &datapath_type);
-    socket_name = xasprintf("%s/%s.%s",
-                            ovs_rundir(), datapath_name, default_suffix);
+    socket_name = xasprintf("%s/%s.%s", ovs_rundir(), datapath_name, suffix);
     free(datapath_name);
     free(datapath_type);
 
     if (strchr(name, ':')) {
-        run(vconn_open_block(name, get_allowed_ofp_versions(), DSCP_DEFAULT,
-                             vconnp),
+        run(vconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, vconnp),
             "connecting to %s", name);
     } else if (!open_vconn_socket(name, vconnp)) {
         /* Fall Through. */
@@ -400,6 +401,10 @@ open_vconn__(const char *name, const char *default_suffix,
         ovs_fatal(0, "%s is not a bridge or a socket", name);
     }
 
+    if (target == SNOOP) {
+        vconn_set_recv_any_version(*vconnp);
+    }
+
     free(bridge_path);
     free(socket_name);
 
@@ -422,7 +427,7 @@ open_vconn__(const char *name, const char *default_suffix,
 static enum ofputil_protocol
 open_vconn(const char *name, struct vconn **vconnp)
 {
-    return open_vconn__(name, "mgmt", vconnp);
+    return open_vconn__(name, MGMT, vconnp);
 }
 
 static void
@@ -1457,7 +1462,7 @@ ofctl_snoop(int argc OVS_UNUSED, char *argv[])
 {
     struct vconn *vconn;
 
-    open_vconn__(argv[1], "snoop", &vconn);
+    open_vconn__(argv[1], SNOOP, &vconn);
     monitor_vconn(vconn);
 }