日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

內核里面已經有網絡協議棧了,為什么還要實現一遍用戶態協議棧呢,主要是站在一個設計者的角度,自己去嘗試實現一個協議棧,那么對協議棧的理解會比較透徹,這不比背八股文強?

獲取原始數據

獲取原始數據的三種方法介紹

1、使用原始套接字raw socket , tcpdump和wireshark就是使用這個做的,raw socket主要用來抓包。

2、dbdk,使用dbdk的話篇幅較長,這里就不展開了,有興趣的可以看

Dpdk/網絡協議棧/vpp/OvS/DDos/NFV/虛擬化/高性能專家-學習視頻教程-騰訊課堂

3?.NETmap是用于用戶層應用程序收發原始網絡數據的高性能框架,本文使用netmap進行數據的收發。

netmap

內核協議棧的數據到應用層的數據會經歷兩次拷貝,而netmap采用mmap的方式,直接將網卡的數據映射到一塊內存中,應用程序可以直接通過mmap操作相應內存的數據。

用戶態協議棧設計實現udp,arp與icmp協議

 

零拷貝

其實通過上面的圖我們就能看到,采用傳統的內核協議棧的方式,會發生兩次拷貝,一是網卡數據拷貝到內核協議棧;二是內核再拷貝到內存中去。而netmap采用的則是零拷貝。

所謂零拷貝,指的是不由CPU操作,copy這個動作是由cpu發出指令move實現的,所以零拷貝就是不由CPU管理,由DMA管理。DMA允許外設與內存直接進行數據傳輸,這個過程不需要CPU的參與。

更多的零拷貝相關內容看一下這個徹底搞懂零拷貝(Zero-Copy)技術

netmap安裝與常用api介紹

安裝netmap

單獨寫一篇安裝教程,按照這個來即可手把手教你ubuntu18.04安裝netmap

netmap的頭文件#include<net/netmap_user.h>在 /netmap/sys/net/下

nm_open

調用 nm_open 函數時,如:nmr = nm_open("netmap:ens33", NULL, 0, NULL); nm_open()會對傳遞的 ifname 指針里面的字符串進行分析,提取出網絡接口名。

nm_open() 會 對 struct nm_desc *d 申 請 內 存 空 間 , 并 通 過 d->fd =open(NETMAP_DEVICE_NAME, O_RDWR);打開一個特殊的設備/dev/netmap 來創建文件描述符 d->fd。

注意這個fd是/dev/netmap這個網卡設備,網卡只要來數據了,相應的這個fd就會有EPOLLIN事件,這個fd是檢測網卡有沒有數據的,因為是mmap,只要網卡有數據了,那么內存就有數據的。

fd是指向網卡,操作數據是操作內存,內存和網卡數據的同步的,而我們cpu只能操作內存,不能操作外設。

簡而言之,struct nm_desc里面包含一個fd,這個fd指向/dev/netmap,用于poll、epoll等系統調用。

一旦調用 nm_open 函數,網卡的數據就不從內核協議棧走了,這時候最好在虛擬機中建兩個網卡,一個用于netmap,一個用于ssh等應用程序的正常工作。

struct nm_desc *nm_open(const char *ifname, const struct nmreq *req, uint64_t new_flags, const struct nm_desc *arg);

struct nm_desc *nmr = nm_open("netmap:ens33", NULL, 0, NULL);

nm_nextpkt

nm_nextpkt是用來接收網卡上到來的數據包的函數。nm_nextpkt會將所有 rx 環都檢查一遍,當發現有一個 rx 環有需要接收的數據包時,得到這個數據包的地址,并返回。所以 nm_nextpkt()每次只能取一個數據包。 因為接收到的數據包沒有經過協議棧處理,因此需要在用戶程序中自己解析。rx 環:想象成一個環形隊列即可,每一項就是一個數據包。

stream即為數據在緩沖區中的首地址,struct nm_pkthdr為返回的數據包頭部信息,不需要管頭部的話直接從stream去取數據就行了。stream現在就是鏈路層的數據

用戶態協議棧設計實現udp,arp與icmp協議

 

static u_char *nm_nextpkt(struct nm_desc *d, struct nm_pkthdr *hdr);

unsigned* stream = nm_nextpkt(nmr, &nmhead);

nm_inject

