Release 4.11 net/ipv6/ip6_offload.c
/*
* 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>
#include <linux/printk.h>
#include <net/protocol.h>
#include <net/ipv6.h>
#include <net/inet_common.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;
opth = (void *)skb->data;
proto = opth->nexthdr;
__skb_pull(skb, len);
}
return proto;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vlad Yasevich | 137 | 93.20% | 1 | 50.00% |
Li RongQing | 10 | 6.80% | 1 | 50.00% |
Total | 147 | 100.00% | 2 | 100.00% |
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;
unsigned int payload_len;
u8 *prevhdr;
int offset = 0;
bool encap, udpfrag;
int nhoff;
bool gso_partial;
skb_reset_network_header(skb);
nhoff = skb_network_header(skb) - skb_mac_header(skb);
if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
goto out;
encap = SKB_GSO_CB(skb)->encap_level > 0;
if (encap)
features &= skb->dev->hw_enc_features;
SKB_GSO_CB(skb)->encap_level += sizeof(*ipv6h);
ipv6h = ipv6_hdr(skb);
__skb_pull(skb, sizeof(*ipv6h));
segs = ERR_PTR(-EPROTONOSUPPORT);
proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
if (skb->encapsulation &&
skb_shinfo(skb)->gso_type & (SKB_GSO_IPXIP4 | SKB_GSO_IPXIP6))
udpfrag = proto == IPPROTO_UDP && encap;
else
udpfrag = proto == IPPROTO_UDP && !skb->encapsulation;
ops = rcu_dereference(inet6_offloads[proto]);
if (likely(ops && ops->callbacks.gso_segment)) {
skb_reset_transport_header(skb);
segs = ops->callbacks.gso_segment(skb, features);
}
if (IS_ERR_OR_NULL(segs))
goto out;
gso_partial = !!(skb_shinfo(segs)->gso_type & SKB_GSO_PARTIAL);
for (skb = segs; skb; skb = skb->next) {
ipv6h = (struct ipv6hdr *)(skb_mac_header(skb) + nhoff);
if (gso_partial)
payload_len = skb_shinfo(skb)->gso_size +
SKB_GSO_CB(skb)->data_offset +
skb->head - (unsigned char *)(ipv6h + 1);
else
payload_len = skb->len - nhoff - sizeof(*ipv6h);
ipv6h->payload_len = htons(payload_len);
skb->network_header = (u8 *)ipv6h - skb->head;
if (udpfrag) {
unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
fptr = (struct frag_hdr *)((u8 *)ipv6h + unfrag_ip6hlen);
fptr->frag_off = htons(offset);
if (skb->next)
fptr->frag_off |= htons(IP6_MF);
offset += (ntohs(ipv6h->payload_len) -
sizeof(struct frag_hdr));
}
if (encap)
skb_reset_inner_headers(skb);
}
out:
return segs;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vlad Yasevich | 276 | 57.02% | 2 | 20.00% |
Eric Dumazet | 81 | 16.74% | 1 | 10.00% |
Hannes Frederic Sowa | 52 | 10.74% | 1 | 10.00% |
Alexander Duyck | 46 | 9.50% | 1 | 10.00% |
Steffen Klassert | 19 | 3.93% | 1 | 10.00% |
Américo Wang | 6 | 1.24% | 1 | 10.00% |
Tom Herbert | 2 | 0.41% | 1 | 10.00% |
Florian Westphal | 1 | 0.21% | 1 | 10.00% |
Artem Savkov | 1 | 0.21% | 1 | 10.00% |
Total | 484 | 100.00% | 10 | 100.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)
{
struct ipv6_opt_hdr *opth = (void *)iph;
int len = 0, proto, optlen = sizeof(*iph);
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;
}
opth = (void *)opth + optlen;
optlen = ipv6_optlen(opth);
len += optlen;
proto = opth->nexthdr;
}
return len;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jerry Chu | 125 | 93.98% | 2 | 66.67% |
Hannes Frederic Sowa | 8 | 6.02% | 1 | 33.33% |
Total | 133 | 100.00% | 3 | 100.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;
u16 flush = 1;
int proto;
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;
}
skb_set_network_header(skb, off);
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]);
if (!ops || !ops->callbacks.gro_receive) {
__pskb_pull(skb, skb_gro_offset(skb));
skb_gro_frag0_invalidate(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]);
if (!ops || !ops->callbacks.gro_receive)
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;
iph2 = (struct ipv6hdr *)(p->data + off);
first_word = *(__be32 *)iph ^ *(__be32 *)iph2;
/* 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)) ||
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;
/* If the previous IP ID value was based on an atomic
* datagram we can overwrite the value and ignore it.
*/
if (NAPI_GRO_CB(skb)->is_atomic)
NAPI_GRO_CB(p)->flush_id = 0;
}
NAPI_GRO_CB(skb)->is_atomic = true;
NAPI_GRO_CB(skb)->flush |= flush;
skb_gro_postpull_rcsum(skb, iph, nlen);
pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
out:
skb_gro_flush_final(skb, pp, flush);
return pp;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vlad Yasevich | 460 | 87.95% | 2 | 20.00% |
Jerry Chu | 20 | 3.82% | 2 | 20.00% |
Alexander Duyck | 19 | 3.63% | 1 | 10.00% |
Tom Herbert | 9 | 1.72% | 1 | 10.00% |
Herbert Xu | 5 | 0.96% | 1 | 10.00% |
Steffen Klassert | 5 | 0.96% | 1 | 10.00% |
Sabrina Dubroca | 3 | 0.57% | 1 | 10.00% |
Eric Dumazet | 2 | 0.38% | 1 | 10.00% |
Total | 523 | 100.00% | 10 | 100.00% |
static struct sk_buff **sit_ip6ip6_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
/* Common GRO receive for SIT and IP6IP6 */
if (NAPI_GRO_CB(skb)->encap_mark) {
NAPI_GRO_CB(skb)->flush = 1;
return NULL;
}
NAPI_GRO_CB(skb)->encap_mark = 1;
return ipv6_gro_receive(head, skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jesse Gross | 59 | 96.72% | 1 | 50.00% |
Tom Herbert | 2 | 3.28% | 1 | 50.00% |
Total | 61 | 100.00% | 2 | 100.00% |
static struct sk_buff **ip4ip6_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
/* Common GRO receive for SIT and IP6IP6 */
if (NAPI_GRO_CB(skb)->encap_mark) {
NAPI_GRO_CB(skb)->flush = 1;
return NULL;
}
NAPI_GRO_CB(skb)->encap_mark = 1;
return inet_gro_receive(head, skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tom Herbert | 61 | 100.00% | 1 | 100.00% |
Total | 61 | 100.00% | 1 | 100.00% |
static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
{
const struct net_offload *ops;
struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + nhoff);
int err = -ENOSYS;
if (skb->encapsulation) {
skb_set_inner_protocol(skb, cpu_to_be16(ETH_P_IPV6));
skb_set_inner_network_header(skb, nhoff);
}
iph->payload_len = htons(skb->len - nhoff - sizeof(*iph));
rcu_read_lock();
nhoff += sizeof(*iph) + ipv6_exthdrs_len(iph, &ops);
if (WARN_ON(!ops || !ops->callbacks.gro_complete))
goto out_unlock;
err = ops->callbacks.gro_complete(skb, nhoff);
out_unlock:
rcu_read_unlock();
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vlad Yasevich | 92 | 63.01% | 2 | 40.00% |
Jerry Chu | 29 | 19.86% | 1 | 20.00% |
Eric Dumazet | 13 | 8.90% | 1 | 20.00% |
Paolo Abeni | 12 | 8.22% | 1 | 20.00% |
Total | 146 | 100.00% | 5 | 100.00% |
static int sit_gro_complete(struct sk_buff *skb, int nhoff)
{
skb->encapsulation = 1;
skb_shinfo(skb)->gso_type |= SKB_GSO_IPXIP4;
return ipv6_gro_complete(skb, nhoff);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric Dumazet | 36 | 97.30% | 1 | 50.00% |
Tom Herbert | 1 | 2.70% | 1 | 50.00% |
Total | 37 | 100.00% | 2 | 100.00% |
static int ip6ip6_gro_complete(struct sk_buff *skb, int nhoff)
{
skb->encapsulation = 1;
skb_shinfo(skb)->gso_type |= SKB_GSO_IPXIP6;
return ipv6_gro_complete(skb, nhoff);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tom Herbert | 37 | 100.00% | 1 | 100.00% |
Total | 37 | 100.00% | 1 | 100.00% |
static int ip4ip6_gro_complete(struct sk_buff *skb, int nhoff)
{
skb->encapsulation = 1;
skb_shinfo(skb)->gso_type |= SKB_GSO_IPXIP6;
return inet_gro_complete(skb, nhoff);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Tom Herbert | 37 | 100.00% | 1 | 100.00% |
Total | 37 | 100.00% | 1 | 100.00% |
static struct packet_offload ipv6_packet_offload __read_mostly = {
.type = cpu_to_be16(ETH_P_IPV6),
.callbacks = {
.gso_segment = ipv6_gso_segment,
.gro_receive = ipv6_gro_receive,
.gro_complete = ipv6_gro_complete,
},
};
static const struct net_offload sit_offload = {
.callbacks = {
.gso_segment = ipv6_gso_segment,
.gro_receive = sit_ip6ip6_gro_receive,
.gro_complete = sit_gro_complete,
},
};
static const struct net_offload ip4ip6_offload = {
.callbacks = {
.gso_segment = inet_gso_segment,
.gro_receive = ip4ip6_gro_receive,
.gro_complete = ip4ip6_gro_complete,
},
};
static const struct net_offload ip6ip6_offload = {
.callbacks = {
.gso_segment = ipv6_gso_segment,
.gro_receive = sit_ip6ip6_gro_receive,
.gro_complete = ip6ip6_gro_complete,
},
};
static int __init ipv6_offload_init(void)
{
if (tcpv6_offload_init() < 0)
pr_crit("%s: Cannot add TCP protocol offload\n", __func__);
if (ipv6_exthdrs_offload_init() < 0)
pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__);
dev_add_offload(&ipv6_packet_offload);
inet_add_offload(&sit_offload, IPPROTO_IPV6);
inet6_add_offload(&ip6ip6_offload, IPPROTO_IPV6);
inet6_add_offload(&ip4ip6_offload, IPPROTO_IPIP);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vlad Yasevich | 46 | 65.71% | 2 | 40.00% |
Tom Herbert | 16 | 22.86% | 2 | 40.00% |
Eric Dumazet | 8 | 11.43% | 1 | 20.00% |
Total | 70 | 100.00% | 5 | 100.00% |
fs_initcall(ipv6_offload_init);
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vlad Yasevich | 1077 | 56.98% | 3 | 10.71% |
Tom Herbert | 225 | 11.90% | 4 | 14.29% |
Jerry Chu | 175 | 9.26% | 3 | 10.71% |
Eric Dumazet | 167 | 8.84% | 4 | 14.29% |
Alexander Duyck | 65 | 3.44% | 2 | 7.14% |
Hannes Frederic Sowa | 60 | 3.17% | 2 | 7.14% |
Jesse Gross | 59 | 3.12% | 1 | 3.57% |
Steffen Klassert | 24 | 1.27% | 2 | 7.14% |
Paolo Abeni | 12 | 0.63% | 1 | 3.57% |
Li RongQing | 10 | 0.53% | 1 | 3.57% |
Américo Wang | 6 | 0.32% | 1 | 3.57% |
Herbert Xu | 5 | 0.26% | 1 | 3.57% |
Sabrina Dubroca | 3 | 0.16% | 1 | 3.57% |
Florian Westphal | 1 | 0.05% | 1 | 3.57% |
Artem Savkov | 1 | 0.05% | 1 | 3.57% |
Total | 1890 | 100.00% | 28 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.