rbbcc 0.11.0.pre → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/examples/dns_blocker.rb +3 -2
- data/examples/lsm_sockblock.rb +142 -0
- data/examples/py-orig/sockblock.py +119 -0
- data/lib/rbbcc/bcc.rb +39 -0
- data/lib/rbbcc/clib.rb +1 -0
- data/lib/rbbcc/consts.rb +3 -0
- data/lib/rbbcc/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6795c09c9054d789de4ce10626404bc7f6e5f7cdb12296e2344b270f2a7fa2a7
|
|
4
|
+
data.tar.gz: e898c4bc91fcc3b7dff968a2daf44d667faf68c5ee9eef29b1eba4f14dc2d123
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d40bb0cc232b51f25cc6970580c00bcb52c13cf7423f5082f3bbb447b9ea5d5a59acb958a68df4b3a3639bf86ac8ee72a673313d009cdb18be9eec52dc3176a9
|
|
7
|
+
data.tar.gz: e9af3796aa61f7f76b0122e13b2ec6c0958c8d33edeb6c0d34a5813e0f38e7f1cbd46eb267290a3935b381d94815cb7b4479d5adfeee428c709124b7456e3fa4
|
data/Gemfile.lock
CHANGED
data/examples/dns_blocker.rb
CHANGED
|
@@ -82,14 +82,15 @@ end
|
|
|
82
82
|
|
|
83
83
|
def attach_tc(interface)
|
|
84
84
|
system(
|
|
85
|
-
"sudo
|
|
85
|
+
"sudo tc filter add dev #{interface} egress" +
|
|
86
86
|
" bpf pinned #{PIN_PATH} da",
|
|
87
87
|
exception: true
|
|
88
88
|
)
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def cleanup_tc(interface)
|
|
92
|
-
|
|
92
|
+
# Run idempotently
|
|
93
|
+
system("sudo tc qdisc del dev #{interface} clsact 2>/dev/null")
|
|
93
94
|
File.unlink(PIN_PATH) if File.exist?(PIN_PATH)
|
|
94
95
|
end
|
|
95
96
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# lsm_sockblock.rb Monitor/block AF_ALG socket_create via BPF LSM.
|
|
4
|
+
#
|
|
5
|
+
# This example uses LSM_PROBE(socket_create) and a BPF_ARRAY map for mode:
|
|
6
|
+
# 0 = preview (log only)
|
|
7
|
+
# 1 = block (return -EPERM)
|
|
8
|
+
#
|
|
9
|
+
# The config map is pinned to bpffs so mode can be changed externally.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# sudo ruby examples/lsm_sockblock.rb
|
|
13
|
+
# sudo ruby examples/lsm_sockblock.rb --mode block
|
|
14
|
+
# sudo ruby examples/lsm_sockblock.rb --pin-path /sys/fs/bpf/my_config_map
|
|
15
|
+
|
|
16
|
+
require 'optparse'
|
|
17
|
+
require 'socket'
|
|
18
|
+
require 'rbbcc'
|
|
19
|
+
|
|
20
|
+
include RbBCC
|
|
21
|
+
|
|
22
|
+
PROGRAM = <<~CLANG
|
|
23
|
+
#include <linux/lsm_hooks.h>
|
|
24
|
+
#include <linux/socket.h>
|
|
25
|
+
#include <uapi/asm-generic/errno-base.h>
|
|
26
|
+
|
|
27
|
+
struct data_t {
|
|
28
|
+
u32 pid;
|
|
29
|
+
int family;
|
|
30
|
+
int type;
|
|
31
|
+
int is_warning;
|
|
32
|
+
int is_blocked;
|
|
33
|
+
char comm[16];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
BPF_PERF_OUTPUT(events);
|
|
37
|
+
BPF_ARRAY(config_map, u32, 1);
|
|
38
|
+
|
|
39
|
+
LSM_PROBE(socket_create, int family, int type, int protocol, int kern)
|
|
40
|
+
{
|
|
41
|
+
u32 pid = bpf_get_current_pid_tgid() >> 32;
|
|
42
|
+
struct data_t data = {};
|
|
43
|
+
|
|
44
|
+
u32 key = 0;
|
|
45
|
+
u32 *mode = config_map.lookup(&key);
|
|
46
|
+
int is_block_mode = (mode && *mode == 1);
|
|
47
|
+
|
|
48
|
+
data.pid = pid;
|
|
49
|
+
data.family = family;
|
|
50
|
+
data.type = type;
|
|
51
|
+
bpf_get_current_comm(&data.comm, sizeof(data.comm));
|
|
52
|
+
|
|
53
|
+
if (family == AF_ALG) {
|
|
54
|
+
data.is_blocked = is_block_mode;
|
|
55
|
+
data.is_warning = 1;
|
|
56
|
+
events.perf_submit(ctx, &data, sizeof(data));
|
|
57
|
+
|
|
58
|
+
if (is_block_mode) {
|
|
59
|
+
return -EPERM;
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
data.is_blocked = 0;
|
|
63
|
+
data.is_warning = 0;
|
|
64
|
+
events.perf_submit(ctx, &data, sizeof(data));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
CLANG
|
|
70
|
+
|
|
71
|
+
options = {
|
|
72
|
+
mode: "preview",
|
|
73
|
+
pin_path: "/sys/fs/bpf/rbbcc_lsm_config_map"
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
OptionParser.new do |opts|
|
|
77
|
+
opts.banner = "Usage: #{$0} [--mode preview|block] [--pin-path PATH]"
|
|
78
|
+
|
|
79
|
+
opts.on("--mode MODE", ["preview", "block"], "Operation mode (default: preview)") do |v|
|
|
80
|
+
options[:mode] = v
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
opts.on("--pin-path PATH", "bpffs pin path (default: /sys/fs/bpf/rbbcc_lsm_config_map)") do |v|
|
|
84
|
+
options[:pin_path] = v
|
|
85
|
+
end
|
|
86
|
+
end.parse!
|
|
87
|
+
|
|
88
|
+
mode_value = (options[:mode] == "block") ? 1 : 0
|
|
89
|
+
|
|
90
|
+
families = Socket.constants.grep(/^AF_/).each_with_object({}) do |name, h|
|
|
91
|
+
begin
|
|
92
|
+
v = Socket.const_get(name)
|
|
93
|
+
h[v] = name.to_s if v.is_a?(Integer)
|
|
94
|
+
rescue NameError
|
|
95
|
+
next
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
types = Socket.constants.grep(/^SOCK_/).each_with_object({}) do |name, h|
|
|
100
|
+
begin
|
|
101
|
+
v = Socket.const_get(name)
|
|
102
|
+
h[v] = name.to_s if v.is_a?(Integer)
|
|
103
|
+
rescue NameError
|
|
104
|
+
next
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
begin
|
|
109
|
+
b = BCC.new(text: PROGRAM)
|
|
110
|
+
|
|
111
|
+
config = b["config_map"]
|
|
112
|
+
config[0] = mode_value
|
|
113
|
+
|
|
114
|
+
File.unlink(options[:pin_path]) if File.exist?(options[:pin_path])
|
|
115
|
+
BCC.pin!(config.map_fd, options[:pin_path])
|
|
116
|
+
|
|
117
|
+
puts "LSM BPF started in #{options[:mode].upcase} mode."
|
|
118
|
+
puts "Pinned config map: #{options[:pin_path]}"
|
|
119
|
+
puts "Tracing AF_ALG socket_create... Press Ctrl-C to exit."
|
|
120
|
+
|
|
121
|
+
b["events"].open_perf_buffer do |cpu, data, size|
|
|
122
|
+
event = b["events"].event(data)
|
|
123
|
+
family = families.fetch(event.family, "AF_UNKNOWN(#{event.family})")
|
|
124
|
+
stype = types.fetch(event.type, "SOCK_UNKNOWN(#{event.type})")
|
|
125
|
+
|
|
126
|
+
puts "PID: #{event.pid.to_s.ljust(7)} | COMM: #{event.comm.to_s.ljust(15)} | FAMILY: #{family.ljust(14)} | TYPE: #{stype}"
|
|
127
|
+
|
|
128
|
+
next if event.is_warning == 0
|
|
129
|
+
|
|
130
|
+
mode_str = (event.is_blocked == 1) ? "BLOCK" : "PREVIEW"
|
|
131
|
+
status = (event.is_blocked == 1) ? "REJECTED" : "WARNING"
|
|
132
|
+
puts "\e[1;31m[#{mode_str}] #{status}: PID #{event.pid} (#{event.comm}) tried AF_ALG socket creation.\e[0m"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
loop do
|
|
136
|
+
b.perf_buffer_poll
|
|
137
|
+
end
|
|
138
|
+
rescue Interrupt
|
|
139
|
+
puts "\nStopping..."
|
|
140
|
+
ensure
|
|
141
|
+
File.unlink(options[:pin_path]) if File.exist?(options[:pin_path])
|
|
142
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from bcc import BPF, lib
|
|
2
|
+
import socket
|
|
3
|
+
import ctypes
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# 1. C言語側のプログラム
|
|
7
|
+
program = r"""
|
|
8
|
+
#include <linux/lsm_hooks.h>
|
|
9
|
+
#include <linux/socket.h>
|
|
10
|
+
#include <uapi/asm-generic/errno-base.h>
|
|
11
|
+
|
|
12
|
+
struct data_t {
|
|
13
|
+
u32 pid;
|
|
14
|
+
int family;
|
|
15
|
+
int type;
|
|
16
|
+
int is_warning;
|
|
17
|
+
int is_blocked;
|
|
18
|
+
char comm[16];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
BPF_PERF_OUTPUT(events);
|
|
22
|
+
|
|
23
|
+
// モード保存用マップ (Index 0 を使用)
|
|
24
|
+
// 1: blockモード, 0: previewモード
|
|
25
|
+
BPF_ARRAY(config_map, u32, 1);
|
|
26
|
+
|
|
27
|
+
LSM_PROBE(socket_create, int family, int type, int protocol, int kern)
|
|
28
|
+
{
|
|
29
|
+
u32 pid = bpf_get_current_pid_tgid() >> 32;
|
|
30
|
+
struct data_t data = {};
|
|
31
|
+
|
|
32
|
+
// マップから現在のモードを取得
|
|
33
|
+
u32 key = 0;
|
|
34
|
+
u32 *mode = config_map.lookup(&key);
|
|
35
|
+
int is_block_mode = (mode && *mode == 1);
|
|
36
|
+
|
|
37
|
+
data.pid = pid;
|
|
38
|
+
data.family = family;
|
|
39
|
+
data.type = type;
|
|
40
|
+
bpf_get_current_comm(&data.comm, sizeof(data.comm));
|
|
41
|
+
|
|
42
|
+
if (family == AF_ALG) {
|
|
43
|
+
data.is_blocked = is_block_mode;
|
|
44
|
+
data.is_warning = 1;
|
|
45
|
+
|
|
46
|
+
// ログデータを送信
|
|
47
|
+
events.perf_submit(ctx, &data, sizeof(data));
|
|
48
|
+
|
|
49
|
+
// blockモードならエラーを返してシステムコールを失敗させる
|
|
50
|
+
if (is_block_mode) {
|
|
51
|
+
return -EPERM; // -1 (Operation not permitted)
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
data.is_blocked = 0;
|
|
55
|
+
data.is_warning = 0;
|
|
56
|
+
|
|
57
|
+
// ログデータを送信
|
|
58
|
+
events.perf_submit(ctx, &data, sizeof(data));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
# 定数解決用
|
|
66
|
+
families = {getattr(socket, n): n for n in dir(socket) if n.startswith('AF_')}
|
|
67
|
+
types = {getattr(socket, n): n for n in dir(socket) if n.startswith('SOCK_')}
|
|
68
|
+
|
|
69
|
+
def print_event(cpu, data, size):
|
|
70
|
+
event = b["events"].event(data)
|
|
71
|
+
|
|
72
|
+
family_str = families.get(event.family, f"AF_UNKNOWN({event.family})")
|
|
73
|
+
type_str = types.get(event.type, f"SOCK_UNKNOWN({event.type})")
|
|
74
|
+
|
|
75
|
+
print(f"PID: {event.pid:<7} | COMM: {event.comm.decode('utf-8'):<15} | "
|
|
76
|
+
f"FAMILY: {family_str:<12} | TYPE: {type_str}")
|
|
77
|
+
|
|
78
|
+
if event.is_warning:
|
|
79
|
+
mode_str = "BLOCK" if event.is_blocked else "PREVIEW"
|
|
80
|
+
status = "!! REJECTED !!" if event.is_blocked else "WARNING"
|
|
81
|
+
|
|
82
|
+
print(f"[{mode_str}] {status}: PID {event.pid} ({event.comm.decode()}) "
|
|
83
|
+
f"tried to create AF_ALG socket.")
|
|
84
|
+
|
|
85
|
+
# 2. ロードと設定
|
|
86
|
+
try:
|
|
87
|
+
b = BPF(text=program)
|
|
88
|
+
|
|
89
|
+
# --- モード設定 ---
|
|
90
|
+
# 1 を書き込むと blockモード、0 だと previewモード
|
|
91
|
+
mode = 0
|
|
92
|
+
config_table = b.get_table("config_map")
|
|
93
|
+
config_table[ctypes.c_uint32(0)] = ctypes.c_uint32(mode)
|
|
94
|
+
|
|
95
|
+
# Mapの永続化 - ユーザ空間からモードを変更できるようにするため
|
|
96
|
+
map_path = "/sys/fs/bpf/my_config_map"
|
|
97
|
+
if os.path.exists(map_path):
|
|
98
|
+
os.remove(map_path)
|
|
99
|
+
print(f"Pinning config map to -> {map_path}")
|
|
100
|
+
map_fd = config_table.get_fd()
|
|
101
|
+
res = lib.bpf_obj_pin(map_fd, ctypes.c_char_p(map_path.encode()))
|
|
102
|
+
if res != 0:
|
|
103
|
+
raise Exception(f"Failed to pin map to {map_path}: {os.strerror(-res)}")
|
|
104
|
+
|
|
105
|
+
# -----------------
|
|
106
|
+
|
|
107
|
+
print(f"LSM BPF started in {'BLOCK' if mode == 1 else 'PREVIEW'} mode.")
|
|
108
|
+
print("Tracing AF_ALG creation... Press Ctrl-C to exit.")
|
|
109
|
+
|
|
110
|
+
b["events"].open_perf_buffer(print_event)
|
|
111
|
+
|
|
112
|
+
while True:
|
|
113
|
+
try:
|
|
114
|
+
b.perf_buffer_poll()
|
|
115
|
+
except KeyboardInterrupt:
|
|
116
|
+
exit()
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
print(f"Failed: {e}")
|
data/lib/rbbcc/bcc.rb
CHANGED
|
@@ -242,6 +242,7 @@ module RbBCC
|
|
|
242
242
|
@uprobe_fds = {}
|
|
243
243
|
@tracepoint_fds = {}
|
|
244
244
|
@raw_tracepoint_fds = {}
|
|
245
|
+
@lsm_fds = {}
|
|
245
246
|
|
|
246
247
|
if src_file
|
|
247
248
|
src_file = BCC._find_file(src_file)
|
|
@@ -433,6 +434,34 @@ module RbBCC
|
|
|
433
434
|
@tracepoint_fds.delete(tp)
|
|
434
435
|
end
|
|
435
436
|
|
|
437
|
+
def attach_lsm(fn_name: "")
|
|
438
|
+
if @lsm_fds.keys.include?(fn_name)
|
|
439
|
+
raise "LSM #{fn_name} has been attached"
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
fn = load_func(fn_name, BPF::LSM)
|
|
443
|
+
fd = Clib.bpf_attach_lsm(fn[:fd])
|
|
444
|
+
if fd < 0
|
|
445
|
+
raise SystemCallError.new("Failed to attach LSM #{fn_name}", Fiddle.last_error)
|
|
446
|
+
end
|
|
447
|
+
Util.debug "Attach: #{fn_name}"
|
|
448
|
+
@lsm_fds[fn_name] = fd
|
|
449
|
+
self
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def detach_lsm(fn_name)
|
|
453
|
+
unless @lsm_fds.keys.include?(fn_name)
|
|
454
|
+
raise "LSM #{fn_name} is not attached"
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
begin
|
|
458
|
+
File.for_fd(@lsm_fds[fn_name]).close
|
|
459
|
+
rescue => e
|
|
460
|
+
warn "Closing fd failed: #{e.inspect}. Ignore and skip"
|
|
461
|
+
end
|
|
462
|
+
@lsm_fds.delete(fn_name)
|
|
463
|
+
end
|
|
464
|
+
|
|
436
465
|
def detach_kprobe_event(ev_name)
|
|
437
466
|
unless @kprobe_fds.keys.include?(ev_name)
|
|
438
467
|
raise "Event #{ev_name} not registered"
|
|
@@ -471,6 +500,10 @@ module RbBCC
|
|
|
471
500
|
@tracepoint_fds.size
|
|
472
501
|
end
|
|
473
502
|
|
|
503
|
+
def num_open_lsms
|
|
504
|
+
@lsm_fds.size
|
|
505
|
+
end
|
|
506
|
+
|
|
474
507
|
def tracefile
|
|
475
508
|
@tracefile ||= File.open("#{TRACEFS}/trace_pipe", "rb")
|
|
476
509
|
end
|
|
@@ -528,6 +561,10 @@ module RbBCC
|
|
|
528
561
|
detach_raw_tracepoint(k)
|
|
529
562
|
end
|
|
530
563
|
|
|
564
|
+
@lsm_fds.each do |k, v|
|
|
565
|
+
detach_lsm(k)
|
|
566
|
+
end
|
|
567
|
+
|
|
531
568
|
if @module
|
|
532
569
|
Clib.bpf_module_destroy(@module)
|
|
533
570
|
end
|
|
@@ -643,6 +680,8 @@ module RbBCC
|
|
|
643
680
|
tp: tp,
|
|
644
681
|
fn_name: fn[:name]
|
|
645
682
|
)
|
|
683
|
+
elsif func_name.start_with?("lsm__")
|
|
684
|
+
attach_lsm(fn_name: func_name)
|
|
646
685
|
end
|
|
647
686
|
end
|
|
648
687
|
end
|
data/lib/rbbcc/clib.rb
CHANGED
|
@@ -114,6 +114,7 @@ module RbBCC
|
|
|
114
114
|
extern 'int bpf_attach_tracepoint(int progfd, char *tp_category, char *tp_name)'
|
|
115
115
|
extern 'int bpf_detach_tracepoint(char *tp_category, char *tp_name)'
|
|
116
116
|
extern 'int bpf_attach_raw_tracepoint(int progfd, char *tp_name)'
|
|
117
|
+
extern 'int bpf_attach_lsm(int progfd)'
|
|
117
118
|
extern 'int bpf_open_perf_event(unsigned int, unsigned long, int, int)'
|
|
118
119
|
extern 'int bpf_close_perf_event_fd(int)'
|
|
119
120
|
extern 'int bpf_get_first_key(int, void *, int)'
|
data/lib/rbbcc/consts.rb
CHANGED
data/lib/rbbcc/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rbbcc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.11.
|
|
4
|
+
version: 0.11.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Uchio Kondo
|
|
@@ -92,9 +92,11 @@ files:
|
|
|
92
92
|
- examples/hello_ring_buffer.rb
|
|
93
93
|
- examples/hello_world.rb
|
|
94
94
|
- examples/kvm_hypercall.rb
|
|
95
|
+
- examples/lsm_sockblock.rb
|
|
95
96
|
- examples/mallocstack.rb
|
|
96
97
|
- examples/networking/http_filter/http-parse-simple.c
|
|
97
98
|
- examples/networking/http_filter/http-parse-simple.rb
|
|
99
|
+
- examples/py-orig/sockblock.py
|
|
98
100
|
- examples/ruby_usdt.rb
|
|
99
101
|
- examples/sbrk_trace.rb
|
|
100
102
|
- examples/syscalluname.rb
|
|
@@ -142,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
142
144
|
- !ruby/object:Gem::Version
|
|
143
145
|
version: '0'
|
|
144
146
|
requirements: []
|
|
145
|
-
rubygems_version:
|
|
147
|
+
rubygems_version: 4.0.6
|
|
146
148
|
specification_version: 4
|
|
147
149
|
summary: BCC port for MRI
|
|
148
150
|
test_files: []
|