diff mbox

[ovs-dev,01/12] datapath: compat: Refactor egress tunnel info

Message ID 1462404940-97654-1-git-send-email-pshelar@ovn.org
State Changes Requested
Headers show

Commit Message

Pravin Shelar May 4, 2016, 11:35 p.m. UTC
upstream tunnel egress info is retrieved using ndo_fill_metadata_dst.
Since we do not have it on older kernel we need to keep vport operation
to do same on these kernels.
Following patch try to merge these to operations into one to avoid code
duplication.

Signed-off-by: Pravin B Shelar <pshelar@ovn.org>
---
 acinclude.m4                                    |  1 +
 datapath/actions.c                              |  9 +--
 datapath/datapath.c                             |  5 +-
 datapath/datapath.h                             |  1 -
 datapath/flow_netlink.c                         | 18 +++---
 datapath/flow_netlink.h                         |  5 +-
 datapath/linux/compat/dev-openvswitch.c         | 29 +++++++++
 datapath/linux/compat/geneve.c                  | 26 +++++++++
 datapath/linux/compat/include/linux/netdevice.h |  4 ++
 datapath/linux/compat/include/net/geneve.h      |  3 +
 datapath/linux/compat/include/net/gre.h         |  3 +
 datapath/linux/compat/include/net/lisp.h        |  3 +
 datapath/linux/compat/include/net/stt.h         |  3 +
 datapath/linux/compat/include/net/vxlan.h       |  2 +
 datapath/linux/compat/ip_gre.c                  | 49 +++++++++++++---
 datapath/linux/compat/lisp.c                    | 43 ++++++++++++++
 datapath/linux/compat/stt.c                     | 49 +++++++++++++---
 datapath/linux/compat/vxlan.c                   | 44 ++++++++++++++
 datapath/vport-geneve.c                         | 14 +----
 datapath/vport-gre.c                            |  9 +--
 datapath/vport-lisp.c                           | 14 +----
 datapath/vport-stt.c                            | 14 +----
 datapath/vport-vxlan.c                          | 20 +------
 datapath/vport.c                                | 78 +------------------------
 datapath/vport.h                                | 15 +----
 25 files changed, 263 insertions(+), 198 deletions(-)

Comments

Pravin Shelar May 4, 2016, 11:35 p.m. UTC | #1
This patch series update OVS compat layer to handle IPv6 UDP based
tunnels. While doing this various fixes and updates to tunnel code
are also brought in.

Pravin B Shelar (12):
  datapath: compat: Refactor egress tunnel info
  datapath: compat: Update iptunnel_pull_header
  datapath: compat: Introduce dst-cache for tunnels
  datapath: compat: Update tunnel_handle_offloads()
  datapath: compat: Update udp-tunnel-xmit
  datapath: compat: Add support for ipv6 to ovs_udp_tun_rx_dst
  datapath: compat: Update udp_sock_create
  datapath: compat: Update setup_udp_tunnel_sock
  datapath: compat: Remove unnecessary iptunnel_xmit() declaration.
  datapath: compat: Prepare tnl-segmentation for ipv6.
  datapath: compat: Add support for IPv6 UDP tunnel segmentation.
  datapath: compat: Add support for IPv6 Geneve and VxLAN.

 NEWS                                               |    1 +
 acinclude.m4                                       |   22 +-
 datapath/actions.c                                 |    9 +-
 datapath/datapath.c                                |    5 +-
 datapath/datapath.h                                |    1 -
 datapath/flow.c                                    |   20 +-
 datapath/flow.h                                    |    1 +
 datapath/flow_netlink.c                            |  150 +-
 datapath/flow_netlink.h                            |    5 +-
 datapath/linux/Modules.mk                          |    3 +
 datapath/linux/compat/dev-openvswitch.c            |   29 +
 datapath/linux/compat/dst_cache.c                  |  169 ++
 datapath/linux/compat/geneve.c                     |  963 ++++++++---
 datapath/linux/compat/gre.c                        |    2 +-
 datapath/linux/compat/gso.c                        |   60 +-
 datapath/linux/compat/gso.h                        |    1 +
 datapath/linux/compat/include/linux/if_link.h      |   20 +
 .../linux/compat/include/linux/netdev_features.h   |    4 +
 datapath/linux/compat/include/linux/netdevice.h    |   13 +
 datapath/linux/compat/include/linux/percpu.h       |   11 +
 datapath/linux/compat/include/linux/skbuff.h       |    7 +
 datapath/linux/compat/include/net/dst_cache.h      |  104 ++
 datapath/linux/compat/include/net/dst_metadata.h   |   29 +-
 datapath/linux/compat/include/net/geneve.h         |    3 +
 datapath/linux/compat/include/net/gre.h            |    3 +
 datapath/linux/compat/include/net/ip6_fib.h        |   36 +
 datapath/linux/compat/include/net/ip6_tunnel.h     |    2 +-
 datapath/linux/compat/include/net/ip_tunnels.h     |   73 +-
 datapath/linux/compat/include/net/ipv6.h           |   32 +
 datapath/linux/compat/include/net/lisp.h           |    3 +
 datapath/linux/compat/include/net/stt.h            |    3 +
 datapath/linux/compat/include/net/udp.h            |    9 +
 datapath/linux/compat/include/net/udp_tunnel.h     |  130 +-
 datapath/linux/compat/include/net/vxlan.h          |  282 +++-
 datapath/linux/compat/ip_gre.c                     |   69 +-
 datapath/linux/compat/ip_tunnels_core.c            |   51 +-
 datapath/linux/compat/lisp.c                       |   52 +-
 datapath/linux/compat/stt.c                        |   52 +-
 datapath/linux/compat/udp_tunnel.c                 |  193 ++-
 datapath/linux/compat/utils.c                      |   25 +
 datapath/linux/compat/vxlan.c                      | 1699 ++++++++++----------
 datapath/vport-geneve.c                            |   14 +-
 datapath/vport-gre.c                               |    9 +-
 datapath/vport-lisp.c                              |   14 +-
 datapath/vport-stt.c                               |   14 +-
 datapath/vport-vxlan.c                             |   20 +-
 datapath/vport.c                                   |   78 +-
 datapath/vport.h                                   |   15 +-
 48 files changed, 2971 insertions(+), 1539 deletions(-)
 create mode 100644 datapath/linux/compat/dst_cache.c
 create mode 100644 datapath/linux/compat/include/net/dst_cache.h
 create mode 100644 datapath/linux/compat/include/net/ip6_fib.h
