Linux Ubuntu 20.04 netmap安装
安装编译netmap
git clone https://github.com/luigirizzo/netmap
进入LINUX目录
初始化环境
./configure
需要长时间等待完成,出现以下界面不用担心(需要等半个小时到一个小时)
等待到出现这步就成功了
编译和安装
make && make install
会有一路这样的check 需要耐心等待
编译问题
可能会出现如下,部分变量未初始化的问题。
此时需要找到对应Makefile文件,将Mackfile中"CFLAGS += -Werror -Wall -Wunused-function"配置行的-Werror参数删掉。
使用netmap
每次使用前都需要insmod netmap.ko ,将编译出来的 netmap 内核模块加载到内核中。
lsmod 指令可以查看内核已经加载的模块。insmod 指令加载的内核模块在每次内核重启之后都需要重新加载。
然后查看ls /dev/netmap -l,出现下面的设备就说明开启成功了。
sudo insmod netmap.ko
ls /dev/netmap -l
恢复的话就是
rmmod netmap.ko
运行测试程序
粘贴了一个别人写的,使用netmap来测试ICMP协议的代码。
insmod netmap.ko
gcc -o icmp_netmap icmp_netmap.c
#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_UDP 17
#define PROTO_ICMP 1struct ethhdr {unsigned char h_dst[ETH_ADDR_LENGTH];//目地MAC地址unsigned char h_src[ETH_ADDR_LENGTH];//源MAC地址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; //生存时间(TTL)unsigned char type; //协议unsigned short check; //首部检验和unsigned int sip; //源IP地址unsigned int dip; //目的IP地址
};struct ippkt {struct ethhdr eh; //14struct iphdr ip; //20
};struct udphdr {unsigned short sport; //源端口unsigned short dport; //目的端口unsigned short length; //封包长度unsigned short check; //校验和
};struct udppkt { struct ethhdr eh; //14struct iphdr ip; //20struct udphdr udp;//8unsigned char data[0];
};struct arphdr {unsigned short h_type; //硬件类型unsigned short h_proto; //协议类型unsigned char h_addrlen; //硬件长度unsigned char h_protolen; //协议长度unsigned short oper; //操作码 ARP请求(1),ARP响应(2)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 != '\0') {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_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);//以太网首部填入目的 macstr2mac(arp_rt->eh.h_src, mac);//以太网首部填入源macarp_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报文填入发送端 ipmemcpy(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; //类型 ping请求是8,ping回应是0unsigned char code; //代码(Code):4位,标明报文的类型。ping的代码为0unsigned 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 seccess\n");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_UDP) {struct udppkt *udp = (struct udppkt *) stream;int udplength = ntohs(udp->udp.length);udp->data[udplength - 8] = '\0';printf("udp ---> %s\n", 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, %x\n", 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.240.130")) {echo_arp_pkt(arp, &arp_rt, "00:0c:29:7b:e4:67");nm_inject(nmr, &arp_rt, sizeof(arp_rt));printf("arp ret\n");}}}}nm_close(nmr);
}
使用ifconfig看下自己Linux的网络地址,直接用物理机ping即可。