| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us> |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 5 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 6 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 7 | | - * (at your option) any later version. |
|---|
| 8 | 4 | */ |
|---|
| 9 | 5 | |
|---|
| 10 | 6 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 16 | 12 | #include <linux/bpf.h> |
|---|
| 17 | 13 | |
|---|
| 18 | 14 | #include <net/netlink.h> |
|---|
| 15 | +#include <net/sock.h> |
|---|
| 19 | 16 | #include <net/pkt_sched.h> |
|---|
| 17 | +#include <net/pkt_cls.h> |
|---|
| 20 | 18 | |
|---|
| 21 | 19 | #include <linux/tc_act/tc_bpf.h> |
|---|
| 22 | 20 | #include <net/tc_act/tc_bpf.h> |
|---|
| .. | .. |
|---|
| 56 | 54 | bpf_compute_data_pointers(skb); |
|---|
| 57 | 55 | filter_res = BPF_PROG_RUN(filter, skb); |
|---|
| 58 | 56 | } |
|---|
| 57 | + if (skb_sk_is_prefetched(skb) && filter_res != TC_ACT_OK) |
|---|
| 58 | + skb_orphan(skb); |
|---|
| 59 | 59 | rcu_read_unlock(); |
|---|
| 60 | 60 | |
|---|
| 61 | 61 | /* A BPF program may overwrite the default action opcode. |
|---|
| .. | .. |
|---|
| 278 | 278 | static int tcf_bpf_init(struct net *net, struct nlattr *nla, |
|---|
| 279 | 279 | struct nlattr *est, struct tc_action **act, |
|---|
| 280 | 280 | int replace, int bind, bool rtnl_held, |
|---|
| 281 | + struct tcf_proto *tp, u32 flags, |
|---|
| 281 | 282 | struct netlink_ext_ack *extack) |
|---|
| 282 | 283 | { |
|---|
| 283 | 284 | struct tc_action_net *tn = net_generic(net, bpf_net_id); |
|---|
| 284 | 285 | struct nlattr *tb[TCA_ACT_BPF_MAX + 1]; |
|---|
| 286 | + struct tcf_chain *goto_ch = NULL; |
|---|
| 285 | 287 | struct tcf_bpf_cfg cfg, old; |
|---|
| 286 | 288 | struct tc_act_bpf *parm; |
|---|
| 287 | 289 | struct tcf_bpf *prog; |
|---|
| .. | .. |
|---|
| 292 | 294 | if (!nla) |
|---|
| 293 | 295 | return -EINVAL; |
|---|
| 294 | 296 | |
|---|
| 295 | | - ret = nla_parse_nested(tb, TCA_ACT_BPF_MAX, nla, act_bpf_policy, NULL); |
|---|
| 297 | + ret = nla_parse_nested_deprecated(tb, TCA_ACT_BPF_MAX, nla, |
|---|
| 298 | + act_bpf_policy, NULL); |
|---|
| 296 | 299 | if (ret < 0) |
|---|
| 297 | 300 | return ret; |
|---|
| 298 | 301 | |
|---|
| .. | .. |
|---|
| 304 | 307 | ret = tcf_idr_check_alloc(tn, &index, act, bind); |
|---|
| 305 | 308 | if (!ret) { |
|---|
| 306 | 309 | ret = tcf_idr_create(tn, index, est, act, |
|---|
| 307 | | - &act_bpf_ops, bind, true); |
|---|
| 310 | + &act_bpf_ops, bind, true, flags); |
|---|
| 308 | 311 | if (ret < 0) { |
|---|
| 309 | 312 | tcf_idr_cleanup(tn, index); |
|---|
| 310 | 313 | return ret; |
|---|
| .. | .. |
|---|
| 324 | 327 | return ret; |
|---|
| 325 | 328 | } |
|---|
| 326 | 329 | |
|---|
| 330 | + ret = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); |
|---|
| 331 | + if (ret < 0) |
|---|
| 332 | + goto release_idr; |
|---|
| 333 | + |
|---|
| 327 | 334 | is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS]; |
|---|
| 328 | 335 | is_ebpf = tb[TCA_ACT_BPF_FD]; |
|---|
| 329 | 336 | |
|---|
| 330 | 337 | if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf)) { |
|---|
| 331 | 338 | ret = -EINVAL; |
|---|
| 332 | | - goto out; |
|---|
| 339 | + goto put_chain; |
|---|
| 333 | 340 | } |
|---|
| 334 | 341 | |
|---|
| 335 | 342 | memset(&cfg, 0, sizeof(cfg)); |
|---|
| .. | .. |
|---|
| 337 | 344 | ret = is_bpf ? tcf_bpf_init_from_ops(tb, &cfg) : |
|---|
| 338 | 345 | tcf_bpf_init_from_efd(tb, &cfg); |
|---|
| 339 | 346 | if (ret < 0) |
|---|
| 340 | | - goto out; |
|---|
| 347 | + goto put_chain; |
|---|
| 341 | 348 | |
|---|
| 342 | 349 | prog = to_bpf(*act); |
|---|
| 343 | 350 | |
|---|
| .. | .. |
|---|
| 351 | 358 | if (cfg.bpf_num_ops) |
|---|
| 352 | 359 | prog->bpf_num_ops = cfg.bpf_num_ops; |
|---|
| 353 | 360 | |
|---|
| 354 | | - prog->tcf_action = parm->action; |
|---|
| 361 | + goto_ch = tcf_action_set_ctrlact(*act, parm->action, goto_ch); |
|---|
| 355 | 362 | rcu_assign_pointer(prog->filter, cfg.filter); |
|---|
| 356 | 363 | spin_unlock_bh(&prog->tcf_lock); |
|---|
| 357 | 364 | |
|---|
| 358 | | - if (res == ACT_P_CREATED) { |
|---|
| 359 | | - tcf_idr_insert(tn, *act); |
|---|
| 360 | | - } else { |
|---|
| 365 | + if (goto_ch) |
|---|
| 366 | + tcf_chain_put_by_act(goto_ch); |
|---|
| 367 | + |
|---|
| 368 | + if (res != ACT_P_CREATED) { |
|---|
| 361 | 369 | /* make sure the program being replaced is no longer executing */ |
|---|
| 362 | 370 | synchronize_rcu(); |
|---|
| 363 | 371 | tcf_bpf_cfg_cleanup(&old); |
|---|
| 364 | 372 | } |
|---|
| 365 | 373 | |
|---|
| 366 | 374 | return res; |
|---|
| 367 | | -out: |
|---|
| 368 | | - tcf_idr_release(*act, bind); |
|---|
| 369 | 375 | |
|---|
| 376 | +put_chain: |
|---|
| 377 | + if (goto_ch) |
|---|
| 378 | + tcf_chain_put_by_act(goto_ch); |
|---|
| 379 | + |
|---|
| 380 | +release_idr: |
|---|
| 381 | + tcf_idr_release(*act, bind); |
|---|
| 370 | 382 | return ret; |
|---|
| 371 | 383 | } |
|---|
| 372 | 384 | |
|---|
| .. | .. |
|---|
| 388 | 400 | return tcf_generic_walker(tn, skb, cb, type, ops, extack); |
|---|
| 389 | 401 | } |
|---|
| 390 | 402 | |
|---|
| 391 | | -static int tcf_bpf_search(struct net *net, struct tc_action **a, u32 index, |
|---|
| 392 | | - struct netlink_ext_ack *extack) |
|---|
| 403 | +static int tcf_bpf_search(struct net *net, struct tc_action **a, u32 index) |
|---|
| 393 | 404 | { |
|---|
| 394 | 405 | struct tc_action_net *tn = net_generic(net, bpf_net_id); |
|---|
| 395 | 406 | |
|---|
| .. | .. |
|---|
| 398 | 409 | |
|---|
| 399 | 410 | static struct tc_action_ops act_bpf_ops __read_mostly = { |
|---|
| 400 | 411 | .kind = "bpf", |
|---|
| 401 | | - .type = TCA_ACT_BPF, |
|---|
| 412 | + .id = TCA_ID_BPF, |
|---|
| 402 | 413 | .owner = THIS_MODULE, |
|---|
| 403 | 414 | .act = tcf_bpf_act, |
|---|
| 404 | 415 | .dump = tcf_bpf_dump, |
|---|