2 * net/sched/act_skbmod.c skb data modifier
4 * Copyright (c) 2016 Jamal Hadi Salim <jhs@mojatatu.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/skbuff.h>
16 #include <linux/rtnetlink.h>
17 #include <net/netlink.h>
18 #include <net/pkt_sched.h>
20 #include <linux/tc_act/tc_skbmod.h>
21 #include <net/tc_act/tc_skbmod.h>
23 #define SKBMOD_TAB_MASK 15
25 static unsigned int skbmod_net_id
;
26 static struct tc_action_ops act_skbmod_ops
;
28 #define MAX_EDIT_LEN ETH_HLEN
29 static int tcf_skbmod_run(struct sk_buff
*skb
, const struct tc_action
*a
,
30 struct tcf_result
*res
)
32 struct tcf_skbmod
*d
= to_skbmod(a
);
34 struct tcf_skbmod_params
*p
;
38 tcf_lastuse_update(&d
->tcf_tm
);
39 bstats_cpu_update(this_cpu_ptr(d
->common
.cpu_bstats
), skb
);
41 /* XXX: if you are going to edit more fields beyond ethernet header
42 * (example when you add IP header replacement or vlan swap)
43 * then MAX_EDIT_LEN needs to change appropriately
45 err
= skb_ensure_writable(skb
, MAX_EDIT_LEN
);
46 if (unlikely(err
)) { /* best policy is to drop on the floor */
47 qstats_overlimit_inc(this_cpu_ptr(d
->common
.cpu_qstats
));
52 action
= READ_ONCE(d
->tcf_action
);
53 if (unlikely(action
== TC_ACT_SHOT
)) {
54 qstats_overlimit_inc(this_cpu_ptr(d
->common
.cpu_qstats
));
59 p
= rcu_dereference(d
->skbmod_p
);
61 if (flags
& SKBMOD_F_DMAC
)
62 ether_addr_copy(eth_hdr(skb
)->h_dest
, p
->eth_dst
);
63 if (flags
& SKBMOD_F_SMAC
)
64 ether_addr_copy(eth_hdr(skb
)->h_source
, p
->eth_src
);
65 if (flags
& SKBMOD_F_ETYPE
)
66 eth_hdr(skb
)->h_proto
= p
->eth_type
;
69 if (flags
& SKBMOD_F_SWAPMAC
) {
70 u16 tmpaddr
[ETH_ALEN
/ 2]; /* ether_addr_copy() requirement */
71 /*XXX: I am sure we can come up with more efficient swapping*/
72 ether_addr_copy((u8
*)tmpaddr
, eth_hdr(skb
)->h_dest
);
73 ether_addr_copy(eth_hdr(skb
)->h_dest
, eth_hdr(skb
)->h_source
);
74 ether_addr_copy(eth_hdr(skb
)->h_source
, (u8
*)tmpaddr
);
80 static const struct nla_policy skbmod_policy
[TCA_SKBMOD_MAX
+ 1] = {
81 [TCA_SKBMOD_PARMS
] = { .len
= sizeof(struct tc_skbmod
) },
82 [TCA_SKBMOD_DMAC
] = { .len
= ETH_ALEN
},
83 [TCA_SKBMOD_SMAC
] = { .len
= ETH_ALEN
},
84 [TCA_SKBMOD_ETYPE
] = { .type
= NLA_U16
},
87 static int tcf_skbmod_init(struct net
*net
, struct nlattr
*nla
,
88 struct nlattr
*est
, struct tc_action
**a
,
91 struct tc_action_net
*tn
= net_generic(net
, skbmod_net_id
);
92 struct nlattr
*tb
[TCA_SKBMOD_MAX
+ 1];
93 struct tcf_skbmod_params
*p
, *p_old
;
94 struct tc_skbmod
*parm
;
106 err
= nla_parse_nested(tb
, TCA_SKBMOD_MAX
, nla
, skbmod_policy
);
110 if (!tb
[TCA_SKBMOD_PARMS
])
113 if (tb
[TCA_SKBMOD_DMAC
]) {
114 daddr
= nla_data(tb
[TCA_SKBMOD_DMAC
]);
115 lflags
|= SKBMOD_F_DMAC
;
118 if (tb
[TCA_SKBMOD_SMAC
]) {
119 saddr
= nla_data(tb
[TCA_SKBMOD_SMAC
]);
120 lflags
|= SKBMOD_F_SMAC
;
123 if (tb
[TCA_SKBMOD_ETYPE
]) {
124 eth_type
= nla_get_u16(tb
[TCA_SKBMOD_ETYPE
]);
125 lflags
|= SKBMOD_F_ETYPE
;
128 parm
= nla_data(tb
[TCA_SKBMOD_PARMS
]);
129 if (parm
->flags
& SKBMOD_F_SWAPMAC
)
130 lflags
= SKBMOD_F_SWAPMAC
;
132 exists
= tcf_hash_check(tn
, parm
->index
, a
, bind
);
140 ret
= tcf_hash_create(tn
, parm
->index
, est
, a
,
141 &act_skbmod_ops
, bind
, true);
147 tcf_hash_release(*a
, bind
);
155 p
= kzalloc(sizeof(struct tcf_skbmod_params
), GFP_KERNEL
);
158 tcf_hash_release(*a
, bind
);
163 d
->tcf_action
= parm
->action
;
165 p_old
= rtnl_dereference(d
->skbmod_p
);
168 spin_lock_bh(&d
->tcf_lock
);
170 if (lflags
& SKBMOD_F_DMAC
)
171 ether_addr_copy(p
->eth_dst
, daddr
);
172 if (lflags
& SKBMOD_F_SMAC
)
173 ether_addr_copy(p
->eth_src
, saddr
);
174 if (lflags
& SKBMOD_F_ETYPE
)
175 p
->eth_type
= htons(eth_type
);
177 rcu_assign_pointer(d
->skbmod_p
, p
);
179 spin_unlock_bh(&d
->tcf_lock
);
182 kfree_rcu(p_old
, rcu
);
184 if (ret
== ACT_P_CREATED
)
185 tcf_hash_insert(tn
, *a
);
189 static void tcf_skbmod_cleanup(struct tc_action
*a
, int bind
)
191 struct tcf_skbmod
*d
= to_skbmod(a
);
192 struct tcf_skbmod_params
*p
;
194 p
= rcu_dereference_protected(d
->skbmod_p
, 1);
198 static int tcf_skbmod_dump(struct sk_buff
*skb
, struct tc_action
*a
,
201 struct tcf_skbmod
*d
= to_skbmod(a
);
202 unsigned char *b
= skb_tail_pointer(skb
);
203 struct tcf_skbmod_params
*p
= rtnl_dereference(d
->skbmod_p
);
204 struct tc_skbmod opt
= {
205 .index
= d
->tcf_index
,
206 .refcnt
= d
->tcf_refcnt
- ref
,
207 .bindcnt
= d
->tcf_bindcnt
- bind
,
208 .action
= d
->tcf_action
,
212 opt
.flags
= p
->flags
;
213 if (nla_put(skb
, TCA_SKBMOD_PARMS
, sizeof(opt
), &opt
))
214 goto nla_put_failure
;
215 if ((p
->flags
& SKBMOD_F_DMAC
) &&
216 nla_put(skb
, TCA_SKBMOD_DMAC
, ETH_ALEN
, p
->eth_dst
))
217 goto nla_put_failure
;
218 if ((p
->flags
& SKBMOD_F_SMAC
) &&
219 nla_put(skb
, TCA_SKBMOD_SMAC
, ETH_ALEN
, p
->eth_src
))
220 goto nla_put_failure
;
221 if ((p
->flags
& SKBMOD_F_ETYPE
) &&
222 nla_put_u16(skb
, TCA_SKBMOD_ETYPE
, ntohs(p
->eth_type
)))
223 goto nla_put_failure
;
225 tcf_tm_dump(&t
, &d
->tcf_tm
);
226 if (nla_put_64bit(skb
, TCA_SKBMOD_TM
, sizeof(t
), &t
, TCA_SKBMOD_PAD
))
227 goto nla_put_failure
;
235 static int tcf_skbmod_walker(struct net
*net
, struct sk_buff
*skb
,
236 struct netlink_callback
*cb
, int type
,
237 const struct tc_action_ops
*ops
)
239 struct tc_action_net
*tn
= net_generic(net
, skbmod_net_id
);
241 return tcf_generic_walker(tn
, skb
, cb
, type
, ops
);
244 static int tcf_skbmod_search(struct net
*net
, struct tc_action
**a
, u32 index
)
246 struct tc_action_net
*tn
= net_generic(net
, skbmod_net_id
);
248 return tcf_hash_search(tn
, a
, index
);
251 static struct tc_action_ops act_skbmod_ops
= {
253 .type
= TCA_ACT_SKBMOD
,
254 .owner
= THIS_MODULE
,
255 .act
= tcf_skbmod_run
,
256 .dump
= tcf_skbmod_dump
,
257 .init
= tcf_skbmod_init
,
258 .cleanup
= tcf_skbmod_cleanup
,
259 .walk
= tcf_skbmod_walker
,
260 .lookup
= tcf_skbmod_search
,
261 .size
= sizeof(struct tcf_skbmod
),
264 static __net_init
int skbmod_init_net(struct net
*net
)
266 struct tc_action_net
*tn
= net_generic(net
, skbmod_net_id
);
268 return tc_action_net_init(tn
, &act_skbmod_ops
, SKBMOD_TAB_MASK
);
271 static void __net_exit
skbmod_exit_net(struct net
*net
)
273 struct tc_action_net
*tn
= net_generic(net
, skbmod_net_id
);
275 tc_action_net_exit(tn
);
278 static struct pernet_operations skbmod_net_ops
= {
279 .init
= skbmod_init_net
,
280 .exit
= skbmod_exit_net
,
281 .id
= &skbmod_net_id
,
282 .size
= sizeof(struct tc_action_net
),
285 MODULE_AUTHOR("Jamal Hadi Salim, <jhs@mojatatu.com>");
286 MODULE_DESCRIPTION("SKB data mod-ing");
287 MODULE_LICENSE("GPL");
289 static int __init
skbmod_init_module(void)
291 return tcf_register_action(&act_skbmod_ops
, &skbmod_net_ops
);
294 static void __exit
skbmod_cleanup_module(void)
296 tcf_unregister_action(&act_skbmod_ops
, &skbmod_net_ops
);
299 module_init(skbmod_init_module
);
300 module_exit(skbmod_cleanup_module
);