nm_inject()是用來往共享內存中寫入待發送的數據包數據的。數據包經共享內存拷貝到網卡,然后發送出去。所以 nm_inject()是用來發包的。

nm_inject()也會查找所有的發送環(tx 環),找到一個可以發送的槽,就將數據包寫入并返回,所以每次函數調用也只能發送一個包。

static int nm_inject(struct nm_desc *d, const void *buf, size_t size);

nm_inject(nmr,&arp_rt,sizeof(arp_rt));

nm_close

nm_close 函數就是回收動態內存,回收共享內存,關閉文件描述符什么的了。

static int nm_close(struct nm_desc *d)

nm_close(nmr);

相關視頻推薦

手寫一個用戶態協議棧以及零拷貝的實現

從netmap到dpdk,從硬件到協議棧,4個維度讓網絡體系構建起來

學習地址:C/C++Linux服務器開發/后臺架構師【零聲教育】-學習視頻教程-騰訊課堂

需要C/C++ linux服務器架構師學習資料加qun812855908獲取(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK,ffmpeg等),免費分享

用戶態協議棧設計實現udp,arp與icmp協議

 

協議棧

協議棧的定義

所謂協議棧,“棧”怎么理解,先進后出,正如下圖udp協議所示。在應用層我們調用sendto發送數據時,我們需要在用戶數據前面加上udp的協議頭,然后進入網絡層加入ip頭,進入鏈路層加入以太網的頭,最后由網卡進行數模轉換變成光電信號發送給對端。對端網卡接收到光電信號后進行模數轉換,再依次拆包,最終到達應用層就是我們最初發送的數據了。這個過程就像棧一樣,先進后出。

協議棧在一定意義是又可以稱為協議族,“族”怎么理解,我們看到傳輸層有udp,tcp等協議,網絡層有ip,icmp協議等等,這些協議形成了一個家族。

用戶態協議棧設計實現udp,arp與icmp協議

 

內核協議棧幫我們解析了傳輸層,網絡層和數據鏈路層的協議,所以我們用戶態協議棧正是去做這三層的協議。

用戶態協議棧設計實現udp,arp與icmp協議

 

鏈路層首部

以太網協議

用戶態協議棧設計實現udp,arp與icmp協議

 

?以太網協議:兩個地址皆為6字節的mac地址,后面2字節的類型區分是什么協議

#pragma pack(1) //一字節對齊
#define NETMAP_WITH_LIBS
#define ETH_ADDR_LENGTH 6
#define PROTO_IP 0x0800 //ip協議
#define PROTO_ARP 0x0806 //arp請求協議
#define PROTO_RARP	0x0835 //rarp應答協議
#define PROTP_UPD 17
struct ethhdr {
    unsigned char h_dst[ETH_ADDR_LENGTH];
    unsigned char h_src[ETH_ADDR_LENGTH];
    unsigned short h_proto;
};

網絡層首部

ip協議

用戶態協議棧設計實現udp,arp與icmp協議

 

?關于ip協議里面第一個字節內的大小端轉換不懂的請查看大端與小端概念、多字節之間與單字節多部分的大小端轉換詳解

struct iphdr {
    unsigned char hdrlen: 4, //一字節,手動大小端轉換
            version: 4;
    unsigned char tos;
    unsigned short totlen;
    unsigned short id;
    unsigned short flag_offset;
    unsigned char ttl;
    unsigned char type;
    unsigned short check;
    unsigned int sip;
    unsigned int dip;
};

struct ippkt {
    struct ethhdr eh; //14
    struct iphdr ip; //20
};

arp協議

用戶態協議棧設計實現udp,arp與icmp協議

 

struct arphdr {
    unsigned short h_type;
    unsigned short h_proto;

    unsigned char h_addrlen;
    unsigned char h_protolen;

    unsigned short oper;

    unsigned char smac[ETH_ADDR_LENGTH];
    unsigned int sip;
    unsigned char dmac[ETH_ADDR_LENGTH];
    unsigned int dip;
};

struct arppkt {
    struct ethhdr eh;
    struct arphdr arp;
};

傳輸層首部

udp協議

這里傳輸層在本文中先只介紹udp,tcp下文再寫

用戶態協議棧設計實現udp,arp與icmp協議

 

