class Metasm::EBPF

www.kernel.org/doc/Documentation/networking/filter.txt

Public Class Methods

new(family = :latest, endianness = :big) click to toggle source
Calls superclass method
# File metasm/cpu/ebpf/main.rb, line 45
def initialize(family = :latest, endianness = :big)
        super()
        @endianness = endianness
        @size = 64
        @family = family
end

Public Instance Methods

addop(name, bin, *args) click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 12
def addop(name, bin, *args)
        o = Opcode.new name, bin
        args.each { |a|
                o.args << a if @valid_args[a]
                o.props.update a if a.kind_of?(::Hash)
        }
        @opcode_list << o
end
addop_alu(name, bin) click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 21
def addop_alu(name, bin)
        addop name, bin | 0x07, :rd, :i
        addop name, bin | 0x0F, :rd, :rs
        addop name+'32', bin | 0x04, :rd, :i
        addop name+'32', bin | 0x0C, :rd, :rs
end
addop_j(name, bin) click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 44
def addop_j(name, bin)
        addop name, bin | 0x00, :rd, :i, :off, :setip => true
        addop name, bin | 0x08, :rd, :rs, :off, :setip => true
end
addop_sz(name, bin, dst, src) click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 39
def addop_sz(name, bin, dst, src)
        addop_sz32(name, bin, dst, src)
        addop name + 'dw', bin | 0x18, dst, src, :msz => 8
end
addop_sz32(name, bin, dst, src) click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 28
def addop_sz32(name, bin, dst, src)
        addop name + 'w',  bin | 0x00, dst, src, :msz => 4
        addop name + 'h',  bin | 0x08, dst, src, :msz => 2
        addop name + 'b',  bin | 0x10, dst, src, :msz => 1
end
addop_sz64(name, bin, dst, src) click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 34
def addop_sz64(name, bin, dst, src)
        addop name + 'w',  bin | 0x00, dst, src, :msz => 4
        addop name + 'dw', bin | 0x18, dst, src, :msz => 8
end
build_bin_lookaside() click to toggle source
# File metasm/cpu/ebpf/decode.rb, line 12
def build_bin_lookaside
        opcode_list.inject({}) { |h, op| h.update op.bin => op }
end
dbg_disable_bp(dbg, bp) click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 58
def dbg_disable_bp(dbg, bp)
end
dbg_enable_bp(dbg, bp) click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 55
def dbg_enable_bp(dbg, bp)
end
dbg_flag_list() click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 22
def dbg_flag_list
        @dbg_flag_list ||= []
end
dbg_need_stepover(dbg, addr, di) click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 30
def dbg_need_stepover(dbg, addr, di)
        false
end
dbg_register_list() click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 18
def dbg_register_list
        @dbg_register_list ||= [:r0, :r1, :r2, :r3, :r4, :r5, :r6, :r7, :r8, :r9, :r10, :pc]
end
dbg_register_pc() click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 11
def dbg_register_pc
        @dbg_register_pc ||= :pc
end
dbg_register_size() click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 26
def dbg_register_size
        @dbg_register_size ||= Hash.new(64)
end
dbg_register_sp() click to toggle source
# File metasm/cpu/ebpf/debug.rb, line 14
def dbg_register_sp
        @dbg_register_sp ||= :r10
end
dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx) click to toggle source
Calls superclass method
# File metasm/cpu/ebpf/debug.rb, line 34
def dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx)
        a = di.instruction.args.map { |aa| symbolic(aa) }

        cond = case di.opcode.name
        when 'jeq'; dbg_ctx.resolve(a[0]) == dbg_ctx.resolve(a[1])
        when 'jgt'; dbg_ctx.resolve(a[0]) >  dbg_ctx.resolve(a[1])
        when 'jge'; dbg_ctx.resolve(a[0]) >= dbg_ctx.resolve(a[1])
        when 'jset'; dbg_ctx.resolve(a[0]) & dbg_ctx.resolve(a[1]) > 0
        when 'jne'; dbg_ctx.resolve(a[0]) != dbg_ctx.resolve(a[1])
        when 'jsgt'; Expression.make_signed(dbg_ctx.resolve(a[0]), 64) >  Expression.make_signed(dbg_ctx.resolve(a[1]), 64)
        when 'jsge'; Expression.make_signed(dbg_ctx.resolve(a[0]), 64) >= Expression.make_signed(dbg_ctx.resolve(a[1]), 64)
        else return super(di, fbd, pc_reg, dbg_ctx)
        end

        if cond
                fbd[pc_reg] = a.last
        else
                fbd[pc_reg] = di.next_addr
        end
end
decode_findopcode(edata) click to toggle source

tries to find the opcode encoded at edata.ptr

