| /* |
| * Copyright (c) 2015-2017 Dmitry V. Levin <ldv@altlinux.org> |
| * Copyright (c) 2017 Quentin Monnet <quentin.monnet@6wind.com> |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "defs.h" |
| #include "print_fields.h" |
| |
| #ifdef HAVE_LINUX_BPF_H |
| # include <linux/bpf.h> |
| #endif |
| |
| #include "xlat/bpf_commands.h" |
| #include "xlat/bpf_map_types.h" |
| #include "xlat/bpf_map_flags.h" |
| #include "xlat/bpf_prog_types.h" |
| #include "xlat/bpf_prog_flags.h" |
| #include "xlat/bpf_map_update_elem_flags.h" |
| #include "xlat/bpf_attach_type.h" |
| #include "xlat/bpf_attach_flags.h" |
| |
| #define DECL_BPF_CMD_DECODER(bpf_cmd_decoder) \ |
| int \ |
| bpf_cmd_decoder(struct tcb *const tcp, \ |
| const kernel_ulong_t addr, \ |
| const unsigned int size, \ |
| void *const data) \ |
| /* End of DECL_BPF_CMD_DECODER definition. */ |
| |
| #define DEF_BPF_CMD_DECODER(bpf_cmd) \ |
| static DECL_BPF_CMD_DECODER(decode_ ## bpf_cmd) |
| |
| #define BPF_CMD_ENTRY(bpf_cmd) \ |
| [bpf_cmd] = decode_ ## bpf_cmd |
| |
| typedef DECL_BPF_CMD_DECODER((*bpf_cmd_decoder_t)); |
| |
| static int |
| decode_attr_extra_data(struct tcb *const tcp, |
| const char *data, |
| unsigned int size, |
| const size_t attr_size) |
| { |
| if (size <= attr_size) |
| return 0; |
| |
| data += attr_size; |
| size -= attr_size; |
| |
| unsigned int i; |
| for (i = 0; i < size; ++i) { |
| if (data[i]) { |
| tprints(", "); |
| if (abbrev(tcp)) |
| tprints("..."); |
| else |
| print_quoted_string(data, size, |
| QUOTE_FORCE_HEX); |
| return RVAL_DECODED; |
| } |
| } |
| |
| return 0; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_MAP_CREATE) |
| { |
| struct { |
| uint32_t map_type, key_size, value_size, max_entries, |
| map_flags, inner_map_fd, numa_node; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_XVAL("{", attr, map_type, bpf_map_types, |
| "BPF_MAP_TYPE_???"); |
| PRINT_FIELD_U(", ", attr, key_size); |
| PRINT_FIELD_U(", ", attr, value_size); |
| PRINT_FIELD_U(", ", attr, max_entries); |
| PRINT_FIELD_FLAGS(", ", attr, map_flags, bpf_map_flags, "BPF_F_???"); |
| PRINT_FIELD_FD(", ", attr, inner_map_fd, tcp); |
| if (attr.map_flags & BPF_F_NUMA_NODE) |
| PRINT_FIELD_U(", ", attr, numa_node); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED | RVAL_FD; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_MAP_LOOKUP_ELEM) |
| { |
| struct bpf_io_elem_struct { |
| uint32_t map_fd; |
| uint64_t ATTRIBUTE_ALIGNED(8) key, value; |
| } attr = {}; |
| |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_FD("{", attr, map_fd, tcp); |
| PRINT_FIELD_X(", ", attr, key); |
| PRINT_FIELD_X(", ", attr, value); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_MAP_UPDATE_ELEM) |
| { |
| struct { |
| uint32_t map_fd; |
| uint64_t ATTRIBUTE_ALIGNED(8) key; |
| uint64_t ATTRIBUTE_ALIGNED(8) value; |
| uint64_t flags; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_FD("{", attr, map_fd, tcp); |
| PRINT_FIELD_X(", ", attr, key); |
| PRINT_FIELD_X(", ", attr, value); |
| PRINT_FIELD_XVAL(", ", attr, flags, bpf_map_update_elem_flags, |
| "BPF_???"); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_MAP_DELETE_ELEM) |
| { |
| struct { |
| uint32_t map_fd; |
| uint64_t ATTRIBUTE_ALIGNED(8) key; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_FD("{", attr, map_fd, tcp); |
| PRINT_FIELD_X(", ", attr, key); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_MAP_GET_NEXT_KEY) |
| { |
| struct bpf_io_elem_struct { |
| uint32_t map_fd; |
| uint64_t ATTRIBUTE_ALIGNED(8) key, next_key; |
| } attr = {}; |
| |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_FD("{", attr, map_fd, tcp); |
| PRINT_FIELD_X(", ", attr, key); |
| PRINT_FIELD_X(", ", attr, next_key); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_PROG_LOAD) |
| { |
| struct bpf_prog_load { |
| uint32_t prog_type, insn_cnt; |
| uint64_t ATTRIBUTE_ALIGNED(8) insns, license; |
| uint32_t log_level, log_size; |
| uint64_t ATTRIBUTE_ALIGNED(8) log_buf; |
| uint32_t kern_version, prog_flags; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_XVAL("{", attr, prog_type, bpf_prog_types, |
| "BPF_PROG_TYPE_???"); |
| PRINT_FIELD_U(", ", attr, insn_cnt); |
| PRINT_FIELD_X(", ", attr, insns); |
| PRINT_FIELD_STR(", ", attr, license, tcp); |
| PRINT_FIELD_U(", ", attr, log_level); |
| PRINT_FIELD_U(", ", attr, log_size); |
| PRINT_FIELD_X(", ", attr, log_buf); |
| PRINT_FIELD_U(", ", attr, kern_version); |
| PRINT_FIELD_FLAGS(", ", attr, prog_flags, bpf_prog_flags, "BPF_F_???"); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED | RVAL_FD; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_OBJ_PIN) |
| { |
| struct bpf_obj { |
| uint64_t ATTRIBUTE_ALIGNED(8) pathname; |
| uint32_t bpf_fd; |
| } attr = {}; |
| const size_t attr_size = |
| offsetofend(struct bpf_obj, bpf_fd); |
| const unsigned int len = size < attr_size ? size : attr_size; |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_PATH("{", attr, pathname, tcp); |
| PRINT_FIELD_FD(", ", attr, bpf_fd, tcp); |
| decode_attr_extra_data(tcp, data, size, attr_size); |
| tprints("}"); |
| |
| return RVAL_DECODED | RVAL_FD; |
| } |
| |
| #define decode_BPF_OBJ_GET decode_BPF_OBJ_PIN |
| |
| DEF_BPF_CMD_DECODER(BPF_PROG_ATTACH) |
| { |
| struct { |
| uint32_t target_fd, attach_bpf_fd, attach_type, attach_flags; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_FD("{", attr, target_fd, tcp); |
| PRINT_FIELD_FD(", ", attr, attach_bpf_fd, tcp); |
| PRINT_FIELD_XVAL(", ", attr, attach_type, bpf_attach_type, "BPF_???"); |
| PRINT_FIELD_FLAGS(", ", attr, attach_flags, bpf_attach_flags, |
| "BPF_F_???"); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_PROG_DETACH) |
| { |
| struct { |
| uint32_t target_fd, dummy, attach_type; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_FD("{", attr, target_fd, tcp); |
| PRINT_FIELD_XVAL(", ", attr, attach_type, bpf_attach_type, "BPF_???"); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_PROG_TEST_RUN) |
| { |
| struct { |
| uint32_t prog_fd, retval, data_size_in, data_size_out; |
| uint64_t ATTRIBUTE_ALIGNED(8) data_in, data_out; |
| uint32_t repeat, duration; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_FD("{test={", attr, prog_fd, tcp); |
| PRINT_FIELD_U(", ", attr, retval); |
| PRINT_FIELD_U(", ", attr, data_size_in); |
| PRINT_FIELD_U(", ", attr, data_size_out); |
| PRINT_FIELD_X(", ", attr, data_in); |
| PRINT_FIELD_X(", ", attr, data_out); |
| PRINT_FIELD_U(", ", attr, repeat); |
| PRINT_FIELD_U(", ", attr, duration); |
| tprints("}"); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_PROG_GET_NEXT_ID) |
| { |
| struct { |
| uint32_t start_id, next_id; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_U("{", attr, start_id); |
| PRINT_FIELD_U(", ", attr, next_id); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| #define decode_BPF_MAP_GET_NEXT_ID decode_BPF_PROG_GET_NEXT_ID |
| |
| DEF_BPF_CMD_DECODER(BPF_PROG_GET_FD_BY_ID) |
| { |
| struct { |
| uint32_t prog_id, next_id; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_U("{", attr, prog_id); |
| PRINT_FIELD_U(", ", attr, next_id); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_MAP_GET_FD_BY_ID) |
| { |
| struct { |
| uint32_t map_id, next_id; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_U("{", attr, map_id); |
| PRINT_FIELD_U(", ", attr, next_id); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED; |
| } |
| |
| DEF_BPF_CMD_DECODER(BPF_OBJ_GET_INFO_BY_FD) |
| { |
| struct { |
| uint32_t bpf_fd, info_len; |
| uint64_t ATTRIBUTE_ALIGNED(8) info; |
| } attr = {}; |
| const unsigned int len = size < sizeof(attr) ? size : sizeof(attr); |
| |
| memcpy(&attr, data, len); |
| |
| PRINT_FIELD_FD("{info={", attr, bpf_fd, tcp); |
| PRINT_FIELD_U(", ", attr, info_len); |
| PRINT_FIELD_X(", ", attr, info); |
| tprints("}"); |
| decode_attr_extra_data(tcp, data, size, sizeof(attr)); |
| tprints("}"); |
| |
| return RVAL_DECODED | RVAL_FD; |
| } |
| |
| SYS_FUNC(bpf) |
| { |
| static const bpf_cmd_decoder_t bpf_cmd_decoders[] = { |
| BPF_CMD_ENTRY(BPF_MAP_CREATE), |
| BPF_CMD_ENTRY(BPF_MAP_LOOKUP_ELEM), |
| BPF_CMD_ENTRY(BPF_MAP_UPDATE_ELEM), |
| BPF_CMD_ENTRY(BPF_MAP_DELETE_ELEM), |
| BPF_CMD_ENTRY(BPF_MAP_GET_NEXT_KEY), |
| BPF_CMD_ENTRY(BPF_PROG_LOAD), |
| BPF_CMD_ENTRY(BPF_OBJ_PIN), |
| BPF_CMD_ENTRY(BPF_OBJ_GET), |
| BPF_CMD_ENTRY(BPF_PROG_ATTACH), |
| BPF_CMD_ENTRY(BPF_PROG_DETACH), |
| BPF_CMD_ENTRY(BPF_PROG_TEST_RUN), |
| BPF_CMD_ENTRY(BPF_PROG_GET_NEXT_ID), |
| BPF_CMD_ENTRY(BPF_MAP_GET_NEXT_ID), |
| BPF_CMD_ENTRY(BPF_PROG_GET_FD_BY_ID), |
| BPF_CMD_ENTRY(BPF_MAP_GET_FD_BY_ID), |
| BPF_CMD_ENTRY(BPF_OBJ_GET_INFO_BY_FD), |
| }; |
| |
| const unsigned int cmd = tcp->u_arg[0]; |
| const kernel_ulong_t addr = tcp->u_arg[1]; |
| const unsigned int size = tcp->u_arg[2]; |
| int rc; |
| |
| if (entering(tcp)) { |
| static size_t page_size; |
| static char *buf; |
| |
| if (!buf) { |
| page_size = get_pagesize(); |
| buf = xmalloc(page_size); |
| } |
| |
| printxval(bpf_commands, cmd, "BPF_???"); |
| tprints(", "); |
| |
| if (size > 0 |
| && size <= get_pagesize() |
| && cmd < ARRAY_SIZE(bpf_cmd_decoders) |
| && bpf_cmd_decoders[cmd]) { |
| rc = umoven_or_printaddr(tcp, addr, size, buf) |
| ? RVAL_DECODED |
| : bpf_cmd_decoders[cmd](tcp, addr, size, buf); |
| } else { |
| printaddr(addr); |
| rc = RVAL_DECODED; |
| } |
| } else { |
| rc = bpf_cmd_decoders[cmd](tcp, addr, size, NULL) | RVAL_DECODED; |
| } |
| |
| if (rc & RVAL_DECODED) |
| tprintf(", %u", size); |
| |
| return rc; |
| } |