Jesse Gross May 20, 2016, 7:14 p.m. UTC | #2
On Wed, May 4, 2016 at 4:35 PM, Pravin B Shelar <pshelar@ovn.org> wrote:
> diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
> index 6ffcc53..0003f89 100644
> --- a/datapath/flow_netlink.c
> +++ b/datapath/flow_netlink.c
>  static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
> @@ -2390,10 +2389,7 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
>                 if (!start)
>                         return -EMSGSIZE;
>
> -               err = ipv4_tun_to_nlattr(skb, &tun_info->key,
> -                                        tun_info->options_len ?
> -                                            ip_tunnel_info_opts(tun_info) : NULL,
> -                                        tun_info->options_len);
> +               err = ovs_nla_put_tunnel_info(skb, tun_info);
>                 if (err)
>                         return err;
>                 nla_nest_end(skb, start);

I think this patch is primarily a backport of fc4099f1 ("openvswitch:
Fix egress tunnel info.") - which would be good to note in the commit
message. However, in doing so, it introduces the problem addressed by
e905eabc ("openvswitch: correct encoding of set tunnel action
attributes"). It would be good to include that as a patch in this
series.

> diff --git a/datapath/linux/compat/geneve.c b/datapath/linux/compat/geneve.c
> index 0399de7..f38d4a3 100644
> --- a/datapath/linux/compat/geneve.c
> +++ b/datapath/linux/compat/geneve.c

There were some other changes to the Geneve routing code that don't
appear here. They are included in the last patch but since they were
associated with this original upstream change, it seems more
appropriate here.

>  static void ipgre_tap_setup(struct net_device *dev)
> diff --git a/datapath/linux/compat/lisp.c b/datapath/linux/compat/lisp.c
> index f1f50ae..0a92dca 100644
> --- a/datapath/linux/compat/lisp.c
> +++ b/datapath/linux/compat/lisp.c
> +static int egress_ipv4_tun_info(struct net_device *dev, struct sk_buff *skb,
> +               struct ip_tunnel_info *info,
> +               __be16 sport, __be16 dport)
> +{
> +       struct lisp_dev *lisp = netdev_priv(dev);
> +       struct rtable *rt;
> +       struct flowi4 fl4;
> +
> +       memset(&fl4, 0, sizeof(fl4));
> +       fl4.flowi4_tos = RT_TOS(info->key.tos);
> +       fl4.flowi4_mark = skb->mark;
> +       fl4.flowi4_proto = IPPROTO_UDP;
> +       fl4.daddr = info->key.u.ipv4.dst;
> +
> +       rt = ip_route_output_key(lisp->net, &fl4);
> +       if (IS_ERR(rt))
> +               return PTR_ERR(rt);
> +       ip_rt_put(rt);
> +
> +       info->key.u.ipv4.src = fl4.saddr;
> +       info->key.tp_src = sport;
> +       info->key.tp_dst = dport;
> +       return 0;
> +}

Was this supposed to be used by the xmit function as well, like you
did with STT?

When comparing this to STT, I also noticed that it seems STT isn't
reporting the L4 ports.

> diff --git a/datapath/vport.c b/datapath/vport.c
> index 44b9dfb..c789a40 100644
> --- a/datapath/vport.c
> +++ b/datapath/vport.c
> @@ -41,6 +41,7 @@
>  #include "gso.h"
>  #include "vport.h"
>  #include "vport-internal_dev.h"
> +#include "vport-netdev.h"

Extra header inclusion? (It's not upstream either.)
Pravin Shelar May 23, 2016, 4:35 a.m. UTC | #3
On Fri, May 20, 2016 at 12:14 PM, Jesse Gross <jesse@kernel.org> wrote:
> On Wed, May 4, 2016 at 4:35 PM, Pravin B Shelar <pshelar@ovn.org> wrote:
>> diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
>> index 6ffcc53..0003f89 100644
>> --- a/datapath/flow_netlink.c
>> +++ b/datapath/flow_netlink.c
>>  static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
>> @@ -2390,10 +2389,7 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
>>                 if (!start)
>>                         return -EMSGSIZE;
>>
>> -               err = ipv4_tun_to_nlattr(skb, &tun_info->key,
>> -                                        tun_info->options_len ?
>> -                                            ip_tunnel_info_opts(tun_info) : NULL,
>> -                                        tun_info->options_len);
>> +               err = ovs_nla_put_tunnel_info(skb, tun_info);
>>                 if (err)
>>                         return err;
>>                 nla_nest_end(skb, start);
>
> I think this patch is primarily a backport of fc4099f1 ("openvswitch:
> Fix egress tunnel info.") - which would be good to note in the commit
> message. However, in doing so, it introduces the problem addressed by
> e905eabc ("openvswitch: correct encoding of set tunnel action
> attributes"). It would be good to include that as a patch in this
> series.
>
ok, I will add separate patch for this fix.

>> diff --git a/datapath/linux/compat/geneve.c b/datapath/linux/compat/geneve.c
>> index 0399de7..f38d4a3 100644
>> --- a/datapath/linux/compat/geneve.c
>> +++ b/datapath/linux/compat/geneve.c
>
> There were some other changes to the Geneve routing code that don't
> appear here. They are included in the last patch but since they were
> associated with this original upstream change, it seems more
> appropriate here.
>
ok.

>>  static void ipgre_tap_setup(struct net_device *dev)
>> diff --git a/datapath/linux/compat/lisp.c b/datapath/linux/compat/lisp.c
>> index f1f50ae..0a92dca 100644
>> --- a/datapath/linux/compat/lisp.c
>> +++ b/datapath/linux/compat/lisp.c
>> +static int egress_ipv4_tun_info(struct net_device *dev, struct sk_buff *skb,
>> +               struct ip_tunnel_info *info,
>> +               __be16 sport, __be16 dport)
>> +{
>> +       struct lisp_dev *lisp = netdev_priv(dev);
>> +       struct rtable *rt;
>> +       struct flowi4 fl4;
>> +
>> +       memset(&fl4, 0, sizeof(fl4));
>> +       fl4.flowi4_tos = RT_TOS(info->key.tos);
>> +       fl4.flowi4_mark = skb->mark;
>> +       fl4.flowi4_proto = IPPROTO_UDP;
>> +       fl4.daddr = info->key.u.ipv4.dst;
>> +
>> +       rt = ip_route_output_key(lisp->net, &fl4);
>> +       if (IS_ERR(rt))
>> +               return PTR_ERR(rt);
>> +       ip_rt_put(rt);
>> +
>> +       info->key.u.ipv4.src = fl4.saddr;
>> +       info->key.tp_src = sport;
>> +       info->key.tp_dst = dport;
>> +       return 0;
>> +}
>
> Was this supposed to be used by the xmit function as well, like you
> did with STT?
>
ok, I refactored code to do same as STT.

> When comparing this to STT, I also noticed that it seems STT isn't
> reporting the L4 ports.
>
ok. I will fix it.

>> diff --git a/datapath/vport.c b/datapath/vport.c
>> index 44b9dfb..c789a40 100644
>> --- a/datapath/vport.c
>> +++ b/datapath/vport.c
>> @@ -41,6 +41,7 @@
>>  #include "gso.h"
>>  #include "vport.h"
>>  #include "vport-internal_dev.h"
>> +#include "vport-netdev.h"
>
> Extra header inclusion? (It's not upstream either.)

I removed it.
diff mbox

Patch

diff --git a/acinclude.m4 b/acinclude.m4
index 23015fe..fc9f11e 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -398,6 +398,7 @@  AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
 
   OVS_GREP_IFELSE([$KSRC/include/linux/net.h], [sock_create_kern.*net],
                   [OVS_DEFINE([HAVE_SOCK_CREATE_KERN_NET])])
+  OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [ndo_fill_metadata_dst])
   OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_disable_lro])
   OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_get_stats])
   OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_get_by_index_rcu])