?為什么在udppkt里面,定義了一個unsigned char data[0];,這個叫柔性數組,或者叫零長數組,是用來定義用戶數據包的起始地址而不占用實際的結構體空間。具體內容自行百度。

struct udphdr {
    unsigned short sport;
    unsigned short dport;
    unsigned short length;
    unsigned short check;
};

struct udppkt {
    struct ethhdr eh; //14
    struct iphdr ip; //20
    struct udphdr udp;//8
    unsigned char data[0];
};

用戶態協議棧設計實現

1. 實現udp協議

設計思路

我們使用udp工具,給我們的服務器發送udp包,看是否能解析出來,然后按再回發回去

如果要解析udp,那么協議順序為 ether -> ip ->udp,所以我們按照這個順序依次解析即可。

用戶態協議棧設計實現udp,arp與icmp協議

 

?代碼

//
// Created by 68725 on 2022/7/19.
//
#include <stdio.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define NETMAP_WITH_LIBS
#include <net/netmap_user.h>
#include <string.h>

#pragma pack(1)

#define ETH_ADDR_LENGTH 6
#define PROTO_IP 0x0800
#define PROTO_ARP 0x0806
#define PROTP_UPD 17
struct ethhdr {
    unsigned char h_dst[ETH_ADDR_LENGTH];
    unsigned char h_src[ETH_ADDR_LENGTH];
    unsigned short h_proto;
};

struct iphdr {
    unsigned char hdrlen: 4,
            version: 4;
    unsigned char tos;
    unsigned short totlen;
    unsigned short id;
    unsigned short flag_offset;
    unsigned char ttl;
    unsigned char type;
    unsigned short check;
    unsigned int sip;
    unsigned int dip;
};

struct ippkt {
    struct ethhdr eh; //14
    struct iphdr ip; //20
};

struct udphdr {
    unsigned short sport;
    unsigned short dport;
    unsigned short length;
    unsigned short check;
};

struct udppkt {
    struct ethhdr eh; //14
    struct iphdr ip; //20
    struct udphdr udp;//8
    unsigned char data[0];
};

struct arphdr {
    unsigned short h_type;
    unsigned short h_proto;

    unsigned char h_addrlen;
    unsigned char h_protolen;

    unsigned short oper;

    unsigned char smac[ETH_ADDR_LENGTH];
    unsigned int sip;
    unsigned char dmac[ETH_ADDR_LENGTH];
    unsigned int dip;
};

struct arppkt {
    struct ethhdr eh;
    struct arphdr arp;
};
void echo_udp_pkt(struct udppkt *udp, struct udppkt *udp_rt) {
    memcpy(udp_rt, udp, sizeof(struct udppkt));
    memcpy(udp_rt->eh.h_dst, udp->eh.h_src, ETH_ADDR_LENGTH);
    memcpy(udp_rt->eh.h_src, udp->eh.h_dst, ETH_ADDR_LENGTH);
    udp_rt->ip.sip = udp->ip.dip;
    udp_rt->ip.dip = udp->ip.sip;
    udp_rt->udp.sport = udp->udp.dport;
    udp_rt->udp.dport = udp->udp.sport;
}
int main() {
    struct nm_pkthdr h;
    struct nm_desc *nmr = nm_open("netmap:ens33", NULL, 0, NULL);
    if (nmr == NULL) {
        return -1;
    }
    printf("open ens33 seccessn");
    struct pollfd pfd = {0};
    pfd.fd = nmr->fd;
    pfd.events = POLLIN;
    while (1) {
        printf("new data coming!n");
        int ret = poll(&pfd, 1, -1);
        if (ret < 0) {
            continue;
        }
        if (pfd.revents & POLLIN) {
            unsigned char *stream = nm_nextpkt(nmr, &h);
            //ether
            struct ethhdr *eh = (struct ethhdr *) stream;
            if (ntohs(eh->h_proto) == PROTO_IP) {
            	//ip
                struct ippkt *iph=(struct ippkt *)stream;
                if (iph->ip.type == PROTP_UPD) {
                	//udp
                    struct udppkt *udp = (struct udppkt *) stream;
                    int udplength = ntohs(udp->udp.length);
                    udp->data[udplength - 8] = '';
                    printf("udp ---> %sn", udp->data);
                    struct udppkt udp_rt;
                    echo_udp_pkt(udp, &udp_rt);
                    nm_inject(nmr, &udp_rt, sizeof(struct udppkt));
                }
            }
        }
    }
    nm_close(nmr);
}

