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