Blame view

net/sched/act_mirred.c 12 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
0c6965dd3   Jiri Pirko   sched: fix act fi...
3
   * net/sched/act_mirred.c	packet mirroring and redirect actions
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
   * Authors:	Jamal Hadi Salim (2002-4)
   *
   * TODO: Add ingress support (and socket redirect support)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
  #include <linux/skbuff.h>
  #include <linux/rtnetlink.h>
  #include <linux/module.h>
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/gfp.h>
c491680f8   Daniel Borkmann   bpf: reuse dev_is...
18
  #include <linux/if_arp.h>
881d966b4   Eric W. Biederman   [NET]: Make the d...
19
  #include <net/net_namespace.h>
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
20
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  #include <net/pkt_sched.h>
e5cf1baf9   Paolo Abeni   act_mirred: use T...
22
  #include <net/pkt_cls.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
  #include <linux/tc_act/tc_mirred.h>
  #include <net/tc_act/tc_mirred.h>
3b87956ea   stephen hemminger   net sched: fix ra...
25
  static LIST_HEAD(mirred_list);
4e232818b   Vlad Buslov   net: sched: act_m...
26
  static DEFINE_SPINLOCK(mirred_list_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27

e2ca070f8   John Hurley   net: sched: prote...
28
29
  #define MIRRED_RECURSION_LIMIT    4
  static DEFINE_PER_CPU(unsigned int, mirred_rec_level);
53592b364   Shmulik Ladkani   net/sched: act_mi...
30
31
32
33
  static bool tcf_mirred_is_act_redirect(int action)
  {
  	return action == TCA_EGRESS_REDIR || action == TCA_INGRESS_REDIR;
  }
8dc07fdbf   Willem de Bruijn   net-tc: convert t...
34
  static bool tcf_mirred_act_wants_ingress(int action)
53592b364   Shmulik Ladkani   net/sched: act_mi...
35
36
37
38
  {
  	switch (action) {
  	case TCA_EGRESS_REDIR:
  	case TCA_EGRESS_MIRROR:
8dc07fdbf   Willem de Bruijn   net-tc: convert t...
39
  		return false;
53592b364   Shmulik Ladkani   net/sched: act_mi...
40
41
  	case TCA_INGRESS_REDIR:
  	case TCA_INGRESS_MIRROR:
8dc07fdbf   Willem de Bruijn   net-tc: convert t...
42
  		return true;
53592b364   Shmulik Ladkani   net/sched: act_mi...
43
44
45
46
  	default:
  		BUG();
  	}
  }
e5cf1baf9   Paolo Abeni   act_mirred: use T...
47
48
49
50
51
52
53
54
55
56
57
  static bool tcf_mirred_can_reinsert(int action)
  {
  	switch (action) {
  	case TC_ACT_SHOT:
  	case TC_ACT_STOLEN:
  	case TC_ACT_QUEUED:
  	case TC_ACT_TRAP:
  		return true;
  	}
  	return false;
  }
4e232818b   Vlad Buslov   net: sched: act_m...
58
59
60
61
62
  static struct net_device *tcf_mirred_dev_dereference(struct tcf_mirred *m)
  {
  	return rcu_dereference_protected(m->tcfm_dev,
  					 lockdep_is_held(&m->tcf_lock));
  }
9a63b255d   Cong Wang   net_sched: remove...
63
  static void tcf_mirred_release(struct tc_action *a)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  {
86062033f   WANG Cong   net_sched: act: h...
65
  	struct tcf_mirred *m = to_mirred(a);
dc327f893   WANG Cong   net_sched: close ...
66
  	struct net_device *dev;
2ee22a90c   Eric Dumazet   net_sched: act_mi...
67

4e232818b   Vlad Buslov   net: sched: act_m...
68
  	spin_lock(&mirred_list_lock);
a5b5c958f   WANG Cong   net_sched: act: r...
69
  	list_del(&m->tcfm_list);
4e232818b   Vlad Buslov   net: sched: act_m...
70
71
72
73
  	spin_unlock(&mirred_list_lock);
  
  	/* last reference to action, no need to lock */
  	dev = rcu_dereference_protected(m->tcfm_dev, 1);
2ee22a90c   Eric Dumazet   net_sched: act_mi...
74
75
  	if (dev)
  		dev_put(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  }
53b2bf3f8   Patrick McHardy   [NET_SCHED]: Use ...
77
78
79
  static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
  	[TCA_MIRRED_PARMS]	= { .len = sizeof(struct tc_mirred) },
  };
c7d03a00b   Alexey Dobriyan   netns: make struc...
80
  static unsigned int mirred_net_id;
a85a970af   WANG Cong   net_sched: move t...
81
  static struct tc_action_ops act_mirred_ops;
ddf97ccdd   WANG Cong   net_sched: add ne...
82

c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
83
  static int tcf_mirred_init(struct net *net, struct nlattr *nla,
789871bb2   Vlad Buslov   net: sched: imple...
84
85
  			   struct nlattr *est, struct tc_action **a,
  			   int ovr, int bind, bool rtnl_held,
85d0966fa   Davide Caratti   net/sched: prepar...
86
  			   struct tcf_proto *tp,
abbb0d336   Vlad Buslov   net: sched: exten...
87
  			   u32 flags, struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
89
  	struct tc_action_net *tn = net_generic(net, mirred_net_id);
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
90
  	struct nlattr *tb[TCA_MIRRED_MAX + 1];
ff9721d32   Davide Caratti   net/sched: act_mi...
91
  	struct tcf_chain *goto_ch = NULL;
165779231   Shmulik Ladkani   net/sched: act_mi...
92
  	bool mac_header_xmit = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  	struct tc_mirred *parm;
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
94
  	struct tcf_mirred *m;
b76965e02   Changli Gao   act_mirred: optim...
95
  	struct net_device *dev;
b2313077e   WANG Cong   net_sched: make t...
96
  	bool exists = false;
0190c1d45   Vlad Buslov   net: sched: atomi...
97
  	int ret, err;
7be8ef2cd   Dmytro Linkin   net: sched: use t...
98
  	u32 index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99

1d4760c75   Alexander Aring   net: sched: act: ...
100
101
  	if (!nla) {
  		NL_SET_ERR_MSG_MOD(extack, "Mirred requires attributes to be passed");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  		return -EINVAL;
1d4760c75   Alexander Aring   net: sched: act: ...
103
  	}
8cb081746   Johannes Berg   netlink: make val...
104
105
  	ret = nla_parse_nested_deprecated(tb, TCA_MIRRED_MAX, nla,
  					  mirred_policy, extack);
b76965e02   Changli Gao   act_mirred: optim...
106
107
  	if (ret < 0)
  		return ret;
1d4760c75   Alexander Aring   net: sched: act: ...
108
109
  	if (!tb[TCA_MIRRED_PARMS]) {
  		NL_SET_ERR_MSG_MOD(extack, "Missing required mirred parameters");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
  		return -EINVAL;
1d4760c75   Alexander Aring   net: sched: act: ...
111
  	}
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
112
  	parm = nla_data(tb[TCA_MIRRED_PARMS]);
7be8ef2cd   Dmytro Linkin   net: sched: use t...
113
114
  	index = parm->index;
  	err = tcf_idr_check_alloc(tn, &index, a, bind);
0190c1d45   Vlad Buslov   net: sched: atomi...
115
116
117
  	if (err < 0)
  		return err;
  	exists = err;
87dfbdc6c   Jamal Hadi Salim   net sched: mirred...
118
119
  	if (exists && bind)
  		return 0;
b76965e02   Changli Gao   act_mirred: optim...
120
121
122
  	switch (parm->eaction) {
  	case TCA_EGRESS_MIRROR:
  	case TCA_EGRESS_REDIR:
53592b364   Shmulik Ladkani   net/sched: act_mi...
123
124
  	case TCA_INGRESS_REDIR:
  	case TCA_INGRESS_MIRROR:
b76965e02   Changli Gao   act_mirred: optim...
125
126
  		break;
  	default:
87dfbdc6c   Jamal Hadi Salim   net sched: mirred...
127
  		if (exists)
65a206c01   Chris Mi   net/sched: Change...
128
  			tcf_idr_release(*a, bind);
0190c1d45   Vlad Buslov   net: sched: atomi...
129
  		else
7be8ef2cd   Dmytro Linkin   net: sched: use t...
130
  			tcf_idr_cleanup(tn, index);
1d4760c75   Alexander Aring   net: sched: act: ...
131
  		NL_SET_ERR_MSG_MOD(extack, "Unknown mirred option");
b76965e02   Changli Gao   act_mirred: optim...
132
133
  		return -EINVAL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134

87dfbdc6c   Jamal Hadi Salim   net sched: mirred...
135
  	if (!exists) {
4e232818b   Vlad Buslov   net: sched: act_m...
136
  		if (!parm->ifindex) {
7be8ef2cd   Dmytro Linkin   net: sched: use t...
137
  			tcf_idr_cleanup(tn, index);
1d4760c75   Alexander Aring   net: sched: act: ...
138
  			NL_SET_ERR_MSG_MOD(extack, "Specified device does not exist");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
  			return -EINVAL;
1d4760c75   Alexander Aring   net: sched: act: ...
140
  		}
e38226786   Vlad Buslov   net: sched: updat...
141
142
  		ret = tcf_idr_create_from_flags(tn, index, est, a,
  						&act_mirred_ops, bind, flags);
0190c1d45   Vlad Buslov   net: sched: atomi...
143
  		if (ret) {
7be8ef2cd   Dmytro Linkin   net: sched: use t...
144
  			tcf_idr_cleanup(tn, index);
86062033f   WANG Cong   net_sched: act: h...
145
  			return ret;
0190c1d45   Vlad Buslov   net: sched: atomi...
146
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
  		ret = ACT_P_CREATED;
4e8ddd7f1   Vlad Buslov   net: sched: don't...
148
  	} else if (!ovr) {
65a206c01   Chris Mi   net/sched: Change...
149
  		tcf_idr_release(*a, bind);
4e8ddd7f1   Vlad Buslov   net: sched: don't...
150
  		return -EEXIST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
  	}
064c5d688   John Hurley   net: sched: fix c...
152
153
154
155
  
  	m = to_mirred(*a);
  	if (ret == ACT_P_CREATED)
  		INIT_LIST_HEAD(&m->tcfm_list);
ff9721d32   Davide Caratti   net/sched: act_mi...
156
157
158
  	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
  	if (err < 0)
  		goto release_idr;
653cd284a   Vlad Buslov   net: sched: alway...
159
  	spin_lock_bh(&m->tcf_lock);
4e232818b   Vlad Buslov   net: sched: act_m...
160
161
162
163
  
  	if (parm->ifindex) {
  		dev = dev_get_by_index(net, parm->ifindex);
  		if (!dev) {
653cd284a   Vlad Buslov   net: sched: alway...
164
  			spin_unlock_bh(&m->tcf_lock);
ff9721d32   Davide Caratti   net/sched: act_mi...
165
166
  			err = -ENODEV;
  			goto put_chain;
4e232818b   Vlad Buslov   net: sched: act_m...
167
168
  		}
  		mac_header_xmit = dev_is_mac_header_xmit(dev);
445d37493   Paul E. McKenney   net/sched: Replac...
169
170
  		dev = rcu_replace_pointer(m->tcfm_dev, dev,
  					  lockdep_is_held(&m->tcf_lock));
4e232818b   Vlad Buslov   net: sched: act_m...
171
172
  		if (dev)
  			dev_put(dev);
165779231   Shmulik Ladkani   net/sched: act_mi...
173
  		m->tcfm_mac_header_xmit = mac_header_xmit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
  	}
ff9721d32   Davide Caratti   net/sched: act_mi...
175
176
  	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
  	m->tcfm_eaction = parm->eaction;
653cd284a   Vlad Buslov   net: sched: alway...
177
  	spin_unlock_bh(&m->tcf_lock);
ff9721d32   Davide Caratti   net/sched: act_mi...
178
179
  	if (goto_ch)
  		tcf_chain_put_by_act(goto_ch);
2ee22a90c   Eric Dumazet   net_sched: act_mi...
180

3b87956ea   stephen hemminger   net sched: fix ra...
181
  	if (ret == ACT_P_CREATED) {
4e232818b   Vlad Buslov   net: sched: act_m...
182
  		spin_lock(&mirred_list_lock);
3b87956ea   stephen hemminger   net sched: fix ra...
183
  		list_add(&m->tcfm_list, &mirred_list);
4e232818b   Vlad Buslov   net: sched: act_m...
184
  		spin_unlock(&mirred_list_lock);
3b87956ea   stephen hemminger   net sched: fix ra...
185
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
  	return ret;
ff9721d32   Davide Caratti   net/sched: act_mi...
188
189
190
191
192
193
  put_chain:
  	if (goto_ch)
  		tcf_chain_put_by_act(goto_ch);
  release_idr:
  	tcf_idr_release(*a, bind);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  }
7c5790c4d   Jamal Hadi Salim   net: sched: act_m...
195
196
  static int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a,
  			  struct tcf_result *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  {
a85a970af   WANG Cong   net_sched: move t...
198
  	struct tcf_mirred *m = to_mirred(a);
e5cf1baf9   Paolo Abeni   act_mirred: use T...
199
  	struct sk_buff *skb2 = skb;
53592b364   Shmulik Ladkani   net/sched: act_mi...
200
  	bool m_mac_header_xmit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  	struct net_device *dev;
e2ca070f8   John Hurley   net: sched: prote...
202
  	unsigned int rec_level;
53592b364   Shmulik Ladkani   net/sched: act_mi...
203
  	int retval, err = 0;
e5cf1baf9   Paolo Abeni   act_mirred: use T...
204
205
206
  	bool use_reinsert;
  	bool want_ingress;
  	bool is_redirect;
70cf3dc73   Shmulik Ladkani   net/sched: act_mi...
207
  	bool expects_nh;
53592b364   Shmulik Ladkani   net/sched: act_mi...
208
209
  	int m_eaction;
  	int mac_len;
70cf3dc73   Shmulik Ladkani   net/sched: act_mi...
210
  	bool at_nh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211

e2ca070f8   John Hurley   net: sched: prote...
212
213
214
215
216
217
218
219
  	rec_level = __this_cpu_inc_return(mirred_rec_level);
  	if (unlikely(rec_level > MIRRED_RECURSION_LIMIT)) {
  		net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s
  ",
  				     netdev_name(skb->dev));
  		__this_cpu_dec(mirred_rec_level);
  		return TC_ACT_SHOT;
  	}
2ee22a90c   Eric Dumazet   net_sched: act_mi...
220
  	tcf_lastuse_update(&m->tcf_tm);
5e1ad95b6   Vlad Buslov   net: sched: extra...
221
  	tcf_action_update_bstats(&m->common, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222

53592b364   Shmulik Ladkani   net/sched: act_mi...
223
224
  	m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit);
  	m_eaction = READ_ONCE(m->tcfm_eaction);
2ee22a90c   Eric Dumazet   net_sched: act_mi...
225
  	retval = READ_ONCE(m->tcf_action);
7fd4b288e   Paolo Abeni   tc/act: remove un...
226
  	dev = rcu_dereference_bh(m->tcfm_dev);
2ee22a90c   Eric Dumazet   net_sched: act_mi...
227
228
229
  	if (unlikely(!dev)) {
  		pr_notice_once("tc mirred: target device is gone
  ");
3b87956ea   stephen hemminger   net sched: fix ra...
230
231
  		goto out;
  	}
2ee22a90c   Eric Dumazet   net_sched: act_mi...
232
  	if (unlikely(!(dev->flags & IFF_UP))) {
e87cc4728   Joe Perches   net: Convert net_...
233
234
235
  		net_notice_ratelimited("tc mirred to Houston: device %s is down
  ",
  				       dev->name);
feed1f172   Changli Gao   act_mirred: cleanup
236
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  	}
e5cf1baf9   Paolo Abeni   act_mirred: use T...
238
239
240
241
242
243
244
245
246
247
248
249
  	/* we could easily avoid the clone only if called by ingress and clsact;
  	 * since we can't easily detect the clsact caller, skip clone only for
  	 * ingress - that covers the TC S/W datapath.
  	 */
  	is_redirect = tcf_mirred_is_act_redirect(m_eaction);
  	use_reinsert = skb_at_tc_ingress(skb) && is_redirect &&
  		       tcf_mirred_can_reinsert(retval);
  	if (!use_reinsert) {
  		skb2 = skb_clone(skb, GFP_ATOMIC);
  		if (!skb2)
  			goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250

e5cf1baf9   Paolo Abeni   act_mirred: use T...
251
  	want_ingress = tcf_mirred_act_wants_ingress(m_eaction);
70cf3dc73   Shmulik Ladkani   net/sched: act_mi...
252
253
254
255
256
257
258
259
  
  	expects_nh = want_ingress || !m_mac_header_xmit;
  	at_nh = skb->data == skb_network_header(skb);
  	if (at_nh != expects_nh) {
  		mac_len = skb_at_tc_ingress(skb) ? skb->mac_len :
  			  skb_network_header(skb) - skb_mac_header(skb);
  		if (expects_nh) {
  			/* target device/action expect data at nh */
53592b364   Shmulik Ladkani   net/sched: act_mi...
260
261
  			skb_pull_rcsum(skb2, mac_len);
  		} else {
70cf3dc73   Shmulik Ladkani   net/sched: act_mi...
262
263
  			/* target device/action expect data at mac */
  			skb_push_rcsum(skb2, mac_len);
53592b364   Shmulik Ladkani   net/sched: act_mi...
264
  		}
feed1f172   Changli Gao   act_mirred: cleanup
265
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266

e5cf1baf9   Paolo Abeni   act_mirred: use T...
267
268
  	skb2->skb_iif = skb->dev->ifindex;
  	skb2->dev = dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
  	/* mirror is always swallowed */
e5cf1baf9   Paolo Abeni   act_mirred: use T...
270
  	if (is_redirect) {
2c64605b5   Pablo Neira Ayuso   net: Fix CONFIG_N...
271
  		skb_set_redirected(skb2, skb2->tc_at_ingress);
e5cf1baf9   Paolo Abeni   act_mirred: use T...
272
273
274
  		/* let's the caller reinsert the packet, if possible */
  		if (use_reinsert) {
  			res->ingress = want_ingress;
ef816f3c4   Vlad Buslov   net: sched: don't...
275
276
  			if (skb_tc_reinsert(skb, res))
  				tcf_action_inc_overlimit_qstats(&m->common);
e2ca070f8   John Hurley   net: sched: prote...
277
  			__this_cpu_dec(mirred_rec_level);
720f22fed   John Hurley   net: sched: refac...
278
  			return TC_ACT_CONSUMED;
e5cf1baf9   Paolo Abeni   act_mirred: use T...
279
  		}
bc31c905e   Willem de Bruijn   net-tc: convert t...
280
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281

e5cf1baf9   Paolo Abeni   act_mirred: use T...
282
  	if (!want_ingress)
53592b364   Shmulik Ladkani   net/sched: act_mi...
283
284
285
  		err = dev_queue_xmit(skb2);
  	else
  		err = netif_receive_skb(skb2);
feed1f172   Changli Gao   act_mirred: cleanup
286

feed1f172   Changli Gao   act_mirred: cleanup
287
  	if (err) {
2ee22a90c   Eric Dumazet   net_sched: act_mi...
288
  out:
26b537a88   Vlad Buslov   net: sched: extra...
289
  		tcf_action_inc_overlimit_qstats(&m->common);
53592b364   Shmulik Ladkani   net/sched: act_mi...
290
  		if (tcf_mirred_is_act_redirect(m_eaction))
16c0b164b   Jason Wang   act_mirred: do no...
291
  			retval = TC_ACT_SHOT;
2ee22a90c   Eric Dumazet   net_sched: act_mi...
292
  	}
e2ca070f8   John Hurley   net: sched: prote...
293
  	__this_cpu_dec(mirred_rec_level);
feed1f172   Changli Gao   act_mirred: cleanup
294
295
  
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
  }
4b61d3e8d   Po Liu   net: qos offload ...
297
298
  static void tcf_stats_update(struct tc_action *a, u64 bytes, u64 packets,
  			     u64 drops, u64 lastuse, bool hw)
9798e6fe4   Jakub Kicinski   net: act_mirred: ...
299
  {
5712bf9c5   Paul Blakey   net/sched: act_mi...
300
301
  	struct tcf_mirred *m = to_mirred(a);
  	struct tcf_t *tm = &m->tcf_tm;
4b61d3e8d   Po Liu   net: qos offload ...
302
  	tcf_action_update_stats(a, bytes, packets, drops, hw);
3bb23421a   Roi Dayan   net/sched: Fix up...
303
  	tm->lastuse = max_t(u64, tm->lastuse, lastuse);
9798e6fe4   Jakub Kicinski   net: act_mirred: ...
304
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
305
306
  static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
  			   int ref)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
  {
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
308
  	unsigned char *b = skb_tail_pointer(skb);
a85a970af   WANG Cong   net_sched: move t...
309
  	struct tcf_mirred *m = to_mirred(a);
1c40be12f   Eric Dumazet   net sched: fix so...
310
311
  	struct tc_mirred opt = {
  		.index   = m->tcf_index,
036bb4432   Vlad Buslov   net: sched: chang...
312
313
  		.refcnt  = refcount_read(&m->tcf_refcnt) - ref,
  		.bindcnt = atomic_read(&m->tcf_bindcnt) - bind,
1c40be12f   Eric Dumazet   net sched: fix so...
314
  	};
4e232818b   Vlad Buslov   net: sched: act_m...
315
  	struct net_device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  	struct tcf_t t;
653cd284a   Vlad Buslov   net: sched: alway...
317
  	spin_lock_bh(&m->tcf_lock);
4e232818b   Vlad Buslov   net: sched: act_m...
318
319
320
321
322
  	opt.action = m->tcf_action;
  	opt.eaction = m->tcfm_eaction;
  	dev = tcf_mirred_dev_dereference(m);
  	if (dev)
  		opt.ifindex = dev->ifindex;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
323
324
  	if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt))
  		goto nla_put_failure;
48d8ee169   Jamal Hadi Salim   net sched actions...
325
326
  
  	tcf_tm_dump(&t, &m->tcf_tm);
9854518ea   Nicolas Dichtel   sched: align nlat...
327
  	if (nla_put_64bit(skb, TCA_MIRRED_TM, sizeof(t), &t, TCA_MIRRED_PAD))
1b34ec43c   David S. Miller   pkt_sched: Stop u...
328
  		goto nla_put_failure;
653cd284a   Vlad Buslov   net: sched: alway...
329
  	spin_unlock_bh(&m->tcf_lock);
4e232818b   Vlad Buslov   net: sched: act_m...
330

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
  	return skb->len;
7ba699c60   Patrick McHardy   [NET_SCHED]: Conv...
332
  nla_put_failure:
653cd284a   Vlad Buslov   net: sched: alway...
333
  	spin_unlock_bh(&m->tcf_lock);
dc5fc579b   Arnaldo Carvalho de Melo   [NETLINK]: Use nl...
334
  	nlmsg_trim(skb, b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
  	return -1;
  }
ddf97ccdd   WANG Cong   net_sched: add ne...
337
338
  static int tcf_mirred_walker(struct net *net, struct sk_buff *skb,
  			     struct netlink_callback *cb, int type,
417801055   Alexander Aring   net: sched: act: ...
339
340
  			     const struct tc_action_ops *ops,
  			     struct netlink_ext_ack *extack)
ddf97ccdd   WANG Cong   net_sched: add ne...
341
342
  {
  	struct tc_action_net *tn = net_generic(net, mirred_net_id);
b36201455   Alexander Aring   net: sched: act: ...
343
  	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
ddf97ccdd   WANG Cong   net_sched: add ne...
344
  }
f061b48c1   Cong Wang   Revert "net: sche...
345
  static int tcf_mirred_search(struct net *net, struct tc_action **a, u32 index)
ddf97ccdd   WANG Cong   net_sched: add ne...
346
347
  {
  	struct tc_action_net *tn = net_generic(net, mirred_net_id);
65a206c01   Chris Mi   net/sched: Change...
348
  	return tcf_idr_search(tn, a, index);
ddf97ccdd   WANG Cong   net_sched: add ne...
349
  }
3b87956ea   stephen hemminger   net sched: fix ra...
350
351
352
  static int mirred_device_event(struct notifier_block *unused,
  			       unsigned long event, void *ptr)
  {
351638e7d   Jiri Pirko   net: pass info st...
353
  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3b87956ea   stephen hemminger   net sched: fix ra...
354
  	struct tcf_mirred *m;
2ee22a90c   Eric Dumazet   net_sched: act_mi...
355
  	ASSERT_RTNL();
6bd00b850   WANG Cong   act_mirred: fix a...
356
  	if (event == NETDEV_UNREGISTER) {
4e232818b   Vlad Buslov   net: sched: act_m...
357
  		spin_lock(&mirred_list_lock);
3b87956ea   stephen hemminger   net sched: fix ra...
358
  		list_for_each_entry(m, &mirred_list, tcfm_list) {
653cd284a   Vlad Buslov   net: sched: alway...
359
  			spin_lock_bh(&m->tcf_lock);
4e232818b   Vlad Buslov   net: sched: act_m...
360
  			if (tcf_mirred_dev_dereference(m) == dev) {
3b87956ea   stephen hemminger   net sched: fix ra...
361
  				dev_put(dev);
2ee22a90c   Eric Dumazet   net_sched: act_mi...
362
363
364
365
  				/* Note : no rcu grace period necessary, as
  				 * net_device are already rcu protected.
  				 */
  				RCU_INIT_POINTER(m->tcfm_dev, NULL);
3b87956ea   stephen hemminger   net sched: fix ra...
366
  			}
653cd284a   Vlad Buslov   net: sched: alway...
367
  			spin_unlock_bh(&m->tcf_lock);
3b87956ea   stephen hemminger   net sched: fix ra...
368
  		}
4e232818b   Vlad Buslov   net: sched: act_m...
369
  		spin_unlock(&mirred_list_lock);
6bd00b850   WANG Cong   act_mirred: fix a...
370
  	}
3b87956ea   stephen hemminger   net sched: fix ra...
371
372
373
374
375
376
377
  
  	return NOTIFY_DONE;
  }
  
  static struct notifier_block mirred_device_notifier = {
  	.notifier_call = mirred_device_event,
  };
470d5060e   Vlad Buslov   net: sched: use g...
378
379
380
381
382
383
384
385
386
387
  static void tcf_mirred_dev_put(void *priv)
  {
  	struct net_device *dev = priv;
  
  	dev_put(dev);
  }
  
  static struct net_device *
  tcf_mirred_get_dev(const struct tc_action *a,
  		   tc_action_priv_destructor *destructor)
255cb3042   Hadar Hen Zion   net/sched: act_mi...
388
  {
843e79d05   Jiri Pirko   net: sched: make ...
389
  	struct tcf_mirred *m = to_mirred(a);
4e232818b   Vlad Buslov   net: sched: act_m...
390
  	struct net_device *dev;
84a75b329   Vlad Buslov   net: sched: exten...
391

4e232818b   Vlad Buslov   net: sched: act_m...
392
393
  	rcu_read_lock();
  	dev = rcu_dereference(m->tcfm_dev);
470d5060e   Vlad Buslov   net: sched: use g...
394
  	if (dev) {
84a75b329   Vlad Buslov   net: sched: exten...
395
  		dev_hold(dev);
470d5060e   Vlad Buslov   net: sched: use g...
396
397
  		*destructor = tcf_mirred_dev_put;
  	}
4e232818b   Vlad Buslov   net: sched: act_m...
398
  	rcu_read_unlock();
255cb3042   Hadar Hen Zion   net/sched: act_mi...
399

84a75b329   Vlad Buslov   net: sched: exten...
400
401
  	return dev;
  }
b84b2d4e3   Roman Mashak   net sched: update...
402
403
404
405
  static size_t tcf_mirred_get_fill_size(const struct tc_action *act)
  {
  	return nla_total_size(sizeof(struct tc_mirred));
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
  static struct tc_action_ops act_mirred_ops = {
  	.kind		=	"mirred",
eddd2cf19   Eli Cohen   net: Change TCA_A...
408
  	.id		=	TCA_ID_MIRRED,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  	.owner		=	THIS_MODULE,
7c5790c4d   Jamal Hadi Salim   net: sched: act_m...
410
  	.act		=	tcf_mirred_act,
9798e6fe4   Jakub Kicinski   net: act_mirred: ...
411
  	.stats_update	=	tcf_stats_update,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  	.dump		=	tcf_mirred_dump,
86062033f   WANG Cong   net_sched: act: h...
413
  	.cleanup	=	tcf_mirred_release,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
  	.init		=	tcf_mirred_init,
ddf97ccdd   WANG Cong   net_sched: add ne...
415
416
  	.walk		=	tcf_mirred_walker,
  	.lookup		=	tcf_mirred_search,
b84b2d4e3   Roman Mashak   net sched: update...
417
  	.get_fill_size	=	tcf_mirred_get_fill_size,
a85a970af   WANG Cong   net_sched: move t...
418
  	.size		=	sizeof(struct tcf_mirred),
843e79d05   Jiri Pirko   net: sched: make ...
419
  	.get_dev	=	tcf_mirred_get_dev,
ddf97ccdd   WANG Cong   net_sched: add ne...
420
421
422
423
424
  };
  
  static __net_init int mirred_init_net(struct net *net)
  {
  	struct tc_action_net *tn = net_generic(net, mirred_net_id);
981471bd3   Cong Wang   net_sched: fix a ...
425
  	return tc_action_net_init(net, tn, &act_mirred_ops);
ddf97ccdd   WANG Cong   net_sched: add ne...
426
  }
039af9c66   Cong Wang   net_sched: switch...
427
  static void __net_exit mirred_exit_net(struct list_head *net_list)
ddf97ccdd   WANG Cong   net_sched: add ne...
428
  {
039af9c66   Cong Wang   net_sched: switch...
429
  	tc_action_net_exit(net_list, mirred_net_id);
ddf97ccdd   WANG Cong   net_sched: add ne...
430
431
432
433
  }
  
  static struct pernet_operations mirred_net_ops = {
  	.init = mirred_init_net,
039af9c66   Cong Wang   net_sched: switch...
434
  	.exit_batch = mirred_exit_net,
ddf97ccdd   WANG Cong   net_sched: add ne...
435
436
  	.id   = &mirred_net_id,
  	.size = sizeof(struct tc_action_net),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
439
440
441
  };
  
  MODULE_AUTHOR("Jamal Hadi Salim(2002)");
  MODULE_DESCRIPTION("Device Mirror/redirect actions");
  MODULE_LICENSE("GPL");
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
442
  static int __init mirred_init_module(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
  {
3b87956ea   stephen hemminger   net sched: fix ra...
444
445
446
  	int err = register_netdevice_notifier(&mirred_device_notifier);
  	if (err)
  		return err;
6ff9c3644   stephen hemminger   net sched: printk...
447
448
  	pr_info("Mirror/redirect action on
  ");
11c9a7d38   YueHaibing   act_mirred: Fix m...
449
450
451
452
453
  	err = tcf_register_action(&act_mirred_ops, &mirred_net_ops);
  	if (err)
  		unregister_netdevice_notifier(&mirred_device_notifier);
  
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
  }
e9ce1cd3c   David S. Miller   [PKT_SCHED]: Kill...
455
  static void __exit mirred_cleanup_module(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
  {
ddf97ccdd   WANG Cong   net_sched: add ne...
457
  	tcf_unregister_action(&act_mirred_ops, &mirred_net_ops);
568a153a2   WANG Cong   net_sched: fix a ...
458
  	unregister_netdevice_notifier(&mirred_device_notifier);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
462
  }
  
  module_init(mirred_init_module);
  module_exit(mirred_cleanup_module);