act_mirred.c 6.2 KB
Newer Older
L
Linus Torvalds 已提交
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 34 35 36 37 38 39 40
/*
 * net/sched/mirred.c	packet mirroring and redirect actions
 *
 *		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 (2002-4)
 *
 * TODO: Add ingress support (and socket redirect support)
 *
 */

#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/types.h>
#include <linux/kernel.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_mirred.h>
#include <net/tc_act/tc_mirred.h>

#include <linux/etherdevice.h>
#include <linux/if_arp.h>

41 42 43
#define MIRRED_TAB_MASK     7
static struct tcf_common *tcf_mirred_ht[MIRRED_TAB_MASK + 1];
static u32 mirred_idx_gen;
L
Linus Torvalds 已提交
44 45
static DEFINE_RWLOCK(mirred_lock);

46 47 48 49 50
static struct tcf_hashinfo mirred_hash_info = {
	.htab	=	tcf_mirred_ht,
	.hmask	=	MIRRED_TAB_MASK,
	.lock	=	&mirred_lock,
};
L
Linus Torvalds 已提交
51

52
static inline int tcf_mirred_release(struct tcf_mirred *m, int bind)
L
Linus Torvalds 已提交
53
{
54
	if (m) {
L
Linus Torvalds 已提交
55
		if (bind)
56 57 58 59 60
			m->tcf_bindcnt--;
		m->tcf_refcnt--;
		if(!m->tcf_bindcnt && m->tcf_refcnt <= 0) {
			dev_put(m->tcfm_dev);
			tcf_hash_destroy(&m->common, &mirred_hash_info);
L
Linus Torvalds 已提交
61 62 63 64 65 66
			return 1;
		}
	}
	return 0;
}

67 68
static int tcf_mirred_init(struct rtattr *rta, struct rtattr *est,
			   struct tc_action *a, int ovr, int bind)
L
Linus Torvalds 已提交
69 70 71
{
	struct rtattr *tb[TCA_MIRRED_MAX];
	struct tc_mirred *parm;
72 73
	struct tcf_mirred *m;
	struct tcf_common *pc;
L
Linus Torvalds 已提交
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	struct net_device *dev = NULL;
	int ret = 0;
	int ok_push = 0;

	if (rta == NULL || rtattr_parse_nested(tb, TCA_MIRRED_MAX, rta) < 0)
		return -EINVAL;

	if (tb[TCA_MIRRED_PARMS-1] == NULL ||
	    RTA_PAYLOAD(tb[TCA_MIRRED_PARMS-1]) < sizeof(*parm))
		return -EINVAL;
	parm = RTA_DATA(tb[TCA_MIRRED_PARMS-1]);

	if (parm->ifindex) {
		dev = __dev_get_by_index(parm->ifindex);
		if (dev == NULL)
			return -ENODEV;
		switch (dev->type) {
			case ARPHRD_TUNNEL:
			case ARPHRD_TUNNEL6:
			case ARPHRD_SIT:
			case ARPHRD_IPGRE:
			case ARPHRD_VOID:
			case ARPHRD_NONE:
				ok_push = 0;
				break;
			default:
				ok_push = 1;
				break;
		}
	}

105 106
	pc = tcf_hash_check(parm->index, a, bind, &mirred_hash_info);
	if (!pc) {
L
Linus Torvalds 已提交
107 108
		if (!parm->ifindex)
			return -EINVAL;
109 110 111
		pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind,
				     &mirred_idx_gen, &mirred_hash_info);
		if (unlikely(!pc))
L
Linus Torvalds 已提交
112 113 114 115
			return -ENOMEM;
		ret = ACT_P_CREATED;
	} else {
		if (!ovr) {
116
			tcf_mirred_release(to_mirred(pc), bind);
L
Linus Torvalds 已提交
117 118 119
			return -EEXIST;
		}
	}
120
	m = to_mirred(pc);
L
Linus Torvalds 已提交
121

122 123 124
	spin_lock_bh(&m->tcf_lock);
	m->tcf_action = parm->action;
	m->tcfm_eaction = parm->eaction;
L
Linus Torvalds 已提交
125
	if (parm->ifindex) {
126
		m->tcfm_ifindex = parm->ifindex;
L
Linus Torvalds 已提交
127
		if (ret != ACT_P_CREATED)
128 129
			dev_put(m->tcfm_dev);
		m->tcfm_dev = dev;
L
Linus Torvalds 已提交
130
		dev_hold(dev);
131
		m->tcfm_ok_push = ok_push;
L
Linus Torvalds 已提交
132
	}
133
	spin_unlock_bh(&m->tcf_lock);
L
Linus Torvalds 已提交
134
	if (ret == ACT_P_CREATED)
135
		tcf_hash_insert(pc, &mirred_hash_info);
L
Linus Torvalds 已提交
136 137 138 139

	return ret;
}

140
static int tcf_mirred_cleanup(struct tc_action *a, int bind)
L
Linus Torvalds 已提交
141
{
142
	struct tcf_mirred *m = a->priv;
L
Linus Torvalds 已提交
143

144 145
	if (m)
		return tcf_mirred_release(m, bind);
L
Linus Torvalds 已提交
146 147 148
	return 0;
}

