DPDK用户态协议栈-KNI

简介: DPDK用户态协议栈-KNI

kni

KNI(Kernel Network Interface)是一种在Linux内核中实现的网络编程接口,它提供了一种高效的方式来处理网络数据包。KNI的原理是将用户空间和内核空间之间的数据传输最小化,以降低网络处理的延迟和开销。

KNI允许用户空间应用程序直接访问内核网络协议栈,从而可以更灵活地控制和处理网络数据包。其主要原理如下:

  1. 网络设备绑定:用户空间应用程序通过创建虚拟网卡(vEth设备),将其与物理网卡绑定起来。这样,在用户空间中就可以对虚拟网卡进行操作。
  2. 数据传输:当网络数据包到达物理网卡时,内核会将数据包复制到用户空间缓冲区中,并触发一个中断通知用户空间。
  3. 用户空间处理:用户空间应用程序收到中断通知后,可以立即开始处理网络数据包。通过KNI接口,应用程序可以直接读取、修改或丢弃数据包。
  4. 内核回写:如果需要将修改后的数据包发送出去,应用程序可以通过KNI接口将数据包写回内核网络协议栈,并选择相应的物理网卡进行发送。

通过使用KNI,用户空间应用程序能够获得更高的灵活性和性能。它适用于需要对网络数据包进行深度处理的场景,如网络加速、防火墙、负载均衡等应用。

简单来说,kni就是vfs中的一个设备,在/dev/目录下。有点像我上次提到的进程间通信组件,我们将数据写入kni而kni负责将数据写入内核协议栈。

kni工作的位置

kni的启动

全局KNI变量
struct rte_kni *global_kni = NULL;
gport配置
static int ng_config_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;
}

这段代码是一个静态函数 ng_config_network_if,它用于配置网络接口的状态。它接受两个参数:port_id 是指要配置的网络接口的端口号,if_up 是一个布尔值,表示是否启用该网络接口。

首先,它会检查给定的 port_id 是否有效,如果无效,则返回错误码 -EINVAL。

然后,在 if_up 为真时(即启用网络接口),它会先停止当前的网络设备(调用 rte_eth_dev_stop(port_id)),然后尝试重新启动该设备(调用 rte_eth_dev_start(port_id))。如果启动失败,将返回相应的错误码。

在 if_up 为假时(即禁用网络接口),它只会停止当前的网络设备(调用 rte_eth_dev_stop(port_id))。

最后,无论成功与否,都会返回 0 表示操作完成。

pkt_process使用kni传入内核(重构协议分发流程)
static int pkt_process(void *arg) {
  struct rte_mempool *mbuf_pool = (struct rte_mempool *)arg;
  struct inout_ring *ring = ringInstance();
  while (1) {
    struct rte_mbuf *mbufs[BURST_SIZE];
    unsigned num_recvd = rte_ring_mc_dequeue_burst(ring->in, (void**)mbufs, BURST_SIZE, NULL);
    
    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 0
#if ENABLE_ARP
      if (ehdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP)) {
        struct rte_arp_hdr *ahdr = rte_pktmbuf_mtod_offset(mbufs[i], 
          struct rte_arp_hdr *, sizeof(struct rte_ether_hdr));
        /*
        struct in_addr addr;
        addr.s_addr = ahdr->arp_data.arp_tip;
        printf("arp ---> src: %s ", inet_ntoa(addr));
        addr.s_addr = gLocalIp;
        printf(" local: %s \n", inet_ntoa(addr));
        */
        
        if (ahdr->arp_data.arp_tip == gLocalIp) {
          if (ahdr->arp_opcode == rte_cpu_to_be_16(RTE_ARP_OP_REQUEST)) {
            //printf("arp --> request\n");
            struct rte_mbuf *arpbuf = ln_send_arp(mbuf_pool, RTE_ARP_OP_REPLY, ahdr->arp_data.arp_sha.addr_bytes, 
              ahdr->arp_data.arp_tip, ahdr->arp_data.arp_sip);
            //rte_eth_tx_burst(gDpdkPortId, 0, &arpbuf, 1);
            //rte_pktmbuf_free(arpbuf);
            rte_ring_mp_enqueue_burst(ring->out, (void**)&arpbuf, 1, NULL);
          } else if (ahdr->arp_opcode == rte_cpu_to_be_16(RTE_ARP_OP_REPLY)) {
            //printf("arp --> reply\n");
            struct arp_table *table = arp_table_instance();
            uint8_t *hwaddr = ln_get_dst_macaddr(ahdr->arp_data.arp_sip);
            if (hwaddr == NULL) {
              struct arp_entry *entry = rte_malloc("arp_entry",sizeof(struct arp_entry), 0);
              if (entry) {
                memset(entry, 0, sizeof(struct arp_entry));
                entry->ip = ahdr->arp_data.arp_sip;
                rte_memcpy(entry->hwaddr, ahdr->arp_data.arp_sha.addr_bytes, RTE_ETHER_ADDR_LEN);
                entry->type = 0;
                
                LL_ADD(entry, table->entries);
                table->count ++;
              }
            }
#if 0 //ENABLE_DEBUG
            struct arp_entry *iter;
            for (iter = table->entries; iter != NULL; iter = iter->next) {
          
              struct in_addr addr;
              addr.s_addr = iter->ip;
              print_ethaddr("arp table --> mac: ", (struct rte_ether_addr *)iter->hwaddr);
                
              printf(" ip: %s \n", inet_ntoa(addr));
          
            }
#endif
            rte_pktmbuf_free(mbufs[i]);
          }
        
          continue;
        } 
      }
#endif
#endif
      if (ehdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
        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) {
          udp_process(mbufs[i]);
          
        }
        else if (iphdr->next_proto_id == IPPROTO_TCP) {
          printf("ln_tcp_process\n");
          ln_tcp_process(mbufs[i]);
          
        }
        else {
          rte_kni_tx_burst(global_kni, mbufs, num_recvd);
          printf("tcp/udp --> rte_kni_handle_request\n");
        }
      }
      else {
        rte_kni_tx_burst(global_kni, mbufs, num_recvd);
        printf("ip --> rte_kni_handle_request\n");
      }