測試能否解析udp包的數據

  • 開啟netmap

每次重啟使用前都需要insmod netmap.ko ,然后我們查看ls /dev/netmap -l,出現下面的設備就說明開啟成功了。

root@wxf:/netmap/LINUX# insmod netmap.ko 
root@wxf:/netmap/LINUX# ls /dev/netmap -l
crw------- 1 root root 10, 54 Jul 18 17:28 /dev/netmap
用戶態協議棧設計實現udp,arp與icmp協議

 

  • 運行上面的udp測試代碼

可以看到我們能夠正常的接收udp數據

用戶態協議棧設計實現udp,arp與icmp協議

 

?但是為什么過了一會再發數據,程序就接收不到了,而且還多了這么多非udp的數據包??因為宿主機不知道虛擬機的ip和mac地址了,我們查看arp表,發現沒有192.168.109.100虛擬機的記錄,此時宿主機會在局域網內廣播arp請求,這也就是為什么new data coming但不是udp的原因。剛開始能正常解析數據是因為,虛擬機剛開機的時候,我用xshell連虛擬機,所以宿主機發送過arp請求,而虛擬機的內核協議棧此時還沒被netmap接管,回應了arp請求,宿主機就將虛擬機的信息暫時的添加到arp表中,當動態arp記錄失效,udp包不知道發給誰,就會先發arp請求。

用戶態協議棧設計實現udp,arp與icmp協議

 


用戶態協議棧設計實現udp,arp與icmp協議

 

?解決這個問題的辦法很簡單,要么我們手動在宿主機上添加一條靜態的arp記錄,要么我們實現arp協議,下面我們來實現arp協議。

2.實現arp協議

設計思路

我們知道arp協議是在網絡層的,所以我們先解析ether,再解析arp即可

用戶態協議棧設計實現udp,arp與icmp協議

 


用戶態協議棧設計實現udp,arp與icmp協議

 

?代碼

//
// Created by 68725 on 2022/7/19.
//
#include <stdio.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define NETMAP_WITH_LIBS
#include <net/netmap_user.h>
#include <string.h>

#pragma pack(1)
#define ETH_ADDR_LENGTH 6
#define PROTO_IP 0x0800
#define PROTO_ARP 0x0806
#define PROTO_RARP	0x0835
#define PROTP_UPD 17
struct ethhdr {
    unsigned char h_dst[ETH_ADDR_LENGTH];
    unsigned char h_src[ETH_ADDR_LENGTH];
    unsigned short h_proto;
};

struct iphdr {
    unsigned char hdrlen: 4,
            version: 4;
    unsigned char tos;
    unsigned short totlen;
    unsigned short id;
    unsigned short flag_offset;
    unsigned char ttl;
    unsigned char type;
    unsigned short check;
    unsigned int sip;
    unsigned int dip;
};

struct ippkt {
    struct ethhdr eh; //14
    struct iphdr ip; //20
};

struct udphdr {
    unsigned short sport;
    unsigned short dport;
    unsigned short length;
    unsigned short check;
};

struct udppkt {
    struct ethhdr eh; //14
    struct iphdr ip; //20
    struct udphdr udp;//8
    unsigned char data[0];
};

struct arphdr {
    unsigned short h_type;
    unsigned short h_proto;

    unsigned char h_addrlen;
    unsigned char h_protolen;

    unsigned short oper;

    unsigned char smac[ETH_ADDR_LENGTH];
    unsigned int sip;
    unsigned char dmac[ETH_ADDR_LENGTH];
    unsigned int dip;
};

struct arppkt {
    struct ethhdr eh;
    struct arphdr arp;
};
int str2mac(char *mac, char *str) {
    char *p = str;
    unsigned char value = 0x0;
    int i = 0;
    while (p != '') {
        if (*p == ':') {
            mac[i++] = value;
            value = 0x0;
        }
        else {
            unsigned char temp = *p;
            if (temp <= '9' && temp >= '0') {
                temp -= '0';
            }
            else if (temp <= 'f' && temp >= 'a') {
                temp -= 'a';
                temp += 10;
            }
            else if (temp <= 'F' && temp >= 'A') {
                temp -= 'A';
                temp += 10;
            }
            else {
                break;
            }
            value <<= 4;
            value |= temp;
        }
        p++;
    }
    mac[i] = value;
    return 0;
}

