ipt.c 7.34 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * 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>
34
#include <linux/kmod.h>
35 36 37 38 39 40 41 42 43 44 45
#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

46
static u32 idx_gen;
47 48 49 50 51
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 */
52 53 54
#define tcf_st		tcf_ipt
#define tcf_t_lock	ipt_lock
#define tcf_ht		tcf_ipt_ht
55

56
#define CONFIG_NET_ACT_INIT
57 58
#include <net/pkt_act.h>

59 60
static int
ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int hook)
61 62 63 64
{
	struct ipt_target *target;
	int ret = 0;

65
	target = ipt_find_target(t->u.user.name, t->u.user.revision);
66 67
	if (!target)
		return -ENOENT;
68

69
	DPRINTK("ipt_init_target: found %s\n", target->name);
70 71 72
	t->u.kernel.target = target;

	if (t->u.kernel.target->checkentry
73
	    && !t->u.kernel.target->checkentry(table, NULL, t->data,
74
					       t->u.target_size - sizeof(*t),
75 76
					       hook)) {
		DPRINTK("ipt_init_target: check failed for `%s'.\n",
77
			t->u.kernel.target->name);
78
		module_put(t->u.kernel.target->me);
79 80 81 82 83 84
		ret = -EINVAL;
	}

	return ret;
}

85 86 87 88 89 90 91 92 93
static void
ipt_destroy_target(struct ipt_entry_target *t)
{
	if (t->u.kernel.target->destroy)
		t->u.kernel.target->destroy(t->data,
		                            t->u.target_size - sizeof(*t));
        module_put(t->u.kernel.target->me);
}

94
static int
95 96
tcf_ipt_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a,
             int ovr, int bind)
97 98 99
{
	struct rtattr *tb[TCA_IPT_MAX];
	struct tcf_ipt *p;
100 101 102
	struct ipt_entry_target *td, *t;
	char *tname;
	int ret = 0, err;
103
	u32 hook = 0;
104
	u32 index = 0;
105

106
	if (rta == NULL ||
107
	    rtattr_parse(tb, TCA_IPT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) < 0)
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
		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]);

	p = tcf_hash_check(index, a, ovr, bind);
	if (p == NULL) {
		p = tcf_hash_create(index, est, a, sizeof(*p), ovr, bind);
		if (p == NULL)
			return -ENOMEM;
		ret = ACT_P_CREATED;
	} else {
		if (!ovr) {
			tcf_hash_release(p, bind);
			return -EEXIST;
134 135 136
		}
	}

137
	hook = *(u32 *)RTA_DATA(tb[TCA_IPT_HOOK-1]);
138

139 140 141 142 143 144 145
	err = -ENOMEM;
	tname = kmalloc(IFNAMSIZ, GFP_KERNEL);
	if (tname == NULL)
		goto err1;
	if (tb[TCA_IPT_TABLE - 1] == NULL ||
	    rtattr_strlcpy(tname, tb[TCA_IPT_TABLE-1], IFNAMSIZ) >= IFNAMSIZ)
		strcpy(tname, "mangle");
146

147 148 149 150
	t = kmalloc(td->u.target_size, GFP_KERNEL);
	if (t == NULL)
		goto err2;
	memcpy(t, td, td->u.target_size);
151

152 153
	if ((err = ipt_init_target(t, tname, hook)) < 0)
		goto err3;
154

155 156 157
	spin_lock_bh(&p->lock);
	if (ret != ACT_P_CREATED) {
		ipt_destroy_target(p->t);
158 159 160
		kfree(p->tname);
		kfree(p->t);
	}
161 162 163 164 165 166
	p->tname = tname;
	p->t     = t;
	p->hook  = hook;
	spin_unlock_bh(&p->lock);
	if (ret == ACT_P_CREATED)
		tcf_hash_insert(p);
167 168
	return ret;

169 170 171 172 173 174 175
err3:
	kfree(t);
err2:
	kfree(tname);
err1:
	kfree(p);
	return err;
176 177
}

178
static int
179 180
tcf_ipt_cleanup(struct tc_action *a, int bind)
{
181 182
	struct tcf_ipt *p = PRIV(a,ipt);

183 184 185 186
	if (NULL != p) {
		struct ipt_entry_target *t = p->t;
		if (t && t->u.kernel.target)
			module_put(t->u.kernel.target->me);
187
		return tcf_hash_release(p, bind);
188
	}
189 190 191
	return 0;
}

192
static int
193 194 195
tcf_ipt(struct sk_buff **pskb, struct tc_action *a)
{
	int ret = 0, result = 0;
196
	struct tcf_ipt *p = PRIV(a, ipt);
197 198 199 200 201
	struct sk_buff *skb = *pskb;

	spin_lock(&p->lock);

	p->tm.lastuse = jiffies;
202 203
	p->bstats.bytes += skb->len;
	p->bstats.packets++;
204 205 206 207 208 209

	/* 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,
210
					    p->hook, p->t->data, NULL);
211 212 213 214 215 216
	switch (ret) {
	case NF_ACCEPT:
		result = TC_ACT_OK;
		break;
	case NF_DROP:
		result = TC_ACT_SHOT;
217
		p->qstats.drops++;
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
		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;

}

233
static int
234 235 236 237 238 239
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;
240
	struct tcf_ipt *p = PRIV(a, ipt);
241 242 243 244 245 246 247

	/* 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);
248
	if (t == NULL)
249 250 251 252 253 254 255 256 257
		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));
258 259 260 261
	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]);
262 263 264 265 266
	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);
267 268 269
	tm.install = jiffies_to_clock_t(jiffies - p->tm.install);
	tm.lastuse = jiffies_to_clock_t(jiffies - p->tm.lastuse);
	tm.expires = jiffies_to_clock_t(p->tm.expires);
270 271 272 273 274 275 276 277
	RTA_PUT(skb, TCA_IPT_TM, sizeof (tm), &tm);
	return skb->len;

      rtattr_failure:
	skb_trim(skb, b - skb->data);
	return -1;
}

278
static struct tc_action_ops act_ipt_ops = {
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
	.kind		=	"ipt",
	.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");

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);