.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * net/sched/act_mirred.c packet mirroring and redirect actions |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or |
---|
5 | | - * modify it under the terms of the GNU General Public License |
---|
6 | | - * as published by the Free Software Foundation; either version |
---|
7 | | - * 2 of the License, or (at your option) any later version. |
---|
8 | 4 | * |
---|
9 | 5 | * Authors: Jamal Hadi Salim (2002-4) |
---|
10 | 6 | * |
---|
11 | 7 | * TODO: Add ingress support (and socket redirect support) |
---|
12 | | - * |
---|
13 | 8 | */ |
---|
14 | 9 | |
---|
15 | 10 | #include <linux/types.h> |
---|
.. | .. |
---|
24 | 19 | #include <linux/if_arp.h> |
---|
25 | 20 | #include <net/net_namespace.h> |
---|
26 | 21 | #include <net/netlink.h> |
---|
| 22 | +#include <net/dst.h> |
---|
27 | 23 | #include <net/pkt_sched.h> |
---|
28 | 24 | #include <net/pkt_cls.h> |
---|
29 | 25 | #include <linux/tc_act/tc_mirred.h> |
---|
.. | .. |
---|
31 | 27 | |
---|
32 | 28 | static LIST_HEAD(mirred_list); |
---|
33 | 29 | static DEFINE_SPINLOCK(mirred_list_lock); |
---|
| 30 | + |
---|
| 31 | +#define MIRRED_NEST_LIMIT 4 |
---|
| 32 | +static DEFINE_PER_CPU(unsigned int, mirred_nest_level); |
---|
34 | 33 | |
---|
35 | 34 | static bool tcf_mirred_is_act_redirect(int action) |
---|
36 | 35 | { |
---|
.. | .. |
---|
94 | 93 | static int tcf_mirred_init(struct net *net, struct nlattr *nla, |
---|
95 | 94 | struct nlattr *est, struct tc_action **a, |
---|
96 | 95 | int ovr, int bind, bool rtnl_held, |
---|
97 | | - struct netlink_ext_ack *extack) |
---|
| 96 | + struct tcf_proto *tp, |
---|
| 97 | + u32 flags, struct netlink_ext_ack *extack) |
---|
98 | 98 | { |
---|
99 | 99 | struct tc_action_net *tn = net_generic(net, mirred_net_id); |
---|
100 | 100 | struct nlattr *tb[TCA_MIRRED_MAX + 1]; |
---|
| 101 | + struct tcf_chain *goto_ch = NULL; |
---|
101 | 102 | bool mac_header_xmit = false; |
---|
102 | 103 | struct tc_mirred *parm; |
---|
103 | 104 | struct tcf_mirred *m; |
---|
.. | .. |
---|
110 | 111 | NL_SET_ERR_MSG_MOD(extack, "Mirred requires attributes to be passed"); |
---|
111 | 112 | return -EINVAL; |
---|
112 | 113 | } |
---|
113 | | - ret = nla_parse_nested(tb, TCA_MIRRED_MAX, nla, mirred_policy, extack); |
---|
| 114 | + ret = nla_parse_nested_deprecated(tb, TCA_MIRRED_MAX, nla, |
---|
| 115 | + mirred_policy, extack); |
---|
114 | 116 | if (ret < 0) |
---|
115 | 117 | return ret; |
---|
116 | 118 | if (!tb[TCA_MIRRED_PARMS]) { |
---|
.. | .. |
---|
147 | 149 | NL_SET_ERR_MSG_MOD(extack, "Specified device does not exist"); |
---|
148 | 150 | return -EINVAL; |
---|
149 | 151 | } |
---|
150 | | - ret = tcf_idr_create(tn, index, est, a, |
---|
151 | | - &act_mirred_ops, bind, true); |
---|
| 152 | + ret = tcf_idr_create_from_flags(tn, index, est, a, |
---|
| 153 | + &act_mirred_ops, bind, flags); |
---|
152 | 154 | if (ret) { |
---|
153 | 155 | tcf_idr_cleanup(tn, index); |
---|
154 | 156 | return ret; |
---|
.. | .. |
---|
158 | 160 | tcf_idr_release(*a, bind); |
---|
159 | 161 | return -EEXIST; |
---|
160 | 162 | } |
---|
161 | | - m = to_mirred(*a); |
---|
162 | 163 | |
---|
| 164 | + m = to_mirred(*a); |
---|
163 | 165 | if (ret == ACT_P_CREATED) |
---|
164 | 166 | INIT_LIST_HEAD(&m->tcfm_list); |
---|
165 | 167 | |
---|
| 168 | + err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); |
---|
| 169 | + if (err < 0) |
---|
| 170 | + goto release_idr; |
---|
| 171 | + |
---|
166 | 172 | spin_lock_bh(&m->tcf_lock); |
---|
167 | | - m->tcf_action = parm->action; |
---|
168 | | - m->tcfm_eaction = parm->eaction; |
---|
169 | 173 | |
---|
170 | 174 | if (parm->ifindex) { |
---|
171 | 175 | dev = dev_get_by_index(net, parm->ifindex); |
---|
172 | 176 | if (!dev) { |
---|
173 | 177 | spin_unlock_bh(&m->tcf_lock); |
---|
174 | | - tcf_idr_release(*a, bind); |
---|
175 | | - return -ENODEV; |
---|
| 178 | + err = -ENODEV; |
---|
| 179 | + goto put_chain; |
---|
176 | 180 | } |
---|
177 | 181 | mac_header_xmit = dev_is_mac_header_xmit(dev); |
---|
178 | | - rcu_swap_protected(m->tcfm_dev, dev, |
---|
179 | | - lockdep_is_held(&m->tcf_lock)); |
---|
| 182 | + dev = rcu_replace_pointer(m->tcfm_dev, dev, |
---|
| 183 | + lockdep_is_held(&m->tcf_lock)); |
---|
180 | 184 | if (dev) |
---|
181 | 185 | dev_put(dev); |
---|
182 | 186 | m->tcfm_mac_header_xmit = mac_header_xmit; |
---|
183 | 187 | } |
---|
| 188 | + goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); |
---|
| 189 | + m->tcfm_eaction = parm->eaction; |
---|
184 | 190 | spin_unlock_bh(&m->tcf_lock); |
---|
| 191 | + if (goto_ch) |
---|
| 192 | + tcf_chain_put_by_act(goto_ch); |
---|
185 | 193 | |
---|
186 | 194 | if (ret == ACT_P_CREATED) { |
---|
187 | 195 | spin_lock(&mirred_list_lock); |
---|
188 | 196 | list_add(&m->tcfm_list, &mirred_list); |
---|
189 | 197 | spin_unlock(&mirred_list_lock); |
---|
190 | | - |
---|
191 | | - tcf_idr_insert(tn, *a); |
---|
192 | 198 | } |
---|
193 | 199 | |
---|
194 | 200 | return ret; |
---|
| 201 | +put_chain: |
---|
| 202 | + if (goto_ch) |
---|
| 203 | + tcf_chain_put_by_act(goto_ch); |
---|
| 204 | +release_idr: |
---|
| 205 | + tcf_idr_release(*a, bind); |
---|
| 206 | + return err; |
---|
| 207 | +} |
---|
| 208 | + |
---|
| 209 | +static bool is_mirred_nested(void) |
---|
| 210 | +{ |
---|
| 211 | + return unlikely(__this_cpu_read(mirred_nest_level) > 1); |
---|
| 212 | +} |
---|
| 213 | + |
---|
| 214 | +static int tcf_mirred_forward(bool want_ingress, struct sk_buff *skb) |
---|
| 215 | +{ |
---|
| 216 | + int err; |
---|
| 217 | + |
---|
| 218 | + if (!want_ingress) |
---|
| 219 | + err = dev_queue_xmit(skb); |
---|
| 220 | + else if (is_mirred_nested()) |
---|
| 221 | + err = netif_rx(skb); |
---|
| 222 | + else |
---|
| 223 | + err = netif_receive_skb(skb); |
---|
| 224 | + |
---|
| 225 | + return err; |
---|
195 | 226 | } |
---|
196 | 227 | |
---|
197 | 228 | static int tcf_mirred_act(struct sk_buff *skb, const struct tc_action *a, |
---|
.. | .. |
---|
201 | 232 | struct sk_buff *skb2 = skb; |
---|
202 | 233 | bool m_mac_header_xmit; |
---|
203 | 234 | struct net_device *dev; |
---|
| 235 | + unsigned int nest_level; |
---|
204 | 236 | int retval, err = 0; |
---|
205 | 237 | bool use_reinsert; |
---|
206 | 238 | bool want_ingress; |
---|
207 | 239 | bool is_redirect; |
---|
| 240 | + bool expects_nh; |
---|
| 241 | + bool at_ingress; |
---|
208 | 242 | int m_eaction; |
---|
209 | 243 | int mac_len; |
---|
| 244 | + bool at_nh; |
---|
| 245 | + |
---|
| 246 | + nest_level = __this_cpu_inc_return(mirred_nest_level); |
---|
| 247 | + if (unlikely(nest_level > MIRRED_NEST_LIMIT)) { |
---|
| 248 | + net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s\n", |
---|
| 249 | + netdev_name(skb->dev)); |
---|
| 250 | + __this_cpu_dec(mirred_nest_level); |
---|
| 251 | + return TC_ACT_SHOT; |
---|
| 252 | + } |
---|
210 | 253 | |
---|
211 | 254 | tcf_lastuse_update(&m->tcf_tm); |
---|
212 | | - bstats_cpu_update(this_cpu_ptr(m->common.cpu_bstats), skb); |
---|
| 255 | + tcf_action_update_bstats(&m->common, skb); |
---|
213 | 256 | |
---|
214 | 257 | m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit); |
---|
215 | 258 | m_eaction = READ_ONCE(m->tcfm_eaction); |
---|
.. | .. |
---|
220 | 263 | goto out; |
---|
221 | 264 | } |
---|
222 | 265 | |
---|
223 | | - if (unlikely(!(dev->flags & IFF_UP))) { |
---|
| 266 | + if (unlikely(!(dev->flags & IFF_UP)) || !netif_carrier_ok(dev)) { |
---|
224 | 267 | net_notice_ratelimited("tc mirred to Houston: device %s is down\n", |
---|
225 | 268 | dev->name); |
---|
226 | 269 | goto out; |
---|
.. | .. |
---|
231 | 274 | * ingress - that covers the TC S/W datapath. |
---|
232 | 275 | */ |
---|
233 | 276 | is_redirect = tcf_mirred_is_act_redirect(m_eaction); |
---|
234 | | - use_reinsert = skb_at_tc_ingress(skb) && is_redirect && |
---|
| 277 | + at_ingress = skb_at_tc_ingress(skb); |
---|
| 278 | + use_reinsert = at_ingress && is_redirect && |
---|
235 | 279 | tcf_mirred_can_reinsert(retval); |
---|
236 | 280 | if (!use_reinsert) { |
---|
237 | 281 | skb2 = skb_clone(skb, GFP_ATOMIC); |
---|
.. | .. |
---|
239 | 283 | goto out; |
---|
240 | 284 | } |
---|
241 | 285 | |
---|
242 | | - /* If action's target direction differs than filter's direction, |
---|
243 | | - * and devices expect a mac header on xmit, then mac push/pull is |
---|
244 | | - * needed. |
---|
245 | | - */ |
---|
246 | 286 | want_ingress = tcf_mirred_act_wants_ingress(m_eaction); |
---|
247 | | - if (skb_at_tc_ingress(skb) != want_ingress && m_mac_header_xmit) { |
---|
248 | | - if (!skb_at_tc_ingress(skb)) { |
---|
249 | | - /* caught at egress, act ingress: pull mac */ |
---|
250 | | - mac_len = skb_network_header(skb) - skb_mac_header(skb); |
---|
| 287 | + |
---|
| 288 | + /* All mirred/redirected skbs should clear previous ct info */ |
---|
| 289 | + nf_reset_ct(skb2); |
---|
| 290 | + if (want_ingress && !at_ingress) /* drop dst for egress -> ingress */ |
---|
| 291 | + skb_dst_drop(skb2); |
---|
| 292 | + |
---|
| 293 | + expects_nh = want_ingress || !m_mac_header_xmit; |
---|
| 294 | + at_nh = skb->data == skb_network_header(skb); |
---|
| 295 | + if (at_nh != expects_nh) { |
---|
| 296 | + mac_len = skb_at_tc_ingress(skb) ? skb->mac_len : |
---|
| 297 | + skb_network_header(skb) - skb_mac_header(skb); |
---|
| 298 | + if (expects_nh) { |
---|
| 299 | + /* target device/action expect data at nh */ |
---|
251 | 300 | skb_pull_rcsum(skb2, mac_len); |
---|
252 | 301 | } else { |
---|
253 | | - /* caught at ingress, act egress: push mac */ |
---|
254 | | - skb_push_rcsum(skb2, skb->mac_len); |
---|
| 302 | + /* target device/action expect data at mac */ |
---|
| 303 | + skb_push_rcsum(skb2, mac_len); |
---|
255 | 304 | } |
---|
256 | 305 | } |
---|
257 | 306 | |
---|
.. | .. |
---|
260 | 309 | |
---|
261 | 310 | /* mirror is always swallowed */ |
---|
262 | 311 | if (is_redirect) { |
---|
263 | | - skb2->tc_redirected = 1; |
---|
264 | | - skb2->tc_from_ingress = skb2->tc_at_ingress; |
---|
| 312 | + skb_set_redirected(skb2, skb2->tc_at_ingress); |
---|
265 | 313 | |
---|
266 | 314 | /* let's the caller reinsert the packet, if possible */ |
---|
267 | 315 | if (use_reinsert) { |
---|
268 | 316 | res->ingress = want_ingress; |
---|
269 | | - res->qstats = this_cpu_ptr(m->common.cpu_qstats); |
---|
270 | | - return TC_ACT_REINSERT; |
---|
| 317 | + err = tcf_mirred_forward(res->ingress, skb); |
---|
| 318 | + if (err) |
---|
| 319 | + tcf_action_inc_overlimit_qstats(&m->common); |
---|
| 320 | + __this_cpu_dec(mirred_nest_level); |
---|
| 321 | + return TC_ACT_CONSUMED; |
---|
271 | 322 | } |
---|
272 | 323 | } |
---|
273 | 324 | |
---|
274 | | - if (!want_ingress) |
---|
275 | | - err = dev_queue_xmit(skb2); |
---|
276 | | - else |
---|
277 | | - err = netif_receive_skb(skb2); |
---|
278 | | - |
---|
| 325 | + err = tcf_mirred_forward(want_ingress, skb2); |
---|
279 | 326 | if (err) { |
---|
280 | 327 | out: |
---|
281 | | - qstats_overlimit_inc(this_cpu_ptr(m->common.cpu_qstats)); |
---|
| 328 | + tcf_action_inc_overlimit_qstats(&m->common); |
---|
282 | 329 | if (tcf_mirred_is_act_redirect(m_eaction)) |
---|
283 | 330 | retval = TC_ACT_SHOT; |
---|
284 | 331 | } |
---|
| 332 | + __this_cpu_dec(mirred_nest_level); |
---|
285 | 333 | |
---|
286 | 334 | return retval; |
---|
287 | 335 | } |
---|
288 | 336 | |
---|
289 | | -static void tcf_stats_update(struct tc_action *a, u64 bytes, u32 packets, |
---|
290 | | - u64 lastuse) |
---|
| 337 | +static void tcf_stats_update(struct tc_action *a, u64 bytes, u64 packets, |
---|
| 338 | + u64 drops, u64 lastuse, bool hw) |
---|
291 | 339 | { |
---|
292 | 340 | struct tcf_mirred *m = to_mirred(a); |
---|
293 | 341 | struct tcf_t *tm = &m->tcf_tm; |
---|
294 | 342 | |
---|
295 | | - _bstats_cpu_update(this_cpu_ptr(a->cpu_bstats), bytes, packets); |
---|
| 343 | + tcf_action_update_stats(a, bytes, packets, drops, hw); |
---|
296 | 344 | tm->lastuse = max_t(u64, tm->lastuse, lastuse); |
---|
297 | 345 | } |
---|
298 | 346 | |
---|
.. | .. |
---|
342 | 390 | return tcf_generic_walker(tn, skb, cb, type, ops, extack); |
---|
343 | 391 | } |
---|
344 | 392 | |
---|
345 | | -static int tcf_mirred_search(struct net *net, struct tc_action **a, u32 index, |
---|
346 | | - struct netlink_ext_ack *extack) |
---|
| 393 | +static int tcf_mirred_search(struct net *net, struct tc_action **a, u32 index) |
---|
347 | 394 | { |
---|
348 | 395 | struct tc_action_net *tn = net_generic(net, mirred_net_id); |
---|
349 | 396 | |
---|
.. | .. |
---|
380 | 427 | .notifier_call = mirred_device_event, |
---|
381 | 428 | }; |
---|
382 | 429 | |
---|
383 | | -static struct net_device *tcf_mirred_get_dev(const struct tc_action *a) |
---|
| 430 | +static void tcf_mirred_dev_put(void *priv) |
---|
| 431 | +{ |
---|
| 432 | + struct net_device *dev = priv; |
---|
| 433 | + |
---|
| 434 | + dev_put(dev); |
---|
| 435 | +} |
---|
| 436 | + |
---|
| 437 | +static struct net_device * |
---|
| 438 | +tcf_mirred_get_dev(const struct tc_action *a, |
---|
| 439 | + tc_action_priv_destructor *destructor) |
---|
384 | 440 | { |
---|
385 | 441 | struct tcf_mirred *m = to_mirred(a); |
---|
386 | 442 | struct net_device *dev; |
---|
387 | 443 | |
---|
388 | 444 | rcu_read_lock(); |
---|
389 | 445 | dev = rcu_dereference(m->tcfm_dev); |
---|
390 | | - if (dev) |
---|
| 446 | + if (dev) { |
---|
391 | 447 | dev_hold(dev); |
---|
| 448 | + *destructor = tcf_mirred_dev_put; |
---|
| 449 | + } |
---|
392 | 450 | rcu_read_unlock(); |
---|
393 | 451 | |
---|
394 | 452 | return dev; |
---|
395 | 453 | } |
---|
396 | 454 | |
---|
397 | | -static void tcf_mirred_put_dev(struct net_device *dev) |
---|
| 455 | +static size_t tcf_mirred_get_fill_size(const struct tc_action *act) |
---|
398 | 456 | { |
---|
399 | | - dev_put(dev); |
---|
| 457 | + return nla_total_size(sizeof(struct tc_mirred)); |
---|
400 | 458 | } |
---|
401 | 459 | |
---|
402 | 460 | static struct tc_action_ops act_mirred_ops = { |
---|
403 | 461 | .kind = "mirred", |
---|
404 | | - .type = TCA_ACT_MIRRED, |
---|
| 462 | + .id = TCA_ID_MIRRED, |
---|
405 | 463 | .owner = THIS_MODULE, |
---|
406 | 464 | .act = tcf_mirred_act, |
---|
407 | 465 | .stats_update = tcf_stats_update, |
---|
.. | .. |
---|
410 | 468 | .init = tcf_mirred_init, |
---|
411 | 469 | .walk = tcf_mirred_walker, |
---|
412 | 470 | .lookup = tcf_mirred_search, |
---|
| 471 | + .get_fill_size = tcf_mirred_get_fill_size, |
---|
413 | 472 | .size = sizeof(struct tcf_mirred), |
---|
414 | 473 | .get_dev = tcf_mirred_get_dev, |
---|
415 | | - .put_dev = tcf_mirred_put_dev, |
---|
416 | 474 | }; |
---|
417 | 475 | |
---|
418 | 476 | static __net_init int mirred_init_net(struct net *net) |
---|