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 <asm/uaccess.h>
# include <asm/system.h>
# include <asm/bitops.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/socket.h>
# include <linux/sockios.h>
# include <linux/in.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/rtnetlink.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/proc_fs.h>
# include <linux/kmod.h>
# include <net/sock.h>
# 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
{
struct ipt_target * target ;
int ret = 0 ;
[NETFILTER] x_tables: Abstraction layer for {ip,ip6,arp}_tables
This monster-patch tries to do the best job for unifying the data
structures and backend interfaces for the three evil clones ip_tables,
ip6_tables and arp_tables. In an ideal world we would never have
allowed this kind of copy+paste programming... but well, our world
isn't (yet?) ideal.
o introduce a new x_tables module
o {ip,arp,ip6}_tables depend on this x_tables module
o registration functions for tables, matches and targets are only
wrappers around x_tables provided functions
o all matches/targets that are used from ip_tables and ip6_tables
are now implemented as xt_FOOBAR.c files and provide module aliases
to ipt_FOOBAR and ip6t_FOOBAR
o header files for xt_matches are in include/linux/netfilter/,
include/linux/netfilter_{ipv4,ipv6} contains compatibility wrappers
around the xt_FOOBAR.h headers
Based on this patchset we're going to further unify the code,
gradually getting rid of all the layer 3 specific assumptions.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-01-13 00:30:04 +03:00
target = xt_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 ) ;
if ( ret )
return ret ;
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 ,
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 ) ;
2005-04-17 02:20:36 +04:00
module_put ( t - > u . kernel . target - > me ) ;
}
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 ;
}
2006-08-22 10:54:55 +04:00
static int tcf_ipt_init ( struct rtattr * rta , struct rtattr * est ,
struct tc_action * a , int ovr , int bind )
2005-04-17 02:20:36 +04:00
{
struct rtattr * tb [ TCA_IPT_MAX ] ;
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 ;
if ( rta = = NULL | | rtattr_parse_nested ( tb , TCA_IPT_MAX , rta ) < 0 )
return - EINVAL ;
if ( tb [ TCA_IPT_HOOK - 1 ] = = NULL | |
RTA_PAYLOAD ( tb [ TCA_IPT_HOOK - 1 ] ) < sizeof ( u32 ) )
return - EINVAL ;
if ( tb [ TCA_IPT_TARG - 1 ] = = NULL | |
RTA_PAYLOAD ( tb [ TCA_IPT_TARG - 1 ] ) < sizeof ( * t ) )
return - EINVAL ;
td = ( struct ipt_entry_target * ) RTA_DATA ( tb [ TCA_IPT_TARG - 1 ] ) ;
if ( RTA_PAYLOAD ( tb [ TCA_IPT_TARG - 1 ] ) < td - > u . target_size )
return - EINVAL ;
if ( tb [ TCA_IPT_INDEX - 1 ] ! = NULL & &
RTA_PAYLOAD ( tb [ TCA_IPT_INDEX - 1 ] ) > = sizeof ( u32 ) )
index = * ( u32 * ) RTA_DATA ( tb [ TCA_IPT_INDEX - 1 ] ) ;
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
hook = * ( u32 * ) RTA_DATA ( tb [ TCA_IPT_HOOK - 1 ] ) ;
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 ;
if ( tb [ TCA_IPT_TABLE - 1 ] = = NULL | |
rtattr_strlcpy ( tname , tb [ TCA_IPT_TABLE - 1 ] , IFNAMSIZ ) > = IFNAMSIZ )
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 */
2006-01-09 09:15:34 +03:00
/* iptables targets take a double skb pointer in case the skb
* needs to be replaced . We don ' t own the skb , so this must not
* happen . The pskb_expand_head above should make sure of this */
2006-08-22 10:54:55 +04:00
ret = ipt - > tcfi_t - > u . kernel . target - > target ( & skb , skb - > dev , NULL ,
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
{
2006-08-22 10:54:55 +04:00
unsigned char * b = skb - > tail ;
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 ) )
2005-04-17 02:20:36 +04:00
goto rtattr_failure ;
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 ) ;
RTA_PUT ( skb , TCA_IPT_TARG , ipt - > tcfi_t - > u . user . target_size , t ) ;
RTA_PUT ( skb , TCA_IPT_INDEX , 4 , & ipt - > tcf_index ) ;
RTA_PUT ( skb , TCA_IPT_HOOK , 4 , & ipt - > tcfi_hook ) ;
2005-04-17 02:20:36 +04:00
RTA_PUT ( skb , TCA_IPT_CNT , sizeof ( struct tc_cnt ) , & c ) ;
2006-08-22 10:54:55 +04:00
RTA_PUT ( skb , TCA_IPT_TABLE , IFNAMSIZ , ipt - > tcfi_tname ) ;
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 ) ;
2005-04-17 02:20:36 +04:00
RTA_PUT ( skb , TCA_IPT_TM , sizeof ( tm ) , & tm ) ;
kfree ( t ) ;
return skb - > len ;
2006-08-22 10:54:55 +04:00
rtattr_failure :
2005-04-17 02:20:36 +04:00
skb_trim ( skb , b - skb - > data ) ;
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 ) ;