1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * net/sched/act_skbmod.c skb data modifier
5 * Copyright (c) 2016 Jamal Hadi Salim <jhs@mojatatu.com>
8 #include <linux/module.h>
9 #include <linux/init.h>
10 #include <linux/kernel.h>
11 #include <linux/skbuff.h>
12 #include <linux/rtnetlink.h>
13 #include <net/netlink.h>
14 #include <net/pkt_sched.h>
15 #include <net/pkt_cls.h>
17 #include <linux/tc_act/tc_skbmod.h>
18 #include <net/tc_act/tc_skbmod.h>
20 static unsigned int skbmod_net_id
;
21 static struct tc_action_ops act_skbmod_ops
;
23 #define MAX_EDIT_LEN ETH_HLEN
24 static int tcf_skbmod_act(struct sk_buff
*skb
, const struct tc_action
*a
,
25 struct tcf_result
*res
)
27 struct tcf_skbmod
*d
= to_skbmod(a
);
29 struct tcf_skbmod_params
*p
;
33 tcf_lastuse_update(&d
->tcf_tm
);
34 bstats_cpu_update(this_cpu_ptr(d
->common
.cpu_bstats
), skb
);
36 /* XXX: if you are going to edit more fields beyond ethernet header
37 * (example when you add IP header replacement or vlan swap)
38 * then MAX_EDIT_LEN needs to change appropriately
40 err
= skb_ensure_writable(skb
, MAX_EDIT_LEN
);
41 if (unlikely(err
)) /* best policy is to drop on the floor */
44 action
= READ_ONCE(d
->tcf_action
);
45 if (unlikely(action
== TC_ACT_SHOT
))
48 p
= rcu_dereference_bh(d
->skbmod_p
);
50 if (flags
& SKBMOD_F_DMAC
)
51 ether_addr_copy(eth_hdr(skb
)->h_dest
, p
->eth_dst
);
52 if (flags
& SKBMOD_F_SMAC
)
53 ether_addr_copy(eth_hdr(skb
)->h_source
, p
->eth_src
);
54 if (flags
& SKBMOD_F_ETYPE
)
55 eth_hdr(skb
)->h_proto
= p
->eth_type
;
57 if (flags
& SKBMOD_F_SWAPMAC
) {
58 u16 tmpaddr
[ETH_ALEN
/ 2]; /* ether_addr_copy() requirement */
59 /*XXX: I am sure we can come up with more efficient swapping*/
60 ether_addr_copy((u8
*)tmpaddr
, eth_hdr(skb
)->h_dest
);
61 ether_addr_copy(eth_hdr(skb
)->h_dest
, eth_hdr(skb
)->h_source
);
62 ether_addr_copy(eth_hdr(skb
)->h_source
, (u8
*)tmpaddr
);
68 qstats_overlimit_inc(this_cpu_ptr(d
->common
.cpu_qstats
));
72 static const struct nla_policy skbmod_policy
[TCA_SKBMOD_MAX
+ 1] = {
73 [TCA_SKBMOD_PARMS
] = { .len
= sizeof(struct tc_skbmod
) },
74 [TCA_SKBMOD_DMAC
] = { .len
= ETH_ALEN
},
75 [TCA_SKBMOD_SMAC
] = { .len
= ETH_ALEN
},
76 [TCA_SKBMOD_ETYPE
] = { .type
= NLA_U16
},
79 static int tcf_skbmod_init(struct net
*net
, struct nlattr
*nla
,
80 struct nlattr
*est
, struct tc_action
**a
,
81 int ovr
, int bind
, bool rtnl_held
,
82 struct tcf_proto
*tp
, u32 flags
,
83 struct netlink_ext_ack
*extack
)
85 struct tc_action_net
*tn
= net_generic(net
, skbmod_net_id
);
86 struct nlattr
*tb
[TCA_SKBMOD_MAX
+ 1];
87 struct tcf_skbmod_params
*p
, *p_old
;
88 struct tcf_chain
*goto_ch
= NULL
;
89 struct tc_skbmod
*parm
;
90 u32 lflags
= 0, index
;
101 err
= nla_parse_nested_deprecated(tb
, TCA_SKBMOD_MAX
, nla
,
102 skbmod_policy
, NULL
);
106 if (!tb
[TCA_SKBMOD_PARMS
])
109 if (tb
[TCA_SKBMOD_DMAC
]) {
110 daddr
= nla_data(tb
[TCA_SKBMOD_DMAC
]);
111 lflags
|= SKBMOD_F_DMAC
;
114 if (tb
[TCA_SKBMOD_SMAC
]) {
115 saddr
= nla_data(tb
[TCA_SKBMOD_SMAC
]);
116 lflags
|= SKBMOD_F_SMAC
;
119 if (tb
[TCA_SKBMOD_ETYPE
]) {
120 eth_type
= nla_get_u16(tb
[TCA_SKBMOD_ETYPE
]);
121 lflags
|= SKBMOD_F_ETYPE
;
124 parm
= nla_data(tb
[TCA_SKBMOD_PARMS
]);
126 if (parm
->flags
& SKBMOD_F_SWAPMAC
)
127 lflags
= SKBMOD_F_SWAPMAC
;
129 err
= tcf_idr_check_alloc(tn
, &index
, a
, bind
);
138 tcf_idr_release(*a
, bind
);
140 tcf_idr_cleanup(tn
, index
);
145 ret
= tcf_idr_create(tn
, index
, est
, a
,
146 &act_skbmod_ops
, bind
, true, 0);
148 tcf_idr_cleanup(tn
, index
);
154 tcf_idr_release(*a
, bind
);
157 err
= tcf_action_check_ctrlact(parm
->action
, tp
, &goto_ch
, extack
);
163 p
= kzalloc(sizeof(struct tcf_skbmod_params
), GFP_KERNEL
);
172 spin_lock_bh(&d
->tcf_lock
);
173 /* Protected by tcf_lock if overwriting existing action. */
174 goto_ch
= tcf_action_set_ctrlact(*a
, parm
->action
, goto_ch
);
175 p_old
= rcu_dereference_protected(d
->skbmod_p
, 1);
177 if (lflags
& SKBMOD_F_DMAC
)
178 ether_addr_copy(p
->eth_dst
, daddr
);
179 if (lflags
& SKBMOD_F_SMAC
)
180 ether_addr_copy(p
->eth_src
, saddr
);
181 if (lflags
& SKBMOD_F_ETYPE
)
182 p
->eth_type
= htons(eth_type
);
184 rcu_assign_pointer(d
->skbmod_p
, p
);
186 spin_unlock_bh(&d
->tcf_lock
);
189 kfree_rcu(p_old
, rcu
);
191 tcf_chain_put_by_act(goto_ch
);
193 if (ret
== ACT_P_CREATED
)
194 tcf_idr_insert(tn
, *a
);
198 tcf_chain_put_by_act(goto_ch
);
200 tcf_idr_release(*a
, bind
);
204 static void tcf_skbmod_cleanup(struct tc_action
*a
)
206 struct tcf_skbmod
*d
= to_skbmod(a
);
207 struct tcf_skbmod_params
*p
;
209 p
= rcu_dereference_protected(d
->skbmod_p
, 1);
214 static int tcf_skbmod_dump(struct sk_buff
*skb
, struct tc_action
*a
,
217 struct tcf_skbmod
*d
= to_skbmod(a
);
218 unsigned char *b
= skb_tail_pointer(skb
);
219 struct tcf_skbmod_params
*p
;
220 struct tc_skbmod opt
= {
221 .index
= d
->tcf_index
,
222 .refcnt
= refcount_read(&d
->tcf_refcnt
) - ref
,
223 .bindcnt
= atomic_read(&d
->tcf_bindcnt
) - bind
,
227 spin_lock_bh(&d
->tcf_lock
);
228 opt
.action
= d
->tcf_action
;
229 p
= rcu_dereference_protected(d
->skbmod_p
,
230 lockdep_is_held(&d
->tcf_lock
));
231 opt
.flags
= p
->flags
;
232 if (nla_put(skb
, TCA_SKBMOD_PARMS
, sizeof(opt
), &opt
))
233 goto nla_put_failure
;
234 if ((p
->flags
& SKBMOD_F_DMAC
) &&
235 nla_put(skb
, TCA_SKBMOD_DMAC
, ETH_ALEN
, p
->eth_dst
))
236 goto nla_put_failure
;
237 if ((p
->flags
& SKBMOD_F_SMAC
) &&
238 nla_put(skb
, TCA_SKBMOD_SMAC
, ETH_ALEN
, p
->eth_src
))
239 goto nla_put_failure
;
240 if ((p
->flags
& SKBMOD_F_ETYPE
) &&
241 nla_put_u16(skb
, TCA_SKBMOD_ETYPE
, ntohs(p
->eth_type
)))
242 goto nla_put_failure
;
244 tcf_tm_dump(&t
, &d
->tcf_tm
);
245 if (nla_put_64bit(skb
, TCA_SKBMOD_TM
, sizeof(t
), &t
, TCA_SKBMOD_PAD
))
246 goto nla_put_failure
;
248 spin_unlock_bh(&d
->tcf_lock
);
251 spin_unlock_bh(&d
->tcf_lock
);
256 static int tcf_skbmod_walker(struct net
*net
, struct sk_buff
*skb
,
257 struct netlink_callback
*cb
, int type
,
258 const struct tc_action_ops
*ops
,
259 struct netlink_ext_ack
*extack
)
261 struct tc_action_net
*tn
= net_generic(net
, skbmod_net_id
);
263 return tcf_generic_walker(tn
, skb
, cb
, type
, ops
, extack
);
266 static int tcf_skbmod_search(struct net
*net
, struct tc_action
**a
, u32 index
)
268 struct tc_action_net
*tn
= net_generic(net
, skbmod_net_id
);
270 return tcf_idr_search(tn
, a
, index
);
273 static struct tc_action_ops act_skbmod_ops
= {
275 .id
= TCA_ACT_SKBMOD
,
276 .owner
= THIS_MODULE
,
277 .act
= tcf_skbmod_act
,
278 .dump
= tcf_skbmod_dump
,
279 .init
= tcf_skbmod_init
,
280 .cleanup
= tcf_skbmod_cleanup
,
281 .walk
= tcf_skbmod_walker
,
282 .lookup
= tcf_skbmod_search
,
283 .size
= sizeof(struct tcf_skbmod
),
286 static __net_init
int skbmod_init_net(struct net
*net
)
288 struct tc_action_net
*tn
= net_generic(net
, skbmod_net_id
);
290 return tc_action_net_init(net
, tn
, &act_skbmod_ops
);
293 static void __net_exit
skbmod_exit_net(struct list_head
*net_list
)
295 tc_action_net_exit(net_list
, skbmod_net_id
);
298 static struct pernet_operations skbmod_net_ops
= {
299 .init
= skbmod_init_net
,
300 .exit_batch
= skbmod_exit_net
,
301 .id
= &skbmod_net_id
,
302 .size
= sizeof(struct tc_action_net
),
305 MODULE_AUTHOR("Jamal Hadi Salim, <jhs@mojatatu.com>");
306 MODULE_DESCRIPTION("SKB data mod-ing");
307 MODULE_LICENSE("GPL");
309 static int __init
skbmod_init_module(void)
311 return tcf_register_action(&act_skbmod_ops
, &skbmod_net_ops
);
314 static void __exit
skbmod_cleanup_module(void)
316 tcf_unregister_action(&act_skbmod_ops
, &skbmod_net_ops
);
319 module_init(skbmod_init_module
);
320 module_exit(skbmod_cleanup_module
);