网络协议栈设计(四)---链路层以太网代码实现(发送)
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); }
好,至此我们就实现了我们上一篇所分析的所有函数,成功完成了以太网数据帧的封装和发送。下一篇,我们分析以太网数据帧的接收。
相关文章推荐
- 网络协议栈设计(六)---链路层以太网代码实现(接收)
- 网络协议栈设计(八)---网络层ARP协议代码实现(发送)
- ASP.NET设计网络硬盘之查看文件夹实现代码
- ASP.NET设计网络硬盘之删除文件夹实现代码
- ASP.NET设计网络硬盘之下载或在线查看实现代码
- ASP.NET设计网络硬盘之上传文件实现代码
- ASP.NET设计网络硬盘之下载或在线查看实现代码
- Linux 2.4.x 网络协议栈QoS模块(TC)的设计与实现(转)
- ASP.NET设计网络硬盘之删除文件夹实现代码
- ASP.NET设计网络硬盘之查看文件夹实现代码
- Linux 2.4.x 网络协议栈QoS模块(TC)的设计与实现
- Linux 2.4.x 网络协议栈QoS模块(TC)的设计与实现
- Linux 网络协议栈开发代码分析篇之VLAN(二)—— Linux下VLAN功能的实现概述
- Linux 2.4.x 网络协议栈QoS模块(TC)的设计与实现
- 关于用Java实现发送邮件(部分代码参考网络来源)
- Linux 2.4.x内核中网络协议栈QoS模块(TC)的设计与实现
- Linux 2.4.x 网络协议栈QoS模块(TC)的设计与实现
- Linux 2.4.x 网络协议栈QoS模块(TC)的设计与实现
- Linux网络协议栈QoS模块(TC)的设计与实现
- Linux 2.4.x 网络协议栈QoS模块(TC)的设计与实现