static int tcf_skbmod_run(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { struct tcf_skbmod *d = to_skbmod(a); int action = READ_ONCE(d->tcf_action); u64 flags = READ_ONCE(d->flags); tcf_lastuse_update(&d->tcf_tm); bstats_cpu_update(this_cpu_ptr(d->common.cpu_bstats), skb); if (unlikely(action == TC_ACT_SHOT)) { qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats)); return action; } flags = READ_ONCE(d->flags); rcu_read_lock(); if (flags & SKBMOD_F_DMAC) ether_addr_copy(eth_hdr(skb)->h_dest, d->eth_dst); if (flags & SKBMOD_F_SMAC) ether_addr_copy(eth_hdr(skb)->h_source, d->eth_src); if (flags & SKBMOD_F_ETYPE) eth_hdr(skb)->h_proto = d->eth_type; if (flags & SKBMOD_F_SWAPMAC) { u8 tmpaddr[ETH_ALEN]; /*XXX: I am sure we can come up with something more efficient */ ether_addr_copy(tmpaddr, eth_hdr(skb)->h_dest); ether_addr_copy(eth_hdr(skb)->h_dest, eth_hdr(skb)->h_source); ether_addr_copy(eth_hdr(skb)->h_source, tmpaddr); } rcu_read_unlock(); return action; } static int tcf_skbmod_init(struct net *net, struct nlattr *nla, struct nlattr *est, struct tc_action **a, int ovr, int bind) { struct tc_action_net *tn = net_generic(net, skbmod_net_id); struct nlattr *tb[TCA_SKBMOD_MAX + 1]; struct tc_skbmod *parm; struct tcf_skbmod *d; u32 lflags = 0; u8 *daddr = NULL; u8 *saddr = NULL; u16 eth_type = 0; bool exists = false; int ret = 0, err; if (nla == NULL) return -EINVAL; err = nla_parse_nested(tb, TCA_SKBMOD_MAX, nla, skbmod_policy); if (err < 0) return err; if (tb[TCA_SKBMOD_PARMS] == NULL) return -EINVAL; if (tb[TCA_SKBMOD_DMAC]) { daddr = nla_data(tb[TCA_SKBMOD_DMAC]); lflags |= SKBMOD_F_DMAC; } if (tb[TCA_SKBMOD_SMAC]) { saddr = nla_data(tb[TCA_SKBMOD_SMAC]); lflags |= SKBMOD_F_SMAC; } if (tb[TCA_SKBMOD_ETYPE]) { eth_type = nla_get_u16(tb[TCA_SKBMOD_ETYPE]); lflags |= SKBMOD_F_ETYPE; } parm = nla_data(tb[TCA_SKBMOD_PARMS]); if (parm->flags & SKBMOD_F_SWAPMAC) lflags = SKBMOD_F_SWAPMAC; exists = tcf_hash_check(tn, parm->index, a, bind); if (exists && bind) return 0; if (!lflags) { return -EINVAL; } if (!exists) { ret = tcf_hash_create(tn, parm->index, est, a, &act_skbmod_ops, bind, false); if (ret) return ret; d = to_skbmod(*a); ret = ACT_P_CREATED; } else { d = to_skbmod(*a); tcf_hash_release(*a, bind); if (!ovr) return -EEXIST; } ASSERT_RTNL(); d->flags = lflags; d->tcf_action = parm->action; if (ovr) spin_lock_bh(&d->tcf_lock); if (lflags & SKBMOD_F_DMAC) ether_addr_copy(d->eth_dst, daddr); if (lflags & SKBMOD_F_SMAC) ether_addr_copy(d->eth_src, saddr); if (lflags & SKBMOD_F_ETYPE) d->eth_type = htons(eth_type); if (ovr) { spin_unlock_bh(&d->tcf_lock); synchronize_rcu(); } if (ret == ACT_P_CREATED) tcf_hash_insert(tn, *a); return ret; }