|
@@ -67,6 +67,7 @@
|
|
|
#include <net/fib_rules.h>
|
|
|
#include <linux/netconf.h>
|
|
|
#include <net/nexthop.h>
|
|
|
+#include <net/switchdev.h>
|
|
|
|
|
|
struct ipmr_rule {
|
|
|
struct fib_rule common;
|
|
@@ -868,6 +869,9 @@ static int vif_add(struct net *net, struct mr_table *mrt,
|
|
|
struct vifctl *vifc, int mrtsock)
|
|
|
{
|
|
|
int vifi = vifc->vifc_vifi;
|
|
|
+ struct switchdev_attr attr = {
|
|
|
+ .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
|
|
|
+ };
|
|
|
struct vif_device *v = &mrt->vif_table[vifi];
|
|
|
struct net_device *dev;
|
|
|
struct in_device *in_dev;
|
|
@@ -942,6 +946,13 @@ static int vif_add(struct net *net, struct mr_table *mrt,
|
|
|
|
|
|
/* Fill in the VIF structures */
|
|
|
|
|
|
+ attr.orig_dev = dev;
|
|
|
+ if (!switchdev_port_attr_get(dev, &attr)) {
|
|
|
+ memcpy(v->dev_parent_id.id, attr.u.ppid.id, attr.u.ppid.id_len);
|
|
|
+ v->dev_parent_id.id_len = attr.u.ppid.id_len;
|
|
|
+ } else {
|
|
|
+ v->dev_parent_id.id_len = 0;
|
|
|
+ }
|
|
|
v->rate_limit = vifc->vifc_rate_limit;
|
|
|
v->local = vifc->vifc_lcl_addr.s_addr;
|
|
|
v->remote = vifc->vifc_rmt_addr.s_addr;
|
|
@@ -1848,10 +1859,33 @@ static inline int ipmr_forward_finish(struct net *net, struct sock *sk,
|
|
|
return dst_output(net, sk, skb);
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_NET_SWITCHDEV
|
|
|
+static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,
|
|
|
+ int in_vifi, int out_vifi)
|
|
|
+{
|
|
|
+ struct vif_device *out_vif = &mrt->vif_table[out_vifi];
|
|
|
+ struct vif_device *in_vif = &mrt->vif_table[in_vifi];
|
|
|
+
|
|
|
+ if (!skb->offload_mr_fwd_mark)
|
|
|
+ return false;
|
|
|
+ if (!out_vif->dev_parent_id.id_len || !in_vif->dev_parent_id.id_len)
|
|
|
+ return false;
|
|
|
+ return netdev_phys_item_id_same(&out_vif->dev_parent_id,
|
|
|
+ &in_vif->dev_parent_id);
|
|
|
+}
|
|
|
+#else
|
|
|
+static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt,
|
|
|
+ int in_vifi, int out_vifi)
|
|
|
+{
|
|
|
+ return false;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
/* Processing handlers for ipmr_forward */
|
|
|
|
|
|
static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
|
|
|
- struct sk_buff *skb, struct mfc_cache *c, int vifi)
|
|
|
+ int in_vifi, struct sk_buff *skb,
|
|
|
+ struct mfc_cache *c, int vifi)
|
|
|
{
|
|
|
const struct iphdr *iph = ip_hdr(skb);
|
|
|
struct vif_device *vif = &mrt->vif_table[vifi];
|
|
@@ -1872,6 +1906,9 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
|
|
|
goto out_free;
|
|
|
}
|
|
|
|
|
|
+ if (ipmr_forward_offloaded(skb, mrt, in_vifi, vifi))
|
|
|
+ goto out_free;
|
|
|
+
|
|
|
if (vif->flags & VIFF_TUNNEL) {
|
|
|
rt = ip_route_output_ports(net, &fl4, NULL,
|
|
|
vif->remote, vif->local,
|
|
@@ -2049,8 +2086,8 @@ forward:
|
|
|
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
|
|
|
|
if (skb2)
|
|
|
- ipmr_queue_xmit(net, mrt, skb2, cache,
|
|
|
- psend);
|
|
|
+ ipmr_queue_xmit(net, mrt, true_vifi,
|
|
|
+ skb2, cache, psend);
|
|
|
}
|
|
|
psend = ct;
|
|
|
}
|
|
@@ -2061,9 +2098,10 @@ last_forward:
|
|
|
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
|
|
|
|
if (skb2)
|
|
|
- ipmr_queue_xmit(net, mrt, skb2, cache, psend);
|
|
|
+ ipmr_queue_xmit(net, mrt, true_vifi, skb2,
|
|
|
+ cache, psend);
|
|
|
} else {
|
|
|
- ipmr_queue_xmit(net, mrt, skb, cache, psend);
|
|
|
+ ipmr_queue_xmit(net, mrt, true_vifi, skb, cache, psend);
|
|
|
return;
|
|
|
}
|
|
|
}
|