网络体系结构(2)链路层

来源:互联网 发布:列数据的句子 编辑:程序博客网 时间:2024/05/21 17:34

引言

TCP/IP协议族中最低层的就是链路层。链路层主要提供三个功能:接收和发送IP数据报;发送ARP请求和接收ARP应答;发送和接收RARP请求和应答。TCP/IP支持多种不同的链路层协议,至于具体采用什么样的协议则取决于硬件,目前使用的最多的就是以太网,所以也主要针对以太网进行说明。

这里要注意一点就是ARP和RARP是两种不同的协议,并且不是协同工作的。不要因为两个分别是将mac地址和ip地址进行变换就认为两者是相互作用,这两个协议主要应用在不同的场景下


以太网

以太网采用CSMA/CD媒体介入方式。且以太网的数据封装格式如下图:


这里使用一个小的代码片来认识以太网的数据帧格式:

#include#include#include#include#include#include#include#include#include#include#define ETH_ALEN 6#define ETH_HLEN 14#define ETH_ZLEN 60#define ETH_DATA_LEN 1500#define ETH_FRAME_LEN 1514int main(int argc,char **argv){int ret;char  ef[ETH_FRAME_LEN];//创建缓存存放以太网数据帧int fd;struct ethhdr *pethhdr;struct arphdr *parp;int i;int n;//读取的字节数fd=socket(AF_INET,SOCK_PACKET,htons(0x0003));if(fd<0){perror("socket");return -1;}n=read(fd,ef,ETH_FRAME_LEN);if(n<0){perror("read()");return -1;}pethhdr=(struct ethhdr*)ef;printf("目的地址:");for(i=0;ih_dest[i]);printf("%x\n",pethhdr->h_dest[ETH_ALEN-1]);printf("源地址:");for(i=0;ih_source[i]);printf("%x\n",pethhdr->h_source[ETH_ALEN-1]);printf("协议类型:%#x\n",htons(pethhdr->h_proto));return 0;}
该代码执行结果:

该代码仅仅抓取了一个数据帧,如果要进一步抓取更多数据帧可以使用循环。这里不多阐述。这段代码使用了原始套机字抓取链路层数据包。相关的编程理论可以查阅《unix环境高级编程》(卷一)第29章。[本例没有采用书中的样例]。注意使用该程序需要在特权模式下执行。

以太网的数据帧中目的地址和源地址各占是6个字节。表示接受方主机的硬件地址和发送方主机的硬件地址,通过这个例子可以看出硬件地址虽然是标识在网卡上的,但是我们可以通过软件的方式进行改写,这也是进行网络欺骗攻击的一种方式;类型占2个字节,如果是0x0800则为IP数据报,如果是0x0806则为ARP数据报,如果是0x8035则为RARP数据报;数据部分根据类型定义的不同而不同,在以太网中该字段为46—1500字节;最后一个字段为CRC用于帧后续字节的循环冗余校验。

针对数据包的向上传递在链路层都是使用内核进行处理的。但是,目前很多数据包都是传递到应用层对数据包的处理,使用内核进行数据包的层层传递似乎有点影响我们的性能。那么针对这个问题Intel公司在针对自己的X86结构上开发了一套自己的开发套件DPDK该套件利用轮询技术将数据接收之后直接传递到我们的应用层,从而绕过了内核,防止了用户空间和内核空间进行的数据复制。同时这块套件中也提供了KNI这套组件,为的就是让内核提供部分服务。目前针对DPDK同样开发出了部分的应用层协议栈。比如ANS


接口信息

针对以太网,系统也同时提供了相关的命令对以太网接口进行配置。ifconfig是比较老的配置命令,该命令目前还在使用中,且命令使用ioctl函数对接口进行操作。



使用ioctl函数有一大缺点,主要就是只能进行内核和用户空间的单向通信,也正是这样的原因,iproute2套件使用了一个ip命令,该命令使用netlink套接字与内核进行通信。旨在取缔ipfconfig这个命令。

这里通过写一段代码获取网络信息类似于ifconfig -a的信息获取命令,相关设置编程可以进一步自行扩展。

#include#include#include#include#include#include#include#include#include#include#define ETH_ALEN 6#define ETH_HLEN 14#define ETH_ZLEN 60#define ETH_DATA_LEN 1500#define ETH_FRAME_LEN 1514int main(int argc,char **argv){int ret;char  ef[ETH_FRAME_LEN];//创建缓存存放以太网数据帧int fd;struct ethhdr *pethhdr;struct arphdr *parp;int i;int n;//读取的字节数fd=socket(AF_INET,SOCK_PACKET,htons(0x0003));if(fd<0){perror("socket");return -1;}n=read(fd,ef,ETH_FRAME_LEN);if(n<0){perror("read()");return -1;}pethhdr=(struct ethhdr*)ef;printf("目的地址:");for(i=0;ih_dest[i]);printf("%x\n",pethhdr->h_dest[ETH_ALEN-1]);printf("源地址:");for(i=0;ih_source[i]);printf("%x\n",pethhdr->h_source[ETH_ALEN-1]);printf("协议类型:%#x\n",htons(pethhdr->h_proto));return 0;}
该代码执行结果如下:


该代码仅仅显示了接口的部分信息。也就是针对这方面的编程抛初一个引子,后面如果要针对其他信息获取或者修改可以参照上面的内容进行扩展。针对ifreq结构可以访问/usr/include/linux/if.h。

此外针对接口还有netstat -i命令。

网卡在对数据接收之后的一系列操作在另外一篇博文中有说明这里不再重复。



0 0