0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031 #define _GNU_SOURCE
0032
0033 #define offsetof(type, member) __builtin_offsetof(type, member)
0034 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
0035
0036 #include <arpa/inet.h>
0037 #include <errno.h>
0038 #include <error.h>
0039 #include <limits.h>
0040 #include <linux/bpf.h>
0041 #include <linux/if_ether.h>
0042 #include <net/if.h>
0043 #include <signal.h>
0044 #include <stdbool.h>
0045 #include <stdint.h>
0046 #include <stdio.h>
0047 #include <stdlib.h>
0048 #include <string.h>
0049 #include <sys/socket.h>
0050 #include <sys/stat.h>
0051 #include <sys/types.h>
0052 #include <unistd.h>
0053 #include <bpf/bpf.h>
0054 #include "bpf_insn.h"
0055
0056 #define PORT 8888
0057
0058 struct stats {
0059 uint32_t uid;
0060 uint64_t packets;
0061 uint64_t bytes;
0062 };
0063
0064 static int map_fd, prog_fd;
0065
0066 static bool test_finish;
0067
0068 static void maps_create(void)
0069 {
0070 map_fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(uint32_t),
0071 sizeof(struct stats), 100, NULL);
0072 if (map_fd < 0)
0073 error(1, errno, "map create failed!\n");
0074 }
0075
0076 static void prog_load(void)
0077 {
0078 static char log_buf[1 << 16];
0079
0080 struct bpf_insn prog[] = {
0081
0082
0083
0084
0085 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0086
0087
0088
0089
0090 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
0091 BPF_FUNC_get_socket_cookie),
0092
0093 BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
0094 BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
0095 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
0096
0097
0098
0099
0100 BPF_LD_MAP_FD(BPF_REG_1, map_fd),
0101 BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
0102 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
0103 BPF_FUNC_map_lookup_elem),
0104
0105
0106
0107
0108
0109 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 14),
0110 BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
0111 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
0112 BPF_FUNC_get_socket_uid),
0113
0114
0115
0116
0117
0118 BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0,
0119 -32 + (__s16)offsetof(struct stats, uid)),
0120 BPF_ST_MEM(BPF_DW, BPF_REG_10,
0121 -32 + (__s16)offsetof(struct stats, packets), 1),
0122
0123
0124
0125
0126 BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6,
0127 offsetof(struct __sk_buff, len)),
0128 BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1,
0129 -32 + (__s16)offsetof(struct stats, bytes)),
0130
0131
0132
0133
0134
0135 BPF_LD_MAP_FD(BPF_REG_1, map_fd),
0136 BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
0137 BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
0138 BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -32),
0139 BPF_MOV64_IMM(BPF_REG_4, 0),
0140 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
0141 BPF_FUNC_map_update_elem),
0142 BPF_JMP_IMM(BPF_JA, 0, 0, 5),
0143
0144
0145
0146
0147
0148 BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),
0149 BPF_MOV64_IMM(BPF_REG_1, 1),
0150 BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_9, BPF_REG_1,
0151 offsetof(struct stats, packets)),
0152 BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6,
0153 offsetof(struct __sk_buff, len)),
0154 BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_9, BPF_REG_1,
0155 offsetof(struct stats, bytes)),
0156 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
0157 offsetof(struct __sk_buff, len)),
0158 BPF_EXIT_INSN(),
0159 };
0160 LIBBPF_OPTS(bpf_prog_load_opts, opts,
0161 .log_buf = log_buf,
0162 .log_size = sizeof(log_buf),
0163 );
0164
0165 prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
0166 prog, ARRAY_SIZE(prog), &opts);
0167 if (prog_fd < 0)
0168 error(1, errno, "failed to load prog\n%s\n", log_buf);
0169 }
0170
0171 static void prog_attach_iptables(char *file)
0172 {
0173 int ret;
0174 char rules[256];
0175
0176 if (bpf_obj_pin(prog_fd, file))
0177 error(1, errno, "bpf_obj_pin");
0178 if (strlen(file) > 50) {
0179 printf("file path too long: %s\n", file);
0180 exit(1);
0181 }
0182 ret = snprintf(rules, sizeof(rules),
0183 "iptables -A OUTPUT -m bpf --object-pinned %s -j ACCEPT",
0184 file);
0185 if (ret < 0 || ret >= sizeof(rules)) {
0186 printf("error constructing iptables command\n");
0187 exit(1);
0188 }
0189 ret = system(rules);
0190 if (ret < 0) {
0191 printf("iptables rule update failed: %d/n", WEXITSTATUS(ret));
0192 exit(1);
0193 }
0194 }
0195
0196 static void print_table(void)
0197 {
0198 struct stats curEntry;
0199 uint32_t curN = UINT32_MAX;
0200 uint32_t nextN;
0201 int res;
0202
0203 while (bpf_map_get_next_key(map_fd, &curN, &nextN) > -1) {
0204 curN = nextN;
0205 res = bpf_map_lookup_elem(map_fd, &curN, &curEntry);
0206 if (res < 0) {
0207 error(1, errno, "fail to get entry value of Key: %u\n",
0208 curN);
0209 } else {
0210 printf("cookie: %u, uid: 0x%x, Packet Count: %lu,"
0211 " Bytes Count: %lu\n", curN, curEntry.uid,
0212 curEntry.packets, curEntry.bytes);
0213 }
0214 }
0215 }
0216
0217 static void udp_client(void)
0218 {
0219 struct sockaddr_in si_other = {0};
0220 struct sockaddr_in si_me = {0};
0221 struct stats dataEntry;
0222 int s_rcv, s_send, i, recv_len;
0223 char message = 'a';
0224 char buf;
0225 uint64_t cookie;
0226 int res;
0227 socklen_t cookie_len = sizeof(cookie);
0228 socklen_t slen = sizeof(si_other);
0229
0230 s_rcv = socket(PF_INET, SOCK_DGRAM, 0);
0231 if (s_rcv < 0)
0232 error(1, errno, "rcv socket creat failed!\n");
0233 si_other.sin_family = AF_INET;
0234 si_other.sin_port = htons(PORT);
0235 if (inet_aton("127.0.0.1", &si_other.sin_addr) == 0)
0236 error(1, errno, "inet_aton\n");
0237 if (bind(s_rcv, (struct sockaddr *)&si_other, sizeof(si_other)) == -1)
0238 error(1, errno, "bind\n");
0239 s_send = socket(PF_INET, SOCK_DGRAM, 0);
0240 if (s_send < 0)
0241 error(1, errno, "send socket creat failed!\n");
0242 res = getsockopt(s_send, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len);
0243 if (res < 0)
0244 printf("get cookie failed: %s\n", strerror(errno));
0245 res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry);
0246 if (res != -1)
0247 error(1, errno, "socket stat found while flow not active\n");
0248 for (i = 0; i < 10; i++) {
0249 res = sendto(s_send, &message, sizeof(message), 0,
0250 (struct sockaddr *)&si_other, slen);
0251 if (res == -1)
0252 error(1, errno, "send\n");
0253 if (res != sizeof(message))
0254 error(1, 0, "%uB != %luB\n", res, sizeof(message));
0255 recv_len = recvfrom(s_rcv, &buf, sizeof(buf), 0,
0256 (struct sockaddr *)&si_me, &slen);
0257 if (recv_len < 0)
0258 error(1, errno, "receive\n");
0259 res = memcmp(&(si_other.sin_addr), &(si_me.sin_addr),
0260 sizeof(si_me.sin_addr));
0261 if (res != 0)
0262 error(1, EFAULT, "sender addr error: %d\n", res);
0263 printf("Message received: %c\n", buf);
0264 res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry);
0265 if (res < 0)
0266 error(1, errno, "lookup sk stat failed, cookie: %lu\n",
0267 cookie);
0268 printf("cookie: %lu, uid: 0x%x, Packet Count: %lu,"
0269 " Bytes Count: %lu\n\n", cookie, dataEntry.uid,
0270 dataEntry.packets, dataEntry.bytes);
0271 }
0272 close(s_send);
0273 close(s_rcv);
0274 }
0275
0276 static int usage(void)
0277 {
0278 printf("Usage: ./run_cookie_uid_helper_example.sh"
0279 " bpfObjName -option\n"
0280 " -t traffic monitor test\n"
0281 " -s getsockopt cookie test\n");
0282 return 1;
0283 }
0284
0285 static void finish(int ret)
0286 {
0287 test_finish = true;
0288 }
0289
0290 int main(int argc, char *argv[])
0291 {
0292 int opt;
0293 bool cfg_test_traffic = false;
0294 bool cfg_test_cookie = false;
0295
0296 if (argc != 3)
0297 return usage();
0298 while ((opt = getopt(argc, argv, "ts")) != -1) {
0299 switch (opt) {
0300 case 't':
0301 cfg_test_traffic = true;
0302 break;
0303 case 's':
0304 cfg_test_cookie = true;
0305 break;
0306
0307 default:
0308 printf("unknown option %c\n", opt);
0309 usage();
0310 return -1;
0311 }
0312 }
0313 maps_create();
0314 prog_load();
0315 prog_attach_iptables(argv[2]);
0316 if (cfg_test_traffic) {
0317 if (signal(SIGINT, finish) == SIG_ERR)
0318 error(1, errno, "register SIGINT handler failed");
0319 if (signal(SIGTERM, finish) == SIG_ERR)
0320 error(1, errno, "register SIGTERM handler failed");
0321 while (!test_finish) {
0322 print_table();
0323 printf("\n");
0324 sleep(1);
0325 }
0326 } else if (cfg_test_cookie) {
0327 udp_client();
0328 }
0329 close(prog_fd);
0330 close(map_fd);
0331 return 0;
0332 }