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