* [PATCH net-next v3 0/3] net: core: Two Helper function about socket information
@ 2017-02-23 4:42 Chenbo Feng
2017-02-23 4:42 ` [PATCH net-next v3 1/3] Add a helper function to get socket cookie in eBPF Chenbo Feng
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Chenbo Feng @ 2017-02-23 4:42 UTC (permalink / raw)
To: netdev, Alexei Starovoitov, Daniel Borkmann
Cc: Lorenzo Colitti, Willem de Bruijn, Chenbo Feng
From: Chenbo Feng <fengc@google.com>
Introduce two eBpf helper function to get the socket cookie and
socket uid for each packet. The helper function is useful when
the *sk field inside sk_buff is not empty. These helper functions
can be used on socket and uid based traffic monitoring programs.
Change since V2:
* Add a sample program to demostrate the usage of the helper function.
* Moved the helper function proto invoking place.
* Add function header into tools/include
* Apply sk_to_full_sk() before getting uid.
Change since V1:
* Removed the unnecessary declarations and export command
* resolved conflict with master branch.
* Examine if the socket is a full socket before getting the uid.
Chenbo Feng (3):
Add a helper function to get socket cookie in eBPF
Add a eBPF helper function to retrieve socket uid
A Sample of using socket cookie and uid for traffic monitoring
include/linux/sock_diag.h | 1 +
include/uapi/linux/bpf.h | 16 +-
net/core/filter.c | 36 +++++
net/core/sock_diag.c | 2 +-
samples/bpf/cookie_uid_helper_example.c | 225 +++++++++++++++++++++++++++
samples/bpf/run_cookie_uid_helper_example.sh | 14 ++
tools/include/uapi/linux/bpf.h | 4 +-
7 files changed, 295 insertions(+), 3 deletions(-)
create mode 100644 samples/bpf/cookie_uid_helper_example.c
create mode 100755 samples/bpf/run_cookie_uid_helper_example.sh
--
2.7.4
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH net-next v3 1/3] Add a helper function to get socket cookie in eBPF 2017-02-23 4:42 [PATCH net-next v3 0/3] net: core: Two Helper function about socket information Chenbo Feng @ 2017-02-23 4:42 ` Chenbo Feng 2017-02-23 4:42 ` [PATCH net-next v3 2/3] Add a eBPF helper function to retrieve socket uid Chenbo Feng ` (2 subsequent siblings) 3 siblings, 0 replies; 8+ messages in thread From: Chenbo Feng @ 2017-02-23 4:42 UTC (permalink / raw) To: netdev, Alexei Starovoitov, Daniel Borkmann Cc: Lorenzo Colitti, Willem de Bruijn, Chenbo Feng, Chenbo Feng From: Chenbo Feng <fengc@google.com> Retrieve the socket cookie generated by sock_gen_cookie() from a sk_buff with a known socket. Generates a new cookie if one was not yet set.If the socket pointer inside sk_buff is NULL, 0 is returned. The helper function coud be useful in monitoring per socket networking traffic statistics and provide a unique socket identifier per namespace. Change since V2: Moved the helper function from bpf_base_func_proto() to both sk_filter_func_proto() and tc_cls_act_func_proto(). Add function name to uapi header file under tools/include. Change since V1: Removed the unnecessary declarations and export command, resolved conflict with master branch. Signed-off-by: Chenbo Feng <chenbofeng.kernel@gmail.com> --- include/linux/sock_diag.h | 1 + include/uapi/linux/bpf.h | 9 ++++++++- net/core/filter.c | 17 +++++++++++++++++ net/core/sock_diag.c | 2 +- tools/include/uapi/linux/bpf.h | 3 ++- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h index a0596ca0..a2f8109 100644 --- a/include/linux/sock_diag.h +++ b/include/linux/sock_diag.h @@ -24,6 +24,7 @@ void sock_diag_unregister(const struct sock_diag_handler *h); void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); +u64 sock_gen_cookie(struct sock *sk); int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie); void sock_diag_save_cookie(struct sock *sk, __u32 *cookie); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 0539a0c..dc81a9f 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -456,6 +456,12 @@ union bpf_attr { * Return: * > 0 length of the string including the trailing NUL on success * < 0 error + * + * u64 bpf_bpf_get_socket_cookie(skb) + * Get the cookie for the socket stored inside sk_buff. + * @skb: pointer to skb + * Return: 8 Bytes non-decreasing number on success or 0 if the socket + * field is missing inside sk_buff */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -503,7 +509,8 @@ union bpf_attr { FN(get_numa_node_id), \ FN(skb_change_head), \ FN(xdp_adjust_head), \ - FN(probe_read_str), + FN(probe_read_str), \ + FN(get_socket_cookie), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index e466e004..06263c0 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -26,6 +26,7 @@ #include <linux/mm.h> #include <linux/fcntl.h> #include <linux/socket.h> +#include <linux/sock_diag.h> #include <linux/in.h> #include <linux/inet.h> #include <linux/netdevice.h> @@ -2599,6 +2600,18 @@ static const struct bpf_func_proto bpf_xdp_event_output_proto = { .arg5_type = ARG_CONST_SIZE, }; +BPF_CALL_1(bpf_get_socket_cookie, struct sk_buff *, skb) +{ + return skb->sk ? sock_gen_cookie(skb->sk) : 0; +} + +static const struct bpf_func_proto bpf_get_socket_cookie_proto = { + .func = bpf_get_socket_cookie, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +}; + static const struct bpf_func_proto * bpf_base_func_proto(enum bpf_func_id func_id) { @@ -2633,6 +2646,8 @@ sk_filter_func_proto(enum bpf_func_id func_id) switch (func_id) { case BPF_FUNC_skb_load_bytes: return &bpf_skb_load_bytes_proto; + case BPF_FUNC_get_socket_cookie: + return &bpf_get_socket_cookie_proto; default: return bpf_base_func_proto(func_id); } @@ -2692,6 +2707,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id) return &bpf_get_smp_processor_id_proto; case BPF_FUNC_skb_under_cgroup: return &bpf_skb_under_cgroup_proto; + case BPF_FUNC_get_socket_cookie: + return &bpf_get_socket_cookie_proto; default: return bpf_base_func_proto(func_id); } diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 6b10573..acd2a6c 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -19,7 +19,7 @@ static int (*inet_rcv_compat)(struct sk_buff *skb, struct nlmsghdr *nlh); static DEFINE_MUTEX(sock_diag_table_mutex); static struct workqueue_struct *broadcast_wq; -static u64 sock_gen_cookie(struct sock *sk) +u64 sock_gen_cookie(struct sock *sk) { while (1) { u64 res = atomic64_read(&sk->sk_cookie); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0539a0c..a94bdd3 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -503,7 +503,8 @@ union bpf_attr { FN(get_numa_node_id), \ FN(skb_change_head), \ FN(xdp_adjust_head), \ - FN(probe_read_str), + FN(probe_read_str), \ + FN(get_socket_cookie), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call -- 2.7.4 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net-next v3 2/3] Add a eBPF helper function to retrieve socket uid 2017-02-23 4:42 [PATCH net-next v3 0/3] net: core: Two Helper function about socket information Chenbo Feng 2017-02-23 4:42 ` [PATCH net-next v3 1/3] Add a helper function to get socket cookie in eBPF Chenbo Feng @ 2017-02-23 4:42 ` Chenbo Feng 2017-02-23 17:03 ` Willem de Bruijn 2017-02-23 4:42 ` [PATCH net-next v3 3/3] A Sample of using socket cookie and uid for traffic monitoring Chenbo Feng 2017-02-23 17:00 ` [PATCH net-next v3 0/3] net: core: Two Helper function about socket information Willem de Bruijn 3 siblings, 1 reply; 8+ messages in thread From: Chenbo Feng @ 2017-02-23 4:42 UTC (permalink / raw) To: netdev, Alexei Starovoitov, Daniel Borkmann Cc: Lorenzo Colitti, Willem de Bruijn, Chenbo Feng, Chenbo Feng From: Chenbo Feng <fengc@google.com> Returns the owner uid of the socket inside a sk_buff. This is useful to perform per-UID accounting of network traffic or per-UID packet filtering. The socket need to be a fullsock otherwise 0 is returned. Change since V2: Add a sk_to_full_sk() check before retrieving the uid. Moved the helper function from bpf_base_func_proto() to both sk_filter_func_proto() and tc_cls_act_func_proto(). Add function name to uapi header file under tools/include Change since V1: Removed the unnecessary declarations and export command, resolved conflict with master branch. Examine if the socket is a full socket before getting the uid. Signed-off-by: Chenbo Feng <chenbofeng.kernel@gmail.com> --- include/uapi/linux/bpf.h | 9 ++++++++- net/core/filter.c | 19 +++++++++++++++++++ tools/include/uapi/linux/bpf.h | 3 ++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index dc81a9f..ff42111 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -462,6 +462,12 @@ union bpf_attr { * @skb: pointer to skb * Return: 8 Bytes non-decreasing number on success or 0 if the socket * field is missing inside sk_buff + * + * u32 bpf_get_socket_uid(skb) + * Get the owner uid of the socket stored inside sk_buff. + * @skb: pointer to skb + * Return: uid of the socket owner on success or 0 if the socket pointer + * inside sk_buff is NULL */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -510,7 +516,8 @@ union bpf_attr { FN(skb_change_head), \ FN(xdp_adjust_head), \ FN(probe_read_str), \ - FN(get_socket_cookie), + FN(get_socket_cookie), \ + FN(get_socket_uid), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index 06263c0..53c4afc 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2612,6 +2612,21 @@ static const struct bpf_func_proto bpf_get_socket_cookie_proto = { .arg1_type = ARG_PTR_TO_CTX, }; +BPF_CALL_1(bpf_get_socket_uid, struct sk_buff *, skb) +{ + struct sock *sk = sk_to_full_sk(skb->sk); + kuid_t kuid = sock_net_uid(dev_net(skb->dev), sk); + + return (u32)kuid.val; +} + +static const struct bpf_func_proto bpf_get_socket_uid_proto = { + .func = bpf_get_socket_uid, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +}; + static const struct bpf_func_proto * bpf_base_func_proto(enum bpf_func_id func_id) { @@ -2648,6 +2663,8 @@ sk_filter_func_proto(enum bpf_func_id func_id) return &bpf_skb_load_bytes_proto; case BPF_FUNC_get_socket_cookie: return &bpf_get_socket_cookie_proto; + case BPF_FUNC_get_socket_uid: + return &bpf_get_socket_uid_proto; default: return bpf_base_func_proto(func_id); } @@ -2709,6 +2726,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id) return &bpf_skb_under_cgroup_proto; case BPF_FUNC_get_socket_cookie: return &bpf_get_socket_cookie_proto; + case BPF_FUNC_get_socket_uid: + return &bpf_get_socket_uid_proto; default: return bpf_base_func_proto(func_id); } diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index a94bdd3..4a2d56d 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -504,7 +504,8 @@ union bpf_attr { FN(skb_change_head), \ FN(xdp_adjust_head), \ FN(probe_read_str), \ - FN(get_socket_cookie), + FN(get_socket_cookie), \ + FN(get_socket_uid), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call -- 2.7.4 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v3 2/3] Add a eBPF helper function to retrieve socket uid 2017-02-23 4:42 ` [PATCH net-next v3 2/3] Add a eBPF helper function to retrieve socket uid Chenbo Feng @ 2017-02-23 17:03 ` Willem de Bruijn 0 siblings, 0 replies; 8+ messages in thread From: Willem de Bruijn @ 2017-02-23 17:03 UTC (permalink / raw) To: Chenbo Feng Cc: Network Development, Alexei Starovoitov, Daniel Borkmann, Lorenzo Colitti, Willem de Bruijn, Chenbo Feng > +BPF_CALL_1(bpf_get_socket_uid, struct sk_buff *, skb) > +{ > + struct sock *sk = sk_to_full_sk(skb->sk); > + kuid_t kuid = sock_net_uid(dev_net(skb->dev), sk); dev_net cannot handle a NULL skb->dev ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net-next v3 3/3] A Sample of using socket cookie and uid for traffic monitoring 2017-02-23 4:42 [PATCH net-next v3 0/3] net: core: Two Helper function about socket information Chenbo Feng 2017-02-23 4:42 ` [PATCH net-next v3 1/3] Add a helper function to get socket cookie in eBPF Chenbo Feng 2017-02-23 4:42 ` [PATCH net-next v3 2/3] Add a eBPF helper function to retrieve socket uid Chenbo Feng @ 2017-02-23 4:42 ` Chenbo Feng 2017-02-23 17:00 ` [PATCH net-next v3 0/3] net: core: Two Helper function about socket information Willem de Bruijn 3 siblings, 0 replies; 8+ messages in thread From: Chenbo Feng @ 2017-02-23 4:42 UTC (permalink / raw) To: netdev, Alexei Starovoitov, Daniel Borkmann Cc: Lorenzo Colitti, Willem de Bruijn, Chenbo Feng, Chenbo Feng From: Chenbo Feng <fengc@google.com> Add a sample program to demostrate the possible usage of get_socket_cookie and get_socket_uid helper function. The program will store bytes and packets counting of in/out traffic monitored by iptable and store the stats in a bpf map in per socket base. The owner uid of the socket will be stored as part of the data entry. A shell script for running the program is also included. Change since V2: Add the example code and the shell script to run the program. Signed-off-by: Chenbo Feng <chenbofeng.kernel@gmail.com> --- samples/bpf/cookie_uid_helper_example.c | 225 +++++++++++++++++++++++++++ samples/bpf/run_cookie_uid_helper_example.sh | 14 ++ 2 files changed, 239 insertions(+) create mode 100644 samples/bpf/cookie_uid_helper_example.c create mode 100755 samples/bpf/run_cookie_uid_helper_example.sh diff --git a/samples/bpf/cookie_uid_helper_example.c b/samples/bpf/cookie_uid_helper_example.c new file mode 100644 index 0000000..ffa4740 --- /dev/null +++ b/samples/bpf/cookie_uid_helper_example.c @@ -0,0 +1,225 @@ +/* This test is a demo of using get_socket_uid and get_socket_cookie + * helper function to do per socket based network traffic monitoring. + * It requires iptable version higher then 1.6.1. to load pined eBPF + * program into the xt_bpf match. + * + * Compile: + * gcc -I ../../usr/include -I ../../tools/lib -I ../../tools/include \ + * -I ./ -Wall cookie_uid_helper_example.c ../../tools/lib/bpf/bpf.c -o \ + * perSocketStats_example + * + * TEST: + * ./run_cookie_uid_helper_example.sh + * Then generate some traffic in variate ways. ping 0 -c 10 would work + * but the cookie and uid in this case could both be 0. A sample output + * with some traffic generated by web browser is shown below: + * + * cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 11058 + * + * cookie: 132, uid: 0x0, Pakcet Count: 2, Bytes Count: 286 + * cookie: 812, uid: 0x3e8, Pakcet Count: 3, Bytes Count: 1726 + * cookie: 802, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 104 + * cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 11058 + * cookie: 831, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 104 + * cookie: 0, uid: 0x0, Pakcet Count: 6, Bytes Count: 712 + * cookie: 880, uid: 0xfffe, Pakcet Count: 1, Bytes Count: 70 + * + * Clean up: if using shell script, the script file will delete the iptables + * rule and unmount the bpf program when exit. Else the iptables rule need + * to be deleted using: + * iptables -D INPUT -m bpf --object-pinned ${mnt_dir}/bpf_prog -j ACCEPT + */ + +#define _GNU_SOURCE + +#define offsetof(type, member) __builtin_offsetof(type, member) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +#include <arpa/inet.h> +#include <errno.h> +#include <error.h> +#include <limits.h> +#include <linux/bpf.h> +#include <linux/if_ether.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <bpf/bpf.h> +#include "libbpf.h" + +struct stats { + uint32_t uid; + uint64_t packets; + uint64_t bytes; +}; + +static int map_fd, prog_fd; + +static void maps_create(void) +{ + map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(uint32_t), + sizeof(struct stats), 100, 0); + if (map_fd < 0) + error(1, errno, "map create failed!\n"); +} + +static void prog_load(void) +{ + static char log_buf[1 << 16]; + + struct bpf_insn prog[] = { + /* + * it for future usage. value stored in R6 to R10 will not be + * reset after a bpf helper function call. + */ + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + /* + * pc1: BPF_FUNC_get_socket_cookie takes one parameter, + * R1: sk_buff + */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_socket_cookie), + /* pc2-4: save &socketCookie to r7 for future usage*/ + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8), + /* + * pc5-8: set up the registers for BPF_FUNC_map_lookup_elem, + * it takes two parameters (R1: map_fd, R2: &socket_cookie) + */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + /* + * pc9. if r0 != 0x0, go to pc+14, since we have the cookie + * stored already + * Otherwise do pc10-22 to setup a new data entry. + */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 14), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_socket_uid), + /* + * Place a struct stats in the R10 stack and sequentially + * place the member value into the memory. Packets value + * is set by directly place a IMM value 1 into the stack. + */ + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, + -32 + offsetof(struct stats, uid)), + BPF_ST_MEM(BPF_DW, BPF_REG_10, + -32 + offsetof(struct stats, packets), 1), + /* + * __sk_buff is a special struct used for eBPF program to + * directly access some sk_buff field. + */ + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, + offsetof(struct __sk_buff, len)), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, + -32 + offsetof(struct stats, bytes)), + /* + * add new map entry using BPF_FUNC_map_update_elem, it takes + * 4 parameters (R1: map_fd, R2: &socket_cookie, R3: &stats) + */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -32), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_update_elem), + BPF_JMP_IMM(BPF_JA, 0, 0, 5), + /* + * pc24-30 update the packet info to a exist data entry, it can + * be done by directly write to pointers instead of using + * BPF_FUNC_map_update_elem helper function + */ + BPF_MOV64_REG(BPF_REG_9, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_1, 1), + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_9, BPF_REG_1, + offsetof(struct stats, packets), 0), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, + offsetof(struct __sk_buff, len)), + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_9, BPF_REG_1, + offsetof(struct stats, bytes), 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, + offsetof(struct __sk_buff, len)), + BPF_EXIT_INSN(), + }; + prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, + ARRAY_SIZE(prog), "GPL", 0, + log_buf, sizeof(log_buf)); + if (prog_fd < 0) + error(1, errno, "failed to load prog\n%s\n", log_buf); +} + +static void prog_attach_iptables(char *file) +{ + int ret; + char rules[100]; + + if (bpf_obj_pin(prog_fd, file)) + error(1, errno, "bpf_obj_pin"); + sprintf(rules, "iptables -A INPUT -m bpf --object-pinned %s -j ACCEPT", + file); + ret = system(rules); + if (ret < 0) { + printf("iptable rule update failed: %d/n", WEXITSTATUS(ret)); + exit(1); + } +} + +static void print_table(void) +{ + struct stats curEntry; + uint32_t curN = UINT32_MAX; + uint32_t nextN, res; + + while (bpf_map_get_next_key(map_fd, &curN, &nextN) > -1) { + curN = nextN; + res = bpf_map_lookup_elem(map_fd, &curN, &curEntry); + if (res < 0) { + error(1, errno, "fail to get entry value of Key: %u\n", + curN); + } else { + printf("cookie: %u, uid: 0x%x, Pakcet Count: %lu," + " Bytes Count: %lu\n", curN, curEntry.uid, + curEntry.packets, curEntry.bytes); + } + } +} + +int main(int argc, char *argv[]) +{ + if (argc > 2) { + printf("Too many argument provided\n"); + return 1; + } else if (argc < 2) { + printf("Usage: %s bpfObjName\n", argv[0]); + return 1; + } + + maps_create(); + prog_load(); + prog_attach_iptables(argv[1]); + + while (true) { + print_table(); + printf("\n"); + sleep(1); + }; + + return 0; +} + + + + + + diff --git a/samples/bpf/run_cookie_uid_helper_example.sh b/samples/bpf/run_cookie_uid_helper_example.sh new file mode 100755 index 0000000..a7a6208 --- /dev/null +++ b/samples/bpf/run_cookie_uid_helper_example.sh @@ -0,0 +1,14 @@ +#!/bin/bash +local_dir="$(pwd)" +root_dir=$local_dir/../.. +mnt_dir=/sys/fs/bpf + +on_exit() { + iptables -D INPUT -m bpf --object-pinned ${mnt_dir}/bpf_prog -j ACCEPT + umount ${mnt_dir} +} + +trap on_exit EXIT +mkdir -p ${mnt_dir} +mount -t bpf bpf ${mnt_dir} +./perSocketStats_example ${mnt_dir}/bpf_prog -- 2.7.4 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v3 0/3] net: core: Two Helper function about socket information 2017-02-23 4:42 [PATCH net-next v3 0/3] net: core: Two Helper function about socket information Chenbo Feng ` (2 preceding siblings ...) 2017-02-23 4:42 ` [PATCH net-next v3 3/3] A Sample of using socket cookie and uid for traffic monitoring Chenbo Feng @ 2017-02-23 17:00 ` Willem de Bruijn 3 siblings, 0 replies; 8+ messages in thread From: Willem de Bruijn @ 2017-02-23 17:00 UTC (permalink / raw) To: Chenbo Feng Cc: Network Development, Alexei Starovoitov, Daniel Borkmann, Lorenzo Colitti, Willem de Bruijn, Chenbo Feng On Wed, Feb 22, 2017 at 11:42 PM, Chenbo Feng <chenbofeng.kernel@gmail.com> wrote: > From: Chenbo Feng <fengc@google.com> > > Introduce two eBpf helper function to get the socket cookie and > socket uid for each packet. The helper function is useful when > the *sk field inside sk_buff is not empty. These helper functions > can be used on socket and uid based traffic monitoring programs. Net-next is closed. But we can review as RFC. ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net-next v3 0/3] net: core: Two Helper function about socket information @ 2017-02-23 4:44 Chenbo Feng 2017-02-23 4:44 ` [PATCH net-next v3 3/3] A Sample of using socket cookie and uid for traffic monitoring Chenbo Feng 0 siblings, 1 reply; 8+ messages in thread From: Chenbo Feng @ 2017-02-23 4:44 UTC (permalink / raw) To: netdev; +Cc: Chenbo Feng From: Chenbo Feng <fengc@google.com> Introduce two eBpf helper function to get the socket cookie and socket uid for each packet. The helper function is useful when the *sk field inside sk_buff is not empty. These helper functions can be used on socket and uid based traffic monitoring programs. Change since V2: * Add a sample program to demostrate the usage of the helper function. * Moved the helper function proto invoking place. * Add function header into tools/include * Apply sk_to_full_sk() before getting uid. Change since V1: * Removed the unnecessary declarations and export command * resolved conflict with master branch. * Examine if the socket is a full socket before getting the uid. Chenbo Feng (3): Add a helper function to get socket cookie in eBPF Add a eBPF helper function to retrieve socket uid A Sample of using socket cookie and uid for traffic monitoring include/linux/sock_diag.h | 1 + include/uapi/linux/bpf.h | 16 +- net/core/filter.c | 36 +++++ net/core/sock_diag.c | 2 +- samples/bpf/cookie_uid_helper_example.c | 225 +++++++++++++++++++++++++++ samples/bpf/run_cookie_uid_helper_example.sh | 14 ++ tools/include/uapi/linux/bpf.h | 4 +- 7 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 samples/bpf/cookie_uid_helper_example.c create mode 100755 samples/bpf/run_cookie_uid_helper_example.sh -- 2.7.4 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net-next v3 3/3] A Sample of using socket cookie and uid for traffic monitoring 2017-02-23 4:44 Chenbo Feng @ 2017-02-23 4:44 ` Chenbo Feng 2017-02-23 17:20 ` Willem de Bruijn 0 siblings, 1 reply; 8+ messages in thread From: Chenbo Feng @ 2017-02-23 4:44 UTC (permalink / raw) To: netdev; +Cc: Chenbo Feng, Chenbo Feng From: Chenbo Feng <fengc@google.com> Add a sample program to demostrate the possible usage of get_socket_cookie and get_socket_uid helper function. The program will store bytes and packets counting of in/out traffic monitored by iptable and store the stats in a bpf map in per socket base. The owner uid of the socket will be stored as part of the data entry. A shell script for running the program is also included. Change since V2: Add the example code and the shell script to run the program. Signed-off-by: Chenbo Feng <chenbofeng.kernel@gmail.com> --- samples/bpf/cookie_uid_helper_example.c | 225 +++++++++++++++++++++++++++ samples/bpf/run_cookie_uid_helper_example.sh | 14 ++ 2 files changed, 239 insertions(+) create mode 100644 samples/bpf/cookie_uid_helper_example.c create mode 100755 samples/bpf/run_cookie_uid_helper_example.sh diff --git a/samples/bpf/cookie_uid_helper_example.c b/samples/bpf/cookie_uid_helper_example.c new file mode 100644 index 0000000..ffa4740 --- /dev/null +++ b/samples/bpf/cookie_uid_helper_example.c @@ -0,0 +1,225 @@ +/* This test is a demo of using get_socket_uid and get_socket_cookie + * helper function to do per socket based network traffic monitoring. + * It requires iptable version higher then 1.6.1. to load pined eBPF + * program into the xt_bpf match. + * + * Compile: + * gcc -I ../../usr/include -I ../../tools/lib -I ../../tools/include \ + * -I ./ -Wall cookie_uid_helper_example.c ../../tools/lib/bpf/bpf.c -o \ + * perSocketStats_example + * + * TEST: + * ./run_cookie_uid_helper_example.sh + * Then generate some traffic in variate ways. ping 0 -c 10 would work + * but the cookie and uid in this case could both be 0. A sample output + * with some traffic generated by web browser is shown below: + * + * cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 11058 + * + * cookie: 132, uid: 0x0, Pakcet Count: 2, Bytes Count: 286 + * cookie: 812, uid: 0x3e8, Pakcet Count: 3, Bytes Count: 1726 + * cookie: 802, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 104 + * cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 11058 + * cookie: 831, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 104 + * cookie: 0, uid: 0x0, Pakcet Count: 6, Bytes Count: 712 + * cookie: 880, uid: 0xfffe, Pakcet Count: 1, Bytes Count: 70 + * + * Clean up: if using shell script, the script file will delete the iptables + * rule and unmount the bpf program when exit. Else the iptables rule need + * to be deleted using: + * iptables -D INPUT -m bpf --object-pinned ${mnt_dir}/bpf_prog -j ACCEPT + */ + +#define _GNU_SOURCE + +#define offsetof(type, member) __builtin_offsetof(type, member) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +#include <arpa/inet.h> +#include <errno.h> +#include <error.h> +#include <limits.h> +#include <linux/bpf.h> +#include <linux/if_ether.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <bpf/bpf.h> +#include "libbpf.h" + +struct stats { + uint32_t uid; + uint64_t packets; + uint64_t bytes; +}; + +static int map_fd, prog_fd; + +static void maps_create(void) +{ + map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(uint32_t), + sizeof(struct stats), 100, 0); + if (map_fd < 0) + error(1, errno, "map create failed!\n"); +} + +static void prog_load(void) +{ + static char log_buf[1 << 16]; + + struct bpf_insn prog[] = { + /* + * it for future usage. value stored in R6 to R10 will not be + * reset after a bpf helper function call. + */ + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + /* + * pc1: BPF_FUNC_get_socket_cookie takes one parameter, + * R1: sk_buff + */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_socket_cookie), + /* pc2-4: save &socketCookie to r7 for future usage*/ + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8), + /* + * pc5-8: set up the registers for BPF_FUNC_map_lookup_elem, + * it takes two parameters (R1: map_fd, R2: &socket_cookie) + */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + /* + * pc9. if r0 != 0x0, go to pc+14, since we have the cookie + * stored already + * Otherwise do pc10-22 to setup a new data entry. + */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 14), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_socket_uid), + /* + * Place a struct stats in the R10 stack and sequentially + * place the member value into the memory. Packets value + * is set by directly place a IMM value 1 into the stack. + */ + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, + -32 + offsetof(struct stats, uid)), + BPF_ST_MEM(BPF_DW, BPF_REG_10, + -32 + offsetof(struct stats, packets), 1), + /* + * __sk_buff is a special struct used for eBPF program to + * directly access some sk_buff field. + */ + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, + offsetof(struct __sk_buff, len)), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, + -32 + offsetof(struct stats, bytes)), + /* + * add new map entry using BPF_FUNC_map_update_elem, it takes + * 4 parameters (R1: map_fd, R2: &socket_cookie, R3: &stats) + */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -32), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_update_elem), + BPF_JMP_IMM(BPF_JA, 0, 0, 5), + /* + * pc24-30 update the packet info to a exist data entry, it can + * be done by directly write to pointers instead of using + * BPF_FUNC_map_update_elem helper function + */ + BPF_MOV64_REG(BPF_REG_9, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_1, 1), + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_9, BPF_REG_1, + offsetof(struct stats, packets), 0), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, + offsetof(struct __sk_buff, len)), + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_9, BPF_REG_1, + offsetof(struct stats, bytes), 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, + offsetof(struct __sk_buff, len)), + BPF_EXIT_INSN(), + }; + prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, + ARRAY_SIZE(prog), "GPL", 0, + log_buf, sizeof(log_buf)); + if (prog_fd < 0) + error(1, errno, "failed to load prog\n%s\n", log_buf); +} + +static void prog_attach_iptables(char *file) +{ + int ret; + char rules[100]; + + if (bpf_obj_pin(prog_fd, file)) + error(1, errno, "bpf_obj_pin"); + sprintf(rules, "iptables -A INPUT -m bpf --object-pinned %s -j ACCEPT", + file); + ret = system(rules); + if (ret < 0) { + printf("iptable rule update failed: %d/n", WEXITSTATUS(ret)); + exit(1); + } +} + +static void print_table(void) +{ + struct stats curEntry; + uint32_t curN = UINT32_MAX; + uint32_t nextN, res; + + while (bpf_map_get_next_key(map_fd, &curN, &nextN) > -1) { + curN = nextN; + res = bpf_map_lookup_elem(map_fd, &curN, &curEntry); + if (res < 0) { + error(1, errno, "fail to get entry value of Key: %u\n", + curN); + } else { + printf("cookie: %u, uid: 0x%x, Pakcet Count: %lu," + " Bytes Count: %lu\n", curN, curEntry.uid, + curEntry.packets, curEntry.bytes); + } + } +} + +int main(int argc, char *argv[]) +{ + if (argc > 2) { + printf("Too many argument provided\n"); + return 1; + } else if (argc < 2) { + printf("Usage: %s bpfObjName\n", argv[0]); + return 1; + } + + maps_create(); + prog_load(); + prog_attach_iptables(argv[1]); + + while (true) { + print_table(); + printf("\n"); + sleep(1); + }; + + return 0; +} + + + + + + diff --git a/samples/bpf/run_cookie_uid_helper_example.sh b/samples/bpf/run_cookie_uid_helper_example.sh new file mode 100755 index 0000000..a7a6208 --- /dev/null +++ b/samples/bpf/run_cookie_uid_helper_example.sh @@ -0,0 +1,14 @@ +#!/bin/bash +local_dir="$(pwd)" +root_dir=$local_dir/../.. +mnt_dir=/sys/fs/bpf + +on_exit() { + iptables -D INPUT -m bpf --object-pinned ${mnt_dir}/bpf_prog -j ACCEPT + umount ${mnt_dir} +} + +trap on_exit EXIT +mkdir -p ${mnt_dir} +mount -t bpf bpf ${mnt_dir} +./perSocketStats_example ${mnt_dir}/bpf_prog -- 2.7.4 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v3 3/3] A Sample of using socket cookie and uid for traffic monitoring 2017-02-23 4:44 ` [PATCH net-next v3 3/3] A Sample of using socket cookie and uid for traffic monitoring Chenbo Feng @ 2017-02-23 17:20 ` Willem de Bruijn 0 siblings, 0 replies; 8+ messages in thread From: Willem de Bruijn @ 2017-02-23 17:20 UTC (permalink / raw) To: Chenbo Feng; +Cc: Network Development, Chenbo Feng > +/* This test is a demo of using get_socket_uid and get_socket_cookie > + * helper function to do per socket based network traffic monitoring. > + * It requires iptable version higher then 1.6.1. to load pined eBPF > + * program into the xt_bpf match. iptable -> iptables (everywhre pined -> pinned > + * Clean up: if using shell script, the script file will delete the iptables > + * rule and unmount the bpf program when exit. Else the iptables rule need > + * to be deleted using: > + * iptables -D INPUT -m bpf --object-pinned ${mnt_dir}/bpf_prog -j ACCEPT this is already in the wrapper script > + struct bpf_insn prog[] = { > + /* > + * it for future usage. value stored in R6 to R10 will not be > + * reset after a bpf helper function call. garbled comment > + */ > + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), > + /* > + * pc1: BPF_FUNC_get_socket_cookie takes one parameter, > + * R1: sk_buff > + */ > + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, > + BPF_FUNC_get_socket_cookie), > + /* pc2-4: save &socketCookie to r7 for future usage*/ > + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), > + BPF_MOV64_REG(BPF_REG_7, BPF_REG_10), > + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8), > + /* > + * pc5-8: set up the registers for BPF_FUNC_map_lookup_elem, > + * it takes two parameters (R1: map_fd, R2: &socket_cookie) > + */ > + BPF_LD_MAP_FD(BPF_REG_1, map_fd), > + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), > + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, > + BPF_FUNC_map_lookup_elem), > + /* > + * pc9. if r0 != 0x0, go to pc+14, since we have the cookie > + * stored already > + * Otherwise do pc10-22 to setup a new data entry. > + */ > + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 14), > + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), > + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, > + BPF_FUNC_get_socket_uid), > + /* > + * Place a struct stats in the R10 stack and sequentially > + * place the member value into the memory. Packets value > + * is set by directly place a IMM value 1 into the stack. > + */ > + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, > + -32 + offsetof(struct stats, uid)), > + BPF_ST_MEM(BPF_DW, BPF_REG_10, > + -32 + offsetof(struct stats, packets), 1), > + /* > + * __sk_buff is a special struct used for eBPF program to > + * directly access some sk_buff field. > + */ > + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, > + offsetof(struct __sk_buff, len)), > + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, > + -32 + offsetof(struct stats, bytes)), > + /* > + * add new map entry using BPF_FUNC_map_update_elem, it takes > + * 4 parameters (R1: map_fd, R2: &socket_cookie, R3: &stats) R4: flags > + */ > + BPF_LD_MAP_FD(BPF_REG_1, map_fd), > + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), > + BPF_MOV64_REG(BPF_REG_3, BPF_REG_10), > + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -32), > + BPF_MOV64_IMM(BPF_REG_4, 0), > + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, > + BPF_FUNC_map_update_elem), > + BPF_JMP_IMM(BPF_JA, 0, 0, 5), > + /* > + * pc24-30 update the packet info to a exist data entry, it can > + * be done by directly write to pointers instead of using > + * BPF_FUNC_map_update_elem helper function > + */ > + BPF_MOV64_REG(BPF_REG_9, BPF_REG_0), > + BPF_MOV64_IMM(BPF_REG_1, 1), > + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_9, BPF_REG_1, > + offsetof(struct stats, packets), 0), BPF_STX_XADD here and below > + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, > + offsetof(struct __sk_buff, len)), > + BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_9, BPF_REG_1, > + offsetof(struct stats, bytes), 0), > + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, > + offsetof(struct __sk_buff, len)), No requirement to return len. It goes unused, can just return non-zero immediate. > +static void prog_attach_iptables(char *file) > +{ > + int ret; > + char rules[100]; > + > + if (bpf_obj_pin(prog_fd, file)) > + error(1, errno, "bpf_obj_pin"); > + sprintf(rules, "iptables -A INPUT -m bpf --object-pinned %s -j ACCEPT", > + file); this is a test, but still: unsafe to pass user input unchecked to sprintf > + printf("cookie: %u, uid: 0x%x, Pakcet Count: %lu," Packet > diff --git a/samples/bpf/run_cookie_uid_helper_example.sh b/samples/bpf/run_cookie_uid_helper_example.sh > new file mode 100755 > index 0000000..a7a6208 > --- /dev/null > +++ b/samples/bpf/run_cookie_uid_helper_example.sh > @@ -0,0 +1,14 @@ > +#!/bin/bash > +local_dir="$(pwd)" > +root_dir=$local_dir/../.. > +mnt_dir=/sys/fs/bpf Don't modify state outside the test environment. Perhaps use a temporary dir: mnt_dir=$(mktemp -d --tmp) and remove it in on_exit ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2017-02-23 17:21 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-02-23 4:42 [PATCH net-next v3 0/3] net: core: Two Helper function about socket information Chenbo Feng 2017-02-23 4:42 ` [PATCH net-next v3 1/3] Add a helper function to get socket cookie in eBPF Chenbo Feng 2017-02-23 4:42 ` [PATCH net-next v3 2/3] Add a eBPF helper function to retrieve socket uid Chenbo Feng 2017-02-23 17:03 ` Willem de Bruijn 2017-02-23 4:42 ` [PATCH net-next v3 3/3] A Sample of using socket cookie and uid for traffic monitoring Chenbo Feng 2017-02-23 17:00 ` [PATCH net-next v3 0/3] net: core: Two Helper function about socket information Willem de Bruijn -- strict thread matches above, loose matches on Subject: below -- 2017-02-23 4:44 Chenbo Feng 2017-02-23 4:44 ` [PATCH net-next v3 3/3] A Sample of using socket cookie and uid for traffic monitoring Chenbo Feng 2017-02-23 17:20 ` Willem de Bruijn
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).