1 /* 2 * net/sched/mirred.c packet mirroring and redirect actions 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 * 9 * Authors: Jamal Hadi Salim (2002-4) 10 * 11 * TODO: Add ingress support (and socket redirect support) 12 * 13 */ 14 15 #include <asm/uaccess.h> 16 #include <asm/system.h> 17 #include <asm/bitops.h> 18 #include <linux/types.h> 19 #include <linux/kernel.h> 20 #include <linux/sched.h> 21 #include <linux/string.h> 22 #include <linux/mm.h> 23 #include <linux/socket.h> 24 #include <linux/sockios.h> 25 #include <linux/in.h> 26 #include <linux/errno.h> 27 #include <linux/interrupt.h> 28 #include <linux/netdevice.h> 29 #include <linux/skbuff.h> 30 #include <linux/rtnetlink.h> 31 #include <linux/module.h> 32 #include <linux/init.h> 33 #include <linux/proc_fs.h> 34 #include <net/sock.h> 35 #include <net/pkt_sched.h> 36 #include <linux/tc_act/tc_mirred.h> 37 #include <net/tc_act/tc_mirred.h> 38 39 #include <linux/etherdevice.h> 40 #include <linux/if_arp.h> 41 42 43 /* use generic hash table */ 44 #define MY_TAB_SIZE 8 45 #define MY_TAB_MASK (MY_TAB_SIZE - 1) 46 static u32 idx_gen; 47 static struct tcf_mirred *tcf_mirred_ht[MY_TAB_SIZE]; 48 static DEFINE_RWLOCK(mirred_lock); 49 50 /* ovewrride the defaults */ 51 #define tcf_st tcf_mirred 52 #define tc_st tc_mirred 53 #define tcf_t_lock mirred_lock 54 #define tcf_ht tcf_mirred_ht 55 56 #define CONFIG_NET_ACT_INIT 1 57 #include <net/pkt_act.h> 58 59 static inline int 60 tcf_mirred_release(struct tcf_mirred *p, int bind) 61 { 62 if (p) { 63 if (bind) 64 p->bindcnt--; 65 p->refcnt--; 66 if(!p->bindcnt && p->refcnt <= 0) { 67 dev_put(p->dev); 68 tcf_hash_destroy(p); 69 return 1; 70 } 71 } 72 return 0; 73 } 74 75 static int 76 tcf_mirred_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, 77 int ovr, int bind) 78 { 79 struct rtattr *tb[TCA_MIRRED_MAX]; 80 struct tc_mirred *parm; 81 struct tcf_mirred *p; 82 struct net_device *dev = NULL; 83 int ret = 0; 84 int ok_push = 0; 85 86 if (rta == NULL || rtattr_parse_nested(tb, TCA_MIRRED_MAX, rta) < 0) 87 return -EINVAL; 88 89 if (tb[TCA_MIRRED_PARMS-1] == NULL || 90 RTA_PAYLOAD(tb[TCA_MIRRED_PARMS-1]) < sizeof(*parm)) 91 return -EINVAL; 92 parm = RTA_DATA(tb[TCA_MIRRED_PARMS-1]); 93 94 if (parm->ifindex) { 95 dev = __dev_get_by_index(parm->ifindex); 96 if (dev == NULL) 97 return -ENODEV; 98 switch (dev->type) { 99 case ARPHRD_TUNNEL: 100 case ARPHRD_TUNNEL6: 101 case ARPHRD_SIT: 102 case ARPHRD_IPGRE: 103 case ARPHRD_VOID: 104 case ARPHRD_NONE: 105 ok_push = 0; 106 break; 107 default: 108 ok_push = 1; 109 break; 110 } 111 } 112 113 p = tcf_hash_check(parm->index, a, ovr, bind); 114 if (p == NULL) { 115 if (!parm->ifindex) 116 return -EINVAL; 117 p = tcf_hash_create(parm->index, est, a, sizeof(*p), ovr, bind); 118 if (p == NULL) 119 return -ENOMEM; 120 ret = ACT_P_CREATED; 121 } else { 122 if (!ovr) { 123 tcf_mirred_release(p, bind); 124 return -EEXIST; 125 } 126 } 127 128 spin_lock_bh(&p->lock); 129 p->action = parm->action; 130 p->eaction = parm->eaction; 131 if (parm->ifindex) { 132 p->ifindex = parm->ifindex; 133 if (ret != ACT_P_CREATED) 134 dev_put(p->dev); 135 p->dev = dev; 136 dev_hold(dev); 137 p->ok_push = ok_push; 138 } 139 spin_unlock_bh(&p->lock); 140 if (ret == ACT_P_CREATED) 141 tcf_hash_insert(p); 142 143 DPRINTK("tcf_mirred_init index %d action %d eaction %d device %s " 144 "ifindex %d\n", parm->index, parm->action, parm->eaction, 145 dev->name, parm->ifindex); 146 return ret; 147 } 148 149 static int 150 tcf_mirred_cleanup(struct tc_action *a, int bind) 151 { 152 struct tcf_mirred *p = PRIV(a, mirred); 153 154 if (p != NULL) 155 return tcf_mirred_release(p, bind); 156 return 0; 157 } 158 159 static int 160 tcf_mirred(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res) 161 { 162 struct tcf_mirred *p = PRIV(a, mirred); 163 struct net_device *dev; 164 struct sk_buff *skb2 = NULL; 165 u32 at = G_TC_AT(skb->tc_verd); 166 167 spin_lock(&p->lock); 168 169 dev = p->dev; 170 p->tm.lastuse = jiffies; 171 172 if (!(dev->flags&IFF_UP) ) { 173 if (net_ratelimit()) 174 printk("mirred to Houston: device %s is gone!\n", 175 dev->name); 176 bad_mirred: 177 if (skb2 != NULL) 178 kfree_skb(skb2); 179 p->qstats.overlimits++; 180 p->bstats.bytes += skb->len; 181 p->bstats.packets++; 182 spin_unlock(&p->lock); 183 /* should we be asking for packet to be dropped? 184 * may make sense for redirect case only 185 */ 186 return TC_ACT_SHOT; 187 } 188 189 skb2 = skb_clone(skb, GFP_ATOMIC); 190 if (skb2 == NULL) 191 goto bad_mirred; 192 if (p->eaction != TCA_EGRESS_MIRROR && p->eaction != TCA_EGRESS_REDIR) { 193 if (net_ratelimit()) 194 printk("tcf_mirred unknown action %d\n", p->eaction); 195 goto bad_mirred; 196 } 197 198 p->bstats.bytes += skb2->len; 199 p->bstats.packets++; 200 if (!(at & AT_EGRESS)) 201 if (p->ok_push) 202 skb_push(skb2, skb2->dev->hard_header_len); 203 204 /* mirror is always swallowed */ 205 if (p->eaction != TCA_EGRESS_MIRROR) 206 skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at); 207 208 skb2->dev = dev; 209 skb2->input_dev = skb->dev; 210 dev_queue_xmit(skb2); 211 spin_unlock(&p->lock); 212 return p->action; 213 } 214 215 static int 216 tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) 217 { 218 unsigned char *b = skb->tail; 219 struct tc_mirred opt; 220 struct tcf_mirred *p = PRIV(a, mirred); 221 struct tcf_t t; 222 223 opt.index = p->index; 224 opt.action = p->action; 225 opt.refcnt = p->refcnt - ref; 226 opt.bindcnt = p->bindcnt - bind; 227 opt.eaction = p->eaction; 228 opt.ifindex = p->ifindex; 229 DPRINTK("tcf_mirred_dump index %d action %d eaction %d ifindex %d\n", 230 p->index, p->action, p->eaction, p->ifindex); 231 RTA_PUT(skb, TCA_MIRRED_PARMS, sizeof(opt), &opt); 232 t.install = jiffies_to_clock_t(jiffies - p->tm.install); 233 t.lastuse = jiffies_to_clock_t(jiffies - p->tm.lastuse); 234 t.expires = jiffies_to_clock_t(p->tm.expires); 235 RTA_PUT(skb, TCA_MIRRED_TM, sizeof(t), &t); 236 return skb->len; 237 238 rtattr_failure: 239 skb_trim(skb, b - skb->data); 240 return -1; 241 } 242 243 static struct tc_action_ops act_mirred_ops = { 244 .kind = "mirred", 245 .type = TCA_ACT_MIRRED, 246 .capab = TCA_CAP_NONE, 247 .owner = THIS_MODULE, 248 .act = tcf_mirred, 249 .dump = tcf_mirred_dump, 250 .cleanup = tcf_mirred_cleanup, 251 .lookup = tcf_hash_search, 252 .init = tcf_mirred_init, 253 .walk = tcf_generic_walker 254 }; 255 256 MODULE_AUTHOR("Jamal Hadi Salim(2002)"); 257 MODULE_DESCRIPTION("Device Mirror/redirect actions"); 258 MODULE_LICENSE("GPL"); 259 260 static int __init 261 mirred_init_module(void) 262 { 263 printk("Mirror/redirect action on\n"); 264 return tcf_register_action(&act_mirred_ops); 265 } 266 267 static void __exit 268 mirred_cleanup_module(void) 269 { 270 tcf_unregister_action(&act_mirred_ops); 271 } 272 273 module_init(mirred_init_module); 274 module_exit(mirred_cleanup_module); 275