/* * 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 OPTION 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 OPTION 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 OPTION 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 OPTION 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); }