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