用户态协议栈-TCPIP

用户态协议栈:
把协议栈当应用来写
网络协议的解析,放到应用层

本来协议栈是在系统层,用户态是调用到应用层来写的。


系统调用
listen
accept


为什么会有用户态协议栈
1、减少CPU上下切换


网卡作用:光电信号(模拟信号)转换为数字信号,AD转换,在物理层和数据链路层之前

 

内核态协议栈处理:
网卡把数据放到sk_buff,
协议栈对网卡数据进行解析,放到recvbuff中

两次拷贝:网卡copy到协议栈 协议栈copy到应用程序

将网卡内存映射到内存中间,dma方式,内存直接通道,减少网卡到协议栈拷贝,减少一次拷贝

dma没有拷贝,dma方式,cpu不需要干预,网卡数据直接到内存,零拷贝,即cpu没有干预

mmap原理:
磁盘中间的文件进行映射,蓝牙、U盘、网卡,都可以进行dma方式,把存储映射到内存中间
mmap前提:有dma总线,即支持dma方式

dma方式:数据映射完,会给cpu发送触发中断。

网卡驱动运行在哪里:运行在内核中,nic子系统,使网卡正常工作的

 

从网卡中取到一帧完整的数据:

 

实现一个协议栈:
1、netmap 开源

 

 

以太网协议头:

 

#define ETH_ALEN    6

struct ethhdr {
    unsigned char h_dest[ETH_ALEN];
    unsigned char h_source[ETH_ALEN];
    unsigned short h_proto;
};

ip头:


struct iphdr {
    unsigned char version;
    unsigned char tos;//实时流数据,流媒体数据方法,不一样的
    unsigned short tot_len;//ip包总长度,65535byte=64k
    unsigned short id;//每个ip包都有id,
    unsigned short flag_off;
    unsigned char ttl;//8位生存时间,每经过一个网关减一,0时网关就丢弃,返回目的不可到达。
    unsigned char protocol;
    unsigned short check;
    unsigned int saddr;
    unsigned int daddr;
};
MTU是网卡的限制,超过1500byte,ip就会进行分包。

 

UDP头:
struct udphdr {
    unsigned short source;
    unsigned short dest;
    unsigned short len;
    unsigned short check;
};
UDP没有数据包的概念

MAC地址是以太网产物,
IP地址是网络层产物
端口号是传输层产物

路由器工作在网络层的

二层交换机在局域网内,在链路层,只对mac地址进行交换

在网络层,则需要路由器,或者是三层交换机

nat 需要对传输层协议进行,

负载均衡LB:
nginx  工作在应用层
haproxy  tcp端口代理处理,传输层
lvs    网络层
f5     数据联络层


UDP packet:
struct udppkt {
    struct ethhdr eh;
    struct iphdr ip;
    struct udphdr udp;
    unsigned char body[0];//柔性数组,数组首地址
};
sizeof(udppkt)= 44;
柔性数组使用条件:
1、内存已分配好
2、柔性数组的长度可以计算出来

netmap 依赖库,翻墙,url以浏览器方式下载
virtio,uio

 

将网卡映射到内存中间,应用程序之间在网卡中取数据

虚拟机中将改为ens33->etho

#include <sys/poll.h>
#include <arpa/inet.h>


#define NETMAP_WITH_LIBS

#include <net/netmap_user.h> 
#pragma pack(1)   //以一个字节对齐,sizeof(udppkt)= 42,这时就不是上面的44字节了

#define PROTO_IP    0x0800
#define PROTO_ARP    0x0806

#define PROTO_UDP    17
#define PROTO_ICMP    1
#define PROTO_IGMP    2

struct udppkt {
    struct ethhdr eh;
    struct iphdr ip;
    struct udphdr udp;
    unsigned char body[0];//柔性数组,数组首地址
};

