Chinaunix首页 | 论坛 | 博客
  • 博客访问: 73627
  • 博文数量: 21
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 189
  • 用 户 组: 普通用户
  • 注册时间: 2015-08-24 20:56
文章分类

全部博文(21)

文章存档

2016年(1)

2015年(20)

我的朋友

分类: LINUX

2015-10-26 09:36:26

本文转载自:http://blog.sina.com.cn/s/blog_5431fc8301008yfd.html

linux环境中要从链路层(MAC)直接收发数据帧,可以通过libpcaplibnet两个动态库来分别完成收与发的工作。虽然它已被广泛使用,但在要求进行跨平台移植的软件中使用仍然有很多弊端。

这里介绍一种更为直接地、无须安装其它库的从MAC层收发数据帧的方式,即通过定义链路层的套接字来完成。

Packet套接字用于在MAC层上收发原始数据帧,这样就允许用户在用户空间完成MAC之上各个层次的实现。给无论是进行开发还是测试的人们带来了极大的便利性。

Packet套接字的定义方式与传送层的套接字定义类似,如下:

packet_socket=socket(PF_PACKET,int socket_type,int protocol);
(这个套接字的打开需要用户有root权限)

其中socket_type有两种类型,一种为SOCK_RAW,它是包含了MAC层头部信息的原始分组,当然这种类型的套接字在发送的时候需要自己加上一个MAC头部(其类型定义在linux/if_ether.h中,ethhdr),另一种是SOCK_DGRAM类型,它是已经进行了MAC层头部处理的,即收上的帧已经去掉了头部,而发送时也无须用户添加头部字段。

Protocol是指其送交的上层的协议号,如IP0x0800,当其为htons(ETH_P_ALL) (其宏定义为0)时表示收发所有的协议。

创建好套接字后,就可以通过与UDP一样的recvfromsendto函数进行数据的收发,其目的地址结构为sockaddr_ll,这与传送层的地址结构定义是不一样的,其长度为20字节(在TCP/IP的链路层地址中使用了18字节),而传送层的地址结构长度为16字节。

Sockaddr_ll结构如下:
               
struct sockaddr_ll
                {
                unsigned short sll_family; /* 总是 AF_PACKET */
                unsigned short sll_protocol; /* 物理层的协议 */
                int sll_ifindex; /* 接口号 */
                unsigned short sll_hatype; /* 报头类型 */
                unsigned char sll_pkttype; /* 分组类型 */
                unsigned char sll_halen; /* 地址长度 */
                unsigned char sll_addr[8]; /* 物理层地址 */
                
};

sll_protocol 是在 linux/if_ether.h 头文件中定义的按网络层排序的标准的以太桢协议类型。sll_ifindex 是接口的索引号(参见netdevice(2))0 匹配所有的接口(当然只有合法的才用于绑定)。 sll_hatype 是在 linux/if_arp.h 中定义的 ARP 硬件地址类型。 sll_pkttype 包含分组类型。有效的分组类型是:目标地址是本地主机的分组用的 PACKET_HOST,物理层广播分组用的PACKET_BROADCAST ,发送到一个物理层多路广播地址的分组用的 PACKET_MULTICAST,在混杂(promiscuous)模式下的设备驱动器发向其他主机的分组用的 PACKET_OTHERHOST,本源于本地主机的分组被环回到分组套接口用的 PACKET_OUTGOING。这些类型只对接收到的分组有意义。sll_addr 和 sll_halen 包括物理层(例如 IEEE 802.3)地址和地址长度。精确的解释依赖于设备。(本段引于packet的用户手册)

当在多个网络接口的主机上使用这个套接字时,若要指定接收或发送的接口时可以使用bind进行绑定,这与TCP套接字的操作一样,但其内涵并不相同。绑定时将根据地址结构中的sll_protocalsll_ifindex分别绑定收发的协议号和接口索引号,接口索引号sll_ifindex0时表示使用有效的所有接口。接口的sll_ifindex值可以通过ioctl获得,如下面是获得名字为“eth0”的接口的索引号

       strcpy(ifr.ifr_name,"eth0");

       ioctl(fd_packet,SIOCGIFINDEX,&ifr);

取得的值保存在ifr结构体的ifr_ifindex中,ifr结构类型为“struct ifreq”

BTW,要获得接口的物理地址同样使用ioctl可以得到

ioctl(fd_packet,SIOCGIFHWADDR,&ifr);

以数据形式保存在ifrifr_hwaddr.sa_data中。


另外需要注意的是,在调用recvfrom函数时返回的地址长度信息为18字节,原因是在sockaddr_ll结构中的sll_addr[8]8字节,MAC地址只使用了其中的前6字节。在用sendto发送时需要将目的地址结构体强制转换为struct sockaddr 类型,而且指定的长度必须为20字节,而不能是18或其它值。


我在使用中当指定了协议类型后可以准备接收该类型的数据帧,但有个问题一直困扰着我,就是无法过滤掉广播帧,必须要收到帧后判断目的地址是否为自己,然后如果用SOCK_DGRAM的时候又如何判断呢?本人正在探索中,一旦有新进展将第一时间与大家分享。

阅读(995) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~