diff options
Diffstat (limited to '')
-rw-r--r-- | zebra/debug_nl.c | 1784 |
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 */ |