You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将sk_buff模拟为TAP接收的数据包送入Linux网络栈?

如何将sk_buff模拟为TAP接收的数据包送入Linux网络栈?

兄弟,我明白你要做啥了——就是把内核里的一个sk_buff,假装成TAP设备从外部收到的数据包,塞进Linux网络栈里处理对吧?你贴的这段尝试代码已经找对路子了,不过我给你补全下关键细节和避坑点:

我尝试从一个驱动将sk_buff发送到另一个设备(TAP,对应Linux源码里的drivers/net/tun.c)的RX队列,用了这样的代码:

#if RECV_SKB
#define NETIF_RX netif_receive_skb
#else
#define NETIF_RX netif_rx
#endif

static net_device * dev; /* a pointer to TAP interface */

int sent_to_tap(struct net_device * dev, struct sk_buff * skb, int txq) {
    struct tun_struct * tun;
    struct tun_file * tfile;
    int ret;

    tun = netdev_priv(dev);
    if (!tun)
        goto drop;
    // 原代码未完成部分

首先你得搞清楚,TAP设备的接收逻辑是用户态和内核态的桥梁,但如果你只是想让上层网络栈认这个包是TAP收到的,其实不用绕TAP的用户态路径,直接修正skb元数据后扔进网络栈就行;如果要走TAP的正规接收流程(比如唤醒用户态监听进程),就得调用TAP驱动的内部接口,给你拆成两种情况说:

快速方案:直接让数据包进入网络栈

这种方式简单直接,只要把skb的“身份”改成TAP设备的,再递给网络栈的接收函数就行:

  • 修正skb元数据:必须把skb->dev设为你的TAP设备指针,同时用skb_reset_mac_header(skb)和skb_reset_network_header(skb)重置头部字段,用eth_type_trans(skb, tap_dev)解析协议类型,确保网络栈能正确识别数据包。
  • 选对接收函数:你代码里用到的netif_rx是老接口了,现在优先用netif_receive_skb;如果是在中断上下文调用,记得用netif_rx_ni避免死锁。
  • 处理skb生命周期:调用接收函数后,网络栈会接管skb,只有当返回NET_RX_DROP时,你才需要用kfree_skb(skb)手动释放,防止内存泄漏。

示例代码:

int send_skb_as_tap_rx(struct net_device *tap_dev, struct sk_buff *skb) {
    // 确保TAP设备处于运行状态
    if (!netif_running(tap_dev)) {
        kfree_skb(skb);
        return -ENODEV;
    }

    // 修正skb的设备归属和头部信息
    skb->dev = tap_dev;
    skb_reset_mac_header(skb);
    skb_reset_network_header(skb);
    skb->protocol = eth_type_trans(skb, tap_dev);
    skb->pkt_type = PACKET_HOST; // 标记为发给本机的包

    // 送入网络栈
    int ret = netif_receive_skb(skb);
    if (ret == NET_RX_DROP) {
        kfree_skb(skb);
        return -EINVAL;
    }
    return 0;
}

正规方案:走TAP驱动的接收流程

如果需要让TAP设备的用户态监听进程(比如OpenVPN)也感知到这个数据包,就得调用TAP驱动的内部接收接口:

  • 获取TAP私有数据:用netdev_priv(tap_dev)拿到tun_struct,这是TAP设备的核心私有结构体。
  • 调用TAP内部接收函数:用tun_rx_skb(不同内核版本可能有差异,比如有些版本需要传入tun_file),这个函数会把数据包同时送入网络栈和通知用户态进程。

示例代码:

int send_skb_via_tap_rx(struct net_device *tap_dev, struct sk_buff *skb) {
    struct tun_struct *tun;
    int ret;

    tun = netdev_priv(tap_dev);
    if (!tun || !netif_running(tap_dev)) {
        kfree_skb(skb);
        return -ENODEV;
    }

    // 修正skb元数据,和快速方案一致
    skb->dev = tap_dev;
    skb_reset_mac_header(skb);
    skb_reset_network_header(skb);
    skb->protocol = eth_type_trans(skb, tap_dev);
    skb->pkt_type = PACKET_HOST;

    // 走TAP正规接收流程
    ret = tun_rx_skb(tun, skb);
    if (ret < 0) {
        kfree_skb(skb);
        return ret;
    }
    return 0;
}

要注意的坑

  • 内核版本兼容性:不同内核版本的TAP驱动接口可能有变化,比如tun_rx_skb的参数、函数名,一定要对照你使用的Linux源码(drivers/net/tun.c)来调整。
  • 调用上下文限制:这些内核函数只能在内核模块或驱动里调用,如果是在原子上下文(比如中断处理函数),不能调用可能导致睡眠的接口。
  • 数据包合法性:skb里的内容必须符合以太网帧格式,比如要有正确的源/目的MAC、协议类型,不然会被网络栈直接丢弃。

备注:内容来源于stack exchange,提问作者Misha T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 17:28:06