linux ping raw socket -(signal),Linux下raw socket编程

参考文章:

(1)博客园-raw socket

通常所说的socket通信是指通假字通信,也就是IP层往上的,IP+接口号完成套接字通信。这种情况下通信产生的数据包如TCP/UDP/ICMP包。某些情况下我们需要执行更底层的操作,通过raw socket通信可以完成基于数据链路层的socket通信。

raw socket的主要结构为:sockfd = socket(PARAM1, SOCK_RAW, protocol);

最原始的套接字PARAM1主要有两种:

一种是处理IP层及其上的数据,通过指定socket第一个参数为AF_INET来创建这种套接字,这种情况下地址是IP地址。(XX.XX.XX.XX),4bytes

另一种是处理数据链路层即其上的数据,通过指定socket第一个参数为AF_PACKET来创建这种套接字,这种情况下地址是MAC地址。(XX:XX:XX:XX:XX:XX),6bytes

AF_INET表示获取从网络层开始的数据

socket(AF_INET, SOCK_RAW, …)

当接收包时,表示用户获得完整的包含IP报头的数据包,即数据从IP报头开始算起。

当发送包时,用户只能发送包含TCP报头或UDP报头或包含其他传输协议的报文,IP报头以及以太网帧头则由内核自动加封。除非是设置了IP_HDRINCL的socket选项。

如果第二个参数为SOCK_STREAM, SOCK_DGRAM,表示接收的数据直接为应用层数据。

PF_PACKET,表示获取的数据是从数据链路层开始的数据

socket(PF_PACKET,SOCK_RAW,htos(ETH_P_IP)):表示获得IPV4的数据链路层帧,即数据包含以太网帧头。14+20+(8:udp 或 20:tcp)

ETH_P_IP: 在中定义,可以查看该文件了解支持的其它协议。

SOCK_RAW, SOCK_DGRAM两个参数都可以使用,区别在于使用SOCK_DGRAM收到的数据不包括数据链路层协议头。

总结起来就是:

socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包

能:该套接字可以接收协议类型为(tcp udp icmp等)发往本机的ip数据包

不能:收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包)

不能:收到从本机发送出去的数据包

发送的话需要自己组织tcp udp icmp等头部.可以setsockopt来自己包装ip头部

这种套接字用来写个ping程序比较适合

socket(PF_PACKET, SOCK_RAW|SOCK_DGRAM, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧

这种套接字比较强大,可以监听网卡上的所有数据帧

能: 接收发往本地mac的数据帧

能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)

能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)

协议类型一共有四个

ETH_P_IP 0x800 只接收发往本机mac的ip类型的数据帧

ETH_P_ARP 0x806 只接受发往本机mac的arp类型的数据帧

ETH_P_RARP 0x8035 只接受发往本机mac的rarp类型的数据帧

