Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F153288731
D36529.id110449.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
29 KB
Referenced Files
None
Subscribers
None
D36529.id110449.diff
View Options
diff --git a/usr.bin/netstat/Makefile b/usr.bin/netstat/Makefile
--- a/usr.bin/netstat/Makefile
+++ b/usr.bin/netstat/Makefile
@@ -6,6 +6,7 @@
PROG= netstat
SRCS= if.c inet.c main.c mbuf.c mroute.c netisr.c nl_symbols.c route.c \
unix.c mroute6.c ipsec.c bpf.c pfkey.c sctp.c common.c nhops.c nhgrp.c \
+ route_netlink.c \
nl_defs.h
nl_symbols.c: nlist_symbols
diff --git a/usr.bin/netstat/common.h b/usr.bin/netstat/common.h
--- a/usr.bin/netstat/common.h
+++ b/usr.bin/netstat/common.h
@@ -45,11 +45,28 @@
const char *fmt_flags(const struct bits *p, int f);
void print_flags_generic(int flags, const struct bits *pbits,
const char *format, const char *tag_name);
-int print_sockaddr(const char *name, struct sockaddr *sa,
- struct sockaddr *mask, int flags, int width);
+int p_sockaddr(const char *name, struct sockaddr *sa, struct sockaddr *mask,
+ int flags, int width);
+
+struct _wid {
+ int dst;
+ int gw;
+ int flags;
+ int pksent;
+ int mtu;
+ int iface;
+ int expire;
+};
+void set_wid(int fam);
+void pr_rthdr(int af1 __unused);
+extern struct _wid wid;
+void p_flags(int f, const char *format);
+
+bool p_rtable_netlink(int fibnum, int af);
struct ifmap_entry {
char ifname[IFNAMSIZ];
+ uint32_t mtu;
};
struct ifmap_entry *prepare_ifmap(size_t *ifmap_size);
diff --git a/usr.bin/netstat/netlink_snl.h b/usr.bin/netstat/netlink_snl.h
new file mode 100644
--- /dev/null
+++ b/usr.bin/netstat/netlink_snl.h
@@ -0,0 +1,432 @@
+
+
+#ifndef _NETLINK_SNL_H_
+#define _NETLINK_SNL_H_
+
+#include <stddef.h>
+
+#define _roundup2(x, y) (((x)+((y)-1))&(~((y)-1)))
+
+#define NETLINK_ALIGN_SIZE sizeof(uint32_t)
+#define NETLINK_ALIGN(_len) _roundup2(_len, NETLINK_ALIGN_SIZE)
+
+#define NLA_ALIGN_SIZE sizeof(uint32_t)
+#define NLA_HDRLEN ((int)sizeof(struct nlattr))
+#define NLA_DATA_LEN(_nla) ((int)((_nla)->nla_len - NLA_HDRLEN))
+#define NLA_DATA(_nla) NL_ITEM_DATA(_nla, NLA_HDRLEN)
+#define NLA_DATA_CONST(_nla) NL_ITEM_DATA_CONST(_nla, NLA_HDRLEN)
+
+#define NLA_TYPE(_nla) ((_nla)->nla_type & 0x3FFF)
+
+#define NLA_NEXT(_attr) (struct nlattr *)((char *)_attr + NLA_ALIGN(_attr->nla_len))
+
+#define _NLA_END(_start, _len) ((char *)(_start) + (_len))
+#define NLA_FOREACH(_attr, _start, _len) \
+ for (_attr = (_start); \
+ ((char *)_attr < _NLA_END(_start, _len)) && \
+ ((char *)NLA_NEXT(_attr) <= _NLA_END(_start, _len)); \
+ _attr = NLA_NEXT(_attr))
+
+#define NL_ARRAY_LEN(_a) (sizeof(_a) / sizeof((_a)[0]))
+
+struct linear_buffer {
+ char *base; /* Base allocated memory pointer */
+ uint32_t offset; /* Currently used offset */
+ uint32_t size; /* Total buffer size */
+};
+
+static inline char *
+lb_allocz(struct linear_buffer *lb, int len)
+{
+ len = roundup2(len, sizeof(uint64_t));
+ if (lb->offset + len > lb->size)
+ return (NULL);
+ void *data = (void *)(lb->base + lb->offset);
+ lb->offset += len;
+ return (data);
+}
+
+static inline void
+lb_clear(struct linear_buffer *lb)
+{
+ memset(lb->base, 0, lb->offset);
+ lb->offset = 0;
+}
+
+struct snl_state {
+ int fd;
+ char *buf;
+ size_t off;
+ size_t bufsize;
+ size_t datalen;
+ uint32_t seq;
+ bool init_done;
+ struct linear_buffer lb;
+};
+#define SCRATCH_BUFFER_SIZE 1024
+
+typedef void snl_parse_field_f(struct snl_state *ss, void *hdr, void *target);
+struct snl_field_parser {
+ uint16_t off_in;
+ uint16_t off_out;
+ snl_parse_field_f *cb;
+};
+
+typedef bool snl_parse_attr_f(struct snl_state *ss, struct nlattr *attr, void *target);
+struct snl_attr_parser {
+ uint16_t type; /* Attribute type */
+ uint16_t off; /* field offset in the target structure */
+ snl_parse_attr_f *cb; /* parser function to call */
+};
+
+struct snl_hdr_parser {
+ int hdr_off; /* aligned header size */
+ int fp_size;
+ int np_size;
+ const struct snl_field_parser *fp; /* array of header field parsers */
+ const struct snl_attr_parser *np; /* array of attribute parsers */
+};
+
+#define SNL_DECLARE_PARSER(_name, _t, _fp, _np) \
+static const struct snl_hdr_parser _name = { \
+ .hdr_off = sizeof(_t), \
+ .fp = &((_fp)[0]), \
+ .np = &((_np)[0]), \
+ .fp_size = NL_ARRAY_LEN(_fp), \
+ .np_size = NL_ARRAY_LEN(_np), \
+}
+
+#define SNL_DECLARE_ATTR_PARSER(_name, _np) \
+static const struct snl_hdr_parser _name = { \
+ .np = &((_np)[0]), \
+ .np_size = NL_ARRAY_LEN(_np), \
+}
+
+
+static void
+snl_free(struct snl_state *ss)
+{
+ if (ss->init_done) {
+ close(ss->fd);
+ if (ss->buf != NULL)
+ free(ss->buf);
+ if (ss->lb.base != NULL)
+ free(ss->lb.base);
+ }
+}
+
+static inline bool
+snl_init(struct snl_state *ss)
+{
+ memset(ss, 0, sizeof(*ss));
+
+ ss->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (ss->fd == -1)
+ return (false);
+ ss->init_done = true;
+
+ int rcvbuf;
+ socklen_t optlen = sizeof(rcvbuf);
+ if (getsockopt(ss->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen) == -1) {
+ snl_free(ss);
+ return (false);
+ }
+
+ ss->bufsize = rcvbuf;
+ ss->buf = malloc(ss->bufsize);
+ if (ss->buf == NULL) {
+ snl_free(ss);
+ return (false);
+ }
+
+ ss->lb.size = SCRATCH_BUFFER_SIZE;
+ ss->lb.base = calloc(1, ss->lb.size);
+ if (ss->lb.base == NULL) {
+ snl_free(ss);
+ return (false);
+ }
+
+ return (true);
+}
+
+static inline void *
+snl_allocz(struct snl_state *ss, int len)
+{
+ return (lb_allocz(&ss->lb, len));
+}
+#define snl_alloc_sockaddr(_npt, _len) ((struct sockaddr *)(snl_allocz(_npt, _len)))
+
+static inline void
+snl_clear_lb(struct snl_state *ss)
+{
+ lb_clear(&ss->lb);
+}
+
+static inline bool
+snl_send(struct snl_state *ss, void *data, int sz)
+{
+ return (send(ss->fd, data, sz, 0) == sz);
+}
+
+static inline uint32_t
+snl_get_seq(struct snl_state *ss)
+{
+ return (++ss->seq);
+}
+
+static inline struct nlmsghdr *
+snl_read_message(struct snl_state *ss)
+{
+ if (ss->off == ss->datalen) {
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = ss->buf,
+ .iov_len = ss->bufsize,
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ ss->off = 0;
+ ss->datalen = 0;
+ for (;;) {
+ ssize_t datalen = recvmsg(ss->fd, &msg, 0);
+ if (datalen > 0) {
+ ss->datalen = datalen;
+ break;
+ } else if (errno != EINTR)
+ return (NULL);
+ }
+ }
+ struct nlmsghdr *hdr = (struct nlmsghdr *)&ss->buf[ss->off];
+ ss->off += NLMSG_ALIGN(hdr->nlmsg_len);
+ return (hdr);
+}
+
+static const struct snl_attr_parser *
+find_parser(const struct snl_attr_parser *ps, int pslen, int key)
+{
+ int left_i = 0, right_i = pslen - 1;
+
+ if (key < ps[0].type || key > ps[pslen - 1].type)
+ return (NULL);
+
+ while (left_i + 1 < right_i) {
+ int mid_i = (left_i + right_i) / 2;
+ if (key < ps[mid_i].type)
+ right_i = mid_i;
+ else if (key > ps[mid_i].type)
+ left_i = mid_i + 1;
+ else
+ return (&ps[mid_i]);
+ }
+ if (ps[left_i].type == key)
+ return (&ps[left_i]);
+ else if (ps[right_i].type == key)
+ return (&ps[right_i]);
+ return (NULL);
+}
+
+static inline bool
+snl_parse_attrs_raw(struct snl_state *ss, struct nlattr *nla_head, int len,
+ const struct snl_attr_parser *ps, int pslen, void *target)
+{
+ struct nlattr *nla;
+
+ NLA_FOREACH(nla, nla_head, len) {
+ if (nla->nla_len < sizeof(struct nlattr))
+ return (false);
+ int nla_type = nla->nla_type & NLA_TYPE_MASK;
+ const struct snl_attr_parser *s = find_parser(ps, pslen, nla_type);
+ if (s != NULL) {
+ void *ptr = (void *)((char *)target + s->off);
+ if (!s->cb(ss, nla, ptr))
+ return (false);
+ }
+ }
+ return (true);
+}
+
+static inline bool
+snl_parse_attrs(struct snl_state *ss, struct nlmsghdr *hdr, int hdrlen,
+ const struct snl_attr_parser *ps, int pslen, void *target)
+{
+ int off = NLMSG_HDRLEN + NETLINK_ALIGN(hdrlen);
+ int len = hdr->nlmsg_len - off;
+ struct nlattr *nla_head = (struct nlattr *)((char *)hdr + off);
+
+ return (snl_parse_attrs_raw(ss, nla_head, len, ps, pslen, target));
+}
+
+static inline bool
+snl_parse_header(struct snl_state *ss, void *hdr, int len,
+ const struct snl_hdr_parser *parser, void *target)
+{
+ /* Extract fields first (if any) */
+ for (int i = 0; i < parser->fp_size; i++) {
+ const struct snl_field_parser *fp = &parser->fp[i];
+ void *src = (char *)hdr + fp->off_in;
+ void *dst = (char *)target + fp->off_out;
+
+ fp->cb(ss, src, dst);
+ }
+
+ struct nlattr *nla_head = (struct nlattr *)((char *)hdr + parser->hdr_off);
+ bool result = snl_parse_attrs_raw(ss, nla_head, len - parser->hdr_off,
+ parser->np, parser->np_size, target);
+
+ return (result);
+}
+
+static inline bool
+snl_parse_nlmsg(struct snl_state *ss, struct nlmsghdr *hdr,
+ const struct snl_hdr_parser *parser, void *target)
+{
+ return (snl_parse_header(ss, hdr + 1, hdr->nlmsg_len - sizeof(*hdr), parser, target));
+}
+
+static inline bool
+snl_attr_get_flag(struct snl_state *ss, struct nlattr *nla, void *target)
+{
+ if (NLA_DATA_LEN(nla) == 0) {
+ *((uint8_t *)target) = 1;
+ return (true);
+ }
+ return (false);
+}
+
+static inline struct sockaddr *
+parse_rta_ip4(struct snl_state *ss, void *rta_data, int *perror)
+{
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)snl_alloc_sockaddr(ss, sizeof(struct sockaddr_in));
+ if (sin == NULL) {
+ *perror = ENOBUFS;
+ return (NULL);
+ }
+ sin->sin_len = sizeof(struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ memcpy(&sin->sin_addr, rta_data, sizeof(struct in_addr));
+ return ((struct sockaddr *)sin);
+}
+
+static inline struct sockaddr *
+parse_rta_ip6(struct snl_state *ss, void *rta_data, int *perror)
+{
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)snl_alloc_sockaddr(ss, sizeof(struct sockaddr_in6));
+ if (sin6 == NULL) {
+ *perror = ENOBUFS;
+ return (NULL);
+ }
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ memcpy(&sin6->sin6_addr, rta_data, sizeof(struct in6_addr));
+ return ((struct sockaddr *)sin6);
+}
+
+static inline struct sockaddr *
+parse_rta_ip(struct snl_state *ss, struct rtattr *rta, int *perror)
+{
+ void *rta_data = NL_RTA_DATA(rta);
+ int rta_len = NL_RTA_DATA_LEN(rta);
+
+ if (rta_len == sizeof(struct in_addr)) {
+ return (parse_rta_ip4(ss, rta_data, perror));
+ } else if (rta_len == sizeof(struct in6_addr)) {
+ return (parse_rta_ip6(ss, rta_data, perror));
+ } else {
+ *perror = ENOTSUP;
+ return (NULL);
+ }
+ return (NULL);
+}
+
+static inline bool
+snl_attr_get_ip(struct snl_state *ss, struct nlattr *nla, void *target)
+{
+ int error = 0;
+ struct sockaddr *sa = parse_rta_ip(ss, (struct rtattr *)nla, &error);
+ if (error == 0) {
+ *((struct sockaddr **)target) = sa;
+ return (true);
+ }
+ return (false);
+}
+
+static inline struct sockaddr *
+parse_rta_via(struct snl_state *ss, struct rtattr *rta, int *perror)
+{
+ struct rtvia *via = NL_RTA_DATA(rta);
+
+ switch (via->rtvia_family) {
+ case AF_INET:
+ return (parse_rta_ip4(ss, via->rtvia_addr, perror));
+ case AF_INET6:
+ return (parse_rta_ip6(ss, via->rtvia_addr, perror));
+ default:
+ *perror = ENOTSUP;
+ return (NULL);
+ }
+}
+
+static inline bool
+snl_attr_get_ipvia(struct snl_state *ss, struct nlattr *nla, void *target)
+{
+ int error = 0;
+
+ struct sockaddr *sa = parse_rta_via(ss, (struct rtattr *)nla, &error);
+ if (error == 0) {
+ *((struct sockaddr **)target) = sa;
+ return (true);
+ }
+ return (false);
+}
+
+
+static inline bool
+snl_attr_get_uint32(struct snl_state *ss, struct nlattr *nla, void *target)
+{
+ if (NLA_DATA_LEN(nla) == sizeof(uint32_t)) {
+ *((uint32_t *)target) = *((const uint32_t *)NL_RTA_DATA_CONST(nla));
+ return (true);
+ }
+ return (false);
+}
+
+static inline bool
+snl_attr_get_string(struct snl_state *ss, struct nlattr *nla, void *target)
+{
+ size_t maxlen = NLA_DATA_LEN(nla);
+
+ if (strnlen((char *)NLA_DATA(nla), maxlen) < maxlen) {
+ *((char **)target) = (char *)NLA_DATA(nla);
+ return (true);
+ }
+ return (false);
+}
+
+static inline bool
+snl_attr_get_nla(struct snl_state *ss, struct nlattr *nla, void *target)
+{
+ *((struct nlattr **)target) = nla;
+ return (true);
+}
+
+static inline void
+snl_field_get_u8(struct snl_state *ss, void *src, void *target)
+{
+ *((uint8_t *)target) = *((uint8_t *)src);
+}
+
+static inline void
+snl_field_get_u32(struct snl_state *ss, void *src, void *target)
+{
+ *((uint32_t *)target) = *((uint32_t *)src);
+}
+
+#endif
diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c
--- a/usr.bin/netstat/route.c
+++ b/usr.bin/netstat/route.c
@@ -106,7 +106,6 @@
#endif
static void p_rtable_sysctl(int, int);
static void p_rtentry_sysctl(const char *name, struct rt_msghdr *);
-static void p_flags(int, const char *);
static void domask(char *, size_t, u_long);
@@ -143,7 +142,8 @@
if (fibnum)
xo_emit(" ({L:fib}: {:fib/%d})", fibnum);
xo_emit("\n");
- p_rtable_sysctl(fibnum, af);
+ if (!p_rtable_netlink(fibnum, af))
+ p_rtable_sysctl(fibnum, af);
xo_close_container("route-information");
}
@@ -197,42 +197,48 @@
#define WID_IF_DEFAULT(af) ((af) == AF_INET6 ? 8 : (Wflag ? 10 : 8))
#endif /*INET6*/
-static int wid_dst;
-static int wid_gw;
-static int wid_flags;
-static int wid_pksent;
-static int wid_mtu;
-static int wid_if;
-static int wid_expire;
+struct _wid wid;
/*
* Print header for routing table columns.
*/
-static void
+void
pr_rthdr(int af1 __unused)
{
if (Wflag) {
xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} "
"{T:/%*.*s} {T:/%*.*s} {T:/%*s}\n",
- wid_dst, wid_dst, "Destination",
- wid_gw, wid_gw, "Gateway",
- wid_flags, wid_flags, "Flags",
- wid_mtu, wid_mtu, "Nhop#",
- wid_mtu, wid_mtu, "Mtu",
- wid_if, wid_if, "Netif",
- wid_expire, "Expire");
+ wid.dst, wid.dst, "Destination",
+ wid.gw, wid.gw, "Gateway",
+ wid.flags, wid.flags, "Flags",
+ wid.mtu, wid.mtu, "Nhop#",
+ wid.mtu, wid.mtu, "Mtu",
+ wid.iface, wid.iface, "Netif",
+ wid.expire, "Expire");
} else {
xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} "
"{T:/%*s}\n",
- wid_dst, wid_dst, "Destination",
- wid_gw, wid_gw, "Gateway",
- wid_flags, wid_flags, "Flags",
- wid_if, wid_if, "Netif",
- wid_expire, "Expire");
+ wid.dst, wid.dst, "Destination",
+ wid.gw, wid.gw, "Gateway",
+ wid.flags, wid.flags, "Flags",
+ wid.iface, wid.iface, "Netif",
+ wid.expire, "Expire");
}
}
+void
+set_wid(int fam)
+{
+ wid.dst = WID_DST_DEFAULT(fam);
+ wid.gw = WID_GW_DEFAULT(fam);
+ wid.flags = 6;
+ wid.pksent = 8;
+ wid.mtu = 6;
+ wid.iface = WID_IF_DEFAULT(fam);
+ wid.expire = 6;
+}
+
static void
p_rtable_sysctl(int fibnum, int af)
{
@@ -278,15 +284,8 @@
xo_close_instance("rt-family");
}
need_table_close = true;
-
fam = sa->sa_family;
- wid_dst = WID_DST_DEFAULT(fam);
- wid_gw = WID_GW_DEFAULT(fam);
- wid_flags = 6;
- wid_pksent = 8;
- wid_mtu = 6;
- wid_if = WID_IF_DEFAULT(fam);
- wid_expire = 6;
+ set_wid(fam);
xo_open_instance("rt-family");
pr_family(fam);
xo_open_list("rt-entry");
@@ -323,22 +322,22 @@
protrusion = p_sockaddr("destination", addr[RTAX_DST],
addr[RTAX_NETMASK],
- rtm->rtm_flags, wid_dst);
+ rtm->rtm_flags, wid.dst);
protrusion = p_sockaddr("gateway", addr[RTAX_GATEWAY], NULL, RTF_HOST,
- wid_gw - protrusion);
+ wid.gw - protrusion);
snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ",
- wid_flags - protrusion);
+ wid.flags - protrusion);
p_flags(rtm->rtm_flags, buffer);
/* Output path weight as non-visual property */
xo_emit("{e:weight/%u}", rtm->rtm_rmx.rmx_weight);
if (Wflag) {
/* XXX: use=0? */
- xo_emit("{t:nhop/%*lu} ", wid_mtu, rtm->rtm_rmx.rmx_nhidx);
+ xo_emit("{t:nhop/%*lu} ", wid.mtu, rtm->rtm_rmx.rmx_nhidx);
if (rtm->rtm_rmx.rmx_mtu != 0)
- xo_emit("{t:mtu/%*lu} ", wid_mtu, rtm->rtm_rmx.rmx_mtu);
+ xo_emit("{t:mtu/%*lu} ", wid.mtu, rtm->rtm_rmx.rmx_mtu);
else
- xo_emit("{P:/%*s} ", wid_mtu, "");
+ xo_emit("{P:/%*s} ", wid.mtu, "");
}
memset(prettyname, 0, sizeof(prettyname));
@@ -350,15 +349,15 @@
}
if (Wflag)
- xo_emit("{t:interface-name/%*s}", wid_if, prettyname);
+ xo_emit("{t:interface-name/%*s}", wid.iface, prettyname);
else
- xo_emit("{t:interface-name/%*.*s}", wid_if, wid_if,
+ xo_emit("{t:interface-name/%*.*s}", wid.iface, wid.iface,
prettyname);
if (rtm->rtm_rmx.rmx_expire) {
time_t expire_time;
if ((expire_time = rtm->rtm_rmx.rmx_expire - uptime.tv_sec) > 0)
- xo_emit(" {:expire-time/%*d}", wid_expire,
+ xo_emit(" {:expire-time/%*d}", wid.expire,
(int)expire_time);
}
@@ -472,7 +471,7 @@
return (cp);
}
-static void
+void
p_flags(int f, const char *format)
{
diff --git a/usr.bin/netstat/route_netlink.c b/usr.bin/netstat/route_netlink.c
new file mode 100644
--- /dev/null
+++ b/usr.bin/netstat/route_netlink.c
@@ -0,0 +1,447 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/time.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 <netinet/in.h>
+#include <netgraph/ng_socket.h>
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <err.h>
+#include <libxo/xo.h>
+#include "netstat.h"
+#include "common.h"
+#include "nl_defs.h"
+#include "netlink_snl.h"
+
+
+static void p_rtentry_netlink(struct snl_state *ss, const char *name, struct nlmsghdr *hdr);
+
+static struct ifmap_entry *ifmap;
+static size_t ifmap_size;
+
+struct nl_parsed_link {
+ uint32_t ifi_index;
+ uint32_t ifla_mtu;
+ char *ifla_ifname;
+};
+
+#define _IN(_field) offsetof(struct ifinfomsg, _field)
+#define _OUT(_field) offsetof(struct nl_parsed_link, _field)
+static struct snl_attr_parser ap_link[] = {
+ { .type = IFLA_IFNAME, .off = _OUT(ifla_ifname), .cb = snl_attr_get_string },
+ { .type = IFLA_MTU, .off = _OUT(ifla_mtu), .cb = snl_attr_get_uint32 },
+};
+static struct snl_field_parser fp_link[] = {
+ {.off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = snl_field_get_u32 },
+};
+#undef _IN
+#undef _OUT
+SNL_DECLARE_PARSER(link_parser, struct ifinfomsg, fp_link, ap_link);
+
+/* Generate ifmap using netlink */
+static struct ifmap_entry *
+prepare_ifmap_netlink(struct snl_state *ss, size_t *pifmap_size)
+{
+ struct {
+ struct nlmsghdr hdr;
+ struct ifinfomsg ifmsg;
+ } msg = {
+ .hdr.nlmsg_type = RTM_GETLINK,
+ .hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .hdr.nlmsg_seq = snl_get_seq(ss),
+ };
+ msg.hdr.nlmsg_len = sizeof(msg);
+
+ if (!snl_send(ss, &msg, sizeof(msg))) {
+ snl_free(ss);
+ return (NULL);
+ }
+
+ struct ifmap_entry *ifmap = NULL;
+ uint32_t ifmap_size = 0;
+ struct nlmsghdr *hdr;
+ while ((hdr = snl_read_message(ss)) != NULL && hdr->nlmsg_type != NLMSG_DONE) {
+ if (hdr->nlmsg_seq != msg.hdr.nlmsg_seq)
+ continue;
+/*
+ if (hdr->nlmsg_type == NLMSG_ERROR)
+ break;
+*/
+ struct nl_parsed_link link = {};
+ if (!snl_parse_nlmsg(ss, hdr, &link_parser, &link))
+ continue;
+ if (link.ifi_index >= ifmap_size) {
+ size_t size = roundup2(link.ifi_index + 1, 32) * sizeof(struct ifmap_entry);
+ if ((ifmap = realloc(ifmap, size)) == NULL)
+ errx(2, "realloc(%lu) failed", size);
+ memset(&ifmap[ifmap_size], 0,
+ size - ifmap_size *
+ sizeof(struct ifmap_entry));
+ ifmap_size = roundup2(link.ifi_index + 1, 32);
+ }
+ if (*ifmap[link.ifi_index].ifname != '\0')
+ continue;
+ strlcpy(ifmap[link.ifi_index].ifname, link.ifla_ifname, IFNAMSIZ);
+ ifmap[link.ifi_index].mtu = link.ifla_mtu;
+ }
+ *pifmap_size = ifmap_size;
+ return (ifmap);
+}
+
+bool
+p_rtable_netlink(int fibnum, int af)
+{
+ int fam = AF_UNSPEC;
+ int need_table_close = false;
+ struct nlmsghdr *hdr;
+
+ struct snl_state ss = {};
+
+ if (!snl_init(&ss))
+ return (false);
+
+ ifmap = prepare_ifmap_netlink(&ss, &ifmap_size);
+
+ struct {
+ struct nlmsghdr hdr;
+ struct rtmsg rtmsg;
+ struct nlattr nla_fibnum;
+ uint32_t fibnum;
+ } msg = {
+ .hdr.nlmsg_type = RTM_GETROUTE,
+ .hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .hdr.nlmsg_seq = snl_get_seq(&ss),
+ .rtmsg.rtm_family = af,
+ .nla_fibnum.nla_len = sizeof(struct nlattr) + sizeof(uint32_t),
+ .nla_fibnum.nla_type = RTA_TABLE,
+ .fibnum = fibnum,
+ };
+ msg.hdr.nlmsg_len = sizeof(msg);
+
+ if (!snl_send(&ss, &msg, sizeof(msg))) {
+ snl_free(&ss);
+ return (false);
+ }
+
+ xo_open_container("route-table");
+ xo_open_list("rt-family");
+ while ((hdr = snl_read_message(&ss)) != NULL && hdr->nlmsg_type != NLMSG_DONE) {
+ if (hdr->nlmsg_seq != msg.hdr.nlmsg_seq)
+ continue;
+ struct rtmsg *rtm = (struct rtmsg *)(hdr + 1);
+ /* Only print family first time. */
+ if (fam != rtm->rtm_family) {
+ if (need_table_close) {
+ xo_close_list("rt-entry");
+ xo_close_instance("rt-family");
+ }
+ need_table_close = true;
+ fam = rtm->rtm_family;
+ set_wid(fam);
+ xo_open_instance("rt-family");
+ pr_family(fam);
+ xo_open_list("rt-entry");
+ pr_rthdr(fam);
+ }
+ p_rtentry_netlink(&ss, "rt-entry", hdr);
+ snl_clear_lb(&ss);
+ }
+ if (need_table_close) {
+ xo_close_list("rt-entry");
+ xo_close_instance("rt-family");
+ }
+ xo_close_list("rt-family");
+ xo_close_container("route-table");
+ snl_free(&ss);
+ return (true);
+}
+
+struct rta_mpath_nh {
+ struct sockaddr *gw;
+ uint32_t ifindex;
+ uint8_t rtnh_flags;
+ uint8_t rtnh_weight;
+};
+
+#define _IN(_field) offsetof(struct rtnexthop, _field)
+#define _OUT(_field) offsetof(struct rta_mpath_nh, _field)
+static const struct snl_attr_parser psnh[] = {
+ { .type = NL_RTA_GATEWAY, .off = _OUT(gw), .cb = snl_attr_get_ip },
+ { .type = NL_RTA_VIA, .off = _OUT(gw), .cb = snl_attr_get_ipvia },
+};
+
+static const struct snl_field_parser fpnh[] = {
+ { .off_in = _IN(rtnh_flags), .off_out = _OUT(rtnh_flags), .cb = snl_field_get_u8 },
+ { .off_in = _IN(rtnh_hops), .off_out = _OUT(rtnh_weight), .cb = snl_field_get_u8 },
+ { .off_in = _IN(rtnh_ifindex), .off_out = _OUT(ifindex), .cb = snl_field_get_u32 },
+};
+#undef _IN
+#undef _OUT
+
+SNL_DECLARE_PARSER(mpath_parser, struct rtnexthop, fpnh, psnh);
+
+struct rta_mpath {
+ int num_nhops;
+ struct rta_mpath_nh nhops[0];
+};
+
+static bool
+nlattr_get_multipath(struct snl_state *ss, struct nlattr *nla, void *target)
+{
+ int data_len = nla->nla_len - sizeof(struct nlattr);
+ struct rtnexthop *rtnh;
+
+ int max_nhops = data_len / sizeof(struct rtnexthop);
+ size_t sz = (max_nhops + 2) * sizeof(struct rta_mpath_nh);
+
+ struct rta_mpath *mp = snl_allocz(ss, sz);
+ mp->num_nhops = 0;
+
+ for (rtnh = (struct rtnexthop *)(nla + 1); data_len > 0; ) {
+ struct rta_mpath_nh *mpnh = &mp->nhops[mp->num_nhops++];
+
+ if (!snl_parse_header(ss, rtnh, rtnh->rtnh_len, &mpath_parser, mpnh))
+ return (false);
+
+ int len = NL_ITEM_ALIGN(rtnh->rtnh_len);
+ data_len -= len;
+ rtnh = (struct rtnexthop *)((char *)rtnh + len);
+ }
+ if (data_len != 0 || mp->num_nhops == 0) {
+ return (false);
+ }
+
+ *((struct rta_mpath **)target) = mp;
+ return (true);
+}
+
+
+struct nl_parsed_route {
+ struct sockaddr *rta_dst;
+ struct sockaddr *rta_gw;
+ struct nlattr *rta_metrics;
+ struct rta_mpath *rta_multipath;
+ uint32_t rta_expires;
+ uint32_t rta_oif;
+ uint32_t rta_expire;
+ uint32_t rta_table;
+ uint32_t rta_knh_id;
+ uint32_t rta_rtflags;
+ uint32_t rtax_mtu;
+ uint8_t rtm_family;
+ uint8_t rtm_type;
+ uint8_t rtm_protocol;
+ uint8_t rtm_dst_len;
+};
+
+#define _IN(_field) offsetof(struct rtmsg, _field)
+#define _OUT(_field) offsetof(struct nl_parsed_route, _field)
+static const struct snl_attr_parser ps[] = {
+ { .type = NL_RTA_DST, .off = _OUT(rta_dst), .cb = snl_attr_get_ip },
+ { .type = NL_RTA_OIF, .off = _OUT(rta_oif), .cb = snl_attr_get_uint32 },
+ { .type = NL_RTA_GATEWAY, .off = _OUT(rta_gw), .cb = snl_attr_get_ip },
+ { .type = NL_RTA_METRICS, .off = _OUT(rta_metrics), .cb = snl_attr_get_nla },
+ { .type = NL_RTA_MULTIPATH, .off = _OUT(rta_multipath), .cb = nlattr_get_multipath },
+ { .type = NL_RTA_KNH_ID, .off = _OUT(rta_knh_id), .cb = snl_attr_get_uint32 },
+ { .type = NL_RTA_RTFLAGS, .off = _OUT(rta_rtflags), .cb = snl_attr_get_uint32 },
+ { .type = NL_RTA_TABLE, .off = _OUT(rta_table), .cb = snl_attr_get_uint32 },
+ { .type = NL_RTA_VIA, .off = _OUT(rta_gw), .cb = snl_attr_get_ipvia },
+ { .type = NL_RTA_EXPIRES, .off = _OUT(rta_expire), .cb = snl_attr_get_uint32 },
+};
+
+/*
+static struct snl_attr_parser psm[] = {
+ { .type = NL_RTAX_MTU, .off = _OUT(rtax_mtu), .cb = snl_attr_get_uint32 },
+};
+*/
+
+static const struct snl_field_parser fprt[] = {
+ {.off_in = _IN(rtm_family), .off_out = _OUT(rtm_family), .cb = snl_field_get_u8 },
+ {.off_in = _IN(rtm_type), .off_out = _OUT(rtm_type), .cb = snl_field_get_u8 },
+ {.off_in = _IN(rtm_protocol), .off_out = _OUT(rtm_protocol), .cb = snl_field_get_u8 },
+ {.off_in = _IN(rtm_dst_len), .off_out = _OUT(rtm_dst_len), .cb = snl_field_get_u8 },
+};
+#undef _IN
+#undef _OUT
+SNL_DECLARE_PARSER(rtm_parser, struct rtmsg, fprt, ps);
+
+#define RTF_UP 0x1
+#define RTF_GATEWAY 0x2
+#define RTF_HOST 0x4
+#define RTF_REJECT 0x8
+#define RTF_DYNAMIC 0x10
+#define RTF_STATIC 0x800
+#define RTF_BLACKHOLE 0x1000
+#define RTF_PROTO2 0x4000
+#define RTF_PROTO1 0x8000
+#define RTF_PROTO3 0x40000
+#define RTF_FIXEDMTU 0x80000
+#define RTF_PINNED 0x100000
+
+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);
+}
+
+static void
+gen_mask(int family, int plen, struct sockaddr *sa)
+{
+ if (family == AF_INET6) {
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ .sin6_len = sizeof(struct sockaddr_in6),
+ };
+ ip6_writemask(&sin6.sin6_addr, plen);
+ *((struct sockaddr_in6 *)sa) = sin6;
+ } else if (family == AF_INET) {
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ .sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0),
+ };
+ *((struct sockaddr_in *)sa) = sin;
+ }
+}
+
+struct sockaddr_dl_short {
+ u_char sdl_len; /* Total length of sockaddr */
+ u_char sdl_family; /* AF_LINK */
+ u_short sdl_index; /* if != 0, system given index for interface */
+ u_char sdl_type; /* interface type */
+ u_char sdl_nlen; /* interface name length, no trailing 0 reqd. */
+ u_char sdl_alen; /* link level address length */
+ u_char sdl_slen; /* link layer selector length */
+ char sdl_data[8]; /* unused */
+};
+
+static void
+p_rtentry_netlink(struct snl_state *ss, const char *name, struct nlmsghdr *hdr)
+{
+ struct sockaddr_in6 mask6;
+ struct sockaddr *pmask = (struct sockaddr *)&mask6;
+ char buffer[128];
+ char prettyname[128];
+ int protrusion;
+
+ struct nl_parsed_route rt = {};
+ if (!snl_parse_nlmsg(ss, hdr, &rtm_parser, &rt))
+ return;
+ gen_mask(rt.rtm_family, rt.rtm_dst_len, pmask);
+
+ struct sockaddr_dl_short sdl_gw = {
+ .sdl_family = AF_LINK,
+ .sdl_len = sizeof(struct sockaddr_dl_short),
+ .sdl_index = rt.rta_oif,
+ };
+ if (rt.rta_gw == NULL)
+ rt.rta_gw = (struct sockaddr *)&sdl_gw;
+
+ xo_open_instance(name);
+
+ protrusion = p_sockaddr("destination", rt.rta_dst, pmask, rt.rta_rtflags, wid.dst);
+ protrusion = p_sockaddr("gateway", rt.rta_gw, NULL, RTF_HOST,
+ wid.gw - protrusion);
+ snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ",
+ wid.flags - protrusion);
+ p_flags(rt.rta_rtflags, buffer);
+ /* Output path weight as non-visual property */
+ int weight = 1;
+ xo_emit("{e:weight/%u}", weight = 1);
+
+ memset(prettyname, 0, sizeof(prettyname));
+ if (rt.rta_oif < ifmap_size) {
+ strlcpy(prettyname, ifmap[rt.rta_oif].ifname,
+ sizeof(prettyname));
+ if (*prettyname == '\0')
+ strlcpy(prettyname, "---", sizeof(prettyname));
+ if (rt.rtax_mtu == 0)
+ rt.rtax_mtu = ifmap[rt.rta_oif].mtu;
+ }
+
+ if (Wflag) {
+ /* XXX: use=0? */
+ xo_emit("{t:nhop/%*lu} ", wid.mtu, rt.rta_knh_id);
+
+ if (rt.rtax_mtu != 0)
+ xo_emit("{t:mtu/%*lu} ", wid.mtu, rt.rtax_mtu);
+ else {
+ /* use interface mtu */
+ xo_emit("{P:/%*s} ", wid.mtu, "");
+ }
+
+ }
+
+ if (Wflag)
+ xo_emit("{t:interface-name/%*s}", wid.iface, prettyname);
+ else
+ xo_emit("{t:interface-name/%*.*s}", wid.iface, wid.iface,
+ prettyname);
+ if (rt.rta_expires > 0) {
+ xo_emit(" {:expire-time/%*u}", wid.expire, rt.rta_expires);
+ }
+
+ xo_emit("\n");
+ xo_close_instance(name);
+}
+
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Apr 21, 6:47 AM (2 h, 12 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31894942
Default Alt Text
D36529.id110449.diff (29 KB)
Attached To
Mode
D36529: netstat: make netstat -rn use netlink instead of rtsock
Attached
Detach File
Event Timeline
Log In to Comment