linuxsock_raw原始套接字编程

linuxsock_raw原始套接字编程

2023年7月15日发(作者:)

linux sock_raw原始套接字编程

sock_raw原始套接字编程能够接收到本机网卡上的数据帧或数据包,对与监听网络的流量和分析是很有作用的.一共能够有3种方式创建这种socket

(AF_INET, SOCK_RAW,

IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包

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

(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);

能:该套接字能够接收协议类型为(tcp udp icmp等)发往本机的ip数据包,从上面看的确实是20+8+100.

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

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

发送的话需要自己组织tcp udp icmp等头部.能够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_IP 0x800 只接收发往本机mac的ip类型的数据帧

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

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大小通吃,强悍

下面是一段相关的代码:

... int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

struct sockaddr_ll sll;

memset( &sll, 0, sizeof(sll) );

_family = AF_PACKET;

struct ifreq ifstruct;

strcpy(_name, "eth0");

ioctl(sockfd, SIOCGIFINDEX, &ifstruct);

_ifindex = _ifindex;

_protocol = htons(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(_name, interface);

if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {

perror("iotcl()");

return -1;

}

_flags |= IFF_PROMISC;

if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) { perror("iotcl()");

return -1;

}

return 0;

}

int unset_promisc(char *interface, int fd) {

struct ifreq ifr;

strcpy(_name, interface);

if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {

perror("iotcl()");

return -1;

}

_flags &= ~IFF_PROMISC;

if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {

perror("iotcl()");

return -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/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"

extern int errno;

int sockfd;

struct sockaddr_in addr; //peer addr

char straddr[128]; //peer addr ip(char*)

char sendbuf[2048];

char recvbuf[2048];

int sendnum;

int recvnum;

int datalen = 30;

unsigned short my_cksum(unsigned short *data, int len) {

int result = 0;

for(int i=0; i

result += *data;

data++;

}

while(result >> 16)result = (result&0xffff) + (result>>16); return ~result;

}

void tv_sub(struct timeval* recvtime, const struct timeval* sendtime) {

int sec = recvtime->tv_sec - sendtime->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_cksum = 0;

icmp->icmp_id = getpid(); //needn't use htons() call,

because peer networking kernel didn't handle this data and won't

make different meanings(bigdian litteldian) icmp->icmp_seq = ++sendnum; //needn't use hotns() call

too.

gettimeofday((struct timeval*)icmp->icmp_data, NULL);

int len = 8+datalen;

icmp->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 timeval 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.s_addr)

{

// printf("ip_src is not : %sn", straddr);

continue;

}

struct icmp *icmp = (struct icmp*)(recvbuf

+ ((ip->ip_hl)<<2));

if(icmp->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,

n, ip->ip_ttl, icmp->icmp_seq, _sec, _usec);

}

}

}

void catch_sigalrm(int signum) {

send_icmp();

alarm(1);

}

void catch_sigint(int signum) {

printf("nPing

packets, %d%%

statics:send

<",

%d packets, recv %d

sendnum, recvnum,

(int)((float)(sendnum-recvnum)/sendnum)*100);

exit(0);

}

int main(int 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,

&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));

_family = AF_INET;

int retval = inet_pton(AF_INET, argv[1], &_addr); if(retval == -1 || retval == 0) {

struct hostent* host = gethostbyname(argv[1]);

if(host == NULL) {

fprintf(stderr,

argv[1], 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)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

*(host->h_addr_list) != NULL) {

strncpy((char*)&_addr,

*(host->h_addr_list), 4);

inet_ntop(AF_INET,

straddr, sizeof(straddr));

*(host->h_addr_list),

!= NULL &&

!=

"gethostbyname(%s):%sn", }

printf("Ping address:%s(%s)nn", host->h_name,

straddr);

} else {

strcpy(straddr, argv[1]);

printf("Ping address:%s(%s)nn", straddr, straddr);

}

struct sigaction sa1;

memset(&sa1, 0, sizeof(sa1));

_handler = catch_sigalrm;

sigemptyset(&_mask);

_flags = 0;

if(sigaction(SIGALRM,

-1)perror("sigaction()");

struct sigaction sa2;

memset(&sa2, 0, sizeof(sa2));

_handler = catch_sigint;

sigemptyset(&_mask);

_flags = 0;

if(sigaction(SIGINT, &sa2, NULL) == -1)perror("sigaction()");

&sa1, NULL) == alarm(1);

recv_icmp();

return 0;

}

发布者:admin,转转请注明出处:http://www.yc00.com/web/1689409767a243473.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信