forked from mirrors/linux
		
	net: sock: allow eBPF programs to be attached to sockets
introduce new setsockopt() command: setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd)) where prog_fd was received from syscall bpf(BPF_PROG_LOAD, attr, ...) and attr->prog_type == BPF_PROG_TYPE_SOCKET_FILTER setsockopt() calls bpf_prog_get() which increments refcnt of the program, so it doesn't get unloaded while socket is using the program. The same eBPF program can be attached to multiple sockets. User task exit automatically closes socket which calls sk_filter_uncharge() which decrements refcnt of eBPF program Signed-off-by: Alexei Starovoitov <ast@plumgrid.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									ddd872bc30
								
							
						
					
					
						commit
						89aa075832
					
				
					 18 changed files with 155 additions and 2 deletions
				
			
		|  | @ -89,4 +89,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _UAPI_ASM_SOCKET_H */ | ||||
|  |  | |||
|  | @ -82,4 +82,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _UAPI__ASM_AVR32_SOCKET_H */ | ||||
|  |  | |||
|  | @ -84,6 +84,9 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _ASM_SOCKET_H */ | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -82,5 +82,8 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _ASM_SOCKET_H */ | ||||
| 
 | ||||
|  |  | |||
|  | @ -91,4 +91,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _ASM_IA64_SOCKET_H */ | ||||
|  |  | |||
|  | @ -82,4 +82,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _ASM_M32R_SOCKET_H */ | ||||
|  |  | |||
|  | @ -100,4 +100,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _UAPI_ASM_SOCKET_H */ | ||||
|  |  | |||
|  | @ -82,4 +82,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _ASM_SOCKET_H */ | ||||
|  |  | |||
|  | @ -81,4 +81,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		0x402A | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		0x402B | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _UAPI_ASM_SOCKET_H */ | ||||
|  |  | |||
|  | @ -89,4 +89,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif	/* _ASM_POWERPC_SOCKET_H */ | ||||
|  |  | |||
|  | @ -88,4 +88,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* _ASM_SOCKET_H */ | ||||
|  |  | |||
|  | @ -78,6 +78,9 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		0x0033 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		0x0034 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| /* Security levels - as per NRL IPv6 - don't actually do anything */ | ||||
| #define SO_SECURITY_AUTHENTICATION		0x5001 | ||||
| #define SO_SECURITY_ENCRYPTION_TRANSPORT	0x5002 | ||||
|  |  | |||
|  | @ -93,4 +93,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif	/* _XTENSA_SOCKET_H */ | ||||
|  |  | |||
|  | @ -128,7 +128,11 @@ struct bpf_prog_aux { | |||
| 	struct work_struct work; | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_BPF_SYSCALL | ||||
| void bpf_prog_put(struct bpf_prog *prog); | ||||
| #else | ||||
| static inline void bpf_prog_put(struct bpf_prog *prog) {} | ||||
| #endif | ||||
| struct bpf_prog *bpf_prog_get(u32 ufd); | ||||
| /* verify correctness of eBPF program */ | ||||
| int bpf_check(struct bpf_prog *fp, union bpf_attr *attr); | ||||
|  |  | |||
|  | @ -381,6 +381,7 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog); | |||
| void bpf_prog_destroy(struct bpf_prog *fp); | ||||
| 
 | ||||
| int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); | ||||
| int sk_attach_bpf(u32 ufd, struct sock *sk); | ||||
| int sk_detach_filter(struct sock *sk); | ||||
| 
 | ||||
| int bpf_check_classic(const struct sock_filter *filter, unsigned int flen); | ||||
|  |  | |||
|  | @ -84,4 +84,7 @@ | |||
| 
 | ||||
| #define SO_INCOMING_CPU		49 | ||||
| 
 | ||||
| #define SO_ATTACH_BPF		50 | ||||
| #define SO_DETACH_BPF		SO_DETACH_FILTER | ||||
| 
 | ||||
| #endif /* __ASM_GENERIC_SOCKET_H */ | ||||
|  |  | |||
|  | @ -44,6 +44,7 @@ | |||
| #include <linux/ratelimit.h> | ||||
| #include <linux/seccomp.h> | ||||
| #include <linux/if_vlan.h> | ||||
| #include <linux/bpf.h> | ||||
| 
 | ||||
