authorgravatar for mattnite@protonmail.comMatt Knight <mattnite@protonmail.com> 2020-08-18 22:03:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-21 11:40:35-07:00
log7f1378909b54dec1636920389e60d8dfacb4e614
tree509ae62aeecf673ece657c5f69297f971b3d2340
parent243b5c7a889a3cbc86b5b0720b9ed1cdc6c29835

moved bpf syscall, added some bpf instructions and tests


2 files changed, 382 insertions(+), 18 deletions(-)

lib/std/os/bits/linux/bpf.zig+378-18
......@@ -5,6 +5,61 @@
55// and substantial portions of the software.
66usingnamespace std.os;
77const std = @import("../../../std.zig");
8const expectEqual = std.testing.expectEqual;
9const fd_t = std.os.fd_t;
10const pid_t = std.os.pid_t;
11
12// instruction classes
13pub const LD = 0x00;
14pub const LDX = 0x01;
15pub const ST = 0x02;
16pub const STX = 0x03;
17pub const ALU = 0x04;
18pub const JMP = 0x05;
19pub const RET = 0x06;
20pub const MISC = 0x07;
21
22/// 32-bit
23pub const W = 0x00;
24/// 16-bit
25pub const H = 0x08;
26/// 8-bit
27pub const B = 0x10;
28/// 64-bit
29pub const DW = 0x18;
30
31pub const IMM = 0x00;
32pub const ABS = 0x20;
33pub const IND = 0x40;
34pub const MEM = 0x60;
35pub const LEN = 0x80;
36pub const MSH = 0xa0;
37
38// alu fields
39pub const ADD = 0x00;
40pub const SUB = 0x10;
41pub const MUL = 0x20;
42pub const DIV = 0x30;
43pub const OR = 0x40;
44pub const AND = 0x50;
45pub const LSH = 0x60;
46pub const RSH = 0x70;
47pub const NEG = 0x80;
48pub const MOD = 0x90;
49pub const XOR = 0xa0;
50
51// jmp fields
52pub const JA = 0x00;
53pub const JEQ = 0x10;
54pub const JGT = 0x20;
55pub const JGE = 0x30;
56pub const JSET = 0x40;
57
58//#define BPF_SRC(code) ((code) & 0x08)
59pub const K = 0x00;
60pub const X = 0x08;
61
62pub const MAXINSNS = 4096;
863
964// instruction classes
1065/// jmp mode in word width
......@@ -13,8 +68,6 @@ pub const JMP32 = 0x06;
1368pub const ALU64 = 0x07;
1469
1570// ld/ldx fields
16/// double word (64-bit)
17pub const DW = 0x18;
1871/// exclusive add
1972pub const XADD = 0xc0;
2073
......@@ -153,6 +206,130 @@ pub const BPF_F_CLONE = 0x200;
153206/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map
154207pub const BPF_F_MMAPABLE = 0x400;
155208
209/// These values correspond to "syscalls" within the BPF program's environment
210pub const Helper = enum(i32) {
211 unspec,
212 map_lookup_elem,
213 map_update_elem,
214 map_delete_elem,
215 probe_read,
216 ktime_get_ns,
217 trace_printk,
218 get_prandom_u32,
219 get_smp_processor_id,
220 skb_store_bytes,
221 l3_csum_replace,
222 l4_csum_replace,
223 tail_call,
224 clone_redirect,
225 get_current_pid_tgid,
226 get_current_uid_gid,
227 get_current_comm,
228 get_cgroup_classid,
229 skb_vlan_push,
230 skb_vlan_pop,
231 skb_get_tunnel_key,
232 skb_set_tunnel_key,
233 perf_event_read,
234 redirect,
235 get_route_realm,
236 perf_event_output,
237 skb_load_bytes,
238 get_stackid,
239 csum_diff,
240 skb_get_tunnel_opt,
241 skb_set_tunnel_opt,
242 skb_change_proto,
243 skb_change_type,
244 skb_under_cgroup,
245 get_hash_recalc,
246 get_current_task,
247 probe_write_user,
248 current_task_under_cgroup,
249 skb_change_tail,
250 skb_pull_data,
251 csum_update,
252 set_hash_invalid,
253 get_numa_node_id,
254 skb_change_head,
255 xdp_adjust_head,
256 probe_read_str,
257 get_socket_cookie,
258 get_socket_uid,
259 set_hash,
260 setsockopt,
261 skb_adjust_room,
262 redirect_map,
263 sk_redirect_map,
264 sock_map_update,
265 xdp_adjust_meta,
266 perf_event_read_value,
267 perf_prog_read_value,
268 getsockopt,
269 override_return,
270 sock_ops_cb_flags_set,
271 msg_redirect_map,
272 msg_apply_bytes,
273 msg_cork_bytes,
274 msg_pull_data,
275 bind,
276 xdp_adjust_tail,
277 skb_get_xfrm_state,
278 get_stack,
279 skb_load_bytes_relative,
280 fib_lookup,
281 sock_hash_update,
282 msg_redirect_hash,
283 sk_redirect_hash,
284 lwt_push_encap,
285 lwt_seg6_store_bytes,
286 lwt_seg6_adjust_srh,
287 lwt_seg6_action,
288 rc_repeat,
289 rc_keydown,
290 skb_cgroup_id,
291 get_current_cgroup_id,
292 get_local_storage,
293 sk_select_reuseport,
294 skb_ancestor_cgroup_id,
295 sk_lookup_tcp,
296 sk_lookup_udp,
297 sk_release,
298 map_push_elem,
299 map_pop_elem,
300 map_peek_elem,
301 msg_push_data,
302 msg_pop_data,
303 rc_pointer_rel,
304 spin_lock,
305 spin_unlock,
306 sk_fullsock,
307 tcp_sock,
308 skb_ecn_set_ce,
309 get_listener_sock,
310 skc_lookup_tcp,
311 tcp_check_syncookie,
312 sysctl_get_name,
313 sysctl_get_current_value,
314 sysctl_get_new_value,
315 sysctl_set_new_value,
316 strtol,
317 strtoul,
318 sk_storage_get,
319 sk_storage_delete,
320 send_signal,
321 tcp_gen_syncookie,
322 skb_output,
323 probe_read_user,
324 probe_read_kernel,
325 probe_read_user_str,
326 probe_read_kernel_str,
327 tcp_send_ack,
328 send_signal_thread,
329 jiffies64,
330 _,
331};
332
156333/// a single BPF instruction
157334pub const Insn = packed struct {
158335 code: u8,
......@@ -163,32 +340,163 @@ pub const Insn = packed struct {
163340
164341 /// r0 - r9 are general purpose 64-bit registers, r10 points to the stack
165342 /// frame
166 pub const Reg = enum(u4) {
167 r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10
343 pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 };
344 const Source = packed enum(u1) { reg, imm };
345 const AluOp = packed enum(u8) {
346 add = ADD,
347 sub = SUB,
348 mul = MUL,
349 div = DIV,
350 op_or = OR,
351 op_and = AND,
352 lsh = LSH,
353 rsh = RSH,
354 neg = NEG,
355 mod = MOD,
356 xor = XOR,
357 mov = MOV,
358 };
359
360 pub const Size = packed enum(u8) {
361 byte = B,
362 half_word = H,
363 word = W,
364 double_word = DW,
168365 };
169366
170 const alu = 0x04;
171 const jmp = 0x05;
172 const mov = 0xb0;
173 const k = 0;
174 const exit_code = 0x90;
367 const JmpOp = packed enum(u8) {
368 ja = JA,
369 jeq = JEQ,
370 jgt = JGT,
371 jge = JGE,
372 jset = JSET,
373 };
374
375 const ImmOrReg = union(Source) {
376 imm: i32,
377 reg: Reg,
378 };
379
380 fn imm_reg(code: u8, dst: Reg, src: anytype, off: i16) Insn {
381 const imm_or_reg = if (@typeInfo(@TypeOf(src)) == .EnumLiteral)
382 ImmOrReg{ .reg = @as(Reg, src) }
383 else
384 ImmOrReg{ .imm = src };
385
386 const src_type = switch (imm_or_reg) {
387 .imm => K,
388 .reg => X,
389 };
390
391 return Insn{
392 .code = code | src_type,
393 .dst = @enumToInt(dst),
394 .src = switch (imm_or_reg) {
395 .imm => 0,
396 .reg => |r| @enumToInt(r),
397 },
398 .off = off,
399 .imm = switch (imm_or_reg) {
400 .imm => |i| i,
401 .reg => 0,
402 },
403 };
404 }
405
406 fn alu(comptime width: comptime_int, op: AluOp, dst: Reg, src: anytype) Insn {
407 const width_bitfield = switch (width) {
408 32 => ALU,
409 64 => ALU64,
410 else => @compileError("width must be 32 or 64"),
411 };
412
413 return imm_reg(width_bitfield | @enumToInt(op), dst, src, 0);
414 }
415
416 pub fn mov(dst: Reg, src: anytype) Insn {
417 return alu(64, .mov, dst, src);
418 }
419
420 pub fn add(dst: Reg, src: anytype) Insn {
421 return alu(64, .add, dst, src);
422 }
423
424 fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn {
425 return imm_reg(JMP | @enumToInt(op), dst, src, off);
426 }
427
428 pub fn jeq(dst: Reg, src: anytype, off: i16) Insn {
429 return jmp(.jeq, dst, src, off);
430 }
175431
176 // TODO: implement more factory functions for the other instructions
177 /// load immediate value into a register
178 pub fn load_imm(dst: Reg, imm: i32) Insn {
432 pub fn stx_mem(size: Size, dst: Reg, src: Reg, off: i16) Insn {
179433 return Insn{
180 .code = alu | mov | k,
434 .code = STX | @enumToInt(size) | MEM,
181435 .dst = @enumToInt(dst),
436 .src = @enumToInt(src),
437 .off = off,
438 .imm = 0,
439 };
440 }
441
442 pub fn xadd(dst: Reg, src: Reg) Insn {
443 return Insn{
444 .code = STX | XADD | DW,
445 .dst = @enumToInt(dst),
446 .src = @enumToInt(src),
447 .off = 0,
448 .imm = 0,
449 };
450 }
451
452 /// direct packet access, R0 = *(uint *)(skb->data + imm32)
453 pub fn ld_abs(size: Size, imm: i32) Insn {
454 return Insn{
455 .code = LD | @enumToInt(size) | ABS,
456 .dst = 0,
182457 .src = 0,
183458 .off = 0,
184459 .imm = imm,
185460 };
186461 }
187462
463 fn ld_imm_impl(dst: Reg, src: Reg, imm: u64) [2]Insn {
464 return [2]Insn{
465 Insn{
466 .code = LD | DW | IMM,
467 .dst = @enumToInt(dst),
468 .src = @enumToInt(src),
469 .off = 0,
470 .imm = @intCast(i32, @truncate(u32, imm)),
471 },
472 Insn{
473 .code = 0,
474 .dst = 0,
475 .src = 0,
476 .off = 0,
477 .imm = @intCast(i32, @truncate(u32, imm >> 32)),
478 },
479 };
480 }
481
482 pub fn ld_map_fd(dst: Reg, map_fd: fd_t) [2]Insn {
483 return ld_imm_impl(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd));
484 }
485
486 pub fn call(helper: Helper) Insn {
487 return Insn{
488 .code = JMP | CALL,
489 .dst = 0,
490 .src = 0,
491 .off = 0,
492 .imm = @enumToInt(helper),
493 };
494 }
495
188496 /// exit BPF program
189497 pub fn exit() Insn {
190498 return Insn{
191 .code = jmp | exit_code,
499 .code = JMP | EXIT,
192500 .dst = 0,
193501 .src = 0,
194502 .off = 0,
......@@ -197,6 +505,62 @@ pub const Insn = packed struct {
197505 }
198506};
199507
508fn expect_insn(insn: Insn, val: u64) void {
509 expectEqual(@bitCast(u64, insn), val);
510}
511
512test "insn bitsize" {
513 expectEqual(@bitSizeOf(Insn), 64);
514}
515
516// mov instructions
517test "mov imm" {
518 expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7);
519}
520
521test "mov reg" {
522 expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf);
523}
524
525// alu instructions
526test "add imm" {
527 expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207);
528}
529
530// ld instructions
531test "ld_abs" {
532 expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030);
533}
534
535test "ld_map_fd" {
536 const insns = Insn.ld_map_fd(.r1, 42);
537 expect_insn(insns[0], 0x0000002a00001118);
538 expect_insn(insns[1], 0x0000000000000000);
539}
540
541// st instructions
542test "stx_mem" {
543 expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63);
544}
545
546test "xadd" {
547 expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db);
548}
549
550// jmp instructions
551test "jeq imm" {
552 expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015);
553}
554
555// other instructions
556test "call" {
557 expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085);
558}
559
560test "exit" {
561 expect_insn(Insn.exit(), 0x0000000000000095);
562}
563
200564pub const Cmd = extern enum(usize) {
201565 map_create,
202566 map_lookup_elem,
......@@ -605,7 +969,3 @@ pub const Attr = extern union {
605969 enable_stats: EnableStatsAttr,
606970 iter_create: IterCreateAttr,
607971};
608
609pub fn bpf(cmd: Cmd, attr: *Attr, size: u32) usize {
610 return syscall3(.bpf, @enumToInt(cmd), @ptrToInt(attr), size);
611}
lib/std/os/linux.zig+4
......@@ -1221,6 +1221,10 @@ pub fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64,
12211221 );
12221222}
12231223
1224pub fn bpf_syscall(cmd: bpf.Cmd, attr: *bpf.Attr, size: u32) usize {
1225 return syscall3(.bpf, @enumToInt(cmd), @ptrToInt(attr), size);
1226}
1227
12241228test "" {
12251229 if (builtin.os.tag == .linux) {
12261230 _ = @import("linux/test.zig");