diff --git a/bash-completion/tc b/bash-completion/tc index 79dd5fcc..ed2796db 100644 --- a/bash-completion/tc +++ b/bash-completion/tc @@ -430,6 +430,12 @@ _tc_action_options() _tc_once_attr 'index dev' return 0 ;; + sample) + _tc_once_attr 'rate' + _tc_once_attr 'trunc' + _tc_once_attr 'group' + return 0 + ;; gact) _tc_one_of_list 'reclassify drop continue pass' _tc_once_attr 'random' @@ -671,7 +677,7 @@ _tc() action) case $subcmd in add|change|replace) - local action acwd ACTION_KIND=' gact mirred bpf ' + local action acwd ACTION_KIND=' gact mirred bpf sample ' for ((acwd=$subcword; acwd < ${#words[@]}-1; acwd++)); do if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then action=${words[acwd]} diff --git a/include/linux/tc_act/tc_sample.h b/include/linux/tc_act/tc_sample.h new file mode 100644 index 00000000..edc9058b --- /dev/null +++ b/include/linux/tc_act/tc_sample.h @@ -0,0 +1,26 @@ +#ifndef __LINUX_TC_SAMPLE_H +#define __LINUX_TC_SAMPLE_H + +#include +#include +#include + +#define TCA_ACT_SAMPLE 26 + +struct tc_sample { + tc_gen; +}; + +enum { + TCA_SAMPLE_UNSPEC, + TCA_SAMPLE_TM, + TCA_SAMPLE_PARMS, + TCA_SAMPLE_RATE, + TCA_SAMPLE_TRUNC_SIZE, + TCA_SAMPLE_PSAMPLE_GROUP, + TCA_SAMPLE_PAD, + __TCA_SAMPLE_MAX +}; +#define TCA_SAMPLE_MAX (__TCA_SAMPLE_MAX - 1) + +#endif diff --git a/tc/Makefile b/tc/Makefile index 7fd0c4ad..6dd984f0 100644 --- a/tc/Makefile +++ b/tc/Makefile @@ -51,6 +51,7 @@ TCMODULES += m_vlan.o TCMODULES += m_connmark.o TCMODULES += m_bpf.o TCMODULES += m_tunnel_key.o +TCMODULES += m_sample.o TCMODULES += p_ip.o TCMODULES += p_icmp.o TCMODULES += p_tcp.o diff --git a/tc/m_sample.c b/tc/m_sample.c new file mode 100644 index 00000000..92911090 --- /dev/null +++ b/tc/m_sample.c @@ -0,0 +1,186 @@ +/* + * m_sample.c ingress/egress packet sampling module + * + * 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: Yotam Gigi + * + */ + +#include +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" +#include + +static void explain(void) +{ + fprintf(stderr, "Usage: sample SAMPLE_CONF\n"); + fprintf(stderr, "where:\n"); + fprintf(stderr, "\tSAMPLE_CONF := SAMPLE_PARAMS | SAMPLE_INDEX\n"); + fprintf(stderr, "\tSAMPLE_PARAMS := rate RATE group GROUP [trunc SIZE] [SAMPLE_INDEX]\n"); + fprintf(stderr, "\tSAMPLE_INDEX := index INDEX\n"); + fprintf(stderr, "\tRATE := The ratio of packets observed at the data source to the samples generated.\n"); + fprintf(stderr, "\tGROUP := the psample sampling group\n"); + fprintf(stderr, "\tSIZE := the truncation size\n"); + fprintf(stderr, "\tINDEX := integer index of the sample action\n"); +} + +static void usage(void) +{ + explain(); + exit(-1); +} + +static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p, + int tca_id, struct nlmsghdr *n) +{ + struct tc_sample p = { 0 }; + bool trunc_set = false; + bool group_set = false; + bool rate_set = false; + char **argv = *argv_p; + struct rtattr *tail; + int argc = *argc_p; + __u32 trunc; + __u32 group; + __u32 rate; + + if (argc <= 1) { + fprintf(stderr, "sample bad argument count %d\n", argc); + usage(); + return -1; + } + + if (matches(*argv, "sample") == 0) { + NEXT_ARG(); + } else { + fprintf(stderr, "sample bad argument %s\n", *argv); + return -1; + } + + while (argc > 0) { + if (matches(*argv, "rate") == 0) { + NEXT_ARG(); + if (get_unsigned(&rate, *argv, 10) != 0) { + fprintf(stderr, "Illegal rate %s\n", *argv); + usage(); + return -1; + } + rate_set = true; + } else if (matches(*argv, "group") == 0) { + NEXT_ARG(); + if (get_unsigned(&group, *argv, 10) != 0) { + fprintf(stderr, "Illegal group num %s\n", + *argv); + usage(); + return -1; + } + group_set = true; + } else if (matches(*argv, "trunc") == 0) { + NEXT_ARG(); + if (get_unsigned(&trunc, *argv, 10) != 0) { + fprintf(stderr, "Illegal truncation size %s\n", + *argv); + usage(); + return -1; + } + trunc_set = true; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + break; + } + + NEXT_ARG_FWD(); + } + + p.action = TC_ACT_PIPE; + if (argc && !action_a2n(*argv, &p.action, false)) + NEXT_ARG_FWD(); + + if (argc) { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "sample: Illegal \"index\"\n"); + return -1; + } + NEXT_ARG_FWD(); + } + } + + if (!p.index && !group_set) { + fprintf(stderr, "param \"group\" not set\n"); + usage(); + } + + if (!p.index && !rate_set) { + fprintf(stderr, "param \"rate\" not set\n"); + usage(); + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_SAMPLE_PARMS, &p, sizeof(p)); + if (rate_set) + addattr32(n, MAX_MSG, TCA_SAMPLE_RATE, rate); + if (group_set) + addattr32(n, MAX_MSG, TCA_SAMPLE_PSAMPLE_GROUP, group); + if (trunc_set) + addattr32(n, MAX_MSG, TCA_SAMPLE_TRUNC_SIZE, trunc); + + tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +static int print_sample(struct action_util *au, FILE *f, struct rtattr *arg) +{ + struct rtattr *tb[TCA_SAMPLE_MAX + 1]; + struct tc_sample *p; + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_SAMPLE_MAX, arg); + + if (!tb[TCA_SAMPLE_PARMS] || !tb[TCA_SAMPLE_RATE] || + !tb[TCA_SAMPLE_PSAMPLE_GROUP]) { + fprintf(f, "[NULL sample parameters]"); + return -1; + } + p = RTA_DATA(tb[TCA_SAMPLE_PARMS]); + + fprintf(f, "sample rate 1/%d group %d", + rta_getattr_u32(tb[TCA_SAMPLE_RATE]), + rta_getattr_u32(tb[TCA_SAMPLE_PSAMPLE_GROUP])); + + if (tb[TCA_SAMPLE_TRUNC_SIZE]) + fprintf(f, " trunc_size %d", + rta_getattr_u32(tb[TCA_SAMPLE_TRUNC_SIZE])); + + fprintf(f, "\n\tindex %d ref %d bind %d", p->index, p->refcnt, + p->bindcnt); + + if (show_stats) { + if (tb[TCA_SAMPLE_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_SAMPLE_TM]); + + print_tm(f, tm); + } + } + fprintf(f, "\n"); + return 0; +} + +struct action_util sample_action_util = { + .id = "sample", + .parse_aopt = parse_sample, + .print_aopt = print_sample, +};