| /**
 | ||||
|  *	sk_filter - run a packet through a socket filter | ||||
|  | @ -813,9 +814,13 @@ static void bpf_release_orig_filter(struct bpf_prog *fp) | |||
| 
 | ||||
| static void __bpf_prog_release(struct bpf_prog *prog) | ||||
| { | ||||
| 	if (prog->aux->prog_type == BPF_PROG_TYPE_SOCKET_FILTER) { | ||||
| 		bpf_prog_put(prog); | ||||
| 	} else { | ||||
| 		bpf_release_orig_filter(prog); | ||||
| 		bpf_prog_free(prog); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void __sk_filter_release(struct sk_filter *fp) | ||||
| { | ||||
|  | @ -1088,6 +1093,94 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) | |||
| } | ||||
| EXPORT_SYMBOL_GPL(sk_attach_filter); | ||||
| 
 | ||||
| #ifdef CONFIG_BPF_SYSCALL | ||||
| int sk_attach_bpf(u32 ufd, struct sock *sk) | ||||
| { | ||||
| 	struct sk_filter *fp, *old_fp; | ||||
| 	struct bpf_prog *prog; | ||||
| 
 | ||||
| 	if (sock_flag(sk, SOCK_FILTER_LOCKED)) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	prog = bpf_prog_get(ufd); | ||||
| 	if (!prog) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (prog->aux->prog_type != BPF_PROG_TYPE_SOCKET_FILTER) { | ||||
| 		/* valid fd, but invalid program type */ | ||||
| 		bpf_prog_put(prog); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	fp = kmalloc(sizeof(*fp), GFP_KERNEL); | ||||
| 	if (!fp) { | ||||
| 		bpf_prog_put(prog); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	fp->prog = prog; | ||||
| 
 | ||||
| 	atomic_set(&fp->refcnt, 0); | ||||
| 
 | ||||
| 	if (!sk_filter_charge(sk, fp)) { | ||||
| 		__sk_filter_release(fp); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	old_fp = rcu_dereference_protected(sk->sk_filter, | ||||
| 					   sock_owned_by_user(sk)); | ||||
| 	rcu_assign_pointer(sk->sk_filter, fp); | ||||
| 
 | ||||
| 	if (old_fp) | ||||
| 		sk_filter_uncharge(sk, old_fp); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* allow socket filters to call
 | ||||
|  * bpf_map_lookup_elem(), bpf_map_update_elem(), bpf_map_delete_elem() | ||||
|  */ | ||||
| static const struct bpf_func_proto *sock_filter_func_proto(enum bpf_func_id func_id) | ||||
| { | ||||
| 	switch (func_id) { | ||||
| 	case BPF_FUNC_map_lookup_elem: | ||||
| 		return &bpf_map_lookup_elem_proto; | ||||
| 	case BPF_FUNC_map_update_elem: | ||||
| 		return &bpf_map_update_elem_proto; | ||||
| 	case BPF_FUNC_map_delete_elem: | ||||
| 		return &bpf_map_delete_elem_proto; | ||||
| 	default: | ||||
| 		return NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static bool sock_filter_is_valid_access(int off, int size, enum bpf_access_type type) | ||||
| { | ||||
| 	/* skb fields cannot be accessed yet */ | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static struct bpf_verifier_ops sock_filter_ops = { | ||||
| 	.get_func_proto = sock_filter_func_proto, | ||||
| 	.is_valid_access = sock_filter_is_valid_access, | ||||
| }; | ||||
| 
 | ||||
| static struct bpf_prog_type_list tl = { | ||||
| 	.ops = &sock_filter_ops, | ||||
| 	.type = BPF_PROG_TYPE_SOCKET_FILTER, | ||||
| }; | ||||
| 
 | ||||
| static int __init register_sock_filter_ops(void) | ||||
| { | ||||
| 	bpf_register_prog_type(&tl); | ||||
| 	return 0; | ||||
| } | ||||
| late_initcall(register_sock_filter_ops); | ||||
| #else | ||||
| int sk_attach_bpf(u32 ufd, struct sock *sk) | ||||
| { | ||||
| 	return -EOPNOTSUPP; | ||||
| } | ||||
| #endif | ||||
| int sk_detach_filter(struct sock *sk) | ||||
| { | ||||
| 	int ret = -ENOENT; | ||||
|  |  | |||
|  | @ -888,6 +888,19 @@ int sock_setsockopt(struct socket *sock, int level, int optname, | |||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case SO_ATTACH_BPF: | ||||
| 		ret = -EINVAL; | ||||
| 		if (optlen == sizeof(u32)) { | ||||
| 			u32 ufd; | ||||
| 
 | ||||
| 			ret = -EFAULT; | ||||
| 			if (copy_from_user(&ufd, optval, sizeof(ufd))) | ||||
| 				break; | ||||
| 
 | ||||
| 			ret = sk_attach_bpf(ufd, sk); | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case SO_DETACH_FILTER: | ||||
| 		ret = sk_detach_filter(sk); | ||||
| 		break; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Alexei Starovoitov
						Alexei Starovoitov