色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何用eBPF實現一個學習型網橋

Linux閱碼場 ? 來源:未知 ? 2019-11-28 16:56 ? 次閱讀

eBPF技術風靡當下,eBPF字節碼正以星火燎原之勢被HOOK在Linux內核中越來越多的位置,在這些HOOK點上,我們可以像編寫普通應用程序一樣編寫內核的HOOK程序,與以往為了實現一個功能動輒patch一整套邏輯框架代碼(比如Netfilter)相比,eBPF的工作方式非常靈活。

我們先來看一下目前eBPF的一些重要HOOK點:


將來這個is_XXX序列肯定會不斷增加,布滿整個內核(有點密集恐懼癥癥狀了...)。

本文將描述如何用eBPF實現一個學習型網橋的快速轉發,并將其部署在XDP。

在開始之前,為了讓所有人都能看懂本文,我們先來回顧一些前置知識,如果暫時還不懂這些前置知識,沒關系,先把程序run起來是一個很好的起點,如果到時候你覺得沒意思,再放棄也不遲。

前置知識

什么是BPF和eBPF

簡單來講,BPF是一套完整的 計算機體系結構 。和x86,ARM這些類似,BPF包含自己的指令集和運行時邏輯,同理,就像在x86平臺編程,最終要落實到x86匯編指令一樣,BPF字節碼也可以看成是匯編指令的序列。我們通過tcpdump的-d/-dd參數可見一斑:

[root@localhost ~]# tcpdump -i any tcp and host 1.1.1.1 -d

(000) ldh [14]

(001) jeq #0x86dd jt 10 jf 2

(002) jeq #0x800 jt 3 jf 10

(003) ldb [25]

(004) jeq #0x6 jt 5 jf 10

(005) ld [28]

(006) jeq #0x1010101 jt 9 jf 7

(007) ld [32]

(008) jeq #0x1010101 jt 9 jf 10

(009) ret #262144

(010) ret #0

[root@localhost ~]#

BPF的歷史非常古老,早在1992年就被構建出來了,其背后的思想是, “與其把數據包復制到用戶空間執行用戶態程序過濾,不如把過濾程序灌進內核去。”

遺憾的是,BPF后來并沒有大行其道,只是被應用于非常有限的并不起眼的比如抓包層面。因此,由于它的語法并不復雜,人們直接手寫BPF匯編指令碼經簡單封裝即可生成最終的字節碼。

當人們認識到BPF非常強壯的功能并準備將其大用時,指令系統以及操作系統內核均已經持續進化了好多年,這意味著簡單的BPF不能再滿足需要,它需要 “被復雜化”

于是就出現了eBPF,即extended BPF。總體而言,eBPF相比BPF有了以下改進:1. 更復雜的指令系統。2. 更多可調用的函數。3. ...詳情可參見下面的鏈接:https://lwn.net/Articles/740157/

就像匯編語言進化到C語言一樣,直接手寫eBPF字節碼顯得即笨拙又低效,于是人們開始使用C語言直接編寫eBPF程序,然后用編譯器將其編譯成eBPF字節碼。遺憾的是,目前eBPF體系結構還不被gcc支持,不過很快就會支持了。我們不得不使用 特定的編譯器 來編譯eBPF的C代碼,比如clang。

什么是XDP

XDP,即eXpress Data Path,它其實是位于網卡驅動程序里的一個快速處理數據包的HOOK點,為什么快?基于以下兩點:

數據包處理位置非常底層,避開了很多內核skb處理開銷。

可以將很多處理邏輯Offload到網卡硬件

顯而易見,在XDP這個HOOK點灌進來一點eBPF字節碼,將是一件令人愉快的事情。

學習型網橋

Linux的Bridge模塊就是一個學習型網橋,其實就是一個現代交換式以太網交換機,它可以從端口學習到MAC地址,在內部生成MAC/端口映射表,以優化轉發效率。

本文我們將用eBPF實現的網橋就是一個學習型網橋,并且它的數據路徑和控制路徑相分離,用eBPF字節碼實現的正是其數據路徑,它將被灌入XDP,而控制路徑則由一個用戶態程序實現。

如何編譯eBPF程序

理論的學習自在平時,當打開電腦的時候,最快的速度run起來一些東西令人愉悅。我們不想花大量的時間在環境的搭建上。對于eBPF程序,內核源碼樹的samples/bpf目錄將是一個非常好的起點。

