Commit 651a6493ae5c055c78777bb7178c23b5565631da
Committed by
David S. Miller
1 parent
382ca8a1ad
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
net_sched: Use default action walker methods
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 8 changed files with 0 additions and 9 deletions Inline Diff
net/sched/act_csum.c
1 | /* | 1 | /* |
2 | * Checksum updating actions | 2 | * Checksum updating actions |
3 | * | 3 | * |
4 | * Copyright (c) 2010 Gregoire Baron <baronchon@n7mm.org> | 4 | * Copyright (c) 2010 Gregoire Baron <baronchon@n7mm.org> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the Free | 7 | * under the terms of the GNU General Public License as published by the Free |
8 | * Software Foundation; either version 2 of the License, or (at your option) | 8 | * Software Foundation; either version 2 of the License, or (at your option) |
9 | * any later version. | 9 | * any later version. |
10 | * | 10 | * |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/types.h> | 13 | #include <linux/types.h> |
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/spinlock.h> | 17 | #include <linux/spinlock.h> |
18 | 18 | ||
19 | #include <linux/netlink.h> | 19 | #include <linux/netlink.h> |
20 | #include <net/netlink.h> | 20 | #include <net/netlink.h> |
21 | #include <linux/rtnetlink.h> | 21 | #include <linux/rtnetlink.h> |
22 | 22 | ||
23 | #include <linux/skbuff.h> | 23 | #include <linux/skbuff.h> |
24 | 24 | ||
25 | #include <net/ip.h> | 25 | #include <net/ip.h> |
26 | #include <net/ipv6.h> | 26 | #include <net/ipv6.h> |
27 | #include <net/icmp.h> | 27 | #include <net/icmp.h> |
28 | #include <linux/icmpv6.h> | 28 | #include <linux/icmpv6.h> |
29 | #include <linux/igmp.h> | 29 | #include <linux/igmp.h> |
30 | #include <net/tcp.h> | 30 | #include <net/tcp.h> |
31 | #include <net/udp.h> | 31 | #include <net/udp.h> |
32 | #include <net/ip6_checksum.h> | 32 | #include <net/ip6_checksum.h> |
33 | 33 | ||
34 | #include <net/act_api.h> | 34 | #include <net/act_api.h> |
35 | 35 | ||
36 | #include <linux/tc_act/tc_csum.h> | 36 | #include <linux/tc_act/tc_csum.h> |
37 | #include <net/tc_act/tc_csum.h> | 37 | #include <net/tc_act/tc_csum.h> |
38 | 38 | ||
39 | #define CSUM_TAB_MASK 15 | 39 | #define CSUM_TAB_MASK 15 |
40 | static struct tcf_common *tcf_csum_ht[CSUM_TAB_MASK + 1]; | 40 | static struct tcf_common *tcf_csum_ht[CSUM_TAB_MASK + 1]; |
41 | static u32 csum_idx_gen; | 41 | static u32 csum_idx_gen; |
42 | static DEFINE_RWLOCK(csum_lock); | 42 | static DEFINE_RWLOCK(csum_lock); |
43 | 43 | ||
44 | static struct tcf_hashinfo csum_hash_info = { | 44 | static struct tcf_hashinfo csum_hash_info = { |
45 | .htab = tcf_csum_ht, | 45 | .htab = tcf_csum_ht, |
46 | .hmask = CSUM_TAB_MASK, | 46 | .hmask = CSUM_TAB_MASK, |
47 | .lock = &csum_lock, | 47 | .lock = &csum_lock, |
48 | }; | 48 | }; |
49 | 49 | ||
50 | static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { | 50 | static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { |
51 | [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), }, | 51 | [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), }, |
52 | }; | 52 | }; |
53 | 53 | ||
54 | static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est, | 54 | static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est, |
55 | struct tc_action *a, int ovr, int bind) | 55 | struct tc_action *a, int ovr, int bind) |
56 | { | 56 | { |
57 | struct nlattr *tb[TCA_CSUM_MAX + 1]; | 57 | struct nlattr *tb[TCA_CSUM_MAX + 1]; |
58 | struct tc_csum *parm; | 58 | struct tc_csum *parm; |
59 | struct tcf_common *pc; | 59 | struct tcf_common *pc; |
60 | struct tcf_csum *p; | 60 | struct tcf_csum *p; |
61 | int ret = 0, err; | 61 | int ret = 0, err; |
62 | 62 | ||
63 | if (nla == NULL) | 63 | if (nla == NULL) |
64 | return -EINVAL; | 64 | return -EINVAL; |
65 | 65 | ||
66 | err = nla_parse_nested(tb, TCA_CSUM_MAX, nla, csum_policy); | 66 | err = nla_parse_nested(tb, TCA_CSUM_MAX, nla, csum_policy); |
67 | if (err < 0) | 67 | if (err < 0) |
68 | return err; | 68 | return err; |
69 | 69 | ||
70 | if (tb[TCA_CSUM_PARMS] == NULL) | 70 | if (tb[TCA_CSUM_PARMS] == NULL) |
71 | return -EINVAL; | 71 | return -EINVAL; |
72 | parm = nla_data(tb[TCA_CSUM_PARMS]); | 72 | parm = nla_data(tb[TCA_CSUM_PARMS]); |
73 | 73 | ||
74 | pc = tcf_hash_check(parm->index, a, bind, &csum_hash_info); | 74 | pc = tcf_hash_check(parm->index, a, bind, &csum_hash_info); |
75 | if (!pc) { | 75 | if (!pc) { |
76 | pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, | 76 | pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, |
77 | &csum_idx_gen, &csum_hash_info); | 77 | &csum_idx_gen, &csum_hash_info); |
78 | if (IS_ERR(pc)) | 78 | if (IS_ERR(pc)) |
79 | return PTR_ERR(pc); | 79 | return PTR_ERR(pc); |
80 | p = to_tcf_csum(pc); | 80 | p = to_tcf_csum(pc); |
81 | ret = ACT_P_CREATED; | 81 | ret = ACT_P_CREATED; |
82 | } else { | 82 | } else { |
83 | p = to_tcf_csum(pc); | 83 | p = to_tcf_csum(pc); |
84 | if (!ovr) { | 84 | if (!ovr) { |
85 | tcf_hash_release(pc, bind, &csum_hash_info); | 85 | tcf_hash_release(pc, bind, &csum_hash_info); |
86 | return -EEXIST; | 86 | return -EEXIST; |
87 | } | 87 | } |
88 | } | 88 | } |
89 | 89 | ||
90 | spin_lock_bh(&p->tcf_lock); | 90 | spin_lock_bh(&p->tcf_lock); |
91 | p->tcf_action = parm->action; | 91 | p->tcf_action = parm->action; |
92 | p->update_flags = parm->update_flags; | 92 | p->update_flags = parm->update_flags; |
93 | spin_unlock_bh(&p->tcf_lock); | 93 | spin_unlock_bh(&p->tcf_lock); |
94 | 94 | ||
95 | if (ret == ACT_P_CREATED) | 95 | if (ret == ACT_P_CREATED) |
96 | tcf_hash_insert(pc, &csum_hash_info); | 96 | tcf_hash_insert(pc, &csum_hash_info); |
97 | 97 | ||
98 | return ret; | 98 | return ret; |
99 | } | 99 | } |
100 | 100 | ||
101 | static int tcf_csum_cleanup(struct tc_action *a, int bind) | 101 | static int tcf_csum_cleanup(struct tc_action *a, int bind) |
102 | { | 102 | { |
103 | struct tcf_csum *p = a->priv; | 103 | struct tcf_csum *p = a->priv; |
104 | return tcf_hash_release(&p->common, bind, &csum_hash_info); | 104 | return tcf_hash_release(&p->common, bind, &csum_hash_info); |
105 | } | 105 | } |
106 | 106 | ||
107 | /** | 107 | /** |
108 | * tcf_csum_skb_nextlayer - Get next layer pointer | 108 | * tcf_csum_skb_nextlayer - Get next layer pointer |
109 | * @skb: sk_buff to use | 109 | * @skb: sk_buff to use |
110 | * @ihl: previous summed headers length | 110 | * @ihl: previous summed headers length |
111 | * @ipl: complete packet length | 111 | * @ipl: complete packet length |
112 | * @jhl: next header length | 112 | * @jhl: next header length |
113 | * | 113 | * |
114 | * Check the expected next layer availability in the specified sk_buff. | 114 | * Check the expected next layer availability in the specified sk_buff. |
115 | * Return the next layer pointer if pass, NULL otherwise. | 115 | * Return the next layer pointer if pass, NULL otherwise. |
116 | */ | 116 | */ |
117 | static void *tcf_csum_skb_nextlayer(struct sk_buff *skb, | 117 | static void *tcf_csum_skb_nextlayer(struct sk_buff *skb, |
118 | unsigned int ihl, unsigned int ipl, | 118 | unsigned int ihl, unsigned int ipl, |
119 | unsigned int jhl) | 119 | unsigned int jhl) |
120 | { | 120 | { |
121 | int ntkoff = skb_network_offset(skb); | 121 | int ntkoff = skb_network_offset(skb); |
122 | int hl = ihl + jhl; | 122 | int hl = ihl + jhl; |
123 | 123 | ||
124 | if (!pskb_may_pull(skb, ipl + ntkoff) || (ipl < hl) || | 124 | if (!pskb_may_pull(skb, ipl + ntkoff) || (ipl < hl) || |
125 | (skb_cloned(skb) && | 125 | (skb_cloned(skb) && |
126 | !skb_clone_writable(skb, hl + ntkoff) && | 126 | !skb_clone_writable(skb, hl + ntkoff) && |
127 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) | 127 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) |
128 | return NULL; | 128 | return NULL; |
129 | else | 129 | else |
130 | return (void *)(skb_network_header(skb) + ihl); | 130 | return (void *)(skb_network_header(skb) + ihl); |
131 | } | 131 | } |
132 | 132 | ||
133 | static int tcf_csum_ipv4_icmp(struct sk_buff *skb, | 133 | static int tcf_csum_ipv4_icmp(struct sk_buff *skb, |
134 | unsigned int ihl, unsigned int ipl) | 134 | unsigned int ihl, unsigned int ipl) |
135 | { | 135 | { |
136 | struct icmphdr *icmph; | 136 | struct icmphdr *icmph; |
137 | 137 | ||
138 | icmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmph)); | 138 | icmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmph)); |
139 | if (icmph == NULL) | 139 | if (icmph == NULL) |
140 | return 0; | 140 | return 0; |
141 | 141 | ||
142 | icmph->checksum = 0; | 142 | icmph->checksum = 0; |
143 | skb->csum = csum_partial(icmph, ipl - ihl, 0); | 143 | skb->csum = csum_partial(icmph, ipl - ihl, 0); |
144 | icmph->checksum = csum_fold(skb->csum); | 144 | icmph->checksum = csum_fold(skb->csum); |
145 | 145 | ||
146 | skb->ip_summed = CHECKSUM_NONE; | 146 | skb->ip_summed = CHECKSUM_NONE; |
147 | 147 | ||
148 | return 1; | 148 | return 1; |
149 | } | 149 | } |
150 | 150 | ||
151 | static int tcf_csum_ipv4_igmp(struct sk_buff *skb, | 151 | static int tcf_csum_ipv4_igmp(struct sk_buff *skb, |
152 | unsigned int ihl, unsigned int ipl) | 152 | unsigned int ihl, unsigned int ipl) |
153 | { | 153 | { |
154 | struct igmphdr *igmph; | 154 | struct igmphdr *igmph; |
155 | 155 | ||
156 | igmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*igmph)); | 156 | igmph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*igmph)); |
157 | if (igmph == NULL) | 157 | if (igmph == NULL) |
158 | return 0; | 158 | return 0; |
159 | 159 | ||
160 | igmph->csum = 0; | 160 | igmph->csum = 0; |
161 | skb->csum = csum_partial(igmph, ipl - ihl, 0); | 161 | skb->csum = csum_partial(igmph, ipl - ihl, 0); |
162 | igmph->csum = csum_fold(skb->csum); | 162 | igmph->csum = csum_fold(skb->csum); |
163 | 163 | ||
164 | skb->ip_summed = CHECKSUM_NONE; | 164 | skb->ip_summed = CHECKSUM_NONE; |
165 | 165 | ||
166 | return 1; | 166 | return 1; |
167 | } | 167 | } |
168 | 168 | ||
169 | static int tcf_csum_ipv6_icmp(struct sk_buff *skb, | 169 | static int tcf_csum_ipv6_icmp(struct sk_buff *skb, |
170 | unsigned int ihl, unsigned int ipl) | 170 | unsigned int ihl, unsigned int ipl) |
171 | { | 171 | { |
172 | struct icmp6hdr *icmp6h; | 172 | struct icmp6hdr *icmp6h; |
173 | const struct ipv6hdr *ip6h; | 173 | const struct ipv6hdr *ip6h; |
174 | 174 | ||
175 | icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h)); | 175 | icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h)); |
176 | if (icmp6h == NULL) | 176 | if (icmp6h == NULL) |
177 | return 0; | 177 | return 0; |
178 | 178 | ||
179 | ip6h = ipv6_hdr(skb); | 179 | ip6h = ipv6_hdr(skb); |
180 | icmp6h->icmp6_cksum = 0; | 180 | icmp6h->icmp6_cksum = 0; |
181 | skb->csum = csum_partial(icmp6h, ipl - ihl, 0); | 181 | skb->csum = csum_partial(icmp6h, ipl - ihl, 0); |
182 | icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, | 182 | icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, |
183 | ipl - ihl, IPPROTO_ICMPV6, | 183 | ipl - ihl, IPPROTO_ICMPV6, |
184 | skb->csum); | 184 | skb->csum); |
185 | 185 | ||
186 | skb->ip_summed = CHECKSUM_NONE; | 186 | skb->ip_summed = CHECKSUM_NONE; |
187 | 187 | ||
188 | return 1; | 188 | return 1; |
189 | } | 189 | } |
190 | 190 | ||
191 | static int tcf_csum_ipv4_tcp(struct sk_buff *skb, | 191 | static int tcf_csum_ipv4_tcp(struct sk_buff *skb, |
192 | unsigned int ihl, unsigned int ipl) | 192 | unsigned int ihl, unsigned int ipl) |
193 | { | 193 | { |
194 | struct tcphdr *tcph; | 194 | struct tcphdr *tcph; |
195 | const struct iphdr *iph; | 195 | const struct iphdr *iph; |
196 | 196 | ||
197 | tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); | 197 | tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); |
198 | if (tcph == NULL) | 198 | if (tcph == NULL) |
199 | return 0; | 199 | return 0; |
200 | 200 | ||
201 | iph = ip_hdr(skb); | 201 | iph = ip_hdr(skb); |
202 | tcph->check = 0; | 202 | tcph->check = 0; |
203 | skb->csum = csum_partial(tcph, ipl - ihl, 0); | 203 | skb->csum = csum_partial(tcph, ipl - ihl, 0); |
204 | tcph->check = tcp_v4_check(ipl - ihl, | 204 | tcph->check = tcp_v4_check(ipl - ihl, |
205 | iph->saddr, iph->daddr, skb->csum); | 205 | iph->saddr, iph->daddr, skb->csum); |
206 | 206 | ||
207 | skb->ip_summed = CHECKSUM_NONE; | 207 | skb->ip_summed = CHECKSUM_NONE; |
208 | 208 | ||
209 | return 1; | 209 | return 1; |
210 | } | 210 | } |
211 | 211 | ||
212 | static int tcf_csum_ipv6_tcp(struct sk_buff *skb, | 212 | static int tcf_csum_ipv6_tcp(struct sk_buff *skb, |
213 | unsigned int ihl, unsigned int ipl) | 213 | unsigned int ihl, unsigned int ipl) |
214 | { | 214 | { |
215 | struct tcphdr *tcph; | 215 | struct tcphdr *tcph; |
216 | const struct ipv6hdr *ip6h; | 216 | const struct ipv6hdr *ip6h; |
217 | 217 | ||
218 | tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); | 218 | tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); |
219 | if (tcph == NULL) | 219 | if (tcph == NULL) |
220 | return 0; | 220 | return 0; |
221 | 221 | ||
222 | ip6h = ipv6_hdr(skb); | 222 | ip6h = ipv6_hdr(skb); |
223 | tcph->check = 0; | 223 | tcph->check = 0; |
224 | skb->csum = csum_partial(tcph, ipl - ihl, 0); | 224 | skb->csum = csum_partial(tcph, ipl - ihl, 0); |
225 | tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, | 225 | tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, |
226 | ipl - ihl, IPPROTO_TCP, | 226 | ipl - ihl, IPPROTO_TCP, |
227 | skb->csum); | 227 | skb->csum); |
228 | 228 | ||
229 | skb->ip_summed = CHECKSUM_NONE; | 229 | skb->ip_summed = CHECKSUM_NONE; |
230 | 230 | ||
231 | return 1; | 231 | return 1; |
232 | } | 232 | } |
233 | 233 | ||
234 | static int tcf_csum_ipv4_udp(struct sk_buff *skb, | 234 | static int tcf_csum_ipv4_udp(struct sk_buff *skb, |
235 | unsigned int ihl, unsigned int ipl, int udplite) | 235 | unsigned int ihl, unsigned int ipl, int udplite) |
236 | { | 236 | { |
237 | struct udphdr *udph; | 237 | struct udphdr *udph; |
238 | const struct iphdr *iph; | 238 | const struct iphdr *iph; |
239 | u16 ul; | 239 | u16 ul; |
240 | 240 | ||
241 | /* | 241 | /* |
242 | * Support both UDP and UDPLITE checksum algorithms, Don't use | 242 | * Support both UDP and UDPLITE checksum algorithms, Don't use |
243 | * udph->len to get the real length without any protocol check, | 243 | * udph->len to get the real length without any protocol check, |
244 | * UDPLITE uses udph->len for another thing, | 244 | * UDPLITE uses udph->len for another thing, |
245 | * Use iph->tot_len, or just ipl. | 245 | * Use iph->tot_len, or just ipl. |
246 | */ | 246 | */ |
247 | 247 | ||
248 | udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph)); | 248 | udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph)); |
249 | if (udph == NULL) | 249 | if (udph == NULL) |
250 | return 0; | 250 | return 0; |
251 | 251 | ||
252 | iph = ip_hdr(skb); | 252 | iph = ip_hdr(skb); |
253 | ul = ntohs(udph->len); | 253 | ul = ntohs(udph->len); |
254 | 254 | ||
255 | if (udplite || udph->check) { | 255 | if (udplite || udph->check) { |
256 | 256 | ||
257 | udph->check = 0; | 257 | udph->check = 0; |
258 | 258 | ||
259 | if (udplite) { | 259 | if (udplite) { |
260 | if (ul == 0) | 260 | if (ul == 0) |
261 | skb->csum = csum_partial(udph, ipl - ihl, 0); | 261 | skb->csum = csum_partial(udph, ipl - ihl, 0); |
262 | else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl)) | 262 | else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl)) |
263 | skb->csum = csum_partial(udph, ul, 0); | 263 | skb->csum = csum_partial(udph, ul, 0); |
264 | else | 264 | else |
265 | goto ignore_obscure_skb; | 265 | goto ignore_obscure_skb; |
266 | } else { | 266 | } else { |
267 | if (ul != ipl - ihl) | 267 | if (ul != ipl - ihl) |
268 | goto ignore_obscure_skb; | 268 | goto ignore_obscure_skb; |
269 | 269 | ||
270 | skb->csum = csum_partial(udph, ul, 0); | 270 | skb->csum = csum_partial(udph, ul, 0); |
271 | } | 271 | } |
272 | 272 | ||
273 | udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, | 273 | udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, |
274 | ul, iph->protocol, | 274 | ul, iph->protocol, |
275 | skb->csum); | 275 | skb->csum); |
276 | 276 | ||
277 | if (!udph->check) | 277 | if (!udph->check) |
278 | udph->check = CSUM_MANGLED_0; | 278 | udph->check = CSUM_MANGLED_0; |
279 | } | 279 | } |
280 | 280 | ||
281 | skb->ip_summed = CHECKSUM_NONE; | 281 | skb->ip_summed = CHECKSUM_NONE; |
282 | 282 | ||
283 | ignore_obscure_skb: | 283 | ignore_obscure_skb: |
284 | return 1; | 284 | return 1; |
285 | } | 285 | } |
286 | 286 | ||
287 | static int tcf_csum_ipv6_udp(struct sk_buff *skb, | 287 | static int tcf_csum_ipv6_udp(struct sk_buff *skb, |
288 | unsigned int ihl, unsigned int ipl, int udplite) | 288 | unsigned int ihl, unsigned int ipl, int udplite) |
289 | { | 289 | { |
290 | struct udphdr *udph; | 290 | struct udphdr *udph; |
291 | const struct ipv6hdr *ip6h; | 291 | const struct ipv6hdr *ip6h; |
292 | u16 ul; | 292 | u16 ul; |
293 | 293 | ||
294 | /* | 294 | /* |
295 | * Support both UDP and UDPLITE checksum algorithms, Don't use | 295 | * Support both UDP and UDPLITE checksum algorithms, Don't use |
296 | * udph->len to get the real length without any protocol check, | 296 | * udph->len to get the real length without any protocol check, |
297 | * UDPLITE uses udph->len for another thing, | 297 | * UDPLITE uses udph->len for another thing, |
298 | * Use ip6h->payload_len + sizeof(*ip6h) ... , or just ipl. | 298 | * Use ip6h->payload_len + sizeof(*ip6h) ... , or just ipl. |
299 | */ | 299 | */ |
300 | 300 | ||
301 | udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph)); | 301 | udph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*udph)); |
302 | if (udph == NULL) | 302 | if (udph == NULL) |
303 | return 0; | 303 | return 0; |
304 | 304 | ||
305 | ip6h = ipv6_hdr(skb); | 305 | ip6h = ipv6_hdr(skb); |
306 | ul = ntohs(udph->len); | 306 | ul = ntohs(udph->len); |
307 | 307 | ||
308 | udph->check = 0; | 308 | udph->check = 0; |
309 | 309 | ||
310 | if (udplite) { | 310 | if (udplite) { |
311 | if (ul == 0) | 311 | if (ul == 0) |
312 | skb->csum = csum_partial(udph, ipl - ihl, 0); | 312 | skb->csum = csum_partial(udph, ipl - ihl, 0); |
313 | 313 | ||
314 | else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl)) | 314 | else if ((ul >= sizeof(*udph)) && (ul <= ipl - ihl)) |
315 | skb->csum = csum_partial(udph, ul, 0); | 315 | skb->csum = csum_partial(udph, ul, 0); |
316 | 316 | ||
317 | else | 317 | else |
318 | goto ignore_obscure_skb; | 318 | goto ignore_obscure_skb; |
319 | } else { | 319 | } else { |
320 | if (ul != ipl - ihl) | 320 | if (ul != ipl - ihl) |
321 | goto ignore_obscure_skb; | 321 | goto ignore_obscure_skb; |
322 | 322 | ||
323 | skb->csum = csum_partial(udph, ul, 0); | 323 | skb->csum = csum_partial(udph, ul, 0); |
324 | } | 324 | } |
325 | 325 | ||
326 | udph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, ul, | 326 | udph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, ul, |
327 | udplite ? IPPROTO_UDPLITE : IPPROTO_UDP, | 327 | udplite ? IPPROTO_UDPLITE : IPPROTO_UDP, |
328 | skb->csum); | 328 | skb->csum); |
329 | 329 | ||
330 | if (!udph->check) | 330 | if (!udph->check) |
331 | udph->check = CSUM_MANGLED_0; | 331 | udph->check = CSUM_MANGLED_0; |
332 | 332 | ||
333 | skb->ip_summed = CHECKSUM_NONE; | 333 | skb->ip_summed = CHECKSUM_NONE; |
334 | 334 | ||
335 | ignore_obscure_skb: | 335 | ignore_obscure_skb: |
336 | return 1; | 336 | return 1; |
337 | } | 337 | } |
338 | 338 | ||
339 | static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags) | 339 | static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags) |
340 | { | 340 | { |
341 | const struct iphdr *iph; | 341 | const struct iphdr *iph; |
342 | int ntkoff; | 342 | int ntkoff; |
343 | 343 | ||
344 | ntkoff = skb_network_offset(skb); | 344 | ntkoff = skb_network_offset(skb); |
345 | 345 | ||
346 | if (!pskb_may_pull(skb, sizeof(*iph) + ntkoff)) | 346 | if (!pskb_may_pull(skb, sizeof(*iph) + ntkoff)) |
347 | goto fail; | 347 | goto fail; |
348 | 348 | ||
349 | iph = ip_hdr(skb); | 349 | iph = ip_hdr(skb); |
350 | 350 | ||
351 | switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) { | 351 | switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) { |
352 | case IPPROTO_ICMP: | 352 | case IPPROTO_ICMP: |
353 | if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP) | 353 | if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP) |
354 | if (!tcf_csum_ipv4_icmp(skb, iph->ihl * 4, | 354 | if (!tcf_csum_ipv4_icmp(skb, iph->ihl * 4, |
355 | ntohs(iph->tot_len))) | 355 | ntohs(iph->tot_len))) |
356 | goto fail; | 356 | goto fail; |
357 | break; | 357 | break; |
358 | case IPPROTO_IGMP: | 358 | case IPPROTO_IGMP: |
359 | if (update_flags & TCA_CSUM_UPDATE_FLAG_IGMP) | 359 | if (update_flags & TCA_CSUM_UPDATE_FLAG_IGMP) |
360 | if (!tcf_csum_ipv4_igmp(skb, iph->ihl * 4, | 360 | if (!tcf_csum_ipv4_igmp(skb, iph->ihl * 4, |
361 | ntohs(iph->tot_len))) | 361 | ntohs(iph->tot_len))) |
362 | goto fail; | 362 | goto fail; |
363 | break; | 363 | break; |
364 | case IPPROTO_TCP: | 364 | case IPPROTO_TCP: |
365 | if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) | 365 | if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) |
366 | if (!tcf_csum_ipv4_tcp(skb, iph->ihl * 4, | 366 | if (!tcf_csum_ipv4_tcp(skb, iph->ihl * 4, |
367 | ntohs(iph->tot_len))) | 367 | ntohs(iph->tot_len))) |
368 | goto fail; | 368 | goto fail; |
369 | break; | 369 | break; |
370 | case IPPROTO_UDP: | 370 | case IPPROTO_UDP: |
371 | if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) | 371 | if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) |
372 | if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4, | 372 | if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4, |
373 | ntohs(iph->tot_len), 0)) | 373 | ntohs(iph->tot_len), 0)) |
374 | goto fail; | 374 | goto fail; |
375 | break; | 375 | break; |
376 | case IPPROTO_UDPLITE: | 376 | case IPPROTO_UDPLITE: |
377 | if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) | 377 | if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) |
378 | if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4, | 378 | if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4, |
379 | ntohs(iph->tot_len), 1)) | 379 | ntohs(iph->tot_len), 1)) |
380 | goto fail; | 380 | goto fail; |
381 | break; | 381 | break; |
382 | } | 382 | } |
383 | 383 | ||
384 | if (update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) { | 384 | if (update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) { |
385 | if (skb_cloned(skb) && | 385 | if (skb_cloned(skb) && |
386 | !skb_clone_writable(skb, sizeof(*iph) + ntkoff) && | 386 | !skb_clone_writable(skb, sizeof(*iph) + ntkoff) && |
387 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) | 387 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) |
388 | goto fail; | 388 | goto fail; |
389 | 389 | ||
390 | ip_send_check(ip_hdr(skb)); | 390 | ip_send_check(ip_hdr(skb)); |
391 | } | 391 | } |
392 | 392 | ||
393 | return 1; | 393 | return 1; |
394 | 394 | ||
395 | fail: | 395 | fail: |
396 | return 0; | 396 | return 0; |
397 | } | 397 | } |
398 | 398 | ||
399 | static int tcf_csum_ipv6_hopopts(struct ipv6_opt_hdr *ip6xh, | 399 | static int tcf_csum_ipv6_hopopts(struct ipv6_opt_hdr *ip6xh, |
400 | unsigned int ixhl, unsigned int *pl) | 400 | unsigned int ixhl, unsigned int *pl) |
401 | { | 401 | { |
402 | int off, len, optlen; | 402 | int off, len, optlen; |
403 | unsigned char *xh = (void *)ip6xh; | 403 | unsigned char *xh = (void *)ip6xh; |
404 | 404 | ||
405 | off = sizeof(*ip6xh); | 405 | off = sizeof(*ip6xh); |
406 | len = ixhl - off; | 406 | len = ixhl - off; |
407 | 407 | ||
408 | while (len > 1) { | 408 | while (len > 1) { |
409 | switch (xh[off]) { | 409 | switch (xh[off]) { |
410 | case IPV6_TLV_PAD1: | 410 | case IPV6_TLV_PAD1: |
411 | optlen = 1; | 411 | optlen = 1; |
412 | break; | 412 | break; |
413 | case IPV6_TLV_JUMBO: | 413 | case IPV6_TLV_JUMBO: |
414 | optlen = xh[off + 1] + 2; | 414 | optlen = xh[off + 1] + 2; |
415 | if (optlen != 6 || len < 6 || (off & 3) != 2) | 415 | if (optlen != 6 || len < 6 || (off & 3) != 2) |
416 | /* wrong jumbo option length/alignment */ | 416 | /* wrong jumbo option length/alignment */ |
417 | return 0; | 417 | return 0; |
418 | *pl = ntohl(*(__be32 *)(xh + off + 2)); | 418 | *pl = ntohl(*(__be32 *)(xh + off + 2)); |
419 | goto done; | 419 | goto done; |
420 | default: | 420 | default: |
421 | optlen = xh[off + 1] + 2; | 421 | optlen = xh[off + 1] + 2; |
422 | if (optlen > len) | 422 | if (optlen > len) |
423 | /* ignore obscure options */ | 423 | /* ignore obscure options */ |
424 | goto done; | 424 | goto done; |
425 | break; | 425 | break; |
426 | } | 426 | } |
427 | off += optlen; | 427 | off += optlen; |
428 | len -= optlen; | 428 | len -= optlen; |
429 | } | 429 | } |
430 | 430 | ||
431 | done: | 431 | done: |
432 | return 1; | 432 | return 1; |
433 | } | 433 | } |
434 | 434 | ||
435 | static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags) | 435 | static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags) |
436 | { | 436 | { |
437 | struct ipv6hdr *ip6h; | 437 | struct ipv6hdr *ip6h; |
438 | struct ipv6_opt_hdr *ip6xh; | 438 | struct ipv6_opt_hdr *ip6xh; |
439 | unsigned int hl, ixhl; | 439 | unsigned int hl, ixhl; |
440 | unsigned int pl; | 440 | unsigned int pl; |
441 | int ntkoff; | 441 | int ntkoff; |
442 | u8 nexthdr; | 442 | u8 nexthdr; |
443 | 443 | ||
444 | ntkoff = skb_network_offset(skb); | 444 | ntkoff = skb_network_offset(skb); |
445 | 445 | ||
446 | hl = sizeof(*ip6h); | 446 | hl = sizeof(*ip6h); |
447 | 447 | ||
448 | if (!pskb_may_pull(skb, hl + ntkoff)) | 448 | if (!pskb_may_pull(skb, hl + ntkoff)) |
449 | goto fail; | 449 | goto fail; |
450 | 450 | ||
451 | ip6h = ipv6_hdr(skb); | 451 | ip6h = ipv6_hdr(skb); |
452 | 452 | ||
453 | pl = ntohs(ip6h->payload_len); | 453 | pl = ntohs(ip6h->payload_len); |
454 | nexthdr = ip6h->nexthdr; | 454 | nexthdr = ip6h->nexthdr; |
455 | 455 | ||
456 | do { | 456 | do { |
457 | switch (nexthdr) { | 457 | switch (nexthdr) { |
458 | case NEXTHDR_FRAGMENT: | 458 | case NEXTHDR_FRAGMENT: |
459 | goto ignore_skb; | 459 | goto ignore_skb; |
460 | case NEXTHDR_ROUTING: | 460 | case NEXTHDR_ROUTING: |
461 | case NEXTHDR_HOP: | 461 | case NEXTHDR_HOP: |
462 | case NEXTHDR_DEST: | 462 | case NEXTHDR_DEST: |
463 | if (!pskb_may_pull(skb, hl + sizeof(*ip6xh) + ntkoff)) | 463 | if (!pskb_may_pull(skb, hl + sizeof(*ip6xh) + ntkoff)) |
464 | goto fail; | 464 | goto fail; |
465 | ip6xh = (void *)(skb_network_header(skb) + hl); | 465 | ip6xh = (void *)(skb_network_header(skb) + hl); |
466 | ixhl = ipv6_optlen(ip6xh); | 466 | ixhl = ipv6_optlen(ip6xh); |
467 | if (!pskb_may_pull(skb, hl + ixhl + ntkoff)) | 467 | if (!pskb_may_pull(skb, hl + ixhl + ntkoff)) |
468 | goto fail; | 468 | goto fail; |
469 | ip6xh = (void *)(skb_network_header(skb) + hl); | 469 | ip6xh = (void *)(skb_network_header(skb) + hl); |
470 | if ((nexthdr == NEXTHDR_HOP) && | 470 | if ((nexthdr == NEXTHDR_HOP) && |
471 | !(tcf_csum_ipv6_hopopts(ip6xh, ixhl, &pl))) | 471 | !(tcf_csum_ipv6_hopopts(ip6xh, ixhl, &pl))) |
472 | goto fail; | 472 | goto fail; |
473 | nexthdr = ip6xh->nexthdr; | 473 | nexthdr = ip6xh->nexthdr; |
474 | hl += ixhl; | 474 | hl += ixhl; |
475 | break; | 475 | break; |
476 | case IPPROTO_ICMPV6: | 476 | case IPPROTO_ICMPV6: |
477 | if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP) | 477 | if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP) |
478 | if (!tcf_csum_ipv6_icmp(skb, | 478 | if (!tcf_csum_ipv6_icmp(skb, |
479 | hl, pl + sizeof(*ip6h))) | 479 | hl, pl + sizeof(*ip6h))) |
480 | goto fail; | 480 | goto fail; |
481 | goto done; | 481 | goto done; |
482 | case IPPROTO_TCP: | 482 | case IPPROTO_TCP: |
483 | if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) | 483 | if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP) |
484 | if (!tcf_csum_ipv6_tcp(skb, | 484 | if (!tcf_csum_ipv6_tcp(skb, |
485 | hl, pl + sizeof(*ip6h))) | 485 | hl, pl + sizeof(*ip6h))) |
486 | goto fail; | 486 | goto fail; |
487 | goto done; | 487 | goto done; |
488 | case IPPROTO_UDP: | 488 | case IPPROTO_UDP: |
489 | if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) | 489 | if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP) |
490 | if (!tcf_csum_ipv6_udp(skb, hl, | 490 | if (!tcf_csum_ipv6_udp(skb, hl, |
491 | pl + sizeof(*ip6h), 0)) | 491 | pl + sizeof(*ip6h), 0)) |
492 | goto fail; | 492 | goto fail; |
493 | goto done; | 493 | goto done; |
494 | case IPPROTO_UDPLITE: | 494 | case IPPROTO_UDPLITE: |
495 | if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) | 495 | if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE) |
496 | if (!tcf_csum_ipv6_udp(skb, hl, | 496 | if (!tcf_csum_ipv6_udp(skb, hl, |
497 | pl + sizeof(*ip6h), 1)) | 497 | pl + sizeof(*ip6h), 1)) |
498 | goto fail; | 498 | goto fail; |
499 | goto done; | 499 | goto done; |
500 | default: | 500 | default: |
501 | goto ignore_skb; | 501 | goto ignore_skb; |
502 | } | 502 | } |
503 | } while (pskb_may_pull(skb, hl + 1 + ntkoff)); | 503 | } while (pskb_may_pull(skb, hl + 1 + ntkoff)); |
504 | 504 | ||
505 | done: | 505 | done: |
506 | ignore_skb: | 506 | ignore_skb: |
507 | return 1; | 507 | return 1; |
508 | 508 | ||
509 | fail: | 509 | fail: |
510 | return 0; | 510 | return 0; |
511 | } | 511 | } |
512 | 512 | ||
513 | static int tcf_csum(struct sk_buff *skb, | 513 | static int tcf_csum(struct sk_buff *skb, |
514 | const struct tc_action *a, struct tcf_result *res) | 514 | const struct tc_action *a, struct tcf_result *res) |
515 | { | 515 | { |
516 | struct tcf_csum *p = a->priv; | 516 | struct tcf_csum *p = a->priv; |
517 | int action; | 517 | int action; |
518 | u32 update_flags; | 518 | u32 update_flags; |
519 | 519 | ||
520 | spin_lock(&p->tcf_lock); | 520 | spin_lock(&p->tcf_lock); |
521 | p->tcf_tm.lastuse = jiffies; | 521 | p->tcf_tm.lastuse = jiffies; |
522 | bstats_update(&p->tcf_bstats, skb); | 522 | bstats_update(&p->tcf_bstats, skb); |
523 | action = p->tcf_action; | 523 | action = p->tcf_action; |
524 | update_flags = p->update_flags; | 524 | update_flags = p->update_flags; |
525 | spin_unlock(&p->tcf_lock); | 525 | spin_unlock(&p->tcf_lock); |
526 | 526 | ||
527 | if (unlikely(action == TC_ACT_SHOT)) | 527 | if (unlikely(action == TC_ACT_SHOT)) |
528 | goto drop; | 528 | goto drop; |
529 | 529 | ||
530 | switch (skb->protocol) { | 530 | switch (skb->protocol) { |
531 | case cpu_to_be16(ETH_P_IP): | 531 | case cpu_to_be16(ETH_P_IP): |
532 | if (!tcf_csum_ipv4(skb, update_flags)) | 532 | if (!tcf_csum_ipv4(skb, update_flags)) |
533 | goto drop; | 533 | goto drop; |
534 | break; | 534 | break; |
535 | case cpu_to_be16(ETH_P_IPV6): | 535 | case cpu_to_be16(ETH_P_IPV6): |
536 | if (!tcf_csum_ipv6(skb, update_flags)) | 536 | if (!tcf_csum_ipv6(skb, update_flags)) |
537 | goto drop; | 537 | goto drop; |
538 | break; | 538 | break; |
539 | } | 539 | } |
540 | 540 | ||
541 | return action; | 541 | return action; |
542 | 542 | ||
543 | drop: | 543 | drop: |
544 | spin_lock(&p->tcf_lock); | 544 | spin_lock(&p->tcf_lock); |
545 | p->tcf_qstats.drops++; | 545 | p->tcf_qstats.drops++; |
546 | spin_unlock(&p->tcf_lock); | 546 | spin_unlock(&p->tcf_lock); |
547 | return TC_ACT_SHOT; | 547 | return TC_ACT_SHOT; |
548 | } | 548 | } |
549 | 549 | ||
550 | static int tcf_csum_dump(struct sk_buff *skb, | 550 | static int tcf_csum_dump(struct sk_buff *skb, |
551 | struct tc_action *a, int bind, int ref) | 551 | struct tc_action *a, int bind, int ref) |
552 | { | 552 | { |
553 | unsigned char *b = skb_tail_pointer(skb); | 553 | unsigned char *b = skb_tail_pointer(skb); |
554 | struct tcf_csum *p = a->priv; | 554 | struct tcf_csum *p = a->priv; |
555 | struct tc_csum opt = { | 555 | struct tc_csum opt = { |
556 | .update_flags = p->update_flags, | 556 | .update_flags = p->update_flags, |
557 | .index = p->tcf_index, | 557 | .index = p->tcf_index, |
558 | .action = p->tcf_action, | 558 | .action = p->tcf_action, |
559 | .refcnt = p->tcf_refcnt - ref, | 559 | .refcnt = p->tcf_refcnt - ref, |
560 | .bindcnt = p->tcf_bindcnt - bind, | 560 | .bindcnt = p->tcf_bindcnt - bind, |
561 | }; | 561 | }; |
562 | struct tcf_t t; | 562 | struct tcf_t t; |
563 | 563 | ||
564 | if (nla_put(skb, TCA_CSUM_PARMS, sizeof(opt), &opt)) | 564 | if (nla_put(skb, TCA_CSUM_PARMS, sizeof(opt), &opt)) |
565 | goto nla_put_failure; | 565 | goto nla_put_failure; |
566 | t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); | 566 | t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); |
567 | t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); | 567 | t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); |
568 | t.expires = jiffies_to_clock_t(p->tcf_tm.expires); | 568 | t.expires = jiffies_to_clock_t(p->tcf_tm.expires); |
569 | if (nla_put(skb, TCA_CSUM_TM, sizeof(t), &t)) | 569 | if (nla_put(skb, TCA_CSUM_TM, sizeof(t), &t)) |
570 | goto nla_put_failure; | 570 | goto nla_put_failure; |
571 | 571 | ||
572 | return skb->len; | 572 | return skb->len; |
573 | 573 | ||
574 | nla_put_failure: | 574 | nla_put_failure: |
575 | nlmsg_trim(skb, b); | 575 | nlmsg_trim(skb, b); |
576 | return -1; | 576 | return -1; |
577 | } | 577 | } |
578 | 578 | ||
579 | static struct tc_action_ops act_csum_ops = { | 579 | static struct tc_action_ops act_csum_ops = { |
580 | .kind = "csum", | 580 | .kind = "csum", |
581 | .hinfo = &csum_hash_info, | 581 | .hinfo = &csum_hash_info, |
582 | .type = TCA_ACT_CSUM, | 582 | .type = TCA_ACT_CSUM, |
583 | .capab = TCA_CAP_NONE, | 583 | .capab = TCA_CAP_NONE, |
584 | .owner = THIS_MODULE, | 584 | .owner = THIS_MODULE, |
585 | .act = tcf_csum, | 585 | .act = tcf_csum, |
586 | .dump = tcf_csum_dump, | 586 | .dump = tcf_csum_dump, |
587 | .cleanup = tcf_csum_cleanup, | 587 | .cleanup = tcf_csum_cleanup, |
588 | .init = tcf_csum_init, | 588 | .init = tcf_csum_init, |
589 | .walk = tcf_generic_walker | ||
590 | }; | 589 | }; |
591 | 590 | ||
592 | MODULE_DESCRIPTION("Checksum updating actions"); | 591 | MODULE_DESCRIPTION("Checksum updating actions"); |
593 | MODULE_LICENSE("GPL"); | 592 | MODULE_LICENSE("GPL"); |
594 | 593 | ||
595 | static int __init csum_init_module(void) | 594 | static int __init csum_init_module(void) |
596 | { | 595 | { |
597 | return tcf_register_action(&act_csum_ops); | 596 | return tcf_register_action(&act_csum_ops); |
598 | } | 597 | } |
599 | 598 | ||
600 | static void __exit csum_cleanup_module(void) | 599 | static void __exit csum_cleanup_module(void) |
601 | { | 600 | { |
602 | tcf_unregister_action(&act_csum_ops); | 601 | tcf_unregister_action(&act_csum_ops); |
603 | } | 602 | } |
604 | 603 | ||
605 | module_init(csum_init_module); | 604 | module_init(csum_init_module); |
606 | module_exit(csum_cleanup_module); | 605 | module_exit(csum_cleanup_module); |
607 | 606 |
net/sched/act_gact.c
1 | /* | 1 | /* |
2 | * net/sched/gact.c Generic actions | 2 | * net/sched/gact.c Generic actions |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License | 5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; either version | 6 | * as published by the Free Software Foundation; either version |
7 | * 2 of the License, or (at your option) any later version. | 7 | * 2 of the License, or (at your option) any later version. |
8 | * | 8 | * |
9 | * copyright Jamal Hadi Salim (2002-4) | 9 | * copyright Jamal Hadi Salim (2002-4) |
10 | * | 10 | * |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/types.h> | 13 | #include <linux/types.h> |
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/string.h> | 15 | #include <linux/string.h> |
16 | #include <linux/errno.h> | 16 | #include <linux/errno.h> |
17 | #include <linux/skbuff.h> | 17 | #include <linux/skbuff.h> |
18 | #include <linux/rtnetlink.h> | 18 | #include <linux/rtnetlink.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
21 | #include <net/netlink.h> | 21 | #include <net/netlink.h> |
22 | #include <net/pkt_sched.h> | 22 | #include <net/pkt_sched.h> |
23 | #include <linux/tc_act/tc_gact.h> | 23 | #include <linux/tc_act/tc_gact.h> |
24 | #include <net/tc_act/tc_gact.h> | 24 | #include <net/tc_act/tc_gact.h> |
25 | 25 | ||
26 | #define GACT_TAB_MASK 15 | 26 | #define GACT_TAB_MASK 15 |
27 | static struct tcf_common *tcf_gact_ht[GACT_TAB_MASK + 1]; | 27 | static struct tcf_common *tcf_gact_ht[GACT_TAB_MASK + 1]; |
28 | static u32 gact_idx_gen; | 28 | static u32 gact_idx_gen; |
29 | static DEFINE_RWLOCK(gact_lock); | 29 | static DEFINE_RWLOCK(gact_lock); |
30 | 30 | ||
31 | static struct tcf_hashinfo gact_hash_info = { | 31 | static struct tcf_hashinfo gact_hash_info = { |
32 | .htab = tcf_gact_ht, | 32 | .htab = tcf_gact_ht, |
33 | .hmask = GACT_TAB_MASK, | 33 | .hmask = GACT_TAB_MASK, |
34 | .lock = &gact_lock, | 34 | .lock = &gact_lock, |
35 | }; | 35 | }; |
36 | 36 | ||
37 | #ifdef CONFIG_GACT_PROB | 37 | #ifdef CONFIG_GACT_PROB |
38 | static int gact_net_rand(struct tcf_gact *gact) | 38 | static int gact_net_rand(struct tcf_gact *gact) |
39 | { | 39 | { |
40 | if (!gact->tcfg_pval || net_random() % gact->tcfg_pval) | 40 | if (!gact->tcfg_pval || net_random() % gact->tcfg_pval) |
41 | return gact->tcf_action; | 41 | return gact->tcf_action; |
42 | return gact->tcfg_paction; | 42 | return gact->tcfg_paction; |
43 | } | 43 | } |
44 | 44 | ||
45 | static int gact_determ(struct tcf_gact *gact) | 45 | static int gact_determ(struct tcf_gact *gact) |
46 | { | 46 | { |
47 | if (!gact->tcfg_pval || gact->tcf_bstats.packets % gact->tcfg_pval) | 47 | if (!gact->tcfg_pval || gact->tcf_bstats.packets % gact->tcfg_pval) |
48 | return gact->tcf_action; | 48 | return gact->tcf_action; |
49 | return gact->tcfg_paction; | 49 | return gact->tcfg_paction; |
50 | } | 50 | } |
51 | 51 | ||
52 | typedef int (*g_rand)(struct tcf_gact *gact); | 52 | typedef int (*g_rand)(struct tcf_gact *gact); |
53 | static g_rand gact_rand[MAX_RAND] = { NULL, gact_net_rand, gact_determ }; | 53 | static g_rand gact_rand[MAX_RAND] = { NULL, gact_net_rand, gact_determ }; |
54 | #endif /* CONFIG_GACT_PROB */ | 54 | #endif /* CONFIG_GACT_PROB */ |
55 | 55 | ||
56 | static const struct nla_policy gact_policy[TCA_GACT_MAX + 1] = { | 56 | static const struct nla_policy gact_policy[TCA_GACT_MAX + 1] = { |
57 | [TCA_GACT_PARMS] = { .len = sizeof(struct tc_gact) }, | 57 | [TCA_GACT_PARMS] = { .len = sizeof(struct tc_gact) }, |
58 | [TCA_GACT_PROB] = { .len = sizeof(struct tc_gact_p) }, | 58 | [TCA_GACT_PROB] = { .len = sizeof(struct tc_gact_p) }, |
59 | }; | 59 | }; |
60 | 60 | ||
61 | static int tcf_gact_init(struct net *net, struct nlattr *nla, | 61 | static int tcf_gact_init(struct net *net, struct nlattr *nla, |
62 | struct nlattr *est, struct tc_action *a, | 62 | struct nlattr *est, struct tc_action *a, |
63 | int ovr, int bind) | 63 | int ovr, int bind) |
64 | { | 64 | { |
65 | struct nlattr *tb[TCA_GACT_MAX + 1]; | 65 | struct nlattr *tb[TCA_GACT_MAX + 1]; |
66 | struct tc_gact *parm; | 66 | struct tc_gact *parm; |
67 | struct tcf_gact *gact; | 67 | struct tcf_gact *gact; |
68 | struct tcf_common *pc; | 68 | struct tcf_common *pc; |
69 | int ret = 0; | 69 | int ret = 0; |
70 | int err; | 70 | int err; |
71 | #ifdef CONFIG_GACT_PROB | 71 | #ifdef CONFIG_GACT_PROB |
72 | struct tc_gact_p *p_parm = NULL; | 72 | struct tc_gact_p *p_parm = NULL; |
73 | #endif | 73 | #endif |
74 | 74 | ||
75 | if (nla == NULL) | 75 | if (nla == NULL) |
76 | return -EINVAL; | 76 | return -EINVAL; |
77 | 77 | ||
78 | err = nla_parse_nested(tb, TCA_GACT_MAX, nla, gact_policy); | 78 | err = nla_parse_nested(tb, TCA_GACT_MAX, nla, gact_policy); |
79 | if (err < 0) | 79 | if (err < 0) |
80 | return err; | 80 | return err; |
81 | 81 | ||
82 | if (tb[TCA_GACT_PARMS] == NULL) | 82 | if (tb[TCA_GACT_PARMS] == NULL) |
83 | return -EINVAL; | 83 | return -EINVAL; |
84 | parm = nla_data(tb[TCA_GACT_PARMS]); | 84 | parm = nla_data(tb[TCA_GACT_PARMS]); |
85 | 85 | ||
86 | #ifndef CONFIG_GACT_PROB | 86 | #ifndef CONFIG_GACT_PROB |
87 | if (tb[TCA_GACT_PROB] != NULL) | 87 | if (tb[TCA_GACT_PROB] != NULL) |
88 | return -EOPNOTSUPP; | 88 | return -EOPNOTSUPP; |
89 | #else | 89 | #else |
90 | if (tb[TCA_GACT_PROB]) { | 90 | if (tb[TCA_GACT_PROB]) { |
91 | p_parm = nla_data(tb[TCA_GACT_PROB]); | 91 | p_parm = nla_data(tb[TCA_GACT_PROB]); |
92 | if (p_parm->ptype >= MAX_RAND) | 92 | if (p_parm->ptype >= MAX_RAND) |
93 | return -EINVAL; | 93 | return -EINVAL; |
94 | } | 94 | } |
95 | #endif | 95 | #endif |
96 | 96 | ||
97 | pc = tcf_hash_check(parm->index, a, bind, &gact_hash_info); | 97 | pc = tcf_hash_check(parm->index, a, bind, &gact_hash_info); |
98 | if (!pc) { | 98 | if (!pc) { |
99 | pc = tcf_hash_create(parm->index, est, a, sizeof(*gact), | 99 | pc = tcf_hash_create(parm->index, est, a, sizeof(*gact), |
100 | bind, &gact_idx_gen, &gact_hash_info); | 100 | bind, &gact_idx_gen, &gact_hash_info); |
101 | if (IS_ERR(pc)) | 101 | if (IS_ERR(pc)) |
102 | return PTR_ERR(pc); | 102 | return PTR_ERR(pc); |
103 | ret = ACT_P_CREATED; | 103 | ret = ACT_P_CREATED; |
104 | } else { | 104 | } else { |
105 | if (!ovr) { | 105 | if (!ovr) { |
106 | tcf_hash_release(pc, bind, &gact_hash_info); | 106 | tcf_hash_release(pc, bind, &gact_hash_info); |
107 | return -EEXIST; | 107 | return -EEXIST; |
108 | } | 108 | } |
109 | } | 109 | } |
110 | 110 | ||
111 | gact = to_gact(pc); | 111 | gact = to_gact(pc); |
112 | 112 | ||
113 | spin_lock_bh(&gact->tcf_lock); | 113 | spin_lock_bh(&gact->tcf_lock); |
114 | gact->tcf_action = parm->action; | 114 | gact->tcf_action = parm->action; |
115 | #ifdef CONFIG_GACT_PROB | 115 | #ifdef CONFIG_GACT_PROB |
116 | if (p_parm) { | 116 | if (p_parm) { |
117 | gact->tcfg_paction = p_parm->paction; | 117 | gact->tcfg_paction = p_parm->paction; |
118 | gact->tcfg_pval = p_parm->pval; | 118 | gact->tcfg_pval = p_parm->pval; |
119 | gact->tcfg_ptype = p_parm->ptype; | 119 | gact->tcfg_ptype = p_parm->ptype; |
120 | } | 120 | } |
121 | #endif | 121 | #endif |
122 | spin_unlock_bh(&gact->tcf_lock); | 122 | spin_unlock_bh(&gact->tcf_lock); |
123 | if (ret == ACT_P_CREATED) | 123 | if (ret == ACT_P_CREATED) |
124 | tcf_hash_insert(pc, &gact_hash_info); | 124 | tcf_hash_insert(pc, &gact_hash_info); |
125 | return ret; | 125 | return ret; |
126 | } | 126 | } |
127 | 127 | ||
128 | static int tcf_gact_cleanup(struct tc_action *a, int bind) | 128 | static int tcf_gact_cleanup(struct tc_action *a, int bind) |
129 | { | 129 | { |
130 | struct tcf_gact *gact = a->priv; | 130 | struct tcf_gact *gact = a->priv; |
131 | 131 | ||
132 | if (gact) | 132 | if (gact) |
133 | return tcf_hash_release(&gact->common, bind, &gact_hash_info); | 133 | return tcf_hash_release(&gact->common, bind, &gact_hash_info); |
134 | return 0; | 134 | return 0; |
135 | } | 135 | } |
136 | 136 | ||
137 | static int tcf_gact(struct sk_buff *skb, const struct tc_action *a, | 137 | static int tcf_gact(struct sk_buff *skb, const struct tc_action *a, |
138 | struct tcf_result *res) | 138 | struct tcf_result *res) |
139 | { | 139 | { |
140 | struct tcf_gact *gact = a->priv; | 140 | struct tcf_gact *gact = a->priv; |
141 | int action = TC_ACT_SHOT; | 141 | int action = TC_ACT_SHOT; |
142 | 142 | ||
143 | spin_lock(&gact->tcf_lock); | 143 | spin_lock(&gact->tcf_lock); |
144 | #ifdef CONFIG_GACT_PROB | 144 | #ifdef CONFIG_GACT_PROB |
145 | if (gact->tcfg_ptype) | 145 | if (gact->tcfg_ptype) |
146 | action = gact_rand[gact->tcfg_ptype](gact); | 146 | action = gact_rand[gact->tcfg_ptype](gact); |
147 | else | 147 | else |
148 | action = gact->tcf_action; | 148 | action = gact->tcf_action; |
149 | #else | 149 | #else |
150 | action = gact->tcf_action; | 150 | action = gact->tcf_action; |
151 | #endif | 151 | #endif |
152 | gact->tcf_bstats.bytes += qdisc_pkt_len(skb); | 152 | gact->tcf_bstats.bytes += qdisc_pkt_len(skb); |
153 | gact->tcf_bstats.packets++; | 153 | gact->tcf_bstats.packets++; |
154 | if (action == TC_ACT_SHOT) | 154 | if (action == TC_ACT_SHOT) |
155 | gact->tcf_qstats.drops++; | 155 | gact->tcf_qstats.drops++; |
156 | gact->tcf_tm.lastuse = jiffies; | 156 | gact->tcf_tm.lastuse = jiffies; |
157 | spin_unlock(&gact->tcf_lock); | 157 | spin_unlock(&gact->tcf_lock); |
158 | 158 | ||
159 | return action; | 159 | return action; |
160 | } | 160 | } |
161 | 161 | ||
162 | static int tcf_gact_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) | 162 | static int tcf_gact_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) |
163 | { | 163 | { |
164 | unsigned char *b = skb_tail_pointer(skb); | 164 | unsigned char *b = skb_tail_pointer(skb); |
165 | struct tcf_gact *gact = a->priv; | 165 | struct tcf_gact *gact = a->priv; |
166 | struct tc_gact opt = { | 166 | struct tc_gact opt = { |
167 | .index = gact->tcf_index, | 167 | .index = gact->tcf_index, |
168 | .refcnt = gact->tcf_refcnt - ref, | 168 | .refcnt = gact->tcf_refcnt - ref, |
169 | .bindcnt = gact->tcf_bindcnt - bind, | 169 | .bindcnt = gact->tcf_bindcnt - bind, |
170 | .action = gact->tcf_action, | 170 | .action = gact->tcf_action, |
171 | }; | 171 | }; |
172 | struct tcf_t t; | 172 | struct tcf_t t; |
173 | 173 | ||
174 | if (nla_put(skb, TCA_GACT_PARMS, sizeof(opt), &opt)) | 174 | if (nla_put(skb, TCA_GACT_PARMS, sizeof(opt), &opt)) |
175 | goto nla_put_failure; | 175 | goto nla_put_failure; |
176 | #ifdef CONFIG_GACT_PROB | 176 | #ifdef CONFIG_GACT_PROB |
177 | if (gact->tcfg_ptype) { | 177 | if (gact->tcfg_ptype) { |
178 | struct tc_gact_p p_opt = { | 178 | struct tc_gact_p p_opt = { |
179 | .paction = gact->tcfg_paction, | 179 | .paction = gact->tcfg_paction, |
180 | .pval = gact->tcfg_pval, | 180 | .pval = gact->tcfg_pval, |
181 | .ptype = gact->tcfg_ptype, | 181 | .ptype = gact->tcfg_ptype, |
182 | }; | 182 | }; |
183 | 183 | ||
184 | if (nla_put(skb, TCA_GACT_PROB, sizeof(p_opt), &p_opt)) | 184 | if (nla_put(skb, TCA_GACT_PROB, sizeof(p_opt), &p_opt)) |
185 | goto nla_put_failure; | 185 | goto nla_put_failure; |
186 | } | 186 | } |
187 | #endif | 187 | #endif |
188 | t.install = jiffies_to_clock_t(jiffies - gact->tcf_tm.install); | 188 | t.install = jiffies_to_clock_t(jiffies - gact->tcf_tm.install); |
189 | t.lastuse = jiffies_to_clock_t(jiffies - gact->tcf_tm.lastuse); | 189 | t.lastuse = jiffies_to_clock_t(jiffies - gact->tcf_tm.lastuse); |
190 | t.expires = jiffies_to_clock_t(gact->tcf_tm.expires); | 190 | t.expires = jiffies_to_clock_t(gact->tcf_tm.expires); |
191 | if (nla_put(skb, TCA_GACT_TM, sizeof(t), &t)) | 191 | if (nla_put(skb, TCA_GACT_TM, sizeof(t), &t)) |
192 | goto nla_put_failure; | 192 | goto nla_put_failure; |
193 | return skb->len; | 193 | return skb->len; |
194 | 194 | ||
195 | nla_put_failure: | 195 | nla_put_failure: |
196 | nlmsg_trim(skb, b); | 196 | nlmsg_trim(skb, b); |
197 | return -1; | 197 | return -1; |
198 | } | 198 | } |
199 | 199 | ||
200 | static struct tc_action_ops act_gact_ops = { | 200 | static struct tc_action_ops act_gact_ops = { |
201 | .kind = "gact", | 201 | .kind = "gact", |
202 | .hinfo = &gact_hash_info, | 202 | .hinfo = &gact_hash_info, |
203 | .type = TCA_ACT_GACT, | 203 | .type = TCA_ACT_GACT, |
204 | .capab = TCA_CAP_NONE, | 204 | .capab = TCA_CAP_NONE, |
205 | .owner = THIS_MODULE, | 205 | .owner = THIS_MODULE, |
206 | .act = tcf_gact, | 206 | .act = tcf_gact, |
207 | .dump = tcf_gact_dump, | 207 | .dump = tcf_gact_dump, |
208 | .cleanup = tcf_gact_cleanup, | 208 | .cleanup = tcf_gact_cleanup, |
209 | .init = tcf_gact_init, | 209 | .init = tcf_gact_init, |
210 | .walk = tcf_generic_walker | ||
211 | }; | 210 | }; |
212 | 211 | ||
213 | MODULE_AUTHOR("Jamal Hadi Salim(2002-4)"); | 212 | MODULE_AUTHOR("Jamal Hadi Salim(2002-4)"); |
214 | MODULE_DESCRIPTION("Generic Classifier actions"); | 213 | MODULE_DESCRIPTION("Generic Classifier actions"); |
215 | MODULE_LICENSE("GPL"); | 214 | MODULE_LICENSE("GPL"); |
216 | 215 | ||
217 | static int __init gact_init_module(void) | 216 | static int __init gact_init_module(void) |
218 | { | 217 | { |
219 | #ifdef CONFIG_GACT_PROB | 218 | #ifdef CONFIG_GACT_PROB |
220 | pr_info("GACT probability on\n"); | 219 | pr_info("GACT probability on\n"); |
221 | #else | 220 | #else |
222 | pr_info("GACT probability NOT on\n"); | 221 | pr_info("GACT probability NOT on\n"); |
223 | #endif | 222 | #endif |
224 | return tcf_register_action(&act_gact_ops); | 223 | return tcf_register_action(&act_gact_ops); |
225 | } | 224 | } |
226 | 225 | ||
227 | static void __exit gact_cleanup_module(void) | 226 | static void __exit gact_cleanup_module(void) |
228 | { | 227 | { |
229 | tcf_unregister_action(&act_gact_ops); | 228 | tcf_unregister_action(&act_gact_ops); |
230 | } | 229 | } |
231 | 230 | ||
232 | module_init(gact_init_module); | 231 | module_init(gact_init_module); |
233 | module_exit(gact_cleanup_module); | 232 | module_exit(gact_cleanup_module); |
234 | 233 |
net/sched/act_ipt.c
1 | /* | 1 | /* |
2 | * net/sched/ipt.c iptables target interface | 2 | * net/sched/ipt.c iptables target interface |
3 | * | 3 | * |
4 | *TODO: Add other tables. For now we only support the ipv4 table targets | 4 | *TODO: Add other tables. For now we only support the ipv4 table targets |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License | 7 | * modify it under the terms of the GNU General Public License |
8 | * as published by the Free Software Foundation; either version | 8 | * as published by the Free Software Foundation; either version |
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | * | 10 | * |
11 | * Copyright: Jamal Hadi Salim (2002-13) | 11 | * Copyright: Jamal Hadi Salim (2002-13) |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/types.h> | 14 | #include <linux/types.h> |
15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
16 | #include <linux/string.h> | 16 | #include <linux/string.h> |
17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
18 | #include <linux/skbuff.h> | 18 | #include <linux/skbuff.h> |
19 | #include <linux/rtnetlink.h> | 19 | #include <linux/rtnetlink.h> |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | #include <net/netlink.h> | 23 | #include <net/netlink.h> |
24 | #include <net/pkt_sched.h> | 24 | #include <net/pkt_sched.h> |
25 | #include <linux/tc_act/tc_ipt.h> | 25 | #include <linux/tc_act/tc_ipt.h> |
26 | #include <net/tc_act/tc_ipt.h> | 26 | #include <net/tc_act/tc_ipt.h> |
27 | 27 | ||
28 | #include <linux/netfilter_ipv4/ip_tables.h> | 28 | #include <linux/netfilter_ipv4/ip_tables.h> |
29 | 29 | ||
30 | 30 | ||
31 | #define IPT_TAB_MASK 15 | 31 | #define IPT_TAB_MASK 15 |
32 | static struct tcf_common *tcf_ipt_ht[IPT_TAB_MASK + 1]; | 32 | static struct tcf_common *tcf_ipt_ht[IPT_TAB_MASK + 1]; |
33 | static u32 ipt_idx_gen; | 33 | static u32 ipt_idx_gen; |
34 | static DEFINE_RWLOCK(ipt_lock); | 34 | static DEFINE_RWLOCK(ipt_lock); |
35 | 35 | ||
36 | static struct tcf_hashinfo ipt_hash_info = { | 36 | static struct tcf_hashinfo ipt_hash_info = { |
37 | .htab = tcf_ipt_ht, | 37 | .htab = tcf_ipt_ht, |
38 | .hmask = IPT_TAB_MASK, | 38 | .hmask = IPT_TAB_MASK, |
39 | .lock = &ipt_lock, | 39 | .lock = &ipt_lock, |
40 | }; | 40 | }; |
41 | 41 | ||
42 | static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int hook) | 42 | static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int hook) |
43 | { | 43 | { |
44 | struct xt_tgchk_param par; | 44 | struct xt_tgchk_param par; |
45 | struct xt_target *target; | 45 | struct xt_target *target; |
46 | int ret = 0; | 46 | int ret = 0; |
47 | 47 | ||
48 | target = xt_request_find_target(AF_INET, t->u.user.name, | 48 | target = xt_request_find_target(AF_INET, t->u.user.name, |
49 | t->u.user.revision); | 49 | t->u.user.revision); |
50 | if (IS_ERR(target)) | 50 | if (IS_ERR(target)) |
51 | return PTR_ERR(target); | 51 | return PTR_ERR(target); |
52 | 52 | ||
53 | t->u.kernel.target = target; | 53 | t->u.kernel.target = target; |
54 | par.table = table; | 54 | par.table = table; |
55 | par.entryinfo = NULL; | 55 | par.entryinfo = NULL; |
56 | par.target = target; | 56 | par.target = target; |
57 | par.targinfo = t->data; | 57 | par.targinfo = t->data; |
58 | par.hook_mask = hook; | 58 | par.hook_mask = hook; |
59 | par.family = NFPROTO_IPV4; | 59 | par.family = NFPROTO_IPV4; |
60 | 60 | ||
61 | ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false); | 61 | ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false); |
62 | if (ret < 0) { | 62 | if (ret < 0) { |
63 | module_put(t->u.kernel.target->me); | 63 | module_put(t->u.kernel.target->me); |
64 | return ret; | 64 | return ret; |
65 | } | 65 | } |
66 | return 0; | 66 | return 0; |
67 | } | 67 | } |
68 | 68 | ||
69 | static void ipt_destroy_target(struct xt_entry_target *t) | 69 | static void ipt_destroy_target(struct xt_entry_target *t) |
70 | { | 70 | { |
71 | struct xt_tgdtor_param par = { | 71 | struct xt_tgdtor_param par = { |
72 | .target = t->u.kernel.target, | 72 | .target = t->u.kernel.target, |
73 | .targinfo = t->data, | 73 | .targinfo = t->data, |
74 | }; | 74 | }; |
75 | if (par.target->destroy != NULL) | 75 | if (par.target->destroy != NULL) |
76 | par.target->destroy(&par); | 76 | par.target->destroy(&par); |
77 | module_put(par.target->me); | 77 | module_put(par.target->me); |
78 | } | 78 | } |
79 | 79 | ||
80 | static int tcf_ipt_release(struct tcf_ipt *ipt, int bind) | 80 | static int tcf_ipt_release(struct tcf_ipt *ipt, int bind) |
81 | { | 81 | { |
82 | int ret = 0; | 82 | int ret = 0; |
83 | if (ipt) { | 83 | if (ipt) { |
84 | if (bind) | 84 | if (bind) |
85 | ipt->tcf_bindcnt--; | 85 | ipt->tcf_bindcnt--; |
86 | ipt->tcf_refcnt--; | 86 | ipt->tcf_refcnt--; |
87 | if (ipt->tcf_bindcnt <= 0 && ipt->tcf_refcnt <= 0) { | 87 | if (ipt->tcf_bindcnt <= 0 && ipt->tcf_refcnt <= 0) { |
88 | ipt_destroy_target(ipt->tcfi_t); | 88 | ipt_destroy_target(ipt->tcfi_t); |
89 | kfree(ipt->tcfi_tname); | 89 | kfree(ipt->tcfi_tname); |
90 | kfree(ipt->tcfi_t); | 90 | kfree(ipt->tcfi_t); |
91 | tcf_hash_destroy(&ipt->common, &ipt_hash_info); | 91 | tcf_hash_destroy(&ipt->common, &ipt_hash_info); |
92 | ret = ACT_P_DELETED; | 92 | ret = ACT_P_DELETED; |
93 | } | 93 | } |
94 | } | 94 | } |
95 | return ret; | 95 | return ret; |
96 | } | 96 | } |
97 | 97 | ||
98 | static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = { | 98 | static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = { |
99 | [TCA_IPT_TABLE] = { .type = NLA_STRING, .len = IFNAMSIZ }, | 99 | [TCA_IPT_TABLE] = { .type = NLA_STRING, .len = IFNAMSIZ }, |
100 | [TCA_IPT_HOOK] = { .type = NLA_U32 }, | 100 | [TCA_IPT_HOOK] = { .type = NLA_U32 }, |
101 | [TCA_IPT_INDEX] = { .type = NLA_U32 }, | 101 | [TCA_IPT_INDEX] = { .type = NLA_U32 }, |
102 | [TCA_IPT_TARG] = { .len = sizeof(struct xt_entry_target) }, | 102 | [TCA_IPT_TARG] = { .len = sizeof(struct xt_entry_target) }, |
103 | }; | 103 | }; |
104 | 104 | ||
105 | static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est, | 105 | static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est, |
106 | struct tc_action *a, int ovr, int bind) | 106 | struct tc_action *a, int ovr, int bind) |
107 | { | 107 | { |
108 | struct nlattr *tb[TCA_IPT_MAX + 1]; | 108 | struct nlattr *tb[TCA_IPT_MAX + 1]; |
109 | struct tcf_ipt *ipt; | 109 | struct tcf_ipt *ipt; |
110 | struct tcf_common *pc; | 110 | struct tcf_common *pc; |
111 | struct xt_entry_target *td, *t; | 111 | struct xt_entry_target *td, *t; |
112 | char *tname; | 112 | char *tname; |
113 | int ret = 0, err; | 113 | int ret = 0, err; |
114 | u32 hook = 0; | 114 | u32 hook = 0; |
115 | u32 index = 0; | 115 | u32 index = 0; |
116 | 116 | ||
117 | if (nla == NULL) | 117 | if (nla == NULL) |
118 | return -EINVAL; | 118 | return -EINVAL; |
119 | 119 | ||
120 | err = nla_parse_nested(tb, TCA_IPT_MAX, nla, ipt_policy); | 120 | err = nla_parse_nested(tb, TCA_IPT_MAX, nla, ipt_policy); |
121 | if (err < 0) | 121 | if (err < 0) |
122 | return err; | 122 | return err; |
123 | 123 | ||
124 | if (tb[TCA_IPT_HOOK] == NULL) | 124 | if (tb[TCA_IPT_HOOK] == NULL) |
125 | return -EINVAL; | 125 | return -EINVAL; |
126 | if (tb[TCA_IPT_TARG] == NULL) | 126 | if (tb[TCA_IPT_TARG] == NULL) |
127 | return -EINVAL; | 127 | return -EINVAL; |
128 | 128 | ||
129 | td = (struct xt_entry_target *)nla_data(tb[TCA_IPT_TARG]); | 129 | td = (struct xt_entry_target *)nla_data(tb[TCA_IPT_TARG]); |
130 | if (nla_len(tb[TCA_IPT_TARG]) < td->u.target_size) | 130 | if (nla_len(tb[TCA_IPT_TARG]) < td->u.target_size) |
131 | return -EINVAL; | 131 | return -EINVAL; |
132 | 132 | ||
133 | if (tb[TCA_IPT_INDEX] != NULL) | 133 | if (tb[TCA_IPT_INDEX] != NULL) |
134 | index = nla_get_u32(tb[TCA_IPT_INDEX]); | 134 | index = nla_get_u32(tb[TCA_IPT_INDEX]); |
135 | 135 | ||
136 | pc = tcf_hash_check(index, a, bind, &ipt_hash_info); | 136 | pc = tcf_hash_check(index, a, bind, &ipt_hash_info); |
137 | if (!pc) { | 137 | if (!pc) { |
138 | pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind, | 138 | pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind, |
139 | &ipt_idx_gen, &ipt_hash_info); | 139 | &ipt_idx_gen, &ipt_hash_info); |
140 | if (IS_ERR(pc)) | 140 | if (IS_ERR(pc)) |
141 | return PTR_ERR(pc); | 141 | return PTR_ERR(pc); |
142 | ret = ACT_P_CREATED; | 142 | ret = ACT_P_CREATED; |
143 | } else { | 143 | } else { |
144 | if (!ovr) { | 144 | if (!ovr) { |
145 | tcf_ipt_release(to_ipt(pc), bind); | 145 | tcf_ipt_release(to_ipt(pc), bind); |
146 | return -EEXIST; | 146 | return -EEXIST; |
147 | } | 147 | } |
148 | } | 148 | } |
149 | ipt = to_ipt(pc); | 149 | ipt = to_ipt(pc); |
150 | 150 | ||
151 | hook = nla_get_u32(tb[TCA_IPT_HOOK]); | 151 | hook = nla_get_u32(tb[TCA_IPT_HOOK]); |
152 | 152 | ||
153 | err = -ENOMEM; | 153 | err = -ENOMEM; |
154 | tname = kmalloc(IFNAMSIZ, GFP_KERNEL); | 154 | tname = kmalloc(IFNAMSIZ, GFP_KERNEL); |
155 | if (unlikely(!tname)) | 155 | if (unlikely(!tname)) |
156 | goto err1; | 156 | goto err1; |
157 | if (tb[TCA_IPT_TABLE] == NULL || | 157 | if (tb[TCA_IPT_TABLE] == NULL || |
158 | nla_strlcpy(tname, tb[TCA_IPT_TABLE], IFNAMSIZ) >= IFNAMSIZ) | 158 | nla_strlcpy(tname, tb[TCA_IPT_TABLE], IFNAMSIZ) >= IFNAMSIZ) |
159 | strcpy(tname, "mangle"); | 159 | strcpy(tname, "mangle"); |
160 | 160 | ||
161 | t = kmemdup(td, td->u.target_size, GFP_KERNEL); | 161 | t = kmemdup(td, td->u.target_size, GFP_KERNEL); |
162 | if (unlikely(!t)) | 162 | if (unlikely(!t)) |
163 | goto err2; | 163 | goto err2; |
164 | 164 | ||
165 | err = ipt_init_target(t, tname, hook); | 165 | err = ipt_init_target(t, tname, hook); |
166 | if (err < 0) | 166 | if (err < 0) |
167 | goto err3; | 167 | goto err3; |
168 | 168 | ||
169 | spin_lock_bh(&ipt->tcf_lock); | 169 | spin_lock_bh(&ipt->tcf_lock); |
170 | if (ret != ACT_P_CREATED) { | 170 | if (ret != ACT_P_CREATED) { |
171 | ipt_destroy_target(ipt->tcfi_t); | 171 | ipt_destroy_target(ipt->tcfi_t); |
172 | kfree(ipt->tcfi_tname); | 172 | kfree(ipt->tcfi_tname); |
173 | kfree(ipt->tcfi_t); | 173 | kfree(ipt->tcfi_t); |
174 | } | 174 | } |
175 | ipt->tcfi_tname = tname; | 175 | ipt->tcfi_tname = tname; |
176 | ipt->tcfi_t = t; | 176 | ipt->tcfi_t = t; |
177 | ipt->tcfi_hook = hook; | 177 | ipt->tcfi_hook = hook; |
178 | spin_unlock_bh(&ipt->tcf_lock); | 178 | spin_unlock_bh(&ipt->tcf_lock); |
179 | if (ret == ACT_P_CREATED) | 179 | if (ret == ACT_P_CREATED) |
180 | tcf_hash_insert(pc, &ipt_hash_info); | 180 | tcf_hash_insert(pc, &ipt_hash_info); |
181 | return ret; | 181 | return ret; |
182 | 182 | ||
183 | err3: | 183 | err3: |
184 | kfree(t); | 184 | kfree(t); |
185 | err2: | 185 | err2: |
186 | kfree(tname); | 186 | kfree(tname); |
187 | err1: | 187 | err1: |
188 | if (ret == ACT_P_CREATED) { | 188 | if (ret == ACT_P_CREATED) { |
189 | if (est) | 189 | if (est) |
190 | gen_kill_estimator(&pc->tcfc_bstats, | 190 | gen_kill_estimator(&pc->tcfc_bstats, |
191 | &pc->tcfc_rate_est); | 191 | &pc->tcfc_rate_est); |
192 | kfree_rcu(pc, tcfc_rcu); | 192 | kfree_rcu(pc, tcfc_rcu); |
193 | } | 193 | } |
194 | return err; | 194 | return err; |
195 | } | 195 | } |
196 | 196 | ||
197 | static int tcf_ipt_cleanup(struct tc_action *a, int bind) | 197 | static int tcf_ipt_cleanup(struct tc_action *a, int bind) |
198 | { | 198 | { |
199 | struct tcf_ipt *ipt = a->priv; | 199 | struct tcf_ipt *ipt = a->priv; |
200 | return tcf_ipt_release(ipt, bind); | 200 | return tcf_ipt_release(ipt, bind); |
201 | } | 201 | } |
202 | 202 | ||
203 | static int tcf_ipt(struct sk_buff *skb, const struct tc_action *a, | 203 | static int tcf_ipt(struct sk_buff *skb, const struct tc_action *a, |
204 | struct tcf_result *res) | 204 | struct tcf_result *res) |
205 | { | 205 | { |
206 | int ret = 0, result = 0; | 206 | int ret = 0, result = 0; |
207 | struct tcf_ipt *ipt = a->priv; | 207 | struct tcf_ipt *ipt = a->priv; |
208 | struct xt_action_param par; | 208 | struct xt_action_param par; |
209 | 209 | ||
210 | if (skb_unclone(skb, GFP_ATOMIC)) | 210 | if (skb_unclone(skb, GFP_ATOMIC)) |
211 | return TC_ACT_UNSPEC; | 211 | return TC_ACT_UNSPEC; |
212 | 212 | ||
213 | spin_lock(&ipt->tcf_lock); | 213 | spin_lock(&ipt->tcf_lock); |
214 | 214 | ||
215 | ipt->tcf_tm.lastuse = jiffies; | 215 | ipt->tcf_tm.lastuse = jiffies; |
216 | bstats_update(&ipt->tcf_bstats, skb); | 216 | bstats_update(&ipt->tcf_bstats, skb); |
217 | 217 | ||
218 | /* yes, we have to worry about both in and out dev | 218 | /* yes, we have to worry about both in and out dev |
219 | * worry later - danger - this API seems to have changed | 219 | * worry later - danger - this API seems to have changed |
220 | * from earlier kernels | 220 | * from earlier kernels |
221 | */ | 221 | */ |
222 | par.in = skb->dev; | 222 | par.in = skb->dev; |
223 | par.out = NULL; | 223 | par.out = NULL; |
224 | par.hooknum = ipt->tcfi_hook; | 224 | par.hooknum = ipt->tcfi_hook; |
225 | par.target = ipt->tcfi_t->u.kernel.target; | 225 | par.target = ipt->tcfi_t->u.kernel.target; |
226 | par.targinfo = ipt->tcfi_t->data; | 226 | par.targinfo = ipt->tcfi_t->data; |
227 | ret = par.target->target(skb, &par); | 227 | ret = par.target->target(skb, &par); |
228 | 228 | ||
229 | switch (ret) { | 229 | switch (ret) { |
230 | case NF_ACCEPT: | 230 | case NF_ACCEPT: |
231 | result = TC_ACT_OK; | 231 | result = TC_ACT_OK; |
232 | break; | 232 | break; |
233 | case NF_DROP: | 233 | case NF_DROP: |
234 | result = TC_ACT_SHOT; | 234 | result = TC_ACT_SHOT; |
235 | ipt->tcf_qstats.drops++; | 235 | ipt->tcf_qstats.drops++; |
236 | break; | 236 | break; |
237 | case XT_CONTINUE: | 237 | case XT_CONTINUE: |
238 | result = TC_ACT_PIPE; | 238 | result = TC_ACT_PIPE; |
239 | break; | 239 | break; |
240 | default: | 240 | default: |
241 | net_notice_ratelimited("tc filter: Bogus netfilter code %d assume ACCEPT\n", | 241 | net_notice_ratelimited("tc filter: Bogus netfilter code %d assume ACCEPT\n", |
242 | ret); | 242 | ret); |
243 | result = TC_POLICE_OK; | 243 | result = TC_POLICE_OK; |
244 | break; | 244 | break; |
245 | } | 245 | } |
246 | spin_unlock(&ipt->tcf_lock); | 246 | spin_unlock(&ipt->tcf_lock); |
247 | return result; | 247 | return result; |
248 | 248 | ||
249 | } | 249 | } |
250 | 250 | ||
251 | static int tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) | 251 | static int tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) |
252 | { | 252 | { |
253 | unsigned char *b = skb_tail_pointer(skb); | 253 | unsigned char *b = skb_tail_pointer(skb); |
254 | struct tcf_ipt *ipt = a->priv; | 254 | struct tcf_ipt *ipt = a->priv; |
255 | struct xt_entry_target *t; | 255 | struct xt_entry_target *t; |
256 | struct tcf_t tm; | 256 | struct tcf_t tm; |
257 | struct tc_cnt c; | 257 | struct tc_cnt c; |
258 | 258 | ||
259 | /* for simple targets kernel size == user size | 259 | /* for simple targets kernel size == user size |
260 | * user name = target name | 260 | * user name = target name |
261 | * for foolproof you need to not assume this | 261 | * for foolproof you need to not assume this |
262 | */ | 262 | */ |
263 | 263 | ||
264 | t = kmemdup(ipt->tcfi_t, ipt->tcfi_t->u.user.target_size, GFP_ATOMIC); | 264 | t = kmemdup(ipt->tcfi_t, ipt->tcfi_t->u.user.target_size, GFP_ATOMIC); |
265 | if (unlikely(!t)) | 265 | if (unlikely(!t)) |
266 | goto nla_put_failure; | 266 | goto nla_put_failure; |
267 | 267 | ||
268 | c.bindcnt = ipt->tcf_bindcnt - bind; | 268 | c.bindcnt = ipt->tcf_bindcnt - bind; |
269 | c.refcnt = ipt->tcf_refcnt - ref; | 269 | c.refcnt = ipt->tcf_refcnt - ref; |
270 | strcpy(t->u.user.name, ipt->tcfi_t->u.kernel.target->name); | 270 | strcpy(t->u.user.name, ipt->tcfi_t->u.kernel.target->name); |
271 | 271 | ||
272 | if (nla_put(skb, TCA_IPT_TARG, ipt->tcfi_t->u.user.target_size, t) || | 272 | if (nla_put(skb, TCA_IPT_TARG, ipt->tcfi_t->u.user.target_size, t) || |
273 | nla_put_u32(skb, TCA_IPT_INDEX, ipt->tcf_index) || | 273 | nla_put_u32(skb, TCA_IPT_INDEX, ipt->tcf_index) || |
274 | nla_put_u32(skb, TCA_IPT_HOOK, ipt->tcfi_hook) || | 274 | nla_put_u32(skb, TCA_IPT_HOOK, ipt->tcfi_hook) || |
275 | nla_put(skb, TCA_IPT_CNT, sizeof(struct tc_cnt), &c) || | 275 | nla_put(skb, TCA_IPT_CNT, sizeof(struct tc_cnt), &c) || |
276 | nla_put_string(skb, TCA_IPT_TABLE, ipt->tcfi_tname)) | 276 | nla_put_string(skb, TCA_IPT_TABLE, ipt->tcfi_tname)) |
277 | goto nla_put_failure; | 277 | goto nla_put_failure; |
278 | tm.install = jiffies_to_clock_t(jiffies - ipt->tcf_tm.install); | 278 | tm.install = jiffies_to_clock_t(jiffies - ipt->tcf_tm.install); |
279 | tm.lastuse = jiffies_to_clock_t(jiffies - ipt->tcf_tm.lastuse); | 279 | tm.lastuse = jiffies_to_clock_t(jiffies - ipt->tcf_tm.lastuse); |
280 | tm.expires = jiffies_to_clock_t(ipt->tcf_tm.expires); | 280 | tm.expires = jiffies_to_clock_t(ipt->tcf_tm.expires); |
281 | if (nla_put(skb, TCA_IPT_TM, sizeof (tm), &tm)) | 281 | if (nla_put(skb, TCA_IPT_TM, sizeof (tm), &tm)) |
282 | goto nla_put_failure; | 282 | goto nla_put_failure; |
283 | kfree(t); | 283 | kfree(t); |
284 | return skb->len; | 284 | return skb->len; |
285 | 285 | ||
286 | nla_put_failure: | 286 | nla_put_failure: |
287 | nlmsg_trim(skb, b); | 287 | nlmsg_trim(skb, b); |
288 | kfree(t); | 288 | kfree(t); |
289 | return -1; | 289 | return -1; |
290 | } | 290 | } |
291 | 291 | ||
292 | static struct tc_action_ops act_ipt_ops = { | 292 | static struct tc_action_ops act_ipt_ops = { |
293 | .kind = "ipt", | 293 | .kind = "ipt", |
294 | .hinfo = &ipt_hash_info, | 294 | .hinfo = &ipt_hash_info, |
295 | .type = TCA_ACT_IPT, | 295 | .type = TCA_ACT_IPT, |
296 | .capab = TCA_CAP_NONE, | 296 | .capab = TCA_CAP_NONE, |
297 | .owner = THIS_MODULE, | 297 | .owner = THIS_MODULE, |
298 | .act = tcf_ipt, | 298 | .act = tcf_ipt, |
299 | .dump = tcf_ipt_dump, | 299 | .dump = tcf_ipt_dump, |
300 | .cleanup = tcf_ipt_cleanup, | 300 | .cleanup = tcf_ipt_cleanup, |
301 | .init = tcf_ipt_init, | 301 | .init = tcf_ipt_init, |
302 | .walk = tcf_generic_walker | ||
303 | }; | 302 | }; |
304 | 303 | ||
305 | static struct tc_action_ops act_xt_ops = { | 304 | static struct tc_action_ops act_xt_ops = { |
306 | .kind = "xt", | 305 | .kind = "xt", |
307 | .hinfo = &ipt_hash_info, | 306 | .hinfo = &ipt_hash_info, |
308 | .type = TCA_ACT_IPT, | 307 | .type = TCA_ACT_IPT, |
309 | .capab = TCA_CAP_NONE, | 308 | .capab = TCA_CAP_NONE, |
310 | .owner = THIS_MODULE, | 309 | .owner = THIS_MODULE, |
311 | .act = tcf_ipt, | 310 | .act = tcf_ipt, |
312 | .dump = tcf_ipt_dump, | 311 | .dump = tcf_ipt_dump, |
313 | .cleanup = tcf_ipt_cleanup, | 312 | .cleanup = tcf_ipt_cleanup, |
314 | .init = tcf_ipt_init, | 313 | .init = tcf_ipt_init, |
315 | .walk = tcf_generic_walker | ||
316 | }; | 314 | }; |
317 | 315 | ||
318 | MODULE_AUTHOR("Jamal Hadi Salim(2002-13)"); | 316 | MODULE_AUTHOR("Jamal Hadi Salim(2002-13)"); |
319 | MODULE_DESCRIPTION("Iptables target actions"); | 317 | MODULE_DESCRIPTION("Iptables target actions"); |
320 | MODULE_LICENSE("GPL"); | 318 | MODULE_LICENSE("GPL"); |
321 | MODULE_ALIAS("act_xt"); | 319 | MODULE_ALIAS("act_xt"); |
322 | 320 | ||
323 | static int __init ipt_init_module(void) | 321 | static int __init ipt_init_module(void) |
324 | { | 322 | { |
325 | int ret1, ret2; | 323 | int ret1, ret2; |
326 | ret1 = tcf_register_action(&act_xt_ops); | 324 | ret1 = tcf_register_action(&act_xt_ops); |
327 | if (ret1 < 0) | 325 | if (ret1 < 0) |
328 | printk("Failed to load xt action\n"); | 326 | printk("Failed to load xt action\n"); |
329 | ret2 = tcf_register_action(&act_ipt_ops); | 327 | ret2 = tcf_register_action(&act_ipt_ops); |
330 | if (ret2 < 0) | 328 | if (ret2 < 0) |
331 | printk("Failed to load ipt action\n"); | 329 | printk("Failed to load ipt action\n"); |
332 | 330 | ||
333 | if (ret1 < 0 && ret2 < 0) | 331 | if (ret1 < 0 && ret2 < 0) |
334 | return ret1; | 332 | return ret1; |
335 | else | 333 | else |
336 | return 0; | 334 | return 0; |
337 | } | 335 | } |
338 | 336 | ||
339 | static void __exit ipt_cleanup_module(void) | 337 | static void __exit ipt_cleanup_module(void) |
340 | { | 338 | { |
341 | tcf_unregister_action(&act_xt_ops); | 339 | tcf_unregister_action(&act_xt_ops); |
342 | tcf_unregister_action(&act_ipt_ops); | 340 | tcf_unregister_action(&act_ipt_ops); |
343 | } | 341 | } |
344 | 342 | ||
345 | module_init(ipt_init_module); | 343 | module_init(ipt_init_module); |
346 | module_exit(ipt_cleanup_module); | 344 | module_exit(ipt_cleanup_module); |
347 | 345 |
net/sched/act_mirred.c
1 | /* | 1 | /* |
2 | * net/sched/mirred.c packet mirroring and redirect actions | 2 | * net/sched/mirred.c packet mirroring and redirect actions |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License | 5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; either version | 6 | * as published by the Free Software Foundation; either version |
7 | * 2 of the License, or (at your option) any later version. | 7 | * 2 of the License, or (at your option) any later version. |
8 | * | 8 | * |
9 | * Authors: Jamal Hadi Salim (2002-4) | 9 | * Authors: Jamal Hadi Salim (2002-4) |
10 | * | 10 | * |
11 | * TODO: Add ingress support (and socket redirect support) | 11 | * TODO: Add ingress support (and socket redirect support) |
12 | * | 12 | * |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/string.h> | 17 | #include <linux/string.h> |
18 | #include <linux/errno.h> | 18 | #include <linux/errno.h> |
19 | #include <linux/skbuff.h> | 19 | #include <linux/skbuff.h> |
20 | #include <linux/rtnetlink.h> | 20 | #include <linux/rtnetlink.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
23 | #include <linux/gfp.h> | 23 | #include <linux/gfp.h> |
24 | #include <net/net_namespace.h> | 24 | #include <net/net_namespace.h> |
25 | #include <net/netlink.h> | 25 | #include <net/netlink.h> |
26 | #include <net/pkt_sched.h> | 26 | #include <net/pkt_sched.h> |
27 | #include <linux/tc_act/tc_mirred.h> | 27 | #include <linux/tc_act/tc_mirred.h> |
28 | #include <net/tc_act/tc_mirred.h> | 28 | #include <net/tc_act/tc_mirred.h> |
29 | 29 | ||
30 | #include <linux/if_arp.h> | 30 | #include <linux/if_arp.h> |
31 | 31 | ||
32 | #define MIRRED_TAB_MASK 7 | 32 | #define MIRRED_TAB_MASK 7 |
33 | static struct tcf_common *tcf_mirred_ht[MIRRED_TAB_MASK + 1]; | 33 | static struct tcf_common *tcf_mirred_ht[MIRRED_TAB_MASK + 1]; |
34 | static u32 mirred_idx_gen; | 34 | static u32 mirred_idx_gen; |
35 | static DEFINE_RWLOCK(mirred_lock); | 35 | static DEFINE_RWLOCK(mirred_lock); |
36 | static LIST_HEAD(mirred_list); | 36 | static LIST_HEAD(mirred_list); |
37 | 37 | ||
38 | static struct tcf_hashinfo mirred_hash_info = { | 38 | static struct tcf_hashinfo mirred_hash_info = { |
39 | .htab = tcf_mirred_ht, | 39 | .htab = tcf_mirred_ht, |
40 | .hmask = MIRRED_TAB_MASK, | 40 | .hmask = MIRRED_TAB_MASK, |
41 | .lock = &mirred_lock, | 41 | .lock = &mirred_lock, |
42 | }; | 42 | }; |
43 | 43 | ||
44 | static int tcf_mirred_release(struct tcf_mirred *m, int bind) | 44 | static int tcf_mirred_release(struct tcf_mirred *m, int bind) |
45 | { | 45 | { |
46 | if (m) { | 46 | if (m) { |
47 | if (bind) | 47 | if (bind) |
48 | m->tcf_bindcnt--; | 48 | m->tcf_bindcnt--; |
49 | m->tcf_refcnt--; | 49 | m->tcf_refcnt--; |
50 | if (!m->tcf_bindcnt && m->tcf_refcnt <= 0) { | 50 | if (!m->tcf_bindcnt && m->tcf_refcnt <= 0) { |
51 | list_del(&m->tcfm_list); | 51 | list_del(&m->tcfm_list); |
52 | if (m->tcfm_dev) | 52 | if (m->tcfm_dev) |
53 | dev_put(m->tcfm_dev); | 53 | dev_put(m->tcfm_dev); |
54 | tcf_hash_destroy(&m->common, &mirred_hash_info); | 54 | tcf_hash_destroy(&m->common, &mirred_hash_info); |
55 | return 1; | 55 | return 1; |
56 | } | 56 | } |
57 | } | 57 | } |
58 | return 0; | 58 | return 0; |
59 | } | 59 | } |
60 | 60 | ||
61 | static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { | 61 | static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { |
62 | [TCA_MIRRED_PARMS] = { .len = sizeof(struct tc_mirred) }, | 62 | [TCA_MIRRED_PARMS] = { .len = sizeof(struct tc_mirred) }, |
63 | }; | 63 | }; |
64 | 64 | ||
65 | static int tcf_mirred_init(struct net *net, struct nlattr *nla, | 65 | static int tcf_mirred_init(struct net *net, struct nlattr *nla, |
66 | struct nlattr *est, struct tc_action *a, int ovr, | 66 | struct nlattr *est, struct tc_action *a, int ovr, |
67 | int bind) | 67 | int bind) |
68 | { | 68 | { |
69 | struct nlattr *tb[TCA_MIRRED_MAX + 1]; | 69 | struct nlattr *tb[TCA_MIRRED_MAX + 1]; |
70 | struct tc_mirred *parm; | 70 | struct tc_mirred *parm; |
71 | struct tcf_mirred *m; | 71 | struct tcf_mirred *m; |
72 | struct tcf_common *pc; | 72 | struct tcf_common *pc; |
73 | struct net_device *dev; | 73 | struct net_device *dev; |
74 | int ret, ok_push = 0; | 74 | int ret, ok_push = 0; |
75 | 75 | ||
76 | if (nla == NULL) | 76 | if (nla == NULL) |
77 | return -EINVAL; | 77 | return -EINVAL; |
78 | ret = nla_parse_nested(tb, TCA_MIRRED_MAX, nla, mirred_policy); | 78 | ret = nla_parse_nested(tb, TCA_MIRRED_MAX, nla, mirred_policy); |
79 | if (ret < 0) | 79 | if (ret < 0) |
80 | return ret; | 80 | return ret; |
81 | if (tb[TCA_MIRRED_PARMS] == NULL) | 81 | if (tb[TCA_MIRRED_PARMS] == NULL) |
82 | return -EINVAL; | 82 | return -EINVAL; |
83 | parm = nla_data(tb[TCA_MIRRED_PARMS]); | 83 | parm = nla_data(tb[TCA_MIRRED_PARMS]); |
84 | switch (parm->eaction) { | 84 | switch (parm->eaction) { |
85 | case TCA_EGRESS_MIRROR: | 85 | case TCA_EGRESS_MIRROR: |
86 | case TCA_EGRESS_REDIR: | 86 | case TCA_EGRESS_REDIR: |
87 | break; | 87 | break; |
88 | default: | 88 | default: |
89 | return -EINVAL; | 89 | return -EINVAL; |
90 | } | 90 | } |
91 | if (parm->ifindex) { | 91 | if (parm->ifindex) { |
92 | dev = __dev_get_by_index(net, parm->ifindex); | 92 | dev = __dev_get_by_index(net, parm->ifindex); |
93 | if (dev == NULL) | 93 | if (dev == NULL) |
94 | return -ENODEV; | 94 | return -ENODEV; |
95 | switch (dev->type) { | 95 | switch (dev->type) { |
96 | case ARPHRD_TUNNEL: | 96 | case ARPHRD_TUNNEL: |
97 | case ARPHRD_TUNNEL6: | 97 | case ARPHRD_TUNNEL6: |
98 | case ARPHRD_SIT: | 98 | case ARPHRD_SIT: |
99 | case ARPHRD_IPGRE: | 99 | case ARPHRD_IPGRE: |
100 | case ARPHRD_VOID: | 100 | case ARPHRD_VOID: |
101 | case ARPHRD_NONE: | 101 | case ARPHRD_NONE: |
102 | ok_push = 0; | 102 | ok_push = 0; |
103 | break; | 103 | break; |
104 | default: | 104 | default: |
105 | ok_push = 1; | 105 | ok_push = 1; |
106 | break; | 106 | break; |
107 | } | 107 | } |
108 | } else { | 108 | } else { |
109 | dev = NULL; | 109 | dev = NULL; |
110 | } | 110 | } |
111 | 111 | ||
112 | pc = tcf_hash_check(parm->index, a, bind, &mirred_hash_info); | 112 | pc = tcf_hash_check(parm->index, a, bind, &mirred_hash_info); |
113 | if (!pc) { | 113 | if (!pc) { |
114 | if (dev == NULL) | 114 | if (dev == NULL) |
115 | return -EINVAL; | 115 | return -EINVAL; |
116 | pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind, | 116 | pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind, |
117 | &mirred_idx_gen, &mirred_hash_info); | 117 | &mirred_idx_gen, &mirred_hash_info); |
118 | if (IS_ERR(pc)) | 118 | if (IS_ERR(pc)) |
119 | return PTR_ERR(pc); | 119 | return PTR_ERR(pc); |
120 | ret = ACT_P_CREATED; | 120 | ret = ACT_P_CREATED; |
121 | } else { | 121 | } else { |
122 | if (!ovr) { | 122 | if (!ovr) { |
123 | tcf_mirred_release(to_mirred(pc), bind); | 123 | tcf_mirred_release(to_mirred(pc), bind); |
124 | return -EEXIST; | 124 | return -EEXIST; |
125 | } | 125 | } |
126 | } | 126 | } |
127 | m = to_mirred(pc); | 127 | m = to_mirred(pc); |
128 | 128 | ||
129 | spin_lock_bh(&m->tcf_lock); | 129 | spin_lock_bh(&m->tcf_lock); |
130 | m->tcf_action = parm->action; | 130 | m->tcf_action = parm->action; |
131 | m->tcfm_eaction = parm->eaction; | 131 | m->tcfm_eaction = parm->eaction; |
132 | if (dev != NULL) { | 132 | if (dev != NULL) { |
133 | m->tcfm_ifindex = parm->ifindex; | 133 | m->tcfm_ifindex = parm->ifindex; |
134 | if (ret != ACT_P_CREATED) | 134 | if (ret != ACT_P_CREATED) |
135 | dev_put(m->tcfm_dev); | 135 | dev_put(m->tcfm_dev); |
136 | dev_hold(dev); | 136 | dev_hold(dev); |
137 | m->tcfm_dev = dev; | 137 | m->tcfm_dev = dev; |
138 | m->tcfm_ok_push = ok_push; | 138 | m->tcfm_ok_push = ok_push; |
139 | } | 139 | } |
140 | spin_unlock_bh(&m->tcf_lock); | 140 | spin_unlock_bh(&m->tcf_lock); |
141 | if (ret == ACT_P_CREATED) { | 141 | if (ret == ACT_P_CREATED) { |
142 | list_add(&m->tcfm_list, &mirred_list); | 142 | list_add(&m->tcfm_list, &mirred_list); |
143 | tcf_hash_insert(pc, &mirred_hash_info); | 143 | tcf_hash_insert(pc, &mirred_hash_info); |
144 | } | 144 | } |
145 | 145 | ||
146 | return ret; | 146 | return ret; |
147 | } | 147 | } |
148 | 148 | ||
149 | static int tcf_mirred_cleanup(struct tc_action *a, int bind) | 149 | static int tcf_mirred_cleanup(struct tc_action *a, int bind) |
150 | { | 150 | { |
151 | struct tcf_mirred *m = a->priv; | 151 | struct tcf_mirred *m = a->priv; |
152 | 152 | ||
153 | if (m) | 153 | if (m) |
154 | return tcf_mirred_release(m, bind); | 154 | return tcf_mirred_release(m, bind); |
155 | return 0; | 155 | return 0; |
156 | } | 156 | } |
157 | 157 | ||
158 | static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a, | 158 | static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a, |
159 | struct tcf_result *res) | 159 | struct tcf_result *res) |
160 | { | 160 | { |
161 | struct tcf_mirred *m = a->priv; | 161 | struct tcf_mirred *m = a->priv; |
162 | struct net_device *dev; | 162 | struct net_device *dev; |
163 | struct sk_buff *skb2; | 163 | struct sk_buff *skb2; |
164 | u32 at; | 164 | u32 at; |
165 | int retval, err = 1; | 165 | int retval, err = 1; |
166 | 166 | ||
167 | spin_lock(&m->tcf_lock); | 167 | spin_lock(&m->tcf_lock); |
168 | m->tcf_tm.lastuse = jiffies; | 168 | m->tcf_tm.lastuse = jiffies; |
169 | bstats_update(&m->tcf_bstats, skb); | 169 | bstats_update(&m->tcf_bstats, skb); |
170 | 170 | ||
171 | dev = m->tcfm_dev; | 171 | dev = m->tcfm_dev; |
172 | if (!dev) { | 172 | if (!dev) { |
173 | printk_once(KERN_NOTICE "tc mirred: target device is gone\n"); | 173 | printk_once(KERN_NOTICE "tc mirred: target device is gone\n"); |
174 | goto out; | 174 | goto out; |
175 | } | 175 | } |
176 | 176 | ||
177 | if (!(dev->flags & IFF_UP)) { | 177 | if (!(dev->flags & IFF_UP)) { |
178 | net_notice_ratelimited("tc mirred to Houston: device %s is down\n", | 178 | net_notice_ratelimited("tc mirred to Houston: device %s is down\n", |
179 | dev->name); | 179 | dev->name); |
180 | goto out; | 180 | goto out; |
181 | } | 181 | } |
182 | 182 | ||
183 | at = G_TC_AT(skb->tc_verd); | 183 | at = G_TC_AT(skb->tc_verd); |
184 | skb2 = skb_act_clone(skb, GFP_ATOMIC, m->tcf_action); | 184 | skb2 = skb_act_clone(skb, GFP_ATOMIC, m->tcf_action); |
185 | if (skb2 == NULL) | 185 | if (skb2 == NULL) |
186 | goto out; | 186 | goto out; |
187 | 187 | ||
188 | if (!(at & AT_EGRESS)) { | 188 | if (!(at & AT_EGRESS)) { |
189 | if (m->tcfm_ok_push) | 189 | if (m->tcfm_ok_push) |
190 | skb_push(skb2, skb2->dev->hard_header_len); | 190 | skb_push(skb2, skb2->dev->hard_header_len); |
191 | } | 191 | } |
192 | 192 | ||
193 | /* mirror is always swallowed */ | 193 | /* mirror is always swallowed */ |
194 | if (m->tcfm_eaction != TCA_EGRESS_MIRROR) | 194 | if (m->tcfm_eaction != TCA_EGRESS_MIRROR) |
195 | skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at); | 195 | skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at); |
196 | 196 | ||
197 | skb2->skb_iif = skb->dev->ifindex; | 197 | skb2->skb_iif = skb->dev->ifindex; |
198 | skb2->dev = dev; | 198 | skb2->dev = dev; |
199 | err = dev_queue_xmit(skb2); | 199 | err = dev_queue_xmit(skb2); |
200 | 200 | ||
201 | out: | 201 | out: |
202 | if (err) { | 202 | if (err) { |
203 | m->tcf_qstats.overlimits++; | 203 | m->tcf_qstats.overlimits++; |
204 | if (m->tcfm_eaction != TCA_EGRESS_MIRROR) | 204 | if (m->tcfm_eaction != TCA_EGRESS_MIRROR) |
205 | retval = TC_ACT_SHOT; | 205 | retval = TC_ACT_SHOT; |
206 | else | 206 | else |
207 | retval = m->tcf_action; | 207 | retval = m->tcf_action; |
208 | } else | 208 | } else |
209 | retval = m->tcf_action; | 209 | retval = m->tcf_action; |
210 | spin_unlock(&m->tcf_lock); | 210 | spin_unlock(&m->tcf_lock); |
211 | 211 | ||
212 | return retval; | 212 | return retval; |
213 | } | 213 | } |
214 | 214 | ||
215 | static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) | 215 | static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) |
216 | { | 216 | { |
217 | unsigned char *b = skb_tail_pointer(skb); | 217 | unsigned char *b = skb_tail_pointer(skb); |
218 | struct tcf_mirred *m = a->priv; | 218 | struct tcf_mirred *m = a->priv; |
219 | struct tc_mirred opt = { | 219 | struct tc_mirred opt = { |
220 | .index = m->tcf_index, | 220 | .index = m->tcf_index, |
221 | .action = m->tcf_action, | 221 | .action = m->tcf_action, |
222 | .refcnt = m->tcf_refcnt - ref, | 222 | .refcnt = m->tcf_refcnt - ref, |
223 | .bindcnt = m->tcf_bindcnt - bind, | 223 | .bindcnt = m->tcf_bindcnt - bind, |
224 | .eaction = m->tcfm_eaction, | 224 | .eaction = m->tcfm_eaction, |
225 | .ifindex = m->tcfm_ifindex, | 225 | .ifindex = m->tcfm_ifindex, |
226 | }; | 226 | }; |
227 | struct tcf_t t; | 227 | struct tcf_t t; |
228 | 228 | ||
229 | if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt)) | 229 | if (nla_put(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt)) |
230 | goto nla_put_failure; | 230 | goto nla_put_failure; |
231 | t.install = jiffies_to_clock_t(jiffies - m->tcf_tm.install); | 231 | t.install = jiffies_to_clock_t(jiffies - m->tcf_tm.install); |
232 | t.lastuse = jiffies_to_clock_t(jiffies - m->tcf_tm.lastuse); | 232 | t.lastuse = jiffies_to_clock_t(jiffies - m->tcf_tm.lastuse); |
233 | t.expires = jiffies_to_clock_t(m->tcf_tm.expires); | 233 | t.expires = jiffies_to_clock_t(m->tcf_tm.expires); |
234 | if (nla_put(skb, TCA_MIRRED_TM, sizeof(t), &t)) | 234 | if (nla_put(skb, TCA_MIRRED_TM, sizeof(t), &t)) |
235 | goto nla_put_failure; | 235 | goto nla_put_failure; |
236 | return skb->len; | 236 | return skb->len; |
237 | 237 | ||
238 | nla_put_failure: | 238 | nla_put_failure: |
239 | nlmsg_trim(skb, b); | 239 | nlmsg_trim(skb, b); |
240 | return -1; | 240 | return -1; |
241 | } | 241 | } |
242 | 242 | ||
243 | static int mirred_device_event(struct notifier_block *unused, | 243 | static int mirred_device_event(struct notifier_block *unused, |
244 | unsigned long event, void *ptr) | 244 | unsigned long event, void *ptr) |
245 | { | 245 | { |
246 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | 246 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
247 | struct tcf_mirred *m; | 247 | struct tcf_mirred *m; |
248 | 248 | ||
249 | if (event == NETDEV_UNREGISTER) | 249 | if (event == NETDEV_UNREGISTER) |
250 | list_for_each_entry(m, &mirred_list, tcfm_list) { | 250 | list_for_each_entry(m, &mirred_list, tcfm_list) { |
251 | if (m->tcfm_dev == dev) { | 251 | if (m->tcfm_dev == dev) { |
252 | dev_put(dev); | 252 | dev_put(dev); |
253 | m->tcfm_dev = NULL; | 253 | m->tcfm_dev = NULL; |
254 | } | 254 | } |
255 | } | 255 | } |
256 | 256 | ||
257 | return NOTIFY_DONE; | 257 | return NOTIFY_DONE; |
258 | } | 258 | } |
259 | 259 | ||
260 | static struct notifier_block mirred_device_notifier = { | 260 | static struct notifier_block mirred_device_notifier = { |
261 | .notifier_call = mirred_device_event, | 261 | .notifier_call = mirred_device_event, |
262 | }; | 262 | }; |
263 | 263 | ||
264 | 264 | ||
265 | static struct tc_action_ops act_mirred_ops = { | 265 | static struct tc_action_ops act_mirred_ops = { |
266 | .kind = "mirred", | 266 | .kind = "mirred", |
267 | .hinfo = &mirred_hash_info, | 267 | .hinfo = &mirred_hash_info, |
268 | .type = TCA_ACT_MIRRED, | 268 | .type = TCA_ACT_MIRRED, |
269 | .capab = TCA_CAP_NONE, | 269 | .capab = TCA_CAP_NONE, |
270 | .owner = THIS_MODULE, | 270 | .owner = THIS_MODULE, |
271 | .act = tcf_mirred, | 271 | .act = tcf_mirred, |
272 | .dump = tcf_mirred_dump, | 272 | .dump = tcf_mirred_dump, |
273 | .cleanup = tcf_mirred_cleanup, | 273 | .cleanup = tcf_mirred_cleanup, |
274 | .init = tcf_mirred_init, | 274 | .init = tcf_mirred_init, |
275 | .walk = tcf_generic_walker | ||
276 | }; | 275 | }; |
277 | 276 | ||
278 | MODULE_AUTHOR("Jamal Hadi Salim(2002)"); | 277 | MODULE_AUTHOR("Jamal Hadi Salim(2002)"); |
279 | MODULE_DESCRIPTION("Device Mirror/redirect actions"); | 278 | MODULE_DESCRIPTION("Device Mirror/redirect actions"); |
280 | MODULE_LICENSE("GPL"); | 279 | MODULE_LICENSE("GPL"); |
281 | 280 | ||
282 | static int __init mirred_init_module(void) | 281 | static int __init mirred_init_module(void) |
283 | { | 282 | { |
284 | int err = register_netdevice_notifier(&mirred_device_notifier); | 283 | int err = register_netdevice_notifier(&mirred_device_notifier); |
285 | if (err) | 284 | if (err) |
286 | return err; | 285 | return err; |
287 | 286 | ||
288 | pr_info("Mirror/redirect action on\n"); | 287 | pr_info("Mirror/redirect action on\n"); |
289 | return tcf_register_action(&act_mirred_ops); | 288 | return tcf_register_action(&act_mirred_ops); |
290 | } | 289 | } |
291 | 290 | ||
292 | static void __exit mirred_cleanup_module(void) | 291 | static void __exit mirred_cleanup_module(void) |
293 | { | 292 | { |
294 | unregister_netdevice_notifier(&mirred_device_notifier); | 293 | unregister_netdevice_notifier(&mirred_device_notifier); |
295 | tcf_unregister_action(&act_mirred_ops); | 294 | tcf_unregister_action(&act_mirred_ops); |
296 | } | 295 | } |
297 | 296 | ||
298 | module_init(mirred_init_module); | 297 | module_init(mirred_init_module); |
299 | module_exit(mirred_cleanup_module); | 298 | module_exit(mirred_cleanup_module); |
300 | 299 |
net/sched/act_nat.c
1 | /* | 1 | /* |
2 | * Stateless NAT actions | 2 | * Stateless NAT actions |
3 | * | 3 | * |
4 | * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au> | 4 | * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the Free | 7 | * under the terms of the GNU General Public License as published by the Free |
8 | * Software Foundation; either version 2 of the License, or (at your option) | 8 | * Software Foundation; either version 2 of the License, or (at your option) |
9 | * any later version. | 9 | * any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/errno.h> | 12 | #include <linux/errno.h> |
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/netfilter.h> | 16 | #include <linux/netfilter.h> |
17 | #include <linux/rtnetlink.h> | 17 | #include <linux/rtnetlink.h> |
18 | #include <linux/skbuff.h> | 18 | #include <linux/skbuff.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/spinlock.h> | 20 | #include <linux/spinlock.h> |
21 | #include <linux/string.h> | 21 | #include <linux/string.h> |
22 | #include <linux/tc_act/tc_nat.h> | 22 | #include <linux/tc_act/tc_nat.h> |
23 | #include <net/act_api.h> | 23 | #include <net/act_api.h> |
24 | #include <net/icmp.h> | 24 | #include <net/icmp.h> |
25 | #include <net/ip.h> | 25 | #include <net/ip.h> |
26 | #include <net/netlink.h> | 26 | #include <net/netlink.h> |
27 | #include <net/tc_act/tc_nat.h> | 27 | #include <net/tc_act/tc_nat.h> |
28 | #include <net/tcp.h> | 28 | #include <net/tcp.h> |
29 | #include <net/udp.h> | 29 | #include <net/udp.h> |
30 | 30 | ||
31 | 31 | ||
32 | #define NAT_TAB_MASK 15 | 32 | #define NAT_TAB_MASK 15 |
33 | static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1]; | 33 | static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1]; |
34 | static u32 nat_idx_gen; | 34 | static u32 nat_idx_gen; |
35 | static DEFINE_RWLOCK(nat_lock); | 35 | static DEFINE_RWLOCK(nat_lock); |
36 | 36 | ||
37 | static struct tcf_hashinfo nat_hash_info = { | 37 | static struct tcf_hashinfo nat_hash_info = { |
38 | .htab = tcf_nat_ht, | 38 | .htab = tcf_nat_ht, |
39 | .hmask = NAT_TAB_MASK, | 39 | .hmask = NAT_TAB_MASK, |
40 | .lock = &nat_lock, | 40 | .lock = &nat_lock, |
41 | }; | 41 | }; |
42 | 42 | ||
43 | static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = { | 43 | static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = { |
44 | [TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) }, | 44 | [TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) }, |
45 | }; | 45 | }; |
46 | 46 | ||
47 | static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, | 47 | static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, |
48 | struct tc_action *a, int ovr, int bind) | 48 | struct tc_action *a, int ovr, int bind) |
49 | { | 49 | { |
50 | struct nlattr *tb[TCA_NAT_MAX + 1]; | 50 | struct nlattr *tb[TCA_NAT_MAX + 1]; |
51 | struct tc_nat *parm; | 51 | struct tc_nat *parm; |
52 | int ret = 0, err; | 52 | int ret = 0, err; |
53 | struct tcf_nat *p; | 53 | struct tcf_nat *p; |
54 | struct tcf_common *pc; | 54 | struct tcf_common *pc; |
55 | 55 | ||
56 | if (nla == NULL) | 56 | if (nla == NULL) |
57 | return -EINVAL; | 57 | return -EINVAL; |
58 | 58 | ||
59 | err = nla_parse_nested(tb, TCA_NAT_MAX, nla, nat_policy); | 59 | err = nla_parse_nested(tb, TCA_NAT_MAX, nla, nat_policy); |
60 | if (err < 0) | 60 | if (err < 0) |
61 | return err; | 61 | return err; |
62 | 62 | ||
63 | if (tb[TCA_NAT_PARMS] == NULL) | 63 | if (tb[TCA_NAT_PARMS] == NULL) |
64 | return -EINVAL; | 64 | return -EINVAL; |
65 | parm = nla_data(tb[TCA_NAT_PARMS]); | 65 | parm = nla_data(tb[TCA_NAT_PARMS]); |
66 | 66 | ||
67 | pc = tcf_hash_check(parm->index, a, bind, &nat_hash_info); | 67 | pc = tcf_hash_check(parm->index, a, bind, &nat_hash_info); |
68 | if (!pc) { | 68 | if (!pc) { |
69 | pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, | 69 | pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, |
70 | &nat_idx_gen, &nat_hash_info); | 70 | &nat_idx_gen, &nat_hash_info); |
71 | if (IS_ERR(pc)) | 71 | if (IS_ERR(pc)) |
72 | return PTR_ERR(pc); | 72 | return PTR_ERR(pc); |
73 | p = to_tcf_nat(pc); | 73 | p = to_tcf_nat(pc); |
74 | ret = ACT_P_CREATED; | 74 | ret = ACT_P_CREATED; |
75 | } else { | 75 | } else { |
76 | p = to_tcf_nat(pc); | 76 | p = to_tcf_nat(pc); |
77 | if (!ovr) { | 77 | if (!ovr) { |
78 | tcf_hash_release(pc, bind, &nat_hash_info); | 78 | tcf_hash_release(pc, bind, &nat_hash_info); |
79 | return -EEXIST; | 79 | return -EEXIST; |
80 | } | 80 | } |
81 | } | 81 | } |
82 | 82 | ||
83 | spin_lock_bh(&p->tcf_lock); | 83 | spin_lock_bh(&p->tcf_lock); |
84 | p->old_addr = parm->old_addr; | 84 | p->old_addr = parm->old_addr; |
85 | p->new_addr = parm->new_addr; | 85 | p->new_addr = parm->new_addr; |
86 | p->mask = parm->mask; | 86 | p->mask = parm->mask; |
87 | p->flags = parm->flags; | 87 | p->flags = parm->flags; |
88 | 88 | ||
89 | p->tcf_action = parm->action; | 89 | p->tcf_action = parm->action; |
90 | spin_unlock_bh(&p->tcf_lock); | 90 | spin_unlock_bh(&p->tcf_lock); |
91 | 91 | ||
92 | if (ret == ACT_P_CREATED) | 92 | if (ret == ACT_P_CREATED) |
93 | tcf_hash_insert(pc, &nat_hash_info); | 93 | tcf_hash_insert(pc, &nat_hash_info); |
94 | 94 | ||
95 | return ret; | 95 | return ret; |
96 | } | 96 | } |
97 | 97 | ||
98 | static int tcf_nat_cleanup(struct tc_action *a, int bind) | 98 | static int tcf_nat_cleanup(struct tc_action *a, int bind) |
99 | { | 99 | { |
100 | struct tcf_nat *p = a->priv; | 100 | struct tcf_nat *p = a->priv; |
101 | 101 | ||
102 | return tcf_hash_release(&p->common, bind, &nat_hash_info); | 102 | return tcf_hash_release(&p->common, bind, &nat_hash_info); |
103 | } | 103 | } |
104 | 104 | ||
105 | static int tcf_nat(struct sk_buff *skb, const struct tc_action *a, | 105 | static int tcf_nat(struct sk_buff *skb, const struct tc_action *a, |
106 | struct tcf_result *res) | 106 | struct tcf_result *res) |
107 | { | 107 | { |
108 | struct tcf_nat *p = a->priv; | 108 | struct tcf_nat *p = a->priv; |
109 | struct iphdr *iph; | 109 | struct iphdr *iph; |
110 | __be32 old_addr; | 110 | __be32 old_addr; |
111 | __be32 new_addr; | 111 | __be32 new_addr; |
112 | __be32 mask; | 112 | __be32 mask; |
113 | __be32 addr; | 113 | __be32 addr; |
114 | int egress; | 114 | int egress; |
115 | int action; | 115 | int action; |
116 | int ihl; | 116 | int ihl; |
117 | int noff; | 117 | int noff; |
118 | 118 | ||
119 | spin_lock(&p->tcf_lock); | 119 | spin_lock(&p->tcf_lock); |
120 | 120 | ||
121 | p->tcf_tm.lastuse = jiffies; | 121 | p->tcf_tm.lastuse = jiffies; |
122 | old_addr = p->old_addr; | 122 | old_addr = p->old_addr; |
123 | new_addr = p->new_addr; | 123 | new_addr = p->new_addr; |
124 | mask = p->mask; | 124 | mask = p->mask; |
125 | egress = p->flags & TCA_NAT_FLAG_EGRESS; | 125 | egress = p->flags & TCA_NAT_FLAG_EGRESS; |
126 | action = p->tcf_action; | 126 | action = p->tcf_action; |
127 | 127 | ||
128 | bstats_update(&p->tcf_bstats, skb); | 128 | bstats_update(&p->tcf_bstats, skb); |
129 | 129 | ||
130 | spin_unlock(&p->tcf_lock); | 130 | spin_unlock(&p->tcf_lock); |
131 | 131 | ||
132 | if (unlikely(action == TC_ACT_SHOT)) | 132 | if (unlikely(action == TC_ACT_SHOT)) |
133 | goto drop; | 133 | goto drop; |
134 | 134 | ||
135 | noff = skb_network_offset(skb); | 135 | noff = skb_network_offset(skb); |
136 | if (!pskb_may_pull(skb, sizeof(*iph) + noff)) | 136 | if (!pskb_may_pull(skb, sizeof(*iph) + noff)) |
137 | goto drop; | 137 | goto drop; |
138 | 138 | ||
139 | iph = ip_hdr(skb); | 139 | iph = ip_hdr(skb); |
140 | 140 | ||
141 | if (egress) | 141 | if (egress) |
142 | addr = iph->saddr; | 142 | addr = iph->saddr; |
143 | else | 143 | else |
144 | addr = iph->daddr; | 144 | addr = iph->daddr; |
145 | 145 | ||
146 | if (!((old_addr ^ addr) & mask)) { | 146 | if (!((old_addr ^ addr) & mask)) { |
147 | if (skb_cloned(skb) && | 147 | if (skb_cloned(skb) && |
148 | !skb_clone_writable(skb, sizeof(*iph) + noff) && | 148 | !skb_clone_writable(skb, sizeof(*iph) + noff) && |
149 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) | 149 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) |
150 | goto drop; | 150 | goto drop; |
151 | 151 | ||
152 | new_addr &= mask; | 152 | new_addr &= mask; |
153 | new_addr |= addr & ~mask; | 153 | new_addr |= addr & ~mask; |
154 | 154 | ||
155 | /* Rewrite IP header */ | 155 | /* Rewrite IP header */ |
156 | iph = ip_hdr(skb); | 156 | iph = ip_hdr(skb); |
157 | if (egress) | 157 | if (egress) |
158 | iph->saddr = new_addr; | 158 | iph->saddr = new_addr; |
159 | else | 159 | else |
160 | iph->daddr = new_addr; | 160 | iph->daddr = new_addr; |
161 | 161 | ||
162 | csum_replace4(&iph->check, addr, new_addr); | 162 | csum_replace4(&iph->check, addr, new_addr); |
163 | } else if ((iph->frag_off & htons(IP_OFFSET)) || | 163 | } else if ((iph->frag_off & htons(IP_OFFSET)) || |
164 | iph->protocol != IPPROTO_ICMP) { | 164 | iph->protocol != IPPROTO_ICMP) { |
165 | goto out; | 165 | goto out; |
166 | } | 166 | } |
167 | 167 | ||
168 | ihl = iph->ihl * 4; | 168 | ihl = iph->ihl * 4; |
169 | 169 | ||
170 | /* It would be nice to share code with stateful NAT. */ | 170 | /* It would be nice to share code with stateful NAT. */ |
171 | switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) { | 171 | switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) { |
172 | case IPPROTO_TCP: | 172 | case IPPROTO_TCP: |
173 | { | 173 | { |
174 | struct tcphdr *tcph; | 174 | struct tcphdr *tcph; |
175 | 175 | ||
176 | if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) || | 176 | if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) || |
177 | (skb_cloned(skb) && | 177 | (skb_cloned(skb) && |
178 | !skb_clone_writable(skb, ihl + sizeof(*tcph) + noff) && | 178 | !skb_clone_writable(skb, ihl + sizeof(*tcph) + noff) && |
179 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) | 179 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) |
180 | goto drop; | 180 | goto drop; |
181 | 181 | ||
182 | tcph = (void *)(skb_network_header(skb) + ihl); | 182 | tcph = (void *)(skb_network_header(skb) + ihl); |
183 | inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, 1); | 183 | inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, 1); |
184 | break; | 184 | break; |
185 | } | 185 | } |
186 | case IPPROTO_UDP: | 186 | case IPPROTO_UDP: |
187 | { | 187 | { |
188 | struct udphdr *udph; | 188 | struct udphdr *udph; |
189 | 189 | ||
190 | if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) || | 190 | if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) || |
191 | (skb_cloned(skb) && | 191 | (skb_cloned(skb) && |
192 | !skb_clone_writable(skb, ihl + sizeof(*udph) + noff) && | 192 | !skb_clone_writable(skb, ihl + sizeof(*udph) + noff) && |
193 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) | 193 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) |
194 | goto drop; | 194 | goto drop; |
195 | 195 | ||
196 | udph = (void *)(skb_network_header(skb) + ihl); | 196 | udph = (void *)(skb_network_header(skb) + ihl); |
197 | if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) { | 197 | if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) { |
198 | inet_proto_csum_replace4(&udph->check, skb, addr, | 198 | inet_proto_csum_replace4(&udph->check, skb, addr, |
199 | new_addr, 1); | 199 | new_addr, 1); |
200 | if (!udph->check) | 200 | if (!udph->check) |
201 | udph->check = CSUM_MANGLED_0; | 201 | udph->check = CSUM_MANGLED_0; |
202 | } | 202 | } |
203 | break; | 203 | break; |
204 | } | 204 | } |
205 | case IPPROTO_ICMP: | 205 | case IPPROTO_ICMP: |
206 | { | 206 | { |
207 | struct icmphdr *icmph; | 207 | struct icmphdr *icmph; |
208 | 208 | ||
209 | if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff)) | 209 | if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff)) |
210 | goto drop; | 210 | goto drop; |
211 | 211 | ||
212 | icmph = (void *)(skb_network_header(skb) + ihl); | 212 | icmph = (void *)(skb_network_header(skb) + ihl); |
213 | 213 | ||
214 | if ((icmph->type != ICMP_DEST_UNREACH) && | 214 | if ((icmph->type != ICMP_DEST_UNREACH) && |
215 | (icmph->type != ICMP_TIME_EXCEEDED) && | 215 | (icmph->type != ICMP_TIME_EXCEEDED) && |
216 | (icmph->type != ICMP_PARAMETERPROB)) | 216 | (icmph->type != ICMP_PARAMETERPROB)) |
217 | break; | 217 | break; |
218 | 218 | ||
219 | if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) + | 219 | if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) + |
220 | noff)) | 220 | noff)) |
221 | goto drop; | 221 | goto drop; |
222 | 222 | ||
223 | icmph = (void *)(skb_network_header(skb) + ihl); | 223 | icmph = (void *)(skb_network_header(skb) + ihl); |
224 | iph = (void *)(icmph + 1); | 224 | iph = (void *)(icmph + 1); |
225 | if (egress) | 225 | if (egress) |
226 | addr = iph->daddr; | 226 | addr = iph->daddr; |
227 | else | 227 | else |
228 | addr = iph->saddr; | 228 | addr = iph->saddr; |
229 | 229 | ||
230 | if ((old_addr ^ addr) & mask) | 230 | if ((old_addr ^ addr) & mask) |
231 | break; | 231 | break; |
232 | 232 | ||
233 | if (skb_cloned(skb) && | 233 | if (skb_cloned(skb) && |
234 | !skb_clone_writable(skb, ihl + sizeof(*icmph) + | 234 | !skb_clone_writable(skb, ihl + sizeof(*icmph) + |
235 | sizeof(*iph) + noff) && | 235 | sizeof(*iph) + noff) && |
236 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) | 236 | pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) |
237 | goto drop; | 237 | goto drop; |
238 | 238 | ||
239 | icmph = (void *)(skb_network_header(skb) + ihl); | 239 | icmph = (void *)(skb_network_header(skb) + ihl); |
240 | iph = (void *)(icmph + 1); | 240 | iph = (void *)(icmph + 1); |
241 | 241 | ||
242 | new_addr &= mask; | 242 | new_addr &= mask; |
243 | new_addr |= addr & ~mask; | 243 | new_addr |= addr & ~mask; |
244 | 244 | ||
245 | /* XXX Fix up the inner checksums. */ | 245 | /* XXX Fix up the inner checksums. */ |
246 | if (egress) | 246 | if (egress) |
247 | iph->daddr = new_addr; | 247 | iph->daddr = new_addr; |
248 | else | 248 | else |
249 | iph->saddr = new_addr; | 249 | iph->saddr = new_addr; |
250 | 250 | ||
251 | inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr, | 251 | inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr, |
252 | 0); | 252 | 0); |
253 | break; | 253 | break; |
254 | } | 254 | } |
255 | default: | 255 | default: |
256 | break; | 256 | break; |
257 | } | 257 | } |
258 | 258 | ||
259 | out: | 259 | out: |
260 | return action; | 260 | return action; |
261 | 261 | ||
262 | drop: | 262 | drop: |
263 | spin_lock(&p->tcf_lock); | 263 | spin_lock(&p->tcf_lock); |
264 | p->tcf_qstats.drops++; | 264 | p->tcf_qstats.drops++; |
265 | spin_unlock(&p->tcf_lock); | 265 | spin_unlock(&p->tcf_lock); |
266 | return TC_ACT_SHOT; | 266 | return TC_ACT_SHOT; |
267 | } | 267 | } |
268 | 268 | ||
269 | static int tcf_nat_dump(struct sk_buff *skb, struct tc_action *a, | 269 | static int tcf_nat_dump(struct sk_buff *skb, struct tc_action *a, |
270 | int bind, int ref) | 270 | int bind, int ref) |
271 | { | 271 | { |
272 | unsigned char *b = skb_tail_pointer(skb); | 272 | unsigned char *b = skb_tail_pointer(skb); |
273 | struct tcf_nat *p = a->priv; | 273 | struct tcf_nat *p = a->priv; |
274 | struct tc_nat opt = { | 274 | struct tc_nat opt = { |
275 | .old_addr = p->old_addr, | 275 | .old_addr = p->old_addr, |
276 | .new_addr = p->new_addr, | 276 | .new_addr = p->new_addr, |
277 | .mask = p->mask, | 277 | .mask = p->mask, |
278 | .flags = p->flags, | 278 | .flags = p->flags, |
279 | 279 | ||
280 | .index = p->tcf_index, | 280 | .index = p->tcf_index, |
281 | .action = p->tcf_action, | 281 | .action = p->tcf_action, |
282 | .refcnt = p->tcf_refcnt - ref, | 282 | .refcnt = p->tcf_refcnt - ref, |
283 | .bindcnt = p->tcf_bindcnt - bind, | 283 | .bindcnt = p->tcf_bindcnt - bind, |
284 | }; | 284 | }; |
285 | struct tcf_t t; | 285 | struct tcf_t t; |
286 | 286 | ||
287 | if (nla_put(skb, TCA_NAT_PARMS, sizeof(opt), &opt)) | 287 | if (nla_put(skb, TCA_NAT_PARMS, sizeof(opt), &opt)) |
288 | goto nla_put_failure; | 288 | goto nla_put_failure; |
289 | t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); | 289 | t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); |
290 | t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); | 290 | t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); |
291 | t.expires = jiffies_to_clock_t(p->tcf_tm.expires); | 291 | t.expires = jiffies_to_clock_t(p->tcf_tm.expires); |
292 | if (nla_put(skb, TCA_NAT_TM, sizeof(t), &t)) | 292 | if (nla_put(skb, TCA_NAT_TM, sizeof(t), &t)) |
293 | goto nla_put_failure; | 293 | goto nla_put_failure; |
294 | 294 | ||
295 | return skb->len; | 295 | return skb->len; |
296 | 296 | ||
297 | nla_put_failure: | 297 | nla_put_failure: |
298 | nlmsg_trim(skb, b); | 298 | nlmsg_trim(skb, b); |
299 | return -1; | 299 | return -1; |
300 | } | 300 | } |
301 | 301 | ||
302 | static struct tc_action_ops act_nat_ops = { | 302 | static struct tc_action_ops act_nat_ops = { |
303 | .kind = "nat", | 303 | .kind = "nat", |
304 | .hinfo = &nat_hash_info, | 304 | .hinfo = &nat_hash_info, |
305 | .type = TCA_ACT_NAT, | 305 | .type = TCA_ACT_NAT, |
306 | .capab = TCA_CAP_NONE, | 306 | .capab = TCA_CAP_NONE, |
307 | .owner = THIS_MODULE, | 307 | .owner = THIS_MODULE, |
308 | .act = tcf_nat, | 308 | .act = tcf_nat, |
309 | .dump = tcf_nat_dump, | 309 | .dump = tcf_nat_dump, |
310 | .cleanup = tcf_nat_cleanup, | 310 | .cleanup = tcf_nat_cleanup, |
311 | .init = tcf_nat_init, | 311 | .init = tcf_nat_init, |
312 | .walk = tcf_generic_walker | ||
313 | }; | 312 | }; |
314 | 313 | ||
315 | MODULE_DESCRIPTION("Stateless NAT actions"); | 314 | MODULE_DESCRIPTION("Stateless NAT actions"); |
316 | MODULE_LICENSE("GPL"); | 315 | MODULE_LICENSE("GPL"); |
317 | 316 | ||
318 | static int __init nat_init_module(void) | 317 | static int __init nat_init_module(void) |
319 | { | 318 | { |
320 | return tcf_register_action(&act_nat_ops); | 319 | return tcf_register_action(&act_nat_ops); |
321 | } | 320 | } |
322 | 321 | ||
323 | static void __exit nat_cleanup_module(void) | 322 | static void __exit nat_cleanup_module(void) |
324 | { | 323 | { |
325 | tcf_unregister_action(&act_nat_ops); | 324 | tcf_unregister_action(&act_nat_ops); |
326 | } | 325 | } |
327 | 326 | ||
328 | module_init(nat_init_module); | 327 | module_init(nat_init_module); |
329 | module_exit(nat_cleanup_module); | 328 | module_exit(nat_cleanup_module); |
330 | 329 |
net/sched/act_pedit.c
1 | /* | 1 | /* |
2 | * net/sched/pedit.c Generic packet editor | 2 | * net/sched/pedit.c Generic packet editor |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License | 5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; either version | 6 | * as published by the Free Software Foundation; either version |
7 | * 2 of the License, or (at your option) any later version. | 7 | * 2 of the License, or (at your option) any later version. |
8 | * | 8 | * |
9 | * Authors: Jamal Hadi Salim (2002-4) | 9 | * Authors: Jamal Hadi Salim (2002-4) |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/types.h> | 12 | #include <linux/types.h> |
13 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
14 | #include <linux/string.h> | 14 | #include <linux/string.h> |
15 | #include <linux/errno.h> | 15 | #include <linux/errno.h> |
16 | #include <linux/skbuff.h> | 16 | #include <linux/skbuff.h> |
17 | #include <linux/rtnetlink.h> | 17 | #include <linux/rtnetlink.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
21 | #include <net/netlink.h> | 21 | #include <net/netlink.h> |
22 | #include <net/pkt_sched.h> | 22 | #include <net/pkt_sched.h> |
23 | #include <linux/tc_act/tc_pedit.h> | 23 | #include <linux/tc_act/tc_pedit.h> |
24 | #include <net/tc_act/tc_pedit.h> | 24 | #include <net/tc_act/tc_pedit.h> |
25 | 25 | ||
26 | #define PEDIT_TAB_MASK 15 | 26 | #define PEDIT_TAB_MASK 15 |
27 | static struct tcf_common *tcf_pedit_ht[PEDIT_TAB_MASK + 1]; | 27 | static struct tcf_common *tcf_pedit_ht[PEDIT_TAB_MASK + 1]; |
28 | static u32 pedit_idx_gen; | 28 | static u32 pedit_idx_gen; |
29 | static DEFINE_RWLOCK(pedit_lock); | 29 | static DEFINE_RWLOCK(pedit_lock); |
30 | 30 | ||
31 | static struct tcf_hashinfo pedit_hash_info = { | 31 | static struct tcf_hashinfo pedit_hash_info = { |
32 | .htab = tcf_pedit_ht, | 32 | .htab = tcf_pedit_ht, |
33 | .hmask = PEDIT_TAB_MASK, | 33 | .hmask = PEDIT_TAB_MASK, |
34 | .lock = &pedit_lock, | 34 | .lock = &pedit_lock, |
35 | }; | 35 | }; |
36 | 36 | ||
37 | static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = { | 37 | static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = { |
38 | [TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) }, | 38 | [TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) }, |
39 | }; | 39 | }; |
40 | 40 | ||
41 | static int tcf_pedit_init(struct net *net, struct nlattr *nla, | 41 | static int tcf_pedit_init(struct net *net, struct nlattr *nla, |
42 | struct nlattr *est, struct tc_action *a, | 42 | struct nlattr *est, struct tc_action *a, |
43 | int ovr, int bind) | 43 | int ovr, int bind) |
44 | { | 44 | { |
45 | struct nlattr *tb[TCA_PEDIT_MAX + 1]; | 45 | struct nlattr *tb[TCA_PEDIT_MAX + 1]; |
46 | struct tc_pedit *parm; | 46 | struct tc_pedit *parm; |
47 | int ret = 0, err; | 47 | int ret = 0, err; |
48 | struct tcf_pedit *p; | 48 | struct tcf_pedit *p; |
49 | struct tcf_common *pc; | 49 | struct tcf_common *pc; |
50 | struct tc_pedit_key *keys = NULL; | 50 | struct tc_pedit_key *keys = NULL; |
51 | int ksize; | 51 | int ksize; |
52 | 52 | ||
53 | if (nla == NULL) | 53 | if (nla == NULL) |
54 | return -EINVAL; | 54 | return -EINVAL; |
55 | 55 | ||
56 | err = nla_parse_nested(tb, TCA_PEDIT_MAX, nla, pedit_policy); | 56 | err = nla_parse_nested(tb, TCA_PEDIT_MAX, nla, pedit_policy); |
57 | if (err < 0) | 57 | if (err < 0) |
58 | return err; | 58 | return err; |
59 | 59 | ||
60 | if (tb[TCA_PEDIT_PARMS] == NULL) | 60 | if (tb[TCA_PEDIT_PARMS] == NULL) |
61 | return -EINVAL; | 61 | return -EINVAL; |
62 | parm = nla_data(tb[TCA_PEDIT_PARMS]); | 62 | parm = nla_data(tb[TCA_PEDIT_PARMS]); |
63 | ksize = parm->nkeys * sizeof(struct tc_pedit_key); | 63 | ksize = parm->nkeys * sizeof(struct tc_pedit_key); |
64 | if (nla_len(tb[TCA_PEDIT_PARMS]) < sizeof(*parm) + ksize) | 64 | if (nla_len(tb[TCA_PEDIT_PARMS]) < sizeof(*parm) + ksize) |
65 | return -EINVAL; | 65 | return -EINVAL; |
66 | 66 | ||
67 | pc = tcf_hash_check(parm->index, a, bind, &pedit_hash_info); | 67 | pc = tcf_hash_check(parm->index, a, bind, &pedit_hash_info); |
68 | if (!pc) { | 68 | if (!pc) { |
69 | if (!parm->nkeys) | 69 | if (!parm->nkeys) |
70 | return -EINVAL; | 70 | return -EINVAL; |
71 | pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, | 71 | pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, |
72 | &pedit_idx_gen, &pedit_hash_info); | 72 | &pedit_idx_gen, &pedit_hash_info); |
73 | if (IS_ERR(pc)) | 73 | if (IS_ERR(pc)) |
74 | return PTR_ERR(pc); | 74 | return PTR_ERR(pc); |
75 | p = to_pedit(pc); | 75 | p = to_pedit(pc); |
76 | keys = kmalloc(ksize, GFP_KERNEL); | 76 | keys = kmalloc(ksize, GFP_KERNEL); |
77 | if (keys == NULL) { | 77 | if (keys == NULL) { |
78 | if (est) | 78 | if (est) |
79 | gen_kill_estimator(&pc->tcfc_bstats, | 79 | gen_kill_estimator(&pc->tcfc_bstats, |
80 | &pc->tcfc_rate_est); | 80 | &pc->tcfc_rate_est); |
81 | kfree_rcu(pc, tcfc_rcu); | 81 | kfree_rcu(pc, tcfc_rcu); |
82 | return -ENOMEM; | 82 | return -ENOMEM; |
83 | } | 83 | } |
84 | ret = ACT_P_CREATED; | 84 | ret = ACT_P_CREATED; |
85 | } else { | 85 | } else { |
86 | p = to_pedit(pc); | 86 | p = to_pedit(pc); |
87 | if (!ovr) { | 87 | if (!ovr) { |
88 | tcf_hash_release(pc, bind, &pedit_hash_info); | 88 | tcf_hash_release(pc, bind, &pedit_hash_info); |
89 | return -EEXIST; | 89 | return -EEXIST; |
90 | } | 90 | } |
91 | if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) { | 91 | if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) { |
92 | keys = kmalloc(ksize, GFP_KERNEL); | 92 | keys = kmalloc(ksize, GFP_KERNEL); |
93 | if (keys == NULL) | 93 | if (keys == NULL) |
94 | return -ENOMEM; | 94 | return -ENOMEM; |
95 | } | 95 | } |
96 | } | 96 | } |
97 | 97 | ||
98 | spin_lock_bh(&p->tcf_lock); | 98 | spin_lock_bh(&p->tcf_lock); |
99 | p->tcfp_flags = parm->flags; | 99 | p->tcfp_flags = parm->flags; |
100 | p->tcf_action = parm->action; | 100 | p->tcf_action = parm->action; |
101 | if (keys) { | 101 | if (keys) { |
102 | kfree(p->tcfp_keys); | 102 | kfree(p->tcfp_keys); |
103 | p->tcfp_keys = keys; | 103 | p->tcfp_keys = keys; |
104 | p->tcfp_nkeys = parm->nkeys; | 104 | p->tcfp_nkeys = parm->nkeys; |
105 | } | 105 | } |
106 | memcpy(p->tcfp_keys, parm->keys, ksize); | 106 | memcpy(p->tcfp_keys, parm->keys, ksize); |
107 | spin_unlock_bh(&p->tcf_lock); | 107 | spin_unlock_bh(&p->tcf_lock); |
108 | if (ret == ACT_P_CREATED) | 108 | if (ret == ACT_P_CREATED) |
109 | tcf_hash_insert(pc, &pedit_hash_info); | 109 | tcf_hash_insert(pc, &pedit_hash_info); |
110 | return ret; | 110 | return ret; |
111 | } | 111 | } |
112 | 112 | ||
113 | static int tcf_pedit_cleanup(struct tc_action *a, int bind) | 113 | static int tcf_pedit_cleanup(struct tc_action *a, int bind) |
114 | { | 114 | { |
115 | struct tcf_pedit *p = a->priv; | 115 | struct tcf_pedit *p = a->priv; |
116 | 116 | ||
117 | if (p) { | 117 | if (p) { |
118 | struct tc_pedit_key *keys = p->tcfp_keys; | 118 | struct tc_pedit_key *keys = p->tcfp_keys; |
119 | if (tcf_hash_release(&p->common, bind, &pedit_hash_info)) { | 119 | if (tcf_hash_release(&p->common, bind, &pedit_hash_info)) { |
120 | kfree(keys); | 120 | kfree(keys); |
121 | return 1; | 121 | return 1; |
122 | } | 122 | } |
123 | } | 123 | } |
124 | return 0; | 124 | return 0; |
125 | } | 125 | } |
126 | 126 | ||
127 | static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a, | 127 | static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a, |
128 | struct tcf_result *res) | 128 | struct tcf_result *res) |
129 | { | 129 | { |
130 | struct tcf_pedit *p = a->priv; | 130 | struct tcf_pedit *p = a->priv; |
131 | int i, munged = 0; | 131 | int i, munged = 0; |
132 | unsigned int off; | 132 | unsigned int off; |
133 | 133 | ||
134 | if (skb_unclone(skb, GFP_ATOMIC)) | 134 | if (skb_unclone(skb, GFP_ATOMIC)) |
135 | return p->tcf_action; | 135 | return p->tcf_action; |
136 | 136 | ||
137 | off = skb_network_offset(skb); | 137 | off = skb_network_offset(skb); |
138 | 138 | ||
139 | spin_lock(&p->tcf_lock); | 139 | spin_lock(&p->tcf_lock); |
140 | 140 | ||
141 | p->tcf_tm.lastuse = jiffies; | 141 | p->tcf_tm.lastuse = jiffies; |
142 | 142 | ||
143 | if (p->tcfp_nkeys > 0) { | 143 | if (p->tcfp_nkeys > 0) { |
144 | struct tc_pedit_key *tkey = p->tcfp_keys; | 144 | struct tc_pedit_key *tkey = p->tcfp_keys; |
145 | 145 | ||
146 | for (i = p->tcfp_nkeys; i > 0; i--, tkey++) { | 146 | for (i = p->tcfp_nkeys; i > 0; i--, tkey++) { |
147 | u32 *ptr, _data; | 147 | u32 *ptr, _data; |
148 | int offset = tkey->off; | 148 | int offset = tkey->off; |
149 | 149 | ||
150 | if (tkey->offmask) { | 150 | if (tkey->offmask) { |
151 | char *d, _d; | 151 | char *d, _d; |
152 | 152 | ||
153 | d = skb_header_pointer(skb, off + tkey->at, 1, | 153 | d = skb_header_pointer(skb, off + tkey->at, 1, |
154 | &_d); | 154 | &_d); |
155 | if (!d) | 155 | if (!d) |
156 | goto bad; | 156 | goto bad; |
157 | offset += (*d & tkey->offmask) >> tkey->shift; | 157 | offset += (*d & tkey->offmask) >> tkey->shift; |
158 | } | 158 | } |
159 | 159 | ||
160 | if (offset % 4) { | 160 | if (offset % 4) { |
161 | pr_info("tc filter pedit" | 161 | pr_info("tc filter pedit" |
162 | " offset must be on 32 bit boundaries\n"); | 162 | " offset must be on 32 bit boundaries\n"); |
163 | goto bad; | 163 | goto bad; |
164 | } | 164 | } |
165 | if (offset > 0 && offset > skb->len) { | 165 | if (offset > 0 && offset > skb->len) { |
166 | pr_info("tc filter pedit" | 166 | pr_info("tc filter pedit" |
167 | " offset %d can't exceed pkt length %d\n", | 167 | " offset %d can't exceed pkt length %d\n", |
168 | offset, skb->len); | 168 | offset, skb->len); |
169 | goto bad; | 169 | goto bad; |
170 | } | 170 | } |
171 | 171 | ||
172 | ptr = skb_header_pointer(skb, off + offset, 4, &_data); | 172 | ptr = skb_header_pointer(skb, off + offset, 4, &_data); |
173 | if (!ptr) | 173 | if (!ptr) |
174 | goto bad; | 174 | goto bad; |
175 | /* just do it, baby */ | 175 | /* just do it, baby */ |
176 | *ptr = ((*ptr & tkey->mask) ^ tkey->val); | 176 | *ptr = ((*ptr & tkey->mask) ^ tkey->val); |
177 | if (ptr == &_data) | 177 | if (ptr == &_data) |
178 | skb_store_bits(skb, off + offset, ptr, 4); | 178 | skb_store_bits(skb, off + offset, ptr, 4); |
179 | munged++; | 179 | munged++; |
180 | } | 180 | } |
181 | 181 | ||
182 | if (munged) | 182 | if (munged) |
183 | skb->tc_verd = SET_TC_MUNGED(skb->tc_verd); | 183 | skb->tc_verd = SET_TC_MUNGED(skb->tc_verd); |
184 | goto done; | 184 | goto done; |
185 | } else | 185 | } else |
186 | WARN(1, "pedit BUG: index %d\n", p->tcf_index); | 186 | WARN(1, "pedit BUG: index %d\n", p->tcf_index); |
187 | 187 | ||
188 | bad: | 188 | bad: |
189 | p->tcf_qstats.overlimits++; | 189 | p->tcf_qstats.overlimits++; |
190 | done: | 190 | done: |
191 | bstats_update(&p->tcf_bstats, skb); | 191 | bstats_update(&p->tcf_bstats, skb); |
192 | spin_unlock(&p->tcf_lock); | 192 | spin_unlock(&p->tcf_lock); |
193 | return p->tcf_action; | 193 | return p->tcf_action; |
194 | } | 194 | } |
195 | 195 | ||
196 | static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a, | 196 | static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a, |
197 | int bind, int ref) | 197 | int bind, int ref) |
198 | { | 198 | { |
199 | unsigned char *b = skb_tail_pointer(skb); | 199 | unsigned char *b = skb_tail_pointer(skb); |
200 | struct tcf_pedit *p = a->priv; | 200 | struct tcf_pedit *p = a->priv; |
201 | struct tc_pedit *opt; | 201 | struct tc_pedit *opt; |
202 | struct tcf_t t; | 202 | struct tcf_t t; |
203 | int s; | 203 | int s; |
204 | 204 | ||
205 | s = sizeof(*opt) + p->tcfp_nkeys * sizeof(struct tc_pedit_key); | 205 | s = sizeof(*opt) + p->tcfp_nkeys * sizeof(struct tc_pedit_key); |
206 | 206 | ||
207 | /* netlink spinlocks held above us - must use ATOMIC */ | 207 | /* netlink spinlocks held above us - must use ATOMIC */ |
208 | opt = kzalloc(s, GFP_ATOMIC); | 208 | opt = kzalloc(s, GFP_ATOMIC); |
209 | if (unlikely(!opt)) | 209 | if (unlikely(!opt)) |
210 | return -ENOBUFS; | 210 | return -ENOBUFS; |
211 | 211 | ||
212 | memcpy(opt->keys, p->tcfp_keys, | 212 | memcpy(opt->keys, p->tcfp_keys, |
213 | p->tcfp_nkeys * sizeof(struct tc_pedit_key)); | 213 | p->tcfp_nkeys * sizeof(struct tc_pedit_key)); |
214 | opt->index = p->tcf_index; | 214 | opt->index = p->tcf_index; |
215 | opt->nkeys = p->tcfp_nkeys; | 215 | opt->nkeys = p->tcfp_nkeys; |
216 | opt->flags = p->tcfp_flags; | 216 | opt->flags = p->tcfp_flags; |
217 | opt->action = p->tcf_action; | 217 | opt->action = p->tcf_action; |
218 | opt->refcnt = p->tcf_refcnt - ref; | 218 | opt->refcnt = p->tcf_refcnt - ref; |
219 | opt->bindcnt = p->tcf_bindcnt - bind; | 219 | opt->bindcnt = p->tcf_bindcnt - bind; |
220 | 220 | ||
221 | if (nla_put(skb, TCA_PEDIT_PARMS, s, opt)) | 221 | if (nla_put(skb, TCA_PEDIT_PARMS, s, opt)) |
222 | goto nla_put_failure; | 222 | goto nla_put_failure; |
223 | t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); | 223 | t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); |
224 | t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); | 224 | t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); |
225 | t.expires = jiffies_to_clock_t(p->tcf_tm.expires); | 225 | t.expires = jiffies_to_clock_t(p->tcf_tm.expires); |
226 | if (nla_put(skb, TCA_PEDIT_TM, sizeof(t), &t)) | 226 | if (nla_put(skb, TCA_PEDIT_TM, sizeof(t), &t)) |
227 | goto nla_put_failure; | 227 | goto nla_put_failure; |
228 | kfree(opt); | 228 | kfree(opt); |
229 | return skb->len; | 229 | return skb->len; |
230 | 230 | ||
231 | nla_put_failure: | 231 | nla_put_failure: |
232 | nlmsg_trim(skb, b); | 232 | nlmsg_trim(skb, b); |
233 | kfree(opt); | 233 | kfree(opt); |
234 | return -1; | 234 | return -1; |
235 | } | 235 | } |
236 | 236 | ||
237 | static struct tc_action_ops act_pedit_ops = { | 237 | static struct tc_action_ops act_pedit_ops = { |
238 | .kind = "pedit", | 238 | .kind = "pedit", |
239 | .hinfo = &pedit_hash_info, | 239 | .hinfo = &pedit_hash_info, |
240 | .type = TCA_ACT_PEDIT, | 240 | .type = TCA_ACT_PEDIT, |
241 | .capab = TCA_CAP_NONE, | 241 | .capab = TCA_CAP_NONE, |
242 | .owner = THIS_MODULE, | 242 | .owner = THIS_MODULE, |
243 | .act = tcf_pedit, | 243 | .act = tcf_pedit, |
244 | .dump = tcf_pedit_dump, | 244 | .dump = tcf_pedit_dump, |
245 | .cleanup = tcf_pedit_cleanup, | 245 | .cleanup = tcf_pedit_cleanup, |
246 | .init = tcf_pedit_init, | 246 | .init = tcf_pedit_init, |
247 | .walk = tcf_generic_walker | ||
248 | }; | 247 | }; |
249 | 248 | ||
250 | MODULE_AUTHOR("Jamal Hadi Salim(2002-4)"); | 249 | MODULE_AUTHOR("Jamal Hadi Salim(2002-4)"); |
251 | MODULE_DESCRIPTION("Generic Packet Editor actions"); | 250 | MODULE_DESCRIPTION("Generic Packet Editor actions"); |
252 | MODULE_LICENSE("GPL"); | 251 | MODULE_LICENSE("GPL"); |
253 | 252 | ||
254 | static int __init pedit_init_module(void) | 253 | static int __init pedit_init_module(void) |
255 | { | 254 | { |
256 | return tcf_register_action(&act_pedit_ops); | 255 | return tcf_register_action(&act_pedit_ops); |
257 | } | 256 | } |
258 | 257 | ||
259 | static void __exit pedit_cleanup_module(void) | 258 | static void __exit pedit_cleanup_module(void) |
260 | { | 259 | { |
261 | tcf_unregister_action(&act_pedit_ops); | 260 | tcf_unregister_action(&act_pedit_ops); |
262 | } | 261 | } |
263 | 262 | ||
264 | module_init(pedit_init_module); | 263 | module_init(pedit_init_module); |
265 | module_exit(pedit_cleanup_module); | 264 | module_exit(pedit_cleanup_module); |
266 | 265 | ||
267 | 266 |
net/sched/act_simple.c
1 | /* | 1 | /* |
2 | * net/sched/simp.c Simple example of an action | 2 | * net/sched/simp.c Simple example of an action |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License | 5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; either version | 6 | * as published by the Free Software Foundation; either version |
7 | * 2 of the License, or (at your option) any later version. | 7 | * 2 of the License, or (at your option) any later version. |
8 | * | 8 | * |
9 | * Authors: Jamal Hadi Salim (2005-8) | 9 | * Authors: Jamal Hadi Salim (2005-8) |
10 | * | 10 | * |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/skbuff.h> | 17 | #include <linux/skbuff.h> |
18 | #include <linux/rtnetlink.h> | 18 | #include <linux/rtnetlink.h> |
19 | #include <net/netlink.h> | 19 | #include <net/netlink.h> |
20 | #include <net/pkt_sched.h> | 20 | #include <net/pkt_sched.h> |
21 | 21 | ||
22 | #define TCA_ACT_SIMP 22 | 22 | #define TCA_ACT_SIMP 22 |
23 | 23 | ||
24 | #include <linux/tc_act/tc_defact.h> | 24 | #include <linux/tc_act/tc_defact.h> |
25 | #include <net/tc_act/tc_defact.h> | 25 | #include <net/tc_act/tc_defact.h> |
26 | 26 | ||
27 | #define SIMP_TAB_MASK 7 | 27 | #define SIMP_TAB_MASK 7 |
28 | static struct tcf_common *tcf_simp_ht[SIMP_TAB_MASK + 1]; | 28 | static struct tcf_common *tcf_simp_ht[SIMP_TAB_MASK + 1]; |
29 | static u32 simp_idx_gen; | 29 | static u32 simp_idx_gen; |
30 | static DEFINE_RWLOCK(simp_lock); | 30 | static DEFINE_RWLOCK(simp_lock); |
31 | 31 | ||
32 | static struct tcf_hashinfo simp_hash_info = { | 32 | static struct tcf_hashinfo simp_hash_info = { |
33 | .htab = tcf_simp_ht, | 33 | .htab = tcf_simp_ht, |
34 | .hmask = SIMP_TAB_MASK, | 34 | .hmask = SIMP_TAB_MASK, |
35 | .lock = &simp_lock, | 35 | .lock = &simp_lock, |
36 | }; | 36 | }; |
37 | 37 | ||
38 | #define SIMP_MAX_DATA 32 | 38 | #define SIMP_MAX_DATA 32 |
39 | static int tcf_simp(struct sk_buff *skb, const struct tc_action *a, | 39 | static int tcf_simp(struct sk_buff *skb, const struct tc_action *a, |
40 | struct tcf_result *res) | 40 | struct tcf_result *res) |
41 | { | 41 | { |
42 | struct tcf_defact *d = a->priv; | 42 | struct tcf_defact *d = a->priv; |
43 | 43 | ||
44 | spin_lock(&d->tcf_lock); | 44 | spin_lock(&d->tcf_lock); |
45 | d->tcf_tm.lastuse = jiffies; | 45 | d->tcf_tm.lastuse = jiffies; |
46 | bstats_update(&d->tcf_bstats, skb); | 46 | bstats_update(&d->tcf_bstats, skb); |
47 | 47 | ||
48 | /* print policy string followed by _ then packet count | 48 | /* print policy string followed by _ then packet count |
49 | * Example if this was the 3rd packet and the string was "hello" | 49 | * Example if this was the 3rd packet and the string was "hello" |
50 | * then it would look like "hello_3" (without quotes) | 50 | * then it would look like "hello_3" (without quotes) |
51 | */ | 51 | */ |
52 | pr_info("simple: %s_%d\n", | 52 | pr_info("simple: %s_%d\n", |
53 | (char *)d->tcfd_defdata, d->tcf_bstats.packets); | 53 | (char *)d->tcfd_defdata, d->tcf_bstats.packets); |
54 | spin_unlock(&d->tcf_lock); | 54 | spin_unlock(&d->tcf_lock); |
55 | return d->tcf_action; | 55 | return d->tcf_action; |
56 | } | 56 | } |
57 | 57 | ||
58 | static int tcf_simp_release(struct tcf_defact *d, int bind) | 58 | static int tcf_simp_release(struct tcf_defact *d, int bind) |
59 | { | 59 | { |
60 | int ret = 0; | 60 | int ret = 0; |
61 | if (d) { | 61 | if (d) { |
62 | if (bind) | 62 | if (bind) |
63 | d->tcf_bindcnt--; | 63 | d->tcf_bindcnt--; |
64 | d->tcf_refcnt--; | 64 | d->tcf_refcnt--; |
65 | if (d->tcf_bindcnt <= 0 && d->tcf_refcnt <= 0) { | 65 | if (d->tcf_bindcnt <= 0 && d->tcf_refcnt <= 0) { |
66 | kfree(d->tcfd_defdata); | 66 | kfree(d->tcfd_defdata); |
67 | tcf_hash_destroy(&d->common, &simp_hash_info); | 67 | tcf_hash_destroy(&d->common, &simp_hash_info); |
68 | ret = 1; | 68 | ret = 1; |
69 | } | 69 | } |
70 | } | 70 | } |
71 | return ret; | 71 | return ret; |
72 | } | 72 | } |
73 | 73 | ||
74 | static int alloc_defdata(struct tcf_defact *d, char *defdata) | 74 | static int alloc_defdata(struct tcf_defact *d, char *defdata) |
75 | { | 75 | { |
76 | d->tcfd_defdata = kzalloc(SIMP_MAX_DATA, GFP_KERNEL); | 76 | d->tcfd_defdata = kzalloc(SIMP_MAX_DATA, GFP_KERNEL); |
77 | if (unlikely(!d->tcfd_defdata)) | 77 | if (unlikely(!d->tcfd_defdata)) |
78 | return -ENOMEM; | 78 | return -ENOMEM; |
79 | strlcpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA); | 79 | strlcpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA); |
80 | return 0; | 80 | return 0; |
81 | } | 81 | } |
82 | 82 | ||
83 | static void reset_policy(struct tcf_defact *d, char *defdata, | 83 | static void reset_policy(struct tcf_defact *d, char *defdata, |
84 | struct tc_defact *p) | 84 | struct tc_defact *p) |
85 | { | 85 | { |
86 | spin_lock_bh(&d->tcf_lock); | 86 | spin_lock_bh(&d->tcf_lock); |
87 | d->tcf_action = p->action; | 87 | d->tcf_action = p->action; |
88 | memset(d->tcfd_defdata, 0, SIMP_MAX_DATA); | 88 | memset(d->tcfd_defdata, 0, SIMP_MAX_DATA); |
89 | strlcpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA); | 89 | strlcpy(d->tcfd_defdata, defdata, SIMP_MAX_DATA); |
90 | spin_unlock_bh(&d->tcf_lock); | 90 | spin_unlock_bh(&d->tcf_lock); |
91 | } | 91 | } |
92 | 92 | ||
93 | static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = { | 93 | static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = { |
94 | [TCA_DEF_PARMS] = { .len = sizeof(struct tc_defact) }, | 94 | [TCA_DEF_PARMS] = { .len = sizeof(struct tc_defact) }, |
95 | [TCA_DEF_DATA] = { .type = NLA_STRING, .len = SIMP_MAX_DATA }, | 95 | [TCA_DEF_DATA] = { .type = NLA_STRING, .len = SIMP_MAX_DATA }, |
96 | }; | 96 | }; |
97 | 97 | ||
98 | static int tcf_simp_init(struct net *net, struct nlattr *nla, | 98 | static int tcf_simp_init(struct net *net, struct nlattr *nla, |
99 | struct nlattr *est, struct tc_action *a, | 99 | struct nlattr *est, struct tc_action *a, |
100 | int ovr, int bind) | 100 | int ovr, int bind) |
101 | { | 101 | { |
102 | struct nlattr *tb[TCA_DEF_MAX + 1]; | 102 | struct nlattr *tb[TCA_DEF_MAX + 1]; |
103 | struct tc_defact *parm; | 103 | struct tc_defact *parm; |
104 | struct tcf_defact *d; | 104 | struct tcf_defact *d; |
105 | struct tcf_common *pc; | 105 | struct tcf_common *pc; |
106 | char *defdata; | 106 | char *defdata; |
107 | int ret = 0, err; | 107 | int ret = 0, err; |
108 | 108 | ||
109 | if (nla == NULL) | 109 | if (nla == NULL) |
110 | return -EINVAL; | 110 | return -EINVAL; |
111 | 111 | ||
112 | err = nla_parse_nested(tb, TCA_DEF_MAX, nla, simple_policy); | 112 | err = nla_parse_nested(tb, TCA_DEF_MAX, nla, simple_policy); |
113 | if (err < 0) | 113 | if (err < 0) |
114 | return err; | 114 | return err; |
115 | 115 | ||
116 | if (tb[TCA_DEF_PARMS] == NULL) | 116 | if (tb[TCA_DEF_PARMS] == NULL) |
117 | return -EINVAL; | 117 | return -EINVAL; |
118 | 118 | ||
119 | if (tb[TCA_DEF_DATA] == NULL) | 119 | if (tb[TCA_DEF_DATA] == NULL) |
120 | return -EINVAL; | 120 | return -EINVAL; |
121 | 121 | ||
122 | parm = nla_data(tb[TCA_DEF_PARMS]); | 122 | parm = nla_data(tb[TCA_DEF_PARMS]); |
123 | defdata = nla_data(tb[TCA_DEF_DATA]); | 123 | defdata = nla_data(tb[TCA_DEF_DATA]); |
124 | 124 | ||
125 | pc = tcf_hash_check(parm->index, a, bind, &simp_hash_info); | 125 | pc = tcf_hash_check(parm->index, a, bind, &simp_hash_info); |
126 | if (!pc) { | 126 | if (!pc) { |
127 | pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, | 127 | pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, |
128 | &simp_idx_gen, &simp_hash_info); | 128 | &simp_idx_gen, &simp_hash_info); |
129 | if (IS_ERR(pc)) | 129 | if (IS_ERR(pc)) |
130 | return PTR_ERR(pc); | 130 | return PTR_ERR(pc); |
131 | 131 | ||
132 | d = to_defact(pc); | 132 | d = to_defact(pc); |
133 | ret = alloc_defdata(d, defdata); | 133 | ret = alloc_defdata(d, defdata); |
134 | if (ret < 0) { | 134 | if (ret < 0) { |
135 | if (est) | 135 | if (est) |
136 | gen_kill_estimator(&pc->tcfc_bstats, | 136 | gen_kill_estimator(&pc->tcfc_bstats, |
137 | &pc->tcfc_rate_est); | 137 | &pc->tcfc_rate_est); |
138 | kfree_rcu(pc, tcfc_rcu); | 138 | kfree_rcu(pc, tcfc_rcu); |
139 | return ret; | 139 | return ret; |
140 | } | 140 | } |
141 | d->tcf_action = parm->action; | 141 | d->tcf_action = parm->action; |
142 | ret = ACT_P_CREATED; | 142 | ret = ACT_P_CREATED; |
143 | } else { | 143 | } else { |
144 | d = to_defact(pc); | 144 | d = to_defact(pc); |
145 | if (!ovr) { | 145 | if (!ovr) { |
146 | tcf_simp_release(d, bind); | 146 | tcf_simp_release(d, bind); |
147 | return -EEXIST; | 147 | return -EEXIST; |
148 | } | 148 | } |
149 | reset_policy(d, defdata, parm); | 149 | reset_policy(d, defdata, parm); |
150 | } | 150 | } |
151 | 151 | ||
152 | if (ret == ACT_P_CREATED) | 152 | if (ret == ACT_P_CREATED) |
153 | tcf_hash_insert(pc, &simp_hash_info); | 153 | tcf_hash_insert(pc, &simp_hash_info); |
154 | return ret; | 154 | return ret; |
155 | } | 155 | } |
156 | 156 | ||
157 | static int tcf_simp_cleanup(struct tc_action *a, int bind) | 157 | static int tcf_simp_cleanup(struct tc_action *a, int bind) |
158 | { | 158 | { |
159 | struct tcf_defact *d = a->priv; | 159 | struct tcf_defact *d = a->priv; |
160 | 160 | ||
161 | if (d) | 161 | if (d) |
162 | return tcf_simp_release(d, bind); | 162 | return tcf_simp_release(d, bind); |
163 | return 0; | 163 | return 0; |
164 | } | 164 | } |
165 | 165 | ||
166 | static int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a, | 166 | static int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a, |
167 | int bind, int ref) | 167 | int bind, int ref) |
168 | { | 168 | { |
169 | unsigned char *b = skb_tail_pointer(skb); | 169 | unsigned char *b = skb_tail_pointer(skb); |
170 | struct tcf_defact *d = a->priv; | 170 | struct tcf_defact *d = a->priv; |
171 | struct tc_defact opt = { | 171 | struct tc_defact opt = { |
172 | .index = d->tcf_index, | 172 | .index = d->tcf_index, |
173 | .refcnt = d->tcf_refcnt - ref, | 173 | .refcnt = d->tcf_refcnt - ref, |
174 | .bindcnt = d->tcf_bindcnt - bind, | 174 | .bindcnt = d->tcf_bindcnt - bind, |
175 | .action = d->tcf_action, | 175 | .action = d->tcf_action, |
176 | }; | 176 | }; |
177 | struct tcf_t t; | 177 | struct tcf_t t; |
178 | 178 | ||
179 | if (nla_put(skb, TCA_DEF_PARMS, sizeof(opt), &opt) || | 179 | if (nla_put(skb, TCA_DEF_PARMS, sizeof(opt), &opt) || |
180 | nla_put_string(skb, TCA_DEF_DATA, d->tcfd_defdata)) | 180 | nla_put_string(skb, TCA_DEF_DATA, d->tcfd_defdata)) |
181 | goto nla_put_failure; | 181 | goto nla_put_failure; |
182 | t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install); | 182 | t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install); |
183 | t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse); | 183 | t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse); |
184 | t.expires = jiffies_to_clock_t(d->tcf_tm.expires); | 184 | t.expires = jiffies_to_clock_t(d->tcf_tm.expires); |
185 | if (nla_put(skb, TCA_DEF_TM, sizeof(t), &t)) | 185 | if (nla_put(skb, TCA_DEF_TM, sizeof(t), &t)) |
186 | goto nla_put_failure; | 186 | goto nla_put_failure; |
187 | return skb->len; | 187 | return skb->len; |
188 | 188 | ||
189 | nla_put_failure: | 189 | nla_put_failure: |
190 | nlmsg_trim(skb, b); | 190 | nlmsg_trim(skb, b); |
191 | return -1; | 191 | return -1; |
192 | } | 192 | } |
193 | 193 | ||
194 | static struct tc_action_ops act_simp_ops = { | 194 | static struct tc_action_ops act_simp_ops = { |
195 | .kind = "simple", | 195 | .kind = "simple", |
196 | .hinfo = &simp_hash_info, | 196 | .hinfo = &simp_hash_info, |
197 | .type = TCA_ACT_SIMP, | 197 | .type = TCA_ACT_SIMP, |
198 | .capab = TCA_CAP_NONE, | 198 | .capab = TCA_CAP_NONE, |
199 | .owner = THIS_MODULE, | 199 | .owner = THIS_MODULE, |
200 | .act = tcf_simp, | 200 | .act = tcf_simp, |
201 | .dump = tcf_simp_dump, | 201 | .dump = tcf_simp_dump, |
202 | .cleanup = tcf_simp_cleanup, | 202 | .cleanup = tcf_simp_cleanup, |
203 | .init = tcf_simp_init, | 203 | .init = tcf_simp_init, |
204 | .walk = tcf_generic_walker, | ||
205 | }; | 204 | }; |
206 | 205 | ||
207 | MODULE_AUTHOR("Jamal Hadi Salim(2005)"); | 206 | MODULE_AUTHOR("Jamal Hadi Salim(2005)"); |
208 | MODULE_DESCRIPTION("Simple example action"); | 207 | MODULE_DESCRIPTION("Simple example action"); |
209 | MODULE_LICENSE("GPL"); | 208 | MODULE_LICENSE("GPL"); |
210 | 209 | ||
211 | static int __init simp_init_module(void) | 210 | static int __init simp_init_module(void) |
212 | { | 211 | { |
213 | int ret = tcf_register_action(&act_simp_ops); | 212 | int ret = tcf_register_action(&act_simp_ops); |
214 | if (!ret) | 213 | if (!ret) |
215 | pr_info("Simple TC action Loaded\n"); | 214 | pr_info("Simple TC action Loaded\n"); |
216 | return ret; | 215 | return ret; |
217 | } | 216 | } |
218 | 217 | ||
219 | static void __exit simp_cleanup_module(void) | 218 | static void __exit simp_cleanup_module(void) |
220 | { | 219 | { |
221 | tcf_unregister_action(&act_simp_ops); | 220 | tcf_unregister_action(&act_simp_ops); |
222 | } | 221 | } |
223 | 222 | ||
224 | module_init(simp_init_module); | 223 | module_init(simp_init_module); |
225 | module_exit(simp_cleanup_module); | 224 | module_exit(simp_cleanup_module); |
226 | 225 |
net/sched/act_skbedit.c
1 | /* | 1 | /* |
2 | * Copyright (c) 2008, Intel Corporation. | 2 | * Copyright (c) 2008, Intel Corporation. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify it | 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, | 5 | * under the terms and conditions of the GNU General Public License, |
6 | * version 2, as published by the Free Software Foundation. | 6 | * version 2, as published by the Free Software Foundation. |
7 | * | 7 | * |
8 | * This program is distributed in the hope it will be useful, but WITHOUT | 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 | 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
11 | * more details. | 11 | * more details. |
12 | * | 12 | * |
13 | * You should have received a copy of the GNU General Public License along with | 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 | 14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | 15 | * Place - Suite 330, Boston, MA 02111-1307 USA. |
16 | * | 16 | * |
17 | * Author: Alexander Duyck <alexander.h.duyck@intel.com> | 17 | * Author: Alexander Duyck <alexander.h.duyck@intel.com> |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/skbuff.h> | 23 | #include <linux/skbuff.h> |
24 | #include <linux/rtnetlink.h> | 24 | #include <linux/rtnetlink.h> |
25 | #include <net/netlink.h> | 25 | #include <net/netlink.h> |
26 | #include <net/pkt_sched.h> | 26 | #include <net/pkt_sched.h> |
27 | 27 | ||
28 | #include <linux/tc_act/tc_skbedit.h> | 28 | #include <linux/tc_act/tc_skbedit.h> |
29 | #include <net/tc_act/tc_skbedit.h> | 29 | #include <net/tc_act/tc_skbedit.h> |
30 | 30 | ||
31 | #define SKBEDIT_TAB_MASK 15 | 31 | #define SKBEDIT_TAB_MASK 15 |
32 | static struct tcf_common *tcf_skbedit_ht[SKBEDIT_TAB_MASK + 1]; | 32 | static struct tcf_common *tcf_skbedit_ht[SKBEDIT_TAB_MASK + 1]; |
33 | static u32 skbedit_idx_gen; | 33 | static u32 skbedit_idx_gen; |
34 | static DEFINE_RWLOCK(skbedit_lock); | 34 | static DEFINE_RWLOCK(skbedit_lock); |
35 | 35 | ||
36 | static struct tcf_hashinfo skbedit_hash_info = { | 36 | static struct tcf_hashinfo skbedit_hash_info = { |
37 | .htab = tcf_skbedit_ht, | 37 | .htab = tcf_skbedit_ht, |
38 | .hmask = SKBEDIT_TAB_MASK, | 38 | .hmask = SKBEDIT_TAB_MASK, |
39 | .lock = &skbedit_lock, | 39 | .lock = &skbedit_lock, |
40 | }; | 40 | }; |
41 | 41 | ||
42 | static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a, | 42 | static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a, |
43 | struct tcf_result *res) | 43 | struct tcf_result *res) |
44 | { | 44 | { |
45 | struct tcf_skbedit *d = a->priv; | 45 | struct tcf_skbedit *d = a->priv; |
46 | 46 | ||
47 | spin_lock(&d->tcf_lock); | 47 | spin_lock(&d->tcf_lock); |
48 | d->tcf_tm.lastuse = jiffies; | 48 | d->tcf_tm.lastuse = jiffies; |
49 | bstats_update(&d->tcf_bstats, skb); | 49 | bstats_update(&d->tcf_bstats, skb); |
50 | 50 | ||
51 | if (d->flags & SKBEDIT_F_PRIORITY) | 51 | if (d->flags & SKBEDIT_F_PRIORITY) |
52 | skb->priority = d->priority; | 52 | skb->priority = d->priority; |
53 | if (d->flags & SKBEDIT_F_QUEUE_MAPPING && | 53 | if (d->flags & SKBEDIT_F_QUEUE_MAPPING && |
54 | skb->dev->real_num_tx_queues > d->queue_mapping) | 54 | skb->dev->real_num_tx_queues > d->queue_mapping) |
55 | skb_set_queue_mapping(skb, d->queue_mapping); | 55 | skb_set_queue_mapping(skb, d->queue_mapping); |
56 | if (d->flags & SKBEDIT_F_MARK) | 56 | if (d->flags & SKBEDIT_F_MARK) |
57 | skb->mark = d->mark; | 57 | skb->mark = d->mark; |
58 | 58 | ||
59 | spin_unlock(&d->tcf_lock); | 59 | spin_unlock(&d->tcf_lock); |
60 | return d->tcf_action; | 60 | return d->tcf_action; |
61 | } | 61 | } |
62 | 62 | ||
63 | static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { | 63 | static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { |
64 | [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, | 64 | [TCA_SKBEDIT_PARMS] = { .len = sizeof(struct tc_skbedit) }, |
65 | [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, | 65 | [TCA_SKBEDIT_PRIORITY] = { .len = sizeof(u32) }, |
66 | [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, | 66 | [TCA_SKBEDIT_QUEUE_MAPPING] = { .len = sizeof(u16) }, |
67 | [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) }, | 67 | [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) }, |
68 | }; | 68 | }; |
69 | 69 | ||
70 | static int tcf_skbedit_init(struct net *net, struct nlattr *nla, | 70 | static int tcf_skbedit_init(struct net *net, struct nlattr *nla, |
71 | struct nlattr *est, struct tc_action *a, | 71 | struct nlattr *est, struct tc_action *a, |
72 | int ovr, int bind) | 72 | int ovr, int bind) |
73 | { | 73 | { |
74 | struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; | 74 | struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; |
75 | struct tc_skbedit *parm; | 75 | struct tc_skbedit *parm; |
76 | struct tcf_skbedit *d; | 76 | struct tcf_skbedit *d; |
77 | struct tcf_common *pc; | 77 | struct tcf_common *pc; |
78 | u32 flags = 0, *priority = NULL, *mark = NULL; | 78 | u32 flags = 0, *priority = NULL, *mark = NULL; |
79 | u16 *queue_mapping = NULL; | 79 | u16 *queue_mapping = NULL; |
80 | int ret = 0, err; | 80 | int ret = 0, err; |
81 | 81 | ||
82 | if (nla == NULL) | 82 | if (nla == NULL) |
83 | return -EINVAL; | 83 | return -EINVAL; |
84 | 84 | ||
85 | err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy); | 85 | err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy); |
86 | if (err < 0) | 86 | if (err < 0) |
87 | return err; | 87 | return err; |
88 | 88 | ||
89 | if (tb[TCA_SKBEDIT_PARMS] == NULL) | 89 | if (tb[TCA_SKBEDIT_PARMS] == NULL) |
90 | return -EINVAL; | 90 | return -EINVAL; |
91 | 91 | ||
92 | if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { | 92 | if (tb[TCA_SKBEDIT_PRIORITY] != NULL) { |
93 | flags |= SKBEDIT_F_PRIORITY; | 93 | flags |= SKBEDIT_F_PRIORITY; |
94 | priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); | 94 | priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]); |
95 | } | 95 | } |
96 | 96 | ||
97 | if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { | 97 | if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) { |
98 | flags |= SKBEDIT_F_QUEUE_MAPPING; | 98 | flags |= SKBEDIT_F_QUEUE_MAPPING; |
99 | queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); | 99 | queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]); |
100 | } | 100 | } |
101 | 101 | ||
102 | if (tb[TCA_SKBEDIT_MARK] != NULL) { | 102 | if (tb[TCA_SKBEDIT_MARK] != NULL) { |
103 | flags |= SKBEDIT_F_MARK; | 103 | flags |= SKBEDIT_F_MARK; |
104 | mark = nla_data(tb[TCA_SKBEDIT_MARK]); | 104 | mark = nla_data(tb[TCA_SKBEDIT_MARK]); |
105 | } | 105 | } |
106 | 106 | ||
107 | if (!flags) | 107 | if (!flags) |
108 | return -EINVAL; | 108 | return -EINVAL; |
109 | 109 | ||
110 | parm = nla_data(tb[TCA_SKBEDIT_PARMS]); | 110 | parm = nla_data(tb[TCA_SKBEDIT_PARMS]); |
111 | 111 | ||
112 | pc = tcf_hash_check(parm->index, a, bind, &skbedit_hash_info); | 112 | pc = tcf_hash_check(parm->index, a, bind, &skbedit_hash_info); |
113 | if (!pc) { | 113 | if (!pc) { |
114 | pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, | 114 | pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind, |
115 | &skbedit_idx_gen, &skbedit_hash_info); | 115 | &skbedit_idx_gen, &skbedit_hash_info); |
116 | if (IS_ERR(pc)) | 116 | if (IS_ERR(pc)) |
117 | return PTR_ERR(pc); | 117 | return PTR_ERR(pc); |
118 | 118 | ||
119 | d = to_skbedit(pc); | 119 | d = to_skbedit(pc); |
120 | ret = ACT_P_CREATED; | 120 | ret = ACT_P_CREATED; |
121 | } else { | 121 | } else { |
122 | d = to_skbedit(pc); | 122 | d = to_skbedit(pc); |
123 | if (!ovr) { | 123 | if (!ovr) { |
124 | tcf_hash_release(pc, bind, &skbedit_hash_info); | 124 | tcf_hash_release(pc, bind, &skbedit_hash_info); |
125 | return -EEXIST; | 125 | return -EEXIST; |
126 | } | 126 | } |
127 | } | 127 | } |
128 | 128 | ||
129 | spin_lock_bh(&d->tcf_lock); | 129 | spin_lock_bh(&d->tcf_lock); |
130 | 130 | ||
131 | d->flags = flags; | 131 | d->flags = flags; |
132 | if (flags & SKBEDIT_F_PRIORITY) | 132 | if (flags & SKBEDIT_F_PRIORITY) |
133 | d->priority = *priority; | 133 | d->priority = *priority; |
134 | if (flags & SKBEDIT_F_QUEUE_MAPPING) | 134 | if (flags & SKBEDIT_F_QUEUE_MAPPING) |
135 | d->queue_mapping = *queue_mapping; | 135 | d->queue_mapping = *queue_mapping; |
136 | if (flags & SKBEDIT_F_MARK) | 136 | if (flags & SKBEDIT_F_MARK) |
137 | d->mark = *mark; | 137 | d->mark = *mark; |
138 | 138 | ||
139 | d->tcf_action = parm->action; | 139 | d->tcf_action = parm->action; |
140 | 140 | ||
141 | spin_unlock_bh(&d->tcf_lock); | 141 | spin_unlock_bh(&d->tcf_lock); |
142 | 142 | ||
143 | if (ret == ACT_P_CREATED) | 143 | if (ret == ACT_P_CREATED) |
144 | tcf_hash_insert(pc, &skbedit_hash_info); | 144 | tcf_hash_insert(pc, &skbedit_hash_info); |
145 | return ret; | 145 | return ret; |
146 | } | 146 | } |
147 | 147 | ||
148 | static int tcf_skbedit_cleanup(struct tc_action *a, int bind) | 148 | static int tcf_skbedit_cleanup(struct tc_action *a, int bind) |
149 | { | 149 | { |
150 | struct tcf_skbedit *d = a->priv; | 150 | struct tcf_skbedit *d = a->priv; |
151 | 151 | ||
152 | if (d) | 152 | if (d) |
153 | return tcf_hash_release(&d->common, bind, &skbedit_hash_info); | 153 | return tcf_hash_release(&d->common, bind, &skbedit_hash_info); |
154 | return 0; | 154 | return 0; |
155 | } | 155 | } |
156 | 156 | ||
157 | static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, | 157 | static int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a, |
158 | int bind, int ref) | 158 | int bind, int ref) |
159 | { | 159 | { |
160 | unsigned char *b = skb_tail_pointer(skb); | 160 | unsigned char *b = skb_tail_pointer(skb); |
161 | struct tcf_skbedit *d = a->priv; | 161 | struct tcf_skbedit *d = a->priv; |
162 | struct tc_skbedit opt = { | 162 | struct tc_skbedit opt = { |
163 | .index = d->tcf_index, | 163 | .index = d->tcf_index, |
164 | .refcnt = d->tcf_refcnt - ref, | 164 | .refcnt = d->tcf_refcnt - ref, |
165 | .bindcnt = d->tcf_bindcnt - bind, | 165 | .bindcnt = d->tcf_bindcnt - bind, |
166 | .action = d->tcf_action, | 166 | .action = d->tcf_action, |
167 | }; | 167 | }; |
168 | struct tcf_t t; | 168 | struct tcf_t t; |
169 | 169 | ||
170 | if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt)) | 170 | if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt)) |
171 | goto nla_put_failure; | 171 | goto nla_put_failure; |
172 | if ((d->flags & SKBEDIT_F_PRIORITY) && | 172 | if ((d->flags & SKBEDIT_F_PRIORITY) && |
173 | nla_put(skb, TCA_SKBEDIT_PRIORITY, sizeof(d->priority), | 173 | nla_put(skb, TCA_SKBEDIT_PRIORITY, sizeof(d->priority), |
174 | &d->priority)) | 174 | &d->priority)) |
175 | goto nla_put_failure; | 175 | goto nla_put_failure; |
176 | if ((d->flags & SKBEDIT_F_QUEUE_MAPPING) && | 176 | if ((d->flags & SKBEDIT_F_QUEUE_MAPPING) && |
177 | nla_put(skb, TCA_SKBEDIT_QUEUE_MAPPING, | 177 | nla_put(skb, TCA_SKBEDIT_QUEUE_MAPPING, |
178 | sizeof(d->queue_mapping), &d->queue_mapping)) | 178 | sizeof(d->queue_mapping), &d->queue_mapping)) |
179 | goto nla_put_failure; | 179 | goto nla_put_failure; |
180 | if ((d->flags & SKBEDIT_F_MARK) && | 180 | if ((d->flags & SKBEDIT_F_MARK) && |
181 | nla_put(skb, TCA_SKBEDIT_MARK, sizeof(d->mark), | 181 | nla_put(skb, TCA_SKBEDIT_MARK, sizeof(d->mark), |
182 | &d->mark)) | 182 | &d->mark)) |
183 | goto nla_put_failure; | 183 | goto nla_put_failure; |
184 | t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install); | 184 | t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install); |
185 | t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse); | 185 | t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse); |
186 | t.expires = jiffies_to_clock_t(d->tcf_tm.expires); | 186 | t.expires = jiffies_to_clock_t(d->tcf_tm.expires); |
187 | if (nla_put(skb, TCA_SKBEDIT_TM, sizeof(t), &t)) | 187 | if (nla_put(skb, TCA_SKBEDIT_TM, sizeof(t), &t)) |
188 | goto nla_put_failure; | 188 | goto nla_put_failure; |
189 | return skb->len; | 189 | return skb->len; |
190 | 190 | ||
191 | nla_put_failure: | 191 | nla_put_failure: |
192 | nlmsg_trim(skb, b); | 192 | nlmsg_trim(skb, b); |
193 | return -1; | 193 | return -1; |
194 | } | 194 | } |
195 | 195 | ||
196 | static struct tc_action_ops act_skbedit_ops = { | 196 | static struct tc_action_ops act_skbedit_ops = { |
197 | .kind = "skbedit", | 197 | .kind = "skbedit", |
198 | .hinfo = &skbedit_hash_info, | 198 | .hinfo = &skbedit_hash_info, |
199 | .type = TCA_ACT_SKBEDIT, | 199 | .type = TCA_ACT_SKBEDIT, |
200 | .capab = TCA_CAP_NONE, | 200 | .capab = TCA_CAP_NONE, |
201 | .owner = THIS_MODULE, | 201 | .owner = THIS_MODULE, |
202 | .act = tcf_skbedit, | 202 | .act = tcf_skbedit, |
203 | .dump = tcf_skbedit_dump, | 203 | .dump = tcf_skbedit_dump, |
204 | .cleanup = tcf_skbedit_cleanup, | 204 | .cleanup = tcf_skbedit_cleanup, |
205 | .init = tcf_skbedit_init, | 205 | .init = tcf_skbedit_init, |
206 | .walk = tcf_generic_walker, | ||
207 | }; | 206 | }; |
208 | 207 | ||
209 | MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); | 208 | MODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>"); |
210 | MODULE_DESCRIPTION("SKB Editing"); | 209 | MODULE_DESCRIPTION("SKB Editing"); |
211 | MODULE_LICENSE("GPL"); | 210 | MODULE_LICENSE("GPL"); |
212 | 211 | ||
213 | static int __init skbedit_init_module(void) | 212 | static int __init skbedit_init_module(void) |
214 | { | 213 | { |
215 | return tcf_register_action(&act_skbedit_ops); | 214 | return tcf_register_action(&act_skbedit_ops); |
216 | } | 215 | } |
217 | 216 | ||
218 | static void __exit skbedit_cleanup_module(void) | 217 | static void __exit skbedit_cleanup_module(void) |
219 | { | 218 | { |
220 | tcf_unregister_action(&act_skbedit_ops); | 219 | tcf_unregister_action(&act_skbedit_ops); |
221 | } | 220 | } |
222 | 221 | ||
223 | module_init(skbedit_init_module); | 222 | module_init(skbedit_init_module); |
224 | module_exit(skbedit_cleanup_module); | 223 | module_exit(skbedit_cleanup_module); |
225 | 224 |