mirror of
https://git.proxmox.com/git/mirror_iproute2
synced 2025-10-05 17:15:45 +00:00

The mirred act admits an optional control action, defaulting
to TC_ACT_PIPE. The parsing code currently emits an error message
if the control action is not provided on the command line, even
if the command itself completes with no error.
This change shuts down the error message, using the appropriate
parsing helper.
Fixes: e67aba5595
("tc: actions: add helpers to parse and print control actions")
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
330 lines
6.6 KiB
C
330 lines
6.6 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 <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"
|
|
"where:\n"
|
|
"\tDIRECTION := <ingress | egress>\n"
|
|
"\tACTION := <mirror | redirect>\n"
|
|
"\tINDEX is the specific policy instance id\n"
|
|
"\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 const char *mirred_direction(int action)
|
|
{
|
|
switch (action) {
|
|
case TCA_EGRESS_REDIR:
|
|
case TCA_EGRESS_MIRROR:
|
|
return "egress";
|
|
case TCA_INGRESS_REDIR:
|
|
case TCA_INGRESS_MIRROR:
|
|
return "ingress";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static const char *mirred_action(int action)
|
|
{
|
|
switch (action) {
|
|
case TCA_EGRESS_REDIR:
|
|
case TCA_INGRESS_REDIR:
|
|
return "redirect";
|
|
case TCA_EGRESS_MIRROR:
|
|
case TCA_INGRESS_MIRROR:
|
|
return "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[IFNAMSIZ] = {};
|
|
|
|
while (argc > 0) {
|
|
|
|
if (matches(*argv, "action") == 0) {
|
|
NEXT_ARG();
|
|
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);
|
|
|
|
idx = ll_name_to_index(d);
|
|
if (!idx)
|
|
return nodev(d);
|
|
|
|
p.ifindex = idx;
|
|
}
|
|
|
|
|
|
if (p.eaction == TCA_EGRESS_MIRROR || p.eaction == TCA_INGRESS_MIRROR)
|
|
parse_action_control_dflt(&argc, &argv, &p.action, false,
|
|
TC_ACT_PIPE);
|
|
|
|
if (argc) {
|
|
if (iok && matches(*argv, "index") == 0) {
|
|
fprintf(stderr, "mirred: Illegal double index\n");
|
|
return -1;
|
|
}
|
|
|
|
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 = addattr_nest(n, MAX_MSG, tca_id);
|
|
addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof(p));
|
|
addattr_nest_end(n, 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) {
|
|
print_string(PRINT_FP, NULL, "%s", "[NULL mirred parameters]");
|
|
return -1;
|
|
}
|
|
p = RTA_DATA(tb[TCA_MIRRED_PARMS]);
|
|
|
|
dev = ll_index_to_name(p->ifindex);
|
|
if (dev == 0) {
|
|
fprintf(stderr, "Cannot find device %d\n", p->ifindex);
|
|
return -1;
|
|
}
|
|
|
|
print_string(PRINT_ANY, "kind", "%s ", "mirred");
|
|
print_string(PRINT_FP, NULL, "(%s", mirred_n2a(p->eaction));
|
|
print_string(PRINT_JSON, "mirred_action", NULL,
|
|
mirred_action(p->eaction));
|
|
print_string(PRINT_JSON, "direction", NULL,
|
|
mirred_direction(p->eaction));
|
|
print_string(PRINT_ANY, "to_dev", " to device %s)", dev);
|
|
print_action_control(f, " ", p->action, "");
|
|
|
|
print_uint(PRINT_ANY, "index", "\n \tindex %u", p->index);
|
|
print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
|
|
print_int(PRINT_ANY, "bind", " bind %d", p->bindcnt);
|
|
|
|
if (show_stats) {
|
|
if (tb[TCA_MIRRED_TM]) {
|
|
struct tcf_t *tm = RTA_DATA(tb[TCA_MIRRED_TM]);
|
|
|
|
print_tm(f, tm);
|
|
}
|
|
}
|
|
print_string(PRINT_FP, NULL, "%s", "\n ");
|
|
return 0;
|
|
}
|
|
|
|
struct action_util mirred_action_util = {
|
|
.id = "mirred",
|
|
.parse_aopt = parse_mirred,
|
|
.print_aopt = print_mirred,
|
|
};
|