以我自己的環境為例,我使用的是Ubuntu 19.10發行版,5.3.0-19-generic內核,安裝源碼后,編譯之,最后編譯samples/bpf即可:

root@zhaoya-VirtualBox:/usr/src/linux-source-5.3.0/linux-source-5.3.0/samples/bpf# make

make -C ../../ /usr/src/linux-source-5.3.0/linux-source-5.3.0/samples/bpf/ BPF_SAMPLES_PATH=/usr/src/linux-source-5.3.0/linux-source-5.3.0/samples/bpf

make[1]: Entering directory '/usr/src/linux-source-5.3.0/linux-source-5.3.0'

CALL scripts/checksyscalls.sh

CALL scripts/atomic/check-atomics.sh

DESCEND objtool

...

samples/bpf目錄下的代碼都是比較典型的范例,我們照貓畫虎就能實現我們想要的功能。

大體上,每一個范例均由兩個部分組成:

XXX_kern.c文件:eBPF字節碼本身。

XXX_user.c文件:用戶態控制程序,控制eBPF字節碼的注入,更新。

即然我們要實現一個網橋,那么文件名我們可以確定為:

xdpbridgekern.c

xdpbridgeuser.c

同時我們修改Makefile文件,加入這兩個文件即可:

root@zhaoya-VirtualBox: samples/bpf# cat Makefile

...

hostprogs-y += xdp2

hostprogs-y += xdp_bridge

hostprogs-y += xdp_router_ipv4

...

xdp_bridge-objs := xdp_bridge_user.o

xdp_router_ipv4-objs := xdp_router_ipv4_user.o

...

always += xdp2_kern.o

always += xdp_bridge_kern.o

always += xdp_router_ipv4_kern.o

網橋XDP快速轉發的實現

對上述前置知識有了充分的理解之后,代碼就非常簡單了,我們剩下的工作就是填充xdpbridgekern.c和xdpbridgeuser.c兩個C文件,然后make它們。

我們先來看xdpbridgekern.c文件:

// xdp_bridge_kern.c

#include

#include

#include "bpf_helpers.h"

// mac_port_map保存該交換機的MAC/端口映射

struct bpf_map_def SEC("maps") mac_port_map = {

.type = BPF_MAP_TYPE_HASH,

.key_size = sizeof(long),

.value_size = sizeof(int),

.max_entries = 100,

};

// 以下函數是網橋轉發路徑的eBPF主函數實現

SEC("xdp_br")

int xdp_bridge_prog(struct xdp_md *ctx)

{

void *data_end = (void *)(long)ctx->data_end;

void *data = (void *)(long)ctx->data;

long dst_mac = 0;

int in_index = ctx->ingress_ifindex, *out_index;

// data即數據包開始位置

struct ethhdr *eth = (struct ethhdr *)data;

char info_fmt[] = "Destination Address: %lx Redirect to:[%d] From:[%d] ";

// 畸形包必須丟棄,否則無法通過內核的eBPF字節碼合法性檢查

if (data + sizeof(struct ethhdr) > data_end) {

return XDP_DROP;

}

// 獲取目標MAC地址

__builtin_memcpy(&dst_mac, eth->h_dest, 6);

// 在MAC/端口映射表里查找對應該MAC的端口

out_index = bpf_map_lookup_elem(&mac_port_map, &dst_mac);

if (out_index == NULL) {

// 如若找不到,則上傳到慢速路徑,必要時由控制路徑更新MAC/端口表項。

return XDP_PASS;

}

// 非Hairpin下生效

if (in_index == *out_index) { // Hairpin ?

return XDP_DROP;

}

// 簡單打印些調試信息

bpf_trace_printk(info_fmt, sizeof(info_fmt), dst_mac, *out_index, in_index);

// 轉發到出端口

return bpf_redirect(*out_index, 0);

}

char _license[] SEC("license") = "GPL";

這里有必要說一下內核對eBPF程序的合法性檢查,這個檢查一點都不多余,它確保你的eBPF代碼是安全的。這樣才不會造成內核數據結構被破壞掉,否則,如果任意eBPF程序都能注入內核,那結局顯然是細思極恐的。

現在繼續我們的用戶態C代碼:

// xdp_bridge_user.c

#include

#include

#include

#include

#include

#include

#include

#include "bpf_util.h"

