Chinaunix首页 | 论坛 | 博客
  • 博客访问: 181929
  • 博文数量: 64
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 616
  • 用 户 组: 普通用户
  • 注册时间: 2015-06-09 20:25
文章分类

全部博文(64)

文章存档

2016年(25)

2015年(39)

我的朋友

分类: 网络与安全

2015-11-04 14:09:04

PF_PACKET 设备层编程接口

定义:

Cpp代码
  1. #include "/usr/include/sys/socket. h"  
  2. #include "/usr/includ/sys/if_packet. h"  
  3. packet_socket = socket(PF_PACKET,  socket_type,  protocol);  

 

描述: 
packet socket用于从设备驱动层接收或发送原始数据包, 可用于用户在物理层以上构建自己的通信协议. 

socket_type 可为:SOCK_RAW或SOCK_DGRAM.

其中SOCK_RAW可用于发送原始数据包, 此时可自定义数据链路层头部; SOCK_DGRAM可用于在数据链路层以上构建包. 结构sockaddr_ll中会用到链路层头部信息. 协议为IEEE 802. 3 协议号的网络序列. 所有到达的属于已定义物理层协议的包先通过packet socket到达已在内核实现的链路层协议处理层. 

仅有特权进程或有CAP_NET_RAW属性的进程才能打开packet套接口. 

如果用SOCK_RAW, 则数据包将直接通过设备驱动程序不加任何改变地发送出去. 这就要求用户程序必须了解物理层头部结构, 并适当地构建包, 此时地址解析将用到标准sockaddr_ll结构. SOCK_RAW很象用于2. 0版核心老的SOCK_PACKET, 但他们并不完全一致. 

SOCK_DGRAM建立在更高层. 在接受包时, 物理头将在到达用户前被去掉; 而在发包时, 物理头部将在发送前被自动添加. 

默认地所有的包都从packet socket层接收. 当仅接收从特定界面来的包时将使用bind来绑定由sockaddr_ll地址结构指定的接口. 

为发送SOCK_RAW包, 用户必须提供空间并构建包括物理头部在内的完整的数据包. 此包将不加任何改变地加入网卡驱动程序发送队列, 而网卡将由目的地址确认. 对于SOCK_DGRAM包, 其头部将在包被加入发送队列前由系统根据地址结构(sockaddr_ll)信息自动填写. 

地址结构: 

sockaddr_ll为设备无关的物理层地址结构.

Cpp代码
  1. struct sockaddr_ll  
  2. {  
  3.     unsigned short sll_family; /* 总填 AF_PACKET */  
  4.     unsigned short sll_protocol;/* 网络序列的物理层协议号 */  
  5.     int sll_ifindex; /* 接口编号 */  
  6.     unsigned short sll_hatype; /* 头部类型 */  
  7.     unsigned char sll_pkttype; /* 包类型 */  
  8.     unsigned char sll_halen; /* 地址长度 */  
  9.     unsigned char sll_addr[8]; /* 物理地址 */  
  10. };  

 

sll_protocol为在sys/if_ether. h中定义的标准以太协议好的网络序列.

sll_pkttype为包类型. 可用的有:
    PACKET_HOST类型用于本机地址的包;
    PACKET_BROADCAST类型用于物理广播;
    PACKET_MULTICAST类型用于物理组播;
    PACKET_OTHERHOST用于在网卡混杂模式下从别的主机通信上接收包;
    PACKET_OUTGOING类型用于从本机packet socket发出的包. 
sll_halen和sll_addr为物理地址及其长度. 

组播和混杂模式的支持: 

Linux2. 2支持一种建立在packet socket上的新方法来配置组播和混杂模式. 它调用setsockopt来工作, 其工作建立在SOL_PACKET packet socket之上, 其选项为PACKET_ADD_MEMBERSHIP或PACKET_DROP_MEMBERSHIP. 底层结构为:

Cpp代码
  1. struct packet_mreq  
  2. {  
  3.     intmr_ifindex; /* 接口编号 */  
  4.     unsigned shortmr_type; /* mreq 类型 */  
  5.     unsigned shortmr_alen; /* 地址长度 */  
  6.     unsigned charmr_address[8]; /* 物理地址 */  
  7. };  

 

mr_interfac包含接口索引, 它指出了谁将要被改变.

mr_type有:
    PACKET_MR_MULTICAST用于绑定套接口和由mr_address指定的物理组播地址;
    PACKET_MR_PROMISC 用于激活混杂模式以接受所有网络包;
    PACKET_DROP_MEMBERSHIP用于撤销绑订或重置. 

输入输出控制: 

输入输出控制可调用ioct:

Cpp代码
  1. ioctl(tcp_socket,  ioctl_type,  value_ptr);  

 

SIOCGSTAMP 返回一个标准timeval结构, 则在须精确时间记录时很有用.

FIOCSETOWN 和 SIOCSPGRP 用于在进程异步通信结束时发送SIGIO信号, 其参数为pid_t类型. 
FIOCGETOWN 和 SIOCGPGRP 用于得到当前接收到SIGIO信号的进程组, 当没有设置时返回0, 参数类型为pid_t.

 

出错处理: 
无出错处理机制. 

兼容性: 

Linux 2. 0仅支持SOCK_RAW, 它使用老的结构:

Cpp代码
  1. struct sockaddr_pkt  
  2. {  
  3.     unsigned short spkt_family;  
  4.     unsigned char spkt_device[14];  
  5.     unsigned short spkt_protocol;  
  6. };  

 

spkt_family包含设备类型.

spkt_protocol为IEEE 802. 3标准协议. 
spkt_device为设备名, 如"eth0";

出错类型: 

ENETDOWN 接口未工作. 
ENOTCONN 没有接口地址. 
ENODEV 未知的设备或接口名. 
EMSGSIZE 包太大. 
ENOBUFS 没有足够的内存来存放接收的包. 
EFAULT 错误的内存地址. 
EINVAL 参数错. 
ENXIO 接口地址包含不合法接口索引. 
EPERM 无打开packet socket接口权用户. 
EADDRNOTAVAIL 未知组播地址. oup address passed. 
ENOENT 未接收到包.

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