/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
*/
/*
* Copyright 2015 Joyent, Inc. All rights reserved.
*/
/*
* ndp - display and manipulate Neighbor Cache Entries from NDP
*/
#include <stdio.h>
#include <stdarg.h>
#include <signal.h>
#include <time.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <libgen.h>
#include <wait.h>
#include <netdb.h>
#include <net/if_types.h>
typedef struct rtmsg_pkt {
} rtmsg_pkt_t;
enum ndp_action {
};
typedef void (ndp_void_f)(void);
static void ndp_usage(const char *, ...);
static void ndp_fatal(const char *, ...);
static void ndp_badflag(enum ndp_action);
static void ndp_missingarg(char);
static void ndp_run_in_child(ndp_void_f *);
static void ndp_do_run(void);
static void ndp_setup_handler(sigset_t *);
struct sockaddr_dl **);
static int ndp_find_interface(int, struct sockaddr *, char *, int);
static int ndp_host_enumerate(char *, ndp_addr_f *, void *);
static int ndp_display(struct lifreq *);
static int ndp_display_missing(struct lifreq *);
static void ndp_lifr2ip(struct lifreq *, char *, int);
static void ndp_get_all(void);
static int ndp_delete(int, struct lifreq *, void *);
static int ndp_set_nce(char *, char *, char *[], int);
static int ndp_set_file(char *);
/*
* Macros borrowed from route(1M) for working with PF_ROUTE messages
*/
l = ndp_salen(u); \
cp += l;
/*
* Print an error to stderr and then exit non-zero.
*/
static void
{
}
/*
* Print out the command usage to stderr, along with any reason why it's being
* printed, and then exit non-zero.
*/
static void
{
}
"Usage: %s [-n] [-i iface] hostname\n"
" %s [-n] [-i iface] -s nodeaddr etheraddr [temp] [proxy]\n"
" %s [-n] [-i iface] -d nodeaddr\n"
" %s [-n] [-i iface] -f filename\n"
" %s [-n] -a\n"
" %s [-n] -A period\n",
}
static void
{
switch (action) {
case NDP_A_DEFAULT:
case NDP_A_GET:
ndp_usage("Already going to print an entry, "
"but extra -%c given", optopt);
break;
case NDP_A_GET_ALL:
ndp_usage("Already going to print all entries (-a), "
"but extra -%c given", optopt);
break;
case NDP_A_GET_FOREVER:
ndp_usage("Already going to repeatedly print all entries (-A), "
"but extra -%c given", optopt);
break;
case NDP_A_DELETE:
ndp_usage("Already going to delete an entry (-d), "
"but extra -%c given", optopt);
break;
case NDP_A_SET_NCE:
ndp_usage("Already going to set an entry (-s), "
"but extra -%c given", optopt);
break;
case NDP_A_SET_FILE:
ndp_usage("Already going to set from file (-f), "
"but extra -%c given", optopt);
break;
}
}
static void
{
switch (flag) {
case 'A':
break;
case 'd':
break;
case 'f':
break;
case 's':
break;
case 'i':
break;
default:
break;
}
}
/*
* Run a function that's going to exec in a child process, and don't return
* until it exits.
*/
static void
{
func();
}
continue;
}
if (status != 0) {
}
}
/*
* SIGALRM handler to schedule a run.
*/
static void
ndp_do_run(void)
{
}
/*
* Prepare signal masks, and install the SIGALRM handler. Return old signal
* masks through the first argument.
*/
static void
{
/*
* Mask off SIGALRM so we only trigger the handler when we're ready
* using sigsuspend(3C), in case the child process takes longer to
* run than the alarm interval.
*/
}
ndp_fatal("Unable to add SIGALRM to signal mask: %s",
}
ndp_fatal("Unable to prepare empty signal set: %s",
}
ndp_fatal("Unable to install timer handler: %s",
}
}
/*
* Start the printing timer.
*/
static void
{
}
}
}
/*
* Run a given function forever periodically in a child process.
*/
static void
{
do {
if (ndp_run) {
}
(void) sigsuspend(&oset);
/*
* Only an EFAULT should get us here. Abort so we get a core dump.
*/
abort();
}
/*
* Given an address, return its size.
*/
static int
{
case AF_INET:
return (sizeof (struct sockaddr_in));
case AF_LINK:
return (sizeof (struct sockaddr_dl));
case AF_INET6:
return (sizeof (struct sockaddr_in6));
default:
warnx("Unrecognized sockaddr with address family %d!",
abort();
}
/*NOTREACHED*/
}
/*
* Extract all socket addresses from a routing message, and return them
* through the pointers given as arguments to ndp_extract_sockaddrs. None
* of the pointers should be null.
*/
static int
struct sockaddr_dl **ifp)
{
char *cp;
int i;
warnx("Routing message version %d not understood",
rtm->rtm_version);
return (-1);
}
warnx("Routing message couldn't be processed: %s",
return (-1);
}
for (i = 1; i != 0; i <<= 1) {
continue;
/*LINTED*/
switch (i) {
case RTA_DST:
break;
case RTA_GATEWAY:
break;
case RTA_NETMASK:
break;
case RTA_IFP:
break;
case RTA_SRC:
break;
}
}
}
return (0);
}
/*
* Given an IPv6 address, use routing information to look up
* the destination and interface it would pass through.
*/
static int
{
static int seq = 0;
int mlen, l;
/* Place the address we're looking up after the header */
/* Load an empty link-level address, so we get an interface back */
/*LINTED*/
"(failed to format IP)");
};
warnx("An appropriate interface for the address %s "
"is not in the routing table; use -i to force an "
"interface", ipaddr);
return (-1);
} else {
warnx("Failed to send routing message: %s",
return (-1);
}
warnx("Failed to write all bytes to routing socket");
return (-1);
}
/*
* Keep reading routing messages until we find the response to the one
* we just sent. Note that we depend on the sequence number being unique
* to the running program.
*/
do {
} while (mlen > 0 &&
if (mlen < 0) {
warnx("Failed to read from routing socket: %s",
return (-1);
}
return (0);
}
/*
* Find the interface that the IPv6 address would be routed through, and store
* the name of the interface in the buffer passed in.
*/
static int
{
return (-1);
}
return (-1);
}
warnx("Unable to find appropriate interface for address");
return (-1);
} else {
warnx("The interface name \"%.*s\" is too big for the "
return (-1);
} else {
}
}
return (0);
}
/*
* Zero out a lifreq struct for a SIOCLIF*ND ioctl, set the address, and fetch
* the appropriate interface using the given routing socket.
*/
static int
{
/* LINTED E_BAD_PTR_CAST_ALIGN */
} else if (sin6p->sin6_scope_id != 0) {
return (-1);
}
warnx("Link-scope addresses should specify an interface with "
"a zone ID, or with -i.");
return (-1);
} else {
return (-1);
}
return (0);
}
/*
* Take a host identifier, find the corresponding IPv6 addresses and then pass
* them to the specified function, along with any desired data.
*/
static int
{
while (attempts < MAX_ATTEMPTS) {
if (err == 0) {
break;
attempts++;
} else {
gai_strerror(err));
return (-1);
}
}
if (attempts == MAX_ATTEMPTS) {
return (-1);
}
if (inet6 < 0) {
err = -1;
}
if (route < 0) {
err = -1;
}
if (err == 0) {
!= 0) {
err = -1;
continue;
}
err = -1;
continue;
}
}
}
err = -1;
}
err = -1;
}
/* Clean up linked list */
return (err);
}
static int
{
warnx("Couldn't convert IPv6 address to string: %s",
return (-1);
};
warnx("Couldn't convert link-layer address to string: %s",
return (-1);
}
return (-1);
}
if (flags & NDF_ISROUTER_ON) {
(void) printf(" router");
}
if (flags & NDF_ANYCAST_ON) {
(void) printf(" any");
}
if (!(flags & NDF_STATIC)) {
(void) printf(" temp");
}
if (flags & NDF_PROXY_ON) {
(void) printf(" proxy");
}
(void) printf("\n");
return (0);
}
static int
{
warnx("Couldn't convert IPv6 address to string: %s",
return (-1);
};
return (-1);
}
return (0);
}
static void
{
};
}
/*
* Perform a SIOCLIFGETND and print out information about it
*/
/*ARGSUSED*/
static int
{
return (ndp_display_missing(lifrp));
} else {
warnx("Couldn't lookup %s: %s",
return (-1);
}
}
return (ndp_display(lifrp));
}
/*
* Print out all NDP entries
*/
static void
ndp_get_all(void)
{
"-f", "inet6", (char *)0);
}
/*
* Perform a SIOCLIFDELND ioctl
*/
/*ARGSUSED*/
static int
{
return (-1);
warnx("Permission denied, "
"could not delete entry for %s", ipaddr);
return (-1);
} else {
warnx("Couldn't delete mapping for %s: %s",
return (-1);
}
}
return (0);
}
/*
* Perform a SIOCLIFSETND ioctl using properties from the example structure.
*/
static int
{
warnx("Permission denied, "
"could not set entry for %s", ipaddr);
return (-1);
} else {
warnx("Failed to set mapping for %s: %s",
return (-1);
}
}
return (0);
}
/*
* Given a host identifier, a link-layer address and possible options,
*/
static int
{
char *opt;
int i;
return (-1);
}
warnx("The size of the link-layer address is "
"too large to set\n");
return (-1);
}
for (i = 0; i < optlen; i++) {
warnx("NDP proxying is currently not supported");
return (-1);
} else {
return (-1);
}
}
if (!temp) {
}
if (any) {
} else {
}
if (router) {
} else {
}
}
/*
* Read in a file and set the mappings from each line.
*/
static int
{
ndp_fatal("Error while opening file %s: %s",
}
errno = 0;
lineno++;
if (line[0] == '#')
continue;
warnx("Line %d incomplete, skipping: "
"missing host identifier", lineno);
continue;
}
warnx("Line %d incomplete, skipping: "
"missing link-layer address", lineno);
continue;
}
break;
}
continue;
}
}
}
}
return (failed_line ? -1 : 0);
}
int
{
char **opts;
char *endptr;
long long period;
if (argc < 2) {
ndp_usage("No arguments given.");
}
switch (c) {
case 'n':
break;
case 'i':
break;
case 's':
if (action != NDP_A_DEFAULT)
ndp_usage("Missing link-layer address after "
"the node address, \"%s\"", flagarg);
}
/*
* Grab any following keywords up to the next flag
*/
ndp_usage("Encountered \"%s\" after "
"flag parsing is done",
optind++;
optlen++;
}
break;
case 'a':
if (action != NDP_A_DEFAULT)
break;
case 'A':
if (action != NDP_A_DEFAULT)
break;
case 'd':
if (action != NDP_A_DEFAULT)
break;
case 'f':
if (action != NDP_A_DEFAULT)
break;
case ':':
break;
case '?':
default:
}
}
ndp_usage("Extra arguments leftover after parsing flags");
}
switch (action) {
case NDP_A_DEFAULT:
case NDP_A_GET:
if (argsleft != 1) {
ndp_usage("Multiple arguments given without any flags");
}
break;
case NDP_A_GET_ALL:
ndp_get_all();
/*NOTREACHED*/
break;
case NDP_A_GET_FOREVER:
errno = 0;
(endptr[0] != '\0') ||
(period < 0)) {
ndp_usage("Given period should be a positive integer,"
" not \"%s\"", flagarg);
}
if (period > 86400) {
ndp_usage("Given period should be shorter than a day;"
" given \"%s\" seconds", flagarg);
}
/*NOTREACHED*/
break;
case NDP_A_DELETE:
break;
case NDP_A_SET_NCE:
break;
case NDP_A_SET_FILE:
break;
}
return (err == 0 ? 0 : 1);
}