]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
Import patch mirred.patch
authornet[shemminger]!shemminger <net[shemminger]!shemminger>
Wed, 8 Dec 2004 20:13:56 +0000 (20:13 +0000)
committernet[shemminger]!shemminger <net[shemminger]!shemminger>
Wed, 8 Dec 2004 20:13:56 +0000 (20:13 +0000)
(Logical change 1.111)

ChangeLog
configure
doc/actions/mirred-usage
include/linux/tc_act/tc_mirred.h
tc/Makefile
tc/m_mirred.c

index 6bf00cbd48b7f13e9b66e440fd70c6af48f237b0..0c4c4fd706e3cef503814b69d185c8e6ca061516 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2004-12-08  Jamal Hadi Salim <hadi@znyx.com>
+
+       * Add mirror and redirect actions
+
 2004-10-20  Stephen Hemminger  <shemminger@osdl.org>
 
        * Don't include <asm/byteorder.h> since then we get dependant on
index 097447bd2b90917d846cc37958745ec7fb0a4bf5..bcded03d4939991fc6312132c23f2968fda47560 100644 (file)
--- 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
+
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3e135a0e249eae69737ba1d358061df355f02fb9 100644 (file)
@@ -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 <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME> 
+where: 
+DIRECTION := <ingress | egress>
+ACTION := <mirror | redirect>
+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.
+
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..71d63409d5684154c515a2c57b5cbe919f045ef3 100644 (file)
@@ -0,0 +1,28 @@
+#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
index 4551c08af3496233496a2cca63ae91a7da93ce49..ad80ea0bfebddb0e8eda554c92b14316b8c05753 100644 (file)
@@ -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)
 
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..05530ca0bdbb5ef495a775bfe33f55d75b87fef6 100644 (file)
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_mirred.h>
+
+int mirred_d = 1;
+
+static void
+explain(void)
+{
+       fprintf(stderr, "Usage: mirred <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME> \n");
+       fprintf(stderr, "where: \n");
+       fprintf(stderr, "DIRECTION := <ingress | egress>\n");
+       fprintf(stderr, "aCTION := <mirror | redirect>\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,
+};