《linux sock_raw原始套接字编程.docx》由会员分享,可在线阅读,更多相关《linux sock_raw原始套接字编程.docx(11页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、linux sock_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
2、|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
3、类型.系统就给每个这样的socket接收缓 冲区发送一个数据帧拷贝.然后进入下一步. 其次,进入了ip输入例程(ip层会对该数据包进行软过滤,就是检查校验或者丢弃非本机ip或者广播ip的数据包等,具体要参考源代码),例子 中就是如果成功的话会进入udp输入例程.但是在交给udp输入例程之前,系统会检查系统中是否有通过socket(AF_INET, SOCK_RAW, .)创建的套接字.如果有的话并且协议相符,在这个例子中就是需要IPPROTO_UDP类型.系统就给每个这样的socket接收缓冲区发送一个数据 帧拷贝.然后进入下一步. 最后,进入udp输入例程 . ps:如果校验和出错的话,内核
4、会直接丢弃该数据包的.而不会拷贝给sock_raw的套接字,因为校验和都出错了,数据肯定有问题的包括所有信息都没有意义了. 进一步分析他们的能力.1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);能:该套接字可以接收协议类型为(tcp udp icmp等)发往本机的ip数据包,从上面看的就是20+8+100.不能:不能收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包).不能:不能收到从本机发送出去的数据包.发送的话需要自己组织tcp udp icmp等头部.可以setsockopt来自己包装ip头部这种套接字用来写个ping程序比较适合
5、 2. socket(PF_PACKET, SOCK_RAW, htons(x); 这个套接字比较强大,创建这种套接字可以监听网卡上的所有数据帧.从上面看就是20+20+8+100.最后一个以太网crc从来都不算进来的,因为内核已经判断过了,对程序来说没有任何意义了.能: 接收发往本地mac的数据帧能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)协议类型一共有四个ETH_P_IP 0x800 只接收发往本机mac的ip类型的数据帧ETH_P_ARP 0x806 只接受发往本机mac的arp类型的
6、数据帧ETH_P_ARP 0x8035 只接受发往本机mac的rarp类型的数据帧ETH_P_ALL 0x3 接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧) 发送的时候需要自己组织整个以太网数据帧.所有相关的地址使用struct sockaddr_ll 而不是struct sockaddr_in(因为协议簇是PF_PACKET不是AF_INET了),比如发送给某个机器,对方的地址需要使用struct sockaddr_ll. 这种socket大小通吃,强悍下面是一段相关的代码: . in
7、t sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL); struct sockaddr_ll sll; memset( &sll, 0, sizeof(sll) ); sll.sll_family = AF_PACKET; struct ifreq ifstruct; strcpy(ifstruct.ifr_name, eth0); ioctl(sockfd, SIOCGIFINDEX, &ifstruct); sll.sll_ifindex = ifstruct.ifr_ifindex; sll.sll_protocol = hton
8、s(ETH_P_ALL); if(bind(fd, (struct sockaddr *) &sll, sizeof(sll) = -1 ) perror(bind(); int set_promisc(char *interface, int fd) struct ifreq ifr; strcpy(ifr.ifr_name, interface); if(ioctl(fd, SIOCGIFFLAGS, &ifr) = -1) perror(iotcl(); return -1; ifr.ifr_flags |= IFF_PROMISC; if(ioctl(fd, SIOCSIFFLAGS,
9、 &ifr) = -1) perror(iotcl(); return -1; return 0; int unset_promisc(char *interface, int fd) struct ifreq ifr; 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(); retur
10、n -1; return 0; 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/typ
11、es.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 extern int errno; int sockfd;struct sockaddr_in addr; /peer addrchar straddr128; /peer addr ip(char*)char sendbuf2048;
12、char recvbuf2048;int sendnum;int recvnum;int datalen = 30; unsigned short my_cksum(unsigned short *data, int len) int result = 0; for(int i=0; i 16)result = (result&0xffff) + (result16); return result;void tv_sub(struct timeval* recvtime, const struct timeval* sendtime) int sec = recvtime-tv_sec - s
13、endtime-tv_sec; int usec = 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; void send_icmp() struct icmp* icmp = (struct icmp*)sendbuf; icmp-icmp_type = ICMP_ECHO; icmp-icmp_code = 0; icmp-icmp_c
14、ksum = 0; icmp-icmp_id = getpid(); /neednt use htons() call, because peer networking kernel didnt handle this data and wont make different meanings(bigdian litteldian) icmp-icmp_seq = +sendnum; /neednt use hotns() call too. gettimeofday(struct timeval*)icmp-icmp_data, NULL); int len = 8+datalen; icm
15、p-icmp_cksum = my_cksum(unsigned short*)icmp, len); int retval = sendto(sockfd, sendbuf, len, 0, (struct sockaddr*)&addr, sizeof(addr); if(retval = -1) perror(sendto(); exit(-1); else / printf(send icmp request to %s(%d) bytesn, straddr, len); void recv_icmp() struct timeval *sendtime; struct timeva
16、l recvtime; for(;) int n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, 0, 0); if(n = -1) if(errno = EINTR)continue; else perror(recvfrom(); exit(-1); else gettimeofday(&recvtime, NULL); struct ip *ip = (struct ip*)recvbuf; if(ip-ip_src.s_addr != addr.sin_addr.s_addr) / printf(ip_src is not : %sn,
17、straddr); continue; struct icmp *icmp = (struct icmp*)(recvbuf + (ip-ip_hl)icmp_id != getpid() / printf(icmp_id is not :%dn, getpid(); continue; recvnum+; sendtime = (struct timeval*)icmp-icmp_data; tv_sub(&recvtime, sendtime); printf(imcp echo from %s(%dbytes)tttl=%dtseq=%dttime=%d.%06d sn, straddr
18、, n, ip-ip_ttl, icmp-icmp_seq, recvtime.tv_sec, recvtime.tv_usec); void catch_sigalrm(int signum) send_icmp(); alarm(1);void catch_sigint(int signum) printf(nPing statics:send %d packets, recv %d packets, %d% lost.n, sendnum, recvnum, (int)(float)(sendnum-recvnum)/sendnum)*100); exit(0); int main(in
19、t argc, char *argv) if(argc != 2) printf(please use format: ping hostnamen); exit(-1); sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if(sockfd = -1) perror(socket(); return -1; /* int sendbufsize = 180; socklen_t sendbufsizelen = sizeof(sendbufsize); if(setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
20、&sendbufsize, sendbufsizelen) = -1)perror(setsockopt(); int recvbufsize; socklen_t recvbufsizelen; if(getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbufsize, &recvbufsizelen) = -1)perror(getsockopt();*/ memset(&addr, 0, sizeof(addr); addr.sin_family = AF_INET; int retval = inet_pton(AF_INET, argv1,
21、&addr.sin_addr); if(retval = -1 | retval = 0) struct hostent* host = gethostbyname(argv1); if(host = NULL) fprintf(stderr, gethostbyname(%s):%sn, argv1, strerror(errno); exit(-1); /* if(host-h_name != NULL)printf(hostent.h_name:%sn, host-h_name); if(host-h_aliases != NULL & *(host-h_aliases) != NULL
22、)printf(hostent.h_aliases:%sn, *(host-h_aliases); printf(hostent.h_addrtype:%dn, host-h_addrtype); printf(hostent.h_length:%dn, 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),
23、straddr, sizeof(straddr); printf(Ping address:%s(%s)nn, host-h_name, straddr); else strcpy(straddr, argv1); printf(Ping address:%s(%s)nn, straddr, straddr); struct sigaction sa1; 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(); struct sigaction sa2; 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(); return 0;
限制150内