ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

原始套接字

2020-06-12 14:06:02  阅读:268  来源: 互联网

标签:sockaddr struct ifr 网卡 sll 原始 接字 sa


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

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

可以获取链路层的数据包

创建原始套接字

<netinet/in.h>
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* 类型,

使用如下

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

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

struct sockaddr_ll
{
  unsigned short sll_family;
  __be16 sll_protocol;
  int sll_ifindex;
  unsigned short sll_hatype;
  unsigned char sll_pkttype;
  unsigned char sll_halen;//物理层地址
  unsigned char sll_addr;//地址长度
}

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

struct sockaddr_ll sll;
struct ifreq ifr;
strcpy(ifr.ifr_name, "eth0");
ioctl(sockfd, SIOCGIFINDEX, &ifr);
sll.sll_ifindex=ifr.ifr_ifindex;

发送数据sendto

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

 

以太网帧,头文件<linux/if_ether.h>      P546

struct ethhdr
{
 unsigned charr h_dest[ETH_ALEN];//目的mac地址
 unsigned cahr h_source[ETH_ALEN];//源mac地址
 u16 h_proto;//网络层所使用的协议类型
}

获取网络接口信息

网络接口请求结构体      P548

struct ifreq
{
  union
 {
   char ifrn_name[IFNAMSIZ];//网络接口名字
 }ifr_ifrn;
 union
 {
   struct sockaddr ifru_addr;
   struct sockaddr ifru_dstaddr;
   struct sockaddr ifru_broadaddr;
   struct sockaddr ifru_netmask;
  struct sockaddr ifru_hwaddr;
...省略 } }

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

获取mac地址信息        P549

struct sockaddr_ll device;
struct ifreq ifr;
int sd=socket(PF_PACKET,SOCK_DGRAM, htons(ETH_P_ALL));
memcpy(ifr.ifr_name,"ens32",sizeof(struct ifreq));
ioctl(sd,SIOCGIFHWADDR,&ifr)
unsigned char dest_mac[6]={0};
memcpy(dest_mac,ifr.ifr_hwaddr.sa_data,6);
close(sd);

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

绑定网络接口

struct sockaddr_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

struct ifreq 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));

 

标签:sockaddr,struct,ifr,网卡,sll,原始,接字,sa
来源: https://www.cnblogs.com/cs0915/p/13098742.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有