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