linux sockraw原始套接字编程.docx
- 文档编号:6194928
- 上传时间:2023-01-04
- 格式:DOCX
- 页数:11
- 大小:18.99KB
linux sockraw原始套接字编程.docx
《linux sockraw原始套接字编程.docx》由会员分享,可在线阅读,更多相关《linux sockraw原始套接字编程.docx(11页珍藏版)》请在冰豆网上搜索。
linuxsockraw原始套接字编程
linuxsock_raw原始套接字编程
sock_raw原始套接字编程可以接收到本机网卡上的数据帧或者数据包,对与监听网络的流量和分析是很有作用的.一共可以有3种方式创建这种socket
1.socket(AF_INET,SOCK_RAW,IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包
2.socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧
3.socket(AF_INET,SOCK_PACKET,htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))过时了,不要用啊
理解一下SOCK_RAW的原理,比如网卡收到了一个14+20+8+100+4的udp的以太网数据帧.
首先,网卡对该数据帧进行硬过滤(根据网卡的模式不同会有不同的动作,如果设置了promisc混杂模式的话,则不做任何过滤直接交给下一层输入例程,否则非本机mac或者广播mac会被直接丢弃).按照上面的例子,如果成功的话,会进入ip输入例程.但是在进入ip输入例程之前,系统会检查系统中是否有通过socket(AF_PACKET,SOCK_RAW,..)创建的套接字.如果有的话并且协议相符,在这个例子中就是需要ETH_P_IP或者ETH_P_ALL类型.系统就给每个这样的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步.
其次,进入了ip输入例程(ip层会对该数据包进行软过滤,就是检查校验或者丢弃非本机ip或者广播ip的数据包等,具体要参考源代码),例子中就是如果成功的话会进入udp输入例程.但是在交给udp输入例程之前,系统会检查系统中是否有通过socket(AF_INET,SOCK_RAW,..)创建的套接字.如果有的话并且协议相符,在这个例子中就是需要IPPROTO_UDP类型.系统就给每个这样的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步.
最后,进入udp输入例程...
ps:
如果校验和出错的话,内核会直接丢弃该数据包的.而不会拷贝给sock_raw的套接字,因为校验和都出错了,数据肯定有问题的包括所有信息都没有意义了.
进一步分析他们的能力.
1.socket(AF_INET,SOCK_RAW,IPPROTO_UDP);
能:
该套接字可以接收协议类型为(tcpudpicmp等)发往本机的ip数据包,从上面看的就是20+8+100.
不能:
不能收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包).
不能:
不能收到从本机发送出去的数据包.
发送的话需要自己组织tcpudpicmp等头部.可以setsockopt来自己包装ip头部
这种套接字用来写个ping程序比较适合
2.socket(PF_PACKET,SOCK_RAW,htons(x));
这个套接字比较强大,创建这种套接字可以监听网卡上的所有数据帧.从上面看就是20+20+8+100.最后一个以太网crc从来都不算进来的,因为内核已经判断过了,对程序来说没有任何意义了.
能:
接收发往本地mac的数据帧
能:
接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)
能:
接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)
协议类型一共有四个
ETH_P_IP0x800只接收发往本机mac的ip类型的数据帧
ETH_P_ARP0x806只接受发往本机mac的arp类型的数据帧
ETH_P_ARP0x8035只接受发往本机mac的rarp类型的数据帧
ETH_P_ALL0x3接收发往本机mac的所有类型iparprarp的数据帧,接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)
发送的时候需要自己组织整个以太网数据帧.所有相关的地址使用structsockaddr_ll而不是structsockaddr_in(因为协议簇是PF_PACKET不是AF_INET了),比如发送给某个机器,对方的地址需要使用structsockaddr_ll.
这种socket大小通吃,强悍
下面是一段相关的代码:
...
intsockfd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
structsockaddr_llsll;
memset(&sll,0,sizeof(sll));
sll.sll_family=AF_PACKET;
structifreqifstruct;
strcpy(ifstruct.ifr_name,"eth0");
ioctl(sockfd,SIOCGIFINDEX,&ifstruct);
sll.sll_ifindex=ifstruct.ifr_ifindex;
sll.sll_protocol=htons(ETH_P_ALL);
if(bind(fd,(structsockaddr*)&sll,sizeof(sll))==-1){
perror("bind()");
}
intset_promisc(char*interface,intfd){
structifreqifr;
strcpy(ifr.ifr_name,interface);
if(ioctl(fd,SIOCGIFFLAGS,&ifr)==-1){
perror("iotcl()");
return-1;
}
ifr.ifr_flags|=IFF_PROMISC;
if(ioctl(fd,SIOCSIFFLAGS,&ifr)==-1){
perror("iotcl()");
return-1;
}
return0;
}
intunset_promisc(char*interface,intfd){
structifreqifr;
strcpy(ifr.ifr_name,interface);
if(ioctl(fd,SIOCGIFFLAGS,&ifr)==-1){
perror("iotcl()");
return-1;
}
ifr.ifr_flags&=~IFF_PROMISC;
if(ioctl(fd,SIOCSIFFLAGS,&ifr)==-1){
perror("iotcl()");
return-1;
}
return0;
}
3.socket(AF_INET,SOCK_PACKET,htons(ETH_P_ALL))这个最好不要用,反正我不用...
总结使用方法:
1.只想收到发往本机某种协议的ip数据包的话用第一种就足够了
2.更多的详细的内容请使用第二种.包括ETH_P_ALL参数和混杂模式都可以使它的能力不断的加强.
ps:
很多自己的想法.虚拟机测试环境.有错欢迎指出交流
qq:
110024218
我写的ping
#include"stdio.h"
#include"stdlib.h"
#include"string.h"
#include"unistd.h"
#include"sys/types.h"
#include"sys/socket.h"
#include"netinet/in.h"
#include"netinet/ip.h"
#include"netinet/ip_icmp.h"
#include"netdb.h"
#include"errno.h"
#include"arpa/inet.h"
#include"signal.h"
#include"sys/time.h"
externinterrno;
intsockfd;
structsockaddr_inaddr;//peeraddr
charstraddr[128];//peeraddrip(char*)
charsendbuf[2048];
charrecvbuf[2048];
intsendnum;
intrecvnum;
intdatalen=30;
unsignedshortmy_cksum(unsignedshort*data,intlen){
intresult=0;
for(inti=0;i result+=*data; data++; } while(result>>16)result=(result&0xffff)+(result>>16); return~result; } voidtv_sub(structtimeval*recvtime,conststructtimeval*sendtime){ intsec=recvtime->tv_sec-sendtime->tv_sec; intusec=recvtime->tv_usec-sendtime->tv_usec; if(usec>=0){ recvtime->tv_sec=sec; recvtime->tv_usec=usec; }else{ recvtime->tv_sec=sec-1; recvtime->tv_usec=-usec; } } voidsend_icmp(){ structicmp*icmp=(structicmp*)sendbuf; icmp->icmp_type=ICMP_ECHO; icmp->icmp_code=0; icmp->icmp_cksum=0; icmp->icmp_id=getpid();//needn'tusehtons()call,becausepeernetworkingkerneldidn'thandlethisdataandwon'tmakedifferentmeanings(bigdianlitteldian) icmp->icmp_seq=++sendnum;//needn'tusehotns()calltoo. gettimeofday((structtimeval*)icmp->icmp_data,NULL); intlen=8+datalen; icmp->icmp_cksum=my_cksum((unsignedshort*)icmp,len); intretval=sendto(sockfd,sendbuf,len,0,(structsockaddr*)&addr,sizeof(addr)); if(retval==-1){ perror("sendto()"); exit(-1); }else{ //printf("sendicmprequestto%s(%d)bytes\n",straddr,len); } } voidrecv_icmp(){ structtimeval*sendtime; structtimevalrecvtime; for(;;){ intn=recvfrom(sockfd,recvbuf,sizeof(recvbuf),0,0,0); if(n==-1){ if(errno==EINTR)continue; else{ perror("recvfrom()"); exit(-1); } }else{ gettimeofday(&recvtime,NULL); structip*ip=(structip*)recvbuf; if(ip->ip_src.s_addr! =addr.sin_addr.s_addr){ //printf("ip_srcisnot: %s\n",straddr); continue; } structicmp*icmp=(structicmp*)(recvbuf+((ip->ip_hl)<<2)); if(icmp->icmp_id! =getpid()){ //printf("icmp_idisnot: %d\n",getpid()); continue; } recvnum++; sendtime=(structtimeval*)icmp->icmp_data; tv_sub(&recvtime,sendtime); printf("imcpechofrom%s(%dbytes)\tttl=%d\tseq=%d\ttime=%d.%06ds\n",straddr,n,ip->ip_ttl,icmp->icmp_seq,recvtime.tv_sec,recvtime.tv_usec); } } } voidcatch_sigalrm(intsignum){ send_icmp(); alarm (1); } voidcatch_sigint(intsignum){ printf("\nPingstatics: send%dpackets,recv%dpackets,%d%%lost...\n",sendnum,recvnum,(int)((float)(sendnum-recvnum)/sendnum)*100); exit(0); } intmain(intargc,char**argv){ if(argc! =2){ printf("pleaseuseformat: pinghostname\n"); exit(-1); } sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); if(sockfd==-1){ perror("socket()"); return-1; } /* intsendbufsize=180; socklen_tsendbufsizelen=sizeof(sendbufsize); if(setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&sendbufsize,sendbufsizelen)==-1)perror("setsockopt()"); intrecvbufsize; socklen_trecvbufsizelen; if(getsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&recvbufsize,&recvbufsizelen)==-1)perror("getsockopt()"); */ memset(&addr,0,sizeof(addr)); addr.sin_family=AF_INET; intretval=inet_pton(AF_INET,argv[1],&addr.sin_addr); if(retval==-1||retval==0){ structhostent*host=gethostbyname(argv[1]); if(host==NULL){ fprintf(stderr,"gethostbyname(%s): %s\n",argv[1],strerror(errno)); exit(-1); } /* if(host->h_name! =NULL)printf("hostent.h_name: %s\n",host->h_name); if(host->h_aliases! =NULL&&*(host->h_aliases)! =NULL)printf("hostent.h_aliases: %s\n",*(host->h_aliases)); printf("hostent.h_addrtype: %d\n",host->h_addrtype); printf("hostent.h_length: %d\n",host->h_length); */ if(host->h_addr_list! =NULL&&*(host->h_addr_list)! =NULL){ strncpy((char*)&addr.sin_addr,*(host->h_addr_list),4); inet_ntop(AF_INET,*(host->h_addr_list),straddr,sizeof(straddr)); } printf("Pingaddress: %s(%s)\n\n",host->h_name,straddr); }else{ strcpy(straddr,argv[1]); printf("Pingaddress: %s(%s)\n\n",straddr,straddr); } structsigactionsa1; memset(&sa1,0,sizeof(sa1)); sa1.sa_handler=catch_sigalrm; sigemptyset(&sa1.sa_mask); sa1.sa_flags=0; if(sigaction(SIGALRM,&sa1,NULL)==-1)perror("sigaction()"); structsigactionsa2; memset(&sa2,0,sizeof(sa2)); sa2.sa_handler=catch_sigint; sigemptyset(&sa2.sa_mask); sa2.sa_flags=0; if(sigaction(SIGINT,&sa2,NULL)==-1)perror("sigaction()"); alarm (1); recv_icmp(); return0; }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux sockraw原始套接字编程 sockraw 原始 套接 编程