rbbcc 0.11.0.pre → 0.11.0

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: 0cafb82823139848b9c16a2182ef4ba3c86c391126700ee6cbb31ab0a427da85
4
+ data.tar.gz: 711be4b7c0bf09879445d02b7bf0379b9c51dc186f56129227c416a0515e00ff
5
5
  SHA512:
6
- metadata.gz: ce916936932a3a0bb00bf28b3bd67d55b2d2068e0d4d05308296d472306db0ffae3dd495a441189fdcfe0db0c29705a389f31781f0e3971f1ef349b1d0eb4a1f
7
- data.tar.gz: 15da5d9cd65ec31dd5a3fd1b3b87ce76edf4c40805f2cb800bbb5178300ec003975aea1bff084e740a1a8820a8137701692c861188bd9b85cbd31a971caf5535
6
+ metadata.gz: 007c4273121411d6548b071fe48fda59c36af94a36c7de183a8fbacceba722c837115af8a2892ec79277957217b5152b58ec12ce4dd56c153293a2ac6fae9894
7
+ data.tar.gz: 94da55a1738a534d0bffc95ac2ebabcb431e5ea98785506a0006e1d249b83d602f8bbcaec47890124afbf42a9a11f394f986b9795b194db0acb101a8d536ca26
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.0)
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
@@ -643,6 +643,9 @@ module RbBCC
643
643
  tp: tp,
644
644
  fn_name: fn[:name]
645
645
  )
646
+ elsif func_name.start_with?("lsm__")
647
+ # LSM_PROBE programs are attached by libbcc while loading.
648
+ load_func(func_name, BPF::LSM)
646
649
  end
647
650
  end
648
651
  end
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.0"
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.0
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: []