计网
1. 概述
2. 物理层
3. 链路层
4. 网络层
5. 传输层
6. 应用层
7. 一些问题
8. 计网软件编程——Ethernet&ARP
网络技术沿着三条主线演变
广域网,城域网,局域网技术与TCP/IP协议伴随着Internet应用技术发展完善
TCP/IP参考模型分为四个层次
特点:
Internet 是通过路由器实现的多个广域网,城域网,局域网互联的大型网际网
传统的Internet应用:E-mail,Web,Telnet,FTP
新型应用:浏览器,HTML,搜索引擎,Java跨平台编程
Adhoc与无线传感器网络(WSN)作为网络技术研究与发展的另一条主线
无线分组网PRnet(Packet Radio net)
无线自组网Ad hoc
从是否需要基础设施角度,无线网络分为基于基础设施(蜂窝移动通信和移动IP通信)和无基础设施的网络
Ad hoc是一组带有无线通信收发设备的移动节点组成的多跳,临时和无中心的自治系统。网络中的移动节点本身具有路由和转发功能。
在Ad hoc网络技术基础上发展出了无线传感器网络WSN和无线网格网WMN
无线传感器网络WSN 通过无线通信方式形成的一个多跳的、自组织的 Ad hoc网格系统,目的是协作地感知、采集和处理网络覆盖区域中感知对象的信息,并发送给观察者
涉及:传感器技术、计算机网络技术、无线传输技术、嵌入式计算机技术、分布式信息处理、微电子、软件编程技术
无线网格网(WMN)的应用使得无线宽带接入Internet
远距离:802.16 WiMAX
技术,50km内提供高传输速率的服务
近距离:802.11
WLAN
WLAN接入点AP可以与邻近的WMN连接。
指电信运营商负责的通信网络中使用的技术,全球范围,传输技术涉及光纤传输,无线传输,卫星传输
主要研究远距离,宽带,高服务质量的核心交换技术
承担用户接入的任务
Ethernet为组建局域网的首选技术
IP协议直接与Ethernet帧接口
网络节点间发送数据都要将他放在帧的有效部分,分为一个或多个帧传送
TCP/IP支持多种不同的链路层协议,取决于网络所用的硬件(Ethernet,令牌环网,FDDI)
帧头部长度18B=目的地址长度6B+源地址6B+类型字段2B+校验字段4B
前导码由56位(7B)的 1010...10
比特序列组成,帧前定界符由 10101011
组成,即62位 10
后,11
之后便是帧的目的地址
前导码(同步作用) 接收电路从开始接收比特到进入稳定状态,需要一定的时间,设计前导码的目的是保证接收电路在帧的目的地址到来之前达到正常的接收状态
硬件地址称作MAC地址,地址长度(6B),格式十六进制 00-12-d3-a2-42-a8
为保证MAC地址的唯一性,有专门的组织负责为网卡的生产厂家分配MAC地址
单播地址:目的地址第一位为 0
表示单播地址。表示只能被目的地址匹配的主机接收
多播地址:目的地址第一位为 1
表示多播地址。表示能被一组节点接收
广播域:目的地址全 1
。表示可被同一冲突域的主机接收
表示网络层所用的协议
0X0800:IP协议
数据长度在46~1500B之间,如果数据少于46B需要补齐
帧检验字段FCS采用32位CRC校验:目的帧MAC,源MAC,类型,数据
在一个局域网中,同一时刻只有一台主机处于发送状态,其余都处于接收状态
对接收到的帧进行长度判断
若帧长度<64B,则丢弃,进入等待状态
检查目的帧地址
单播地址:且为本机地址,则接收该帧
组播地址:且本主机位于该组,则接收该帧
广播地址:接收该帧
对接收到的帧进行CRC校验
校验正确,则将帧中数据送往上层,报告“成功接收”
检验错误:
载波侦听多路访问(CDMA/CD):先听后说,边听边说,冲突停止,延迟重发
随机延迟重发
# include
# include //开发win32套接字头文件
# include //抓包
FILE *fp;
void packet_handler(u_char *param,
const struct pcap_pkthdr *header,
const u_char *pkt_data);
void pprint_mac(FILE *f, const u_char *p);
int main() {
printf("Hello World!\n");
SetDllDirectory((LPCWSTR)"C:\\Windows\\System32\\Npcap"); //system32下级目录
//在Linux中将设备看做文件,文件使用指针进行操作
pcap_if_t *alldevs;//设备链表
pcap_if_t *d;//指向某一设备的指针
int i = 0, inum;
char errbuf[PCAP_ERRBUF_SIZE];//保存返回的错误信息
/*1. 从本机获取设备列表,若未找到,则返回错误并退出*/
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,
NULL /* auth is not needed */,
&alldevs, errbuf) == -1) { //指针的指针
fprintf(stderr,
"Error in pcap_findalldevs_ex: %s\n",
errbuf);
exit(1);
}
/*2. 打印设备列表*/
for (d = alldevs; d != NULL; d = d->next){
printf("%d. %s\n", ++i, d->description);
/*打印ip*/
pcap_addr *a;
for (a = d->addresses; a; a = a->next) {
if (AF_INET == a->addr->sa_family) {//IPv4
//((sockaddr_in *)a->addr)->sin_addr; //通用套接字指针,需要进行转化
//sin_port端口号 输出in_32位整数
//inet_ntoa将整数的ipv4地址转换成字符串 有风险
printf(" %s\n", inet_ntoa(((sockaddr_in *)a->addr)->sin_addr));
}
}
/*
printf("%d. %s", ++i, d->name);//注册表中网卡名
if (d->description)
printf(" (%s)\n", d->description);//网卡描述
else
printf(" (No description available)\n");
*/
}
if (i == 0){
printf("\nNo interfaces found! Make sure Npcap is installed.\n");
return 0;
}
printf("\n");
/*3. 输入序号,查看是否有目标网卡*/
printf("Enter the interface number (1-%d):", i);
scanf_s("%d", &inum);
if (inum < 1 || inum > i){//检验网卡号合法性
printf("\nInterface number out of range.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
/*4. 将设备指针切换到目标网卡*/
for (d = alldevs, i = 0; i< inum - 1; d = d->next, i++);
/*获取设备句柄*/
pcap_t *adhandle;
if ((adhandle = pcap_open(d->name, // name of the device
65536, // portion of the packet to capture
// 65536 guarantees that the whole packet will
// be captured on all the link layers
PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
1000, // read timeout
NULL, // authentication on the remote machine
errbuf // error buffer
)) == NULL){
fprintf(stderr,
"\nUnable to open the adapter. %s is not supported by Npcap\n",
d->name);
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
printf("\nlistening on %s...\n", d->description);
printf("%s\n", d->name);
/*5. 打开文件,同步数据*/
fp = fopen("D:\\capture22.txt", "w");
/*捕包,解析*/
/* start the capture */
//需定义包处理函数
pcap_loop(adhandle, 0, packet_handler, NULL);
fclose(fp);
/* We don't need any more the device list. Free it */
pcap_freealldevs(alldevs);
return 0;
}
/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char *param,
const struct pcap_pkthdr *header,
const u_char *pkt_data){
//printf("%.6d len:%d\n",header->ts.tv_usec, header->len);
// header->caplen //实时捕获 ts:时间戳 len:打开文件才能查看
pprint_mac(fp, pkt_data);//目的mac
fprintf(fp, "<=");
pprint_mac(fp, pkt_data+6);//源mac
fprintf(fp, " 0X%02X%02X", pkt_data[12], pkt_data[13]);//协议类型
//0X0800表示IP协议
//输出数据长度
fprintf(fp, " %d\n", header->caplen);//报文长度,总长,包含以太网头部
fflush(fp);
}
void pprint_mac(FILE *f, const u_char *p) {
fprintf(f, "%02X-%02X-%02X-%02X-%02X-%02X", p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
}
同一局域网内的不同主机之间根据MAC直接进行通信
传输层/网络层传来的数据包只包含目的主机的IP地址
ARP(address resolution protocal):根据目的主机的IP地址获得其MAC地址,并保持映射关系
在拦截Ethernet报文的基础上添加 ARP
的过滤器
/*6. 添加过滤器,只获取ARP报文*/
//compile the filter 针对ARP协议过滤
bpf_program fcode;
bpf_u_int32 mask = ((sockaddr_in *)d->addresses)->sin_addr.S_un.S_addr;
//配置过滤器
char packet_filter[] = "ether proto \\arp";//要抓取的包的类型,这里是抓取ARP包
if (pcap_compile(adhandle, &fcode, packet_filter, 1, mask) < 0){
fprintf(stderr,
"\nUnable to compile the packet filter. Check the syntax.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
//为设备添加过滤器
if (pcap_setfilter(adhandle, &fcode) < 0){
fprintf(stderr, "\nError setting the filter.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
定义ARP请求报文结构
#pragma pack (1) //使结构体按1字节方式对齐
struct ethernet_head
{
unsigned char dest_mac[6]; //目标主机MAC地址
unsigned char source_mac[6]; //源端MAC地址
unsigned short eh_type; //以太网类型
};
struct arp_head
{
unsigned short hardware_type; //硬件类型:以太网接口类型为1
unsigned short protocol_type; //协议类型:IP协议类型为0X0800
unsigned char add_len; //硬件地址长度:MAC地址长度为6B
unsigned char pro_len; //协议地址长度:IP地址长度为4B
unsigned short option; //操作:ARP请求为1,ARP应答为2
unsigned char sour_addr[6]; //源MAC地址:发送方的MAC地址
unsigned long sour_ip; //源IP地址:发送方的IP地址
unsigned char dest_addr[6]; //目的MAC地址:ARP请求中该字段没有意义;ARP响应中为接收方的MAC地址
unsigned long dest_ip; //目的IP地址:ARP请求中为请求解析的IP地址;ARP响应中为接收方的IP地址
unsigned char padding[18];
};
struct arp_packet //最终arp包结构
{
ethernet_head eth; //以太网头部
arp_head arp; //arp数据包头部
};
#pragma pack() //恢复对齐方式
创建包构造函数
void BuildARPReqRequestPacket(const unsigned char *srcMAC, const unsigned long sour_ip,
const unsigned long dest_ip, struct arp_packet *pkt) {
memset(pkt->eth.dest_mac, 0xFF, 6);
memcpy(pkt->eth.source_mac, srcMAC, 6);
pkt->eth.eh_type = htons(0x0806);
pkt->arp.hardware_type = htons(1);
pkt->arp.protocol_type = htons(0x0800);
pkt->arp.add_len = 6;
pkt->arp.pro_len= 4;
pkt->arp.option = htons(1);
memcpy(pkt->arp.sour_addr, srcMAC, 6);
pkt->arp.sour_ip = sour_ip;// inet_addr("192.168.1.3");
memset(pkt->arp.dest_addr, 0, 6);//目的MAC
pkt->arp.dest_ip = dest_ip;//inet_addr("192.168.1.6");
memset(pkt->arp.padding, 0, 18);
}
轮询发送线程
DWORD WINAPI ARPSendThread(LPVOID p) {
MyParam * param = (MyParam *)p;
//本机IP,网络字节顺序
unsigned long localIP = param->ip;
//子网掩码,网络字节顺序
unsigned long netmask = param->mask;
//网络总主机数目,子网掩码按位取反即可
unsigned int netsize = ntohl(~netmask);
//子网地址,网络字节顺序
unsigned long net = localIP&netmask;
struct arp_packet pkt;
unsigned long ip = 0;
for (unsigned int i = 1; i<netsize; i++) {
//网络中第i台主机的子网内地址,网络字节顺序
unsigned long n = htonl(i);
//第i台主机的IP地址,网络字节顺序
unsigned long ip = net | n;
//此处出错 目的ip构造
BuildARPReqRequestPacket(param->mac, localIP, ip, &pkt);
pcap_sendpacket(param->handle, (const u_char *)&pkt, sizeof(pkt));
}
return 0;
}
启动线程
DWORD tid;
HANDLE h = CreateThread(0, 0, ARPSendThread, &myparam, 0, &tid);
unsigned short type = *(unsigned short*)&pkt_data[12];
if (htons(0x0806) != type) {
return;
}
arp_packet *p = (arp_packet *)&pkt_data[14];
unsigned short opcode = *(unsigned short*)&pkt_data[20];
if (opcode == htons(0X0002)) { //ARP response
printf("arp response\n");
print_mac(stdout, pkt_data);
printf("<=");
print_mac(stdout, pkt_data + 6);
struct in_addr inaddr;
inaddr.S_un.S_addr = *(unsigned long long *)&pkt_data[28];
printf(":%s\n", inet_ntoa(inaddr));
}
解释一下为什么是
pkt_data[20]
如图,以太网帧占14B(除去前导码和帧界定符),ARP请求报文实际从第15B开始,也就是 pkt_data[14]
而ARP首部占6B,所以从第21B开始,即 pkt_data[20]
*(unsigned long long *)&pkt_data[28]
从 pkt_data[28]
开始,取 32 bit 的二进制序列
mylib.h
#pragma once
//可被多次包含,但只被定义一次
#ifndef _MYLIB_H_
#define _MYLIB_H_
# include
# include
#pragma pack (1) //使结构体按1字节方式对齐
struct ethernet_head
{
unsigned char dest_mac[6]; //目标主机MAC地址
unsigned char source_mac[6]; //源端MAC地址
unsigned short eh_type; //以太网类型
};
struct arp_head
{
unsigned short hardware_type; //硬件类型:以太网接口类型为1
unsigned short protocol_type; //协议类型:IP协议类型为0X0800
unsigned char add_len; //硬件地址长度:MAC地址长度为6B
unsigned char pro_len; //协议地址长度:IP地址长度为4B
unsigned short option; //操作:ARP请求为1,ARP应答为2
unsigned char sour_addr[6]; //源MAC地址:发送方的MAC地址
unsigned long sour_ip; //源IP地址:发送方的IP地址
unsigned char dest_addr[6]; //目的MAC地址:ARP请求中该字段没有意义;ARP响应中为接收方的MAC地址
unsigned long dest_ip; //目的IP地址:ARP请求中为请求解析的IP地址;ARP响应中为接收方的IP地址
unsigned char padding[18];
};
struct arp_packet //最终arp包结构
{
ethernet_head eth; //以太网头部
arp_head arp; //arp数据包头部
};
#pragma pack() //恢复对齐方式
// ifname :接口名字
// mac:输出获取到的mac地址
bool get_mac(const char *ifname, unsigned char *mac);
//向文件指针,从偏移字节开始输出字节
void print_mac(FILE *fp, const unsigned char *p);
void BuildARPReqRequestPacket(const unsigned char *srcMAC, const unsigned long sour_ip,
const unsigned long dest_ip, struct arp_packet *pkt);
#endif
mylib.cpp
# include "mylib.h"
#include
//arp头文件
#include
#include
//不生效# define _WINSOCK_DEPRECATED_NO_WARNINGS 1
void print_mac(FILE *f, const u_char *p) {
fprintf(f, "%02X-%02X-%02X-%02X-%02X-%02X", p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
}
bool get_mac(const char *ifname, unsigned char *mac) {
LPADAPTER lpAdapter = PacketOpenAdapter(ifname);
if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE)) {
return false;//打开失败,返回错误
}
PPACKET_OID_DATA OidData = (PPACKET_OID_DATA)malloc(6 + sizeof(PACKET_OID_DATA));
if (OidData == NULL) {
PacketCloseAdapter(lpAdapter);
return false;
}
OidData->Oid = OID_802_3_CURRENT_ADDRESS;
OidData->Length = 6;
ZeroMemory(OidData->Data, 6);
BOOLEAN Status = PacketRequest(lpAdapter, FALSE, OidData);
if (Status) {
memcpy(mac, OidData->Data, 6);
printf("The MAC address of the adapter is %.2x-%.2x-%.2x-%.2x-%.2x-%.2x\n",
(PCHAR)(OidData->Data)[0],
(PCHAR)(OidData->Data)[1],
(PCHAR)(OidData->Data)[2],
(PCHAR)(OidData->Data)[3],
(PCHAR)(OidData->Data)[4],
(PCHAR)(OidData->Data)[5]);
free(OidData);
PacketCloseAdapter(lpAdapter);
return true;
}
else {
printf("error retrieving the MAC address of the adapter!\n");
free(OidData);
PacketCloseAdapter(lpAdapter);
return false;
}
}
void BuildARPReqRequestPacket(const unsigned char *srcMAC, const unsigned long sour_ip,
const unsigned long dest_ip, struct arp_packet *pkt) {
memset(pkt->eth.dest_mac, 0xFF, 6);
memcpy(pkt->eth.source_mac, srcMAC, 6);
pkt->eth.eh_type = htons(0x0806);
pkt->arp.hardware_type = htons(1);
pkt->arp.protocol_type = htons(0x0800);
pkt->arp.add_len = 6;
pkt->arp.pro_len= 4;
pkt->arp.option = htons(1);
memcpy(pkt->arp.sour_addr, srcMAC, 6);
pkt->arp.sour_ip = sour_ip;// inet_addr("192.168.1.3");
memset(pkt->arp.dest_addr, 0, 6);//目的MAC
pkt->arp.dest_ip = dest_ip;//inet_addr("192.168.1.6");
memset(pkt->arp.padding, 0, 18);
}
ARPScan.cpp
# include "mylib.h"
#include
//arp头文件
#include
#include
//不生效# define _WINSOCK_DEPRECATED_NO_WARNINGS 1
void print_mac(FILE *f, const u_char *p) {
fprintf(f, "%02X-%02X-%02X-%02X-%02X-%02X", p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
}
bool get_mac(const char *ifname, unsigned char *mac) {
LPADAPTER lpAdapter = PacketOpenAdapter(ifname);
if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE)) {
return false;//打开失败,返回错误
}
PPACKET_OID_DATA OidData = (PPACKET_OID_DATA)malloc(6 + sizeof(PACKET_OID_DATA));
if (OidData == NULL) {
PacketCloseAdapter(lpAdapter);
return false;
}
OidData->Oid = OID_802_3_CURRENT_ADDRESS;
OidData->Length = 6;
ZeroMemory(OidData->Data, 6);
BOOLEAN Status = PacketRequest(lpAdapter, FALSE, OidData);
if (Status) {
memcpy(mac, OidData->Data, 6);
printf("The MAC address of the adapter is %.2x-%.2x-%.2x-%.2x-%.2x-%.2x\n",
(PCHAR)(OidData->Data)[0],
(PCHAR)(OidData->Data)[1],
(PCHAR)(OidData->Data)[2],
(PCHAR)(OidData->Data)[3],
(PCHAR)(OidData->Data)[4],
(PCHAR)(OidData->Data)[5]);
free(OidData);
PacketCloseAdapter(lpAdapter);
return true;
}
else {
printf("error retrieving the MAC address of the adapter!\n");
free(OidData);
PacketCloseAdapter(lpAdapter);
return false;
}
}
void BuildARPReqRequestPacket(const unsigned char *srcMAC, const unsigned long sour_ip,
const unsigned long dest_ip, struct arp_packet *pkt) {
memset(pkt->eth.dest_mac, 0xFF, 6);
memcpy(pkt->eth.source_mac, srcMAC, 6);
pkt->eth.eh_type = htons(0x0806);
pkt->arp.hardware_type = htons(1);
pkt->arp.protocol_type = htons(0x0800);
pkt->arp.add_len = 6;
pkt->arp.pro_len= 4;
pkt->arp.option = htons(1);
memcpy(pkt->arp.sour_addr, srcMAC, 6);
pkt->arp.sour_ip = sour_ip;// inet_addr("192.168.1.3");
memset(pkt->arp.dest_addr, 0, 6);//目的MAC
pkt->arp.dest_ip = dest_ip;//inet_addr("192.168.1.6");
memset(pkt->arp.padding, 0, 18);
}