X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=net%2Fipv6%2Ficmp.c;h=d42dd16d3487d8f80f15cd678950878bba1f789f;hb=df39e8ba56a788733d369068c7319e04b1da3cd5;hp=18f220a04486c123df3cb9ea29ee41ea3959a8a2;hpb=405666db84b984b68fc75794069f424c02e5796c;p=safe%2Fjmp%2Flinux-2.6 diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 18f220a..d42dd16 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -80,8 +81,10 @@ EXPORT_SYMBOL(icmpv6msg_statistics); * * On SMP we have one ICMP socket per-cpu. */ -static DEFINE_PER_CPU(struct sock *, __icmpv6_sk) = NULL; -#define icmpv6_sk __get_cpu_var(__icmpv6_sk) +static inline struct sock *icmpv6_sk(struct net *net) +{ + return net->ipv6.icmp_sk[smp_processor_id()]; +} static int icmpv6_rcv(struct sk_buff *skb); @@ -161,6 +164,7 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, int type, struct flowi *fl) { struct dst_entry *dst; + struct net *net = sock_net(sk); int res = 0; /* Informational messages are not limited. */ @@ -176,7 +180,7 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, int type, * XXX: perhaps the expire for routing entries cloned by * this lookup should be more aggressive (not longer than timeout). */ - dst = ip6_route_output(sk, fl); + dst = ip6_route_output(net, sk, fl); if (dst->error) { IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); @@ -184,7 +188,7 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, int type, res = 1; } else { struct rt6_info *rt = (struct rt6_info *)dst; - int tmo = init_net.ipv6.sysctl.icmpv6_time; + int tmo = net->ipv6.sysctl.icmpv6_time; /* Give more bandwidth to wider prefixes. */ if (rt->rt6i_dst.plen < 128) @@ -303,6 +307,7 @@ static inline void mip6_addr_swap(struct sk_buff *skb) {} void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, struct net_device *dev) { + struct net *net = dev_net(skb->dev); struct inet6_dev *idev = NULL; struct ipv6hdr *hdr = ipv6_hdr(skb); struct sock *sk; @@ -332,7 +337,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, */ addr_type = ipv6_addr_type(&hdr->daddr); - if (ipv6_chk_addr(&init_net, &hdr->daddr, skb->dev, 0)) + if (ipv6_chk_addr(net, &hdr->daddr, skb->dev, 0)) saddr = &hdr->daddr; /* @@ -389,7 +394,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, fl.fl_icmp_code = code; security_skb_classify_flow(skb, &fl); - sk = icmpv6_sk; + sk = icmpv6_sk(net); np = inet6_sk(sk); if (icmpv6_xmit_lock(sk)) @@ -436,33 +441,33 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, } if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6)) - goto out; + goto relookup_failed; if (ip6_dst_lookup(sk, &dst2, &fl)) - goto out; + goto relookup_failed; err = xfrm_lookup(&dst2, &fl, sk, XFRM_LOOKUP_ICMP); - if (err == -ENOENT) { + switch (err) { + case 0: + dst_release(dst); + dst = dst2; + break; + case -EPERM: + goto out_dst_release; + default: +relookup_failed: if (!dst) goto out; - goto route_done; + break; } - dst_release(dst); - dst = dst2; - - if (err) - goto out; - route_done: if (ipv6_addr_is_multicast(&fl.fl6_dst)) hlimit = np->mcast_hops; else hlimit = np->hop_limit; if (hlimit < 0) - hlimit = dst_metric(dst, RTAX_HOPLIMIT); - if (hlimit < 0) - hlimit = ipv6_get_hoplimit(dst->dev); + hlimit = ip6_dst_hoplimit(dst); tclass = np->tclass; if (tclass < 0) @@ -505,6 +510,7 @@ EXPORT_SYMBOL(icmpv6_send); static void icmpv6_echo_reply(struct sk_buff *skb) { + struct net *net = dev_net(skb->dev); struct sock *sk; struct inet6_dev *idev; struct ipv6_pinfo *np; @@ -535,7 +541,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl.fl_icmp_type = ICMPV6_ECHO_REPLY; security_skb_classify_flow(skb, &fl); - sk = icmpv6_sk; + sk = icmpv6_sk(net); np = inet6_sk(sk); if (icmpv6_xmit_lock(sk)) @@ -555,9 +561,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) else hlimit = np->hop_limit; if (hlimit < 0) - hlimit = dst_metric(dst, RTAX_HOPLIMIT); - if (hlimit < 0) - hlimit = ipv6_get_hoplimit(dst->dev); + hlimit = ip6_dst_hoplimit(dst); tclass = np->tclass; if (tclass < 0) @@ -775,20 +779,40 @@ drop_no_count: return 0; } +void icmpv6_flow_init(struct sock *sk, struct flowi *fl, + u8 type, + const struct in6_addr *saddr, + const struct in6_addr *daddr, + int oif) +{ + memset(fl, 0, sizeof(*fl)); + ipv6_addr_copy(&fl->fl6_src, saddr); + ipv6_addr_copy(&fl->fl6_dst, daddr); + fl->proto = IPPROTO_ICMPV6; + fl->fl_icmp_type = type; + fl->fl_icmp_code = 0; + fl->oif = oif; + security_sk_classify_flow(sk, fl); +} + /* * Special lock-class for __icmpv6_sk: */ static struct lock_class_key icmpv6_socket_sk_dst_lock_key; -int __init icmpv6_init(void) +static int __net_init icmpv6_sk_init(struct net *net) { struct sock *sk; int err, i, j; + net->ipv6.icmp_sk = + kzalloc(nr_cpu_ids * sizeof(struct sock *), GFP_KERNEL); + if (net->ipv6.icmp_sk == NULL) + return -ENOMEM; + for_each_possible_cpu(i) { - struct socket *sock; - err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, - &sock); + err = inet_ctl_sock_create(&sk, PF_INET6, + SOCK_RAW, IPPROTO_ICMPV6, net); if (err < 0) { printk(KERN_ERR "Failed to initialize the ICMP6 control socket " @@ -797,8 +821,8 @@ int __init icmpv6_init(void) goto fail; } - per_cpu(__icmpv6_sk, i) = sk = sock->sk; - sk->sk_allocation = GFP_ATOMIC; + net->ipv6.icmp_sk[i] = sk; + /* * Split off their lock-class, because sk->sk_dst_lock * gets used from softirqs, which is safe for @@ -813,39 +837,57 @@ int __init icmpv6_init(void) */ sk->sk_sndbuf = (2 * ((64 * 1024) + sizeof(struct sk_buff))); - - sk->sk_prot->unhash(sk); } - - - if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) { - printk(KERN_ERR "Failed to register ICMP6 protocol\n"); - err = -EAGAIN; - goto fail; - } - return 0; fail: - for (j = 0; j < i; j++) { - if (!cpu_possible(j)) - continue; - sock_release(per_cpu(__icmpv6_sk, j)->sk_socket); - } - + for (j = 0; j < i; j++) + inet_ctl_sock_destroy(net->ipv6.icmp_sk[j]); + kfree(net->ipv6.icmp_sk); return err; } -void icmpv6_cleanup(void) +static void __net_exit icmpv6_sk_exit(struct net *net) { int i; for_each_possible_cpu(i) { - sock_release(per_cpu(__icmpv6_sk, i)->sk_socket); + inet_ctl_sock_destroy(net->ipv6.icmp_sk[i]); } + kfree(net->ipv6.icmp_sk); +} + +static struct pernet_operations icmpv6_sk_ops = { + .init = icmpv6_sk_init, + .exit = icmpv6_sk_exit, +}; + +int __init icmpv6_init(void) +{ + int err; + + err = register_pernet_subsys(&icmpv6_sk_ops); + if (err < 0) + return err; + + err = -EAGAIN; + if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) + goto fail; + return 0; + +fail: + printk(KERN_ERR "Failed to register ICMP6 protocol\n"); + unregister_pernet_subsys(&icmpv6_sk_ops); + return err; +} + +void icmpv6_cleanup(void) +{ + unregister_pernet_subsys(&icmpv6_sk_ops); inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6); } + static const struct icmp6_err { int err; int fatal; @@ -926,6 +968,10 @@ struct ctl_table *ipv6_icmp_sysctl_init(struct net *net) table = kmemdup(ipv6_icmp_table_template, sizeof(ipv6_icmp_table_template), GFP_KERNEL); + + if (table) + table[0].data = &net->ipv6.sysctl.icmpv6_time; + return table; } #endif