2005-08-10 06:30:24 +04:00
/* Netfilter messages via netlink socket. Allows for user space
* protocol helpers and general trouble making from userspace .
*
* ( C ) 2001 by Jay Schulist < jschlst @ samba . org > ,
* ( C ) 2002 - 2005 by Harald Welte < laforge @ gnumonks . org >
2007-03-15 02:40:38 +03:00
* ( C ) 2005 , 2007 by Pablo Neira Ayuso < pablo @ netfilter . org >
2005-08-10 06:30:24 +04:00
*
* Initial netfilter messages via netlink development funded and
* generally made possible by Network Robots , Inc . ( www . networkrobots . com )
*
* Further development of this code funded by Astaro AG ( http : //www.astaro.com)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/sockios.h>
# include <linux/net.h>
# include <linux/skbuff.h>
# include <asm/uaccess.h>
# include <net/sock.h>
# include <linux/init.h>
2013-03-27 10:47:04 +04:00
# include <net/netlink.h>
2005-08-10 06:30:24 +04:00
# include <linux/netfilter/nfnetlink.h>
MODULE_LICENSE ( " GPL " ) ;
2005-08-10 06:40:55 +04:00
MODULE_AUTHOR ( " Harald Welte <laforge@netfilter.org> " ) ;
MODULE_ALIAS_NET_PF_PROTO ( PF_NETLINK , NETLINK_NETFILTER ) ;
2005-08-10 06:30:24 +04:00
static char __initdata nfversion [ ] = " 0.30 " ;
2013-02-05 04:50:26 +04:00
static struct {
struct mutex mutex ;
const struct nfnetlink_subsystem __rcu * subsys ;
} table [ NFNL_SUBSYS_COUNT ] ;
2005-08-10 06:30:24 +04:00
2012-06-29 10:15:22 +04:00
static const int nfnl_group2type [ NFNLGRP_MAX + 1 ] = {
[ NFNLGRP_CONNTRACK_NEW ] = NFNL_SUBSYS_CTNETLINK ,
[ NFNLGRP_CONNTRACK_UPDATE ] = NFNL_SUBSYS_CTNETLINK ,
[ NFNLGRP_CONNTRACK_DESTROY ] = NFNL_SUBSYS_CTNETLINK ,
[ NFNLGRP_CONNTRACK_EXP_NEW ] = NFNL_SUBSYS_CTNETLINK_EXP ,
[ NFNLGRP_CONNTRACK_EXP_UPDATE ] = NFNL_SUBSYS_CTNETLINK_EXP ,
[ NFNLGRP_CONNTRACK_EXP_DESTROY ] = NFNL_SUBSYS_CTNETLINK_EXP ,
} ;
2013-02-05 04:50:26 +04:00
void nfnl_lock ( __u8 subsys_id )
2005-08-10 06:30:24 +04:00
{
2013-02-05 04:50:26 +04:00
mutex_lock ( & table [ subsys_id ] . mutex ) ;
2005-08-10 06:30:24 +04:00
}
2008-10-14 22:58:31 +04:00
EXPORT_SYMBOL_GPL ( nfnl_lock ) ;
2005-08-10 06:30:24 +04:00
2013-02-05 04:50:26 +04:00
void nfnl_unlock ( __u8 subsys_id )
2007-03-15 02:39:25 +03:00
{
2013-02-05 04:50:26 +04:00
mutex_unlock ( & table [ subsys_id ] . mutex ) ;
2005-08-10 06:30:24 +04:00
}
2008-10-14 22:58:31 +04:00
EXPORT_SYMBOL_GPL ( nfnl_unlock ) ;
2005-08-10 06:30:24 +04:00
2007-09-29 01:15:45 +04:00
int nfnetlink_subsys_register ( const struct nfnetlink_subsystem * n )
2005-08-10 06:30:24 +04:00
{
2013-02-05 04:50:26 +04:00
nfnl_lock ( n - > subsys_id ) ;
if ( table [ n - > subsys_id ] . subsys ) {
nfnl_unlock ( n - > subsys_id ) ;
2005-08-10 06:43:44 +04:00
return - EBUSY ;
}
2013-02-05 04:50:26 +04:00
rcu_assign_pointer ( table [ n - > subsys_id ] . subsys , n ) ;
nfnl_unlock ( n - > subsys_id ) ;
2005-08-10 06:30:24 +04:00
return 0 ;
}
2007-03-15 02:42:11 +03:00
EXPORT_SYMBOL_GPL ( nfnetlink_subsys_register ) ;
2005-08-10 06:30:24 +04:00
2007-09-29 01:15:45 +04:00
int nfnetlink_subsys_unregister ( const struct nfnetlink_subsystem * n )
2005-08-10 06:30:24 +04:00
{
2013-02-05 04:50:26 +04:00
nfnl_lock ( n - > subsys_id ) ;
table [ n - > subsys_id ] . subsys = NULL ;
nfnl_unlock ( n - > subsys_id ) ;
2011-07-18 18:08:07 +04:00
synchronize_rcu ( ) ;
2005-08-10 06:30:24 +04:00
return 0 ;
}
2007-03-15 02:42:11 +03:00
EXPORT_SYMBOL_GPL ( nfnetlink_subsys_unregister ) ;
2005-08-10 06:30:24 +04:00
2007-09-29 01:15:45 +04:00
static inline const struct nfnetlink_subsystem * nfnetlink_get_subsys ( u_int16_t type )
2005-08-10 06:30:24 +04:00
{
u_int8_t subsys_id = NFNL_SUBSYS_ID ( type ) ;
2007-03-15 02:41:28 +03:00
if ( subsys_id > = NFNL_SUBSYS_COUNT )
2005-08-10 06:30:24 +04:00
return NULL ;
2013-02-05 04:50:26 +04:00
return rcu_dereference ( table [ subsys_id ] . subsys ) ;
2005-08-10 06:30:24 +04:00
}
2007-09-29 01:15:45 +04:00
static inline const struct nfnl_callback *
nfnetlink_find_client ( u_int16_t type , const struct nfnetlink_subsystem * ss )
2005-08-10 06:30:24 +04:00
{
u_int8_t cb_id = NFNL_MSG_TYPE ( type ) ;
2007-02-12 22:15:49 +03:00
2007-03-15 02:40:38 +03:00
if ( cb_id > = ss - > cb_count )
2005-08-10 06:30:24 +04:00
return NULL ;
return & ss - > cb [ cb_id ] ;
}
2010-01-13 18:02:14 +03:00
int nfnetlink_has_listeners ( struct net * net , unsigned int group )
2006-03-21 05:03:59 +03:00
{
2010-01-13 18:02:14 +03:00
return netlink_has_listeners ( net - > nfnl , group ) ;
2006-03-21 05:03:59 +03:00
}
EXPORT_SYMBOL_GPL ( nfnetlink_has_listeners ) ;
2013-04-17 10:47:09 +04:00
struct sk_buff * nfnetlink_alloc_skb ( struct net * net , unsigned int size ,
u32 dst_portid , gfp_t gfp_mask )
{
return netlink_alloc_skb ( net - > nfnl , size , dst_portid , gfp_mask ) ;
}
EXPORT_SYMBOL_GPL ( nfnetlink_alloc_skb ) ;
2013-04-17 10:47:08 +04:00
int nfnetlink_send ( struct sk_buff * skb , struct net * net , u32 portid ,
2012-04-15 09:58:06 +04:00
unsigned int group , int echo , gfp_t flags )
2005-08-10 06:30:24 +04:00
{
2013-04-17 10:47:08 +04:00
return nlmsg_notify ( net - > nfnl , skb , portid , group , echo , flags ) ;
2005-08-10 06:30:24 +04:00
}
2007-03-15 02:42:11 +03:00
EXPORT_SYMBOL_GPL ( nfnetlink_send ) ;
2005-08-10 06:30:24 +04:00
2013-04-17 10:47:08 +04:00
int nfnetlink_set_err ( struct net * net , u32 portid , u32 group , int error )
2009-03-23 15:21:06 +03:00
{
2013-04-17 10:47:08 +04:00
return netlink_set_err ( net - > nfnl , portid , group , error ) ;
2009-03-23 15:21:06 +03:00
}
EXPORT_SYMBOL_GPL ( nfnetlink_set_err ) ;
2013-04-17 10:47:08 +04:00
int nfnetlink_unicast ( struct sk_buff * skb , struct net * net , u32 portid ,
int flags )
2005-08-10 06:30:24 +04:00
{
2013-04-17 10:47:08 +04:00
return netlink_unicast ( net - > nfnl , skb , portid , flags ) ;
2005-08-10 06:30:24 +04:00
}
2007-03-15 02:42:11 +03:00
EXPORT_SYMBOL_GPL ( nfnetlink_unicast ) ;
2005-08-10 06:30:24 +04:00
/* Process one complete nfnetlink message. */
2007-03-23 09:30:12 +03:00
static int nfnetlink_rcv_msg ( struct sk_buff * skb , struct nlmsghdr * nlh )
2005-08-10 06:30:24 +04:00
{
2010-01-13 18:02:14 +03:00
struct net * net = sock_net ( skb - > sk ) ;
2007-09-29 01:15:45 +04:00
const struct nfnl_callback * nc ;
const struct nfnetlink_subsystem * ss ;
2007-03-23 09:30:12 +03:00
int type , err ;
2005-08-10 06:30:24 +04:00
2012-11-16 07:03:07 +04:00
if ( ! ns_capable ( net - > user_ns , CAP_NET_ADMIN ) )
2007-03-23 09:30:12 +03:00
return - EPERM ;
2005-11-15 02:24:59 +03:00
2005-08-10 06:30:24 +04:00
/* All the messages must at least contain nfgenmsg */
2013-03-27 10:47:04 +04:00
if ( nlmsg_len ( nlh ) < sizeof ( struct nfgenmsg ) )
2005-08-10 06:30:24 +04:00
return 0 ;
type = nlh - > nlmsg_type ;
2008-10-14 22:58:31 +04:00
replay :
2011-07-18 18:08:07 +04:00
rcu_read_lock ( ) ;
2005-08-10 06:30:24 +04:00
ss = nfnetlink_get_subsys ( type ) ;
2005-08-10 06:43:44 +04:00
if ( ! ss ) {
2008-10-17 02:24:51 +04:00
# ifdef CONFIG_MODULES
2011-07-18 18:08:07 +04:00
rcu_read_unlock ( ) ;
2005-11-15 02:24:59 +03:00
request_module ( " nfnetlink-subsys-%d " , NFNL_SUBSYS_ID ( type ) ) ;
2011-07-18 18:08:07 +04:00
rcu_read_lock ( ) ;
2005-11-15 02:24:59 +03:00
ss = nfnetlink_get_subsys ( type ) ;
2005-08-10 06:43:44 +04:00
if ( ! ss )
# endif
2011-07-18 18:08:07 +04:00
{
rcu_read_unlock ( ) ;
2007-03-23 09:30:12 +03:00
return - EINVAL ;
2011-07-18 18:08:07 +04:00
}
2005-08-10 06:43:44 +04:00
}
2005-08-10 06:30:24 +04:00
nc = nfnetlink_find_client ( type , ss ) ;
2011-07-18 18:08:07 +04:00
if ( ! nc ) {
rcu_read_unlock ( ) ;
2007-03-23 09:30:12 +03:00
return - EINVAL ;
2011-07-18 18:08:07 +04:00
}
2005-08-10 06:30:24 +04:00
{
2013-03-27 10:47:04 +04:00
int min_len = nlmsg_total_size ( sizeof ( struct nfgenmsg ) ) ;
2007-09-29 01:38:52 +04:00
u_int8_t cb_id = NFNL_MSG_TYPE ( nlh - > nlmsg_type ) ;
2009-06-02 22:03:33 +04:00
struct nlattr * cda [ ss - > cb [ cb_id ] . attr_count + 1 ] ;
struct nlattr * attr = ( void * ) nlh + min_len ;
int attrlen = nlh - > nlmsg_len - min_len ;
2013-02-05 04:50:26 +04:00
__u8 subsys_id = NFNL_SUBSYS_ID ( type ) ;
2009-06-02 22:03:33 +04:00
err = nla_parse ( cda , ss - > cb [ cb_id ] . attr_count ,
attr , attrlen , ss - > cb [ cb_id ] . policy ) ;
2012-06-28 06:57:49 +04:00
if ( err < 0 ) {
rcu_read_unlock ( ) ;
2009-06-02 22:03:33 +04:00
return err ;
2012-06-28 06:57:49 +04:00
}
2007-02-12 22:15:49 +03:00
2011-07-18 18:08:07 +04:00
if ( nc - > call_rcu ) {
err = nc - > call_rcu ( net - > nfnl , skb , nlh ,
( const struct nlattr * * ) cda ) ;
rcu_read_unlock ( ) ;
} else {
rcu_read_unlock ( ) ;
2013-02-05 04:50:26 +04:00
nfnl_lock ( subsys_id ) ;
if ( rcu_dereference_protected ( table [ subsys_id ] . subsys ,
2013-03-04 06:45:41 +04:00
lockdep_is_held ( & table [ subsys_id ] . mutex ) ) ! = ss | |
2011-07-18 18:08:07 +04:00
nfnetlink_find_client ( type , ss ) ! = nc )
err = - EAGAIN ;
2012-06-28 06:57:47 +04:00
else if ( nc - > call )
2011-07-18 18:08:07 +04:00
err = nc - > call ( net - > nfnl , skb , nlh ,
( const struct nlattr * * ) cda ) ;
2012-06-28 06:57:47 +04:00
else
err = - EINVAL ;
2013-02-05 04:50:26 +04:00
nfnl_unlock ( subsys_id ) ;
2011-07-18 18:08:07 +04:00
}
2008-10-14 22:58:31 +04:00
if ( err = = - EAGAIN )
goto replay ;
return err ;
2005-08-10 06:30:24 +04:00
}
}
2007-10-11 08:15:29 +04:00
static void nfnetlink_rcv ( struct sk_buff * skb )
2005-08-10 06:30:24 +04:00
{
2007-10-11 08:15:29 +04:00
netlink_rcv_skb ( skb , & nfnetlink_rcv_msg ) ;
2005-08-10 06:30:24 +04:00
}
2012-06-29 10:15:22 +04:00
# ifdef CONFIG_MODULES
static void nfnetlink_bind ( int group )
{
const struct nfnetlink_subsystem * ss ;
int type = nfnl_group2type [ group ] ;
rcu_read_lock ( ) ;
ss = nfnetlink_get_subsys ( type ) ;
if ( ! ss ) {
rcu_read_unlock ( ) ;
request_module ( " nfnetlink-subsys-%d " , type ) ;
return ;
}
rcu_read_unlock ( ) ;
}
# endif
2010-01-13 18:02:14 +03:00
static int __net_init nfnetlink_net_init ( struct net * net )
2005-08-10 06:30:24 +04:00
{
2010-01-13 18:02:14 +03:00
struct sock * nfnl ;
2012-06-29 10:15:21 +04:00
struct netlink_kernel_cfg cfg = {
. groups = NFNLGRP_MAX ,
. input = nfnetlink_rcv ,
2012-06-29 10:15:22 +04:00
# ifdef CONFIG_MODULES
. bind = nfnetlink_bind ,
# endif
2012-06-29 10:15:21 +04:00
} ;
2010-01-13 18:02:14 +03:00
2012-09-08 06:53:54 +04:00
nfnl = netlink_kernel_create ( net , NETLINK_NETFILTER , & cfg ) ;
2010-01-13 18:02:14 +03:00
if ( ! nfnl )
return - ENOMEM ;
net - > nfnl_stash = nfnl ;
2012-01-12 08:41:32 +04:00
rcu_assign_pointer ( net - > nfnl , nfnl ) ;
2010-01-13 18:02:14 +03:00
return 0 ;
2005-08-10 06:30:24 +04:00
}
2010-01-13 18:02:14 +03:00
static void __net_exit nfnetlink_net_exit_batch ( struct list_head * net_exit_list )
2005-08-10 06:30:24 +04:00
{
2010-01-13 18:02:14 +03:00
struct net * net ;
2005-08-10 06:30:24 +04:00
2010-01-13 18:02:14 +03:00
list_for_each_entry ( net , net_exit_list , exit_list )
2011-08-01 20:19:00 +04:00
RCU_INIT_POINTER ( net - > nfnl , NULL ) ;
2010-01-13 18:02:14 +03:00
synchronize_net ( ) ;
list_for_each_entry ( net , net_exit_list , exit_list )
netlink_kernel_release ( net - > nfnl_stash ) ;
}
2005-08-10 06:30:24 +04:00
2010-01-13 18:02:14 +03:00
static struct pernet_operations nfnetlink_net_ops = {
. init = nfnetlink_net_init ,
. exit_batch = nfnetlink_net_exit_batch ,
} ;
static int __init nfnetlink_init ( void )
{
2013-02-05 04:50:26 +04:00
int i ;
for ( i = 0 ; i < NFNL_SUBSYS_COUNT ; i + + )
mutex_init ( & table [ i ] . mutex ) ;
2010-05-13 17:02:08 +04:00
pr_info ( " Netfilter messages via NETLINK v%s. \n " , nfversion ) ;
2010-01-13 18:02:14 +03:00
return register_pernet_subsys ( & nfnetlink_net_ops ) ;
2005-08-10 06:30:24 +04:00
}
2010-01-13 18:02:14 +03:00
static void __exit nfnetlink_exit ( void )
{
2010-05-13 17:02:08 +04:00
pr_info ( " Removing netfilter NETLINK layer. \n " ) ;
2010-01-13 18:02:14 +03:00
unregister_pernet_subsys ( & nfnetlink_net_ops ) ;
}
2005-08-10 06:30:24 +04:00
module_init ( nfnetlink_init ) ;
module_exit ( nfnetlink_exit ) ;