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