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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c5cbecb6f86bc56c109433915be6daea255c5578145372e7fef05e643c74f22
4
- data.tar.gz: 73b4dcb08ed7882ffd905f44b3662e1f26fbc7075b9e832c29f44701304b1826
3
+ metadata.gz: 6795c09c9054d789de4ce10626404bc7f6e5f7cdb12296e2344b270f2a7fa2a7
4
+ data.tar.gz: e898c4bc91fcc3b7dff968a2daf44d667faf68c5ee9eef29b1eba4f14dc2d123
5
5
  SHA512:
6
- metadata.gz: ce916936932a3a0bb00bf28b3bd67d55b2d2068e0d4d05308296d472306db0ffae3dd495a441189fdcfe0db0c29705a389f31781f0e3971f1ef349b1d0eb4a1f
7
- data.tar.gz: 15da5d9cd65ec31dd5a3fd1b3b87ce76edf4c40805f2cb800bbb5178300ec003975aea1bff084e740a1a8820a8137701692c861188bd9b85cbd31a971caf5535
6
+ metadata.gz: d40bb0cc232b51f25cc6970580c00bcb52c13cf7423f5082f3bbb447b9ea5d5a59acb958a68df4b3a3639bf86ac8ee72a673313d009cdb18be9eec52dc3176a9
7
+ data.tar.gz: e9af3796aa61f7f76b0122e13b2ec6c0958c8d33edeb6c0d34a5813e0f38e7f1cbd46eb267290a3935b381d94815cb7b4479d5adfeee428c709124b7456e3fa4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbbcc (0.11.0.pre)
4
+ rbbcc (0.11.1)
5
5
  fiddle
6
6
 
7
7
  GEM
@@ -82,14 +82,15 @@ end
82
82
 
83
83
  def attach_tc(interface)
84
84
  system(
85
- "sudo `tc filter add dev #{interface} egress" +
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
- system("sudo tc qdisc del dev #{interface} clsact", exception: true)
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
@@ -19,5 +19,8 @@ module RbBCC
19
19
  SK_MSG = 16
20
20
  RAW_TRACEPOINT = 17
21
21
  CGROUP_SOCK_ADDR = 18
22
+ CGROUP_SOCKOPT = 25
23
+ TRACING = 26
24
+ LSM = 29
22
25
  end
23
26
  end
data/lib/rbbcc/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RbBCC
2
- VERSION = "0.11.0.pre"
2
+ VERSION = "0.11.1"
3
3
  end
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.0.pre
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: 3.6.9
147
+ rubygems_version: 4.0.6
146
148
  specification_version: 4
147
149
  summary: BCC port for MRI
148
150
  test_files: []