void echo_arp_pkt(struct arppkt *arp, struct arppkt *arp_rt, char *mac) {
    memcpy(arp_rt, arp, sizeof(struct arppkt));
    memcpy(arp_rt->eh.h_dst, arp->eh.h_src, ETH_ADDR_LENGTH);//以太網首部填入目的 mac
    str2mac(arp_rt->eh.h_src, mac);//以太網首部填入源mac
    arp_rt->eh.h_proto = arp->eh.h_proto;//以太網協議還是arp協議
    arp_rt->arp.h_addrlen = 6;
    arp_rt->arp.h_protolen = 4;
    arp_rt->arp.oper = htons(2); // ARP響應
    str2mac(arp_rt->arp.smac, mac);//arp報文填入源mac 
    arp_rt->arp.sip = arp->arp.dip; // arp報文填入發送端 ip
    memcpy(arp_rt->arp.dmac, arp->arp.smac, ETH_ADDR_LENGTH);//arp報文填入目的 mac 
    arp_rt->arp.dip = arp->arp.sip; // arp報文填入目的 ip
}
int main() {
    struct nm_pkthdr h;
    struct nm_desc *nmr = nm_open("netmap:ens33", NULL, 0, NULL);
    if (nmr == NULL) {
        return -1;
    }
    printf("open ens33 seccessn");
    struct pollfd pfd = {0};
    pfd.fd = nmr->fd;
    pfd.events = POLLIN;
    while (1) {
        printf("new data coming!n");
        int ret = poll(&pfd, 1, -1);
        if (ret < 0) {
            continue;
        }
        if (pfd.revents & POLLIN) {
            unsigned char *stream = nm_nextpkt(nmr, &h);
            struct ethhdr *eh = (struct ethhdr *) stream;
            if (ntohs(eh->h_proto) == PROTO_IP) {
                struct ippkt *iph=(struct ippkt *)stream;
                if (iph->ip.type == PROTP_UPD) {
                    struct udppkt *udp = (struct udppkt *) stream;
                    int udplength = ntohs(udp->udp.length);
                    udp->data[udplength - 8] = '';
                    printf("udp ---> %sn", udp->data);
                    struct udppkt udp_rt;
                    echo_udp_pkt(udp, &udp_rt);
                    nm_inject(nmr, &udp_rt, sizeof(struct udppkt));
                }
            }
            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.109.100")) {
                    echo_arp_pkt(arp, &arp_rt, "00:0c:29:1b:18:20");
                    nm_inject(nmr, &arp_rt, sizeof(arp_rt));
                    printf("arp retn");
                }
            }
        }
    }
    nm_close(nmr);
}
//gcc -o main main.c -I /netmap/sys/
//insmod netmap.ko

測試能否回應arp請求

  • 開啟netmap

每次重啟使用前都需要insmod netmap.ko ,然后我們查看ls /dev/netmap -l,出現下面的設備就說明開啟成功了。

root@wxf:/netmap/LINUX# insmod netmap.ko 
root@wxf:/netmap/LINUX# ls /dev/netmap -l
crw------- 1 root root 10, 54 Jul 18 17:28 /dev/netmap
  • 運行上面的udp+arp測試代碼

我們發現成功響應了arp請求

用戶態協議棧設計實現udp,arp與icmp協議

 

?前面也說過了,一旦調用nm_open函數,網卡的數據就不從內核協議棧走了,所以我們ping一下看看能不能ping通,ping是icmp協議。發現ping不同,但是我們是接收到數據包了的,下面就來實現icmp協議

用戶態協議棧設計實現udp,arp與icmp協議

 

3.實現icmp協議

設計思路

icmp協議是ip協議的一部分

用戶態協議棧設計實現udp,arp與icmp協議

 


用戶態協議棧設計實現udp,arp與icmp協議

 

?icmp的類型:ping請求是8,ping回應是0,其他的就不介紹了

  • 代碼:ping的代碼為0
  • 校驗和:它的計算方法與IP數據報中的首部校驗和計算方式是一樣IP首部校驗和計算原理

ICMP類型為ping時,具體協議格式如下:

用戶態協議棧設計實現udp,arp與icmp協議

 

