我是靠谱客的博主 唠叨啤酒,最近开发中收集的这篇文章主要介绍linux eth_p_ip,linux数据链路访问之ETH_P_ALL等等,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

linux自身有两种从数据链路层接收分组:

一种为fd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));

另一种为fd=socket(AF_INET,SOCK_PACKET,htons(ETH_P_ALL));

这其中协议族为PF_PACKET套接字使用较多。

ETH_P_ALL自身定义于  /usr/include/linux/if_ether.h中,

#define ETH_P_ALL       0x0003

ETH_P_ALL占两个字节值为0x0003

其他的:

#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 */

#define ETH_P_X25 0x0805 /* CCITT X.25 */

#define ETH_P_ARP 0x0806 /* Address Resolution packet */

#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN

#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */

#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet*/

以太网封装中,数据链路帧中类型字段为0800指示的数据为ip报文,0806为ARP报文等等。

在socket函数中,若指定第三个参数为ETH_P_ARP则指示只将接收的帧中的哪些类型的帧传递给所创建的套接字。

知道了这些,那么我们可以接收一些if_ether.h里并没有定义的类型的数据链路帧

比如:

socket(PF_PACKET, SOCK_RAW, htons(0x88CC));

这个就是向套接字传递数据链路报文数据类型为88CC的报文,0x88cc报文为LLDP报文,if_ether.h并没有定义。我们使用recvfrom就能够获取到该类型的数据链路帧。

而使用sendto函数可以发送由我们自己定义的数据链路帧报头(这里可以将类型字段设置为0x88cc)。

想想内核将接收的报文传递给原始套接字两个的限制条件:

1)一是TCP/UDP分组绝不会传递给Raw socket

二是大多数ICMP在内核处理完后,传递给Raw socket

三是所有的IGMP在内核处理完后,传递给Raw socket

四是内核不认识其协议字段的IP数据报文传递给Raw socket

五是某个数据以分片的形式到达,则在所有分片到达重组之后,才传递给Raw socket

若以上5个条件满足,那么下面的3个条件也是必需要满足:

2)一是创建原始套接字时,协议字段非0,则接收的数据报文协议字段,必需匹配该值,否则,不传递该报文

二是bind了本地IP地址,数据报的目的地址必需和本地IP地址匹配

三是connect某个外地IP地址,那么则数据报源地址也必须匹配。

也就是说原始套接字实际上是不能够接收tcp/udp分组的,只能使用libpcap库来接收。但是libpcap库的一点限制就是只能接收不能发。

如以下实例发送以太网数据链路帧,指定上层报文类型为0x88cc

/*

author:gstime:2013/9/23

function:send L2 packets

*/

#include#include#include#include#include/*for uint16_t*/

#include#include#includestruct lldpdu

{

char dst[6];

char src[6];

uint16_t type;

};

int main(int argc,char* argv[])

{

struct lldpdu* lpacket = malloc(sizeof(struct lldpdu));

memset(lpacket,0,sizeof(struct lldpdu));

struct sockaddr_ll sll;

memset(&sll,0,sizeof(struct sockaddr_ll));

int sk = socket(PF_PACKET, SOCK_RAW, htons(0x88CC));/*使用pf_packet接口创建套接字*/

sll.sll_family = PF_PACKET;

sll.sll_ifindex = if_nametoindex("eth0");/*sll虽然是用来指定目的地址的,但是在这个结构体中sll_ifindex 却指定的是本机发送报文的接口索引*/

sll.sll_protocol = htons(0x88CC);

sll.sll_addr[0] = 0x34;/*sll_addr指定目的MAC地址*/

sll.sll_addr[1] = 0xb0;

sll.sll_addr[2] = 0x52;

sll.sll_addr[3] = 0xda;

sll.sll_addr[4] = 0xda;

sll.sll_addr[5] = 0x18;

lpacket->dst[0] = 0x34;/*自己构造的L2 报文的目的地址*/

lpacket->dst[1] = 0xb0;

lpacket->dst[2] = 0x52;

lpacket->dst[3] = 0xda;

lpacket->dst[4] = 0xda;

lpacket->dst[5] = 0x18;

lpacket->src[0] = 0x00;/*自己构造的L2 报文的源地址*/

lpacket->src[1] = 0x0c;

lpacket->src[2] = 0x29;

lpacket->src[3] = 0x8d;

lpacket->src[4] = 0xf1;

lpacket->src[5] = 0x04;

lpacket->type = htons(0x88cc);/*报文类型*/

while(1)

{

/*只有L2头,数据为空。每隔3秒发送0X88CC报文,即LLDP报文*/

sendto(sk, lpacket, 14, 0, (struct sockaddr*)&sll, sizeof(struct sockaddr_ll));

printf("send one lldp packetn");

sleep(3);

}

return 0;

}

这之中要注意的是sockaddr_ll,其中的sll_ifindex指定的为本机发送接口索引,而非目的地址接口索引。

以上程序发送的LLDP报文,能够在wireshark中捕捉到。

发现一个奇怪的现象,当sll中指定的目的地址和报文中的目的地址不一致时,会出现什么呢?

经实际测试,发现这种情况下,wireshark捕捉的报文以报文中的目的地址为准。而实际上,sll中不指定目的的MAC地址,报文也能被发送到正确的目的地去。它会使用报文的前6个字节,当做目的地址。

最后

以上就是唠叨啤酒为你收集整理的linux eth_p_ip,linux数据链路访问之ETH_P_ALL等等的全部内容,希望文章能够帮你解决linux eth_p_ip,linux数据链路访问之ETH_P_ALL等等所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部