找回密码
 立即注册
注册 登录
×
热搜: 活动 交友 discuz
查看: 83|回复: 0

eBPF Talk: 解密 XDP generic 模式

[复制链接]

1

主题

1

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2022-11-27 11:02:16 | 显示全部楼层 |阅读模式
近期又得搞 XDP 了,就顺手研究了下 XDP generic 模式的源代码。
XDP generic 模式的函数位置

已知,内核协议栈会对每个 skb 都执行一次 XDP generic 模式处理,当然是启用了 XDP generic 模式的情况下。 参考:Generic XDP 处理(软件 XDP)。
相关源代码如下:
// net/core/dev.c

static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
                    struct packet_type **ppt_prev)
{
    // ...

    if (static_branch_unlikely(&generic_xdp_needed_key)) {
        int ret2;

        preempt_disable();
        ret2 = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb);
        preempt_enable();

        if (ret2 != XDP_PASS) {
            ret = NET_RX_DROP;
            goto out;
        }
        skb_reset_mac_len(skb);
    }

    // ...

out:
    *pskb = skb;
    return ret;
}调用 do_xdp_generic() 函数,如果函数返回结果不是 XDP_PASS,则结束当前 skb 的处理流程。
为什么不是 XDP_PASS 就结束当前的处理流程了呢?
先看一下 do_xdp_generic() 函数有哪些返回结果:
// include/uapi/linux/bpf.h

/* User return codes for XDP prog type.
* A valid XDP program must return one of these defined values. All other
* return codes are reserved for future use. Unknown return codes will
* result in packet drops and a warning via bpf_warn_invalid_xdp_action().
*/
enum xdp_action {
    XDP_ABORTED = 0,
    XDP_DROP,
    XDP_PASS,
    XDP_TX,
    XDP_REDIRECT,
};而这些返回结果对应的处理流程,稍候有详细讲解。
do_xdp_generic

// net/core/dev.c

int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb)
{
    if (xdp_prog) {
        struct xdp_buff xdp;
        u32 act;
        int err;

        act = netif_receive_generic_xdp(skb, &xdp, xdp_prog);
        if (act != XDP_PASS) {
            switch (act) {
            case XDP_REDIRECT: // 对 skb 进行转发处理
                err = xdp_do_generic_redirect(skb->dev, skb,
                                  &xdp, xdp_prog);
                if (err)
                    goto out_redir;
                break;
            case XDP_TX: // 对 skb 进行发包处理
                generic_xdp_tx(skb, xdp_prog);
                break;
            }
            return XDP_DROP;
        }
    }
    return XDP_PASS;
out_redir:
    kfree_skb(skb);
    return XDP_DROP;
}调用 netif_receive_generic_xdp() 函数去实现 XDP generic 模式,并处理 XDP_REDIRECT 和 XDP_TX 这两种结果。
XDP_REDIRECT:调用 xdp_do_generic_redirect() 函数进行转发处理。
XDP_TX:调用 generic_xdp_tx() 函数进行发包处理。
如果 netif_receive_generic_xdp() 函数的返回结果不是 XDP_PASS,则返回 XDP_DROP。意即,所有非 XDP_PASS 的网络包都已经处理完毕。
netif_receive_generic_xdp

// net/core/dev.c

static u32 netif_receive_generic_xdp(struct sk_buff *skb,
                     struct xdp_buff *xdp,
                     struct bpf_prog *xdp_prog)
{
    // ...

    // 准备 XDP 元数据,即准备 XDP 程序的第一个参数

    /* The XDP program wants to see the packet starting at the MAC
     * header.
     */
    mac_len = skb->data - skb_mac_header(skb);
    hlen = skb_headlen(skb) + mac_len;
    xdp->data = skb->data - mac_len;
    xdp->data_meta = xdp->data;
    xdp->data_end = xdp->data + hlen;
    xdp->data_hard_start = skb->data - skb_headroom(skb);

    /* SKB "head" area always have tailroom for skb_shared_info */
    xdp->frame_sz  = (void *)skb_end_pointer(skb) - xdp->data_hard_start;
    xdp->frame_sz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));

    orig_data_end = xdp->data_end;
    orig_data = xdp->data;
    eth = (struct ethhdr *)xdp->data;
    orig_bcast = is_multicast_ether_addr_64bits(eth->h_dest);
    orig_eth_type = eth->h_proto;

    rxqueue = netif_get_rxqueue(skb);
    xdp->rxq = &rxqueue->xdp_rxq;

    act = bpf_prog_run_xdp(xdp_prog, xdp);

    // ...

    switch (act) {
    case XDP_REDIRECT:
    case XDP_TX:
        __skb_push(skb, mac_len);
        break;
    case XDP_PASS:
        metalen = xdp->data - xdp->data_meta;
        if (metalen)
            skb_metadata_set(skb, metalen);
        break;
    default:
        bpf_warn_invalid_xdp_action(act);
        fallthrough;
    case XDP_ABORTED:
        trace_xdp_exception(skb->dev, xdp_prog, act);
        fallthrough;
    case XDP_DROP:
    do_drop:
        kfree_skb(skb);
        break;
    }

    return act;
}以上代码片段做了以下工作:

  • 准备 XDP 元数据
  • xdp->data 网络包起始地址指向二层头部
  • 运行 XDP 程序
  • 对 XDP 程序运行结果进行简单处理,其中 XDP_ABORTED 和 XDP_DROP 直接释放 skb
结合 do_xdp_generic() 函数处理逻辑可知,do_xdp_generic() + netif_receive_generic_xdp() 处理了所有非 XDP_PASS 的情况; 所以在 __netif_receive_skb_core() 函数中,所有非 XDP_PASS 的情况都结束当前 skb 的处理流程。
bpf_prog_run_xdp

// include/linux/filter.h

static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog,
                        struct xdp_buff *xdp)
{
    /* Caller needs to hold rcu_read_lock() (!), otherwise program
     * can be released while still running, or map elements could be
     * freed early while still having concurrent users. XDP fastpath
     * already takes rcu_read_lock() when fetching the program, so
     * it's not necessary here anymore.
     */
    return __BPF_PROG_RUN(prog, xdp, BPF_DISPATCHER_FUNC(xdp));
}直接运行指定的 BPF 程序。此处不再展开 __BPF_PROG_RUN()。
小结

从源代码可知,XDP eBPF 程序处理的网络包是从二层开始的。因此,就有了一个类似 tcpdump 的抓包工具 xdp-dump。
而在 XDP eBPF 程序中,可对当前网络包进行以下处理:

  • XDP_ABORTED:中断网络包处理流程
  • XDP_DROP:丢包,同时不会继续后面的网络包处理流程
  • XDP_PASS:继续后面的网络包处理流程
  • XDP_TX:将网络包从当前网卡发包出去
  • XDP_REDIRECT:将网络包从其他网卡发包出去,即转发网络包
P.S. 网络上有不少 XDP 例子,在此就不提供 demo 啦。
<hr/>更多内容,请查看 eBPF Talk。
<hr/>
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋| 黑客通

GMT+8, 2025-10-15 19:48 , Processed in 0.130655 second(s), 24 queries .

Powered by Discuz! X3.4

Copyright © 2020, LianLian.

快速回复 返回顶部 返回列表