Chinaunix首页 | 论坛 | 博客
  • 博客访问: 257446
  • 博文数量: 58
  • 博客积分: 2241
  • 博客等级: 大尉
  • 技术积分: 522
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-07 09:53
文章分类
文章存档

2012年(4)

2011年(19)

2010年(31)

2009年(4)

分类: LINUX

2010-11-23 15:06:54

PF_PACKET 设备层编程接口

定义:
#include "/usr/include/sys/socket.h"
#include "/usr/includ/sys/if_packet.h"

packet_socket = socket(PF_PACKET, socket_type, protocol);

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

socket_type 可为: SOCK_RAW/或SOCK_DGRAM.其中SOCK_RAW可用于发送
原始数据包,此时可自定义数据链路层头部;SOCK_DGRAM可用于在数据链
路层以上构建包.结构sockaddr_ll中会用到链路层头部信息.协议为IE-
EE 802.3 协议号的网络序列(可参考文件--它包含了
可接受的协议号定义).所有到达的属于已定义物理层协议的包先通过p-
acket 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为设备无关的物理层地址结构.

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为在sys/if_ether.h中定义的标准以太协议好的网络序列.
sll_pkttype为包类型.可用的有PACKET_HOST类型用于本机地址的包;PAC-
KET_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.底层结构为:

struct packet_mreq
{
intmr_ifindex; /* 接口编号 */
unsigned shortmr_type; /* mreq 类型 */
unsigned shortmr_alen; /* 地址长度 */
unsigned charmr_address[8]; /* 物理地址 */
};

mr_interfac包含接口索引,他指出了谁将要被改变.
mr_type有:PACKET_MR_MULTICAST用于绑定套接口和由mr_address指定的物理
组播地址;PACKET_MR_PROMISC 用于激活混杂模式以接受所有网络包;
PACKET_DROP_MEMBERSHIP用于撤销绑订或重置.

输入输出控制:
输入输出控制可调用ioct:
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它使用老的结构:
struct sockaddr_pkt
{
unsigned short spkt_family;
unsigned char spkt_device[14];
unsigned short spkt_protocol;
};

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.

linux sock_raw原始套接字编程


sock_raw原始套接字编程可以接收到本机网卡上的数据帧或者数据包,对与监听网络的流量和分析是很有作用的.一共可以有3种方式创建这种 socket
1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包
2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧
3.socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))过时了,不要用啊
理解一下SOCK_RAW的原理, 比如网卡收到了一个 14+20+8+100+4 的udp的以太网数据帧.
首先,网卡对该数据帧进行硬过滤(根据网卡的模式不同会有不同的动作,如果设置了promisc混杂模式的话,则不做任何过滤直接交给下一层输 入例程,否则非本机mac或者广播mac会被直接丢弃).按照上面的例子,如果成功的话,会进入ip输入例程.但是在进入ip输入例程之前,系统会检查系 统中是否有通过socket(PF_PACKET, SOCK_RAW, ..)创建的套接字.如果有的话并且协议相符,在这个例子中就是需要ETH_P_IP或者ETH_P_ALL类型.系统就给每个这样的socket接收缓 冲区发送一个数据帧拷贝.然后进入下一步.
其次,进入了ip输入例程(ip层会对该数据包进行软过滤,就是检查校验或者丢弃非本机ip或者广播ip的数据包等,具体要参考源代码),例子 中就是如果成功的话会进入udp输入例程.但是在交给udp输入例程之前,系统会检查系统中是否有通过socket(AF_INET, SOCK_RAW, ..)创建的套接字.如果有的话并且协议相符,在这个例子中就是需要IPPROTO_UDP类型.系统就给每个这样的socket接收缓冲区发送一个数据 帧拷贝.然后进入下一步.
最后,进入udp输入例程 ...
ps:如果校验和出错的话,内核会直接丢弃该数据包的.而不会拷贝给sock_raw的套接字,因为校验和都出错了,数据肯定有问题的包括所有信息都没有意义了.
进一步分析他们的能力.
1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
能:该套接字可以接收协议类型为(tcp udp icmp等)发往本机的ip数据包,从上面看的就是20+8+100.
不能:不能收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包).
不能:不能收到从本机发送出去的数据包.
发送的话需要自己组织tcp udp icmp等头部.可以setsockopt来自己包装ip头部
这种套接字用来写个ping程序比较适合
     
2. socket(PF_PACKET, SOCK_RAW, htons(x));
这个套接字比较强大,创建这种套接字可以监听网卡上的所有数据帧.从上面看就是20+20+8+100.最后一个以太网crc从来都不算进来 的,因为内核已经判断过了,对程序来说没有任何意义了.
能: 接收发往本地mac的数据帧
能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)
能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)
协议类型一共有四个
ETH_P_IP 0x800      只接收发往本机mac的ip类型的数据帧
ETH_P_ARP 0x806      只接受发往本机mac的arp类型的数据帧
ETH_P_RARP 0x8035     只接受发往本机mac的rarp类型的数据帧
ETH_P_ALL 0x3         接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)
发送的时候需要自己组织整个以太网数据帧.所有相关的地址使用struct sockaddr_ll 而不是struct sockaddr_in(因为协议簇是PF_PACKET不是AF_INET了),比如发送给某个机器,对方的地址需要使用struct sockaddr_ll.
这种socket大小通吃,强悍
下面是一段相关的代码:

     ...
    int
sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    struct sockaddr_ll sll;
     memset( &sll, 0, sizeof(sll) );
     sll.sll_family = PF_PACKET;
    struct ifreq ifstruct;
   strcpy(ifstruct.ifr_name, "eth0");
   ioctl(sockfd, SIOCGIFINDEX, &ifstruct);
    sll.sll_ifindex = ifstruct.ifr_ifindex;
     sll.sll_protocol = htons(ETH_P_ALL);
    if(bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1 ) {
       perror("bind()");
    ...

int set_promisc(char *interface, int fd) {
        struct ifreq ifr;
         strcpy(ifr.ifr_name, interface);
        if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
                 perror("iotcl()");
                 return -1;
         }
         ifr.ifr_flags |= IFF_PROMISC;
        if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
                 perror("iotcl()");
                 return -1;
         }
         return 0;
}

int unset_promisc(char *interface, int fd) {
        struct ifreq ifr;
         strcpy(ifr.ifr_name, interface);
        if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
                 perror("iotcl()");
                 return -1;
         }
         ifr.ifr_flags &= ~IFF_PROMISC;
        if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
                 perror("iotcl()");
                 return -1;
         }
         return 0;
}

3. socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))这个最好不要用,反正我不用...
总结使用方法: 1.只想收到发往本机某种协议的ip数据包的话用第一种就足够了
            2. 更多的详细的内容请使用第二种.包括ETH_P_ALL参数和混杂模式都可以使它的能力不断的加强.


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

chinaunix网友2010-11-24 13:27:52

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com