在linux環境中要從鏈路層(MAC)直接收發資料幀,可以通過libpcap與libnet兩個動態庫來分別完成收與發的工作。雖然它已被廣泛使用,但在要求進行跨平台移植的軟體中使用就很難辦到了。。
這是一種更為直接地、無須安裝其它庫的從MAC層收發資料幀的方式,即通過定義鏈路層的通訊端來完成。
下面的代碼也是我做的項目中的代碼(誇網段訪問網路中的嵌入式裝置),去掉了敏感部分,和大家共用。
但是得尊重別人的勞動成果,轉載註明出處,謝謝。
#include <string.h> #include <stdio.h> #include <unistd.h>#include <stdlib.h>#include <errno.h> #include <pthread.h>#include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <linux/if_packet.h>#include <arpa/inet.h>/*鏈路層socket接收埠*/#define RAW_PROTOCOL 0x0909#define ETH_NAME "eth0"#define __DEBUG#ifdef __DEBUG#define DBG(fmt,args...) fprintf(stdout, fmt, ##args)#else#define DBG(fmt,args...)#endif#define ERR(fmt,args...) fprintf(stderr, fmt, ##args)static int raw_fd;static int if_index;static int isSearchQuit = 0;unsigned char my_mac[6];/*用於本機儲存網卡地址*//*接收鏈路層資料包*/int GetPacket(unsigned char *buf, int *len){int length = 0;length = recvfrom( raw_fd, buf, 2000, 0, NULL, NULL );if ( length < 0 ){ERR("failed to receive buf!");return -1;}else{*len = length;return 0;}}/*發送鏈路層資料包*/int SendPacket(unsigned char *buf, int len){struct sockaddr_ll link;link.sll_ifindex = if_index;memcpy( link.sll_addr, buf, link.sll_halen );if ( sendto( raw_fd, buf, len, 0, (struct sockaddr *)&link, sizeof(link) ) < 0 ){ERR( "failed to send to RAW socket!\r\n" );return -1;}return 0;}void ShowData(unsigned char *d1,int len){int i;for(i=0;i<len;i++)printf("%02x ",d1[i]);printf("\n\r");}void *RawSocketThread(void *arg){unsigned char rcvPackage[70];unsigned char sndPackage[70];int len;int curStatus = 0;while(1){int ret = 0;int done = 0;ret = GetPacket(rcvPackage,&len);//block hereif(ret == -1)break;ShowData(rcvPackage,len);sleep(1);ret = SendPacket(rcvPackage,len);if(ret ==-1)break;}pthread_exit(NULL);close(raw_fd);}int RawSocketInit(){pthread_t tRawSocketThr;struct ifreq req;/*建立鏈路層socket,注意PF_PACKET,SOCK_RAW,以及RAW_PROTOCOL(自訂的)*/if ( (raw_fd = socket(PF_PACKET, SOCK_RAW, htons( RAW_PROTOCOL ) ) ) < 0 ){ERR( "failed to create raw socket!\n" );return -1;}/*綁定網卡*/strcpy( req.ifr_name, ETH_NAME );if ( ioctl( raw_fd, SIOCGIFFLAGS, &req ) < 0 ){ERR( "failed to do ioctl!" );return -1;}/*設定工作模式*/req.ifr_flags |= IFF_PROMISC;if ( ioctl( raw_fd, SIOCSIFFLAGS, &req ) < 0 ){ERR( "failed to set eth0 into promisc mode!" );return -1;}if ( ioctl( raw_fd, SIOCGIFHWADDR, &req ) < 0 ){ERR( "failed to get interface hw address!" );return -1;}memcpy( my_mac, req.ifr_hwaddr.sa_data, sizeof(my_mac) );if ( ioctl( raw_fd, SIOCGIFINDEX, &req ) < 0 ){ERR( "failed to get interface index!" );return -1;}if_index = req.ifr_ifindex;if(pthread_create(&tRawSocketThr,NULL,RawSocketThread,NULL) == -1){ERR("ERROR in RawSocketThread\n");return -1;}pthread_detach(tIPNCSearchThr);return 0;}#if 1int main(){int count =0;RawSocketInit();while(1){sleep(1);}}#endif
運行結果,接收自網路中的完整資料包:
[root@localhost src]# ./RawSocketff ff ff ff ff ff 1c 6f 65 dc fa fb 09 09 00 00 00 00 06 04 01 00 00 00 00 00 00 00 c0 a8 01 dc ff ff ff ff ff ff c0 a8 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00