diff --git a/datapath/actions.c b/datapath/actions.c
index dcf8591..f7b11f2 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -770,7 +770,6 @@  static int output_userspace(struct datapath *dp, struct sk_buff *skb,
 			    struct sw_flow_key *key, const struct nlattr *attr,
 			    const struct nlattr *actions, int actions_len)
 {
-	struct ip_tunnel_info info;
 	struct dp_upcall_info upcall;
 	const struct nlattr *a;
 	int rem;
@@ -798,11 +797,9 @@  static int output_userspace(struct datapath *dp, struct sk_buff *skb,
 			if (vport) {
 				int err;
 
-				upcall.egress_tun_info = &info;
-				err = ovs_vport_get_egress_tun_info(vport, skb,
-								    &upcall);
-				if (err)
-					upcall.egress_tun_info = NULL;
+				err = dev_fill_metadata_dst(vport->dev, skb);
+				if (!err)
+					upcall.egress_tun_info = skb_tunnel_info(skb);
 			}
 
 			break;
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 5bec072..31203a3 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -497,9 +497,8 @@  static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
 
 	if (upcall_info->egress_tun_info) {
 		nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_EGRESS_TUN_KEY);
-		err = ovs_nla_put_egress_tunnel_key(user_skb,
-						    upcall_info->egress_tun_info,
-						    upcall_info->egress_tun_opts);
+		err = ovs_nla_put_tunnel_info(user_skb,
+					      upcall_info->egress_tun_info);
 		BUG_ON(err);
 		nla_nest_end(user_skb, nla);
 	}
diff --git a/datapath/datapath.h b/datapath/datapath.h
index ceb3372..9da6093 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -118,7 +118,6 @@  struct ovs_skb_cb {
  */
 struct dp_upcall_info {
 	struct ip_tunnel_info *egress_tun_info;
-	const void *egress_tun_opts;
 	const struct nlattr *userdata;
 	const struct nlattr *actions;
 	int actions_len;
diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index 6ffcc53..0003f89 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -719,7 +719,7 @@  static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
 	if ((output->tun_flags & TUNNEL_OAM) &&
 	    nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_OAM))
 		return -EMSGSIZE;
-	if (tun_opts) {
+	if (swkey_tun_opts_len) {
 		if (output->tun_flags & TUNNEL_GENEVE_OPT &&
 		    nla_put(skb, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,
 			    swkey_tun_opts_len, tun_opts))
@@ -751,13 +751,12 @@  static int ipv4_tun_to_nlattr(struct sk_buff *skb,
 	return 0;
 }
 
-int ovs_nla_put_egress_tunnel_key(struct sk_buff *skb,
-				  const struct ip_tunnel_info *egress_tun_info,
-				  const void *egress_tun_opts)
+int ovs_nla_put_tunnel_info(struct sk_buff *skb,
+			    struct ip_tunnel_info *tun_info)
 {
-	return __ipv4_tun_to_nlattr(skb, &egress_tun_info->key,
-				    egress_tun_opts,
-				    egress_tun_info->options_len);
+	return __ipv4_tun_to_nlattr(skb, &tun_info->key,
+				    ip_tunnel_info_opts(tun_info),
+				    tun_info->options_len);
 }
 
 static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
@@ -2390,10 +2389,7 @@  static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
 		if (!start)
 			return -EMSGSIZE;
 
-		err = ipv4_tun_to_nlattr(skb, &tun_info->key,
-					 tun_info->options_len ?
-					     ip_tunnel_info_opts(tun_info) : NULL,
-					 tun_info->options_len);
+		err = ovs_nla_put_tunnel_info(skb, tun_info);
 		if (err)
 			return err;
 		nla_nest_end(skb, start);
diff --git a/datapath/flow_netlink.h b/datapath/flow_netlink.h
index 1c01755..1c4208b 100644
--- a/datapath/flow_netlink.h
+++ b/datapath/flow_netlink.h
@@ -55,9 +55,8 @@  int ovs_nla_put_mask(const struct sw_flow *flow, struct sk_buff *skb);
 int ovs_nla_get_match(struct net *, struct sw_flow_match *,
 		      const struct nlattr *key, const struct nlattr *mask,
 		      bool log);
-int ovs_nla_put_egress_tunnel_key(struct sk_buff *skb,
-				  const struct ip_tunnel_info *egress_tun_info,
-				  const void *egress_tun_opts);
+int ovs_nla_put_tunnel_info(struct sk_buff *skb,
+			    struct ip_tunnel_info *tun_info);
 
 bool ovs_nla_get_ufid(struct sw_flow_id *, const struct nlattr *, bool log);
 int ovs_nla_get_identifier(struct sw_flow_id *sfid, const struct nlattr *ufid,
diff --git a/datapath/linux/compat/dev-openvswitch.c b/datapath/linux/compat/dev-openvswitch.c
index 0d2088b..544c5e1 100644
--- a/datapath/linux/compat/dev-openvswitch.c
+++ b/datapath/linux/compat/dev-openvswitch.c
@@ -3,6 +3,11 @@ 
 #include <linux/version.h>
 #include <net/rtnetlink.h>
 
+#include "gso.h"
+#include "vport.h"
+#include "vport-internal_dev.h"
+#include "vport-netdev.h"
+
 #ifndef HAVE_DEV_DISABLE_LRO
 
 #ifdef NETIF_F_LRO
@@ -54,3 +59,27 @@  int rpl_rtnl_delete_link(struct net_device *dev)
 #endif
 	return 0;
 }
+
+#ifndef HAVE_NDO_FILL_METADATA_DST
+int ovs_dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+	struct ip_tunnel_info *info;
+	struct vport *vport;
+
+	vport = ovs_netdev_get_vport(dev);
+	if (!vport)
+		return -EINVAL;
+
+	if (!vport->ops->fill_metadata_dst)
+		return -EINVAL;
+
+	info = skb_tunnel_info(skb);
+	if (!info)
+		return -ENOMEM;
+	if (unlikely(!(info->mode & IP_TUNNEL_INFO_TX)))
+		return -EINVAL;
+
+	return vport->ops->fill_metadata_dst(dev, skb);
+}
+EXPORT_SYMBOL_GPL(ovs_dev_fill_metadata_dst);
+#endif
diff --git a/datapath/linux/compat/geneve.c b/datapath/linux/compat/geneve.c
index 0399de7..f38d4a3 100644
--- a/datapath/linux/compat/geneve.c
+++ b/datapath/linux/compat/geneve.c
@@ -765,6 +765,29 @@  static int geneve_change_mtu(struct net_device *dev, int new_mtu)
 	return __geneve_change_mtu(dev, new_mtu, true);
 }
 
+int ovs_geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+	struct ip_tunnel_info *info = skb_tunnel_info(skb);
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct rtable *rt;
+	struct flowi4 fl4;
+
+	if (ip_tunnel_info_af(info) != AF_INET)
+		return -EINVAL;
+
+	rt = geneve_get_rt(skb, dev, &fl4, info);
+	if (IS_ERR(rt))
+		return PTR_ERR(rt);
+
+	ip_rt_put(rt);
+	info->key.u.ipv4.src = fl4.saddr;
+	info->key.tp_src = udp_flow_src_port(geneve->net, skb,
+					     1, USHRT_MAX, true);
+	info->key.tp_dst = geneve->dst_port;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ovs_geneve_fill_metadata_dst);
+
 static const struct net_device_ops geneve_netdev_ops = {
 	.ndo_init		= geneve_init,
 	.ndo_uninit		= geneve_uninit,
@@ -775,6 +798,9 @@  static const struct net_device_ops geneve_netdev_ops = {
 	.ndo_change_mtu		= geneve_change_mtu,
 	.ndo_validate_addr	= eth_validate_addr,
 	.ndo_set_mac_address	= eth_mac_addr,
+#ifdef HAVE_NDO_FILL_METADATA_DST
+	.ndo_fill_metadata_dst  = geneve_fill_metadata_dst,
+#endif
 };
 
 static void geneve_get_drvinfo(struct net_device *dev,
diff --git a/datapath/linux/compat/include/linux/netdevice.h b/datapath/linux/compat/include/linux/netdevice.h
index e9fa995..ac612ef 100644
--- a/datapath/linux/compat/include/linux/netdevice.h
+++ b/datapath/linux/compat/include/linux/netdevice.h
@@ -249,4 +249,8 @@  do {								\
 
 #endif
 
+#ifndef HAVE_NDO_FILL_METADATA_DST
+#define dev_fill_metadata_dst ovs_dev_fill_metadata_dst
+int ovs_dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
+#endif
 #endif /* __LINUX_NETDEVICE_WRAPPER_H */
diff --git a/datapath/linux/compat/include/net/geneve.h b/datapath/linux/compat/include/net/geneve.h
index 550f4a7..a7f2252 100644
--- a/datapath/linux/compat/include/net/geneve.h
+++ b/datapath/linux/compat/include/net/geneve.h
@@ -91,4 +91,7 @@  netdev_tx_t rpl_geneve_xmit(struct sk_buff *skb);
 #define geneve_init_module rpl_geneve_init_module
 #define geneve_cleanup_module rpl_geneve_cleanup_module
 
+#define geneve_fill_metadata_dst ovs_geneve_fill_metadata_dst
+int ovs_geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
+
 #endif /*ifdef__NET_GENEVE_H */
diff --git a/datapath/linux/compat/include/net/gre.h b/datapath/linux/compat/include/net/gre.h
index 60618df..8082a98 100644
--- a/datapath/linux/compat/include/net/gre.h
+++ b/datapath/linux/compat/include/net/gre.h
@@ -66,4 +66,7 @@  netdev_tx_t rpl_gre_fb_xmit(struct sk_buff *skb);
 #define ipgre_init rpl_ipgre_init
 #define ipgre_fini rpl_ipgre_fini
 
+#define gre_fill_metadata_dst ovs_gre_fill_metadata_dst
+int ovs_gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
+
 #endif
diff --git a/datapath/linux/compat/include/net/lisp.h b/datapath/linux/compat/include/net/lisp.h
index b8af17d..6b43c77 100644
--- a/datapath/linux/compat/include/net/lisp.h
+++ b/datapath/linux/compat/include/net/lisp.h
@@ -21,4 +21,7 @@  void rpl_lisp_cleanup_module(void);
 #define lisp_xmit rpl_lisp_xmit
 netdev_tx_t rpl_lisp_xmit(struct sk_buff *skb);
 
+#define lisp_fill_metadata_dst ovs_lisp_fill_metadata_dst
+int ovs_lisp_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
+
 #endif /*ifdef__NET_LISP_H */
diff --git a/datapath/linux/compat/include/net/stt.h b/datapath/linux/compat/include/net/stt.h
index 28d4dc5..d2e63d1 100644
--- a/datapath/linux/compat/include/net/stt.h
+++ b/datapath/linux/compat/include/net/stt.h
@@ -64,4 +64,7 @@  static inline netdev_tx_t ovs_stt_xmit(struct sk_buff *skb)
 #define stt_init_module ovs_stt_init_module
 #define stt_cleanup_module ovs_stt_cleanup_module
 
+#define stt_fill_metadata_dst ovs_stt_fill_metadata_dst
+int ovs_stt_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
+
 #endif /*ifdef__NET_STT_H */
diff --git a/datapath/linux/compat/include/net/vxlan.h b/datapath/linux/compat/include/net/vxlan.h
index 75a5a7a..589e6f2 100644
--- a/datapath/linux/compat/include/net/vxlan.h
+++ b/datapath/linux/compat/include/net/vxlan.h
@@ -274,4 +274,6 @@  netdev_tx_t rpl_vxlan_xmit(struct sk_buff *skb);
 #define vxlan_init_module rpl_vxlan_init_module
 #define vxlan_cleanup_module rpl_vxlan_cleanup_module
 
+#define vxlan_fill_metadata_dst ovs_vxlan_fill_metadata_dst
+int ovs_vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
 #endif
diff --git a/datapath/linux/compat/ip_gre.c b/datapath/linux/compat/ip_gre.c
index 2e13843..67db9af 100644
--- a/datapath/linux/compat/ip_gre.c
+++ b/datapath/linux/compat/ip_gre.c
@@ -256,11 +256,26 @@  static void build_header(struct sk_buff *skb, int hdr_len, __be16 flags,
 	ovs_skb_set_inner_protocol(skb, proto);
 }
 
+static struct rtable *gre_get_rt(struct sk_buff *skb,
+				 struct net_device *dev,
+				 struct flowi4 *fl,
+				 const struct ip_tunnel_key *key)
+{
+	struct net *net = dev_net(dev);
+
+	memset(fl, 0, sizeof(*fl));
+	fl->daddr = key->u.ipv4.dst;
+	fl->saddr = key->u.ipv4.src;
+	fl->flowi4_tos = RT_TOS(key->tos);
+	fl->flowi4_mark = skb->mark;
+	fl->flowi4_proto = IPPROTO_GRE;
+
+	return ip_route_output_key(net, fl);
+}
 
 netdev_tx_t rpl_gre_fb_xmit(struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
-	struct net *net = dev_net(dev);
 	struct ip_tunnel_info *tun_info;
 	const struct ip_tunnel_key *key;
 	struct flowi4 fl;
@@ -276,14 +291,8 @@  netdev_tx_t rpl_gre_fb_xmit(struct sk_buff *skb)
 		goto err_free_skb;
 
 	key = &tun_info->key;
-	memset(&fl, 0, sizeof(fl));
-	fl.daddr = key->u.ipv4.dst;
-	fl.saddr = key->u.ipv4.src;
-	fl.flowi4_tos = RT_TOS(key->tos);
-	fl.flowi4_mark = skb->mark;
-	fl.flowi4_proto = IPPROTO_GRE;
-
-	rt = ip_route_output_key(net, &fl);
+
+	rt = gre_get_rt(skb, dev, &fl, key);
 	if (IS_ERR(rt))
 		goto err_free_skb;
 
@@ -459,6 +468,25 @@  static netdev_tx_t gre_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 	return NETDEV_TX_OK;
 }
 
+int ovs_gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+	struct ip_tunnel_info *info = skb_tunnel_info(skb);
+	struct rtable *rt;
+	struct flowi4 fl4;
+
+	if (ip_tunnel_info_af(info) != AF_INET)
+		return -EINVAL;
+
+	rt = gre_get_rt(skb, dev, &fl4, &info->key);
+	if (IS_ERR(rt))
+		return PTR_ERR(rt);
+
+	ip_rt_put(rt);
+	info->key.u.ipv4.src = fl4.saddr;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ovs_gre_fill_metadata_dst);
+
 static const struct net_device_ops gre_tap_netdev_ops = {
 	.ndo_init		= gre_tap_init,
 	.ndo_uninit		= ip_tunnel_uninit,
@@ -472,6 +500,9 @@  static const struct net_device_ops gre_tap_netdev_ops = {
 #ifdef HAVE_NDO_GET_IFLINK
 	.ndo_get_iflink		= ip_tunnel_get_iflink,
 #endif
+#ifdef HAVE_NDO_FILL_METADATA_DST
+	.ndo_fill_metadata_dst  = gre_fill_metadata_dst,
+#endif
 };
 
 static void ipgre_tap_setup(struct net_device *dev)
diff --git a/datapath/linux/compat/lisp.c b/datapath/linux/compat/lisp.c
index f1f50ae..0a92dca 100644
--- a/datapath/linux/compat/lisp.c
+++ b/datapath/linux/compat/lisp.c
@@ -456,6 +456,46 @@  static int lisp_change_mtu(struct net_device *dev, int new_mtu)
 	return 0;
 }
 
+static int egress_ipv4_tun_info(struct net_device *dev, struct sk_buff *skb,
+		struct ip_tunnel_info *info,
+		__be16 sport, __be16 dport)
+{
+	struct lisp_dev *lisp = netdev_priv(dev);
+	struct rtable *rt;
+	struct flowi4 fl4;
+
+	memset(&fl4, 0, sizeof(fl4));
+	fl4.flowi4_tos = RT_TOS(info->key.tos);
+	fl4.flowi4_mark = skb->mark;
+	fl4.flowi4_proto = IPPROTO_UDP;
+	fl4.daddr = info->key.u.ipv4.dst;
+
+	rt = ip_route_output_key(lisp->net, &fl4);
+	if (IS_ERR(rt))
+		return PTR_ERR(rt);
+	ip_rt_put(rt);
+
+	info->key.u.ipv4.src = fl4.saddr;
+	info->key.tp_src = sport;
+	info->key.tp_dst = dport;
+	return 0;
+}
+
+int ovs_lisp_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+	struct lisp_dev *lisp = netdev_priv(dev);
+	struct ip_tunnel_info *info = skb_tunnel_info(skb);
+	__be16 sport, dport;
+
+	sport = udp_flow_src_port(dev_net(dev), skb, 1, USHRT_MAX, true);
+	dport = info->key.tp_dst ? : lisp->dst_port;
+
+	if (ip_tunnel_info_af(info) == AF_INET)
+		return egress_ipv4_tun_info(dev, skb, info, sport, dport);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ovs_lisp_fill_metadata_dst);
+
 static const struct net_device_ops lisp_netdev_ops = {
 	.ndo_init               = lisp_init,
 	.ndo_uninit             = lisp_uninit,
@@ -466,6 +506,9 @@  static const struct net_device_ops lisp_netdev_ops = {
 	.ndo_change_mtu         = lisp_change_mtu,
 	.ndo_validate_addr      = eth_validate_addr,
 	.ndo_set_mac_address    = eth_mac_addr,
+#ifdef HAVE_NDO_FILL_METADATA_DST
+	.ndo_fill_metadata_dst  = lisp_fill_metadata_dst,
+#endif
 };
 
 static void lisp_get_drvinfo(struct net_device *dev,
diff --git a/datapath/linux/compat/stt.c b/datapath/linux/compat/stt.c
index 86d225e..1528cbe 100644
--- a/datapath/linux/compat/stt.c
+++ b/datapath/linux/compat/stt.c
@@ -980,6 +980,24 @@  err_free_rt:
 	return ret;
 }
 
+static struct rtable *stt_get_rt(struct sk_buff *skb,
+				 struct net_device *dev,
+				 struct flowi4 *fl,
+				 const struct ip_tunnel_key *key)
+{
+	struct net *net = dev_net(dev);
+
+	/* Route lookup */
+	memset(fl, 0, sizeof(*fl));
+	fl->daddr = key->u.ipv4.dst;
+	fl->saddr = key->u.ipv4.src;
+	fl->flowi4_tos = RT_TOS(key->tos);
+	fl->flowi4_mark = skb->mark;
+	fl->flowi4_proto = IPPROTO_TCP;
+
+	return ip_route_output_key(net, fl);
+}
+
 netdev_tx_t ovs_stt_xmit(struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
@@ -1002,14 +1020,7 @@  netdev_tx_t ovs_stt_xmit(struct sk_buff *skb)
 
 	tun_key = &tun_info->key;
 
-	/* Route lookup */
-	memset(&fl, 0, sizeof(fl));
-	fl.daddr = tun_key->u.ipv4.dst;
-	fl.saddr = tun_key->u.ipv4.src;
-	fl.flowi4_tos = RT_TOS(tun_key->tos);
-	fl.flowi4_mark = skb->mark;
-	fl.flowi4_proto = IPPROTO_TCP;
-	rt = ip_route_output_key(net, &fl);
+	rt = stt_get_rt(skb, dev, &fl, tun_key);
 	if (IS_ERR(rt)) {
 		err = PTR_ERR(rt);
 		goto error;
@@ -1802,6 +1813,25 @@  static int stt_change_mtu(struct net_device *dev, int new_mtu)
 	return __stt_change_mtu(dev, new_mtu, true);
 }
 
+int ovs_stt_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+	struct ip_tunnel_info *info = skb_tunnel_info(skb);
+	struct rtable *rt;
+	struct flowi4 fl4;
+
+	if (ip_tunnel_info_af(info) != AF_INET)
+		return -EINVAL;
+
+	rt = stt_get_rt(skb, dev, &fl4, &info->key);
+	if (IS_ERR(rt))
+		return PTR_ERR(rt);
+
+	ip_rt_put(rt);
+	info->key.u.ipv4.src = fl4.saddr;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ovs_stt_fill_metadata_dst);
+
 static const struct net_device_ops stt_netdev_ops = {
 	.ndo_init               = stt_init,
 	.ndo_uninit             = stt_uninit,
@@ -1812,6 +1842,9 @@  static const struct net_device_ops stt_netdev_ops = {
 	.ndo_change_mtu         = stt_change_mtu,
 	.ndo_validate_addr      = eth_validate_addr,
 	.ndo_set_mac_address    = eth_mac_addr,
+#ifdef HAVE_NDO_FILL_METADATA_DST
+	.ndo_fill_metadata_dst  = stt_fill_metadata_dst,
+#endif
 };
 
 static void stt_get_drvinfo(struct net_device *dev,
diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c
index 4faa18f..b92a902 100644
--- a/datapath/linux/compat/vxlan.c
+++ b/datapath/linux/compat/vxlan.c
@@ -1648,6 +1648,47 @@  static netdev_tx_t vxlan_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 	return NETDEV_TX_OK;
 }
 
+static int egress_ipv4_tun_info(struct net_device *dev, struct sk_buff *skb,
+		struct ip_tunnel_info *info,
+		__be16 sport, __be16 dport)
+{
+	struct vxlan_dev *vxlan = netdev_priv(dev);
+	struct rtable *rt;
+	struct flowi4 fl4;
+
+	memset(&fl4, 0, sizeof(fl4));
+	fl4.flowi4_tos = RT_TOS(info->key.tos);
+	fl4.flowi4_mark = skb->mark;
+	fl4.flowi4_proto = IPPROTO_UDP;
+	fl4.daddr = info->key.u.ipv4.dst;
+
+	rt = ip_route_output_key(vxlan->net, &fl4);
+	if (IS_ERR(rt))
+		return PTR_ERR(rt);
+	ip_rt_put(rt);
+
+	info->key.u.ipv4.src = fl4.saddr;
+	info->key.tp_src = sport;
+	info->key.tp_dst = dport;
+	return 0;
+}
+
+int ovs_vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
+{
+	struct vxlan_dev *vxlan = netdev_priv(dev);
+	struct ip_tunnel_info *info = skb_tunnel_info(skb);
+	__be16 sport, dport;
+
+	sport = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min,
+			vxlan->cfg.port_max, true);
+	dport = info->key.tp_dst ? : vxlan->cfg.dst_port;
+
+	if (ip_tunnel_info_af(info) == AF_INET)
+		return egress_ipv4_tun_info(dev, skb, info, sport, dport);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ovs_vxlan_fill_metadata_dst);
+
 static const struct net_device_ops vxlan_netdev_ops = {
 	.ndo_init		= vxlan_init,
 	.ndo_uninit		= vxlan_uninit,
@@ -1659,6 +1700,9 @@  static const struct net_device_ops vxlan_netdev_ops = {
 	.ndo_change_mtu		= vxlan_change_mtu,
 	.ndo_validate_addr	= eth_validate_addr,
 	.ndo_set_mac_address	= eth_mac_addr,
+#ifdef HAVE_NDO_FILL_METADATA_DST
+	.ndo_fill_metadata_dst  = vxlan_fill_metadata_dst,
+#endif
 };
 
 /* Info for udev, that this is a virtual tunnel endpoint */
diff --git a/datapath/vport-geneve.c b/datapath/vport-geneve.c
index e65e43d..718b077 100644
--- a/datapath/vport-geneve.c
+++ b/datapath/vport-geneve.c
@@ -52,18 +52,6 @@  static int geneve_get_options(const struct vport *vport,
 	return 0;
 }
 
-static int geneve_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-				      struct dp_upcall_info *upcall)
-{
-	struct geneve_port *geneve_port = geneve_vport(vport);
-	struct net *net = ovs_dp_get_net(vport->dp);
-	__be16 dport = htons(geneve_port->dst_port);
-	__be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true);
-
-	return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp),
-					  skb, IPPROTO_UDP, sport, dport);
-}
-
 static struct vport *geneve_tnl_create(const struct vport_parms *parms)
 {
 	struct net *net = ovs_dp_get_net(parms->dp);
@@ -128,8 +116,8 @@  static struct vport_ops ovs_geneve_vport_ops = {
 	.create		= geneve_create,
 	.destroy	= ovs_netdev_tunnel_destroy,
 	.get_options	= geneve_get_options,
+	.fill_metadata_dst = geneve_fill_metadata_dst,
 	.send		= geneve_xmit,
-	.get_egress_tun_info	= geneve_get_egress_tun_info,
 };
 
 static int __init ovs_geneve_tnl_init(void)
diff --git a/datapath/vport-gre.c b/datapath/vport-gre.c
index 50aaba5..ffe0079 100644
--- a/datapath/vport-gre.c
+++ b/datapath/vport-gre.c
@@ -84,18 +84,11 @@  static struct vport *gre_create(const struct vport_parms *parms)
 	return ovs_netdev_link(vport, parms->name);
 }
 
