From 6c55c8c4617c5853b00cee3eefe94654d9a25c41 Mon Sep 17 00:00:00 2001 From: Vlad Zolotarov Date: Thu, 30 Apr 2015 13:46:43 +0300 Subject: [PATCH 01/27] ip link set vf: Added "query_rss" command Add a new option to toggle the ability of querying the RSS configuration of a specific VF. VF RSS information like RSS hash key may be considered sensitive on some devices where this information is shared between VF and PF and thus its querying may be prohibited by default. This new option allows a system administrator with privileges to modify a PF state to control if the above VF querying is allowed or not. For example: To enable RSS querying of VF[0] of ethX: >> ip link set dev ethX vf 0 query_rss on Signed-off-by: Vlad Zolotarov --- ip/iplink.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ip/iplink.c b/ip/iplink.c index 023f53b7..bb437b96 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -80,6 +80,7 @@ void iplink_usage(void) fprintf(stderr, " [ rate TXRATE ] ] \n"); fprintf(stderr, " [ spoofchk { on | off} ] ] \n"); + fprintf(stderr, " [ query_rss { on | off} ] ] \n"); fprintf(stderr, " [ state { auto | enable | disable} ] ]\n"); fprintf(stderr, " [ master DEVICE ]\n"); fprintf(stderr, " [ nomaster ]\n"); @@ -331,6 +332,18 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp, ivs.vf = vf; addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs)); + } else if (matches(*argv, "query_rss") == 0) { + struct ifla_vf_rss_query_en ivs; + NEXT_ARG(); + if (matches(*argv, "on") == 0) + ivs.setting = 1; + else if (matches(*argv, "off") == 0) + ivs.setting = 0; + else + invarg("Invalid \"query_rss\" value\n", *argv); + ivs.vf = vf; + addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(ivs)); + } else if (matches(*argv, "state") == 0) { struct ifla_vf_link_state ivl; NEXT_ARG(); From ce743da17109f113adcfc672d905f38d6a89296b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 6 May 2015 09:47:22 -0700 Subject: [PATCH 02/27] ip: document exit code The ip command has always had a consistent exit status document it so that developers see it. --- man/man8/ip.8 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/man/man8/ip.8 b/man/man8/ip.8 index 43ddac91..045414dd 100644 --- a/man/man8/ip.8 +++ b/man/man8/ip.8 @@ -245,7 +245,7 @@ Use color output. .PP The names of all objects may be written in full or -abbreviated form, for exampe +abbreviated form, for example .B address can be abbreviated as .B addr @@ -275,6 +275,10 @@ Usually it is or, if the objects of this class cannot be listed, .BR "help" . +.SH EXIT STATUS +Exit status is 0 if command was successful, and 1 if there is a syntax error. +If an error was reported by the kernel exit status is 2. + .SH HISTORY .B ip was written by Alexey N. Kuznetsov and added in Linux 2.2. From d58ba4ba2a532f3ebfa640c42ec2622b54740cdf Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 6 May 2015 09:48:06 -0700 Subject: [PATCH 03/27] ip: return correct exit code on route failure If kernel complains about ip route request, exit status should be 2 not 1. This fixes regression introduced by: commit 42ecedd4bae534fc688194a795eb4548c6530cda Author: Roopa Prabhu Date: Tue Mar 17 19:26:32 2015 -0700 fix ip -force -batch to continue on errors --- ip/iproute.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ip/iproute.c b/ip/iproute.c index e0a61598..06aea6ff 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -1164,7 +1164,7 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) req.r.rtm_family = AF_INET; if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) - return -1; + return -2; return 0; } From 076ae7089a4b4874a1aedd4c5f84bb889dcda34e Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 6 May 2015 09:53:41 -0700 Subject: [PATCH 04/27] ip: fix exit code for rule failures If ip rule command fails talking to kernel, exit code should be 2. The sub-command is called by cmd loop and the exit code is negative of return value from the command callback. --- ip/iprule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ip/iprule.c b/ip/iprule.c index 967969c0..986a5bcb 100644 --- a/ip/iprule.c +++ b/ip/iprule.c @@ -381,7 +381,7 @@ static int iprule_modify(int cmd, int argc, char **argv) req.r.rtm_table = RT_TABLE_MAIN; if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) - return 2; + return -2; return 0; } From 906cafe3ff7a3e285e59f95e7b58c91610d5d16f Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 6 May 2015 09:55:07 -0700 Subject: [PATCH 05/27] ip: fix exit code for addrlabel The exit code for ip label was not correct. The return from the command function is negated and turned into the exit code on failure. --- ip/ipaddrlabel.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ip/ipaddrlabel.c b/ip/ipaddrlabel.c index f6a638b5..19a93083 100644 --- a/ip/ipaddrlabel.c +++ b/ip/ipaddrlabel.c @@ -183,7 +183,7 @@ static int ipaddrlabel_modify(int cmd, int argc, char **argv) req.ifal.ifal_family = AF_INET6; if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) - return 2; + return -2; return 0; } @@ -232,12 +232,12 @@ static int ipaddrlabel_flush(int argc, char **argv) if (rtnl_wilddump_request(&rth, af, RTM_GETADDRLABEL) < 0) { perror("Cannot send dump request"); - return 1; + return -1; } if (rtnl_dump_filter(&rth, flush_addrlabel, NULL) < 0) { fprintf(stderr, "Flush terminated\n"); - return 1; + return -1; } return 0; From f7dd7e5e713d5820da2ad5cbd3fa88286e20d4f1 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 5 May 2015 02:14:16 +0200 Subject: [PATCH 06/27] iproute2: Fix typo in get_prefix_1() Fixes a typo in get_prefix_1() which broke the prefix default names { default | any | all }. The most obvious fallout from this bug was: $ ip route add default via 1.1.1.1 Error: an inet prefix is expected rather than "default". Fixes: dacc5d4197c1 ("add basic mpls support to iproute") Signed-off-by: Thomas Graf --- lib/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.c b/lib/utils.c index 428ad8f9..29b4f548 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -496,7 +496,7 @@ int get_prefix_1(inet_prefix *dst, char *arg, int family) if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0 || strcmp(arg, "all") == 0) { - if ((family == AF_DECnet) || (family = AF_MPLS)) + if ((family == AF_DECnet) || (family == AF_MPLS)) return -1; dst->family = family; dst->bytelen = 0; From 285e7768e865a9061ee6a6a92e96a0ce041f5789 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 5 May 2015 15:30:20 -0700 Subject: [PATCH 07/27] tc: fill in handle before checking argc When deleting a specific basic filter with handle, tc command always ignores the 'handle' option, so tcm_handle is always 0 and kernel deletes all filters in the selected group. This is wrong, we should respect 'handle' in cmdline. Cc: Jamal Hadi Salim Cc: Stephen Hemminger Signed-off-by: Cong Wang --- tc/f_basic.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tc/f_basic.c b/tc/f_basic.c index 1c33ca3d..4adf1d22 100644 --- a/tc/f_basic.c +++ b/tc/f_basic.c @@ -43,9 +43,6 @@ static int basic_parse_opt(struct filter_util *qu, char *handle, struct rtattr *tail; long h = 0; - if (argc == 0) - return 0; - if (handle) { h = strtol(handle, NULL, 0); if (h == LONG_MIN || h == LONG_MAX) { @@ -54,9 +51,11 @@ static int basic_parse_opt(struct filter_util *qu, char *handle, return -1; } } - t->tcm_handle = h; + if (argc == 0) + return 0; + tail = (struct rtattr*)(((void*)n)+NLMSG_ALIGN(n->nlmsg_len)); addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); From 38a7f268287b57d80950b2036a7877761916752b Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 6 May 2015 17:25:39 +0200 Subject: [PATCH 08/27] route: Add missing newline in helptext Signed-off-by: Thomas Graf --- ip/iproute.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ip/iproute.c b/ip/iproute.c index 06aea6ff..670a4c64 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -77,7 +77,7 @@ static void usage(void) fprintf(stderr, " [ scope SCOPE ] [ metric METRIC ]\n"); fprintf(stderr, "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n"); fprintf(stderr, "NH := [ via [ FAMILY ] ADDRESS ] [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"); - fprintf(stderr, "FAMILY := [ inet | inet6 | ipx | dnet | mpls | bridge | link ]"); + fprintf(stderr, "FAMILY := [ inet | inet6 | ipx | dnet | mpls | bridge | link ]\n"); fprintf(stderr, "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ] [ as [ to ] ADDRESS ]\n"); fprintf(stderr, " [ rtt TIME ] [ rttvar TIME ] [ reordering NUMBER ]\n"); fprintf(stderr, " [ window NUMBER] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n"); From 656e8fdd2dc00c79602f5bb8567e7771d21fb692 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 6 May 2015 11:33:23 -0700 Subject: [PATCH 09/27] ss: small optim in tcp_show_info() Kernel can give us smaller tcp_info than our. We copy the kernel provided structure and fill with 0 the remaining part. Lets clear only the missing part to save some cycles, as we intend to slightly increase tcp_info size in the future. Signed-off-by: Eric Dumazet --- misc/ss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/ss.c b/misc/ss.c index 46dbb39a..68961fcc 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -1893,8 +1893,8 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, /* workaround for older kernels with less fields */ if (len < sizeof(*info)) { info = alloca(sizeof(*info)); - memset(info, 0, sizeof(*info)); memcpy(info, RTA_DATA(tb[INET_DIAG_INFO]), len); + memset((char *)info + len, 0, sizeof(*info) - len); } else info = RTA_DATA(tb[INET_DIAG_INFO]); From 3bf5445c5ee583309e841051dc6a9489f9f331f4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 8 May 2015 13:28:40 -0700 Subject: [PATCH 10/27] ss: dctcp changes Missing space before dctcp: markers. With dctcp, cwnd=2 is pretty common, just display cwnd value even if cwnd has this value, it makes parsing easier. Signed-off-by: Eric Dumazet Acked-by: Daniel Borkmann --- misc/ss.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc/ss.c b/misc/ss.c index 68961fcc..870cad18 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -1684,7 +1684,7 @@ static void tcp_stats_print(struct tcpstat *s) if (s->mss) printf(" mss:%d", s->mss); - if (s->cwnd && s->cwnd != 2) + if (s->cwnd) printf(" cwnd:%d", s->cwnd); if (s->ssthresh) printf(" ssthresh:%d", s->ssthresh); @@ -1692,11 +1692,11 @@ static void tcp_stats_print(struct tcpstat *s) if (s->dctcp && s->dctcp->enabled) { struct dctcpstat *dctcp = s->dctcp; - printf("dctcp:(ce_state:%u,alpha:%u,ab_ecn:%u,ab_tot:%u)", + printf(" dctcp:(ce_state:%u,alpha:%u,ab_ecn:%u,ab_tot:%u)", dctcp->ce_state, dctcp->alpha, dctcp->ab_ecn, dctcp->ab_tot); } else if (s->dctcp) { - printf("dctcp:fallback_mode"); + printf(" dctcp:fallback_mode"); } if (s->send_bps) From ec6f5abcea541da9ef5eec9491d833e6d3950f69 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 9 May 2015 22:59:17 +0200 Subject: [PATCH 11/27] tc: minor cleanup on ingress Fix whitespacing and remove the unnecessary condition. Signed-off-by: Daniel Borkmann --- tc/q_ingress.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/tc/q_ingress.c b/tc/q_ingress.c index ba58e722..30b24e7d 100644 --- a/tc/q_ingress.c +++ b/tc/q_ingress.c @@ -34,18 +34,14 @@ static void explain(void) static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { - - if (argc > 0) { - while (argc > 0) { - - if (strcmp(*argv, "handle") == 0) { - NEXT_ARG(); - argc--; argv++; - } else { - fprintf(stderr, "What is \"%s\"?\n", *argv); - explain(); - return -1; - } + while (argc > 0) { + if (strcmp(*argv, "handle") == 0) { + NEXT_ARG(); + argc--; argv++; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; } } @@ -55,8 +51,7 @@ static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, struc static int ingress_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { - - fprintf(f, "---------------- "); + fprintf(f, "---------------- "); return 0; } From 2c0feda8be9d6250697a2da39f0288310c230602 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 11 May 2015 10:17:14 +0300 Subject: [PATCH 12/27] man ip-link: Add missing lowpan link type Signed-off-by: Lennert Buytenhek --- man/man8/ip-link.8.in | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index 0dfcb2ea..823246f3 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -75,7 +75,8 @@ ip-link \- network device configuration .BR ip6gretap " |" .BR vti " |" .BR nlmon " |" -.BR ipvlan " ]" +.BR ipvlan " |" +.BR lowpan " ]" .ti -8 .BR "ip link delete " { @@ -243,6 +244,9 @@ Link types: .sp .BR ipvlan - Interface for L3 (IPv6/IPv4) based VLANs +.sp +.BR lowpan +- Interface for 6LoWPAN (IPv6) over IEEE 802.15.4 / Bluetooth .in -8 .TP @@ -915,6 +919,12 @@ encap-dport 5555 encap-csum encap-remcsum Creates an IPIP that is encapsulated with Generic UDP Encapsulation, and the outer UDP checksum and remote checksum offload are enabled. +.RE +.PP +ip link add link wpan0 lowpan0 type lowpan +.RS 4 +Creates a 6LoWPAN interface named lowpan0 on the underlying +IEEE 802.15.4 device wpan0. .RE .SH SEE ALSO From 699589f6df02a6173e09202887a692967bb4e7d1 Mon Sep 17 00:00:00 2001 From: Vadim Kochan Date: Wed, 13 May 2015 18:03:51 +0300 Subject: [PATCH 13/27] man ip-link: Remove extra GROUP explanation Remove double explanation of GROUP option from 'ip link set' section. Signed-off-by: Vadim Kochan --- man/man8/ip-link.8.in | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index 823246f3..714aab48 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -713,12 +713,6 @@ tool can be used. But it allows to change network namespace only for physical de .BI alias " NAME" give the device a symbolic name for easy reference. -.TP -.BI group " GROUP" -specify the group the device belongs to. -The available groups are listed in file -.BR "@SYSCONFDIR@/group" . - .TP .BI vf " NUM" specify a Virtual Function device to be configured. The associated PF device @@ -867,7 +861,7 @@ specifies which help of link type to dislpay. .SS .I GROUP may be a number or a string from the file -.B /etc/iproute2/group +.B @SYSCONFDIR@/group which can be manually filled. .SH "EXAMPLES" From 9d9a67c7567fb7f650095db5d424023ee2c3da5c Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 18 May 2015 11:35:05 -0400 Subject: [PATCH 14/27] tc: red, gred: Rename overloaded variable wlog It is used when parsing three different parameters, only one of which is Wlog. Change the name to make the code less confusing. Signed-off-by: David Ward --- tc/q_gred.c | 17 ++++++++--------- tc/q_red.c | 16 ++++++++-------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index 88bd0947..625bcf96 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -123,7 +123,7 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n unsigned avpkt = 0; double probability = 0.02; unsigned rate = 0; - int wlog; + int parm; __u8 sbuf[256]; struct rtattr *tail; __u32 max_P; @@ -227,27 +227,26 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt); fprintf(stderr, "GRED: set burst to %u\n", burst); } - - if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { + if ((parm = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { fprintf(stderr, "GRED: failed to calculate EWMA constant.\n"); return -1; } - if (wlog >= 10) + if (parm >= 10) fprintf(stderr, "GRED: WARNING. Burst %d seems to be too " "large.\n", burst); - opt.Wlog = wlog; - if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { + opt.Wlog = parm; + if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { fprintf(stderr, "GRED: failed to calculate probability.\n"); return -1; } - opt.Plog = wlog; - if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) + opt.Plog = parm; + if ((parm = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) { fprintf(stderr, "GRED: failed to calculate idle damping " "table.\n"); return -1; } - opt.Scell_log = wlog; + opt.Scell_log = parm; tail = NLMSG_TAIL(n); addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); diff --git a/tc/q_red.c b/tc/q_red.c index 89e73201..5a74c500 100644 --- a/tc/q_red.c +++ b/tc/q_red.c @@ -40,7 +40,7 @@ static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl unsigned avpkt = 0; double probability = 0.02; unsigned rate = 0; - int wlog; + int parm; __u8 sbuf[256]; __u32 max_P; struct rtattr *tail; @@ -126,23 +126,23 @@ static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl opt.qth_min = opt.qth_max / 3; if (!burst) burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt); - if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { + if ((parm = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { fprintf(stderr, "RED: failed to calculate EWMA constant.\n"); return -1; } - if (wlog >= 10) + if (parm >= 10) fprintf(stderr, "RED: WARNING. Burst %d seems to be too large.\n", burst); - opt.Wlog = wlog; - if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { + opt.Wlog = parm; + if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { fprintf(stderr, "RED: failed to calculate probability.\n"); return -1; } - opt.Plog = wlog; - if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) { + opt.Plog = parm; + if ((parm = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) { fprintf(stderr, "RED: failed to calculate idle damping table.\n"); return -1; } - opt.Scell_log = wlog; + opt.Scell_log = parm; tail = NLMSG_TAIL(n); addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); From 6c99695da283677b04675bd8a33a12fdb89b1048 Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 18 May 2015 11:35:06 -0400 Subject: [PATCH 15/27] tc: red, gred: Fix format specifier in burst size warning burst is an unsigned value. Signed-off-by: David Ward --- tc/q_gred.c | 2 +- tc/q_red.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index 625bcf96..d876f83f 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -232,7 +232,7 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n return -1; } if (parm >= 10) - fprintf(stderr, "GRED: WARNING. Burst %d seems to be too " + fprintf(stderr, "GRED: WARNING. Burst %u seems to be too " "large.\n", burst); opt.Wlog = parm; if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { diff --git a/tc/q_red.c b/tc/q_red.c index 5a74c500..6555eb2a 100644 --- a/tc/q_red.c +++ b/tc/q_red.c @@ -131,7 +131,7 @@ static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl return -1; } if (parm >= 10) - fprintf(stderr, "RED: WARNING. Burst %d seems to be too large.\n", burst); + fprintf(stderr, "RED: WARNING. Burst %u seems to be too large.\n", burst); opt.Wlog = parm; if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { fprintf(stderr, "RED: failed to calculate probability.\n"); From d93c909a4cf1ef5d3833044e7b9e3b7a7230881a Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 18 May 2015 11:35:07 -0400 Subject: [PATCH 16/27] tc: red, gred: Notify when using the default value for "bandwidth" The "bandwidth" parameter is optional, but ensure the user is aware of its default value, to proactively avoid configuration problems. Signed-off-by: David Ward --- tc/q_gred.c | 7 ++++--- tc/q_red.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index d876f83f..5e0dfcfc 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -214,9 +214,6 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n argc--; argv++; } - if (rate == 0) - get_rate(&rate, "10Mbit"); - if (!opt.qth_min || !opt.qth_max || !opt.limit || !avpkt || (opt.DP<0)) { fprintf(stderr, "Required parameter (min, max, limit, " @@ -227,6 +224,10 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt); fprintf(stderr, "GRED: set burst to %u\n", burst); } + if (!rate) { + get_rate(&rate, "10Mbit"); + fprintf(stderr, "GRED: set bandwidth to 10Mbit\n"); + } if ((parm = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { fprintf(stderr, "GRED: failed to calculate EWMA constant.\n"); return -1; diff --git a/tc/q_red.c b/tc/q_red.c index 6555eb2a..1bf2bd9d 100644 --- a/tc/q_red.c +++ b/tc/q_red.c @@ -109,9 +109,6 @@ static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl argc--; argv++; } - if (rate == 0) - get_rate(&rate, "10Mbit"); - if (!opt.limit || !avpkt) { fprintf(stderr, "RED: Required parameter (limit, avpkt) is missing\n"); return -1; @@ -126,6 +123,10 @@ static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl opt.qth_min = opt.qth_max / 3; if (!burst) burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt); + if (!rate) { + get_rate(&rate, "10Mbit"); + fprintf(stderr, "RED: set bandwidth to 10Mbit\n"); + } if ((parm = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { fprintf(stderr, "RED: failed to calculate EWMA constant.\n"); return -1; From 7bf17a2264c2f636c1b99c4eb7f94ee9ae896aae Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 18 May 2015 11:35:08 -0400 Subject: [PATCH 17/27] tc: red: Mark "bandwidth" parameter as optional in usage text Signed-off-by: David Ward --- tc/q_red.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tc/q_red.c b/tc/q_red.c index 1bf2bd9d..abd86c7b 100644 --- a/tc/q_red.c +++ b/tc/q_red.c @@ -29,7 +29,7 @@ static void explain(void) { fprintf(stderr, "Usage: ... red limit BYTES [min BYTES] [max BYTES] avpkt BYTES [burst PACKETS]\n"); - fprintf(stderr, " [adaptive] [probability PROBABILITY] bandwidth KBPS\n"); + fprintf(stderr, " [adaptive] [probability PROBABILITY] [bandwidth KBPS]\n"); fprintf(stderr, " [ecn] [harddrop]\n"); } From d73e0408e2d1c02d94512c035d568b1530854e23 Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 18 May 2015 11:35:09 -0400 Subject: [PATCH 18/27] tc: gred: Fix whitespace issues in code Signed-off-by: David Ward --- tc/q_gred.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index 5e0dfcfc..a3dc722b 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -143,8 +143,7 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n fprintf(stderr, "Illegal \"setup\"\n"); return -1; } - return init_gred(qu,argc-1, argv+1,n); - + return init_gred(qu, argc-1, argv+1, n); } else if (strcmp(*argv, "min") == 0) { NEXT_ARG(); if (get_size(&opt.qth_min, *argv)) { @@ -172,7 +171,7 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n ok++; } else if (strcmp(*argv, "burst") == 0) { NEXT_ARG(); - if (get_unsigned(&burst, *argv, 0)) { + if (get_unsigned(&burst, *argv, 0)) { fprintf(stderr, "Illegal \"burst\"\n"); return -1; } From a77905ef6aeaf85b26c51a62e409a7ccc5128410 Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 18 May 2015 11:35:10 -0400 Subject: [PATCH 19/27] tc: gred: Print usage text if no arguments appear after "gred" This is more helpful to the user, since the command takes two forms, and the message that would otherwise appear about missing parameters assumes one of those forms. Signed-off-by: David Ward --- tc/q_gred.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tc/q_gred.c b/tc/q_gred.c index a3dc722b..65caeee6 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -213,6 +213,10 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n argc--; argv++; } + if (!ok) { + explain(); + return -1; + } if (!opt.qth_min || !opt.qth_max || !opt.limit || !avpkt || (opt.DP<0)) { fprintf(stderr, "Required parameter (min, max, limit, " From 1693a4d392779adf004b99b0864f9cf0cd4a8a4f Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 18 May 2015 11:35:11 -0400 Subject: [PATCH 20/27] tc: gred: Improve parameter/statistics output Make the output more consistent with the RED qdisc, and only show details/statistics if the appropriate flag is set when calling tc. Show the parameters used with "gred setup". Add missing statistics "pdrop" and "other". Fix format specifiers for unsigned values. Signed-off-by: David Ward --- tc/q_gred.c | 63 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index 65caeee6..d3e5ec7c 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -265,14 +265,13 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { struct rtattr *tb[TCA_GRED_MAX + 1]; + struct tc_gred_sopt *sopt; struct tc_gred_qopt *qopt; __u32 *max_p = NULL; - int i; + unsigned i; SPRINT_BUF(b1); SPRINT_BUF(b2); SPRINT_BUF(b3); - SPRINT_BUF(b4); - SPRINT_BUF(b5); if (opt == NULL) return 0; @@ -286,40 +285,50 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) RTA_PAYLOAD(tb[TCA_GRED_MAX_P]) >= sizeof(__u32) * MAX_DPs) max_p = RTA_DATA(tb[TCA_GRED_MAX_P]); + sopt = RTA_DATA(tb[TCA_GRED_DPS]); qopt = RTA_DATA(tb[TCA_GRED_PARMS]); - if (RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) { + if (RTA_PAYLOAD(tb[TCA_GRED_DPS]) < sizeof(*sopt) || + RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) { fprintf(f,"\n GRED received message smaller than expected\n"); return -1; - } + } /* Bad hack! should really return a proper message as shown above*/ + fprintf(f, "DPs %u default %u %s", + sopt->DPs, + sopt->def_DP, + sopt->grio ? "grio " : ""); + for (i=0;iDP >= MAX_DPs) continue; - fprintf(f, "\n DP:%d (prio %d) Average Queue %s Measured " - "Queue %s ", + fprintf(f, "\n DP %u prio %hhu limit %s min %s max %s ", qopt->DP, qopt->prio, - sprint_size(qopt->qave, b4), - sprint_size(qopt->backlog, b5)); - fprintf(f, "\n\t Packet drops: %d (forced %d early %d) ", - qopt->forced+qopt->early, - qopt->forced, - qopt->early); - fprintf(f, "\n\t Packet totals: %u (bytes %u) ", - qopt->packets, - qopt->bytesin); - if (show_details) - fprintf(f, "\n limit %s min %s max %s ", - sprint_size(qopt->limit, b1), - sprint_size(qopt->qth_min, b2), - sprint_size(qopt->qth_max, b3)); - fprintf(f, "ewma %u ", qopt->Wlog); - if (max_p) - fprintf(f, "probability %lg ", max_p[i] / pow(2, 32)); - else - fprintf(f, "Plog %u ", qopt->Plog); - fprintf(f, "Scell_log %u", qopt->Scell_log); + sprint_size(qopt->limit, b1), + sprint_size(qopt->qth_min, b2), + sprint_size(qopt->qth_max, b3)); + if (show_details) { + fprintf(f, "ewma %u ", qopt->Wlog); + if (max_p) + fprintf(f, "probability %lg ", max_p[i] / pow(2, 32)); + else + fprintf(f, "Plog %u ", qopt->Plog); + fprintf(f, "Scell_log %u ", qopt->Scell_log); + } + if (show_stats) { + fprintf(f, "\n Queue size: average %s current %s ", + sprint_size(qopt->qave, b1), + sprint_size(qopt->backlog, b2)); + fprintf(f, "\n Dropped packets: forced %u early %u pdrop %u other %u ", + qopt->forced, + qopt->early, + qopt->pdrop, + qopt->other); + fprintf(f, "\n Total packets: %u (%s) ", + qopt->packets, + sprint_size(qopt->bytesin, b1)); + } } return 0; } From eb6d7d6af1c2d65c4e905807fc44aba682e71b4c Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 18 May 2015 11:35:12 -0400 Subject: [PATCH 21/27] tc: gred: Handle unsigned values properly in option parsing/printing DPs, def_DP, and DP are unsigned values that are sent and received in TCA_GRED_* netlink attributes; handle them properly when they are parsed or printed. Use MAX_DPs as the initial value for def_DP and DP, and fix the operator used for bounds checking them. Signed-off-by: David Ward --- tc/q_gred.c | 65 +++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index d3e5ec7c..0192d9fd 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -53,34 +53,34 @@ static int init_gred(struct qdisc_util *qu, int argc, char **argv, struct rtattr *tail; struct tc_gred_sopt opt = { 0 }; - int dps = 0; - int def_dp = -1; + + opt.def_DP = MAX_DPs; while (argc > 0) { DPRINTF(stderr,"init_gred: invoked with %s\n",*argv); if (strcmp(*argv, "DPs") == 0) { NEXT_ARG(); - DPRINTF(stderr,"init_gred: next_arg with %s\n",*argv); - dps = strtol(*argv, (char **)NULL, 10); - if (dps < 0 || dps >MAX_DPs) { - fprintf(stderr, "DPs =%d\n", dps); + if (get_unsigned(&opt.DPs, *argv, 10)) { fprintf(stderr, "Illegal \"DPs\"\n"); - fprintf(stderr, "GRED: only %d DPs are " - "currently supported\n",MAX_DPs); + return -1; + } else if (opt.DPs > MAX_DPs) { + fprintf(stderr, "GRED: only %u DPs are " + "currently supported\n", MAX_DPs); return -1; } } else if (strcmp(*argv, "default") == 0) { - NEXT_ARG(); - def_dp = strtol(*argv, (char **)NULL, 10); - if (dps == 0) { - fprintf(stderr, "\"default DP\" must be " - "defined after DPs\n"); + if (opt.DPs == 0) { + fprintf(stderr, "\"default\" must be defined " + "after \"DPs\"\n"); return -1; } - if (def_dp < 0 || def_dp > dps) { - fprintf(stderr, - "\"default DP\" must be less than %d\n", - opt.DPs); + NEXT_ARG(); + if (get_unsigned(&opt.def_DP, *argv, 10)) { + fprintf(stderr, "Illegal \"default\"\n"); + return -1; + } else if (opt.def_DP >= opt.DPs) { + fprintf(stderr, "\"default\" must be less than " + "\"DPs\"\n"); return -1; } } else if (strcmp(*argv, "grio") == 0) { @@ -96,15 +96,12 @@ static int init_gred(struct qdisc_util *qu, int argc, char **argv, argc--; argv++; } - if (!dps || def_dp == -1) { + if (!opt.DPs || opt.def_DP == MAX_DPs) { fprintf(stderr, "Illegal gred setup parameters \n"); return -1; } - opt.DPs = dps; - opt.def_DP = def_dp; - - DPRINTF("TC_GRED: sending DPs=%d default=%d\n",opt.DPs,opt.def_DP); + DPRINTF("TC_GRED: sending DPs=%u def_DP=%u\n",opt.DPs,opt.def_DP); n->nlmsg_flags|=NLM_F_CREATE; tail = NLMSG_TAIL(n); addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); @@ -118,7 +115,7 @@ static int init_gred(struct qdisc_util *qu, int argc, char **argv, static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) { int ok=0; - struct tc_gred_qopt opt; + struct tc_gred_qopt opt = { 0 }; unsigned burst = 0; unsigned avpkt = 0; double probability = 0.02; @@ -128,7 +125,7 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n struct rtattr *tail; __u32 max_P; - memset(&opt, 0, sizeof(opt)); + opt.DP = MAX_DPs; while (argc > 0) { if (strcmp(*argv, "limit") == 0) { @@ -160,14 +157,14 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n ok++; } else if (strcmp(*argv, "DP") == 0) { NEXT_ARG(); - opt.DP=strtol(*argv, (char **)NULL, 10); - DPRINTF ("\n ******* DP =%u\n",opt.DP); - if (opt.DP >MAX_DPs) { /* need a better error check */ - fprintf(stderr, "DP =%u \n",opt.DP); + if (get_unsigned(&opt.DP, *argv, 10)) { fprintf(stderr, "Illegal \"DP\"\n"); - fprintf(stderr, "GRED: only %d DPs are currently supported\n",MAX_DPs); return -1; - } + } else if (opt.DP >= MAX_DPs) { + fprintf(stderr, "GRED: only %u DPs are " + "currently supported\n", MAX_DPs); + return -1; + } /* need a better error check */ ok++; } else if (strcmp(*argv, "burst") == 0) { NEXT_ARG(); @@ -217,10 +214,10 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n explain(); return -1; } - if (!opt.qth_min || !opt.qth_max || !opt.limit || !avpkt || - (opt.DP<0)) { - fprintf(stderr, "Required parameter (min, max, limit, " - "avpkt, DP) is missing\n"); + if (opt.DP == MAX_DPs || !opt.limit || !opt.qth_min || !opt.qth_max || + !avpkt) { + fprintf(stderr, "Required parameter (DP, limit, min, max, " + "avpkt) is missing\n"); return -1; } if (!burst) { From 357c45ad3a3fba94adc3591c150506af95fba50e Mon Sep 17 00:00:00 2001 From: David Ward Date: Mon, 18 May 2015 11:35:13 -0400 Subject: [PATCH 22/27] tc: gred: Adopt the term VQ in the command syntax and output In the GRED kernel source code, both of the terms "drop parameters" (DP) and "virtual queue" (VQ) are used to refer to the same thing. Each "DP" is better understood as a "set of drop parameters", since it has values for limit, min, max, avpkt, etc. This terminology can result in confusion when creating a GRED qdisc having multiple DPs. Netlink attributes and struct members with the DP name seem to have been left intact for compatibility, while the term VQ was otherwise adopted in the code, which is more intuitive. Use the VQ term in the tc command syntax and output (but maintain compatibility with the old syntax). Rewrite the usage text to be concise and similar to other qdiscs. Signed-off-by: David Ward --- tc/q_gred.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/tc/q_gred.c b/tc/q_gred.c index 0192d9fd..463d725f 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -37,14 +37,11 @@ static void explain(void) { - fprintf(stderr, "Usage: ... gred DP drop-probability limit BYTES " - "min BYTES max BYTES\n"); - fprintf(stderr, " avpkt BYTES burst PACKETS probability PROBABILITY " - "bandwidth KBPS\n"); - fprintf(stderr, " [prio value]\n"); - fprintf(stderr," OR ...\n"); - fprintf(stderr," gred setup DPs default " - "[grio]\n"); + fprintf(stderr, "Usage: tc qdisc { add | replace | change } ... gred setup vqs NUMBER\n"); + fprintf(stderr, " default DEFAULT_VQ [ grio ]\n"); + fprintf(stderr, " tc qdisc change ... gred vq VQ [ prio VALUE ] limit BYTES\n"); + fprintf(stderr, " min BYTES max BYTES avpkt BYTES [ burst PACKETS ]\n"); + fprintf(stderr, " [ probability PROBABILITY ] [ bandwidth KBPS ]\n"); } static int init_gred(struct qdisc_util *qu, int argc, char **argv, @@ -58,20 +55,21 @@ static int init_gred(struct qdisc_util *qu, int argc, char **argv, while (argc > 0) { DPRINTF(stderr,"init_gred: invoked with %s\n",*argv); - if (strcmp(*argv, "DPs") == 0) { + if (strcmp(*argv, "vqs") == 0 || + strcmp(*argv, "DPs") == 0) { NEXT_ARG(); if (get_unsigned(&opt.DPs, *argv, 10)) { - fprintf(stderr, "Illegal \"DPs\"\n"); + fprintf(stderr, "Illegal \"vqs\"\n"); return -1; } else if (opt.DPs > MAX_DPs) { - fprintf(stderr, "GRED: only %u DPs are " + fprintf(stderr, "GRED: only %u VQs are " "currently supported\n", MAX_DPs); return -1; } } else if (strcmp(*argv, "default") == 0) { if (opt.DPs == 0) { fprintf(stderr, "\"default\" must be defined " - "after \"DPs\"\n"); + "after \"vqs\"\n"); return -1; } NEXT_ARG(); @@ -80,7 +78,7 @@ static int init_gred(struct qdisc_util *qu, int argc, char **argv, return -1; } else if (opt.def_DP >= opt.DPs) { fprintf(stderr, "\"default\" must be less than " - "\"DPs\"\n"); + "\"vqs\"\n"); return -1; } } else if (strcmp(*argv, "grio") == 0) { @@ -155,13 +153,14 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n return -1; } ok++; - } else if (strcmp(*argv, "DP") == 0) { + } else if (strcmp(*argv, "vq") == 0 || + strcmp(*argv, "DP") == 0) { NEXT_ARG(); if (get_unsigned(&opt.DP, *argv, 10)) { - fprintf(stderr, "Illegal \"DP\"\n"); + fprintf(stderr, "Illegal \"vq\"\n"); return -1; } else if (opt.DP >= MAX_DPs) { - fprintf(stderr, "GRED: only %u DPs are " + fprintf(stderr, "GRED: only %u VQs are " "currently supported\n", MAX_DPs); return -1; } /* need a better error check */ @@ -216,7 +215,7 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n } if (opt.DP == MAX_DPs || !opt.limit || !opt.qth_min || !opt.qth_max || !avpkt) { - fprintf(stderr, "Required parameter (DP, limit, min, max, " + fprintf(stderr, "Required parameter (vq, limit, min, max, " "avpkt) is missing\n"); return -1; } @@ -292,14 +291,14 @@ static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) /* Bad hack! should really return a proper message as shown above*/ - fprintf(f, "DPs %u default %u %s", + fprintf(f, "vqs %u default %u %s", sopt->DPs, sopt->def_DP, sopt->grio ? "grio " : ""); for (i=0;iDP >= MAX_DPs) continue; - fprintf(f, "\n DP %u prio %hhu limit %s min %s max %s ", + fprintf(f, "\n vq %u prio %hhu limit %s min %s max %s ", qopt->DP, qopt->prio, sprint_size(qopt->limit, b1), From cbb99f7fbe28e4a4b3afebe2d143109d7e28956e Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 21 May 2015 14:39:27 -0700 Subject: [PATCH 23/27] Update to latest kernel headers Also add tipc_netlink.h for later TIPC support --- include/linux/tipc_netlink.h | 253 +++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 include/linux/tipc_netlink.h diff --git a/include/linux/tipc_netlink.h b/include/linux/tipc_netlink.h new file mode 100644 index 00000000..d4c8f142 --- /dev/null +++ b/include/linux/tipc_netlink.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2014, Ericsson AB + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LINUX_TIPC_NETLINK_H_ +#define _LINUX_TIPC_NETLINK_H_ + +#define TIPC_GENL_V2_NAME "TIPCv2" +#define TIPC_GENL_V2_VERSION 0x1 + +/* Netlink commands */ +enum { + TIPC_NL_UNSPEC, + TIPC_NL_LEGACY, + TIPC_NL_BEARER_DISABLE, + TIPC_NL_BEARER_ENABLE, + TIPC_NL_BEARER_GET, + TIPC_NL_BEARER_SET, + TIPC_NL_SOCK_GET, + TIPC_NL_PUBL_GET, + TIPC_NL_LINK_GET, + TIPC_NL_LINK_SET, + TIPC_NL_LINK_RESET_STATS, + TIPC_NL_MEDIA_GET, + TIPC_NL_MEDIA_SET, + TIPC_NL_NODE_GET, + TIPC_NL_NET_GET, + TIPC_NL_NET_SET, + TIPC_NL_NAME_TABLE_GET, + + __TIPC_NL_CMD_MAX, + TIPC_NL_CMD_MAX = __TIPC_NL_CMD_MAX - 1 +}; + +/* Top level netlink attributes */ +enum { + TIPC_NLA_UNSPEC, + TIPC_NLA_BEARER, /* nest */ + TIPC_NLA_SOCK, /* nest */ + TIPC_NLA_PUBL, /* nest */ + TIPC_NLA_LINK, /* nest */ + TIPC_NLA_MEDIA, /* nest */ + TIPC_NLA_NODE, /* nest */ + TIPC_NLA_NET, /* nest */ + TIPC_NLA_NAME_TABLE, /* nest */ + + __TIPC_NLA_MAX, + TIPC_NLA_MAX = __TIPC_NLA_MAX - 1 +}; + +/* Bearer info */ +enum { + TIPC_NLA_BEARER_UNSPEC, + TIPC_NLA_BEARER_NAME, /* string */ + TIPC_NLA_BEARER_PROP, /* nest */ + TIPC_NLA_BEARER_DOMAIN, /* u32 */ + TIPC_NLA_BEARER_UDP_OPTS, /* nest */ + + __TIPC_NLA_BEARER_MAX, + TIPC_NLA_BEARER_MAX = __TIPC_NLA_BEARER_MAX - 1 +}; + +enum { + TIPC_NLA_UDP_UNSPEC, + TIPC_NLA_UDP_LOCAL, /* sockaddr_storage */ + TIPC_NLA_UDP_REMOTE, /* sockaddr_storage */ + + __TIPC_NLA_UDP_MAX, + TIPC_NLA_UDP_MAX = __TIPC_NLA_UDP_MAX - 1 +}; +/* Socket info */ +enum { + TIPC_NLA_SOCK_UNSPEC, + TIPC_NLA_SOCK_ADDR, /* u32 */ + TIPC_NLA_SOCK_REF, /* u32 */ + TIPC_NLA_SOCK_CON, /* nest */ + TIPC_NLA_SOCK_HAS_PUBL, /* flag */ + + __TIPC_NLA_SOCK_MAX, + TIPC_NLA_SOCK_MAX = __TIPC_NLA_SOCK_MAX - 1 +}; + +/* Link info */ +enum { + TIPC_NLA_LINK_UNSPEC, + TIPC_NLA_LINK_NAME, /* string */ + TIPC_NLA_LINK_DEST, /* u32 */ + TIPC_NLA_LINK_MTU, /* u32 */ + TIPC_NLA_LINK_BROADCAST, /* flag */ + TIPC_NLA_LINK_UP, /* flag */ + TIPC_NLA_LINK_ACTIVE, /* flag */ + TIPC_NLA_LINK_PROP, /* nest */ + TIPC_NLA_LINK_STATS, /* nest */ + TIPC_NLA_LINK_RX, /* u32 */ + TIPC_NLA_LINK_TX, /* u32 */ + + __TIPC_NLA_LINK_MAX, + TIPC_NLA_LINK_MAX = __TIPC_NLA_LINK_MAX - 1 +}; + +/* Media info */ +enum { + TIPC_NLA_MEDIA_UNSPEC, + TIPC_NLA_MEDIA_NAME, /* string */ + TIPC_NLA_MEDIA_PROP, /* nest */ + + __TIPC_NLA_MEDIA_MAX, + TIPC_NLA_MEDIA_MAX = __TIPC_NLA_MEDIA_MAX - 1 +}; + +/* Node info */ +enum { + TIPC_NLA_NODE_UNSPEC, + TIPC_NLA_NODE_ADDR, /* u32 */ + TIPC_NLA_NODE_UP, /* flag */ + + __TIPC_NLA_NODE_MAX, + TIPC_NLA_NODE_MAX = __TIPC_NLA_NODE_MAX - 1 +}; + +/* Net info */ +enum { + TIPC_NLA_NET_UNSPEC, + TIPC_NLA_NET_ID, /* u32 */ + TIPC_NLA_NET_ADDR, /* u32 */ + + __TIPC_NLA_NET_MAX, + TIPC_NLA_NET_MAX = __TIPC_NLA_NET_MAX - 1 +}; + +/* Name table info */ +enum { + TIPC_NLA_NAME_TABLE_UNSPEC, + TIPC_NLA_NAME_TABLE_PUBL, /* nest */ + + __TIPC_NLA_NAME_TABLE_MAX, + TIPC_NLA_NAME_TABLE_MAX = __TIPC_NLA_NAME_TABLE_MAX - 1 +}; + +/* Publication info */ +enum { + TIPC_NLA_PUBL_UNSPEC, + + TIPC_NLA_PUBL_TYPE, /* u32 */ + TIPC_NLA_PUBL_LOWER, /* u32 */ + TIPC_NLA_PUBL_UPPER, /* u32 */ + TIPC_NLA_PUBL_SCOPE, /* u32 */ + TIPC_NLA_PUBL_NODE, /* u32 */ + TIPC_NLA_PUBL_REF, /* u32 */ + TIPC_NLA_PUBL_KEY, /* u32 */ + + __TIPC_NLA_PUBL_MAX, + TIPC_NLA_PUBL_MAX = __TIPC_NLA_PUBL_MAX - 1 +}; + +/* Nest, connection info */ +enum { + TIPC_NLA_CON_UNSPEC, + + TIPC_NLA_CON_FLAG, /* flag */ + TIPC_NLA_CON_NODE, /* u32 */ + TIPC_NLA_CON_SOCK, /* u32 */ + TIPC_NLA_CON_TYPE, /* u32 */ + TIPC_NLA_CON_INST, /* u32 */ + + __TIPC_NLA_CON_MAX, + TIPC_NLA_CON_MAX = __TIPC_NLA_CON_MAX - 1 +}; + +/* Nest, link propreties. Valid for link, media and bearer */ +enum { + TIPC_NLA_PROP_UNSPEC, + + TIPC_NLA_PROP_PRIO, /* u32 */ + TIPC_NLA_PROP_TOL, /* u32 */ + TIPC_NLA_PROP_WIN, /* u32 */ + + __TIPC_NLA_PROP_MAX, + TIPC_NLA_PROP_MAX = __TIPC_NLA_PROP_MAX - 1 +}; + +/* Nest, statistics info */ +enum { + TIPC_NLA_STATS_UNSPEC, + + TIPC_NLA_STATS_RX_INFO, /* u32 */ + TIPC_NLA_STATS_RX_FRAGMENTS, /* u32 */ + TIPC_NLA_STATS_RX_FRAGMENTED, /* u32 */ + TIPC_NLA_STATS_RX_BUNDLES, /* u32 */ + TIPC_NLA_STATS_RX_BUNDLED, /* u32 */ + TIPC_NLA_STATS_TX_INFO, /* u32 */ + TIPC_NLA_STATS_TX_FRAGMENTS, /* u32 */ + TIPC_NLA_STATS_TX_FRAGMENTED, /* u32 */ + TIPC_NLA_STATS_TX_BUNDLES, /* u32 */ + TIPC_NLA_STATS_TX_BUNDLED, /* u32 */ + TIPC_NLA_STATS_MSG_PROF_TOT, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_CNT, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_TOT, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P0, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P1, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P2, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P3, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P4, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P5, /* u32 */ + TIPC_NLA_STATS_MSG_LEN_P6, /* u32 */ + TIPC_NLA_STATS_RX_STATES, /* u32 */ + TIPC_NLA_STATS_RX_PROBES, /* u32 */ + TIPC_NLA_STATS_RX_NACKS, /* u32 */ + TIPC_NLA_STATS_RX_DEFERRED, /* u32 */ + TIPC_NLA_STATS_TX_STATES, /* u32 */ + TIPC_NLA_STATS_TX_PROBES, /* u32 */ + TIPC_NLA_STATS_TX_NACKS, /* u32 */ + TIPC_NLA_STATS_TX_ACKS, /* u32 */ + TIPC_NLA_STATS_RETRANSMITTED, /* u32 */ + TIPC_NLA_STATS_DUPLICATES, /* u32 */ + TIPC_NLA_STATS_LINK_CONGS, /* u32 */ + TIPC_NLA_STATS_MAX_QUEUE, /* u32 */ + TIPC_NLA_STATS_AVG_QUEUE, /* u32 */ + + __TIPC_NLA_STATS_MAX, + TIPC_NLA_STATS_MAX = __TIPC_NLA_STATS_MAX - 1 +}; + +#endif From f043759dd492897513d5aa057427bcdab8592d4b Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Thu, 7 May 2015 15:07:36 +0200 Subject: [PATCH 24/27] tipc: add new TIPC configuration tool tipc is a user-space configuration tool for TIPC (Transparent Inter-process Communication). It utilizes the TIPC netlink API in the kernel to fetch data or perform actions. The tipc tool has somewhat similar syntax to the ip tool meaning that users of the ip tool should not feel that unfamiliar with this tool. Signed-off-by: Richard Alpe Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Reviewed-by: Jon Maloy --- Makefile | 2 +- tipc/.gitignore | 1 + tipc/Makefile | 19 ++ tipc/README | 63 ++++ tipc/bearer.c | 725 +++++++++++++++++++++++++++++++++++++++++++++++ tipc/bearer.h | 22 ++ tipc/cmdl.c | 127 +++++++++ tipc/cmdl.h | 46 +++ tipc/link.c | 520 +++++++++++++++++++++++++++++++++ tipc/link.h | 21 ++ tipc/media.c | 260 +++++++++++++++++ tipc/media.h | 21 ++ tipc/misc.c | 35 +++ tipc/misc.h | 19 ++ tipc/msg.c | 170 +++++++++++ tipc/msg.h | 20 ++ tipc/nametable.c | 109 +++++++ tipc/nametable.h | 21 ++ tipc/node.c | 267 +++++++++++++++++ tipc/node.h | 21 ++ tipc/socket.c | 140 +++++++++ tipc/socket.h | 21 ++ tipc/tipc.c | 96 +++++++ 23 files changed, 2745 insertions(+), 1 deletion(-) create mode 100644 tipc/.gitignore create mode 100644 tipc/Makefile create mode 100644 tipc/README create mode 100644 tipc/bearer.c create mode 100644 tipc/bearer.h create mode 100644 tipc/cmdl.c create mode 100644 tipc/cmdl.h create mode 100644 tipc/link.c create mode 100644 tipc/link.h create mode 100644 tipc/media.c create mode 100644 tipc/media.h create mode 100644 tipc/misc.c create mode 100644 tipc/misc.h create mode 100644 tipc/msg.c create mode 100644 tipc/msg.h create mode 100644 tipc/nametable.c create mode 100644 tipc/nametable.h create mode 100644 tipc/node.c create mode 100644 tipc/node.h create mode 100644 tipc/socket.c create mode 100644 tipc/socket.h create mode 100644 tipc/tipc.c diff --git a/Makefile b/Makefile index b794f080..2e91c328 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ WFLAGS += -Wmissing-declarations -Wold-style-definition -Wformat=2 CFLAGS := $(WFLAGS) $(CCOPTS) -I../include $(DEFINES) $(CFLAGS) YACCFLAGS = -d -t -v -SUBDIRS=lib ip tc bridge misc netem genl man +SUBDIRS=lib ip tc bridge misc netem genl tipc man LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a LDLIBS += $(LIBNETLINK) diff --git a/tipc/.gitignore b/tipc/.gitignore new file mode 100644 index 00000000..39ed83d6 --- /dev/null +++ b/tipc/.gitignore @@ -0,0 +1 @@ +tipc diff --git a/tipc/Makefile b/tipc/Makefile new file mode 100644 index 00000000..4bda8c5d --- /dev/null +++ b/tipc/Makefile @@ -0,0 +1,19 @@ +TIPCOBJ=bearer.o \ + cmdl.o link.o \ + media.o misc.o \ + msg.o nametable.o \ + node.o socket.o \ + tipc.o + +TARGETS=tipc +LDLIBS += -lmnl + +all: $(TARGETS) $(LIBS) + +tipc: $(TIPCOBJ) + +install: all + install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(TIPCOBJ) $(TARGETS) diff --git a/tipc/README b/tipc/README new file mode 100644 index 00000000..578a0b7b --- /dev/null +++ b/tipc/README @@ -0,0 +1,63 @@ +DESIGN DECISIONS +---------------- + +HELP +~~~~ +--help or -h is used for help. We do not reserve the bare word "help", which +for example the ip command does. Reserving a bare word like help quickly +becomes cumbersome to handle in the code. It might be simple to handle +when it's passed early in the command chain like "ip addr help". But when +the user tries to pass "help" further down this requires manual checks and +special treatment. For example, at the time of writing this tool, it's +possible to create a vlan named "help" with the ip tool, but it's impossible +to remove it, the command just shows help. This is an effect of treating +bare words specially. + +Help texts are not dynamically generated. That is, we do not pass datastructures +like command list or option lists and print them dynamically. This is +intentional. There is always that exception and when it comes to help texts +these exceptions are normally neglected at the expence of usability. + +KEY-VALUE +~~~~~~~~~ +All options are key-values. There are both drawbacks and benefits to this. +The main drawback is that it becomes more to write for the user and +information might seem redundant. The main benefits is scalability and code +simplification. Consistency is important. + +Consider this. +1. tipc link set priority PRIO link LINK +2. tipc link set LINK priority PRIO + +Link might seem redundant in (1). However, if the command should live for many +years and be able to evolve example (2) limits the set command to only work on a +single link with no ability to extend. As an example, lets say we introduce +grouping on the kernel side. + +1. tipc link set priority PRIO group GROUP +2. tipc link set ??? priority PRIO group GROUP + +2. breaks, we can't extend the command to cover a group. + +PARSING +~~~~~~~ +Commands are single words. As an example, all words in "tipc link list" are +commands. Options are key-values that can be given in any order. In +"tipc link set priority PRIO link LINK" "tipc link set" are commands while +priority and link are options. Meaning that they can be given like +"tipc link set link LINK priority PRIO". + +Abbreviation matching works for both command and options. Meaning that +"tipc link set priority PRIO link LINK" could be given as +"tipc l s p PRIO l LINK" and "tipc link list" as "tipc l l". + +MEMORY +~~~~~~ +The tool strives to avoid allocating memory on the heap. Most (if not all) +memory allocations are on the stack. + +RETURNING +~~~~~~~~~ +The tool could throw exit() deep down in functions but doing so always seems +to limit the program in the long run. So we output the error and return an +appropriate error code upon failure. diff --git a/tipc/bearer.c b/tipc/bearer.c new file mode 100644 index 00000000..33295f9a --- /dev/null +++ b/tipc/bearer.c @@ -0,0 +1,725 @@ +/* + * bearer.c TIPC bearer functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "cmdl.h" +#include "msg.h" +#include "bearer.h" + +static void _print_bearer_opts(void) +{ + fprintf(stderr, + "\nOPTIONS\n" + " priority - Bearer link priority\n" + " tolerance - Bearer link tolerance\n" + " window - Bearer link window\n"); +} + +static void _print_bearer_media(void) +{ + fprintf(stderr, + "\nMEDIA\n" + " udp - User Datagram Protocol\n" + " ib - Infiniband\n" + " eth - Ethernet\n"); +} + +static void cmd_bearer_enable_l2_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s bearer enable media MEDIA device DEVICE [OPTIONS]\n" + "\nOPTIONS\n" + " domain DOMAIN - Discovery domain\n" + " priority PRIORITY - Bearer priority\n", + cmdl->argv[0]); +} + +static void cmd_bearer_enable_udp_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s bearer enable media udp name NAME localip IP [OPTIONS]\n" + "\nOPTIONS\n" + " domain DOMAIN - Discovery domain\n" + " priority PRIORITY - Bearer priority\n" + " localport PORT - Local UDP port (default 6118)\n" + " remoteip IP - Remote IP address\n" + " remoteport IP - Remote UDP port (default 6118)\n", + cmdl->argv[0]); +} + +static int enable_l2_bearer(struct nlmsghdr *nlh, struct opt *opts, + struct cmdl *cmdl) +{ + struct opt *opt; + char id[TIPC_MAX_BEARER_NAME]; + + if (!(opt = get_opt(opts, "device"))) { + fprintf(stderr, "error: missing bearer device\n"); + return -EINVAL; + } + snprintf(id, sizeof(id), "eth:%s", opt->val); + mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, id); + + return 0; +} + +static int get_netid_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_NET_MAX + 1] = {}; + int *netid = (int*)data; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_NET]) + return MNL_CB_ERROR; + mnl_attr_parse_nested(info[TIPC_NLA_NET], parse_attrs, attrs); + if (!attrs[TIPC_NLA_NET_ID]) + return MNL_CB_ERROR; + *netid = mnl_attr_get_u32(attrs[TIPC_NLA_NET_ID]); + + return MNL_CB_OK; +} + +static int generate_multicast(short af, char *buf, int bufsize) +{ + int netid; + char mnl_msg[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + + if (!(nlh = msg_init(mnl_msg, TIPC_NL_NET_GET))) { + fprintf(stderr, "error, message initialization failed\n"); + return -1; + } + if (msg_dumpit(nlh, get_netid_cb, &netid)) { + fprintf(stderr, "error, failed to fetch TIPC network id from kernel\n"); + return -EINVAL; + } + if (af == AF_INET) + snprintf(buf, bufsize, "228.0.%u.%u", (netid>>8) & 0xFF, netid & 0xFF); + else + snprintf(buf, bufsize, "ff02::%u", netid); + + return 0; +} + +static int enable_udp_bearer(struct nlmsghdr *nlh, struct opt *opts, + struct cmdl *cmdl) +{ + int err; + struct opt *opt; + struct nlattr *nest; + char buf[INET6_ADDRSTRLEN]; + char *locport = "6118"; + char *remport = "6118"; + char *locip = NULL; + char *remip = NULL; + char name[TIPC_MAX_BEARER_NAME]; + struct addrinfo *loc = NULL; + struct addrinfo *rem = NULL; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM + }; + + if (help_flag) { + cmd_bearer_enable_udp_help(cmdl); + /* TODO find a better error code? */ + return -EINVAL; + } + + if (!(opt = get_opt(opts, "name"))) { + fprintf(stderr, "error, udp bearer name missing\n"); + cmd_bearer_enable_udp_help(cmdl); + return -EINVAL; + } + snprintf(name, sizeof(name), "udp:%s", opt->val); + + if (!(opt = get_opt(opts, "localip"))) { + fprintf(stderr, "error, udp bearer localip missing\n"); + cmd_bearer_enable_udp_help(cmdl); + return -EINVAL; + } + locip = opt->val; + + if ((opt = get_opt(opts, "remoteip"))) + remip = opt->val; + + if ((opt = get_opt(opts, "localport"))) + locport = opt->val; + + if ((opt = get_opt(opts, "remoteport"))) + remport = opt->val; + + if ((err = getaddrinfo(locip, locport, &hints, &loc))) { + fprintf(stderr, "UDP local address error: %s\n", + gai_strerror(err)); + return err; + } + + if (!remip) { + if (generate_multicast(loc->ai_family, buf, sizeof(buf))) { + fprintf(stderr, "Failed to generate multicast address\n"); + return -EINVAL; + } + remip = buf; + } + + if ((err = getaddrinfo(remip, remport, &hints, &rem))) { + fprintf(stderr, "UDP remote address error: %s\n", + gai_strerror(err)); + freeaddrinfo(loc); + return err; + } + + if (rem->ai_family != loc->ai_family) { + fprintf(stderr, "UDP local and remote AF mismatch\n"); + return -EINVAL; + } + + mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, name); + + nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER_UDP_OPTS); + mnl_attr_put(nlh, TIPC_NLA_UDP_LOCAL, loc->ai_addrlen, loc->ai_addr); + mnl_attr_put(nlh, TIPC_NLA_UDP_REMOTE, rem->ai_addrlen, rem->ai_addr); + mnl_attr_nest_end(nlh, nest); + + freeaddrinfo(rem); + freeaddrinfo(loc); + + return 0; +} + +static void cmd_bearer_enable_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s bearer enable [OPTIONS] media MEDIA ARGS...\n\n" + "OPTIONS\n" + " domain DOMAIN - Discovery domain\n" + " priority PRIORITY - Bearer priority\n", + cmdl->argv[0]); + _print_bearer_media(); +} + +static int cmd_bearer_enable(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int err; + struct opt *opt; + struct nlattr *nest; + char buf[MNL_SOCKET_BUFFER_SIZE]; + char *media; + struct opt opts[] = { + { "device", NULL }, + { "domain", NULL }, + { "localip", NULL }, + { "localport", NULL }, + { "media", NULL }, + { "name", NULL }, + { "priority", NULL }, + { "remoteip", NULL }, + { "remoteport", NULL }, + { NULL } + }; + + if (parse_opts(opts, cmdl) < 0) { + if (help_flag) + (cmd->help)(cmdl); + return -EINVAL; + } + + if (!(opt = get_opt(opts, "media"))) { + if (help_flag) + (cmd->help)(cmdl); + else + fprintf(stderr, "error, missing bearer media\n"); + return -EINVAL; + } + media = opt->val; + + if (!(nlh = msg_init(buf, TIPC_NL_BEARER_ENABLE))) { + fprintf(stderr, "error: message initialisation failed\n"); + return -1; + } + nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER); + + if ((opt = get_opt(opts, "domain"))) + mnl_attr_put_u32(nlh, TIPC_NLA_BEARER_DOMAIN, atoi(opt->val)); + + if ((opt = get_opt(opts, "priority"))) { + struct nlattr *props; + + props = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER_PROP); + mnl_attr_put_u32(nlh, TIPC_NLA_PROP_PRIO, atoi(opt->val)); + mnl_attr_nest_end(nlh, props); + } + + if (strcmp(media, "udp") == 0) { + if (help_flag) { + cmd_bearer_enable_udp_help(cmdl); + return -EINVAL; + } + if ((err = enable_udp_bearer(nlh, opts, cmdl))) + return err; + } else if ((strcmp(media, "eth") == 0) || (strcmp(media, "udp") == 0)) { + if (help_flag) { + cmd_bearer_enable_l2_help(cmdl); + return -EINVAL; + } + if ((err = enable_l2_bearer(nlh, opts, cmdl))) + return err; + } else { + fprintf(stderr, "error, invalid media type \"%s\"\n", media); + return -EINVAL; + } + + mnl_attr_nest_end(nlh, nest); + + return msg_doit(nlh, NULL, NULL); +} + +static int add_l2_bearer(struct nlmsghdr *nlh, struct opt *opts) +{ + struct opt *opt; + char id[TIPC_MAX_BEARER_NAME]; + + if (!(opt = get_opt(opts, "device"))) { + fprintf(stderr, "error: missing bearer device\n"); + return -EINVAL; + } + snprintf(id, sizeof(id), "eth:%s", opt->val); + + mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, id); + + return 0; +} + +static int add_udp_bearer(struct nlmsghdr *nlh, struct opt *opts) +{ + struct opt *opt; + char id[TIPC_MAX_BEARER_NAME]; + + if (!(opt = get_opt(opts, "name"))) { + fprintf(stderr, "error: missing bearer name\n"); + return -EINVAL; + } + snprintf(id, sizeof(id), "udp:%s", opt->val); + + mnl_attr_put_strz(nlh, TIPC_NLA_BEARER_NAME, id); + + return 0; +} + +static void cmd_bearer_disable_l2_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s bearer disable media udp device DEVICE\n", + cmdl->argv[0]); +} + +static void cmd_bearer_disable_udp_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s bearer disable media udp name NAME\n", + cmdl->argv[0]); +} + +static void cmd_bearer_disable_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s bearer disable media MEDIA ARGS...\n", + cmdl->argv[0]); + _print_bearer_media(); +} + +static int cmd_bearer_disable(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int err; + char *media; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *nest; + struct opt *opt; + struct opt opts[] = { + { "device", NULL }, + { "name", NULL }, + { "media", NULL }, + { NULL } + }; + + if (parse_opts(opts, cmdl) < 0) { + if (help_flag) + (cmd->help)(cmdl); + return -EINVAL; + } + + if (!(opt = get_opt(opts, "media"))) { + if (help_flag) + (cmd->help)(cmdl); + else + fprintf(stderr, "error, missing bearer media\n"); + return -EINVAL; + } + media = opt->val; + + if (!(nlh = msg_init(buf, TIPC_NL_BEARER_DISABLE))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + nest = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER); + + if (strcmp(media, "udp") == 0) { + if (help_flag) { + cmd_bearer_disable_udp_help(cmdl); + return -EINVAL; + } + if ((err = add_udp_bearer(nlh, opts))) + return err; + } else if ((strcmp(media, "eth") == 0) || (strcmp(media, "udp") == 0)) { + if (help_flag) { + cmd_bearer_disable_l2_help(cmdl); + return -EINVAL; + } + if ((err = add_l2_bearer(nlh, opts))) + return err; + } else { + fprintf(stderr, "error, invalid media type \"%s\"\n", media); + return -EINVAL; + } + mnl_attr_nest_end(nlh, nest); + + return msg_doit(nlh, NULL, NULL); + +} + +static void cmd_bearer_set_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s bearer set [OPTIONS] media MEDIA ARGS...\n", + cmdl->argv[0]); + _print_bearer_opts(); + _print_bearer_media(); +} + +static void cmd_bearer_set_udp_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s bearer set [OPTIONS] media udp name NAME\n\n", + cmdl->argv[0]); + _print_bearer_opts(); +} + +static void cmd_bearer_set_l2_help(struct cmdl *cmdl, char *media) +{ + fprintf(stderr, + "Usage: %s bearer set [OPTION]... media %s device DEVICE\n", + cmdl->argv[0], media); + _print_bearer_opts(); +} + +static int cmd_bearer_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int err; + int val; + int prop; + char *media; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *props; + struct nlattr *attrs; + struct opt *opt; + struct opt opts[] = { + { "device", NULL }, + { "media", NULL }, + { "name", NULL }, + { NULL } + }; + + if (strcmp(cmd->cmd, "priority") == 0) + prop = TIPC_NLA_PROP_PRIO; + else if ((strcmp(cmd->cmd, "tolerance") == 0)) + prop = TIPC_NLA_PROP_TOL; + else if ((strcmp(cmd->cmd, "window") == 0)) + prop = TIPC_NLA_PROP_WIN; + else + return -EINVAL; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (cmdl->optind >= cmdl->argc) { + fprintf(stderr, "error, missing value\n"); + return -EINVAL; + } + val = atoi(shift_cmdl(cmdl)); + + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if (!(nlh = msg_init(buf, TIPC_NL_BEARER_SET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + attrs = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER); + + props = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER_PROP); + mnl_attr_put_u32(nlh, prop, val); + mnl_attr_nest_end(nlh, props); + + if (!(opt = get_opt(opts, "media"))) { + fprintf(stderr, "error, missing media\n"); + return -EINVAL; + } + media = opt->val; + + if (strcmp(media, "udp") == 0) { + if (help_flag) { + cmd_bearer_set_udp_help(cmdl); + return -EINVAL; + } + if ((err = add_udp_bearer(nlh, opts))) + return err; + } else if ((strcmp(media, "eth") == 0) || (strcmp(media, "udp") == 0)) { + if (help_flag) { + cmd_bearer_set_l2_help(cmdl, media); + return -EINVAL; + } + if ((err = add_l2_bearer(nlh, opts))) + return err; + } else { + fprintf(stderr, "error, invalid media type \"%s\"\n", media); + return -EINVAL; + } + mnl_attr_nest_end(nlh, attrs); + + return msg_doit(nlh, NULL, NULL); +} + +static int cmd_bearer_set(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "priority", cmd_bearer_set_prop, cmd_bearer_set_help }, + { "tolerance", cmd_bearer_set_prop, cmd_bearer_set_help }, + { "window", cmd_bearer_set_prop, cmd_bearer_set_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +static void cmd_bearer_get_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s bearer get [OPTIONS] media MEDIA ARGS...\n", + cmdl->argv[0]); + _print_bearer_opts(); + _print_bearer_media(); +} + +static void cmd_bearer_get_udp_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s bearer get [OPTIONS] media udp name NAME\n\n", + cmdl->argv[0]); + _print_bearer_opts(); +} + +static void cmd_bearer_get_l2_help(struct cmdl *cmdl, char *media) +{ + fprintf(stderr, + "Usage: %s bearer get [OPTION]... media %s device DEVICE\n", + cmdl->argv[0], media); + _print_bearer_opts(); +} + +static int bearer_get_cb(const struct nlmsghdr *nlh, void *data) +{ + int *prop = data; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1] = {}; + struct nlattr *props[TIPC_NLA_PROP_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_BEARER]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_BEARER], parse_attrs, attrs); + if (!attrs[TIPC_NLA_BEARER_PROP]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(attrs[TIPC_NLA_BEARER_PROP], parse_attrs, props); + if (!props[*prop]) + return MNL_CB_ERROR; + + printf("%u\n", mnl_attr_get_u32(props[*prop])); + + return MNL_CB_OK; +} + +static int cmd_bearer_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int err; + int prop; + char *media; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *attrs; + struct opt *opt; + struct opt opts[] = { + { "device", NULL }, + { "media", NULL }, + { "name", NULL }, + { NULL } + }; + + if (strcmp(cmd->cmd, "priority") == 0) + prop = TIPC_NLA_PROP_PRIO; + else if ((strcmp(cmd->cmd, "tolerance") == 0)) + prop = TIPC_NLA_PROP_TOL; + else if ((strcmp(cmd->cmd, "window") == 0)) + prop = TIPC_NLA_PROP_WIN; + else + return -EINVAL; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if (!(nlh = msg_init(buf, TIPC_NL_BEARER_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + if (!(opt = get_opt(opts, "media"))) { + fprintf(stderr, "error, missing media\n"); + return -EINVAL; + } + media = opt->val; + + attrs = mnl_attr_nest_start(nlh, TIPC_NLA_BEARER); + if (strcmp(media, "udp") == 0) { + if (help_flag) { + cmd_bearer_get_udp_help(cmdl); + return -EINVAL; + } + if ((err = add_udp_bearer(nlh, opts))) + return err; + } else if ((strcmp(media, "eth") == 0) || (strcmp(media, "udp") == 0)) { + if (help_flag) { + cmd_bearer_get_l2_help(cmdl, media); + return -EINVAL; + } + if ((err = add_l2_bearer(nlh, opts))) + return err; + } else { + fprintf(stderr, "error, invalid media type \"%s\"\n", media); + return -EINVAL; + } + mnl_attr_nest_end(nlh, attrs); + + return msg_doit(nlh, bearer_get_cb, &prop); +} + +static int cmd_bearer_get(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "priority", cmd_bearer_get_prop, cmd_bearer_get_help }, + { "tolerance", cmd_bearer_get_prop, cmd_bearer_get_help }, + { "window", cmd_bearer_get_prop, cmd_bearer_get_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +static int bearer_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_BEARER]) { + fprintf(stderr, "No bearer in netlink response\n"); + return MNL_CB_ERROR; + } + + mnl_attr_parse_nested(info[TIPC_NLA_BEARER], parse_attrs, attrs); + if (!attrs[TIPC_NLA_BEARER_NAME]) { + fprintf(stderr, "Bearer name missing in netlink response\n"); + return MNL_CB_ERROR; + } + + printf("%s\n", mnl_attr_get_str(attrs[TIPC_NLA_BEARER_NAME])); + + return MNL_CB_OK; +} + +static int cmd_bearer_list(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (help_flag) { + fprintf(stderr, "Usage: %s bearer list\n", cmdl->argv[0]); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_BEARER_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_dumpit(nlh, bearer_list_cb, NULL); +} + +void cmd_bearer_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s bearer COMMAND [ARGS] ...\n" + "\n" + "COMMANDS\n" + " enable - Enable a bearer\n" + " disable - Disable a bearer\n" + " set - Set various bearer properties\n" + " get - Get various bearer properties\n" + " list - List bearers\n", cmdl->argv[0]); +} + +int cmd_bearer(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data) +{ + const struct cmd cmds[] = { + { "disable", cmd_bearer_disable, cmd_bearer_disable_help }, + { "enable", cmd_bearer_enable, cmd_bearer_enable_help }, + { "get", cmd_bearer_get, cmd_bearer_get_help }, + { "list", cmd_bearer_list, NULL }, + { "set", cmd_bearer_set, cmd_bearer_set_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} diff --git a/tipc/bearer.h b/tipc/bearer.h new file mode 100644 index 00000000..9459d65e --- /dev/null +++ b/tipc/bearer.h @@ -0,0 +1,22 @@ +/* + * bearer.h TIPC bearer functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#ifndef _TIPC_BEARER_H +#define _TIPC_BEARER_H + +#include "cmdl.h" + +extern int help_flag; + +int cmd_bearer(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, void *data); +void cmd_bearer_help(struct cmdl *cmdl); + +#endif diff --git a/tipc/cmdl.c b/tipc/cmdl.c new file mode 100644 index 00000000..b816f7d4 --- /dev/null +++ b/tipc/cmdl.c @@ -0,0 +1,127 @@ +/* + * cmdl.c Framework for handling command line options. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include +#include + +#include + +#include "cmdl.h" + +const struct cmd *find_cmd(const struct cmd *cmds, char *str) +{ + const struct cmd *c; + const struct cmd *match = NULL; + + for (c = cmds; c->cmd; c++) { + if (strstr(c->cmd, str) != c->cmd) + continue; + if (match) + return NULL; + match = c; + } + + return match; +} + +static struct opt *find_opt(struct opt *opts, char *str) +{ + struct opt *o; + struct opt *match = NULL; + + for (o = opts; o->key; o++) { + if (strstr(o->key, str) != o->key) + continue; + if (match) + return NULL; + + match = o; + } + + return match; +} + +struct opt *get_opt(struct opt *opts, char *key) +{ + struct opt *o; + + for (o = opts; o->key; o++) { + if (strcmp(o->key, key) == 0 && o->val) + return o; + } + + return NULL; +} + +char *shift_cmdl(struct cmdl *cmdl) +{ + int next; + + if (cmdl->optind < cmdl->argc) + next = (cmdl->optind)++; + else + next = cmdl->argc; + + return cmdl->argv[next]; +} + +/* Returns the number of options parsed or a negative error code upon failure */ +int parse_opts(struct opt *opts, struct cmdl *cmdl) +{ + int i; + int cnt = 0; + + for (i = cmdl->optind; i < cmdl->argc; i += 2) { + struct opt *o; + + o = find_opt(opts, cmdl->argv[i]); + if (!o) { + fprintf(stderr, "error, invalid option \"%s\"\n", + cmdl->argv[i]); + return -EINVAL; + } + cnt++; + o->val = cmdl->argv[i + 1]; + cmdl->optind += 2; + } + + return cnt; +} + +int run_cmd(struct nlmsghdr *nlh, const struct cmd *caller, + const struct cmd *cmds, struct cmdl *cmdl, void *data) +{ + char *name; + const struct cmd *cmd; + + if ((cmdl->optind) >= cmdl->argc) { + if (caller->help) + (caller->help)(cmdl); + return -EINVAL; + } + name = cmdl->argv[cmdl->optind]; + (cmdl->optind)++; + + cmd = find_cmd(cmds, name); + if (!cmd) { + /* Show help about last command if we don't find this one */ + if (help_flag && caller->help) { + (caller->help)(cmdl); + } else { + fprintf(stderr, "error, invalid command \"%s\"\n", name); + fprintf(stderr, "use --help for command help\n"); + } + return -EINVAL; + } + + return (cmd->func)(nlh, cmd, cmdl, data); +} diff --git a/tipc/cmdl.h b/tipc/cmdl.h new file mode 100644 index 00000000..9f2666f8 --- /dev/null +++ b/tipc/cmdl.h @@ -0,0 +1,46 @@ +/* + * cmdl.h Framework for handling command line options. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#ifndef _TIPC_CMDL_H +#define _TIPC_CMDL_H + +#include + +extern int help_flag; + +struct cmdl { + int optind; + int argc; + char **argv; +}; + +struct cmd { + const char *cmd; + int (*func)(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data); + void (*help)(struct cmdl *cmdl); +}; + +struct opt { + const char *key; + char *val; +}; + +struct opt *get_opt(struct opt *opts, char *key); +int parse_opts(struct opt *opts, struct cmdl *cmdl); +char *shift_cmdl(struct cmdl *cmdl); + +int run_cmd(struct nlmsghdr *nlh, const struct cmd *caller, + const struct cmd *cmds, struct cmdl *cmdl, void *data); + +const struct cmd *find_cmd(const struct cmd *cmds, char *str); + +#endif diff --git a/tipc/link.c b/tipc/link.c new file mode 100644 index 00000000..89fb4ff4 --- /dev/null +++ b/tipc/link.c @@ -0,0 +1,520 @@ +/* + * link.c TIPC link functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cmdl.h" +#include "msg.h" +#include "link.h" + +static int link_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_LINK]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs); + if (!attrs[TIPC_NLA_LINK_NAME]) + return MNL_CB_ERROR; + + printf("%s: ", mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME])); + + if (attrs[TIPC_NLA_LINK_UP]) + printf("up\n"); + else + printf("down\n"); + + return MNL_CB_OK; +} + +static int cmd_link_list(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (help_flag) { + fprintf(stderr, "Usage: %s link list\n", cmdl->argv[0]); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_LINK_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_dumpit(nlh, link_list_cb, NULL); +} + +static int link_get_cb(const struct nlmsghdr *nlh, void *data) +{ + int *prop = data; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {}; + struct nlattr *props[TIPC_NLA_PROP_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_LINK]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs); + if (!attrs[TIPC_NLA_LINK_PROP]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_PROP], parse_attrs, props); + if (!props[*prop]) + return MNL_CB_ERROR; + + printf("%u\n", mnl_attr_get_u32(props[*prop])); + + return MNL_CB_OK; +} + + +static int cmd_link_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int prop; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct opt *opt; + struct opt opts[] = { + { "link", NULL }, + { NULL } + }; + + if (strcmp(cmd->cmd, "priority") == 0) + prop = TIPC_NLA_PROP_PRIO; + else if ((strcmp(cmd->cmd, "tolerance") == 0)) + prop = TIPC_NLA_PROP_TOL; + else if ((strcmp(cmd->cmd, "window") == 0)) + prop = TIPC_NLA_PROP_WIN; + else + return -EINVAL; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if (!(nlh = msg_init(buf, TIPC_NL_LINK_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + if (!(opt = get_opt(opts, "link"))) { + fprintf(stderr, "error, missing link\n"); + return -EINVAL; + } + mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, opt->val); + + return msg_doit(nlh, link_get_cb, &prop); +} + +static void cmd_link_get_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s link get PPROPERTY link LINK\n\n" + "PROPERTIES\n" + " tolerance - Get link tolerance\n" + " priority - Get link priority\n" + " window - Get link window\n", + cmdl->argv[0]); +} + +static int cmd_link_get(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "priority", cmd_link_get_prop, cmd_link_get_help }, + { "tolerance", cmd_link_get_prop, cmd_link_get_help }, + { "window", cmd_link_get_prop, cmd_link_get_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +static void cmd_link_stat_reset_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s link stat reset link LINK\n\n", cmdl->argv[0]); +} + +static int cmd_link_stat_reset(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char *link; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct opt *opt; + struct nlattr *nest; + struct opt opts[] = { + { "link", NULL }, + { NULL } + }; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (parse_opts(opts, cmdl) != 1) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_LINK_RESET_STATS))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + if (!(opt = get_opt(opts, "link"))) { + fprintf(stderr, "error, missing link\n"); + return -EINVAL; + } + link = opt->val; + + nest = mnl_attr_nest_start(nlh, TIPC_NLA_LINK); + mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, link); + mnl_attr_nest_end(nlh, nest); + + return msg_doit(nlh, NULL, NULL); +} + +static uint32_t perc(uint32_t count, uint32_t total) +{ + return (count * 100 + (total / 2)) / total; +} + +static int _show_link_stat(struct nlattr *attrs[], struct nlattr *prop[], + struct nlattr *stats[]) +{ + uint32_t proft; + + if (attrs[TIPC_NLA_LINK_ACTIVE]) + printf(" ACTIVE"); + else if (attrs[TIPC_NLA_LINK_UP]) + printf(" STANDBY"); + else + printf(" DEFUNCT"); + + printf(" MTU:%u Priority:%u Tolerance:%u ms Window:%u packets\n", + mnl_attr_get_u32(attrs[TIPC_NLA_LINK_MTU]), + mnl_attr_get_u32(prop[TIPC_NLA_PROP_PRIO]), + mnl_attr_get_u32(prop[TIPC_NLA_PROP_TOL]), + mnl_attr_get_u32(prop[TIPC_NLA_PROP_WIN])); + + printf(" RX packets:%u fragments:%u/%u bundles:%u/%u\n", + mnl_attr_get_u32(attrs[TIPC_NLA_LINK_RX]) - + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_INFO]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED])); + + printf(" TX packets:%u fragments:%u/%u bundles:%u/%u\n", + mnl_attr_get_u32(attrs[TIPC_NLA_LINK_TX]) - + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_INFO]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED])); + + proft = mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_PROF_TOT]); + printf(" TX profile sample:%u packets average:%u octets\n", + mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_CNT]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_TOT]) / proft); + + printf(" 0-64:%u%% -256:%u%% -1024:%u%% -4096:%u%% " + "-16384:%u%% -32768:%u%% -66000:%u%%\n", + perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P0]), proft), + perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P1]), proft), + perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P2]), proft), + perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P3]), proft), + perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P4]), proft), + perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P5]), proft), + perc(mnl_attr_get_u32(stats[TIPC_NLA_STATS_MSG_LEN_P6]), proft)); + + printf(" RX states:%u probes:%u naks:%u defs:%u dups:%u\n", + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_STATES]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_PROBES]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_DUPLICATES])); + + printf(" TX states:%u probes:%u naks:%u acks:%u dups:%u\n", + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_STATES]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_PROBES]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED])); + + printf(" Congestion link:%u Send queue max:%u avg:%u\n", + mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE])); + + return MNL_CB_OK; +} + +static int _show_bc_link_stat(struct nlattr *prop[], struct nlattr *stats[]) +{ + printf(" Window:%u packets\n", + mnl_attr_get_u32(prop[TIPC_NLA_PROP_WIN])); + + printf(" RX packets:%u fragments:%u/%u bundles:%u/%u\n", + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_INFO]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_FRAGMENTED]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLES]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_BUNDLED])); + + printf(" TX packets:%u fragments:%u/%u bundles:%u/%u\n", + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_INFO]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_FRAGMENTED]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLES]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_BUNDLED])); + + printf(" RX naks:%u defs:%u dups:%u\n", + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_NACKS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RX_DEFERRED]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_DUPLICATES])); + + printf(" TX naks:%u acks:%u dups:%u\n", + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_NACKS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_TX_ACKS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_RETRANSMITTED])); + + printf(" Congestion link:%u Send queue max:%u avg:%u\n", + mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]), + mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE])); + + return MNL_CB_OK; +} + +static int link_stat_show_cb(const struct nlmsghdr *nlh, void *data) +{ + const char *name; + const char *link = data; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1] = {}; + struct nlattr *prop[TIPC_NLA_PROP_MAX + 1] = {}; + struct nlattr *stats[TIPC_NLA_STATS_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_LINK]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs); + if (!attrs[TIPC_NLA_LINK_NAME] || !attrs[TIPC_NLA_LINK_PROP] || + !attrs[TIPC_NLA_LINK_STATS]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_PROP], parse_attrs, prop); + mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_STATS], parse_attrs, stats); + + name = mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]); + + /* If a link is passed, skip all but that link */ + if (link && (strcmp(name, link) != 0)) + return MNL_CB_OK; + + if (attrs[TIPC_NLA_LINK_BROADCAST]) { + printf("Link <%s>\n", name); + return _show_bc_link_stat(prop, stats); + } + + printf("\nLink <%s>\n", name); + + return _show_link_stat(attrs, prop, stats); +} + +static void cmd_link_stat_show_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s link stat show [ link LINK ]\n", + cmdl->argv[0]); +} + +static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char *link = NULL; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct opt *opt; + struct opt opts[] = { + { "link", NULL }, + { NULL } + }; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_LINK_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if ((opt = get_opt(opts, "link"))) + link = opt->val; + + return msg_dumpit(nlh, link_stat_show_cb, link); +} + +static void cmd_link_stat_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s link stat COMMAND [ARGS]\n\n" + "COMMANDS:\n" + " reset - Reset link statistics for link\n" + " show - Get link priority\n", + cmdl->argv[0]); +} + +static int cmd_link_stat(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "reset", cmd_link_stat_reset, cmd_link_stat_reset_help }, + { "show", cmd_link_stat_show, cmd_link_stat_show_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +static void cmd_link_set_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s link set PPROPERTY link LINK\n\n" + "PROPERTIES\n" + " tolerance TOLERANCE - Set link tolerance\n" + " priority PRIORITY - Set link priority\n" + " window WINDOW - Set link window\n", + cmdl->argv[0]); +} + +static int cmd_link_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int val; + int prop; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *props; + struct nlattr *attrs; + struct opt *opt; + struct opt opts[] = { + { "link", NULL }, + { NULL } + }; + + if (strcmp(cmd->cmd, "priority") == 0) + prop = TIPC_NLA_PROP_PRIO; + else if ((strcmp(cmd->cmd, "tolerance") == 0)) + prop = TIPC_NLA_PROP_TOL; + else if ((strcmp(cmd->cmd, "window") == 0)) + prop = TIPC_NLA_PROP_WIN; + else + return -EINVAL; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (cmdl->optind >= cmdl->argc) { + fprintf(stderr, "error, missing value\n"); + return -EINVAL; + } + val = atoi(shift_cmdl(cmdl)); + + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if (!(nlh = msg_init(buf, TIPC_NL_LINK_SET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK); + + if (!(opt = get_opt(opts, "link"))) { + fprintf(stderr, "error, missing link\n"); + return -EINVAL; + } + mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, opt->val); + + props = mnl_attr_nest_start(nlh, TIPC_NLA_LINK_PROP); + mnl_attr_put_u32(nlh, prop, val); + mnl_attr_nest_end(nlh, props); + + mnl_attr_nest_end(nlh, attrs); + + return msg_doit(nlh, link_get_cb, &prop); + + return 0; +} + +static int cmd_link_set(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "priority", cmd_link_set_prop, cmd_link_set_help }, + { "tolerance", cmd_link_set_prop, cmd_link_set_help }, + { "window", cmd_link_set_prop, cmd_link_set_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +void cmd_link_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s link COMMAND [ARGS] ...\n" + "\n" + "COMMANDS\n" + " list - List links\n" + " get - Get various link properties\n" + " set - Set various link properties\n" + " statistics - Show or reset statistics\n", + cmdl->argv[0]); +} + +int cmd_link(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data) +{ + const struct cmd cmds[] = { + { "get", cmd_link_get, cmd_link_get_help }, + { "list", cmd_link_list, NULL }, + { "set", cmd_link_set, cmd_link_set_help }, + { "statistics", cmd_link_stat, cmd_link_stat_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} diff --git a/tipc/link.h b/tipc/link.h new file mode 100644 index 00000000..6dc95e5b --- /dev/null +++ b/tipc/link.h @@ -0,0 +1,21 @@ +/* + * link.c TIPC link functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#ifndef _TIPC_LINK_H +#define _TIPC_LINK_H + +extern int help_flag; + +int cmd_link(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data); +void cmd_link_help(struct cmdl *cmdl); + +#endif diff --git a/tipc/media.c b/tipc/media.c new file mode 100644 index 00000000..a902ab78 --- /dev/null +++ b/tipc/media.c @@ -0,0 +1,260 @@ +/* + * media.c TIPC link functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cmdl.h" +#include "msg.h" +#include "media.h" + +static int media_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_MEDIA]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_MEDIA], parse_attrs, attrs); + if (!attrs[TIPC_NLA_MEDIA_NAME]) + return MNL_CB_ERROR; + + printf("%s\n", mnl_attr_get_str(attrs[TIPC_NLA_MEDIA_NAME])); + + return MNL_CB_OK; +} + +static int cmd_media_list(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (help_flag) { + fprintf(stderr, "Usage: %s media list\n", cmdl->argv[0]); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_MEDIA_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_dumpit(nlh, media_list_cb, NULL); +} + +static int media_get_cb(const struct nlmsghdr *nlh, void *data) +{ + int *prop = data; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_MEDIA_MAX + 1] = {}; + struct nlattr *props[TIPC_NLA_PROP_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_MEDIA]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_MEDIA], parse_attrs, attrs); + if (!attrs[TIPC_NLA_MEDIA_PROP]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(attrs[TIPC_NLA_MEDIA_PROP], parse_attrs, props); + if (!props[*prop]) + return MNL_CB_ERROR; + + printf("%u\n", mnl_attr_get_u32(props[*prop])); + + return MNL_CB_OK; +} + +static int cmd_media_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int prop; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *nest; + struct opt *opt; + struct opt opts[] = { + { "media", NULL }, + { NULL } + }; + + if (strcmp(cmd->cmd, "priority") == 0) + prop = TIPC_NLA_PROP_PRIO; + else if ((strcmp(cmd->cmd, "tolerance") == 0)) + prop = TIPC_NLA_PROP_TOL; + else if ((strcmp(cmd->cmd, "window") == 0)) + prop = TIPC_NLA_PROP_WIN; + else + return -EINVAL; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if (!(nlh = msg_init(buf, TIPC_NL_MEDIA_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + if (!(opt = get_opt(opts, "media"))) { + fprintf(stderr, "error, missing media\n"); + return -EINVAL; + } + nest = mnl_attr_nest_start(nlh, TIPC_NLA_MEDIA); + mnl_attr_put_strz(nlh, TIPC_NLA_MEDIA_NAME, opt->val); + mnl_attr_nest_end(nlh, nest); + + return msg_doit(nlh, media_get_cb, &prop); +} + +static void cmd_media_get_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s media get PPROPERTY media MEDIA\n\n" + "PROPERTIES\n" + " tolerance - Get media tolerance\n" + " priority - Get media priority\n" + " window - Get media window\n", + cmdl->argv[0]); +} + +static int cmd_media_get(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "priority", cmd_media_get_prop, cmd_media_get_help }, + { "tolerance", cmd_media_get_prop, cmd_media_get_help }, + { "window", cmd_media_get_prop, cmd_media_get_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +static void cmd_media_set_help(struct cmdl *cmdl) +{ + fprintf(stderr, "Usage: %s media set PPROPERTY media MEDIA\n\n" + "PROPERTIES\n" + " tolerance TOLERANCE - Set media tolerance\n" + " priority PRIORITY - Set media priority\n" + " window WINDOW - Set media window\n", + cmdl->argv[0]); +} + +static int cmd_media_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int val; + int prop; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *props; + struct nlattr *attrs; + struct opt *opt; + struct opt opts[] = { + { "media", NULL }, + { NULL } + }; + + if (strcmp(cmd->cmd, "priority") == 0) + prop = TIPC_NLA_PROP_PRIO; + else if ((strcmp(cmd->cmd, "tolerance") == 0)) + prop = TIPC_NLA_PROP_TOL; + else if ((strcmp(cmd->cmd, "window") == 0)) + prop = TIPC_NLA_PROP_WIN; + else + return -EINVAL; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (cmdl->optind >= cmdl->argc) { + fprintf(stderr, "error, missing value\n"); + return -EINVAL; + } + val = atoi(shift_cmdl(cmdl)); + + if (parse_opts(opts, cmdl) < 0) + return -EINVAL; + + if (!(nlh = msg_init(buf, TIPC_NL_MEDIA_SET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + attrs = mnl_attr_nest_start(nlh, TIPC_NLA_MEDIA); + + if (!(opt = get_opt(opts, "media"))) { + fprintf(stderr, "error, missing media\n"); + return -EINVAL; + } + mnl_attr_put_strz(nlh, TIPC_NLA_MEDIA_NAME, opt->val); + + props = mnl_attr_nest_start(nlh, TIPC_NLA_MEDIA_PROP); + mnl_attr_put_u32(nlh, prop, val); + mnl_attr_nest_end(nlh, props); + + mnl_attr_nest_end(nlh, attrs); + + return msg_doit(nlh, NULL, NULL); +} + +static int cmd_media_set(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "priority", cmd_media_set_prop, cmd_media_set_help }, + { "tolerance", cmd_media_set_prop, cmd_media_set_help }, + { "window", cmd_media_set_prop, cmd_media_set_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +void cmd_media_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s media COMMAND [ARGS] ...\n" + "\n" + "Commands:\n" + " list - List active media types\n" + " get - Get various media properties\n" + " set - Set various media properties\n", + cmdl->argv[0]); +} + +int cmd_media(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data) +{ + const struct cmd cmds[] = { + { "get", cmd_media_get, cmd_media_get_help }, + { "list", cmd_media_list, NULL }, + { "set", cmd_media_set, cmd_media_set_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} diff --git a/tipc/media.h b/tipc/media.h new file mode 100644 index 00000000..8584af74 --- /dev/null +++ b/tipc/media.h @@ -0,0 +1,21 @@ +/* + * media.h TIPC link functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#ifndef _TIPC_MEDIA_H +#define _TIPC_MEDIA_H + +extern int help_flag; + +int cmd_media(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data); +void cmd_media_help(struct cmdl *cmdl); + +#endif diff --git a/tipc/misc.c b/tipc/misc.c new file mode 100644 index 00000000..80912228 --- /dev/null +++ b/tipc/misc.c @@ -0,0 +1,35 @@ +/* + * misc.c Miscellaneous TIPC helper functions. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include +#include + +#include "misc.h" + +#define IN_RANGE(val, low, high) ((val) <= (high) && (val) >= (low)) + +uint32_t str2addr(char *str) +{ + unsigned int z, c, n; + char dummy; + + if (sscanf(str, "%u.%u.%u%c", &z, &c, &n, &dummy) != 3) { + fprintf(stderr, "invalid network address, syntax: Z.C.N\n"); + return 0; + } + + if (IN_RANGE(z, 0, 255) && IN_RANGE(c, 0, 4095) && IN_RANGE(n, 0, 4095)) + return tipc_addr(z, c, n); + + fprintf(stderr, "invalid network address \"%s\"\n", str); + return 0; +} diff --git a/tipc/misc.h b/tipc/misc.h new file mode 100644 index 00000000..585df745 --- /dev/null +++ b/tipc/misc.h @@ -0,0 +1,19 @@ +/* + * misc.h Miscellaneous TIPC helper functions. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#ifndef _TIPC_MISC_H +#define _TIPC_MISC_H + +#include + +uint32_t str2addr(char *str); + +#endif diff --git a/tipc/msg.c b/tipc/msg.c new file mode 100644 index 00000000..22c22226 --- /dev/null +++ b/tipc/msg.c @@ -0,0 +1,170 @@ +/* + * msg.c Messaging (netlink) helper functions. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "msg.h" + +int parse_attrs(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + tb[type] = attr; + + return MNL_CB_OK; +} + +static int family_id_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + int *id = data; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, tb); + if (!tb[CTRL_ATTR_FAMILY_ID]) + return MNL_CB_ERROR; + + *id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + + return MNL_CB_OK; +} + +static struct mnl_socket *msg_send(struct nlmsghdr *nlh) +{ + int ret; + struct mnl_socket *nl; + + nl = mnl_socket_open(NETLINK_GENERIC); + if (nl == NULL) { + perror("mnl_socket_open"); + return NULL; + } + + ret = mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID); + if (ret < 0) { + perror("mnl_socket_bind"); + return NULL; + } + + ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + if (ret < 0) { + perror("mnl_socket_send"); + return NULL; + } + + return nl; +} + +static int msg_recv(struct mnl_socket *nl, mnl_cb_t callback, void *data, int seq) +{ + int ret; + unsigned int portid; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + portid = mnl_socket_get_portid(nl); + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, callback, data); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) + perror("error"); + + mnl_socket_close(nl); + + return ret; +} + +static int msg_query(struct nlmsghdr *nlh, mnl_cb_t callback, void *data) +{ + unsigned int seq; + struct mnl_socket *nl; + + seq = time(NULL); + nlh->nlmsg_seq = seq; + + nl = msg_send(nlh); + if (!nl) + return -ENOTSUP; + + return msg_recv(nl, callback, data, seq); +} + +static int get_family(void) +{ + int err; + int nl_family; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = GENL_ID_CTRL; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd = CTRL_CMD_GETFAMILY; + genl->version = 1; + + mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL); + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TIPC_GENL_V2_NAME); + + if ((err = msg_query(nlh, family_id_cb, &nl_family))) + return err; + + return nl_family; +} + +int msg_doit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data) +{ + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + return msg_query(nlh, callback, data); +} + +int msg_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data) +{ + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + return msg_query(nlh, callback, data); +} + +struct nlmsghdr *msg_init(char *buf, int cmd) +{ + int family; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + + family = get_family(); + if (family <= 0) { + fprintf(stderr, + "Unable to get TIPC nl family id (module loaded?)\n"); + return NULL; + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = family; + + genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genl->cmd = cmd; + genl->version = 1; + + return nlh; +} diff --git a/tipc/msg.h b/tipc/msg.h new file mode 100644 index 00000000..41fd1ad1 --- /dev/null +++ b/tipc/msg.h @@ -0,0 +1,20 @@ +/* + * msg.h Messaging (netlink) helper functions. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#ifndef _TIPC_MSG_H +#define _TIPC_MSG_H + +struct nlmsghdr *msg_init(char *buf, int cmd); +int msg_doit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data); +int msg_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data); +int parse_attrs(const struct nlattr *attr, void *data); + +#endif diff --git a/tipc/nametable.c b/tipc/nametable.c new file mode 100644 index 00000000..770a644c --- /dev/null +++ b/tipc/nametable.c @@ -0,0 +1,109 @@ +/* + * nametable.c TIPC nametable functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include + +#include +#include +#include +#include + +#include "cmdl.h" +#include "msg.h" +#include "nametable.h" + +#define PORTID_STR_LEN 45 /* Four u32 and five delimiter chars */ + +static int nametable_show_cb(const struct nlmsghdr *nlh, void *data) +{ + int *iteration = data; + char port_id[PORTID_STR_LEN]; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_NAME_TABLE_MAX + 1] = {}; + struct nlattr *publ[TIPC_NLA_PUBL_MAX + 1] = {}; + const char *scope[] = { "", "zone", "cluster", "node" }; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_NAME_TABLE]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_NAME_TABLE], parse_attrs, attrs); + if (!attrs[TIPC_NLA_NAME_TABLE_PUBL]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(attrs[TIPC_NLA_NAME_TABLE_PUBL], parse_attrs, publ); + if (!publ[TIPC_NLA_NAME_TABLE_PUBL]) + return MNL_CB_ERROR; + + if (!*iteration) + printf("%-10s %-10s %-10s %-26s %-10s\n", + "Type", "Lower", "Upper", "Port Identity", + "Publication Scope"); + (*iteration)++; + + snprintf(port_id, sizeof(port_id), "<%u.%u.%u:%u>", + tipc_zone(mnl_attr_get_u32(publ[TIPC_NLA_PUBL_NODE])), + tipc_cluster(mnl_attr_get_u32(publ[TIPC_NLA_PUBL_NODE])), + tipc_node(mnl_attr_get_u32(publ[TIPC_NLA_PUBL_NODE])), + mnl_attr_get_u32(publ[TIPC_NLA_PUBL_REF])); + + printf("%-10u %-10u %-10u %-26s %-12u", + mnl_attr_get_u32(publ[TIPC_NLA_PUBL_TYPE]), + mnl_attr_get_u32(publ[TIPC_NLA_PUBL_LOWER]), + mnl_attr_get_u32(publ[TIPC_NLA_PUBL_UPPER]), + port_id, + mnl_attr_get_u32(publ[TIPC_NLA_PUBL_KEY])); + + printf("%s\n", scope[mnl_attr_get_u32(publ[TIPC_NLA_PUBL_SCOPE])]); + + return MNL_CB_OK; +} + +static int cmd_nametable_show(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int iteration = 0; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (help_flag) { + fprintf(stderr, "Usage: %s nametable show\n", cmdl->argv[0]); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_NAME_TABLE_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_dumpit(nlh, nametable_show_cb, &iteration); +} + +void cmd_nametable_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s nametable COMMAND\n\n" + "COMMANDS\n" + " show - Show nametable\n", + cmdl->argv[0]); +} + +int cmd_nametable(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data) +{ + const struct cmd cmds[] = { + { "show", cmd_nametable_show, NULL }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} diff --git a/tipc/nametable.h b/tipc/nametable.h new file mode 100644 index 00000000..e0473e18 --- /dev/null +++ b/tipc/nametable.h @@ -0,0 +1,21 @@ +/* + * nametable.h TIPC nametable functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#ifndef _TIPC_NAMETABLE_H +#define _TIPC_NAMETABLE_H + +extern int help_flag; + +void cmd_nametable_help(struct cmdl *cmdl); +int cmd_nametable(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data); + +#endif diff --git a/tipc/node.c b/tipc/node.c new file mode 100644 index 00000000..163fb743 --- /dev/null +++ b/tipc/node.c @@ -0,0 +1,267 @@ +/* + * node.c TIPC node functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cmdl.h" +#include "msg.h" +#include "misc.h" +#include "node.h" + +static int node_list_cb(const struct nlmsghdr *nlh, void *data) +{ + uint32_t addr; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_NODE_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_NODE]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_NODE], parse_attrs, attrs); + if (!attrs[TIPC_NLA_NODE_ADDR]) + return MNL_CB_ERROR; + + addr = mnl_attr_get_u32(attrs[TIPC_NLA_NODE_ADDR]); + printf("<%u.%u.%u>: ", + tipc_zone(addr), + tipc_cluster(addr), + tipc_node(addr)); + + if (attrs[TIPC_NLA_NODE_UP]) + printf("up\n"); + else + printf("down\n"); + + return MNL_CB_OK; +} + +static int cmd_node_list(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (help_flag) { + fprintf(stderr, "Usage: %s node list\n", cmdl->argv[0]); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_NODE_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_dumpit(nlh, node_list_cb, NULL); +} + +static int cmd_node_set_addr(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char *str; + uint32_t addr; + struct nlattr *nest; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (cmdl->argc != cmdl->optind + 1) { + fprintf(stderr, "Usage: %s node set address ADDRESS\n", + cmdl->argv[0]); + return -EINVAL; + } + + str = shift_cmdl(cmdl); + addr = str2addr(str); + if (!addr) + return -1; + + if (!(nlh = msg_init(buf, TIPC_NL_NET_SET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET); + mnl_attr_put_u32(nlh, TIPC_NLA_NET_ADDR, addr); + mnl_attr_nest_end(nlh, nest); + + return msg_doit(nlh, NULL, NULL); +} + +static int cmd_node_get_addr(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int sk; + socklen_t sz = sizeof(struct sockaddr_tipc); + struct sockaddr_tipc addr; + + if (!(sk = socket(AF_TIPC, SOCK_RDM, 0))) { + fprintf(stderr, "opening TIPC socket: %s\n", strerror(errno)); + return -1; + } + + if (getsockname(sk, (struct sockaddr *)&addr, &sz) < 0) { + fprintf(stderr, "getting TIPC socket address: %s\n", + strerror(errno)); + close(sk); + return -1; + } + close(sk); + + printf("<%u.%u.%u>\n", + tipc_zone(addr.addr.id.node), + tipc_cluster(addr.addr.id.node), + tipc_node(addr.addr.id.node)); + + return 0; +} + +static int netid_get_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_NET_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_NET]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_NET], parse_attrs, attrs); + if (!attrs[TIPC_NLA_NET_ID]) + return MNL_CB_ERROR; + + printf("%u\n", mnl_attr_get_u32(attrs[TIPC_NLA_NET_ID])); + + return MNL_CB_OK; +} + +static int cmd_node_get_netid(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_NET_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_dumpit(nlh, netid_get_cb, NULL); +} + +static int cmd_node_set_netid(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + int netid; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *nest; + + if (help_flag) { + (cmd->help)(cmdl); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_NET_SET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + if (cmdl->argc != cmdl->optind + 1) { + fprintf(stderr, "Usage: %s node set netid NETID\n", + cmdl->argv[0]); + return -EINVAL; + } + netid = atoi(shift_cmdl(cmdl)); + + nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET); + mnl_attr_put_u32(nlh, TIPC_NLA_NET_ID, netid); + mnl_attr_nest_end(nlh, nest); + + return msg_doit(nlh, NULL, NULL); +} + +static void cmd_node_set_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s node set PROPERTY\n\n" + "PROPERTIES\n" + " address ADDRESS - Set local address\n" + " netid NETID - Set local netid\n", + cmdl->argv[0]); +} + +static int cmd_node_set(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "address", cmd_node_set_addr, NULL }, + { "netid", cmd_node_set_netid, NULL }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +static void cmd_node_get_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s node get PROPERTY\n\n" + "PROPERTIES\n" + " address - Get local address\n" + " netid - Get local netid\n", + cmdl->argv[0]); +} + +static int cmd_node_get(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + const struct cmd cmds[] = { + { "address", cmd_node_get_addr, NULL }, + { "netid", cmd_node_get_netid, NULL }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} + +void cmd_node_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s media COMMAND [ARGS] ...\n\n" + "COMMANDS\n" + " list - List remote nodes\n" + " get - Get local node parameters\n" + " set - Set local node parameters\n", + cmdl->argv[0]); +} + +int cmd_node(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data) +{ + const struct cmd cmds[] = { + { "list", cmd_node_list, NULL }, + { "get", cmd_node_get, cmd_node_get_help }, + { "set", cmd_node_set, cmd_node_set_help }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} diff --git a/tipc/node.h b/tipc/node.h new file mode 100644 index 00000000..afee1fd0 --- /dev/null +++ b/tipc/node.h @@ -0,0 +1,21 @@ +/* + * node.h TIPC node functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#ifndef _TIPC_NODE_H +#define _TIPC_NODE_H + +extern int help_flag; + +int cmd_node(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data); +void cmd_node_help(struct cmdl *cmdl); + +#endif diff --git a/tipc/socket.c b/tipc/socket.c new file mode 100644 index 00000000..48ba8215 --- /dev/null +++ b/tipc/socket.c @@ -0,0 +1,140 @@ +/* + * socket.c TIPC socket functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include + +#include +#include +#include +#include + +#include "cmdl.h" +#include "msg.h" +#include "socket.h" + +#define PORTID_STR_LEN 45 /* Four u32 and five delimiter chars */ + +static int publ_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_SOCK_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_PUBL]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_PUBL], parse_attrs, attrs); + + printf(" bound to {%u,%u,%u}\n", + mnl_attr_get_u32(attrs[TIPC_NLA_PUBL_TYPE]), + mnl_attr_get_u32(attrs[TIPC_NLA_PUBL_LOWER]), + mnl_attr_get_u32(attrs[TIPC_NLA_PUBL_UPPER])); + + return MNL_CB_OK; +} + +static int publ_list(uint32_t sock) +{ + struct nlmsghdr *nlh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *nest; + + if (!(nlh = msg_init(buf, TIPC_NL_PUBL_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + nest = mnl_attr_nest_start(nlh, TIPC_NLA_SOCK); + mnl_attr_put_u32(nlh, TIPC_NLA_SOCK_REF, sock); + mnl_attr_nest_end(nlh, nest); + + return msg_dumpit(nlh, publ_list_cb, NULL); +} + +static int sock_list_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *info[TIPC_NLA_MAX + 1] = {}; + struct nlattr *attrs[TIPC_NLA_SOCK_MAX + 1] = {}; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_SOCK]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_SOCK], parse_attrs, attrs); + if (!attrs[TIPC_NLA_SOCK_REF]) + return MNL_CB_ERROR; + + printf("socket %u\n", mnl_attr_get_u32(attrs[TIPC_NLA_SOCK_REF])); + + if (attrs[TIPC_NLA_SOCK_CON]) { + uint32_t node; + struct nlattr *con[TIPC_NLA_CON_MAX + 1] = {}; + + mnl_attr_parse_nested(attrs[TIPC_NLA_SOCK_CON], parse_attrs, con); + node = mnl_attr_get_u32(con[TIPC_NLA_CON_NODE]); + + printf(" connected to <%u.%u.%u:%u>", tipc_zone(node), + tipc_cluster(node), tipc_node(node), + mnl_attr_get_u32(con[TIPC_NLA_CON_SOCK])); + + if (con[TIPC_NLA_CON_FLAG]) + printf(" via {%u,%u}\n", + mnl_attr_get_u32(con[TIPC_NLA_CON_TYPE]), + mnl_attr_get_u32(con[TIPC_NLA_CON_INST])); + else + printf("\n"); + } else if (attrs[TIPC_NLA_SOCK_HAS_PUBL]) { + publ_list(mnl_attr_get_u32(attrs[TIPC_NLA_SOCK_REF])); + } + + return MNL_CB_OK; +} + +static int cmd_socket_list(struct nlmsghdr *nlh, const struct cmd *cmd, + struct cmdl *cmdl, void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (help_flag) { + fprintf(stderr, "Usage: %s socket list\n", cmdl->argv[0]); + return -EINVAL; + } + + if (!(nlh = msg_init(buf, TIPC_NL_SOCK_GET))) { + fprintf(stderr, "error, message initialisation failed\n"); + return -1; + } + + return msg_dumpit(nlh, sock_list_cb, NULL); +} + +void cmd_socket_help(struct cmdl *cmdl) +{ + fprintf(stderr, + "Usage: %s socket COMMAND\n\n" + "Commands:\n" + " list - List sockets (ports)\n", + cmdl->argv[0]); +} + +int cmd_socket(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data) +{ + const struct cmd cmds[] = { + { "list", cmd_socket_list, NULL }, + { NULL } + }; + + return run_cmd(nlh, cmd, cmds, cmdl, NULL); +} diff --git a/tipc/socket.h b/tipc/socket.h new file mode 100644 index 00000000..9d1b6487 --- /dev/null +++ b/tipc/socket.h @@ -0,0 +1,21 @@ +/* + * socket.h TIPC socket functionality. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#ifndef _TIPC_SOCKET_H +#define _TIPC_SOCKET_H + +extern int help_flag; + +void cmd_socket_help(struct cmdl *cmdl); +int cmd_socket(struct nlmsghdr *nlh, const struct cmd *cmd, struct cmdl *cmdl, + void *data); + +#endif diff --git a/tipc/tipc.c b/tipc/tipc.c new file mode 100644 index 00000000..44398052 --- /dev/null +++ b/tipc/tipc.c @@ -0,0 +1,96 @@ +/* + * tipc. TIPC utility frontend. + * + * This program is free software; you can redistribute 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: Richard Alpe + */ + +#include +#include +#include +#include + +#include "bearer.h" +#include "link.h" +#include "nametable.h" +#include "socket.h" +#include "media.h" +#include "node.h" +#include "cmdl.h" + +int help_flag; + +static void about(struct cmdl *cmdl) +{ + fprintf(stderr, + "Transparent Inter-Process Communication Protocol\n" + "Usage: %s [OPTIONS] COMMAND [ARGS] ...\n" + "\n" + "Options:\n" + " -h, --help \t\tPrint help for last given command\n" + "\n" + "Commands:\n" + " bearer - Show or modify bearers\n" + " link - Show or modify links\n" + " media - Show or modify media\n" + " nametable - Show nametable\n" + " node - Show or modify node related parameters\n" + " socket - Show sockets\n", + cmdl->argv[0]); +} + +int main(int argc, char *argv[]) +{ + int i; + int res; + struct cmdl cmdl; + const struct cmd cmd = {"tipc", NULL, about}; + struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + const struct cmd cmds[] = { + { "bearer", cmd_bearer, cmd_bearer_help}, + { "link", cmd_link, cmd_link_help}, + { "media", cmd_media, cmd_media_help}, + { "nametable", cmd_nametable, cmd_nametable_help}, + { "node", cmd_node, cmd_node_help}, + { "socket", cmd_socket, cmd_socket_help}, + { NULL } + }; + + do { + int option_index = 0; + + i = getopt_long(argc, argv, "h", long_options, &option_index); + + switch (i) { + case 'h': + /* + * We want the help for the last command, so we flag + * here in order to print later. + */ + help_flag = 1; + break; + case -1: + /* End of options */ + break; + default: + /* Invalid option, error msg is printed by getopts */ + return 1; + } + } while (i != -1); + + cmdl.optind = optind; + cmdl.argc = argc; + cmdl.argv = argv; + + if ((res = run_cmd(NULL, &cmd, cmds, &cmdl, NULL)) != 0) + return 1; + + return 0; +} From 64dedc47399073d40be43685c1ceb79fd949d804 Mon Sep 17 00:00:00 2001 From: Vadim Kochan Date: Tue, 12 May 2015 17:40:15 +0300 Subject: [PATCH 25/27] tests: Run each test in network namespace Changed to forcely running each test in network namespace to do not affect on current network setup. Signed-off-by: Vadim Kochan --- testsuite/Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/testsuite/Makefile b/testsuite/Makefile index a2c8a2d9..4b945b0c 100644 --- a/testsuite/Makefile +++ b/testsuite/Makefile @@ -1,9 +1,11 @@ ## -- Config -- DEV := lo -PREFIX := sudo -E +PREFIX := sudo -E unshare -n RESULTS_DIR := results ## -- End Config -- +HAVE_UNSHARED_UTIL := $(shell unshare --version 2> /dev/null) + rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)) TESTS := $(patsubst tests/%,%,$(call rwildcard,tests/,*.t)) @@ -38,6 +40,9 @@ distclean: clean echo "Entering iproute2" && cd iproute2 && $(MAKE) distclean && cd ..; $(TESTS): clean +ifeq (,$(HAVE_UNSHARED_UTIL)) + $(error Please install util-linux tools to run tests in separated network namespace) +endif @mkdir -p $(RESULTS_DIR) @for d in $(TESTS_DIR); do \ From fede6dd9b324e43993c51939ae0f7c71ef957fc0 Mon Sep 17 00:00:00 2001 From: Vadim Kochan Date: Tue, 12 May 2015 17:40:16 +0300 Subject: [PATCH 26/27] tests: Add test for 'ip route add default' Signed-off-by: Vadim Kochan --- testsuite/tests/ip/route/add_default_route.t | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100755 testsuite/tests/ip/route/add_default_route.t diff --git a/testsuite/tests/ip/route/add_default_route.t b/testsuite/tests/ip/route/add_default_route.t new file mode 100755 index 00000000..29085288 --- /dev/null +++ b/testsuite/tests/ip/route/add_default_route.t @@ -0,0 +1,12 @@ +#!/bin/sh + +source lib/generic.sh + +ts_log "[Testing add default route]" + +DEV=dummy0 + +ts_ip "$0" "Add new interface $DEV" link add $DEV type dummy +ts_ip "$0" "Set $DEV into UP state" link set up dev $DEV +ts_ip "$0" "Add 1.1.1.1/24 addr on $DEV" addr add 1.1.1.1/24 dev $DEV +ts_ip "$0" "Add default route via 1.1.1.1" route add default via 1.1.1.1 From 2631b856668a717f076fdde94f30b128a1cd5553 Mon Sep 17 00:00:00 2001 From: Vadim Kochan Date: Fri, 15 May 2015 17:19:30 +0300 Subject: [PATCH 27/27] ss: Show more info (ring,fanout) for packet socks Print such info like version, tx/rx ring, fanout for packet sockets when '-e' option was specified. Signed-off-by: Vadim Kochan --- misc/ss.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/misc/ss.c b/misc/ss.c index 870cad18..347e3a12 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -2834,13 +2834,27 @@ static int packet_stats_print(struct sockstat *s, const struct filter *f) return 0; } +static void packet_show_ring(struct packet_diag_ring *ring) +{ + printf("blk_size:%d", ring->pdr_block_size); + printf(",blk_nr:%d", ring->pdr_block_nr); + printf(",frm_size:%d", ring->pdr_frame_size); + printf(",frm_nr:%d", ring->pdr_frame_nr); + printf(",tmo:%d", ring->pdr_retire_tmo); + printf(",features:0x%x", ring->pdr_features); +} + static int packet_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh, void *arg) { const struct filter *f = arg; struct packet_diag_msg *r = NLMSG_DATA(nlh); + struct packet_diag_info *pinfo = NULL; + struct packet_diag_ring *ring_rx = NULL, *ring_tx = NULL; struct rtattr *tb[PACKET_DIAG_MAX+1]; struct sockstat stat = {}; + uint32_t fanout = 0; + bool has_fanout = false; parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr*)(r+1), nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); @@ -2861,16 +2875,82 @@ static int packet_show_sock(const struct sockaddr_nl *addr, } if (tb[PACKET_DIAG_INFO]) { - struct packet_diag_info *pinfo = RTA_DATA(tb[PACKET_DIAG_INFO]); + pinfo = RTA_DATA(tb[PACKET_DIAG_INFO]); stat.lport = stat.iface = pinfo->pdi_index; } if (tb[PACKET_DIAG_UID]) stat.uid = *(__u32 *)RTA_DATA(tb[PACKET_DIAG_UID]); + if (tb[PACKET_DIAG_RX_RING]) + ring_rx = RTA_DATA(tb[PACKET_DIAG_RX_RING]); + + if (tb[PACKET_DIAG_TX_RING]) + ring_tx = RTA_DATA(tb[PACKET_DIAG_TX_RING]); + + if (tb[PACKET_DIAG_FANOUT]) { + has_fanout = true; + fanout = *(uint32_t *)RTA_DATA(tb[PACKET_DIAG_FANOUT]); + } + if (packet_stats_print(&stat, f)) return 0; + if (show_details) { + if (pinfo) { + printf("\n\tver:%d", pinfo->pdi_version); + printf(" cpy_thresh:%d", pinfo->pdi_copy_thresh); + printf(" flags( "); + if (pinfo->pdi_flags & PDI_RUNNING) + printf("running"); + if (pinfo->pdi_flags & PDI_AUXDATA) + printf(" auxdata"); + if (pinfo->pdi_flags & PDI_ORIGDEV) + printf(" origdev"); + if (pinfo->pdi_flags & PDI_VNETHDR) + printf(" vnethdr"); + if (pinfo->pdi_flags & PDI_LOSS) + printf(" loss"); + if (!pinfo->pdi_flags) + printf("0"); + printf(" )"); + } + if (ring_rx) { + printf("\n\tring_rx("); + packet_show_ring(ring_rx); + printf(")"); + } + if (ring_tx) { + printf("\n\tring_tx("); + packet_show_ring(ring_tx); + printf(")"); + } + if (has_fanout) { + uint16_t type = (fanout >> 16) & 0xffff; + + printf("\n\tfanout("); + printf("id:%d,", fanout & 0xffff); + printf("type:"); + + if (type == 0) + printf("hash"); + else if (type == 1) + printf("lb"); + else if (type == 2) + printf("cpu"); + else if (type == 3) + printf("roll"); + else if (type == 4) + printf("random"); + else if (type == 5) + printf("qm"); + else + printf("0x%x", type); + + printf(")"); + } + } + if (show_bpf && tb[PACKET_DIAG_FILTER]) { struct sock_filter *fil = RTA_DATA(tb[PACKET_DIAG_FILTER]); @@ -2894,7 +2974,8 @@ static int packet_show_netlink(struct filter *f) DIAG_REQUEST(req, struct packet_diag_req r); req.r.sdiag_family = AF_PACKET; - req.r.pdiag_show = PACKET_SHOW_INFO | PACKET_SHOW_MEMINFO | PACKET_SHOW_FILTER; + req.r.pdiag_show = PACKET_SHOW_INFO | PACKET_SHOW_MEMINFO | + PACKET_SHOW_FILTER | PACKET_SHOW_RING_CFG | PACKET_SHOW_FANOUT; return handle_netlink_request(f, &req.nlh, sizeof(req), packet_show_sock); }