diff --git a/ChangeLog b/ChangeLog index 6bf00cbd..0c4c4fd7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2004-12-08 Jamal Hadi Salim + + * Add mirror and redirect actions + 2004-10-20 Stephen Hemminger * Don't include since then we get dependant on diff --git a/configure b/configure index 097447bd..bcded03d 100644 --- a/configure +++ b/configure @@ -26,12 +26,19 @@ else fi rm -f /tmp/atmtest.c /tmp/atmtest -# hack for now +# hacks for now echo "TC actions" if [ -e "tc/m_gact.c" ] then + echo " GACT found" echo "TC_CONFIG_ACTION_GACT=y" >>Config echo "TC_CONFIG_ACTION_PROB=y" >>Config fi +if [ -e "tc/m_mirred.c" ] +then + echo " MIRRED found" + echo "TC_CONFIG_ACTION_MIRRED=y" >>Config +fi + diff --git a/doc/actions/mirred-usage b/doc/actions/mirred-usage index e69de29b..3e135a0e 100644 --- a/doc/actions/mirred-usage +++ b/doc/actions/mirred-usage @@ -0,0 +1,71 @@ + +Very funky action. I do plan to add to a few more things to it +This is the basic stuff. Idea borrowed from the way ethernet switches +mirror and redirect packets. + +Usage: + +mirred [index INDEX] +where: +DIRECTION := +ACTION := +INDEX is the specific policy instance id +DEVICENAME is the devicename + + +Mirroring essentially takes a copy of the packet whereas redirecting +steals the packet and redirects to specified destination. + +Some examples: +Host A is hooked up to us on eth0 + +tc qdisc add dev lo ingress +# redirect all packets arriving on ingress of lo to eth0 +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 action mirred egress redirect dev eth0 + +On host A start a tcpdump on interface connecting to us. + +on our host ping -c 2 127.0.0.1 + +Ping would fail sinc all packets are heading out eth0 +tcpudmp on host A would show them + +if you substitute the redirect with mirror above as in: +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 action mirred egress mirror dev eth0 + +Then you should see the packets on both host A and the local +stack (i.e ping would work). + +Even more funky example: + +# +#allow 1 out 10 packets to randomly make it to the +# host A (Randomness uses the netrand generator) +# +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 \ +action drop random determ ok 10\ +action mirred egress mirror dev eth0 + +------ +Example 2: +# for packets coming from 10.0.0.9: +#Redirect packets on egress (to ISP A) if you exceed a certain rate +# to eth1 (to ISP B) if you exceed a certain rate +# + +tc qdisc add dev eth0 handle 1:0 root prio + +tc filter add dev eth0 parent 1:0 protocol ip prio 6 u32 \ +match ip src 10.0.0.9/32 flowid 1:16 \ +action police rate 100kbit burst 90k ok \ +action mirred egress mirror dev eth1 + +--- + +A more interesting example is when you mirror flows to a dummy device +so you could tcpdump them (dummy by defaults drops all devices it sees). +This is a very useful debug feature. + diff --git a/include/linux/tc_act/tc_mirred.h b/include/linux/tc_act/tc_mirred.h index e69de29b..71d63409 100644 --- a/include/linux/tc_act/tc_mirred.h +++ b/include/linux/tc_act/tc_mirred.h @@ -0,0 +1,28 @@ +#ifndef __LINUX_TC_MIR_H +#define __LINUX_TC_MIR_H + +#include + +#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 diff --git a/tc/Makefile b/tc/Makefile index 4551c08a..ad80ea0b 100644 --- a/tc/Makefile +++ b/tc/Makefile @@ -21,6 +21,7 @@ TCMODULES += q_ingress.o TCMODULES += q_hfsc.o TCMODULES += q_htb.o TCMODULES += m_gact.o +TCMODULES += m_mirred.o TCOBJ += $(TCMODULES) diff --git a/tc/m_mirred.c b/tc/m_mirred.c index e69de29b..05530ca0 100644 --- a/tc/m_mirred.c +++ b/tc/m_mirred.c @@ -0,0 +1,310 @@ +/* + * m_egress.c ingress/egress packet mirror/redir actions module + * + * This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca) + * + * TODO: Add Ingress support + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" +#include "tc_util.h" +#include + +int mirred_d = 1; + +static void +explain(void) +{ + fprintf(stderr, "Usage: mirred [index INDEX] \n"); + fprintf(stderr, "where: \n"); + fprintf(stderr, "DIRECTION := \n"); + fprintf(stderr, "aCTION := \n"); + fprintf(stderr, " : INDEX is the specific policy instance id\n"); + fprintf(stderr, " : DEVICENAME is the devicename \n"); +} + +#define usage() return(-1) + +char *mirred_n2a(int action) +{ + switch (action) { + case TCA_EGRESS_REDIR: + return "Egress Redirect"; + case TCA_INGRESS_REDIR: + return "Ingress Redirect"; + case TCA_EGRESS_MIRROR: + return "Egress Mirror"; + case TCA_INGRESS_MIRROR: + return "Ingress Mirror"; + default: + return "unknown"; + } +} + +int +parse_egress(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + + int argc = *argc_p; + char **argv = *argv_p; + int ok = 0, iok = 0, mirror=0,redir=0; + struct tc_mirred p; + struct rtattr *tail; + char d[16]; + struct rtnl_handle rth; + + memset(d,0,sizeof(d)-1); + memset(&p,0,sizeof(struct tc_mirred)); + + while (argc > 0) { + + if (matches(*argv, "action") == 0) { + break; + } else if (matches(*argv, "egress") == 0) { + NEXT_ARG(); + ok++; + continue; + } else { + + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + iok++; + if (!ok) { + argc--; + argv++; + break; + } + } else if(!ok) { + fprintf(stderr, "was expecting egress (%s)\n", *argv); + break; + + } else if (!mirror && matches(*argv, "mirror") == 0) { + mirror=1; + if (redir) { + fprintf(stderr, "Cant have both mirror and redir\n"); + return -1; + } + p.eaction = TCA_EGRESS_MIRROR; + p.action = TC_ACT_PIPE; + ok++; + } else if (!redir && matches(*argv, "redirect") == 0) { + redir=1; + if (mirror) { + fprintf(stderr, "Cant have both mirror and redir\n"); + return -1; + } + p.eaction = TCA_EGRESS_REDIR; + p.action = TC_ACT_STOLEN; + ok++; + } else if ((redir || mirror) && matches(*argv, "dev") == 0) { + NEXT_ARG(); + if (strlen(d)) + duparg("dev", *argv); + + strncpy(d, *argv, sizeof(d)-1); + argc--; + argv++; + + break; + + } + } + + NEXT_ARG(); + } + + if (!ok && !iok) { + explain(); + return -1; + } + + + + if (d[0]) { + int idx; + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + exit(1); + } + ll_init_map(&rth); + + + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + rtnl_close(&rth); + return -1; + } + + p.ifindex = idx; + rtnl_close(&rth); + } + + + if (argc && p.eaction == TCA_EGRESS_MIRROR) { + + if (matches(*argv, "reclassify") == 0) { + p.action = TC_POLICE_RECLASSIFY; + NEXT_ARG(); + } else if (matches(*argv, "pipe") == 0) { + p.action = TC_POLICE_PIPE; + NEXT_ARG(); + } else if (matches(*argv, "drop") == 0 || + matches(*argv, "shot") == 0) { + p.action = TC_POLICE_SHOT; + NEXT_ARG(); + } else if (matches(*argv, "continue") == 0) { + p.action = TC_POLICE_UNSPEC; + NEXT_ARG(); + } else if (matches(*argv, "pass") == 0) { + p.action = TC_POLICE_OK; + NEXT_ARG(); + } + + } + + if (argc) { + if (iok && matches(*argv, "index") == 0) { + fprintf(stderr, "mirred: Illegal double index\n"); + return -1; + } else { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "mirred: Illegal \"index\"\n"); + return -1; + } + argc--; + argv++; + } + } + } + + if (mirred_d) + fprintf(stdout, "Action %d device %s ifindex %d\n",p.action, d,p.ifindex); + + tail = (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof (p)); + tail->rta_len = + (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)) - (void *) tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + + +int +parse_mirred(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 0) { + fprintf(stderr,"mirred bad arguement count %d\n", argc); + return -1; + } + + if (matches(*argv, "mirred") == 0) { + NEXT_ARG(); + } else { + fprintf(stderr,"mirred bad arguement %s\n", *argv); + return -1; + } + + + if (matches(*argv, "egress") == 0 || matches(*argv, "index") == 0) { + int ret = parse_egress(a, &argc, &argv, tca_id, n); + if (ret == 0) { + *argc_p = argc; + *argv_p = argv; + return 0; + } + + } else if (matches(*argv, "ingress") == 0) { + fprintf(stderr,"mirred ingress not supported at the moment\n"); + + } else { + fprintf(stderr,"mirred not supported %s\n", *argv); + } + + return -1; + +} + +int +print_mirred(struct action_util *au,FILE * f, struct rtattr *arg) +{ + struct tc_mirred *p; + struct rtattr *tb[TCA_MIRRED_MAX + 1]; + struct rtnl_handle rth; + const char *dev; + SPRINT_BUF(b1); + + if (arg == NULL) + return -1; + + memset(tb, 0, sizeof (tb)); + parse_rtattr(tb, TCA_MIRRED_MAX, RTA_DATA(arg), RTA_PAYLOAD(arg)); + + if (tb[TCA_MIRRED_PARMS] == NULL) { + fprintf(f, "[NULL mirred parameters]"); + return -1; + } + p = RTA_DATA(tb[TCA_MIRRED_PARMS]); + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + return -1; + } + + ll_init_map(&rth); + + + if ((dev = ll_index_to_name(p->ifindex)) == 0) { + fprintf(stderr, "Cannot find device %d\n", p->ifindex); + rtnl_close(&rth); + return -1; + } + + fprintf(f, "mirred (%s to device %s) %s", mirred_n2a(p->eaction), dev,action_n2a(p->action, b1, sizeof (b1))); + + fprintf(f, "\n "); + fprintf(f, "\tindex %d ref %d bind %d",p->index,p->refcnt,p->bindcnt); + + if (show_stats) { + if (tb[TCA_MIRRED_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_MIRRED_TM]); + print_tm(f,tm); + } + } + fprintf(f, "\n "); + rtnl_close(&rth); + return 0; +} + +struct action_util mirred_util = { + .id = "mirred", + .parse_aopt = parse_mirred, + .print_aopt = print_mirred, +};