您的位置:首页 > 理论基础 > 计算机网络

网络协议栈设计(四)---链路层以太网代码实现(发送)

2017-04-12 00:20 375 查看

网络协议栈设计(四)

//结合上一篇的分析,我们来具体实现上一篇的函数,再完善上一节中的细节

//**Ethernet.h**

#pragma once
#include "Header_Include.h"
//包含打包的常用头文件,第二篇中已经说明,今后不再注释

//ethernet header
struct ethernet_header
{
u_int8_t destination_mac[6];
u_int8_t source_mac[6];
u_int16_t ethernet_type;
};

//calculate CRC  计算CRC校验码需要先生成一个crc_table,根据crc_table计算校验码
void generate_crc32_table();
u_int32_t calculate_crc(u_int8_t *buffer, int len);

//loading buffer
void load_ethernet_header(u_int8_t *destination_mac, u_int16_t ethernet_type);
int load_ethernet_data(u_int8_t *buffer, u_int8_t *upper_buffer, int len);

//send packet
int ethernet_send_packet(u_int8_t *upper_buffer, u_int8_t *destination_mac, u_int16_t ethernet_type);

void open_device();
void close_device();
//这两个函数大家不用关心,老师提供的,在main函数开头调用open_device(),打开网卡获取网卡句柄给handle
//main函数结束时,调用close_device()关闭网卡


//***Ethernet.cpp***

#include "Ethernet.h"
#include "Resource.h"

u_int32_t crc32_table[256] = { 0 };

u_int8_t buffer[MAX_SIZE];
u_int32_t size_of_packet = 0;    //以太网帧长度,初始化位0,加载过程中动态修改
/*因为调用winpcap中的数据发送函数需要提供一个数据帧的长度,所以我们定义一个全局变量来保存我们加载到buffer[MAX_SIZE]中的数据总长度*/

extern pcap_t *handle;    //网卡句柄,这个由open_devive()函数得到

extern u_int8_t local_mac[6];
extern int ethernet_upper_len;
extern u_int8_t * dest_mac;
/*目的MAC由全局变量提供,它可以由查询ARP缓存表或者发送ARP请求解析得到,上层发送数据前会修改这个指针指向目的MAC地址*/

//初始化crc32_table
void generate_crc32_table()
{
int i, j;
u_int32_t crc;
for (i = 0; i < 256; i++)
{
crc = i;
for (j = 0; j < 8; j++)
{
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320;
else
crc >>= 1;
}
crc32_table[i] = crc;
}
}

//根据crc_table计算提供的数据的CRC校验码,返回32位校验码
u_int32_t calculate_crc(u_int8_t *buffer, int len)
{
int i;
u_int32_t crc;
crc = 0xffffffff;
for (i = 0; i < len; i++)
{
crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ buffer[i]];
}
crc ^= 0xffffffff;
return crc;
}

//加载以太网帧头部到buffer[]中
void load_ethernet_header(u_int8_t *destination_mac,u_int16_t ethernet_type)
{
struct ethernet_header *hdr = (struct ethernet_header *)buffer;
size_of_packet = 0;
// add destination mac address
int i = 0;
for(i = 0;i < 6;i++)
{
hdr->destination_mac[i] = destination_mac[i];
}

//add source mac address
for(i = 0;i < 6;i++)
{
hdr->source_mac[i] = local_mac[i];
}

// add source typy
hdr->ethernet_type = htons(ethernet_type);

// caculate the size_of_packet now
size_of_packet += sizeof(ethernet_header);
}

//加载以太网数据部分到buffer[]中
int load_ethernet_data(u_int8_t *buffer, u_int8_t *upper_buffer, int len)
{
if (len > 1500)
{
printf("IP buffer is too large. So we stop the procedure.");
return -1;
}

int i;
for (i = 0; i < len; i++)
{
*(buffer + i) = *(upper_buffer + i);
}

//add a serial 0 at the end
while (len < 46)
{
*(buffer + len) = 0;
len++;
}

//generate_crc32_table();
u_int32_t crc = calculate_crc(buffer - sizeof( ethernet_header), len + sizeof(struct ethernet_header));

*(u_int32_t *)(buffer + len) = crc;

//帧的总长度再加上数据长度和4个字节crc校验码
size_of_packet += len + 4;
return 1;
}

//上层调用的接口函数,上层提供帧数据部分,目的MAC地址及以太网上层协议类型
int ethernet_send_packet(u_int8_t *upper_buffer,u_int8_t *destination_mac,u_int16_t ethernet_type)
{
//根据上层提供的参数加载首部
load_ethernet_header(destination_mac, ethernet_type);

//根据上层提供的数据加载MAC数据部分,若返回-1,则加载失败直接返回
if(load_ethernet_data(buffer + sizeof(struct ethernet_header), upper_buffer, ethernet_upper_len) == -1)  return —1;

//调用winpcap数据发送函数pcap_sendpacket()发送数据
if (pcap_sendpacket(handle, (const u_char *)buffer, size_of_packet) != 0)
{
printf("Sending failed..\n");
return -1;
}
else
{
printf("Sending Succeed..\n");
return 1;
}
}

//以下两个函数知道具体的作用即可,由老师提供,大家不用关心具体实现
void open_device()//打开网卡
{
extern  char *device;
char error_buffer[PCAP_ERRBUF_SIZE];
/*device = pcap_lookupdev(error_buffer);
handle = pcap_open_live(device, 65536, 1, 1000, error_buffer);*/

//pcap_t *adhandle;
pcap_if_t *alldevs;
pcap_if_t *d;
int i = 0;
int inum;

// get the all network adapter handle

if (pcap_findalldevs(&alldevs, error_buffer) == -1)
{
printf("%s\n", error_buffer);
return ;
}

/* Print the list of all network adapter information */
for (d = alldevs; d; d = d->next)
{
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 WinPcap is installed.\n");
return ;
}

printf("Enter the interface number (1-%d):", i);
scanf("%d", &inum);
if (inum < 1 || inum > i)
{
printf("\nInterface number out of range.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return ;
}

/* Jump to the selected adapter */
for (d = alldevs, i = 0; i< inum - 1; d = d->next, i++);

/* Open the adapter */
if ((handle = pcap_open_live(d->name, // name of the device
65536, // portion of the packet to capture.65536 grants that the whole packet will be captured on/// all the MACs.
1, // promiscuous mode
1000, // read timeout
error_buffer // error buffer
)) == NULL)
{
printf("\nUnable to open the adapter. %s is not supported by WinPcap\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return ;
}

/* Check the link layer. We support only Ethernet for simplicity. */
if (pcap_datalink(handle) != DLT_EN10MB)
{
fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return ;
}

//handle = pcap_open_live(device, 65536, 1, 1000, error_buffer);

generate_crc32_table();
}
void close_device() //关闭网卡
{
pcap_close(handle);
}


好,至此我们就实现了我们上一篇所分析的所有函数,成功完成了以太网数据帧的封装和发送。下一篇,我们分析以太网数据帧的接收。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: