net: core: Two Helper function about socket information
Subsystems (5)
v3 cover net: core: Two Helper function about socket information
From: Chenbo Feng <[email protected]>
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
Signature · 1 line
2.7.4
PATCH v3 1/3 Add a helper function to get socket cookie in eBPF
From: Chenbo Feng <[email protected]>
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.
--- 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(-)
include/linux/sock_diag.h +1 -0
@@ -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);
include/uapi/linux/bpf.h +8 -1
@@ -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
net/core/filter.c +17 -0
@@ -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); }
net/core/sock_diag.c +1 -1
@@ -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);
tools/include/uapi/linux/bpf.h +2 -1
@@ -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
Signature · 1 line
2.7.4
PATCH v3 2/3 Add a eBPF helper function to retrieve socket uid
In reply to Chenbo FengFrom: Chenbo Feng <[email protected]>
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.
--- 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(-)
include/uapi/linux/bpf.h +8 -1
@@ -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
net/core/filter.c +19 -0
@@ -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); }
tools/include/uapi/linux/bpf.h +2 -1
@@ -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
Signature · 1 line
2.7.4
Re: [PATCH net-next v3 2/3] Add a eBPF helper function to retrieve socket uid
net/core/filter.c static const struct bpf_func_proto bpf_get_socket_cookie_proto = { · line 2618
Quoted 4 lines
net/core/filter.c +4
-0+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
PATCH v3 3/3 A Sample of using socket cookie and uid for traffic monitoring
In reply to Chenbo FengFrom: Chenbo Feng <[email protected]>
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.
--- 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
samples/bpf/cookie_uid_helper_example.c new file +225 -0
@@ -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 ; +} + + + + + +
samples/bpf/run_cookie_uid_helper_example.sh new file +14 -0
@@ -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
Signature · 1 line
2.7.4
Quoted 6 lines · Chenbo Feng
From: Chenbo Feng <[email protected]>
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_buffis 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.