?其中增加的標識符和序號字段按照請求端的數據返回即可,同時對選項數據也必須進行回顯,客戶端可以會驗證這些數據。

代碼

//
// Created by 68725 on 2022/7/19.
//
#include <stdio.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define NETMAP_WITH_LIBS

#include <net/netmap_user.h>
#include <string.h>

#pragma pack(1)
#define ETH_ADDR_LENGTH 6
#define PROTO_IP 0x0800
#define PROTO_ARP 0x0806
#define PROTO_RARP    0x0835
#define PROTP_UPD 17
#define PROTO_ICMP    1

struct ethhdr {
    unsigned char h_dst[ETH_ADDR_LENGTH];
    unsigned char h_src[ETH_ADDR_LENGTH];
    unsigned short h_proto;
};

struct iphdr {
    unsigned char hdrlen: 4,
            version: 4;
    unsigned char tos;
    unsigned short totlen;
    unsigned short id;
    unsigned short flag_offset;
    unsigned char ttl;
    unsigned char type;
    unsigned short check;
    unsigned int sip;
    unsigned int dip;
};

struct ippkt {
    struct ethhdr eh; //14
    struct iphdr ip; //20
};

struct udphdr {
    unsigned short sport;
    unsigned short dport;
    unsigned short length;
    unsigned short check;
};

struct udppkt {
    struct ethhdr eh; //14
    struct iphdr ip; //20
    struct udphdr udp;//8
    unsigned char data[0];
};

struct arphdr {
    unsigned short h_type;
    unsigned short h_proto;

    unsigned char h_addrlen;
    unsigned char h_protolen;

    unsigned short oper;

    unsigned char smac[ETH_ADDR_LENGTH];
    unsigned int sip;
    unsigned char dmac[ETH_ADDR_LENGTH];
    unsigned int dip;
};

struct arppkt {
    struct ethhdr eh;
    struct arphdr arp;
};

int str2mac(char *mac, char *str) {
    char *p = str;
    unsigned char value = 0x0;
    int i = 0;
    while (p != '') {
        if (*p == ':') {
            mac[i++] = value;
            value = 0x0;
        }
        else {
            unsigned char temp = *p;
            if (temp <= '9' && temp >= '0') {
                temp -= '0';
            }
            else if (temp <= 'f' && temp >= 'a') {
                temp -= 'a';
                temp += 10;
            }
            else if (temp <= 'F' && temp >= 'A') {
                temp -= 'A';
                temp += 10;
            }
            else {
                break;
            }
            value <<= 4;
            value |= temp;
        }
        p++;
    }
    mac[i] = value;//a
    return 0;
}

void echo_udp_pkt(struct udppkt *udp, struct udppkt *udp_rt) {
    memcpy(udp_rt, udp, sizeof(struct udppkt));
    memcpy(udp_rt->eh.h_dst, udp->eh.h_src, ETH_ADDR_LENGTH);
    memcpy(udp_rt->eh.h_src, udp->eh.h_dst, ETH_ADDR_LENGTH);
    udp_rt->ip.sip = udp->ip.dip;
    udp_rt->ip.dip = udp->ip.sip;
    udp_rt->udp.sport = udp->udp.dport;
    udp_rt->udp.dport = udp->udp.sport;
}

void echo_arp_pkt(struct arppkt *arp, struct arppkt *arp_rt, char *mac) {
    memcpy(arp_rt, arp, sizeof(struct arppkt));
    memcpy(arp_rt->eh.h_dst, arp->eh.h_src, ETH_ADDR_LENGTH);//以太網首部填入目的 mac
    str2mac(arp_rt->eh.h_src, mac);//以太網首部填入源mac
    arp_rt->eh.h_proto = arp->eh.h_proto;//以太網協議還是arp協議
    arp_rt->arp.h_addrlen = 6;
    arp_rt->arp.h_protolen = 4;//aa
    arp_rt->arp.oper = htons(2); // ARP響應
    str2mac(arp_rt->arp.smac, mac);//arp報文填入源mac
    arp_rt->arp.sip = arp->arp.dip; // arp報文填入發送端 ip
    memcpy(arp_rt->arp.dmac, arp->arp.smac, ETH_ADDR_LENGTH);//arp報文填入目的 mac
    arp_rt->arp.dip = arp->arp.sip; // arp報文填入目的 ip
}

