Blame view
net/sched/act_pedit.c
6.12 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 |
/* * net/sched/pedit.c Generic packet editor * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Authors: Jamal Hadi Salim (2002-4) */ |
1da177e4c Linux-2.6.12-rc2 |
11 12 |
#include <linux/types.h> #include <linux/kernel.h> |
1da177e4c Linux-2.6.12-rc2 |
13 |
#include <linux/string.h> |
1da177e4c Linux-2.6.12-rc2 |
14 |
#include <linux/errno.h> |
1da177e4c Linux-2.6.12-rc2 |
15 16 17 18 |
#include <linux/skbuff.h> #include <linux/rtnetlink.h> #include <linux/module.h> #include <linux/init.h> |
5a0e3ad6a include cleanup: ... |
19 |
#include <linux/slab.h> |
dc5fc579b [NETLINK]: Use nl... |
20 |
#include <net/netlink.h> |
1da177e4c Linux-2.6.12-rc2 |
21 22 23 |
#include <net/pkt_sched.h> #include <linux/tc_act/tc_pedit.h> #include <net/tc_act/tc_pedit.h> |
e9ce1cd3c [PKT_SCHED]: Kill... |
24 25 26 |
#define PEDIT_TAB_MASK 15 static struct tcf_common *tcf_pedit_ht[PEDIT_TAB_MASK + 1]; static u32 pedit_idx_gen; |
1da177e4c Linux-2.6.12-rc2 |
27 |
static DEFINE_RWLOCK(pedit_lock); |
e9ce1cd3c [PKT_SCHED]: Kill... |
28 29 30 31 32 |
static struct tcf_hashinfo pedit_hash_info = { .htab = tcf_pedit_ht, .hmask = PEDIT_TAB_MASK, .lock = &pedit_lock, }; |
1da177e4c Linux-2.6.12-rc2 |
33 |
|
53b2bf3f8 [NET_SCHED]: Use ... |
34 |
static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = { |
53f7e35f8 pkt_sched: pedit ... |
35 |
[TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) }, |
53b2bf3f8 [NET_SCHED]: Use ... |
36 |
}; |
7ba699c60 [NET_SCHED]: Conv... |
37 |
static int tcf_pedit_init(struct nlattr *nla, struct nlattr *est, |
e9ce1cd3c [PKT_SCHED]: Kill... |
38 |
struct tc_action *a, int ovr, int bind) |
1da177e4c Linux-2.6.12-rc2 |
39 |
{ |
7ba699c60 [NET_SCHED]: Conv... |
40 |
struct nlattr *tb[TCA_PEDIT_MAX + 1]; |
1da177e4c Linux-2.6.12-rc2 |
41 |
struct tc_pedit *parm; |
cee63723b [NET_SCHED]: Prop... |
42 |
int ret = 0, err; |
1da177e4c Linux-2.6.12-rc2 |
43 |
struct tcf_pedit *p; |
e9ce1cd3c [PKT_SCHED]: Kill... |
44 |
struct tcf_common *pc; |
1da177e4c Linux-2.6.12-rc2 |
45 46 |
struct tc_pedit_key *keys = NULL; int ksize; |
cee63723b [NET_SCHED]: Prop... |
47 |
if (nla == NULL) |
1da177e4c Linux-2.6.12-rc2 |
48 |
return -EINVAL; |
53b2bf3f8 [NET_SCHED]: Use ... |
49 |
err = nla_parse_nested(tb, TCA_PEDIT_MAX, nla, pedit_policy); |
cee63723b [NET_SCHED]: Prop... |
50 51 |
if (err < 0) return err; |
53b2bf3f8 [NET_SCHED]: Use ... |
52 |
if (tb[TCA_PEDIT_PARMS] == NULL) |
1da177e4c Linux-2.6.12-rc2 |
53 |
return -EINVAL; |
7ba699c60 [NET_SCHED]: Conv... |
54 |
parm = nla_data(tb[TCA_PEDIT_PARMS]); |
1da177e4c Linux-2.6.12-rc2 |
55 |
ksize = parm->nkeys * sizeof(struct tc_pedit_key); |
7ba699c60 [NET_SCHED]: Conv... |
56 |
if (nla_len(tb[TCA_PEDIT_PARMS]) < sizeof(*parm) + ksize) |
1da177e4c Linux-2.6.12-rc2 |
57 |
return -EINVAL; |
e9ce1cd3c [PKT_SCHED]: Kill... |
58 59 |
pc = tcf_hash_check(parm->index, a, bind, &pedit_hash_info); if (!pc) { |
1da177e4c Linux-2.6.12-rc2 |
60 61 |
if (!parm->nkeys) return -EINVAL; |
e9ce1cd3c [PKT_SCHED]: Kill... |
62 63 |
pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, &pedit_idx_gen, &pedit_hash_info); |
0e991ec6a tc: propogate err... |
64 |
if (IS_ERR(pc)) |
cc7ec456f net_sched: cleanups |
65 |
return PTR_ERR(pc); |
e9ce1cd3c [PKT_SCHED]: Kill... |
66 |
p = to_pedit(pc); |
1da177e4c Linux-2.6.12-rc2 |
67 68 |
keys = kmalloc(ksize, GFP_KERNEL); if (keys == NULL) { |
e9ce1cd3c [PKT_SCHED]: Kill... |
69 |
kfree(pc); |
1da177e4c Linux-2.6.12-rc2 |
70 71 72 73 |
return -ENOMEM; } ret = ACT_P_CREATED; } else { |
e9ce1cd3c [PKT_SCHED]: Kill... |
74 |
p = to_pedit(pc); |
1da177e4c Linux-2.6.12-rc2 |
75 |
if (!ovr) { |
e9ce1cd3c [PKT_SCHED]: Kill... |
76 |
tcf_hash_release(pc, bind, &pedit_hash_info); |
1da177e4c Linux-2.6.12-rc2 |
77 78 |
return -EEXIST; } |
e9ce1cd3c [PKT_SCHED]: Kill... |
79 |
if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) { |
1da177e4c Linux-2.6.12-rc2 |
80 81 82 83 84 |
keys = kmalloc(ksize, GFP_KERNEL); if (keys == NULL) return -ENOMEM; } } |
e9ce1cd3c [PKT_SCHED]: Kill... |
85 86 87 |
spin_lock_bh(&p->tcf_lock); p->tcfp_flags = parm->flags; p->tcf_action = parm->action; |
1da177e4c Linux-2.6.12-rc2 |
88 |
if (keys) { |
e9ce1cd3c [PKT_SCHED]: Kill... |
89 90 91 |
kfree(p->tcfp_keys); p->tcfp_keys = keys; p->tcfp_nkeys = parm->nkeys; |
1da177e4c Linux-2.6.12-rc2 |
92 |
} |
e9ce1cd3c [PKT_SCHED]: Kill... |
93 94 |
memcpy(p->tcfp_keys, parm->keys, ksize); spin_unlock_bh(&p->tcf_lock); |
1da177e4c Linux-2.6.12-rc2 |
95 |
if (ret == ACT_P_CREATED) |
e9ce1cd3c [PKT_SCHED]: Kill... |
96 |
tcf_hash_insert(pc, &pedit_hash_info); |
1da177e4c Linux-2.6.12-rc2 |
97 98 |
return ret; } |
e9ce1cd3c [PKT_SCHED]: Kill... |
99 |
static int tcf_pedit_cleanup(struct tc_action *a, int bind) |
1da177e4c Linux-2.6.12-rc2 |
100 |
{ |
e9ce1cd3c [PKT_SCHED]: Kill... |
101 |
struct tcf_pedit *p = a->priv; |
1da177e4c Linux-2.6.12-rc2 |
102 |
|
e9ce1cd3c [PKT_SCHED]: Kill... |
103 104 105 |
if (p) { struct tc_pedit_key *keys = p->tcfp_keys; if (tcf_hash_release(&p->common, bind, &pedit_hash_info)) { |
1da177e4c Linux-2.6.12-rc2 |
106 107 108 109 110 111 |
kfree(keys); return 1; } } return 0; } |
dc7f9f6e8 net: sched: const... |
112 |
static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a, |
e9ce1cd3c [PKT_SCHED]: Kill... |
113 |
struct tcf_result *res) |
1da177e4c Linux-2.6.12-rc2 |
114 |
{ |
e9ce1cd3c [PKT_SCHED]: Kill... |
115 |
struct tcf_pedit *p = a->priv; |
1da177e4c Linux-2.6.12-rc2 |
116 |
int i, munged = 0; |
db2c24175 act_pedit: access... |
117 |
unsigned int off; |
1da177e4c Linux-2.6.12-rc2 |
118 |
|
cc7ec456f net_sched: cleanups |
119 120 121 |
if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) return p->tcf_action; |
1da177e4c Linux-2.6.12-rc2 |
122 |
|
db2c24175 act_pedit: access... |
123 |
off = skb_network_offset(skb); |
1da177e4c Linux-2.6.12-rc2 |
124 |
|
e9ce1cd3c [PKT_SCHED]: Kill... |
125 |
spin_lock(&p->tcf_lock); |
1da177e4c Linux-2.6.12-rc2 |
126 |
|
e9ce1cd3c [PKT_SCHED]: Kill... |
127 |
p->tcf_tm.lastuse = jiffies; |
1da177e4c Linux-2.6.12-rc2 |
128 |
|
e9ce1cd3c [PKT_SCHED]: Kill... |
129 130 |
if (p->tcfp_nkeys > 0) { struct tc_pedit_key *tkey = p->tcfp_keys; |
1da177e4c Linux-2.6.12-rc2 |
131 |
|
e9ce1cd3c [PKT_SCHED]: Kill... |
132 |
for (i = p->tcfp_nkeys; i > 0; i--, tkey++) { |
db2c24175 act_pedit: access... |
133 |
u32 *ptr, _data; |
1da177e4c Linux-2.6.12-rc2 |
134 135 136 |
int offset = tkey->off; if (tkey->offmask) { |
db2c24175 act_pedit: access... |
137 138 139 140 141 |
char *d, _d; d = skb_header_pointer(skb, off + tkey->at, 1, &_d); if (!d) |
1da177e4c Linux-2.6.12-rc2 |
142 |
goto bad; |
db2c24175 act_pedit: access... |
143 |
offset += (*d & tkey->offmask) >> tkey->shift; |
1da177e4c Linux-2.6.12-rc2 |
144 145 146 |
} if (offset % 4) { |
6ff9c3644 net sched: printk... |
147 148 149 |
pr_info("tc filter pedit" " offset must be on 32 bit boundaries "); |
1da177e4c Linux-2.6.12-rc2 |
150 151 |
goto bad; } |
75202e768 [NET]: Fix compar... |
152 |
if (offset > 0 && offset > skb->len) { |
6ff9c3644 net sched: printk... |
153 |
pr_info("tc filter pedit" |
25985edce Fix common misspe... |
154 155 |
" offset %d can't exceed pkt length %d ", |
1da177e4c Linux-2.6.12-rc2 |
156 157 158 |
offset, skb->len); goto bad; } |
db2c24175 act_pedit: access... |
159 160 161 |
ptr = skb_header_pointer(skb, off + offset, 4, &_data); if (!ptr) goto bad; |
1da177e4c Linux-2.6.12-rc2 |
162 163 |
/* just do it, baby */ *ptr = ((*ptr & tkey->mask) ^ tkey->val); |
db2c24175 act_pedit: access... |
164 165 |
if (ptr == &_data) skb_store_bits(skb, off + offset, ptr, 4); |
1da177e4c Linux-2.6.12-rc2 |
166 167 |
munged++; } |
10297b993 [NET] SCHED: Fix ... |
168 |
|
1da177e4c Linux-2.6.12-rc2 |
169 170 171 |
if (munged) skb->tc_verd = SET_TC_MUNGED(skb->tc_verd); goto done; |
6ff9c3644 net sched: printk... |
172 173 174 |
} else WARN(1, "pedit BUG: index %d ", p->tcf_index); |
1da177e4c Linux-2.6.12-rc2 |
175 176 |
bad: |
e9ce1cd3c [PKT_SCHED]: Kill... |
177 |
p->tcf_qstats.overlimits++; |
1da177e4c Linux-2.6.12-rc2 |
178 |
done: |
bfe0d0298 net_sched: factor... |
179 |
bstats_update(&p->tcf_bstats, skb); |
e9ce1cd3c [PKT_SCHED]: Kill... |
180 181 |
spin_unlock(&p->tcf_lock); return p->tcf_action; |
1da177e4c Linux-2.6.12-rc2 |
182 |
} |
e9ce1cd3c [PKT_SCHED]: Kill... |
183 184 |
static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) |
1da177e4c Linux-2.6.12-rc2 |
185 |
{ |
27a884dc3 [SK_BUFF]: Conver... |
186 |
unsigned char *b = skb_tail_pointer(skb); |
e9ce1cd3c [PKT_SCHED]: Kill... |
187 |
struct tcf_pedit *p = a->priv; |
1da177e4c Linux-2.6.12-rc2 |
188 |
struct tc_pedit *opt; |
1da177e4c Linux-2.6.12-rc2 |
189 |
struct tcf_t t; |
10297b993 [NET] SCHED: Fix ... |
190 |
int s; |
e9ce1cd3c [PKT_SCHED]: Kill... |
191 |
s = sizeof(*opt) + p->tcfp_nkeys * sizeof(struct tc_pedit_key); |
1da177e4c Linux-2.6.12-rc2 |
192 193 |
/* netlink spinlocks held above us - must use ATOMIC */ |
0da974f4f [NET]: Conversion... |
194 |
opt = kzalloc(s, GFP_ATOMIC); |
e9ce1cd3c [PKT_SCHED]: Kill... |
195 |
if (unlikely(!opt)) |
1da177e4c Linux-2.6.12-rc2 |
196 |
return -ENOBUFS; |
1da177e4c Linux-2.6.12-rc2 |
197 |
|
e9ce1cd3c [PKT_SCHED]: Kill... |
198 199 200 201 202 203 204 205 |
memcpy(opt->keys, p->tcfp_keys, p->tcfp_nkeys * sizeof(struct tc_pedit_key)); opt->index = p->tcf_index; opt->nkeys = p->tcfp_nkeys; opt->flags = p->tcfp_flags; opt->action = p->tcf_action; opt->refcnt = p->tcf_refcnt - ref; opt->bindcnt = p->tcf_bindcnt - bind; |
1da177e4c Linux-2.6.12-rc2 |
206 |
|
7ba699c60 [NET_SCHED]: Conv... |
207 |
NLA_PUT(skb, TCA_PEDIT_PARMS, s, opt); |
e9ce1cd3c [PKT_SCHED]: Kill... |
208 209 210 |
t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); t.expires = jiffies_to_clock_t(p->tcf_tm.expires); |
7ba699c60 [NET_SCHED]: Conv... |
211 |
NLA_PUT(skb, TCA_PEDIT_TM, sizeof(t), &t); |
541673c85 [PKT_SCHED]: Fix ... |
212 |
kfree(opt); |
1da177e4c Linux-2.6.12-rc2 |
213 |
return skb->len; |
7ba699c60 [NET_SCHED]: Conv... |
214 |
nla_put_failure: |
dc5fc579b [NETLINK]: Use nl... |
215 |
nlmsg_trim(skb, b); |
541673c85 [PKT_SCHED]: Fix ... |
216 |
kfree(opt); |
1da177e4c Linux-2.6.12-rc2 |
217 218 |
return -1; } |
e9ce1cd3c [PKT_SCHED]: Kill... |
219 |
static struct tc_action_ops act_pedit_ops = { |
1da177e4c Linux-2.6.12-rc2 |
220 |
.kind = "pedit", |
e9ce1cd3c [PKT_SCHED]: Kill... |
221 |
.hinfo = &pedit_hash_info, |
1da177e4c Linux-2.6.12-rc2 |
222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
.type = TCA_ACT_PEDIT, .capab = TCA_CAP_NONE, .owner = THIS_MODULE, .act = tcf_pedit, .dump = tcf_pedit_dump, .cleanup = tcf_pedit_cleanup, .lookup = tcf_hash_search, .init = tcf_pedit_init, .walk = tcf_generic_walker }; MODULE_AUTHOR("Jamal Hadi Salim(2002-4)"); MODULE_DESCRIPTION("Generic Packet Editor actions"); MODULE_LICENSE("GPL"); |
e9ce1cd3c [PKT_SCHED]: Kill... |
236 |
static int __init pedit_init_module(void) |
1da177e4c Linux-2.6.12-rc2 |
237 238 239 |
{ return tcf_register_action(&act_pedit_ops); } |
e9ce1cd3c [PKT_SCHED]: Kill... |
240 |
static void __exit pedit_cleanup_module(void) |
1da177e4c Linux-2.6.12-rc2 |
241 242 243 244 245 246 |
{ tcf_unregister_action(&act_pedit_ops); } module_init(pedit_init_module); module_exit(pedit_cleanup_module); |