]> err.no Git - linux-2.6/blobdiff - net/ipv4/icmp.c
[ICMP]: Avoid sparse warnings in net/ipv4/icmp.c
[linux-2.6] / net / ipv4 / icmp.c
index d38cbba92a4dddf62a7d53da29671ce0fa1896d7..1a6024978e28b3ae84b33f04a1431a9a293e0adf 100644 (file)
@@ -92,6 +92,7 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <net/checksum.h>
+#include <net/xfrm.h>
 
 /*
  *     Build xmit assembly blocks
@@ -115,6 +116,7 @@ struct icmp_bxm {
  *     Statistics
  */
 DEFINE_SNMP_STAT(struct icmp_mib, icmp_statistics) __read_mostly;
+DEFINE_SNMP_STAT(struct icmpmsg_mib, icmpmsg_statistics) __read_mostly;
 
 /* An array of errno for error messages from dest unreach. */
 /* RFC 1122: 3.2.2.1 States that NET_UNREACH, HOST_UNREACH and SR_FAILED MUST be considered 'transient errs'. */
@@ -214,8 +216,6 @@ int sysctl_icmp_errors_use_inbound_ifaddr __read_mostly;
  */
 
 struct icmp_control {
-       int output_entry;       /* Field for increment on output */
-       int input_entry;        /* Field for increment on input */
        void (*handler)(struct sk_buff *skb);
        short   error;          /* This ICMP is classed as an error message */
 };
@@ -232,7 +232,7 @@ static const struct icmp_control icmp_pointers[NR_ICMP_TYPES+1];
 static DEFINE_PER_CPU(struct socket *, __icmp_socket) = NULL;
 #define icmp_socket    __get_cpu_var(__icmp_socket)
 
-static __inline__ int icmp_xmit_lock(void)
+static inline int icmp_xmit_lock(void)
 {
        local_bh_disable();
 
@@ -246,7 +246,7 @@ static __inline__ int icmp_xmit_lock(void)
        return 0;
 }
 
-static void icmp_xmit_unlock(void)
+static inline void icmp_xmit_unlock(void)
 {
        spin_unlock_bh(&icmp_socket->sk->sk_lock.slock);
 }
@@ -316,12 +316,10 @@ out:
 /*
  *     Maintain the counters used in the SNMP statistics for outgoing ICMP
  */
-static void icmp_out_count(int type)
+void icmp_out_count(unsigned char type)
 {
-       if (type <= NR_ICMP_TYPES) {
-               ICMP_INC_STATS(icmp_pointers[type].output_entry);
-               ICMP_INC_STATS(ICMP_MIB_OUTMSGS);
-       }
+       ICMPMSGOUT_INC_STATS(type);
+       ICMP_INC_STATS(ICMP_MIB_OUTMSGS);
 }
 
 /*
@@ -390,7 +388,6 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
                return;
 
        icmp_param->data.icmph.checksum = 0;
-       icmp_out_count(icmp_param->data.icmph.type);
 
        inet->tos = ip_hdr(skb)->tos;
        daddr = ipc.addr = rt->rt_src;
@@ -514,9 +511,15 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
 
        saddr = iph->daddr;
        if (!(rt->rt_flags & RTCF_LOCAL)) {
-               if (sysctl_icmp_errors_use_inbound_ifaddr)
-                       saddr = inet_select_addr(skb_in->dev, 0, RT_SCOPE_LINK);
-               else
+               struct net_device *dev = NULL;
+
+               if (rt->fl.iif && sysctl_icmp_errors_use_inbound_ifaddr)
+                       dev = dev_get_by_index(&init_net, rt->fl.iif);
+
+               if (dev) {
+                       saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK);
+                       dev_put(dev);
+               } else
                        saddr = 0;
        }
 
@@ -538,7 +541,6 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        icmp_param.data.icmph.checksum   = 0;
        icmp_param.skb    = skb_in;
        icmp_param.offset = skb_network_offset(skb_in);
-       icmp_out_count(icmp_param.data.icmph.type);
        inet_sk(icmp_socket->sk)->tos = tos;
        ipc.addr = iph->saddr;
        ipc.opt = &icmp_param.replyopts;
@@ -562,11 +564,71 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
                                }
                        }
                };
+               int err;
+               struct rtable *rt2;
+
                security_skb_classify_flow(skb_in, &fl);
-               if (ip_route_output_key(&rt, &fl))
+               if (__ip_route_output_key(&rt, &fl))
+                       goto out_unlock;
+
+               /* No need to clone since we're just using its address. */
+               rt2 = rt;
+
+               err = xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0);
+               switch (err) {
+               case 0:
+                       if (rt != rt2)
+                               goto route_done;
+                       break;
+               case -EPERM:
+                       rt = NULL;
+                       break;
+               default:
+                       goto out_unlock;
+               }
+
+               if (xfrm_decode_session_reverse(skb_in, &fl, AF_INET))
+                       goto out_unlock;
+
+               if (inet_addr_type(fl.fl4_src) == RTN_LOCAL)
+                       err = __ip_route_output_key(&rt2, &fl);
+               else {
+                       struct flowi fl2 = {};
+                       struct dst_entry *odst;
+
+                       fl2.fl4_dst = fl.fl4_src;
+                       if (ip_route_output_key(&rt2, &fl2))
+                               goto out_unlock;
+
+                       /* Ugh! */
+                       odst = skb_in->dst;
+                       err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src,
+                                            RT_TOS(tos), rt2->u.dst.dev);
+
+                       dst_release(&rt2->u.dst);
+                       rt2 = (struct rtable *)skb_in->dst;
+                       skb_in->dst = odst;
+               }
+
+               if (err)
+                       goto out_unlock;
+
+               err = xfrm_lookup((struct dst_entry **)&rt2, &fl, NULL,
+                                 XFRM_LOOKUP_ICMP);
+               if (err == -ENOENT) {
+                       if (!rt)
+                               goto out_unlock;
+                       goto route_done;
+               }
+
+               dst_release(&rt->u.dst);
+               rt = rt2;
+
+               if (err)
                        goto out_unlock;
        }
 
