TCP_IP第四次作业


由于自己的偷懒,现在才开始补作业。
2017年1月10日0点

#第四次作业 myping
这个作业主要是用到icmp差错检查的知识,不是很难

设置ICMP报头函数,压包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*设置ICMP报头*/  
int pack(int pack_no)
{
int i,packsize;
struct icmp *icmp;
struct timeval *tval;
icmp = (struct icmp*)sendpacket;
icmp->icmp_type = ICMP_ECHO; //ICMP_ECHO类型的类型号为0
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
icmp->icmp_seq = pack_no; //发送的数据报编号
icmp->icmp_id = pid;

packsize = 8 + datalen; //数据报大小为64字节
tval = (struct timeval *)icmp->icmp_data;
gettimeofday(tval,NULL); //记录发送时间
//校验算法
icmp->icmp_cksum = cal_chksum((unsigned shortshort *)icmp,packsize);
return packsize;
}

#发送ICMP报文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void send_packet()  
{
int packetsize;
if(nsend < MAX_NO_PACKETS)
{
nsend++;
packetsize = pack(nsend); //设置ICMP报头
//发送数据报
if(sendto(sockfd,sendpacket,packetsize,0,
(struct sockaddr *)&dest_addr,sizeof(dest_addr)) < 0)
{
perror("sendto error");
}
}
}

#接受ICMP报文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void recv_packet()  
{
int n,fromlen;
extern int error;
fromlen = sizeof(from);
if(nreceived < nsend)
{
//接收数据报
if((n = recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,
(struct sockaddr *)&from,&fromlen)) < 0)
{
perror("recvfrom error");
}
gettimeofday(&tvrecv,NULL); //记录接收时间
unpack(recvpacket,n); //剥去ICMP报头
nreceived++;
}
}

拆包,做时间计算等

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
int unpack(charchar *buf,int len)  
{
int i;
int iphdrlen; //ip头长度
struct ip *ip;
struct icmp *icmp;
struct timeval *tvsend;
double rtt;


ip = (struct ip *)buf;
iphdrlen = ip->ip_hl << 2; //求IP报文头长度,即IP报头长度乘4
icmp = (struct icmp *)(buf + iphdrlen); //越过IP头,指向ICMP报头
len -= iphdrlen; //ICMP报头及数据报的总长度
if(len < 8) //小于ICMP报头的长度则不合理
{
printf("ICMP packet\'s length is less than 8\n");
return -1;
}
//确保所接收的是所发的ICMP的回应
if((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid))
{
tvsend = (struct timeval *)icmp->icmp_data;
tv_sub(&tvrecv,tvsend); //接收和发送的时间差
//以毫秒为单位计算rtt
rtt = tvrecv.tv_sec*1000 + tvrecv.tv_usec/1000;
temp_rtt[nreceived] = rtt;
all_time += rtt; //总时间
//显示相关的信息
printf("%d bytes from %s: icmp_seq=%u ttl=%d time=%.1f ms\n",
len,inet_ntoa(from.sin_addr),
icmp->icmp_seq,ip->ip_ttl,rtt);
}
else return -1;
}

#主调用函数
生成ICMP的原始套接字,压入ip,发包,守包,检查包内内容,然后设置ttl

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
main(int argc,charchar *argv[])  
{
struct hostent *host;
struct protoent *protocol;
unsigned long inaddr = 0;
int size = 550 * 1024;
addr[0] = argv[1];
//参数小于两个
if(argc < 2)
{
printf("usage:%s hostname/IP address\n",argv[0]);
exit(1);
}
//不是ICMP协议
if((protocol = getprotobyname("icmp")) == NULL)
{
perror("getprotobyname");
exit(1);
}

//生成使用ICMP的原始套接字
if((sockfd = socket(AF_INET,SOCK_RAW,protocol->p_proto)) < 0)
{
perror("socket error");
exit(1);
}

setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size));
bzero(&dest_addr,sizeof(dest_addr)); //初始化
dest_addr.sin_family = AF_INET; //套接字域是AF_INET(网络套接字)

//压入ip
dest_addr.sin_addr.s_addr = inet_addr(argv[1]);

pid = getpid();
printf("PING %s(%s):%d bytes of data.\n",argv[1],
inet_ntoa(dest_addr.sin_addr),datalen);

//当按下ctrl+c时发出中断信号,并开始执行统计函数
signal(SIGINT,statistics);
while(nsend < MAX_NO_PACKETS){
sleep(1); //每隔一秒发送一个ICMP报文
send_packet(); //发送ICMP报文
recv_packet(); //接收ICMP报文
}
return 0;
}