162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2017 Facebook
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or
562306a36Sopenharmony_ci * modify it under the terms of version 2 of the GNU General Public
662306a36Sopenharmony_ci * License as published by the Free Software Foundation.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#define KBUILD_MODNAME "foo"
962306a36Sopenharmony_ci#include "vmlinux.h"
1062306a36Sopenharmony_ci#include <linux/version.h>
1162306a36Sopenharmony_ci#include <bpf/bpf_helpers.h>
1262306a36Sopenharmony_ci#include <bpf/bpf_tracing.h>
1362306a36Sopenharmony_ci#include <bpf/bpf_core_read.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define MAX_NR_PORTS 65536
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define EINVAL 22
1862306a36Sopenharmony_ci#define ENOENT 2
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* map #0 */
2162306a36Sopenharmony_cistruct inner_a {
2262306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_ARRAY);
2362306a36Sopenharmony_ci	__type(key, u32);
2462306a36Sopenharmony_ci	__type(value, int);
2562306a36Sopenharmony_ci	__uint(max_entries, MAX_NR_PORTS);
2662306a36Sopenharmony_ci} port_a SEC(".maps");
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* map #1 */
2962306a36Sopenharmony_cistruct inner_h {
3062306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH);
3162306a36Sopenharmony_ci	__type(key, u32);
3262306a36Sopenharmony_ci	__type(value, int);
3362306a36Sopenharmony_ci	__uint(max_entries, 1);
3462306a36Sopenharmony_ci} port_h SEC(".maps");
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* map #2 */
3762306a36Sopenharmony_cistruct {
3862306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH);
3962306a36Sopenharmony_ci	__type(key, u32);
4062306a36Sopenharmony_ci	__type(value, int);
4162306a36Sopenharmony_ci	__uint(max_entries, 1);
4262306a36Sopenharmony_ci} reg_result_h SEC(".maps");
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* map #3 */
4562306a36Sopenharmony_cistruct {
4662306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH);
4762306a36Sopenharmony_ci	__type(key, u32);
4862306a36Sopenharmony_ci	__type(value, int);
4962306a36Sopenharmony_ci	__uint(max_entries, 1);
5062306a36Sopenharmony_ci} inline_result_h SEC(".maps");
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* map #4 */ /* Test case #0 */
5362306a36Sopenharmony_cistruct {
5462306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
5562306a36Sopenharmony_ci	__uint(max_entries, MAX_NR_PORTS);
5662306a36Sopenharmony_ci	__uint(key_size, sizeof(u32));
5762306a36Sopenharmony_ci	__array(values, struct inner_a); /* use inner_a as inner map */
5862306a36Sopenharmony_ci} a_of_port_a SEC(".maps");
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* map #5 */ /* Test case #1 */
6162306a36Sopenharmony_cistruct {
6262306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
6362306a36Sopenharmony_ci	__uint(max_entries, 1);
6462306a36Sopenharmony_ci	__uint(key_size, sizeof(u32));
6562306a36Sopenharmony_ci	__array(values, struct inner_a); /* use inner_a as inner map */
6662306a36Sopenharmony_ci} h_of_port_a SEC(".maps");
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* map #6 */ /* Test case #2 */
6962306a36Sopenharmony_cistruct {
7062306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
7162306a36Sopenharmony_ci	__uint(max_entries, 1);
7262306a36Sopenharmony_ci	__uint(key_size, sizeof(u32));
7362306a36Sopenharmony_ci	__array(values, struct inner_h); /* use inner_h as inner map */
7462306a36Sopenharmony_ci} h_of_port_h SEC(".maps");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic __always_inline int do_reg_lookup(void *inner_map, u32 port)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	int *result;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	result = bpf_map_lookup_elem(inner_map, &port);
8162306a36Sopenharmony_ci	return result ? *result : -ENOENT;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic __always_inline int do_inline_array_lookup(void *inner_map, u32 port)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	int *result;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (inner_map != &port_a)
8962306a36Sopenharmony_ci		return -EINVAL;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	result = bpf_map_lookup_elem(&port_a, &port);
9262306a36Sopenharmony_ci	return result ? *result : -ENOENT;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic __always_inline int do_inline_hash_lookup(void *inner_map, u32 port)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	int *result;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (inner_map != &port_h)
10062306a36Sopenharmony_ci		return -EINVAL;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	result = bpf_map_lookup_elem(&port_h, &port);
10362306a36Sopenharmony_ci	return result ? *result : -ENOENT;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciSEC("ksyscall/connect")
10762306a36Sopenharmony_ciint BPF_KSYSCALL(trace_sys_connect, unsigned int fd, struct sockaddr_in6 *in6, int addrlen)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	u16 test_case, port, dst6[8];
11062306a36Sopenharmony_ci	int ret, inline_ret, ret_key = 0;
11162306a36Sopenharmony_ci	u32 port_key;
11262306a36Sopenharmony_ci	void *outer_map, *inner_map;
11362306a36Sopenharmony_ci	bool inline_hash = false;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (addrlen != sizeof(*in6))
11662306a36Sopenharmony_ci		return 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	ret = bpf_probe_read_user(dst6, sizeof(dst6), &in6->sin6_addr);
11962306a36Sopenharmony_ci	if (ret) {
12062306a36Sopenharmony_ci		inline_ret = ret;
12162306a36Sopenharmony_ci		goto done;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (dst6[0] != 0xdead || dst6[1] != 0xbeef)
12562306a36Sopenharmony_ci		return 0;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	test_case = dst6[7];
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	ret = bpf_probe_read_user(&port, sizeof(port), &in6->sin6_port);
13062306a36Sopenharmony_ci	if (ret) {
13162306a36Sopenharmony_ci		inline_ret = ret;
13262306a36Sopenharmony_ci		goto done;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	port_key = port;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	ret = -ENOENT;
13862306a36Sopenharmony_ci	if (test_case == 0) {
13962306a36Sopenharmony_ci		outer_map = &a_of_port_a;
14062306a36Sopenharmony_ci	} else if (test_case == 1) {
14162306a36Sopenharmony_ci		outer_map = &h_of_port_a;
14262306a36Sopenharmony_ci	} else if (test_case == 2) {
14362306a36Sopenharmony_ci		outer_map = &h_of_port_h;
14462306a36Sopenharmony_ci	} else {
14562306a36Sopenharmony_ci		ret = __LINE__;
14662306a36Sopenharmony_ci		inline_ret = ret;
14762306a36Sopenharmony_ci		goto done;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	inner_map = bpf_map_lookup_elem(outer_map, &port_key);
15162306a36Sopenharmony_ci	if (!inner_map) {
15262306a36Sopenharmony_ci		ret = __LINE__;
15362306a36Sopenharmony_ci		inline_ret = ret;
15462306a36Sopenharmony_ci		goto done;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	ret = do_reg_lookup(inner_map, port_key);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (test_case == 0 || test_case == 1)
16062306a36Sopenharmony_ci		inline_ret = do_inline_array_lookup(inner_map, port_key);
16162306a36Sopenharmony_ci	else
16262306a36Sopenharmony_ci		inline_ret = do_inline_hash_lookup(inner_map, port_key);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cidone:
16562306a36Sopenharmony_ci	bpf_map_update_elem(&reg_result_h, &ret_key, &ret, BPF_ANY);
16662306a36Sopenharmony_ci	bpf_map_update_elem(&inline_result_h, &ret_key, &inline_ret, BPF_ANY);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cichar _license[] SEC("license") = "GPL";
17262306a36Sopenharmony_ciu32 _version SEC("version") = LINUX_VERSION_CODE;
173