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/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 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; 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(); diff --git a/ip/iproute.c b/ip/iproute.c index e0a61598..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"); @@ -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; } 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; } 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; diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index 0dfcb2ea..714aab48 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 @@ -709,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 @@ -863,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" @@ -915,6 +913,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 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. diff --git a/misc/ss.c b/misc/ss.c index 46dbb39a..347e3a12 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) @@ -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]); @@ -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); } 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); diff --git a/tc/q_gred.c b/tc/q_gred.c index 88bd0947..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, @@ -53,34 +50,35 @@ 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) { + if (strcmp(*argv, "vqs") == 0 || + 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); - fprintf(stderr, "Illegal \"DPs\"\n"); - fprintf(stderr, "GRED: only %d DPs are " - "currently supported\n",MAX_DPs); + if (get_unsigned(&opt.DPs, *argv, 10)) { + fprintf(stderr, "Illegal \"vqs\"\n"); + return -1; + } else if (opt.DPs > MAX_DPs) { + fprintf(stderr, "GRED: only %u VQs 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 \"vqs\"\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 " + "\"vqs\"\n"); return -1; } } else if (strcmp(*argv, "grio") == 0) { @@ -96,15 +94,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,17 +113,17 @@ 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; unsigned rate = 0; - int wlog; + int parm; __u8 sbuf[256]; struct rtattr *tail; __u32 max_P; - memset(&opt, 0, sizeof(opt)); + opt.DP = MAX_DPs; while (argc > 0) { if (strcmp(*argv, "limit") == 0) { @@ -143,8 +138,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)) { @@ -159,20 +153,21 @@ 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(); - 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); - fprintf(stderr, "Illegal \"DP\"\n"); - fprintf(stderr, "GRED: only %d DPs are currently supported\n",MAX_DPs); + if (get_unsigned(&opt.DP, *argv, 10)) { + fprintf(stderr, "Illegal \"vq\"\n"); return -1; - } + } else if (opt.DP >= MAX_DPs) { + fprintf(stderr, "GRED: only %u VQs are " + "currently supported\n", MAX_DPs); + return -1; + } /* need a better error check */ 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; } @@ -214,40 +209,44 @@ 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, " - "avpkt, DP) is missing\n"); + if (!ok) { + explain(); + return -1; + } + if (opt.DP == MAX_DPs || !opt.limit || !opt.qth_min || !opt.qth_max || + !avpkt) { + fprintf(stderr, "Required parameter (vq, limit, min, max, " + "avpkt) is missing\n"); return -1; } if (!burst) { 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 (!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; } - if (wlog >= 10) - fprintf(stderr, "GRED: WARNING. Burst %d seems to be too " + if (parm >= 10) + fprintf(stderr, "GRED: WARNING. Burst %u 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); @@ -262,14 +261,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; @@ -283,40 +281,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, "vqs %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 vq %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; } 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; } diff --git a/tc/q_red.c b/tc/q_red.c index 89e73201..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"); } @@ -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; @@ -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,23 +123,27 @@ 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 (!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; } - if (wlog >= 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) { + if (parm >= 10) + 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"); 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); 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 \ 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 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; +}