-static int gre_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-				   struct dp_upcall_info *upcall)
-{
-	return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp),
-					  skb, IPPROTO_GRE, 0, 0);
-}
-
 static struct vport_ops ovs_gre_vport_ops = {
 	.type		= OVS_VPORT_TYPE_GRE,
 	.create		= gre_create,
 	.send		= gre_fb_xmit,
-	.get_egress_tun_info	= gre_get_egress_tun_info,
+	.fill_metadata_dst = gre_fill_metadata_dst,
 	.destroy	= ovs_netdev_tunnel_destroy,
 };
 
diff --git a/datapath/vport-lisp.c b/datapath/vport-lisp.c
index 3ce8613..25f6932 100644
--- a/datapath/vport-lisp.c
+++ b/datapath/vport-lisp.c
@@ -52,18 +52,6 @@  static int lisp_get_options(const struct vport *vport,
 	return 0;
 }
 
-static int lisp_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-				      struct dp_upcall_info *upcall)
-{
-	struct lisp_port *lisp_port = lisp_vport(vport);
-	struct net *net = ovs_dp_get_net(vport->dp);
-	__be16 dport = htons(lisp_port->port_no);
-	__be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true);
-
-	return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp),
-					  skb, IPPROTO_UDP, sport, dport);
-}
-
 static struct vport *lisp_tnl_create(const struct vport_parms *parms)
 {
 	struct net *net = ovs_dp_get_net(parms->dp);
@@ -128,8 +116,8 @@  static struct vport_ops ovs_lisp_vport_ops = {
 	.create		= lisp_create,
 	.destroy	= ovs_netdev_tunnel_destroy,
 	.get_options	= lisp_get_options,
+	.fill_metadata_dst = lisp_fill_metadata_dst,
 	.send		= lisp_xmit,
-	.get_egress_tun_info	= lisp_get_egress_tun_info,
 };
 
 static int __init ovs_lisp_tnl_init(void)
diff --git a/datapath/vport-stt.c b/datapath/vport-stt.c
index b8e0c88..e46bec1 100644
--- a/datapath/vport-stt.c
+++ b/datapath/vport-stt.c
@@ -54,18 +54,6 @@  static int stt_get_options(const struct vport *vport,
 	return 0;
 }
 
-static int stt_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-				      struct dp_upcall_info *upcall)
-{
-	struct stt_port *stt_port = stt_vport(vport);
-	struct net *net = ovs_dp_get_net(vport->dp);
-	__be16 dport = htons(stt_port->port_no);
-	__be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true);
-
-	return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp),
-					  skb, IPPROTO_UDP, sport, dport);
-}
-
 static struct vport *stt_tnl_create(const struct vport_parms *parms)
 {
 	struct net *net = ovs_dp_get_net(parms->dp);
@@ -130,8 +118,8 @@  static struct vport_ops ovs_stt_vport_ops = {
 	.create		= stt_create,
 	.destroy	= ovs_netdev_tunnel_destroy,
 	.get_options	= stt_get_options,
+	.fill_metadata_dst = stt_fill_metadata_dst,
 	.send		= ovs_stt_xmit,
-	.get_egress_tun_info	= stt_get_egress_tun_info,
 };
 
 static int __init ovs_stt_tnl_init(void)
diff --git a/datapath/vport-vxlan.c b/datapath/vport-vxlan.c
index c05f5d4..37e6da1 100644
--- a/datapath/vport-vxlan.c
+++ b/datapath/vport-vxlan.c
@@ -148,31 +148,13 @@  static struct vport *vxlan_create(const struct vport_parms *parms)
 	return ovs_netdev_link(vport, parms->name);
 }
 
