summaryrefslogtreecommitdiff
path: root/datapath/linux/compat
diff options
context:
space:
mode:
Diffstat (limited to 'datapath/linux/compat')
-rw-r--r--datapath/linux/compat/dev-openvswitch.c32
-rw-r--r--datapath/linux/compat/geneve.c43
-rw-r--r--datapath/linux/compat/gso.h45
-rw-r--r--datapath/linux/compat/include/linux/netdevice.h4
-rw-r--r--datapath/linux/compat/include/net/geneve.h3
-rw-r--r--datapath/linux/compat/include/net/gre.h3
-rw-r--r--datapath/linux/compat/include/net/lisp.h3
-rw-r--r--datapath/linux/compat/include/net/stt.h3
-rw-r--r--datapath/linux/compat/include/net/vxlan.h2
-rw-r--r--datapath/linux/compat/ip_gre.c49
-rw-r--r--datapath/linux/compat/lisp.c64
-rw-r--r--datapath/linux/compat/stt.c55
-rw-r--r--datapath/linux/compat/vxlan.c44
13 files changed, 318 insertions, 32 deletions
diff --git a/datapath/linux/compat/dev-openvswitch.c b/datapath/linux/compat/dev-openvswitch.c
index 0d2088b38..1e870431f 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,30 @@ 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;
+
+ if (!SKB_SETUP_FILL_METADATA_DST(skb))
+ return -ENOMEM;
+
+ 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 0399de706..054049485 100644
--- a/datapath/linux/compat/geneve.c
+++ b/datapath/linux/compat/geneve.c
@@ -615,14 +615,12 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
rt = ip_route_output_key(geneve->net, fl4);
if (IS_ERR(rt)) {
netdev_dbg(dev, "no route to %pI4\n", &fl4->daddr);
- dev->stats.tx_carrier_errors++;
- return rt;
+ return ERR_PTR(-ENETUNREACH);
}
if (rt->dst.dev == dev) { /* is this necessary? */
netdev_dbg(dev, "circular route to %pI4\n", &fl4->daddr);
- dev->stats.collisions++;
ip_rt_put(rt);
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(-ELOOP);
}
return rt;
}
@@ -649,12 +647,12 @@ netdev_tx_t rpl_geneve_xmit(struct sk_buff *skb)
struct ip_tunnel_info *info = NULL;
struct rtable *rt = NULL;
const struct iphdr *iip; /* interior IP header */
+ int err = -EINVAL;
struct flowi4 fl4;
__u8 tos, ttl;
__be16 sport;
bool udp_csum;
__be16 df;
- int err;
if (geneve->collect_md) {
info = skb_tunnel_info(skb);
@@ -669,7 +667,7 @@ netdev_tx_t rpl_geneve_xmit(struct sk_buff *skb)
rt = geneve_get_rt(skb, dev, &fl4, info);
if (IS_ERR(rt)) {
netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr);
- dev->stats.tx_carrier_errors++;
+ err = PTR_ERR(rt);
goto tx_error;
}
@@ -721,7 +719,12 @@ netdev_tx_t rpl_geneve_xmit(struct sk_buff *skb)
tx_error:
dev_kfree_skb(skb);
err:
- dev->stats.tx_errors++;
+ if (err == -ELOOP)
+ dev->stats.collisions++;
+ else if (err == -ENETUNREACH)
+ dev->stats.tx_carrier_errors++;
+ else
+ dev->stats.tx_errors++;
return NETDEV_TX_OK;
}
EXPORT_SYMBOL(rpl_geneve_xmit);
@@ -765,6 +768,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 +801,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/gso.h b/datapath/linux/compat/gso.h
index 0f2b09a9a..7a20942a5 100644
--- a/datapath/linux/compat/gso.h
+++ b/datapath/linux/compat/gso.h
@@ -23,6 +23,11 @@ struct ovs_gso_cb {
#ifndef HAVE_INNER_NETWORK_HEADER
unsigned int inner_network_header;
#endif
+#ifndef HAVE_NDO_FILL_METADATA_DST
+ /* Keep original tunnel info during userspace action execution. */
+ struct metadata_dst *fill_md_dst;
+#endif
+
};
#define OVS_GSO_CB(skb) ((struct ovs_gso_cb *)(skb)->cb)
@@ -197,4 +202,44 @@ static inline void ovs_dst_release(struct dst_entry *dst)
#define ovs_dst_release dst_release
#endif
+#ifndef HAVE_NDO_FILL_METADATA_DST
+#define SKB_INIT_FILL_METADATA_DST(skb) OVS_GSO_CB(skb)->fill_md_dst = NULL;
+
+#define SKB_RESTORE_FILL_METADATA_DST(skb) do { \
+ if (OVS_GSO_CB(skb)->fill_md_dst) { \
+ kfree(OVS_GSO_CB(skb)->tun_dst); \
+ OVS_GSO_CB(skb)->tun_dst = OVS_GSO_CB(skb)->fill_md_dst; \
+ } \
+} while (0)
+
+
+#define SKB_SETUP_FILL_METADATA_DST(skb) ({ \
+ struct metadata_dst *new_md_dst; \
+ struct metadata_dst *md_dst; \
+ int md_size; \
+ int ret = 1; \
+ \
+ SKB_RESTORE_FILL_METADATA_DST(skb); \
+ new_md_dst = kmalloc(sizeof(struct metadata_dst) + 256, GFP_ATOMIC); \
+ if (new_md_dst) { \
+ md_dst = OVS_GSO_CB(skb)->tun_dst; \
+ md_size = new_md_dst->u.tun_info.options_len; \
+ memcpy(&new_md_dst->u.tun_info, &md_dst->u.tun_info, \
+ sizeof(struct ip_tunnel_info) + md_size); \
+ \
+ OVS_GSO_CB(skb)->fill_md_dst = md_dst; \
+ OVS_GSO_CB(skb)->tun_dst = new_md_dst; \
+ ret = 1; \
+ } else { \
+ ret = 0; \
+ } \
+ ret; \
+})
+
+#else
+#define SKB_INIT_FILL_METADATA_DST(skb) do {} while(0)
+#define SKB_SETUP_FILL_METADATA_DST(skb) (true)
+#define SKB_RESTORE_FILL_METADATA_DST(skb) do {} while(0)
+#endif
+
#endif
diff --git a/datapath/linux/compat/include/linux/netdevice.h b/datapath/linux/compat/include/linux/netdevice.h
index e9fa99579..ac612efee 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 550f4a77e..a7f22526e 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 60618df9e..8082a9853 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 b8af17dbb..6b43c77e2 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 28d4dc53c..d2e63d163 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 75a5a7acd..589e6f259 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 2e1384348..67db9af73 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 f1f50aea5..ae3e5f37b 100644
--- a/datapath/linux/compat/lisp.c
+++ b/datapath/linux/compat/lisp.c
@@ -269,6 +269,24 @@ out:
return 0;
}
+static struct rtable *lisp_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_UDP;
+
+ return ip_route_output_key(net, fl);
+}
+
netdev_tx_t rpl_lisp_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
@@ -298,14 +316,7 @@ netdev_tx_t rpl_lisp_xmit(struct sk_buff *skb)
tun_key = &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_UDP;
- rt = ip_route_output_key(net, &fl);
+ rt = lisp_get_rt(skb, dev, &fl, tun_key);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
goto error;
@@ -456,6 +467,40 @@ 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 rtable *rt;
+ struct flowi4 fl4;
+
+ rt = lisp_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;
+ 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 net *net = lisp->net;
+ struct ip_tunnel_info *info = skb_tunnel_info(skb);
+ __be16 sport, dport;
+
+ sport = htons(get_src_port(net, skb));
+ dport = 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 +511,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 86d225e86..0bdd4db49 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,31 @@ 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 stt_dev *stt_dev = netdev_priv(dev);
+ struct net *net = stt_dev->net;
+ __be16 dport = stt_dev->dst_port;
+ struct flowi4 fl4;
+ struct rtable *rt;
+
+ 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;
+ info->key.tp_src = udp_flow_src_port(net, skb, 1, USHRT_MAX, true);
+ info->key.tp_dst = dport;
+ 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 +1848,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 4faa18f3e..b92a90259 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 */