在剖析icmp数据包处置工序预先阻止,我有以下几个问题:
1、为什么只为每个CPU创办一点钟套接字来发送ICMP音讯?,以防可以在不运用套接字的形势下发送ICMP音讯?
2、平的道德标准是什么?
3、TraceouTE的规律是什么?
一、IMCP协定的设定初值
1)的ICMP收执处置有或起作用设定初值
我们的了解ICMP协定是一点钟附加到IP层的3层协定。,3层协定是在IP数据做切片中回忆ICMP数据的协定。。和TCP、UDP亦TCP、UDP数据的4层协定回忆在IP数据包的数据做切片。。
怨恨ICMP和TCP等协定不属于相同的广泛分布LA。,但这都是在3层IP协定随后。,这将给ICMP、TCP的处置效能。因而在Linux,都是召集inet_add_protocol将其收执处置有或起作用中间定位的数据作曲添加到装饰inet_protos中去的(在起作用的三、四层收执在起作用的数据处置完全符合的知。
的收执处置有或起作用ICMP的作曲的限界如次:
static const struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
.no_policy = 1,
.netns_ok = 1,
};
当inet_init设定初值,更确切地说,iNETAdAdx协定称为TCP协定。、udp、icmp、与IGMP和静止PR中间定位的收执处置作曲的对齐,并保在装饰中。
当收执到的数据包的协定是ICMP时,更确切地说,将召集ICMPYRCV举行后续处置。。
2) ICMP协定模块的设定初值
首要是召集有或起作用register_pernet_subsys(在起作用的该有或起作用的任务工序,请看。,将ICMP协定模块完全符合到广泛分布命令空白的,叫OPS -> init设定初值与协定中间定位的密码。。
向ICMP,其pernet_operations的限界如次:
static structpernet_operations __net_initdata icmp_sk_ops = {
.init = icmp_sk_init,
.exit = icmp_sk_exit,
};
在召集register_pernet_subsys将icmp协定模块完全符合到广泛分布命名空白的后,它将召集ICMPTHSKIN设定初值ICMP协定。,我们的剖析了ICMPSkySKIT。
该有或起作用首要造成以下效能:
/*
1、 为每一点钟cpu创办一点钟用于产生icmp数据包的socket
2、 设置必然的限度局限,包孕速率约束、收执数据包必须先具备的等。
*/
static int __net_init icmp_sk_init(作曲) net 广泛分布)
{
int i, err;
依从的ICMPSK的空白的,主宰与CPU中间定位的套接字影响都回忆在ICMPSK SK装饰中。
net-> =
kzalloc(nr_cpu_ids *sizeof(作曲) sock *), GFP_KERNEL);
if (网) 空)
return -ENOMEM;
为每个CPU创办一点钟原始套接字
for_each_possible_cpu(i) {
struct sock *sk;
/*
创办一点钟 RAW 插座典型,召集(*SK)-> SKYPROT–>散列,将该socket从hash链表[RAW_HTABLE_SIZE]中剪下与该socket的关系
*/
err =inet_ctl_sock_create(&sk, PF_INET,
SOCK_RAW, IPPROTO_ICMP, 广泛分布)
if (背面的) < 0)
goto fail;
net->[i]= sk;
/* Enough space FR2 64K ICMP packets, including
* sk_buff struct 经费。
*/
sk->sk_sndbuf =
(2 * ((64 *1024) + sizeof(作曲) sk_buff)));
/*
* Speedup sock_wfree()
*/
sock_set_flag(SK),SOCK_USE_WRITE_QUEUE);
inet_sk(SK))->pmtudisc= IP_PMTUDISC_DONT;
}
net->= 0;
反照播送的索取 */
net->= 1;
/* 疏忽播送ICMP 背面的的恢复物
net->ipv4.sysctl_icmp_ignore_bogus_error_responses= 1;
net->= 1 * HZ; 限速值
/*举行速率限度局限的icmp数据包典型,最首要的是 unreachable 、source quench time exceeded 、parameter problem*/
net->= 0x1818;
net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr= 0;
return 0;
fail:
for_each_possible_cpu(i)
inet_ctl_sock_destroy(网)[i]);
kfree(网));
return err;
}
怀疑:当新创办的套接字,为什么要将其从hash链表[RAW_HTABLE_SIZE]中剪下呢?
因我们的只运用因此套接字发送数据包。,用不着运用因此套接字收执数据包。。因而把它从哈希列表中剪下。。
为什么不立即的运用套接字收执ICMP音讯呢?,我的默认是,以防您运用套接字收执音讯,你需求在内核创办一点钟内核穿成串,用于侦听以防有数据抵达套接字。,此后改装一遍。
并立即的运用内核四层协定举行完全符合,处置收执到的ICMP音讯恰好是出恭。,和更少的内核资源的使用,因而为内核创办了套接字,收执处理或负责大致是采取四内核层造成。涂层创办的与ICMP中间定位的套接字将不家具。
二、ICMP协定的收执处置效能
ICMP收执处置效能是ICMPY-RCV,因此有或起作用的以下剖析。
首要效能:
1、 反省数据包的有理性
2、 如ICMP典型,
int icmp_rcv(作曲) sk_buff SKB)
{
structicmphdr *icmph;
structrtable *rt = skb_rtable(SKB)
structnet *net = dev_net(RT);
/*
鉴于高扩展性的广泛分布安全体系作曲的保险单,胸部核作曲浊度
在这里心不在焉剖析,成穹状弯曲。
*/
if(!xfrm4_policy_check(NULL, XFRM_POLICY_IN, SKB) {
structsec_path *sp = skb_sec_path(SKB)
intnh;
if(!(SP) && sp->xvec[sp->len – 1]->props.flags &
XFRM_STATE_ICMP))
gotodrop;
if(!pskb_may_pull(SK)b, sizeof(*icmph) + sizeof(作曲) iphdr)))
gotodrop;
nh= skb_network_offset(SKB)
skb_set_network_header(SK)b,sizeof(*icmph));
if(!xfrm4_policy_check_reverse(NULL, XFRM_POLICY_IN, SKB)
gotodrop;
skb_set_network_header(SK)b,NH)
}
ICMP_INC_STATS_BH(net,ICMP_MIB_INMSGS);
核实和物
switch(SK)b->ip_summed) {
caseCHECKSUM_COMPLETE:
if(!csum_fold(SK)b->csum))
break;
/*fall through */
caseCHECKSUM_NONE:
skb->csum= 0;
if(__skb_checksum_complete(SKB)
gotoerror;
}
if(!pskb_pull(SK)b, sizeof(*icmph)))
gotoerror;
收到ICMP头
icmph= icmp_hdr(SKB)
ICMPMSGIN_INC_STATS_BH(net,icmph->type);
/*
向不供养的ICMP音讯,立即的丢掉
*/
if(icmph->type > NR_ICMP_TYPES)
gotoerror;
/*
断定以防打出的牌掉多播典型的icmp数据包
1、只处置回响、timestamp、address_mask_request、address_mask_reply典型的多播icmp数据包
*/
if(RTrt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) {
/*
* RFC1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be
* silently ignored (我们的 let user decide with asysctl).
* RFC1122: 3.2.2.8 An ICMP_TIMESTAMP MAY be silently
* discarded if to broadcast/multicast.
*/
if((icmph->type == ICMP_ECHO ||
icmph->type == ICMP_TIMESTAMP)&&
net->) {
gotoerror;
}
if(icmph->type != ICMP_ECHO &&
icmph->type != ICMP_TIMESTAMP &&
icmph->type != ICMP_ADDRESS &&
icmph->type != ICMP_ADDRESSREPLY) {
gotoerror;
}
}
/*如icmp数据包典型,召集有重大意义的的处置有或起作用
icmp_pointers[icmph->type].handler(SKB)
drop:
kfree_skb(SKB)
return0;
error:
ICMP_INC_STATS_BH(net,ICMP_MIB_INERRORS);
gotodrop;
}
向ICMP_pointers的限界如次:
/*
* This table is the definition of how wehandle ICMP。
*/
static const struct icmp_controlicmp_pointers[NR_ICMP_TYPES + 1] = {
[ICMP_ECHOREPLY]= {
.handler= icmp_discard,
},
[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_address,
},
[ICMP_ADDRESSREPLY]= {
.handler= icmp_address_reply,
},
};
流行的由内核处置的ICMP音讯具有ICMP-UnReA。、icmp_address、icmp_address_reply、icmp_timestamp、icmp_echo、icmp_redirect。
icmp_echo
/*
该效能首要是将ICMP的典型设置为ICMP-ECHECURPLY。,叫icmp_reply发送的包了
*/
static void icmp_echo(作曲) sk_buff SKB)
{
structnet *net;
net= dev_net(SK)b_dst(SK)b)->dev);
if(!net->) {
structicmp_bxm icmp_param;
=*icmp_hdr(SKB)
.type= ICMP_ECHOREPLY;
= skb;
= 0;
=skb->len;
=sizeof(作曲) icmphdr);
icmp_reply(&icmp_param,SKB)
}
}
Timestamp
/*
设置工夫戳值,并将ICMP的典型设置为ICMPTyTimePracpPyPress,经过ICMPL应对发送
*/
static void icmp_timestamp(作曲) sk_buffSKB)
{
structtimespec tv;
structicmp_bxm icmp_param;
/*
* Tooshort.
*/
以防(SK)b->len < 4)
gotoout_err;
/*
* 弗林 the current time as ms since midnight UT:
*/
getnstimeofday(&tv);
[1]= htonl((tv.tv_sec % 86400) * MSEC_PER_SEC +
tv.tv_nsec / NSEC_PER_MSEC);
[2]= [1];
(SKB*Copype位(SKB), 0, &[0], 4))
BUG();
=*icmp_hdr(SKB)
.type= ICMP_TIMESTAMPREPLY;
.code= 0;
= skb;
= 0;
=0;
=sizeof(作曲) icmphdr) + 12;
icmp_reply(&icmp_param,SKB)
out:
return;
out_err:
ICMP_INC_STATS_BH(dev_net(SK)b_dst(SK)b)->dev),ICMP_MIB_INERRORS);
gotoout;
}
Unreach 数据处置
效能:如ICMP无效装载数据的胜任,召集播送层的背面的处置有或起作用来处置
static void icmp_unreach(作曲) sk_buffSKB)
{
structiphdr *iph;
structicmphdr *icmph;
inthash, protocol;
conststruct net_protocol *ipprot;
u32info = 0;
structnet *net;
net= dev_net(SK)b_dst(SK)b)->dev);
/*
* Incompleteheader ?
* Onlychecks for the IP header, there should be an
* additionalcheck for longer headers in upper 程度。
*/
if(!pskb_may_pull(SK)b, sizeof(作曲) iphdr)))
gotoout_err;
率先如愿以偿ICMP
icmph= icmp_hdr(SKB)
iph = (作曲) iphdr *)skb->data;
断定IP一号做切片完整的
if(iph->ihl < 5) /* Mangled header, drop. */
gotoout_err;
只处置典型为3或12的数据包
1、当典型为3时,就是密码是FRAG 需求的物
a)当零碎不供养PMTU,报废要旨
B)当零碎供养PMTU,召集IPHRTHFrack需求修正PMTU的值
2、当典型12,经过ICMPH如愿以偿背面的偏移值(相向字组分类)。
*/
if(icmph->type == ICMP_DEST_UNREACH) {
switch(icmph->code & 15) {
caseICMP_NET_UNREACH:
caseICMP_HOST_UNREACH:
caseICMP_PROT_UNREACH:
caseICMP_PORT_UNREACH:
break;
caseICMP_FRAG_NEEDED:
if() {
LIMIT_NETDEBUG(KERN_INFO”ICMP: %pI4: fragmentation needed and DF set.\n”,
&iph->daddr);
}else {
info= ip_rt_frag_needed(net, iph,
ntohs(icmph->),
skb->dev);
if(!物)
gotoout;
}
break;
caseICMP_SR_FAILED:
LIMIT_NETDEBUG(KERN_INFO”ICMP: %pI4: Source Route Failed.\n”,
&iph->daddr);
break;
default:
break;
}
if(icmph->code > NR_ICMP_UNREACH)
gotoout;
}else if (icmph->type == ICMP_PARAMETERPROB)
info= ntohl(icmph->) >> 24;
/*
* Throwit at our lower layers
*
* RFC1122: 3.2.2 MUST extract the protocol ID from the passed
* 头。
* RFC1122: 3.2.2.1 MUST pass ICMP unreach messages to the
* transport 层。
* RFC1122: 3.2.2.2 MUST pass ICMP time expired messages to
* transport 层。
*/
/*
* 反省 other end isnt violating RFC 1122. Some routers send
* bogusresponses to broadcast 帧。 If you see this message
* firstcheck your netmask matches at both ends, if it does then
* 如愿以偿 other vendor to fix their kit.
*/
/*
向挥向地址是播送的icmp数据包,当你需求疏忽它,标记背面的
疏忽数据包
*/
if(!net->ipv4.sysctl_icmp_ignore_bogus_error_responses &&
inet_addr_type(net, iph->daddr) ==RTN_BROADCAST) {
if(net_ratelimit())
printk(KERN_WARNING”%pI4 sent an invalid ICMP “
“type %u, code %u “
“error to a broadcast: %pI4 on%s\n”,
&ip_hdr(SK)b)->saddr,
icmph->type, icmph->code,
&iph->daddr,
skb->dev->name);
gotoout;
}
/*
检测icmp要旨中无效装载做切片内容音长以防大于胜任ip头部物扩大8八位位组
发送ICMP背面的音讯时,ICMP数据做切片的值被设置为IP姓名牌物。 IP无效装载的前8个八位位组,
可以断定TrSPO的涂数据播送背面的。
*/
if(!pskb_may_pull(SK)b, iph->ihl * 4 + 8))
gotoout;
/*
此刻IPH,是ICMP无效装载说得中肯IP姓名牌物,在icmp_rcv,SKB ->数据已对准ICMP音讯。
无效装载做切片。
*/
iph= (作曲) iphdr *)skb->data;
获取播送层协定值
protocol= iph->protocol;
/*
一号次召集RAWYICMP背面的,将背面的音讯发送到趣味源 sockte
*/
raw_icmp_error(SK)b,protocol, 物);
如协定值,找到一点钟合格的四级收执处置哈希装饰,
召集其背面的处置有或起作用举行后续处置
hash= protocol & (MAX_INET_PROTOS – 1);
rcu_read_lock();
ipprot= rcu_dereference(inet_protos[hash]);
if(ipprot && ipprot->err_handler)
ipprot->err_handler(SK)b,物);
rcu_read_unlock();
out:
return;
out_err:
ICMP_INC_STATS_BH(net,ICMP_MIB_INERRORS);
gotoout;
}
重定位处置:
效能:如ICMP中数据做切片的值,召集ip_rt_redirect,后续快速地流动(不熟悉路由重定位密码),对路由分叉的下一步剖析举行了朝外的剖析。。
static void icmp_redirect(作曲) sk_buffSKB)
{
structiphdr *iph;
包使合法化
以防(SK)b->len < sizeof(作曲) iphdr))
gotoout_err;
if(!pskb_may_pull(SK)b, sizeof(作曲) iphdr)))
gotoout;
获取IP姓名牌物经过ICMP数据做切片举行
iph= (作曲) iphdr *)skb->data;
密码是 、ICMP_REDIR_NETTOS 、ICMP_REDIR_HOST 、ICMP_REDIR_HOSTTOS ,召集ip_rt_redirect 路由重定位处置
结束电流(ICMPH-HDR(SKB)->密码 & 7) {
caseICMP_REDIR_NET:
caseICMP_REDIR_NETTOS:
/*
* As per RFC recommendations now handle it asa host 重定位。
*/
caseICMP_REDIR_HOST:
caseICMP_REDIR_HOSTTOS:
ip_rt_redirect(ip_hdr(SK)b)->saddr,iph->daddr,
icmp_hdr(SK)b)->,
iph->saddr, skb->dev);
break;
}
out:
return;
out_err:
ICMP_INC_STATS_BH(dev_net(SK)b->dev),ICMP_MIB_INERRORS);
gotoout;
}
Icmp_reply
在前面绍介ICMP 发射应答ICMP 工夫戳的应答工夫,这些有或起作用都由ICMPX应对召集来发送数据。,在这里是因此有或起作用的剖析
效能:
1、查找路由,查找终成泡影,立即的来回;显示证据二踩成
2、枯萎:使枯萎限度局限的ICPv4xxRimLim容许呼叫速率限度局限效能,容许发送时,使生效第三个踩,别的来回
3、召集ICMPthPuxy回答发送数据
/*
* Driving logic for building and sending ICMPmessages.
*/
static void icmp_reply(作曲) icmp_bxm*icmp_param, struct sk_buff SKB)
{
structipcm_cookie ipc;
structrtable *rt = skb_rtable(SKB)
structnet *net = dev_net(RT);
structsock *sk;
structinet_sock *inet;
__be32daddr;
if(ip_options_echo(&icmp_param->replyopts, SKB)
return;
sk= icmp_xmit_lock(广泛分布)
以防(SK) == 空)
return;
inet= inet_sk(SK));
icmp_param->data.icmph.checksum= 0;
inet->tos= IPH-HDR(SKB)-TOS
daddr= ipc.addr = rt->rt_src;
ipc.opt= NULL;
ipc.shtx.flags= 0;
if(icmp_param->) {
ipc.opt= &icmp_param->replyopts;
if()
daddr= icmp_param->
}
{
structflowi fl = { .nl_u = { .ip4_u =
{ .daddr = daddr,
.saddr= rt->rt_spec_dst,
.tos= RT_TOS(ip_hdr(SK)b)->tos) } },
.proto = IPPROTO_ICMP };
security_skb_classify_flow(SK)b,FL)
以防(Ipou-RouthyOutUpPosikKEY(net), &rt, FL)
gotoout_unlock;
}
以防(ICMPv4xxRimLim容许(net), rt, icmp_param->,
icmp_param->))
icmp_push_reply(icmp_param,&ipc, RT)
ip_rt_put(RT);
out_unlock:
icmp_xmit_unlock(SK));
}
a)速率把持效能ICMPv4xxRimLim容许
速率把持效能,首要是由两个有或起作用完整的icmpv4_xrlim_allow、xrlim_allow。
xrlim_allow是一点钟真正的限速运转;ICMPv4XXRIMLIAL是来回成的首要器,它是为了用不着枯萎:使枯萎限度局限的数据。,更确切地说,来回容许经过
效能:决定以防有工夫片用于数据播送,因此有或起作用的工序构成复杂。。
int xrlim_allow(作曲) dst_entry *dst, int超出的时间)
{
unsignedlong now, token = dst->rate_tokens;
intrc = 0;
now= jiffies;
token+= now – dst->rate_last;
dst->rate_last= now;
以防(预兆) > XRLIM_BURST_FACTOR * 超出的时间)
token= XRLIM_BURST_FACTOR * timeout;
以防(预兆) >= 超出的时间) {
token-= timeout;
rc= 1;
}
dst->rate_tokens= token;
returnrc;
}
效能:决定数据以防被容许发送的数据
1、 向不供养的ICMP type典型,容许发送来回
2、 典型典型为ICMP-TestBuffunDelphi 密码是一点钟包icmp_frag_needed,容许发送
3、 因目的配件的巡回立基于,容许发送来回
4、 向静止典型的ICMP音讯,有重大意义的的包就是1将被限度局限。,向静止典型的数据包,立即的容许发送来回(即不限速)
static inline int icmpv4_xrlim_allow(作曲)net *net, struct rtable *rt,
inttype, int 密码)
{
structdst_entry *dst = &rt->
intrc = 1;
以防(典型) > NR_ICMP_TYPES)
gotoout;
/*Don”t limit PMTU 显示证据。 */
以防(典型) == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
gotoout;
/*No rate limit on loopback */
if(dst->dev && (dst->dev->flags&IFF_LOOPBACK))
gotoout;
/*Limit if icmp type is enabled in ratemask. */
以防(1) << type) & net->)
rc= xrlim_allow(dst, net->);
out:
returnrc;
}
b)数据播送
效能:
1、 召集ip_append_data,缓存数据
2、 召集IPH-FuluSupIdgIn帧立即的发送数据。
static void icmp_push_reply(作曲) icmp_bxm*icmp_param,
struct ipcm_cookie *ipc, struct rtable**RT)
{
structsock *sk;
structsk_buff *skb;
推进插座
sk= icmp_sk(dev_net((*RT)->));
/*召集ip_append_data,缓存要发送到SK-> SKWRead Engy队列的数据
并召集IP-PASS,发送数据
Ip- AppEnthDI数据(SK), icmp_glue_bits, icmp_param,
icmp_param->data_len+icmp_param->head_len,
icmp_param->head_len,
ipc, rt, MSG_DONTWAIT) < 0)
ip_flush_pending_frames(SK));
elseif (SKB) = skb_peek(&sk->sk_write_queue)) != 空) {
structicmphdr *icmph = icmp_hdr(SKB)
__wsumcsum = 0;
structsk_buff *skb1;
skb_queue_walk(&sk->sk_write_queue,skb1) {
csum= csum_add(csum, skb1->csum);
}
csum= csum_partial_copy_nocheck((void *)&icmp_param->data,
(炭) *)icmph,
icmp_param->head_len, csum);
icmph->checksum= csum_fold(csum);
skb->ip_summed= CHECKSUM_NONE;
ip_push_pending_frames(SK));
}
}
icmp_send有或起作用
如从进入权和出口数据包终成泡影的处理或负责,下层协定将召集ICMPySead发送数据。,因此有或起作用的以下剖析
效能:发送ICMP error 数据包
无法发送ICMP error 数据包必须先具备的
1、传入的数据包,它们是多播数据包(计算机硬件或IP地址,不要发送ICMP error 数据包
2、参加比赛的人数据包有段,仅用于一号段的输出数据包,发送ICMP 背面的的数据包
3、进入权包自身是ICMP error典型的,不要发送ICMP数据包的进入权 error
以防输出数据包使失望前述的必须先具备的,你需求发送一点钟ICMP的数据包 背面的典型数据
1、查找路由
2、路由查找成时,此后,将召集ICMPthPuxy回答。 发送数据
void icmp_send(作曲) sk_buff *skb_in, int type, int code, __be32 物)
{
struct iphdr *iph;
int room;
struct icmp_bxm icmp_param;
struct rtable *rt = skb_rtable(SK)b_in);
struct ipcm_cookie ipc;
__be32 saddr;
u8 tos;
struct net *net;
struct sock *sk;
if (!RT)
goto out;
net = dev_net(RT);
/*
*Find the original 头。 It is expected to be valid, of 快跑。
*Check this, icmp_send is called from the most obscure devices
*间或。
*/
iph = ip_hdr(SK)b_in);
/*
1、对用大锤打犬举行有理的反省,担保ipheader在sk_buff->head与sk_buff->tail暗中的范围内
*/
if ((u8 *)iph < skb_in->head ||
(SK)b_in->network_header + sizeof(*iph)) > skb_in->tail)
goto out;
/*
*No replies to physical multicast/broadcast
*/
数据链路层的地址以防决定传入的P。,以防你停止
if (SK)b_in->pkt_type != PACKET_HOST)
goto out;
/*
*Now check at the protocol level
*/
/*
1、反省输出数据包以防播送、组播数据
*/
if (RTrt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
goto out;
/*
*Only reply to fragment 0. We byte re-order the constant
*mask for 赢利性。
*/
/*
1、向IP部门数据,仅将ICMP背面的物发送到一号部门字组分类
*/
if (iph->frag_off & htons(IP_OFFSET))
goto out;
/*
*If we send an ICMP error to an ICMP error a mess would 成果。
*/
/*
1、决定收执到的数据包以防是ICMP 背面的物包,以防ICMP背面的音讯不回包
*/
if (ICMP*影响[典型]背面的) {
/*
*We are an error, check if we are replying to an
*ICMP error
*/
if (iph->protocol == IPPROTO_ICMP) {
u8 _inner_type, *itp;
itp = skb_header_pointer(SK)b_in,
skb_network_header(SK)b_in) +
(iph->ihl << 2) +
offsetof(作曲) icmphdr,
典型) –
skb_in->data,
sizeof(_inner_type),
&_inner_type);
if (ITP) == 空)
goto out;
/*
*Assume any unknown ICMP type is an 背面的。 This
*isn”t specified by the RFC, but think about it..
*/
if (*itp > NR_ICMP_TYPES ||
ICMP*影响[*ITP]背面的
goto out;
}
}
结束软停止,并为套接字添加旋转锁,确保就是一点钟ICMP音讯被发送出去的同时。
sk = icmp_xmit_lock(广泛分布)
if (SK) == 空)
return;
/*
*Construct source address and 得到或获准进行选择。
*/
/*
1、向挥向地址的当地的进入权数据包,当地的地址用作ICMP一组建议的源IP地址。
2、的挥向地址的数据包不回进入权,则如sysctl_icmp_errors_use_inbound_ifaddr的值来设置源ip地址
*/
saddr = iph->daddr;
if (!(RTrt_flags & RTCF_LOCAL)) {
struct net_device *dev = NULL;
rcu_read_lock();
if (RT &&
net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr)
dev = dev_get_by_index_rcu(net, rt->);
if (DEV)
saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK);
else
saddr = 0;
rcu_read_unlock();
}
设置TOS值
tos = ICMP*影响[典型]背面的 ? ((iph->tos & IPTOS_TOS_MASK) |
IPTOS_PREC_INTERNETCONTROL) :
iph->tos;
if (ip_options_echo(&icmp_param.replyopts, skb_in))
goto out_unlock;
/*
*Prepare data for ICMP 头。
*/
设置ICMP的头物
.type= type;
.code= code;
. = info;
.checksum= 0;
= skb_in;
= skb_network_offset(SK)b_in);
inet_sk(SK))->tos = tos;
ipc.addr = iph->saddr;
ipc.opt = &icmp_param.replyopts;
ipc.shtx.flags = 0;
{
struct flowi fl = {
.nl_u = {
.ip4_u = {
.daddr = icmp_param.replyopts.srr ?
icmp_param.replyopts.faddr :
iph->saddr,
.saddr = saddr,
.tos = RT_TOS(tos)
}
},
.proto = IPPROTO_ICMP,
.uli_u = {
.icmpt = {
.type = type,
.code = code
}
}
};
int err;
struct rtable *rt2;
/*xfrm 架构中间定位密码不默认XFRM密码,成穹状弯曲在这里立即的,开场白内核心不在焉翻开XFRM。
security_skb_classify_flow(SK)b_in, FL)
当内核不翻开XFRM
a)以防心不在焉找到路由,立即的召集ICMPXXMITIX解锁,翻开软停止放开旋转锁定
B)以防找到路由,ICMPv4xxRimLIVE高的限速处理或负责。,并召集icmp_push_reply发送数据
if (__ip_route_output_key(net, &rt, FL)
goto out_unlock;
/* No need to clone since we”re just using its 地址。 */
rt2 = rt;
/*xfrm 架构中间定位密码,不懂XFRM密码,成穹状弯曲在这里立即的,开场白内核心不在焉翻开XFRM。
err = xfrm_lookup(net, (作曲) dst_entry **)&rt, &fl, NULL, 0);
switch (背面的)) {
case 0:
if (RT != rt2)
goto route_done;
break;
case -EPERM:
rt = NULL;
break;
default:
goto out_unlock;
}
if (xfrm_decode_session_reverse(SK)b_in, &fl, AF_INET))
goto relookup_failed;
if (inet_addr_type(net, ) == RTN_LOCAL)
err = __ip_route_output_key(net, &rt2, FL)
else {
struct flowi fl2 = {};
struct dst_entry *odst;
fl2.fl4_dst = ;
if (ip_route_output_key(net, &rt2, &fl2))
goto relookup_failed;
/* Ugh! */
odst = skb_dst(SK)b_in);
err = ip_route_input(SK)b_in, fl.fl4_dst, ,
RT_TOS(tos), rt2->);
dst_release(&rt2->);
rt2 = skb_rtable(SK)b_in);
skb_dst_set(SK)b_in, odst);
}
if (背面的))
goto relookup_failed;
err = xfrm_lookup(net, (作曲) dst_entry **)&rt2, &fl, NULL,
XFRM_LOOKUP_ICMP);
switch (背面的)) {
case 0:
dst_release(&rt->);
rt = rt2;
break;
case -EPERM:
goto ende;
default:
relookup_failed:
if (!RT)
goto out_unlock;
break;
}
}
route_done:
if (!icmpv4_xrlim_allow(net, rt, type, 密码))
goto ende;
/* RFC says return as much as we can without exceeding 576 八位位组。 */
room = dst_mtu(&rt->);
if (房间) > 576)
room = 576;
room -= sizeof(作曲) iphdr) + icmp_param.;
room -= sizeof(作曲) icmphdr);
= skb_in->len – ;
if ( > 房间)
= room;
= sizeof(作曲) icmphdr);
icmp_push_reply(&icmp_param, &ipc, RT)
ende:
ip_rt_put(RT);
out_unlock:
icmp_xmit_unlock(SK));
out:;
}
静止有或起作用:
static struct sock *icmp_sk(作曲) net 广泛分布)
{
/*
1、获取流行的CPU的家具形势 全袜,首要用于发送ICMP数据包
*/
return net->[smp_processor_id()];
}
/*
效能:
1、结束软停止
2、推进袜套的旋转锁
*/
static inline struct sock *icmp_xmit_lock(作曲) net 广泛分布)
{
struct sock *sk;
local_bh_disable();
/*
1、获取ICMP sock
2、袜套旋转锁,以防终成泡影,来回空值,以防成,回到袜套
*/
sk = icmp_sk(广泛分布)
if (不值得讨论的)!spin_trylock(&sk->))) {
/* This can happen if the output path signals a
* dst_link_failure() for an outgoing ICMP 小风趣的人.
*/
local_bh_enable();
return NULL;
}
return sk;
}
/*
效能:
1、翻开软停止
2、放开旋转锁定
*/
static inline void icmp_xmit_unlock(作曲) sock SK)
{
spin_unlock_bh(&sk->);
}
这么,ICMP数据收执与收执快速地流动剖析