mirror_iproute2/tc/m_mirred.c
Shmulik Ladkani 5eca0a3701 tc: m_mirred: Add support for ingress redirect/mirror
So far, only the 'egress' direction was implemented.

Allow specifying 'ingress' as the direction packet appears on the target
interface.

For example, this takes incoming 802.1q frames on veth0 and redirects
them for input on dummy0:

 # tc filter add dev veth0 parent ffff: pref 1 protocol 802.1q basic \
     action mirred ingress redirect dev dummy0

Signed-off-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
2016-10-26 11:20:47 -07:00

297 lines
6.0 KiB
C

/*
* 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 "tc_common.h"
#include <linux/tc_act/tc_mirred.h>
static void
explain(void)
{
fprintf(stderr, "Usage: mirred <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME>\n");
fprintf(stderr, "where:\n");
fprintf(stderr, "\tDIRECTION := <ingress | egress>\n");
fprintf(stderr, "\tACTION := <mirror | redirect>\n");
fprintf(stderr, "\tINDEX is the specific policy instance id\n");
fprintf(stderr, "\tDEVICENAME is the devicename\n");
}
static void
usage(void)
{
explain();
exit(-1);
}
static const 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";
}
}
static int
parse_direction(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, ingress = 0, egress = 0;
struct tc_mirred p = {};
struct rtattr *tail;
char d[16] = {};
while (argc > 0) {
if (matches(*argv, "action") == 0) {
break;
} else if (!egress && matches(*argv, "egress") == 0) {
egress = 1;
if (ingress) {
fprintf(stderr, "Can't have both egress and ingress\n");
return -1;
}
NEXT_ARG();
ok++;
continue;
} else if (!ingress && matches(*argv, "ingress") == 0) {
ingress = 1;
if (egress) {
fprintf(stderr, "Can't have both ingress and egress\n");
return -1;
}
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 or ingress (%s)\n", *argv);
break;
} else if (!mirror && matches(*argv, "mirror") == 0) {
mirror = 1;
if (redir) {
fprintf(stderr, "Can't have both mirror and redir\n");
return -1;
}
p.eaction = egress ? TCA_EGRESS_MIRROR :
TCA_INGRESS_MIRROR;
p.action = TC_ACT_PIPE;
ok++;
} else if (!redir && matches(*argv, "redirect") == 0) {
redir = 1;
if (mirror) {
fprintf(stderr, "Can't have both mirror and redir\n");
return -1;
}
p.eaction = egress ? TCA_EGRESS_REDIR :
TCA_INGRESS_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) {
return -1;
}
if (d[0]) {
int idx;
ll_init_map(&rth);
if ((idx = ll_name_to_index(d)) == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n", d);
return -1;
}
p.ifindex = idx;
}
if (argc &&
(p.eaction == TCA_EGRESS_MIRROR || p.eaction == TCA_INGRESS_MIRROR)
&& !action_a2n(*argv, &p.action, false))
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++;
}
}
}
tail = NLMSG_TAIL(n);
addattr_l(n, MAX_MSG, tca_id, NULL, 0);
addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof(p));
tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
*argc_p = argc;
*argv_p = argv;
return 0;
}
static 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 argument count %d\n", argc);
return -1;
}
if (matches(*argv, "mirred") == 0) {
NEXT_ARG();
} else {
fprintf(stderr, "mirred bad argument %s\n", *argv);
return -1;
}
if (matches(*argv, "egress") == 0 || matches(*argv, "ingress") == 0 ||
matches(*argv, "index") == 0) {
int ret = parse_direction(a, &argc, &argv, tca_id, n);
if (ret == 0) {
*argc_p = argc;
*argv_p = argv;
return 0;
}
} else if (matches(*argv, "help") == 0) {
usage();
} else {
fprintf(stderr, "mirred option not supported %s\n", *argv);
}
return -1;
}
static int
print_mirred(struct action_util *au, FILE * f, struct rtattr *arg)
{
struct tc_mirred *p;
struct rtattr *tb[TCA_MIRRED_MAX + 1];
const char *dev;
if (arg == NULL)
return -1;
parse_rtattr_nested(tb, TCA_MIRRED_MAX, arg);
if (tb[TCA_MIRRED_PARMS] == NULL) {
fprintf(f, "[NULL mirred parameters]");
return -1;
}
p = RTA_DATA(tb[TCA_MIRRED_PARMS]);
/*
ll_init_map(&rth);
*/
if ((dev = ll_index_to_name(p->ifindex)) == 0) {
fprintf(stderr, "Cannot find device %d\n", p->ifindex);
return -1;
}
fprintf(f, "mirred (%s to device %s) %s",
mirred_n2a(p->eaction), dev, action_n2a(p->action));
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 ");
return 0;
}
struct action_util mirred_action_util = {
.id = "mirred",
.parse_aopt = parse_mirred,
.print_aopt = print_mirred,
};