Created
June 27, 2017 06:48
-
-
Save eloycoto/51f0d2beb8f93236904448f48b920d53 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef RAW_INSN_H | |
#define RAW_INSN_H | |
#include <linux/bpf.h> | |
#define BPF_CALL_FUNC(FUNC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_CALL | BPF_K, \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = FUNC }) | |
#define BPF_ALU64_REG(OP, DST, SRC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = 0 }) | |
#define BPF_ALU32_REG(OP, DST, SRC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_OP(OP) | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = 0 }) | |
#define BPF_ALU64_IMM(OP, DST, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_ALU32_IMM(OP, DST, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_OP(OP) | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_ENDIAN(TYPE, DST, LEN) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_END | BPF_SRC(TYPE), \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = LEN }) | |
#define BPF_MOV64_REG(DST, SRC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_MOV | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = 0 }) | |
#define BPF_MOV32_REG(DST, SRC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_MOV | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = 0 }) | |
#define BPF_MOV64_IMM(DST, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_MOV | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_MOV32_IMM(DST, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_MOV | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_LD_IMM64(DST, IMM) \ | |
BPF_LD_IMM64_RAW(DST, 0, IMM) | |
#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_LD | BPF_DW | BPF_IMM, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = (__u32) (IMM) }), \ | |
((struct bpf_insn) { \ | |
.code = 0, \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = ((__u64) (IMM)) >> 32 }) | |
#define BPF_LD_MAP_FD(DST, MAP_FD) \ | |
BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) | |
#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE), \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_MOV32_RAW(TYPE, DST, SRC, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_MOV | BPF_SRC(TYPE), \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_LD_ABS(SIZE, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_LD_IND(SIZE, SRC, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_LD | BPF_SIZE(SIZE) | BPF_IND, \ | |
.dst_reg = 0, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = OFF, \ | |
.imm = IMM }) | |
#define BPF_JMP_REG(OP, DST, SRC, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_OP(OP) | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = OFF, \ | |
.imm = IMM }) | |
#define BPF_EMIT_CALL(FUNC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_CALL, \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = ((FUNC) - BPF_FUNC_unspec) }) | |
#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ | |
((struct bpf_insn) { \ | |
.code = CODE, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = IMM }) | |
#define BPF_EXIT_INSN() \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_EXIT, \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = 0 }) | |
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = OFF, \ | |
.imm = IMM }) | |
#endif /* RAW_INSN_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <errno.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <assert.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <arpa/inet.h> | |
#include <linux/bpf.h> | |
#include <linux/filter.h> | |
#include <linux/if_ether.h> | |
#include <linux/if_packet.h> | |
#include <linux/ip.h> | |
#include <linux/unistd.h> | |
#include <net/if.h> | |
#include <sys/socket.h> | |
#include "libbpf.h" | |
#ifndef __NR_bpf | |
# if defined(__i386__) | |
# define __NR_bpf 357 | |
# elif defined(__x86_64__) | |
# define __NR_bpf 321 | |
# elif defined(__aarch64__) | |
# define __NR_bpf 280 | |
# else | |
# warning __NR_bpf not defined. | |
# endif | |
#endif | |
struct bpf_elf_map { | |
__u32 type; | |
__u32 size_key; | |
__u32 size_value; | |
__u32 max_elem; | |
__u32 flags; | |
__u32 id; | |
__u32 pinning; | |
}; | |
static int bpf(int cmd, union bpf_attr *attr, unsigned int size) | |
{ | |
#ifdef __NR_bpf | |
return syscall(__NR_bpf, cmd, attr, size); | |
#else | |
errno = ENOSYS; | |
return -2; | |
#endif | |
} | |
static uint64_t bpf_ptr_to_u64(const void *ptr) | |
{ | |
return (uint64_t)(unsigned long)ptr; | |
} | |
static int bpf_map_create(enum bpf_map_type type, uint32_t size_key, | |
uint32_t size_value, uint32_t max_elem, | |
uint32_t flags) | |
{ | |
union bpf_attr attr; | |
memset(&attr, 0, sizeof(attr)); | |
attr.map_type = type; | |
attr.key_size = size_key; | |
attr.value_size = size_value; | |
attr.max_entries = max_elem; | |
attr.map_flags = flags; | |
return bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); | |
} | |
static inline __u64 ptr_to_u64(const void *ptr) | |
{ | |
return (__u64) (unsigned long) ptr; | |
} | |
int bpf_lookup_elem(int fd, void *key, void *value) | |
{ | |
union bpf_attr attr = { | |
.map_fd = fd, | |
.key = ptr_to_u64(key), | |
.value = ptr_to_u64(value), | |
}; | |
return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); | |
} | |
int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns, | |
size_t num_insns, const char *license, char *log, | |
size_t size_log) | |
{ | |
union bpf_attr attr; | |
memset(&attr, 0, sizeof(attr)); | |
attr.prog_type = type; | |
attr.insns = bpf_ptr_to_u64(insns); | |
attr.insn_cnt = num_insns; | |
attr.license = bpf_ptr_to_u64(license); | |
if (size_log > 0) { | |
attr.log_buf = bpf_ptr_to_u64(log); | |
attr.log_size = size_log; | |
attr.log_level = 1; | |
} | |
return bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); | |
} | |
static inline int open_raw_sock(const char *name) | |
{ | |
struct sockaddr_ll sll; | |
int sock; | |
sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL)); | |
if (sock < 0) { | |
printf("cannot create raw socket\n"); | |
return -1; | |
} | |
memset(&sll, 0, sizeof(sll)); | |
sll.sll_family = AF_PACKET; | |
sll.sll_ifindex = if_nametoindex(name); | |
sll.sll_protocol = htons(ETH_P_ALL); | |
if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { | |
printf("bind to %s: %s\n", name, strerror(errno)); | |
close(sock); | |
return -1; | |
} | |
return sock; | |
} | |
int bpf_map_lookup_elem(int fd, const void *key, void *value) | |
{ | |
union bpf_attr attr; | |
bzero(&attr, sizeof(attr)); | |
attr.map_fd = fd; | |
attr.key = ptr_to_u64(key); | |
attr.value = ptr_to_u64(value); | |
return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); | |
} | |
int main(void) | |
{ | |
static char bpf_vlog[1U << 16]; | |
int sock = -1, map_fd, prog_fd, i, key; | |
long long value = 0, tcp_cnt, udp_cnt, icmp_cnt; | |
map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), | |
256, 0); | |
if (map_fd < 0) { | |
printf("failed to create map '%s'\n", strerror(errno)); | |
} | |
// All the info related to this on "Programming Linux Hacker Tools Uncovered" | |
// book, Chapter 9.1 | |
printf("BPF_MAP_CREATE: FD=%d\n", map_fd); | |
struct bpf_insn prog[] = { | |
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
BPF_LD_ABS(BPF_B, ETH_HLEN + offsetof(struct iphdr, protocol) /* R0 = ip->proto */), | |
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ | |
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | |
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ | |
BPF_LD_MAP_FD(BPF_REG_1, map_fd), | |
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), | |
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), | |
BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */ | |
BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */ | |
BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */ | |
BPF_EXIT_INSN(), | |
}; | |
size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); | |
prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, insns_cnt, | |
"GPL", bpf_vlog, sizeof(bpf_vlog)); | |
if (prog_fd < 0) { | |
printf("failed to load prog '%s'\n", strerror(errno)); | |
} | |
printf("%d \n", prog_fd); | |
sock = open_raw_sock("lo"); | |
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, | |
sizeof(prog_fd)) < 0) { | |
printf("setsockopt %s\n", strerror(errno)); | |
return 0; | |
} | |
for (i = 0; i < 10; i++) { | |
key = IPPROTO_TCP; | |
assert(bpf_map_lookup_elem(map_fd, &key, &tcp_cnt) == 0); | |
key = IPPROTO_UDP; | |
assert(bpf_map_lookup_elem(map_fd, &key, &udp_cnt) == 0); | |
key = IPPROTO_ICMP; | |
assert(bpf_map_lookup_elem(map_fd, &key, &icmp_cnt) == 0); | |
printf("TCP %lld UDP %lld ICMP %lld packets\n", | |
tcp_cnt, udp_cnt, icmp_cnt); | |
sleep(1); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment