DPDK开发之KNI模块代码实现
创始人
2024-05-23 07:18:25
0

DPDK开发之KNI模块代码实现

  • 背景
  • KNI实现原理 -- ifreq
  • 代码实现
  • 总结

背景

在DPDK开发的时候,如果有些协议不想处理,只处理关注的协议,可以把其他协议写回内核,让内核处理。此时的DPDK就起到分发的作用,类似一个过滤器。

KNI实现原理 – ifreq

主要利用内核的/dev/net/tun。做VPN时也会用到这个设备文件。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include int tun_alloc(char *dev)
{struct ifreq ifr;memset(&ifr,0,sizeof(ifr));int fd=open("/dev/net/tun",O_RDWR);if(fd<0)return -1;// IFF_TAP针对的是以太网协议,需要传入MAC;TUN主要针对IP层协议ifr.ifr_flags=IFF_TAP|IFF_NO_PI;memcpy(ifr.ifr_name,dev,strlen(dev));int err;printf("fd = %d, dev = %s, len = %ld\n",fd,dev,strlen(dev));// 设置进去if((err=ioctl(fd,TUNSETIFF,(char *)&ifr))<0){printf("ioctl fail(%d): %s\n",err,strerror(errno));close(fd);return err;}return fd;
}int main()
{int code = tun_alloc("MyDev");printf("return code %d\n",code);getchar();return 0;
}

特别注意,ifr.ifr_name不能有空格。
编译:

gcc -o ifr ifr.c

执行后,使用如下命名查询:

ifconfig -a

可以看到多了MyDev。

MyDev     Link encap:以太网  硬件地址 c2:44:70:f2:79:f9  BROADCAST MULTICAST  MTU:1500  跃点数:1接收数据包:0 错误:0 丢弃:0 过载:0 帧数:0发送数据包:0 错误:0 丢弃:0 过载:0 载波:0碰撞:0 发送队列长度:1000 接收字节:0 (0.0 B)  发送字节:0 (0.0 B)ens33     Link encap:以太网  硬件地址 00:0c:29:79:9b:f7  inet 地址:192.168.0.106  广播:192.168.0.255  掩码:255.255.255.0inet6 地址: fe80::b608:7cba:aa19:e2d/64 Scope:LinkUP BROADCAST RUNNING MULTICAST  MTU:1500  跃点数:1接收数据包:7543 错误:0 丢弃:0 过载:0 帧数:0发送数据包:4518 错误:0 丢弃:0 过载:0 载波:0碰撞:0 发送队列长度:1000 接收字节:3014986 (3.0 MB)  发送字节:657222 (657.2 KB)lo        Link encap:本地环回  inet 地址:127.0.0.1  掩码:255.0.0.0inet6 地址: ::1/128 Scope:HostUP LOOPBACK RUNNING  MTU:65536  跃点数:1接收数据包:304 错误:0 丢弃:0 过载:0 帧数:0发送数据包:304 错误:0 丢弃:0 过载:0 载波:0碰撞:0 发送队列长度:1000 接收字节:25426 (25.4 KB)  发送字节:25426 (25.4 KB)

这就是kni的实现原理,由两部分组成:
(1)对外提供了一个字符设备,通过ioctl()操作。
(2)底层是一个网口。

代码实现

  1. 定义全局的KNI变量:struct rte_kni *。
  2. KNI初始化:rte_kni_init(…)。
  3. 完善struct rte_kni_conf,用于写入内核中。
  4. 完善struct rte_kni_ops。
  5. 实现一个config_network_if类型的函数,用于网络的up、down操作。
  6. 分配KNI,保存到全局变量中:rte_kni_alloc(…)。
  7. 把包发送到内核中:rte_kni_tx_brust(…)。
  8. 特别注意,要打开混杂模式:rte_eth_promiscuous_enable(…)。
  9. 这里演示了把包发送到内核,并没有从内核中抓取返回的包发送出去。

(dpdk_udp.c)