+route_done:
        if (!icmpv4_xrlim_allow(rt, type, code))
                goto ende;
 
@@ -602,7 +664,6 @@ static void icmp_unreach(struct sk_buff *skb)
        struct icmphdr *icmph;
        int hash, protocol;
        struct net_protocol *ipprot;
-       struct sock *raw_sk;
        u32 info = 0;
 
        /*
@@ -696,21 +757,9 @@ static void icmp_unreach(struct sk_buff *skb)
        /*
         *      Deliver ICMP message to raw sockets. Pretty useless feature?
         */
+       raw_icmp_error(skb, protocol, info);
 
-       /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
        hash = protocol & (MAX_INET_PROTOS - 1);
-       read_lock(&raw_v4_lock);
-       if ((raw_sk = sk_head(&raw_v4_htable[hash])) != NULL) {
-               while ((raw_sk = __raw_v4_lookup(raw_sk, protocol, iph->daddr,
-                                                iph->saddr,
-                                                skb->dev->ifindex)) != NULL) {
-                       raw_err(raw_sk, skb, info);
-                       raw_sk = sk_next(raw_sk);
-                       iph = (struct iphdr *)skb->data;
-               }
-       }
-       read_unlock(&raw_v4_lock);
-
        rcu_read_lock();
        ipprot = rcu_dereference(inet_protos[hash]);
        if (ipprot && ipprot->err_handler)
@@ -928,6 +977,25 @@ int icmp_rcv(struct sk_buff *skb)
        struct icmphdr *icmph;
        struct rtable *rt = (struct rtable *)skb->dst;
 
+       if (!xfrm4_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;
+
+               if (!pskb_may_pull(skb, sizeof(*icmph) + sizeof(struct iphdr)))
+                       goto drop;
+
+               nh = skb_network_offset(skb);
+               skb_set_network_header(skb, sizeof(*icmph));
+
+               if (!xfrm4_policy_check_reverse(NULL, XFRM_POLICY_IN, skb))
+                       goto drop;
+
+               skb_set_network_header(skb, nh);
+       }
+
        ICMP_INC_STATS_BH(ICMP_MIB_INMSGS);
 
        switch (skb->ip_summed) {
@@ -941,11 +1009,11 @@ int icmp_rcv(struct sk_buff *skb)
                        goto error;
        }
 
-       if (!pskb_pull(skb, sizeof(struct icmphdr)))
-               goto error;
+       __skb_pull(skb, sizeof(*icmph));
 
        icmph = icmp_hdr(skb);
 
