summaryrefslogtreecommitdiffstats
path: root/zebra/debug_nl.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/debug_nl.c')
-rw-r--r--zebra/debug_nl.c1784
1 files changed, 1784 insertions, 0 deletions
diff --git a/zebra/debug_nl.c b/zebra/debug_nl.c
new file mode 100644
index 0000000..afefab6
--- /dev/null
+++ b/zebra/debug_nl.c
@@ -0,0 +1,1784 @@
+/*
+ * Copyright (c) 2018 Rafael Zalamena
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <zebra.h>
+
+#if defined(HAVE_NETLINK) && defined(NETLINK_DEBUG)
+
+#include <sys/socket.h>
+
+#include <linux/netconf.h>
+#include <linux/netlink.h>
+#include <linux/nexthop.h>
+#include <linux/rtnetlink.h>
+#include <net/if_arp.h>
+#include <linux/fib_rules.h>
+#include <linux/lwtunnel.h>
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "zebra/rt_netlink.h"
+#include "zebra/kernel_netlink.h"
+#include "lib/vxlan.h"
+
+const char *nlmsg_type2str(uint16_t type)
+{
+ switch (type) {
+ /* Generic */
+ case NLMSG_NOOP:
+ return "NOOP";
+ case NLMSG_ERROR:
+ return "ERROR";
+ case NLMSG_DONE:
+ return "DONE";
+ case NLMSG_OVERRUN:
+ return "OVERRUN";
+
+ /* RTM */
+ case RTM_NEWLINK:
+ return "NEWLINK";
+ case RTM_DELLINK:
+ return "DELLINK";
+ case RTM_GETLINK:
+ return "GETLINK";
+ case RTM_SETLINK:
+ return "SETLINK";
+
+ case RTM_NEWADDR:
+ return "NEWADDR";
+ case RTM_DELADDR:
+ return "DELADDR";
+ case RTM_GETADDR:
+ return "GETADDR";
+
+ case RTM_NEWROUTE:
+ return "NEWROUTE";
+ case RTM_DELROUTE:
+ return "DELROUTE";
+ case RTM_GETROUTE:
+ return "GETROUTE";
+
+ case RTM_NEWNEIGH:
+ return "NEWNEIGH";
+ case RTM_DELNEIGH:
+ return "DELNEIGH";
+ case RTM_GETNEIGH:
+ return "GETNEIGH";
+
+ case RTM_NEWRULE:
+ return "NEWRULE";
+ case RTM_DELRULE:
+ return "DELRULE";
+ case RTM_GETRULE:
+ return "GETRULE";
+
+ case RTM_NEWNEXTHOP:
+ return "NEWNEXTHOP";
+ case RTM_DELNEXTHOP:
+ return "DELNEXTHOP";
+ case RTM_GETNEXTHOP:
+ return "GETNEXTHOP";
+
+ case RTM_NEWTUNNEL:
+ return "NEWTUNNEL";
+ case RTM_DELTUNNEL:
+ return "DELTUNNEL";
+ case RTM_GETTUNNEL:
+ return "GETTUNNEL";
+
+ case RTM_NEWNETCONF:
+ return "RTM_NEWNETCONF";
+ case RTM_DELNETCONF:
+ return "RTM_DELNETCONF";
+
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *af_type2str(int type)
+{
+ switch (type) {
+ case AF_UNSPEC:
+ return "AF_UNSPEC";
+ case AF_UNIX:
+ return "AF_UNIX";
+ case AF_INET:
+ return "AF_INET";
+ case AF_INET6:
+ return "AF_INET6";
+ case AF_BRIDGE:
+ return "AF_BRIDGE";
+ case AF_NETLINK:
+ return "AF_NETLINK";
+#ifdef AF_MPLS
+ case AF_MPLS:
+ return "AF_MPLS";
+#endif /* AF_MPLS */
+ case AF_BLUETOOTH:
+ return "AF_BLUETOOTH";
+ case AF_VSOCK:
+ return "AF_VSOCK";
+ case AF_KEY:
+ return "AF_KEY";
+ case AF_PACKET:
+ return "AF_PACKET";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *ifi_type2str(int type)
+{
+ switch (type) {
+ case ARPHRD_ETHER:
+ return "ETHER";
+ case ARPHRD_EETHER:
+ return "EETHER";
+ case ARPHRD_NETROM:
+ return "NETROM";
+ case ARPHRD_AX25:
+ return "AX25";
+ case ARPHRD_PRONET:
+ return "PRONET";
+ case ARPHRD_CHAOS:
+ return "CHAOS";
+ case ARPHRD_IEEE802:
+ return "IEEE802";
+ case ARPHRD_ARCNET:
+ return "ARCNET";
+ case ARPHRD_APPLETLK:
+ return "APPLETLK";
+ case ARPHRD_DLCI:
+ return "DLCI";
+ case ARPHRD_ATM:
+ return "ATM";
+ case ARPHRD_METRICOM:
+ return "METRICOM";
+ case ARPHRD_IEEE1394:
+ return "IEEE1394";
+ case ARPHRD_EUI64:
+ return "EUI64";
+ case ARPHRD_INFINIBAND:
+ return "INFINIBAND";
+ case ARPHRD_SLIP:
+ return "SLIP";
+ case ARPHRD_CSLIP:
+ return "CSLIP";
+ case ARPHRD_SLIP6:
+ return "SLIP6";
+ case ARPHRD_CSLIP6:
+ return "CSLIP6";
+ case ARPHRD_RSRVD:
+ return "RSRVD";
+ case ARPHRD_ADAPT:
+ return "ADAPT";
+ case ARPHRD_ROSE:
+ return "ROSE";
+ case ARPHRD_X25:
+ return "X25";
+ case ARPHRD_PPP:
+ return "PPP";
+ case ARPHRD_HDLC:
+ return "HDLC";
+ case ARPHRD_LAPB:
+ return "LAPB";
+ case ARPHRD_DDCMP:
+ return "DDCMP";
+ case ARPHRD_RAWHDLC:
+ return "RAWHDLC";
+ case ARPHRD_TUNNEL:
+ return "TUNNEL";
+ case ARPHRD_TUNNEL6:
+ return "TUNNEL6";
+ case ARPHRD_FRAD:
+ return "FRAD";
+ case ARPHRD_SKIP:
+ return "SKIP";
+ case ARPHRD_LOOPBACK:
+ return "LOOPBACK";
+ case ARPHRD_LOCALTLK:
+ return "LOCALTLK";
+ case ARPHRD_FDDI:
+ return "FDDI";
+ case ARPHRD_BIF:
+ return "BIF";
+ case ARPHRD_SIT:
+ return "SIT";
+ case ARPHRD_IPDDP:
+ return "IPDDP";
+ case ARPHRD_IPGRE:
+ return "IPGRE";
+ case ARPHRD_PIMREG:
+ return "PIMREG";
+ case ARPHRD_HIPPI:
+ return "HIPPI";
+ case ARPHRD_ASH:
+ return "ASH";
+ case ARPHRD_ECONET:
+ return "ECONET";
+ case ARPHRD_IRDA:
+ return "IRDA";
+ case ARPHRD_FCPP:
+ return "FCPP";
+ case ARPHRD_FCAL:
+ return "FCAL";
+ case ARPHRD_FCPL:
+ return "FCPL";
+ case ARPHRD_FCFABRIC:
+ return "FCFABRIC";
+ case ARPHRD_IEEE802_TR:
+ return "IEEE802_TR";
+ case ARPHRD_IEEE80211:
+ return "IEEE80211";
+ case ARPHRD_IEEE80211_PRISM:
+ return "IEEE80211_PRISM";
+ case ARPHRD_IEEE80211_RADIOTAP:
+ return "IEEE80211_RADIOTAP";
+ case ARPHRD_IEEE802154:
+ return "IEEE802154";
+#ifdef ARPHRD_VSOCKMON
+ case ARPHRD_VSOCKMON:
+ return "VSOCKMON";
+#endif /* ARPHRD_VSOCKMON */
+ case ARPHRD_VOID:
+ return "VOID";
+ case ARPHRD_NONE:
+ return "NONE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *ifla_pdr_type2str(int type)
+{
+ switch (type) {
+ case IFLA_PROTO_DOWN_REASON_UNSPEC:
+ return "UNSPEC";
+ case IFLA_PROTO_DOWN_REASON_MASK:
+ return "MASK";
+ case IFLA_PROTO_DOWN_REASON_VALUE:
+ return "VALUE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *ifla_info_type2str(int type)
+{
+ switch (type) {
+ case IFLA_INFO_UNSPEC:
+ return "UNSPEC";
+ case IFLA_INFO_KIND:
+ return "KIND";
+ case IFLA_INFO_DATA:
+ return "DATA";
+ case IFLA_INFO_XSTATS:
+ return "XSTATS";
+ case IFLA_INFO_SLAVE_KIND:
+ return "SLAVE_KIND";
+ case IFLA_INFO_SLAVE_DATA:
+ return "SLAVE_DATA";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *rta_type2str(int type)
+{
+ switch (type) {
+ case IFLA_UNSPEC:
+ return "UNSPEC";
+ case IFLA_ADDRESS:
+ return "ADDRESS";
+ case IFLA_BROADCAST:
+ return "BROADCAST";
+ case IFLA_IFNAME:
+ return "IFNAME";
+ case IFLA_MTU:
+ return "MTU";
+ case IFLA_LINK:
+ return "LINK";
+ case IFLA_QDISC:
+ return "QDISC";
+ case IFLA_STATS:
+ return "STATS";
+ case IFLA_COST:
+ return "COST";
+ case IFLA_PRIORITY:
+ return "PRIORITY";
+ case IFLA_MASTER:
+ return "MASTER";
+ case IFLA_WIRELESS:
+ return "WIRELESS";
+ case IFLA_PROTINFO:
+ return "PROTINFO";
+ case IFLA_TXQLEN:
+ return "TXQLEN";
+ case IFLA_MAP:
+ return "MAP";
+ case IFLA_WEIGHT:
+ return "WEIGHT";
+ case IFLA_OPERSTATE:
+ return "OPERSTATE";
+ case IFLA_LINKMODE:
+ return "LINKMODE";
+ case IFLA_LINKINFO:
+ return "LINKINFO";
+ case IFLA_NET_NS_PID:
+ return "NET_NS_PID";
+ case IFLA_IFALIAS:
+ return "IFALIAS";
+ case IFLA_NUM_VF:
+ return "NUM_VF";
+ case IFLA_VFINFO_LIST:
+ return "VFINFO_LIST";
+ case IFLA_STATS64:
+ return "STATS64";
+ case IFLA_VF_PORTS:
+ return "VF_PORTS";
+ case IFLA_PORT_SELF:
+ return "PORT_SELF";
+ case IFLA_AF_SPEC:
+ return "AF_SPEC";
+ case IFLA_GROUP:
+ return "GROUP";
+ case IFLA_NET_NS_FD:
+ return "NET_NS_FD";
+ case IFLA_EXT_MASK:
+ return "EXT_MASK";
+ case IFLA_PROMISCUITY:
+ return "PROMISCUITY";
+ case IFLA_NUM_TX_QUEUES:
+ return "NUM_TX_QUEUES";
+ case IFLA_NUM_RX_QUEUES:
+ return "NUM_RX_QUEUES";
+ case IFLA_CARRIER:
+ return "CARRIER";
+ case IFLA_PHYS_PORT_ID:
+ return "PHYS_PORT_ID";
+ case IFLA_CARRIER_CHANGES:
+ return "CARRIER_CHANGES";
+ case IFLA_PHYS_SWITCH_ID:
+ return "PHYS_SWITCH_ID";
+ case IFLA_LINK_NETNSID:
+ return "LINK_NETNSID";
+ case IFLA_PHYS_PORT_NAME:
+ return "PHYS_PORT_NAME";
+ case IFLA_PROTO_DOWN:
+ return "PROTO_DOWN";
+#ifdef IFLA_GSO_MAX_SEGS
+ case IFLA_GSO_MAX_SEGS:
+ return "GSO_MAX_SEGS";
+#endif /* IFLA_GSO_MAX_SEGS */
+#ifdef IFLA_GSO_MAX_SIZE
+ case IFLA_GSO_MAX_SIZE:
+ return "GSO_MAX_SIZE";
+#endif /* IFLA_GSO_MAX_SIZE */
+#ifdef IFLA_PAD
+ case IFLA_PAD:
+ return "PAD";
+#endif /* IFLA_PAD */
+#ifdef IFLA_XDP
+ case IFLA_XDP:
+ return "XDP";
+#endif /* IFLA_XDP */
+#ifdef IFLA_EVENT
+ case IFLA_EVENT:
+ return "EVENT";
+#endif /* IFLA_EVENT */
+ case IFLA_PROTO_DOWN_REASON:
+ return "PROTO_DOWN_REASON";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *rtm_type2str(int type)
+{
+ switch (type) {
+ case RTN_UNSPEC:
+ return "UNSPEC";
+ case RTN_UNICAST:
+ return "UNICAST";
+ case RTN_LOCAL:
+ return "LOCAL";
+ case RTN_BROADCAST:
+ return "BROADCAST";
+ case RTN_ANYCAST:
+ return "ANYCAST";
+ case RTN_MULTICAST:
+ return "MULTICAST";
+ case RTN_BLACKHOLE:
+ return "BLACKHOLE";
+ case RTN_UNREACHABLE:
+ return "UNREACHABLE";
+ case RTN_PROHIBIT:
+ return "PROHIBIT";
+ case RTN_THROW:
+ return "THROW";
+ case RTN_NAT:
+ return "NAT";
+ case RTN_XRESOLVE:
+ return "XRESOLVE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *rtm_protocol2str(int type)
+{
+ switch (type) {
+ case RTPROT_UNSPEC:
+ return "UNSPEC";
+ case RTPROT_REDIRECT:
+ return "REDIRECT";
+ case RTPROT_KERNEL:
+ return "KERNEL";
+ case RTPROT_BOOT:
+ return "BOOT";
+ case RTPROT_STATIC:
+ return "STATIC";
+ case RTPROT_GATED:
+ return "GATED";
+ case RTPROT_RA:
+ return "RA";
+ case RTPROT_MRT:
+ return "MRT";
+ case RTPROT_ZEBRA:
+ return "ZEBRA";
+ case RTPROT_BGP:
+ return "BGP";
+ case RTPROT_ISIS:
+ return "ISIS";
+ case RTPROT_OSPF:
+ return "OSPF";
+ case RTPROT_BIRD:
+ return "BIRD";
+ case RTPROT_DNROUTED:
+ return "DNROUTED";
+ case RTPROT_XORP:
+ return "XORP";
+ case RTPROT_NTK:
+ return "NTK";
+ case RTPROT_DHCP:
+ return "DHCP";
+ case RTPROT_MROUTED:
+ return "MROUTED";
+ case RTPROT_BABEL:
+ return "BABEL";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *rtm_scope2str(int type)
+{
+ switch (type) {
+ case RT_SCOPE_UNIVERSE:
+ return "UNIVERSE";
+ case RT_SCOPE_SITE:
+ return "SITE";
+ case RT_SCOPE_LINK:
+ return "LINK";
+ case RT_SCOPE_HOST:
+ return "HOST";
+ case RT_SCOPE_NOWHERE:
+ return "NOWHERE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *rtm_rta2str(int type)
+{
+ switch (type) {
+ case RTA_UNSPEC:
+ return "UNSPEC";
+ case RTA_DST:
+ return "DST";
+ case RTA_SRC:
+ return "SRC";
+ case RTA_IIF:
+ return "IIF";
+ case RTA_OIF:
+ return "OIF";
+ case RTA_GATEWAY:
+ return "GATEWAY";
+ case RTA_PRIORITY:
+ return "PRIORITY";
+ case RTA_PREF:
+ return "PREF";
+ case RTA_PREFSRC:
+ return "PREFSRC";
+ case RTA_MARK:
+ return "MARK";
+ case RTA_METRICS:
+ return "METRICS";
+ case RTA_MULTIPATH:
+ return "MULTIPATH";
+ case RTA_PROTOINFO:
+ return "PROTOINFO";
+ case RTA_FLOW:
+ return "FLOW";
+ case RTA_CACHEINFO:
+ return "CACHEINFO";
+ case RTA_TABLE:
+ return "TABLE";
+ case RTA_MFC_STATS:
+ return "MFC_STATS";
+ case RTA_NH_ID:
+ return "NH_ID";
+ case RTA_EXPIRES:
+ return "EXPIRES";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *neigh_rta2str(int type)
+{
+ switch (type) {
+ case NDA_UNSPEC:
+ return "UNSPEC";
+ case NDA_DST:
+ return "DST";
+ case NDA_LLADDR:
+ return "LLADDR";
+ case NDA_CACHEINFO:
+ return "CACHEINFO";
+ case NDA_PROBES:
+ return "PROBES";
+ case NDA_VLAN:
+ return "VLAN";
+ case NDA_PORT:
+ return "PORT";
+ case NDA_VNI:
+ return "VNI";
+ case NDA_IFINDEX:
+ return "IFINDEX";
+ case NDA_MASTER:
+ return "MASTER";
+ case NDA_LINK_NETNSID:
+ return "LINK_NETNSID";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *ifa_rta2str(int type)
+{
+ switch (type) {
+ case IFA_UNSPEC:
+ return "UNSPEC";
+ case IFA_ADDRESS:
+ return "ADDRESS";
+ case IFA_LOCAL:
+ return "LOCAL";
+ case IFA_LABEL:
+ return "LABEL";
+ case IFA_BROADCAST:
+ return "BROADCAST";
+ case IFA_ANYCAST:
+ return "ANYCAST";
+ case IFA_CACHEINFO:
+ return "CACHEINFO";
+ case IFA_MULTICAST:
+ return "MULTICAST";
+ case IFA_FLAGS:
+ return "FLAGS";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *nhm_rta2str(int type)
+{
+ switch (type) {
+ case NHA_UNSPEC:
+ return "UNSPEC";
+ case NHA_ID:
+ return "ID";
+ case NHA_GROUP:
+ return "GROUP";
+ case NHA_GROUP_TYPE:
+ return "GROUP_TYPE";
+ case NHA_BLACKHOLE:
+ return "BLACKHOLE";
+ case NHA_OIF:
+ return "OIF";
+ case NHA_GATEWAY:
+ return "GATEWAY";
+ case NHA_ENCAP_TYPE:
+ return "ENCAP_TYPE";
+ case NHA_ENCAP:
+ return "ENCAP";
+ case NHA_GROUPS:
+ return "GROUPS";
+ case NHA_MASTER:
+ return "MASTER";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *frh_rta2str(int type)
+{
+ switch (type) {
+ case FRA_DST:
+ return "DST";
+ case FRA_SRC:
+ return "SRC";
+ case FRA_IIFNAME:
+ return "IIFNAME";
+ case FRA_GOTO:
+ return "GOTO";
+ case FRA_UNUSED2:
+ return "UNUSED2";
+ case FRA_PRIORITY:
+ return "PRIORITY";
+ case FRA_UNUSED3:
+ return "UNUSED3";
+ case FRA_UNUSED4:
+ return "UNUSED4";
+ case FRA_UNUSED5:
+ return "UNUSED5";
+ case FRA_FWMARK:
+ return "FWMARK";
+ case FRA_FLOW:
+ return "FLOW";
+ case FRA_TUN_ID:
+ return "TUN_ID";
+ case FRA_SUPPRESS_IFGROUP:
+ return "SUPPRESS_IFGROUP";
+ case FRA_SUPPRESS_PREFIXLEN:
+ return "SUPPRESS_PREFIXLEN";
+ case FRA_TABLE:
+ return "TABLE";
+ case FRA_FWMASK:
+ return "FWMASK";
+ case FRA_OIFNAME:
+ return "OIFNAME";
+ case FRA_PAD:
+ return "PAD";
+ case FRA_L3MDEV:
+ return "L3MDEV";
+ case FRA_UID_RANGE:
+ return "UID_RANGE";
+ case FRA_PROTOCOL:
+ return "PROTOCOL";
+ case FRA_IP_PROTO:
+ return "IP_PROTO";
+ case FRA_SPORT_RANGE:
+ return "SPORT_RANGE";
+ case FRA_DPORT_RANGE:
+ return "DPORT_RANGE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *frh_action2str(uint8_t action)
+{
+ switch (action) {
+ case FR_ACT_TO_TBL:
+ return "TO_TBL";
+ case FR_ACT_GOTO:
+ return "GOTO";
+ case FR_ACT_NOP:
+ return "NOP";
+ case FR_ACT_RES3:
+ return "RES3";
+ case FR_ACT_RES4:
+ return "RES4";
+ case FR_ACT_BLACKHOLE:
+ return "BLACKHOLE";
+ case FR_ACT_UNREACHABLE:
+ return "UNREACHABLE";
+ case FR_ACT_PROHIBIT:
+ return "PROHIBIT";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static const char *ncm_rta2str(int type)
+{
+ switch (type) {
+ case NETCONFA_UNSPEC:
+ return "UNSPEC";
+ case NETCONFA_IFINDEX:
+ return "IFINDEX";
+ case NETCONFA_FORWARDING:
+ return "FORWARDING";
+ case NETCONFA_RP_FILTER:
+ return "RP_FILTER";
+ case NETCONFA_MC_FORWARDING:
+ return "MCAST";
+ case NETCONFA_PROXY_NEIGH:
+ return "PROXY_NEIGH";
+ case NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN:
+ return "IGNORE_LINKDOWN";
+ case NETCONFA_INPUT:
+ return "MPLS";
+ case NETCONFA_BC_FORWARDING:
+ return "BCAST";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void dump_on_off(uint32_t ival, const char *prefix)
+{
+ zlog_debug("%s%s", prefix, (ival != 0) ? "on" : "off");
+}
+
+static inline void flag_write(int flags, int flag, const char *flagstr,
+ char *buf, size_t buflen)
+{
+ if (CHECK_FLAG(flags, flag) == 0)
+ return;
+
+ if (buf[0])
+ strlcat(buf, ",", buflen);
+
+ strlcat(buf, flagstr, buflen);
+}
+
+const char *nlmsg_flags2str(uint16_t flags, char *buf, size_t buflen)
+{
+ const char *bufp = buf;
+
+ *buf = 0;
+ /* Specific flags. */
+ flag_write(flags, NLM_F_REQUEST, "REQUEST", buf, buflen);
+ flag_write(flags, NLM_F_MULTI, "MULTI", buf, buflen);
+ flag_write(flags, NLM_F_ACK, "ACK", buf, buflen);
+ flag_write(flags, NLM_F_ECHO, "ECHO", buf, buflen);
+ flag_write(flags, NLM_F_DUMP, "DUMP", buf, buflen);
+
+ /* Netlink family type dependent. */
+ flag_write(flags, 0x0100, "(ROOT|REPLACE|CAPPED)", buf, buflen);
+ flag_write(flags, 0x0200, "(MATCH|EXCLUDE|ACK_TLVS)", buf, buflen);
+ flag_write(flags, 0x0400, "(ATOMIC|CREATE)", buf, buflen);
+ flag_write(flags, 0x0800, "(DUMP|APPEND)", buf, buflen);
+
+ return (bufp);
+}
+
+const char *if_flags2str(uint32_t flags, char *buf, size_t buflen)
+{
+ const char *bufp = buf;
+
+ *buf = 0;
+ flag_write(flags, IFF_UP, "UP", buf, buflen);
+ flag_write(flags, IFF_BROADCAST, "BROADCAST", buf, buflen);
+ flag_write(flags, IFF_DEBUG, "DEBUG", buf, buflen);
+ flag_write(flags, IFF_LOOPBACK, "LOOPBACK", buf, buflen);
+ flag_write(flags, IFF_POINTOPOINT, "POINTOPOINT", buf, buflen);
+ flag_write(flags, IFF_NOTRAILERS, "NOTRAILERS", buf, buflen);
+ flag_write(flags, IFF_RUNNING, "RUNNING", buf, buflen);
+ flag_write(flags, IFF_NOARP, "NOARP", buf, buflen);
+ flag_write(flags, IFF_PROMISC, "PROMISC", buf, buflen);
+ flag_write(flags, IFF_ALLMULTI, "ALLMULTI", buf, buflen);
+ flag_write(flags, IFF_MASTER, "MASTER", buf, buflen);
+ flag_write(flags, IFF_SLAVE, "SLAVE", buf, buflen);
+ flag_write(flags, IFF_MULTICAST, "MULTICAST", buf, buflen);
+ flag_write(flags, IFF_PORTSEL, "PORTSEL", buf, buflen);
+ flag_write(flags, IFF_AUTOMEDIA, "AUTOMEDIA", buf, buflen);
+ flag_write(flags, IFF_DYNAMIC, "DYNAMIC", buf, buflen);
+
+ return (bufp);
+}
+
+const char *rtm_flags2str(uint32_t flags, char *buf, size_t buflen)
+{
+ const char *bufp = buf;
+
+ *buf = 0;
+ flag_write(flags, RTM_F_NOTIFY, "NOTIFY", buf, buflen);
+ flag_write(flags, RTM_F_CLONED, "CLONED", buf, buflen);
+ flag_write(flags, RTM_F_EQUALIZE, "EQUALIZE", buf, buflen);
+
+ return (bufp);
+}
+
+const char *neigh_state2str(uint32_t flags, char *buf, size_t buflen)
+{
+ const char *bufp = buf;
+
+ *buf = 0;
+ flag_write(flags, NUD_INCOMPLETE, "INCOMPLETE", buf, buflen);
+ flag_write(flags, NUD_REACHABLE, "REACHABLE", buf, buflen);
+ flag_write(flags, NUD_STALE, "STALE", buf, buflen);
+ flag_write(flags, NUD_DELAY, "DELAY", buf, buflen);
+ flag_write(flags, NUD_PROBE, "PROBE", buf, buflen);
+ flag_write(flags, NUD_FAILED, "FAILED", buf, buflen);
+ flag_write(flags, NUD_NOARP, "NOARP", buf, buflen);
+ flag_write(flags, NUD_PERMANENT, "PERMANENT", buf, buflen);
+
+ return (bufp);
+}
+
+const char *neigh_flags2str(uint32_t flags, char *buf, size_t buflen)
+{
+ const char *bufp = buf;
+
+ *buf = 0;
+ flag_write(flags, NTF_USE, "USE", buf, buflen);
+ flag_write(flags, NTF_SELF, "SELF", buf, buflen);
+ flag_write(flags, NTF_MASTER, "MASTER", buf, buflen);
+ flag_write(flags, NTF_PROXY, "PROXY", buf, buflen);
+ flag_write(flags, NTF_EXT_LEARNED, "EXT_LEARNED", buf, buflen);
+#ifdef NTF_OFFLOADED
+ flag_write(flags, NTF_OFFLOADED, "OFFLOADED", buf, buflen);
+#endif /* NTF_OFFLOADED */
+ flag_write(flags, NTF_ROUTER, "ROUTER", buf, buflen);
+
+ return (bufp);
+}
+
+const char *ifa_flags2str(uint32_t flags, char *buf, size_t buflen)
+{
+ const char *bufp = buf;
+
+ *buf = 0;
+ flag_write(flags, IFA_F_SECONDARY, "SECONDARY", buf, buflen);
+ flag_write(flags, IFA_F_NODAD, "NODAD", buf, buflen);
+ flag_write(flags, IFA_F_OPTIMISTIC, "OPTIMISTIC", buf, buflen);
+ flag_write(flags, IFA_F_DADFAILED, "DADFAILED", buf, buflen);
+ flag_write(flags, IFA_F_HOMEADDRESS, "HOMEADDRESS", buf, buflen);
+ flag_write(flags, IFA_F_DEPRECATED, "DEPRECATED", buf, buflen);
+ flag_write(flags, IFA_F_TENTATIVE, "TENTATIVE", buf, buflen);
+ flag_write(flags, IFA_F_PERMANENT, "PERMANENT", buf, buflen);
+ flag_write(flags, IFA_F_MANAGETEMPADDR, "MANAGETEMPADDR", buf, buflen);
+ flag_write(flags, IFA_F_NOPREFIXROUTE, "NOPREFIXROUTE", buf, buflen);
+ flag_write(flags, IFA_F_MCAUTOJOIN, "MCAUTOJOIN", buf, buflen);
+ flag_write(flags, IFA_F_STABLE_PRIVACY, "STABLE_PRIVACY", buf, buflen);
+
+ return (bufp);
+}
+
+const char *nh_flags2str(uint32_t flags, char *buf, size_t buflen)
+{
+ const char *bufp = buf;
+
+ *buf = 0;
+ flag_write(flags, RTNH_F_DEAD, "DEAD", buf, buflen);
+ flag_write(flags, RTNH_F_PERVASIVE, "PERVASIVE", buf, buflen);
+ flag_write(flags, RTNH_F_ONLINK, "ONLINK", buf, buflen);
+ flag_write(flags, RTNH_F_OFFLOAD, "OFFLOAD", buf, buflen);
+ flag_write(flags, RTNH_F_LINKDOWN, "LINKDOWN", buf, buflen);
+ flag_write(flags, RTNH_F_UNRESOLVED, "UNRESOLVED", buf, buflen);
+
+ return (bufp);
+}
+
+/*
+ * Netlink abstractions.
+ */
+static void nllink_pdr_dump(struct rtattr *rta, size_t msglen)
+{
+ size_t plen;
+ uint32_t u32v;
+
+next_rta:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+ zlog_debug(" linkinfo [len=%d (payload=%zu) type=(%d) %s]",
+ rta->rta_len, plen, rta->rta_type,
+ ifla_pdr_type2str(rta->rta_type));
+ switch (rta->rta_type) {
+ case IFLA_PROTO_DOWN_REASON_MASK:
+ case IFLA_PROTO_DOWN_REASON_VALUE:
+ if (plen < sizeof(uint32_t)) {
+ zlog_debug(" invalid length");
+ break;
+ }
+
+ u32v = *(uint32_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u32v);
+ break;
+
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
+static void nllink_linkinfo_dump(struct rtattr *rta, size_t msglen)
+{
+ size_t plen;
+ char dbuf[128];
+
+next_rta:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+ zlog_debug(" linkinfo [len=%d (payload=%zu) type=(%d) %s]",
+ rta->rta_len, plen, rta->rta_type,
+ ifla_info_type2str(rta->rta_type));
+ switch (rta->rta_type) {
+ case IFLA_INFO_KIND:
+ if (plen == 0) {
+ zlog_debug(" invalid length");
+ break;
+ }
+
+ snprintf(dbuf, sizeof(dbuf), "%s", (char *)RTA_DATA(rta));
+ zlog_debug(" %s", dbuf);
+ break;
+ case IFLA_INFO_SLAVE_KIND:
+ if (plen == 0) {
+ zlog_debug(" invalid length");
+ break;
+ }
+
+ snprintf(dbuf, sizeof(dbuf), "%s", (char *)RTA_DATA(rta));
+ zlog_debug(" %s", dbuf);
+ break;
+
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
+static void nllink_dump(struct ifinfomsg *ifi, size_t msglen)
+{
+ uint8_t *datap;
+ struct rtattr *rta;
+ size_t plen, it;
+ uint32_t u32v;
+ uint8_t u8v;
+ char bytestr[16];
+ char dbuf[128];
+ unsigned short rta_type;
+
+ /* Get the first attribute and go from there. */
+ rta = IFLA_RTA(ifi);
+next_rta:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+ rta_type = rta->rta_type & ~NLA_F_NESTED;
+ zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
+ plen, rta_type, rta_type2str(rta_type));
+ switch (rta_type) {
+ case IFLA_IFALIAS:
+ if (plen == 0) {
+ zlog_debug(" invalid length");
+ break;
+ }
+
+ snprintf(dbuf, sizeof(dbuf), "%s", (char *)RTA_DATA(rta));
+ zlog_debug(" %s", dbuf);
+ break;
+
+ case IFLA_MTU:
+ case IFLA_TXQLEN:
+ case IFLA_NUM_TX_QUEUES:
+ case IFLA_NUM_RX_QUEUES:
+ case IFLA_GROUP:
+ case IFLA_PROMISCUITY:
+#ifdef IFLA_GSO_MAX_SEGS
+ case IFLA_GSO_MAX_SEGS:
+#endif /* IFLA_GSO_MAX_SEGS */
+#ifdef IFLA_GSO_MAX_SIZE
+ case IFLA_GSO_MAX_SIZE:
+#endif /* IFLA_GSO_MAX_SIZE */
+ case IFLA_CARRIER_CHANGES:
+ case IFLA_MASTER:
+ case IFLA_LINK:
+ if (plen < sizeof(uint32_t)) {
+ zlog_debug(" invalid length");
+ break;
+ }
+
+ u32v = *(uint32_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u32v);
+ break;
+
+ case IFLA_PROTO_DOWN:
+ if (plen < sizeof(uint8_t)) {
+ zlog_debug(" invalid length");
+ break;
+ }
+
+ u8v = *(uint8_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u8v);
+ break;
+ case IFLA_ADDRESS:
+ datap = RTA_DATA(rta);
+ dbuf[0] = 0;
+ for (it = 0; it < plen; it++) {
+ snprintf(bytestr, sizeof(bytestr), "%02X:", *datap);
+ strlcat(dbuf, bytestr, sizeof(dbuf));
+ datap++;
+ }
+ /* Remove trailing ':'. */
+ if (dbuf[0])
+ dbuf[strlen(dbuf) - 1] = 0;
+
+ zlog_debug(" %s", dbuf[0] ? dbuf : "<empty>");
+ break;
+
+ case IFLA_LINKINFO:
+ nllink_linkinfo_dump(RTA_DATA(rta), plen);
+ break;
+
+ case IFLA_PROTO_DOWN_REASON:
+ nllink_pdr_dump(RTA_DATA(rta), plen);
+ break;
+
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
+static void nlroute_dump(struct rtmsg *rtm, size_t msglen)
+{
+ struct rta_mfc_stats *mfc_stats;
+ struct rtattr *rta;
+ size_t plen;
+ uint32_t u32v;
+ uint64_t u64v;
+
+ /* Get the first attribute and go from there. */
+ rta = RTM_RTA(rtm);
+next_rta:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+ zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
+ plen, rta->rta_type & NLA_TYPE_MASK,
+ rtm_rta2str(rta->rta_type & NLA_TYPE_MASK));
+ switch (rta->rta_type & NLA_TYPE_MASK) {
+ case RTA_IIF:
+ case RTA_OIF:
+ case RTA_PRIORITY:
+ case RTA_TABLE:
+ case RTA_NH_ID:
+ u32v = *(uint32_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u32v);
+ break;
+
+ case RTA_EXPIRES:
+ u64v = *(uint64_t *)RTA_DATA(rta);
+ zlog_debug(" %" PRIu64, u64v);
+ break;
+
+ case RTA_GATEWAY:
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_PREFSRC:
+ switch (plen) {
+ case sizeof(struct in_addr):
+ zlog_debug(" %pI4",
+ (struct in_addr *)RTA_DATA(rta));
+ break;
+ case sizeof(struct in6_addr):
+ zlog_debug(" %pI6",
+ (struct in6_addr *)RTA_DATA(rta));
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RTA_MFC_STATS:
+ mfc_stats = (struct rta_mfc_stats *)RTA_DATA(rta);
+ zlog_debug(" pkts=%ju bytes=%ju wrong_if=%ju",
+ (uintmax_t)mfc_stats->mfcs_packets,
+ (uintmax_t)mfc_stats->mfcs_bytes,
+ (uintmax_t)mfc_stats->mfcs_wrong_if);
+ break;
+
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
+static void nlneigh_dump(struct ndmsg *ndm, size_t msglen)
+{
+ struct rtattr *rta;
+ uint8_t *datap;
+ size_t plen, it;
+ uint16_t vid;
+ char bytestr[16];
+ char dbuf[128];
+ unsigned short rta_type;
+
+#ifndef NDA_RTA
+#define NDA_RTA(ndm) \
+ /* struct ndmsg *ndm; */ \
+ ((struct rtattr *)(((uint8_t *)(ndm)) \
+ + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif /* NDA_RTA */
+
+ /* Get the first attribute and go from there. */
+ rta = NDA_RTA(ndm);
+next_rta:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+ rta_type = rta->rta_type & ~NLA_F_NESTED;
+ zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
+ plen, rta->rta_type, neigh_rta2str(rta_type));
+ switch (rta_type) {
+ case NDA_LLADDR:
+ datap = RTA_DATA(rta);
+ dbuf[0] = 0;
+ for (it = 0; it < plen; it++) {
+ snprintf(bytestr, sizeof(bytestr), "%02X:", *datap);
+ strlcat(dbuf, bytestr, sizeof(dbuf));
+ datap++;
+ }
+ /* Remove trailing ':'. */
+ if (dbuf[0])
+ dbuf[strlen(dbuf) - 1] = 0;
+
+ zlog_debug(" %s", dbuf[0] ? dbuf : "<empty>");
+ break;
+
+ case NDA_DST:
+ switch (plen) {
+ case sizeof(struct in_addr):
+ zlog_debug(" %pI4",
+ (struct in_addr *)RTA_DATA(rta));
+ break;
+ case sizeof(struct in6_addr):
+ zlog_debug(" %pI6",
+ (struct in6_addr *)RTA_DATA(rta));
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case NDA_VLAN:
+ vid = *(uint16_t *)RTA_DATA(rta);
+ zlog_debug(" %d", vid);
+ break;
+
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
+static void nlifa_dump(struct ifaddrmsg *ifa, size_t msglen)
+{
+ struct rtattr *rta;
+ size_t plen;
+ uint32_t u32v;
+
+ /* Get the first attribute and go from there. */
+ rta = IFA_RTA(ifa);
+next_rta:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+ zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
+ plen, rta->rta_type, ifa_rta2str(rta->rta_type));
+ switch (rta->rta_type) {
+ case IFA_UNSPEC:
+ u32v = *(uint32_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u32v);
+ break;
+
+ case IFA_LABEL:
+ zlog_debug(" %s", (const char *)RTA_DATA(rta));
+ break;
+
+ case IFA_ADDRESS:
+ case IFA_LOCAL:
+ case IFA_BROADCAST:
+ switch (plen) {
+ case 4:
+ zlog_debug(" %pI4",
+ (struct in_addr *)RTA_DATA(rta));
+ break;
+ case 16:
+ zlog_debug(" %pI6",
+ (struct in6_addr *)RTA_DATA(rta));
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
+static void nltnl_dump(struct tunnel_msg *tnlm, size_t msglen)
+{
+ struct rtattr *attr;
+ vni_t vni_start = 0, vni_end = 0;
+ struct rtattr *ttb[VXLAN_VNIFILTER_ENTRY_MAX + 1];
+ uint8_t rta_type;
+
+ attr = TUNNEL_RTA(tnlm);
+next_attr:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(attr, msglen) == 0)
+ return;
+
+ rta_type = attr->rta_type & NLA_TYPE_MASK;
+
+ if (rta_type != VXLAN_VNIFILTER_ENTRY) {
+ attr = RTA_NEXT(attr, msglen);
+ goto next_attr;
+ }
+
+ memset(ttb, 0, sizeof(ttb));
+
+ netlink_parse_rtattr_flags(ttb, VXLAN_VNIFILTER_ENTRY_MAX,
+ RTA_DATA(attr), RTA_PAYLOAD(attr),
+ NLA_F_NESTED);
+
+ if (ttb[VXLAN_VNIFILTER_ENTRY_START])
+ vni_start =
+ *(uint32_t *)RTA_DATA(ttb[VXLAN_VNIFILTER_ENTRY_START]);
+
+ if (ttb[VXLAN_VNIFILTER_ENTRY_END])
+ vni_end = *(uint32_t *)RTA_DATA(ttb[VXLAN_VNIFILTER_ENTRY_END]);
+ zlog_debug(" vni_start %u, vni_end %u", vni_start, vni_end);
+
+ attr = RTA_NEXT(attr, msglen);
+ goto next_attr;
+}
+
+static const char *lwt_type2str(uint16_t type)
+{
+ switch (type) {
+ case LWTUNNEL_ENCAP_NONE:
+ return "NONE";
+ case LWTUNNEL_ENCAP_MPLS:
+ return "MPLS";
+ case LWTUNNEL_ENCAP_IP:
+ return "IPv4";
+ case LWTUNNEL_ENCAP_ILA:
+ return "ILA";
+ case LWTUNNEL_ENCAP_IP6:
+ return "IPv6";
+ case LWTUNNEL_ENCAP_SEG6:
+ return "SEG6";
+ case LWTUNNEL_ENCAP_BPF:
+ return "BPF";
+ case LWTUNNEL_ENCAP_SEG6_LOCAL:
+ return "SEG6_LOCAL";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static const char *nhg_type2str(uint16_t type)
+{
+ switch (type) {
+ case NEXTHOP_GRP_TYPE_MPATH:
+ return "MULTIPATH";
+ case NEXTHOP_GRP_TYPE_RES:
+ return "RESILIENT MULTIPATH";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void nlnh_dump(struct nhmsg *nhm, size_t msglen)
+{
+ struct rtattr *rta;
+ int ifindex;
+ size_t plen;
+ uint16_t u16v;
+ uint32_t u32v;
+ unsigned long count, i;
+ struct nexthop_grp *nhgrp;
+ unsigned short rta_type;
+
+ rta = RTM_NHA(nhm);
+
+next_rta:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+ rta_type = rta->rta_type & ~NLA_F_NESTED;
+ zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
+ plen, rta->rta_type, nhm_rta2str(rta_type));
+ switch (rta_type) {
+ case NHA_ID:
+ u32v = *(uint32_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u32v);
+ break;
+ case NHA_GROUP:
+ nhgrp = (struct nexthop_grp *)RTA_DATA(rta);
+ count = (RTA_PAYLOAD(rta) / sizeof(*nhgrp));
+ if (count == 0
+ || (count * sizeof(*nhgrp)) != RTA_PAYLOAD(rta)) {
+ zlog_debug(" invalid nexthop group received");
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ zlog_debug(" id %d weight %d", nhgrp[i].id,
+ nhgrp[i].weight);
+ break;
+ case NHA_ENCAP_TYPE:
+ u16v = *(uint16_t *)RTA_DATA(rta);
+ zlog_debug(" %s", lwt_type2str(u16v));
+ break;
+ case NHA_GROUP_TYPE:
+ u16v = *(uint16_t *)RTA_DATA(rta);
+ zlog_debug(" %s", nhg_type2str(u16v));
+ break;
+ case NHA_BLACKHOLE:
+ /* NOTHING */
+ break;
+ case NHA_OIF:
+ ifindex = *(int *)RTA_DATA(rta);
+ zlog_debug(" %d", ifindex);
+ break;
+ case NHA_GATEWAY:
+ switch (nhm->nh_family) {
+ case AF_INET:
+ zlog_debug(" %pI4",
+ (struct in_addr *)RTA_DATA(rta));
+ break;
+ case AF_INET6:
+ zlog_debug(" %pI6",
+ (struct in6_addr *)RTA_DATA(rta));
+ break;
+
+ default:
+ zlog_debug(" invalid family %d", nhm->nh_family);
+ break;
+ }
+ break;
+ case NHA_ENCAP:
+ /* TODO: handle MPLS labels. */
+ zlog_debug(" unparsed MPLS labels");
+ break;
+ case NHA_GROUPS:
+ /* TODO: handle this message. */
+ zlog_debug(" unparsed GROUPS message");
+ break;
+
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
+static void nlrule_dump(struct fib_rule_hdr *frh, size_t msglen)
+{
+ struct rtattr *rta;
+ size_t plen;
+ uint8_t u8v;
+ uint32_t u32v;
+ int32_t s32v;
+ uint64_t u64v;
+ char dbuf[128];
+ struct fib_rule_uid_range *u_range;
+ struct fib_rule_port_range *p_range;
+
+ /* Get the first attribute and go from there. */
+ rta = RTM_RTA(frh);
+next_rta:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+ zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
+ plen, rta->rta_type, frh_rta2str(rta->rta_type));
+ switch (rta->rta_type) {
+ case FRA_DST:
+ case FRA_SRC:
+ switch (plen) {
+ case sizeof(struct in_addr):
+ zlog_debug(" %pI4",
+ (struct in_addr *)RTA_DATA(rta));
+ break;
+ case sizeof(struct in6_addr):
+ zlog_debug(" %pI6",
+ (struct in6_addr *)RTA_DATA(rta));
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case FRA_IIFNAME:
+ case FRA_OIFNAME:
+ snprintf(dbuf, sizeof(dbuf), "%s", (char *)RTA_DATA(rta));
+ zlog_debug(" %s", dbuf);
+ break;
+
+ case FRA_GOTO:
+ case FRA_UNUSED2:
+ case FRA_PRIORITY:
+ case FRA_UNUSED3:
+ case FRA_UNUSED4:
+ case FRA_UNUSED5:
+ case FRA_FWMARK:
+ case FRA_FLOW:
+ case FRA_TABLE:
+ case FRA_FWMASK:
+ u32v = *(uint32_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u32v);
+ break;
+
+ case FRA_SUPPRESS_IFGROUP:
+ case FRA_SUPPRESS_PREFIXLEN:
+ s32v = *(int32_t *)RTA_DATA(rta);
+ zlog_debug(" %d", s32v);
+ break;
+
+ case FRA_TUN_ID:
+ u64v = *(uint64_t *)RTA_DATA(rta);
+ zlog_debug(" %" PRIu64, u64v);
+ break;
+
+ case FRA_L3MDEV:
+ case FRA_PROTOCOL:
+ case FRA_IP_PROTO:
+ u8v = *(uint8_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u8v);
+ break;
+
+ case FRA_UID_RANGE:
+ u_range = (struct fib_rule_uid_range *)RTA_DATA(rta);
+ if (u_range->start == u_range->end)
+ zlog_debug(" %u", u_range->start);
+ else
+ zlog_debug(" %u-%u", u_range->start, u_range->end);
+ break;
+
+ case FRA_SPORT_RANGE:
+ case FRA_DPORT_RANGE:
+ p_range = (struct fib_rule_port_range *)RTA_DATA(rta);
+ if (p_range->start == p_range->end)
+ zlog_debug(" %u", p_range->start);
+ else
+ zlog_debug(" %u-%u", p_range->start, p_range->end);
+ break;
+
+ case FRA_PAD: /* fallthrough */
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
+static const char *tcm_nltype2str(int nltype)
+{
+ switch (nltype) {
+ case RTM_NEWQDISC:
+ case RTM_DELQDISC:
+ return "qdisc";
+ case RTM_NEWTCLASS:
+ case RTM_DELTCLASS:
+ return "tclass";
+ case RTM_NEWTFILTER:
+ case RTM_DELTFILTER:
+ return "tfilter";
+ default:
+ /* should never hit */
+ return "unknown";
+ }
+}
+
+static void nlncm_dump(const struct netconfmsg *ncm, size_t msglen)
+{
+ const struct rtattr *rta;
+ size_t plen;
+ uint32_t ival;
+
+ rta = (void *)((const char *)ncm +
+ NLMSG_ALIGN(sizeof(struct netconfmsg)));
+
+next_rta:
+ /* Check the attr header for valid length. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+
+ zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
+ plen, rta->rta_type, ncm_rta2str(rta->rta_type));
+
+ switch (rta->rta_type) {
+ case NETCONFA_IFINDEX:
+ ival = *(uint32_t *)RTA_DATA(rta);
+ zlog_debug(" %d", (int32_t)ival);
+ break;
+
+ /* Most attrs are just on/off. */
+ case NETCONFA_FORWARDING:
+ case NETCONFA_RP_FILTER:
+ case NETCONFA_MC_FORWARDING:
+ case NETCONFA_PROXY_NEIGH:
+ case NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN:
+ case NETCONFA_INPUT:
+ case NETCONFA_BC_FORWARDING:
+ ival = *(uint32_t *)RTA_DATA(rta);
+ dump_on_off(ival, " ");
+ break;
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
+void nl_dump(void *msg, size_t msglen)
+{
+ struct nlmsghdr *nlmsg = msg;
+ struct nlmsgerr *nlmsgerr;
+ struct rtgenmsg *rtgen;
+ struct ifaddrmsg *ifa;
+ struct ndmsg *ndm;
+ struct rtmsg *rtm;
+ struct nhmsg *nhm;
+ struct netconfmsg *ncm;
+ struct ifinfomsg *ifi;
+ struct tunnel_msg *tnlm;
+ struct fib_rule_hdr *frh;
+ struct tcmsg *tcm;
+
+ char fbuf[128];
+ char ibuf[128];
+
+next_header:
+ zlog_debug(
+ "nlmsghdr [len=%u type=(%d) %s flags=(0x%04x) {%s} seq=%u pid=%u]",
+ nlmsg->nlmsg_len, nlmsg->nlmsg_type,
+ nlmsg_type2str(nlmsg->nlmsg_type), nlmsg->nlmsg_flags,
+ nlmsg_flags2str(nlmsg->nlmsg_flags, fbuf, sizeof(fbuf)),
+ nlmsg->nlmsg_seq, nlmsg->nlmsg_pid);
+
+ switch (nlmsg->nlmsg_type) {
+ /* Generic. */
+ case NLMSG_NOOP:
+ break;
+ case NLMSG_ERROR:
+ nlmsgerr = NLMSG_DATA(nlmsg);
+ zlog_debug(" nlmsgerr [error=(%d) %s]", nlmsgerr->error,
+ strerror(-nlmsgerr->error));
+ break;
+ case NLMSG_DONE:
+ return;
+ case NLMSG_OVERRUN:
+ break;
+
+ /* RTM. */
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+ case RTM_SETLINK:
+ ifi = NLMSG_DATA(nlmsg);
+ zlog_debug(
+ " ifinfomsg [family=%d type=(%d) %s index=%d flags=0x%04x {%s}]",
+ ifi->ifi_family, ifi->ifi_type,
+ ifi_type2str(ifi->ifi_type), ifi->ifi_index,
+ ifi->ifi_flags,
+ if_flags2str(ifi->ifi_flags, ibuf, sizeof(ibuf)));
+ nllink_dump(ifi, nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
+ break;
+ case RTM_GETLINK:
+ rtgen = NLMSG_DATA(nlmsg);
+ zlog_debug(" rtgen [family=(%d) %s]", rtgen->rtgen_family,
+ af_type2str(rtgen->rtgen_family));
+ break;
+
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ case RTM_GETROUTE:
+ rtm = NLMSG_DATA(nlmsg);
+ zlog_debug(
+ " rtmsg [family=(%d) %s dstlen=%d srclen=%d tos=%d table=%d protocol=(%d) %s scope=(%d) %s type=(%d) %s flags=0x%04x {%s}]",
+ rtm->rtm_family, af_type2str(rtm->rtm_family),
+ rtm->rtm_dst_len, rtm->rtm_src_len, rtm->rtm_tos,
+ rtm->rtm_table, rtm->rtm_protocol,
+ rtm_protocol2str(rtm->rtm_protocol), rtm->rtm_scope,
+ rtm_scope2str(rtm->rtm_scope), rtm->rtm_type,
+ rtm_type2str(rtm->rtm_type), rtm->rtm_flags,
+ rtm_flags2str(rtm->rtm_flags, fbuf, sizeof(fbuf)));
+ nlroute_dump(rtm,
+ nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*rtm)));
+ break;
+
+ case RTM_NEWNEIGH:
+ case RTM_DELNEIGH:
+ ndm = NLMSG_DATA(nlmsg);
+ zlog_debug(
+ " ndm [family=%d (%s) ifindex=%d state=0x%04x {%s} flags=0x%04x {%s} type=%d (%s)]",
+ ndm->ndm_family, af_type2str(ndm->ndm_family),
+ ndm->ndm_ifindex, ndm->ndm_state,
+ neigh_state2str(ndm->ndm_state, ibuf, sizeof(ibuf)),
+ ndm->ndm_flags,
+ neigh_flags2str(ndm->ndm_flags, fbuf, sizeof(fbuf)),
+ ndm->ndm_type, rtm_type2str(ndm->ndm_type));
+ nlneigh_dump(ndm,
+ nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*ndm)));
+ break;
+
+ case RTM_NEWRULE:
+ case RTM_DELRULE:
+ frh = NLMSG_DATA(nlmsg);
+ zlog_debug(
+ " frh [family=%d (%s) dst_len=%d src_len=%d tos=%d table=%d res1=%d res2=%d action=%d (%s) flags=0x%x]",
+ frh->family, af_type2str(frh->family), frh->dst_len,
+ frh->src_len, frh->tos, frh->table, frh->res1,
+ frh->res2, frh->action, frh_action2str(frh->action),
+ frh->flags);
+ nlrule_dump(frh, nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*frh)));
+ break;
+
+
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifa = NLMSG_DATA(nlmsg);
+ zlog_debug(
+ " ifa [family=(%d) %s prefixlen=%d flags=0x%04x {%s} scope=%d index=%u]",
+ ifa->ifa_family, af_type2str(ifa->ifa_family),
+ ifa->ifa_prefixlen, ifa->ifa_flags,
+ if_flags2str(ifa->ifa_flags, fbuf, sizeof(fbuf)),
+ ifa->ifa_scope, ifa->ifa_index);
+ nlifa_dump(ifa, nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+ break;
+
+ case RTM_NEWNEXTHOP:
+ case RTM_DELNEXTHOP:
+ case RTM_GETNEXTHOP:
+ nhm = NLMSG_DATA(nlmsg);
+ zlog_debug(
+ " nhm [family=(%d) %s scope=(%d) %s protocol=(%d) %s flags=0x%08x {%s}]",
+ nhm->nh_family, af_type2str(nhm->nh_family),
+ nhm->nh_scope, rtm_scope2str(nhm->nh_scope),
+ nhm->nh_protocol, rtm_protocol2str(nhm->nh_protocol),
+ nhm->nh_flags,
+ nh_flags2str(nhm->nh_flags, fbuf, sizeof(fbuf)));
+ nlnh_dump(nhm, nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*nhm)));
+ break;
+
+ case RTM_NEWTUNNEL:
+ case RTM_DELTUNNEL:
+ case RTM_GETTUNNEL:
+ tnlm = NLMSG_DATA(nlmsg);
+ zlog_debug(" tnlm [family=(%d) %s ifindex=%d ", tnlm->family,
+ af_type2str(tnlm->family), tnlm->ifindex);
+ nltnl_dump(tnlm,
+ nlmsg->nlmsg_len -
+ NLMSG_LENGTH(sizeof(struct tunnel_msg)));
+ break;
+
+
+ case RTM_NEWNETCONF:
+ case RTM_DELNETCONF:
+ ncm = NLMSG_DATA(nlmsg);
+ zlog_debug(" ncm [family=%s (%d)]",
+ af_type2str(ncm->ncm_family), ncm->ncm_family);
+ nlncm_dump(ncm, nlmsg->nlmsg_len - NLMSG_LENGTH(sizeof(*ncm)));
+ break;
+
+ case RTM_NEWQDISC:
+ case RTM_DELQDISC:
+ case RTM_NEWTCLASS:
+ case RTM_DELTCLASS:
+ case RTM_NEWTFILTER:
+ case RTM_DELTFILTER:
+ tcm = NLMSG_DATA(nlmsg);
+ zlog_debug(
+ " tcm [type=%s family=%s (%d) ifindex=%d handle=%04x:%04x]",
+ tcm_nltype2str(nlmsg->nlmsg_type),
+ af_type2str(tcm->tcm_family), tcm->tcm_family,
+ tcm->tcm_ifindex, tcm->tcm_handle >> 16,
+ tcm->tcm_handle & 0xffff);
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * Try to get the next header. There should only be more
+ * messages if this header was flagged as MULTI, otherwise just
+ * end it here.
+ */
+ nlmsg = NLMSG_NEXT(nlmsg, msglen);
+ if (NLMSG_OK(nlmsg, msglen) == 0)
+ return;
+
+ goto next_header;
+}
+
+#endif /* NETLINK_DEBUG */