int main() {
    
    struct ethhdr *eh;
    struct pollfd pfd = {0};
    struct nm_pkthdr h;
    unsigned char *stream = NULL;

    struct nm_desc *nmr = nm_open("netmap:eth0", NULL, 0, NULL);//将网卡的数据映射到内存中,不会再走内核协议栈,etch0被netmap接管
    if (nmr == NULL) {
        return -1;
    }

    pfd.fd = nmr->fd;
    pfd.events = POLLIN;

    while (1) {
        int ret = poll(&pfd, 1, -1);//dma方式数据映射完,会给cpu发送触发中断。知道网卡来数据了,netmap封装的数据
        if (ret < 0) continue;
        
        if (pfd.revents & POLLIN) {
            stream = nm_nextpkt(nmr, &h);//操作内存,循环队列,读出下一个包,netmap封装,纯内存操作,没有所谓的阻塞和非阻塞概念
            eh = (struct ethhdr*)stream;

            if (ntohs(eh->h_proto) == PROTO_IP) {

                struct udppkt *udp = (struct udppkt*)stream;
                if (udp->ip.protocol == PROTO_UDP) {//ntohs()??

                    struct in_addr addr;
                    addr.s_addr = udp->ip.saddr;

                    int udp_length = ntohs(udp->udp.len);
                    printf("%s:%d:length:%d, ip_len:%d --> ", inet_ntoa(addr), udp->udp.source, 
                        udp_length, ntohs(udp->ip.tot_len));

                    udp->body[udp_length-8] = '\0';
                    printf("udp --> %s\n", udp->body);
#if 1
                    struct udppkt udp_rt;
                    echo_udp_pkt(udp, &udp_rt);
                    nm_inject(nmr, &udp_rt, sizeof(struct udppkt));
#endif
#if 0
                } else if (udp->ip.protocol == PROTO_ICMP) {//ntohs()??
                    
                    struct icmppkt *icmp = (struct icmppkt*)stream;

                    printf("icmp ---------- --> %d, %x\n", icmp->icmp.type, icmp->icmp.check);
                    if (icmp->icmp.type == 0x08) {
                        struct icmppkt icmp_rt = {0};
                        echo_icmp_pkt(icmp, &icmp_rt);

                        //printf("icmp check %x\n", icmp_rt.icmp.check);
                        nm_inject(nmr, &icmp_rt, sizeof(struct icmppkt));
                    }
#endif    
                } else if (udp->ip.protocol == PROTO_IGMP) {//ntohs()??

                } else {
                    printf("other ip packet");
                }
#if 0
            }  else if (ntohs(eh->h_proto) == PROTO_ARP) {

                struct arppkt *arp = (struct arppkt *)stream;
                struct arppkt arp_rt;

                if (arp->arp.dip == inet_addr("192.168.2.217")) {
                    echo_arp_pkt(arp, &arp_rt, "00:50:56:33:1c:ca");
                    nm_inject(nmr, &arp_rt, sizeof(struct arppkt));
                }
#endif
            }
        } 
    }
}

libpcap 原生socket
netmap 对socket进行了优化

read :从外存读到内存
write:

外界向网卡发送数据时,网卡将数据映射到内存中,内存中用循环队列存储,只要记得队列都在哪,

0拷贝运用场景
日志操作 fwrite,write
mmap,open文件映射到内存,日志 落盘到内存,由netmap操作内存写到文件中
效率:mmap效率比之间操作文件要快。
缺点:可能会丢数据,进程断电时,会丢几帧数据。不能保证数据完全存入到文件

ntohs 网络字节 操作2字节以上的 必须做网络字节序转换 网络字节转本地


arp,linux用netmap接管的网卡,代码中没有实现arp协议,windows arp缓存表超时删除了接管网卡的mac地址,没有学到mac地址,所以ping不通,
同时接管的网卡也没有实现icmp协议

arp:局域网内,广播,我是192.168.0.1 你的mac地址是多少,本地建立arp表,ip地址是多少,mac地址是多少

windows下 查看arp表:
arp -a

UDP优点
1、实时性强,游戏
2、不带拥塞控制,传输速度比tcp快

用户态协议栈使用场景:网关,负载均衡,网络防火墙,性能,


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值