static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { struct tc_cls_bpf_offload *cls_bpf = type_data; struct nfp_net *nn = cb_priv; struct bpf_prog *oldprog; struct nfp_bpf_vnic *bv; int err; if (type != TC_SETUP_CLSBPF) { NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, "only offload of BPF classifiers supported"); return -EOPNOTSUPP; } if (!tc_cls_can_offload_and_chain0(nn->dp.netdev, &cls_bpf->common)) return -EOPNOTSUPP; if (!nfp_net_ebpf_capable(nn)) { NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, "NFP firmware does not support eBPF offload"); return -EOPNOTSUPP; } if (cls_bpf->common.protocol != htons(ETH_P_ALL)) { NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, "only ETH_P_ALL supported as filter protocol"); return -EOPNOTSUPP; } /* Only support TC direct action */ if (!cls_bpf->exts_integrated || tcf_exts_has_actions(cls_bpf->exts)) { NL_SET_ERR_MSG_MOD(cls_bpf->common.extack, "only direct action with no legacy actions supported"); return -EOPNOTSUPP; } if (cls_bpf->command != TC_CLSBPF_OFFLOAD) return -EOPNOTSUPP; bv = nn->app_priv; oldprog = cls_bpf->oldprog; /* Don't remove if oldprog doesn't match driver's state */ if (bv->tc_prog != oldprog) { oldprog = NULL; if (!cls_bpf->prog) return 0; } err = nfp_net_bpf_offload(nn, cls_bpf->prog, oldprog, cls_bpf->common.extack); if (err) return err; bv->tc_prog = cls_bpf->prog; nn->port->tc_offload_cnt = !!bv->tc_prog; return 0; }
/* Fill ch_filter_specification with parsed action. */ static int fill_action_fields(struct adapter *adap, struct ch_filter_specification *fs, struct tc_cls_u32_offload *cls) { unsigned int num_actions = 0; const struct tc_action *a; struct tcf_exts *exts; int i; exts = cls->knode.exts; if (!tcf_exts_has_actions(exts)) return -EINVAL; tcf_exts_for_each_action(i, a, exts) { /* Don't allow more than one action per rule. */ if (num_actions) return -EINVAL; /* Drop in hardware. */ if (is_tcf_gact_shot(a)) { fs->action = FILTER_DROP; num_actions++; continue; } /* Re-direct to specified port in hardware. */ if (is_tcf_mirred_egress_redirect(a)) { struct net_device *n_dev, *target_dev; bool found = false; unsigned int i; target_dev = tcf_mirred_dev(a); for_each_port(adap, i) { n_dev = adap->port[i]; if (target_dev == n_dev) { fs->action = FILTER_SWITCH; fs->eport = i; found = true; break; } } /* Interface doesn't belong to any port of * the underlying hardware. */ if (!found) return -EINVAL; num_actions++; continue; } /* Un-supported action. */ return -EINVAL; }
static int bnxt_tc_parse_actions(struct bnxt *bp, struct bnxt_tc_actions *actions, struct tcf_exts *tc_exts) { const struct tc_action *tc_act; int i, rc; if (!tcf_exts_has_actions(tc_exts)) { netdev_info(bp->dev, "no actions"); return -EINVAL; } tcf_exts_for_each_action(i, tc_act, tc_exts) { /* Drop action */ if (is_tcf_gact_shot(tc_act)) { actions->flags |= BNXT_TC_ACTION_FLAG_DROP; return 0; /* don't bother with other actions */ } /* Redirect action */ if (is_tcf_mirred_egress_redirect(tc_act)) { rc = bnxt_tc_parse_redir(bp, actions, tc_act); if (rc) return rc; continue; } /* Push/pop VLAN */ if (is_tcf_vlan(tc_act)) { rc = bnxt_tc_parse_vlan(bp, actions, tc_act); if (rc) return rc; continue; } /* Tunnel encap */ if (is_tcf_tunnel_set(tc_act)) { rc = bnxt_tc_parse_tunnel_set(bp, actions, tc_act); if (rc) return rc; continue; } /* Tunnel decap */ if (is_tcf_tunnel_release(tc_act)) { actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_DECAP; continue; } } if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) { /* dst_fid is PF's fid */ actions->dst_fid = bp->pf.fw_fid; } else { /* find the FID from dst_dev */ actions->dst_fid = bnxt_flow_get_dst_fid(bp, actions->dst_dev); if (actions->dst_fid == BNXT_FID_INVALID) return -EINVAL; } } return 0; }
static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_block *block, struct mlxsw_sp_acl_rule_info *rulei, struct tcf_exts *exts, struct netlink_ext_ack *extack) { const struct tc_action *a; int err, i; if (!tcf_exts_has_actions(exts)) return 0; /* Count action is inserted first */ err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack); if (err) return err; tcf_exts_for_each_action(i, a, exts) { if (is_tcf_gact_ok(a)) { err = mlxsw_sp_acl_rulei_act_terminate(rulei); if (err) { NL_SET_ERR_MSG_MOD(extack, "Cannot append terminate action"); return err; } } else if (is_tcf_gact_shot(a)) { err = mlxsw_sp_acl_rulei_act_drop(rulei); if (err) { NL_SET_ERR_MSG_MOD(extack, "Cannot append drop action"); return err; } } else if (is_tcf_gact_trap(a)) { err = mlxsw_sp_acl_rulei_act_trap(rulei); if (err) { NL_SET_ERR_MSG_MOD(extack, "Cannot append trap action"); return err; } } else if (is_tcf_gact_goto_chain(a)) { u32 chain_index = tcf_gact_goto_chain_index(a); struct mlxsw_sp_acl_ruleset *ruleset; u16 group_id; ruleset = mlxsw_sp_acl_ruleset_lookup(mlxsw_sp, block, chain_index, MLXSW_SP_ACL_PROFILE_FLOWER); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); group_id = mlxsw_sp_acl_ruleset_group_id(ruleset); err = mlxsw_sp_acl_rulei_act_jump(rulei, group_id); if (err) { NL_SET_ERR_MSG_MOD(extack, "Cannot append jump action"); return err; } } else if (is_tcf_mirred_egress_redirect(a)) { struct net_device *out_dev; struct mlxsw_sp_fid *fid; u16 fid_index; fid = mlxsw_sp_acl_dummy_fid(mlxsw_sp); fid_index = mlxsw_sp_fid_index(fid); err = mlxsw_sp_acl_rulei_act_fid_set(mlxsw_sp, rulei, fid_index, extack); if (err) return err; out_dev = tcf_mirred_dev(a); err = mlxsw_sp_acl_rulei_act_fwd(mlxsw_sp, rulei, out_dev, extack); if (err) return err; } else if (is_tcf_mirred_egress_mirror(a)) { struct net_device *out_dev = tcf_mirred_dev(a); err = mlxsw_sp_acl_rulei_act_mirror(mlxsw_sp, rulei, block, out_dev, extack); if (err) return err; } else if (is_tcf_vlan(a)) { u16 proto = be16_to_cpu(tcf_vlan_push_proto(a)); u32 action = tcf_vlan_action(a); u8 prio = tcf_vlan_push_prio(a); u16 vid = tcf_vlan_push_vid(a); return mlxsw_sp_acl_rulei_act_vlan(mlxsw_sp, rulei, action, vid, proto, prio, extack); } else { NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n"); return -EOPNOTSUPP; } } return 0; }