2007-02-09 17:25:16 +03:00
/* net/sched/sch_ingress.c - Ingress qdisc
2005-04-17 02:20:36 +04:00
* 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 .
*
* Authors : Jamal Hadi Salim 1999
*/
# include <linux/module.h>
# include <linux/types.h>
2007-07-03 09:49:07 +04:00
# include <linux/list.h>
2005-04-17 02:20:36 +04:00
# include <linux/skbuff.h>
# include <linux/rtnetlink.h>
# include <linux/netfilter_ipv4.h>
# include <linux/netfilter_ipv6.h>
# include <linux/netfilter.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>
2008-01-21 11:11:01 +03:00
/* Thanks to Doron Oz for this hack */
2005-04-17 02:20:36 +04:00
# ifndef CONFIG_NET_CLS_ACT
# ifdef CONFIG_NETFILTER
2007-02-09 17:25:16 +03:00
static int nf_registered ;
2005-04-17 02:20:36 +04:00
# endif
# endif
struct ingress_qdisc_data {
struct tcf_proto * filter_list ;
} ;
/* ------------------------- Class/flow operations ------------------------- */
2008-01-21 11:11:01 +03:00
static int ingress_graft ( struct Qdisc * sch , unsigned long arg ,
struct Qdisc * new , struct Qdisc * * old )
2005-04-17 02:20:36 +04:00
{
2007-02-09 17:25:16 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
static struct Qdisc * ingress_leaf ( struct Qdisc * sch , unsigned long arg )
{
return NULL ;
}
2008-01-21 11:11:01 +03:00
static unsigned long ingress_get ( struct Qdisc * sch , u32 classid )
2005-04-17 02:20:36 +04:00
{
return TC_H_MIN ( classid ) + 1 ;
}
static unsigned long ingress_bind_filter ( struct Qdisc * sch ,
2008-01-21 11:11:01 +03:00
unsigned long parent , u32 classid )
2005-04-17 02:20:36 +04:00
{
return ingress_get ( sch , classid ) ;
}
static void ingress_put ( struct Qdisc * sch , unsigned long cl )
{
}
static int ingress_change ( struct Qdisc * sch , u32 classid , u32 parent ,
2008-01-21 11:11:01 +03:00
struct rtattr * * tca , unsigned long * arg )
2005-04-17 02:20:36 +04:00
{
return 0 ;
}
2008-01-21 11:11:01 +03:00
static void ingress_walk ( struct Qdisc * sch , struct qdisc_walker * walker )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:11:21 +03:00
return ;
2005-04-17 02:20:36 +04:00
}
2008-01-21 11:11:01 +03:00
static struct tcf_proto * * ingress_find_tcf ( struct Qdisc * sch , unsigned long cl )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:11:48 +03:00
struct ingress_qdisc_data * p = qdisc_priv ( sch ) ;
2005-04-17 02:20:36 +04:00
return & p - > filter_list ;
}
/* --------------------------- Qdisc operations ---------------------------- */
2008-01-21 11:11:01 +03:00
static int ingress_enqueue ( struct sk_buff * skb , struct Qdisc * sch )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:11:48 +03:00
struct ingress_qdisc_data * p = qdisc_priv ( sch ) ;
2005-04-17 02:20:36 +04:00
struct tcf_result res ;
int result ;
result = tc_classify ( skb , p - > filter_list , & res ) ;
2008-01-21 11:11:21 +03:00
2005-04-17 02:20:36 +04:00
/*
* Unlike normal " enqueue " functions , ingress_enqueue returns a
* firewall FW_ * code .
*/
# ifdef CONFIG_NET_CLS_ACT
sch - > bstats . packets + + ;
sch - > bstats . bytes + = skb - > len ;
switch ( result ) {
2008-01-21 11:11:01 +03:00
case TC_ACT_SHOT :
result = TC_ACT_SHOT ;
sch - > qstats . drops + + ;
break ;
case TC_ACT_STOLEN :
case TC_ACT_QUEUED :
result = TC_ACT_STOLEN ;
break ;
case TC_ACT_RECLASSIFY :
case TC_ACT_OK :
skb - > tc_index = TC_H_MIN ( res . classid ) ;
default :
result = TC_ACT_OK ;
break ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
# else
result = NF_ACCEPT ;
sch - > bstats . packets + + ;
sch - > bstats . bytes + = skb - > len ;
# endif
return result ;
}
static struct sk_buff * ingress_dequeue ( struct Qdisc * sch )
{
return NULL ;
}
2008-01-21 11:11:01 +03:00
static int ingress_requeue ( struct sk_buff * skb , struct Qdisc * sch )
2005-04-17 02:20:36 +04:00
{
return 0 ;
}
static unsigned int ingress_drop ( struct Qdisc * sch )
{
return 0 ;
}
# ifndef CONFIG_NET_CLS_ACT
# ifdef CONFIG_NETFILTER
2008-01-21 11:11:01 +03:00
static unsigned int ing_hook ( unsigned int hook , struct sk_buff * skb ,
2007-02-09 17:25:16 +03:00
const struct net_device * indev ,
const struct net_device * outdev ,
int ( * okfn ) ( struct sk_buff * ) )
2005-04-17 02:20:36 +04:00
{
2007-02-09 17:25:16 +03:00
2005-04-17 02:20:36 +04:00
struct Qdisc * q ;
2007-02-09 17:25:16 +03:00
struct net_device * dev = skb - > dev ;
2008-01-21 11:11:01 +03:00
int fwres = NF_ACCEPT ;
2005-04-17 02:20:36 +04:00
if ( dev - > qdisc_ingress ) {
2007-04-17 04:07:08 +04:00
spin_lock ( & dev - > ingress_lock ) ;
2005-04-17 02:20:36 +04:00
if ( ( q = dev - > qdisc_ingress ) ! = NULL )
fwres = q - > enqueue ( skb , q ) ;
2007-04-17 04:07:08 +04:00
spin_unlock ( & dev - > ingress_lock ) ;
2007-02-09 17:25:16 +03:00
}
2005-04-17 02:20:36 +04:00
return fwres ;
}
/* after ipt_filter */
2007-12-05 12:23:00 +03:00
static struct nf_hook_ops ing_ops [ ] __read_mostly = {
2007-12-05 12:22:43 +03:00
{
. hook = ing_hook ,
. owner = THIS_MODULE ,
. pf = PF_INET ,
. hooknum = NF_INET_PRE_ROUTING ,
. priority = NF_IP_PRI_FILTER + 1 ,
} ,
{
. hook = ing_hook ,
. owner = THIS_MODULE ,
. pf = PF_INET6 ,
. hooknum = NF_INET_PRE_ROUTING ,
. priority = NF_IP6_PRI_FILTER + 1 ,
} ,
2005-04-17 02:20:36 +04:00
} ;
# endif
# endif
2008-01-21 11:11:01 +03:00
static int ingress_init ( struct Qdisc * sch , struct rtattr * opt )
2005-04-17 02:20:36 +04:00
{
2008-01-21 11:11:01 +03:00
/* Make sure either netfilter or preferably CLS_ACT is
* compiled in */
2005-04-17 02:20:36 +04:00
# ifndef CONFIG_NET_CLS_ACT
# ifndef CONFIG_NETFILTER
printk ( " You MUST compile classifier actions into the kernel \n " ) ;
return - EINVAL ;
# else
printk ( " Ingress scheduler: Classifier actions prefered over netfilter \n " ) ;
# endif
# endif
2007-02-09 17:25:16 +03:00
2005-04-17 02:20:36 +04:00
# ifndef CONFIG_NET_CLS_ACT
# ifdef CONFIG_NETFILTER
if ( ! nf_registered ) {
2007-12-05 12:22:43 +03:00
if ( nf_register_hooks ( ing_ops , ARRAY_SIZE ( ing_ops ) ) < 0 ) {
2005-04-17 02:20:36 +04:00
printk ( " ingress qdisc registration error \n " ) ;
return - EINVAL ;
}
nf_registered + + ;
}
# endif
# endif
return 0 ;
}
static void ingress_reset ( struct Qdisc * sch )
{
2008-01-21 11:11:21 +03:00
return ;
2005-04-17 02:20:36 +04:00
}
/* ------------------------------------------------------------- */
static void ingress_destroy ( struct Qdisc * sch )
{
2008-01-21 11:11:48 +03:00
struct ingress_qdisc_data * p = qdisc_priv ( sch ) ;
2005-04-17 02:20:36 +04:00
2007-03-23 21:29:43 +03:00
tcf_destroy_chain ( p - > filter_list ) ;
2005-04-17 02:20:36 +04:00
}
static int ingress_dump ( struct Qdisc * sch , struct sk_buff * skb )
{
2007-04-20 07:29:13 +04:00
unsigned char * b = skb_tail_pointer ( skb ) ;
2005-04-17 02:20:36 +04:00
struct rtattr * rta ;
2008-01-21 11:11:01 +03:00
rta = ( struct rtattr * ) b ;
2005-04-17 02:20:36 +04:00
RTA_PUT ( skb , TCA_OPTIONS , 0 , NULL ) ;
2007-04-20 07:29:13 +04:00
rta - > rta_len = skb_tail_pointer ( skb ) - b ;
2005-04-17 02:20:36 +04:00
return skb - > len ;
rtattr_failure :
2007-03-26 10:06:12 +04:00
nlmsg_trim ( skb , b ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2007-11-14 12:44:41 +03:00
static const struct Qdisc_class_ops ingress_class_ops = {
2005-04-17 02:20:36 +04:00
. graft = ingress_graft ,
. leaf = ingress_leaf ,
. get = ingress_get ,
. put = ingress_put ,
. change = ingress_change ,
. walk = ingress_walk ,
. tcf_chain = ingress_find_tcf ,
. bind_tcf = ingress_bind_filter ,
. unbind_tcf = ingress_put ,
} ;
2007-11-14 12:44:41 +03:00
static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
2005-04-17 02:20:36 +04:00
. cl_ops = & ingress_class_ops ,
. id = " ingress " ,
. priv_size = sizeof ( struct ingress_qdisc_data ) ,
. enqueue = ingress_enqueue ,
. dequeue = ingress_dequeue ,
. requeue = ingress_requeue ,
. drop = ingress_drop ,
. init = ingress_init ,
. reset = ingress_reset ,
. destroy = ingress_destroy ,
. dump = ingress_dump ,
. owner = THIS_MODULE ,
} ;
static int __init ingress_module_init ( void )
{
int ret = 0 ;
if ( ( ret = register_qdisc ( & ingress_qdisc_ops ) ) < 0 ) {
printk ( " Unable to register Ingress qdisc \n " ) ;
return ret ;
}
return ret ;
}
2008-01-21 11:11:01 +03:00
2007-02-09 17:25:16 +03:00
static void __exit ingress_module_exit ( void )
2005-04-17 02:20:36 +04:00
{
unregister_qdisc ( & ingress_qdisc_ops ) ;
# ifndef CONFIG_NET_CLS_ACT
# ifdef CONFIG_NETFILTER
2007-12-05 12:22:43 +03:00
if ( nf_registered )
nf_unregister_hooks ( ing_ops , ARRAY_SIZE ( ing_ops ) ) ;
2005-04-17 02:20:36 +04:00
# endif
# endif
}
2008-01-21 11:11:01 +03:00
2005-04-17 02:20:36 +04:00
module_init ( ingress_module_init )
module_exit ( ingress_module_exit )
MODULE_LICENSE ( " GPL " ) ;