| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 15:27:09 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Prototyping new API for userspace xsk/AF_XDP to access XDP-hints, which is | 
					
						
							|  |  |  |  * BTF typed info in XDP metadata area (located just before packets headers). | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | #include "hashmap.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <bpf/btf.h> /* provided by libbpf */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 10:01:30 +01:00
										 |  |  | #include "lib_xsk_extend.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 18:14:17 +01:00
										 |  |  | int xsk_umem__btf_id(void *umem_pkt_data) // , const struct xsk_umem *umem)
 | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | { | 
					
						
							|  |  |  | //	if (umem->config.xdp_headroom < sizeof(int))
 | 
					
						
							|  |  |  | //		return -EINVAL;
 | 
					
						
							| 
									
										
										
										
											2021-11-03 18:14:17 +01:00
										 |  |  | 	// TODO: Need some check that know of metadata is enabled for frame
 | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-09 17:47:11 +01:00
										 |  |  | 	/* IDEA: Use retval 0 to indicate no-btf-id?
 | 
					
						
							|  |  |  | 	 * - This would simplify API users | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 15:27:09 +01:00
										 |  |  | 	return *(int *)(umem_pkt_data - sizeof(int)); | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct xsk_btf_info { | 
					
						
							|  |  |  | 	struct hashmap map; | 
					
						
							|  |  |  | 	struct btf *btf; | 
					
						
							|  |  |  | 	const struct btf_type *type; | 
					
						
							| 
									
										
										
										
											2021-11-03 18:14:17 +01:00
										 |  |  | 	__u32 btf_type_id; | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 18:14:17 +01:00
										 |  |  | __u32 xsk_btf__btf_type_id(struct xsk_btf_info *xbi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return xbi->btf_type_id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | static void __xsk_btf_free_hash(struct xsk_btf_info *xbi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct hashmap_entry *entry; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hashmap__for_each_entry((&(xbi->map)), entry, i) { | 
					
						
							|  |  |  | 		free(entry->value); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	hashmap__clear(&(xbi->map)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static size_t __xsk_hash_fn(const void *key, void *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Note that, the hashmap used to speed-up offset location into the BTF
 | 
					
						
							|  |  |  | 	 * doesn't use the field name as a string as key to the hashmap. It | 
					
						
							|  |  |  | 	 * directly uses the pointer value instead, as it is expected that most | 
					
						
							|  |  |  | 	 * of time, field names will be addressed by a shared constant string | 
					
						
							|  |  |  | 	 * residing on read-only memory, thus saving some time. If this | 
					
						
							|  |  |  | 	 * assumption is not entirely true, this optimisation needs to be | 
					
						
							|  |  |  | 	 * rethought (or discarded altogether). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	return (size_t)key; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool __xsk_equal_fn(const void *k1, const void *k2, void *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return k1 == k2; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 15:27:09 +01:00
										 |  |  | int xsk_btf__init_xdp_hint(struct btf *btf_obj, | 
					
						
							|  |  |  | 			   const char *xdp_hints_name, | 
					
						
							|  |  |  | 			   struct xsk_btf_info **xbi) | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	const struct btf_member *m; | 
					
						
							|  |  |  | 	const struct btf_type *t; | 
					
						
							|  |  |  | 	unsigned short vlen; | 
					
						
							|  |  |  | 	int i, id, ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!xbi) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 09:18:44 +01:00
										 |  |  | 	/* Require XDP-hints are defined as a struct */ | 
					
						
							| 
									
										
										
										
											2021-11-03 15:27:09 +01:00
										 |  |  | 	id = btf__find_by_name_kind(btf_obj, xdp_hints_name, BTF_KIND_STRUCT); | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 	if (id < 0) { | 
					
						
							|  |  |  | 		ret = id; | 
					
						
							|  |  |  | 		goto error_btf; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 15:27:09 +01:00
										 |  |  | 	t = btf__type_by_id(btf_obj, id); | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	*xbi = malloc(sizeof(**xbi)); | 
					
						
							|  |  |  | 	if (!*xbi) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto error_btf; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hashmap__init(&(*xbi)->map, __xsk_hash_fn, __xsk_equal_fn, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Validate no BTF field is a bitfield */ | 
					
						
							|  |  |  | 	m = btf_members(t); | 
					
						
							|  |  |  | 	vlen = BTF_INFO_VLEN(t->info); | 
					
						
							|  |  |  | 	for (i = 0; i < vlen; i++, m++) { | 
					
						
							|  |  |  | 		if (BTF_MEMBER_BITFIELD_SIZE(m->offset)) { | 
					
						
							|  |  |  | 			ret = -ENOTSUP; | 
					
						
							|  |  |  | 			goto error_entry; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 15:27:09 +01:00
										 |  |  | 	(*xbi)->btf = btf_obj; | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 	(*xbi)->type = t; | 
					
						
							| 
									
										
										
										
											2021-11-03 18:14:17 +01:00
										 |  |  | 	(*xbi)->btf_type_id = id; | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error_entry: | 
					
						
							|  |  |  | 	__xsk_btf_free_hash(*xbi); | 
					
						
							|  |  |  | 	free(*xbi); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error_btf: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __xsk_btf_field_entry(struct xsk_btf_info *xbi, const char *field, | 
					
						
							| 
									
										
										
										
											2021-11-04 10:01:30 +01:00
										 |  |  | 			  struct xsk_btf_member *entry) | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	const struct btf_member *m; | 
					
						
							|  |  |  | 	unsigned short vlen; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m = btf_members(xbi->type); | 
					
						
							|  |  |  | 	vlen = BTF_INFO_VLEN(xbi->type->info); | 
					
						
							|  |  |  | 	for (i = 0; i < vlen; i++, m++) { | 
					
						
							|  |  |  | 		const char *name = btf__name_by_offset(xbi->btf, m->name_off); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (strcmp(name, field)) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (entry) { | 
					
						
							|  |  |  | 			/* As we bail out at init for bit fields, there should
 | 
					
						
							|  |  |  | 			 * be no entries whose offset is not a multiple of byte */ | 
					
						
							| 
									
										
										
										
											2021-11-04 09:44:53 +01:00
										 |  |  | 			entry->offset = BTF_MEMBER_BIT_OFFSET(m->offset) / 8; | 
					
						
							|  |  |  | 			entry->size = btf__resolve_size(xbi->btf, m->type); | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -ENOENT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool xsk_btf__has_field(const char *field, struct xsk_btf_info *xbi) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!xbi) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 15:27:09 +01:00
										 |  |  | 	return __xsk_btf_field_entry(xbi, field, NULL) ? false : true; | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 10:01:30 +01:00
										 |  |  | bool xsk_btf__field_member(const char *field, struct xsk_btf_info *xbi, | 
					
						
							|  |  |  | 			  struct xsk_btf_member *entry) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!xbi) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return __xsk_btf_field_entry(xbi, field, entry) ? false : true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 15:27:09 +01:00
										 |  |  | void xsk_btf__free_xdp_hint(struct xsk_btf_info *xbi) | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (!xbi) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__xsk_btf_free_hash(xbi); | 
					
						
							|  |  |  | 	free(xbi); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-09 21:59:14 +01:00
										 |  |  | int xsk_btf__read(void **dest, size_t size, | 
					
						
							|  |  |  | 		  struct xsk_btf_member *entry, | 
					
						
							|  |  |  | 		  struct xsk_btf_info *xbi, const void *addr) | 
					
						
							| 
									
										
										
										
											2021-11-04 10:25:16 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (!entry || !xbi || !dest || !addr) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (entry->size != size) | 
					
						
							|  |  |  | 		return -EFAULT; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-05 10:13:42 +01:00
										 |  |  | 	/* Remember XDP-hints are located in metadata (xdp_ctx->data_meta),
 | 
					
						
							|  |  |  | 	 * which is located just before packet data starts.  Thus, accessing BTF | 
					
						
							|  |  |  | 	 * described memory area via a negative offset, based on the size of the | 
					
						
							|  |  |  | 	 * BTF struct that XDP-prog used. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2021-11-04 10:25:16 +01:00
										 |  |  | 	*dest = (void *)((char *)addr - xbi->type->size + entry->offset); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-09 20:56:32 +01:00
										 |  |  | int xsk_btf__read_field(void **dest, size_t size, const char *field, | 
					
						
							|  |  |  | 			struct xsk_btf_info *xbi, const void *addr) | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-11-04 10:01:30 +01:00
										 |  |  | 	struct xsk_btf_member *entry; | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!field || !xbi || !dest || !addr) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!hashmap__find(&(xbi->map), field, (void **)&entry)) { | 
					
						
							| 
									
										
										
										
											2021-11-04 10:01:30 +01:00
										 |  |  | 		struct xsk_btf_member e; | 
					
						
							| 
									
										
										
										
											2021-11-04 09:44:53 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		err = __xsk_btf_field_entry(xbi, field, &e); | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 		if (err) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 09:44:53 +01:00
										 |  |  | 		entry = malloc(sizeof(*entry)); | 
					
						
							|  |  |  | 		if (!entry) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 		*entry = e; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 		hashmap__add(&(xbi->map), field, entry); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-09 21:59:14 +01:00
										 |  |  | 	xsk_btf__read(dest, size, entry, xbi,addr); | 
					
						
							| 
									
										
										
										
											2021-11-03 12:23:56 +01:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } |