| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (c) 2008, Intel Corporation. | 
|  | 3 | * | 
|  | 4 | * This program is free software; you can redistribute it and/or modify it | 
|  | 5 | * under the terms and conditions of the GNU General Public License, | 
|  | 6 | * version 2, as published by the Free Software Foundation. | 
|  | 7 | * | 
|  | 8 | * This program is distributed in the hope it will be useful, but WITHOUT | 
|  | 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | 10 | * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
|  | 11 | * more details. | 
|  | 12 | * | 
|  | 13 | * You should have received a copy of the GNU General Public License along with | 
|  | 14 | * this program; if not, see <http://www.gnu.org/licenses/>. | 
|  | 15 | * | 
|  | 16 | * Author: Alexander Duyck <alexander.h.duyck@intel.com> | 
|  | 17 | */ | 
|  | 18 |  | 
|  | 19 | #include <linux/module.h> | 
|  | 20 | #include <linux/init.h> | 
|  | 21 | #include <linux/kernel.h> | 
|  | 22 | #include <linux/skbuff.h> | 
|  | 23 | #include <linux/rtnetlink.h> | 
|  | 24 | #include <net/netlink.h> | 
|  | 25 | #include <net/pkt_sched.h> | 
|  | 26 | #include <net/ip.h> | 
|  | 27 | #include <net/ipv6.h> | 
|  | 28 | #include <net/dsfield.h> | 
|  | 29 |  | 
|  | 30 | #include <linux/tc_act/tc_skbedit.h> | 
|  | 31 | #include <net/tc_act/tc_skbedit.h> | 
|  | 32 |  | 
|  | 33 | static unsigned int skbedit_net_id; | 
|  | 34 | static struct tc_action_ops act_skbedit_ops; | 
|  | 35 |  | 
|  | 36 | static int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a, | 
|  | 37 | struct tcf_result *res) | 
|  | 38 | { | 
|  | 39 | struct tcf_skbedit *d = to_skbedit(a); | 
|  | 40 | struct tcf_skbedit_params *params; | 
|  | 41 | int action; | 
|  | 42 |  | 
|  | 43 | tcf_lastuse_update(&d->tcf_tm); | 
|  | 44 | bstats_cpu_update(this_cpu_ptr(d->common.cpu_bstats), skb); | 
|  | 45 |  | 
|  | 46 | params = rcu_dereference_bh(d->params); | 
|  | 47 | action = READ_ONCE(d->tcf_action); | 
|  | 48 |  | 
|  | 49 | if (params->flags & SKBEDIT_F_PRIORITY) | 
|  | 50 | skb->priority = params->priority; | 
|  | 51 | if (params->flags & SKBEDIT_F_INHERITDSFIELD) { | 
|  | 52 | int wlen = skb_network_offset(skb); | 
|  | 53 |  | 
|  | 54 | switch (tc_skb_protocol(skb)) { | 
|  | 55 | case htons(ETH_P_IP): | 
|  | 56 | wlen += sizeof(struct iphdr); | 
|  | 57 | if (!pskb_may_pull(skb, wlen)) | 
|  | 58 | goto err; | 
|  | 59 | skb->priority = ipv4_get_dsfield(ip_hdr(skb)) >> 2; | 
|  | 60 | break; | 
|  | 61 |  | 
|  | 62 | case htons(ETH_P_IPV6): | 
|  | 63 | wlen += sizeof(struct ipv6hdr); | 
|  | 64 | if (!pskb_may_pull(skb, wlen)) | 
|  | 65 | goto err; | 
|  | 66 | skb->priority = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; | 
|  | 67 | break; | 
|  | 68 | } | 
|  | 69 | } | 
|  | 70 | if (params->flags & SKBEDIT_F_QUEUE_MAPPING && | 
|  | 71 | skb->dev->real_num_tx_queues > params->queue_mapping) | 
|  | 72 | skb_set_queue_mapping(skb, params->queue_mapping); | 
|  | 73 | if (params->flags & SKBEDIT_F_MARK) { | 
|  | 74 | skb->mark &= ~params->mask; | 
|  | 75 | skb->mark |= params->mark & params->mask; | 
|  | 76 | } | 
|  | 77 | if (params->flags & SKBEDIT_F_PTYPE) | 
|  | 78 | skb->pkt_type = params->ptype; | 
|  | 79 | return action; | 
|  | 80 |  | 
|  | 81 | err: | 
|  | 82 | qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats)); | 
|  | 83 | return TC_ACT_SHOT; | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { | 
|  | 87 | [TCA_SKBEDIT_PARMS]		= { .len = sizeof(struct tc_skbedit) }, | 
|  | 88 | [TCA_SKBEDIT_PRIORITY]		= { .len = sizeof(u32) }, | 
|  | 89 | [TCA_SKBEDIT_QUEUE_MAPPING]	= { .len = sizeof(u16) }, | 
|  | 90 | [TCA_SKBEDIT_MARK]		= { .len = sizeof(u32) }, | 
|  | 91 | [TCA_SKBEDIT_PTYPE]		= { .len = sizeof(u16) }, | 
|  | 92 | [TCA_SKBEDIT_MASK]		= { .len = sizeof(u32) }, | 
|  | 93 | [TCA_SKBEDIT_FLAGS]		= { .len = sizeof(u64) }, | 
|  | 94 | }; | 
|  | 95 |  | 
|  | 96 | static int tcf_skbedit_init(struct net *net, struct nlattr *nla, | 
|  | 97 | struct nlattr *est, struct tc_action **a, | 
|  | 98 | int ovr, int bind, bool rtnl_held, | 
|  | 99 | struct netlink_ext_ack *extack) | 
|  | 100 | { | 
|  | 101 | struct tc_action_net *tn = net_generic(net, skbedit_net_id); | 
|  | 102 | struct tcf_skbedit_params *params_old, *params_new; | 
|  | 103 | struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; | 
|  | 104 | struct tc_skbedit *parm; | 
|  | 105 | struct tcf_skbedit *d; | 
|  | 106 | u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL; | 
|  | 107 | u16 *queue_mapping = NULL, *ptype = NULL; | 
|  | 108 | bool exists = false; | 
|  | 109 | int ret = 0, err; | 
|  | 110 | u32 index; | 
|  | 111 |  | 
|  | 112 | if (nla == NULL) | 
|  | 113 | return -EINVAL; | 
|  | 114 |  | 
|  | 115 | err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy, NULL); | 
|  | 116 | if (err < 0) | 
|  | 117 | return err; | 
|  | 118 |  | 
|  | 119 | if (tb[TCA_SKBEDIT_PARMS] == NULL) | 
|  | 120 | return -EINVAL; | 
|  | 121 |  | 
|  | 122 | if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { | 
|  | 123 | flags |= SKBEDIT_F_PRIORITY; | 
|  | 124 | priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); | 
|  | 125 | } | 
|  | 126 |  | 
|  | 127 | if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { | 
|  | 128 | flags |= SKBEDIT_F_QUEUE_MAPPING; | 
|  | 129 | queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); | 
|  | 130 | } | 
|  | 131 |  | 
|  | 132 | if (tb[TCA_SKBEDIT_PTYPE] != NULL) { | 
|  | 133 | ptype = nla_data(tb[TCA_SKBEDIT_PTYPE]); | 
|  | 134 | if (!skb_pkt_type_ok(*ptype)) | 
|  | 135 | return -EINVAL; | 
|  | 136 | flags |= SKBEDIT_F_PTYPE; | 
|  | 137 | } | 
|  | 138 |  | 
|  | 139 | if (tb[TCA_SKBEDIT_MARK] != NULL) { | 
|  | 140 | flags |= SKBEDIT_F_MARK; | 
|  | 141 | mark = nla_data(tb[TCA_SKBEDIT_MARK]); | 
|  | 142 | } | 
|  | 143 |  | 
|  | 144 | if (tb[TCA_SKBEDIT_MASK] != NULL) { | 
|  | 145 | flags |= SKBEDIT_F_MASK; | 
|  | 146 | mask = nla_data(tb[TCA_SKBEDIT_MASK]); | 
|  | 147 | } | 
|  | 148 |  | 
|  | 149 | if (tb[TCA_SKBEDIT_FLAGS] != NULL) { | 
|  | 150 | u64 *pure_flags = nla_data(tb[TCA_SKBEDIT_FLAGS]); | 
|  | 151 |  | 
|  | 152 | if (*pure_flags & SKBEDIT_F_INHERITDSFIELD) | 
|  | 153 | flags |= SKBEDIT_F_INHERITDSFIELD; | 
|  | 154 | } | 
|  | 155 |  | 
|  | 156 | parm = nla_data(tb[TCA_SKBEDIT_PARMS]); | 
|  | 157 | index = parm->index; | 
|  | 158 | err = tcf_idr_check_alloc(tn, &index, a, bind); | 
|  | 159 | if (err < 0) | 
|  | 160 | return err; | 
|  | 161 | exists = err; | 
|  | 162 | if (exists && bind) | 
|  | 163 | return 0; | 
|  | 164 |  | 
|  | 165 | if (!flags) { | 
|  | 166 | if (exists) | 
|  | 167 | tcf_idr_release(*a, bind); | 
|  | 168 | else | 
|  | 169 | tcf_idr_cleanup(tn, index); | 
|  | 170 | return -EINVAL; | 
|  | 171 | } | 
|  | 172 |  | 
|  | 173 | if (!exists) { | 
|  | 174 | ret = tcf_idr_create(tn, index, est, a, | 
|  | 175 | &act_skbedit_ops, bind, true); | 
|  | 176 | if (ret) { | 
|  | 177 | tcf_idr_cleanup(tn, index); | 
|  | 178 | return ret; | 
|  | 179 | } | 
|  | 180 |  | 
|  | 181 | d = to_skbedit(*a); | 
|  | 182 | ret = ACT_P_CREATED; | 
|  | 183 | } else { | 
|  | 184 | d = to_skbedit(*a); | 
|  | 185 | if (!ovr) { | 
|  | 186 | tcf_idr_release(*a, bind); | 
|  | 187 | return -EEXIST; | 
|  | 188 | } | 
|  | 189 | } | 
|  | 190 |  | 
|  | 191 | ASSERT_RTNL(); | 
|  | 192 |  | 
|  | 193 | params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); | 
|  | 194 | if (unlikely(!params_new)) { | 
|  | 195 | tcf_idr_release(*a, bind); | 
|  | 196 | return -ENOMEM; | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | params_new->flags = flags; | 
|  | 200 | if (flags & SKBEDIT_F_PRIORITY) | 
|  | 201 | params_new->priority = *priority; | 
|  | 202 | if (flags & SKBEDIT_F_QUEUE_MAPPING) | 
|  | 203 | params_new->queue_mapping = *queue_mapping; | 
|  | 204 | if (flags & SKBEDIT_F_MARK) | 
|  | 205 | params_new->mark = *mark; | 
|  | 206 | if (flags & SKBEDIT_F_PTYPE) | 
|  | 207 | params_new->ptype = *ptype; | 
|  | 208 | /* default behaviour is to use all the bits */ | 
|  | 209 | params_new->mask = 0xffffffff; | 
|  | 210 | if (flags & SKBEDIT_F_MASK) | 
|  | 211 | params_new->mask = *mask; | 
|  | 212 |  | 
|  | 213 | d->tcf_action = parm->action; | 
|  | 214 | params_old = rtnl_dereference(d->params); | 
|  | 215 | rcu_assign_pointer(d->params, params_new); | 
|  | 216 | if (params_old) | 
|  | 217 | kfree_rcu(params_old, rcu); | 
|  | 218 |  | 
|  | 219 | if (ret == ACT_P_CREATED) | 
|  | 220 | tcf_idr_insert(tn, *a); | 
|  | 221 | return ret; | 
|  | 222 | } | 
|  | 223 |  | 
|  | 224 | static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, | 
|  | 225 | int bind, int ref) | 
|  | 226 | { | 
|  | 227 | unsigned char *b = skb_tail_pointer(skb); | 
|  | 228 | struct tcf_skbedit *d = to_skbedit(a); | 
|  | 229 | struct tcf_skbedit_params *params; | 
|  | 230 | struct tc_skbedit opt = { | 
|  | 231 | .index   = d->tcf_index, | 
|  | 232 | .refcnt  = refcount_read(&d->tcf_refcnt) - ref, | 
|  | 233 | .bindcnt = atomic_read(&d->tcf_bindcnt) - bind, | 
|  | 234 | .action  = d->tcf_action, | 
|  | 235 | }; | 
|  | 236 | u64 pure_flags = 0; | 
|  | 237 | struct tcf_t t; | 
|  | 238 |  | 
|  | 239 | params = rtnl_dereference(d->params); | 
|  | 240 |  | 
|  | 241 | if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt)) | 
|  | 242 | goto nla_put_failure; | 
|  | 243 | if ((params->flags & SKBEDIT_F_PRIORITY) && | 
|  | 244 | nla_put_u32(skb, TCA_SKBEDIT_PRIORITY, params->priority)) | 
|  | 245 | goto nla_put_failure; | 
|  | 246 | if ((params->flags & SKBEDIT_F_QUEUE_MAPPING) && | 
|  | 247 | nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING, params->queue_mapping)) | 
|  | 248 | goto nla_put_failure; | 
|  | 249 | if ((params->flags & SKBEDIT_F_MARK) && | 
|  | 250 | nla_put_u32(skb, TCA_SKBEDIT_MARK, params->mark)) | 
|  | 251 | goto nla_put_failure; | 
|  | 252 | if ((params->flags & SKBEDIT_F_PTYPE) && | 
|  | 253 | nla_put_u16(skb, TCA_SKBEDIT_PTYPE, params->ptype)) | 
|  | 254 | goto nla_put_failure; | 
|  | 255 | if ((params->flags & SKBEDIT_F_MASK) && | 
|  | 256 | nla_put_u32(skb, TCA_SKBEDIT_MASK, params->mask)) | 
|  | 257 | goto nla_put_failure; | 
|  | 258 | if (params->flags & SKBEDIT_F_INHERITDSFIELD) | 
|  | 259 | pure_flags |= SKBEDIT_F_INHERITDSFIELD; | 
|  | 260 | if (pure_flags != 0 && | 
|  | 261 | nla_put(skb, TCA_SKBEDIT_FLAGS, sizeof(pure_flags), &pure_flags)) | 
|  | 262 | goto nla_put_failure; | 
|  | 263 |  | 
|  | 264 | tcf_tm_dump(&t, &d->tcf_tm); | 
|  | 265 | if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD)) | 
|  | 266 | goto nla_put_failure; | 
|  | 267 | return skb->len; | 
|  | 268 |  | 
|  | 269 | nla_put_failure: | 
|  | 270 | nlmsg_trim(skb, b); | 
|  | 271 | return -1; | 
|  | 272 | } | 
|  | 273 |  | 
|  | 274 | static void tcf_skbedit_cleanup(struct tc_action *a) | 
|  | 275 | { | 
|  | 276 | struct tcf_skbedit *d = to_skbedit(a); | 
|  | 277 | struct tcf_skbedit_params *params; | 
|  | 278 |  | 
|  | 279 | params = rcu_dereference_protected(d->params, 1); | 
|  | 280 | if (params) | 
|  | 281 | kfree_rcu(params, rcu); | 
|  | 282 | } | 
|  | 283 |  | 
|  | 284 | static int tcf_skbedit_walker(struct net *net, struct sk_buff *skb, | 
|  | 285 | struct netlink_callback *cb, int type, | 
|  | 286 | const struct tc_action_ops *ops, | 
|  | 287 | struct netlink_ext_ack *extack) | 
|  | 288 | { | 
|  | 289 | struct tc_action_net *tn = net_generic(net, skbedit_net_id); | 
|  | 290 |  | 
|  | 291 | return tcf_generic_walker(tn, skb, cb, type, ops, extack); | 
|  | 292 | } | 
|  | 293 |  | 
|  | 294 | static int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index, | 
|  | 295 | struct netlink_ext_ack *extack) | 
|  | 296 | { | 
|  | 297 | struct tc_action_net *tn = net_generic(net, skbedit_net_id); | 
|  | 298 |  | 
|  | 299 | return tcf_idr_search(tn, a, index); | 
|  | 300 | } | 
|  | 301 |  | 
|  | 302 | static struct tc_action_ops act_skbedit_ops = { | 
|  | 303 | .kind		=	"skbedit", | 
|  | 304 | .type		=	TCA_ACT_SKBEDIT, | 
|  | 305 | .owner		=	THIS_MODULE, | 
|  | 306 | .act		=	tcf_skbedit_act, | 
|  | 307 | .dump		=	tcf_skbedit_dump, | 
|  | 308 | .init		=	tcf_skbedit_init, | 
|  | 309 | .cleanup	=	tcf_skbedit_cleanup, | 
|  | 310 | .walk		=	tcf_skbedit_walker, | 
|  | 311 | .lookup		=	tcf_skbedit_search, | 
|  | 312 | .size		=	sizeof(struct tcf_skbedit), | 
|  | 313 | }; | 
|  | 314 |  | 
|  | 315 | static __net_init int skbedit_init_net(struct net *net) | 
|  | 316 | { | 
|  | 317 | struct tc_action_net *tn = net_generic(net, skbedit_net_id); | 
|  | 318 |  | 
|  | 319 | return tc_action_net_init(net, tn, &act_skbedit_ops); | 
|  | 320 | } | 
|  | 321 |  | 
|  | 322 | static void __net_exit skbedit_exit_net(struct list_head *net_list) | 
|  | 323 | { | 
|  | 324 | tc_action_net_exit(net_list, skbedit_net_id); | 
|  | 325 | } | 
|  | 326 |  | 
|  | 327 | static struct pernet_operations skbedit_net_ops = { | 
|  | 328 | .init = skbedit_init_net, | 
|  | 329 | .exit_batch = skbedit_exit_net, | 
|  | 330 | .id   = &skbedit_net_id, | 
|  | 331 | .size = sizeof(struct tc_action_net), | 
|  | 332 | }; | 
|  | 333 |  | 
|  | 334 | MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); | 
|  | 335 | MODULE_DESCRIPTION("SKB Editing"); | 
|  | 336 | MODULE_LICENSE("GPL"); | 
|  | 337 |  | 
|  | 338 | static int __init skbedit_init_module(void) | 
|  | 339 | { | 
|  | 340 | return tcf_register_action(&act_skbedit_ops, &skbedit_net_ops); | 
|  | 341 | } | 
|  | 342 |  | 
|  | 343 | static void __exit skbedit_cleanup_module(void) | 
|  | 344 | { | 
|  | 345 | tcf_unregister_action(&act_skbedit_ops, &skbedit_net_ops); | 
|  | 346 | } | 
|  | 347 |  | 
|  | 348 | module_init(skbedit_init_module); | 
|  | 349 | module_exit(skbedit_cleanup_module); |