用户态协议栈学习笔记

什么是协议栈

我认为是按照相反的顺序解析数据报,打包数据时候从应用层下到物理层,接受到数据的时候解包从物理层到应用层。

数据从网线上到应用程序的流程如下:

在这里插入图片描述

在这里插入图片描述

上图表示每一个TCP连接的读写缓冲区大小

实现一个协议栈怎么做?

  1. 获取最原始的数据(以太网的数据

    1. raw socket

    2. (本次课采用netmap)旁路(自己写一个driver来获得数据,让原来dirver停止工作)netmap, dpdk(其中driver都是用igx_uio,并不是自己实现的)

      注:netmap是一个高效的收发报文的 I/O 框架

    3. hook(bpf/ebpf)

本文工作

自己实现一个UDP协议栈

报文头格式

udp数据帧

在这里插入图片描述

以太网协议头

在这里插入图片描述

IP报文格式

在这里插入图片描述

首部四位,故最大值为15,表示头部最大长度为 15 * 4 = 60B;

标志位一般缺省

UDP协议头

在这里插入图片描述

传输层是不知道应用层的信息,链路层解析时就知道网络层协议,网络层解析报文时就知道传输层的协议,传输层解析报文时不知道应用层协议;

UDP长度位表示UDP整个报文的长度;

协议结构体实现

以太网头、ip头和udp头实现
struct etherhdr {
    unsigned char dst_mac[ETHER_ADDR_LEN];
    unsigned char src_mac[ETHER_ADDR_LEN];
    unsigned short protocol;
};

struct iphdr {
    //将8bit用位域进行划分
    unsigned char version:4,
                hdrlen:4;
    unsigned char tos;
    unsigned short totlen; // 16bit
    unsigned short id;
    unsigned short flag:3,
                offset:13;
    unsigned char ttl;
    unsigned char protocol;
    unsigned short checksum;

    unsigned int sip;
    unsigned int dip;
};

struct udphdr {
    unsigned short sport;
    unsigned short dport;
    unsigned short length;
    unsigned short check;
};
udp数据包结构体
struct udppkt {
    struct etherhdr eth;
    struct iphdr ip;
    struct udphdr udp;
    
    unsigned char payload[0]; //零长数组,直接使得头后面就是数据数组,即数组名不占空间
};

零长数组一般用于:1长度不确定但可计算出来;2内存已分配出来。
常用在 memory pool中

udp数据报的数据部分不能如下定义

unsigned char *payload; //会占用4B空间,没法达到头部后面直接跟数据的效果
unsigned char payload[65535]; //会出现越界的问题

使用netmap进行接管网卡数据

安装完netmap之后,输入下方代码使其工作:

insmod netmap.ko

此时工作流程如下,网卡数据映射到内存上

在这里插入图片描述

在这里插入图片描述

c表示/dev/netmap为字符设备

int main() 
{
    //printf("sizeof(struct ether_hdr) = %ld\n", sizeof(struct ether_hdr));
    struct nm_pkthdr h;
    struct nm_desc *nmr = nm_open("netmap:eth0", NULL, 0, NULL);

    if(nmr == NULL) return -1;
    struct pollfd pfd = {0};
    pfd.fd = nmr->fd;  //nmr->指向/dev/netmap的fd

    pfd.events = POLLIN;

    while(1) {
        //第二个参数:用来指定第一个参数数组元素个数
        //第三个参数:timeout ,-1等待直到事件发生,0是非阻塞直接返回
        int ret = poll(&pfd, 1, -1);
        if(ret < 0) continue;

        if(pfd.revents & POLLIN) {
            //接受网卡上到来的数据包
            //会将所有rx环都检查一遍,当发现一个rx环有需要接受的数据包时,
            //得到这个包的地址,并返回。所以nm_nextpkt()每次只能取一个数据包。
            //返回数据包的地址
            unsigned char *stream = nm_nextpkt(nmr, &h);

            struct etherhdr *eh = (struct etherhdr *)stream;
            if(ntohs(eh->protocol) == PROTO_IP) {
                struct udppkt *pkt = (struct udppkt *)stream;
                if(pkt->ip.protocol == PROTO_UDP) {
                    int length = ntohs(pkt->udp.length);
					//hdr|data,其中hdr占8B,payload相当于|, pkt->payload[length - 8]相当于数据的末尾
                    pkt->payload[length - 8] = '\0';

                    printf("pkt : %s\n", pkt->payload);
                }
            }
        }
    }
    return 0;
}

对nm_open()的解释

在这里插入图片描述

利用netmap,可以使得网卡直接改道,通过nmap的方式(即DMA方式),直接将数据传输进入内存,也就是接管了网卡的数据,/dev/netmap这个fd的写事件到来就置为可读作为一个“数据到来的信号“,并不从这个文件中读数据

read:一般指从外存读到内存

杂项

如何修改网卡名为eth0

  • 客户端宕机,服务端send返回什么?

send本身返回成功,send把用户态数据拷贝到内核空间

  1. 七层网络模型

  2. 以太网 ether

  3. ip协议

  4. icmp ,ping 用到的

  5. arp -a,arp攻击(不管是谁的arp请求都回复,导致arp表比较混乱)

  6. 网卡不工作在七层模型中,只做A/D and D/A转换

  7. 如何高效的发送tcp报文?

     1. **到底在网络上发多少**
     	1.  通过往返时间确定有没有超时,从而放缓发送
         	1.  超时计算 rtt_new = 0.9 * rtt_old + 0.1rtt;
         	2. 确定包
             		**i. 慢启动,拥塞控制**
    
    1. 能够接收多少
      1. TCP的window size字段


8.一个面试题:
​ 若对方一直不recv()数据,对方发来的报文中window size最后变为0,导致本方send()返回-1,表示不能发了

问题

  • 发送数据给eth0的ip一段时间后就不能发送:
    观察arp表,发现数据接受不到的时候,arp表中eth0对应的ip项就没了。问题在于发送数据的时候也有发arp的请求,但是我们的接受程序没有对arp请求处理。所以我们需要实现arp
  • 不能ping通,是因为icmp协议没实现
  • dpdk方式待了解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值