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/skbuff.h> 13 #include <linux/netdevice.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 <linux/smp.h> 19 #include <net/pkt_sched.h> 20 #include <asm/byteorder.h> 21 #include <asm/uaccess.h> 22 #include <linux/kmod.h> 23 #include <linux/stat.h> 24 #include <linux/interrupt.h> 25 #include <linux/list.h> 26 27 28 #undef DEBUG_INGRESS 29 30 #ifdef DEBUG_INGRESS /* control */ 31 #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 32 #else 33 #define DPRINTK(format,args...) 34 #endif 35 36 #if 0 /* data */ 37 #define D2PRINTK(format,args...) printk(KERN_DEBUG format,##args) 38 #else 39 #define D2PRINTK(format,args...) 40 #endif 41 42 43 #define PRIV(sch) qdisc_priv(sch) 44 45 46 /* Thanks to Doron Oz for this hack 47 */ 48 #ifndef CONFIG_NET_CLS_ACT 49 #ifdef CONFIG_NETFILTER 50 static int nf_registered; 51 #endif 52 #endif 53 54 struct ingress_qdisc_data { 55 struct Qdisc *q; 56 struct tcf_proto *filter_list; 57 }; 58 59 60 /* ------------------------- Class/flow operations ------------------------- */ 61 62 63 static int ingress_graft(struct Qdisc *sch,unsigned long arg, 64 struct Qdisc *new,struct Qdisc **old) 65 { 66 #ifdef DEBUG_INGRESS 67 struct ingress_qdisc_data *p = PRIV(sch); 68 #endif 69 70 DPRINTK("ingress_graft(sch %p,[qdisc %p],new %p,old %p)\n", 71 sch, p, new, old); 72 DPRINTK("\n ingress_graft: You cannot add qdiscs to classes"); 73 return 1; 74 } 75 76 77 static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) 78 { 79 return NULL; 80 } 81 82 83 static unsigned long ingress_get(struct Qdisc *sch,u32 classid) 84 { 85 #ifdef DEBUG_INGRESS 86 struct ingress_qdisc_data *p = PRIV(sch); 87 #endif 88 DPRINTK("ingress_get(sch %p,[qdisc %p],classid %x)\n", sch, p, classid); 89 return TC_H_MIN(classid) + 1; 90 } 91 92 93 static unsigned long ingress_bind_filter(struct Qdisc *sch, 94 unsigned long parent, u32 classid) 95 { 96 return ingress_get(sch, classid); 97 } 98 99 100 static void ingress_put(struct Qdisc *sch, unsigned long cl) 101 { 102 } 103 104 105 static int ingress_change(struct Qdisc *sch, u32 classid, u32 parent, 106 struct rtattr **tca, unsigned long *arg) 107 { 108 #ifdef DEBUG_INGRESS 109 struct ingress_qdisc_data *p = PRIV(sch); 110 #endif 111 DPRINTK("ingress_change(sch %p,[qdisc %p],classid %x,parent %x)," 112 "arg 0x%lx\n", sch, p, classid, parent, *arg); 113 DPRINTK("No effect. sch_ingress doesn't maintain classes at the moment"); 114 return 0; 115 } 116 117 118 119 static void ingress_walk(struct Qdisc *sch,struct qdisc_walker *walker) 120 { 121 #ifdef DEBUG_INGRESS 122 struct ingress_qdisc_data *p = PRIV(sch); 123 #endif 124 DPRINTK("ingress_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker); 125 DPRINTK("No effect. sch_ingress doesn't maintain classes at the moment"); 126 } 127 128 129 static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch,unsigned long cl) 130 { 131 struct ingress_qdisc_data *p = PRIV(sch); 132 133 return &p->filter_list; 134 } 135 136 137 /* --------------------------- Qdisc operations ---------------------------- */ 138 139 140 static int ingress_enqueue(struct sk_buff *skb,struct Qdisc *sch) 141 { 142 struct ingress_qdisc_data *p = PRIV(sch); 143 struct tcf_result res; 144 int result; 145 146 D2PRINTK("ingress_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p); 147 result = tc_classify(skb, p->filter_list, &res); 148 D2PRINTK("result %d class 0x%04x\n", result, res.classid); 149 /* 150 * Unlike normal "enqueue" functions, ingress_enqueue returns a 151 * firewall FW_* code. 152 */ 153 #ifdef CONFIG_NET_CLS_ACT 154 sch->bstats.packets++; 155 sch->bstats.bytes += skb->len; 156 switch (result) { 157 case TC_ACT_SHOT: 158 result = TC_ACT_SHOT; 159 sch->qstats.drops++; 160 break; 161 case TC_ACT_STOLEN: 162 case TC_ACT_QUEUED: 163 result = TC_ACT_STOLEN; 164 break; 165 case TC_ACT_RECLASSIFY: 166 case TC_ACT_OK: 167 case TC_ACT_UNSPEC: 168 default: 169 skb->tc_index = TC_H_MIN(res.classid); 170 result = TC_ACT_OK; 171 break; 172 }; 173 /* backward compat */ 174 #else 175 #ifdef CONFIG_NET_CLS_POLICE 176 switch (result) { 177 case TC_POLICE_SHOT: 178 result = NF_DROP; 179 sch->qstats.drops++; 180 break; 181 case TC_POLICE_RECLASSIFY: /* DSCP remarking here ? */ 182 case TC_POLICE_OK: 183 case TC_POLICE_UNSPEC: 184 default: 185 sch->bstats.packets++; 186 sch->bstats.bytes += skb->len; 187 result = NF_ACCEPT; 188 break; 189 }; 190 191 #else 192 D2PRINTK("Overriding result to ACCEPT\n"); 193 result = NF_ACCEPT; 194 sch->bstats.packets++; 195 sch->bstats.bytes += skb->len; 196 #endif 197 #endif 198 199 return result; 200 } 201 202 203 static struct sk_buff *ingress_dequeue(struct Qdisc *sch) 204 { 205 /* 206 struct ingress_qdisc_data *p = PRIV(sch); 207 D2PRINTK("ingress_dequeue(sch %p,[qdisc %p])\n",sch,PRIV(p)); 208 */ 209 return NULL; 210 } 211 212 213 static int ingress_requeue(struct sk_buff *skb,struct Qdisc *sch) 214 { 215 /* 216 struct ingress_qdisc_data *p = PRIV(sch); 217 D2PRINTK("ingress_requeue(skb %p,sch %p,[qdisc %p])\n",skb,sch,PRIV(p)); 218 */ 219 return 0; 220 } 221 222 static unsigned int ingress_drop(struct Qdisc *sch) 223 { 224 #ifdef DEBUG_INGRESS 225 struct ingress_qdisc_data *p = PRIV(sch); 226 #endif 227 DPRINTK("ingress_drop(sch %p,[qdisc %p])\n", sch, p); 228 return 0; 229 } 230 231 #ifndef CONFIG_NET_CLS_ACT 232 #ifdef CONFIG_NETFILTER 233 static unsigned int 234 ing_hook(unsigned int hook, struct sk_buff **pskb, 235 const struct net_device *indev, 236 const struct net_device *outdev, 237 int (*okfn)(struct sk_buff *)) 238 { 239 240 struct Qdisc *q; 241 struct sk_buff *skb = *pskb; 242 struct net_device *dev = skb->dev; 243 int fwres=NF_ACCEPT; 244 245 DPRINTK("ing_hook: skb %s dev=%s len=%u\n", 246 skb->sk ? "(owned)" : "(unowned)", 247 skb->dev ? (*pskb)->dev->name : "(no dev)", 248 skb->len); 249 250 /* 251 revisit later: Use a private since lock dev->queue_lock is also 252 used on the egress (might slow things for an iota) 253 */ 254 255 if (dev->qdisc_ingress) { 256 spin_lock(&dev->queue_lock); 257 if ((q = dev->qdisc_ingress) != NULL) 258 fwres = q->enqueue(skb, q); 259 spin_unlock(&dev->queue_lock); 260 } 261 262 return fwres; 263 } 264 265 /* after ipt_filter */ 266 static struct nf_hook_ops ing_ops = { 267 .hook = ing_hook, 268 .owner = THIS_MODULE, 269 .pf = PF_INET, 270 .hooknum = NF_IP_PRE_ROUTING, 271 .priority = NF_IP_PRI_FILTER + 1, 272 }; 273 274 static struct nf_hook_ops ing6_ops = { 275 .hook = ing_hook, 276 .owner = THIS_MODULE, 277 .pf = PF_INET6, 278 .hooknum = NF_IP6_PRE_ROUTING, 279 .priority = NF_IP6_PRI_FILTER + 1, 280 }; 281 282 #endif 283 #endif 284 285 static int ingress_init(struct Qdisc *sch,struct rtattr *opt) 286 { 287 struct ingress_qdisc_data *p = PRIV(sch); 288 289 /* Make sure either netfilter or preferably CLS_ACT is 290 * compiled in */ 291 #ifndef CONFIG_NET_CLS_ACT 292 #ifndef CONFIG_NETFILTER 293 printk("You MUST compile classifier actions into the kernel\n"); 294 return -EINVAL; 295 #else 296 printk("Ingress scheduler: Classifier actions prefered over netfilter\n"); 297 #endif 298 #endif 299 300 #ifndef CONFIG_NET_CLS_ACT 301 #ifdef CONFIG_NETFILTER 302 if (!nf_registered) { 303 if (nf_register_hook(&ing_ops) < 0) { 304 printk("ingress qdisc registration error \n"); 305 return -EINVAL; 306 } 307 nf_registered++; 308 309 if (nf_register_hook(&ing6_ops) < 0) { 310 printk("IPv6 ingress qdisc registration error, " \ 311 "disabling IPv6 support.\n"); 312 } else 313 nf_registered++; 314 } 315 #endif 316 #endif 317 318 DPRINTK("ingress_init(sch %p,[qdisc %p],opt %p)\n",sch,p,opt); 319 p->q = &noop_qdisc; 320 return 0; 321 } 322 323 324 static void ingress_reset(struct Qdisc *sch) 325 { 326 struct ingress_qdisc_data *p = PRIV(sch); 327 328 DPRINTK("ingress_reset(sch %p,[qdisc %p])\n", sch, p); 329 330 /* 331 #if 0 332 */ 333 /* for future use */ 334 qdisc_reset(p->q); 335 /* 336 #endif 337 */ 338 } 339 340 /* ------------------------------------------------------------- */ 341 342 343 /* ------------------------------------------------------------- */ 344 345 static void ingress_destroy(struct Qdisc *sch) 346 { 347 struct ingress_qdisc_data *p = PRIV(sch); 348 struct tcf_proto *tp; 349 350 DPRINTK("ingress_destroy(sch %p,[qdisc %p])\n", sch, p); 351 while (p->filter_list) { 352 tp = p->filter_list; 353 p->filter_list = tp->next; 354 tcf_destroy(tp); 355 } 356 #if 0 357 /* for future use */ 358 qdisc_destroy(p->q); 359 #endif 360 } 361 362 363 static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb) 364 { 365 unsigned char *b = skb->tail; 366 struct rtattr *rta; 367 368 rta = (struct rtattr *) b; 369 RTA_PUT(skb, TCA_OPTIONS, 0, NULL); 370 rta->rta_len = skb->tail - b; 371 return skb->len; 372 373 rtattr_failure: 374 skb_trim(skb, b - skb->data); 375 return -1; 376 } 377 378 static struct Qdisc_class_ops ingress_class_ops = { 379 .graft = ingress_graft, 380 .leaf = ingress_leaf, 381 .get = ingress_get, 382 .put = ingress_put, 383 .change = ingress_change, 384 .delete = NULL, 385 .walk = ingress_walk, 386 .tcf_chain = ingress_find_tcf, 387 .bind_tcf = ingress_bind_filter, 388 .unbind_tcf = ingress_put, 389 .dump = NULL, 390 }; 391 392 static struct Qdisc_ops ingress_qdisc_ops = { 393 .next = NULL, 394 .cl_ops = &ingress_class_ops, 395 .id = "ingress", 396 .priv_size = sizeof(struct ingress_qdisc_data), 397 .enqueue = ingress_enqueue, 398 .dequeue = ingress_dequeue, 399 .requeue = ingress_requeue, 400 .drop = ingress_drop, 401 .init = ingress_init, 402 .reset = ingress_reset, 403 .destroy = ingress_destroy, 404 .change = NULL, 405 .dump = ingress_dump, 406 .owner = THIS_MODULE, 407 }; 408 409 static int __init ingress_module_init(void) 410 { 411 int ret = 0; 412 413 if ((ret = register_qdisc(&ingress_qdisc_ops)) < 0) { 414 printk("Unable to register Ingress qdisc\n"); 415 return ret; 416 } 417 418 return ret; 419 } 420 static void __exit ingress_module_exit(void) 421 { 422 unregister_qdisc(&ingress_qdisc_ops); 423 #ifndef CONFIG_NET_CLS_ACT 424 #ifdef CONFIG_NETFILTER 425 if (nf_registered) { 426 nf_unregister_hook(&ing_ops); 427 if (nf_registered > 1) 428 nf_unregister_hook(&ing6_ops); 429 } 430 #endif 431 #endif 432 } 433 module_init(ingress_module_init) 434 module_exit(ingress_module_exit) 435 MODULE_LICENSE("GPL"); 436