20100201 链路层协议通信sokect方式 2(转)

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

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

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

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

 

packet_socket=socket(PF_PACKET,int socket_type,int protocol);

 

PF_PACKET项指在链路层的选择,该选项用于指定socket使用何种地址类型,在其它层次或其它类型网络中还有诸多可选项,完整的定义在/usr/include/bits/socket.h 内,常见的协议有:
PF_UNIX/PF_LOCAL/AF_UNIX/AF_LOCAL UNIX 进程通信协议
PF_INET/AF_INET Ipv4网络协议
PF_INET6/AF_INET6 Ipv6 网络协议
PF_IPX/AF_IPX IPX-Novell协议
PF_NETLINK/AF_NETLINK 核心用户接口装置
PF_X25/AF_X25 ITU-T X.25/ISO-8208 协议
PF_AX25/AF_AX25 业余无线AX.25协议
PF_ATMPVC/AF_ATMPVC 存取原始ATM PVCs
PF_APPLETALK/AF_APPLETALK appletalk(DDP)协议
PF_PACKET/AF_PACKET 初级封包接口

 

接下来的socket_type有下列几种数值选择:


SOCK_STREAM 提供双向连续且可信赖的数据流,即TCP。支持OOB 机制,在所有数据传送前必须使用connect()来建立连线状态。
SOCK_DGRAM 使用不连续不可信赖的数据包连接(UDP)
SOCK_SEQPACKET 提供连续可信赖的数据包连接
SOCK_RAW 提供原始网络协议存取
SOCK_RDM 提供可信赖的数据包连接
SOCK_PACKET 提供和网络驱动程序直接通信。

 

而此处的这个套接字的打开需要用户有root权限,并且对于链路层的数据传输,socket_type只能有两种可选类型,一种为SOCK_RAW,它是包含了MAC层头部信息的原始分组,当然这种类型的套接字在发送的时候需要自己加上一个MAC头部(其类型定义在linux/if_ether.h中,ethhdr),另一种是SOCK_DGRAM类型,它是已经进行了MAC层头部处理的,即收上的帧已经去掉了头部,而发送时也无须用户添加头部字段。

 

Protocol是指其送交的上层的协议号,如IP为0x0800,当其为htons(ETH_P_ALL) (其宏定义为0)时表示收发所有的协议。另外的选项见(链路层协议通信sokect方式 1)

 

创建好套接字后,就可以通过与UDP一样的recvfrom与sendto函数进行数据的收发,其目的地址结构为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 头文件中定义的按网络层排序的标准的以太帧协议类型,即为socket函数中的第三个参数protocol,在某些私有协议中,此处可能会定义自己的protocol。

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_protocal和sll_ifindex分别绑定收发的协议号接口索引号,接口索引号sll_ifindex为0时表示使用有效的所有接口。接口的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);

 

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

 

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值