2005-04-17 02:20:36 +04:00
/*
* net / sched / ipt . c iptables target interface
*
* TODO : Add other tables . For now we only support the ipv4 table targets
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* Copyright : Jamal Hadi Salim ( 2002 - 4 )
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/skbuff.h>
# include <linux/rtnetlink.h>
# include <linux/module.h>
# include <linux/init.h>
2007-03-26 10:06:12 +04:00
# include <net/netlink.h>
2005-04-17 02:20:36 +04:00
# include <net/pkt_sched.h>
# include <linux/tc_act/tc_ipt.h>
# include <net/tc_act/tc_ipt.h>
# include <linux/netfilter_ipv4/ip_tables.h>
2006-08-22 10:54:55 +04:00
# define IPT_TAB_MASK 15
static struct tcf_common * tcf_ipt_ht [ IPT_TAB_MASK + 1 ] ;
static u32 ipt_idx_gen ;
2005-04-17 02:20:36 +04:00
static DEFINE_RWLOCK ( ipt_lock ) ;
2006-08-22 10:54:55 +04:00
static struct tcf_hashinfo ipt_hash_info = {
. htab = tcf_ipt_ht ,
. hmask = IPT_TAB_MASK ,
. lock = & ipt_lock ,
} ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
static int ipt_init_target ( struct ipt_entry_target * t , char * table , unsigned int hook )
2005-04-17 02:20:36 +04:00
{
2007-02-08 02:12:33 +03:00
struct xt_target * target ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
2007-02-02 11:40:36 +03:00
target = xt_request_find_target ( AF_INET , t - > u . user . name ,
t - > u . user . revision ) ;
2005-04-17 02:20:36 +04:00
if ( ! target )
return - ENOENT ;
t - > u . kernel . target = target ;
2006-04-25 04:18:59 +04:00
ret = xt_check_target ( target , AF_INET , t - > u . target_size - sizeof ( * t ) ,
table , hook , 0 , 0 ) ;
2007-02-02 11:40:36 +03:00
if ( ret ) {
module_put ( t - > u . kernel . target - > me ) ;
2006-04-25 04:18:59 +04:00
return ret ;
2007-02-02 11:40:36 +03:00
}
2005-04-17 02:20:36 +04:00
if ( t - > u . kernel . target - > checkentry
2006-03-21 05:02:15 +03:00
& & ! t - > u . kernel . target - > checkentry ( table , NULL ,
2007-02-09 17:25:16 +03:00
t - > u . kernel . target , t - > data ,
2005-04-17 02:20:36 +04:00
hook ) ) {
module_put ( t - > u . kernel . target - > me ) ;
ret = - EINVAL ;
}
return ret ;
}
2006-08-22 10:54:55 +04:00
static void ipt_destroy_target ( struct ipt_entry_target * t )
2005-04-17 02:20:36 +04:00
{
if ( t - > u . kernel . target - > destroy )
2006-08-22 11:36:37 +04:00
t - > u . kernel . target - > destroy ( t - > u . kernel . target , t - > data ) ;
2007-02-09 17:25:16 +03:00
module_put ( t - > u . kernel . target - > me ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 10:54:55 +04:00
static int tcf_ipt_release ( struct tcf_ipt * ipt , int bind )
2005-04-17 02:20:36 +04:00
{
int ret = 0 ;
2006-08-22 10:54:55 +04:00
if ( ipt ) {
2005-04-17 02:20:36 +04:00
if ( bind )
2006-08-22 10:54:55 +04:00
ipt - > tcf_bindcnt - - ;
ipt - > tcf_refcnt - - ;
if ( ipt - > tcf_bindcnt < = 0 & & ipt - > tcf_refcnt < = 0 ) {
ipt_destroy_target ( ipt - > tcfi_t ) ;
kfree ( ipt - > tcfi_tname ) ;
kfree ( ipt - > tcfi_t ) ;
tcf_hash_destroy ( & ipt - > common , & ipt_hash_info ) ;
2005-04-17 02:20:36 +04:00
ret = ACT_P_DELETED ;
}
}
return ret ;
}
2008-01-23 09:11:50 +03:00
static int tcf_ipt_init ( struct nlattr * nla , struct nlattr * est ,
2006-08-22 10:54:55 +04:00
struct tc_action * a , int ovr , int bind )
2005-04-17 02:20:36 +04:00
{
2008-01-23 09:11:50 +03:00
struct nlattr * tb [ TCA_IPT_MAX + 1 ] ;
2006-08-22 10:54:55 +04:00
struct tcf_ipt * ipt ;
struct tcf_common * pc ;
2005-04-17 02:20:36 +04:00
struct ipt_entry_target * td , * t ;
char * tname ;
int ret = 0 , err ;
u32 hook = 0 ;
u32 index = 0 ;
2008-01-23 09:11:50 +03:00
if ( nla = = NULL | | nla_parse_nested ( tb , TCA_IPT_MAX , nla , NULL ) < 0 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2008-01-23 09:11:50 +03:00
if ( tb [ TCA_IPT_HOOK ] = = NULL | |
nla_len ( tb [ TCA_IPT_HOOK ] ) < sizeof ( u32 ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2008-01-23 09:11:50 +03:00
if ( tb [ TCA_IPT_TARG ] = = NULL | |
nla_len ( tb [ TCA_IPT_TARG ] ) < sizeof ( * t ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2008-01-23 09:11:50 +03:00
td = ( struct ipt_entry_target * ) nla_data ( tb [ TCA_IPT_TARG ] ) ;
if ( nla_len ( tb [ TCA_IPT_TARG ] ) < td - > u . target_size )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2008-01-23 09:11:50 +03:00
if ( tb [ TCA_IPT_INDEX ] ! = NULL & &
nla_len ( tb [ TCA_IPT_INDEX ] ) > = sizeof ( u32 ) )
index = * ( u32 * ) nla_data ( tb [ TCA_IPT_INDEX ] ) ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
pc = tcf_hash_check ( index , a , bind , & ipt_hash_info ) ;
if ( ! pc ) {
pc = tcf_hash_create ( index , est , a , sizeof ( * ipt ) , bind ,
& ipt_idx_gen , & ipt_hash_info ) ;
if ( unlikely ( ! pc ) )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
ret = ACT_P_CREATED ;
} else {
if ( ! ovr ) {
2006-08-22 10:54:55 +04:00
tcf_ipt_release ( to_ipt ( pc ) , bind ) ;
2005-04-17 02:20:36 +04:00
return - EEXIST ;
}
}
2006-08-22 10:54:55 +04:00
ipt = to_ipt ( pc ) ;
2005-04-17 02:20:36 +04:00
2008-01-23 09:11:50 +03:00
hook = * ( u32 * ) nla_data ( tb [ TCA_IPT_HOOK ] ) ;
2005-04-17 02:20:36 +04:00
err = - ENOMEM ;
tname = kmalloc ( IFNAMSIZ , GFP_KERNEL ) ;
2006-08-22 10:54:55 +04:00
if ( unlikely ( ! tname ) )
2005-04-17 02:20:36 +04:00
goto err1 ;
2008-01-23 09:11:50 +03:00
if ( tb [ TCA_IPT_TABLE ] = = NULL | |
nla_strlcpy ( tname , tb [ TCA_IPT_TABLE ] , IFNAMSIZ ) > = IFNAMSIZ )
2005-04-17 02:20:36 +04:00
strcpy ( tname , " mangle " ) ;
2006-11-21 06:19:40 +03:00
t = kmemdup ( td , td - > u . target_size , GFP_KERNEL ) ;
2006-08-22 10:54:55 +04:00
if ( unlikely ( ! t ) )
2005-04-17 02:20:36 +04:00
goto err2 ;
if ( ( err = ipt_init_target ( t , tname , hook ) ) < 0 )
goto err3 ;
2006-08-22 10:54:55 +04:00
spin_lock_bh ( & ipt - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
if ( ret ! = ACT_P_CREATED ) {
2006-08-22 10:54:55 +04:00
ipt_destroy_target ( ipt - > tcfi_t ) ;
kfree ( ipt - > tcfi_tname ) ;
kfree ( ipt - > tcfi_t ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 10:54:55 +04:00
ipt - > tcfi_tname = tname ;
ipt - > tcfi_t = t ;
ipt - > tcfi_hook = hook ;
spin_unlock_bh ( & ipt - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
if ( ret = = ACT_P_CREATED )
2006-08-22 10:54:55 +04:00
tcf_hash_insert ( pc , & ipt_hash_info ) ;
2005-04-17 02:20:36 +04:00
return ret ;
err3 :
kfree ( t ) ;
err2 :
kfree ( tname ) ;
err1 :
2006-08-22 10:54:55 +04:00
kfree ( pc ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2006-08-22 10:54:55 +04:00
static int tcf_ipt_cleanup ( struct tc_action * a , int bind )
2005-04-17 02:20:36 +04:00
{
2006-08-22 10:54:55 +04:00
struct tcf_ipt * ipt = a - > priv ;
return tcf_ipt_release ( ipt , bind ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-22 10:54:55 +04:00
static int tcf_ipt ( struct sk_buff * skb , struct tc_action * a ,
struct tcf_result * res )
2005-04-17 02:20:36 +04:00
{
int ret = 0 , result = 0 ;
2006-08-22 10:54:55 +04:00
struct tcf_ipt * ipt = a - > priv ;
2005-04-17 02:20:36 +04:00
if ( skb_cloned ( skb ) ) {
if ( pskb_expand_head ( skb , 0 , 0 , GFP_ATOMIC ) )
return TC_ACT_UNSPEC ;
}
2006-08-22 10:54:55 +04:00
spin_lock ( & ipt - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
ipt - > tcf_tm . lastuse = jiffies ;
ipt - > tcf_bstats . bytes + = skb - > len ;
ipt - > tcf_bstats . packets + + ;
2005-04-17 02:20:36 +04:00
/* yes, we have to worry about both in and out dev
worry later - danger - this API seems to have changed
from earlier kernels */
2007-10-15 11:53:15 +04:00
ret = ipt - > tcfi_t - > u . kernel . target - > target ( skb , skb - > dev , NULL ,
2006-08-22 10:54:55 +04:00
ipt - > tcfi_hook ,
ipt - > tcfi_t - > u . kernel . target ,
2006-08-22 11:35:47 +04:00
ipt - > tcfi_t - > data ) ;
2005-04-17 02:20:36 +04:00
switch ( ret ) {
case NF_ACCEPT :
result = TC_ACT_OK ;
break ;
case NF_DROP :
result = TC_ACT_SHOT ;
2006-08-22 10:54:55 +04:00
ipt - > tcf_qstats . drops + + ;
2005-04-17 02:20:36 +04:00
break ;
case IPT_CONTINUE :
result = TC_ACT_PIPE ;
break ;
default :
if ( net_ratelimit ( ) )
printk ( " Bogus netfilter code %d assume ACCEPT \n " , ret ) ;
result = TC_POLICE_OK ;
break ;
}
2006-08-22 10:54:55 +04:00
spin_unlock ( & ipt - > tcf_lock ) ;
2005-04-17 02:20:36 +04:00
return result ;
}
2006-08-22 10:54:55 +04:00
static int tcf_ipt_dump ( struct sk_buff * skb , struct tc_action * a , int bind , int ref )
2005-04-17 02:20:36 +04:00
{
2007-04-20 07:29:13 +04:00
unsigned char * b = skb_tail_pointer ( skb ) ;
2006-08-22 10:54:55 +04:00
struct tcf_ipt * ipt = a - > priv ;
2005-04-17 02:20:36 +04:00
struct ipt_entry_target * t ;
struct tcf_t tm ;
struct tc_cnt c ;
/* for simple targets kernel size == user size
* * user name = target name
* * for foolproof you need to not assume this
*/
2006-11-21 06:19:40 +03:00
t = kmemdup ( ipt - > tcfi_t , ipt - > tcfi_t - > u . user . target_size , GFP_ATOMIC ) ;
2006-08-22 10:54:55 +04:00
if ( unlikely ( ! t ) )
2008-01-23 09:11:50 +03:00
goto nla_put_failure ;
2005-04-17 02:20:36 +04:00
2006-08-22 10:54:55 +04:00
c . bindcnt = ipt - > tcf_bindcnt - bind ;
c . refcnt = ipt - > tcf_refcnt - ref ;
strcpy ( t - > u . user . name , ipt - > tcfi_t - > u . kernel . target - > name ) ;
2008-01-23 09:11:50 +03:00
NLA_PUT ( skb , TCA_IPT_TARG , ipt - > tcfi_t - > u . user . target_size , t ) ;
NLA_PUT ( skb , TCA_IPT_INDEX , 4 , & ipt - > tcf_index ) ;
NLA_PUT ( skb , TCA_IPT_HOOK , 4 , & ipt - > tcfi_hook ) ;
NLA_PUT ( skb , TCA_IPT_CNT , sizeof ( struct tc_cnt ) , & c ) ;
NLA_PUT ( skb , TCA_IPT_TABLE , IFNAMSIZ , ipt - > tcfi_tname ) ;
2006-08-22 10:54:55 +04:00
tm . install = jiffies_to_clock_t ( jiffies - ipt - > tcf_tm . install ) ;
tm . lastuse = jiffies_to_clock_t ( jiffies - ipt - > tcf_tm . lastuse ) ;
tm . expires = jiffies_to_clock_t ( ipt - > tcf_tm . expires ) ;
2008-01-23 09:11:50 +03:00
NLA_PUT ( skb , TCA_IPT_TM , sizeof ( tm ) , & tm ) ;
2005-04-17 02:20:36 +04:00
kfree ( t ) ;
return skb - > len ;
2008-01-23 09:11:50 +03:00
nla_put_failure :
2007-03-26 10:06:12 +04:00
nlmsg_trim ( skb , b ) ;
2005-04-17 02:20:36 +04:00
kfree ( t ) ;
return - 1 ;
}
static struct tc_action_ops act_ipt_ops = {
. kind = " ipt " ,
2006-08-22 10:54:55 +04:00
. hinfo = & ipt_hash_info ,
2005-04-17 02:20:36 +04:00
. type = TCA_ACT_IPT ,
. capab = TCA_CAP_NONE ,
. owner = THIS_MODULE ,
. act = tcf_ipt ,
. dump = tcf_ipt_dump ,
. cleanup = tcf_ipt_cleanup ,
. lookup = tcf_hash_search ,
. init = tcf_ipt_init ,
. walk = tcf_generic_walker
} ;
MODULE_AUTHOR ( " Jamal Hadi Salim(2002-4) " ) ;
MODULE_DESCRIPTION ( " Iptables target actions " ) ;
MODULE_LICENSE ( " GPL " ) ;
2006-08-22 10:54:55 +04:00
static int __init ipt_init_module ( void )
2005-04-17 02:20:36 +04:00
{
return tcf_register_action ( & act_ipt_ops ) ;
}
2006-08-22 10:54:55 +04:00
static void __exit ipt_cleanup_module ( void )
2005-04-17 02:20:36 +04:00
{
tcf_unregister_action ( & act_ipt_ops ) ;
}
module_init ( ipt_init_module ) ;
module_exit ( ipt_cleanup_module ) ;