mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-06 16:40:47 +00:00
ndp: convert ndp(8) to netlink.
The change is intended to be fully transparent to the users. Similarly to route(8) and netstat(8), ndp can be build without netlink by defining WITHOUT_NETLINK in make.conf. Differential Revision: https://reviews.freebsd.org/D39720
This commit is contained in:
parent
35e7180a32
commit
91fbe0819b
|
@ -31,6 +31,13 @@ CFLAGS+= -DEXPERIMENTAL
|
|||
CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG
|
||||
.endif
|
||||
|
||||
.if ${MK_NETLINK_SUPPORT} != "no"
|
||||
SRCS+= ndp_netlink.c
|
||||
.else
|
||||
CFLAGS+=-DWITHOUT_NETLINK
|
||||
.endif
|
||||
|
||||
|
||||
WARNS?= 3
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <nlist.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <paths.h>
|
||||
|
@ -112,6 +113,8 @@
|
|||
#include <libxo/xo.h>
|
||||
#include "gmt2local.h"
|
||||
|
||||
#include "ndp.h"
|
||||
|
||||
#define NEXTADDR(w, s) \
|
||||
if (rtm->rtm_addrs & (w)) { \
|
||||
bcopy((char *)&s, cp, sizeof(s)); \
|
||||
|
@ -119,8 +122,6 @@
|
|||
}
|
||||
|
||||
static pid_t pid;
|
||||
static int nflag;
|
||||
static int tflag;
|
||||
static int32_t thiszone; /* time difference with gmt */
|
||||
static int s = -1;
|
||||
static int repeat = 0;
|
||||
|
@ -129,16 +130,13 @@ static char host_buf[NI_MAXHOST]; /* getnameinfo() */
|
|||
static char ifix_buf[IFNAMSIZ]; /* if_indextoname() */
|
||||
|
||||
static int file(char *);
|
||||
static void getsocket(void);
|
||||
static int set(int, char **);
|
||||
static void get(char *);
|
||||
static int delete(char *);
|
||||
static void dump(struct sockaddr_in6 *, int);
|
||||
static int dump(struct sockaddr_in6 *, int);
|
||||
static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int);
|
||||
static char *ether_str(struct sockaddr_dl *);
|
||||
static int ndp_ether_aton(char *, u_char *);
|
||||
static void usage(void);
|
||||
static int rtmsg(int);
|
||||
static void ifinfo(char *, int, char **);
|
||||
static void rtrlist(void);
|
||||
static void plist(void);
|
||||
|
@ -149,8 +147,11 @@ static void harmonize_rtr(void);
|
|||
static void getdefif(void);
|
||||
static void setdefif(char *);
|
||||
#endif
|
||||
static char *sec2str(time_t);
|
||||
static void ts_print(const struct timeval *);
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
static void getsocket(void);
|
||||
static int rtmsg(int);
|
||||
#endif
|
||||
|
||||
static const char *rtpref_str[] = {
|
||||
"medium", /* 00 */
|
||||
|
@ -159,8 +160,27 @@ static const char *rtpref_str[] = {
|
|||
"low" /* 11 */
|
||||
};
|
||||
|
||||
struct ndp_opts opts = {};
|
||||
|
||||
#define NDP_XO_VERSION "1"
|
||||
|
||||
bool
|
||||
valid_type(int if_type)
|
||||
{
|
||||
switch (if_type) {
|
||||
case IFT_ETHER:
|
||||
case IFT_FDDI:
|
||||
case IFT_ISO88023:
|
||||
case IFT_ISO88024:
|
||||
case IFT_ISO88025:
|
||||
case IFT_L2VLAN:
|
||||
case IFT_BRIDGE:
|
||||
return (true);
|
||||
break;
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
|
@ -206,10 +226,10 @@ main(int argc, char **argv)
|
|||
arg = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
nflag = 1;
|
||||
opts.nflag = true;
|
||||
break;
|
||||
case 't':
|
||||
tflag = 1;
|
||||
opts.tflag = true;
|
||||
break;
|
||||
case 'A':
|
||||
if (mode) {
|
||||
|
@ -385,12 +405,12 @@ static struct sockaddr_dl blank_sdl = {
|
|||
.sdl_family = AF_LINK
|
||||
};
|
||||
static struct sockaddr_dl sdl_m;
|
||||
static time_t expire_time;
|
||||
static int flags, found_entry;
|
||||
#ifdef WITHOUT_NETLINK
|
||||
static struct {
|
||||
struct rt_msghdr m_rtm;
|
||||
char m_space[512];
|
||||
} m_rtmsg;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set an individual neighbor cache entry
|
||||
|
@ -398,44 +418,44 @@ static struct {
|
|||
static int
|
||||
set(int argc, char **argv)
|
||||
{
|
||||
register struct sockaddr_in6 *sin = &sin_m;
|
||||
register struct sockaddr_dl *sdl;
|
||||
register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
|
||||
struct addrinfo hints, *res;
|
||||
struct sockaddr_in6 *sin = &sin_m;
|
||||
int gai_error;
|
||||
u_char *ea;
|
||||
char *host = argv[0], *eaddr = argv[1];
|
||||
|
||||
getsocket();
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
sdl_m = blank_sdl;
|
||||
sin_m = blank_sin;
|
||||
|
||||
bzero(&hints, sizeof(hints));
|
||||
hints.ai_family = AF_INET6;
|
||||
gai_error = getaddrinfo(host, NULL, &hints, &res);
|
||||
gai_error = getaddr(host, sin);
|
||||
if (gai_error) {
|
||||
xo_warnx("%s: %s", host, gai_strerror(gai_error));
|
||||
return 1;
|
||||
}
|
||||
sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
|
||||
sin->sin6_scope_id =
|
||||
((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
|
||||
|
||||
ea = (u_char *)LLADDR(&sdl_m);
|
||||
if (ndp_ether_aton(eaddr, ea) == 0)
|
||||
sdl_m.sdl_alen = 6;
|
||||
flags = expire_time = 0;
|
||||
while (argc-- > 0) {
|
||||
if (strncmp(argv[0], "temp", 4) == 0) {
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, 0);
|
||||
expire_time = now.tv_sec + 20 * 60;
|
||||
opts.expire_time = now.tv_sec + 20 * 60;
|
||||
} else if (strncmp(argv[0], "proxy", 5) == 0)
|
||||
flags |= RTF_ANNOUNCE;
|
||||
opts.flags |= RTF_ANNOUNCE;
|
||||
argv++;
|
||||
}
|
||||
|
||||
#ifndef WITHOUT_NETLINK
|
||||
return (set_nl(0, sin, &sdl_m, host));
|
||||
#else
|
||||
struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
|
||||
struct sockaddr_dl *sdl;
|
||||
|
||||
getsocket();
|
||||
|
||||
if (rtmsg(RTM_GET) < 0) {
|
||||
xo_errx(1, "RTM_GET(%s) failed", host);
|
||||
/* NOTREACHED */
|
||||
|
@ -445,12 +465,8 @@ set(int argc, char **argv)
|
|||
if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
|
||||
if (sdl->sdl_family == AF_LINK &&
|
||||
!(rtm->rtm_flags & RTF_GATEWAY)) {
|
||||
switch (sdl->sdl_type) {
|
||||
case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
|
||||
case IFT_ISO88024: case IFT_ISO88025:
|
||||
case IFT_L2VLAN: case IFT_BRIDGE:
|
||||
if (valid_type(sdl->sdl_type))
|
||||
goto overwrite;
|
||||
}
|
||||
}
|
||||
xo_warnx("cannot configure a new entry");
|
||||
return 1;
|
||||
|
@ -464,6 +480,24 @@ set(int argc, char **argv)
|
|||
sdl_m.sdl_type = sdl->sdl_type;
|
||||
sdl_m.sdl_index = sdl->sdl_index;
|
||||
return (rtmsg(RTM_ADD));
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
getaddr(char *host, struct sockaddr_in6 *sin6)
|
||||
{
|
||||
struct addrinfo hints = { .ai_family = AF_INET6 };
|
||||
struct addrinfo *res;
|
||||
|
||||
int gai_error = getaddrinfo(host, NULL, &hints, &res);
|
||||
if (gai_error != 0)
|
||||
return (gai_error);
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_len = sizeof(*sin6);
|
||||
sin6->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
|
||||
sin6->sin6_scope_id =
|
||||
((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -473,55 +507,45 @@ static void
|
|||
get(char *host)
|
||||
{
|
||||
struct sockaddr_in6 *sin = &sin_m;
|
||||
struct addrinfo hints, *res;
|
||||
int gai_error;
|
||||
|
||||
sin_m = blank_sin;
|
||||
bzero(&hints, sizeof(hints));
|
||||
hints.ai_family = AF_INET6;
|
||||
gai_error = getaddrinfo(host, NULL, &hints, &res);
|
||||
|
||||
gai_error = getaddr(host, sin);
|
||||
if (gai_error) {
|
||||
xo_warnx("%s: %s", host, gai_strerror(gai_error));
|
||||
return;
|
||||
}
|
||||
sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
|
||||
sin->sin6_scope_id =
|
||||
((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
|
||||
dump(sin, 0);
|
||||
if (found_entry == 0) {
|
||||
if (dump(sin, 0) == 0) {
|
||||
getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
|
||||
sizeof(host_buf), NULL ,0,
|
||||
(nflag ? NI_NUMERICHOST : 0));
|
||||
(opts.nflag ? NI_NUMERICHOST : 0));
|
||||
xo_errx(1, "%s (%s) -- no entry", host, host_buf);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
/*
|
||||
* Delete a neighbor cache entry
|
||||
*/
|
||||
static int
|
||||
delete(char *host)
|
||||
delete_rtsock(char *host)
|
||||
{
|
||||
struct sockaddr_in6 *sin = &sin_m;
|
||||
register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
|
||||
register char *cp = m_rtmsg.m_space;
|
||||
struct sockaddr_dl *sdl;
|
||||
struct addrinfo hints, *res;
|
||||
int gai_error;
|
||||
|
||||
getsocket();
|
||||
sin_m = blank_sin;
|
||||
|
||||
bzero(&hints, sizeof(hints));
|
||||
hints.ai_family = AF_INET6;
|
||||
gai_error = getaddrinfo(host, NULL, &hints, &res);
|
||||
gai_error = getaddr(host, sin);
|
||||
if (gai_error) {
|
||||
xo_warnx("%s: %s", host, gai_strerror(gai_error));
|
||||
return 1;
|
||||
}
|
||||
sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
|
||||
sin->sin6_scope_id =
|
||||
((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
|
||||
|
||||
if (rtmsg(RTM_GET) < 0) {
|
||||
xo_errx(1, "RTM_GET(%s) failed", host);
|
||||
/* NOTREACHED */
|
||||
|
@ -552,7 +576,7 @@ delete(char *host)
|
|||
getnameinfo((struct sockaddr *)sin,
|
||||
sin->sin6_len, host_buf,
|
||||
sizeof(host_buf), NULL, 0,
|
||||
(nflag ? NI_NUMERICHOST : 0));
|
||||
(opts.nflag ? NI_NUMERICHOST : 0));
|
||||
xo_open_instance("neighbor-cache");
|
||||
|
||||
char *ifname = if_indextoname(sdl->sdl_index, ifix_buf);
|
||||
|
@ -571,15 +595,11 @@ delete(char *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define W_ADDR 36
|
||||
#define W_LL 17
|
||||
#define W_IF 6
|
||||
|
||||
/*
|
||||
* Dump the entire neighbor cache
|
||||
*/
|
||||
static void
|
||||
dump(struct sockaddr_in6 *addr, int cflag)
|
||||
static int
|
||||
dump_rtsock(struct sockaddr_in6 *addr, int cflag)
|
||||
{
|
||||
int mib[6];
|
||||
size_t needed;
|
||||
|
@ -596,7 +616,7 @@ dump(struct sockaddr_in6 *addr, int cflag)
|
|||
char *ifname;
|
||||
|
||||
/* Print header */
|
||||
if (!tflag && !cflag) {
|
||||
if (!opts.tflag && !cflag) {
|
||||
char xobuf[200];
|
||||
snprintf(xobuf, sizeof(xobuf),
|
||||
"{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n",
|
||||
|
@ -626,6 +646,7 @@ again:;
|
|||
} else
|
||||
buf = lim = NULL;
|
||||
|
||||
int count = 0;
|
||||
for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
|
||||
int isrouter = 0, prbs = 0;
|
||||
|
||||
|
@ -658,9 +679,9 @@ again:;
|
|||
&sin->sin6_addr) == 0 ||
|
||||
addr->sin6_scope_id != sin->sin6_scope_id)
|
||||
continue;
|
||||
found_entry = 1;
|
||||
} else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
|
||||
continue;
|
||||
count++;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
|
||||
IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
|
||||
/* XXX: should scope id be filled in the kernel? */
|
||||
|
@ -668,7 +689,7 @@ again:;
|
|||
sin->sin6_scope_id = sdl->sdl_index;
|
||||
}
|
||||
getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
|
||||
sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0));
|
||||
sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0));
|
||||
if (cflag) {
|
||||
#ifdef RTF_WASCLONED
|
||||
if (rtm->rtm_flags & RTF_WASCLONED)
|
||||
|
@ -684,7 +705,7 @@ again:;
|
|||
continue;
|
||||
}
|
||||
gettimeofday(&now, 0);
|
||||
if (tflag)
|
||||
if (opts.tflag)
|
||||
ts_print(&now);
|
||||
|
||||
addrwidth = strlen(host_buf);
|
||||
|
@ -795,6 +816,30 @@ again:;
|
|||
}
|
||||
|
||||
xo_close_list("neighbor-cache");
|
||||
|
||||
return (count);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
delete(char *host)
|
||||
{
|
||||
#ifndef WITHOUT_NETLINK
|
||||
return (delete_nl(0, host));
|
||||
#else
|
||||
return (delete_rtsock(host));
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
dump(struct sockaddr_in6 *addr, int cflag)
|
||||
{
|
||||
#ifndef WITHOUT_NETLINK
|
||||
return (print_entries_nl(0, addr, cflag));
|
||||
#else
|
||||
return (dump_rtsock(addr, cflag));
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct in6_nbrinfo *
|
||||
|
@ -820,7 +865,7 @@ getnbrinfo(struct in6_addr *addr, int ifindex, int warning)
|
|||
return(&nbi);
|
||||
}
|
||||
|
||||
static char *
|
||||
char *
|
||||
ether_str(struct sockaddr_dl *sdl)
|
||||
{
|
||||
static char hbuf[NI_MAXHOST];
|
||||
|
@ -869,6 +914,7 @@ usage(void)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
static int
|
||||
rtmsg(int cmd)
|
||||
{
|
||||
|
@ -882,7 +928,7 @@ rtmsg(int cmd)
|
|||
if (cmd == RTM_DELETE)
|
||||
goto doit;
|
||||
bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
|
||||
rtm->rtm_flags = flags;
|
||||
rtm->rtm_flags = opts.flags;
|
||||
rtm->rtm_version = RTM_VERSION;
|
||||
|
||||
switch (cmd) {
|
||||
|
@ -890,8 +936,8 @@ rtmsg(int cmd)
|
|||
xo_errx(1, "internal wrong cmd");
|
||||
case RTM_ADD:
|
||||
rtm->rtm_addrs |= RTA_GATEWAY;
|
||||
if (expire_time) {
|
||||
rtm->rtm_rmx.rmx_expire = expire_time;
|
||||
if (opts.expire_time) {
|
||||
rtm->rtm_rmx.rmx_expire = opts.expire_time;
|
||||
rtm->rtm_inits = RTV_EXPIRE;
|
||||
}
|
||||
rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
|
||||
|
@ -922,6 +968,7 @@ rtmsg(int cmd)
|
|||
xo_warn("read from routing socket");
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
ifinfo(char *ifname, int argc, char **argv)
|
||||
|
@ -1129,9 +1176,9 @@ rtrlist(void)
|
|||
|
||||
if (getnameinfo((struct sockaddr *)&p->rtaddr,
|
||||
p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0,
|
||||
(nflag ? NI_NUMERICHOST : 0)) != 0)
|
||||
(opts.nflag ? NI_NUMERICHOST : 0)) != 0)
|
||||
strlcpy(host_buf, "?", sizeof(host_buf));
|
||||
if (nflag)
|
||||
if (opts.nflag)
|
||||
paddr = host_buf;
|
||||
else {
|
||||
inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, abuf, sizeof(abuf));
|
||||
|
@ -1187,7 +1234,7 @@ plist(void)
|
|||
size_t l;
|
||||
struct timeval now;
|
||||
const int niflags = NI_NUMERICHOST;
|
||||
int ninflags = nflag ? NI_NUMERICHOST : 0;
|
||||
int ninflags = opts.nflag ? NI_NUMERICHOST : 0;
|
||||
char namebuf[NI_MAXHOST];
|
||||
|
||||
if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
|
||||
|
@ -1430,7 +1477,7 @@ getdefif(void)
|
|||
}
|
||||
#endif /* SIOCSDEFIFACE_IN6 */
|
||||
|
||||
static char *
|
||||
char *
|
||||
sec2str(time_t total)
|
||||
{
|
||||
static char result[256];
|
||||
|
@ -1475,7 +1522,7 @@ sec2str(time_t total)
|
|||
* Print the timestamp
|
||||
* from tcpdump/util.c
|
||||
*/
|
||||
static void
|
||||
void
|
||||
ts_print(const struct timeval *tvp)
|
||||
{
|
||||
int sec;
|
||||
|
|
27
usr.sbin/ndp/ndp.h
Normal file
27
usr.sbin/ndp/ndp.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef _USR_SBIN_NDP_NDP_H_
|
||||
#define _USR_SBIN_NDP_NDP_H_
|
||||
|
||||
#define W_ADDR 36
|
||||
#define W_LL 17
|
||||
#define W_IF 6
|
||||
|
||||
struct ndp_opts {
|
||||
bool nflag;
|
||||
bool tflag;
|
||||
int flags;
|
||||
time_t expire_time;
|
||||
};
|
||||
|
||||
extern struct ndp_opts opts;
|
||||
|
||||
bool valid_type(int if_type);
|
||||
void ts_print(const struct timeval *tvp);
|
||||
char *ether_str(struct sockaddr_dl *sdl);
|
||||
char *sec2str(time_t total);
|
||||
int getaddr(char *host, struct sockaddr_in6 *sin6);
|
||||
int print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag);
|
||||
int delete_nl(uint32_t ifindex, char *host);
|
||||
int set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl,
|
||||
char *host);
|
||||
|
||||
#endif
|
511
usr.sbin/ndp/ndp_netlink.c
Normal file
511
usr.sbin/ndp/ndp_netlink.c
Normal file
|
@ -0,0 +1,511 @@
|
|||
#include <sys/param.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/linker.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_types.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet6/in6_var.h>
|
||||
#include <netinet6/nd6.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <nlist.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <paths.h>
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <libxo/xo.h>
|
||||
#include "gmt2local.h"
|
||||
|
||||
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/netlink_route.h>
|
||||
#include <netlink/netlink_snl.h>
|
||||
#include <netlink/netlink_snl_route.h>
|
||||
#include <netlink/netlink_snl_route_compat.h>
|
||||
#include <netlink/netlink_snl_route_parsers.h>
|
||||
|
||||
#include <libxo/xo.h>
|
||||
#include "ndp.h"
|
||||
|
||||
#define RTF_ANNOUNCE RTF_PROTO2
|
||||
|
||||
static void
|
||||
nl_init_socket(struct snl_state *ss)
|
||||
{
|
||||
if (snl_init(ss, NETLINK_ROUTE))
|
||||
return;
|
||||
|
||||
if (modfind("netlink") == -1 && errno == ENOENT) {
|
||||
/* Try to load */
|
||||
if (kldload("netlink") == -1)
|
||||
err(1, "netlink is not loaded and load attempt failed");
|
||||
if (snl_init(ss, NETLINK_ROUTE))
|
||||
return;
|
||||
}
|
||||
|
||||
err(1, "unable to open netlink socket");
|
||||
}
|
||||
|
||||
static bool
|
||||
get_link_info(struct snl_state *ss, uint32_t ifindex,
|
||||
struct snl_parsed_link_simple *link)
|
||||
{
|
||||
struct snl_writer nw;
|
||||
|
||||
snl_init_writer(ss, &nw);
|
||||
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
|
||||
struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
|
||||
if (ifmsg != NULL)
|
||||
ifmsg->ifi_index = ifindex;
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
|
||||
return (false);
|
||||
|
||||
hdr = snl_read_reply(ss, hdr->nlmsg_seq);
|
||||
|
||||
if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK)
|
||||
return (false);
|
||||
|
||||
if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link))
|
||||
return (false);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool
|
||||
has_l2(struct snl_state *ss, uint32_t ifindex)
|
||||
{
|
||||
struct snl_parsed_link_simple link = {};
|
||||
|
||||
if (!get_link_info(ss, ifindex, &link))
|
||||
return (false);
|
||||
|
||||
return (valid_type(link.ifi_type) != 0);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
get_myfib()
|
||||
{
|
||||
uint32_t fibnum = 0;
|
||||
size_t len = sizeof(fibnum);
|
||||
|
||||
sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0);
|
||||
|
||||
return (fibnum);
|
||||
}
|
||||
|
||||
static void
|
||||
ip6_writemask(struct in6_addr *addr6, uint8_t mask)
|
||||
{
|
||||
uint32_t *cp;
|
||||
|
||||
for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
|
||||
*cp++ = 0xFFFFFFFF;
|
||||
if (mask > 0)
|
||||
*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
|
||||
}
|
||||
#define s6_addr32 __u6_addr.__u6_addr32
|
||||
#define IN6_MASK_ADDR(a, m) do { \
|
||||
(a)->s6_addr32[0] &= (m)->s6_addr32[0]; \
|
||||
(a)->s6_addr32[1] &= (m)->s6_addr32[1]; \
|
||||
(a)->s6_addr32[2] &= (m)->s6_addr32[2]; \
|
||||
(a)->s6_addr32[3] &= (m)->s6_addr32[3]; \
|
||||
} while (0)
|
||||
|
||||
static int
|
||||
guess_ifindex(struct snl_state *ss, uint32_t fibnum, const struct sockaddr_in6 *dst)
|
||||
{
|
||||
struct snl_writer nw;
|
||||
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr))
|
||||
return (dst->sin6_scope_id);
|
||||
else if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
|
||||
return (0);
|
||||
|
||||
|
||||
snl_init_writer(ss, &nw);
|
||||
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE);
|
||||
struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
|
||||
rtm->rtm_family = AF_INET6;
|
||||
|
||||
snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)dst);
|
||||
snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum);
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
|
||||
return (0);
|
||||
|
||||
hdr = snl_read_reply(ss, hdr->nlmsg_seq);
|
||||
|
||||
if (hdr->nlmsg_type != NL_RTM_NEWROUTE) {
|
||||
/* No route found, unable to guess ifindex */
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct snl_parsed_route r = {};
|
||||
if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
|
||||
return (0);
|
||||
|
||||
if (r.rta_multipath || (r.rta_rtflags & RTF_GATEWAY))
|
||||
return (0);
|
||||
|
||||
/* Check if the interface is of supported type */
|
||||
if (has_l2(ss, r.rta_oif))
|
||||
return (r.rta_oif);
|
||||
|
||||
/* Check the case when we matched the loopback route for P2P */
|
||||
snl_init_writer(ss, &nw);
|
||||
hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP);
|
||||
snl_reserve_msg_object(&nw, struct nhmsg);
|
||||
|
||||
int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD);
|
||||
snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id);
|
||||
snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET);
|
||||
snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum);
|
||||
snl_end_attr_nested(&nw, off);
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
|
||||
return (0);
|
||||
|
||||
hdr = snl_read_reply(ss, hdr->nlmsg_seq);
|
||||
|
||||
if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) {
|
||||
/* No nexthop found, unable to guess ifindex */
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct snl_parsed_nhop nh = {};
|
||||
if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh))
|
||||
return (0);
|
||||
|
||||
return (nh.nhaf_aif);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
fix_ifindex(struct snl_state *ss, uint32_t ifindex, const struct sockaddr_in6 *sa)
|
||||
{
|
||||
if (ifindex == 0)
|
||||
ifindex = guess_ifindex(ss, get_myfib(), sa);
|
||||
return (ifindex);
|
||||
}
|
||||
|
||||
static void
|
||||
print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link)
|
||||
{
|
||||
struct timeval now;
|
||||
char host_buf[NI_MAXHOST];
|
||||
int addrwidth;
|
||||
int llwidth;
|
||||
int ifwidth;
|
||||
char *ifname;
|
||||
|
||||
getnameinfo(neigh->nda_dst, sizeof(struct sockaddr_in6), host_buf,
|
||||
sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0));
|
||||
|
||||
gettimeofday(&now, 0);
|
||||
if (opts.tflag)
|
||||
ts_print(&now);
|
||||
|
||||
struct sockaddr_dl sdl = {
|
||||
.sdl_family = AF_LINK,
|
||||
.sdl_type = link->ifi_type,
|
||||
.sdl_len = sizeof(struct sockaddr_dl),
|
||||
.sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr),
|
||||
};
|
||||
memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen);
|
||||
|
||||
addrwidth = strlen(host_buf);
|
||||
if (addrwidth < W_ADDR)
|
||||
addrwidth = W_ADDR;
|
||||
llwidth = strlen(ether_str(&sdl));
|
||||
if (W_ADDR + W_LL - addrwidth > llwidth)
|
||||
llwidth = W_ADDR + W_LL - addrwidth;
|
||||
ifname = link->ifla_ifname;
|
||||
ifwidth = strlen(ifname);
|
||||
if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
|
||||
ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
|
||||
|
||||
xo_open_instance("neighbor-cache");
|
||||
/* Compose format string for libxo, as it doesn't support *.* */
|
||||
char xobuf[200];
|
||||
snprintf(xobuf, sizeof(xobuf),
|
||||
"{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}",
|
||||
addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth);
|
||||
xo_emit(xobuf, host_buf, ether_str(&sdl), ifname);
|
||||
|
||||
/* Print neighbor discovery specific information */
|
||||
uint32_t expire = neigh->ndaf_next_ts;
|
||||
int expire_in = expire - now.tv_sec;
|
||||
if (expire > now.tv_sec)
|
||||
xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in);
|
||||
else if (expire == 0)
|
||||
xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent");
|
||||
else
|
||||
xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in);
|
||||
|
||||
const char *lle_state = "";
|
||||
switch (neigh->ndm_state) {
|
||||
case NUD_INCOMPLETE:
|
||||
lle_state = "I";
|
||||
break;
|
||||
case NUD_REACHABLE:
|
||||
lle_state = "R";
|
||||
break;
|
||||
case NUD_STALE:
|
||||
lle_state = "S";
|
||||
break;
|
||||
case NUD_DELAY:
|
||||
lle_state = "D";
|
||||
break;
|
||||
case NUD_PROBE:
|
||||
lle_state = "P";
|
||||
break;
|
||||
case NUD_FAILED:
|
||||
lle_state = "F";
|
||||
break;
|
||||
default:
|
||||
lle_state = "N";
|
||||
break;
|
||||
}
|
||||
xo_emit(" {:neighbor-state/%s}", lle_state);
|
||||
|
||||
bool isrouter = neigh->ndm_flags & NTF_ROUTER;
|
||||
|
||||
/*
|
||||
* other flags. R: router, P: proxy, W: ??
|
||||
*/
|
||||
char flgbuf[8];
|
||||
snprintf(flgbuf, sizeof(flgbuf), "%s%s",
|
||||
isrouter ? "R" : "",
|
||||
(neigh->ndm_flags & NTF_PROXY) ? "p" : "");
|
||||
xo_emit(" {:nd-flags/%s}", flgbuf);
|
||||
|
||||
if (neigh->nda_probes != 0)
|
||||
xo_emit("{u:/ %d}", neigh->nda_probes);
|
||||
|
||||
xo_emit("\n");
|
||||
xo_close_instance("neighbor-cache");
|
||||
}
|
||||
|
||||
int
|
||||
print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag)
|
||||
{
|
||||
struct snl_state ss_req = {}, ss_cmd = {};
|
||||
struct snl_parsed_link_simple link = {};
|
||||
struct snl_writer nw;
|
||||
|
||||
nl_init_socket(&ss_req);
|
||||
snl_init_writer(&ss_req, &nw);
|
||||
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH);
|
||||
struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
|
||||
if (ndmsg != NULL) {
|
||||
ndmsg->ndm_family = AF_INET6;
|
||||
ndmsg->ndm_ifindex = ifindex;
|
||||
}
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(&ss_req, hdr)) {
|
||||
snl_free(&ss_req);
|
||||
return (0);
|
||||
}
|
||||
|
||||
uint32_t nlmsg_seq = hdr->nlmsg_seq;
|
||||
struct snl_errmsg_data e = {};
|
||||
int count = 0;
|
||||
nl_init_socket(&ss_cmd);
|
||||
|
||||
/* Print header */
|
||||
if (!opts.tflag && !cflag) {
|
||||
char xobuf[200];
|
||||
snprintf(xobuf, sizeof(xobuf),
|
||||
"{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n",
|
||||
W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF);
|
||||
xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags");
|
||||
}
|
||||
xo_open_list("neighbor-cache");
|
||||
|
||||
while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) {
|
||||
struct snl_parsed_neigh neigh = {};
|
||||
|
||||
if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh))
|
||||
continue;
|
||||
|
||||
if (neigh.nda_ifindex != link.ifi_index) {
|
||||
snl_clear_lb(&ss_cmd);
|
||||
memset(&link, 0, sizeof(link));
|
||||
if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* TODO: embed LL in the parser */
|
||||
struct sockaddr_in6 *dst = (struct sockaddr_in6 *)neigh.nda_dst;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr))
|
||||
dst->sin6_scope_id = neigh.nda_ifindex;
|
||||
|
||||
if (addr != NULL) {
|
||||
if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
|
||||
&dst->sin6_addr) == 0 ||
|
||||
addr->sin6_scope_id != dst->sin6_scope_id)
|
||||
continue;
|
||||
}
|
||||
|
||||
print_entry(&neigh, &link);
|
||||
if (cflag) {
|
||||
char dst_str[INET6_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET6, &dst->sin6_addr, dst_str, sizeof(dst_str));
|
||||
delete_nl(neigh.nda_ifindex, dst_str);
|
||||
}
|
||||
count++;
|
||||
snl_clear_lb(&ss_req);
|
||||
}
|
||||
xo_close_list("neighbor-cache");
|
||||
|
||||
snl_free(&ss_req);
|
||||
snl_free(&ss_cmd);
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
int
|
||||
delete_nl(uint32_t ifindex, char *host)
|
||||
{
|
||||
struct snl_state ss = {};
|
||||
struct snl_writer nw;
|
||||
struct sockaddr_in6 dst;
|
||||
|
||||
int gai_error = getaddr(host, &dst);
|
||||
if (gai_error) {
|
||||
xo_warnx("%s: %s", host, gai_strerror(gai_error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
nl_init_socket(&ss);
|
||||
|
||||
ifindex = fix_ifindex(&ss, ifindex, &dst);
|
||||
if (ifindex == 0) {
|
||||
xo_warnx("delete: cannot locate %s", host);
|
||||
snl_free(&ss);
|
||||
return (0);
|
||||
}
|
||||
|
||||
snl_init_writer(&ss, &nw);
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH);
|
||||
struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
|
||||
if (ndmsg != NULL) {
|
||||
ndmsg->ndm_family = AF_INET6;
|
||||
ndmsg->ndm_ifindex = ifindex;
|
||||
}
|
||||
snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst);
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) {
|
||||
snl_free(&ss);
|
||||
return (1);
|
||||
}
|
||||
|
||||
struct snl_errmsg_data e = {};
|
||||
snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
|
||||
if (e.error != 0) {
|
||||
if (e.error_str != NULL)
|
||||
xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str);
|
||||
else
|
||||
xo_warnx("delete %s: %s", host, strerror(e.error));
|
||||
} else {
|
||||
char host_buf[NI_MAXHOST];
|
||||
char ifix_buf[IFNAMSIZ];
|
||||
|
||||
getnameinfo((struct sockaddr *)&dst,
|
||||
dst.sin6_len, host_buf,
|
||||
sizeof(host_buf), NULL, 0,
|
||||
(opts.nflag ? NI_NUMERICHOST : 0));
|
||||
|
||||
char *ifname = if_indextoname(ifindex, ifix_buf);
|
||||
if (ifname == NULL) {
|
||||
strlcpy(ifix_buf, "?", sizeof(ifix_buf));
|
||||
ifname = ifix_buf;
|
||||
}
|
||||
char abuf[INET6_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET6, &dst.sin6_addr, abuf, sizeof(abuf));
|
||||
|
||||
xo_open_instance("neighbor-cache");
|
||||
xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf);
|
||||
xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname);
|
||||
xo_close_instance("neighbor-cache");
|
||||
}
|
||||
snl_free(&ss);
|
||||
|
||||
return (e.error != 0);
|
||||
}
|
||||
|
||||
int
|
||||
set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char *host)
|
||||
{
|
||||
struct snl_state ss = {};
|
||||
struct snl_writer nw;
|
||||
|
||||
nl_init_socket(&ss);
|
||||
|
||||
ifindex = fix_ifindex(&ss, ifindex, dst);
|
||||
if (ifindex == 0) {
|
||||
xo_warnx("delete: cannot locate %s", host);
|
||||
snl_free(&ss);
|
||||
return (0);
|
||||
}
|
||||
|
||||
snl_init_writer(&ss, &nw);
|
||||
struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
|
||||
hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
|
||||
struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
|
||||
if (ndmsg != NULL) {
|
||||
uint8_t nl_flags = NTF_STICKY;
|
||||
|
||||
ndmsg->ndm_family = AF_INET6;
|
||||
ndmsg->ndm_ifindex = ifindex;
|
||||
ndmsg->ndm_state = NUD_PERMANENT;
|
||||
|
||||
if (opts.flags & RTF_ANNOUNCE)
|
||||
nl_flags |= NTF_PROXY;
|
||||
ndmsg->ndm_flags = nl_flags;
|
||||
}
|
||||
snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
|
||||
snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl));
|
||||
|
||||
if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) {
|
||||
snl_free(&ss);
|
||||
return (1);
|
||||
}
|
||||
|
||||
struct snl_errmsg_data e = {};
|
||||
snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
|
||||
if (e.error != 0) {
|
||||
if (e.error_str != NULL)
|
||||
xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
|
||||
else
|
||||
xo_warnx("set %s: %s", host, strerror(e.error));
|
||||
}
|
||||
snl_free(&ss);
|
||||
|
||||
return (e.error != 0);
|
||||
}
|
||||
|
Loading…
Reference in a new issue