+// SPDX-License-Identifier: ISC
/*
* 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>
#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)
{
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:
}
}
+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_EVENT:
return "EVENT";
#endif /* IFLA_EVENT */
+ case IFLA_PROTO_DOWN_REASON:
+ return "PROTO_DOWN_REASON";
default:
return "UNKNOWN";
}
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 "MFC_STATS";
case RTA_NH_ID:
return "NH_ID";
+ case RTA_EXPIRES:
+ return "EXPIRES";
default:
return "UNKNOWN";
}
/*
* 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;
plen = RTA_PAYLOAD(rta);
zlog_debug(" linkinfo [len=%d (payload=%zu) type=(%d) %s]",
rta->rta_len, plen, rta->rta_type,
- rta_type2str(rta->rta_type));
+ ifla_info_type2str(rta->rta_type));
switch (rta->rta_type) {
case IFLA_INFO_KIND:
if (plen == 0) {
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);
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, rta_type2str(rta->rta_type));
- switch (rta->rta_type) {
- case IFLA_IFNAME:
+ plen, rta_type, rta_type2str(rta_type));
+ switch (rta_type) {
case IFLA_IFALIAS:
if (plen == 0) {
zlog_debug(" invalid length");
#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;
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;
break;
case IFLA_LINKINFO:
- nllink_linkinfo_dump(RTA_DATA(rta), msglen);
+ nllink_linkinfo_dump(RTA_DATA(rta), plen);
+ break;
+
+ case IFLA_PROTO_DOWN_REASON:
+ nllink_pdr_dump(RTA_DATA(rta), plen);
break;
default:
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);
plen = RTA_PAYLOAD(rta);
zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
- plen, rta->rta_type, rtm_rta2str(rta->rta_type));
- switch (rta->rta_type) {
+ 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:
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:
}
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;
uint16_t vid;
char bytestr[16];
char dbuf[128];
+ unsigned short rta_type;
#ifndef NDA_RTA
#define NDA_RTA(ndm) \
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->rta_type));
- switch (rta->rta_type & ~ NLA_F_NESTED) {
+ plen, rta->rta_type, neigh_rta2str(rta_type));
+ switch (rta_type) {
case NDA_LLADDR:
datap = RTA_DATA(rta);
dbuf[0] = 0;
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;
uint32_t u32v;
unsigned long count, i;
struct nexthop_grp *nhgrp;
+ unsigned short rta_type;
rta = RTM_NHA(nhm);
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->rta_type));
- switch (rta->rta_type & ~NLA_F_NESTED) {
+ plen, rta->rta_type, nhm_rta2str(rta_type));
+ switch (rta_type) {
case NHA_ID:
u32v = *(uint32_t *)RTA_DATA(rta);
zlog_debug(" %u", u32v);
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(" %d", u16v);
+ zlog_debug(" %s", nhg_type2str(u16v));
break;
case NHA_BLACKHOLE:
/* NOTHING */
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;
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];
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);
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;
}