Professional Documents
Culture Documents
#include <linux/module.h>
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/string.h>
#include <linux/netfilter_ipv4.h>
#include <linux/slab.h>
#include <net/snmp.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/protocol.h>
#include <net/icmp.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/raw.h>
#include <net/ping.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <net/checksum.h>
#include <net/xfrm.h>
#include <net/inet_common.h>
#include <net/ip_fib.h>
#include <net/l3mdev.h>
/*
*/
struct icmp_bxm {
int offset;
int data_len;
struct {
__be32 times[3];
} data;
int head_len;
};
/* RFC 1122: 3.2.2.1 States that NET_UNREACH, HOST_UNREACH and SR_FAILED MUST be considered
'transient errs'. */
const struct icmp_err icmp_err_convert[] = {
.fatal = 0,
},
.fatal = 0,
},
.fatal = 1,
},
.fatal = 1,
},
.fatal = 0,
},
.fatal = 0,
},
.fatal = 1,
},
{
.fatal = 1,
},
.fatal = 1,
},
.fatal = 1,
},
.fatal = 1,
},
.fatal = 0,
},
.fatal = 0,
},
.fatal = 1,
},
{
.errno = EHOSTUNREACH, /* ICMP_PREC_VIOLATION */
.fatal = 1,
},
.fatal = 1,
},
};
EXPORT_SYMBOL(icmp_err_convert);
/*
*/
struct icmp_control {
};
/*
* The ICMP socket(s). This is the most convenient way to flow control
*/
return *this_cpu_ptr(net->ipv4.icmp_sk);
sk = icmp_sk(net);
if (unlikely(!spin_trylock(&sk->sk_lock.slock))) {
*/
return NULL;
return sk;
spin_unlock(&sk->sk_lock.slock);
static struct {
spinlock_t lock;
u32 credit;
u32 stamp;
} icmp_global = {
.lock = __SPIN_LOCK_UNLOCKED(icmp_global.lock),
};
/**
* Returns false if we reached the limit and can not send another packet.
*/
bool icmp_global_allow(void)
bool rc = false;
*/
if (!icmp_global.credit) {
return false;
spin_lock(&icmp_global.lock);
delta = min_t(u32, now - icmp_global.stamp, HZ);
if (incr)
icmp_global.stamp = now;
if (credit) {
credit--;
rc = true;
icmp_global.credit = credit;
spin_unlock(&icmp_global.lock);
return rc;
EXPORT_SYMBOL(icmp_global_allow);
return true;
return true;
return true;
return false;
return true;
if (icmp_global_allow())
return true;
return false;
/*
*/
bool rc = true;
int vif;
goto out;
/* No rate limit on loopback */
goto out;
vif = l3mdev_master_ifindex(dst->dev);
rc = inet_peer_xrlim_allow(peer, net->ipv4.sysctl_icmp_ratelimit);
if (peer)
inet_putpeer(peer);
out:
return rc;
/*
* Maintain the counters used in the SNMP statistics for outgoing ICMP
*/
ICMPMSGOUT_INC_STATS(net, type);
ICMP_INC_STATS(net, ICMP_MIB_OUTMSGS);
/*
* Checksum each fragment, and on the first include the headers and final
* checksum.
*/
static int icmp_glue_bits(void *from, char *to, int offset, int len, int odd,
__wsum csum;
csum = skb_copy_and_csum_bits(icmp_param->skb,
icmp_param->offset + offset,
if (icmp_pointers[icmp_param->data.icmph.type].error)
nf_ct_attach(skb, icmp_param->skb);
return 0;
sk = icmp_sk(dev_net((*rt)->dst.dev));
icmp_param->data_len+icmp_param->head_len,
icmp_param->head_len,
__ICMP_INC_STATS(sock_net(sk), ICMP_MIB_OUTERRORS);
ip_flush_pending_frames(sk);
__wsum csum = 0;
skb_queue_walk(&sk->sk_write_queue, skb1) {
(char *)icmph,
icmp_param->head_len, csum);
icmph->checksum = csum_fold(csum);
skb->ip_summed = CHECKSUM_NONE;
ip_push_pending_frames(sk, fl4);
/*
*/
return;
local_bh_disable();
/* global icmp_msgs_per_sec */
goto out_bh_enable;
sk = icmp_xmit_lock(net);
if (!sk)
goto out_bh_enable;
inet = inet_sk(sk);
icmp_param->data.icmph.checksum = 0;
ipcm_init(&ipc);
inet->tos = ip_hdr(skb)->tos;
sk->sk_mark = mark;
saddr = fib_compute_spec_dst(skb);
if (icmp_param->replyopts.opt.opt.optlen) {
ipc.opt = &icmp_param->replyopts.opt;
if (ipc.opt->opt.srr)
daddr = icmp_param->replyopts.opt.opt.faddr;
memset(&fl4, 0, sizeof(fl4));
fl4.daddr = daddr;
fl4.saddr = saddr;
fl4.flowi4_mark = mark;
fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
fl4.flowi4_proto = IPPROTO_ICMP;
fl4.flowi4_oif = l3mdev_master_ifindex(skb->dev);
security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
rt = ip_route_output_key(net, &fl4);
if (IS_ERR(rt))
goto out_unlock;
ip_rt_put(rt);
out_unlock:
icmp_xmit_unlock(sk);
out_bh_enable:
local_bh_enable();
int err;
memset(fl4, 0, sizeof(*fl4));
fl4->daddr = (param->replyopts.opt.opt.srr ?
param->replyopts.opt.opt.faddr : iph->saddr);
fl4->saddr = saddr;
fl4->flowi4_mark = mark;
fl4->flowi4_tos = RT_TOS(tos);
fl4->flowi4_proto = IPPROTO_ICMP;
fl4->fl4_icmp_type = type;
fl4->fl4_icmp_code = code;
fl4->flowi4_oif = l3mdev_master_ifindex(skb_dst(skb_in)->dev);
security_skb_classify_flow(skb_in, flowi4_to_flowi(fl4));
if (IS_ERR(rt))
return rt;
rt2 = rt;
if (rt != rt2)
return rt;
rt = NULL;
} else
return rt;
if (err)
goto relookup_failed;
if (inet_addr_type_dev_table(net, skb_dst(skb_in)->dev,
fl4_dec.saddr) == RTN_LOCAL) {
if (IS_ERR(rt2))
err = PTR_ERR(rt2);
} else {
fl4_2.daddr = fl4_dec.saddr;
if (IS_ERR(rt2)) {
err = PTR_ERR(rt2);
goto relookup_failed;
/* Ugh! */
RT_TOS(tos), rt2->dst.dev);
dst_release(&rt2->dst);
rt2 = skb_rtable(skb_in);
if (err)
goto relookup_failed;
flowi4_to_flowi(&fl4_dec), NULL,
XFRM_LOOKUP_ICMP);
if (!IS_ERR(rt2)) {
dst_release(&rt->dst);
rt = rt2;
if (rt)
dst_release(&rt->dst);
return rt2;
} else {
err = PTR_ERR(rt2);
goto relookup_failed;
return rt;
relookup_failed:
if (rt)
return rt;
return ERR_PTR(err);
/*
* RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header.
*/
void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
int room;
__be32 saddr;
u8 tos;
u32 mark;
struct net *net;
if (!rt)
goto out;
net = dev_net(rt->dst.dev);
/*
* sometimes.
*/
iph = ip_hdr(skb_in);
skb_tail_pointer(skb_in))
goto out;
/*
*/
if (skb_in->pkt_type != PACKET_HOST)
goto out;
/*
*/
/*
*/
goto out;
/*
*/
if (icmp_pointers[type].error) {
/*
* ICMP error
*/
if (iph->protocol == IPPROTO_ICMP) {
u8 _inner_type, *itp;
itp = skb_header_pointer(skb_in,
skb_network_header(skb_in) +
(iph->ihl << 2) +
offsetof(struct icmphdr,
type) -
skb_in->data,
sizeof(_inner_type),
&_inner_type);
if (!itp)
goto out;
/*
*/
icmp_pointers[*itp].error)
goto out;
local_bh_disable();
*/
goto out_bh_enable;
sk = icmp_xmit_lock(net);
if (!sk)
goto out_bh_enable;
/*
saddr = iph->daddr;
rcu_read_lock();
if (rt_is_input_route(rt) &&
net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr)
if (dev)
else
saddr = 0;
rcu_read_unlock();
IPTOS_PREC_INTERNETCONTROL) :
iph->tos;
goto out_unlock;
/*
icmp_param.data.icmph.type = type;
icmp_param.data.icmph.code = code;
icmp_param.data.icmph.un.gateway = info;
icmp_param.data.icmph.checksum = 0;
icmp_param.skb = skb_in;
icmp_param.offset = skb_network_offset(skb_in);
inet_sk(sk)->tos = tos;
sk->sk_mark = mark;
ipcm_init(&ipc);
ipc.addr = iph->saddr;
ipc.opt = &icmp_param.replyopts.opt;
if (IS_ERR(rt))
goto out_unlock;
/* peer icmp_ratelimit */
goto ende;
room = dst_mtu(&rt->dst);
room = 576;
icmp_param.data_len = room;
ende:
ip_rt_put(rt);
out_unlock:
icmp_xmit_unlock(sk);
out_bh_enable:
local_bh_enable();
out:;
EXPORT_SYMBOL(__icmp_send);
*/
return;
ipprot = rcu_dereference(inet_protos[protocol]);
ipprot->err_handler(skb, info);
bool ok;
rcu_read_lock();
ok = rcu_dereference(inet_protos[proto])->icmp_strict_tag_validation;
rcu_read_unlock();
return ok;
/*
* ICMP_PARAMETERPROB.
*/
u32 info = 0;
net = dev_net(skb_dst(skb)->dev);
/*
* Incomplete header ?
*/
goto out_err;
icmph = icmp_hdr(skb);
goto out_err;
switch (icmph->type) {
case ICMP_DEST_UNREACH:
case ICMP_NET_UNREACH:
case ICMP_HOST_UNREACH:
case ICMP_PROT_UNREACH:
case ICMP_PORT_UNREACH:
break;
case ICMP_FRAG_NEEDED:
* Documentation/networking/ip-sysctl.txt
*/
switch (net->ipv4.sysctl_ip_no_pmtu_disc) {
default:
&iph->daddr);
break;
case 2:
goto out;
case 3:
if (!icmp_tag_validation(iph->protocol))
goto out;
/* fall through */
case 0:
info = ntohs(icmph->un.frag.mtu);
break;
case ICMP_SR_FAILED:
&iph->daddr);
break;
default:
break;
goto out;
break;
case ICMP_PARAMETERPROB:
break;
case ICMP_TIME_EXCEEDED:
__ICMP_INC_STATS(net, ICMP_MIB_INTIMEEXCDS);
if (icmph->code == ICMP_EXC_FRAGTIME)
goto out;
break;
/*
* RFC 1122: 3.2.2 MUST extract the protocol ID from the passed
* header.
* transport layer.
* transport layer.
*/
/*
* Check the other end isn't violating RFC 1122. Some routers send
*/
if (!net->ipv4.sysctl_icmp_ignore_bogus_error_responses &&
&ip_hdr(skb)->saddr,
icmph->type, icmph->code,
&iph->daddr, skb->dev->name);
goto out;
icmp_socket_deliver(skb, info);
out:
return true;
out_err:
__ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
return false;
/*
* Handle ICMP_REDIRECT.
*/
__ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS);
return false;
}
return false;
icmp_socket_deliver(skb, icmp_hdr(skb)->un.gateway);
return true;
/*
* RFC 1122: 3.2.2.6 MUST have an echo server that answers ICMP echo
* requests.
* RFC 1812: 4.3.3.6 SHOULD have a config option for silently ignoring
* See also WRT handling of options once they are done and working.
*/
net = dev_net(skb_dst(skb)->dev);
if (!net->ipv4.sysctl_icmp_echo_ignore_all) {
struct icmp_bxm icmp_param;
icmp_param.data.icmph = *icmp_hdr(skb);
icmp_param.data.icmph.type = ICMP_ECHOREPLY;
icmp_param.skb = skb;
icmp_param.offset = 0;
icmp_param.data_len = skb->len;
icmp_reply(&icmp_param, skb);
return true;
/*
*/
/*
* Too short.
*/
if (skb->len < 4)
goto out_err;
/*
*/
icmp_param.data.times[1] = inet_current_timestamp();
icmp_param.data.times[2] = icmp_param.data.times[1];
icmp_param.data.icmph = *icmp_hdr(skb);
icmp_param.data.icmph.type = ICMP_TIMESTAMPREPLY;
icmp_param.data.icmph.code = 0;
icmp_param.skb = skb;
icmp_param.offset = 0;
icmp_param.data_len = 0;
icmp_reply(&icmp_param, skb);
return true;
out_err:
__ICMP_INC_STATS(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
return false;
return true;
}
/*
*/
bool success;
int nh;
XFRM_STATE_ICMP))
goto drop;
goto drop;
nh = skb_network_offset(skb);
skb_set_network_header(skb, sizeof(*icmph));
goto drop;
skb_set_network_header(skb, nh);
}
__ICMP_INC_STATS(net, ICMP_MIB_INMSGS);
if (skb_checksum_simple_validate(skb))
goto csum_error;
if (!pskb_pull(skb, sizeof(*icmph)))
goto error;
icmph = icmp_hdr(skb);
ICMPMSGIN_INC_STATS(net, icmph->type);
/*
* discarded.
*/
goto error;
/*
*/
/*
* RFC 1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be
* discarded if to broadcast/multicast.
*/
if ((icmph->type == ICMP_ECHO ||
net->ipv4.sysctl_icmp_echo_ignore_broadcasts) {
goto error;
icmph->type != ICMP_ADDRESSREPLY) {
goto error;
success = icmp_pointers[icmph->type].handler(skb);
if (success) {
consume_skb(skb);
return NET_RX_SUCCESS;
drop:
kfree_skb(skb);
return NET_RX_DROP;
csum_error:
__ICMP_INC_STATS(net, ICMP_MIB_CSUMERRORS);
error:
__ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
goto drop;
/*
*/
if (icmph->type != ICMP_ECHOREPLY) {
return 0;
/*
*/
[ICMP_ECHOREPLY] = {
.handler = ping_rcv,
},
[1] = {
.handler = icmp_discard,
.error = 1,
},
[2] = {
.handler = icmp_discard,
.error = 1,
},
[ICMP_DEST_UNREACH] = {
.handler = icmp_unreach,
.error = 1,
},
[ICMP_SOURCE_QUENCH] = {
.handler = icmp_unreach,
.error = 1,
},
[ICMP_REDIRECT] = {
.handler = icmp_redirect,
.error = 1,
},
[6] = {
.handler = icmp_discard,
.error = 1,
},
[7] = {
.handler = icmp_discard,
.error = 1,
},
[ICMP_ECHO] = {
.handler = icmp_echo,
},
[9] = {
.handler = icmp_discard,
.error = 1,
},
[10] = {
.handler = icmp_discard,
.error = 1,
},
[ICMP_TIME_EXCEEDED] = {
.handler = icmp_unreach,
.error = 1,
},
[ICMP_PARAMETERPROB] = {
.handler = icmp_unreach,
.error = 1,
},
[ICMP_TIMESTAMP] = {
.handler = icmp_timestamp,
},
[ICMP_TIMESTAMPREPLY] = {
.handler = icmp_discard,
},
[ICMP_INFO_REQUEST] = {
.handler = icmp_discard,
},
[ICMP_INFO_REPLY] = {
.handler = icmp_discard,
},
[ICMP_ADDRESS] = {
.handler = icmp_discard,
},
[ICMP_ADDRESSREPLY] = {
.handler = icmp_discard,
},
};
int i;
for_each_possible_cpu(i)
inet_ctl_sock_destroy(*per_cpu_ptr(net->ipv4.icmp_sk, i));
free_percpu(net->ipv4.icmp_sk);
net->ipv4.icmp_sk = NULL;
}
static int __net_init icmp_sk_init(struct net *net)
int i, err;
if (!net->ipv4.icmp_sk)
return -ENOMEM;
for_each_possible_cpu(i) {
if (err < 0)
goto fail;
*per_cpu_ptr(net->ipv4.icmp_sk, i) = sk;
*/
/*
* Speedup sock_wfree()
*/
sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
inet_sk(sk)->pmtudisc = IP_PMTUDISC_DONT;
}
/* Control parameters for ECHO replies. */
net->ipv4.sysctl_icmp_echo_ignore_all = 0;
net->ipv4.sysctl_icmp_echo_ignore_broadcasts = 1;
net->ipv4.sysctl_icmp_ignore_bogus_error_responses = 1;
/*
* default:
*/
net->ipv4.sysctl_icmp_ratelimit = 1 * HZ;
net->ipv4.sysctl_icmp_ratemask = 0x1818;
net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr = 0;
return 0;
fail:
icmp_sk_exit(net);
return err;
}
.init = icmp_sk_init,
.exit = icmp_sk_exit,
};
return register_pernet_subsys(&icmp_sk_ops);