struct icmphdr {
    unsigned char type;
    unsigned char code;
    unsigned short check;
    unsigned short identifier;
    unsigned short seq;
    unsigned char data[32];
};

struct icmppkt {
    struct ethhdr eh;
    struct iphdr ip;
    struct icmphdr icmp;
};

unsigned short in_cksum(unsigned short *addr, int len) {
    register int nleft = len;
    register unsigned short *w = addr;
    register int sum = 0;
    unsigned short answer = 0;
    while (nleft > 1) {
        sum += *w++;
        nleft -= 2;
    }
    if (nleft == 1) {
        *(u_char *) (&answer) = *(u_char *) w;
        sum += answer;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return (answer);
}

void echo_icmp_pkt(struct icmppkt *icmp, struct icmppkt *icmp_rt) {
    memcpy(icmp_rt, icmp, sizeof(struct icmppkt));
    icmp_rt->icmp.type = 0x0; //
    icmp_rt->icmp.code = 0x0; //
    icmp_rt->icmp.check = 0x0;
    icmp_rt->ip.sip = icmp->ip.dip;
    icmp_rt->ip.dip = icmp->ip.sip;
    memcpy(icmp_rt->eh.h_dst, icmp->eh.h_src, ETH_ADDR_LENGTH);
    memcpy(icmp_rt->eh.h_src, icmp->eh.h_dst, ETH_ADDR_LENGTH);
    icmp_rt->icmp.check = in_cksum((unsigned short *) &icmp_rt->icmp, sizeof(struct icmphdr));
}

int main() {
    struct nm_pkthdr h;
    struct nm_desc *nmr = nm_open("netmap:ens33", NULL, 0, NULL);
    if (nmr == NULL) {
        return -1;
    }
    printf("open ens33 seccessn");
    struct pollfd pfd = {0};
    pfd.fd = nmr->fd;
    pfd.events = POLLIN;
    while (1) {
        printf("new data coming!n");
        int ret = poll(&pfd, 1, -1);
        if (ret < 0) {
            continue;
        }
        if (pfd.revents & POLLIN) {
            unsigned char *stream = nm_nextpkt(nmr, &h);
            struct ethhdr *eh = (struct ethhdr *) stream;
            if (ntohs(eh->h_proto) == PROTO_IP) {
                struct ippkt *iph = (struct ippkt *) stream;
                if (iph->ip.type == PROTP_UPD) {
                    struct udppkt *udp = (struct udppkt *) stream;
                    int udplength = ntohs(udp->udp.length);
                    udp->data[udplength - 8] = '';
                    printf("udp ---> %sn", udp->data);
                    struct udppkt udp_rt;
                    echo_udp_pkt(udp, &udp_rt);
                    nm_inject(nmr, &udp_rt, sizeof(struct udppkt));
                }
                else if (iph->ip.type == PROTO_ICMP) {
                    struct icmppkt *icmp = (struct icmppkt *) stream;
                    printf("icmp ---------- --> %d, %xn", icmp->icmp.type, icmp->icmp.check);
                    if (icmp->icmp.type == 0x08) {
                        struct icmppkt icmp_rt = {0};
                        echo_icmp_pkt(icmp, &icmp_rt);
                        nm_inject(nmr, &icmp_rt, sizeof(struct icmppkt));
                    }
                }
            }
            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.109.100")) {
                    echo_arp_pkt(arp, &arp_rt, "00:0c:29:1b:18:20");
                    nm_inject(nmr, &arp_rt, sizeof(arp_rt));
                    printf("arp retn");
                }
            }
        }
    }
    nm_close(nmr);
}
//gcc -o main main.c -I /netmap/sys/
//insmod netmap.ko

測試能否回應ping請求

  • 開啟netmap

每次重啟使用前都需要insmod netmap.ko ,然后我們查看ls /dev/netmap -l,出現下面的設備就說明開啟成功了。

root@wxf:/netmap/LINUX# insmod netmap.ko 
root@wxf:/netmap/LINUX# ls /dev/netmap -l
crw------- 1 root root 10, 54 Jul 18 17:28 /dev/netmap

運行上面的代碼,發現udp,arp,icmp都可以正常解析和發送

用戶態協議棧設計實現udp,arp與icmp協議

 

分享到:
標簽:協議
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定