aboutsummaryrefslogtreecommitdiff
path: root/net/ipv6
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2009-01-10 12:04:41 +0100
committerIngo Molnar <mingo@elte.hu>2009-01-10 12:04:41 +0100
commitb17304245f0db0ac69b795c411407808f3f2796d (patch)
tree63ed3915d9295bd08f640bf25c322064ba787fad /net/ipv6
parent889c92d21db40be0b7d22a59395060237895bb85 (diff)
parent9a100a4464917b5ffff3a8ce1c2758088fd9bb32 (diff)
Merge branch 'linus' into x86/setup-lzma
Conflicts: init/do_mounts_rd.c
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/af_inet6.c107
-rw-r--r--net/ipv6/ipv6_sockglue.c2
-rw-r--r--net/ipv6/route.c54
-rw-r--r--net/ipv6/sysctl_net_ipv6.c2
-rw-r--r--net/ipv6/tcp_ipv6.c47
5 files changed, 194 insertions, 18 deletions
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 437b750b98f..94f74f5b0cb 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -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;
@@ -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)
@@ -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);
@@ -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);
@@ -777,11 +780,105 @@ out:
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)
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index eeeaad2e8b5..40f324655e2 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -404,7 +404,7 @@ sticky_done:
else if (optlen < sizeof(struct in6_pktinfo) || optval == NULL)
goto e_inval;
- if (copy_from_user(&pkt, optval, optlen)) {
+ if (copy_from_user(&pkt, optval, sizeof(struct in6_pktinfo))) {
retv = -EFAULT;
break;
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 18c486cf498..c4a59824ac2 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -627,6 +627,9 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad
rt = ip6_rt_copy(ort);
if (rt) {
+ struct neighbour *neigh;
+ int attempts = !in_softirq();
+
if (!(rt->rt6i_flags&RTF_GATEWAY)) {
if (rt->rt6i_dst.plen != 128 &&
ipv6_addr_equal(&rt->rt6i_dst.addr, daddr))
@@ -646,7 +649,35 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad
}
#endif
- rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+ retry:
+ neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+ if (IS_ERR(neigh)) {
+ struct net *net = dev_net(rt->rt6i_dev);
+ int saved_rt_min_interval =
+ net->ipv6.sysctl.ip6_rt_gc_min_interval;
+ int saved_rt_elasticity =
+ net->ipv6.sysctl.ip6_rt_gc_elasticity;
+
+ if (attempts-- > 0) {
+ net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
+ net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
+
+ ip6_dst_gc(net->ipv6.ip6_dst_ops);
+
+ net->ipv6.sysctl.ip6_rt_gc_elasticity =
+ saved_rt_elasticity;
+ net->ipv6.sysctl.ip6_rt_gc_min_interval =
+ saved_rt_min_interval;
+ goto retry;
+ }
+
+ if (net_ratelimit())
+ printk(KERN_WARNING
+ "Neighbour table overflow.\n");
+ dst_free(&rt->u.dst);
+ return NULL;
+ }
+ rt->rt6i_nexthop = neigh;
}
@@ -945,8 +976,11 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
dev_hold(dev);
if (neigh)
neigh_hold(neigh);
- else
+ else {
neigh = ndisc_get_neigh(dev, addr);
+ if (IS_ERR(neigh))
+ neigh = NULL;
+ }
rt->rt6i_dev = dev;
rt->rt6i_idev = idev;
@@ -1887,6 +1921,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
{
struct net *net = dev_net(idev->dev);
struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+ struct neighbour *neigh;
if (rt == NULL)
return ERR_PTR(-ENOMEM);
@@ -1909,11 +1944,18 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
rt->rt6i_flags |= RTF_ANYCAST;
else
rt->rt6i_flags |= RTF_LOCAL;
- rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
- if (rt->rt6i_nexthop == NULL) {
+ neigh = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+ if (IS_ERR(neigh)) {
dst_free(&rt->u.dst);
- return ERR_PTR(-ENOMEM);
+
+ /* We are casting this because that is the return
+ * value type. But an errno encoded pointer is the
+ * same regardless of the underlying pointer type,
+ * and that's what we are returning. So this is OK.
+ */
+ return (struct rt6_info *) neigh;
}
+ rt->rt6i_nexthop = neigh;
ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
rt->rt6i_dst.plen = 128;
@@ -2710,7 +2752,7 @@ int __init ip6_route_init(void)
kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!ip6_dst_ops_template.kmem_cachep)
- goto out;;
+ goto out;
ret = register_pernet_subsys(&ip6_route_net_ops);
if (ret)
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index 9048fe7e7ea..a031034720b 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -128,7 +128,7 @@ static struct ctl_table_header *ip6_header;
int ipv6_sysctl_register(void)
{
- int err = -ENOMEM;;
+ int err = -ENOMEM;
ip6_header = register_net_sysctl_rotable(net_ipv6_ctl_path, ipv6_table);
if (ip6_header == NULL)
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index e8b8337a831..e5b85d45bee 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -101,7 +101,7 @@ static void tcp_v6_hash(struct sock *sk)
}
}
-static __inline__ __sum16 tcp_v6_check(struct tcphdr *th, int len,
+static __inline__ __sum16 tcp_v6_check(int len,
struct in6_addr *saddr,
struct in6_addr *daddr,
__wsum base)
@@ -501,7 +501,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req)
if (skb) {
struct tcphdr *th = tcp_hdr(skb);
- th->check = tcp_v6_check(th, skb->len,
+ th->check = tcp_v6_check(skb->len,
&treq->loc_addr, &treq->rmt_addr,
csum_partial(th, skb->len, skb->csum));
@@ -942,6 +942,41 @@ static int tcp_v6_gso_send_check(struct sk_buff *skb)
return 0;
}
+struct sk_buff **tcp6_gro_receive(struct sk_buff **head, struct sk_buff *skb)
+{
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+
+ switch (skb->ip_summed) {
+ case CHECKSUM_COMPLETE:
+ if (!tcp_v6_check(skb->len, &iph->saddr, &iph->daddr,
+ skb->csum)) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ break;
+ }
+
+ /* fall through */
+ case CHECKSUM_NONE:
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
+ }
+
+ return tcp_gro_receive(head, skb);
+}
+EXPORT_SYMBOL(tcp6_gro_receive);
+
+int tcp6_gro_complete(struct sk_buff *skb)
+{
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+ struct tcphdr *th = tcp_hdr(skb);
+
+ th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
+ &iph->saddr, &iph->daddr, 0);
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+
+ return tcp_gro_complete(skb);
+}
+EXPORT_SYMBOL(tcp6_gro_complete);
+
static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
u32 ts, struct tcp_md5sig_key *key, int rst)
{
@@ -1429,14 +1464,14 @@ out:
static __sum16 tcp_v6_checksum_init(struct sk_buff *skb)
{
if (skb->ip_summed == CHECKSUM_COMPLETE) {
- if (!tcp_v6_check(tcp_hdr(skb), skb->len, &ipv6_hdr(skb)->saddr,
+ if (!tcp_v6_check(skb->len, &ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr, skb->csum)) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
return 0;
}
}
- skb->csum = ~csum_unfold(tcp_v6_check(tcp_hdr(skb), skb->len,
+ skb->csum = ~csum_unfold(tcp_v6_check(skb->len,
&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr, 0));
@@ -1640,7 +1675,7 @@ process:
#ifdef CONFIG_NET_DMA
struct tcp_sock *tp = tcp_sk(sk);
if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
- tp->ucopy.dma_chan = get_softnet_dma();
+ tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY);
if (tp->ucopy.dma_chan)
ret = tcp_v6_do_rcv(sk, skb);
else
@@ -2062,6 +2097,8 @@ static struct inet6_protocol tcpv6_protocol = {
.err_handler = tcp_v6_err,
.gso_send_check = tcp_v6_gso_send_check,
.gso_segment = tcp_tso_segment,
+ .gro_receive = tcp6_gro_receive,
+ .gro_complete = tcp6_gro_complete,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};