-static int vxlan_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-				     struct dp_upcall_info *upcall)
-{
-	struct vxlan_dev *vxlan = netdev_priv(vport->dev);
-	struct net *net = ovs_dp_get_net(vport->dp);
-	__be16 dst_port = vxlan_dev_dst_port(vxlan);
-	__be16 src_port;
-	int port_min;
-	int port_max;
-
-	inet_get_local_port_range(net, &port_min, &port_max);
-	src_port = udp_flow_src_port(net, skb, 0, 0, true);
-
-	return ovs_tunnel_get_egress_info(upcall, net,
-					  skb, IPPROTO_UDP,
-					  src_port, dst_port);
-}
-
 static struct vport_ops ovs_vxlan_netdev_vport_ops = {
 	.type			= OVS_VPORT_TYPE_VXLAN,
 	.create			= vxlan_create,
 	.destroy		= ovs_netdev_tunnel_destroy,
 	.get_options		= vxlan_get_options,
+	.fill_metadata_dst	= vxlan_fill_metadata_dst,
 	.send			= vxlan_xmit,
-	.get_egress_tun_info	= vxlan_get_egress_tun_info,
 };
 
 static int __init ovs_vxlan_tnl_init(void)
diff --git a/datapath/vport.c b/datapath/vport.c
index 44b9dfb..c789a40 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -41,6 +41,7 @@ 
 #include "gso.h"
 #include "vport.h"
 #include "vport-internal_dev.h"
