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

在这里插入图片描述

相关内容

热门资讯

非安卓10系统手机,探索非安卓... 你有没有想过,为什么有些人会选择非安卓10系统的手机呢?是不是觉得这有点奇怪?别急,今天就来带你一探...
手机图标制作安卓系统,手机图标... 你有没有想过,那些在手机屏幕上跳动的图标,其实都是精心设计出来的艺术品呢?没错,今天就要带你一探究竟...
安卓系统和鸿蒙系统谁大,谁才是... 你有没有想过,手机里的操作系统就像是一场无声的较量?今天,咱们就来聊聊这个话题:安卓系统和鸿蒙系统,...
bj40安卓系统,功能解析与使... 你有没有发现,最近你的BJ40越野车变得聪明多了?没错,它升级了安卓系统,简直就像给它装上了个智能大...
安卓系统硬件核心板,揭秘智能设... 你有没有想过,手机里的安卓系统其实就像是一个庞大的城市,而硬件核心板就是这座城市的核心区域?今天,就...
王者荣耀安卓系统转区ios系统... 你有没有想过,把你的王者荣耀账号从安卓系统转到iOS系统呢?这可不是一件小事,里面可是有大学问的哦!...
安卓系统的音游,节奏与音乐的完... 你有没有发现,手机里的游戏越来越好玩了?尤其是那些音游,简直让人停不下来!今天,就让我带你深入了解一...
安卓系统取消下方按键,探索全新... 你知道吗?最近安卓系统来了一次大变动,那就是取消了下方按键!这可真是让人眼前一亮,让我们一起来看看这...
安卓系统显示原理设置,从像素到... 你有没有发现,你的安卓手机屏幕上那些花花绿绿的图标和文字,其实背后有着一套神奇而又复杂的显示原理呢?...
平板安卓4.0系统下载,平板下... 你有没有想过,拥有一台运行着最新安卓4.0系统的平板电脑,那感觉简直就像拥有了未来的钥匙?想象流畅的...
安卓原生12系统下载,原生系统... 你有没有听说?安卓原生12系统终于来了!这款全新的操作系统,不仅带来了全新的视觉体验,还有一堆实用的...
安卓怎么下泼辣系统,安卓设备轻... 你有没有想过给你的安卓手机来个“大变身”?想象你的手机瞬间变成了一个泼辣的“女侠”,不仅个性十足,而...
安卓版小米系统下载,畅享智能生... 你有没有发现,最近手机圈里又掀起了一股热潮?没错,就是安卓版小米系统的下载。这款系统自从推出以来,就...
提取安卓系统自带软件,探索安卓... 你有没有想过,你的安卓手机里那些看似不起眼的自带软件,其实都是隐藏的宝藏呢?今天,就让我带你一探究竟...
安卓系统投屏到鸿蒙系统,鸿蒙系... 亲爱的读者们,你是否有过这样的体验:手里拿着安卓手机,却想在大屏幕的鸿蒙系统设备上展示内容?别急,今...
sony 电视安卓8.0系统,... 亲爱的读者们,你是否也和我一样,对科技产品有着浓厚的兴趣呢?今天,我要和你聊聊一个让我眼前一亮的话题...
安卓 替换系统下载,探索安卓系... 你有没有想过,你的安卓手机其实可以换换口味呢?没错,就是那个一直默默陪伴你的系统,今天就来带你一起探...
安卓系统证书信任,安卓系统证书... 你有没有遇到过这种情况?手机里突然弹出一个提示,告诉你某个应用需要获取系统证书信任,然后你一脸懵逼,...
安卓系统应用数据目录,揭秘系统... 你有没有想过,你的安卓手机里那些应用,它们的数据都藏在哪个角落呢?今天,就让我带你一探究竟,揭开安卓...
kindle 安卓 系统 刷机... 亲爱的读者们,你是不是也和我一样,对电子阅读设备情有独钟?尤其是那款小巧便携的Kindle,简直是阅...