ETH_P_ALL 0x3 接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Linux系统中ping命令的源代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netdb.h> #include <arpa/inet.h> #define PACKET_SIZE 4096 #define MAX_WAIT_TIME 5 #define MAX_NO_PACKETS 3 char sendpacket[PACKET_SIZE]; char recvpacket[PACKET_SIZE]; int sockfd,datalen=56; int nsend=0,nreceived=0; struct sockaddr_in dest_addr; pid_t pid; struct sockaddr_in from; struct timeval tvrecv; void statistics(int signo); unsigned short cal_chksum(unsigned short *addr,int len); int pack(int pack_no); void send_packet(void); void recv_packet(void); int unpack(char *buf,int len); void tv_sub(struct timeval *out,struct timeval *in) { if ((out->tv_usec -= in->tv_usec) < 0) { --out->tv_sec; out->tv_usec += 1000000; } out->tv_sec -= in->tv_sec; } int main(int argc,char *argv[]) { struct hostent *host; struct protoent *protocol; unsigned long inaddr=0l; int waittime=MAX_WAIT_TIME; int size=50*1024; int ttl=255; int i=0; bzero(&dest_addr,sizeof(dest_addr)); if(argc<2) { printf("usage:%s hostname/IP address\n",argv[0]); exit(1); } if((protocol=getprotobyname("icmp"))==NULL) { perror("getprotobyname"); exit(1); } if((sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto))<0) { perror("socket error"); exit(1); } setuid(getuid()); setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size)); setsockopt(sockfd,IPPROTO_IP,IP_TTL,&ttl,sizeof(ttl)); bzero(&dest_addr,sizeof(dest_addr)); dest_addr.sin_family=AF_INET; if(inaddr=inet_addr(argv[1])==INADDR_NONE) { if((host=gethostbyname(argv[1]))==NULL) { perror("gethostbyname error"); exit(1); } memcpy((char *)&dest_addr.sin_addr,host->h_addr,host->h_length); } else dest_addr.sin_addr.s_addr=inaddr; pid=getpid(); printf("PING %s(%s):%d bytes of data.\n",argv[1],inet_ntoa(dest_addr.sin_addr),datalen); for(i=0;i<MAX_NO_PACKETS;i++) { if(pack(i)<0) continue; send_packet(); recv_packet(); sleep(1); } statistics(SIGALRM); return 0; } void statistics(int signo) { printf("\n--------------------PING statistics-------------------\n"); printf("%d packets transmitted,%d received,%.0f%% packet loss\n", nsend,nreceived,(float)(nsend-nreceived)/nsend*100); close(sockfd); exit(0); } unsigned short cal_chksum(unsigned short *addr,int len) { int nleft=len; int sum=0; unsigned short *w=addr; unsigned short answer=0; while(nleft>1) { sum+=*w++; nleft-=2; } if(nleft==1) { *(unsigned char *)(&answer)=*(unsigned char *)w; sum+=answer; } sum=(sum>>16)+(sum&0xffff); sum+=(sum>>16); answer=~sum; return answer; } int pack(int pack_no) { int i=0; int packsize=0; struct icmp *icmp; struct timeval *tval; icmp=(struct icmp*)sendpacket; icmp->icmp_type=ICMP_ECHO; icmp->icmp_code=0; icmp->icmp_cksum=0; icmp->icmp_seq=pack_no; icmp->icmp_id=pid; packsize=8+datalen; tval= (struct timeval *)icmp->icmp_data; gettimeofday(tval,NULL); icmp->icmp_cksum=cal_chksum((unsigned short *)icmp,packsize); return packsize; } void send_packet() { int packetsize=0; nsend++; packetsize=pack(nsend); if(sendto(sockfd,sendpacket,packetsize,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr))<0) perror("sendto error"); } void recv_packet() { int n,fromlen; extern int errno; signal(SIGALRM,statistics); fromlen=sizeof(from); alarm(MAX_WAIT_TIME); while(1) { if((n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,(struct sockaddr *)&from,&fromlen))<0) { if(errno==EINTR) continue; perror("recvfrom error"); continue; } gettimeofday(&tvrecv,NULL); if(unpack(recvpacket,n)==-1) continue; nreceived++; break; } } int unpack(char *buf,int len) { int i,iphdrlen; struct ip *ip; struct icmp *icmp; struct timeval *tvsend; double rtt; ip=(struct ip *)buf; iphdrlen=ip->ip_hl<<2; icmp=(struct icmp *)(buf+iphdrlen); len-=iphdrlen; if(len<8) { printf("ICMP packets\'s length is less than 8\n"); return -1; } if((icmp->icmp_type==ICMP_ECHOREPLY)&&(icmp->icmp_id==pid)) { tvsend=(struct timeval *)icmp->icmp_data; tv_sub(&tvrecv,tvsend); rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000; printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n", len,inet_ntoa(from.sin_addr),icmp->icmp_seq,ip->ip_ttl,rtt); } else return -1; } ``` 上述代码实现了ping命令的基本功能,包括构造ICMP报文、发送ICMP报文、接收ICMP报文、计算RTT等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值