2012-11-15 16:49:16 +08:00
|
|
|
/*
|
|
|
|
* IPV6 GSO/GRO offload support
|
|
|
|
* Linux INET6 implementation
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/socket.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/skbuff.h>
|
2012-11-15 16:49:22 +08:00
|
|
|
#include <linux/printk.h>
|
2012-11-15 16:49:16 +08:00
|
|
|
|
|
|
|
#include <net/protocol.h>
|
|
|
|
#include <net/ipv6.h>
|
|
|
|
|
|
|
|
#include "ip6_offload.h"
|
|
|
|
|
|
|
|
static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
|
|
|
|
{
|
|
|
|
const struct net_offload *ops = NULL;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
struct ipv6_opt_hdr *opth;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (proto != NEXTHDR_HOP) {
|
|
|
|
ops = rcu_dereference(inet6_offloads[proto]);
|
|
|
|
|
|
|
|
if (unlikely(!ops))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlikely(!pskb_may_pull(skb, 8)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
opth = (void *)skb->data;
|
|
|
|
len = ipv6_optlen(opth);
|
|
|
|
|
|
|
|
if (unlikely(!pskb_may_pull(skb, len)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
proto = opth->nexthdr;
|
|
|
|
__skb_pull(skb, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return proto;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipv6_gso_send_check(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
const struct ipv6hdr *ipv6h;
|
|
|
|
const struct net_offload *ops;
|
|
|
|
int err = -EINVAL;
|
|
|
|
|
|
|
|
if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ipv6h = ipv6_hdr(skb);
|
|
|
|
__skb_pull(skb, sizeof(*ipv6h));
|
|
|
|
err = -EPROTONOSUPPORT;
|
|
|
|
|
|
|
|
ops = rcu_dereference(inet6_offloads[
|
|
|
|
ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
|
|
|
|
|
2012-11-15 16:49:23 +08:00
|
|
|
if (likely(ops && ops->callbacks.gso_send_check)) {
|
2012-11-15 16:49:16 +08:00
|
|
|
skb_reset_transport_header(skb);
|
2012-11-15 16:49:23 +08:00
|
|
|
err = ops->callbacks.gso_send_check(skb);
|
2012-11-15 16:49:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
|
|
|
|
netdev_features_t features)
|
|
|
|
{
|
|
|
|
struct sk_buff *segs = ERR_PTR(-EINVAL);
|
|
|
|
struct ipv6hdr *ipv6h;
|
|
|
|
const struct net_offload *ops;
|
|
|
|
int proto;
|
|
|
|
struct frag_hdr *fptr;
|
|
|
|
unsigned int unfrag_ip6hlen;
|
|
|
|
u8 *prevhdr;
|
|
|
|
int offset = 0;
|
2013-08-31 13:44:37 +08:00
|
|
|
bool tunnel;
|
2013-10-21 11:47:29 +08:00
|
|
|
int nhoff;
|
2012-11-15 16:49:16 +08:00
|
|
|
|
|
|
|
if (unlikely(skb_shinfo(skb)->gso_type &
|
|
|
|
~(SKB_GSO_UDP |
|
|
|
|
SKB_GSO_DODGY |
|
|
|
|
SKB_GSO_TCP_ECN |
|
2013-02-14 22:02:41 +08:00
|
|
|
SKB_GSO_GRE |
|
2013-10-20 02:42:57 +08:00
|
|
|
SKB_GSO_IPIP |
|
2013-10-21 11:47:30 +08:00
|
|
|
SKB_GSO_SIT |
|
2013-03-07 21:21:51 +08:00
|
|
|
SKB_GSO_UDP_TUNNEL |
|
2013-05-24 05:02:52 +08:00
|
|
|
SKB_GSO_MPLS |
|
2012-11-15 16:49:16 +08:00
|
|
|
SKB_GSO_TCPV6 |
|
|
|
|
0)))
|
|
|
|
goto out;
|
|
|
|
|
2013-10-21 11:47:29 +08:00
|
|
|
skb_reset_network_header(skb);
|
|
|
|
nhoff = skb_network_header(skb) - skb_mac_header(skb);
|
2012-11-15 16:49:16 +08:00
|
|
|
if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
|
|
|
|
goto out;
|
|
|
|
|
2013-10-21 11:47:29 +08:00
|
|
|
tunnel = SKB_GSO_CB(skb)->encap_level > 0;
|
|
|
|
if (tunnel)
|
|
|
|
features = skb->dev->hw_enc_features & netif_skb_features(skb);
|
|
|
|
SKB_GSO_CB(skb)->encap_level += sizeof(*ipv6h);
|
|
|
|
|
2012-11-15 16:49:16 +08:00
|
|
|
ipv6h = ipv6_hdr(skb);
|
|
|
|
__skb_pull(skb, sizeof(*ipv6h));
|
|
|
|
segs = ERR_PTR(-EPROTONOSUPPORT);
|
|
|
|
|
|
|
|
proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
|
2013-10-19 05:43:55 +08:00
|
|
|
|
2012-11-15 16:49:16 +08:00
|
|
|
ops = rcu_dereference(inet6_offloads[proto]);
|
2012-11-15 16:49:23 +08:00
|
|
|
if (likely(ops && ops->callbacks.gso_segment)) {
|
2012-11-15 16:49:16 +08:00
|
|
|
skb_reset_transport_header(skb);
|
2012-11-15 16:49:23 +08:00
|
|
|
segs = ops->callbacks.gso_segment(skb, features);
|
2012-11-15 16:49:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ERR(segs))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
for (skb = segs; skb; skb = skb->next) {
|
2013-10-21 11:47:29 +08:00
|
|
|
ipv6h = (struct ipv6hdr *)(skb_mac_header(skb) + nhoff);
|
|
|
|
ipv6h->payload_len = htons(skb->len - nhoff - sizeof(*ipv6h));
|
|
|
|
if (tunnel) {
|
|
|
|
skb_reset_inner_headers(skb);
|
|
|
|
skb->encapsulation = 1;
|
|
|
|
}
|
|
|
|
skb->network_header = (u8 *)ipv6h - skb->head;
|
|
|
|
|
2013-08-31 13:44:37 +08:00
|
|
|
if (!tunnel && proto == IPPROTO_UDP) {
|
2012-11-15 16:49:16 +08:00
|
|
|
unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
|
2013-10-21 11:47:29 +08:00
|
|
|
fptr = (struct frag_hdr *)((u8 *)ipv6h + unfrag_ip6hlen);
|
2012-11-15 16:49:16 +08:00
|
|
|
fptr->frag_off = htons(offset);
|
|
|
|
if (skb->next != NULL)
|
|
|
|
fptr->frag_off |= htons(IP6_MF);
|
|
|
|
offset += (ntohs(ipv6h->payload_len) -
|
|
|
|
sizeof(struct frag_hdr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return segs;
|
|
|
|
}
|
|
|
|
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
/* Return the total length of all the extension hdrs, following the same
|
|
|
|
* logic in ipv6_gso_pull_exthdrs() when parsing ext-hdrs.
|
|
|
|
*/
|
|
|
|
static int ipv6_exthdrs_len(struct ipv6hdr *iph,
|
|
|
|
const struct net_offload **opps)
|
|
|
|
{
|
2013-12-16 10:48:07 +08:00
|
|
|
struct ipv6_opt_hdr *opth = (void *)iph;
|
|
|
|
int len = 0, proto, optlen = sizeof(*iph);
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
|
|
|
|
proto = iph->nexthdr;
|
|
|
|
for (;;) {
|
|
|
|
if (proto != NEXTHDR_HOP) {
|
|
|
|
*opps = rcu_dereference(inet6_offloads[proto]);
|
|
|
|
if (unlikely(!(*opps)))
|
|
|
|
break;
|
|
|
|
if (!((*opps)->flags & INET6_PROTO_GSO_EXTHDR))
|
|
|
|
break;
|
|
|
|
}
|
2013-12-16 10:48:07 +08:00
|
|
|
opth = (void *)opth + optlen;
|
|
|
|
optlen = ipv6_optlen(opth);
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
len += optlen;
|
|
|
|
proto = opth->nexthdr;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2012-11-15 16:49:16 +08:00
|
|
|
static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
const struct net_offload *ops;
|
|
|
|
struct sk_buff **pp = NULL;
|
|
|
|
struct sk_buff *p;
|
|
|
|
struct ipv6hdr *iph;
|
|
|
|
unsigned int nlen;
|
|
|
|
unsigned int hlen;
|
|
|
|
unsigned int off;
|
|
|
|
int flush = 1;
|
|
|
|
int proto;
|
|
|
|
__wsum csum;
|
|
|
|
|
|
|
|
off = skb_gro_offset(skb);
|
|
|
|
hlen = off + sizeof(*iph);
|
|
|
|
iph = skb_gro_header_fast(skb, off);
|
|
|
|
if (skb_gro_header_hard(skb, hlen)) {
|
|
|
|
iph = skb_gro_header_slow(skb, hlen, off);
|
|
|
|
if (unlikely(!iph))
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
skb_set_network_header(skb, off);
|
2012-11-15 16:49:16 +08:00
|
|
|
skb_gro_pull(skb, sizeof(*iph));
|
|
|
|
skb_set_transport_header(skb, skb_gro_offset(skb));
|
|
|
|
|
|
|
|
flush += ntohs(iph->payload_len) != skb_gro_len(skb);
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
proto = iph->nexthdr;
|
|
|
|
ops = rcu_dereference(inet6_offloads[proto]);
|
2012-11-15 16:49:23 +08:00
|
|
|
if (!ops || !ops->callbacks.gro_receive) {
|
2012-11-15 16:49:16 +08:00
|
|
|
__pskb_pull(skb, skb_gro_offset(skb));
|
|
|
|
proto = ipv6_gso_pull_exthdrs(skb, proto);
|
|
|
|
skb_gro_pull(skb, -skb_transport_offset(skb));
|
|
|
|
skb_reset_transport_header(skb);
|
|
|
|
__skb_push(skb, skb_gro_offset(skb));
|
|
|
|
|
|
|
|
ops = rcu_dereference(inet6_offloads[proto]);
|
2012-11-15 16:49:23 +08:00
|
|
|
if (!ops || !ops->callbacks.gro_receive)
|
2012-11-15 16:49:16 +08:00
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
iph = ipv6_hdr(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
NAPI_GRO_CB(skb)->proto = proto;
|
|
|
|
|
|
|
|
flush--;
|
|
|
|
nlen = skb_network_header_len(skb);
|
|
|
|
|
|
|
|
for (p = *head; p; p = p->next) {
|
|
|
|
const struct ipv6hdr *iph2;
|
|
|
|
__be32 first_word; /* <Version:4><Traffic_Class:8><Flow_Label:20> */
|
|
|
|
|
|
|
|
if (!NAPI_GRO_CB(p)->same_flow)
|
|
|
|
continue;
|
|
|
|
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
iph2 = (struct ipv6hdr *)(p->data + off);
|
2012-11-15 16:49:16 +08:00
|
|
|
first_word = *(__be32 *)iph ^ *(__be32 *)iph2 ;
|
|
|
|
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
/* All fields must match except length and Traffic Class.
|
|
|
|
* XXX skbs on the gro_list have all been parsed and pulled
|
|
|
|
* already so we don't need to compare nlen
|
|
|
|
* (nlen != (sizeof(*iph2) + ipv6_exthdrs_len(iph2, &ops)))
|
|
|
|
* memcmp() alone below is suffcient, right?
|
|
|
|
*/
|
|
|
|
if ((first_word & htonl(0xF00FFFFF)) ||
|
2012-11-15 16:49:16 +08:00
|
|
|
memcmp(&iph->nexthdr, &iph2->nexthdr,
|
|
|
|
nlen - offsetof(struct ipv6hdr, nexthdr))) {
|
|
|
|
NAPI_GRO_CB(p)->same_flow = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* flush if Traffic Class fields are different */
|
|
|
|
NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000));
|
|
|
|
NAPI_GRO_CB(p)->flush |= flush;
|
|
|
|
}
|
|
|
|
|
|
|
|
NAPI_GRO_CB(skb)->flush |= flush;
|
|
|
|
|
|
|
|
csum = skb->csum;
|
|
|
|
skb_postpull_rcsum(skb, iph, skb_network_header_len(skb));
|
|
|
|
|
2012-11-15 16:49:23 +08:00
|
|
|
pp = ops->callbacks.gro_receive(head, skb);
|
2012-11-15 16:49:16 +08:00
|
|
|
|
|
|
|
skb->csum = csum;
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
out:
|
|
|
|
NAPI_GRO_CB(skb)->flush |= flush;
|
|
|
|
|
|
|
|
return pp;
|
|
|
|
}
|
|
|
|
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
|
2012-11-15 16:49:16 +08:00
|
|
|
{
|
|
|
|
const struct net_offload *ops;
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + nhoff);
|
2012-11-15 16:49:16 +08:00
|
|
|
int err = -ENOSYS;
|
|
|
|
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
iph->payload_len = htons(skb->len - nhoff - sizeof(*iph));
|
2012-11-15 16:49:16 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
|
|
|
|
nhoff += sizeof(*iph) + ipv6_exthdrs_len(iph, &ops);
|
2012-11-15 16:49:23 +08:00
|
|
|
if (WARN_ON(!ops || !ops->callbacks.gro_complete))
|
2012-11-15 16:49:16 +08:00
|
|
|
goto out_unlock;
|
|
|
|
|
net-gro: Prepare GRO stack for the upcoming tunneling support
This patch modifies the GRO stack to avoid the use of "network_header"
and associated macros like ip_hdr() and ipv6_hdr() in order to allow
an arbitary number of IP hdrs (v4 or v6) to be used in the
encapsulation chain. This lays the foundation for various IP
tunneling support (IP-in-IP, GRE, VXLAN, SIT,...) to be added later.
With this patch, the GRO stack traversing now is mostly based on
skb_gro_offset rather than special hdr offsets saved in skb (e.g.,
skb->network_header). As a result all but the top layer (i.e., the
the transport layer) must have hdrs of the same length in order for
a pkt to be considered for aggregation. Therefore when adding a new
encap layer (e.g., for tunneling), one must check and skip flows
(e.g., by setting NAPI_GRO_CB(p)->same_flow to 0) that have a
different hdr length.
Note that unlike the network header, the transport header can and
will continue to be set by the GRO code since there will be at
most one "transport layer" in the encap chain.
Signed-off-by: H.K. Jerry Chu <hkchu@google.com>
Suggested-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-12 12:53:45 +08:00
|
|
|
err = ops->callbacks.gro_complete(skb, nhoff);
|
2012-11-15 16:49:16 +08:00
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct packet_offload ipv6_packet_offload __read_mostly = {
|
|
|
|
.type = cpu_to_be16(ETH_P_IPV6),
|
2012-11-15 16:49:23 +08:00
|
|
|
.callbacks = {
|
|
|
|
.gso_send_check = ipv6_gso_send_check,
|
|
|
|
.gso_segment = ipv6_gso_segment,
|
|
|
|
.gro_receive = ipv6_gro_receive,
|
|
|
|
.gro_complete = ipv6_gro_complete,
|
|
|
|
},
|
2012-11-15 16:49:16 +08:00
|
|
|
};
|
|
|
|
|
2013-10-21 11:47:30 +08:00
|
|
|
static const struct net_offload sit_offload = {
|
|
|
|
.callbacks = {
|
|
|
|
.gso_send_check = ipv6_gso_send_check,
|
|
|
|
.gso_segment = ipv6_gso_segment,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2012-11-15 16:49:22 +08:00
|
|
|
static int __init ipv6_offload_init(void)
|
2012-11-15 16:49:16 +08:00
|
|
|
{
|
2012-11-15 16:49:22 +08:00
|
|
|
|
|
|
|
if (tcpv6_offload_init() < 0)
|
|
|
|
pr_crit("%s: Cannot add TCP protocol offload\n", __func__);
|
|
|
|
if (udp_offload_init() < 0)
|
|
|
|
pr_crit("%s: Cannot add UDP protocol offload\n", __func__);
|
|
|
|
if (ipv6_exthdrs_offload_init() < 0)
|
|
|
|
pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__);
|
|
|
|
|
2012-11-15 16:49:16 +08:00
|
|
|
dev_add_offload(&ipv6_packet_offload);
|
2013-10-21 11:47:30 +08:00
|
|
|
|
|
|
|
inet_add_offload(&sit_offload, IPPROTO_IPV6);
|
|
|
|
|
2012-11-15 16:49:22 +08:00
|
|
|
return 0;
|
2012-11-15 16:49:16 +08:00
|
|
|
}
|
|
|
|
|
2012-11-15 16:49:22 +08:00
|
|
|
fs_initcall(ipv6_offload_init);
|