diff options
Diffstat (limited to 'tc/tc_util.c')
-rw-r--r-- | tc/tc_util.c | 307 |
1 files changed, 253 insertions, 54 deletions
diff --git a/tc/tc_util.c b/tc/tc_util.c index 4764ecce..b39e5508 100644 --- a/tc/tc_util.c +++ b/tc/tc_util.c @@ -32,7 +32,7 @@ #define LIBDIR "/usr/lib" #endif -static struct db_names *cls_names = NULL; +static struct db_names *cls_names; #define NAMES_DB "/etc/iproute2/tc_cls" @@ -82,10 +82,10 @@ int get_qdisc_handle(__u32 *h, const char *str) if (strcmp(str, "none") == 0) goto ok; maj = strtoul(str, &p, 16); - if (p == str) + if (p == str || maj >= (1 << 16)) return -1; maj <<= 16; - if (*p != ':' && *p!=0) + if (*p != ':' && *p != 0) return -1; ok: *h = maj; @@ -192,7 +192,7 @@ static const struct rate_suffix { }; -int get_rate(unsigned *rate, const char *str) +int get_rate(unsigned int *rate, const char *str) { char *p; double bps = strtod(str, &p); @@ -266,13 +266,13 @@ void print_rate(char *buf, int len, __u64 rate) snprintf(buf, len, "%.0f%s%sbit", (double)rate, units[i], str); } -char * sprint_rate(__u64 rate, char *buf) +char *sprint_rate(__u64 rate, char *buf) { print_rate(buf, SPRINT_BSIZE-1, rate); return buf; } -int get_time(unsigned *time, const char *str) +int get_time(unsigned int *time, const char *str) { double t; char *p; @@ -282,13 +282,13 @@ int get_time(unsigned *time, const char *str) return -1; if (*p) { - if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 || - strcasecmp(p, "secs")==0) + if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec") == 0 || + strcasecmp(p, "secs") == 0) t *= TIME_UNITS_PER_SEC; - else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 || + else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec") == 0 || strcasecmp(p, "msecs") == 0) t *= TIME_UNITS_PER_SEC/1000; - else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 || + else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec") == 0 || strcasecmp(p, "usecs") == 0) t *= TIME_UNITS_PER_SEC/1000000; else @@ -312,18 +312,18 @@ void print_time(char *buf, int len, __u32 time) snprintf(buf, len, "%uus", time); } -char * sprint_time(__u32 time, char *buf) +char *sprint_time(__u32 time, char *buf) { print_time(buf, SPRINT_BSIZE-1, time); return buf; } -char * sprint_ticks(__u32 ticks, char *buf) +char *sprint_ticks(__u32 ticks, char *buf) { return sprint_time(tc_core_tick2time(ticks), buf); } -int get_size(unsigned *size, const char *str) +int get_size(unsigned int *size, const char *str) { double sz; char *p; @@ -333,13 +333,13 @@ int get_size(unsigned *size, const char *str) return -1; if (*p) { - if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k")==0) + if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k") == 0) sz *= 1024; - else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g")==0) + else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g") == 0) sz *= 1024*1024*1024; else if (strcasecmp(p, "gbit") == 0) sz *= 1024*1024*1024/8; - else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m")==0) + else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m") == 0) sz *= 1024*1024; else if (strcasecmp(p, "mbit") == 0) sz *= 1024*1024/8; @@ -353,9 +353,9 @@ int get_size(unsigned *size, const char *str) return 0; } -int get_size_and_cell(unsigned *size, int *cell_log, char *str) +int get_size_and_cell(unsigned int *size, int *cell_log, char *str) { - char * slash = strchr(str, '/'); + char *slash = strchr(str, '/'); if (slash) *slash = 0; @@ -371,7 +371,7 @@ int get_size_and_cell(unsigned *size, int *cell_log, char *str) return -1; *slash = '/'; - for (i=0; i<32; i++) { + for (i = 0; i < 32; i++) { if ((1<<i) == cell) { *cell_log = i; return 0; @@ -394,7 +394,7 @@ void print_size(char *buf, int len, __u32 sz) snprintf(buf, len, "%ub", sz); } -char * sprint_size(__u32 size, char *buf) +char *sprint_size(__u32 size, char *buf) { print_size(buf, SPRINT_BSIZE-1, size); return buf; @@ -405,62 +405,258 @@ void print_qdisc_handle(char *buf, int len, __u32 h) snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16); } -char * sprint_qdisc_handle(__u32 h, char *buf) +char *sprint_qdisc_handle(__u32 h, char *buf) { print_qdisc_handle(buf, SPRINT_BSIZE-1, h); return buf; } -char * action_n2a(int action, char *buf, int len) +static const char *action_n2a(int action) { + static char buf[64]; + + if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) + return "goto"; + if (TC_ACT_EXT_CMP(action, TC_ACT_JUMP)) + return "jump"; switch (action) { - case -1: + case TC_ACT_UNSPEC: return "continue"; - break; case TC_ACT_OK: return "pass"; - break; case TC_ACT_SHOT: return "drop"; - break; case TC_ACT_RECLASSIFY: return "reclassify"; case TC_ACT_PIPE: return "pipe"; case TC_ACT_STOLEN: return "stolen"; + case TC_ACT_TRAP: + return "trap"; default: - snprintf(buf, len, "%d", action); + snprintf(buf, 64, "%d", action); return buf; } } -int action_a2n(char *arg, int *result) +/* Convert action branch name into numeric format. + * + * Parameters: + * @arg - string to parse + * @result - pointer to output variable + * @allow_num - whether @arg may be in numeric format already + * + * In error case, returns -1 and does not touch @result. Otherwise returns 0. + */ +static int action_a2n(char *arg, int *result, bool allow_num) { - int res; + int n; + char dummy; + struct { + const char *a; + int n; + } a2n[] = { + {"continue", TC_ACT_UNSPEC}, + {"drop", TC_ACT_SHOT}, + {"shot", TC_ACT_SHOT}, + {"pass", TC_ACT_OK}, + {"ok", TC_ACT_OK}, + {"reclassify", TC_ACT_RECLASSIFY}, + {"pipe", TC_ACT_PIPE}, + {"goto", TC_ACT_GOTO_CHAIN}, + {"jump", TC_ACT_JUMP}, + {"trap", TC_ACT_TRAP}, + { NULL }, + }, *iter; + + for (iter = a2n; iter->a; iter++) { + if (matches(arg, iter->a) != 0) + continue; + *result = iter->n; + return 0; + } + if (!allow_num || sscanf(arg, "%d%c", &n, &dummy) != 1) + return -1; + + *result = n; + return 0; +} + +static int __parse_action_control(int *argc_p, char ***argv_p, int *result_p, + bool allow_num, bool ignore_a2n_miss) +{ + int argc = *argc_p; + char **argv = *argv_p; + int result; + + if (!argc) + return -1; + if (action_a2n(*argv, &result, allow_num) == -1) { + if (!ignore_a2n_miss) + fprintf(stderr, "Bad action type %s\n", *argv); + return -1; + } + if (result == TC_ACT_GOTO_CHAIN) { + __u32 chain_index; + + NEXT_ARG(); + if (matches(*argv, "chain") != 0) { + fprintf(stderr, "\"chain index\" expected\n"); + return -1; + } + NEXT_ARG(); + if (get_u32(&chain_index, *argv, 10) || + chain_index > TC_ACT_EXT_VAL_MASK) { + fprintf(stderr, "Illegal \"chain index\"\n"); + return -1; + } + result |= chain_index; + } + if (result == TC_ACT_JUMP) { + __u32 jump_cnt = 0; - if (matches(arg, "continue") == 0) - res = -1; - else if (matches(arg, "drop") == 0) - res = TC_ACT_SHOT; - else if (matches(arg, "shot") == 0) - res = TC_ACT_SHOT; - else if (matches(arg, "pass") == 0) - res = TC_ACT_OK; - else if (strcmp(arg, "ok") == 0) - res = TC_ACT_OK; - else if (matches(arg, "reclassify") == 0) - res = TC_ACT_RECLASSIFY; - else { - char dummy; - if (sscanf(arg, "%d%c", &res, &dummy) != 1) + NEXT_ARG(); + if (get_u32(&jump_cnt, *argv, 10) || + jump_cnt > TC_ACT_EXT_VAL_MASK) { + fprintf(stderr, "Invalid \"jump count\" (%s)\n", *argv); return -1; + } + result |= jump_cnt; + } + NEXT_ARG_FWD(); + *argc_p = argc; + *argv_p = argv; + *result_p = result; + return 0; +} + +/* Parse action control including possible options. + * + * Parameters: + * @argc_p - pointer to argc to parse + * @argv_p - pointer to argv to parse + * @result_p - pointer to output variable + * @allow_num - whether action may be in numeric format already + * + * In error case, returns -1 and does not touch @result_1p. Otherwise returns 0. + */ +int parse_action_control(int *argc_p, char ***argv_p, + int *result_p, bool allow_num) +{ + return __parse_action_control(argc_p, argv_p, result_p, + allow_num, false); +} + +/* Parse action control including possible options. + * + * Parameters: + * @argc_p - pointer to argc to parse + * @argv_p - pointer to argv to parse + * @result_p - pointer to output variable + * @allow_num - whether action may be in numeric format already + * @default_result - set as a result in case of parsing error + * + * In case there is an error during parsing, the default result is used. + */ +void parse_action_control_dflt(int *argc_p, char ***argv_p, + int *result_p, bool allow_num, + int default_result) +{ + if (__parse_action_control(argc_p, argv_p, result_p, allow_num, true)) + *result_p = default_result; +} + +static int parse_action_control_slash_spaces(int *argc_p, char ***argv_p, + int *result1_p, int *result2_p, + bool allow_num) +{ + int argc = *argc_p; + char **argv = *argv_p; + int result1, result2; + int *result_p = &result1; + int ok = 0; + int ret; + + while (argc > 0) { + switch (ok) { + case 1: + if (strcmp(*argv, "/") != 0) + goto out; + result_p = &result2; + NEXT_ARG(); + /* fall-through */ + case 0: /* fall-through */ + case 2: + ret = parse_action_control(&argc, &argv, + result_p, allow_num); + if (ret) + return ret; + ok++; + break; + default: + goto out; + } + } +out: + *result1_p = result1; + if (ok == 2) + *result2_p = result2; + *argc_p = argc; + *argv_p = argv; + return 0; +} + +/* Parse action control with slash including possible options. + * + * Parameters: + * @argc_p - pointer to argc to parse + * @argv_p - pointer to argv to parse + * @result1_p - pointer to the first (before slash) output variable + * @result2_p - pointer to the second (after slash) output variable + * @allow_num - whether action may be in numeric format already + * + * In error case, returns -1 and does not touch @result*. Otherwise returns 0. + */ +int parse_action_control_slash(int *argc_p, char ***argv_p, + int *result1_p, int *result2_p, bool allow_num) +{ + char **argv = *argv_p; + int result1, result2; + char *p = strchr(*argv, '/'); + + if (!p) + return parse_action_control_slash_spaces(argc_p, argv_p, + result1_p, result2_p, + allow_num); + *p = 0; + if (action_a2n(*argv, &result1, allow_num)) { + if (p) + *p = '/'; + return -1; } - *result = res; + + *p = '/'; + if (action_a2n(p + 1, &result2, allow_num)) + return -1; + + *result1_p = result1; + *result2_p = result2; return 0; } -int get_linklayer(unsigned *val, const char *arg) +void print_action_control(FILE *f, const char *prefix, + int action, const char *suffix) +{ + fprintf(f, "%s%s", prefix, action_n2a(action)); + if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) + fprintf(f, " chain %u", action & TC_ACT_EXT_VAL_MASK); + if (TC_ACT_EXT_CMP(action, TC_ACT_JUMP)) + fprintf(f, " %u", action & TC_ACT_EXT_VAL_MASK); + fprintf(f, "%s", suffix); +} + +int get_linklayer(unsigned int *val, const char *arg) { int res; @@ -477,7 +673,7 @@ int get_linklayer(unsigned *val, const char *arg) return 0; } -void print_linklayer(char *buf, int len, unsigned linklayer) +void print_linklayer(char *buf, int len, unsigned int linklayer) { switch (linklayer) { case LINKLAYER_UNSPEC: @@ -495,21 +691,22 @@ void print_linklayer(char *buf, int len, unsigned linklayer) } } -char *sprint_linklayer(unsigned linklayer, char *buf) +char *sprint_linklayer(unsigned int linklayer, char *buf) { print_linklayer(buf, SPRINT_BSIZE-1, linklayer); return buf; } -void print_tm(FILE * f, const struct tcf_t *tm) +void print_tm(FILE *f, const struct tcf_t *tm) { int hz = get_user_hz(); + if (tm->install != 0) - fprintf(f, " installed %u sec", (unsigned)(tm->install/hz)); + fprintf(f, " installed %u sec", (unsigned int)(tm->install/hz)); if (tm->lastuse != 0) - fprintf(f, " used %u sec", (unsigned)(tm->lastuse/hz)); + fprintf(f, " used %u sec", (unsigned int)(tm->lastuse/hz)); if (tm->expires != 0) - fprintf(f, " expires %u sec", (unsigned)(tm->expires/hz)); + fprintf(f, " expires %u sec", (unsigned int)(tm->expires/hz)); } void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats) @@ -521,6 +718,7 @@ void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtat if (tbs[TCA_STATS_BASIC]) { struct gnet_stats_basic bs = {0}; + memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs))); fprintf(fp, "%sSent %llu bytes %u pkt", prefix, (unsigned long long) bs.bytes, bs.packets); @@ -528,6 +726,7 @@ void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtat if (tbs[TCA_STATS_QUEUE]) { struct gnet_stats_queue q = {0}; + memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); fprintf(fp, " (dropped %u, overlimits %u requeues %u) ", q.drops, q.overlimits, q.requeues); @@ -552,6 +751,7 @@ void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtat if (tbs[TCA_STATS_QUEUE]) { struct gnet_stats_queue q = {0}; + memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); if (!tbs[TCA_STATS_RATE_EST]) fprintf(fp, "\n%s", prefix); @@ -575,10 +775,9 @@ void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtat } /* backward compatibility */ if (tb[TCA_STATS]) { - struct tc_stats st; + struct tc_stats st = {}; /* handle case where kernel returns more/less than we know about */ - memset(&st, 0, sizeof(st)); memcpy(&st, RTA_DATA(tb[TCA_STATS]), MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st))); fprintf(fp, "%sSent %llu bytes %u pkts (dropped %u, overlimits %u) ", |