X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=net%2Fipv6%2Ficmp.c;h=121d517bf91c9d16467c5aa85df47efdd0b6c58e;hb=3d4d4582e5b3f67a68f2cf32fd5b70d8d80f119d;hp=6a6714d154ed08422141ffecd05032be02fb8a80;hpb=40b42f1ebf653cd72c32eb1a1a0b9fea2dfbfd7d;p=linux-2.6 diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 6a6714d154..121d517bf9 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -63,12 +63,15 @@ #include #include #include +#include #include #include DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics) __read_mostly; EXPORT_SYMBOL(icmpv6_statistics); +DEFINE_SNMP_STAT(struct icmpv6msg_mib, icmpv6msg_statistics) __read_mostly; +EXPORT_SYMBOL(icmpv6msg_statistics); /* * The ICMP socket(s). This is the most convenient way to flow control @@ -80,11 +83,11 @@ EXPORT_SYMBOL(icmpv6_statistics); static DEFINE_PER_CPU(struct socket *, __icmpv6_socket) = NULL; #define icmpv6_socket __get_cpu_var(__icmpv6_socket) -static int icmpv6_rcv(struct sk_buff **pskb); +static int icmpv6_rcv(struct sk_buff *skb); static struct inet6_protocol icmpv6_protocol = { .handler = icmpv6_rcv, - .flags = INET6_PROTO_FINAL, + .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, }; static __inline__ int icmpv6_xmit_lock(void) @@ -151,8 +154,6 @@ static int is_ineligible(struct sk_buff *skb) return 0; } -static int sysctl_icmpv6_time __read_mostly = 1*HZ; - /* * Check the ICMP output rate limit */ @@ -183,7 +184,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 = sysctl_icmpv6_time; + int tmo = init_net.ipv6.sysctl.icmpv6_time; /* Give more bandwidth to wider prefixes. */ if (rt->rt6i_dst.plen < 128) @@ -308,8 +309,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, struct ipv6_pinfo *np; struct in6_addr *saddr = NULL; struct dst_entry *dst; + struct dst_entry *dst2; struct icmp6hdr tmp_hdr; struct flowi fl; + struct flowi fl2; struct icmpv6_msg msg; int iif = 0; int addr_type = 0; @@ -329,7 +332,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(&hdr->daddr, skb->dev, 0)) + if (ipv6_chk_addr(&init_net, &hdr->daddr, skb->dev, 0)) saddr = &hdr->daddr; /* @@ -416,9 +419,42 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, goto out_dst_release; } - if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) + /* No need to clone since we're just using its address. */ + dst2 = dst; + + err = xfrm_lookup(&dst, &fl, sk, 0); + switch (err) { + case 0: + if (dst != dst2) + goto route_done; + break; + case -EPERM: + dst = NULL; + break; + default: goto out; + } + if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6)) + goto out; + + if (ip6_dst_lookup(sk, &dst2, &fl)) + goto out; + + err = xfrm_lookup(&dst2, &fl, sk, XFRM_LOOKUP_ICMP); + if (err == -ENOENT) { + if (!dst) + goto out; + goto route_done; + } + + dst_release(dst); + dst = dst2; + + if (err) + goto out; + +route_done: if (ipv6_addr_is_multicast(&fl.fl6_dst)) hlimit = np->mcast_hops; else @@ -456,10 +492,6 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, } err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, len + sizeof(struct icmp6hdr)); - if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) - ICMP6_INC_STATS_OFFSET_BH(idev, ICMP6_MIB_OUTDESTUNREACHS, type - ICMPV6_DEST_UNREACH); - ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS); - out_put: if (likely(idev != NULL)) in6_dev_put(idev); @@ -547,9 +579,6 @@ static void icmpv6_echo_reply(struct sk_buff *skb) } err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, skb->len + sizeof(struct icmp6hdr)); - ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTECHOREPLIES); - ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS); - out_put: if (likely(idev != NULL)) in6_dev_put(idev); @@ -560,9 +589,7 @@ out: static void icmpv6_notify(struct sk_buff *skb, int type, int code, __be32 info) { - struct in6_addr *saddr, *daddr; struct inet6_protocol *ipprot; - struct sock *sk; int inner_offset; int hash; u8 nexthdr; @@ -584,9 +611,6 @@ static void icmpv6_notify(struct sk_buff *skb, int type, int code, __be32 info) if (!pskb_may_pull(skb, inner_offset+8)) return; - saddr = &ipv6_hdr(skb)->saddr; - daddr = &ipv6_hdr(skb)->daddr; - /* BUGGG_FUTURE: we should try to parse exthdrs in this packet. Without this we will not able f.e. to make source routed pmtu discovery. @@ -602,24 +626,15 @@ static void icmpv6_notify(struct sk_buff *skb, int type, int code, __be32 info) ipprot->err_handler(skb, NULL, type, code, inner_offset, info); rcu_read_unlock(); - read_lock(&raw_v6_lock); - if ((sk = sk_head(&raw_v6_htable[hash])) != NULL) { - while ((sk = __raw_v6_lookup(sk, nexthdr, saddr, daddr, - IP6CB(skb)->iif))) { - rawv6_err(sk, skb, NULL, type, code, inner_offset, info); - sk = sk_next(sk); - } - } - read_unlock(&raw_v6_lock); + raw6_icmp_error(skb, nexthdr, type, code, inner_offset, info); } /* * Handle icmp messages */ -static int icmpv6_rcv(struct sk_buff **pskb) +static int icmpv6_rcv(struct sk_buff *skb) { - struct sk_buff *skb = *pskb; struct net_device *dev = skb->dev; struct inet6_dev *idev = __in6_dev_get(dev); struct in6_addr *saddr, *daddr; @@ -627,6 +642,25 @@ static int icmpv6_rcv(struct sk_buff **pskb) struct icmp6hdr *hdr; int type; + if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { + int nh; + + if (!(skb->sp && skb->sp->xvec[skb->sp->len - 1]->props.flags & + XFRM_STATE_ICMP)) + goto drop_no_count; + + if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(*orig_hdr))) + goto drop_no_count; + + nh = skb_network_offset(skb); + skb_set_network_header(skb, sizeof(*hdr)); + + if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN, skb)) + goto drop_no_count; + + skb_set_network_header(skb, nh); + } + ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS); saddr = &ipv6_hdr(skb)->saddr; @@ -649,17 +683,14 @@ static int icmpv6_rcv(struct sk_buff **pskb) } } - if (!pskb_pull(skb, sizeof(struct icmp6hdr))) + if (!pskb_pull(skb, sizeof(*hdr))) goto discard_it; hdr = icmp6_hdr(skb); type = hdr->icmp6_type; - if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) - ICMP6_INC_STATS_OFFSET_BH(idev, ICMP6_MIB_INDESTUNREACHS, type - ICMPV6_DEST_UNREACH); - else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT) - ICMP6_INC_STATS_OFFSET_BH(idev, ICMP6_MIB_INECHOS, type - ICMPV6_ECHO_REQUEST); + ICMP6MSGIN_INC_STATS_BH(idev, type); switch (type) { case ICMPV6_ECHO_REQUEST: @@ -739,6 +770,7 @@ static int icmpv6_rcv(struct sk_buff **pskb) discard_it: ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); +drop_no_count: kfree_skb(skb); return 0; } @@ -874,16 +906,26 @@ int icmpv6_err_convert(int type, int code, int *err) EXPORT_SYMBOL(icmpv6_err_convert); #ifdef CONFIG_SYSCTL -ctl_table ipv6_icmp_table[] = { +ctl_table ipv6_icmp_table_template[] = { { .ctl_name = NET_IPV6_ICMP_RATELIMIT, .procname = "ratelimit", - .data = &sysctl_icmpv6_time, + .data = &init_net.ipv6.sysctl.icmpv6_time, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec }, { .ctl_name = 0 }, }; + +struct ctl_table *ipv6_icmp_sysctl_init(struct net *net) +{ + struct ctl_table *table; + + table = kmemdup(ipv6_icmp_table_template, + sizeof(ipv6_icmp_table_template), + GFP_KERNEL); + return table; +} #endif