1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2018 Facebook */ 3 4 #include <stdlib.h> 5 #include <linux/in.h> 6 #include <linux/ip.h> 7 #include <linux/ipv6.h> 8 #include <linux/tcp.h> 9 #include <linux/udp.h> 10 #include <linux/bpf.h> 11 #include <linux/types.h> 12 #include <linux/if_ether.h> 13 14 #include "bpf_endian.h" 15 #include "bpf_helpers.h" 16 #include "test_select_reuseport_common.h" 17 18 int _version SEC("version") = 1; 19 20 #ifndef offsetof 21 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 22 #endif 23 24 struct bpf_map_def SEC("maps") outer_map = { 25 .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, 26 .key_size = sizeof(__u32), 27 .value_size = sizeof(__u32), 28 .max_entries = 1, 29 }; 30 31 struct bpf_map_def SEC("maps") result_map = { 32 .type = BPF_MAP_TYPE_ARRAY, 33 .key_size = sizeof(__u32), 34 .value_size = sizeof(__u32), 35 .max_entries = NR_RESULTS, 36 }; 37 38 struct bpf_map_def SEC("maps") tmp_index_ovr_map = { 39 .type = BPF_MAP_TYPE_ARRAY, 40 .key_size = sizeof(__u32), 41 .value_size = sizeof(int), 42 .max_entries = 1, 43 }; 44 45 struct bpf_map_def SEC("maps") linum_map = { 46 .type = BPF_MAP_TYPE_ARRAY, 47 .key_size = sizeof(__u32), 48 .value_size = sizeof(__u32), 49 .max_entries = 1, 50 }; 51 52 struct bpf_map_def SEC("maps") data_check_map = { 53 .type = BPF_MAP_TYPE_ARRAY, 54 .key_size = sizeof(__u32), 55 .value_size = sizeof(struct data_check), 56 .max_entries = 1, 57 }; 58 59 #define GOTO_DONE(_result) ({ \ 60 result = (_result); \ 61 linum = __LINE__; \ 62 goto done; \ 63 }) 64 65 SEC("select_by_skb_data") 66 int _select_by_skb_data(struct sk_reuseport_md *reuse_md) 67 { 68 __u32 linum, index = 0, flags = 0, index_zero = 0; 69 __u32 *result_cnt, *linum_value; 70 struct data_check data_check = {}; 71 struct cmd *cmd, cmd_copy; 72 void *data, *data_end; 73 void *reuseport_array; 74 enum result result; 75 int *index_ovr; 76 int err; 77 78 data = reuse_md->data; 79 data_end = reuse_md->data_end; 80 data_check.len = reuse_md->len; 81 data_check.eth_protocol = reuse_md->eth_protocol; 82 data_check.ip_protocol = reuse_md->ip_protocol; 83 data_check.hash = reuse_md->hash; 84 data_check.bind_inany = reuse_md->bind_inany; 85 if (data_check.eth_protocol == bpf_htons(ETH_P_IP)) { 86 if (bpf_skb_load_bytes_relative(reuse_md, 87 offsetof(struct iphdr, saddr), 88 data_check.skb_addrs, 8, 89 BPF_HDR_START_NET)) 90 GOTO_DONE(DROP_MISC); 91 } else { 92 if (bpf_skb_load_bytes_relative(reuse_md, 93 offsetof(struct ipv6hdr, saddr), 94 data_check.skb_addrs, 32, 95 BPF_HDR_START_NET)) 96 GOTO_DONE(DROP_MISC); 97 } 98 99 /* 100 * The ip_protocol could be a compile time decision 101 * if the bpf_prog.o is dedicated to either TCP or 102 * UDP. 103 * 104 * Otherwise, reuse_md->ip_protocol or 105 * the protocol field in the iphdr can be used. 106 */ 107 if (data_check.ip_protocol == IPPROTO_TCP) { 108 struct tcphdr *th = data; 109 110 if (th + 1 > data_end) 111 GOTO_DONE(DROP_MISC); 112 113 data_check.skb_ports[0] = th->source; 114 data_check.skb_ports[1] = th->dest; 115 116 if ((th->doff << 2) + sizeof(*cmd) > data_check.len) 117 GOTO_DONE(DROP_ERR_SKB_DATA); 118 if (bpf_skb_load_bytes(reuse_md, th->doff << 2, &cmd_copy, 119 sizeof(cmd_copy))) 120 GOTO_DONE(DROP_MISC); 121 cmd = &cmd_copy; 122 } else if (data_check.ip_protocol == IPPROTO_UDP) { 123 struct udphdr *uh = data; 124 125 if (uh + 1 > data_end) 126 GOTO_DONE(DROP_MISC); 127 128 data_check.skb_ports[0] = uh->source; 129 data_check.skb_ports[1] = uh->dest; 130 131 if (sizeof(struct udphdr) + sizeof(*cmd) > data_check.len) 132 GOTO_DONE(DROP_ERR_SKB_DATA); 133 if (data + sizeof(struct udphdr) + sizeof(*cmd) > data_end) { 134 if (bpf_skb_load_bytes(reuse_md, sizeof(struct udphdr), 135 &cmd_copy, sizeof(cmd_copy))) 136 GOTO_DONE(DROP_MISC); 137 cmd = &cmd_copy; 138 } else { 139 cmd = data + sizeof(struct udphdr); 140 } 141 } else { 142 GOTO_DONE(DROP_MISC); 143 } 144 145 reuseport_array = bpf_map_lookup_elem(&outer_map, &index_zero); 146 if (!reuseport_array) 147 GOTO_DONE(DROP_ERR_INNER_MAP); 148 149 index = cmd->reuseport_index; 150 index_ovr = bpf_map_lookup_elem(&tmp_index_ovr_map, &index_zero); 151 if (!index_ovr) 152 GOTO_DONE(DROP_MISC); 153 154 if (*index_ovr != -1) { 155 index = *index_ovr; 156 *index_ovr = -1; 157 } 158 err = bpf_sk_select_reuseport(reuse_md, reuseport_array, &index, 159 flags); 160 if (!err) 161 GOTO_DONE(PASS); 162 163 if (cmd->pass_on_failure) 164 GOTO_DONE(PASS_ERR_SK_SELECT_REUSEPORT); 165 else 166 GOTO_DONE(DROP_ERR_SK_SELECT_REUSEPORT); 167 168 done: 169 result_cnt = bpf_map_lookup_elem(&result_map, &result); 170 if (!result_cnt) 171 return SK_DROP; 172 173 bpf_map_update_elem(&linum_map, &index_zero, &linum, BPF_ANY); 174 bpf_map_update_elem(&data_check_map, &index_zero, &data_check, BPF_ANY); 175 176 (*result_cnt)++; 177 return result < PASS ? SK_DROP : SK_PASS; 178 } 179 180 char _license[] SEC("license") = "GPL"; 181