From 68d5ba54296ba689ec96e20c82bc5936d63ecc66 Mon Sep 17 00:00:00 2001 From: "osdl.net!shemminger" Date: Fri, 13 Aug 2004 23:54:55 +0000 Subject: [PATCH] (Logical change 1.66) --- include/linux/pkt_cls.h | 314 +++++++++++++++++++ tc/m_action.c | 659 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 973 insertions(+) diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index e69de29b..1bcdd11e 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -0,0 +1,314 @@ +#ifndef __LINUX_PKT_CLS_H +#define __LINUX_PKT_CLS_H + +/* I think i could have done better macros ; for now this is stolen from + * some arch/mips code - jhs +*/ +#define _TC_MAKE32(x) ((x)) + +#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n)) +#define _TC_MAKEMASK(v,n) (_TC_MAKE32((_TC_MAKE32(1)<<(v))-1) << _TC_MAKE32(n)) +#define _TC_MAKEVALUE(v,n) (_TC_MAKE32(v) << _TC_MAKE32(n)) +#define _TC_GETVALUE(v,n,m) ((_TC_MAKE32(v) & _TC_MAKE32(m)) >> _TC_MAKE32(n)) + +/* verdict bit breakdown + * +bit 0: when set -> this packet has been munged already + +bit 1: when set -> It is ok to munge this packet + +bit 2,3,4,5: Reclassify counter - sort of reverse TTL - if exceeded +assume loop + +bit 6,7: Where this packet was last seen +0: Above the transmit example at the socket level +1: on the Ingress +2: on the Egress + +bit 8: when set --> Request not to classify on ingress. + +bits 9,10,11: redirect counter - redirect TTL. Loop avoidance + + * + * */ + +#define TC_MUNGED _TC_MAKEMASK1(0) +#define SET_TC_MUNGED(v) ( TC_MUNGED | (v & ~TC_MUNGED)) +#define CLR_TC_MUNGED(v) ( v & ~TC_MUNGED) + +#define TC_OK2MUNGE _TC_MAKEMASK1(1) +#define SET_TC_OK2MUNGE(v) ( TC_OK2MUNGE | (v & ~TC_OK2MUNGE)) +#define CLR_TC_OK2MUNGE(v) ( v & ~TC_OK2MUNGE) + +#define S_TC_VERD _TC_MAKE32(2) +#define M_TC_VERD _TC_MAKEMASK(4,S_TC_VERD) +#define G_TC_VERD(x) _TC_GETVALUE(x,S_TC_VERD,M_TC_VERD) +#define V_TC_VERD(x) _TC_MAKEVALUE(x,S_TC_VERD) +#define SET_TC_VERD(v,n) ((V_TC_VERD(n)) | (v & ~M_TC_VERD)) + +#define S_TC_FROM _TC_MAKE32(6) +#define M_TC_FROM _TC_MAKEMASK(2,S_TC_FROM) +#define G_TC_FROM(x) _TC_GETVALUE(x,S_TC_FROM,M_TC_FROM) +#define V_TC_FROM(x) _TC_MAKEVALUE(x,S_TC_FROM) +#define SET_TC_FROM(v,n) ((V_TC_FROM(n)) | (v & ~M_TC_FROM)) +#define AT_STACK 0x0 +#define AT_INGRESS 0x1 +#define AT_EGRESS 0x2 + +#define TC_NCLS _TC_MAKEMASK1(8) +#define SET_TC_NCLS(v) ( TC_NCLS | (v & ~TC_NCLS)) +#define CLR_TC_NCLS(v) ( v & ~TC_NCLS) + +#define S_TC_RTTL _TC_MAKE32(9) +#define M_TC_RTTL _TC_MAKEMASK(3,S_TC_RTTL) +#define G_TC_RTTL(x) _TC_GETVALUE(x,S_TC_RTTL,M_TC_RTTL) +#define V_TC_RTTL(x) _TC_MAKEVALUE(x,S_TC_RTTL) +#define SET_TC_RTTL(v,n) ((V_TC_RTTL(n)) | (v & ~M_TC_RTTL)) + +#define S_TC_AT _TC_MAKE32(12) +#define M_TC_AT _TC_MAKEMASK(2,S_TC_AT) +#define G_TC_AT(x) _TC_GETVALUE(x,S_TC_AT,M_TC_AT) +#define V_TC_AT(x) _TC_MAKEVALUE(x,S_TC_AT) +#define SET_TC_AT(v,n) ((V_TC_AT(n)) | (v & ~M_TC_AT)) + +/* Action attributes */ +enum +{ + TCA_ACT_UNSPEC, + TCA_ACT_KIND, + TCA_ACT_OPTIONS, + TCA_ACT_INDEX, + __TCA_ACT_MAX +}; + +#define TCA_ACT_MAX __TCA_ACT_MAX +#define TCA_OLD_COMPAT (TCA_ACT_MAX+1) +#define TCA_ACT_MAX_PRIO 32 +#define TCA_ACT_BIND 1 +#define TCA_ACT_NOBIND 0 +#define TCA_ACT_UNBIND 1 +#define TCA_ACT_NOUNBIND 0 +#define TCA_ACT_REPLACE 1 +#define TCA_ACT_NOREPLACE 0 +#define MAX_REC_LOOP 4 +#define MAX_RED_LOOP 4 + +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_RECLASSIFY 1 +#define TC_ACT_SHOT 2 +#define TC_ACT_PIPE 3 +#define TC_ACT_STOLEN 4 +#define TC_ACT_QUEUED 5 +#define TC_ACT_REPEAT 6 +#define TC_ACT_JUMP 0x10000000 + +/* Action type identifiers*/ +enum +{ + TCA_ID_UNSPEC=0, + TCA_ID_POLICE=1, + /* other actions go here */ + __TCA_ID_MAX=255 +}; + +#define TCA_ID_MAX __TCA_ID_MAX + +struct tc_police +{ + __u32 index; + int action; +#define TC_POLICE_UNSPEC TC_ACT_UNSPEC +#define TC_POLICE_OK TC_ACT_OK +#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY +#define TC_POLICE_SHOT TC_ACT_SHOT +#define TC_POLICE_PIPE TC_ACT_PIPE + + __u32 limit; + __u32 burst; + __u32 mtu; + struct tc_ratespec rate; + struct tc_ratespec peakrate; + int refcnt; + int bindcnt; + __u32 capab; +}; + +struct tcf_t +{ + __u32 install; + __u32 lastuse; + __u32 expires; +}; + +struct tc_cnt +{ + int refcnt; + int bindcnt; +}; + +#define tc_gen \ + __u32 index; \ + __u32 capab; \ + int action; \ + int refcnt; \ + int bindcnt + +enum +{ + TCA_POLICE_UNSPEC, + TCA_POLICE_TBF, + TCA_POLICE_RATE, + TCA_POLICE_PEAKRATE, + TCA_POLICE_AVRATE, + TCA_POLICE_RESULT, + __TCA_POLICE_MAX +#define TCA_POLICE_RESULT TCA_POLICE_RESULT +}; + +#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) + +/* U32 filters */ + +#define TC_U32_HTID(h) ((h)&0xFFF00000) +#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20) +#define TC_U32_HASH(h) (((h)>>12)&0xFF) +#define TC_U32_NODE(h) ((h)&0xFFF) +#define TC_U32_KEY(h) ((h)&0xFFFFF) +#define TC_U32_UNSPEC 0 +#define TC_U32_ROOT (0xFFF00000) + +enum +{ + TCA_U32_UNSPEC, + TCA_U32_CLASSID, + TCA_U32_HASH, + TCA_U32_LINK, + TCA_U32_DIVISOR, + TCA_U32_SEL, + TCA_U32_POLICE, + TCA_U32_ACT, + TCA_U32_INDEV, + TCA_U32_PCNT, + __TCA_U32_MAX +}; + +#define TCA_U32_MAX (__TCA_U32_MAX - 1) + +struct tc_u32_key +{ + __u32 mask; + __u32 val; + int off; + int offmask; +}; + +struct tc_u32_sel +{ + unsigned char flags; + unsigned char offshift; + unsigned char nkeys; + + __u16 offmask; + __u16 off; + short offoff; + + short hoff; + __u32 hmask; + struct tc_u32_key keys[0]; +}; + +struct tc_u32_pcnt +{ + __u64 rcnt; + __u64 rhit; + __u64 kcnts[0]; +}; +/* Flags */ + +#define TC_U32_TERMINAL 1 +#define TC_U32_OFFSET 2 +#define TC_U32_VAROFFSET 4 +#define TC_U32_EAT 8 + +#define TC_U32_MAXDEPTH 8 + + +/* RSVP filter */ + +enum +{ + TCA_RSVP_UNSPEC, + TCA_RSVP_CLASSID, + TCA_RSVP_DST, + TCA_RSVP_SRC, + TCA_RSVP_PINFO, + TCA_RSVP_POLICE, + __TCA_RSVP_MAX +}; + +#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 ) + +struct tc_rsvp_gpi +{ + __u32 key; + __u32 mask; + int offset; +}; + +struct tc_rsvp_pinfo +{ + struct tc_rsvp_gpi dpi; + struct tc_rsvp_gpi spi; + __u8 protocol; + __u8 tunnelid; + __u8 tunnelhdr; +}; + +/* ROUTE filter */ + +enum +{ + TCA_ROUTE4_UNSPEC, + TCA_ROUTE4_CLASSID, + TCA_ROUTE4_TO, + TCA_ROUTE4_FROM, + TCA_ROUTE4_IIF, + TCA_ROUTE4_POLICE, + __TCA_ROUTE4_MAX +}; + +#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1) + + +/* FW filter */ + +enum +{ + TCA_FW_UNSPEC, + TCA_FW_CLASSID, + TCA_FW_POLICE, + TCA_FW_INDEV, /* used by CONFIG_NET_CLS_IND */ + TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */ + __TCA_FW_MAX +}; + +#define TCA_FW_MAX (__TCA_FW_MAX - 1) + +/* TC index filter */ + +enum +{ + TCA_TCINDEX_UNSPEC, + TCA_TCINDEX_HASH, + TCA_TCINDEX_MASK, + TCA_TCINDEX_SHIFT, + TCA_TCINDEX_FALL_THROUGH, + TCA_TCINDEX_CLASSID, + TCA_TCINDEX_POLICE, + __TCA_TCINDEX_MAX +}; + +#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) + +#endif diff --git a/tc/m_action.c b/tc/m_action.c index e69de29b..aa3a3aa0 100644 --- a/tc/m_action.c +++ b/tc/m_action.c @@ -0,0 +1,659 @@ +/* + * m_action.c Action Management + * + * 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: + * - parse to be passed a filedescriptor for logging purposes + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "tc_util.h" + +void *aBODY; + + +static struct action_util * action_list; +#ifdef CONFIG_GACT +int gact_ld = 0 ; //fuckin backward compatibility +#endif +int batch_c = 0; +int tab_flush = 0; + +void act_usage(void) +{ + fprintf (stderr, "action usage improper\n"); +} + +static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt) +{ + if (opt && RTA_PAYLOAD(opt)) + fprintf(f, "[Unknown action, optlen=%u] ", RTA_PAYLOAD(opt)); + return 0; +} + +static int parse_noaopt(struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + + if (argc) { + fprintf(stderr, "Unknown action \"%s\", hence option \"%s\" is unparsable\n", au->id, *argv); + } else { + fprintf(stderr, "Unknown action \"%s\"\n", au->id); + } + return -1; +} + +struct action_util *get_action_kind(char *str) +{ + void *dlh; + char buf[256]; + struct action_util *a; +#ifdef CONFIG_GACT + int looked4gact = 0; +restart_s: +#endif + for (a = action_list; a; a = a->next) { + if (strcmp(a->id, str) == 0) + return a; + } + + snprintf(buf, sizeof(buf), "m_%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + dlh = aBODY; + if (dlh == NULL) { + dlh = aBODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "%s_util", str); + a = dlsym(dlh, buf); + if (a == NULL) + goto noexist; + +reg: + a->next = action_list; + action_list = a; + return a; + +noexist: +#ifdef CONFIG_GACT + if (!looked4gact) { + looked4gact = 1; + strcpy(str,"gact"); + goto restart_s; + } +#endif + a = malloc(sizeof(*a)); + if (a) { + memset(a, 0, sizeof(*a)); + strncpy(a->id, "noact", 15); + a->parse_aopt = parse_noaopt; + a->print_aopt = print_noaopt; + goto reg; + } + return a; +} + +int +new_cmd(char **argv) +{ + if ((matches(*argv, "change") == 0) || + (matches(*argv, "replace") == 0)|| + (matches(*argv, "delete") == 0)|| + (matches(*argv, "add") == 0)) + return 1; + + return 0; + +} + +int +parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + struct rtattr *tail, *tail2; + char k[16]; + int ok = 0; + int eap = 0; /* expect action parameters */ + + int ret = 0; + int prio = 0; + + if (argc <= 0) + return -1; + + tail = tail2 = + (struct rtattr *) (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)); + + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + + while (argc > 0) { + + memset(k, 0, sizeof (k)); + + if (strcmp(*argv, "action") == 0 ) { + argc--; + argv++; + eap = 1; +#ifdef CONFIG_GACT + if (!gact_ld) { + get_action_kind("gact"); + } +#endif + continue; + } else if (strcmp(*argv, "help") == 0) { + return -1; + } else if (new_cmd(argv)) { + goto done0; + } else { + struct action_util *a = NULL; + strncpy(k, *argv, sizeof (k) - 1); + eap = 0; + if (argc > 0 ) { + a = get_action_kind(k); + } else { +done0: + if (ok) + break; + else + goto done; + } + + if (NULL == a) { + goto bad_val; + } + + tail = + (struct rtattr *) (((void *) n) + + NLMSG_ALIGN(n->nlmsg_len)); + addattr_l(n, MAX_MSG, ++prio, NULL, 0); + addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); + + ret = a->parse_aopt(a,&argc, &argv, TCA_ACT_OPTIONS, n); + + if (ret < 0) { + fprintf(stderr,"bad action parsing\n"); + goto bad_val; + } + tail->rta_len = + (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)) - + (void *) tail; + ok++; + } + + } + + if (eap > 0) { + fprintf(stderr,"bad action empty %d\n",eap); + goto bad_val; + } + + tail2->rta_len = + (((void *) n) + NLMSG_ALIGN(n->nlmsg_len)) - (void *) tail2; + +done: + *argc_p = argc; + *argv_p = argv; + return 0; +bad_val: + /* no need to undo things, returning from here should + * cause enough pain */ + fprintf(stderr, "parse_action: bad value (%d:%s)!\n",argc,*argv); + return -1; +} + +int +tc_print_one_action(FILE * f, struct rtattr *arg) +{ + + struct rtattr *tb[TCA_ACT_MAX + 1]; + int err = 0; + struct action_util *a = NULL; + + if (arg == NULL) + return -1; + + memset(tb, 0, sizeof (tb)); + parse_rtattr(tb, TCA_ACT_MAX, RTA_DATA(arg), RTA_PAYLOAD(arg)); + if (tb[TCA_ACT_KIND] == NULL) { + fprintf(stderr, "NULL Action!\n"); + return -1; + } + + + a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND])); + if (NULL == a) + return err; + + if (tab_flush) { + fprintf(f," %s \n", a->id); + tab_flush = 0; + return 0; + } + + err = a->print_aopt(a,f,tb[TCA_ACT_OPTIONS]); + + + if (0 > err) + return err; + + if (show_stats) { + if (tb[TCA_STATS]) { +#ifndef STOOPID_8BYTE + if (RTA_PAYLOAD(tb[TCA_STATS]) < + sizeof (struct tc_stats)) + fprintf(f, "statistics truncated"); + else { +#endif + struct tc_stats st; + memcpy(&st, RTA_DATA(tb[TCA_STATS]), + sizeof (st)); + fprintf(f, "\t"); + print_tcstats(f, &st); + fprintf(f, "\n"); +#ifndef STOOPID_8BYTE + } +#endif + } + } + + return 0; +} + +int +tc_print_action(FILE * f, struct rtattr *arg) +{ + + int i; + struct rtattr *tb[TCA_ACT_MAX_PRIO + 1]; + + if (arg == NULL) + return 0; + + memset(tb, 0, sizeof (tb)); + parse_rtattr(tb, TCA_ACT_MAX_PRIO, RTA_DATA(arg), RTA_PAYLOAD(arg)); + + if (tab_flush && NULL != tb[0] && NULL == tb[1]) { + int ret = tc_print_one_action(f, tb[0]); + return ret; + } + + for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { + if (tb[i]) { + fprintf(f, "\n\taction order %d: ", i + batch_c); + if (0 > tc_print_one_action(f, tb[i])) { + fprintf(f, "Error printing action\n"); + } + } + + } + + batch_c+=TCA_ACT_MAX_PRIO ; + return 0; +} + +int do_print_action(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct tcamsg *t = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[TCAA_MAX+1]; + + len -= NLMSG_LENGTH(sizeof(*t)); + + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len); + + if (NULL == tb[TCA_ACT_TAB]) { + if (n->nlmsg_type != RTM_GETACTION) + fprintf(stderr, "do_print_action: NULL kind\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELACTION) { + if (n->nlmsg_flags & NLM_F_ROOT) { + fprintf(fp, "Flushed table "); + tab_flush = 1; + } else { + fprintf(fp, "deleted action "); + } + } + + if (n->nlmsg_type == RTM_NEWACTION) + fprintf(fp, "Added action "); + tc_print_action(fp, tb[TCA_ACT_TAB]); + + return 0; +} + +int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p) +{ + char k[16]; + struct action_util *a = NULL; + int argc = *argc_p; + char **argv = *argv_p; + int prio = 0; + int ret = 0; + __u32 i; + struct rtnl_handle rth; + struct sockaddr_nl nladdr; + struct rtattr *tail; + struct rtattr *tail2; + struct nlmsghdr *ans = NULL; + + struct { + struct nlmsghdr n; + struct tcamsg t; + char buf[MAX_MSG]; + } req; + + req.t.tca_family = AF_UNSPEC; + + memset(&req, 0, sizeof(req)); + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + argc -=1; + argv +=1; + + + tail = (struct rtattr*)(((void*)&req.n)+NLMSG_ALIGN(req.n.nlmsg_len)); + + addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); + + while (argc > 0) { + if (strcmp(*argv, "action") == 0 ) { + argc--; + argv++; + continue; + } else if (strcmp(*argv, "help") == 0) { + return -1; + } + + strncpy(k, *argv, sizeof (k) - 1); + a = get_action_kind(k); + if (NULL == a) { + fprintf(stderr, "Error: non existent action: %s\n",k); + ret = -1; + goto bad_val; + } + if (strcmp(a->id, k) != 0) { + fprintf(stderr, "Error: non existent action: %s\n",k); + ret = -1; + goto bad_val; + } + + argc -=1; + argv +=1; + if (argc <= 0) { + fprintf(stderr, "Error: no index specified action: %s\n",k); + ret = -1; + goto bad_val; + } + + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&i, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + ret = -1; + goto bad_val; + } + argc -=1; + argv +=1; + } else { + fprintf(stderr, "Error: no index specified action: %s\n",k); + ret = -1; + goto bad_val; + } + + tail2 = + (struct rtattr *) (((void *) &req.n) + NLMSG_ALIGN(req.n.nlmsg_len)); + addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); + addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); + addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i); + tail2->rta_len = + (((void *) &req.n) + NLMSG_ALIGN(req.n.nlmsg_len)) - (void *) tail2; + + } + + tail->rta_len = (((void*)&req.n)+ NLMSG_ALIGN(req.n.nlmsg_len)) - (void*)tail; + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + exit(1); + } + + req.n.nlmsg_seq = rth.dump = ++rth.seq; + if (cmd == RTM_GETACTION) + ans = &req.n; + if (rtnl_talk(&rth, &req.n, 0, 0, ans, NULL, NULL) < 0) { + fprintf(stderr, "We have an error talking to the kernel\n"); + rtnl_close(&rth); + exit (1); + } + + if (ans && do_print_action(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + rtnl_close(&rth); + exit(1); + } + + *argc_p = argc; + *argv_p = argv; + rtnl_close(&rth); +bad_val: + return ret; +} + +int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p) +{ + int argc = *argc_p; + char **argv = *argv_p; + int ret = 0; + + struct rtnl_handle rth; + struct rtattr *tail; + struct { + struct nlmsghdr n; + struct tcamsg t; + char buf[MAX_MSG]; + } req; + + req.t.tca_family = AF_UNSPEC; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + tail = (struct rtattr*)(((void*)&req.n)+NLMSG_ALIGN(req.n.nlmsg_len)); + argc -=1; + argv +=1; + if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) { + fprintf(stderr, "Illegal \"action\"\n"); + return -1; + } + tail->rta_len = (((void*)&req.n)+req.n.nlmsg_len) - (void*)tail; + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + exit(1); + } + + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { + fprintf(stderr, "We have an error talking to the kernel\n"); + ret = -1; + } + + *argc_p = argc; + *argv_p = argv; + rtnl_close(&rth); + return ret; +} + +int tc_act_list_or_flush(int argc, char **argv, int event) +{ + int ret = 0, prio = 0, msg_size = 0; + char k[16]; + struct rtnl_handle rth; + struct rtattr *tail,*tail2; + struct action_util *a = NULL; + struct { + struct nlmsghdr n; + struct tcamsg t; + char buf[MAX_MSG]; + } req; + + req.t.tca_family = AF_UNSPEC; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + + tail = (struct rtattr*)(((void*)&req.n)+NLMSG_ALIGN(req.n.nlmsg_len)); + + addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); + tail2 = + (struct rtattr *) (((void *) &req.n) + NLMSG_ALIGN(req.n.nlmsg_len)); + + strncpy(k, *argv, sizeof (k) - 1); +#ifdef CONFIG_GACT + if (!gact_ld) { + get_action_kind("gact"); + } +#endif + a = get_action_kind(k); + if (NULL == a) { + fprintf(stderr,"bad action %s\n",k); + goto bad_val; + } + if (strcmp(a->id, k) != 0) { + fprintf(stderr,"bad action %s\n",k); + goto bad_val; + } + strncpy(k, *argv, sizeof (k) - 1); + + addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); + addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); + tail2->rta_len = + (((void *) &req.n) + NLMSG_ALIGN(req.n.nlmsg_len)) - (void *) tail2; + tail->rta_len = (((void*)&req.n)+NLMSG_ALIGN(req.n.nlmsg_len)) - (void*)tail; + + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + exit(1); + } + + msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr)); + + if (event == RTM_GETACTION) { + if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) { + perror("Cannot send dump request"); + exit(1); + } + ret = rtnl_dump_filter(&rth, do_print_action, stdout, NULL, NULL); + } + + if (event == RTM_DELACTION) { + req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len); + req.n.nlmsg_type = RTM_DELACTION; + req.n.nlmsg_flags |= NLM_F_ROOT; + req.n.nlmsg_flags |= NLM_F_REQUEST; + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { + fprintf(stderr, "We have an error flushing\n"); + rtnl_close(&rth); + exit (1); + } + + } + +bad_val: + + rtnl_close(&rth); + return ret; +} + +int do_action(int argc, char **argv) +{ + + int ret = 0; + + while (argc > 0) { + + if (matches(*argv, "add") == 0) { + ret = tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv); + } else if (matches(*argv, "change") == 0 || + matches(*argv, "replace") == 0) { + ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv); + } else if (matches(*argv, "delete") == 0) { + argc -=1; + argv +=1; + ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv); + } else if (matches(*argv, "get") == 0) { + argc -=1; + argv +=1; + ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv); + } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) { + if (argc <= 2) { + act_usage(); + return -1; + } + return tc_act_list_or_flush(argc-2, argv+2, RTM_GETACTION); + } else if (matches(*argv, "flush") == 0) { + if (argc <= 2) { + act_usage(); + return -1; + } + return tc_act_list_or_flush(argc-2, argv+2, RTM_DELACTION); + } else if (matches(*argv, "help") == 0) { + act_usage(); + return -1; + } else { + + ret = -1; + } + + if (ret < 0) { + fprintf(stderr, "Command \"%s\" is unknown, try \"tc action help\".\n", *argv); + return -1; + } + } + + return 0; +} +