1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* m_simple.c simple action
*
* Authors: J Hadi Salim <jhs@mojatatu.com>
*
* Pedagogical example. Adds a string that will be printed every time
* the simple instance is hit.
* Use this as a skeleton action and keep modifying it to meet your needs.
* Look at linux/tc_act/tc_defact.h for the different components ids and
* definitions used in this actions
*
* example use, yell "Incoming ICMP!" every time you see an incoming ICMP on
* eth0. Steps are:
* 1) Add an ingress qdisc point to eth0
* 2) Start a chain on ingress of eth0 that first matches ICMP then invokes
* the simple action to shout.
* 3) display stats and show that no packet has been seen by the action
* 4) Send one ping packet to google (expect to receive a response back)
* 5) grep the logs to see the logged message
* 6) display stats again and observe increment by 1
*
hadi@noma1:$ tc qdisc add dev eth0 ingress
hadi@noma1:$tc filter add dev eth0 parent ffff: protocol ip prio 5 \
u32 match ip protocol 1 0xff flowid 1:1 action simple "Incoming ICMP"
hadi@noma1:$ sudo tc -s filter ls dev eth0 parent ffff:
filter protocol ip pref 5 u32
filter protocol ip pref 5 u32 fh 800: ht divisor 1
filter protocol ip pref 5 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1
match 00010000/00ff0000 at 8
action order 1: Simple <Incoming ICMP>
index 4 ref 1 bind 1 installed 29 sec used 29 sec
Action statistics:
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
hadi@noma1$ ping -c 1 www.google.ca
PING www.google.ca (74.125.225.120) 56(84) bytes of data.
64 bytes from ord08s08-in-f24.1e100.net (74.125.225.120): icmp_req=1 ttl=53 time=31.3 ms
--- www.google.ca ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 31.316/31.316/31.316/0.000 ms
hadi@noma1$ dmesg | grep simple
[135354.473951] simple: Incoming ICMP_1
hadi@noma1$ sudo tc/tc -s filter ls dev eth0 parent ffff:
filter protocol ip pref 5 u32
filter protocol ip pref 5 u32 fh 800: ht divisor 1
filter protocol ip pref 5 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1
match 00010000/00ff0000 at 8
action order 1: Simple <Incoming ICMP>
index 4 ref 1 bind 1 installed 206 sec used 67 sec
Action statistics:
Sent 84 bytes 1 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
*/
#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 <linux/tc_act/tc_defact.h>
#ifndef SIMP_MAX_DATA
#define SIMP_MAX_DATA 32
#endif
static void explain(void)
{
fprintf(stderr,
"Usage:... simple [sdata STRING] [index INDEX] [CONTROL]\n"
"\tSTRING being an arbitrary string\n"
"\tINDEX := optional index value used\n"
"\tCONTROL := reclassify|pipe|drop|continue|ok\n");
}
static void usage(void)
{
explain();
exit(-1);
}
static int
parse_simple(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
struct nlmsghdr *n)
{
struct tc_defact sel = {};
int argc = *argc_p;
char **argv = *argv_p;
int ok = 0;
struct rtattr *tail;
char *simpdata = NULL;
while (argc > 0) {
if (matches(*argv, "simple") == 0) {
NEXT_ARG();
} else if (matches(*argv, "sdata") == 0) {
NEXT_ARG();
ok += 1;
simpdata = *argv;
argc--;
argv++;
} else if (matches(*argv, "help") == 0) {
usage();
} else {
break;
}
}
parse_action_control_dflt(&argc, &argv, &sel.action, false,
TC_ACT_PIPE);
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
if (get_u32(&sel.index, *argv, 10)) {
fprintf(stderr, "simple: Illegal \"index\" (%s)\n",
*argv);
return -1;
}
ok += 1;
argc--;
argv++;
}
}
if (!ok) {
explain();
return -1;
}
if (simpdata && (strlen(simpdata) > (SIMP_MAX_DATA - 1))) {
fprintf(stderr, "simple: Illegal string len %zu <%s>\n",
strlen(simpdata), simpdata);
return -1;
}
tail = addattr_nest(n, MAX_MSG, tca_id);
addattr_l(n, MAX_MSG, TCA_DEF_PARMS, &sel, sizeof(sel));
if (simpdata)
addattr_l(n, MAX_MSG, TCA_DEF_DATA, simpdata, SIMP_MAX_DATA);
addattr_nest_end(n, tail);
*argc_p = argc;
*argv_p = argv;
return 0;
}
static int print_simple(const struct action_util *au, FILE *f, struct rtattr *arg)
{
struct tc_defact *sel;
struct rtattr *tb[TCA_DEF_MAX + 1];
char *simpdata;
if (arg == NULL)
return 0;
parse_rtattr_nested(tb, TCA_DEF_MAX, arg);
if (tb[TCA_DEF_PARMS] == NULL) {
fprintf(stderr, "Missing simple parameters\n");
return -1;
}
sel = RTA_DATA(tb[TCA_DEF_PARMS]);
if (tb[TCA_DEF_DATA] == NULL) {
fprintf(stderr, "Missing simple string\n");
return -1;
}
simpdata = RTA_DATA(tb[TCA_DEF_DATA]);
print_string(PRINT_ANY, "simple", "Simple <%s>", simpdata);
print_nl();
print_uint(PRINT_ANY, "index", "\t index %u ", sel->index);
print_int(PRINT_ANY, "ref", "ref %d ", sel->refcnt);
print_int(PRINT_ANY, "bind","bind %d", sel->bindcnt);
if (show_stats) {
if (tb[TCA_DEF_TM]) {
struct tcf_t *tm = RTA_DATA(tb[TCA_DEF_TM]);
print_tm(tm);
}
}
print_nl();
return 0;
}
struct action_util simple_action_util = {
.id = "simple",
.parse_aopt = parse_simple,
.print_aopt = print_simple,
};
|