linux raw socket
来源:互联网 发布:网络安利是什么意思 编辑:程序博客网 时间:2024/05/16 17:20
raw socket即原始套接字,可以通过创建socket时,设置参数SOCK_RAW来创建。相比平时使用SOCK_STREAM创建的应用层的套接字,raw socket可以直接处理ip首部和tcp首部,并且可以监听抓取经过本机的ip数据包和mac帧,可以处理ICMP和IGMP等网络控制报文。通常有两种方式:
//发送接收ip数据包socket(PF_INET,SOCK_RAW,IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)//发送接收以太网数据帧socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))
PF_INET可以处理和获取ip层级以上的数据,而PF_PACKET可以处理和获取数据链路层及以上的数据。
IP 首部结构
对应的结构体,头文件netinet/ip.h
struct iphdr {#if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int ihl:4; unsigned int version:4;#elif __BYTE_ORDER == __BIG_ENDIAN unsigned int version:4; unsigned int ihl:4;#else# error "Please fix <bits/endian.h>"#endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ };
tcp首部结构
对应结构体,头文件netinet/tcp.h
struct tcphdr { __extension__ union { struct { u_int16_t th_sport; /* source port */ u_int16_t th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */# if __BYTE_ORDER == __LITTLE_ENDIAN u_int8_t th_x2:4; /* (unused) */ u_int8_t th_off:4; /* data offset */# endif# if __BYTE_ORDER == __BIG_ENDIAN u_int8_t th_off:4; /* data offset */ u_int8_t th_x2:4; /* (unused) */# endif u_int8_t th_flags;# define TH_FIN 0x01# define TH_SYN 0x02# define TH_RST 0x04# define TH_PUSH 0x08# define TH_ACK 0x10# define TH_URG 0x20 u_int16_t th_win; /* window */ u_int16_t th_sum; /* checksum */ u_int16_t th_urp; /* urgent pointer */ }; struct { u_int16_t source; u_int16_t dest; u_int32_t seq; u_int32_t ack_seq;# if __BYTE_ORDER == __LITTLE_ENDIAN u_int16_t res1:4; u_int16_t doff:4; u_int16_t fin:1; u_int16_t syn:1; u_int16_t rst:1; u_int16_t psh:1; u_int16_t ack:1; u_int16_t urg:1; u_int16_t res2:2;# elif __BYTE_ORDER == __BIG_ENDIAN u_int16_t doff:4; u_int16_t res1:4; u_int16_t res2:2; u_int16_t urg:1; u_int16_t ack:1; u_int16_t psh:1; u_int16_t rst:1; u_int16_t syn:1; u_int16_t fin:1;# else# error "Adjust your <bits/endian.h> defines"# endif u_int16_t window; u_int16_t check; u_int16_t urg_ptr; }; };};
udp首部结构
对应结构体,头文件netinet/udp.h
struct udphdr{ __extension__ union { struct { u_int16_t uh_sport; /* source port */ u_int16_t uh_dport; /* destination port */ u_int16_t uh_ulen; /* udp length */ u_int16_t uh_sum; /* udp checksum */ }; struct { u_int16_t source; u_int16_t dest; u_int16_t len; u_int16_t check; }; };};
下面是tcp raw socket的一个栗子
通过填写ip首部和tcp首部向服务器发送SYN请求包,模拟tcp三次握手的过程
int s=socket(PF_INET,SOCK_RAW,IPPROTO_TCP);
创建一个tcp raw socket,并通过设置IP_HDRINCL,通知内核数据包中已经包含了ip首部,内核不用填充。
int one=1; const int *val=&one; if(setsockopt(s,IPPROTO_IP,IP_HDRINCL,val,sizeof(int))<0) { perror("Error setting IP_HDRINCL\n"); exit(0); }
rawsock.cpp具体代码如下:
/* raw tcp packets */#include<unistd.h>#include<stdio.h>#include<string.h>#include<sys/socket.h>#include<stdlib.h>#include<errno.h>#include<netinet/tcp.h>#include<netinet/ip.h>#include <arpa/inet.h>#define DEST_PORT 8888#define SRC_PORT 9999#define SRC_ADDRESS "192.168.0.105"#define DEST_ADDRESS "192.168.0.174"//tcp伪首部struct pseudo_header{ u_int32_t source_address; u_int32_t dest_address; u_int8_t placeholder; u_int8_t protocol; u_int16_t tcp_length;};/* Generic checksum calculation function*/unsigned short csum(unsigned short *ptr,int nbytes){ register long sum; unsigned short oddbyte; register short answer; sum=0; while(nbytes>1) { sum+=*ptr++; nbytes-=2; } if(nbytes==1) { oddbyte=0; *((u_char*)&oddbyte)=*(u_char*)ptr; sum+=oddbyte; } sum = (sum>>16)+(sum & 0xffff); sum = sum + (sum>>16); answer=(short)~sum; return(answer);}int main(){ //Create a raw socket int s=socket(PF_INET,SOCK_RAW,IPPROTO_TCP); if(s==-1) { perror("Failed to create socket\n"); exit(1); } //Datagram to represent the packet char datagram[4096],source_ip[32],*data,*pseudogram; memset(datagram,0,sizeof(datagram)); //IP Header struct iphdr *iph=(struct iphdr*)datagram; //TCP Header struct tcphdr *tcph=(struct tcphdr*)(datagram+sizeof(struct iphdr)); struct sockaddr_in sin; struct pseudo_header psh; //Data part data=datagram+sizeof(struct iphdr)+sizeof(struct tcphdr); strcpy(data , "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); strcpy(source_ip,SRC_ADDRESS);//源ip地址 sin.sin_family=AF_INET; sin.sin_port=htons(DEST_PORT); sin.sin_addr.s_addr=inet_addr(DEST_ADDRESS); //Fill in the IP Header iph->ihl=5;// 20 byte iph->version=4;//ipv4 iph->tos=0; iph->tot_len=sizeof(struct iphdr)+sizeof(struct tcphdr)+strlen(data); iph->id=htonl(54321);//ID iph->frag_off=0; iph->ttl=255; iph->protocol=IPPROTO_TCP; iph->check=0; iph->saddr=inet_addr(source_ip); iph->daddr=sin.sin_addr.s_addr; //IP checksum iph->check=csum((unsigned short*)datagram,sizeof(struct iphdr)); //TCP header tcph->source = htons(SRC_PORT); tcph->dest = htons(DEST_PORT); tcph->seq = 0; tcph->ack_seq = 0; tcph->doff = 5; //tcp header size tcph->fin=0; tcph->syn=1; tcph->rst=0; tcph->psh=0; tcph->ack=0; tcph->urg=0; tcph->window = htons (5840); /* maximum allowed window size */ tcph->check = 0; //leave checksum 0 now, filled later by pseudo header tcph->urg_ptr = 0; //Now the TCP checksum psh.source_address = inet_addr(source_ip); psh.dest_address = sin.sin_addr.s_addr; psh.placeholder = 0; psh.protocol = IPPROTO_TCP; psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data)); int psize=sizeof(struct pseudo_header)+sizeof(struct tcphdr)+strlen(data); pseudogram=(char*)malloc(psize); memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header)); memcpy(pseudogram + sizeof(struct pseudo_header) , tcph , sizeof(struct tcphdr) + strlen(data)); //tcp CRC校验包括:12字节伪首部+tcp首部+数据包 tcph->check = csum((unsigned short*)pseudogram,psize); //TP_HDRINCL to tell the kernel that headers are included in the pakcet int one=1; const int *val=&one; if(setsockopt(s,IPPROTO_IP,IP_HDRINCL,val,sizeof(int))<0) { perror("Error setting IP_HDRINCL\n"); exit(0); } //loop if you want to flood while(1) { //send the packet if(sendto(s,datagram,iph->tot_len,0,(struct sockaddr*)&sin,sizeof(sin))<0) { perror("send to failed\n"); } else { printf("Packet Send Length:%d \n",iph->tot_len); } //sleep(2); } return 0;}
测试服务端sev.cpp代码如下:
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <arpa/inet.h>#include <errno.h>#include <unistd.h>#define PORT 8888int main(){ int fd = socket(PF_INET,SOCK_STREAM,0); if(fd<0) { perror("create socket error"); exit(0); } struct sockaddr_in sin; sin.sin_family=AF_INET; sin.sin_port=htons(PORT); sin.sin_addr.s_addr=INADDR_ANY; if(bind(fd,(struct sockaddr*)&sin,sizeof(struct sockaddr))<0) { perror("socket bind error"); exit(0); } if(listen(fd,100)<0) { perror("socket listen error"); exit(0); } printf("start listening\n"); int clifd; struct sockaddr_in client; socklen_t len; while(1) { if((clifd=accept(fd,(struct sockaddr*)&client,&len))<0) { perror("accept error"); } else { printf("accept success\n"); } }}
测试结果如下:
运行服务端,bind的端口号为8888
启动raw socket发送SYN包,目的ip地址“192.168.0.174”(这里不知道威为什么我电脑上使用“127.0.0.1”一直失败),源ip地址为“192.168.0.105”(随意填写),端口号9999,注意使用root权限启动
结果服务端收到SYN请求,状态变为SYN_RECV
参考:
https://en.wikipedia.org/wiki/IPv4#Packet_structure
https://en.wikipedia.org/wiki/Transmission_Control_Protocol
https://en.wikipedia.org/wiki/User_Datagram_Protocol
http://www.binarytides.com/raw-sockets-c-code-linux/
http://blog.chinaunix.net/uid-23069658-id-3280895.html
- linux下RAW SOCKET
- linux raw socket
- linux raw socket program
- Linux Raw Socket
- linux下RAW SOCKET
- Linux raw socket
- Linux raw socket
- linux raw socket
- [linux]raw socket example
- raw socket in linux
- Linux Raw Socket使用总结
- Linux socket编程(TCP,UDP,RAW)
- linux利用raw socket抓包
- raw socket 编程资料(linux环境)
- Linux socket编程(TCP,UDP,RAW)
- [linux] raw socket example tcp sync
- Raw Socket
- raw socket
- mfc学习笔记之如何修改mfc消息机制处理顺序
- 设计模式---行为类型---职责链
- 在Java中如何高效的判断数组中是否包含某个元素
- ASP.NET Core 和 NLog 集成
- Java 优雅的终止线程
- linux raw socket
- 用C语言实现九九乘法表
- Android Animation实战之屏幕底部弹出PopupWindow
- 解决SecureCRT超时自动断开的问题
- Java烧脑驴游(六)--修饰符
- 自定义View之onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法解释
- 在MAC上查找和设置$JAVA_HOME
- 线程安全与共享资源
- How do you add?(UVA 10943)