# File metasm/cpu/ebpf/decode.rb, line 17
def decode_findopcode(edata)
        return if edata.ptr > edata.data.length-8
        di = DecodedInstruction.new self
        code_off = (@endianness == :little ? 0 : 7)
        code = edata.data[edata.ptr+code_off, 1].unpack('C')[0]
        return di if di.opcode = @bin_lookaside[code]
end
decode_instr_interpret(di, addr) click to toggle source
# File metasm/cpu/ebpf/decode.rb, line 61
def decode_instr_interpret(di, addr)
        if di.opcode.props[:setip]
                delta = di.instruction.args[-1].reduce + 1
                arg = Expression[addr, :+, 8*delta].reduce
                di.instruction.args[-1] = Expression[arg]
        end

        di
end
decode_instr_op(edata, di) click to toggle source
# File metasm/cpu/ebpf/decode.rb, line 25
def decode_instr_op(edata, di)
        op = di.opcode
        di.instruction.opname = op.name
        di.bin_length = 8
        blob = edata.decode_imm(:u64, @endianness)
        imm = (blob >> 32) & 0xffff_ffff
        imm = Expression.make_signed(imm, 32)
        off = (blob >> 16) & 0xffff
        off = Expression.make_signed(off, 16)
        src = (blob >> 12) & 0xf
        dst = (blob >>  8) & 0xf
        #code = blob & 0xff

        if di.opcode.props[:imm64]
                imm = (imm & 0xffff_ffff) | (edata.decode_imm(:u64, @endianness) & 0xffff_ffff_0000_0000)    # next_imm << 32
                di.bin_length += 8
        end

        op.args.each { |a|
                di.instruction.args << case a
                when :i;    Expression[imm]
                when :r0;   Reg.new(0)
                when :rs;   Reg.new(src)
                when :rd;   Reg.new(dst)
                when :off;  Expression[off]
                when :p_rs_o; Memref.new(Reg.new(src), Expression[off], op.props[:msz])
                when :p_rd_o; Memref.new(Reg.new(dst), Expression[off], op.props[:msz])
                when :p_pkt_i; Pktref.new(nil, Expression[imm], op.props[:msz])
                when :p_pkt_rs_i; Pktref.new(Reg.new(src), Expression[imm], op.props[:msz])
                else raise "unhandled arg #{a}"
                end
        }

        di
end
init_backtrace_binding() click to toggle source

populate the @backtrace_binding hash with default values

# File metasm/cpu/ebpf/decode.rb, line 72
def init_backtrace_binding
        @backtrace_binding ||= {}

        bswap = lambda { |val, nbytes|
                case nbytes
                when 1; val
                when 2; Expression[[[val, :&, 0xff], :<<, 8], :|, [[val, :&, 0xff00], :>>, 8]]
                when 4; Expression[[bswap[Expression[val, :&, 0xffff], 2], :<<, 16], :|, bswap[Expression[[val, :>>, 16], :&, 0xffff], 2]]
                when 8; Expression[[bswap[Expression[val, :&, 0xffffffff], 4], :<<, 32], :|, bswap[Expression[[val, :>>, 32], :&, 0xffffffff], 4]]
                end
        }

        opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op|
                binding = case op

                when 'add'; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1] } }
                when 'sub'; lambda { |di, a0, a1| { a0 => Expression[a0, :-, a1] } }
                when 'mul'; lambda { |di, a0, a1| { a0 => Expression[[a0, :*, a1], :&, 0xffff_ffff_ffff_ffff] } }
                when 'div'; lambda { |di, a0, a1| { a0 => Expression[a0, :/, a1] } }
                when 'or';  lambda { |di, a0, a1| { a0 => Expression[a0, :|, a1] } }
                when 'and'; lambda { |di, a0, a1| { a0 => Expression[a0, :&, a1] } }
                when 'shl'; lambda { |di, a0, a1| { a0 => Expression[[a0, :<<, a1], :&, 0xffff_ffff_ffff_ffff] } }
                when 'shr'; lambda { |di, a0, a1| { a0 => Expression[a0, :>>, a1] } }        # XXX sign
                when 'neg'; lambda { |di, a0|     { a0 => Expression[:-, a0] } }
                when 'mod'; lambda { |di, a0, a1| { a0 => Expression[a0, :%, a1] } }
                when 'xor'; lambda { |di, a0, a1| { a0 => Expression[a0, :^, a1] } }
                when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
                when 'sar'; lambda { |di, a0, a1| { a0 => Expression[a0, :>>, a1] } }

                when 'add32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :+, a1], :&, 0xffff_ffff] } }
                when 'sub32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :-, a1], :&, 0xffff_ffff] } }
                when 'mul32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :*, a1], :&, 0xffff_ffff] } }
                when 'div32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :/, a1], :&, 0xffff_ffff] } }
                when 'or32';  lambda { |di, a0, a1| { a0 => Expression[[a0, :|, a1], :&, 0xffff_ffff] } }
                when 'and32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :&, a1], :&, 0xffff_ffff] } }
                when 'shl32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :<<, a1], :&, 0xffff_ffff] } }
                when 'shr32'; lambda { |di, a0, a1| { a0 => Expression[[[a0, :&, 0xffff_ffff], :>>, a1], :&, 0xffff_ffff] } }        # XXX sign
                when 'neg32'; lambda { |di, a0|     { a0 => Expression[:-, [a0, :&, 0xffff_ffff]] } }
                when 'mod32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :%, a1], :&, 0xffff_ffff] } }
                when 'xor32'; lambda { |di, a0, a1| { a0 => Expression[[a0, :^, a1], :&, 0xffff_ffff] } }
                when 'mov32'; lambda { |di, a0, a1| { a0 => Expression[a1, :&, 0xffff_ffff] } }
                when 'sar32'; lambda { |di, a0, a1| { a0 => Expression[[[a0, :&, 0xffff_ffff], :>>, a1], :&, 0xffff_ffff] } }

                when 'be', 'le'; lambda { |di, a0, a1|
                        if @endianness.to_s[0] == di.opcode.name[0]
                                {}
                        else
                                { a1 => bswap[a1, Expression[a0].reduce] }
                        end
                }
                when /^ldind|^ldabs|^stind|^stabs/; lambda { |di, a0, a1|
                        if @endianness == :big
                                { a0 => Expression[a1] }
                        else
                                { a0 => bswap[a1, di.opcode.props[:msz]] }
                        end
                }
                when /^ld|^st/; lambda { |di, a0, a1| { a0 => Expression[a1] } }
                when /^xadd/; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1] } }       # XXX bswap ?

                when 'call'; lambda { |di, *a| { :r0 => Expression::Unknown } }

                when 'jmp', 'jeq', 'jgt', 'jge', 'jset', 'jne', 'jsgt', 'jsge'; lambda { |di, *a| { } }
                end
                @backtrace_binding[op] ||= binding if binding
        }

        @backtrace_binding
