/*
* NET3: Implementation of the ICMP protocol layer.
*
- * Alan Cox, <alan@redhat.com>
+ * Alan Cox, <alan@lxorguk.ukuu.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
unsigned char optbuf[40];
};
-/*
- * 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'. */
return net->ipv4.icmp_sk[smp_processor_id()];
}
-static inline int icmp_xmit_lock(struct sock *sk)
+static inline struct sock *icmp_xmit_lock(struct net *net)
{
+ struct sock *sk;
+
local_bh_disable();
+ sk = icmp_sk(net);
+
if (unlikely(!spin_trylock(&sk->sk_lock.slock))) {
/* This can happen if the output path signals a
* dst_link_failure() for an outgoing ICMP packet.
*/
local_bh_enable();
- return 1;
+ return NULL;
}
- return 0;
+ return sk;
}
static inline void icmp_xmit_unlock(struct sock *sk)
*/
void icmp_out_count(struct net *net, unsigned char type)
{
- ICMPMSGOUT_INC_STATS(type);
- ICMP_INC_STATS(ICMP_MIB_OUTMSGS);
+ ICMPMSGOUT_INC_STATS(net, type);
+ ICMP_INC_STATS(net, ICMP_MIB_OUTMSGS);
}
/*
}
static void icmp_push_reply(struct icmp_bxm *icmp_param,
- struct ipcm_cookie *ipc, struct rtable *rt)
+ struct ipcm_cookie *ipc, struct rtable **rt)
{
struct sock *sk;
struct sk_buff *skb;
- sk = icmp_sk(dev_net(rt->u.dst.dev));
+ sk = icmp_sk(dev_net((*rt)->u.dst.dev));
if (ip_append_data(sk, icmp_glue_bits, icmp_param,
icmp_param->data_len+icmp_param->head_len,
icmp_param->head_len,
static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
{
struct ipcm_cookie ipc;
- struct rtable *rt = skb->rtable;
+ struct rtable *rt = skb_rtable(skb);
struct net *net = dev_net(rt->u.dst.dev);
- struct sock *sk = icmp_sk(net);
- struct inet_sock *inet = inet_sk(sk);
+ struct sock *sk;
+ struct inet_sock *inet;
__be32 daddr;
if (ip_options_echo(&icmp_param->replyopts, skb))
return;
- if (icmp_xmit_lock(sk))
+ sk = icmp_xmit_lock(net);
+ if (sk == NULL)
return;
+ inet = inet_sk(sk);
icmp_param->data.icmph.checksum = 0;
inet->tos = ip_hdr(skb)->tos;
daddr = ipc.addr = rt->rt_src;
ipc.opt = NULL;
+ ipc.shtx.flags = 0;
if (icmp_param->replyopts.optlen) {
ipc.opt = &icmp_param->replyopts;
if (ipc.opt->srr)
}
if (icmpv4_xrlim_allow(net, rt, icmp_param->data.icmph.type,
icmp_param->data.icmph.code))
- icmp_push_reply(icmp_param, &ipc, rt);
+ icmp_push_reply(icmp_param, &ipc, &rt);
ip_rt_put(rt);
out_unlock:
icmp_xmit_unlock(sk);
struct iphdr *iph;
int room;
struct icmp_bxm icmp_param;
- struct rtable *rt = skb_in->rtable;
+ struct rtable *rt = skb_rtable(skb_in);
struct ipcm_cookie ipc;
__be32 saddr;
u8 tos;
if (!rt)
goto out;
net = dev_net(rt->u.dst.dev);
- sk = icmp_sk(net);
/*
* Find the original header. It is expected to be valid, of course.
}
}
- if (icmp_xmit_lock(sk))
+ sk = icmp_xmit_lock(net);
+ if (sk == NULL)
return;
/*
if (!(rt->rt_flags & RTCF_LOCAL)) {
struct net_device *dev = NULL;
+ rcu_read_lock();
if (rt->fl.iif &&
net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr)
- dev = dev_get_by_index(net, rt->fl.iif);
+ dev = dev_get_by_index_rcu(net, rt->fl.iif);
- if (dev) {
+ if (dev)
saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK);
- dev_put(dev);
- } else
+ else
saddr = 0;
+ rcu_read_unlock();
}
tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) |
inet_sk(sk)->tos = tos;
ipc.addr = iph->saddr;
ipc.opt = &icmp_param.replyopts;
+ ipc.shtx.flags = 0;
{
struct flowi fl = {
/* No need to clone since we're just using its address. */
rt2 = rt;
- err = xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0);
+ err = xfrm_lookup(net, (struct dst_entry **)&rt, &fl, NULL, 0);
switch (err) {
case 0:
if (rt != rt2)
goto relookup_failed;
/* Ugh! */
- odst = skb_in->dst;
+ odst = skb_dst(skb_in);
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 = skb_in->rtable;
- skb_in->dst = odst;
+ rt2 = skb_rtable(skb_in);
+ skb_dst_set(skb_in, odst);
}
if (err)
goto relookup_failed;
- err = xfrm_lookup((struct dst_entry **)&rt2, &fl, NULL,
+ err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL,
XFRM_LOOKUP_ICMP);
switch (err) {
case 0:
icmp_param.data_len = room;
icmp_param.head_len = sizeof(struct icmphdr);
- icmp_push_reply(&icmp_param, &ipc, rt);
+ icmp_push_reply(&icmp_param, &ipc, &rt);
ende:
ip_rt_put(rt);
out_unlock:
struct iphdr *iph;
struct icmphdr *icmph;
int hash, protocol;
- struct net_protocol *ipprot;
+ const struct net_protocol *ipprot;
u32 info = 0;
struct net *net;
- net = dev_net(skb->dst->dev);
+ net = dev_net(skb_dst(skb)->dev);
/*
* Incomplete header ?
break;
case ICMP_FRAG_NEEDED:
if (ipv4_config.no_pmtu_disc) {
- LIMIT_NETDEBUG(KERN_INFO "ICMP: " NIPQUAD_FMT ": "
- "fragmentation needed "
- "and DF set.\n",
- NIPQUAD(iph->daddr));
+ LIMIT_NETDEBUG(KERN_INFO "ICMP: %pI4: fragmentation needed and DF set.\n",
+ &iph->daddr);
} else {
info = ip_rt_frag_needed(net, iph,
ntohs(icmph->un.frag.mtu),
}
break;
case ICMP_SR_FAILED:
- LIMIT_NETDEBUG(KERN_INFO "ICMP: " NIPQUAD_FMT ": Source "
- "Route Failed.\n",
- NIPQUAD(iph->daddr));
+ LIMIT_NETDEBUG(KERN_INFO "ICMP: %pI4: Source Route Failed.\n",
+ &iph->daddr);
break;
default:
break;
if (!net->ipv4.sysctl_icmp_ignore_bogus_error_responses &&
inet_addr_type(net, iph->daddr) == RTN_BROADCAST) {
if (net_ratelimit())
- printk(KERN_WARNING NIPQUAD_FMT " sent an invalid ICMP "
+ printk(KERN_WARNING "%pI4 sent an invalid ICMP "
"type %u, code %u "
- "error to a broadcast: " NIPQUAD_FMT " on %s\n",
- NIPQUAD(ip_hdr(skb)->saddr),
+ "error to a broadcast: %pI4 on %s\n",
+ &ip_hdr(skb)->saddr,
icmph->type, icmph->code,
- NIPQUAD(iph->daddr),
+ &iph->daddr,
skb->dev->name);
goto out;
}
out:
return;
out_err:
- ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+ ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
goto out;
}
out:
return;
out_err:
- ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+ ICMP_INC_STATS_BH(dev_net(skb->dev), ICMP_MIB_INERRORS);
goto out;
}
{
struct net *net;
- net = dev_net(skb->dst->dev);
+ net = dev_net(skb_dst(skb)->dev);
if (!net->ipv4.sysctl_icmp_echo_ignore_all) {
struct icmp_bxm icmp_param;
out:
return;
out_err:
- ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+ ICMP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
goto out;
}
static void icmp_address_reply(struct sk_buff *skb)
{
- struct rtable *rt = skb->rtable;
+ struct rtable *rt = skb_rtable(skb);
struct net_device *dev = skb->dev;
struct in_device *in_dev;
struct in_ifaddr *ifa;
break;
}
if (!ifa && net_ratelimit()) {
- printk(KERN_INFO "Wrong address mask " NIPQUAD_FMT " from "
- "%s/" NIPQUAD_FMT "\n",
- NIPQUAD(*mp), dev->name, NIPQUAD(rt->rt_src));
+ printk(KERN_INFO "Wrong address mask %pI4 from %s/%pI4\n",
+ mp, dev->name, &rt->rt_src);
}
}
rcu_read_unlock();
int icmp_rcv(struct sk_buff *skb)
{
struct icmphdr *icmph;
- struct rtable *rt = skb->rtable;
+ struct rtable *rt = skb_rtable(skb);
+ struct net *net = dev_net(rt->u.dst.dev);
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+ struct sec_path *sp = skb_sec_path(skb);
int nh;
- if (!(skb->sp && skb->sp->xvec[skb->sp->len - 1]->props.flags &
+ if (!(sp && sp->xvec[sp->len - 1]->props.flags &
XFRM_STATE_ICMP))
goto drop;
skb_set_network_header(skb, nh);
}
- ICMP_INC_STATS_BH(ICMP_MIB_INMSGS);
+ ICMP_INC_STATS_BH(net, ICMP_MIB_INMSGS);
switch (skb->ip_summed) {
case CHECKSUM_COMPLETE:
icmph = icmp_hdr(skb);
- ICMPMSGIN_INC_STATS_BH(icmph->type);
+ ICMPMSGIN_INC_STATS_BH(net, icmph->type);
/*
* 18 is the highest 'known' ICMP type. Anything else is a mystery
*
*/
if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) {
- struct net *net;
-
- net = dev_net(rt->u.dst.dev);
/*
* RFC 1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be
* silently ignored (we let user decide with a sysctl).
kfree_skb(skb);
return 0;
error:
- ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
+ ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
goto drop;
}
sk->sk_sndbuf =
(2 * ((64 * 1024) + sizeof(struct sk_buff)));
+ /*
+ * Speedup sock_wfree()
+ */
+ sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
inet_sk(sk)->pmtudisc = IP_PMTUDISC_DONT;
}
int __init icmp_init(void)
{
- return register_pernet_device(&icmp_sk_ops);
+ return register_pernet_subsys(&icmp_sk_ops);
}
EXPORT_SYMBOL(icmp_err_convert);
EXPORT_SYMBOL(icmp_send);
-EXPORT_SYMBOL(icmp_statistics);
EXPORT_SYMBOL(xrlim_allow);