summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/command.h1
-rw-r--r--lib/iana_afi.h15
-rw-r--r--lib/prefix.c172
-rw-r--r--lib/prefix.h39
-rw-r--r--lib/table.c16
-rw-r--r--lib/vty.c7
-rw-r--r--lib/zebra.h9
7 files changed, 232 insertions, 27 deletions
diff --git a/lib/command.h b/lib/command.h
index 718d34b007..36640c493f 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -174,6 +174,7 @@ enum node_type {
BMP_NODE, /* BMP config under router bgp */
ISIS_SRV6_NODE, /* ISIS SRv6 node */
ISIS_SRV6_NODE_MSD_NODE, /* ISIS SRv6 Node MSDs node */
+ BGP_LS_NODE, /* BGP-LS configuration node */
NODE_TYPE_MAX, /* maximum */
};
/* clang-format on */
diff --git a/lib/iana_afi.h b/lib/iana_afi.h
index b9c19cc3d5..9b4d33fa4d 100644
--- a/lib/iana_afi.h
+++ b/lib/iana_afi.h
@@ -26,6 +26,7 @@ typedef enum {
IANA_AFI_IPV4 = 1,
IANA_AFI_IPV6 = 2,
IANA_AFI_L2VPN = 25,
+ IANA_AFI_LINKSTATE = 16388, /* BGP-LS RFC 7752 */
} iana_afi_t;
typedef enum {
@@ -35,6 +36,8 @@ typedef enum {
IANA_SAFI_LABELED_UNICAST = 4,
IANA_SAFI_ENCAP = 7,
IANA_SAFI_EVPN = 70,
+ IANA_SAFI_LINKSTATE = 71, /* BGP-LS RFC 7752 */
+ IANA_SAFI_LINKSTATE_VPN = 72, /* BGP-LS RFC 7752 */
IANA_SAFI_MPLS_VPN = 128,
IANA_SAFI_FLOWSPEC = 133
} iana_safi_t;
@@ -48,6 +51,8 @@ static inline afi_t afi_iana2int(iana_afi_t afi)
return AFI_IP6;
case IANA_AFI_L2VPN:
return AFI_L2VPN;
+ case IANA_AFI_LINKSTATE:
+ return AFI_LINKSTATE;
case IANA_AFI_RESERVED:
return AFI_MAX;
}
@@ -64,6 +69,8 @@ static inline iana_afi_t afi_int2iana(afi_t afi)
return IANA_AFI_IPV6;
case AFI_L2VPN:
return IANA_AFI_L2VPN;
+ case AFI_LINKSTATE:
+ return IANA_AFI_LINKSTATE;
case AFI_UNSPEC:
case AFI_MAX:
return IANA_AFI_RESERVED;
@@ -94,6 +101,10 @@ static inline safi_t safi_iana2int(iana_safi_t safi)
return SAFI_LABELED_UNICAST;
case IANA_SAFI_FLOWSPEC:
return SAFI_FLOWSPEC;
+ case IANA_SAFI_LINKSTATE:
+ return SAFI_LINKSTATE;
+ case IANA_SAFI_LINKSTATE_VPN:
+ return SAFI_LINKSTATE_VPN;
case IANA_SAFI_RESERVED:
return SAFI_MAX;
}
@@ -118,6 +129,10 @@ static inline iana_safi_t safi_int2iana(safi_t safi)
return IANA_SAFI_LABELED_UNICAST;
case SAFI_FLOWSPEC:
return IANA_SAFI_FLOWSPEC;
+ case SAFI_LINKSTATE:
+ return IANA_SAFI_LINKSTATE;
+ case SAFI_LINKSTATE_VPN:
+ return IANA_SAFI_LINKSTATE_VPN;
case SAFI_UNSPEC:
case SAFI_MAX:
return IANA_SAFI_RESERVED;
diff --git a/lib/prefix.c b/lib/prefix.c
index f342c4c1db..cde8677cf0 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -20,6 +20,7 @@
DEFINE_MTYPE_STATIC(LIB, PREFIX, "Prefix");
DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec");
+DEFINE_MTYPE_STATIC(LIB, PREFIX_LINKSTATE, "Prefix Link-State");
/* Maskbit. */
static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
@@ -32,6 +33,18 @@ static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff)
+char *(*prefix_linkstate_display_hook)(char *buf, size_t size,
+ uint16_t nlri_type, uintptr_t ptr,
+ uint16_t len) = NULL;
+
+void prefix_set_linkstate_display_hook(char *(*func)(char *buf, size_t size,
+ uint16_t nlri_type,
+ uintptr_t ptr,
+ uint16_t len))
+{
+ prefix_linkstate_display_hook = func;
+}
+
int is_zero_mac(const struct ethaddr *mac)
{
int i = 0;
@@ -81,6 +94,8 @@ int str2family(const char *string)
return AF_ETHERNET;
else if (!strcmp("evpn", string))
return AF_EVPN;
+ else if (!strcmp("link-state", string))
+ return AF_LINKSTATE;
return -1;
}
@@ -95,6 +110,8 @@ const char *family2str(int family)
return "Ethernet";
case AF_EVPN:
return "Evpn";
+ case AF_LINKSTATE:
+ return "Link-State";
}
return "?";
}
@@ -109,6 +126,8 @@ int afi2family(afi_t afi)
else if (afi == AFI_L2VPN)
return AF_ETHERNET;
/* NOTE: EVPN code should NOT use this interface. */
+ else if (afi == AFI_LINKSTATE)
+ return AF_LINKSTATE;
return 0;
}
@@ -120,6 +139,8 @@ afi_t family2afi(int family)
return AFI_IP6;
else if (family == AF_ETHERNET || family == AF_EVPN)
return AFI_L2VPN;
+ else if (family == AF_LINKSTATE)
+ return AFI_LINKSTATE;
return 0;
}
@@ -132,6 +153,8 @@ const char *afi2str_lower(afi_t afi)
return "ipv6";
case AFI_L2VPN:
return "l2vpn";
+ case AFI_LINKSTATE:
+ return "link-state";
case AFI_MAX:
case AFI_UNSPEC:
return "bad-value";
@@ -149,6 +172,8 @@ const char *afi2str(afi_t afi)
return "IPv6";
case AFI_L2VPN:
return "l2vpn";
+ case AFI_LINKSTATE:
+ return "link-state";
case AFI_MAX:
case AFI_UNSPEC:
return "bad-value";
@@ -174,6 +199,10 @@ const char *safi2str(safi_t safi)
return "labeled-unicast";
case SAFI_FLOWSPEC:
return "flowspec";
+ case SAFI_LINKSTATE:
+ return "link-state";
+ case SAFI_LINKSTATE_VPN:
+ return "link-state-vpn";
case SAFI_UNSPEC:
case SAFI_MAX:
return "unknown";
@@ -215,6 +244,21 @@ int prefix_match(union prefixconstptr unet, union prefixconstptr upfx)
if (np[offset] != pp[offset])
return 0;
return 1;
+ } else if (n->family == AF_LINKSTATE) {
+ if (n->u.prefix_linkstate.nlri_type !=
+ p->u.prefix_linkstate.nlri_type)
+ return 0;
+
+ /* Set both prefix's head pointer. */
+ np = (const uint8_t *)&n->u.prefix_linkstate.ptr;
+ pp = (const uint8_t *)&p->u.prefix_linkstate.ptr;
+
+ offset = n->prefixlen; /* length is checked above */
+
+ while (offset--)
+ if (np[offset] != pp[offset])
+ return 0;
+ return 1;
}
/* Set both prefix's head pointer. */
@@ -316,6 +360,8 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
{
struct prefix *dest = udest.p;
const struct prefix *src = usrc.p;
+ void *temp;
+ int len;
dest->family = src->family;
dest->prefixlen = src->prefixlen;
@@ -334,9 +380,6 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
dest->u.lp.id = src->u.lp.id;
dest->u.lp.adv_router = src->u.lp.adv_router;
} else if (src->family == AF_FLOWSPEC) {
- void *temp;
- int len;
-
len = src->u.prefix_flowspec.prefixlen;
dest->u.prefix_flowspec.prefixlen =
src->u.prefix_flowspec.prefixlen;
@@ -347,6 +390,14 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
dest->u.prefix_flowspec.ptr = (uintptr_t)temp;
memcpy((void *)dest->u.prefix_flowspec.ptr,
(void *)src->u.prefix_flowspec.ptr, len);
+ } else if (src->family == AF_LINKSTATE) {
+ len = src->prefixlen;
+ dest->u.prefix_linkstate.nlri_type =
+ src->u.prefix_linkstate.nlri_type;
+ temp = XCALLOC(MTYPE_PREFIX_LINKSTATE, len);
+ dest->u.prefix_linkstate.ptr = (uintptr_t)temp;
+ memcpy((void *)dest->u.prefix_linkstate.ptr,
+ (void *)src->u.prefix_linkstate.ptr, len);
} else {
flog_err(EC_LIB_DEVELOPMENT,
"prefix_copy(): Unknown address family %d",
@@ -436,6 +487,14 @@ int prefix_same(union prefixconstptr up1, union prefixconstptr up2)
p2->u.prefix_flowspec.prefixlen))
return 1;
}
+ if (p1->family == AF_LINKSTATE) {
+ if (p1->u.prefix_linkstate.nlri_type !=
+ p2->u.prefix_linkstate.nlri_type)
+ return 0;
+ if (!memcmp(&p1->u.prefix_linkstate.ptr,
+ &p2->u.prefix_linkstate.ptr, p2->prefixlen))
+ return 1;
+ }
}
return 0;
}
@@ -483,6 +542,22 @@ int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2)
if (pp1[offset] != pp2[offset])
return numcmp(pp1[offset], pp2[offset]);
return 0;
+ } else if (p1->family == AF_LINKSTATE) {
+ pp1 = (const uint8_t *)p1->u.prefix_linkstate.ptr;
+ pp2 = (const uint8_t *)p2->u.prefix_linkstate.ptr;
+
+ if (p1->u.prefix_linkstate.nlri_type !=
+ p2->u.prefix_linkstate.nlri_type)
+ return 1;
+
+ if (p1->prefixlen != p2->prefixlen)
+ return numcmp(p1->prefixlen, p2->prefixlen);
+
+ offset = p1->prefixlen;
+ while (offset--)
+ if (pp1[offset] != pp2[offset])
+ return numcmp(pp1[offset], pp2[offset]);
+ return 0;
}
pp1 = p1->u.val;
pp2 = p2->u.val;
@@ -1072,10 +1147,26 @@ static const char *prefixevpn2str(const struct prefix_evpn *p, char *str,
return str;
}
+const char *bgp_linkstate_nlri_type_2str(uint16_t nlri_type)
+{
+ switch (nlri_type) {
+ case BGP_LINKSTATE_NODE:
+ return "Node";
+ case BGP_LINKSTATE_LINK:
+ return "Link";
+ case BGP_LINKSTATE_PREFIX4:
+ return "IPv4-Prefix";
+ case BGP_LINKSTATE_PREFIX6:
+ return "IPv6-Prefix";
+ }
+
+ return "Unknown";
+}
+
const char *prefix2str(union prefixconstptr pu, char *str, int size)
{
const struct prefix *p = pu.p;
- char buf[PREFIX2STR_BUFFER];
+ char buf[PREFIX_STRLEN_EXTENDED];
int byte, tmp, a, b;
bool z = false;
size_t l;
@@ -1116,6 +1207,22 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
strlcpy(str, "FS prefix", size);
break;
+ case AF_LINKSTATE:
+ if (prefix_linkstate_display_hook)
+ snprintf(str, size, "%s/%d",
+ prefix_linkstate_display_hook(
+ buf, sizeof(buf),
+ p->u.prefix_linkstate.nlri_type,
+ p->u.prefix_linkstate.ptr,
+ p->prefixlen),
+ p->prefixlen);
+ else
+ snprintf(str, size, "%s/%d",
+ bgp_linkstate_nlri_type_2str(
+ p->u.prefix_linkstate.nlri_type),
+ p->prefixlen);
+ break;
+
default:
strlcpy(str, "UNK prefix", size);
break;
@@ -1127,7 +1234,7 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
static ssize_t prefixhost2str(struct fbuf *fbuf, union prefixconstptr pu)
{
const struct prefix *p = pu.p;
- char buf[PREFIX2STR_BUFFER];
+ char buf[PREFIX_STRLEN_EXTENDED];
switch (p->family) {
case AF_INET:
@@ -1139,6 +1246,17 @@ static ssize_t prefixhost2str(struct fbuf *fbuf, union prefixconstptr pu)
prefix_mac2str(&p->u.prefix_eth, buf, sizeof(buf));
return bputs(fbuf, buf);
+ case AF_LINKSTATE:
+ if (prefix_linkstate_display_hook)
+ prefix_linkstate_display_hook(
+ buf, sizeof(buf),
+ p->u.prefix_linkstate.nlri_type,
+ p->u.prefix_linkstate.ptr, p->prefixlen);
+ else
+ snprintf(buf, sizeof(buf), "%s",
+ bgp_linkstate_nlri_type_2str(
+ p->u.prefix_linkstate.nlri_type));
+ return bputs(fbuf, buf);
default:
return bprintfrr(fbuf, "{prefix.af=%dPF}", p->family);
}
@@ -1173,6 +1291,20 @@ const char *prefix_sg2str(const struct prefix_sg *sg, char *sg_str)
return sg_str;
}
+
+void prefix_linkstate_ptr_free(struct prefix *p)
+{
+ void *temp;
+
+ if (!p || p->family != AF_LINKSTATE || !p->u.prefix_linkstate.ptr)
+ return;
+
+ temp = (void *)p->u.prefix_linkstate.ptr;
+ XFREE(MTYPE_PREFIX_LINKSTATE, temp);
+ p->u.prefix_linkstate.ptr = (uintptr_t)NULL;
+}
+
+
struct prefix *prefix_new(void)
{
struct prefix *p;
@@ -1322,17 +1454,16 @@ char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size)
unsigned prefix_hash_key(const void *pp)
{
struct prefix copy;
+ uint32_t len;
+ void *temp;
- if (((struct prefix *)pp)->family == AF_FLOWSPEC) {
- uint32_t len;
- void *temp;
+ /* make sure *all* unused bits are zero, particularly including
+ * alignment /
+ * padding and unused prefix bytes. */
+ memset(&copy, 0, sizeof(copy));
+ prefix_copy(&copy, (struct prefix *)pp);
- /* make sure *all* unused bits are zero,
- * particularly including alignment /
- * padding and unused prefix bytes.
- */
- memset(&copy, 0, sizeof(copy));
- prefix_copy(&copy, (struct prefix *)pp);
+ if (((struct prefix *)pp)->family == AF_FLOWSPEC) {
len = jhash((void *)copy.u.prefix_flowspec.ptr,
copy.u.prefix_flowspec.prefixlen,
0x55aa5a5a);
@@ -1340,12 +1471,13 @@ unsigned prefix_hash_key(const void *pp)
XFREE(MTYPE_PREFIX_FLOWSPEC, temp);
copy.u.prefix_flowspec.ptr = (uintptr_t)NULL;
return len;
+ } else if (((struct prefix *)pp)->family == AF_LINKSTATE) {
+ len = jhash((void *)copy.u.prefix_linkstate.ptr, copy.prefixlen,
+ 0x55aa5a5a);
+ prefix_linkstate_ptr_free(&copy);
+ return len;
}
- /* make sure *all* unused bits are zero, particularly including
- * alignment /
- * padding and unused prefix bytes. */
- memset(&copy, 0, sizeof(copy));
- prefix_copy(&copy, (struct prefix *)pp);
+
return jhash(&copy,
offsetof(struct prefix, u.prefix) + PSIZE(copy.prefixlen),
0x55aa5a5a);
@@ -1620,7 +1752,7 @@ static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea,
if (host_only)
return prefixhost2str(buf, (struct prefix *)ptr);
else {
- char cbuf[PREFIX_STRLEN];
+ char cbuf[PREFIX_STRLEN_EXTENDED];
prefix2str(ptr, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
diff --git a/lib/prefix.h b/lib/prefix.h
index fc6e32dd54..f1aff43689 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -125,6 +125,15 @@ struct evpn_addr {
#define prefix_addr u._prefix_addr
};
+/* BGP Link-State NRLI types*/
+enum bgp_linkstate_nlri_type {
+ /* RFC7752 Table 1 */
+ BGP_LINKSTATE_NODE = 1,
+ BGP_LINKSTATE_LINK = 2,
+ BGP_LINKSTATE_PREFIX4 = 3, /* IPv4 Topology Prefix */
+ BGP_LINKSTATE_PREFIX6 = 4, /* IPv6 Topology Prefix */
+};
+
/*
* A struct prefix contains an address family, a prefix length, and an
* address. This can represent either a 'network prefix' as defined
@@ -158,12 +167,21 @@ struct evpn_addr {
#define AF_FLOWSPEC (AF_MAX + 2)
#endif
+#if !defined(AF_LINKSTATE)
+#define AF_LINKSTATE (AF_MAX + 3)
+#endif
+
struct flowspec_prefix {
uint8_t family;
uint16_t prefixlen; /* length in bytes */
uintptr_t ptr;
};
+struct linkstate_prefix {
+ uint16_t nlri_type;
+ uintptr_t ptr;
+};
+
/* FRR generic prefix structure. */
struct prefix {
uint8_t family;
@@ -182,6 +200,7 @@ struct prefix {
uintptr_t ptr;
struct evpn_addr prefix_evpn; /* AF_EVPN */
struct flowspec_prefix prefix_flowspec; /* AF_FLOWSPEC */
+ struct linkstate_prefix prefix_linkstate; /* AF_LINKSTATE */
} u __attribute__((aligned(8)));
};
@@ -279,6 +298,14 @@ struct prefix_fs {
struct flowspec_prefix prefix __attribute__((aligned(8)));
};
+
+/* Prefix for a BGP-LS entry */
+struct prefix_bgpls {
+ uint8_t family;
+ uint16_t prefixlen;
+ struct linkstate_prefix prefix __attribute__((aligned(8)));
+};
+
struct prefix_sg {
uint8_t family;
uint16_t prefixlen;
@@ -320,6 +347,11 @@ union prefixconstptr {
/* Maximum string length of the result of prefix2str */
#define PREFIX_STRLEN 80
+/* Maximum string length of the result of prefix2str for
+ * long string prefixes (eg. BGP Link-State)
+ */
+#define PREFIX_STRLEN_EXTENDED 512
+
/*
* Longest possible length of a (S,G) string is 34 bytes
* 123.123.123.123 = 15 * 2
@@ -376,6 +408,10 @@ static inline void ipv4_addr_copy(struct in_addr *dst,
#define s6_addr32 __u6_addr.__u6_addr32
#endif /*s6_addr32*/
+extern void prefix_set_linkstate_display_hook(
+ char *(*func)(char *buf, size_t size, uint16_t nlri_type, uintptr_t ptr,
+ uint16_t len));
+
/* Prototypes. */
extern int str2family(const char *string);
extern int afi2family(afi_t afi);
@@ -401,6 +437,8 @@ static inline afi_t prefix_afi(union prefixconstptr pu)
*/
extern unsigned int prefix_bit(const uint8_t *prefix, const uint16_t bit_index);
+extern void prefix_linkstate_ptr_free(struct prefix *p);
+
extern struct prefix *prefix_new(void);
extern void prefix_free(struct prefix **p);
/*
@@ -418,6 +456,7 @@ extern void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr,
extern const char *prefix_sg2str(const struct prefix_sg *sg, char *str);
extern const char *prefix2str(union prefixconstptr upfx, char *buffer,
int size);
+extern const char *bgp_linkstate_nlri_type_2str(uint16_t nlri_type);
extern int evpn_type5_prefix_match(const struct prefix *evpn_pfx,
const struct prefix *match_pfx);
extern int prefix_match(union prefixconstptr unet, union prefixconstptr upfx);
diff --git a/lib/table.c b/lib/table.c
index 3bf93894ec..dbfc3f8b91 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -142,7 +142,7 @@ static void route_common(const struct prefix *n, const struct prefix *p,
const uint8_t *pp;
uint8_t *newp;
- if (n->family == AF_FLOWSPEC)
+ if (n->family == AF_FLOWSPEC || n->family == AF_LINKSTATE)
return prefix_copy(new, p);
np = (const uint8_t *)&n->u.prefix;
pp = (const uint8_t *)&p->u.prefix;
@@ -281,15 +281,22 @@ struct route_node *route_node_get(struct route_table *table,
const uint8_t *prefix = &p->u.prefix;
node = rn_hash_node_find(&table->hash, &search);
- if (node && node->info)
+ if (node && node->info) {
+ if (family2afi(p->family) == AFI_LINKSTATE)
+ prefix_linkstate_ptr_free(p);
+
return route_lock_node(node);
+ }
match = NULL;
node = table->top;
while (node && node->p.prefixlen <= prefixlen
&& prefix_match(&node->p, p)) {
- if (node->p.prefixlen == prefixlen)
+ if (node->p.prefixlen == prefixlen) {
+ if (family2afi(p->family) == AFI_LINKSTATE)
+ prefix_linkstate_ptr_free(p);
return route_lock_node(node);
+ }
match = node;
node = node->link[prefix_bit(prefix, node->p.prefixlen)];
@@ -324,6 +331,9 @@ struct route_node *route_node_get(struct route_table *table,
table->count++;
route_lock_node(new);
+ if (family2afi(p->family) == AFI_LINKSTATE)
+ prefix_linkstate_ptr_free(p);
+
return new;
}
diff --git a/lib/vty.c b/lib/vty.c
index 8f8effa9cc..15cc340eb0 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -2386,9 +2386,14 @@ static void vtysh_read(struct event *thread)
* => skip vty_event(VTYSH_READ, vty)!
*/
return;
- } else
+ } else {
+ assertf(vty->status != VTY_PASSFD,
+ "%p address=%s passfd=%d", vty,
+ vty->address, vty->pass_fd);
+
/* normalize other invalid values */
vty->pass_fd = -1;
+ }
/* hack for asynchronous "write integrated"
* - other commands in "buf" will be ditched
diff --git a/lib/zebra.h b/lib/zebra.h
index ecc87f58f1..cd0b72834c 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -326,13 +326,14 @@ struct in_pktinfo {
#define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */
#endif
-/* Address family numbers from RFC1700. */
+/* Address family numbers. */
typedef enum {
AFI_UNSPEC = 0,
AFI_IP = 1,
AFI_IP6 = 2,
AFI_L2VPN = 3,
- AFI_MAX = 4
+ AFI_LINKSTATE = 4, /* BGP-LS RFC 7752 */
+ AFI_MAX = 5,
} afi_t;
#define IS_VALID_AFI(a) ((a) > AFI_UNSPEC && (a) < AFI_MAX)
@@ -347,7 +348,9 @@ typedef enum {
SAFI_EVPN = 5,
SAFI_LABELED_UNICAST = 6,
SAFI_FLOWSPEC = 7,
- SAFI_MAX = 8
+ SAFI_LINKSTATE = 8, /* BGP-LS RFC 7752 */
+ SAFI_LINKSTATE_VPN = 9, /* BGP-LS RFC 7752 */
+ SAFI_MAX = 10,
} safi_t;
#define FOREACH_AFI_SAFI(afi, safi) \