Linux系统里的网络通信宛如错综复杂的迷宫,尤其从数据链路层接收分组这一点,藏着许多值得深入研究的奥秘。这里不仅有高手间的较量,比如两种不同的分组接收方法,还涉及到了多个方面,如协议族、源代码的设定、以太网的封装等。其中隐藏着许多鲜为人知的秘密,正等待着我们去一一揭开它们神秘的面纱。
数据链路层分组接收方式
在Linux系统中,处理数据链路层的分组接收有两种独特的方法。第一种是使用fd=(,,htons());第二种则是fd=(,,htons())。这两种方法在网络操作中扮演着重要角色。设想在网络公司数据中心这样的网络运行环境中,各种程序在运行时可能会根据实际需求选择其中一种方法来接收分组。这两种方法就好比网络通信中的两把钥匙。此外,协议族在套接字的使用上非常普遍,它与这两种接收方式也有着密切的关系。
#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*/
这些接收方式与某些定义文件相关联。比如,有些定义存放在/usr//linux/.h文件中。这些定义对网络通信起到指导作用,就像指挥棒一样,它指导着分组接收的各个环节。同时,这些定义也影响着网络的稳定性以及数据传输的准确性。
以太网封装中的类型字段
以太网封装并不容易,它涉及的数据链路帧中的类型字段承载着关键信息。例如,当类型字段显示为0800时,它代表的是IP数据包;而当显示为0806时,则是指示ARP数据包。在日常网络使用中,比如办公室里员工用电脑办公时,这些类型字段在底层网络传输中默默引导着数据的流动。不同的数值对应不同类型的数据,确保数据能够精确地送达目标位置。
进一步解释,若在函数调用时设定第三个参数,该参数能决定仅将接收到的特定类别的帧传递给所建立的套接字。这相当于一个严格的准入机制,仅对特定身份者(即特定类型的帧)开放。
接收未定义类型的帧
掌握了先前的知识,我们便能够处理那些在.h文件中未明确定义的数据链路帧。例如,使用htons()函数,可以向套接字发送数据链路报文,指定类型为88CC,这指的是LLDP报文。即便.h文件中未对其进行定义,我们依然能够获取这类数据链路帧。这在特定网络环境下具有重要意义,比如在特定网络设备的运维过程中,它犹如发现了一处隐藏的宝藏,让运维人员得以获取到特殊类型的数据。
自定义数据链路帧报头
接收特定种类的帧之外,我们还能通过函数发送自定格式的数据链路帧头部。类型这一栏可以调整为我们所需的数值。比如在网络研发的试验环境中,为了达成特定网络效果,研发者便能运用这一功能。这宛如打开了潘多拉魔盒,尽管面临诸多挑战,却也蕴藏着无尽的机遇。
内核传给原始套接字的条件
内核在将接收到的报文转发给原始套接字时,有着严格的限制。首先,TCP/UDP的数据包是不会被转发给Raw的,它们就像是两个独立的世界。大多数的ICMP报文在内核处理完毕后才会转发给Raw。所有的IGMP报文也是如此。如果内核不认识某个IP数据报的协议字段,也会将其转发给Raw。另外,当一个数据包以分片的形式到达时,只有在所有分片都重组完成后,才会转发给Raw。不仅如此,还要满足其他三个条件,比如在创建原始套接字时,协议字段不能为0,以及其他相关匹配问题。
这些条件彼此协调,就好比在大型网络平台上,犹如精密机器中各部件需紧密配合,若不满足这些要求,原始套接字的功能将受影响,进而严重干扰网络通信。
/*
author:gs
time:2013/9/23
function:send L2 packets
*/
#include
#include
#include
#include
#include/*for uint16_t*/
#include
#include
#include
struct 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 packet\n");
sleep(3);
}
return 0;
}
原始套接字接收限制与示例问题
原始套接字无法直接接收TCP/UDP分组,必须借助库来处理,而库仅能接收不能发送数据。这好比一个人左脚正常,右脚却有问题。举例来说,发送以太网数据链路帧时,若指定上层报文类型为特定值,需要注意一点,那就是所指定的为本机发送接口索引,而非目的地址接口索引。例如,上述程序发送的LLDP报文,可以通过特定工具捕捉到。然而,在实际测试中,若sll中指定的目的地址与报文中的目的地址不匹配,捕捉到的报文将依据报文中的目的地址。另外,即使sll未指定目的MAC地址,报文也能正确发送到目的地,这是因为它会使用报文的前6个字节作为目的地址。这些现象背后,实际上揭示了网络运行更深层次的原理。
在网络调试过程中,你是否曾遭遇过这类地址相关的不寻常情况?欢迎在评论区留言,同时,也请为这篇文章点赞和转发。
暂无评论
发表评论