]> rtime.felk.cvut.cz Git - lisovros/iproute2_canprio.git/blob - tc/m_action.c
6d730630352beaec948e2c47ea906e0a6ae76103
[lisovros/iproute2_canprio.git] / tc / m_action.c
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_util.h"
29
30 void *aBODY;
31
32
33 static struct action_util * action_list;
34 #ifdef CONFIG_GACT
35 int gact_ld = 0 ; //fuckin backward compatibility
36 #endif
37 int batch_c = 0;
38 int tab_flush = 0;
39
40 void act_usage(void)
41 {
42         fprintf (stderr, "action usage improper\n");
43 }
44
45 static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt)
46 {
47         if (opt && RTA_PAYLOAD(opt))
48                 fprintf(f, "[Unknown action, optlen=%u] ", RTA_PAYLOAD(opt));
49         return 0;
50 }
51
52 static int parse_noaopt(struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n)
53 {
54         int argc = *argc_p;
55         char **argv = *argv_p;
56
57         if (argc) {
58                 fprintf(stderr, "Unknown action \"%s\", hence option \"%s\" is unparsable\n", au->id, *argv);
59         } else {
60                 fprintf(stderr, "Unknown action \"%s\"\n", au->id);
61         }
62         return -1;
63 }
64
65 struct action_util *get_action_kind(char *str)
66 {
67         void *dlh;
68         char buf[256];
69         struct action_util *a;
70 #ifdef CONFIG_GACT
71         int looked4gact = 0;
72 restart_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
95 reg:
96         a->next = action_list;
97         action_list = a;
98         return a;
99
100 noexist:
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
119 int
120 new_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
132 int
133 parse_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 =
149             (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len));
150
151         addattr_l(n, MAX_MSG, tca_id, NULL, 0);
152
153         while (argc > 0) {
154
155                 memset(k, 0, sizeof (k));
156
157                 if (strcmp(*argv, "action") == 0 ) {
158                         argc--;
159                         argv++;
160                         eap = 1;
161 #ifdef CONFIG_GACT
162                         if (!gact_ld) {
163                                 get_action_kind("gact");
164                         }
165 #endif
166                         continue;
167                 } else if (strcmp(*argv, "help") == 0) {
168                         return -1;
169                 } else if (new_cmd(argv)) {
170                         goto done0;
171                 } else {
172                         struct action_util *a = NULL;
173                         strncpy(k, *argv, sizeof (k) - 1);
174                         eap = 0;
175                         if (argc > 0 ) {
176                                 a = get_action_kind(k);
177                         } else {
178 done0:
179                                 if (ok)
180                                         break;
181                                 else
182                                         goto done;
183                         }
184
185                         if (NULL == a) { 
186                                 goto bad_val;
187                         }
188
189                         tail =
190                             (struct rtattr *) (((void *) n) +
191                                        NLMSG_ALIGN(n->nlmsg_len));
192                         addattr_l(n, MAX_MSG, ++prio, NULL, 0);
193                         addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
194
195                         ret = a->parse_aopt(a,&argc, &argv, TCA_ACT_OPTIONS, n);
196
197                         if (ret < 0) {
198                                 fprintf(stderr,"bad action parsing\n");
199                                 goto bad_val;
200                         }
201                         tail->rta_len =
202                             (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)) -
203                             (void *) tail;
204                         ok++;
205                 }
206
207         }
208
209         if (eap > 0) {
210                 fprintf(stderr,"bad action empty %d\n",eap);
211                 goto bad_val;
212         }
213
214         tail2->rta_len =
215             (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)) - (void *) tail2;
216
217 done:
218         *argc_p = argc;
219         *argv_p = argv;
220         return 0;
221 bad_val:
222         /* no need to undo things, returning from here should 
223          * cause enough pain */
224         fprintf(stderr, "parse_action: bad value (%d:%s)!\n",argc,*argv);
225         return -1;
226 }
227
228 int
229 tc_print_one_action(FILE * f, struct rtattr *arg)
230 {
231
232         struct rtattr *tb[TCA_ACT_MAX + 1];
233         int err = 0;
234         struct action_util *a = NULL;
235
236         if (arg == NULL)
237                 return -1;
238
239         memset(tb, 0, sizeof (tb));
240         parse_rtattr(tb, TCA_ACT_MAX, RTA_DATA(arg), RTA_PAYLOAD(arg));
241         if (tb[TCA_ACT_KIND] == NULL) {
242                 fprintf(stderr, "NULL Action!\n");
243                 return -1;
244         }
245
246
247         a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND]));
248         if (NULL == a)
249                 return err;
250
251         if (tab_flush) {
252                 fprintf(f," %s \n", a->id);
253                 tab_flush = 0;
254                 return 0;
255         }
256
257         err = a->print_aopt(a,f,tb[TCA_ACT_OPTIONS]);
258
259
260         if (0 > err)
261                 return err;
262
263         if (show_stats && tb[TCA_STATS]) {
264                 fprintf(f, "\t");
265                 print_tcstats_attr(f, tb[TCA_STATS]);
266                 fprintf(f, "\n");
267         }
268
269         return 0;
270 }
271
272 int
273 tc_print_action(FILE * f, const struct rtattr *arg)
274 {
275
276         int i;
277         struct rtattr *tb[TCA_ACT_MAX_PRIO + 1];
278
279         if (arg == NULL)
280                 return 0;
281
282         memset(tb, 0, sizeof (tb));
283         parse_rtattr(tb, TCA_ACT_MAX_PRIO, RTA_DATA(arg), RTA_PAYLOAD(arg));
284
285         if (tab_flush && NULL != tb[0]  && NULL == tb[1]) {
286                 int ret = tc_print_one_action(f, tb[0]);
287                 return ret;
288         }
289
290         for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
291                 if (tb[i]) {
292                         fprintf(f, "\n\taction order %d: ", i + batch_c);
293                         if (0 > tc_print_one_action(f, tb[i])) {
294                                 fprintf(f, "Error printing action\n");
295                         }
296                 }
297
298         }
299
300         batch_c+=TCA_ACT_MAX_PRIO ;
301         return 0;
302 }
303
304 static int do_print_action(const struct sockaddr_nl *who,
305                            const struct nlmsghdr *n,
306                            void *arg)
307 {
308         FILE *fp = (FILE*)arg;
309         struct tcamsg *t = NLMSG_DATA(n);
310         int len = n->nlmsg_len;
311         struct rtattr * tb[TCAA_MAX+1];
312
313         len -= NLMSG_LENGTH(sizeof(*t));
314
315         if (len < 0) {
316                 fprintf(stderr, "Wrong len %d\n", len);
317                 return -1;
318         }
319
320         memset(tb, 0, sizeof(tb));
321         parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len);
322
323         if (NULL == tb[TCA_ACT_TAB]) {
324                 if (n->nlmsg_type != RTM_GETACTION)
325                         fprintf(stderr, "do_print_action: NULL kind\n");
326                 return -1;
327         }     
328
329         if (n->nlmsg_type == RTM_DELACTION) {
330                 if (n->nlmsg_flags & NLM_F_ROOT) {
331                         fprintf(fp, "Flushed table "); 
332                         tab_flush = 1;
333                 } else {
334                         fprintf(fp, "deleted action ");
335                 }
336         }
337
338         if (n->nlmsg_type == RTM_NEWACTION)
339                 fprintf(fp, "Added action ");
340         tc_print_action(fp, tb[TCA_ACT_TAB]);
341
342         return 0;
343 }
344
345 int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p)
346 {
347         char k[16];
348         struct action_util *a = NULL;
349         int argc = *argc_p;
350         char **argv = *argv_p;
351         int prio = 0;
352         int ret = 0;
353         __u32 i;
354         struct rtnl_handle rth;
355         struct sockaddr_nl nladdr;
356         struct rtattr *tail;
357         struct rtattr *tail2;
358         struct nlmsghdr *ans = NULL;
359
360         struct {
361                 struct nlmsghdr         n;
362                 struct tcamsg           t;
363                 char                    buf[MAX_MSG];
364         } req;
365
366         req.t.tca_family = AF_UNSPEC;
367
368         memset(&req, 0, sizeof(req));
369
370         memset(&nladdr, 0, sizeof(nladdr));
371         nladdr.nl_family = AF_NETLINK;
372
373         req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
374         req.n.nlmsg_flags = NLM_F_REQUEST|flags;
375         req.n.nlmsg_type = cmd;
376         argc -=1;
377         argv +=1;
378
379
380         tail = (struct rtattr*)(((void*)&req.n)+NLMSG_ALIGN(req.n.nlmsg_len));
381
382         addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
383
384         while (argc > 0) {
385                 if (strcmp(*argv, "action") == 0 ) {
386                         argc--;
387                         argv++;
388                         continue;
389                 } else if (strcmp(*argv, "help") == 0) {
390                         return -1;
391                 }
392
393                 strncpy(k, *argv, sizeof (k) - 1);
394                 a = get_action_kind(k);
395                 if (NULL == a) { 
396                         fprintf(stderr, "Error: non existent action: %s\n",k);
397                         ret = -1;
398                         goto bad_val;
399                 }
400                 if (strcmp(a->id, k) != 0) {
401                         fprintf(stderr, "Error: non existent action: %s\n",k);
402                         ret = -1;
403                         goto bad_val;
404                 }
405
406                 argc -=1;
407                 argv +=1;
408                 if (argc <= 0) {
409                         fprintf(stderr, "Error: no index specified action: %s\n",k);
410                         ret = -1;
411                         goto bad_val;
412                 }
413
414                 if (matches(*argv, "index") == 0) {
415                         NEXT_ARG();
416                         if (get_u32(&i, *argv, 10)) {
417                                 fprintf(stderr, "Illegal \"index\"\n");
418                                 ret = -1;
419                                 goto bad_val;
420                         }
421                         argc -=1;
422                         argv +=1;
423                 } else {
424                         fprintf(stderr, "Error: no index specified action: %s\n",k);
425                         ret = -1;
426                         goto bad_val;
427                 }
428
429         tail2 =
430            (struct rtattr *) (((void *) &req.n) + NLMSG_ALIGN(req.n.nlmsg_len));
431         addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
432         addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
433         addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i);
434         tail2->rta_len =
435             (((void *) &req.n) + NLMSG_ALIGN(req.n.nlmsg_len)) - (void *) tail2;
436
437         }
438
439         tail->rta_len = (((void*)&req.n)+ NLMSG_ALIGN(req.n.nlmsg_len)) - (void*)tail;
440
441         if (rtnl_open(&rth, 0) < 0) {
442                 fprintf(stderr, "Cannot open rtnetlink\n");
443                 exit(1);
444         }
445
446         req.n.nlmsg_seq = rth.dump = ++rth.seq;
447         if (cmd == RTM_GETACTION)
448                 ans = &req.n;
449         if (rtnl_talk(&rth, &req.n, 0, 0, ans, NULL, NULL) < 0) {
450                 fprintf(stderr, "We have an error talking to the kernel\n");
451                 rtnl_close(&rth);
452                 exit (1);
453         }
454
455         if (ans && do_print_action(NULL, &req.n, (void*)stdout) < 0) {
456                 fprintf(stderr, "Dump terminated\n");
457                 rtnl_close(&rth);
458                 exit(1);
459         }
460
461         *argc_p = argc;
462         *argv_p = argv;
463         rtnl_close(&rth);
464 bad_val:
465         return ret;
466 }
467
468 int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p)
469 {
470         int argc = *argc_p;
471         char **argv = *argv_p;
472         int ret = 0;
473
474         struct rtnl_handle rth;
475         struct rtattr *tail;
476         struct {
477                 struct nlmsghdr         n;
478                 struct tcamsg           t;
479                 char                    buf[MAX_MSG];
480         } req;
481
482         req.t.tca_family = AF_UNSPEC;
483
484         memset(&req, 0, sizeof(req));
485
486         req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
487         req.n.nlmsg_flags = NLM_F_REQUEST|flags;
488         req.n.nlmsg_type = cmd;
489         tail = (struct rtattr*)(((void*)&req.n)+NLMSG_ALIGN(req.n.nlmsg_len));
490         argc -=1;
491         argv +=1;
492         if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
493                 fprintf(stderr, "Illegal \"action\"\n");
494                 return -1;
495         }
496         tail->rta_len = (((void*)&req.n)+req.n.nlmsg_len) - (void*)tail;
497
498         if (rtnl_open(&rth, 0) < 0) {
499                 fprintf(stderr, "Cannot open rtnetlink\n");
500                 exit(1);
501         }
502
503
504         if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
505                 fprintf(stderr, "We have an error talking to the kernel\n");
506                 ret = -1;
507         }
508
509         *argc_p = argc;
510         *argv_p = argv;
511         rtnl_close(&rth);
512         return ret;
513 }
514
515 int tc_act_list_or_flush(int argc, char **argv, int event)
516 {
517         int ret = 0, prio = 0, msg_size = 0;
518         char k[16];
519         struct rtnl_handle rth;
520         struct rtattr *tail,*tail2;
521         struct action_util *a = NULL;
522         struct {
523                 struct nlmsghdr         n;
524                 struct tcamsg           t;
525                 char                    buf[MAX_MSG];
526         } req;
527
528         req.t.tca_family = AF_UNSPEC;
529
530         memset(&req, 0, sizeof(req));
531
532         req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
533
534         tail = (struct rtattr*)(((void*)&req.n)+NLMSG_ALIGN(req.n.nlmsg_len));
535
536         addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
537         tail2 =
538            (struct rtattr *) (((void *) &req.n) + NLMSG_ALIGN(req.n.nlmsg_len));
539
540         strncpy(k, *argv, sizeof (k) - 1);
541 #ifdef CONFIG_GACT
542         if (!gact_ld) {
543                 get_action_kind("gact");
544         }
545 #endif
546         a = get_action_kind(k);
547         if (NULL == a) { 
548                 fprintf(stderr,"bad action %s\n",k);
549                 goto bad_val;
550         }
551         if (strcmp(a->id, k) != 0) {
552                 fprintf(stderr,"bad action %s\n",k);
553                 goto bad_val;
554         }
555         strncpy(k, *argv, sizeof (k) - 1);
556
557         addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
558         addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
559         tail2->rta_len =
560             (((void *) &req.n) + NLMSG_ALIGN(req.n.nlmsg_len)) - (void *) tail2;
561         tail->rta_len = (((void*)&req.n)+NLMSG_ALIGN(req.n.nlmsg_len)) - (void*)tail;
562
563
564         if (rtnl_open(&rth, 0) < 0) {
565                 fprintf(stderr, "Cannot open rtnetlink\n");
566                 exit(1);
567         }
568
569         msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr));
570
571         if (event == RTM_GETACTION) { 
572                 if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) {
573                         perror("Cannot send dump request");
574                         exit(1);
575                 }
576                 ret = rtnl_dump_filter(&rth, do_print_action, stdout, NULL, NULL);
577         }
578
579         if (event == RTM_DELACTION) { 
580                 req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len);
581                 req.n.nlmsg_type = RTM_DELACTION;
582                 req.n.nlmsg_flags |= NLM_F_ROOT;
583                 req.n.nlmsg_flags |= NLM_F_REQUEST;
584                 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
585                         fprintf(stderr, "We have an error flushing\n");
586                         rtnl_close(&rth);
587                         exit (1);
588                 }
589
590         }
591
592 bad_val:
593
594         rtnl_close(&rth);
595         return ret;
596 }
597
598 int do_action(int argc, char **argv)
599 {
600
601         int ret = 0;
602
603         while (argc > 0) {
604
605                 if (matches(*argv, "add") == 0) {
606                         ret =  tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv);
607                 } else if (matches(*argv, "change") == 0 ||
608                           matches(*argv, "replace") == 0) {
609                         ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv);
610                 } else if (matches(*argv, "delete") == 0) {
611                         argc -=1;
612                         argv +=1;
613                         ret = tc_action_gd(RTM_DELACTION, 0,  &argc, &argv);
614                 } else if (matches(*argv, "get") == 0) {
615                         argc -=1;
616                         argv +=1;
617                         ret = tc_action_gd(RTM_GETACTION, 0,  &argc, &argv);
618                 } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
619                                                 || matches(*argv, "lst") == 0) {
620                         if (argc <= 2) {
621                                 act_usage();
622                                 return -1;
623                         }
624                         return tc_act_list_or_flush(argc-2, argv+2, RTM_GETACTION);
625                 } else if (matches(*argv, "flush") == 0) {
626                         if (argc <= 2) {
627                                 act_usage();
628                                 return -1;
629                         }
630                         return tc_act_list_or_flush(argc-2, argv+2, RTM_DELACTION);
631                 } else if (matches(*argv, "help") == 0) {
632                         act_usage();
633                         return -1;
634                 } else {
635
636                         ret = -1;
637                 }
638
639                 if (ret < 0) {
640                         fprintf(stderr, "Command \"%s\" is unknown, try \"tc action help\".\n", *argv);
641                         return -1;
642                 }
643         }
644
645         return 0;
646 }
647