#include 
#include 
#include 
#include #include 
#include #define ENABLE_SEND	1#define ENABLE_KNI	1#define NUM_MBUFS (4096-1)#define BURST_SIZE	32int gDpdkPortId = 0; //static const struct rte_eth_conf port_conf_default = {.rxmode = {.max_rx_pkt_len = RTE_ETHER_MAX_LEN }
};#if ENABLE_KNIstruct rte_kni *global_kni = NULL;#endif#if ENABLE_SEND// sender 
static uint32_t gSrcIp;
static uint32_t gDstIp;static uint16_t gSrcPort;
static uint32_t gDstPort;static uint8_t gSrcMac[RTE_ETHER_ADDR_LEN];
static uint8_t gDstMac[RTE_ETHER_ADDR_LEN];#endif//
static void ng_init_port(struct rte_mempool *mbuf_pool) {//1 count availuint16_t nb_sys_ports= rte_eth_dev_count_avail(); //if (nb_sys_ports == 0) {rte_exit(EXIT_FAILURE, "No Supported eth found\n");}//1 struct rte_eth_dev_info dev_info;rte_eth_dev_info_get(gDpdkPortId, &dev_info); ////1 const int num_rx_queues = 1;const int num_tx_queues = 1;struct rte_eth_conf port_conf = port_conf_default;rte_eth_dev_configure(gDpdkPortId, num_rx_queues, num_tx_queues, &port_conf);//1 rx queue setupif (rte_eth_rx_queue_setup(gDpdkPortId, 0 , 1024, rte_eth_dev_socket_id(gDpdkPortId),NULL, mbuf_pool) < 0) {rte_exit(EXIT_FAILURE, "Could not setup RX queue\n");}#if ENABLE_SENDstruct rte_eth_txconf txq_conf = dev_info.default_txconf;txq_conf.offloads = port_conf.rxmode.offloads;if (rte_eth_tx_queue_setup(gDpdkPortId, 0 , 1024, rte_eth_dev_socket_id(gDpdkPortId), &txq_conf) < 0) {rte_exit(EXIT_FAILURE, "Could not setup TX queue\n");}#endif//1 startif (rte_eth_dev_start(gDpdkPortId) < 0 ) {rte_exit(EXIT_FAILURE, "Could not start\n");}rte_eth_promiscuous_enable( gDpdkPortId);}#if ENABLE_SENDstatic int ng_encode_udp_pkt(uint8_t *msg, unsigned char *data, uint16_t 
total_len) {// encode // 1 ethhdrstruct rte_ether_hdr *eth = (struct rte_ether_hdr *)msg;rte_memcpy(eth->s_addr.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN);rte_memcpy(eth->d_addr.addr_bytes, gDstMac, RTE_ETHER_ADDR_LEN);eth->ether_type = htons(RTE_ETHER_TYPE_IPV4);// 2 iphdr struct rte_ipv4_hdr *ip = (struct rte_ipv4_hdr *)(msg + sizeof(struct 
rte_ether_hdr));ip->version_ihl = 0x45;ip->type_of_service = 0;ip->total_length = htons(total_len - sizeof(struct rte_ether_hdr));ip->packet_id = 0;ip->fragment_offset = 0;ip->time_to_live = 64; // ttl = 64ip->next_proto_id = IPPROTO_UDP;ip->src_addr = gSrcIp;ip->dst_addr = gDstIp;ip->hdr_checksum = 0;ip->hdr_checksum = rte_ipv4_cksum(ip);// 3 udphdr struct rte_udp_hdr *udp = (struct rte_udp_hdr *)(msg + sizeof(struct 
rte_ether_hdr) + sizeof(struct rte_ipv4_hdr));udp->src_port = gSrcPort;udp->dst_port = gDstPort;uint16_t udplen = total_len - sizeof(struct rte_ether_hdr) - sizeof(struct 
rte_ipv4_hdr);udp->dgram_len = htons(udplen);rte_memcpy((uint8_t*)(udp+1), data, udplen);udp->dgram_cksum = 0;udp->dgram_cksum = rte_ipv4_udptcp_cksum(ip, udp);struct in_addr addr;addr.s_addr = gSrcIp;printf(" --> src: %s:%d, ", inet_ntoa(addr), ntohs(gSrcPort));addr.s_addr = gDstIp;printf("dst: %s:%d\n", inet_ntoa(addr), ntohs(gDstPort));return 0;
}static struct rte_mbuf * ng_send(struct rte_mempool *mbuf_pool, uint8_t *data
, uint16_t length) {// mempool --> mbufconst unsigned total_len = length + 42;struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool);if (!mbuf) {rte_exit(EXIT_FAILURE, "rte_pktmbuf_alloc\n");}mbuf->pkt_len = total_len;mbuf->data_len = total_len;uint8_t *pktdata = rte_pktmbuf_mtod(mbuf, uint8_t*);ng_encode_udp_pkt(pktdata, data, total_len);return mbuf;}#endif#if ENABLE_KNIstatic int gconfig_network_if(uint16_t port_id, uint8_t if_up) {if (!rte_eth_dev_is_valid_port(port_id)) {return -EINVAL;}int ret = 0;if (if_up) { //rte_eth_dev_stop(port_id);ret = rte_eth_dev_start(port_id);} else {rte_eth_dev_stop(port_id);}if (ret < 0) {printf("Failed to start port : %d\n", port_id);}return 0;
}#endifint main(int argc, char *argv[]) {if (rte_eal_init(argc, argv) < 0) {rte_exit(EXIT_FAILURE, "Error with EAL init\n");}struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("mbuf pool", 
NUM_MBUFS,0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {rte_exit(EXIT_FAILURE, "Could not create mbuf pool\n");}#if ENABLE_KNI rte_kni_init(gDpdkPortId);
#endifng_init_port(mbuf_pool);#if ENABLE_KNIstruct rte_kni_conf conf;memset(&conf, 0, sizeof(conf));snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%d", gDpdkPortId);conf.group_id = gDpdkPortId;conf.mbuf_size = RTE_MBUF_DEFAULT_BUF_SIZE;//conf.rte_eth_macaddr_get(gDpdkPortId, (struct rte_ether_addr*)conf.mac_addr);rte_eth_dev_get_mtu(gDpdkPortId, &conf.mtu);struct rte_kni_ops ops;memset(&ops, 0, sizeof(ops));ops.port_id = gDpdkPortId;ops.config_network_if = gconfig_network_if;global_kni = rte_kni_alloc(mbuf_pool, &conf, &ops);#endifwhile (1) {struct rte_mbuf *mbufs[BURST_SIZE];unsigned num_recvd = rte_eth_rx_burst(gDpdkPortId, 0, mbufs, BURST_SIZE);if (num_recvd > BURST_SIZE) {rte_exit(EXIT_FAILURE, "Error receiving from eth\n");}unsigned i = 0;for (i = 0;i < num_recvd;i ++) {struct rte_ether_hdr *ehdr = rte_pktmbuf_mtod(mbufs[i], struct 
rte_ether_hdr*);if (ehdr->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {continue;}struct rte_ipv4_hdr *iphdr =  rte_pktmbuf_mtod_offset(mbufs[i], struct 
rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));if (iphdr->next_proto_id == IPPROTO_UDP) {struct rte_udp_hdr *udphdr = (struct rte_udp_hdr *)(iphdr + 1);#if ENABLE_SEND		// echo// mac exchangerte_memcpy(gDstMac, ehdr->s_addr.addr_bytes, RTE_ETHER_ADDR_LEN);rte_memcpy(gSrcMac, ehdr->d_addr.addr_bytes, RTE_ETHER_ADDR_LEN);// ip exchangerte_memcpy(&gSrcIp, &iphdr->dst_addr, sizeof(uint32_t));rte_memcpy(&gDstIp, &iphdr->src_addr, sizeof(uint32_t));// port exchangerte_memcpy(&gSrcPort, &udphdr->dst_port, sizeof(uint16_t));rte_memcpy(&gDstPort, &udphdr->src_port, sizeof(uint16_t));#endifuint16_t length = ntohs(udphdr->dgram_len);*((char*)udphdr + length) = '\0';struct in_addr addr;addr.s_addr = iphdr->src_addr;printf("src: %s:%d, ", inet_ntoa(addr), udphdr->src_port);addr.s_addr = iphdr->dst_addr;printf("dst: %s:%d, %s\n", inet_ntoa(addr), udphdr->src_port, (char *)(udphdr+1));#if ENABLE_SENDstruct rte_mbuf *txbuf = ng_send(mbuf_pool, (unsigned char*)(udphdr+1), 
length);rte_eth_tx_burst(gDpdkPortId, 0, &txbuf, 1);#endifrte_pktmbuf_free(mbufs[i]);} else {rte_kni_tx_burst(global_kni, &mbufs[i], 1);}}}}

