arp: convert arp(8) to netlink.
The change is intended to be fully transparent to the users. Similarly to route(8) and netstat(8), arp 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
e83f23eb5e
commit
6ad73dbf65
@ -1,9 +1,20 @@
|
||||
# @(#)Makefile 8.2 (Berkeley) 4/18/94
|
||||
# $FreeBSD$
|
||||
|
||||
.include <src.opts.mk>
|
||||
|
||||
PROG= arp
|
||||
MAN= arp.4 arp.8
|
||||
|
||||
|
||||
SRCS= arp.c
|
||||
|
||||
.if ${MK_NETLINK_SUPPORT} != "no"
|
||||
SRCS+= arp_netlink.c
|
||||
.else
|
||||
CFLAGS+=-DWITHOUT_NETLINK
|
||||
.endif
|
||||
|
||||
LIBADD= xo
|
||||
|
||||
WARNS?= 3
|
||||
|
@ -74,6 +74,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <netdb.h>
|
||||
#include <nlist.h>
|
||||
#include <paths.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -81,13 +82,12 @@ __FBSDID("$FreeBSD$");
|
||||
#include <unistd.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <libxo/xo.h>
|
||||
#include "arp.h"
|
||||
|
||||
typedef void (action_fn)(struct sockaddr_dl *sdl, struct sockaddr_in *s_in,
|
||||
struct rt_msghdr *rtm);
|
||||
|
||||
static int search(u_long addr, action_fn *action);
|
||||
static action_fn print_entry;
|
||||
static action_fn nuke_entry;
|
||||
static void nuke_entries(uint32_t ifindex, struct in_addr addr);
|
||||
static int print_entries(uint32_t ifindex, struct in_addr addr);
|
||||
|
||||
static int delete(char *host);
|
||||
static void usage(void);
|
||||
@ -97,17 +97,15 @@ static int file(char *name);
|
||||
static struct rt_msghdr *rtmsg(int cmd,
|
||||
struct sockaddr_in *dst, struct sockaddr_dl *sdl);
|
||||
static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
|
||||
static struct sockaddr_in *getaddr(char *host);
|
||||
static int valid_type(int type);
|
||||
static int set_rtsock(struct sockaddr_in *dst, struct sockaddr_dl *sdl_m,
|
||||
char *host);
|
||||
|
||||
static int nflag; /* no reverse dns lookups */
|
||||
static char *rifname;
|
||||
|
||||
static time_t expire_time;
|
||||
static int flags, doing_proxy;
|
||||
|
||||
struct if_nameindex *ifnameindex;
|
||||
|
||||
struct arp_opts opts = {};
|
||||
|
||||
/* which function we're supposed to do */
|
||||
#define F_GET 1
|
||||
#define F_SET 2
|
||||
@ -124,7 +122,6 @@ main(int argc, char *argv[])
|
||||
{
|
||||
int ch, func = 0;
|
||||
int rtn = 0;
|
||||
int aflag = 0; /* do it for all entries */
|
||||
|
||||
argc = xo_parse_args(argc, argv);
|
||||
if (argc < 0)
|
||||
@ -133,13 +130,13 @@ main(int argc, char *argv[])
|
||||
while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
|
||||
switch(ch) {
|
||||
case 'a':
|
||||
aflag = 1;
|
||||
opts.aflag = true;
|
||||
break;
|
||||
case 'd':
|
||||
SETFUNC(F_DELETE);
|
||||
break;
|
||||
case 'n':
|
||||
nflag = 1;
|
||||
opts.nflag = true;
|
||||
break;
|
||||
case 'S':
|
||||
SETFUNC(F_REPLACE);
|
||||
@ -163,7 +160,7 @@ main(int argc, char *argv[])
|
||||
if (!func)
|
||||
func = F_GET;
|
||||
if (rifname) {
|
||||
if (func != F_GET && !(func == F_DELETE && aflag))
|
||||
if (func != F_GET && !(func == F_DELETE && opts.aflag))
|
||||
xo_errx(1, "-i not applicable to this operation");
|
||||
if (if_nametoindex(rifname) == 0) {
|
||||
if (errno == ENXIO)
|
||||
@ -175,7 +172,7 @@ main(int argc, char *argv[])
|
||||
}
|
||||
switch (func) {
|
||||
case F_GET:
|
||||
if (aflag) {
|
||||
if (opts.aflag) {
|
||||
if (argc != 0)
|
||||
usage();
|
||||
|
||||
@ -183,7 +180,8 @@ main(int argc, char *argv[])
|
||||
xo_open_container("arp");
|
||||
xo_open_list("arp-cache");
|
||||
|
||||
search(0, print_entry);
|
||||
struct in_addr all_addrs = {};
|
||||
print_entries(0, all_addrs);
|
||||
|
||||
xo_close_list("arp-cache");
|
||||
xo_close_container("arp");
|
||||
@ -203,10 +201,11 @@ main(int argc, char *argv[])
|
||||
rtn = set(argc, argv) ? 1 : 0;
|
||||
break;
|
||||
case F_DELETE:
|
||||
if (aflag) {
|
||||
if (opts.aflag) {
|
||||
if (argc != 0)
|
||||
usage();
|
||||
search(0, nuke_entry);
|
||||
struct in_addr all_addrs = {};
|
||||
nuke_entries(0, all_addrs);
|
||||
} else {
|
||||
if (argc != 1)
|
||||
usage();
|
||||
@ -269,7 +268,7 @@ file(char *name)
|
||||
* the address of the host and returns a pointer to the
|
||||
* structure.
|
||||
*/
|
||||
static struct sockaddr_in *
|
||||
struct sockaddr_in *
|
||||
getaddr(char *host)
|
||||
{
|
||||
struct hostent *hp;
|
||||
@ -290,10 +289,11 @@ getaddr(char *host)
|
||||
return (&reply);
|
||||
}
|
||||
|
||||
int valid_type(int type);
|
||||
/*
|
||||
* Returns true if the type is a valid one for ARP.
|
||||
*/
|
||||
static int
|
||||
int
|
||||
valid_type(int type)
|
||||
{
|
||||
|
||||
@ -318,10 +318,7 @@ valid_type(int type)
|
||||
static int
|
||||
set(int argc, char **argv)
|
||||
{
|
||||
struct sockaddr_in *addr;
|
||||
struct sockaddr_in *dst; /* what are we looking for */
|
||||
struct sockaddr_dl *sdl;
|
||||
struct rt_msghdr *rtm;
|
||||
struct ether_addr *ea;
|
||||
char *host = argv[0], *eaddr = argv[1];
|
||||
struct sockaddr_dl sdl_m;
|
||||
@ -336,21 +333,17 @@ set(int argc, char **argv)
|
||||
dst = getaddr(host);
|
||||
if (dst == NULL)
|
||||
return (1);
|
||||
doing_proxy = flags = expire_time = 0;
|
||||
while (argc-- > 0) {
|
||||
if (strcmp(argv[0], "temp") == 0) {
|
||||
struct timespec tp;
|
||||
int max_age;
|
||||
size_t len = sizeof(max_age);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
if (sysctlbyname("net.link.ether.inet.max_age",
|
||||
&max_age, &len, NULL, 0) != 0)
|
||||
xo_err(1, "sysctlbyname");
|
||||
expire_time = tp.tv_sec + max_age;
|
||||
opts.expire_time = max_age;
|
||||
} else if (strcmp(argv[0], "pub") == 0) {
|
||||
flags |= RTF_ANNOUNCE;
|
||||
doing_proxy = 1;
|
||||
opts.flags |= RTF_ANNOUNCE;
|
||||
if (argc && strcmp(argv[1], "only") == 0) {
|
||||
/*
|
||||
* Compatibility: in pre FreeBSD 8 times
|
||||
@ -361,17 +354,17 @@ set(int argc, char **argv)
|
||||
argc--; argv++;
|
||||
}
|
||||
} else if (strcmp(argv[0], "blackhole") == 0) {
|
||||
if (flags & RTF_REJECT) {
|
||||
if (opts.flags & RTF_REJECT) {
|
||||
xo_errx(1, "Choose one of blackhole or reject, "
|
||||
"not both.");
|
||||
}
|
||||
flags |= RTF_BLACKHOLE;
|
||||
opts.flags |= RTF_BLACKHOLE;
|
||||
} else if (strcmp(argv[0], "reject") == 0) {
|
||||
if (flags & RTF_BLACKHOLE) {
|
||||
if (opts.flags & RTF_BLACKHOLE) {
|
||||
xo_errx(1, "Choose one of blackhole or reject, "
|
||||
"not both.");
|
||||
}
|
||||
flags |= RTF_REJECT;
|
||||
opts.flags |= RTF_REJECT;
|
||||
} else {
|
||||
xo_warnx("Invalid parameter '%s'", argv[0]);
|
||||
usage();
|
||||
@ -379,7 +372,7 @@ set(int argc, char **argv)
|
||||
argv++;
|
||||
}
|
||||
ea = (struct ether_addr *)LLADDR(&sdl_m);
|
||||
if (doing_proxy && !strcmp(eaddr, "auto")) {
|
||||
if ((opts.flags & RTF_ANNOUNCE) && !strcmp(eaddr, "auto")) {
|
||||
if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
|
||||
xo_warnx("no interface found for %s",
|
||||
inet_ntoa(dst->sin_addr));
|
||||
@ -397,6 +390,20 @@ set(int argc, char **argv)
|
||||
sdl_m.sdl_alen = ETHER_ADDR_LEN;
|
||||
}
|
||||
}
|
||||
#ifndef WITHOUT_NETLINK
|
||||
return (set_nl(0, dst, &sdl_m, host));
|
||||
#else
|
||||
return (set_rtsock(dst, &sdl_m, host));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITHOUT_NETLINK
|
||||
static int
|
||||
set_rtsock(struct sockaddr_in *dst, struct sockaddr_dl *sdl_m, char *host)
|
||||
{
|
||||
struct sockaddr_in *addr;
|
||||
struct sockaddr_dl *sdl;
|
||||
struct rt_msghdr *rtm;
|
||||
|
||||
/*
|
||||
* In the case a proxy-arp entry is being added for
|
||||
@ -420,10 +427,11 @@ set(int argc, char **argv)
|
||||
xo_warnx("cannot intuit interface index and type for %s", host);
|
||||
return (1);
|
||||
}
|
||||
sdl_m.sdl_type = sdl->sdl_type;
|
||||
sdl_m.sdl_index = sdl->sdl_index;
|
||||
return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
|
||||
sdl_m->sdl_type = sdl->sdl_type;
|
||||
sdl_m->sdl_index = sdl->sdl_index;
|
||||
return (rtmsg(RTM_ADD, dst, sdl_m) == NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Display an individual arp entry
|
||||
@ -442,7 +450,7 @@ get(char *host)
|
||||
xo_open_container("arp");
|
||||
xo_open_list("arp-cache");
|
||||
|
||||
found = search(addr->sin_addr.s_addr, print_entry);
|
||||
found = print_entries(0, addr->sin_addr);
|
||||
|
||||
if (found == 0) {
|
||||
xo_emit("{d:hostname/%s} ({d:ip-address/%s}) -- no entry",
|
||||
@ -462,8 +470,9 @@ get(char *host)
|
||||
/*
|
||||
* Delete an arp entry
|
||||
*/
|
||||
#ifdef WITHOUT_NETLINK
|
||||
static int
|
||||
delete(char *host)
|
||||
delete_rtsock(char *host)
|
||||
{
|
||||
struct sockaddr_in *addr, *dst;
|
||||
struct rt_msghdr *rtm;
|
||||
@ -476,7 +485,7 @@ delete(char *host)
|
||||
/*
|
||||
* Perform a regular entry delete first.
|
||||
*/
|
||||
flags &= ~RTF_ANNOUNCE;
|
||||
opts.flags &= ~RTF_ANNOUNCE;
|
||||
|
||||
for (;;) { /* try twice */
|
||||
rtm = rtmsg(RTM_GET, dst, NULL);
|
||||
@ -506,12 +515,12 @@ delete(char *host)
|
||||
* Regular entry delete failed, now check if there
|
||||
* is a proxy-arp entry to remove.
|
||||
*/
|
||||
if (flags & RTF_ANNOUNCE) {
|
||||
if (opts.flags & RTF_ANNOUNCE) {
|
||||
xo_warnx("delete: cannot locate %s", host);
|
||||
return (1);
|
||||
}
|
||||
|
||||
flags |= RTF_ANNOUNCE;
|
||||
opts.flags |= RTF_ANNOUNCE;
|
||||
}
|
||||
rtm->rtm_flags |= RTF_LLDATA;
|
||||
if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
|
||||
@ -520,6 +529,17 @@ delete(char *host)
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
delete(char *host)
|
||||
{
|
||||
#ifdef WITHOUT_NETLINK
|
||||
return (delete_rtsock(host));
|
||||
#else
|
||||
return (delete_nl(0, host));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@ -600,7 +620,7 @@ print_entry(struct sockaddr_dl *sdl,
|
||||
|
||||
xo_open_instance("arp-cache");
|
||||
|
||||
if (nflag == 0)
|
||||
if (!opts.nflag)
|
||||
hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
|
||||
sizeof addr->sin_addr, AF_INET);
|
||||
else
|
||||
@ -610,7 +630,7 @@ print_entry(struct sockaddr_dl *sdl,
|
||||
else {
|
||||
host = "?";
|
||||
if (h_errno == TRY_AGAIN)
|
||||
nflag = 1;
|
||||
opts.nflag = true;
|
||||
}
|
||||
xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host,
|
||||
inet_ntoa(addr->sin_addr));
|
||||
@ -640,6 +660,8 @@ print_entry(struct sockaddr_dl *sdl,
|
||||
xo_emit("{d:/ permanent}{en:permanent/true}");
|
||||
else {
|
||||
static struct timespec tp;
|
||||
time_t expire_time = 0;
|
||||
|
||||
if (tp.tv_sec == 0)
|
||||
clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0)
|
||||
@ -683,6 +705,17 @@ print_entry(struct sockaddr_dl *sdl,
|
||||
xo_close_instance("arp-cache");
|
||||
}
|
||||
|
||||
static int
|
||||
print_entries(uint32_t ifindex, struct in_addr addr)
|
||||
{
|
||||
#ifndef WITHOUT_NETLINK
|
||||
return (print_entries_nl(ifindex, addr));
|
||||
#else
|
||||
return (search(addr.s_addr, print_entry));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Nuke an arp entry
|
||||
*/
|
||||
@ -699,6 +732,12 @@ nuke_entry(struct sockaddr_dl *sdl __unused,
|
||||
delete(ip);
|
||||
}
|
||||
|
||||
static void
|
||||
nuke_entries(uint32_t ifindex, struct in_addr addr)
|
||||
{
|
||||
search(addr.s_addr, nuke_entry);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
@ -745,7 +784,7 @@ rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl)
|
||||
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) {
|
||||
@ -753,7 +792,12 @@ rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl)
|
||||
xo_errx(1, "internal wrong cmd");
|
||||
case RTM_ADD:
|
||||
rtm->rtm_addrs |= RTA_GATEWAY;
|
||||
rtm->rtm_rmx.rmx_expire = expire_time;
|
||||
if (opts.expire_time != 0) {
|
||||
struct timespec tp;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
rtm->rtm_rmx.rmx_expire = opts.expire_time + tp.tv_sec;
|
||||
}
|
||||
rtm->rtm_inits = RTV_EXPIRE;
|
||||
rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
|
||||
/* FALLTHROUGH */
|
||||
|
21
usr.sbin/arp/arp.h
Normal file
21
usr.sbin/arp/arp.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef _USR_SBIN_ARP_ARP_H_
|
||||
#define _USR_SBIN_ARP_ARP_H_
|
||||
|
||||
int valid_type(int type);
|
||||
struct sockaddr_in *getaddr(char *host);
|
||||
int print_entries_nl(uint32_t ifindex, struct in_addr addr);
|
||||
|
||||
struct arp_opts {
|
||||
bool aflag;
|
||||
bool nflag;
|
||||
time_t expire_time;
|
||||
int flags;
|
||||
};
|
||||
extern struct arp_opts opts;
|
||||
|
||||
int print_entries_nl(uint32_t ifindex, struct in_addr addr);
|
||||
int delete_nl(uint32_t ifindex, char *host);
|
||||
int set_nl(uint32_t ifindex, struct sockaddr_in *dst, struct sockaddr_dl *sdl,
|
||||
char *host);
|
||||
|
||||
#endif
|
433
usr.sbin/arp/arp_netlink.c
Normal file
433
usr.sbin/arp/arp_netlink.c
Normal file
@ -0,0 +1,433 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <sys/bitcount.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/linker.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_types.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 "arp.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 int
|
||||
guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr)
|
||||
{
|
||||
struct snl_writer nw;
|
||||
|
||||
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_INET;
|
||||
|
||||
struct sockaddr_in dst = { .sin_family = AF_INET, .sin_addr = addr };
|
||||
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, struct in_addr addr)
|
||||
{
|
||||
if (ifindex == 0)
|
||||
ifindex = guess_ifindex(ss, get_myfib(), addr);
|
||||
return (ifindex);
|
||||
}
|
||||
|
||||
static void
|
||||
print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link)
|
||||
{
|
||||
const char *host;
|
||||
struct hostent *hp;
|
||||
struct sockaddr_in *addr = (struct sockaddr_in *)neigh->nda_dst;
|
||||
|
||||
xo_open_instance("arp-cache");
|
||||
|
||||
if (!opts.nflag)
|
||||
hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
|
||||
sizeof(addr->sin_addr), AF_INET);
|
||||
else
|
||||
hp = 0;
|
||||
if (hp)
|
||||
host = hp->h_name;
|
||||
else {
|
||||
host = "?";
|
||||
if (h_errno == TRY_AGAIN)
|
||||
opts.nflag = true;
|
||||
}
|
||||
xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host,
|
||||
inet_ntoa(addr->sin_addr));
|
||||
if (neigh->nda_lladdr != NULL) {
|
||||
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);
|
||||
|
||||
if ((sdl.sdl_type == IFT_ETHER ||
|
||||
sdl.sdl_type == IFT_L2VLAN ||
|
||||
sdl.sdl_type == IFT_BRIDGE) &&
|
||||
sdl.sdl_alen == ETHER_ADDR_LEN)
|
||||
xo_emit("{:mac-address/%s}",
|
||||
ether_ntoa((struct ether_addr *)LLADDR(&sdl)));
|
||||
else {
|
||||
|
||||
xo_emit("{:mac-address/%s}", link_ntoa(&sdl));
|
||||
}
|
||||
} else
|
||||
xo_emit("{d:/(incomplete)}{en:incomplete/true}");
|
||||
xo_emit(" on {:interface/%s}", link->ifla_ifname);
|
||||
|
||||
if (neigh->ndaf_next_ts == 0)
|
||||
xo_emit("{d:/ permanent}{en:permanent/true}");
|
||||
else {
|
||||
time_t expire_time;
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, 0);
|
||||
if ((expire_time = neigh->ndaf_next_ts - now.tv_sec) > 0)
|
||||
xo_emit(" expires in {:expires/%d} seconds",
|
||||
(int)expire_time);
|
||||
else
|
||||
xo_emit("{d:/ expired}{en:expired/true}");
|
||||
}
|
||||
|
||||
if (neigh->ndm_flags & NTF_PROXY)
|
||||
xo_emit("{d:/ published}{en:published/true}");
|
||||
|
||||
switch(link->ifi_type) {
|
||||
case IFT_ETHER:
|
||||
xo_emit(" [{:type/ethernet}]");
|
||||
break;
|
||||
case IFT_FDDI:
|
||||
xo_emit(" [{:type/fddi}]");
|
||||
break;
|
||||
case IFT_ATM:
|
||||
xo_emit(" [{:type/atm}]");
|
||||
break;
|
||||
case IFT_L2VLAN:
|
||||
xo_emit(" [{:type/vlan}]");
|
||||
break;
|
||||
case IFT_IEEE1394:
|
||||
xo_emit(" [{:type/firewire}]");
|
||||
break;
|
||||
case IFT_BRIDGE:
|
||||
xo_emit(" [{:type/bridge}]");
|
||||
break;
|
||||
case IFT_INFINIBAND:
|
||||
xo_emit(" [{:type/infiniband}]");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
xo_emit("\n");
|
||||
|
||||
xo_close_instance("arp-cache");
|
||||
}
|
||||
|
||||
int
|
||||
print_entries_nl(uint32_t ifindex, struct in_addr addr)
|
||||
{
|
||||
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_INET;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
print_entry(&neigh, &link);
|
||||
count++;
|
||||
snl_clear_lb(&ss_req);
|
||||
}
|
||||
|
||||
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_in *dst;
|
||||
|
||||
dst = getaddr(host);
|
||||
if (dst == NULL)
|
||||
return (1);
|
||||
|
||||
nl_init_socket(&ss);
|
||||
|
||||
ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr);
|
||||
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_INET;
|
||||
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));
|
||||
}
|
||||
snl_free(&ss);
|
||||
|
||||
return (e.error != 0);
|
||||
}
|
||||
|
||||
int
|
||||
set_nl(uint32_t ifindex, struct sockaddr_in *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->sin_addr);
|
||||
if (ifindex == 0) {
|
||||
xo_warnx("delete: cannot locate %s", host);
|
||||
snl_free(&ss);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (opts.expire_time != 0)
|
||||
opts.flags &= ~RTF_STATIC;
|
||||
printf("EXPIRE: %ld\n", opts.expire_time);
|
||||
|
||||
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 = 0;
|
||||
|
||||
ndmsg->ndm_family = AF_INET;
|
||||
ndmsg->ndm_ifindex = ifindex;
|
||||
ndmsg->ndm_state = (opts.flags & RTF_STATIC) ? NUD_PERMANENT : NUD_NONE;
|
||||
|
||||
if (opts.flags & RTF_ANNOUNCE)
|
||||
nl_flags |= NTF_PROXY;
|
||||
if (opts.flags & RTF_STATIC)
|
||||
nl_flags |= NTF_STICKY;
|
||||
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 (opts.expire_time != 0) {
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, 0);
|
||||
int off = snl_add_msg_attr_nested(&nw, NDA_FREEBSD);
|
||||
snl_add_msg_attr_u32(&nw, NDAF_NEXT_STATE_TS, now.tv_sec + opts.expire_time);
|
||||
snl_end_attr_nested(&nw, off);
|
||||
}
|
||||
|
||||
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 New Issue
Block a user