歡迎光臨
每天分享高質量文章

從Bcc到xdp原理分析

 

 

 

附:ebpf框架(map作為用戶態和內核態註入代碼的通信(目前upstream xdp支持devmap、cpumap、xskmap),通過verfier檢查,通過JIT即時翻譯,底層有各種靜態探針點,比如xdp,tracepoint,perf,也有動態探針點,比如kprobe)

xdp註入流程

以bcc的開源xdp_redirect_map為例講述整個xdp的流程
https://github.com/iovisor/bcc/blob/b0bf04ac4042a6c004b15dcc5e40e12ae78020f9/examples/networking/xdp/xdp_redirect_map.py

int xdp_redirect_map(struct xdp_md *ctx) {
    void* data_end = (void*)(long)ctx->data_end;
    void* data = (void*)(long)ctx->data;
    struct ethhdr *eth = data;
    uint32_t key = 0;
    long *value;
    uint64_t nh_off;
    nh_off = sizeof(*eth);
    if (data + nh_off  > data_end)
        return XDP_DROP;
    value = rxcnt.lookup(&key;);
    if (value)
        *value += 1;
    swap_src_dst_mac(data);
    return tx_port.redirect_map(0, 0);
}
int xdp_dummy(struct xdp_md *ctx) {
    return XDP_PASS;
}
""", cflags=["-w"])

tx_port = b.get_table("tx_port")
tx_port[0] = ct.c_int(out_idx)

in_fn = b.load_func("xdp_redirect_map", BPF.XDP)
out_fn = b.load_func("xdp_dummy", BPF.XDP)

b.attach_xdp(in_if, in_fn, flags)
b.attach_xdp(out_if, out_fn, flags)

這個sample講的是將in ifdev的包文redirect到out ifdev發出去(比如in為eth0,out為eth1),
in_fn = b.load_func(“xdp_redirect_map”, BPF.XDP) //加載xdp_redirect_map函式,傳回prog_fd給in_fn
in_if就是eth0 dev設備
b.attach_xdp(in_if, in_fn, flags),//attach xdp

attach_xdp

呼叫邏輯
(bcc)/src/cc/libbpf.c bpf_attach_xdp(const char *dev_name, int progfd, uint32_t flags)
—(linux)/tools/lib/bpf/netlink.c bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
建立netlink socket,發送
req.nh.nlmsg_type = RTM_SETLINK
req.ifinfo.ifi_family = AF_UNSPEC
req.ifinfo.ifi_index = ifindex; //傳入out ifindex
nla->nla_type = NLA_F_NESTED | IFLA_XDP
memcpy((char *)nla_xdp + NLA_HDRLEN, &fd;, sizeof(fd)); //傳入xdp_redirect_map的prog_fd
接受端:
(linux)/net/core/rtnetlink.c
rtnl_setlink
根據ifm->ifi_index查詢本net namespace的dev(後面簡稱outdev)
-do_setlink
從xdp[IFLA_XDP_PROG_ID中獲取到prog_fd
–dev_change_xdp_fd

dev_change_xdp_fd

呼叫dev_xdp_install安裝xdp函式
這裡會呼叫dev->netdev_ops->ndo_bpf(XDP_SETUP_PROG)
以i40e網卡驅動為例,呼叫:
i40e_xdp_setup
將vsi->xdp_prog賦值為prog_fd
將vsi->rx_rings[i]->xdp_prog賦值為prog_fd
這樣後面呼叫vsi->rx_rings[i]->xdp_prog時,就會呼叫到用戶註入的代碼

xdp收包

以i40e為例,底層驅動收包流程如下
i40e_napi_poll
—i40e_clean_rx_irq_zc
——i40e_run_xdp
———bpf_prog_run_xdp
————BPF_PROG_RUN
—————(*(prog)->bpf_func)(ctx, (prog)->insnsi)//這裡就是用戶態註入的函式本例註入的xdp_redirect_map函式(vsi->rx_rings[i]->xdp_prog->bpf_func)
——————swap_src_dst_mac(data);//交換src dst的mac,這樣src為eth0 mac,dst為原nc的mac
——————tx_port.redirect_map//tx_port的設置是通過map機制,為eth2的ifdex
———————bpf_xdp_redirect_map(這個怎麼來的?xdp_verifier_ops->xdp_func_proto->BPF_FUNC_redirect_map->bpf_xdp_redirect_map_proto->bpf_xdp_redirect_map)
————————struct bpf_redirect_info *ri = this_cpu_ptr(&bpf;_redirect_info);
————————ri->ifindex = ifindex;
————————WRITE_ONCE(ri->map, map);
————————傳回XDP_REDIRECT
———xdp_do_redirect
————xdp_do_redirect_map
————— __xdp_map_lookup_elem//根據index和map獲取到標的端口(eth1)的信息
—————__bpf_tx_xdp_map  //將xdp資料賦值到標的eth1 dev的xdp_bluk_quue(this_cpu_ptr(obj->bulkq)->q[blukq->count])裡面,這裡遺留個flag MEM_TYPE_ZERO_COPY用於零拷貝(這個feature引入的文章https://lwn.net/Articles/754659/)
—————ri->map_to_flush = map;//設置map_to_flush標誌
—— i40e_finalize_xdp_rx(rx_ring, xdp_xmit);//將xdp_bluk_quue裡面的
——— xdp_do_flush_map
————__dev_map_flush
————— bq_xmit_all
—————— ndo_xdp_xmit(dev, count,q) //呼叫標的eth1的xdp發包

xdp發包

ndo_xdp_xmit

i40e網卡對應i40e_xdp_xmit
呼叫i40e_xmit_xdp_ring往DMA發包,這樣從eth0收到的包文,就直接通過eth1發出去啦。

參考文獻

https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf/

赞(0)

分享創造快樂