4880d44ee978d0df85c90814096998ac4d3dc94d
[safe/jmp/linux-2.6] / net / sched / sch_ingress.c
1 /* net/sched/sch_ingress.c - Ingress qdisc
2  *              This program is free software; you can redistribute it and/or
3  *              modify it under the terms of the GNU General Public License
4  *              as published by the Free Software Foundation; either version
5  *              2 of the License, or (at your option) any later version.
6  *
7  * Authors:     Jamal Hadi Salim 1999
8  */
9
10 #include <linux/module.h>
11 #include <linux/types.h>
12 #include <linux/list.h>
13 #include <linux/skbuff.h>
14 #include <linux/rtnetlink.h>
15 #include <linux/netfilter_ipv4.h>
16 #include <linux/netfilter_ipv6.h>
17 #include <linux/netfilter.h>
18 #include <net/netlink.h>
19 #include <net/pkt_sched.h>
20
21
22 #undef DEBUG_INGRESS
23
24 #ifdef DEBUG_INGRESS  /* control */
25 #define DPRINTK(format, args...) printk(KERN_DEBUG format,##args)
26 #else
27 #define DPRINTK(format, args...)
28 #endif
29
30 #if 0  /* data */
31 #define D2PRINTK(format, args...) printk(KERN_DEBUG format,##args)
32 #else
33 #define D2PRINTK(format, args...)
34 #endif
35
36 #define PRIV(sch) qdisc_priv(sch)
37
38 /* Thanks to Doron Oz for this hack */
39 #ifndef CONFIG_NET_CLS_ACT
40 #ifdef CONFIG_NETFILTER
41 static int nf_registered;
42 #endif
43 #endif
44
45 struct ingress_qdisc_data {
46         struct Qdisc            *q;
47         struct tcf_proto        *filter_list;
48 };
49
50 /* ------------------------- Class/flow operations ------------------------- */
51
52 static int ingress_graft(struct Qdisc *sch, unsigned long arg,
53                          struct Qdisc *new, struct Qdisc **old)
54 {
55 #ifdef DEBUG_INGRESS
56         struct ingress_qdisc_data *p = PRIV(sch);
57 #endif
58
59         DPRINTK("ingress_graft(sch %p,[qdisc %p],new %p,old %p)\n",
60                 sch, p, new, old);
61         DPRINTK("\n ingress_graft: You cannot add qdiscs to classes");
62         return 1;
63 }
64
65 static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg)
66 {
67         return NULL;
68 }
69
70 static unsigned long ingress_get(struct Qdisc *sch, u32 classid)
71 {
72 #ifdef DEBUG_INGRESS
73         struct ingress_qdisc_data *p = PRIV(sch);
74 #endif
75         DPRINTK("ingress_get(sch %p,[qdisc %p],classid %x)\n",
76                 sch, p, classid);
77         return TC_H_MIN(classid) + 1;
78 }
79
80 static unsigned long ingress_bind_filter(struct Qdisc *sch,
81                                          unsigned long parent, u32 classid)
82 {
83         return ingress_get(sch, classid);
84 }
85
86 static void ingress_put(struct Qdisc *sch, unsigned long cl)
87 {
88 }
89
90 static int ingress_change(struct Qdisc *sch, u32 classid, u32 parent,
91                           struct rtattr **tca, unsigned long *arg)
92 {
93 #ifdef DEBUG_INGRESS
94         struct ingress_qdisc_data *p = PRIV(sch);
95 #endif
96         DPRINTK("ingress_change(sch %p,[qdisc %p],classid %x,parent %x),"
97                 "arg 0x%lx\n", sch, p, classid, parent, *arg);
98         DPRINTK("No effect. sch_ingress doesn't maintain classes at the moment");
99         return 0;
100 }
101
102 static void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker)
103 {
104 #ifdef DEBUG_INGRESS
105         struct ingress_qdisc_data *p = PRIV(sch);
106 #endif
107         DPRINTK("ingress_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker);
108         DPRINTK("No effect. sch_ingress doesn't maintain classes at the moment");
109 }
110
111 static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch, unsigned long cl)
112 {
113         struct ingress_qdisc_data *p = PRIV(sch);
114
115         return &p->filter_list;
116 }
117
118 /* --------------------------- Qdisc operations ---------------------------- */
119
120 static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
121 {
122         struct ingress_qdisc_data *p = PRIV(sch);
123         struct tcf_result res;
124         int result;
125
126         D2PRINTK("ingress_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
127         result = tc_classify(skb, p->filter_list, &res);
128         D2PRINTK("result %d class 0x%04x\n", result, res.classid);
129         /*
130          * Unlike normal "enqueue" functions, ingress_enqueue returns a
131          * firewall FW_* code.
132          */
133 #ifdef CONFIG_NET_CLS_ACT
134         sch->bstats.packets++;
135         sch->bstats.bytes += skb->len;
136         switch (result) {
137         case TC_ACT_SHOT:
138                 result = TC_ACT_SHOT;
139                 sch->qstats.drops++;
140                 break;
141         case TC_ACT_STOLEN:
142         case TC_ACT_QUEUED:
143                 result = TC_ACT_STOLEN;
144                 break;
145         case TC_ACT_RECLASSIFY:
146         case TC_ACT_OK:
147                 skb->tc_index = TC_H_MIN(res.classid);
148         default:
149                 result = TC_ACT_OK;
150                 break;
151         }
152 #else
153         D2PRINTK("Overriding result to ACCEPT\n");
154         result = NF_ACCEPT;
155         sch->bstats.packets++;
156         sch->bstats.bytes += skb->len;
157 #endif
158
159         return result;
160 }
161
162 static struct sk_buff *ingress_dequeue(struct Qdisc *sch)
163 {
164 /*
165         struct ingress_qdisc_data *p = PRIV(sch);
166         D2PRINTK("ingress_dequeue(sch %p,[qdisc %p])\n",sch,PRIV(p));
167 */
168         return NULL;
169 }
170
171 static int ingress_requeue(struct sk_buff *skb, struct Qdisc *sch)
172 {
173 /*
174         struct ingress_qdisc_data *p = PRIV(sch);
175         D2PRINTK("ingress_requeue(skb %p,sch %p,[qdisc %p])\n",skb,sch,PRIV(p));
176 */
177         return 0;
178 }
179
180 static unsigned int ingress_drop(struct Qdisc *sch)
181 {
182 #ifdef DEBUG_INGRESS
183         struct ingress_qdisc_data *p = PRIV(sch);
184 #endif
185         DPRINTK("ingress_drop(sch %p,[qdisc %p])\n", sch, p);
186         return 0;
187 }
188
189 #ifndef CONFIG_NET_CLS_ACT
190 #ifdef CONFIG_NETFILTER
191 static unsigned int ing_hook(unsigned int hook, struct sk_buff *skb,
192                              const struct net_device *indev,
193                              const struct net_device *outdev,
194                              int (*okfn)(struct sk_buff *))
195 {
196
197         struct Qdisc *q;
198         struct net_device *dev = skb->dev;
199         int fwres = NF_ACCEPT;
200
201         DPRINTK("ing_hook: skb %s dev=%s len=%u\n",
202                 skb->sk ? "(owned)" : "(unowned)",
203                 skb->dev ? skb->dev->name : "(no dev)",
204                 skb->len);
205
206         if (dev->qdisc_ingress) {
207                 spin_lock(&dev->ingress_lock);
208                 if ((q = dev->qdisc_ingress) != NULL)
209                         fwres = q->enqueue(skb, q);
210                 spin_unlock(&dev->ingress_lock);
211         }
212
213         return fwres;
214 }
215
216 /* after ipt_filter */
217 static struct nf_hook_ops ing_ops[] __read_mostly = {
218         {
219                 .hook           = ing_hook,
220                 .owner          = THIS_MODULE,
221                 .pf             = PF_INET,
222                 .hooknum        = NF_INET_PRE_ROUTING,
223                 .priority       = NF_IP_PRI_FILTER + 1,
224         },
225         {
226                 .hook           = ing_hook,
227                 .owner          = THIS_MODULE,
228                 .pf             = PF_INET6,
229                 .hooknum        = NF_INET_PRE_ROUTING,
230                 .priority       = NF_IP6_PRI_FILTER + 1,
231         },
232 };
233 #endif
234 #endif
235
236 static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
237 {
238         struct ingress_qdisc_data *p = PRIV(sch);
239
240         /* Make sure either netfilter or preferably CLS_ACT is
241          * compiled in */
242 #ifndef CONFIG_NET_CLS_ACT
243 #ifndef CONFIG_NETFILTER
244         printk("You MUST compile classifier actions into the kernel\n");
245         return -EINVAL;
246 #else
247         printk("Ingress scheduler: Classifier actions prefered over netfilter\n");
248 #endif
249 #endif
250
251 #ifndef CONFIG_NET_CLS_ACT
252 #ifdef CONFIG_NETFILTER
253         if (!nf_registered) {
254                 if (nf_register_hooks(ing_ops, ARRAY_SIZE(ing_ops)) < 0) {
255                         printk("ingress qdisc registration error \n");
256                         return -EINVAL;
257                 }
258                 nf_registered++;
259         }
260 #endif
261 #endif
262
263         DPRINTK("ingress_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt);
264         p->q = &noop_qdisc;
265         return 0;
266 }
267
268 static void ingress_reset(struct Qdisc *sch)
269 {
270         struct ingress_qdisc_data *p = PRIV(sch);
271
272         DPRINTK("ingress_reset(sch %p,[qdisc %p])\n", sch, p);
273
274 /*
275 #if 0
276 */
277 /* for future use */
278         qdisc_reset(p->q);
279 /*
280 #endif
281 */
282 }
283
284 /* ------------------------------------------------------------- */
285
286 static void ingress_destroy(struct Qdisc *sch)
287 {
288         struct ingress_qdisc_data *p = PRIV(sch);
289
290         DPRINTK("ingress_destroy(sch %p,[qdisc %p])\n", sch, p);
291         tcf_destroy_chain(p->filter_list);
292 #if 0
293 /* for future use */
294         qdisc_destroy(p->q);
295 #endif
296 }
297
298 static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)
299 {
300         unsigned char *b = skb_tail_pointer(skb);
301         struct rtattr *rta;
302
303         rta = (struct rtattr *)b;
304         RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
305         rta->rta_len = skb_tail_pointer(skb) - b;
306         return skb->len;
307
308 rtattr_failure:
309         nlmsg_trim(skb, b);
310         return -1;
311 }
312
313 static const struct Qdisc_class_ops ingress_class_ops = {
314         .graft          =       ingress_graft,
315         .leaf           =       ingress_leaf,
316         .get            =       ingress_get,
317         .put            =       ingress_put,
318         .change         =       ingress_change,
319         .walk           =       ingress_walk,
320         .tcf_chain      =       ingress_find_tcf,
321         .bind_tcf       =       ingress_bind_filter,
322         .unbind_tcf     =       ingress_put,
323 };
324
325 static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
326         .cl_ops         =       &ingress_class_ops,
327         .id             =       "ingress",
328         .priv_size      =       sizeof(struct ingress_qdisc_data),
329         .enqueue        =       ingress_enqueue,
330         .dequeue        =       ingress_dequeue,
331         .requeue        =       ingress_requeue,
332         .drop           =       ingress_drop,
333         .init           =       ingress_init,
334         .reset          =       ingress_reset,
335         .destroy        =       ingress_destroy,
336         .dump           =       ingress_dump,
337         .owner          =       THIS_MODULE,
338 };
339
340 static int __init ingress_module_init(void)
341 {
342         int ret = 0;
343
344         if ((ret = register_qdisc(&ingress_qdisc_ops)) < 0) {
345                 printk("Unable to register Ingress qdisc\n");
346                 return ret;
347         }
348
349         return ret;
350 }
351
352 static void __exit ingress_module_exit(void)
353 {
354         unregister_qdisc(&ingress_qdisc_ops);
355 #ifndef CONFIG_NET_CLS_ACT
356 #ifdef CONFIG_NETFILTER
357         if (nf_registered)
358                 nf_unregister_hooks(ing_ops, ARRAY_SIZE(ing_ops));
359 #endif
360 #endif
361 }
362
363 module_init(ingress_module_init)
364 module_exit(ingress_module_exit)
365 MODULE_LICENSE("GPL");