+       ICMPMSGIN_INC_STATS_BH(icmph->type);
        /*
         *      18 is the highest 'known' ICMP type. Anything else is a mystery
         *
@@ -980,7 +1048,6 @@ int icmp_rcv(struct sk_buff *skb)
                }
        }
 
-       ICMP_INC_STATS_BH(icmp_pointers[icmph->type].input_entry);
        icmp_pointers[icmph->type].handler(skb);
 
 drop:
@@ -996,109 +1063,71 @@ error:
  */
 static const struct icmp_control icmp_pointers[NR_ICMP_TYPES + 1] = {
        [ICMP_ECHOREPLY] = {
-               .output_entry = ICMP_MIB_OUTECHOREPS,
-               .input_entry = ICMP_MIB_INECHOREPS,
                .handler = icmp_discard,
        },
        [1] = {
-               .output_entry = ICMP_MIB_DUMMY,
-               .input_entry = ICMP_MIB_INERRORS,
                .handler = icmp_discard,
                .error = 1,
        },
        [2] = {
-               .output_entry = ICMP_MIB_DUMMY,
-               .input_entry = ICMP_MIB_INERRORS,
                .handler = icmp_discard,
                .error = 1,
        },
        [ICMP_DEST_UNREACH] = {
-               .output_entry = ICMP_MIB_OUTDESTUNREACHS,
-               .input_entry = ICMP_MIB_INDESTUNREACHS,
                .handler = icmp_unreach,
                .error = 1,
        },
        [ICMP_SOURCE_QUENCH] = {
-               .output_entry = ICMP_MIB_OUTSRCQUENCHS,
-               .input_entry = ICMP_MIB_INSRCQUENCHS,
                .handler = icmp_unreach,
                .error = 1,
        },
        [ICMP_REDIRECT] = {
-               .output_entry = ICMP_MIB_OUTREDIRECTS,
-               .input_entry = ICMP_MIB_INREDIRECTS,
                .handler = icmp_redirect,
                .error = 1,
        },
        [6] = {
-               .output_entry = ICMP_MIB_DUMMY,
-               .input_entry = ICMP_MIB_INERRORS,
                .handler = icmp_discard,
                .error = 1,
        },
        [7] = {
-               .output_entry = ICMP_MIB_DUMMY,
-               .input_entry = ICMP_MIB_INERRORS,
                .handler = icmp_discard,
                .error = 1,
        },
        [ICMP_ECHO] = {
-               .output_entry = ICMP_MIB_OUTECHOS,
-               .input_entry = ICMP_MIB_INECHOS,
                .handler = icmp_echo,
        },
        [9] = {
-               .output_entry = ICMP_MIB_DUMMY,
-               .input_entry = ICMP_MIB_INERRORS,
                .handler = icmp_discard,
                .error = 1,
        },
        [10] = {
-               .output_entry = ICMP_MIB_DUMMY,
-               .input_entry = ICMP_MIB_INERRORS,
                .handler = icmp_discard,
                .error = 1,
        },
        [ICMP_TIME_EXCEEDED] = {
-               .output_entry = ICMP_MIB_OUTTIMEEXCDS,
-               .input_entry = ICMP_MIB_INTIMEEXCDS,
                .handler = icmp_unreach,
                .error = 1,
        },
        [ICMP_PARAMETERPROB] = {
-               .output_entry = ICMP_MIB_OUTPARMPROBS,
-               .input_entry = ICMP_MIB_INPARMPROBS,
                .handler = icmp_unreach,
                .error = 1,
        },
        [ICMP_TIMESTAMP] = {
-               .output_entry = ICMP_MIB_OUTTIMESTAMPS,
-               .input_entry = ICMP_MIB_INTIMESTAMPS,
                .handler = icmp_timestamp,
        },
        [ICMP_TIMESTAMPREPLY] = {
-               .output_entry = ICMP_MIB_OUTTIMESTAMPREPS,
-               .input_entry = ICMP_MIB_INTIMESTAMPREPS,
                .handler = icmp_discard,
        },
        [ICMP_INFO_REQUEST] = {
-               .output_entry = ICMP_MIB_DUMMY,
-               .input_entry = ICMP_MIB_DUMMY,
                .handler = icmp_discard,
        },
        [ICMP_INFO_REPLY] = {
-               .output_entry = ICMP_MIB_DUMMY,
-               .input_entry = ICMP_MIB_DUMMY,
                .handler = icmp_discard,
        },
        [ICMP_ADDRESS] = {
-               .output_entry = ICMP_MIB_OUTADDRMASKS,
-               .input_entry = ICMP_MIB_INADDRMASKS,
                .handler = icmp_address,
        },
        [ICMP_ADDRESSREPLY] = {
-               .output_entry = ICMP_MIB_OUTADDRMASKREPS,
-               .input_entry = ICMP_MIB_INADDRMASKREPS,
                .handler = icmp_address_reply,
        },
 };