原始套接字 实例 抓 以太网 linux,原始套接字

标准套接字不能对IP首部或TCP、UDP首部进行操作,如果开发底层的应用,比如发送一个自定义的IP包、UDP包、TCP包、ICMP包,伪装本机IP地址,捕获所有经过本机的数据包,就要用到原始套接字。

一、面向链路层的原始套接字

可以获取链路层的数据包

创建原始套接字

int socket(int family,int type, int protocol);

family:面向链路层取PF_PACKET;type:SOCK_RAW,接收的帧包含MAC头部信息,发送帧时也要自己加上MAC头部信息;SOCK_DGRAM,收到的帧无MAC头部信息,已经经过处理,发送时也无需添加头部信息;protocol:指定要收发的数据包类型,ETH_P_IP、ETH_P_ARP、ETH_P_RARP、ETH_P_ALL.注意传入参数时候,要htons转换,比如(ETH_P_ALL)。

接收数据

ssize_t recvfrom(int sock, void* buf, size_t len, int flags, struct sockaddr* from, socklen_t* fromlen);

倒数第二个参数传入时需要的是一个sockaddr_ll* 类型,

使用如下

structsockaddr_ll sa_recv;

recvfrom(fd,buf,sizeof(buf), (struct sockaddr* )& sarecv, &sa_len)

sockaddr_ll结构体——表示的是一个与物理设备无关的物理层地址     P542

structsockaddr_ll

{

unsignedshortsll_family;

__be16 sll_protocol;intsll_ifindex;

unsignedshortsll_hatype;

unsignedcharsll_pkttype;

unsignedcharsll_halen;//物理层地址

unsignedcharsll_addr;//地址长度

}

sll_ifindex为网络接口类型,单网卡时可以为0,表示处理所有接口,对于多网卡,则要获取网卡的接口索引然后赋值

structsockaddr_ll sll;structifreq ifr;

strcpy(ifr.ifr_name,"eth0");

ioctl(sockfd, SIOCGIFINDEX,&ifr);

sll.sll_ifindex=ifr.ifr_ifindex;

发送数据sendto

structsockaddr_ll sa;

sendto(fd,bufsizeof(buf), 0, (struct sockaddr* )&sa, sizeof(struct sockaddr_ll);

以太网帧,头文件      P546

structethhdr

{

unsigned charr h_dest[ETH_ALEN];//目的mac地址

unsigned cahr h_source[ETH_ALEN];//源mac地址

u16 h_proto;//网络层所使用的协议类型

}

获取网络接口信息

网络接口请求结构体      P548

structifreq

{

union

{char ifrn_name[IFNAMSIZ];//网络接口名字

}ifr_ifrn;

union

{structsockaddr ifru_addr;structsockaddr ifru_dstaddr;structsockaddr ifru_broadaddr;structsockaddr ifru_netmask;

structsockaddr ifru_hwaddr;

...省略 } }

获取网络接口信息,一般把网络接口名字传给ifrn_name,调用ioctl来获取所需要的信息。

获取mac地址信息        P549

structsockaddr_ll device;structifreq ifr;int sd=socket(PF_PACKET,SOCK_DGRAM, htons(ETH_P_ALL));

memcpy(ifr.ifr_name,"ens32",sizeof(structifreq));

ioctl(sd,SIOCGIFHWADDR,&ifr)

unsignedchar dest_mac[6]={0};

memcpy(dest_mac,ifr.ifr_hwaddr.sa_data,6);

close(sd);

默认情况下,从任何接口收到的符合指定协议的数据报文都会出传到原始PACKET套接字口,而使用bind系统调用并以一个sockaddr_ll结构体对象将PACKET套接字与某个接口网络像绑定,可以使我们的原始套接字只接收指定接口的数据报文。

绑定网络接口

structsockaddr_ll sa;int fd=socket(PF_PACKET,SOCK_RAW,htons(0x8902));

//初始化sa

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

sa.sll_family=PF_PACKET;

sa.sll_protocol=htons(0x8902);

strcpy(ifr.ifr_name,if_name);

ioctl(fd,SIOCGIFFLAGES,&ifr);

ioctl(fd,SIOCGIFINDEX,&ifr);

sa.sll_ifindex=ifr.ifr_ifindex;

//绑定

bind(fd,(struct sockaddr*)&sa, sizeof(struct sockaddr_ll);

混杂模式

网卡工作模式

广播模式:接收广播帧

多播模式:不管是不是组内成员,均接收多播帧

直接模式:只接收发给自己的帧

混杂模式:接收所有经过自己网卡的帧

无论套接字是否绑定网卡,都会全部接收通过本主机(可能有多个网卡)所有网卡的所有帧。

设置网卡混杂模式    P579

structifreq ifr;

strcpy(ifr.ifr_name,if_name);

ioctl(fd,SIOCGIFFLAGS,&ifr);

ifr.ifr_flags|=IFF_PROMISC;

ioctl(fd,SIOCSIFFLAGS,&ifr);

二、面向IP层的原始套接字

可以获取网络层的数据包

socket(AF_INET,SOCK_RAW,protocol);

protocol:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP、IPPROTO_RAW,可以多个进行或操作。此处不需要htons,与链路层原始套接字不同。

接收包,能接收到完整的IP包,包括IP头部。

发送包时,不用IP头部,内核自动封装,即发送从IP后的第一个字节开始。除非设置了IP_HDRINCL的socket项。不能收到自己发送出去的包,需要自己组织TCP UDP ICMP等头部。

获取网卡IP地址信息    P598

int sock=socket(AF_INET,SOCK_RAW, IPPROTO_TCP);

strcpy(ifr.ifr_name,"ens32");

ioctl(sock,SIOCGIFADDR,&ifr);

printf("%s\n",inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值