Commit ad162bcb authored by Jamal Hadi Salim's avatar Jamal Hadi Salim Committed by David S. Miller

[NET]: Add Mirred TC action.

Signed-off-by: default avatarJamal Hadi Salim <hadi@cyberus.ca>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8f8d3178
#ifndef __LINUX_TC_MIR_H
#define __LINUX_TC_MIR_H
#include <linux/pkt_cls.h>
#define TCA_ACT_MIRRED 8
#define TCA_EGRESS_REDIR 1 /* packet redirect to EGRESS*/
#define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
#define TCA_INGRESS_REDIR 3 /* packet redirect to INGRESS*/
#define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
struct tc_mirred
{
tc_gen;
int eaction; /* one of IN/EGRESS_MIRROR/REDIR */
__u32 ifindex; /* ifindex of egress port */
};
enum
{
TCA_MIRRED_UNSPEC,
TCA_MIRRED_TM,
TCA_MIRRED_PARMS,
__TCA_MIRRED_MAX
};
#define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
#endif
#ifndef __NET_TC_MIR_H
#define __NET_TC_MIR_H
#include <net/pkt_sched.h>
struct tcf_mirred
{
tca_gen(mirred);
int eaction;
int ifindex;
int ok_push;
struct net_device *dev;
};
#endif
......@@ -399,3 +399,10 @@ config GACT_PROB
depends on NET_ACT_GACT
---help---
Allows generic actions to be randomly or deterministically used
config NET_ACT_MIRRED
tristate "Packet In/Egress redirecton/mirror Actions"
depends on NET_CLS_ACT
---help---
requires new iproute2
This allows packets to be mirrored or redirected to netdevices
......@@ -11,6 +11,7 @@ obj-$(CONFIG_NET_CLS_ACT) += act_api.o
obj-$(CONFIG_NET_ACT_POLICE) += police.o
obj-$(CONFIG_NET_CLS_POLICE) += police.o
obj-$(CONFIG_NET_ACT_GACT) += gact.o
obj-$(CONFIG_NET_ACT_MIRRED) += mirred.o
obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o
obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o
obj-$(CONFIG_NET_SCH_HPFQ) += sch_hpfq.o
......
/*
* net/sched/mirred.c packet mirroring and redirect actions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Jamal Hadi Salim (2002-4)
*
* TODO: Add ingress support (and socket redirect support)
*
*/
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <net/sock.h>
#include <net/pkt_sched.h>
#include <linux/tc_act/tc_mirred.h>
#include <net/tc_act/tc_mirred.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
/* use generic hash table */
#define MY_TAB_SIZE 8
#define MY_TAB_MASK (MY_TAB_SIZE - 1)
static u32 idx_gen;
static struct tcf_mirred *tcf_mirred_ht[MY_TAB_SIZE];
static rwlock_t mirred_lock = RW_LOCK_UNLOCKED;
/* ovewrride the defaults */
#define tcf_st tcf_mirred
#define tc_st tc_mirred
#define tcf_t_lock mirred_lock
#define tcf_ht tcf_mirred_ht
#define CONFIG_NET_ACT_INIT 1
#include <net/pkt_act.h>
static inline int
tcf_mirred_release(struct tcf_mirred *p, int bind)
{
if (p) {
if (bind) {
p->bindcnt--;
}
p->refcnt--;
if(!p->bindcnt && p->refcnt <= 0) {
dev_put(p->dev);
tcf_hash_destroy(p);
return 1;
}
}
return 0;
}
int
tcf_mirred_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a,int ovr, int bind)
{
struct rtattr *tb[TCA_MIRRED_MAX];
struct tc_mirred *parm;
struct tcf_mirred *p;
struct net_device *dev = NULL;
int size = sizeof (*p), new = 0;
if (rtattr_parse(tb, TCA_MIRRED_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) < 0) {
DPRINTK("tcf_mirred_init BUG in user space couldnt parse properly\n");
return -1;
}
if (NULL == a || NULL == tb[TCA_MIRRED_PARMS - 1]) {
DPRINTK("BUG: tcf_mirred_init called with NULL params\n");
return -1;
}
parm = RTA_DATA(tb[TCA_MIRRED_PARMS - 1]);
p = tcf_hash_check(parm, a, ovr, bind);
if (NULL == p) { /* new */
p = tcf_hash_create(parm,est,a,size,ovr,bind);
new = 1;
if (NULL == p)
return -1;
}
if (parm->ifindex) {
dev = dev_get_by_index(parm->ifindex);
if (NULL == dev) {
printk("BUG: tcf_mirred_init called with bad device\n");
return -1;
}
switch (dev->type) {
case ARPHRD_TUNNEL:
case ARPHRD_TUNNEL6:
case ARPHRD_SIT:
case ARPHRD_IPGRE:
case ARPHRD_VOID:
case ARPHRD_NONE:
p->ok_push = 0;
break;
default:
p->ok_push = 1;
break;
}
} else {
if (new) {
kfree(p);
return -1;
}
}
if (new || ovr) {
spin_lock(&p->lock);
p->action = parm->action;
p->eaction = parm->eaction;
if (parm->ifindex) {
p->ifindex = parm->ifindex;
p->dev = dev;
dev_hold(p->dev);
}
spin_unlock(&p->lock);
}
DPRINTK(" tcf_mirred_init index %d action %d eaction %d device %s ifndex %d\n",parm->index,parm->action,parm->eaction,dev->name,parm->ifindex);
return new;
}
int
tcf_mirred_cleanup(struct tc_action *a, int bind)
{
struct tcf_mirred *p;
p = PRIV(a,mirred);
if (NULL != p)
return tcf_mirred_release(p, bind);
return 0;
}
int
tcf_mirred(struct sk_buff **pskb, struct tc_action *a)
{
struct tcf_mirred *p;
struct net_device *dev;
struct sk_buff *skb2 = NULL;
struct sk_buff *skb = *pskb;
__u32 at = G_TC_AT(skb->tc_verd);
if (NULL == a) {
if (net_ratelimit())
printk("BUG: tcf_mirred called with NULL action!\n");
return -1;
}
p = PRIV(a,mirred);
if (NULL == p) {
if (net_ratelimit())
printk("BUG: tcf_mirred called with NULL params\n");
return -1;
}
spin_lock(&p->lock);
dev = p->dev;
p->tm.lastuse = jiffies;
if (NULL == dev || !(dev->flags&IFF_UP) ) {
if (net_ratelimit())
printk("mirred to Houston: device %s is gone!\n",
dev?dev->name:"");
bad_mirred:
if (NULL != skb2)
kfree_skb(skb2);
p->stats.overlimits++;
p->stats.bytes += skb->len;
p->stats.packets++;
spin_unlock(&p->lock);
/* should we be asking for packet to be dropped?
* may make sense for redirect case only
*/
return TC_ACT_SHOT;
}
skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2 == NULL) {
goto bad_mirred;
}
if (TCA_EGRESS_MIRROR != p->eaction &&
TCA_EGRESS_REDIR != p->eaction) {
if (net_ratelimit())
printk("tcf_mirred unknown action %d\n",p->eaction);
goto bad_mirred;
}
p->stats.bytes += skb2->len;
p->stats.packets++;
if ( !(at & AT_EGRESS)) {
if (p->ok_push) {
skb_push(skb2, skb2->dev->hard_header_len);
}
}
/* mirror is always swallowed */
if (TCA_EGRESS_MIRROR != p->eaction)
skb2->tc_verd = SET_TC_FROM(skb2->tc_verd,at);
skb2->dev = dev;
skb2->input_dev = skb->dev;
dev_queue_xmit(skb2);
spin_unlock(&p->lock);
return p->action;
}
int
tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a,int bind, int ref)
{
unsigned char *b = skb->tail;
struct tc_mirred opt;
struct tcf_mirred *p;
struct tcf_t t;
p = PRIV(a,mirred);
if (NULL == p) {
printk("BUG: tcf_mirred_dump called with NULL params\n");
goto rtattr_failure;
}
opt.index = p->index;
opt.action = p->action;
opt.refcnt = p->refcnt - ref;
opt.bindcnt = p->bindcnt - bind;
opt.eaction = p->eaction;
opt.ifindex = p->ifindex;
DPRINTK(" tcf_mirred_dump index %d action %d eaction %d ifndex %d\n",p->index,p->action,p->eaction,p->ifindex);
RTA_PUT(skb, TCA_MIRRED_PARMS, sizeof (opt), &opt);
t.install = jiffies - p->tm.install;
t.lastuse = jiffies - p->tm.lastuse;
t.expires = p->tm.expires;
RTA_PUT(skb, TCA_MIRRED_TM, sizeof (t), &t);
return skb->len;
rtattr_failure:
skb_trim(skb, b - skb->data);
return -1;
}
int
tcf_mirred_stats(struct sk_buff *skb, struct tc_action *a)
{
struct tcf_mirred *p;
p = PRIV(a,mirred);
if (NULL != p)
return qdisc_copy_stats(skb, &p->stats, p->stats_lock);
return 1;
}
static struct tc_action_ops act_mirred_ops = {
.next = NULL,
.kind = "mirred",
.type = TCA_ACT_MIRRED,
.capab = TCA_CAP_NONE,
.owner = THIS_MODULE,
.act = tcf_mirred,
.get_stats = tcf_mirred_stats,
.dump = tcf_mirred_dump,
.cleanup = tcf_mirred_cleanup,
.lookup = tcf_hash_search,
.init = tcf_mirred_init,
.walk = tcf_generic_walker
};
MODULE_AUTHOR("Jamal Hadi Salim(2002)");
MODULE_DESCRIPTION("Device Mirror/redirect actions");
MODULE_LICENSE("GPL");
static int __init
mirred_init_module(void)
{
printk("Mirror/redirect action on\n");
return tcf_register_action(&act_mirred_ops);
}
static void __exit
mirred_cleanup_module(void)
{
tcf_unregister_action(&act_mirred_ops);
}
module_init(mirred_init_module);
module_exit(mirred_cleanup_module);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment