11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * net/sched/mirred.c	packet mirroring and redirect actions
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		This program is free software; you can redistribute it and/or
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		modify it under the terms of the GNU General Public License
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		as published by the Free Software Foundation; either version
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		2 of the License, or (at your option) any later version.
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Authors:	Jamal Hadi Salim (2002-4)
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TODO: Add ingress support (and socket redirect support)
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/rtnetlink.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
235a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h>
24881d966b48b035ab3f3aeaae0f3d3f9b584f45b2Eric W. Biederman#include <net/net_namespace.h>
25dc5fc579b90ed0a9a4e55b0218cdbaf0a8cf2e67Arnaldo Carvalho de Melo#include <net/netlink.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/pkt_sched.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tc_act/tc_mirred.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/tc_act/tc_mirred.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/if_arp.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
32e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller#define MIRRED_TAB_MASK     7
333b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemmingerstatic LIST_HEAD(mirred_list);
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35a5b5c958ffd1610545d6b4b8290aa9c5266d10faWANG Congstatic void tcf_mirred_release(struct tc_action *a, int bind)
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3786062033feb8a1692f7a3d570c652f1b4a4b4b52WANG Cong	struct tcf_mirred *m = to_mirred(a);
38a5b5c958ffd1610545d6b4b8290aa9c5266d10faWANG Cong	list_del(&m->tcfm_list);
39a5b5c958ffd1610545d6b4b8290aa9c5266d10faWANG Cong	if (m->tcfm_dev)
40a5b5c958ffd1610545d6b4b8290aa9c5266d10faWANG Cong		dev_put(m->tcfm_dev);
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4353b2bf3f8a652c9c8e86831f94ae2c5767ea54d7Patrick McHardystatic const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
4453b2bf3f8a652c9c8e86831f94ae2c5767ea54d7Patrick McHardy	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
4553b2bf3f8a652c9c8e86831f94ae2c5767ea54d7Patrick McHardy};
4653b2bf3f8a652c9c8e86831f94ae2c5767ea54d7Patrick McHardy
47c1b52739e45f5969b208ebc377f52468280af11eBenjamin LaHaisestatic int tcf_mirred_init(struct net *net, struct nlattr *nla,
48c1b52739e45f5969b208ebc377f52468280af11eBenjamin LaHaise			   struct nlattr *est, struct tc_action *a, int ovr,
49c1b52739e45f5969b208ebc377f52468280af11eBenjamin LaHaise			   int bind)
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
517ba699c604ab811972eee2e041fd6b07659a2e6ePatrick McHardy	struct nlattr *tb[TCA_MIRRED_MAX + 1];
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tc_mirred *parm;
53e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	struct tcf_mirred *m;
54b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	struct net_device *dev;
55b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	int ret, ok_push = 0;
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57cee63723b358e594225e812d6e14a2a0abfd5c88Patrick McHardy	if (nla == NULL)
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
59b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	ret = nla_parse_nested(tb, TCA_MIRRED_MAX, nla, mirred_policy);
60b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	if (ret < 0)
61b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		return ret;
6253b2bf3f8a652c9c8e86831f94ae2c5767ea54d7Patrick McHardy	if (tb[TCA_MIRRED_PARMS] == NULL)
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
647ba699c604ab811972eee2e041fd6b07659a2e6ePatrick McHardy	parm = nla_data(tb[TCA_MIRRED_PARMS]);
65b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	switch (parm->eaction) {
66b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	case TCA_EGRESS_MIRROR:
67b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	case TCA_EGRESS_REDIR:
68b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		break;
69b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	default:
70b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		return -EINVAL;
71b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	}
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (parm->ifindex) {
73c1b52739e45f5969b208ebc377f52468280af11eBenjamin LaHaise		dev = __dev_get_by_index(net, parm->ifindex);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dev == NULL)
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENODEV;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (dev->type) {
77b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		case ARPHRD_TUNNEL:
78b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		case ARPHRD_TUNNEL6:
79b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		case ARPHRD_SIT:
80b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		case ARPHRD_IPGRE:
81b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		case ARPHRD_VOID:
82b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		case ARPHRD_NONE:
83b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao			ok_push = 0;
84b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao			break;
85b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		default:
86b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao			ok_push = 1;
87b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao			break;
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
89b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	} else {
90b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		dev = NULL;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9386062033feb8a1692f7a3d570c652f1b4a4b4b52WANG Cong	if (!tcf_hash_check(parm->index, a, bind)) {
94b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		if (dev == NULL)
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
9686062033feb8a1692f7a3d570c652f1b4a4b4b52WANG Cong		ret = tcf_hash_create(parm->index, est, a, sizeof(*m), bind);
9786062033feb8a1692f7a3d570c652f1b4a4b4b52WANG Cong		if (ret)
9886062033feb8a1692f7a3d570c652f1b4a4b4b52WANG Cong			return ret;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = ACT_P_CREATED;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ovr) {
102a5b5c958ffd1610545d6b4b8290aa9c5266d10faWANG Cong			tcf_hash_release(a, bind);
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EEXIST;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10686062033feb8a1692f7a3d570c652f1b4a4b4b52WANG Cong	m = to_mirred(a);
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
108e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	spin_lock_bh(&m->tcf_lock);
109e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	m->tcf_action = parm->action;
110e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	m->tcfm_eaction = parm->eaction;
111b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao	if (dev != NULL) {
112e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller		m->tcfm_ifindex = parm->ifindex;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ret != ACT_P_CREATED)
114e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller			dev_put(m->tcfm_dev);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_hold(dev);
116b76965e02bfdd4164c00bf946ff6ca1818ed9fcdChangli Gao		m->tcfm_dev = dev;
117e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller		m->tcfm_ok_push = ok_push;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
119e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	spin_unlock_bh(&m->tcf_lock);
1203b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	if (ret == ACT_P_CREATED) {
1213b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger		list_add(&m->tcfm_list, &mirred_list);
12286062033feb8a1692f7a3d570c652f1b4a4b4b52WANG Cong		tcf_hash_insert(a);
1233b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	}
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
128dc7f9f6e8838556f226c2ebd1da7bb305cb25654Eric Dumazetstatic int tcf_mirred(struct sk_buff *skb, const struct tc_action *a,
129e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller		      struct tcf_result *res)
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
131e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	struct tcf_mirred *m = a->priv;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
133feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao	struct sk_buff *skb2;
134feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao	u32 at;
135feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao	int retval, err = 1;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
137e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	spin_lock(&m->tcf_lock);
138e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	m->tcf_tm.lastuse = jiffies;
139bfe0d0298f2a67d94d58c39ea904a999aeeb7c3cEric Dumazet	bstats_update(&m->tcf_bstats, skb);
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
141feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao	dev = m->tcfm_dev;
1423b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	if (!dev) {
1433b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger		printk_once(KERN_NOTICE "tc mirred: target device is gone\n");
1443b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger		goto out;
1453b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	}
1463b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger
147feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao	if (!(dev->flags & IFF_UP)) {
148e87cc4728f0e2fb663e592a1141742b1d6c63256Joe Perches		net_notice_ratelimited("tc mirred to Houston: device %s is down\n",
149e87cc4728f0e2fb663e592a1141742b1d6c63256Joe Perches				       dev->name);
150feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao		goto out;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
153210d6de78c5d7c785fc532556cea340e517955e1Changli Gao	at = G_TC_AT(skb->tc_verd);
154210d6de78c5d7c785fc532556cea340e517955e1Changli Gao	skb2 = skb_act_clone(skb, GFP_ATOMIC, m->tcf_action);
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (skb2 == NULL)
156feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao		goto out;
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
158feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao	if (!(at & AT_EGRESS)) {
159e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller		if (m->tcfm_ok_push)
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			skb_push(skb2, skb2->dev->hard_header_len);
161feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao	}
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* mirror is always swallowed */
164e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	if (m->tcfm_eaction != TCA_EGRESS_MIRROR)
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at);
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1678964be4a9a5ca8cab1219bb046db2f6d1936227cEric Dumazet	skb2->skb_iif = skb->dev->ifindex;
168210d6de78c5d7c785fc532556cea340e517955e1Changli Gao	skb2->dev = dev;
1698919bc13e8d92c5b082c5c0321567383a071f5bcJamal Hadi Salim	err = dev_queue_xmit(skb2);
170feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao
171feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gaoout:
172feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao	if (err) {
173feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao		m->tcf_qstats.overlimits++;
17416c0b164bd24d44db137693a36b428ba28970c62Jason Wang		if (m->tcfm_eaction != TCA_EGRESS_MIRROR)
17516c0b164bd24d44db137693a36b428ba28970c62Jason Wang			retval = TC_ACT_SHOT;
17616c0b164bd24d44db137693a36b428ba28970c62Jason Wang		else
17716c0b164bd24d44db137693a36b428ba28970c62Jason Wang			retval = m->tcf_action;
17816c0b164bd24d44db137693a36b428ba28970c62Jason Wang	} else
179feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao		retval = m->tcf_action;
180e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	spin_unlock(&m->tcf_lock);
181feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao
182feed1f17241d26261e77ddb5f2fc2a91a3c16739Changli Gao	return retval;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
185e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Millerstatic int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
18727a884dc3cb63b93c2b3b643f5b31eed5f8a4d26Arnaldo Carvalho de Melo	unsigned char *b = skb_tail_pointer(skb);
188e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	struct tcf_mirred *m = a->priv;
1891c40be12f7d8ca1d387510d39787b12e512a7ce8Eric Dumazet	struct tc_mirred opt = {
1901c40be12f7d8ca1d387510d39787b12e512a7ce8Eric Dumazet		.index   = m->tcf_index,
1911c40be12f7d8ca1d387510d39787b12e512a7ce8Eric Dumazet		.action  = m->tcf_action,
1921c40be12f7d8ca1d387510d39787b12e512a7ce8Eric Dumazet		.refcnt  = m->tcf_refcnt - ref,
1931c40be12f7d8ca1d387510d39787b12e512a7ce8Eric Dumazet		.bindcnt = m->tcf_bindcnt - bind,
1941c40be12f7d8ca1d387510d39787b12e512a7ce8Eric Dumazet		.eaction = m->tcfm_eaction,
1951c40be12f7d8ca1d387510d39787b12e512a7ce8Eric Dumazet		.ifindex = m->tcfm_ifindex,
1961c40be12f7d8ca1d387510d39787b12e512a7ce8Eric Dumazet	};
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tcf_t t;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1991b34ec43c9b3de44a5420841ab293d1b2035a94cDavid S. Miller	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
2001b34ec43c9b3de44a5420841ab293d1b2035a94cDavid S. Miller		goto nla_put_failure;
201e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	t.install = jiffies_to_clock_t(jiffies - m->tcf_tm.install);
202e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	t.lastuse = jiffies_to_clock_t(jiffies - m->tcf_tm.lastuse);
203e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Miller	t.expires = jiffies_to_clock_t(m->tcf_tm.expires);
2041b34ec43c9b3de44a5420841ab293d1b2035a94cDavid S. Miller	if (nla_put(skb, TCA_MIRRED_TM, sizeof(t), &t))
2051b34ec43c9b3de44a5420841ab293d1b2035a94cDavid S. Miller		goto nla_put_failure;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return skb->len;
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2087ba699c604ab811972eee2e041fd6b07659a2e6ePatrick McHardynla_put_failure:
209dc5fc579b90ed0a9a4e55b0218cdbaf0a8cf2e67Arnaldo Carvalho de Melo	nlmsg_trim(skb, b);
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -1;
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2133b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemmingerstatic int mirred_device_event(struct notifier_block *unused,
2143b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger			       unsigned long event, void *ptr)
2153b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger{
216351638e7deeed2ec8ce451b53d33921b3da68f83Jiri Pirko	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
2173b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	struct tcf_mirred *m;
2183b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger
2193b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	if (event == NETDEV_UNREGISTER)
2203b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger		list_for_each_entry(m, &mirred_list, tcfm_list) {
221224e923cd9b001c612b7b68933264156271722f9Cong Wang			spin_lock_bh(&m->tcf_lock);
2223b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger			if (m->tcfm_dev == dev) {
2233b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger				dev_put(dev);
2243b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger				m->tcfm_dev = NULL;
2253b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger			}
226224e923cd9b001c612b7b68933264156271722f9Cong Wang			spin_unlock_bh(&m->tcf_lock);
2273b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger		}
2283b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger
2293b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	return NOTIFY_DONE;
2303b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger}
2313b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger
2323b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemmingerstatic struct notifier_block mirred_device_notifier = {
2333b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	.notifier_call = mirred_device_event,
2343b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger};
2353b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tc_action_ops act_mirred_ops = {
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.kind		=	"mirred",
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.type		=	TCA_ACT_MIRRED,
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner		=	THIS_MODULE,
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.act		=	tcf_mirred,
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.dump		=	tcf_mirred_dump,
24286062033feb8a1692f7a3d570c652f1b4a4b4b52WANG Cong	.cleanup	=	tcf_mirred_release,
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.init		=	tcf_mirred_init,
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Jamal Hadi Salim(2002)");
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Device Mirror/redirect actions");
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
250e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Millerstatic int __init mirred_init_module(void)
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2523b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	int err = register_netdevice_notifier(&mirred_device_notifier);
2533b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger	if (err)
2543b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger		return err;
2553b87956ea645fb4de7e59c7d0aa94de04be72615stephen hemminger
2566ff9c3644e72bfac20844e0155c2cc8108602820stephen hemminger	pr_info("Mirror/redirect action on\n");
2574f1e9d8949b438c7791993515fc164312e9080e2WANG Cong	return tcf_register_action(&act_mirred_ops, MIRRED_TAB_MASK);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
260e9ce1cd3cf6cf35b21d0ce990f2e738f35907386David S. Millerstatic void __exit mirred_cleanup_module(void)
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tcf_unregister_action(&act_mirred_ops);
263568a153a22d8f338a5ebda70e6bd139f6d8bb2c3WANG Cong	unregister_netdevice_notifier(&mirred_device_notifier);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(mirred_init_module);
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(mirred_cleanup_module);
268