Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 127823
b: refs/heads/master
c: 787e920
h: refs/heads/master
i:
  127821: 42a2294
  127819: ff0c52c
  127815: 070b74f
  127807: 0f30761
v: v3
  • Loading branch information
Herbert Xu authored and David S. Miller committed Jan 8, 2009
1 parent e5ccc19 commit 0866fef
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 6 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: d9d6f46b42294754f8d4ed743124ae8bb8e01fba
refs/heads/master: 787e9208360117835101f513f7db593dc2525cf8
3 changes: 3 additions & 0 deletions trunk/include/net/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ struct inet6_protocol
int (*gso_send_check)(struct sk_buff *skb);
struct sk_buff *(*gso_segment)(struct sk_buff *skb,
int features);
struct sk_buff **(*gro_receive)(struct sk_buff **head,
struct sk_buff *skb);
int (*gro_complete)(struct sk_buff *skb);

unsigned int flags; /* INET6_PROTO_xxx */
};
Expand Down
107 changes: 102 additions & 5 deletions trunk/net/ipv6/af_inet6.c
Original file line number Diff line number Diff line change
Expand Up @@ -672,8 +672,7 @@ int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)

EXPORT_SYMBOL_GPL(ipv6_opt_accepted);

static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb,
int proto)
static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
{
struct inet6_protocol *ops = NULL;

Expand Down Expand Up @@ -704,7 +703,7 @@ static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb,
__skb_pull(skb, len);
}

return ops;
return proto;
}

static int ipv6_gso_send_check(struct sk_buff *skb)
Expand All @@ -721,7 +720,9 @@ static int ipv6_gso_send_check(struct sk_buff *skb)
err = -EPROTONOSUPPORT;

rcu_read_lock();
ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
ops = rcu_dereference(inet6_protos[
ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);

if (likely(ops && ops->gso_send_check)) {
skb_reset_transport_header(skb);
err = ops->gso_send_check(skb);
Expand Down Expand Up @@ -757,7 +758,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
segs = ERR_PTR(-EPROTONOSUPPORT);

rcu_read_lock();
ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
ops = rcu_dereference(inet6_protos[
ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);

if (likely(ops && ops->gso_segment)) {
skb_reset_transport_header(skb);
segs = ops->gso_segment(skb, features);
Expand All @@ -777,11 +780,105 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
return segs;
}

struct ipv6_gro_cb {
struct napi_gro_cb napi;
int proto;
};

#define IPV6_GRO_CB(skb) ((struct ipv6_gro_cb *)(skb)->cb)

static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
struct inet6_protocol *ops;
struct sk_buff **pp = NULL;
struct sk_buff *p;
struct ipv6hdr *iph;
unsigned int nlen;
int flush = 1;
int proto;

if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
goto out;

iph = ipv6_hdr(skb);
__skb_pull(skb, sizeof(*iph));

flush += ntohs(iph->payload_len) != skb->len;

rcu_read_lock();
proto = ipv6_gso_pull_exthdrs(skb, iph->nexthdr);
IPV6_GRO_CB(skb)->proto = proto;
ops = rcu_dereference(inet6_protos[proto]);
if (!ops || !ops->gro_receive)
goto out_unlock;

flush--;
skb_reset_transport_header(skb);
nlen = skb_network_header_len(skb);

for (p = *head; p; p = p->next) {
struct ipv6hdr *iph2;

if (!NAPI_GRO_CB(p)->same_flow)
continue;

iph2 = ipv6_hdr(p);

/* All fields must match except length. */
if (nlen != skb_network_header_len(p) ||
memcmp(iph, iph2, offsetof(struct ipv6hdr, payload_len)) ||
memcmp(&iph->nexthdr, &iph2->nexthdr,
nlen - offsetof(struct ipv6hdr, nexthdr))) {
NAPI_GRO_CB(p)->same_flow = 0;
continue;
}

NAPI_GRO_CB(p)->flush |= flush;
}

NAPI_GRO_CB(skb)->flush |= flush;

pp = ops->gro_receive(head, skb);

out_unlock:
rcu_read_unlock();

out:
NAPI_GRO_CB(skb)->flush |= flush;

return pp;
}

static int ipv6_gro_complete(struct sk_buff *skb)
{
struct inet6_protocol *ops;
struct ipv6hdr *iph = ipv6_hdr(skb);
int err = -ENOSYS;

iph->payload_len = htons(skb->len - skb_network_offset(skb) -
sizeof(*iph));

rcu_read_lock();
ops = rcu_dereference(inet6_protos[IPV6_GRO_CB(skb)->proto]);
if (WARN_ON(!ops || !ops->gro_complete))
goto out_unlock;

err = ops->gro_complete(skb);

out_unlock:
rcu_read_unlock();

return err;
}

static struct packet_type ipv6_packet_type = {
.type = __constant_htons(ETH_P_IPV6),
.func = ipv6_rcv,
.gso_send_check = ipv6_gso_send_check,
.gso_segment = ipv6_gso_segment,
.gro_receive = ipv6_gro_receive,
.gro_complete = ipv6_gro_complete,
};

static int __init ipv6_packet_init(void)
Expand Down

0 comments on commit 0866fef

Please sign in to comment.