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>
2007-03-15 02:39:25 +03:00
# include <net/netlink.h>
2005-08-10 06:30:24 +04:00
# include <linux/init.h>
# include <linux/netlink.h>
# 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 " ;
2011-07-18 18:08:07 +04:00
static const struct nfnetlink_subsystem __rcu * subsys_table [ NFNL_SUBSYS_COUNT ] ;
2007-03-15 02:39:25 +03:00
static DEFINE_MUTEX ( nfnl_mutex ) ;
2005-08-10 06:30:24 +04:00
2008-10-14 22:58:31 +04:00
void nfnl_lock ( void )
2005-08-10 06:30:24 +04:00
{
2007-03-15 02:39:25 +03:00
mutex_lock ( & nfnl_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
2008-10-14 22:58:31 +04:00
void nfnl_unlock ( void )
2007-03-15 02:39:25 +03:00
{
mutex_unlock ( & nfnl_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
{
nfnl_lock ( ) ;
2005-08-10 06:43:44 +04:00
if ( subsys_table [ n - > subsys_id ] ) {
nfnl_unlock ( ) ;
return - EBUSY ;
}
2012-01-12 08:41:32 +04:00
rcu_assign_pointer ( subsys_table [ n - > subsys_id ] , n ) ;
2005-08-10 06:30:24 +04:00
nfnl_unlock ( ) ;
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
{
nfnl_lock ( ) ;
subsys_table [ n - > subsys_id ] = NULL ;
nfnl_unlock ( ) ;
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 ;
2011-07-18 18:08:07 +04:00
return rcu_dereference ( subsys_table [ subsys_id ] ) ;
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 ) ;
2010-01-13 18:02:14 +03:00
int nfnetlink_send ( struct sk_buff * skb , struct net * net , u32 pid ,
2012-04-15 09:58:06 +04:00
unsigned int group , int echo , gfp_t flags )
2005-08-10 06:30:24 +04:00
{
2010-01-13 18:02:14 +03:00
return nlmsg_notify ( net - > nfnl , skb , pid , 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
2010-03-16 16:30:21 +03:00
int nfnetlink_set_err ( struct net * net , u32 pid , u32 group , int error )
2009-03-23 15:21:06 +03:00
{
2010-03-16 16:30:21 +03:00
return netlink_set_err ( net - > nfnl , pid , group , error ) ;
2009-03-23 15:21:06 +03:00
}
EXPORT_SYMBOL_GPL ( nfnetlink_set_err ) ;
2010-01-13 18:02:14 +03:00
int nfnetlink_unicast ( struct sk_buff * skb , struct net * net , u_int32_t pid , int flags )
2005-08-10 06:30:24 +04:00
{
2010-01-13 18:02:14 +03:00
return netlink_unicast ( net - > nfnl , skb , pid , 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-01-03 21:25:16 +04:00
if ( ! capable ( 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 */
2009-06-02 22:03:33 +04:00
if ( nlh - > nlmsg_len < NLMSG_LENGTH ( 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
{
2007-09-29 01:38:52 +04:00
int min_len = NLMSG_SPACE ( sizeof ( struct nfgenmsg ) ) ;
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 ;
err = nla_parse ( cda , ss - > cb [ cb_id ] . attr_count ,
attr , attrlen , ss - > cb [ cb_id ] . policy ) ;
if ( err < 0 )
return err ;
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 ( ) ;
nfnl_lock ( ) ;
if ( rcu_dereference_protected (
subsys_table [ NFNL_SUBSYS_ID ( type ) ] ,
lockdep_is_held ( & nfnl_mutex ) ) ! = ss | |
nfnetlink_find_client ( type , ss ) ! = nc )
err = - EAGAIN ;
else
err = nc - > call ( net - > nfnl , skb , nlh ,
( const struct nlattr * * ) cda ) ;
nfnl_unlock ( ) ;
}
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
}
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 ;
nfnl = netlink_kernel_create ( net , NETLINK_NETFILTER , NFNLGRP_MAX ,
nfnetlink_rcv , NULL , THIS_MODULE ) ;
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 )
{
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 ) ;