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 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 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 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") 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