2012-07-11 14:56:57 +04:00
/*
* net / sched / em_ipset . c ipset ematch
*
* Copyright ( c ) 2012 Florian Westphal < fw @ strlen . de >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*/
# include <linux/gfp.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/skbuff.h>
# include <linux/netfilter/xt_set.h>
# include <linux/ipv6.h>
# include <net/ip.h>
# include <net/pkt_cls.h>
2014-10-06 08:27:53 +04:00
static int em_ipset_change ( struct net * net , void * data , int data_len ,
2012-07-11 14:56:57 +04:00
struct tcf_ematch * em )
{
struct xt_set_info * set = data ;
ip_set_id_t index ;
if ( data_len ! = sizeof ( * set ) )
return - EINVAL ;
2013-09-30 19:07:02 +04:00
index = ip_set_nfnl_get_byindex ( net , set - > index ) ;
2012-07-11 14:56:57 +04:00
if ( index = = IPSET_INVALID_ID )
return - ENOENT ;
em - > datalen = sizeof ( * set ) ;
em - > data = ( unsigned long ) kmemdup ( data , em - > datalen , GFP_KERNEL ) ;
if ( em - > data )
return 0 ;
2013-09-30 19:07:02 +04:00
ip_set_nfnl_put ( net , index ) ;
2012-07-11 14:56:57 +04:00
return - ENOMEM ;
}
2014-10-06 08:27:53 +04:00
static void em_ipset_destroy ( struct tcf_ematch * em )
2012-07-11 14:56:57 +04:00
{
const struct xt_set_info * set = ( const void * ) em - > data ;
if ( set ) {
2014-10-06 08:27:53 +04:00
ip_set_nfnl_put ( em - > net , set - > index ) ;
2012-07-11 14:56:57 +04:00
kfree ( ( void * ) em - > data ) ;
}
}
static int em_ipset_match ( struct sk_buff * skb , struct tcf_ematch * em ,
struct tcf_pkt_info * info )
{
struct ip_set_adt_opt opt ;
struct xt_action_param acpar ;
const struct xt_set_info * set = ( const void * ) em - > data ;
struct net_device * dev , * indev = NULL ;
2016-11-03 12:56:21 +03:00
struct nf_hook_state state = {
. net = em - > net ,
} ;
2012-07-11 14:56:57 +04:00
int ret , network_offset ;
2015-01-13 19:13:43 +03:00
switch ( tc_skb_protocol ( skb ) ) {
2012-07-11 14:56:57 +04:00
case htons ( ETH_P_IP ) :
2016-11-03 12:56:21 +03:00
state . pf = NFPROTO_IPV4 ;
2012-07-11 14:56:57 +04:00
if ( ! pskb_network_may_pull ( skb , sizeof ( struct iphdr ) ) )
return 0 ;
acpar . thoff = ip_hdrlen ( skb ) ;
break ;
case htons ( ETH_P_IPV6 ) :
2016-11-03 12:56:21 +03:00
state . pf = NFPROTO_IPV6 ;
2012-07-11 14:56:57 +04:00
if ( ! pskb_network_may_pull ( skb , sizeof ( struct ipv6hdr ) ) )
return 0 ;
/* doesn't call ipv6_find_hdr() because ipset doesn't use thoff, yet */
acpar . thoff = sizeof ( struct ipv6hdr ) ;
break ;
default :
return 0 ;
}
2016-11-03 12:56:21 +03:00
opt . family = state . pf ;
2012-07-11 14:56:57 +04:00
opt . dim = set - > dim ;
opt . flags = set - > flags ;
opt . cmdflags = 0 ;
2013-04-27 16:28:55 +04:00
opt . ext . timeout = ~ 0u ;
2012-07-11 14:56:57 +04:00
network_offset = skb_network_offset ( skb ) ;
skb_pull ( skb , network_offset ) ;
dev = skb - > dev ;
rcu_read_lock ( ) ;
2015-06-17 18:28:17 +03:00
if ( skb - > skb_iif )
indev = dev_get_by_index_rcu ( em - > net , skb - > skb_iif ) ;
2012-07-11 14:56:57 +04:00
2016-11-03 12:56:21 +03:00
state . in = indev ? indev : dev ;
state . out = dev ;
acpar . state = & state ;
2012-07-11 14:56:57 +04:00
ret = ip_set_test ( set - > index , skb , & acpar , & opt ) ;
rcu_read_unlock ( ) ;
skb_push ( skb , network_offset ) ;
return ret ;
}
static struct tcf_ematch_ops em_ipset_ops = {
. kind = TCF_EM_IPSET ,
. change = em_ipset_change ,
. destroy = em_ipset_destroy ,
. match = em_ipset_match ,
. owner = THIS_MODULE ,
. link = LIST_HEAD_INIT ( em_ipset_ops . link )
} ;
static int __init init_em_ipset ( void )
{
return tcf_em_register ( & em_ipset_ops ) ;
}
static void __exit exit_em_ipset ( void )
{
tcf_em_unregister ( & em_ipset_ops ) ;
}
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Florian Westphal <fw@strlen.de> " ) ;
MODULE_DESCRIPTION ( " TC extended match for IP sets " ) ;
module_init ( init_em_ipset ) ;
module_exit ( exit_em_ipset ) ;
MODULE_ALIAS_TCF_EMATCH ( TCF_EM_IPSET ) ;