Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
act_skbedit.c
Go to the documentation of this file.
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, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Author: Alexander Duyck <[email protected]>
18  */
19 
20 #include <linux/module.h>
21 #include <linux/init.h>
22 #include <linux/kernel.h>
23 #include <linux/skbuff.h>
24 #include <linux/rtnetlink.h>
25 #include <net/netlink.h>
26 #include <net/pkt_sched.h>
27 
29 #include <net/tc_act/tc_skbedit.h>
30 
31 #define SKBEDIT_TAB_MASK 15
32 static struct tcf_common *tcf_skbedit_ht[SKBEDIT_TAB_MASK + 1];
33 static u32 skbedit_idx_gen;
34 static DEFINE_RWLOCK(skbedit_lock);
35 
36 static struct tcf_hashinfo skbedit_hash_info = {
37  .htab = tcf_skbedit_ht,
38  .hmask = SKBEDIT_TAB_MASK,
39  .lock = &skbedit_lock,
40 };
41 
42 static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a,
43  struct tcf_result *res)
44 {
45  struct tcf_skbedit *d = a->priv;
46 
47  spin_lock(&d->tcf_lock);
48  d->tcf_tm.lastuse = jiffies;
49  bstats_update(&d->tcf_bstats, skb);
50 
51  if (d->flags & SKBEDIT_F_PRIORITY)
52  skb->priority = d->priority;
53  if (d->flags & SKBEDIT_F_QUEUE_MAPPING &&
54  skb->dev->real_num_tx_queues > d->queue_mapping)
55  skb_set_queue_mapping(skb, d->queue_mapping);
56  if (d->flags & SKBEDIT_F_MARK)
57  skb->mark = d->mark;
58 
59  spin_unlock(&d->tcf_lock);
60  return d->tcf_action;
61 }
62 
63 static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = {
64  [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) },
65  [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) },
66  [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) },
67  [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) },
68 };
69 
70 static int tcf_skbedit_init(struct nlattr *nla, struct nlattr *est,
71  struct tc_action *a, int ovr, int bind)
72 {
73  struct nlattr *tb[TCA_SKBEDIT_MAX + 1];
74  struct tc_skbedit *parm;
75  struct tcf_skbedit *d;
76  struct tcf_common *pc;
77  u32 flags = 0, *priority = NULL, *mark = NULL;
78  u16 *queue_mapping = NULL;
79  int ret = 0, err;
80 
81  if (nla == NULL)
82  return -EINVAL;
83 
84  err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy);
85  if (err < 0)
86  return err;
87 
88  if (tb[TCA_SKBEDIT_PARMS] == NULL)
89  return -EINVAL;
90 
91  if (tb[TCA_SKBEDIT_PRIORITY] != NULL) {
92  flags |= SKBEDIT_F_PRIORITY;
93  priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]);
94  }
95 
96  if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
97  flags |= SKBEDIT_F_QUEUE_MAPPING;
98  queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]);
99  }
100 
101  if (tb[TCA_SKBEDIT_MARK] != NULL) {
102  flags |= SKBEDIT_F_MARK;
103  mark = nla_data(tb[TCA_SKBEDIT_MARK]);
104  }
105 
106  if (!flags)
107  return -EINVAL;
108 
109  parm = nla_data(tb[TCA_SKBEDIT_PARMS]);
110 
111  pc = tcf_hash_check(parm->index, a, bind, &skbedit_hash_info);
112  if (!pc) {
113  pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind,
114  &skbedit_idx_gen, &skbedit_hash_info);
115  if (IS_ERR(pc))
116  return PTR_ERR(pc);
117 
118  d = to_skbedit(pc);
119  ret = ACT_P_CREATED;
120  } else {
121  d = to_skbedit(pc);
122  if (!ovr) {
123  tcf_hash_release(pc, bind, &skbedit_hash_info);
124  return -EEXIST;
125  }
126  }
127 
128  spin_lock_bh(&d->tcf_lock);
129 
130  d->flags = flags;
131  if (flags & SKBEDIT_F_PRIORITY)
132  d->priority = *priority;
133  if (flags & SKBEDIT_F_QUEUE_MAPPING)
134  d->queue_mapping = *queue_mapping;
135  if (flags & SKBEDIT_F_MARK)
136  d->mark = *mark;
137 
138  d->tcf_action = parm->action;
139 
140  spin_unlock_bh(&d->tcf_lock);
141 
142  if (ret == ACT_P_CREATED)
143  tcf_hash_insert(pc, &skbedit_hash_info);
144  return ret;
145 }
146 
147 static int tcf_skbedit_cleanup(struct tc_action *a, int bind)
148 {
149  struct tcf_skbedit *d = a->priv;
150 
151  if (d)
152  return tcf_hash_release(&d->common, bind, &skbedit_hash_info);
153  return 0;
154 }
155 
156 static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a,
157  int bind, int ref)
158 {
159  unsigned char *b = skb_tail_pointer(skb);
160  struct tcf_skbedit *d = a->priv;
161  struct tc_skbedit opt = {
162  .index = d->tcf_index,
163  .refcnt = d->tcf_refcnt - ref,
164  .bindcnt = d->tcf_bindcnt - bind,
165  .action = d->tcf_action,
166  };
167  struct tcf_t t;
168 
169  if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt))
170  goto nla_put_failure;
171  if ((d->flags & SKBEDIT_F_PRIORITY) &&
172  nla_put(skb, TCA_SKBEDIT_PRIORITY, sizeof(d->priority),
173  &d->priority))
174  goto nla_put_failure;
175  if ((d->flags & SKBEDIT_F_QUEUE_MAPPING) &&
177  sizeof(d->queue_mapping), &d->queue_mapping))
178  goto nla_put_failure;
179  if ((d->flags & SKBEDIT_F_MARK) &&
180  nla_put(skb, TCA_SKBEDIT_MARK, sizeof(d->mark),
181  &d->mark))
182  goto nla_put_failure;
183  t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install);
184  t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse);
185  t.expires = jiffies_to_clock_t(d->tcf_tm.expires);
186  if (nla_put(skb, TCA_SKBEDIT_TM, sizeof(t), &t))
187  goto nla_put_failure;
188  return skb->len;
189 
190 nla_put_failure:
191  nlmsg_trim(skb, b);
192  return -1;
193 }
194 
195 static struct tc_action_ops act_skbedit_ops = {
196  .kind = "skbedit",
197  .hinfo = &skbedit_hash_info,
198  .type = TCA_ACT_SKBEDIT,
199  .capab = TCA_CAP_NONE,
200  .owner = THIS_MODULE,
201  .act = tcf_skbedit,
202  .dump = tcf_skbedit_dump,
203  .cleanup = tcf_skbedit_cleanup,
204  .init = tcf_skbedit_init,
205  .walk = tcf_generic_walker,
206 };
207 
208 MODULE_AUTHOR("Alexander Duyck, <[email protected]>");
209 MODULE_DESCRIPTION("SKB Editing");
210 MODULE_LICENSE("GPL");
211 
212 static int __init skbedit_init_module(void)
213 {
214  return tcf_register_action(&act_skbedit_ops);
215 }
216 
217 static void __exit skbedit_cleanup_module(void)
218 {
219  tcf_unregister_action(&act_skbedit_ops);
220 }
221 
222 module_init(skbedit_init_module);
223 module_exit(skbedit_cleanup_module);