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/major.h>
# include <linux/timer.h>
# include <linux/string.h>
# include <linux/sockios.h>
# include <linux/net.h>
# include <linux/fcntl.h>
# include <linux/skbuff.h>
# include <asm/uaccess.h>
# include <asm/system.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 " ;
static struct sock * nfnl = NULL ;
2007-09-29 01:15:45 +04:00
static const struct nfnetlink_subsystem * 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
2007-10-11 08:13:32 +04:00
static inline 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
}
2007-10-11 08:13:32 +04:00
static inline void nfnl_unlock ( void )
2007-03-15 02:39:25 +03:00
{
mutex_unlock ( & nfnl_mutex ) ;
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 ;
}
2005-08-10 06:30:24 +04:00
subsys_table [ n - > subsys_id ] = n ;
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 ( ) ;
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 ;
return subsys_table [ subsys_id ] ;
}
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 ] ;
}
2006-03-21 05:03:59 +03:00
int nfnetlink_has_listeners ( unsigned int group )
{
return netlink_has_listeners ( nfnl , group ) ;
}
EXPORT_SYMBOL_GPL ( nfnetlink_has_listeners ) ;
2005-08-10 06:30:24 +04:00
int nfnetlink_send ( struct sk_buff * skb , u32 pid , unsigned group , int echo )
{
2007-09-29 01:38:07 +04:00
return nlmsg_notify ( nfnl , skb , pid , group , echo , gfp_any ( ) ) ;
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
int nfnetlink_unicast ( struct sk_buff * skb , u_int32_t pid , int flags )
{
return netlink_unicast ( nfnl , skb , pid , flags ) ;
}
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
{
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
2007-03-23 09:30:12 +03:00
if ( security_netlink_recv ( skb , CAP_NET_ADMIN ) )
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 */
2007-03-15 02:40:38 +03:00
if ( nlh - > nlmsg_len < NLMSG_SPACE ( sizeof ( struct nfgenmsg ) ) )
2005-08-10 06:30:24 +04:00
return 0 ;
type = nlh - > nlmsg_type ;
ss = nfnetlink_get_subsys ( type ) ;
2005-08-10 06:43:44 +04:00
if ( ! ss ) {
# ifdef CONFIG_KMOD
2007-10-11 08:13:32 +04:00
nfnl_unlock ( ) ;
2005-11-15 02:24:59 +03:00
request_module ( " nfnetlink-subsys-%d " , NFNL_SUBSYS_ID ( type ) ) ;
2007-03-15 02:39:25 +03:00
nfnl_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
2007-03-23 09:30:12 +03:00
return - EINVAL ;
2005-08-10 06:43:44 +04:00
}
2005-08-10 06:30:24 +04:00
nc = nfnetlink_find_client ( type , ss ) ;
2007-03-15 02:40:38 +03:00
if ( ! nc )
2007-03-23 09:30:12 +03:00
return - EINVAL ;
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 ) ;
u_int16_t attr_count = ss - > cb [ cb_id ] . attr_count ;
2007-09-29 01:37:03 +04:00
struct nlattr * cda [ attr_count + 1 ] ;
2005-08-10 06:30:24 +04:00
2007-09-29 01:38:52 +04:00
if ( likely ( nlh - > nlmsg_len > = min_len ) ) {
struct nlattr * attr = ( void * ) nlh + NLMSG_ALIGN ( min_len ) ;
int attrlen = nlh - > nlmsg_len - NLMSG_ALIGN ( min_len ) ;
err = nla_parse ( cda , attr_count , attr , attrlen ,
ss - > cb [ cb_id ] . policy ) ;
if ( err < 0 )
return err ;
} else
return - EINVAL ;
2007-02-12 22:15:49 +03:00
2007-03-23 09:30:12 +03:00
return nc - > call ( nfnl , skb , nlh , cda ) ;
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
nfnl_lock ( ) ;
netlink_rcv_skb ( skb , & nfnetlink_rcv_msg ) ;
nfnl_unlock ( ) ;
2005-08-10 06:30:24 +04:00
}
2005-09-06 05:06:45 +04:00
static void __exit nfnetlink_exit ( void )
2005-08-10 06:30:24 +04:00
{
printk ( " Removing netfilter NETLINK layer. \n " ) ;
sock_release ( nfnl - > sk_socket ) ;
return ;
}
2005-09-06 05:06:45 +04:00
static int __init nfnetlink_init ( void )
2005-08-10 06:30:24 +04:00
{
printk ( " Netfilter messages via NETLINK v%s. \n " , nfversion ) ;
2007-09-12 15:05:38 +04:00
nfnl = netlink_kernel_create ( & init_net , NETLINK_NETFILTER , NFNLGRP_MAX ,
2007-04-21 01:14:21 +04:00
nfnetlink_rcv , NULL , THIS_MODULE ) ;
2005-08-10 06:30:24 +04:00
if ( ! nfnl ) {
printk ( KERN_ERR " cannot initialize nfnetlink! \n " ) ;
return - 1 ;
}
return 0 ;
}
module_init ( nfnetlink_init ) ;
module_exit ( nfnetlink_exit ) ;