学无止境--linux 注册虚拟网卡将SDK(厂商)上送至CPU的报文注入协议栈
备注:学习记录所用,若有高手不吝赐教,万分感谢!
一、问题描述:

如图中所示,是楠菲微的SF9056架构,厂商将图中1的两个RGMII注册成了管理/调试网口,使能相关选项就能看到有eth0和eth1两个网络设备,但是厂商的SDK只将图中2的部分进行了数据转发功能,该部分没有开发诸如DSA之类的驱动,虽然SDK有将进入该部分的报文上送至CPU的API,但是无法注入协议栈,也就无法与CPU通信;想要解决该问题,要么就自己开发DSA驱动,要么就需要注册一个虚拟网络设备(无对应的物理网卡)将报文注入协议栈,并在协议栈发出应答报文时发送到SDK,让SDK通过面板口发送出去。
注:才疏学浅,能力有限,如有描述不当,敬请谅解,欢迎各位大神指正。
以下是流程以及代码,需要确保有/dev/net/tun设备
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/if_packet.h>
#include <pthread.h>
#include "ifmHwCmd.h"
#include "ifmIfApi.h"
#include "vxw_hdrs.h"
int pkt_dbg = 0; /*调试FLAG*/
int g_dst_port = 0; /*记录源端口号*/
/************************* 配置参数 *************************/
#define TAP_DEV_NAME "tap0" // 虚拟网卡名
#define TAP_IP_ADDR "192.168.11.231" // 协议栈IP
#define TAP_MAC_ADDR "00:11:22:33:44:55" // 虚拟MAC
#define TAP_NETMASK "255.255.255.0"
#define MAX_PACKET_SIZE 1536 // 以太网最大帧
// ------------------- 全局变量 -------------------
int tap_fd = -1; // TAP文件描述符
uint8_t tx_buf[MAX_PACKET_SIZE];
/************************* TAP网卡创建 *************************/
int tap_create(const char *dev)
{
struct ifreq ifr;
int fd, err;
printf("tap_create\r\n");
// 打开TUN设备
if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
printf("open /dev/net/tun failed\r\n");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI; // IFF_TAP=二层以太网帧 IFF_NO_PI=不带额外头部
strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
// 创建TAP网卡
if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
printf("ioctl TUNSETIFF failed\r\n");
close(fd);
return -1;
}
tap_fd = fd;
printf("TAP网卡创建成功: %s\n", ifr.ifr_name);
return fd;
}
/************************* TAP网卡配置IP/MAC *************************/
void tap_config(const char *dev, const char *ip, const char *mac, const char *netmask)
{
char cmd[256];
printf("tap_config\r\n");
// 配置MAC地址
sprintf(cmd, "ifconfig %s hw ether %s", dev, mac);
system(cmd);
// 配置IP和子网掩码
sprintf(cmd, "ifconfig %s %s netmask %s up", dev, ip, netmask);
system(cmd);
printf("TAP网卡配置完成: IP=%s MAC=%s\n", ip, mac);
}
/************************* 【核心】平台SDK收包回调函数 *************************/
/*tPacket *pkt是平台中的数据结构,包含了报文(pkt->packet),需要替换成自己的平台中的数据结构*/
/*该函数注册到了平台的收包流程*/
int gdi_packet_rx_recv(int unit, tPacket *pkt, void *cookie)
{
ssize_t wlen = 0;
int i = 0;
// 1. 合法性校验
if (!pkt || !pkt->packet || pkt->len <= 0 || pkt->len > MAX_PACKET_SIZE)
return ERROR;
// 2. 将收到的完整二层报文 写入TAP → 注入Linux协议栈
wlen = write(tap_fd, pkt->packet, pkt->len);
if (wlen < 0) {
printf("write tap failed\r\n");
return ERROR;
}
g_dst_port = unit;
/*调试信息:打印注入协议栈的报文*/
if (pkt_dbg & 0x02) {
printf("【收包】从CPU口收到报文,注入协议栈: 长度=%d g_dst_port=%d\r\n", pkt->len, g_dst_port);
for (i = 0; i < pkt->len; i++) {
printf("%02x ", pkt->packet[i]);
if ((i + 1) % 16 == 0)
printf("\r\n");
}
printf("\r\n");
}
return OK; // 告诉SDK:报文已处理
}
/************************* 协议栈发包线程(读TAP→SDK发送) *************************/
void *tx_thread(void *arg)
{
int len;
int i = 0;
int chip_port = 0;
tPhyIfInfo phy_info;
printf("协议栈回包线程运行中...\n");
while (1)
{
// 从TAP读取协议栈发出的报文(ARP应答/IP报文等)
len = read(tap_fd, tx_buf, MAX_PACKET_SIZE);
if (len <= 0) continue;
/*调试信息:打印从协议栈发出的应答报文*/
if (pkt_dbg & 0x01) {
printf("\r\n← 协议栈应答报文,发回交换芯片:len=%d\r\n", len);
for (i = 0; i < len; i++) {
printf("%02x ", tx_buf[i]);
if ((i + 1) % 16 == 0)
printf("\r\n");
}
printf("\r\n");
}
/*这部分是平台的发包API,需要根据自己的平台调整*/
/*平台的发包API内部调用了厂商的SDK发包流程*/
gdi_card_logic2phy_api((void *)&g_dst_port, &chip_port);
gdi_pkt_local_send(0, chip_port, tx_buf, len);
}
return NULL;
}
/************************* 主函数 *************************/
int gdi_packet_net_create(void)
{
pthread_t tid;
// 步骤1:创建并配置TAP网卡
if (tap_create(TAP_DEV_NAME) < 0)
return -1;
tap_config(TAP_DEV_NAME, TAP_IP_ADDR, TAP_MAC_ADDR, TAP_NETMASK);
// 步骤2:启动协议栈发包线程
pthread_create(&tid, NULL, tx_thread, NULL);
pthread_detach(tid);
// 主线程保持运行
printf("交换芯片<->协议栈 转发服务运行中...\n");
return 0;
}

浙公网安备 33010602011771号