+#include "vport-netdev.h"
 
 static LIST_HEAD(vport_ops_list);
 
@@ -525,83 +526,6 @@  void ovs_vport_deferred_free(struct vport *vport)
 }
 EXPORT_SYMBOL_GPL(ovs_vport_deferred_free);
 
-static struct rtable *ovs_tunnel_route_lookup(struct net *net,
-					      const struct ip_tunnel_key *key,
-					      u32 mark,
-					      struct flowi4 *fl,
-					      u8 protocol)
-{
-	struct rtable *rt;
-
-	memset(fl, 0, sizeof(*fl));
-	fl->daddr = key->u.ipv4.dst;
-	fl->saddr = key->u.ipv4.src;
-	fl->flowi4_tos = RT_TOS(key->tos);
-	fl->flowi4_mark = mark;
-	fl->flowi4_proto = protocol;
-
-	rt = ip_route_output_key(net, fl);
-	return rt;
-}
-
-int ovs_tunnel_get_egress_info(struct dp_upcall_info *upcall,
-			       struct net *net,
-			       struct sk_buff *skb,
-			       u8 ipproto,
-			       __be16 tp_src,
-			       __be16 tp_dst)
-{
-	struct ip_tunnel_info *egress_tun_info = upcall->egress_tun_info;
-	struct ip_tunnel_info *tun_info = skb_tunnel_info(skb);
-	const struct ip_tunnel_key *tun_key;
-	u32 skb_mark = skb->mark;
-	struct rtable *rt;
-	struct flowi4 fl;
-
-	if (unlikely(!tun_info))
-		return -EINVAL;
-	if (ip_tunnel_info_af(tun_info) != AF_INET)
-		return -EINVAL;
-
-	tun_key = &tun_info->key;
-
-	/* Route lookup to get srouce IP address.
-	 * The process may need to be changed if the corresponding process
-	 * in vports ops changed.
-	 */
-	rt = ovs_tunnel_route_lookup(net, tun_key, skb_mark, &fl, ipproto);
-	if (IS_ERR(rt))
-		return PTR_ERR(rt);
-
-	ip_rt_put(rt);
-
-	/* Generate egress_tun_info based on tun_info,
-	 * saddr, tp_src and tp_dst
-	 */
-	ip_tunnel_key_init(&egress_tun_info->key,
-			   fl.saddr, tun_key->u.ipv4.dst,
-			   tun_key->tos,
-			   tun_key->ttl,
-			   tp_src, tp_dst,
-			   tun_key->tun_id,
-			   tun_key->tun_flags);
-	egress_tun_info->options_len = tun_info->options_len;
-	egress_tun_info->mode = tun_info->mode;
-	upcall->egress_tun_opts = ip_tunnel_info_opts(tun_info);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(ovs_tunnel_get_egress_info);
-
-int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-				  struct dp_upcall_info *upcall)
-{
-	/* get_egress_tun_info() is only implemented on tunnel ports. */
-	if (unlikely(!vport->ops->get_egress_tun_info))
-		return -EINVAL;
-
-	return vport->ops->get_egress_tun_info(vport, skb, upcall);
-}
-
 static unsigned int packet_length(const struct sk_buff *skb)
 {
 	unsigned int length = skb->len - ETH_HLEN;
diff --git a/datapath/vport.h b/datapath/vport.h
index 6e80493..96a5b2b 100644
--- a/datapath/vport.h
+++ b/datapath/vport.h
@@ -52,16 +52,6 @@  int ovs_vport_set_upcall_portids(struct vport *, const struct nlattr *pids);
 int ovs_vport_get_upcall_portids(const struct vport *, struct sk_buff *);
 u32 ovs_vport_find_upcall_portid(const struct vport *, struct sk_buff *);
 
-int ovs_tunnel_get_egress_info(struct dp_upcall_info *upcall,
-			       struct net *net,
-			       struct sk_buff *,
-			       u8 ipproto,
-			       __be16 tp_src,
-			       __be16 tp_dst);
-
-int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-				  struct dp_upcall_info *upcall);
-
 /**
  * struct vport_portids - array of netlink portids of a vport.
  *                        must be protected by rcu.
@@ -140,8 +130,6 @@  struct vport_parms {
  * have any configuration.
  * @send: Send a packet on the device.
  * zero for dropped packets or negative for error.
- * @get_egress_tun_info: Get the egress tunnel 5-tuple and other info for
- * a packet.
  */
 struct vport_ops {
 	enum ovs_vport_type type;
@@ -154,8 +142,7 @@  struct vport_ops {
 	int (*get_options)(const struct vport *, struct sk_buff *);
 
 	netdev_tx_t (*send)(struct sk_buff *skb);
-	int (*get_egress_tun_info)(struct vport *, struct sk_buff *,
-				   struct dp_upcall_info *upcall);
+	int  (*fill_metadata_dst)(struct net_device *dev, struct sk_buff *skb);
 
 	struct module *owner;
 	struct list_head list;