1/*
2 * m_action.c		Action Management
3 *
4 *		This program is free software; you can distribute it and/or
5 *		modify it under the terms of the GNU General Public License
6 *		as published by the Free Software Foundation; either version
7 *		2 of the License, or (at your option) any later version.
8 *
9 * Authors:  J Hadi Salim (hadi@cyberus.ca)
10 *
11 * TODO:
12 * - parse to be passed a filedescriptor for logging purposes
13 *
14*/
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <unistd.h>
19#include <syslog.h>
20#include <fcntl.h>
21#include <sys/socket.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <string.h>
25#include <dlfcn.h>
26
27#include "utils.h"
28#include "tc_common.h"
29#include "tc_util.h"
30
31static struct action_util * action_list;
32#ifdef CONFIG_GACT
33int gact_ld = 0 ; //fuckin backward compatibility
34#endif
35int batch_c = 0;
36int tab_flush = 0;
37
38void act_usage(void)
39{
40	fprintf (stderr, "action usage improper\n");
41}
42
43static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt)
44{
45	if (opt && RTA_PAYLOAD(opt))
46		fprintf(f, "[Unknown action, optlen=%u] ",
47			(unsigned) RTA_PAYLOAD(opt));
48	return 0;
49}
50
51static int parse_noaopt(struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n)
52{
53	int argc = *argc_p;
54	char **argv = *argv_p;
55
56	if (argc) {
57		fprintf(stderr, "Unknown action \"%s\", hence option \"%s\" is unparsable\n", au->id, *argv);
58	} else {
59		fprintf(stderr, "Unknown action \"%s\"\n", au->id);
60	}
61	return -1;
62}
63
64struct action_util *get_action_kind(char *str)
65{
66	static void *aBODY;
67	void *dlh;
68	char buf[256];
69	struct action_util *a;
70#ifdef CONFIG_GACT
71	int looked4gact = 0;
72restart_s:
73#endif
74	for (a = action_list; a; a = a->next) {
75		if (strcmp(a->id, str) == 0)
76			return a;
77	}
78
79	snprintf(buf, sizeof(buf), "m_%s.so", str);
80	dlh = dlopen(buf, RTLD_LAZY);
81	if (dlh == NULL) {
82		dlh = aBODY;
83		if (dlh == NULL) {
84			dlh = aBODY = dlopen(NULL, RTLD_LAZY);
85			if (dlh == NULL)
86				goto noexist;
87		}
88	}
89
90	snprintf(buf, sizeof(buf), "%s_action_util", str);
91	a = dlsym(dlh, buf);
92	if (a == NULL)
93		goto noexist;
94
95reg:
96	a->next = action_list;
97	action_list = a;
98	return a;
99
100noexist:
101#ifdef CONFIG_GACT
102	if (!looked4gact) {
103		looked4gact = 1;
104		strcpy(str,"gact");
105		goto restart_s;
106	}
107#endif
108	a = malloc(sizeof(*a));
109	if (a) {
110		memset(a, 0, sizeof(*a));
111		strncpy(a->id, "noact", 15);
112		a->parse_aopt = parse_noaopt;
113		a->print_aopt = print_noaopt;
114		goto reg;
115	}
116	return a;
117}
118
119int
120new_cmd(char **argv)
121{
122	if ((matches(*argv, "change") == 0) ||
123		(matches(*argv, "replace") == 0)||
124		(matches(*argv, "delete") == 0)||
125		(matches(*argv, "add") == 0))
126			return 1;
127
128	return 0;
129
130}
131
132int
133parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
134{
135	int argc = *argc_p;
136	char **argv = *argv_p;
137	struct rtattr *tail, *tail2;
138	char k[16];
139	int ok = 0;
140	int eap = 0; /* expect action parameters */
141
142	int ret = 0;
143	int prio = 0;
144
145	if (argc <= 0)
146		return -1;
147
148	tail = tail2 = NLMSG_TAIL(n);
149
150	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
151
152	while (argc > 0) {
153
154		memset(k, 0, sizeof (k));
155
156		if (strcmp(*argv, "action") == 0 ) {
157			argc--;
158			argv++;
159			eap = 1;
160#ifdef CONFIG_GACT
161			if (!gact_ld) {
162				get_action_kind("gact");
163			}
164#endif
165			continue;
166		} else if (strcmp(*argv, "help") == 0) {
167			return -1;
168		} else if (new_cmd(argv)) {
169			goto done0;
170		} else {
171			struct action_util *a = NULL;
172			strncpy(k, *argv, sizeof (k) - 1);
173			eap = 0;
174			if (argc > 0 ) {
175				a = get_action_kind(k);
176			} else {
177done0:
178				if (ok)
179					break;
180				else
181					goto done;
182			}
183
184			if (NULL == a) {
185				goto bad_val;
186			}
187
188			tail = NLMSG_TAIL(n);
189			addattr_l(n, MAX_MSG, ++prio, NULL, 0);
190			addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
191
192			ret = a->parse_aopt(a,&argc, &argv, TCA_ACT_OPTIONS, n);
193
194			if (ret < 0) {
195				fprintf(stderr,"bad action parsing\n");
196				goto bad_val;
197			}
198			tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
199			ok++;
200		}
201
202	}
203
204	if (eap > 0) {
205		fprintf(stderr,"bad action empty %d\n",eap);
206		goto bad_val;
207	}
208
209	tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2;
210
211done:
212	*argc_p = argc;
213	*argv_p = argv;
214	return 0;
215bad_val:
216	/* no need to undo things, returning from here should
217	 * cause enough pain */
218	fprintf(stderr, "parse_action: bad value (%d:%s)!\n",argc,*argv);
219	return -1;
220}
221
222int
223tc_print_one_action(FILE * f, struct rtattr *arg)
224{
225
226	struct rtattr *tb[TCA_ACT_MAX + 1];
227	int err = 0;
228	struct action_util *a = NULL;
229
230	if (arg == NULL)
231		return -1;
232
233	parse_rtattr_nested(tb, TCA_ACT_MAX, arg);
234	if (tb[TCA_ACT_KIND] == NULL) {
235		fprintf(stderr, "NULL Action!\n");
236		return -1;
237	}
238
239
240	a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND]));
241	if (NULL == a)
242		return err;
243
244	if (tab_flush) {
245		fprintf(f," %s \n", a->id);
246		tab_flush = 0;
247		return 0;
248	}
249
250	err = a->print_aopt(a,f,tb[TCA_ACT_OPTIONS]);
251
252
253	if (0 > err)
254		return err;
255
256	if (show_stats && tb[TCA_ACT_STATS]) {
257		fprintf(f, "\tAction statistics:\n");
258		print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
259		fprintf(f, "\n");
260	}
261
262	return 0;
263}
264
265int
266tc_print_action(FILE * f, const struct rtattr *arg)
267{
268
269	int i;
270	struct rtattr *tb[TCA_ACT_MAX_PRIO + 1];
271
272	if (arg == NULL)
273		return 0;
274
275	parse_rtattr_nested(tb, TCA_ACT_MAX_PRIO, arg);
276
277	if (tab_flush && NULL != tb[0]  && NULL == tb[1]) {
278		int ret = tc_print_one_action(f, tb[0]);
279		return ret;
280	}
281
282	for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
283		if (tb[i]) {
284			fprintf(f, "\n\taction order %d: ", i + batch_c);
285			if (0 > tc_print_one_action(f, tb[i])) {
286				fprintf(f, "Error printing action\n");
287			}
288		}
289
290	}
291
292	batch_c+=TCA_ACT_MAX_PRIO ;
293	return 0;
294}
295
296static int do_print_action(const struct sockaddr_nl *who,
297			   struct nlmsghdr *n,
298			   void *arg)
299{
300	FILE *fp = (FILE*)arg;
301	struct tcamsg *t = NLMSG_DATA(n);
302	int len = n->nlmsg_len;
303	struct rtattr * tb[TCAA_MAX+1];
304
305	len -= NLMSG_LENGTH(sizeof(*t));
306
307	if (len < 0) {
308		fprintf(stderr, "Wrong len %d\n", len);
309		return -1;
310	}
311
312	parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len);
313
314	if (NULL == tb[TCA_ACT_TAB]) {
315		if (n->nlmsg_type != RTM_GETACTION)
316			fprintf(stderr, "do_print_action: NULL kind\n");
317		return -1;
318	}
319
320	if (n->nlmsg_type == RTM_DELACTION) {
321		if (n->nlmsg_flags & NLM_F_ROOT) {
322			fprintf(fp, "Flushed table ");
323			tab_flush = 1;
324		} else {
325			fprintf(fp, "deleted action ");
326		}
327	}
328
329	if (n->nlmsg_type == RTM_NEWACTION)
330		fprintf(fp, "Added action ");
331	tc_print_action(fp, tb[TCA_ACT_TAB]);
332
333	return 0;
334}
335
336int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p)
337{
338	char k[16];
339	struct action_util *a = NULL;
340	int argc = *argc_p;
341	char **argv = *argv_p;
342	int prio = 0;
343	int ret = 0;
344	__u32 i;
345	struct sockaddr_nl nladdr;
346	struct rtattr *tail;
347	struct rtattr *tail2;
348	struct nlmsghdr *ans = NULL;
349
350	struct {
351		struct nlmsghdr         n;
352		struct tcamsg           t;
353		char                    buf[MAX_MSG];
354	} req;
355
356	req.t.tca_family = AF_UNSPEC;
357
358	memset(&req, 0, sizeof(req));
359
360	memset(&nladdr, 0, sizeof(nladdr));
361	nladdr.nl_family = AF_NETLINK;
362
363	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
364	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
365	req.n.nlmsg_type = cmd;
366	argc -=1;
367	argv +=1;
368
369
370	tail = NLMSG_TAIL(&req.n);
371	addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
372
373	while (argc > 0) {
374		if (strcmp(*argv, "action") == 0 ) {
375			argc--;
376			argv++;
377			continue;
378		} else if (strcmp(*argv, "help") == 0) {
379			return -1;
380		}
381
382		strncpy(k, *argv, sizeof (k) - 1);
383		a = get_action_kind(k);
384		if (NULL == a) {
385			fprintf(stderr, "Error: non existent action: %s\n",k);
386			ret = -1;
387			goto bad_val;
388		}
389		if (strcmp(a->id, k) != 0) {
390			fprintf(stderr, "Error: non existent action: %s\n",k);
391			ret = -1;
392			goto bad_val;
393		}
394
395		argc -=1;
396		argv +=1;
397		if (argc <= 0) {
398			fprintf(stderr, "Error: no index specified action: %s\n",k);
399			ret = -1;
400			goto bad_val;
401		}
402
403		if (matches(*argv, "index") == 0) {
404			NEXT_ARG();
405			if (get_u32(&i, *argv, 10)) {
406				fprintf(stderr, "Illegal \"index\"\n");
407				ret = -1;
408				goto bad_val;
409			}
410			argc -=1;
411			argv +=1;
412		} else {
413			fprintf(stderr, "Error: no index specified action: %s\n",k);
414			ret = -1;
415			goto bad_val;
416		}
417
418		tail2 = NLMSG_TAIL(&req.n);
419		addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
420		addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
421		addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i);
422		tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
423
424	}
425
426	tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
427
428	req.n.nlmsg_seq = rth.dump = ++rth.seq;
429	if (cmd == RTM_GETACTION)
430		ans = &req.n;
431
432	if (rtnl_talk(&rth, &req.n, 0, 0, ans, NULL, NULL) < 0) {
433		fprintf(stderr, "We have an error talking to the kernel\n");
434		return 1;
435	}
436
437	if (ans && do_print_action(NULL, &req.n, (void*)stdout) < 0) {
438		fprintf(stderr, "Dump terminated\n");
439		return 1;
440	}
441
442	*argc_p = argc;
443	*argv_p = argv;
444bad_val:
445	return ret;
446}
447
448int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p)
449{
450	int argc = *argc_p;
451	char **argv = *argv_p;
452	int ret = 0;
453
454	struct rtattr *tail;
455	struct {
456		struct nlmsghdr         n;
457		struct tcamsg           t;
458		char                    buf[MAX_MSG];
459	} req;
460
461	req.t.tca_family = AF_UNSPEC;
462
463	memset(&req, 0, sizeof(req));
464
465	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
466	req.n.nlmsg_flags = NLM_F_REQUEST|flags;
467	req.n.nlmsg_type = cmd;
468	tail = NLMSG_TAIL(&req.n);
469	argc -=1;
470	argv +=1;
471	if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
472		fprintf(stderr, "Illegal \"action\"\n");
473		return -1;
474	}
475	tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
476
477	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
478		fprintf(stderr, "We have an error talking to the kernel\n");
479		ret = -1;
480	}
481
482	*argc_p = argc;
483	*argv_p = argv;
484
485	return ret;
486}
487
488int tc_act_list_or_flush(int argc, char **argv, int event)
489{
490	int ret = 0, prio = 0, msg_size = 0;
491	char k[16];
492	struct rtattr *tail,*tail2;
493	struct action_util *a = NULL;
494	struct {
495		struct nlmsghdr         n;
496		struct tcamsg           t;
497		char                    buf[MAX_MSG];
498	} req;
499
500	req.t.tca_family = AF_UNSPEC;
501
502	memset(&req, 0, sizeof(req));
503
504	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
505
506	tail = NLMSG_TAIL(&req.n);
507	addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
508	tail2 = NLMSG_TAIL(&req.n);
509
510	strncpy(k, *argv, sizeof (k) - 1);
511#ifdef CONFIG_GACT
512	if (!gact_ld) {
513		get_action_kind("gact");
514	}
515#endif
516	a = get_action_kind(k);
517	if (NULL == a) {
518		fprintf(stderr,"bad action %s\n",k);
519		goto bad_val;
520	}
521	if (strcmp(a->id, k) != 0) {
522		fprintf(stderr,"bad action %s\n",k);
523		goto bad_val;
524	}
525	strncpy(k, *argv, sizeof (k) - 1);
526
527	addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
528	addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
529	tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
530	tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
531
532	msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr));
533
534	if (event == RTM_GETACTION) {
535		if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) {
536			perror("Cannot send dump request");
537			return 1;
538		}
539		ret = rtnl_dump_filter(&rth, do_print_action, stdout, NULL, NULL);
540	}
541
542	if (event == RTM_DELACTION) {
543		req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len);
544		req.n.nlmsg_type = RTM_DELACTION;
545		req.n.nlmsg_flags |= NLM_F_ROOT;
546		req.n.nlmsg_flags |= NLM_F_REQUEST;
547		if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
548			fprintf(stderr, "We have an error flushing\n");
549			return 1;
550		}
551
552	}
553
554bad_val:
555
556	return ret;
557}
558
559int do_action(int argc, char **argv)
560{
561
562	int ret = 0;
563
564	while (argc > 0) {
565
566		if (matches(*argv, "add") == 0) {
567			ret =  tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv);
568		} else if (matches(*argv, "change") == 0 ||
569			  matches(*argv, "replace") == 0) {
570			ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv);
571		} else if (matches(*argv, "delete") == 0) {
572			argc -=1;
573			argv +=1;
574			ret = tc_action_gd(RTM_DELACTION, 0,  &argc, &argv);
575		} else if (matches(*argv, "get") == 0) {
576			argc -=1;
577			argv +=1;
578			ret = tc_action_gd(RTM_GETACTION, 0,  &argc, &argv);
579		} else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
580						|| matches(*argv, "lst") == 0) {
581			if (argc <= 2) {
582				act_usage();
583				return -1;
584			}
585			return tc_act_list_or_flush(argc-2, argv+2, RTM_GETACTION);
586		} else if (matches(*argv, "flush") == 0) {
587			if (argc <= 2) {
588				act_usage();
589				return -1;
590			}
591			return tc_act_list_or_flush(argc-2, argv+2, RTM_DELACTION);
592		} else if (matches(*argv, "help") == 0) {
593			act_usage();
594			return -1;
595		} else {
596
597			ret = -1;
598		}
599
600		if (ret < 0) {
601			fprintf(stderr, "Command \"%s\" is unknown, try \"tc action help\".\n", *argv);
602			return -1;
603		}
604	}
605
606	return 0;
607}
608
609