This patch hooks the IPsec GSO code into the generic
network stack.

Signed-off-by: Steffen Klassert <[email protected]>
---
 include/linux/netdevice.h |  2 +-
 include/net/xfrm.h        |  1 +
 net/core/dev.c            | 40 +++++++++++++++++++++++++++++++++++-----
 net/ipv4/ip_output.c      |  8 +++++---
 net/ipv4/xfrm4_output.c   |  2 +-
 net/sched/sch_generic.c   |  2 +-
 net/xfrm/xfrm_output.c    | 14 +++++++++++++-
 7 files changed, 57 insertions(+), 12 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d049c02..659eeec 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3192,7 +3192,7 @@ int dev_get_phys_port_id(struct net_device *dev,
 int dev_get_phys_port_name(struct net_device *dev,
                           char *name, size_t len);
 int dev_change_proto_down(struct net_device *dev, bool proto_down);
-struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device 
*dev);
+struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device 
*dev, int *ret);
 struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device 
*dev,
                                    struct netdev_queue *txq, int *ret);
 int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 7939c39..3a69883 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1505,6 +1505,7 @@ struct xfrmk_spdinfo {
        u32 spdhmcnt;
 };
 
+void xfrm_dev_backlog(struct sk_buff_head *xfrm_backlog);
 struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
 int xfrm_state_delete(struct xfrm_state *x);
 int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
diff --git a/net/core/dev.c b/net/core/dev.c
index f083cbb..611e93c 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2911,9 +2911,10 @@ static struct sk_buff *validate_xmit_vlan(struct sk_buff 
*skb,
        return skb;
 }
 
-static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct 
net_device *dev)
+static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct 
net_device *dev, int *ret)
 {
        netdev_features_t features;
+       int err = 0;
 
        if (skb->next)
                return skb;
@@ -2925,6 +2926,7 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff 
*skb, struct net_device
 
        if (netif_needs_gso(skb, features)) {
                struct sk_buff *segs;
+               struct sk_buff *skb2;
 
                segs = skb_gso_segment(skb, features);
                if (IS_ERR(segs)) {
@@ -2932,7 +2934,25 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff 
*skb, struct net_device
                } else if (segs) {
                        consume_skb(skb);
                        skb = segs;
+
+                       if  (skb->hw_xfrm) {
+                               do {
+                                       skb2 = segs->next;
+                                       segs->next = NULL;
+
+                                       err = 
dev->xfrmdev_ops->xdo_dev_validate(segs);
+                                       if (!err)
+                                               segs->next = skb2;
+                                       else if (err != -EINPROGRESS)
+                                               kfree_skb(segs);
+                                       else if (skb == segs)
+                                               skb = skb2;
+
+                                       segs = skb2;
+                               } while (segs);
+                       }
                }
+
        } else {
                if (skb_needs_linearize(skb, features) &&
                    __skb_linearize(skb))
@@ -2955,6 +2975,9 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff 
*skb, struct net_device
                }
        }
 
+       if ((err == -EINPROGRESS) && !skb)
+               *ret = NETDEV_TX_OK;
+
        return skb;
 
 out_kfree_skb:
@@ -2963,7 +2986,7 @@ out_null:
        return NULL;
 }
 
-struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device 
*dev)
+struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device 
*dev, int *ret)
 {
        struct sk_buff *next, *head = NULL, *tail;
 
@@ -2974,7 +2997,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff 
*skb, struct net_device *d
                /* in case skb wont be segmented, point to itself */
                skb->prev = skb;
 
-               skb = validate_xmit_skb(skb, dev);
+               skb = validate_xmit_skb(skb, dev, ret);
                if (!skb)
                        continue;
 
@@ -3347,8 +3370,10 @@ static int __dev_queue_xmit(struct sk_buff *skb, void 
*accel_priv)
                        if (__this_cpu_read(xmit_recursion) > RECURSION_LIMIT)
                                goto recursion_alert;
 
-                       skb = validate_xmit_skb(skb, dev);
-                       if (!skb)
+                       skb = validate_xmit_skb(skb, dev, &rc);
+                       if (!skb && rc == NETDEV_TX_OK)
+                               goto out;
+                       else if (!skb)
                                goto drop;
 
                        HARD_TX_LOCK(dev, txq, cpu);
@@ -3867,6 +3892,11 @@ static void net_tx_action(struct softirq_action *h)
                        }
                }
        }
