Assignment1
eBPFプログラムが再ロードされた場合、xdp_statsプログラムでそれを検知して、mapを再取得するというもの。
解いてみる
誤ったファイルディスクリプタを使っていないかbpfマップを検証する関数を実装する
問題文に書いてある通り、新しいbpfプログラムが再ロードされたら、bpfマップのidが変わるので、それを利用して、誤ったファイルディスクリプタを使っていないか検証できる。
xdp_stats.c
@@ -180,6 +180,16 @@ static bool map_collect(int fd, __u32 map_type, __u32 key, struct record *rec) return true; } +static int is_wrong_fd(__u32 id, char * pin_dir) { + struct bpf_map_info info = { 0 }; + int map_fd = open_bpf_map_file(pin_dir, "xdp_stats_map", &info); + if (map_fd < 0) { + return -1; + } + close(map_fd); + return id != info.id; +} + static void stats_collect(int map_fd, __u32 map_type, struct stats_record *stats_rec) {
stats_pollのループの中でbpfマップを検証する。
stats_pollのループの中でbpfマップを検証するように修正を行う。
open_bpf_map_fileでエラーが起きた場合にはxdp_statsをEXIT_FAILで終了させている。
EXIT_FAILはcommon_difine.hで定義されていた終了値コードで、これを使わなくても良いが、既存のコードに合わせて使用している。
xdp_stats.c
@@ -191,7 +201,7 @@ static void stats_collect(int map_fd, __u32 map_type, } } -static void stats_poll(int map_fd, __u32 map_type, int interval) +static int stats_poll(int map_fd, __u32 map_type, int interval, __u32 id, char * pin_dir) { struct stats_record prev, record = { 0 }; @@ -203,6 +213,11 @@ static void stats_poll(int map_fd, __u32 map_type, int interval) usleep(1000000/4); while (1) { + int result; + result = is_wrong_fd(id, pin_dir); + if (result != 0) { + return result; + } prev = record; /* struct copy */ stats_collect(map_fd, map_type, &record); stats_print(&record, &prev); @@ -247,29 +262,38 @@ int main(int argc, char **argv) return EXIT_FAIL_OPTION; } - stats_map_fd = open_bpf_map_file(pin_dir, "xdp_stats_map", &info); - if (stats_map_fd < 0) { - return EXIT_FAIL_BPF; - } - - /* check map info, e.g. datarec is expected size */ - map_expect.key_size = sizeof(__u32); - map_expect.value_size = sizeof(struct datarec); - map_expect.max_entries = XDP_ACTION_MAX; - err = check_map_fd_info(&info, &map_expect); - if (err) { - fprintf(stderr, "ERR: map via FD not compatible\n"); - return err; - } - if (verbose) { - printf("\nCollecting stats from BPF map\n"); - printf(" - BPF map (bpf_map_type:%d) id:%d name:%s" - " key_size:%d value_size:%d max_entries:%d\n", - info.type, info.id, info.name, - info.key_size, info.value_size, info.max_entries - ); + while (1) { + stats_map_fd = open_bpf_map_file(pin_dir, "xdp_stats_map", &info); + if (stats_map_fd < 0) { + return EXIT_FAIL_BPF; + } + + /* check map info, e.g. datarec is expected size */ + map_expect.key_size = sizeof(__u32); + map_expect.value_size = sizeof(struct datarec); + map_expect.max_entries = XDP_ACTION_MAX; + err = check_map_fd_info(&info, &map_expect); + if (err) { + fprintf(stderr, "ERR: map via FD not compatible\n"); + return err; + } + if (verbose) { + printf("\nCollecting stats from BPF map\n"); + printf(" - BPF map (bpf_map_type:%d) id:%d name:%s" + " key_size:%d value_size:%d max_entries:%d\n", + info.type, info.id, info.name, + info.key_size, info.value_size, info.max_entries + ); + } + + int result; + result = stats_poll(stats_map_fd, info.type, interval, info.id, pin_dir); + if (result == -1) { + goto ERROR_CASE; + } + printf("bpf map will be reloaded.\n"); } - - stats_poll(stats_map_fd, info.type, interval); return EXIT_OK; +ERROR_CASE: + return EXIT_FAIL; }
試してみる
注意点としてbasic04では以下のようにprgonameを明示的に指定する必要がある。
また、attach_modeとしてデフォルトでXDP_MODE_NATIVEが指定されている。
私の環境はXDP_MODE_NATIVEを実行できる環境ではないので、-Aを指定してSKBモードで動作させている。
$ sudo ./xdp_loader --progname=xdp_pass_func --dev=lo -A $ sudo ./xdp_stats --dev=lo
上記を実行後、別ウィンドウでxdpプログラムを以下のように再ロードする。
$ sudo ./xdp_loader --progname=xdp_pass_func --dev=lo -A
xdp_statsの出力は以下となった。
Collecting stats from BPF map - BPF map (bpf_map_type:6) id:71 name:xdp_stats_map key_size:4 value_size:16 max_entries:5 XDP-action XDP_ABORTED 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250328 XDP_DROP 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250258 XDP_PASS 2 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250259 XDP_TX 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250259 XDP_REDIRECT 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250260 XDP-action XDP_ABORTED 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000448 XDP_DROP 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000450 XDP_PASS 2 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000450 XDP_TX 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000450 XDP_REDIRECT 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000450 XDP-action XDP_ABORTED 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000432 XDP_DROP 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000432 XDP_PASS 2 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000432 XDP_TX 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000432 XDP_REDIRECT 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000432 XDP-action XDP_ABORTED 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000417 XDP_DROP 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000417 XDP_PASS 2 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000417 XDP_TX 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000417 XDP_REDIRECT 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000417 XDP-action XDP_ABORTED 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000586 XDP_DROP 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000586 XDP_PASS 2 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000586 XDP_TX 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000586 XDP_REDIRECT 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000586 XDP-action XDP_ABORTED 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000425 XDP_DROP 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000425 XDP_PASS 2 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000425 XDP_TX 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000425 XDP_REDIRECT 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:2.000425 bpf map will be reloaded. Collecting stats from BPF map - BPF map (bpf_map_type:6) id:79 name:xdp_stats_map key_size:4 value_size:16 max_entries:5 XDP-action XDP_ABORTED 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250216 XDP_DROP 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250216 XDP_PASS 1 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250216 XDP_TX 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250216 XDP_REDIRECT 0 pkts ( 0 pps) 0 Kbytes ( 0 Mbits/s) period:0.250217
bpf_loaderを再実行したタイミングでbpf map will be reloaded
というログが出るのを確認できた。
Assignment2
ピンどめされたbpfマップを再利用するように修正する。
再利用のやり方は問題文が載っているページにほぼ載っている。
解いてみる
問題文が載っているページのヒントにしたがって解く
ピンどめされたbpfマップがあるとき、そのままの状態だとそれをクリアしているが、それをクリアせずに再利用させる。
xdp_loader.c
@@ -69,7 +69,7 @@ const char *pin_basedir = "/sys/fs/bpf"; const char *map_name = "xdp_stats_map"; /* Pinning maps under /sys/fs/bpf in subdir */ -int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, const char *subdir) +int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, const char *subdir, char *filename) { char map_filename[PATH_MAX]; char pin_dir[PATH_MAX]; @@ -90,16 +90,11 @@ int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, const char *subdir) /* Existing/previous XDP prog might not have cleaned up */ if (access(map_filename, F_OK ) != -1 ) { - if (verbose) - printf(" - Unpinning (remove) prev maps in %s/\n", - pin_dir); - - /* Basically calls unlink(3) on map_filename */ - err = bpf_object__unpin_maps(bpf_obj, pin_dir); - if (err) { - fprintf(stderr, "ERR: UNpinning maps in %s\n", pin_dir); - return EXIT_FAIL_BPF; - } + int pinned_map_fd = bpf_obj_get(map_filename); + struct bpf_object *obj = bpf_object__open(filename); + struct bpf_map *map = bpf_object__find_map_by_name(obj, map_name); + bpf_map__reuse_fd(map, pinned_map_fd); + bpf_object__load(obj); } if (verbose) printf(" - Pinning maps in %s/\n", pin_dir); @@ -150,7 +145,7 @@ int main(int argc, char **argv) } /* Use the --dev name as subdir for exporting/pinning maps */ - err = pin_maps_in_bpf_object(xdp_program__bpf_obj(program), cfg.ifname); + err = pin_maps_in_bpf_object(xdp_program__bpf_obj(program), cfg.ifname, cfg.filename); if (err) { fprintf(stderr, "ERR: pinning maps\n"); return err;
上のdiffにおける以下の部分は丸々、問題文ページにあるやり方の所をコピペしてやっている。
+ int pinned_map_fd = bpf_obj_get(map_filename); + struct bpf_object *obj = bpf_object__open(filename); + struct bpf_map *map = bpf_object__find_map_by_name(obj, map_name); + bpf_map__reuse_fd(map, pinned_map_fd); + bpf_object__load(obj);