diff --git a/utils/ndptool.c b/utils/ndptool.c index ba24d75..afa516f 100644 --- a/utils/ndptool.c +++ b/utils/ndptool.c @@ -421,7 +421,7 @@ int main(int argc, char **argv) free(ifname); ifname = strdup(optarg); break; - case 'd': + case 'T': free(addr); addr = strdup(optarg); break; diff --git a/utils/ndptool.c.ndptool_fix_target_parameter_typo b/utils/ndptool.c.ndptool_fix_target_parameter_typo new file mode 100644 index 0000000..ba24d75 --- /dev/null +++ b/utils/ndptool.c.ndptool_fix_target_parameter_typo @@ -0,0 +1,513 @@ +/* + * ndptool.c - Neighbour discovery tool + * Copyright (C) 2013-2015 Jiri Pirko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum verbosity_level { + VERB1, + VERB2, + VERB3, + VERB4, +}; + +#define DEFAULT_VERB VERB1 +static int g_verbosity = DEFAULT_VERB; + +static uint8_t flags = ND_OPT_NORMAL; + +#define pr_err(args...) fprintf(stderr, ##args) +#define pr_outx(verb_level, args...) \ + do { \ + if (verb_level <= g_verbosity) \ + fprintf(stdout, ##args); \ + } while (0) +#define pr_out(args...) pr_outx(DEFAULT_VERB, ##args) +#define pr_out2(args...) pr_outx(VERB2, ##args) +#define pr_out3(args...) pr_outx(VERB3, ##args) +#define pr_out4(args...) pr_outx(VERB4, ##args) + +static void empty_signal_handler(int signal) +{ +} + +static int run_main_loop(struct ndp *ndp) +{ + fd_set rfds; + fd_set rfds_tmp; + int fdmax; + int ret; + struct sigaction siginfo; + sigset_t mask; + int ndp_fd; + int err = 0; + + sigemptyset(&siginfo.sa_mask); + siginfo.sa_flags = 0; + siginfo.sa_handler = empty_signal_handler; + ret = sigaction(SIGINT, &siginfo, NULL); + if (ret == -1) { + pr_err("Failed to set SIGINT handler\n"); + return -errno; + } + ret = sigaction(SIGQUIT, &siginfo, NULL); + if (ret == -1) { + pr_err("Failed to set SIGQUIT handler\n"); + return -errno; + } + ret = sigaction(SIGTERM, &siginfo, NULL); + if (ret == -1) { + pr_err("Failed to set SIGTERM handler\n"); + return -errno; + } + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGQUIT); + sigaddset(&mask, SIGTERM); + + ret = sigprocmask(SIG_BLOCK, &mask, NULL); + if (ret == -1) { + pr_err("Failed to set blocked signals\n"); + return -errno; + } + + sigemptyset(&mask); + + FD_ZERO(&rfds); + ndp_fd = ndp_get_eventfd(ndp); + FD_SET(ndp_fd, &rfds); + fdmax = ndp_fd + 1; + + for (;;) { + rfds_tmp = rfds; + ret = pselect(fdmax, &rfds_tmp, NULL, NULL, NULL, &mask); + if (ret == -1) { + if (errno == EINTR) { + goto out; + } + pr_err("Select failed\n"); + err = -errno; + goto out; + } + if (FD_ISSET(ndp_fd, &rfds_tmp)) { + err = ndp_call_eventfd_handler(ndp); + if (err) { + pr_err("ndp eventfd handler call failed\n"); + return err; + } + } + } +out: + return err; +} + +static void print_help(const char *argv0) { + pr_out( + "%s [options] command\n" + "\t-h --help Show this help\n" + "\t-v --verbose Increase output verbosity\n" + "\t-t --msg-type=TYPE Specify message type\n" + "\t (\"rs\", \"ra\", \"ns\", \"na\")\n" + "\t-T --target=TARGET Target address for NS or NA\n" + "\t-i --ifname=IFNAME Specify interface name\n" + "\t-U --unsolicited Send Unsolicited NA\n" + "Available commands:\n" + "\tmonitor\n" + "\tsend\n", + argv0); +} + +static const char *str_in6_addr(struct in6_addr *addr) +{ + static char buf[INET6_ADDRSTRLEN]; + + return inet_ntop(AF_INET6, addr, buf, sizeof(buf)); +} + +static void pr_out_hwaddr(unsigned char *hwaddr, size_t len) +{ + int i; + + for (i = 0; i < len; i++) { + if (i) + pr_out(":"); + pr_out("%02x", hwaddr[i]); + } + pr_out("\n"); +} + +static void pr_out_route_preference(enum ndp_route_preference pref) +{ + switch (pref) { + case NDP_ROUTE_PREF_LOW: + pr_out("low"); + break; + case NDP_ROUTE_PREF_MEDIUM: + pr_out("medium"); + break; + case NDP_ROUTE_PREF_HIGH: + pr_out("high"); + break; + } +} + +static void pr_out_lft(uint32_t lifetime) +{ + if (lifetime == (uint32_t) -1) + pr_out("infinity"); + else + pr_out("%us", lifetime); +} + +static int msgrcv_handler_func(struct ndp *ndp, struct ndp_msg *msg, void *priv) +{ + char ifname[IF_NAMESIZE]; + enum ndp_msg_type msg_type = ndp_msg_type(msg); + int offset; + + if_indextoname(ndp_msg_ifindex(msg), ifname); + pr_out("NDP payload len %zu, from addr: %s, iface: %s\n", + ndp_msg_payload_len(msg), + str_in6_addr(ndp_msg_addrto(msg)), ifname); + if (msg_type == NDP_MSG_RS) { + pr_out(" Type: RS\n"); + } else if (msg_type == NDP_MSG_RA) { + struct ndp_msgra *msgra = ndp_msgra(msg); + + pr_out(" Type: RA\n"); + pr_out(" Hop limit: %u\n", ndp_msgra_curhoplimit(msgra)); + pr_out(" Managed address configuration: %s\n", + ndp_msgra_flag_managed(msgra) ? "yes" : "no"); + pr_out(" Other configuration: %s\n", + ndp_msgra_flag_other(msgra) ? "yes" : "no"); + pr_out(" Default router preference: "); + pr_out_route_preference(ndp_msgra_route_preference(msgra)); + pr_out("\n"); + pr_out(" Router lifetime: %us\n", + ndp_msgra_router_lifetime(msgra)); + pr_out(" Reachable time: "); + if (ndp_msgra_reachable_time(msgra)) { + pr_out("%ums\n", + ndp_msgra_reachable_time(msgra)); + } else { + pr_out("unspecified\n"); + } + pr_out(" Retransmit time: "); + if (ndp_msgra_retransmit_time(msgra)) { + pr_out("%ums\n", + ndp_msgra_retransmit_time(msgra)); + } else { + pr_out("unspecified\n"); + } + + ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_SLLADDR) { + pr_out(" Source linkaddr: "); + pr_out_hwaddr(ndp_msg_opt_slladdr(msg, offset), + ndp_msg_opt_slladdr_len(msg, offset)); + } + ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_TLLADDR) { + pr_out(" Target linkaddr: "); + pr_out_hwaddr(ndp_msg_opt_tlladdr(msg, offset), + ndp_msg_opt_tlladdr_len(msg, offset)); + } + ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_PREFIX) { + uint32_t valid_time; + uint32_t preferred_time; + + valid_time = ndp_msg_opt_prefix_valid_time(msg, offset); + preferred_time = ndp_msg_opt_prefix_preferred_time(msg, offset); + pr_out(" Prefix: %s/%u", + str_in6_addr(ndp_msg_opt_prefix(msg, offset)), + ndp_msg_opt_prefix_len(msg, offset)); + pr_out(", valid_time: "); + if (valid_time == (uint32_t) -1) + pr_out("infinity"); + else + pr_out("%us", valid_time); + pr_out(", preferred_time: "); + if (preferred_time == (uint32_t) -1) + pr_out("infinity"); + else + pr_out("%us", preferred_time); + pr_out(", on_link: %s", + ndp_msg_opt_prefix_flag_on_link(msg, offset) ? "yes" : "no"); + pr_out(", autonomous_addr_conf: %s", + ndp_msg_opt_prefix_flag_auto_addr_conf(msg, offset) ? "yes" : "no"); + pr_out(", router_addr: %s", + ndp_msg_opt_prefix_flag_router_addr(msg, offset) ? "yes" : "no"); + pr_out("\n"); + } + ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_MTU) + pr_out(" MTU: %u\n", ndp_msg_opt_mtu(msg, offset)); + ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_ROUTE) { + pr_out(" Route: %s/%u", + str_in6_addr(ndp_msg_opt_route_prefix(msg, offset)), + ndp_msg_opt_route_prefix_len(msg, offset)); + pr_out(", lifetime: "); + pr_out_lft(ndp_msg_opt_route_lifetime(msg, offset)); + pr_out(", preference: "); + pr_out_route_preference(ndp_msg_opt_route_preference(msg, offset)); + pr_out("\n"); + } + ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_RDNSS) { + static struct in6_addr *addr; + int addr_index; + + pr_out(" Recursive DNS Servers: "); + ndp_msg_opt_rdnss_for_each_addr(addr, addr_index, msg, offset) { + if (addr_index != 0) + pr_out(", "); + pr_out("%s", str_in6_addr(addr)); + } + pr_out(", lifetime: "); + pr_out_lft(ndp_msg_opt_rdnss_lifetime(msg, offset)); + pr_out("\n"); + } + ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_DNSSL) { + char *domain; + int domain_index; + + pr_out(" DNS Search List: "); + ndp_msg_opt_dnssl_for_each_domain(domain, domain_index, msg, offset) { + if (domain_index != 0) + pr_out(" "); + pr_out("%s", domain); + } + pr_out(", lifetime: "); + pr_out_lft(ndp_msg_opt_rdnss_lifetime(msg, offset)); + pr_out("\n"); + } + } else if (msg_type == NDP_MSG_NS) { + pr_out(" Type: NS\n"); + } else if (msg_type == NDP_MSG_NA) { + pr_out(" Type: NA\n"); + } else if (msg_type == NDP_MSG_R) { + pr_out(" Type: R\n"); + } else { + return 0; + } + return 0; +} + +static int run_cmd_monitor(struct ndp *ndp, enum ndp_msg_type msg_type, + uint32_t ifindex) +{ + int err; + + err = ndp_msgrcv_handler_register(ndp, &msgrcv_handler_func, msg_type, + ifindex, NULL); + if (err) { + pr_err("Failed to register msgrcv handler\n"); + return err; + } + err = run_main_loop(ndp); + ndp_msgrcv_handler_unregister(ndp, &msgrcv_handler_func, msg_type, + ifindex, NULL); + return err; +} + +static int run_cmd_send(struct ndp *ndp, enum ndp_msg_type msg_type, + uint32_t ifindex, struct in6_addr *target) +{ + struct ndp_msg *msg; + int err; + + err = ndp_msg_new(&msg, msg_type); + if (err) { + pr_err("Failed to create message\n"); + return err; + } + ndp_msg_ifindex_set(msg, ifindex); + ndp_msg_target_set(msg, target); + ndp_msg_opt_set(msg); + + err = ndp_msg_send_with_flags(ndp, msg, flags); + if (err) { + pr_err("Failed to send message\n"); + goto msg_destroy; + } + +msg_destroy: + ndp_msg_destroy(msg); + return err; +} + + +static int get_msg_type(enum ndp_msg_type *p_msg_type, char *msgtypestr) +{ + if (!msgtypestr) + *p_msg_type = NDP_MSG_ALL; + else if (!strcmp(msgtypestr, "rs")) + *p_msg_type = NDP_MSG_RS; + else if (!strcmp(msgtypestr, "ra")) + *p_msg_type = NDP_MSG_RA; + else if (!strcmp(msgtypestr, "ns")) + *p_msg_type = NDP_MSG_NS; + else if (!strcmp(msgtypestr, "na")) + *p_msg_type = NDP_MSG_NA; + else if (!strcmp(msgtypestr, "r")) + *p_msg_type = NDP_MSG_R; + else + return -EINVAL; + return 0; +} + +int main(int argc, char **argv) +{ + char *argv0 = argv[0]; + static const struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "verbose", no_argument, NULL, 'v' }, + { "msg-type", required_argument, NULL, 't' }, + { "ifname", required_argument, NULL, 'i' }, + { "target", required_argument, NULL, 'T' }, + { "unsolicited",no_argument, NULL, 'U' }, + { NULL, 0, NULL, 0 } + }; + int opt; + struct ndp *ndp; + char *msgtypestr = NULL; + enum ndp_msg_type msg_type; + char *ifname = NULL; + char *addr = NULL; + struct in6_addr target = IN6ADDR_ANY_INIT; + uint32_t ifindex; + char *cmd_name; + int err; + int res = EXIT_FAILURE; + + while ((opt = getopt_long(argc, argv, "hvt:T:i:U", + long_options, NULL)) >= 0) { + + switch(opt) { + case 'h': + print_help(argv0); + return EXIT_SUCCESS; + case 'v': + g_verbosity++; + break; + case 't': + free(msgtypestr); + msgtypestr = strdup(optarg); + break; + case 'i': + free(ifname); + ifname = strdup(optarg); + break; + case 'd': + free(addr); + addr = strdup(optarg); + break; + case 'U': + flags |= ND_OPT_NA_UNSOL; + break; + case '?': + pr_err("unknown option.\n"); + print_help(argv0); + return EXIT_FAILURE; + default: + pr_err("unknown option \"%c\".\n", opt); + print_help(argv0); + return EXIT_FAILURE; + } + } + + if (optind >= argc) { + pr_err("No command specified.\n"); + print_help(argv0); + goto errout; + } + + argv += optind; + cmd_name = *argv++; + argc -= optind + 1; + + ifindex = 0; + if (ifname) { + ifindex = if_nametoindex(ifname); + if (!ifindex) { + pr_err("Interface \"%s\" does not exist\n", ifname); + goto errout; + } + } + + if (addr && inet_pton(AF_INET6, addr, &target) <= 0) { + pr_err("Invalid target address \"%s\"\n", addr); + goto errout; + } + + err = get_msg_type(&msg_type, msgtypestr); + if (err) { + pr_err("Invalid message type \"%s\" selected\n", msgtypestr); + print_help(argv0); + goto errout; + } + + err = ndp_open(&ndp); + if (err) { + pr_err("Failed to open ndp: %s\n", strerror(-err)); + goto errout; + } + + if (!strncmp(cmd_name, "monitor", strlen(cmd_name))) { + err = run_cmd_monitor(ndp, msg_type, ifindex); + } else if (!strncmp(cmd_name, "send", strlen(cmd_name))) { + bool all_ok = true; + + if (msg_type == NDP_MSG_ALL) { + pr_err("Message type must be selected\n"); + all_ok = false; + } + if (!ifindex) { + pr_err("Interface name must be selected\n"); + all_ok = false; + } + if (!all_ok) { + print_help(argv0); + goto errout; + } + err = run_cmd_send(ndp, msg_type, ifindex, &target); + } else { + pr_err("Unknown command \"%s\"\n", cmd_name); + goto ndp_close; + } + + if (err) { + pr_err("Command failed \"%s\"\n", strerror(-err)); + goto ndp_close; + } + + res = EXIT_SUCCESS; + +ndp_close: + ndp_close(ndp); +errout: + return res; +}