+
+#ifdef CONFIG_XFRM
+       if (!skb_queue_empty(&sd->xfrm_backlog))
+                       xfrm_dev_backlog(&sd->xfrm_backlog);
+#endif
 }
 
 #if (defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)) && \
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 64878ef..0d75161 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -173,7 +173,7 @@ EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);
 
 static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff 
*skb)
 {
-       struct dst_entry *dst = skb_dst(skb);
+       struct dst_entry *dst = skb_dst(skb)->path;
        struct rtable *rt = (struct rtable *)dst;
        struct net_device *dev = dst->dev;
        unsigned int hh_len = LL_RESERVED_SPACE(dev);
@@ -269,7 +269,9 @@ static int ip_finish_output(struct net *net, struct sock 
*sk, struct sk_buff *sk
 
 #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
        /* Policy lookup after SNAT yielded a new policy */
-       if (skb_dst(skb)->xfrm) {
+       if (skb_dst(skb)->xfrm &&
+           !((skb_dst(skb)->dev->features & NETIF_F_ESP_OFFLOAD) ||
+             (skb_shinfo(skb)->gso_type & SKB_GSO_ESP))) {
                IPCB(skb)->flags |= IPSKB_REROUTED;
                return dst_output(net, sk, skb);
        }
@@ -348,7 +350,7 @@ int ip_mc_output(struct net *net, struct sock *sk, struct 
sk_buff *skb)
 
 int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
-       struct net_device *dev = skb_dst(skb)->dev;
+       struct net_device *dev = skb_dst(skb)->path->dev;
 
        IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
 
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index 7ee6518..14e42ba 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -29,7 +29,7 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
                goto out;
 
        mtu = dst_mtu(skb_dst(skb));
-       if (skb->len > mtu) {
+       if ((!skb_is_gso(skb) && skb->len > mtu) || (skb_is_gso(skb) && 
skb_gso_network_seglen(skb) > ip_skb_dst_mtu(skb))) {
                skb->protocol = htons(ETH_P_IP);
 
                if (skb->sk)
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 16bc83b..5b11424 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -157,7 +157,7 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
 
        /* Note that we validate skb (GSO, checksum, ...) outside of locks */
        if (validate)
-               skb = validate_xmit_skb_list(skb, dev);
+               skb = validate_xmit_skb_list(skb, dev, &ret);
 
        if (skb) {
                HARD_TX_LOCK(dev, txq, smp_processor_id());
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index ff4a91f..ad452e0 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -196,9 +196,21 @@ static int xfrm_output_gso(struct net *net, struct sock 
*sk, struct sk_buff *skb
 
 int xfrm_output(struct sock *sk, struct sk_buff *skb)
 {
-       struct net *net = dev_net(skb_dst(skb)->dev);
+       struct net_device *dev = skb_dst(skb)->dev;
+       struct net *net = dev_net(dev);
        int err;
 
+       if ((dev->features & NETIF_F_ESP_OFFLOAD) || skb_is_gso(skb)) {
+               err = skb_dst(skb)->ops->local_out(net, skb->sk, skb);
+               if (unlikely(err != 1))
+                       return err;
+
+               if (skb_is_gso(skb))
+                       skb_shinfo(skb)->gso_type |= SKB_GSO_ESP;
+
+               return dev->xfrmdev_ops->xdo_dev_encap(skb);
+       }
+
        if (skb_is_gso(skb))
                return xfrm_output_gso(net, sk, skb);
 
-- 
1.9.1

Reply via email to