nf_conntrack_bpf.c (7925B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* Unstable Conntrack Helpers for XDP and TC-BPF hook 3 * 4 * These are called from the XDP and SCHED_CLS BPF programs. Note that it is 5 * allowed to break compatibility for these functions since the interface they 6 * are exposed through to BPF programs is explicitly unstable. 7 */ 8 9#include <linux/bpf.h> 10#include <linux/btf.h> 11#include <linux/types.h> 12#include <linux/btf_ids.h> 13#include <linux/net_namespace.h> 14#include <net/netfilter/nf_conntrack.h> 15#include <net/netfilter/nf_conntrack_bpf.h> 16#include <net/netfilter/nf_conntrack_core.h> 17 18/* bpf_ct_opts - Options for CT lookup helpers 19 * 20 * Members: 21 * @netns_id - Specify the network namespace for lookup 22 * Values: 23 * BPF_F_CURRENT_NETNS (-1) 24 * Use namespace associated with ctx (xdp_md, __sk_buff) 25 * [0, S32_MAX] 26 * Network Namespace ID 27 * @error - Out parameter, set for any errors encountered 28 * Values: 29 * -EINVAL - Passed NULL for bpf_tuple pointer 30 * -EINVAL - opts->reserved is not 0 31 * -EINVAL - netns_id is less than -1 32 * -EINVAL - opts__sz isn't NF_BPF_CT_OPTS_SZ (12) 33 * -EPROTO - l4proto isn't one of IPPROTO_TCP or IPPROTO_UDP 34 * -ENONET - No network namespace found for netns_id 35 * -ENOENT - Conntrack lookup could not find entry for tuple 36 * -EAFNOSUPPORT - tuple__sz isn't one of sizeof(tuple->ipv4) 37 * or sizeof(tuple->ipv6) 38 * @l4proto - Layer 4 protocol 39 * Values: 40 * IPPROTO_TCP, IPPROTO_UDP 41 * @dir: - connection tracking tuple direction. 42 * @reserved - Reserved member, will be reused for more options in future 43 * Values: 44 * 0 45 */ 46struct bpf_ct_opts { 47 s32 netns_id; 48 s32 error; 49 u8 l4proto; 50 u8 dir; 51 u8 reserved[2]; 52}; 53 54enum { 55 NF_BPF_CT_OPTS_SZ = 12, 56}; 57 58static struct nf_conn *__bpf_nf_ct_lookup(struct net *net, 59 struct bpf_sock_tuple *bpf_tuple, 60 u32 tuple_len, u8 protonum, 61 s32 netns_id, u8 *dir) 62{ 63 struct nf_conntrack_tuple_hash *hash; 64 struct nf_conntrack_tuple tuple; 65 struct nf_conn *ct; 66 67 if (unlikely(protonum != IPPROTO_TCP && protonum != IPPROTO_UDP)) 68 return ERR_PTR(-EPROTO); 69 if (unlikely(netns_id < BPF_F_CURRENT_NETNS)) 70 return ERR_PTR(-EINVAL); 71 72 memset(&tuple, 0, sizeof(tuple)); 73 switch (tuple_len) { 74 case sizeof(bpf_tuple->ipv4): 75 tuple.src.l3num = AF_INET; 76 tuple.src.u3.ip = bpf_tuple->ipv4.saddr; 77 tuple.src.u.tcp.port = bpf_tuple->ipv4.sport; 78 tuple.dst.u3.ip = bpf_tuple->ipv4.daddr; 79 tuple.dst.u.tcp.port = bpf_tuple->ipv4.dport; 80 break; 81 case sizeof(bpf_tuple->ipv6): 82 tuple.src.l3num = AF_INET6; 83 memcpy(tuple.src.u3.ip6, bpf_tuple->ipv6.saddr, sizeof(bpf_tuple->ipv6.saddr)); 84 tuple.src.u.tcp.port = bpf_tuple->ipv6.sport; 85 memcpy(tuple.dst.u3.ip6, bpf_tuple->ipv6.daddr, sizeof(bpf_tuple->ipv6.daddr)); 86 tuple.dst.u.tcp.port = bpf_tuple->ipv6.dport; 87 break; 88 default: 89 return ERR_PTR(-EAFNOSUPPORT); 90 } 91 92 tuple.dst.protonum = protonum; 93 94 if (netns_id >= 0) { 95 net = get_net_ns_by_id(net, netns_id); 96 if (unlikely(!net)) 97 return ERR_PTR(-ENONET); 98 } 99 100 hash = nf_conntrack_find_get(net, &nf_ct_zone_dflt, &tuple); 101 if (netns_id >= 0) 102 put_net(net); 103 if (!hash) 104 return ERR_PTR(-ENOENT); 105 106 ct = nf_ct_tuplehash_to_ctrack(hash); 107 if (dir) 108 *dir = NF_CT_DIRECTION(hash); 109 110 return ct; 111} 112 113__diag_push(); 114__diag_ignore_all("-Wmissing-prototypes", 115 "Global functions as their definitions will be in nf_conntrack BTF"); 116 117/* bpf_xdp_ct_lookup - Lookup CT entry for the given tuple, and acquire a 118 * reference to it 119 * 120 * Parameters: 121 * @xdp_ctx - Pointer to ctx (xdp_md) in XDP program 122 * Cannot be NULL 123 * @bpf_tuple - Pointer to memory representing the tuple to look up 124 * Cannot be NULL 125 * @tuple__sz - Length of the tuple structure 126 * Must be one of sizeof(bpf_tuple->ipv4) or 127 * sizeof(bpf_tuple->ipv6) 128 * @opts - Additional options for lookup (documented above) 129 * Cannot be NULL 130 * @opts__sz - Length of the bpf_ct_opts structure 131 * Must be NF_BPF_CT_OPTS_SZ (12) 132 */ 133struct nf_conn * 134bpf_xdp_ct_lookup(struct xdp_md *xdp_ctx, struct bpf_sock_tuple *bpf_tuple, 135 u32 tuple__sz, struct bpf_ct_opts *opts, u32 opts__sz) 136{ 137 struct xdp_buff *ctx = (struct xdp_buff *)xdp_ctx; 138 struct net *caller_net; 139 struct nf_conn *nfct; 140 141 BUILD_BUG_ON(sizeof(struct bpf_ct_opts) != NF_BPF_CT_OPTS_SZ); 142 143 if (!opts) 144 return NULL; 145 if (!bpf_tuple || opts->reserved[0] || opts->reserved[1] || 146 opts__sz != NF_BPF_CT_OPTS_SZ) { 147 opts->error = -EINVAL; 148 return NULL; 149 } 150 caller_net = dev_net(ctx->rxq->dev); 151 nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts->l4proto, 152 opts->netns_id, &opts->dir); 153 if (IS_ERR(nfct)) { 154 opts->error = PTR_ERR(nfct); 155 return NULL; 156 } 157 return nfct; 158} 159 160/* bpf_skb_ct_lookup - Lookup CT entry for the given tuple, and acquire a 161 * reference to it 162 * 163 * Parameters: 164 * @skb_ctx - Pointer to ctx (__sk_buff) in TC program 165 * Cannot be NULL 166 * @bpf_tuple - Pointer to memory representing the tuple to look up 167 * Cannot be NULL 168 * @tuple__sz - Length of the tuple structure 169 * Must be one of sizeof(bpf_tuple->ipv4) or 170 * sizeof(bpf_tuple->ipv6) 171 * @opts - Additional options for lookup (documented above) 172 * Cannot be NULL 173 * @opts__sz - Length of the bpf_ct_opts structure 174 * Must be NF_BPF_CT_OPTS_SZ (12) 175 */ 176struct nf_conn * 177bpf_skb_ct_lookup(struct __sk_buff *skb_ctx, struct bpf_sock_tuple *bpf_tuple, 178 u32 tuple__sz, struct bpf_ct_opts *opts, u32 opts__sz) 179{ 180 struct sk_buff *skb = (struct sk_buff *)skb_ctx; 181 struct net *caller_net; 182 struct nf_conn *nfct; 183 184 BUILD_BUG_ON(sizeof(struct bpf_ct_opts) != NF_BPF_CT_OPTS_SZ); 185 186 if (!opts) 187 return NULL; 188 if (!bpf_tuple || opts->reserved[0] || opts->reserved[1] || 189 opts__sz != NF_BPF_CT_OPTS_SZ) { 190 opts->error = -EINVAL; 191 return NULL; 192 } 193 caller_net = skb->dev ? dev_net(skb->dev) : sock_net(skb->sk); 194 nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts->l4proto, 195 opts->netns_id, &opts->dir); 196 if (IS_ERR(nfct)) { 197 opts->error = PTR_ERR(nfct); 198 return NULL; 199 } 200 return nfct; 201} 202 203/* bpf_ct_release - Release acquired nf_conn object 204 * 205 * This must be invoked for referenced PTR_TO_BTF_ID, and the verifier rejects 206 * the program if any references remain in the program in all of the explored 207 * states. 208 * 209 * Parameters: 210 * @nf_conn - Pointer to referenced nf_conn object, obtained using 211 * bpf_xdp_ct_lookup or bpf_skb_ct_lookup. 212 */ 213void bpf_ct_release(struct nf_conn *nfct) 214{ 215 if (!nfct) 216 return; 217 nf_ct_put(nfct); 218} 219 220__diag_pop() 221 222BTF_SET_START(nf_ct_xdp_check_kfunc_ids) 223BTF_ID(func, bpf_xdp_ct_lookup) 224BTF_ID(func, bpf_ct_release) 225BTF_SET_END(nf_ct_xdp_check_kfunc_ids) 226 227BTF_SET_START(nf_ct_tc_check_kfunc_ids) 228BTF_ID(func, bpf_skb_ct_lookup) 229BTF_ID(func, bpf_ct_release) 230BTF_SET_END(nf_ct_tc_check_kfunc_ids) 231 232BTF_SET_START(nf_ct_acquire_kfunc_ids) 233BTF_ID(func, bpf_xdp_ct_lookup) 234BTF_ID(func, bpf_skb_ct_lookup) 235BTF_SET_END(nf_ct_acquire_kfunc_ids) 236 237BTF_SET_START(nf_ct_release_kfunc_ids) 238BTF_ID(func, bpf_ct_release) 239BTF_SET_END(nf_ct_release_kfunc_ids) 240 241/* Both sets are identical */ 242#define nf_ct_ret_null_kfunc_ids nf_ct_acquire_kfunc_ids 243 244static const struct btf_kfunc_id_set nf_conntrack_xdp_kfunc_set = { 245 .owner = THIS_MODULE, 246 .check_set = &nf_ct_xdp_check_kfunc_ids, 247 .acquire_set = &nf_ct_acquire_kfunc_ids, 248 .release_set = &nf_ct_release_kfunc_ids, 249 .ret_null_set = &nf_ct_ret_null_kfunc_ids, 250}; 251 252static const struct btf_kfunc_id_set nf_conntrack_tc_kfunc_set = { 253 .owner = THIS_MODULE, 254 .check_set = &nf_ct_tc_check_kfunc_ids, 255 .acquire_set = &nf_ct_acquire_kfunc_ids, 256 .release_set = &nf_ct_release_kfunc_ids, 257 .ret_null_set = &nf_ct_ret_null_kfunc_ids, 258}; 259 260int register_nf_conntrack_bpf(void) 261{ 262 int ret; 263 264 ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &nf_conntrack_xdp_kfunc_set); 265 return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &nf_conntrack_tc_kfunc_set); 266}