緬懷Stevens大師。
最好的參考資料:
1.師從互聯網。
2.Linux man 命令:man netlink,man rtnetlink。
3.UNP v1第29章 。
第一條:概述
原始套接口使得我們可以讀寫內核不處理的IP數據報,而對數據鏈路層訪問則把這種能力進一步擴大——讀寫任何類型的數據鏈路幀,而不僅僅是IP數據報。
訪問數據鏈路提供如下兩種能力:
1.監視由數據鏈路層接收的分組。
2.讓某些程序作爲普通的應用進程而不是內核的一部分運行,這對於減小內核大小是非常有益的。
第二條:linux數據鏈路socket
創建數據鏈路socket需要root權限!
可以使用兩種方法創建數據鏈路socket描述符
方法一:發送接收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