2017-11-27 23:10:23 +03:00
/*
* Copyright ( C ) 2017 Netronome Systems , Inc .
*
* This software is licensed under the GNU General License Version 2 ,
* June 1991 as shown in the file COPYING in the top - level directory of this
* source tree .
*
* THE COPYRIGHT HOLDERS AND / OR OTHER PARTIES PROVIDE THE PROGRAM " AS IS "
* WITHOUT WARRANTY OF ANY KIND , EITHER EXPRESSED OR IMPLIED , INCLUDING ,
* BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE . THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
* OF THE PROGRAM IS WITH YOU . SHOULD THE PROGRAM PROVE DEFECTIVE , YOU ASSUME
* THE COST OF ALL NECESSARY SERVICING , REPAIR OR CORRECTION .
*/
2017-11-03 23:56:17 +03:00
# include <linux/bpf.h>
# include <linux/bpf_verifier.h>
# include <linux/bug.h>
2017-12-28 05:39:09 +03:00
# include <linux/kdev_t.h>
2017-11-03 23:56:17 +03:00
# include <linux/list.h>
# include <linux/netdevice.h>
# include <linux/printk.h>
2017-12-28 05:39:09 +03:00
# include <linux/proc_ns.h>
2017-11-03 23:56:17 +03:00
# include <linux/rtnetlink.h>
2017-12-28 05:39:03 +03:00
# include <linux/rwsem.h>
2017-11-03 23:56:17 +03:00
2017-12-28 05:39:03 +03:00
/* Protects bpf_prog_offload_devs and offload members of all progs.
* RTNL lock cannot be taken when holding this lock .
*/
static DECLARE_RWSEM ( bpf_devs_lock ) ;
2017-11-03 23:56:17 +03:00
static LIST_HEAD ( bpf_prog_offload_devs ) ;
2018-01-12 07:29:08 +03:00
static int bpf_dev_offload_check ( struct net_device * netdev )
{
if ( ! netdev )
return - EINVAL ;
if ( ! netdev - > netdev_ops - > ndo_bpf )
return - EOPNOTSUPP ;
return 0 ;
}
2017-11-03 23:56:17 +03:00
int bpf_prog_offload_init ( struct bpf_prog * prog , union bpf_attr * attr )
{
2018-01-12 07:29:07 +03:00
struct bpf_prog_offload * offload ;
2018-01-12 07:29:08 +03:00
int err ;
2017-11-03 23:56:17 +03:00
2017-11-21 02:21:52 +03:00
if ( attr - > prog_type ! = BPF_PROG_TYPE_SCHED_CLS & &
attr - > prog_type ! = BPF_PROG_TYPE_XDP )
return - EINVAL ;
2017-11-03 23:56:17 +03:00
if ( attr - > prog_flags )
return - EINVAL ;
offload = kzalloc ( sizeof ( * offload ) , GFP_USER ) ;
if ( ! offload )
return - ENOMEM ;
offload - > prog = prog ;
2017-12-28 05:39:03 +03:00
offload - > netdev = dev_get_by_index ( current - > nsproxy - > net_ns ,
attr - > prog_ifindex ) ;
2018-01-12 07:29:08 +03:00
err = bpf_dev_offload_check ( offload - > netdev ) ;
if ( err )
goto err_maybe_put ;
2017-11-03 23:56:17 +03:00
2017-12-28 05:39:03 +03:00
down_write ( & bpf_devs_lock ) ;
2018-01-12 07:29:08 +03:00
if ( offload - > netdev - > reg_state ! = NETREG_REGISTERED ) {
err = - EINVAL ;
2017-12-28 05:39:03 +03:00
goto err_unlock ;
2018-01-12 07:29:08 +03:00
}
2017-11-03 23:56:17 +03:00
prog - > aux - > offload = offload ;
list_add_tail ( & offload - > offloads , & bpf_prog_offload_devs ) ;
2017-12-28 05:39:03 +03:00
dev_put ( offload - > netdev ) ;
up_write ( & bpf_devs_lock ) ;
2017-11-03 23:56:17 +03:00
return 0 ;
2017-12-28 05:39:03 +03:00
err_unlock :
up_write ( & bpf_devs_lock ) ;
2018-01-12 07:29:08 +03:00
err_maybe_put :
if ( offload - > netdev )
dev_put ( offload - > netdev ) ;
2017-12-28 05:39:03 +03:00
kfree ( offload ) ;
2018-01-12 07:29:08 +03:00
return err ;
2017-11-03 23:56:17 +03:00
}
static int __bpf_offload_ndo ( struct bpf_prog * prog , enum bpf_netdev_command cmd ,
struct netdev_bpf * data )
{
2018-01-12 07:29:07 +03:00
struct bpf_prog_offload * offload = prog - > aux - > offload ;
2017-12-28 05:39:06 +03:00
struct net_device * netdev ;
2017-11-03 23:56:17 +03:00
ASSERT_RTNL ( ) ;
2017-12-28 05:39:06 +03:00
if ( ! offload )
2017-11-03 23:56:17 +03:00
return - ENODEV ;
2017-12-28 05:39:06 +03:00
netdev = offload - > netdev ;
2017-11-03 23:56:17 +03:00
data - > command = cmd ;
return netdev - > netdev_ops - > ndo_bpf ( netdev , data ) ;
}
int bpf_prog_offload_verifier_prep ( struct bpf_verifier_env * env )
{
struct netdev_bpf data = { } ;
int err ;
data . verifier . prog = env - > prog ;
rtnl_lock ( ) ;
err = __bpf_offload_ndo ( env - > prog , BPF_OFFLOAD_VERIFIER_PREP , & data ) ;
if ( err )
goto exit_unlock ;
2017-12-28 05:39:05 +03:00
env - > prog - > aux - > offload - > dev_ops = data . verifier . ops ;
2017-11-03 23:56:17 +03:00
env - > prog - > aux - > offload - > dev_state = true ;
exit_unlock :
rtnl_unlock ( ) ;
return err ;
}
2017-12-28 05:39:05 +03:00
int bpf_prog_offload_verify_insn ( struct bpf_verifier_env * env ,
int insn_idx , int prev_insn_idx )
{
2018-01-12 07:29:07 +03:00
struct bpf_prog_offload * offload ;
2017-12-28 05:39:05 +03:00
int ret = - ENODEV ;
down_read ( & bpf_devs_lock ) ;
offload = env - > prog - > aux - > offload ;
2017-12-28 05:39:06 +03:00
if ( offload )
2017-12-28 05:39:05 +03:00
ret = offload - > dev_ops - > insn_hook ( env , insn_idx , prev_insn_idx ) ;
up_read ( & bpf_devs_lock ) ;
return ret ;
}
2017-11-03 23:56:17 +03:00
static void __bpf_prog_offload_destroy ( struct bpf_prog * prog )
{
2018-01-12 07:29:07 +03:00
struct bpf_prog_offload * offload = prog - > aux - > offload ;
2017-11-03 23:56:17 +03:00
struct netdev_bpf data = { } ;
data . offload . prog = prog ;
if ( offload - > dev_state )
WARN_ON ( __bpf_offload_ndo ( prog , BPF_OFFLOAD_DESTROY , & data ) ) ;
2017-12-28 05:39:07 +03:00
/* Make sure BPF_PROG_GET_NEXT_ID can't find this dead program */
bpf_prog_free_id ( prog , true ) ;
2017-11-03 23:56:17 +03:00
list_del_init ( & offload - > offloads ) ;
2017-12-28 05:39:06 +03:00
kfree ( offload ) ;
prog - > aux - > offload = NULL ;
2017-11-03 23:56:17 +03:00
}
void bpf_prog_offload_destroy ( struct bpf_prog * prog )
{
rtnl_lock ( ) ;
2017-12-28 05:39:03 +03:00
down_write ( & bpf_devs_lock ) ;
2017-12-28 05:39:06 +03:00
if ( prog - > aux - > offload )
__bpf_prog_offload_destroy ( prog ) ;
2017-12-28 05:39:03 +03:00
up_write ( & bpf_devs_lock ) ;
2017-11-03 23:56:17 +03:00
rtnl_unlock ( ) ;
}
static int bpf_prog_offload_translate ( struct bpf_prog * prog )
{
struct netdev_bpf data = { } ;
int ret ;
data . offload . prog = prog ;
rtnl_lock ( ) ;
ret = __bpf_offload_ndo ( prog , BPF_OFFLOAD_TRANSLATE , & data ) ;
rtnl_unlock ( ) ;
return ret ;
}
static unsigned int bpf_prog_warn_on_exec ( const void * ctx ,
const struct bpf_insn * insn )
{
WARN ( 1 , " attempt to execute device eBPF program on the host! " ) ;
return 0 ;
}
int bpf_prog_offload_compile ( struct bpf_prog * prog )
{
prog - > bpf_func = bpf_prog_warn_on_exec ;
return bpf_prog_offload_translate ( prog ) ;
}
2017-12-28 05:39:09 +03:00
struct ns_get_path_bpf_prog_args {
struct bpf_prog * prog ;
struct bpf_prog_info * info ;
} ;
static struct ns_common * bpf_prog_offload_info_fill_ns ( void * private_data )
{
struct ns_get_path_bpf_prog_args * args = private_data ;
struct bpf_prog_aux * aux = args - > prog - > aux ;
struct ns_common * ns ;
struct net * net ;
rtnl_lock ( ) ;
down_read ( & bpf_devs_lock ) ;
if ( aux - > offload ) {
args - > info - > ifindex = aux - > offload - > netdev - > ifindex ;
net = dev_net ( aux - > offload - > netdev ) ;
get_net ( net ) ;
ns = & net - > ns ;
} else {
args - > info - > ifindex = 0 ;
ns = NULL ;
}
up_read ( & bpf_devs_lock ) ;
rtnl_unlock ( ) ;
return ns ;
}
int bpf_prog_offload_info_fill ( struct bpf_prog_info * info ,
struct bpf_prog * prog )
{
struct ns_get_path_bpf_prog_args args = {
. prog = prog ,
. info = info ,
} ;
struct inode * ns_inode ;
struct path ns_path ;
void * res ;
res = ns_get_path_cb ( & ns_path , bpf_prog_offload_info_fill_ns , & args ) ;
if ( IS_ERR ( res ) ) {
if ( ! info - > ifindex )
return - ENODEV ;
return PTR_ERR ( res ) ;
}
ns_inode = ns_path . dentry - > d_inode ;
info - > netns_dev = new_encode_dev ( ns_inode - > i_sb - > s_dev ) ;
info - > netns_ino = ns_inode - > i_ino ;
path_put ( & ns_path ) ;
return 0 ;
}
2017-11-03 23:56:17 +03:00
const struct bpf_prog_ops bpf_offload_prog_ops = {
} ;
static int bpf_offload_notification ( struct notifier_block * notifier ,
ulong event , void * ptr )
{
struct net_device * netdev = netdev_notifier_info_to_dev ( ptr ) ;
2018-01-12 07:29:07 +03:00
struct bpf_prog_offload * offload , * tmp ;
2017-11-03 23:56:17 +03:00
ASSERT_RTNL ( ) ;
switch ( event ) {
case NETDEV_UNREGISTER :
2017-11-21 02:21:57 +03:00
/* ignore namespace changes */
if ( netdev - > reg_state ! = NETREG_UNREGISTERING )
break ;
2017-12-28 05:39:03 +03:00
down_write ( & bpf_devs_lock ) ;
2017-11-03 23:56:17 +03:00
list_for_each_entry_safe ( offload , tmp , & bpf_prog_offload_devs ,
offloads ) {
if ( offload - > netdev = = netdev )
__bpf_prog_offload_destroy ( offload - > prog ) ;
}
2017-12-28 05:39:03 +03:00
up_write ( & bpf_devs_lock ) ;
2017-11-03 23:56:17 +03:00
break ;
default :
break ;
}
return NOTIFY_OK ;
}
static struct notifier_block bpf_offload_notifier = {
. notifier_call = bpf_offload_notification ,
} ;
static int __init bpf_offload_init ( void )
{
register_netdevice_notifier ( & bpf_offload_notifier ) ;
return 0 ;
}
subsys_initcall ( bpf_offload_init ) ;