Skip to content

Commit

Permalink
Merge pull request #3 from takehaya/feature/impl_xdp_link_code
Browse files Browse the repository at this point in the history
feat: demo sample code
  • Loading branch information
takehaya authored Oct 3, 2024
2 parents 171e816 + 21ed73f commit 4d00f19
Show file tree
Hide file tree
Showing 13 changed files with 244 additions and 39 deletions.
2 changes: 1 addition & 1 deletion docs/elf_inspect.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## elfの中身を眺める
```shell
$ readelf -a kprobe.o
$ readelf -a kprobe.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Expand Down
5 changes: 5 additions & 0 deletions docs/perl_env.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ pre-commit run -a
## package update
```shell
sudo PERL5LIB=$PERL5LIB $(which minil) test
```

## bpf printk
```shell
sudo cat /sys/kernel/debug/tracing/trace_pipe
```
32 changes: 26 additions & 6 deletions lib/Sys/Ebpf/Map.pm
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ sub raw_get_next_key {
my $next_key = "\0" x $self->{key_size};
my $res = syscall_bpf_map_elem( BPF_MAP_GET_NEXT_KEY(), $self->{map_fd},
$key, $next_key, 0 );
if ( $res < 0 ) {
my $errno = $!;
warn "Failed to get_next_key BPF map entry with key ",
unpack( "H*", $key ), ": $errno ($!)\n";
}
return $res == 0 ? $next_key : undef;
}

Expand Down Expand Up @@ -232,10 +237,10 @@ sub delete {

sub get_next_key {
my ( $self, $key ) = @_;
my $packed_key
= defined $key
? $self->_serialize( $key, $self->{key_schema} )
: undef;
if ( !defined $self->{key_schema} || !defined $self->{value_schema} ) {
die "Key and value schema must be defined to use update method\n";
}
my $packed_key = $self->_serialize( $key, $self->{key_schema} );
my $next_packed_key = $self->raw_get_next_key($packed_key);
return undef unless defined $next_packed_key;
return $self->_deserialize( $next_packed_key, $self->{key_schema} );
Expand Down Expand Up @@ -290,6 +295,11 @@ sub _match_uint_or_uint_array {
return $type =~ /^uint(\d+)(?:\[(\d+)\])?$/ ? ( $1, $2 ) : ();
}

sub _match_string {
my ($type) = @_;
return $type =~ /^string\[(\d+)\]$/ ? ( 8, $1 ) : ();
}

sub _get_type_size {
my ($type) = @_;

Expand All @@ -299,8 +309,8 @@ sub _get_type_size {
return ( $bit_size / 8 ) * $array_size;
}
}
elsif ( $type =~ /^string\((\d+)\)$/ ) {
return $1;
elsif ( ( $bit_size, $array_size ) = _match_string($type) ) {
return ( $bit_size / 8 ) * $array_size;
}

die "Unsupported type: $type";
Expand All @@ -316,6 +326,10 @@ sub _pack_value {
if ref $value eq 'ARRAY';
return pack( $pack_char, $value );
}
elsif ( ( $bit_size, $array_size ) = _match_string($type) ) {
return pack( "A$array_size", $value )
if length($value) <= $array_size;
}

die "Unsupported type: $type";
}
Expand Down Expand Up @@ -347,6 +361,12 @@ sub _unpack_value {
# Otherwise, return the single value
return ( $values[0], $offset + $byte_size );
}
elsif ( ( $bit_size, $array_size ) = _match_string($type) ) {
my $value
= unpack( "A$array_size", substr( $data, $offset, $array_size ) );
my $len = length($value);
return ( $value, $offset + $len );
}

die "Unsupported type: $type";
}
Expand Down
3 changes: 3 additions & 0 deletions sample/kprobe_file_open_tracker/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

clang -O3 -emit-llvm -c kprobe_file_open_tracker.c -o - | llc -march=bpf -filetype=obj -o kprobe_file_open_tracker.o
65 changes: 65 additions & 0 deletions sample/kprobe_file_open_tracker/kprobe_file_open_tracker.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include <linux/bpf.h>
#include <linux/ptrace.h>
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/sched.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

char __license[] SEC("license") = "Dual MIT/GPL";

#define MAX_FILENAME_LEN 128
#define MAX_ENTRIES 1024

struct file_open_info
{
__u32 count;
char filename[MAX_FILENAME_LEN];
};

struct bpf_map_def SEC("maps") file_open_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(struct file_open_info),
.max_entries = MAX_ENTRIES,
};

SEC("kprobe/sys_open")
int kprobe_sys_open(struct pt_regs *ctx)
{
__u32 pid = bpf_get_current_pid_tgid() >> 32;

// __userを削除
const char *filename_ptr = (const char *)PT_REGS_PARM1(ctx);
char filename[MAX_FILENAME_LEN];
int ret = bpf_probe_read_kernel_str(filename, sizeof(filename), filename_ptr);
if (ret < 0)
{
bpf_printk("bpf_probe_read_user_str failed: %d\n", ret);
return 0;
}
bpf_printk("filename: %s\n", filename);
struct file_open_info info = {};
struct file_open_info *pinfo;

pinfo = bpf_map_lookup_elem(&file_open_map, &pid);
if (pinfo)
{
// 既存のエントリがある場合はカウントを増加
info.count = pinfo->count + 1;
}
else
{
// 新しいエントリの場合はカウントを1に設定
info.count = 1;
}

// ファイル名をコピー
__builtin_memcpy(&info.filename, filename, sizeof(info.filename));
bpf_printk("pid: %d\n", pid);
// bpf_printk("filename: %s\n", info.filename);
// マップを更新
bpf_map_update_elem(&file_open_map, &pid, &info, BPF_ANY);

return 0;
}
54 changes: 54 additions & 0 deletions sample/kprobe_file_open_tracker/kprobe_file_open_tracker.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use lib '../../lib';
use Sys::Ebpf::Loader;
use Sys::Ebpf::Link::Perf::Kprobe;

my $file = "kprobe_file_open_tracker.o";
my $loader = Sys::Ebpf::Loader->new($file);
my $data = $loader->load_elf();

my $kprobe_fn = "kprobe/sys_open";

my ( $map_data, $prog_fd ) = $loader->load_bpf($kprobe_fn);
my $map_file_open = $map_data->{file_open_map};
$map_file_open->{key_schema} = [ [ 'pid', 'uint32' ] ];
$map_file_open->{value_schema}
= [ [ 'count', 'uint32' ], [ 'filename', 'string[128]' ] ];

my $kprobe_info
= Sys::Ebpf::Link::Perf::Kprobe::attach_kprobe( $prog_fd, $kprobe_fn );

print "Program FD: $prog_fd\n";
print "ファイルオープンの追跡を開始します。Ctrl+Cで停止します。\n";

$map_file_open->update( { pid => $$ }, { count => 0, filename => "sample" } );

while (1) {
my $prev_key = undef;
my $has_entries = 0;
while ( defined( my $key = $map_file_open->get_next_key($prev_key) ) ) {
$has_entries = 1;
my $value = $map_file_open->lookup($key);
if ( defined $value ) {
printf "PID: %d, ファイル名: %s, オープン回数: %d\n",
$key->{pid}, $value->{filename}, $value->{count};
}
$prev_key = $key;
sleep(1);
}
if ( !$has_entries ) {
print "マップにエントリがありません。\n";
}
print "---\n";
sleep(1);
}

END {
if ($kprobe_info) {
Sys::Ebpf::Link::Perf::Kprobe::detach_kprobe($kprobe_info);
}
}
3 changes: 3 additions & 0 deletions sample/xdp_count_8080_port/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

clang -O3 -emit-llvm -c xdp_count_8080_port.c -o - | llc -march=bpf -filetype=obj -o xdp_count_8080_port.o
56 changes: 56 additions & 0 deletions sample/xdp_count_8080_port/xdp_count_8080_port.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

struct bpf_map_def SEC("maps") xdp_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.max_entries = 1,
};

SEC("xdp/xdp_count_8080_port")
int xdp_count_8080_port(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
__u32 key = 0;
__u64 *value;

// Ethernetヘッダのサイズ確認
if (data + sizeof(*eth) > data_end)
return XDP_PASS;

// IPヘッダの確認
struct iphdr *ip = data + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end)
return XDP_PASS;

// TCPパケットかどうかを確認
if (ip->protocol != IPPROTO_TCP)
return XDP_PASS;

// TCPヘッダの確認
struct tcphdr *tcp = (void *)ip + sizeof(*ip);
if ((void *)tcp + sizeof(*tcp) > data_end)
return XDP_PASS;

// 目的ポートが8080かどうか確認
if (tcp->dest == bpf_htons(8080))
{
value = bpf_map_lookup_elem(&xdp_map, &key);
if (value)
{
__sync_fetch_and_add(value, 1);
}
}

return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
use Sys::Ebpf::Loader;
use Sys::Ebpf::Link::Netlink::Xdp;

my $file = "xdp_dport_counter.o";
my $file = "xdp_count_8080_port.o";
my $loader = Sys::Ebpf::Loader->new($file);
my $data = $loader->load_elf();

my $xdp_fn = "xdp/xdp_pass";
my $xdp_fn = "xdp/xdp_count_8080_port";

my ( $map_data, $prog_fd ) = $loader->load_bpf($xdp_fn);
my $map_xdp_map = $map_data->{xdp_map};
Expand Down
3 changes: 0 additions & 3 deletions sample/xdp_dport_counter/build.sh

This file was deleted.

26 changes: 0 additions & 26 deletions sample/xdp_dport_counter/xdp_dport_counter.c

This file was deleted.

Empty file modified script/remove_bak.sh
100644 → 100755
Empty file.
30 changes: 29 additions & 1 deletion t/02_ebpf_map_serialization.t
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ my $map = Sys::Ebpf::Map->new(
[ 'uint16_id', 'uint16[2]' ],
[ 'uint32_id', 'uint32' ],
[ 'uint64_id', 'uint64' ],
[ 'string_id', 'string[128]' ],
],
value_schema => [
[ 'uint8_value', 'uint8[4]' ],
[ 'uint16_value', 'uint16[2]' ],
[ 'uint32_value', 'uint32' ],
[ 'uint64_value', 'uint64' ],
[ 'string_value', 'string[128]' ],
],
);

Expand All @@ -37,6 +39,7 @@ subtest '_match_uint_or_uint_array tests' => sub {
{ type => 'uint16[4]', bitsize => 16, array_size => 4 },
{ type => 'uint32[4]', bitsize => 32, array_size => 4 },
{ type => 'uint64[4]', bitsize => 64, array_size => 4 },

);

for my $case (@test_cases) {
Expand All @@ -49,6 +52,26 @@ subtest '_match_uint_or_uint_array tests' => sub {
}
};

subtest '_match_string tests' => sub {
my @test_cases = (
{ type => 'string[128]', bitsize => 8, array_size => 128 },
{ type => 'string[64]', bitsize => 8, array_size => 64 },
{ type => 'string[32]', bitsize => 8, array_size => 32 },
{ type => 'string[16]', bitsize => 8, array_size => 16 },
{ type => 'string[8]', bitsize => 8, array_size => 8 },
{ type => 'string[1]', bitsize => 8, array_size => 1 },
);

for my $case (@test_cases) {
my ( $bit_size, $array_size )
= Sys::Ebpf::Map::_match_string( $case->{type} );
is( $bit_size, $case->{bitsize},
"Correct bit size for $case->{type}" );
is( $array_size, $case->{array_size},
"Correct array size for $case->{type}" );
}
};

subtest '_pack_value and _unpack_value tests' => sub {
my @test_cases = (
{ type => 'uint8[4]',
Expand All @@ -64,6 +87,10 @@ subtest '_pack_value and _unpack_value tests' => sub {
value => 1234567890,
expected => pack( 'Q', 1234567890 )
},
{ type => 'string[128]',
value => 'test',
expected => pack( 'A128', 'test' )
},
);

for my $case (@test_cases) {
Expand All @@ -84,10 +111,11 @@ subtest '_serialize and _deserialize tests' => sub {
uint16_id => [ 256, 512 ],
uint32_id => 12345,
uint64_id => 1234567890,
string_id => 'test',
};

my $serialized = $map->_serialize( $test_data, $map->{key_schema} );
is( length($serialized), 20, "Serialized key has correct length" );
is( length($serialized), 148, "Serialized key has correct length" );

my $deserialized = $map->_deserialize( $serialized, $map->{key_schema} );
is_deeply( $deserialized, $test_data,
Expand Down

0 comments on commit 4d00f19

Please sign in to comment.