netfilter: xtables: move extension arguments into compound structure (4/6)
[safe/jmp/linux-2.6] / net / sched / act_ipt.c
1 /*
2  * net/sched/ipt.c      iptables target interface
3  *
4  *TODO: Add other tables. For now we only support the ipv4 table targets
5  *
6  *              This program is free software; you can redistribute it and/or
7  *              modify it under the terms of the GNU General Public License
8  *              as published by the Free Software Foundation; either version
9  *              2 of the License, or (at your option) any later version.
10  *
11  * Copyright:   Jamal Hadi Salim (2002-4)
12  */
13
14 #include <linux/types.h>
15 #include <linux/kernel.h>
16 #include <linux/string.h>
17 #include <linux/errno.h>
18 #include <linux/skbuff.h>
19 #include <linux/rtnetlink.h>
20 #include <linux/module.h>
21 #include <linux/init.h>
22 #include <net/netlink.h>
23 #include <net/pkt_sched.h>
24 #include <linux/tc_act/tc_ipt.h>
25 #include <net/tc_act/tc_ipt.h>
26
27 #include <linux/netfilter_ipv4/ip_tables.h>
28
29
30 #define IPT_TAB_MASK     15
31 static struct tcf_common *tcf_ipt_ht[IPT_TAB_MASK + 1];
32 static u32 ipt_idx_gen;
33 static DEFINE_RWLOCK(ipt_lock);
34
35 static struct tcf_hashinfo ipt_hash_info = {
36         .htab   =       tcf_ipt_ht,
37         .hmask  =       IPT_TAB_MASK,
38         .lock   =       &ipt_lock,
39 };
40
41 static int ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int hook)
42 {
43         struct xt_target *target;
44         int ret = 0;
45
46         target = xt_request_find_target(AF_INET, t->u.user.name,
47                                         t->u.user.revision);
48         if (!target)
49                 return -ENOENT;
50
51         t->u.kernel.target = target;
52
53         ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t),
54                               table, hook, 0, 0, NULL, t->data);
55         if (ret < 0) {
56                 module_put(t->u.kernel.target->me);
57                 return ret;
58         }
59         return 0;
60 }
61
62 static void ipt_destroy_target(struct ipt_entry_target *t)
63 {
64         if (t->u.kernel.target->destroy)
65                 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
66         module_put(t->u.kernel.target->me);
67 }
68
69 static int tcf_ipt_release(struct tcf_ipt *ipt, int bind)
70 {
71         int ret = 0;
72         if (ipt) {
73                 if (bind)
74                         ipt->tcf_bindcnt--;
75                 ipt->tcf_refcnt--;
76                 if (ipt->tcf_bindcnt <= 0 && ipt->tcf_refcnt <= 0) {
77                         ipt_destroy_target(ipt->tcfi_t);
78                         kfree(ipt->tcfi_tname);
79                         kfree(ipt->tcfi_t);
80                         tcf_hash_destroy(&ipt->common, &ipt_hash_info);
81                         ret = ACT_P_DELETED;
82                 }
83         }
84         return ret;
85 }
86
87 static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = {
88         [TCA_IPT_TABLE] = { .type = NLA_STRING, .len = IFNAMSIZ },
89         [TCA_IPT_HOOK]  = { .type = NLA_U32 },
90         [TCA_IPT_INDEX] = { .type = NLA_U32 },
91         [TCA_IPT_TARG]  = { .len = sizeof(struct ipt_entry_target) },
92 };
93
94 static int tcf_ipt_init(struct nlattr *nla, struct nlattr *est,
95                         struct tc_action *a, int ovr, int bind)
96 {
97         struct nlattr *tb[TCA_IPT_MAX + 1];
98         struct tcf_ipt *ipt;
99         struct tcf_common *pc;
100         struct ipt_entry_target *td, *t;
101         char *tname;
102         int ret = 0, err;
103         u32 hook = 0;
104         u32 index = 0;
105
106         if (nla == NULL)
107                 return -EINVAL;
108
109         err = nla_parse_nested(tb, TCA_IPT_MAX, nla, ipt_policy);
110         if (err < 0)
111                 return err;
112
113         if (tb[TCA_IPT_HOOK] == NULL)
114                 return -EINVAL;
115         if (tb[TCA_IPT_TARG] == NULL)
116                 return -EINVAL;
117
118         td = (struct ipt_entry_target *)nla_data(tb[TCA_IPT_TARG]);
119         if (nla_len(tb[TCA_IPT_TARG]) < td->u.target_size)
120                 return -EINVAL;
121
122         if (tb[TCA_IPT_INDEX] != NULL)
123                 index = nla_get_u32(tb[TCA_IPT_INDEX]);
124
125         pc = tcf_hash_check(index, a, bind, &ipt_hash_info);
126         if (!pc) {
127                 pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind,
128                                      &ipt_idx_gen, &ipt_hash_info);
129                 if (unlikely(!pc))
130                         return -ENOMEM;
131                 ret = ACT_P_CREATED;
132         } else {
133                 if (!ovr) {
134                         tcf_ipt_release(to_ipt(pc), bind);
135                         return -EEXIST;
136                 }
137         }
138         ipt = to_ipt(pc);
139
140         hook = nla_get_u32(tb[TCA_IPT_HOOK]);
141
142         err = -ENOMEM;
143         tname = kmalloc(IFNAMSIZ, GFP_KERNEL);
144         if (unlikely(!tname))
145                 goto err1;
146         if (tb[TCA_IPT_TABLE] == NULL ||
147             nla_strlcpy(tname, tb[TCA_IPT_TABLE], IFNAMSIZ) >= IFNAMSIZ)
148                 strcpy(tname, "mangle");
149
150         t = kmemdup(td, td->u.target_size, GFP_KERNEL);
151         if (unlikely(!t))
152                 goto err2;
153
154         if ((err = ipt_init_target(t, tname, hook)) < 0)
155                 goto err3;
156
157         spin_lock_bh(&ipt->tcf_lock);
158         if (ret != ACT_P_CREATED) {
159                 ipt_destroy_target(ipt->tcfi_t);
160                 kfree(ipt->tcfi_tname);
161                 kfree(ipt->tcfi_t);
162         }
163         ipt->tcfi_tname = tname;
164         ipt->tcfi_t     = t;
165         ipt->tcfi_hook  = hook;
166         spin_unlock_bh(&ipt->tcf_lock);
167         if (ret == ACT_P_CREATED)
168                 tcf_hash_insert(pc, &ipt_hash_info);
169         return ret;
170
171 err3:
172         kfree(t);
173 err2:
174         kfree(tname);
175 err1:
176         kfree(pc);
177         return err;
178 }
179
180 static int tcf_ipt_cleanup(struct tc_action *a, int bind)
181 {
182         struct tcf_ipt *ipt = a->priv;
183         return tcf_ipt_release(ipt, bind);
184 }
185
186 static int tcf_ipt(struct sk_buff *skb, struct tc_action *a,
187                    struct tcf_result *res)
188 {
189         int ret = 0, result = 0;
190         struct tcf_ipt *ipt = a->priv;
191         struct xt_target_param par;
192
193         if (skb_cloned(skb)) {
194                 if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
195                         return TC_ACT_UNSPEC;
196         }
197
198         spin_lock(&ipt->tcf_lock);
199
200         ipt->tcf_tm.lastuse = jiffies;
201         ipt->tcf_bstats.bytes += qdisc_pkt_len(skb);
202         ipt->tcf_bstats.packets++;
203
204         /* yes, we have to worry about both in and out dev
205          worry later - danger - this API seems to have changed
206          from earlier kernels */
207         par.in       = skb->dev;
208         par.out      = NULL;
209         par.hooknum  = ipt->tcfi_hook;
210         par.target   = ipt->tcfi_t->u.kernel.target;
211         par.targinfo = ipt->tcfi_t->data;
212         ret = par.target->target(skb, &par);
213
214         switch (ret) {
215         case NF_ACCEPT:
216                 result = TC_ACT_OK;
217                 break;
218         case NF_DROP:
219                 result = TC_ACT_SHOT;
220                 ipt->tcf_qstats.drops++;
221                 break;
222         case IPT_CONTINUE:
223                 result = TC_ACT_PIPE;
224                 break;
225         default:
226                 if (net_ratelimit())
227                         printk("Bogus netfilter code %d assume ACCEPT\n", ret);
228                 result = TC_POLICE_OK;
229                 break;
230         }
231         spin_unlock(&ipt->tcf_lock);
232         return result;
233
234 }
235
236 static int tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
237 {
238         unsigned char *b = skb_tail_pointer(skb);
239         struct tcf_ipt *ipt = a->priv;
240         struct ipt_entry_target *t;
241         struct tcf_t tm;
242         struct tc_cnt c;
243
244         /* for simple targets kernel size == user size
245         ** user name = target name
246         ** for foolproof you need to not assume this
247         */
248
249         t = kmemdup(ipt->tcfi_t, ipt->tcfi_t->u.user.target_size, GFP_ATOMIC);
250         if (unlikely(!t))
251                 goto nla_put_failure;
252
253         c.bindcnt = ipt->tcf_bindcnt - bind;
254         c.refcnt = ipt->tcf_refcnt - ref;
255         strcpy(t->u.user.name, ipt->tcfi_t->u.kernel.target->name);
256
257         NLA_PUT(skb, TCA_IPT_TARG, ipt->tcfi_t->u.user.target_size, t);
258         NLA_PUT_U32(skb, TCA_IPT_INDEX, ipt->tcf_index);
259         NLA_PUT_U32(skb, TCA_IPT_HOOK, ipt->tcfi_hook);
260         NLA_PUT(skb, TCA_IPT_CNT, sizeof(struct tc_cnt), &c);
261         NLA_PUT_STRING(skb, TCA_IPT_TABLE, ipt->tcfi_tname);
262         tm.install = jiffies_to_clock_t(jiffies - ipt->tcf_tm.install);
263         tm.lastuse = jiffies_to_clock_t(jiffies - ipt->tcf_tm.lastuse);
264         tm.expires = jiffies_to_clock_t(ipt->tcf_tm.expires);
265         NLA_PUT(skb, TCA_IPT_TM, sizeof (tm), &tm);
266         kfree(t);
267         return skb->len;
268
269 nla_put_failure:
270         nlmsg_trim(skb, b);
271         kfree(t);
272         return -1;
273 }
274
275 static struct tc_action_ops act_ipt_ops = {
276         .kind           =       "ipt",
277         .hinfo          =       &ipt_hash_info,
278         .type           =       TCA_ACT_IPT,
279         .capab          =       TCA_CAP_NONE,
280         .owner          =       THIS_MODULE,
281         .act            =       tcf_ipt,
282         .dump           =       tcf_ipt_dump,
283         .cleanup        =       tcf_ipt_cleanup,
284         .lookup         =       tcf_hash_search,
285         .init           =       tcf_ipt_init,
286         .walk           =       tcf_generic_walker
287 };
288
289 MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
290 MODULE_DESCRIPTION("Iptables target actions");
291 MODULE_LICENSE("GPL");
292
293 static int __init ipt_init_module(void)
294 {
295         return tcf_register_action(&act_ipt_ops);
296 }
297
298 static void __exit ipt_cleanup_module(void)
299 {
300         tcf_unregister_action(&act_ipt_ops);
301 }
302
303 module_init(ipt_init_module);
304 module_exit(ipt_cleanup_module);