Linux——数据链路访问

缅怀Stevens大师。html

最好的参考资料:

1.师从互联网。linux

2.Linux man 命令:man   netlink,man rtnetlink。c++

3.UNP v1第29章 。编程

第一条:概述

原始套接口使得咱们能够读写内核不处理的IP数据报,而对数据链路层访问则把这种能力进一步扩大——读写任何类型的数据链路帧,而不单单是IP数据报。socket

访问数据链路提供以下两种能力:函数

1.监视由数据链路层接收的分组。oop

2.让某些程序做为普通的应用进程而不是内核的一部分运行,这对于减少内核大小是很是有益的。性能

第二条:linux数据链路socket

建立数据链路socket须要root权限!ui

可使用两种方法建立数据链路socket描述符spa

方法一:发送接收ip数据包:raw socket:参见上一篇文章。再也不讨论。

 

int sockfd=socket(AF_INET, SOCK_RAW, IPPROTO_XXX);

 

方法二:发送接收以太网数据帧有两种方法:

新方法:int sockfd=socket(PF_PACKET,sock_type,eth_protocol);//引入了更多的过滤和性能特性。

sock_type参数的值:

SOCK_DGRAM:表示扣除链路层头部的“煮熟”分组(cooked)。

SOCK_RAW:表示“未煮”的完整的链路层分组——以太网帧(raw)。

能够将这个套接口设置为混杂模式(promiscuous mode),以下:

 

 int sockfd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP));

struct ifreq ifr;

struct packet_mreq mreq;

bzero(&ifr,sizeof(ifr));//初始化ifr

strcpy(ifr.ifr_name,"eth0");//这里的“eth0”能够换成其余设备的名字

if(ioctl(sockfd,SIOCGIFINDEX,&ifr)<0)//获得设备对应的索引

{

 

perror("ioctl SIOCGIFINDEX:");            

}

mreq.mr_type=PACKET_MR_PROMISC;//混杂模式

mreq.mr_ifindex=ifr.ifr_ifindex;//设定套接口将要投入的混杂模式的设备索引号

mreq.mr_alen=0;

mreq.mr_address[0]='/0';

if(setsockopt(sockfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)

{

   perror("setsockopt");

   exit(0);

 }

 

旧方法:int sockfd=socket(AF_INET,SOCK_PACKET,eth_protocol);//UNPv1中:这个方法可用面广,但缺少灵活性。只返回以太网帧。

 

一样也可将旧方法建立socket投入到混杂模式:

 

int sockfd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP));

struct ifreq ifr;

bzero(&ifr,sizeof(ifr));

strcpy(ifr.ifr_name,"eth0");

if(ioctl(sockfd,SIOCGIFFLAGS,&ifr)<0){

perror("ioctl SIOCGIFFLAGS:");

}

ifr.ifr_flags |=IFF_PROMISC;

if(ioctl(sockfd,SIOCSIFFLAGS,&ifr)<0){

perror("ioclt SIOCSIFFLAGS:");

}

 

 

eth_protocol参数的值以下,这个参数是用来告诉数据链路层把什么类型的帧传给锁建立的socket:定义在linux/if_ether.h

#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) *///从数据链路接收全部帧

 

#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */

#define ETH_P_PUP 0x0200 /* Xerox PUP packet */

#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */

#define ETH_P_IP 0x0800 /* Internet Protocol packet *///只接收IPv4帧

第三条:关于Linux数据链路socket的内核缓冲、内核过滤、设备过滤

依据UNPv1中的描述以下:

1.内核缓冲:新旧方法,都不提供内核缓冲。

2.内核过滤:旧方法不支持,新方法建立的socket可按以下方式,安装过滤器:

/*Try and keep these values and structures similar to BSD, especially  the BPF code definitions which need to match so you can share filters  */

//这两个结构定义在linux/filter.h

struct sock_filter { /* Filter block */

__u16 code;   /* Actual filter code */

__u8 jt; /* Jump true */

__u8 jf; /* Jump false */

__u32 k;      /* Generic multiuse field */

};

struct sock_fprog { /* Required for SO_ATTACH_FILTER. */

unsigned short len; /* Number of filter blocks */

struct sock_filter *filter;

};

struct sock_fprog Filter;

//////////////////////////////////

这里对Filter初始化;

//////////////////////////////////

setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter));//安装过滤器

setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, NULL, 0);//卸载过滤器。若以前close了sockfd过滤器将自动卸载!!

关于这SO_ATTACH_FILTER、SO_DETACH_FILTER:man 7 socket中指出:

BUGS:

The  CONFIG_FILTER socket options SO_ATTACH_FILTER and SO_DETACH_FILTER are not documented.  The suggested interface to use  them  is  via  the  libpcap library。

3.设备过滤:旧方法一样不支持,新方法能够经过bind函数使socket关联相关设备:

   struct sockaddr_ll addr;//linux/if_packet.h

   bzero(&addr, sizeof(addr));

   addr.sll_family   = PF_PACKET;

   addr.sll_protocol = htons(ETH_P_ALL);

   addr.sll_ifindex  = ifr.ifr_ifindex;//此值获取方法,见上面。

   if( bind(sockfd, (struct sockaddr*) &addr, sizeof(addr))<0)

                       perror("bind error:");

第四条:libpcap、libnet

libnet百度百科:http://baike.baidu.com/view/1520138.htm

libpcap百度百科:http://baike.baidu.com/view/1319961.htm

 

libnet/libnids库函数介绍:http://blog.ccidnet.com/blog-htm-do-showone-uid-36931-itemid-138338-type-blog.html

 

linux下libnet编程:http://xdz2005.blog.163.com/blog/static/107873282010710112519755/

libpcap函数库详细介绍:http://dev.firnow.com/course/3_program/c++/cppjs/2008324/106112.html