Path: blob/master/samples/bpf/cookie_uid_helper_example.c
25924 views
/* This test is a demo of using get_socket_uid and get_socket_cookie1* helper function to do per socket based network traffic monitoring.2* It requires iptables version higher then 1.6.1. to load pinned eBPF3* program into the xt_bpf match.4*5* TEST:6* ./run_cookie_uid_helper_example.sh -option7* option:8* -t: do traffic monitoring test, the program will continuously9* print out network traffic happens after program started A sample10* output is shown below:11*12* cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 1105813* cookie: 132, uid: 0x0, Pakcet Count: 2, Bytes Count: 28614* cookie: 812, uid: 0x3e8, Pakcet Count: 3, Bytes Count: 172615* cookie: 802, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 10416* cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 1105817* cookie: 831, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 10418* cookie: 0, uid: 0x0, Pakcet Count: 6, Bytes Count: 71219* cookie: 880, uid: 0xfffe, Pakcet Count: 1, Bytes Count: 7020*21* -s: do getsockopt SO_COOKIE test, the program will set up a pair of22* UDP sockets and send packets between them. And read out the traffic data23* directly from the ebpf map based on the socket cookie.24*25* Clean up: if using shell script, the script file will delete the iptables26* rule and unmount the bpf program when exit. Else the iptables rule need27* to be deleted by hand, see run_cookie_uid_helper_example.sh for detail.28*/2930#define _GNU_SOURCE3132#define offsetof(type, member) __builtin_offsetof(type, member)33#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))3435#include <arpa/inet.h>36#include <errno.h>37#include <error.h>38#include <limits.h>39#include <linux/bpf.h>40#include <linux/if_ether.h>41#include <net/if.h>42#include <signal.h>43#include <stdbool.h>44#include <stdint.h>45#include <stdio.h>46#include <stdlib.h>47#include <string.h>48#include <sys/socket.h>49#include <sys/stat.h>50#include <sys/types.h>51#include <unistd.h>52#include <bpf/bpf.h>53#include "bpf_insn.h"5455#define PORT 88885657struct stats {58uint32_t uid;59uint64_t packets;60uint64_t bytes;61};6263static int map_fd, prog_fd;6465static bool test_finish;6667static void maps_create(void)68{69map_fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(uint32_t),70sizeof(struct stats), 100, NULL);71if (map_fd < 0)72error(1, errno, "map create failed!\n");73}7475static void prog_load(void)76{77static char log_buf[1 << 16];7879struct bpf_insn prog[] = {80/*81* Save sk_buff for future usage. value stored in R6 to R10 will82* not be reset after a bpf helper function call.83*/84BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),85/*86* pc1: BPF_FUNC_get_socket_cookie takes one parameter,87* R1: sk_buff88*/89BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,90BPF_FUNC_get_socket_cookie),91/* pc2-4: save &socketCookie to r7 for future usage*/92BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),93BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),94BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),95/*96* pc5-8: set up the registers for BPF_FUNC_map_lookup_elem,97* it takes two parameters (R1: map_fd, R2: &socket_cookie)98*/99BPF_LD_MAP_FD(BPF_REG_1, map_fd),100BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),101BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,102BPF_FUNC_map_lookup_elem),103/*104* pc9. if r0 != 0x0, go to pc+14, since we have the cookie105* stored already106* Otherwise do pc10-22 to setup a new data entry.107*/108BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 14),109BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),110BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,111BPF_FUNC_get_socket_uid),112/*113* Place a struct stats in the R10 stack and sequentially114* place the member value into the memory. Packets value115* is set by directly place a IMM value 1 into the stack.116*/117BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0,118-32 + (__s16)offsetof(struct stats, uid)),119BPF_ST_MEM(BPF_DW, BPF_REG_10,120-32 + (__s16)offsetof(struct stats, packets), 1),121/*122* __sk_buff is a special struct used for eBPF program to123* directly access some sk_buff field.124*/125BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6,126offsetof(struct __sk_buff, len)),127BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1,128-32 + (__s16)offsetof(struct stats, bytes)),129/*130* add new map entry using BPF_FUNC_map_update_elem, it takes131* 4 parameters (R1: map_fd, R2: &socket_cookie, R3: &stats,132* R4: flags)133*/134BPF_LD_MAP_FD(BPF_REG_1, map_fd),135BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),136BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),137BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -32),138BPF_MOV64_IMM(BPF_REG_4, 0),139BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,140BPF_FUNC_map_update_elem),141BPF_JMP_IMM(BPF_JA, 0, 0, 5),142/*143* pc24-30 update the packet info to a exist data entry, it can144* be done by directly write to pointers instead of using145* BPF_FUNC_map_update_elem helper function146*/147BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),148BPF_MOV64_IMM(BPF_REG_1, 1),149BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_9, BPF_REG_1,150offsetof(struct stats, packets)),151BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6,152offsetof(struct __sk_buff, len)),153BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_9, BPF_REG_1,154offsetof(struct stats, bytes)),155BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,156offsetof(struct __sk_buff, len)),157BPF_EXIT_INSN(),158};159LIBBPF_OPTS(bpf_prog_load_opts, opts,160.log_buf = log_buf,161.log_size = sizeof(log_buf),162);163164prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",165prog, ARRAY_SIZE(prog), &opts);166if (prog_fd < 0)167error(1, errno, "failed to load prog\n%s\n", log_buf);168}169170static void prog_attach_iptables(char *file)171{172int ret;173char rules[256];174175if (bpf_obj_pin(prog_fd, file))176error(1, errno, "bpf_obj_pin");177if (strlen(file) > 50) {178printf("file path too long: %s\n", file);179exit(1);180}181ret = snprintf(rules, sizeof(rules),182"iptables -A OUTPUT -m bpf --object-pinned %s -j ACCEPT",183file);184if (ret < 0 || ret >= sizeof(rules)) {185printf("error constructing iptables command\n");186exit(1);187}188ret = system(rules);189if (ret < 0) {190printf("iptables rule update failed: %d/n", WEXITSTATUS(ret));191exit(1);192}193}194195static void print_table(void)196{197struct stats curEntry;198uint32_t curN = UINT32_MAX;199uint32_t nextN;200int res;201202while (bpf_map_get_next_key(map_fd, &curN, &nextN) > -1) {203curN = nextN;204res = bpf_map_lookup_elem(map_fd, &curN, &curEntry);205if (res < 0) {206error(1, errno, "fail to get entry value of Key: %u\n",207curN);208} else {209printf("cookie: %u, uid: 0x%x, Packet Count: %lu,"210" Bytes Count: %lu\n", curN, curEntry.uid,211curEntry.packets, curEntry.bytes);212}213}214}215216static void udp_client(void)217{218struct sockaddr_in si_other = {0};219struct sockaddr_in si_me = {0};220struct stats dataEntry;221int s_rcv, s_send, i, recv_len;222char message = 'a';223char buf;224uint64_t cookie;225int res;226socklen_t cookie_len = sizeof(cookie);227socklen_t slen = sizeof(si_other);228229s_rcv = socket(PF_INET, SOCK_DGRAM, 0);230if (s_rcv < 0)231error(1, errno, "rcv socket creat failed!\n");232si_other.sin_family = AF_INET;233si_other.sin_port = htons(PORT);234if (inet_aton("127.0.0.1", &si_other.sin_addr) == 0)235error(1, errno, "inet_aton\n");236if (bind(s_rcv, (struct sockaddr *)&si_other, sizeof(si_other)) == -1)237error(1, errno, "bind\n");238s_send = socket(PF_INET, SOCK_DGRAM, 0);239if (s_send < 0)240error(1, errno, "send socket creat failed!\n");241res = getsockopt(s_send, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len);242if (res < 0)243printf("get cookie failed: %s\n", strerror(errno));244res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry);245if (res != -1)246error(1, errno, "socket stat found while flow not active\n");247for (i = 0; i < 10; i++) {248res = sendto(s_send, &message, sizeof(message), 0,249(struct sockaddr *)&si_other, slen);250if (res == -1)251error(1, errno, "send\n");252if (res != sizeof(message))253error(1, 0, "%uB != %luB\n", res, sizeof(message));254recv_len = recvfrom(s_rcv, &buf, sizeof(buf), 0,255(struct sockaddr *)&si_me, &slen);256if (recv_len < 0)257error(1, errno, "receive\n");258res = memcmp(&(si_other.sin_addr), &(si_me.sin_addr),259sizeof(si_me.sin_addr));260if (res != 0)261error(1, EFAULT, "sender addr error: %d\n", res);262printf("Message received: %c\n", buf);263res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry);264if (res < 0)265error(1, errno, "lookup sk stat failed, cookie: %lu\n",266cookie);267printf("cookie: %lu, uid: 0x%x, Packet Count: %lu,"268" Bytes Count: %lu\n\n", cookie, dataEntry.uid,269dataEntry.packets, dataEntry.bytes);270}271close(s_send);272close(s_rcv);273}274275static int usage(void)276{277printf("Usage: ./run_cookie_uid_helper_example.sh"278" bpfObjName -option\n"279" -t traffic monitor test\n"280" -s getsockopt cookie test\n");281return 1;282}283284static void finish(int ret)285{286test_finish = true;287}288289int main(int argc, char *argv[])290{291int opt;292bool cfg_test_traffic = false;293bool cfg_test_cookie = false;294295if (argc != 3)296return usage();297while ((opt = getopt(argc, argv, "ts")) != -1) {298switch (opt) {299case 't':300cfg_test_traffic = true;301break;302case 's':303cfg_test_cookie = true;304break;305306default:307printf("unknown option %c\n", opt);308usage();309return -1;310}311}312maps_create();313prog_load();314prog_attach_iptables(argv[2]);315if (cfg_test_traffic) {316if (signal(SIGINT, finish) == SIG_ERR)317error(1, errno, "register SIGINT handler failed");318if (signal(SIGTERM, finish) == SIG_ERR)319error(1, errno, "register SIGTERM handler failed");320while (!test_finish) {321print_table();322printf("\n");323sleep(1);324}325} else if (cfg_test_cookie) {326udp_client();327}328close(prog_fd);329close(map_fd);330return 0;331}332333334