Makefle:

# binary name
APP = dpdk_udp# all source are stored in SRCS-y
SRCS-y := dpdk_udp.c# Build using pkg-config variables if possible
ifeq ($(shell pkg-config --exists libdpdk && echo 0),0)all: shared
.PHONY: shared static
shared: build/$(APP)-sharedln -sf $(APP)-shared build/$(APP)
static: build/$(APP)-staticln -sf $(APP)-static build/$(APP)PKGCONF=pkg-config --define-prefixPC_FILE := $(shell $(PKGCONF) --path libdpdk)
CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk)
LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)build:@mkdir -p $@.PHONY: clean
clean:rm -f build/$(APP) build/$(APP)-static build/$(APP)-sharedtest -d build && rmdir -p build || trueelseifeq ($(RTE_SDK),)
$(error "Please define RTE_SDK environment variable")
endif# Default target, detect a build directory, by looking for a path with a .config
RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))include $(RTE_SDK)/mk/rte.vars.mk

总结

调试时,需要把/sys/devices/virtual/net/vEth0/carrier置为1。允许内核收发数据。

echo 1 > /sys/devices/virtual/net/vEth0/carrier

在这里插入图片描述

相关内容

热门资讯

安卓子系统windows11,... 你知道吗?最近科技圈可是炸开了锅,因为安卓子系统在Windows 11上的兼容性成了大家热议的话题。...
电脑里怎么下载安卓系统,电脑端... 你有没有想过,你的电脑里也能装上安卓系统呢?没错,就是那个让你手机不离手的安卓!今天,就让我来带你一...
索尼相机魔改安卓系统,魔改系统... 你知道吗?最近在摄影圈里掀起了一股热潮,那就是索尼相机魔改安卓系统。这可不是一般的改装,而是让这些专...
安卓系统哪家的最流畅,安卓系统... 你有没有想过,为什么你的手机有时候像蜗牛一样慢吞吞的,而别人的手机却能像风一样快?这背后,其实就是安...
安卓最新系统4.42,深度解析... 你有没有发现,你的安卓手机最近是不是有点儿不一样了?没错,就是那个一直在默默更新的安卓最新系统4.4...
android和安卓什么系统最... 你有没有想过,你的安卓手机到底是用的是什么系统呢?是不是有时候觉得手机卡顿,运行缓慢,其实跟这个系统...
平板装安卓xp系统好,探索复古... 你有没有想过,把安卓系统装到平板上,再配上XP系统,这会是怎样一番景象呢?想象一边享受着安卓的便捷,...
投影仪装安卓系统,开启智能投影... 你有没有想过,家里的老式投影仪也能焕发第二春呢?没错,就是那个曾经陪你熬夜看电影的“老伙计”,现在它...
安卓系统无线车载carplay... 你有没有想过,开车的时候也能享受到苹果设备的便利呢?没错,就是那个让你在日常生活中离不开的iOS系统...
谷歌安卓8系统包,系统包解析与... 你有没有发现,手机更新换代的速度简直就像坐上了火箭呢?这不,最近谷歌又发布了安卓8系统包,听说这个新...
微软平板下软件安卓系统,开启全... 你有没有想过,在微软平板上也能畅享安卓系统的乐趣呢?没错,这就是今天我要跟你分享的神奇故事。想象你手...
coloros是基于安卓系统吗... 你有没有想过,手机里的那个色彩斑斓的界面,背后其实有着一个有趣的故事呢?没错,我要说的就是Color...
安卓神盾系统应用市场,一站式智... 你有没有发现,手机里的安卓神盾系统应用市场最近可是火得一塌糊涂啊!这不,我就来给你好好扒一扒,看看这...
黑莓平板安卓系统升级,解锁无限... 亲爱的读者们,你是否还记得那个曾经风靡一时的黑莓手机?那个标志性的全键盘,那个独特的黑莓体验,如今它...
安卓文件系统采用华为,探索高效... 你知道吗?最近安卓系统在文件管理上可是有了大动作呢!华为这个科技巨头,竟然悄悄地给安卓文件系统来了个...
深度系统能用安卓app,探索智... 你知道吗?现在科技的发展真是让人惊叹不已!今天,我要给你揭秘一个超级酷炫的话题——深度系统能用安卓a...
安卓系统的分区类型,深度解析存... 你有没有发现,你的安卓手机里藏着不少秘密?没错,就是那些神秘的分区类型。今天,就让我带你一探究竟,揭...
安卓系统铠无法兑换,揭秘无法兑... 最近是不是有很多小伙伴在玩安卓系统的游戏,突然发现了一个让人头疼的问题——铠无法兑换!别急,今天就来...
汽车安卓系统崩溃怎么刷,一键刷... 亲爱的车主朋友们,你是否曾遇到过汽车安卓系统崩溃的尴尬时刻?手机系统崩溃还能重启,但汽车系统崩溃了,...
miui系统可以刷安卓p系统吗... 亲爱的手机控们,你是否对MIUI系统情有独钟,同时又对安卓P系统的新鲜功能垂涎欲滴?今天,就让我带你...