end
init_ebpf() click to toggle source
# File metasm/cpu/ebpf/opcodes.rb, line 49
def init_ebpf
        @opcode_list = []
        [:i, :rs, :rd, :off, :p_rs_o, :p_rd_o, :r0, :p_pkt_i, :p_pkt_rs_i].each { |a| @valid_args[a] = true }

        # ALU
        addop_alu 'add', 0x00
        addop_alu 'sub', 0x10
        addop_alu 'mul', 0x20
        addop_alu 'div', 0x30
        addop_alu 'or',  0x40
        addop_alu 'and', 0x50
        addop_alu 'shl', 0x60
        addop_alu 'shr', 0x70
        addop 'neg',     0x87, :rd
        addop 'neg32',   0x84, :rd
        addop_alu 'mod', 0x90
        addop_alu 'xor', 0xa0
        addop_alu 'mov', 0xb0
        addop_alu 'sar', 0xc0

        addop 'le', 0xd4, :i, :rd     # native to little endian (short if i==16, word if i==32, quad if imm==64)
        addop 'be', 0xdC, :i, :rd     # native to big endian

        # LD/ST
        addop 'lddw', 0x18, :rd, :i, :imm64 => true   # next insns serves only to store high 32bits of imm64
        addop_sz32 'ldabs', 0x20, :r0, :p_pkt_i
        addop_sz32 'ldind', 0x40, :r0, :p_pkt_rs_i
        addop_sz   'ldx', 0x61, :rd, :p_rs_o
        addop_sz   'st', 0x62, :p_rd_o, :i
        addop_sz   'stx', 0x63, :p_rd_o, :rs
        addop_sz64 'xadd', 0xC3, :p_rd_o, :rs

        # BRANCH
        addop 'jmp', 0x05, :off, :setip => true, :stopexec => true    # 'ja'
        addop_j 'jeq',  0x15
        addop_j 'jgt',  0x25
        addop_j 'jge',  0x35
        addop_j 'jset', 0x45
        addop_j 'jne',  0x55
        addop_j 'jsgt', 0x65
        addop_j 'jsge', 0x75
        addop 'call', 0x85, :i        # native call, doesn't interfere with bpf code flow
        addop 'tailcall', 0x8D, :i, :stopexec => true # tail call: r2 is a map of bpf programs, r3 is an index, pass control to r2[r3] (no return)
        addop 'exit', 0x95, :stopexec => true
end
Also aliased as: init_latest
init_latest()
Alias for: init_ebpf
init_opcode_list() click to toggle source
# File metasm/cpu/ebpf/main.rb, line 52
def init_opcode_list
        send("init_#@family")
        @opcode_list
end