#if 0
#if ENABLE_ICMP
      if (iphdr->next_proto_id == IPPROTO_ICMP) {
        struct rte_icmp_hdr *icmphdr = (struct rte_icmp_hdr *)(iphdr + 1);
        
        struct in_addr addr;
        addr.s_addr = iphdr->src_addr;
        printf("icmp ---> src: %s ", inet_ntoa(addr));
        
        if (icmphdr->icmp_type == RTE_IP_ICMP_ECHO_REQUEST) {
          addr.s_addr = iphdr->dst_addr;
          printf(" local: %s , type : %d\n", inet_ntoa(addr), icmphdr->icmp_type);
        
          struct rte_mbuf *txbuf = ln_send_icmp(mbuf_pool, ehdr->s_addr.addr_bytes,
            iphdr->dst_addr, iphdr->src_addr, icmphdr->icmp_ident, icmphdr->icmp_seq_nb);
          //rte_eth_tx_burst(gDpdkPortId, 0, &txbuf, 1);
          //rte_pktmbuf_free(txbuf);
          rte_ring_mp_enqueue_burst(ring->out, (void**)&txbuf, 1, NULL);
          rte_pktmbuf_free(mbufs[i]);
        }
        
      }
#endif
#endif
      
    }
    rte_kni_handle_request(global_kni);
#if ENABLE_UDP_APP
    udp_out(mbuf_pool);
#endif
#if ENABLE_TCP_APP
    ln_tcp_out(mbuf_pool);
#endif
  }
  return 0;
}
主函数启动KNI
#if ENABLE_KNI
  if (-1 == rte_kni_init(gDpdkPortId)) {
    rte_exit(EXIT_FAILURE, "kni init failed");
  }
  ln_init_port(mbuf_pool);
#else
  global_kni = ln_alloc_kni(mbuf_pool);
#endif

arptable加锁实现线程安全

struct arp_table {
  struct arp_entry *entries;
  int count;
  pthread_spinlock_t spinlock;
};
static int ln_arp_entry_insert(uint32_t ip, uint8_t* mac) {
  struct arp_table* table = arp_table_instance();
  uint8_t* hwaddr = ln_get_dst_macaddr(ip);
  if (hwaddr == NULL) {
    struct arp_entry* entry = rte_malloc("arp_entry", sizeof(struct arp_entry), 0);
    if (entry) {
      memset(entry, 0, sizeof(struct arp_entry));
      entry->ip = ip;
      rte_memcpy(entry->hwaddr, mac, RTE_ETHER_ADDR_LEN);
      entry->type = 0;
      pthread_spin_lock(&table->spinlock);
      LL_ADD(entry, table->entries);
      table->count++;
      pthread_spin_unlock(&table->spinlock);
    }
    return 1;
  }
  return 0;
}

在我们使用多线程对同一张arp表进行读取和写入的时候,不可避免的带来了线程安全的问题。为了防止死锁带来的问题,这里我们在插入arp的时候设置锁,解决线程安全的问题。

效果

KNI的设备文件

KNI设备文件创建的虚拟网卡

不需要的协议

这里可以看到主机是无法被ping通的,这个问题是因为KNI相关还没有完全实现,后面会得到解决;同时,我们通过tcpdump抓包可以看到,在我们开启了混杂模式之后,内核是可以接收到icmp的数据包的,所以证明我们的kni启动其实是没有问题的。

需要的协议

相关文章
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
137 9
|
25天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
24 1
|
12天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
30 5
|
28天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
1月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
1月前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
50 4
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
2月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
39 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
2月前
初步认识栈和队列
初步认识栈和队列
61 10
|
2月前
数据结构(栈与列队)
数据结构(栈与列队)
22 1