IPv4 协议收发实验
[TOC]
实验目的
IPv4 协议是互联网的核心协议,它保证了网络节点(包括网络设备和主机)在网络层能够按照标准协议互相通信。IPv4 地址唯一标识了网络节点。在我们日常使用的计算机的主机协议栈中,IPv4 协议必不可少,它能够接收网络中传送给本机的分组,同时也能根据上层协议的要求将报文封装为 IPv4 分组发送出去。
本实验通过设计实现主机协议栈中的 IPv4 协议,让学生深入了解网络层协议的基本原理,学习 IPv4 协议基本的分组接收和发送流程。
另外,通过本实验,学生可以初步接触互联网协议栈的结构和计算机网络实验系统,为后面进行更为深入复杂的实验奠定良好的基础。
实验要求
根据计算机网络实验系统所提供的上下层接口函数和协议中分组收发的主要流程,独立设计实现一个简单的 IPv4 分组收发模块。要求实现的主要功能包括:
- IPv4 分组的基本接收处理;
- IPv4 分组的封装发送;
注:不要求实现 IPv4 协议中的选项和分片处理功能
实验内容
- 实现 IPv4 分组的基本接收处理功能对于接收到的 IPv4 分组,检查目的地址是否为本地地址,并检查 IPv4 分组头部中其它字段的合法性。提交正确的分组给上层协议继续处理,丢弃错误的分组并说明错误类型。
- 实现 IPv4 分组的封装发送根据上层协议所提供的参数,封装 IPv4 分组,调用系统提供的发送接口函数将分组发送出去。
实验过程
接收流程
在接口函数stud_ip_recv()
中,需要完成下列处理步骤(仅供参考):- 检查接收到的 IPv4 分组头部的字段,包括版本号(
Version
)、头部长度(IP Head length
)、生存时间(Time to live
)以及头校验和 (Header checksum
)字段。对于出错的分组调用ip_DiscardPkt()
丢弃,并说明错误类型。 - 检查 IPv4 分组是否应该由本机接收。如果分组的目的地址是本机地址或广播地址,则说明此分组是发送给本机的;否则调用
ip_DiscardPkt()
丢弃,并说明错误类型。 - 如果 IPV4 分组应该由本机接收,则提取得到上层协议类型,调用
ip_SendtoUp()
接口函数,交给系统进行后续接收处理。
- 检查接收到的 IPv4 分组头部的字段,包括版本号(
发送流程
在接口函数stud_ip_Upsend()
中,需要完成下列处理步骤(仅供参考):- 根据所传参数(如数据大小),来确定分配的存储空间的大小并申请分组的存储空间。
- 按照 IPv4 协议标准填写 IPv4 分组头部各字段,标识符(
Identification
)字段可以使用一个随机数来填写。(注意:部分字段内容需要转换成网络字节序) - 完成 IPv4 分组的封装后,调用
ip_SendtoLower()
接口函数完成后续的发送处理工作,最终将分组发送到网络中。
问题记录
发送数据时,在设置
totalLen
时,需要注意使用htonl
函数转换得到的是4字节数据,即为unsigned
类型,直接赋值给short
类型会发生数据截取,导致设置totalLen
时使用short totalLen = htonl(len+20);
赋值时,得到的数据永远为0,因为截取时只能得到转换后的低16位,即原来的高16位,即0。因此需要移位或者自行设置每个字节。
- 计算得到的校验和时的
sum
应预设为int
型,赋值给checksum
字段时再转换,才能正确计算出结果。
程序源码
/*
* THIS FILE IS FOR IP TEST
*/
// system support
#include "sysInclude.h"
#include<iostream>
#include <bitset>
using namespace std;
extern void ip_DiscardPkt(char* pBuffer,int type);
extern void ip_SendtoLower(char*pBuffer,int length);
extern void ip_SendtoUp(char *pBuffer,int length);
extern unsigned int getIpv4Address();
// implemented by students
int stud_ip_recv(char *pBuffer,unsigned short length)
{
printf("Check IP Version:\n");
unsigned int version=(pBuffer[0]>>4);
printf("IP Version is %d\n", version);
if(version!=4){
ip_DiscardPkt(pBuffer, STUD_IP_TEST_VERSION_ERROR);
return 1;
}
printf("Check Header Length:\n");
unsigned int len=(pBuffer[0]&0x0F);
printf("Header Length is %d\n", len);
if(len<5){
ip_DiscardPkt(pBuffer, STUD_IP_TEST_HEADLEN_ERROR);
return 1;
}
printf("Check Time To Live:\n");
unsigned int ttl=pBuffer[8];
printf("Time To Live is %d\n", ttl);
if(ttl<=0){
ip_DiscardPkt(pBuffer, STUD_IP_TEST_TTL_ERROR);
return 1;
}
printf("Check CheckSum:\n");
unsigned int sum=0;
for(int i=0;i<len*2;i++){
sum+=((unsigned short*)pBuffer)[i];
sum=(sum>>16)+(sum&0xffff);
}
cout<<sum<<endl<<~sum<<endl;
cout<<bitset<sizeof(short)*8>((unsigned short)sum)<<endl;
cout<<bitset<sizeof(short)*8>(~(unsigned short)sum)<<endl;
printf("Check Sum is %d\n", ~sum);
printf("CheckSUM is %d\n", ((unsigned short*)pBuffer)[5]);
if(((unsigned short)sum)!=0xffff){
ip_DiscardPkt(pBuffer, STUD_IP_TEST_CHECKSUM_ERROR);
return 1;
}
printf("Check Destination:\n");
unsigned int destination=ntohl(*(unsigned int*)(pBuffer+16));
unsigned int ip=getIpv4Address();
printf("destination is %d, host ip is %d\n", destination, ip);
if(ip!=destination && destination!=0xffffffff){
ip_DiscardPkt(pBuffer, STUD_IP_TEST_DESTINATION_ERROR);
return 1;
}
printf("Send To UP\n");
ip_SendtoUp(pBuffer,length);
return 0;
}
int stud_ip_Upsend(char *pBuffer,unsigned short len,unsigned int srcAddr, unsigned int dstAddr,byte protocol,byte ttl)
{
char* buffer=new char[len+20];
memset(buffer,0,len+20);
memcpy(buffer + 20, pBuffer, len);
unsigned short totalLen = len + 20;
buffer[0]=(4<<4)+5;
buffer[3]=totalLen&0xff;
buffer[2]=totalLen>>8;
printf("ttl is %x\n", ttl);
buffer[8]=ttl;
printf("protocol is %x\n", protocol);
buffer[9]=protocol;
((unsigned*)buffer)[3]=htonl(srcAddr);
((unsigned*)buffer)[4]=htonl(dstAddr);
unsigned int sum=0;
for(int i=0;i<10;i++){
printf("%x\n",((unsigned short*)buffer)[i]);
sum+=((unsigned short*)buffer)[i];
sum=(sum>>16)+(sum&0xffff);
}
cout<<bitset<sizeof(short)*8>((unsigned short)sum)<<endl;
cout<<bitset<sizeof(short)*8>(~(unsigned short)sum)<<endl;
cout<<hex<<(unsigned short)sum<<endl;
cout<<hex<<~(unsigned short)sum<<endl;
((unsigned short*)buffer)[5]=~((unsigned short)sum);
sum=0;
for(int i=0;i<10;i++){
sum+=((unsigned short*)buffer)[i];
sum=(sum>>16)+(sum&0xffff);
}
cout<<bitset<sizeof(short)*8>(sum)<<endl;
cout<<bitset<sizeof(short)*8>(~sum)<<endl;
ip_SendtoLower(buffer,totalLen);
return 0;
}