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 /* Thanks to Doron Oz for this hack */ 23 #if !defined(CONFIG_NET_CLS_ACT) && defined(CONFIG_NETFILTER) 24 static int nf_registered; 25 #endif 26 27 struct ingress_qdisc_data { 28 struct tcf_proto *filter_list; 29 }; 30 31 /* ------------------------- Class/flow operations ------------------------- */ 32 33 static int ingress_graft(struct Qdisc *sch, unsigned long arg, 34 struct Qdisc *new, struct Qdisc **old) 35 { 36 return -EOPNOTSUPP; 37 } 38 39 static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) 40 { 41 return NULL; 42 } 43 44 static unsigned long ingress_get(struct Qdisc *sch, u32 classid) 45 { 46 return TC_H_MIN(classid) + 1; 47 } 48 49 static unsigned long ingress_bind_filter(struct Qdisc *sch, 50 unsigned long parent, u32 classid) 51 { 52 return ingress_get(sch, classid); 53 } 54 55 static void ingress_put(struct Qdisc *sch, unsigned long cl) 56 { 57 } 58 59 static int ingress_change(struct Qdisc *sch, u32 classid, u32 parent, 60 struct rtattr **tca, unsigned long *arg) 61 { 62 return 0; 63 } 64 65 static void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker) 66 { 67 return; 68 } 69 70 static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch, unsigned long cl) 71 { 72 struct ingress_qdisc_data *p = qdisc_priv(sch); 73 74 return &p->filter_list; 75 } 76 77 /* --------------------------- Qdisc operations ---------------------------- */ 78 79 static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch) 80 { 81 struct ingress_qdisc_data *p = qdisc_priv(sch); 82 struct tcf_result res; 83 int result; 84 85 result = tc_classify(skb, p->filter_list, &res); 86 87 /* 88 * Unlike normal "enqueue" functions, ingress_enqueue returns a 89 * firewall FW_* code. 90 */ 91 #ifdef CONFIG_NET_CLS_ACT 92 sch->bstats.packets++; 93 sch->bstats.bytes += skb->len; 94 switch (result) { 95 case TC_ACT_SHOT: 96 result = TC_ACT_SHOT; 97 sch->qstats.drops++; 98 break; 99 case TC_ACT_STOLEN: 100 case TC_ACT_QUEUED: 101 result = TC_ACT_STOLEN; 102 break; 103 case TC_ACT_RECLASSIFY: 104 case TC_ACT_OK: 105 skb->tc_index = TC_H_MIN(res.classid); 106 default: 107 result = TC_ACT_OK; 108 break; 109 } 110 #else 111 result = NF_ACCEPT; 112 sch->bstats.packets++; 113 sch->bstats.bytes += skb->len; 114 #endif 115 116 return result; 117 } 118 119 #if !defined(CONFIG_NET_CLS_ACT) && defined(CONFIG_NETFILTER) 120 static unsigned int ing_hook(unsigned int hook, struct sk_buff *skb, 121 const struct net_device *indev, 122 const struct net_device *outdev, 123 int (*okfn)(struct sk_buff *)) 124 { 125 126 struct Qdisc *q; 127 struct net_device *dev = skb->dev; 128 int fwres = NF_ACCEPT; 129 130 if (dev->qdisc_ingress) { 131 spin_lock(&dev->ingress_lock); 132 if ((q = dev->qdisc_ingress) != NULL) 133 fwres = q->enqueue(skb, q); 134 spin_unlock(&dev->ingress_lock); 135 } 136 137 return fwres; 138 } 139 140 /* after ipt_filter */ 141 static struct nf_hook_ops ing_ops[] __read_mostly = { 142 { 143 .hook = ing_hook, 144 .owner = THIS_MODULE, 145 .pf = PF_INET, 146 .hooknum = NF_INET_PRE_ROUTING, 147 .priority = NF_IP_PRI_FILTER + 1, 148 }, 149 { 150 .hook = ing_hook, 151 .owner = THIS_MODULE, 152 .pf = PF_INET6, 153 .hooknum = NF_INET_PRE_ROUTING, 154 .priority = NF_IP6_PRI_FILTER + 1, 155 }, 156 }; 157 #endif 158 159 static int ingress_init(struct Qdisc *sch, struct rtattr *opt) 160 { 161 #if !defined(CONFIG_NET_CLS_ACT) && defined(CONFIG_NETFILTER) 162 printk("Ingress scheduler: Classifier actions prefered over netfilter\n"); 163 164 if (!nf_registered) { 165 if (nf_register_hooks(ing_ops, ARRAY_SIZE(ing_ops)) < 0) { 166 printk("ingress qdisc registration error \n"); 167 return -EINVAL; 168 } 169 nf_registered++; 170 } 171 #endif 172 return 0; 173 } 174 175 /* ------------------------------------------------------------- */ 176 177 static void ingress_destroy(struct Qdisc *sch) 178 { 179 struct ingress_qdisc_data *p = qdisc_priv(sch); 180 181 tcf_destroy_chain(p->filter_list); 182 } 183 184 static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb) 185 { 186 unsigned char *b = skb_tail_pointer(skb); 187 struct rtattr *rta; 188 189 rta = (struct rtattr *)b; 190 RTA_PUT(skb, TCA_OPTIONS, 0, NULL); 191 rta->rta_len = skb_tail_pointer(skb) - b; 192 return skb->len; 193 194 rtattr_failure: 195 nlmsg_trim(skb, b); 196 return -1; 197 } 198 199 static const struct Qdisc_class_ops ingress_class_ops = { 200 .graft = ingress_graft, 201 .leaf = ingress_leaf, 202 .get = ingress_get, 203 .put = ingress_put, 204 .change = ingress_change, 205 .walk = ingress_walk, 206 .tcf_chain = ingress_find_tcf, 207 .bind_tcf = ingress_bind_filter, 208 .unbind_tcf = ingress_put, 209 }; 210 211 static struct Qdisc_ops ingress_qdisc_ops __read_mostly = { 212 .cl_ops = &ingress_class_ops, 213 .id = "ingress", 214 .priv_size = sizeof(struct ingress_qdisc_data), 215 .enqueue = ingress_enqueue, 216 .init = ingress_init, 217 .destroy = ingress_destroy, 218 .dump = ingress_dump, 219 .owner = THIS_MODULE, 220 }; 221 222 static int __init ingress_module_init(void) 223 { 224 return register_qdisc(&ingress_qdisc_ops); 225 } 226 227 static void __exit ingress_module_exit(void) 228 { 229 unregister_qdisc(&ingress_qdisc_ops); 230 #if !defined(CONFIG_NET_CLS_ACT) && defined(CONFIG_NETFILTER) 231 if (nf_registered) 232 nf_unregister_hooks(ing_ops, ARRAY_SIZE(ing_ops)); 233 #endif 234 } 235 236 module_init(ingress_module_init) 237 module_exit(ingress_module_exit) 238 MODULE_LICENSE("GPL"); 239