l2fwd应用程序为RX_PORT上接收的每个数据包执行二层转发。 目标端口是启用的端口掩码的相邻端口,即,如果启用前四个端口(端口掩码0xf,每个端口用一个比特位表示,启动4个就是4个比特位置1),端口1和2相互转发,端口3和4相互转发。 此外,如果启用了MAC地址更新,则MAC地址按照如下方式更新:
数据包的源mac会变成发送端口的mac地址
数据包的目的mac会变成02:00:00:00:00:发送端口的ID
也就是说在不开启mac地址更新的情况下,仅仅对数据包进行紧邻端口的转发不对数据包的mac地址进行修改,如果开启的话,会对需要转发的数据包的mac地址按照如上的规则进行更新。
参照官网给出的图,转发的模式如下
也就是说端口0和端口1是相邻的两个端口,2和3是相邻的两个端口,0口进的数据包会从1口发出,1口进的数据包会从0口发出,同理2和3两个端口的转发逻辑也是一样的。
现在对源码做一下跟读,从main函数开始
1. 命令行参数解析
命令行函数由两部分构成:EAL参数和应用程序参数分别由rte_eal_init函数和l2fwd_parse_args函数来解析
//1. 解析命令行参数
/* init EAL */
ret = rte_eal_init(argc, argv); // 解析命令行EAL参数 环境初始化
if (ret < 0)
rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
argc -= ret;
argv += ret;// 跳过EAL参数 参数的数量减小 同时指针后移
force_quit = false; // 终止main loop中while循环的变量
signal(SIGINT, signal_handler); // 利用signal函数来处理接收到的信号
signal(SIGTERM, signal_handler);
/* parse application arguments (after the EAL ones) */
ret = l2fwd_parse_args(argc, argv); // 解析应用参数
if (ret < 0)
rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n");
rte_eal_init不仅有解析命令行参数的作用还会进行复杂的环境初始化,l2fwd_parse_args函数则主要是用来解析应用程序的参数,这个函数的主体是一个switch,这个switch有三个case,也就是对命令行里输入的-p,-q,-T的解析
switch (opt) {
/* portmask */
case 'p': // 解析-p参数 要配置的端口的掩码
l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg);
if (l2fwd_enabled_port_mask == 0) {
printf("invalid portmask\n");
l2fwd_usage(prgname);
return -1;
}
break;
/* nqueue */
case 'q': // 每一个逻辑核处理多少个端口
l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg);
if (l2fwd_rx_queue_per_lcore == 0) {
printf("invalid queue number\n");
l2fwd_usage(prgname);
return -1;
}
break;
/* timer period */
case 'T': // 设置打印端口数据的周期
timer_secs = l2fwd_parse_timer_period(optarg);
if (timer_secs < 0) {
printf("invalid timer period\n");
l2fwd_usage(prgname);
return -1;
}
timer_period = timer_secs;
break;
/* long options */
case 0:
break;
default:
l2fwd_usage(prgname);
return -1;
}
可以看的出来-p会对l2fwd_enabled_port_mask变量赋值,这个值表示的就是端口掩码,也就是一个比特位表示一个端口,如果这个端口开启,就把对应的比特位置1,-q会对l2fwd_rx_queue_per_lcore变量赋值,表示的是每个逻辑核处理多少个端口。-T会对timer_period变量赋值,也就是打印端口数据的周期
2. 创建mbuf池
命令行参数解析完以后,会创建mbuf池,mbuf pool里边包含很多mbuf对象,mbuf对象用来存储网络数据包
//2. 创建mnuf池
/* create the mbuf pool */
l2fwd_pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF, // 从已分配的大页中创建内存池
MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE,
rte_socket_id());
if (l2fwd_pktmbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n");
nb_ports = rte_eth_dev_count(); // 获取全部开启的端口的数量
if (nb_ports == 0)
rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
其中l2fwd_pktmbuf_pool是rte_mempool结构体,他是用于处理对象池的通用结构,这里创建的mbuf的数量是NB_MBUF,各mbuf的大小是RTE_MBUF_DEFAULT_BUF_SIZE。