BPF(Berkeley Packet Filter)の話題は下記に続いてですが、カーネルプログラムのことなので、本当はこちらを先にやるべきでしたが、後追いでテストしてみました。(環境は下記と同じ)
「BPF Compiler Collection」
参考)
「BPFによるパケットトレース――C言語によるBPFプログラムの作り方、使い方」
https://atmarkit.itmedia.co.jp/ait/articles/1911/05/news010.html
ここで紹介されているのはカーネルにあるBPFりサンプルプログラムなので、自分の環境で動かすことはBPF以外でも使えますのでやっておく意義があります。
https://github.com/torvalds/linux/blob/v5.3/samples/bpf/sockex1_user.c
紹介されているコードは、上記カーネルの一部なので、ダウンロードしてきてビルドを試しましたが、ソースのパッケージとしてダウンロードしたものを利用した方がよいようです。
uname -r
4.15.0-202-generic
sudo apt install linux-source-4.15.0
バージョンにあったものをインストールしました。
cd /usr/src/linux-source-4.15.0/linux-source-4.15.0
sudo make olddefconfig
sudo make headers_installsudo apt install libssl-dev
sudo apt install netperfsudo make samples/bpf/
makeで、test_attr__openが未定義でエラーになるので、下記を参考に無効化しました。
https://github.com/bpftools/linux-observability-with-bpf/issues/46
https://lore.kernel.org/all/157111750919.12254.12122425573168365300.tip-bot2@tip-bot2/T/
tools/perf/perf-sys.h
sys_perf_event_open(struct perf_event_attr *attr,
pid_t pid, int cpu, int group_fd,
unsigned long flags)
{
int fd;fd = syscall(__NR_perf_event_open, attr, pid, cpu,
group_fd, flags);
#undef HAVE_ATTR_TEST
#ifdef HAVE_ATTR_TEST
if (unlikely(test_attr__enabled))
test_attr__open(attr, pid, cpu, fd, group_fd, flags);
#endif
return fd;
}
対象ファイル
/usr/src/linux-source-4.15.0/linux-source-4.15.0/samples/bpf/sockex1_user.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// SPDX-License-Identifier: GPL-2.0 #include <stdio.h> #include <assert.h> #include <linux/bpf.h> #include "libbpf.h" #include "bpf_load.h" #include "sock_example.h" #include <unistd.h> #include <arpa/inet.h> #include <sys/resource.h> int main(int ac, char **argv) { struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; char filename[256]; //FILE *f; int i, sock; snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); setrlimit(RLIMIT_MEMLOCK, &r); if (load_bpf_file(filename)) { printf("%s", bpf_log_buf); return 1; } sock = open_raw_sock("lo"); assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, prog_fd, sizeof(prog_fd[0])) == 0); //f = popen("ping -4 -c5 localhost", "r"); //(void) f; for (i = 0; i < 30; i++) { long long tcp_cnt, udp_cnt, icmp_cnt; int key; key = IPPROTO_TCP; assert(bpf_map_lookup_elem(map_fd[0], &key, &tcp_cnt) == 0); key = IPPROTO_UDP; assert(bpf_map_lookup_elem(map_fd[0], &key, &udp_cnt) == 0); key = IPPROTO_ICMP; assert(bpf_map_lookup_elem(map_fd[0], &key, &icmp_cnt) == 0); printf("TCP %lld UDP %lld ICMP %lld bytes\n", tcp_cnt, udp_cnt, icmp_cnt); sleep(1); } return 0; } |
実行(カレントディレクトリ: /usr/src/linux-source-4.15.0/linux-source-4.15.0/samples/bpf/)
sudo ./sockex1
変更部分
実行すると下記エラーがでたことから、sockex2_user.cを参考に、setrlimit部分を追加しました。
また、pingの接続は外部コマンドで実行したのでコメント、そして監視時間を30秒の伸ばしました。
failed to create map ‘Operation not permitted’
sockex1_user.cは、下記コードとセットになって動作します。(sockex1_kern.oをロードしている)
sockex1_kern.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <uapi/linux/bpf.h> #include <uapi/linux/if_ether.h> #include <uapi/linux/if_packet.h> #include <uapi/linux/ip.h> #include "bpf_helpers.h" struct bpf_map_def SEC("maps") my_map = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(long), .max_entries = 256, }; SEC("socket1") int bpf_prog1(struct __sk_buff *skb) { int index = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)); long *value; if (skb->pkt_type != PACKET_OUTGOING) return 0; value = bpf_map_lookup_elem(&my_map, &index); if (value) __sync_fetch_and_add(value, skb->len); return 0; } char _license[] SEC("license") = "GPL"; |
実行画面
上記実行後、別コンソールでUDP通信(下記)、PING通信(ping localhost)と続いてやりました。
nc -u -l -p 9999
別コンソールで
nc -u localhost 9999
TCP通信は、内部で動作しているサーバやSSH接続によるものと思われます。
ここまで動作確認できました。