summaryrefslogtreecommitdiffstats
path: root/tc/m_simple.c
blob: bc86be27cbccfee76f1d2acbbb6426450f927a1c (plain)
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
205
206
/*
 * m_simple.c	simple action
 *
 *		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 <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(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(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]);

	fprintf(f, "Simple <%s>\n", simpdata);
	fprintf(f, "\t index %u ref %d bind %d", sel->index,
		sel->refcnt, sel->bindcnt);

	if (show_stats) {
		if (tb[TCA_DEF_TM]) {
			struct tcf_t *tm = RTA_DATA(tb[TCA_DEF_TM]);

			print_tm(f, tm);
		}
	}
	print_nl();

	return 0;
}

struct action_util simple_action_util = {
	.id = "simple",
	.parse_aopt = parse_simple,
	.print_aopt = print_simple,
};