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
......@@ -407,6 +407,13 @@ config NET_ACT_MIRRED
requires new iproute2
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
tristate "Generic Packet Editor Actions"
depends on NET_CLS_ACT
......
......@@ -12,6 +12,7 @@ obj-$(CONFIG_NET_ACT_POLICE) += police.o
obj-$(CONFIG_NET_CLS_POLICE) += police.o
obj-$(CONFIG_NET_ACT_GACT) += gact.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_SCH_CBQ) += sch_cbq.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