设计目的:
-
ARP协议用于已知邻居的IP地址时得到其MAC地址;ICMP协议是网络层 的重要协议;TCP是运输层的可靠传输协议。通过编程封装、发送、捕获并 解析这些协议的数据分组,加深对网络协议的理解,掌握ARP/ICMP/TCP的 协议数据结构和工作原理及其对协议栈的贡献。
设计要求:
-
按照下图所示的拓扑结构连接网络设备和PC机(也可再加入交换机/路由器 扩展网络拓扑结构),并对其进行网络配置,测试PC机之间的连通性,保证 2 台PC机能正常进行网络通信。
-
如果不具备上述实验条件,则可使用以太网或WLAN技术建立一个局域网。 例如,家庭环境可以使用无线路由器或手机热点组建一个WLAN,用笔记本 电脑访问同一WLAN内的无线路由器或手机,也可通过无线路由器或手机热 点访问互联网。
-
选择ARP、ICMP或TCP之一,在一台计算机上编写、编译和运行程序,使 其访问另一台计算机(可以是路由器)。两台计算机可以位于一个网络内 (ARP/ICMP/TCP), 也 可 以 在 不 同 网 络 中(ICMP/TCP)。
-
程序功能:①根据ARP/ICMP/TCP协议数据的结构,封装成数据帧发送给另 一台计算机(可以是手机);②捕获网络中包含ARP/ICMP/TCP协议数据的数 据帧,解析协议数据的内容,并在标准输出中显示报文首部字段的内容,同 179 时写入日志文件。
-
以命令行或图形界面形式运行程序。
-
运行程序的同时开启Wirshark抓包软件,检验本地计算机发出与收到的数据 分组。
设计分析:
-
使用原始套接字或者WinPcap实现(也可使用和WinPcap功能近似的库,如 jpcap 或 libpcap 等)。
-
定义ARP/ICMP/TCP首部的数据结构。
-
自定义并填充数据包,发送数据包,捕获数据包。
发送程序:
#include "WinSock2.h" //包含winsock2.h头文件
#include "pcap.h" //包含pcap.h头文件
#include <iostream>
#include <string>
#pragma comment(lib, "wpcap.lib") // 链接库文件
#pragma comment(lib, "ws2_32.lib") // 链接库文件
using namespace std;
typedef struct phyFrame
{
unsigned char DstMac[6]; // 目的MAC地址
unsigned char SrcMac[6]; // 源MAC地址
unsigned short FrameType; // 帧类型
} PHYFRAME, * LPPHYFRAME;
typedef struct arpFrame
{
unsigned short HardwareType; // 硬件类型
unsigned short ProtocolType; // 上层协议类型
unsigned char MacLength; // MAC地址长度
unsigned char IpLength; // IP地址长度
unsigned short Flag; // 1表示请求,2表示应答
unsigned char SrcMac[6]; // 源MAC地址
unsigned char SrcIp[4]; // 源IP地址
unsigned char DstMac[6]; // 目的MAC地址
unsigned char DstIp[4]; // 目的IP地址
unsigned char Padding[18]; // 填充
} ARPFRAME, * LPARPFRAME;
typedef struct ArpPacket
{
PHYFRAME phyFrame; // 物理帧
ARPFRAME ArpFrame; // ARP帧
} ARPPACKET, * LPARPPACKET;
ArpPacket arpPacket; // ARP数据包
void FillMACAddress(const char* macString, unsigned char* macAddr)
{
char mac[20];
int j = 0;
strcpy_s(mac, macString);
for (int i = 0; i < 6; i++)
{
macAddr[i] = strtol(mac + j, NULL, 16);
j += 3; // 跳过':'或者其他分隔符
}
}
void FillIPAddress(const char* ipString, unsigned char* ipAddr)
{
char ip[20];
int j = 0;
strcpy_s(ip, ipString);
for (int i = 0; i < 4; i++)
{
ipAddr[i] = atoi(ip + j);
while (ip[j] != '.' && ip[j] != '\0')
{
j++;
}
j++; // 跳过'.'
}
}
int main(int argc, char* argv[])
{
// 填充 ARP 数据包
arpPacket.phyFrame.FrameType = htons(0x0806); // 设置帧类型为 ARP
arpPacket.ArpFrame.HardwareType = htons(0x0001); // 设置硬件类型为以太网
arpPacket.ArpFrame.ProtocolType = htons(0x0800); // 设置协议类型为 IPv4
arpPacket.ArpFrame.MacLength = 6; // MAC 地址长度为 6
arpPacket.ArpFrame.IpLength = 4; // IP 地址长度为 4
arpPacket.ArpFrame.Flag = htons(0x0001); // 设置标志为请求
// 填充源 MAC 地址、源 IP 地址、目的 MAC 地址和目的 IP 地址
//arpPacket.ArpFrame.SrcMac[0] = 0xF0;
//arpPacket.ArpFrame.SrcMac[1] = 0x77;
//arpPacket.ArpFrame.SrcMac[2] = 0xc3;
//arpPacket.ArpFrame.SrcMac[3] = 0x1D;
//arpPacket.ArpFrame.SrcMac[4] = 0x6E;
//arpPacket.ArpFrame.SrcMac[5] = 0x29;
//cout << arpPacket.ArpFrame.SrcMac << endl;
FillMACAddress("F0:77:C3:1D:6E:29", arpPacket.phyFrame.SrcMac); // 设置源 MAC 地址
FillMACAddress("ff:ff:ff:ff:ff:ff", arpPacket.phyFrame.DstMac); // 设置目的 MAC 地址(广播地址)
FillMACAddress("F0:77:C3:1D:6E:29", arpPacket.ArpFrame.SrcMac); // 设置源 MAC 地址
FillIPAddress("192.168.43.183", arpPacket.ArpFrame.SrcIp); // 设置源 IP 地址
FillMACAddress("ff:ff:ff:ff:ff:ff", arpPacket.ArpFrame.DstMac); // 设置目的 MAC 地址(广播地址)
FillIPAddress("192.168.43.84", arpPacket.ArpFrame.DstIp); // 设置目的 IP 地址
// 填充填充字段
memset(arpPacket.ArpFrame.Padding, 0, 18); // 将填充字段清零
// 初始化网卡相关参数
pcap_if_t* alldevs;
pcap_if_t* d, * head = NULL;
pcap_t* fp;
char errbuf[PCAP_ERRBUF_SIZE];
// 获取网卡列表
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
cout << "Unable to create adapter list!" << endl;
return 0;
}
// 列出网卡数目
int i = 0;
for (d = alldevs; d; d = d->next)
{
cout << ++i << ": " << d->name;
if (d->description)
cout << " " << d->description << endl;
}
// 没有发现网卡
if (i == 0)
{
cout << "No adapter found!" << endl;
return 0;
}
// 选择要使用的网卡
cout << "Enter the interface number (1-" << i << "):";
int k;
cin >> k;
if (k < 1 || k > i)
{
cout << "Out of range!" << endl;
return 0;
}
for (d = alldevs, i = 1; i < k; d = d->next, i++)
;
head = d;
// 以混杂模式打开网卡
if ((fp = pcap_open_live(head->name, 1000, 1, 1000, errbuf)) == NULL)
{
cout << "Unable to open the adapter!" << endl;
pcap_freealldevs(alldevs);
return 0;
}
// 通过网卡发送ARP包
if (pcap_sendpacket(fp, (unsigned char*)&arpPacket, sizeof(ARPPACKET)) == -1)
{
cout << "ARP packet send error: " << pcap_geterr(fp) << endl;
return 0;
}
cout << "ARP packet send success!" << endl;
}
接收程序:
#include <conio.h>
#include <fstream>
#include <iomanip>
#include "pcap.h"
#include <winsock2.h>
#include <iostream>
#pragma comment(lib, "ws2_32. lib")
#pragma comment(lib, "wpcap. lib")
using namespace std;
// 定义ARP包结构
struct arppkt
{
unsigned short hdtyp; // 硬件类型
unsigned short protyp; // 协议类型
unsigned char hdsize; // 硬件地址长度
unsigned char prosize; // 协议地址长度
unsigned short op; // 操作类型
u_char smac[6]; // 源MAC地址
u_char sip[4]; // 源IP地址
u_char dmac[6]; // 目的MAC地址
u_char dip[4]; // 目的IP地址
};
void packet_handler(const pcap_pkthdr *header, const u_char *pkt_data, ostream &out)
{
// 从ARP包中找到头部位置
arppkt *arph = (arppkt *)(pkt_data + 14);
// 输出源IP地址
for (int i = 0; i < 3; i++)
out << int(arph->sip[i]) << '.';
out.setf(ios::left);
out << setw(3) << int(arph->sip[3]) << " ";
out.unsetf(ios::left);
// 输出源MAC地址
char oldfillchar = out.fill('0');
out.setf(ios::uppercase);
for (int i = 0; i < 5; i++)
out << hex << setw(2) << int(arph->smac[i]) << '-';
out << hex << setw(2) << int(arph->smac[5]) << " ";
out.fill(oldfillchar);
out.unsetf(ios::hex | ios::uppercase);
// 输出目的IP地址
for (int i = 0; i < 3; i++)
out << int(arph->dip[i]) << '-';
out.setf(ios::left);
out << setw(3) << int(arph->dip[3]) << " ";
out.unsetf(ios::left);
// 输出目的MAC地址
out.fill('0');
out.setf(ios::uppercase);
for (int i = 0; i < 5; i++)
out << hex << setw(2) << int(arph->dmac[i]) << '-';
out << hex << setw(2) << int(arph->dmac[5]) << " ";
out.fill(oldfillchar);
out.unsetf(ios::hex | ios::uppercase);
// 输出操作类型
out << ntohs(arph->op) << " ";
// 输出操作时间
struct tm *ltime;
ltime = localtime((const time_t *)&header->ts.tv_sec);
out.fill('0');
out << ltime->tm_hour << ':' << setw(2) << ltime->tm_min << ':' << setw(2) << ltime->tm_sec;
out.fill(oldfillchar);
out << endl;
}
void main(int argc, char *argv[])
{
// 检查输入命令格式
if (argc != 2)
{
cout << "Please input command: ParseArp output_file" << endl;
return;
}
// 初始化网络设备相关参数
pcap_if_t *alldevs;
pcap_if_t *d;
pcap_t *adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
u_int network;
char packet_filter[] = "ether proto \\arp";
struct bpf_program fcode;
struct pcap_pkthdr *header;
const u_char *pkt_data;
// 获取网络设备列表
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
cout << "Error in pcap_find all devs: " << errbuf;
return;
}
// 选择一个Ethernet网卡
for (d = alldevs; d; d = d->next)
{
// 网卡设为混杂模式, 接收所有帧
if ((adhandle = pcap_open_live(d->name, 1000, 1, 300, errbuf)) == NULL)
{
cout << "Unable to open the adapter.";
pcap_freealldevs(alldevs);
return;
}
// 检查数据链路是否为Ethernet
if (pcap_datalink(adhandle) == DLT_EN10MB && d->addresses != NULL)
break;
}
if (d == NULL)
{
cout << "No interfaces found! Make sure WinPcap is installed.";
return;
}
// 获得子网掩码
network = ((sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
// 编译过滤器, 只捕获ARP包
if (pcap_compile(adhandle, &fcode, packet_filter, 1, network) < 0)
{
cout << "Unable to compile the packet filter. Check the syntax.";
pcap_freealldevs(alldevs);
return;
}
// 设置过滤器
if (pcap_setfilter(adhandle, &fcode) < 0)
{
cout << "Error setting the filter.";
pcap_freealldevs(alldevs);
return;
}
// 显示提示信息及每项含义
cout << "Listening on " << d->description << "…" << endl;
ofstream fout(argv[1], ios::app);
time_t t;
time(&t);
fout.seekp(0, ios::end);
if (fout.tellp() != 0)
fout << endl;
fout << "\t\tARP request(1)/reply(2) on " << ctime(&t);
cout << "Sour IP Addr"
<< " "
<< "Sour MAC Address"
<< " "
<< "Des IP Addr"
<< " "
<< " Des MAC Address "
<< " "
<< " OP "
<< " "
<< " Time " << endl;
fout
<< "Sour IP Addr"
<< " "
<< "Sour MAC Address"
<< " "
<< "Des IP Addr"
<< " "
<< " Des MAC Address "
<< " "
<< " OP "
<< " "
<< " Time " << endl;
pcap_freealldevs(alldevs);
// 开始截获ARP包
int result;
while ((result = pcap_next_ex(adhandle, &header, &pkt_data)) >= 0)
{
if (result == 0)
continue;
// 解析ARP包, 结果输出到屏幕与文件
packet_handler(header, pkt_data, cout);
packet_handler(header, pkt_data, fout);
}
}
测试结果:
主机A能够成功发送ARP请求。主机B能够正确接收并处理ARP请求。主机A和主机B之间能够建立通信。
(5)实验数据
发送端:
IP地址:192.168.43.183
MAC地址:F0:77:C3:1D:6E:29
接受端:
IP地址:192.168.43.84
MAC地址:未知(使用FF:FF:FF:FF:FF:FF广播)
(6)实验结果分析
a) 程序运行结果
b) 运行结果分析
由运行结果图片可知,主机A(192.168.43.183)发送arp广播数据包成功,在双方的wireshark软件上和主机B(192.168.43.84)接收端均能收到该数据包,主机B接受并正确处理该数据包,将其记录到日志文件log.txt中。
c) 实验结论
主机A成功发送ARP请求,主机B正确接收并处理ARP请求,建立了主机A和主机B之间的通信,验证了ARP协议的基本功能,证明了ARP协议在局域网通信中的重要性和有效性。