int flags = XDP_FLAGS_UPDATE_IF_NOEXIST;

static int mac_port_map_fd;

static int *ifindex_list;

// 退出時卸載掉XDP的eBPF字節碼

static void int_exit(int sig)

{

int i = 0;

for (i = 0; i < 2; i++) {

bpf_set_link_xdp_fd(ifindex_list[i], -1, 0);

}

exit(0);

}

int main(int argc, char *argv[])

{

int sock, i;

char buf[1024];

char filename[64];

static struct sockaddr_nl g_addr;

struct bpf_object *obj;

struct bpf_prog_load_attr prog_load_attr = {

// prog_type指明eBPF字節碼注入的位置,我們網橋的例子中當然是XDP

.prog_type = BPF_PROG_TYPE_XDP,

};

int prog_fd;

snprintf(filename, sizeof(filename), "xdp_bridge_kern.o");

prog_load_attr.file = filename;

// 載入eBPF字節碼

if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) {

return 1;

}

mac_port_map_fd = bpf_object__find_map_fd_by_name(obj, "mac_port_map");

ifindex_list = (int *)calloc(2, sizeof(int *));

// 我們的例子中僅僅支持兩個端口的網橋,事實上可以多個。

ifindex_list[0] = if_nametoindex(argv[1]);

ifindex_list[1] = if_nametoindex(argv[2]);

for (i = 0; i < 2/*total */; i++) {

// 將eBPF字節碼注入到感興趣網卡的XDP

if (bpf_set_link_xdp_fd(ifindex_list[i], prog_fd, flags) < 0) {

printf("link set xdp fd failed ");

return 1;

}

}

signal(SIGINT, int_exit);

bzero(&g_addr, sizeof(g_addr));

g_addr.nl_family = AF_NETLINK;

g_addr.nl_groups = RTM_NEWNEIGH;

