-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathicmp源码阅读笔记.txt
300 lines (282 loc) · 10.8 KB
/
icmp源码阅读笔记.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
icmp数据包发送函数
void icmp_send(struct sk_buff*skb_in,int type,int code,unsigned long info,struct device*dev)
{
//获取收到的数据包的iph头
iph=(struct iphdr*)(skb_in->data+dev->hard_header_len);
//判断收到的数据包地址是否是本机
//PACKET_HOST 0
//PACKET_BROADREST 1
//PACKET_MULTICAST 2
//PACKET_OTHERHOST 3
if(skb_in->pkt_type!=PACKET_HOST)
return ;//不是本机的数据包不管
atype=ip_chk_addr(iph->daddr);
if(atype==IS_BROADCAT||IN_MULTICAST(iph->daddr))
return ;//检查是否是个广播或者多播地址是则直接返回也不发送icmp
if(ntohs(iph->frag_off)&IP_OFFSET)
return ;//icmp只对第一个分片响应
if(type==ICMP_DEST_UNREACH||type==ICMP_REDIRECT||type==ICMP_SOURCE_QUENCH||type==ICMP_TIME_EXCEEDED)
{
if(iph->protocol==IPPROTO_ICMP)//是icmp报文
{
icmph=(struct icmphdr*)((char*)iph+4*iph->ihl);//获取icmp数据包的icmp头
if(icmph->type==ICMP_DEST_UNREACH||icmph->type==ICMP_SOURCE_QUENCH||icmph->type==ICMP_REDIRECT||icmph->type==ICMP_TIME_EXCEEDED
||icmph->type==ICMP_PARAMETERPROB)
{
return ;//不对icmp数据包的这些类型进行响应
}
}
}
//计数
switch(type):
{
根据type类型进行统计计数。
}
len=dev->hard_header_len+sizeof(struct iphdr)+sizeof(struct icmphdr)
+sizeof(struct iphdr)+32;//因为icmp负载是源数据包的iph头以及八字节的传输层头
skb=(struct sk_buff*)alloc_skb(len,GFP_ATOMIC);
if(skb==NULL)
{
//分配失败
//计数
return ;
}
skb->free=1;//不需要重传
our_addr=dev->pa_addr;
if(iph->daddr!=our_addr&&ip_chk_addr(iph->daddr)==IS_MYADDR)
our_addr=iph->daddr;//如果收到的数据包设备地址与数据包目的地址不是同一个但是数据包目的地址确实
//是本机的,则 使用数据包中的目的之作为源地址进行响应
//构建ip以及硬件头
offset=ip_build_header(skb,our_addr,....);
//更新skb->len
skb->len=offset+sizoef(struct iphdr)+sizeof(struct icmphdr)+8;//负载为ihpdr+8
//填充icmp
icmph=(struct icmphdr)(skb->data+offset);
icmph->type=type;
icmph->code=code;
icmph->checksum=0;
icmph->un.gateway=info;
memcpy(icmph+1;iph,sizeof(struct iphdr)+8);//拷贝源数据包的iph以及传输层8字节
icmph->checksum=ip_compute_csum(...);//计算icmp效验和
ip_queue_xmit(NULL,ndev,skb,1);//进入网络层发送
}
static void icmp_unreach(struct icmphdr*icmph,struct sk_buff*skb)
{
err=icmph->type<<8|icmph->code;//将type以及code赋值给err
iph=(struct icmphdr*)(icmph+1);//读取负载
switch(icmph->type&7)
{
case ICMP_NET_UNREACH:
break;
case ICMP_HOST_UNREACH:
break;
case ICMP_PROT_UNREACH:
printk("icmp%s :%d protocol unreachable\n",in_ntoa(iph->daddr),ntohs(iph->protocol));
break;
case ICMP_PORT_UNREACH:
break;
case ICMP_FRAG_NEEDED:
printk("icmp %s:fragmentation needed and df set\n",in_ntoa(iph->daddr));
break;
case ICMP_SR_FAILED:
printk("icmp %s source route failed\n",in_ntoa(iph->daddr));
break;
default :
break;
}
hash=iph->protocol&(MAX_INET_PROTOS-1);
ipprot=(struct inet_protocol*)inet_prot[hash];//查找本地对应的协议
while(ipprot!=NULL)
{
nextip=ipprot->next;
if(iph->protocl==ipprot->protocol&&ipprot->err_handler)
ipprot->err_handler(err,(struct icmphdr*)(icmph+1),iph->daddr,iphdr->saddr,ipprot);
ipprot=nextip;
}//遍历对应的协议,调用对应的错误处理函数进行处理
kfree_skb(skb,FREE_READ);
}
处理重定向icmp报文
void icmp_redirect(struct icmphdr*icmph,struct sk_buff*skb,struct device*dev,unsigned long source)
{
iph=(struct iphdr*)(icmph+1);//icmph+1就是原先发送数据包的ip首部.负载为源ip+8字节
ip=iph->daddr;
//ICMP_REDIR_NET 0
//ICMP_REDIR_HOST 1
//ICMP_REDIR_NETTOS 2
//ICMP_REDIR_HOSTTOS 3
swicth(icmph->code&7)
{
case ICMP_REDIR_NET :
ip_rt_add((RTF_DYNAMIC|RTF_MODFIED|RTF_GATEWAY),ip,0,icmph->uh.gateway,dev,0,0);
break;
case ICMP_REDIR_HOST:
if((rt=ip_rt_route(ip,NULL,NULL))==NULL)
break;//找不到路由则跳出不做操作
if(rt->rt_gateway!=source||ip_chk_addr(icmph->un.gateway))
break;//如果路由源地址不是发icmp重定向报文地址以及重定向地址不是广播以及多播地址等
ip_rt_add((RTF_DYNAMIC|RTF_MODFIED|RTF_HOST|RTF_GATEWAY),ip,0,icmph->un.gateway,dev,0,0);
break;
case ICMP_REDIR_HOSTTOS:
case ICMP_REDIR_NETTOS:
break;//不支持
default :break;
}
kfree_skb(skb,FREE_READ);
}
echo 类型icmp处理函数//saddr 是远端地址,daddr是本地地址
void icmp_echo(struct icmphdr*icmph,struct sk_buff*skb,struct device*dev,
unsigned long saddr,unsigned long daddr,int len,struct options*opt)
{
size=dev->hard_header_len+64+len;
skb2=alloc_skb(size,GFP_ATOMIC);
skb2->free=1;
offset=ip_build_header(...);
skb2->len=offset+len;
icmphr=(struct icmphdr*)(skb2->data+offset);
memcpy((char*)icmphr,(char*)icmph,len);//reply数据包就是原数据包只是改变了type
icmphr->type=ICMP_ECHOREPLAY;//设置reply类型
icmphr->code=0;
icmphr->checksum=0;
icmphr->checksum=ip_compute_csum((unsigned char*)icmphr,len);//计算icmp效验和
ip_queue_xmit((struct sock*)NULL,ndev,skb2,1);//发送
kfree_skb(skb,FREE_READ);
}
时间戳报文处理
//icmph时间戳请求报文的icmp首部,saddr发送请求数据包里的源地址
//len是ip负载长度包括icmp以及icmp负载
void icmp_timestamp(struct icmphdr*icmph,struct sk_buff*skb,struct device*dev,
unsigned long saddr,unsigned long daddr,int len,struct options*opt)
{
if(len<20)
{
//计数
if(len<12)//小于12字节表示没有发送端时间戳无法继续处理
return;
}
size=dev->hard_header_len+84;//84是60字节ip首部+icmp20字节首部+4字节帧效验字段
skb2=alloc_skb(size,GFP_ATOMIC);
skb2->free=1;
offset=ip_build_header(...);
skb2->len=offset+20;//预留20字节时间戳12字节时间戳+8字节icmp头
icmphr=(struct icmphdr*)((char*)(skb2->data+offset));
memcpy((cahr*)icmphr,(char*)icmph,12);
icmphr->type=ICMP_TIMESTAMPREPLY;
icmphr->code=icmphr->checksum=0;
midtime=(xtime.tv_sec%86400)*1000+xtime.tv_usec*1000;//取余86400表示当天零时起秒数*1000转换成毫秒
timeptr=(unsigned long*)(icmphr+1);//跳过icmp头,8字节icmp头
/* struct icmphdr{
unsigned char type;
unsigned char code;
unsigned short checksum;
union{
struct {unsigned short id;
unsigned short sequence;
}echo;
unsigned long gateway;
}un;
};
*/
timeptr[1]=timeptr[2]=htonl(midtime);//第一个不用动值 操作第二个第三个
icmphr->check_sum=ip_compute_csum(...);//计算icmp效验和
ip_queue_xmit((struct sock*)NULL,ndev,skb2,1);
kfree_skb(skb2,FREE_READ);
}
信息请求icmp处理报文
void icmp_info(struct icmphdr*icmph,struct sk_buff*skb,struct device*dev,
unsigned long saddr,unsigned long daddr,int len,struct options*opt)
{
kfree_skb(skb,FREE_READ);
}
地址掩码icmp查询请求报文//返回本地地址对应的ip地址掩码
void icmp_address(struct icmphdr*icmph,struct sk_buff*skb,struct device*dev,
unsigned long saddr,unsigned long daddr,int len,struct options*opt)
{
size=dev->hard_header_len+64+len;//mac首部+64(60字节ip首部+4字节帧效验长度)+len负载长度
skb2=alloc_skb(size,GFP_ATOMIC);
skb2->free=1;
offset=ip_build_header(skb2,daddr,saddr,&ndev,..)
skb2->len=offset+len;
icmphr=(struct icmphdr*)(skb2->data+offset);
icmphr->type=ICMP_ADDRESSREPLY;
icmphr->code=0;
icmphr->checksum=0;
icmphr->un.echo.id=icmph->echo.un.id;//原id
icmphr->un.echo.sequence=icmph->echo.un.sequence;//原序列号
memcpy((cahr*)(icmphr+1),(char*)&dev->pa_mask,sizeof(dev->pa_mask));//拷贝掩码到icmp头之后的数据区
icmphr->checksump=ip_compute_csum(...);
ip_queue_xmit((strucy sock*)NULL,ndev,skb2,1);
skb2->sk=NULL;
kfree_skb(skb2,FREE_READ);
}
//总入口函数,skb1是接受的icmp报文,redo=0表示是个新接收的报文,daddr表示这个报文的目的地址
int icmp_rcv(struct sk_buff*skb1,struct device*dev,struct options*opt,unsigned long daddr,
unsigned short len,unsigned long saddr,int redo,struct inet_protocol*protocol)
{
buff=skb1->h.raw;
icmph=(struct icmphdr*)buff;
if(ip_compute_csum(...))
{
//效验失败
kfree_skb(skb1,FREE_READ);
return 0;
}
if(ip_chk_addr(daddr)!=IS_MYADDR)
{
if(icmph->type!=ICMP_ECHO)//只对地址不是本机的icmp报文应答,因为mac能收但是地址不是本机就是可能本机是网关或者路由
{
kfree_skb(skb1,FREE_READ);
return 0;
}
daddr=dev->pa_addr;
}
switch (icmph->type)
{
case ICMP_TIME_EXCEEDED:
icmp_unreach(icmph,skb1);
return 0;
case ICMP_DEST_UNREACH :
icmp_unreach(icmph,skb1);
return 0;
case ICMP_SOURCE_QUENCH :
icmp_unreach(icmph,skb1);
return 0;
case ICMP_REDIRECT :
icmp_redirect(icmph,skb1,dev,saddr);
return 0;
case ICMP_ECHO:
icmp_echo(cimph,skb1,dev,saddr,daddr,len,dev);
return 0;
case ICMP_ECHOREPLY:
kfree_skb(skb1,FREE_READ);
return 0;
case ICMP_TIMESTAMP:
icmp_timestamp(icmph,skb1,dev,saddr,daddr,len,opt);
return 0;
case ICMP_TIMESTAMPREPLY:
skb1->sk=NULL;
kfree_skb(skb1,FREE_READ);
return 0;
case ICMP_INFO_REQUEST:
icmp_info(icmph,skb1,dev,saddr,daddr,len,opt);
return 0;
case ICMP_INFO_REPLY:
skb1->sk=NULL;
kfree_skb(skb1,FREE_READ);
return 0;
case ICMP_ADDRESS:
icmp_address(icmph,skb1,dev,saddr,daddr,len,opt);
return 0;
case ICMP_ADDRESSREPLY :
kfree_skb(skb1,FREE_READ);
return 0;
default :
kfree_skb(skb1,FREE_READ);
return 0;
}
kfree_skb(skb1,FREE_READ);
return -1;
}
int icmp_ioctl(struct sock*sk,int cmd,unsigned long arg)
{
.....
}