149 150
static int tcf_mirred(struct sk_buff *skb, struct tc_action *a,
		      struct tcf_result *res)
L
Linus Torvalds 已提交
151
{
152
	struct tcf_mirred *m = a->priv;
L
Linus Torvalds 已提交
153 154 155 156
	struct net_device *dev;
	struct sk_buff *skb2 = NULL;
	u32 at = G_TC_AT(skb->tc_verd);

157
	spin_lock(&m->tcf_lock);
L
Linus Torvalds 已提交
158

159 160
	dev = m->tcfm_dev;
	m->tcf_tm.lastuse = jiffies;
L
Linus Torvalds 已提交
161 162 163 164 165 166 167 168

	if (!(dev->flags&IFF_UP) ) {
		if (net_ratelimit())
			printk("mirred to Houston: device %s is gone!\n",
			       dev->name);
bad_mirred:
		if (skb2 != NULL)
			kfree_skb(skb2);
169 170 171 172
		m->tcf_qstats.overlimits++;
		m->tcf_bstats.bytes += skb->len;
		m->tcf_bstats.packets++;
		spin_unlock(&m->tcf_lock);
L
Linus Torvalds 已提交
173 174 175 176 177 178 179 180 181
		/* should we be asking for packet to be dropped?
		 * may make sense for redirect case only
		*/
		return TC_ACT_SHOT;
	}

	skb2 = skb_clone(skb, GFP_ATOMIC);
	if (skb2 == NULL)
		goto bad_mirred;
182 183
	if (m->tcfm_eaction != TCA_EGRESS_MIRROR &&
	    m->tcfm_eaction != TCA_EGRESS_REDIR) {
L
Linus Torvalds 已提交
184
		if (net_ratelimit())
185 186
			printk("tcf_mirred unknown action %d\n",
			       m->tcfm_eaction);
L
Linus Torvalds 已提交
187 188 189
		goto bad_mirred;
	}

190 191
	m->tcf_bstats.bytes += skb2->len;
	m->tcf_bstats.packets++;
L
Linus Torvalds 已提交
192
	if (!(at & AT_EGRESS))
193
		if (m->tcfm_ok_push)
L
Linus Torvalds 已提交
194 195 196
			skb_push(skb2, skb2->dev->hard_header_len);

	/* mirror is always swallowed */
197
	if (m->tcfm_eaction != TCA_EGRESS_MIRROR)
L
Linus Torvalds 已提交
198 199 200 201 202
		skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at);

	skb2->dev = dev;
	skb2->input_dev = skb->dev;
	dev_queue_xmit(skb2);
203 204
	spin_unlock(&m->tcf_lock);
	return m->tcf_action;
L
Linus Torvalds 已提交
205 206
}

207
static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
L
Linus Torvalds 已提交
208 209
{
	unsigned char *b = skb->tail;
210
	struct tcf_mirred *m = a->priv;
L
Linus Torvalds 已提交
211 212 213
	struct tc_mirred opt;
	struct tcf_t t;

214 215 216 217 218 219
	opt.index = m->tcf_index;
	opt.action = m->tcf_action;
	opt.refcnt = m->tcf_refcnt - ref;
	opt.bindcnt = m->tcf_bindcnt - bind;
	opt.eaction = m->tcfm_eaction;
	opt.ifindex = m->tcfm_ifindex;
L
Linus Torvalds 已提交
220
	RTA_PUT(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt);
221 222 223
	t.install = jiffies_to_clock_t(jiffies - m->tcf_tm.install);
	t.lastuse = jiffies_to_clock_t(jiffies - m->tcf_tm.lastuse);
	t.expires = jiffies_to_clock_t(m->tcf_tm.expires);
L
Linus Torvalds 已提交
224 225 226
	RTA_PUT(skb, TCA_MIRRED_TM, sizeof(t), &t);
	return skb->len;

227
rtattr_failure:
L
Linus Torvalds 已提交
228 229 230 231 232 233
	skb_trim(skb, b - skb->data);
	return -1;
}

static struct tc_action_ops act_mirred_ops = {
	.kind		=	"mirred",
234
	.hinfo		=	&mirred_hash_info,
L
Linus Torvalds 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
	.type		=	TCA_ACT_MIRRED,
	.capab		=	TCA_CAP_NONE,
	.owner		=	THIS_MODULE,
	.act		=	tcf_mirred,
	.dump		=	tcf_mirred_dump,
	.cleanup	=	tcf_mirred_cleanup,
	.lookup		=	tcf_hash_search,
	.init		=	tcf_mirred_init,
	.walk		=	tcf_generic_walker
};

MODULE_AUTHOR("Jamal Hadi Salim(2002)");
MODULE_DESCRIPTION("Device Mirror/redirect actions");
MODULE_LICENSE("GPL");

250
static int __init mirred_init_module(void)
L
Linus Torvalds 已提交
251 252 253 254 255
{
	printk("Mirror/redirect action on\n");
	return tcf_register_action(&act_mirred_ops);
}

256
static void __exit mirred_cleanup_module(void)
L
Linus Torvalds 已提交
257 258 259 260 261 262
{
	tcf_unregister_action(&act_mirred_ops);
}

module_init(mirred_init_module);
module_exit(mirred_cleanup_module);