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(struct sk_buff *skb, const struct tc_action *a, 37 struct tcf_result *res) 38 { 39 struct tcf_skbedit *d = to_skbedit(a); 40 41 spin_lock(&d->tcf_lock); 42 tcf_lastuse_update(&d->tcf_tm); 43 bstats_update(&d->tcf_bstats, skb); 44 45 if (d->flags & SKBEDIT_F_PRIORITY) 46 skb->priority = d->priority; 47 if (d->flags & SKBEDIT_F_INHERITDSFIELD) { 48 int wlen = skb_network_offset(skb); 49 50 switch (tc_skb_protocol(skb)) { 51 case htons(ETH_P_IP): 52 wlen += sizeof(struct iphdr); 53 if (!pskb_may_pull(skb, wlen)) 54 goto err; 55 skb->priority = ipv4_get_dsfield(ip_hdr(skb)) >> 2; 56 break; 57 58 case htons(ETH_P_IPV6): 59 wlen += sizeof(struct ipv6hdr); 60 if (!pskb_may_pull(skb, wlen)) 61 goto err; 62 skb->priority = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; 63 break; 64 } 65 } 66 if (d->flags & SKBEDIT_F_QUEUE_MAPPING && 67 skb->dev->real_num_tx_queues > d->queue_mapping) 68 skb_set_queue_mapping(skb, d->queue_mapping); 69 if (d->flags & SKBEDIT_F_MARK) { 70 skb->mark &= ~d->mask; 71 skb->mark |= d->mark & d->mask; 72 } 73 if (d->flags & SKBEDIT_F_PTYPE) 74 skb->pkt_type = d->ptype; 75 76 spin_unlock(&d->tcf_lock); 77 return d->tcf_action; 78 79 err: 80 d->tcf_qstats.drops++; 81 spin_unlock(&d->tcf_lock); 82 return TC_ACT_SHOT; 83 } 84 85 static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { 86 [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, 87 [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, 88 [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, 89 [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) }, 90 [TCA_SKBEDIT_PTYPE] = { .len = sizeof(u16) }, 91 [TCA_SKBEDIT_MASK] = { .len = sizeof(u32) }, 92 [TCA_SKBEDIT_FLAGS] = { .len = sizeof(u64) }, 93 }; 94 95 static int tcf_skbedit_init(struct net *net, struct nlattr *nla, 96 struct nlattr *est, struct tc_action **a, 97 int ovr, int bind, struct netlink_ext_ack *extack) 98 { 99 struct tc_action_net *tn = net_generic(net, skbedit_net_id); 100 struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; 101 struct tc_skbedit *parm; 102 struct tcf_skbedit *d; 103 u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL; 104 u16 *queue_mapping = NULL, *ptype = NULL; 105 bool exists = false; 106 int ret = 0, err; 107 108 if (nla == NULL) 109 return -EINVAL; 110 111 err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy, NULL); 112 if (err < 0) 113 return err; 114 115 if (tb[TCA_SKBEDIT_PARMS] == NULL) 116 return -EINVAL; 117 118 if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { 119 flags |= SKBEDIT_F_PRIORITY; 120 priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); 121 } 122 123 if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { 124 flags |= SKBEDIT_F_QUEUE_MAPPING; 125 queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); 126 } 127 128 if (tb[TCA_SKBEDIT_PTYPE] != NULL) { 129 ptype = nla_data(tb[TCA_SKBEDIT_PTYPE]); 130 if (!skb_pkt_type_ok(*ptype)) 131 return -EINVAL; 132 flags |= SKBEDIT_F_PTYPE; 133 } 134 135 if (tb[TCA_SKBEDIT_MARK] != NULL) { 136 flags |= SKBEDIT_F_MARK; 137 mark = nla_data(tb[TCA_SKBEDIT_MARK]); 138 } 139 140 if (tb[TCA_SKBEDIT_MASK] != NULL) { 141 flags |= SKBEDIT_F_MASK; 142 mask = nla_data(tb[TCA_SKBEDIT_MASK]); 143 } 144 145 if (tb[TCA_SKBEDIT_FLAGS] != NULL) { 146 u64 *pure_flags = nla_data(tb[TCA_SKBEDIT_FLAGS]); 147 148 if (*pure_flags & SKBEDIT_F_INHERITDSFIELD) 149 flags |= SKBEDIT_F_INHERITDSFIELD; 150 } 151 152 parm = nla_data(tb[TCA_SKBEDIT_PARMS]); 153 154 exists = tcf_idr_check(tn, parm->index, a, bind); 155 if (exists && bind) 156 return 0; 157 158 if (!flags) { 159 if (exists) 160 tcf_idr_release(*a, bind); 161 return -EINVAL; 162 } 163 164 if (!exists) { 165 ret = tcf_idr_create(tn, parm->index, est, a, 166 &act_skbedit_ops, bind, false); 167 if (ret) 168 return ret; 169 170 d = to_skbedit(*a); 171 ret = ACT_P_CREATED; 172 } else { 173 d = to_skbedit(*a); 174 tcf_idr_release(*a, bind); 175 if (!ovr) 176 return -EEXIST; 177 } 178 179 spin_lock_bh(&d->tcf_lock); 180 181 d->flags = flags; 182 if (flags & SKBEDIT_F_PRIORITY) 183 d->priority = *priority; 184 if (flags & SKBEDIT_F_QUEUE_MAPPING) 185 d->queue_mapping = *queue_mapping; 186 if (flags & SKBEDIT_F_MARK) 187 d->mark = *mark; 188 if (flags & SKBEDIT_F_PTYPE) 189 d->ptype = *ptype; 190 /* default behaviour is to use all the bits */ 191 d->mask = 0xffffffff; 192 if (flags & SKBEDIT_F_MASK) 193 d->mask = *mask; 194 195 d->tcf_action = parm->action; 196 197 spin_unlock_bh(&d->tcf_lock); 198 199 if (ret == ACT_P_CREATED) 200 tcf_idr_insert(tn, *a); 201 return ret; 202 } 203 204 static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, 205 int bind, int ref) 206 { 207 unsigned char *b = skb_tail_pointer(skb); 208 struct tcf_skbedit *d = to_skbedit(a); 209 struct tc_skbedit opt = { 210 .index = d->tcf_index, 211 .refcnt = d->tcf_refcnt - ref, 212 .bindcnt = d->tcf_bindcnt - bind, 213 .action = d->tcf_action, 214 }; 215 struct tcf_t t; 216 u64 pure_flags = 0; 217 218 if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt)) 219 goto nla_put_failure; 220 if ((d->flags & SKBEDIT_F_PRIORITY) && 221 nla_put_u32(skb, TCA_SKBEDIT_PRIORITY, d->priority)) 222 goto nla_put_failure; 223 if ((d->flags & SKBEDIT_F_QUEUE_MAPPING) && 224 nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING, d->queue_mapping)) 225 goto nla_put_failure; 226 if ((d->flags & SKBEDIT_F_MARK) && 227 nla_put_u32(skb, TCA_SKBEDIT_MARK, d->mark)) 228 goto nla_put_failure; 229 if ((d->flags & SKBEDIT_F_PTYPE) && 230 nla_put_u16(skb, TCA_SKBEDIT_PTYPE, d->ptype)) 231 goto nla_put_failure; 232 if ((d->flags & SKBEDIT_F_MASK) && 233 nla_put_u32(skb, TCA_SKBEDIT_MASK, d->mask)) 234 goto nla_put_failure; 235 if (d->flags & SKBEDIT_F_INHERITDSFIELD) 236 pure_flags |= SKBEDIT_F_INHERITDSFIELD; 237 if (pure_flags != 0 && 238 nla_put(skb, TCA_SKBEDIT_FLAGS, sizeof(pure_flags), &pure_flags)) 239 goto nla_put_failure; 240 241 tcf_tm_dump(&t, &d->tcf_tm); 242 if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD)) 243 goto nla_put_failure; 244 return skb->len; 245 246 nla_put_failure: 247 nlmsg_trim(skb, b); 248 return -1; 249 } 250 251 static int tcf_skbedit_walker(struct net *net, struct sk_buff *skb, 252 struct netlink_callback *cb, int type, 253 const struct tc_action_ops *ops, 254 struct netlink_ext_ack *extack) 255 { 256 struct tc_action_net *tn = net_generic(net, skbedit_net_id); 257 258 return tcf_generic_walker(tn, skb, cb, type, ops, extack); 259 } 260 261 static int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index, 262 struct netlink_ext_ack *extack) 263 { 264 struct tc_action_net *tn = net_generic(net, skbedit_net_id); 265 266 return tcf_idr_search(tn, a, index); 267 } 268 269 static struct tc_action_ops act_skbedit_ops = { 270 .kind = "skbedit", 271 .type = TCA_ACT_SKBEDIT, 272 .owner = THIS_MODULE, 273 .act = tcf_skbedit, 274 .dump = tcf_skbedit_dump, 275 .init = tcf_skbedit_init, 276 .walk = tcf_skbedit_walker, 277 .lookup = tcf_skbedit_search, 278 .size = sizeof(struct tcf_skbedit), 279 }; 280 281 static __net_init int skbedit_init_net(struct net *net) 282 { 283 struct tc_action_net *tn = net_generic(net, skbedit_net_id); 284 285 return tc_action_net_init(tn, &act_skbedit_ops); 286 } 287 288 static void __net_exit skbedit_exit_net(struct list_head *net_list) 289 { 290 tc_action_net_exit(net_list, skbedit_net_id); 291 } 292 293 static struct pernet_operations skbedit_net_ops = { 294 .init = skbedit_init_net, 295 .exit_batch = skbedit_exit_net, 296 .id = &skbedit_net_id, 297 .size = sizeof(struct tc_action_net), 298 }; 299 300 MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); 301 MODULE_DESCRIPTION("SKB Editing"); 302 MODULE_LICENSE("GPL"); 303 304 static int __init skbedit_init_module(void) 305 { 306 return tcf_register_action(&act_skbedit_ops, &skbedit_net_ops); 307 } 308 309 static void __exit skbedit_cleanup_module(void) 310 { 311 tcf_unregister_action(&act_skbedit_ops, &skbedit_net_ops); 312 } 313 314 module_init(skbedit_init_module); 315 module_exit(skbedit_cleanup_module); 316