1e04946f5SDaniel T. Lee /*
2e04946f5SDaniel T. Lee * Copyright (c) 2017 Facebook
3e04946f5SDaniel T. Lee *
4e04946f5SDaniel T. Lee * This program is free software; you can redistribute it and/or
5e04946f5SDaniel T. Lee * modify it under the terms of version 2 of the GNU General Public
6e04946f5SDaniel T. Lee * License as published by the Free Software Foundation.
7e04946f5SDaniel T. Lee */
8e04946f5SDaniel T. Lee #define KBUILD_MODNAME "foo"
9e04946f5SDaniel T. Lee #include "vmlinux.h"
10e04946f5SDaniel T. Lee #include <linux/version.h>
11e04946f5SDaniel T. Lee #include <bpf/bpf_helpers.h>
12e04946f5SDaniel T. Lee #include <bpf/bpf_tracing.h>
13e04946f5SDaniel T. Lee #include <bpf/bpf_core_read.h>
14e04946f5SDaniel T. Lee
15e04946f5SDaniel T. Lee #define MAX_NR_PORTS 65536
16e04946f5SDaniel T. Lee
17e04946f5SDaniel T. Lee #define EINVAL 22
18e04946f5SDaniel T. Lee #define ENOENT 2
19e04946f5SDaniel T. Lee
20e04946f5SDaniel T. Lee /* map #0 */
21e04946f5SDaniel T. Lee struct inner_a {
22e04946f5SDaniel T. Lee __uint(type, BPF_MAP_TYPE_ARRAY);
23e04946f5SDaniel T. Lee __type(key, u32);
24e04946f5SDaniel T. Lee __type(value, int);
25e04946f5SDaniel T. Lee __uint(max_entries, MAX_NR_PORTS);
26e04946f5SDaniel T. Lee } port_a SEC(".maps");
27e04946f5SDaniel T. Lee
28e04946f5SDaniel T. Lee /* map #1 */
29e04946f5SDaniel T. Lee struct inner_h {
30e04946f5SDaniel T. Lee __uint(type, BPF_MAP_TYPE_HASH);
31e04946f5SDaniel T. Lee __type(key, u32);
32e04946f5SDaniel T. Lee __type(value, int);
33e04946f5SDaniel T. Lee __uint(max_entries, 1);
34e04946f5SDaniel T. Lee } port_h SEC(".maps");
35e04946f5SDaniel T. Lee
36e04946f5SDaniel T. Lee /* map #2 */
37e04946f5SDaniel T. Lee struct {
38e04946f5SDaniel T. Lee __uint(type, BPF_MAP_TYPE_HASH);
39e04946f5SDaniel T. Lee __type(key, u32);
40e04946f5SDaniel T. Lee __type(value, int);
41e04946f5SDaniel T. Lee __uint(max_entries, 1);
42e04946f5SDaniel T. Lee } reg_result_h SEC(".maps");
43e04946f5SDaniel T. Lee
44e04946f5SDaniel T. Lee /* map #3 */
45e04946f5SDaniel T. Lee struct {
46e04946f5SDaniel T. Lee __uint(type, BPF_MAP_TYPE_HASH);
47e04946f5SDaniel T. Lee __type(key, u32);
48e04946f5SDaniel T. Lee __type(value, int);
49e04946f5SDaniel T. Lee __uint(max_entries, 1);
50e04946f5SDaniel T. Lee } inline_result_h SEC(".maps");
51e04946f5SDaniel T. Lee
52e04946f5SDaniel T. Lee /* map #4 */ /* Test case #0 */
53e04946f5SDaniel T. Lee struct {
54e04946f5SDaniel T. Lee __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
55e04946f5SDaniel T. Lee __uint(max_entries, MAX_NR_PORTS);
56e04946f5SDaniel T. Lee __uint(key_size, sizeof(u32));
57e04946f5SDaniel T. Lee __array(values, struct inner_a); /* use inner_a as inner map */
58e04946f5SDaniel T. Lee } a_of_port_a SEC(".maps");
59e04946f5SDaniel T. Lee
60e04946f5SDaniel T. Lee /* map #5 */ /* Test case #1 */
61e04946f5SDaniel T. Lee struct {
62e04946f5SDaniel T. Lee __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
63e04946f5SDaniel T. Lee __uint(max_entries, 1);
64e04946f5SDaniel T. Lee __uint(key_size, sizeof(u32));
65e04946f5SDaniel T. Lee __array(values, struct inner_a); /* use inner_a as inner map */
66e04946f5SDaniel T. Lee } h_of_port_a SEC(".maps");
67e04946f5SDaniel T. Lee
68e04946f5SDaniel T. Lee /* map #6 */ /* Test case #2 */
69e04946f5SDaniel T. Lee struct {
70e04946f5SDaniel T. Lee __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
71e04946f5SDaniel T. Lee __uint(max_entries, 1);
72e04946f5SDaniel T. Lee __uint(key_size, sizeof(u32));
73e04946f5SDaniel T. Lee __array(values, struct inner_h); /* use inner_h as inner map */
74e04946f5SDaniel T. Lee } h_of_port_h SEC(".maps");
75e04946f5SDaniel T. Lee
do_reg_lookup(void * inner_map,u32 port)76e04946f5SDaniel T. Lee static __always_inline int do_reg_lookup(void *inner_map, u32 port)
77e04946f5SDaniel T. Lee {
78e04946f5SDaniel T. Lee int *result;
79e04946f5SDaniel T. Lee
80e04946f5SDaniel T. Lee result = bpf_map_lookup_elem(inner_map, &port);
81e04946f5SDaniel T. Lee return result ? *result : -ENOENT;
82e04946f5SDaniel T. Lee }
83e04946f5SDaniel T. Lee
do_inline_array_lookup(void * inner_map,u32 port)84e04946f5SDaniel T. Lee static __always_inline int do_inline_array_lookup(void *inner_map, u32 port)
85e04946f5SDaniel T. Lee {
86e04946f5SDaniel T. Lee int *result;
87e04946f5SDaniel T. Lee
88e04946f5SDaniel T. Lee if (inner_map != &port_a)
89e04946f5SDaniel T. Lee return -EINVAL;
90e04946f5SDaniel T. Lee
91e04946f5SDaniel T. Lee result = bpf_map_lookup_elem(&port_a, &port);
92e04946f5SDaniel T. Lee return result ? *result : -ENOENT;
93e04946f5SDaniel T. Lee }
94e04946f5SDaniel T. Lee
do_inline_hash_lookup(void * inner_map,u32 port)95e04946f5SDaniel T. Lee static __always_inline int do_inline_hash_lookup(void *inner_map, u32 port)
96e04946f5SDaniel T. Lee {
97e04946f5SDaniel T. Lee int *result;
98e04946f5SDaniel T. Lee
99e04946f5SDaniel T. Lee if (inner_map != &port_h)
100e04946f5SDaniel T. Lee return -EINVAL;
101e04946f5SDaniel T. Lee
102e04946f5SDaniel T. Lee result = bpf_map_lookup_elem(&port_h, &port);
103e04946f5SDaniel T. Lee return result ? *result : -ENOENT;
104e04946f5SDaniel T. Lee }
105e04946f5SDaniel T. Lee
106*8dc80551SDaniel T. Lee SEC("ksyscall/connect")
BPF_KSYSCALL(trace_sys_connect,unsigned int fd,struct sockaddr_in6 * in6,int addrlen)107*8dc80551SDaniel T. Lee int BPF_KSYSCALL(trace_sys_connect, unsigned int fd, struct sockaddr_in6 *in6, int addrlen)
108e04946f5SDaniel T. Lee {
109e04946f5SDaniel T. Lee u16 test_case, port, dst6[8];
110*8dc80551SDaniel T. Lee int ret, inline_ret, ret_key = 0;
111e04946f5SDaniel T. Lee u32 port_key;
112e04946f5SDaniel T. Lee void *outer_map, *inner_map;
113e04946f5SDaniel T. Lee bool inline_hash = false;
114e04946f5SDaniel T. Lee
115e04946f5SDaniel T. Lee if (addrlen != sizeof(*in6))
116e04946f5SDaniel T. Lee return 0;
117e04946f5SDaniel T. Lee
118e04946f5SDaniel T. Lee ret = bpf_probe_read_user(dst6, sizeof(dst6), &in6->sin6_addr);
119e04946f5SDaniel T. Lee if (ret) {
120e04946f5SDaniel T. Lee inline_ret = ret;
121e04946f5SDaniel T. Lee goto done;
122e04946f5SDaniel T. Lee }
123e04946f5SDaniel T. Lee
124e04946f5SDaniel T. Lee if (dst6[0] != 0xdead || dst6[1] != 0xbeef)
125e04946f5SDaniel T. Lee return 0;
126e04946f5SDaniel T. Lee
127e04946f5SDaniel T. Lee test_case = dst6[7];
128e04946f5SDaniel T. Lee
129e04946f5SDaniel T. Lee ret = bpf_probe_read_user(&port, sizeof(port), &in6->sin6_port);
130e04946f5SDaniel T. Lee if (ret) {
131e04946f5SDaniel T. Lee inline_ret = ret;
132e04946f5SDaniel T. Lee goto done;
133e04946f5SDaniel T. Lee }
134e04946f5SDaniel T. Lee
135e04946f5SDaniel T. Lee port_key = port;
136e04946f5SDaniel T. Lee
137e04946f5SDaniel T. Lee ret = -ENOENT;
138e04946f5SDaniel T. Lee if (test_case == 0) {
139e04946f5SDaniel T. Lee outer_map = &a_of_port_a;
140e04946f5SDaniel T. Lee } else if (test_case == 1) {
141e04946f5SDaniel T. Lee outer_map = &h_of_port_a;
142e04946f5SDaniel T. Lee } else if (test_case == 2) {
143e04946f5SDaniel T. Lee outer_map = &h_of_port_h;
144e04946f5SDaniel T. Lee } else {
145e04946f5SDaniel T. Lee ret = __LINE__;
146e04946f5SDaniel T. Lee inline_ret = ret;
147e04946f5SDaniel T. Lee goto done;
148e04946f5SDaniel T. Lee }
149e04946f5SDaniel T. Lee
150e04946f5SDaniel T. Lee inner_map = bpf_map_lookup_elem(outer_map, &port_key);
151e04946f5SDaniel T. Lee if (!inner_map) {
152e04946f5SDaniel T. Lee ret = __LINE__;
153e04946f5SDaniel T. Lee inline_ret = ret;
154e04946f5SDaniel T. Lee goto done;
155e04946f5SDaniel T. Lee }
156e04946f5SDaniel T. Lee
157e04946f5SDaniel T. Lee ret = do_reg_lookup(inner_map, port_key);
158e04946f5SDaniel T. Lee
159e04946f5SDaniel T. Lee if (test_case == 0 || test_case == 1)
160e04946f5SDaniel T. Lee inline_ret = do_inline_array_lookup(inner_map, port_key);
161e04946f5SDaniel T. Lee else
162e04946f5SDaniel T. Lee inline_ret = do_inline_hash_lookup(inner_map, port_key);
163e04946f5SDaniel T. Lee
164e04946f5SDaniel T. Lee done:
165e04946f5SDaniel T. Lee bpf_map_update_elem(®_result_h, &ret_key, &ret, BPF_ANY);
166e04946f5SDaniel T. Lee bpf_map_update_elem(&inline_result_h, &ret_key, &inline_ret, BPF_ANY);
167e04946f5SDaniel T. Lee
168e04946f5SDaniel T. Lee return 0;
169e04946f5SDaniel T. Lee }
170e04946f5SDaniel T. Lee
171e04946f5SDaniel T. Lee char _license[] SEC("license") = "GPL";
172e04946f5SDaniel T. Lee u32 _version SEC("version") = LINUX_VERSION_CODE;
173