Commit 2a9c7232 authored by Jamal Hadi Salim's avatar Jamal Hadi Salim Committed by David S. Miller

[PKT_SCHED]: Add iptables action.

Signed-off-by: default avatarjamal <hadi@cyberus.ca>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7e030815
#ifndef __LINUX_TC_IPT_H
#define __LINUX_TC_IPT_H
#include <linux/pkt_cls.h>
#define TCA_ACT_IPT 6
enum
{
TCA_IPT_UNSPEC,
TCA_IPT_TABLE,
TCA_IPT_HOOK,
TCA_IPT_INDEX,
TCA_IPT_CNT,
TCA_IPT_TM,
TCA_IPT_TARG,
__TCA_IPT_MAX
};
#define TCA_IPT_MAX (__TCA_IPT_MAX - 1)
#endif
#ifndef __NET_TC_IPT_H
#define __NET_TC_IPT_H
#include <net/act_api.h>
struct ipt_entry_target;
struct tcf_ipt
{
tca_gen(ipt);
u32 hook;
char *tname;
struct ipt_entry_target *t;
};
#endif
...@@ -393,7 +393,7 @@ config NET_ACT_GACT ...@@ -393,7 +393,7 @@ config NET_ACT_GACT
---help--- ---help---
You must have new iproute2 to use this feature. You must have new iproute2 to use this feature.
This adds simple filtering actions like drop, accept etc. This adds simple filtering actions like drop, accept etc.
config GACT_PROB config GACT_PROB
bool "generic Actions probability" bool "generic Actions probability"
depends on NET_ACT_GACT depends on NET_ACT_GACT
...@@ -407,6 +407,13 @@ config NET_ACT_MIRRED ...@@ -407,6 +407,13 @@ config NET_ACT_MIRRED
requires new iproute2 requires new iproute2
This allows packets to be mirrored or redirected to netdevices This allows packets to be mirrored or redirected to netdevices
config NET_ACT_IPT
tristate "iptables Actions"
depends on NET_CLS_ACT && NETFILTER && IP_NF_IPTABLES
---help---
requires new iproute2
This allows iptables targets to be used by tc filters
config NET_ACT_PEDIT config NET_ACT_PEDIT
tristate "Generic Packet Editor Actions" tristate "Generic Packet Editor Actions"
depends on NET_CLS_ACT depends on NET_CLS_ACT
......
...@@ -12,6 +12,7 @@ obj-$(CONFIG_NET_ACT_POLICE) += police.o ...@@ -12,6 +12,7 @@ obj-$(CONFIG_NET_ACT_POLICE) += police.o
obj-$(CONFIG_NET_CLS_POLICE) += police.o obj-$(CONFIG_NET_CLS_POLICE) += police.o
obj-$(CONFIG_NET_ACT_GACT) += gact.o obj-$(CONFIG_NET_ACT_GACT) += gact.o
obj-$(CONFIG_NET_ACT_MIRRED) += mirred.o obj-$(CONFIG_NET_ACT_MIRRED) += mirred.o
obj-$(CONFIG_NET_ACT_IPT) += ipt.o
obj-$(CONFIG_NET_ACT_PEDIT) += pedit.o obj-$(CONFIG_NET_ACT_PEDIT) += pedit.o
obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o
obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o
......
/*
* 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/config.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 <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>
/* use generic hash table */
#define MY_TAB_SIZE 16
#define MY_TAB_MASK 15
u32 idx_gen;
static struct tcf_ipt *tcf_ipt_ht[MY_TAB_SIZE];
/* ipt hash table lock */
static rwlock_t ipt_lock = RW_LOCK_UNLOCKED;
/* ovewrride the defaults */
#define tcf_st tcf_ipt
#define tcf_t_lock ipt_lock
#define tcf_ht tcf_ipt_ht
#include <net/pkt_act.h>
static inline int
init_targ(struct tcf_ipt *p)
{
struct ipt_target *target;
int ret = 0;
struct ipt_entry_target *t = p->t;
target = __ipt_find_target_lock(t->u.user.name, &ret);
if (!target) {
printk("init_targ: Failed to find %s\n",
t->u.kernel.target->name);
return -1;
}
DPRINTK("init_targ: found %s\n", target->name);
/* we really need proper ref counting
seems to be only needed for modules?? Talk to laforge */
/* if (target->me)
__MOD_INC_USE_COUNT(target->me);
*/
t->u.kernel.target = target;
__ipt_mutex_up();
if (t->u.kernel.target->checkentry
&& !t->u.kernel.target->checkentry(p->tname, NULL, t->data,
t->u.target_size
- sizeof (*t), p->hook)) {
/* if (t->u.kernel.target->me)
__MOD_DEC_USE_COUNT(t->u.kernel.target->me);
*/
DPRINTK("ip_tables: check failed for `%s'.\n",
t->u.kernel.target->name);
ret = -EINVAL;
}
return ret;
}
int
tcf_ipt_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, int ovr, int bind)
{
struct ipt_entry_target *t;
unsigned h;
struct rtattr *tb[TCA_IPT_MAX];
struct tcf_ipt *p;
int ret = 0;
u32 index = 0;
u32 hook = 0;
if (NULL == a || NULL == rta ||
(rtattr_parse(tb, TCA_IPT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) <
0)) {
return -1;
}
if (tb[TCA_IPT_INDEX - 1]) {
index = *(u32 *) RTA_DATA(tb[TCA_IPT_INDEX - 1]);
DPRINTK("ipt index %d\n", index);
}
if (index && (p = tcf_hash_lookup(index)) != NULL) {
a->priv = (void *) p;
spin_lock(&p->lock);
if (bind) {
p->bindcnt += 1;
p->refcnt += 1;
}
if (ovr) {
goto override;
}
spin_unlock(&p->lock);
return ret;
}
if (NULL == tb[TCA_IPT_TARG - 1] || NULL == tb[TCA_IPT_HOOK - 1]) {
return -1;
}
p = kmalloc(sizeof (*p), GFP_KERNEL);
if (p == NULL)
return -1;
memset(p, 0, sizeof (*p));
p->refcnt = 1;
ret = 1;
spin_lock_init(&p->lock);
p->stats_lock = &p->lock;
if (bind)
p->bindcnt = 1;
override:
hook = *(u32 *) RTA_DATA(tb[TCA_IPT_HOOK - 1]);
t = (struct ipt_entry_target *) RTA_DATA(tb[TCA_IPT_TARG - 1]);
p->t = kmalloc(t->u.target_size, GFP_KERNEL);
if (p->t == NULL) {
if (ovr) {
printk("ipt policy messed up \n");
spin_unlock(&p->lock);
return -1;
}
kfree(p);
return -1;
}
memcpy(p->t, RTA_DATA(tb[TCA_IPT_TARG - 1]), t->u.target_size);
DPRINTK(" target NAME %s size %d data[0] %x data[1] %x\n",
t->u.user.name, t->u.target_size, t->data[0], t->data[1]);
p->tname = kmalloc(IFNAMSIZ, GFP_KERNEL);
if (p->tname == NULL) {
if (ovr) {
printk("ipt policy messed up 2 \n");
spin_unlock(&p->lock);
return -1;
}
kfree(p->t);
kfree(p);
return -1;
} else {
int csize = IFNAMSIZ - 1;
memset(p->tname, 0, IFNAMSIZ);
if (tb[TCA_IPT_TABLE - 1]) {
if (strlen((char *) RTA_DATA(tb[TCA_IPT_TABLE - 1])) <
csize)
csize = strlen(RTA_DATA(tb[TCA_IPT_TABLE - 1]));
strncpy(p->tname, RTA_DATA(tb[TCA_IPT_TABLE - 1]),
csize);
DPRINTK("table name %s\n", p->tname);
} else {
strncpy(p->tname, "mangle", 1 + strlen("mangle"));
}
}
if (0 > init_targ(p)) {
if (ovr) {
printk("ipt policy messed up 2 \n");
spin_unlock(&p->lock);
return -1;
}
kfree(p->tname);
kfree(p->t);
kfree(p);
return -1;
}
if (ovr) {
spin_unlock(&p->lock);
return -1;
}
p->index = index ? : tcf_hash_new_index();
p->tm.lastuse = jiffies;
/*
p->tm.expires = jiffies;
*/
p->tm.install = jiffies;
#ifdef CONFIG_NET_ESTIMATOR
if (est) {
qdisc_new_estimator(&p->stats, p->stats_lock, est);
}
#endif
h = tcf_hash(p->index);
write_lock_bh(&ipt_lock);
p->next = tcf_ipt_ht[h];
tcf_ipt_ht[h] = p;
write_unlock_bh(&ipt_lock);
a->priv = (void *) p;
return ret;
}
int
tcf_ipt_cleanup(struct tc_action *a, int bind)
{
struct tcf_ipt *p;
p = PRIV(a,ipt);
if (NULL != p)
return tcf_hash_release(p, bind);
return 0;
}
int
tcf_ipt(struct sk_buff **pskb, struct tc_action *a)
{
int ret = 0, result = 0;
struct tcf_ipt *p;
struct sk_buff *skb = *pskb;
p = PRIV(a,ipt);
if (NULL == p || NULL == skb) {
return -1;
}
spin_lock(&p->lock);
p->tm.lastuse = jiffies;
p->stats.bytes += skb->len;
p->stats.packets++;
if (skb_cloned(skb) ) {
if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
return -1;
}
}
/* yes, we have to worry about both in and out dev
worry later - danger - this API seems to have changed
from earlier kernels */
ret = p->t->u.kernel.target->target(&skb, skb->dev, NULL,
p->hook, p->t->data, (void *)NULL);
switch (ret) {
case NF_ACCEPT:
result = TC_ACT_OK;
break;
case NF_DROP:
result = TC_ACT_SHOT;
p->stats.drops++;
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;
}
spin_unlock(&p->lock);
return result;
}
int
tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
{
struct ipt_entry_target *t;
struct tcf_t tm;
struct tc_cnt c;
unsigned char *b = skb->tail;
struct tcf_ipt *p;
p = PRIV(a,ipt);
if (NULL == p) {
printk("BUG: tcf_ipt_dump called with NULL params\n");
goto rtattr_failure;
}
/* for simple targets kernel size == user size
** user name = target name
** for foolproof you need to not assume this
*/
t = kmalloc(p->t->u.user.target_size, GFP_ATOMIC);
if (NULL == t)
goto rtattr_failure;
c.bindcnt = p->bindcnt - bind;
c.refcnt = p->refcnt - ref;
memcpy(t, p->t, p->t->u.user.target_size);
strcpy(t->u.user.name, p->t->u.kernel.target->name);
DPRINTK("\ttcf_ipt_dump tablename %s length %d\n", p->tname,
strlen(p->tname));
DPRINTK
("\tdump target name %s size %d size user %d data[0] %x data[1] %x\n",
p->t->u.kernel.target->name, p->t->u.target_size, p->t->u.user.target_size,
p->t->data[0], p->t->data[1]);
RTA_PUT(skb, TCA_IPT_TARG, p->t->u.user.target_size, t);
RTA_PUT(skb, TCA_IPT_INDEX, 4, &p->index);
RTA_PUT(skb, TCA_IPT_HOOK, 4, &p->hook);
RTA_PUT(skb, TCA_IPT_CNT, sizeof(struct tc_cnt), &c);
RTA_PUT(skb, TCA_IPT_TABLE, IFNAMSIZ, p->tname);
tm.install = jiffies - p->tm.install;
tm.lastuse = jiffies - p->tm.lastuse;
tm.expires = p->tm.expires;
RTA_PUT(skb, TCA_IPT_TM, sizeof (tm), &tm);
return skb->len;
rtattr_failure:
skb_trim(skb, b - skb->data);
return -1;
}
int
tcf_ipt_stats(struct sk_buff *skb, struct tc_action *a)
{
struct tcf_ipt *p;
p = PRIV(a,ipt);
if (NULL != p)
return qdisc_copy_stats(skb, &p->stats, p->stats_lock);
return 1;
}
struct tc_action_ops act_ipt_ops = {
.next = NULL,
.kind = "ipt",
.type = TCA_ACT_IPT,
.capab = TCA_CAP_NONE,
.owner = THIS_MODULE,
.act = tcf_ipt,
.get_stats = tcf_ipt_stats,
.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");
static int __init
ipt_init_module(void)
{
return tcf_register_action(&act_ipt_ops);
}
static void __exit
ipt_cleanup_module(void)
{
tcf_unregister_action(&act_ipt_ops);
}
module_init(ipt_init_module);
module_exit(ipt_cleanup_module);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment