rbbcc 0.4.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,114 @@
1
+ #include <uapi/linux/ptrace.h>
2
+ #include <net/sock.h>
3
+ #include <bcc/proto.h>
4
+
5
+ #define IP_TCP 6
6
+ #define ETH_HLEN 14
7
+
8
+ /*eBPF program.
9
+ Filter IP and TCP packets, having payload not empty
10
+ and containing "HTTP", "GET", "POST" ... as first bytes of payload
11
+ if the program is loaded as PROG_TYPE_SOCKET_FILTER
12
+ and attached to a socket
13
+ return 0 -> DROP the packet
14
+ return -1 -> KEEP the packet and return it to user space (userspace can read it from the socket_fd )
15
+ */
16
+ int http_filter(struct __sk_buff *skb) {
17
+
18
+ u8 *cursor = 0;
19
+
20
+ struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
21
+ //filter IP packets (ethernet type = 0x0800)
22
+ if (!(ethernet->type == 0x0800)) {
23
+ goto DROP;
24
+ }
25
+
26
+ struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
27
+ //filter TCP packets (ip next protocol = 0x06)
28
+ if (ip->nextp != IP_TCP) {
29
+ goto DROP;
30
+ }
31
+
32
+ u32 tcp_header_length = 0;
33
+ u32 ip_header_length = 0;
34
+ u32 payload_offset = 0;
35
+ u32 payload_length = 0;
36
+
37
+ //calculate ip header length
38
+ //value to multiply * 4
39
+ //e.g. ip->hlen = 5 ; IP Header Length = 5 x 4 byte = 20 byte
40
+ ip_header_length = ip->hlen << 2; //SHL 2 -> *4 multiply
41
+
42
+ //check ip header length against minimum
43
+ if (ip_header_length < sizeof(*ip)) {
44
+ goto DROP;
45
+ }
46
+
47
+ //shift cursor forward for dynamic ip header size
48
+ void *_ = cursor_advance(cursor, (ip_header_length-sizeof(*ip)));
49
+
50
+ struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp));
51
+
52
+ //calculate tcp header length
53
+ //value to multiply *4
54
+ //e.g. tcp->offset = 5 ; TCP Header Length = 5 x 4 byte = 20 byte
55
+ tcp_header_length = tcp->offset << 2; //SHL 2 -> *4 multiply
56
+
57
+ //calculate payload offset and length
58
+ payload_offset = ETH_HLEN + ip_header_length + tcp_header_length;
59
+ payload_length = ip->tlen - ip_header_length - tcp_header_length;
60
+
61
+ //http://stackoverflow.com/questions/25047905/http-request-minimum-size-in-bytes
62
+ //minimum length of http request is always geater than 7 bytes
63
+ //avoid invalid access memory
64
+ //include empty payload
65
+ if(payload_length < 7) {
66
+ goto DROP;
67
+ }
68
+
69
+ //load first 7 byte of payload into p (payload_array)
70
+ //direct access to skb not allowed
71
+ unsigned long p[7];
72
+ int i = 0;
73
+ for (i = 0; i < 7; i++) {
74
+ p[i] = load_byte(skb , payload_offset + i);
75
+ }
76
+
77
+ //find a match with an HTTP message
78
+ //HTTP
79
+ if ((p[0] == 'H') && (p[1] == 'T') && (p[2] == 'T') && (p[3] == 'P')) {
80
+ goto KEEP;
81
+ }
82
+ //GET
83
+ if ((p[0] == 'G') && (p[1] == 'E') && (p[2] == 'T')) {
84
+ goto KEEP;
85
+ }
86
+ //POST
87
+ if ((p[0] == 'P') && (p[1] == 'O') && (p[2] == 'S') && (p[3] == 'T')) {
88
+ goto KEEP;
89
+ }
90
+ //PUT
91
+ if ((p[0] == 'P') && (p[1] == 'U') && (p[2] == 'T')) {
92
+ goto KEEP;
93
+ }
94
+ //DELETE
95
+ if ((p[0] == 'D') && (p[1] == 'E') && (p[2] == 'L') && (p[3] == 'E') && (p[4] == 'T') && (p[5] == 'E')) {
96
+ goto KEEP;
97
+ }
98
+ //HEAD
99
+ if ((p[0] == 'H') && (p[1] == 'E') && (p[2] == 'A') && (p[3] == 'D')) {
100
+ goto KEEP;
101
+ }
102
+
103
+ //no HTTP match
104
+ goto DROP;
105
+
106
+ //keep the packet and send it to userspace retruning -1
107
+ KEEP:
108
+ return -1;
109
+
110
+ //drop the packet returning 0
111
+ DROP:
112
+ return 0;
113
+
114
+ }
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ #Original http-parse-simple.py in invisor/bcc
4
+ #Bertrone Matteo - Polytechnic of Turin
5
+ #November 2015
6
+ #Ruby version by Uchio Kondo, License follows
7
+ #
8
+ #eBPF application that parses HTTP packets
9
+ #and extracts (and prints on screen) the URL contained in the GET/POST request.
10
+ #
11
+ #eBPF program http_filter is used as SOCKET_FILTER attached to eth0 interface.
12
+ #only packet of type ip and tcp containing HTTP GET/POST are returned to userspace, others dropped
13
+ #
14
+ #python script uses bcc BPF Compiler Collection by iovisor (https://github.com/iovisor/bcc)
15
+ #and prints on stdout the first line of the HTTP GET/POST request containing the url
16
+
17
+ require 'rbbcc'
18
+ require 'socket'
19
+ require 'io/nonblock'
20
+ include RbBCC
21
+
22
+ def usage
23
+ puts <<-USAGE
24
+ USAGE: #{$0} [-i <if_name>]
25
+ USAGE
26
+ exit
27
+ end
28
+
29
+ interface = "eth0"
30
+
31
+ if ARGV.size == 2
32
+ if ARGV[0] == '-i'
33
+ interface = ARGV[1]
34
+ else
35
+ usage
36
+ end
37
+ elsif ARGV.size != 0
38
+ usage
39
+ end
40
+
41
+ puts("binding socket to '%s'" % interface)
42
+
43
+ bpf = BCC.new(src_file: "http-parse-simple.c")
44
+
45
+ function_http_filter = bpf.load_func("http_filter", BPF::SOCKET_FILTER)
46
+
47
+ BCC.attach_raw_socket(function_http_filter, interface)
48
+
49
+ socket_fd = function_http_filter[:sock]
50
+
51
+ sock = Socket.for_fd socket_fd
52
+ sock.nonblock = false
53
+
54
+ ETH_HLEN = 14
55
+ loop do
56
+ packet_str = sock.sysread(2048)
57
+ packet_bytearray = packet_str.bytes
58
+
59
+ # See original comment...
60
+ #calculate packet total length
61
+ total_length = packet_bytearray[ETH_HLEN + 2] #load MSB
62
+ total_length = total_length << 8 #shift MSB
63
+ total_length = total_length + packet_bytearray[ETH_HLEN + 3] #add LSB
64
+
65
+ #calculate ip header length
66
+ ip_header_length = packet_bytearray[ETH_HLEN] #load Byte
67
+ ip_header_length = ip_header_length & 0x0F #mask bits 0..3
68
+ ip_header_length = ip_header_length << 2 #shift to obtain length
69
+
70
+ tcp_header_length = packet_bytearray[ETH_HLEN + ip_header_length + 12] #load Byte
71
+ tcp_header_length = tcp_header_length & 0xF0 #mask bit 4..7
72
+ tcp_header_length = tcp_header_length >> 2 #SHR 4 ; SHL 2 -> SHR 2
73
+
74
+ payload_offset = ETH_HLEN + ip_header_length + tcp_header_length
75
+
76
+ ((payload_offset-1)..(packet_bytearray.size-1)).each do |i|
77
+ if packet_bytearray[i] == 0x0A
78
+ if packet_bytearray[i-1] == 0x0D
79
+ break
80
+ end
81
+ end
82
+ print(packet_bytearray[i].chr)
83
+ end
84
+ puts
85
+ end
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env ruby
2
+ # To run this example, please build the target ruby with an `--enable-dtrace` option in advance.
3
+ # To build via rbenv, sample command is:
4
+ # $ RUBY_CONFIGURE_OPTS='--enable-dtrace' rbenv install 2.7.0
5
+ #
6
+ # Example autput:
7
+ # # bundle exec ruby examples/ruby_usdt.rb $(pidof irb)
8
+ # TIME(s) COMM KLASS PATH
9
+ # 0.000000000 irb Struct::Key /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline.rb
10
+ # 0.000055206 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
11
+ # 0.000088588 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
12
+ # 0.000117740 irb Ripper::Lexer::Elem /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
13
+ # 0.000126697 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
14
+ # 0.000213388 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
15
+ # 0.000225678 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
16
+ # 0.000243638 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
17
+ # 0.000254680 irb Range /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb/ruby-lex.rb
18
+ # 0.000264707 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
19
+ # 0.000275579 irb Ripper::Lexer::Elem /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
20
+ # 0.000282438 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
21
+ # 0.000326136 irb String /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb.rb
22
+ # 0.001353621 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
23
+ # 0.001385320 irb IRB::Color::SymbolState /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb/color.rb
24
+ # 0.001397043 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb/color.rb
25
+ # 0.001416420 irb Ripper::Lexer::Elem /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
26
+ # 0.001423861 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
27
+ # 0.001462010 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
28
+ # 0.001478995 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
29
+ # 0.001487499 irb Range /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb/ruby-lex.rb
30
+ # 0.001496666 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
31
+ # 0.001508224 irb Ripper::Lexer::Elem /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
32
+ # 0.001515143 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
33
+ # 0.001556170 irb String /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb.rb
34
+ # 0.001726273 irb String /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
35
+ # 0.001946948 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
36
+ # 0.001956585 irb String /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline.rb
37
+
38
+ require 'rbbcc'
39
+ include RbBCC
40
+
41
+ pid = ARGV[0] || begin
42
+ puts("USAGE: #{$0} PID")
43
+ exit()
44
+ end
45
+ debug = !!ENV['DEBUG']
46
+
47
+ bpf_text = <<BPF
48
+ #include <uapi/linux/ptrace.h>
49
+ #include <linux/sched.h>
50
+
51
+ struct data_t {
52
+ u64 ts;
53
+ char comm[TASK_COMM_LEN];
54
+ char klass[64];
55
+ char path[256];
56
+ };
57
+ BPF_PERF_OUTPUT(events);
58
+
59
+ int do_trace_create_object(struct pt_regs *ctx) {
60
+ struct data_t data = {};
61
+ uint64_t addr, addr2;
62
+
63
+ data.ts = bpf_ktime_get_ns();
64
+
65
+ bpf_get_current_comm(&data.comm, sizeof(data.comm));
66
+ bpf_usdt_readarg_p(1, ctx, &data.klass, sizeof(data.klass));
67
+ bpf_usdt_readarg_p(2, ctx, &data.path, sizeof(data.path));
68
+
69
+ events.perf_submit(ctx, &data, sizeof(data));
70
+
71
+ return 0;
72
+ };
73
+ BPF
74
+
75
+ u = USDT.new(pid: pid.to_i)
76
+ u.enable_probe(probe: "object__create", fn_name: "do_trace_create_object")
77
+ if debug
78
+ puts(u.get_text)
79
+ puts(bpf_text)
80
+ end
81
+
82
+ # initialize BPF
83
+ b = BCC.new(text: bpf_text, usdt_contexts: [u])
84
+
85
+ puts("%-18s %-6s %-24s %s" % ["TIME(s)", "COMM", "KLASS", "PATH"])
86
+
87
+ # process event
88
+ start = 0
89
+ b["events"].open_perf_buffer do |cpu, data, size|
90
+ event = b["events"].event(data)
91
+ if start == 0
92
+ start = event.ts
93
+ end
94
+
95
+ time_s = ((event.ts - start).to_f) / 1000000000
96
+ puts(
97
+ "%-18.9f %-6s %-24s %s" %
98
+ [time_s, event.comm, event.klass, event.path]
99
+ )
100
+ end
101
+
102
+ Signal.trap(:INT) { puts "\nDone."; exit }
103
+ loop do
104
+ b.perf_buffer_poll()
105
+ end
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env ruby
2
+ # Trace example for libc's USDT:
3
+ # - memory_sbrk_more
4
+ # - memory_sbrk_less
5
+ # - memory_mallopt_free_dyn_thresholds
6
+ # Description is here: https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Probes.html
7
+ #
8
+ # Example output:
9
+ # bundle exec ruby examples/sbrk_trace.rb -c ruby
10
+ # !! Trace start.
11
+ # [ 0.000000000] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x55f34b979000 size=135168
12
+ # [ 0.036549804] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9760000 size=135168
13
+ # [ 0.036804183] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9781000 size=143360
14
+ # [ 0.036855378] pid=32756 comm=ruby probe=memory_sbrk_less addr=0x557fd97a0000 size=16384
15
+ # [ 0.036931376] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd97a0000 size=147456
16
+ # [ 0.036940382] pid=32756 comm=ruby probe=memory_sbrk_less addr=0x557fd97c0000 size=16384
17
+ # [ 0.037022971] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd97c0000 size=151552
18
+ # [ 0.038602464] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd97e5000 size=204800
19
+ # [ 0.039398297] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9817000 size=135168
20
+ # [ 0.039909594] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9838000 size=135168
21
+ # [ 0.040536005] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9859000 size=163840
22
+ # ...
23
+
24
+ require 'rbbcc'
25
+ include RbBCC
26
+
27
+ def usage
28
+ puts("USAGE: #{$0} [-p PID|-c COMM]")
29
+ exit()
30
+ end
31
+
32
+ def find_libc_location
33
+ if File.exist?('/lib/x86_64-linux-gnu/libc.so.6')
34
+ '/lib/x86_64-linux-gnu/libc.so.6'
35
+ else
36
+ `find /lib -name 'libc.so*' | grep -v musl | head -1`.chomp
37
+ end
38
+ end
39
+
40
+ usage if ARGV.size != 0 && ARGV.size != 2
41
+
42
+ pid = comm = nil
43
+ path = find_libc_location
44
+ case ARGV[0]
45
+ when '-p', '--pid'
46
+ pid = ARGV[1].to_i
47
+ when '-c', '--comm'
48
+ comm = ARGV[1]
49
+ when nil
50
+ # nop
51
+ else
52
+ usage
53
+ end
54
+
55
+ debug = !!ENV['DEBUG']
56
+
57
+ bpf_text = <<BPF
58
+ #include <uapi/linux/ptrace.h>
59
+ #include <linux/sched.h>
60
+
61
+ struct data_t {
62
+ u32 type;
63
+ u64 ts;
64
+ u32 pid;
65
+ char comm[TASK_COMM_LEN];
66
+ u64 addr;
67
+ u32 sbrk_size;
68
+ u32 adjusted_mmap;
69
+ u32 trim_thresholds;
70
+ };
71
+ BPF_PERF_OUTPUT(events);
72
+
73
+ static inline bool streq(uintptr_t str) {
74
+ char needle[] = "{{NEEDLE}}";
75
+ char haystack[sizeof(needle)];
76
+ bpf_probe_read(&haystack, sizeof(haystack), (void *)str);
77
+ for (int i = 0; i < sizeof(needle) - 1; ++i) {
78
+ if (needle[i] != haystack[i]) {
79
+ return false;
80
+ }
81
+ }
82
+ return true;
83
+ }
84
+
85
+ #define PROBE_TYPE_more 1
86
+ #define PROBE_TYPE_less 2
87
+ #define PROBE_TYPE_free 3
88
+
89
+ {{FUNC_MORE}}
90
+
91
+ {{FUNC_LESS}}
92
+
93
+ int trace_memory_free(struct pt_regs *ctx) {
94
+ struct data_t data = {};
95
+ long buf;
96
+
97
+ data.type = PROBE_TYPE_free;
98
+ data.ts = bpf_ktime_get_ns();
99
+ data.pid = bpf_get_current_pid_tgid();
100
+ bpf_get_current_comm(&data.comm, sizeof(data.comm));
101
+
102
+ {{NEEDLE_START}}
103
+ bpf_usdt_readarg(1, ctx, &buf);
104
+ data.adjusted_mmap = buf;
105
+
106
+ bpf_usdt_readarg(2, ctx, &buf);
107
+ data.trim_thresholds = buf;
108
+
109
+ events.perf_submit(ctx, &data, sizeof(data));
110
+ {{NEEDLE_END}}
111
+
112
+ return 0;
113
+ };
114
+
115
+ BPF
116
+
117
+ trace_fun_sbrk = <<FUNC
118
+ int trace_memory_sbrk_{{TYPE}}(struct pt_regs *ctx) {
119
+ struct data_t data = {};
120
+ long buf;
121
+
122
+ data.type = PROBE_TYPE_{{TYPE}};
123
+ data.ts = bpf_ktime_get_ns();
124
+ data.pid = bpf_get_current_pid_tgid();
125
+ bpf_get_current_comm(&data.comm, sizeof(data.comm));
126
+
127
+ {{NEEDLE_START}}
128
+ bpf_usdt_readarg(1, ctx, &buf);
129
+ data.addr = buf;
130
+
131
+ bpf_usdt_readarg(2, ctx, &buf);
132
+ data.sbrk_size = buf;
133
+
134
+ events.perf_submit(ctx, &data, sizeof(data));
135
+ {{NEEDLE_END}}
136
+
137
+ return 0;
138
+ };
139
+ FUNC
140
+
141
+ PROBE_TYPE_more = 1
142
+ PROBE_TYPE_less = 2
143
+ PROBE_TYPE_free = 3
144
+ PROBE_MAP = {
145
+ PROBE_TYPE_more => 'memory_sbrk_more',
146
+ PROBE_TYPE_less => 'memory_sbrk_less',
147
+ PROBE_TYPE_free => 'memory_mallopt_free_dyn_thresholds'
148
+ }
149
+
150
+ bpf_text.sub!('{{FUNC_MORE}}', trace_fun_sbrk.gsub('{{TYPE}}', 'more'))
151
+ bpf_text.sub!('{{FUNC_LESS}}', trace_fun_sbrk.gsub('{{TYPE}}', 'less'))
152
+
153
+ if comm
154
+ bpf_text.sub!('{{NEEDLE}}', comm)
155
+ bpf_text.gsub!('{{NEEDLE_START}}', "if(streq((uintptr_t)data.comm)) {")
156
+ bpf_text.gsub!('{{NEEDLE_END}}', "}")
157
+ else
158
+ bpf_text.sub!('{{NEEDLE}}', "")
159
+ bpf_text.gsub!('{{NEEDLE_START}}', "")
160
+ bpf_text.gsub!('{{NEEDLE_END}}', "")
161
+ end
162
+
163
+ u = USDT.new(pid: pid, path: path)
164
+ u.enable_probe(probe: "memory_sbrk_more", fn_name: "trace_memory_sbrk_more")
165
+ u.enable_probe(probe: "memory_sbrk_less", fn_name: "trace_memory_sbrk_less")
166
+ if pid
167
+ # FIXME: Only available when PID is specified
168
+ # otherwise got an error:
169
+ # bpf: Failed to load program: Invalid argument
170
+ # last insn is not an exit or jmp
171
+ # It seems libbcc won't generate proper readarg helper
172
+ u.enable_probe(probe: "memory_mallopt_free_dyn_thresholds", fn_name: "trace_memory_free")
173
+ end
174
+
175
+ # initialize BPF
176
+ b = BCC.new(text: bpf_text, usdt_contexts: [u])
177
+
178
+ puts "!! Trace start."
179
+ # process event
180
+ start = 0
181
+ b["events"].open_perf_buffer do |cpu, data, size|
182
+ event = b["events"].event(data)
183
+ if start == 0
184
+ start = event.ts
185
+ end
186
+
187
+ time_s = ((event.ts - start).to_f) / 1000000000
188
+ if [PROBE_TYPE_more, PROBE_TYPE_less].include?(event.type)
189
+ puts(
190
+ "[%18.9f] pid=%d comm=%s probe=%s addr=%#x size=%d" %
191
+ [time_s, event.pid, event.comm, PROBE_MAP[event.type], event.addr, event.sbrk_size]
192
+ )
193
+ else
194
+ puts(
195
+ "[%18.9f] pid=%d comm=%s probe=%s adjusted_mmap=%d trim_thresholds=%d" %
196
+ [time_s, event.pid, event.comm, PROBE_MAP[event.type], event.adjusted_mmap, event.trim_thresholds]
197
+ )
198
+ end
199
+ end
200
+
201
+ Signal.trap(:INT) { puts "\n!! Done."; exit }
202
+ loop do
203
+ b.perf_buffer_poll()
204
+ end