if ((sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {

int_exit(0);

return -1;

}

if (bind(sock, (struct sockaddr *) &g_addr, sizeof(g_addr)) < 0) {

int_exit(0);

return 1;

}

// 持續監聽socket,捕獲Linux網橋上傳的notify信息,從而更新,刪除eBPF的map里特定的MAC/端口表項

while (1) {

int len;

struct nlmsghdr *nh;

struct ndmsg *ifimsg ;

int ifindex = 0;

unsigned char *cmac;

unsigned long lkey = 0;

len = recv(sock, buf, sizeof(buf), 0);

if (len <= 0) continue;

for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {

ifimsg = NLMSG_DATA(nh) ;

if (ifimsg->ndm_family != AF_BRIDGE) {

continue;

}

// 獲取notify信息中的端口

ifindex = ifimsg->ndm_ifindex;

for (i = 0; i < 2; i++) {

if (ifindex == ifindex_list[i]) break;

}

if (i == 2) continue;

// 獲取notify信息中的MAC地址

cmac = (unsigned char *)ifimsg + sizeof(struct ndmsg) + 4;

memcpy(&lkey, cmac, 6);

if (nh->nlmsg_type == RTM_DELNEIGH) {

bpf_map_delete_elem(mac_port_map_fd, (const void *)&lkey);

printf("Delete XDP bpf map-[HW Address:Port] item Key:[%lx] Value:[%d] ", lkey, ifindex);

} else if (nh->nlmsg_type == RTM_NEWNEIGH) {

bpf_map_update_elem(mac_port_map_fd, (const void *)&lkey, (const void *)&ifindex, 0);

printf("Update XDP bpf map-[HW Address:Port] item Key:[%lx] Value:[%d] ", lkey, ifindex);

}

}

}

}

用戶態程序同樣很容易理解。

數據面和控制面分離,這是網絡設備的標準路數,幾十年前就這樣了,如今我們也能簡單實現一個了,很有趣不是嗎?

run起來

執行make之后,我們可以得到可執行文件xdpbridge以及eBPF字節碼文件xdpbridge_kern.o,在當前目錄下直接執行即可:

root@zhaoya-VirtualBox:samples/bpf# ./xdp_bridge enp0s9 enp0s10

在另一個終端查看eBPF字節碼里的map,即MAC/端口映射表:

root@zhaoya-VirtualBox:/home/zhaoya# bpftool p |tail -n 4

166: xdp name xdp_bridge_prog tag 956a68e9ac54a0b3 gpl

loaded_at 2019-11-08T01:14:46+0800 uid 0

xlated 576B jited 340B memlock 4096B map_ids 105

btf_id 114

root@zhaoya-VirtualBox:/home/zhaoya# bpftool map dump id 105

Found 0 elements

root@zhaoya-VirtualBox:/home/zhaoya#

OK,一切順利。現在讓我們正式用它搭建一個網橋吧。

暫時X掉xdp_bridge程序的運行,讓我們一步一步來。

首先構建下面的拓撲:

中間的Linux Bridge主機(后面簡稱主機B)的enp0s9,enp0s10網卡將是我們注入eBPF字節碼的位置。

現在讓我們在主機B上創建一個標準的Linux網橋:

brctl addbr br0;

brctl addif br0 enp0s9;

brctl addif br0 enp0s10;

ifconfig br0 up;

在主機H1和主機H2的enp0s9上配置同網段的地址:

H1-enp0s9:40.40.40.201/24

H2-enp0s9:40.40.40.100/24

互相ping確認是通的,并且主機B的enp0s9/enp0s10可以抓到雙向包,這說明主機B的Linux標準網橋工作是OK的。

接下來,停掉這一切,把br0也刪除掉。重新運行xdpbridge程序,確認OK后創建Linux標準網橋,從H1來ping H2,很暢通,同時我們會發現主機B的xdpbridge程序的輸出:

root@zhaoya-VirtualBox:/usr/src/linux-source-5.3.0/linux-source-5.3.0/samples/bpf# ./xdp_bridge enp0s9 enp0s10

Update XDP bpf map-[HW Address:Port] item Key:[683dbb270008] Value:[4]

Update XDP bpf map-[HW Address:Port] item Key:[683dbb270008] Value:[4]

Update XDP bpf map-[HW Address:Port] item Key:[e7f09f270008] Value:[5]

Update XDP bpf map-[HW Address:Port] item Key:[e7f09f270008] Value:[5]

Update XDP bpf map-[HW Address:Port] item Key:[e6f09f270008] Value:[4]

很顯然,eBPF的map學習到了新的MAC地址,我們可以用bpftool確認:

root@zhaoya-VirtualBox:~# bpftool p |tail -n 4

170: xdp name xdp_bridge_prog tag 956a68e9ac54a0b3 gpl

loaded_at 2019-11-08T01:26:19+0800 uid 0

xlated 576B jited 340B memlock 4096B map_ids 107

btf_id 117

root@zhaoya-VirtualBox:~# bpftool map dump id 107

key: 08 00 27 9f f0 e7 00 00 value: 05 00 00 00

key: 08 00 27 9f f0 e6 00 00 value: 04 00 00 00

key: 08 00 27 bb 3d 68 00 00 value: 04 00 00 00

Found 3 elements

此時,主機B的enp0s9和enp0s10就抓不到任何H1和H2之間單播包了。廣播包仍然會被上傳到慢速路徑被標準Linux網橋處理。

我們看trace日志:

root@zhaoya-VirtualBox:~# cat /sys/kernel/debug/tracing/trace_pipe

-0 [003] ..s. 44274.198178: 0: Destination Address: e6f09f270008 Redirect to:[4] From:[5]

...

雖然主機B的網卡上沒有抓到包,但如何確保數據包真的就是從XDP的eBPF字節碼轉發走的而不是直接飛過去的呢?

很好的問題,這作為下一個練習不是更好嗎?嗯,你應該試試加一個統計功能,而這個并不復雜。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11292

    瀏覽量

    209329
  • 網橋
    +關注

    關注

    0

    文章

    130

    瀏覽量

    16969
  • BPF
    BPF
    +關注

    關注

    0

    文章

    25

    瀏覽量

    4002

原文標題:實現一個基于XDP_eBPF的學習型網橋

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    基于LPC1114的學習型紅外遙控器程序設計

    本設計就是采用LPC1114+OLED+紅外接收頭構成學習型紅外遙控器,
    發表于 12-14 16:44 ?6026次閱讀

    分享下學習型紅外遙控資料

    分享下學習型紅外遙控資料
    發表于 07-30 19:47

    學習型遙控器

    學習型遙控器
    發表于 08-16 16:59

    智能學習型紅外遙控器設計

    智能學習型紅外遙控器設計
    發表于 08-16 19:26

    用89C52 做一個學習型遙控器

    現在有單片機89C52 紅外線發射二級管 三級管 按鈕 電阻 電容 0038紅外接收 晶振 想做一個 學習型遙控器 遙控電視 和空調 帶串口的更好 需要給我電路圖和元器件型號 還有單片機文件 不要粘貼復制的那種 網上找了很多 都不行 需要懂行的朋友幫助 非常感謝你們 急
    發表于 02-13 09:04

    學習型紅外遙控器設計

    有沒有前輩知道怎么寫基于MSP430F149的學習型紅外遙控器設計程序的?麻煩聯系我。O(∩_∩)O謝謝984300719
    發表于 05-01 15:22

    如何增加學習型遙控器的學習距離?

    如圖,學習型遙控器,那部分是學習天線,如何增大信號?
    發表于 08-16 09:48

    請問單片機解碼433MHZ EV1527學習型編碼ic 學習功能如何用程序實現

    目前我在做一個51單片機解碼433Mhz模塊的EV1527學習型編碼IC,不知道如何學習對碼?
    發表于 09-09 15:04

    基于AT89C52的學習型遙控器的設計

    本文介紹了種基于 AT89C52 的學習型遙控器,并對其工作原理及軟、硬件的設計和實現方法進行了詳細的闡述。關鍵詞: AT89C52; 學習型遙控器; 紅外遙控編碼Abstract:
    發表于 08-14 08:58 ?208次下載

    基于NiosⅡ的紅外學習型遙控器設計

      本文設計了種基于NiosⅡ的紅外學習型遙控器,把載波頻率測量、紅外信號解調、脈寬測量、調制發送IP核集中到FPG
    發表于 12-15 10:39 ?2615次閱讀

    自主學習型網絡課件的研究與開發

    隨著網絡信息的高速發展,傳統網絡課件以被動知識展示為主要手段已經不能滿足學習者自主學習的需求。自主學習型網絡課件立足學習者自身知識儲備,可以實現
    發表于 10-24 15:19 ?0次下載
    自主<b class='flag-5'>學習型</b>網絡課件的研究與開發

    學習型紅外線遙控設計與制作解析

    本文主要介紹了學習型紅外線遙控設計與制作解析.
    發表于 06-26 08:00 ?92次下載

    關于R8C/Lx學習型遙控器設計的介紹

    R8C/Lx學習型遙控器參考設計
    的頭像 發表于 07-23 01:04 ?3221次閱讀

    紅外學習型遙控器方案說明

    紅外學習型遙控器可通過學習操作學習其它遙控器上的部分按鍵,實現遙控器可遙控兩種設備,方便用戶操作。
    的頭像 發表于 10-18 11:31 ?4462次閱讀

    學習型遙控器的設計與實現

    電子發燒友網站提供《學習型遙控器的設計與實現.doc》資料免費下載
    發表于 10-24 09:33 ?1次下載
    <b class='flag-5'>學習型</b>遙控器的設計與<b class='flag-5'>實現</b>
    主站蜘蛛池模板: 国精产品一区二区三区有限公司| 欧美激情一区二区三区AA片| 色多多污网站在线观看| 影音先锋av男人资源| 国产精品久久久久久久久齐齐 | 亚洲AV久久久噜噜噜噜| 99久久免费精品国产免费| 精品国产在天天线在线麻豆| 色综合伊人色综合网站中国| 97综合久久| 久久草福利自拍视频在线观看| 网址在线观看你懂我意思吧免费的| 99视频精品在线| 就去色电影| 亚洲一级特黄| 国产欧美日韩亚洲第一页| 热中文热国产热综合| 91精品国产91| 久久精品成人免费看| 亚洲黄色官网| 国产福利不卡在线视频| 全球真实小U女视频合集| 国产精品xxxav免费视频| 97se se| 黄色直接观看| 亚洲H成年动漫在线观看不卡| 国产AV果冻传奇麻豆| 人人在线碰碰视频免费| 99在线国产视频| 美女xx00| 2018高清国产一区二区三区| 久久精品视频在线看15| 亚洲中文字幕一二三四区苍井空| 国产午夜在线观看视频播放| 无码不卡中文字幕在线观看| 成人女人A级毛片免费软件| 青春禁区动漫免费观看| tobu中国日本高清| 欧美最猛性XXX孕妇| SM高H黄暴NP辣H调教性奴| 欧美AAAAAA级午夜福利视频|