mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-03 06:59:21 +00:00
isisd: learn and advertise IPv6 dst-src routes
Receive IPv6 dst-src routes from zebra and advertise them in our LSPs if so configured. Signed-off-by: Christian Franke <chris@opensourcerouting.org>
This commit is contained in:
parent
c9b0e434bc
commit
d43d2df5f3
@ -37,6 +37,7 @@
|
||||
#include "checksum.h"
|
||||
#include "md5.h"
|
||||
#include "table.h"
|
||||
#include "srcdest_table.h"
|
||||
|
||||
#include "isisd/dict.h"
|
||||
#include "isisd/isis_constants.h"
|
||||
@ -765,18 +766,28 @@ static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp,
|
||||
return;
|
||||
|
||||
for (struct route_node *rn = route_top(er_table); rn;
|
||||
rn = route_next(rn)) {
|
||||
rn = srcdest_route_next(rn)) {
|
||||
if (!rn->info)
|
||||
continue;
|
||||
|
||||
struct prefix_ipv6 *ipv6 = (struct prefix_ipv6 *)&rn->p;
|
||||
struct isis_ext_info *info = rn->info;
|
||||
|
||||
struct prefix_ipv6 *p, *src_p;
|
||||
srcdest_rnode_prefixes(rn, (const struct prefix **)&p,
|
||||
(const struct prefix **)&src_p);
|
||||
|
||||
uint32_t metric = info->metric;
|
||||
if (info->metric > MAX_WIDE_PATH_METRIC)
|
||||
metric = MAX_WIDE_PATH_METRIC;
|
||||
isis_tlvs_add_ipv6_reach(
|
||||
lsp->tlvs, isis_area_ipv6_topology(area), ipv6, metric);
|
||||
|
||||
if (!src_p || !src_p->prefixlen) {
|
||||
isis_tlvs_add_ipv6_reach(lsp->tlvs,
|
||||
isis_area_ipv6_topology(area),
|
||||
p, metric);
|
||||
} else if (isis_area_ipv6_dstsrc_enabled(area)) {
|
||||
isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs,
|
||||
ISIS_MT_IPV6_DSTSRC,
|
||||
p, src_p, metric);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,14 @@ DEFINE_MTYPE_STATIC(ISISD, MT_AREA_SETTING, "ISIS MT Area Setting")
|
||||
DEFINE_MTYPE_STATIC(ISISD, MT_CIRCUIT_SETTING, "ISIS MT Circuit Setting")
|
||||
DEFINE_MTYPE_STATIC(ISISD, MT_ADJ_INFO, "ISIS MT Adjacency Info")
|
||||
|
||||
bool isis_area_ipv6_dstsrc_enabled(struct isis_area *area)
|
||||
{
|
||||
struct isis_area_mt_setting *area_mt_setting;
|
||||
area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_DSTSRC);
|
||||
|
||||
return (area_mt_setting && area_mt_setting->enabled);
|
||||
}
|
||||
|
||||
uint16_t isis_area_ipv6_topology(struct isis_area *area)
|
||||
{
|
||||
struct isis_area_mt_setting *area_mt_setting;
|
||||
@ -61,6 +69,8 @@ const char *isis_mtid2str(uint16_t mtid)
|
||||
return "ipv6-multicast";
|
||||
case ISIS_MT_IPV6_MGMT:
|
||||
return "ipv6-mgmt";
|
||||
case ISIS_MT_IPV6_DSTSRC:
|
||||
return "ipv6-dstsrc";
|
||||
default:
|
||||
snprintf(buf, sizeof(buf), "%" PRIu16, mtid);
|
||||
return buf;
|
||||
@ -81,6 +91,8 @@ uint16_t isis_str2mtid(const char *name)
|
||||
return ISIS_MT_IPV6_MULTICAST;
|
||||
if (!strcmp(name, "ipv6-mgmt"))
|
||||
return ISIS_MT_IPV6_MGMT;
|
||||
if (!strcmp(name, "ipv6-dstsrc"))
|
||||
return ISIS_MT_IPV6_DSTSRC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#define ISIS_MT_IPV4_MULTICAST 3
|
||||
#define ISIS_MT_IPV6_MULTICAST 4
|
||||
#define ISIS_MT_IPV6_MGMT 5
|
||||
#define ISIS_MT_IPV6_DSTSRC 3996 /* FIXME: IANA */
|
||||
|
||||
#define ISIS_MT_NAMES \
|
||||
"<ipv4-unicast" \
|
||||
@ -40,6 +41,7 @@
|
||||
"|ipv4-multicast" \
|
||||
"|ipv6-multicast" \
|
||||
"|ipv6-mgmt" \
|
||||
"|ipv6-dstsrc" \
|
||||
">"
|
||||
|
||||
#define ISIS_MT_DESCRIPTIONS \
|
||||
@ -48,7 +50,9 @@
|
||||
"IPv6 unicast topology\n" \
|
||||
"IPv4 multicast topology\n" \
|
||||
"IPv6 multicast topology\n" \
|
||||
"IPv6 management topology\n"
|
||||
"IPv6 management topology\n" \
|
||||
"IPv6 dst-src topology\n" \
|
||||
""
|
||||
|
||||
#define ISIS_MT_INFO_FIELDS uint16_t mtid;
|
||||
|
||||
@ -75,6 +79,8 @@ struct tlvs;
|
||||
struct te_is_neigh;
|
||||
struct isis_tlvs;
|
||||
|
||||
bool isis_area_ipv6_dstsrc_enabled(struct isis_area *area);
|
||||
|
||||
uint16_t isis_area_ipv6_topology(struct isis_area *area);
|
||||
|
||||
struct isis_area_mt_setting *area_lookup_mt_setting(struct isis_area *area,
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "stream.h"
|
||||
#include "table.h"
|
||||
#include "vty.h"
|
||||
#include "srcdest_table.h"
|
||||
|
||||
#include "isisd/dict.h"
|
||||
#include "isisd/isis_constants.h"
|
||||
@ -86,33 +87,13 @@ struct route_table *get_ext_reach(struct isis_area *area, int family, int level)
|
||||
return area->ext_reach[protocol][level - 1];
|
||||
}
|
||||
|
||||
static struct route_node *
|
||||
isis_redist_route_node_create(route_table_delegate_t *delegate,
|
||||
struct route_table *table)
|
||||
{
|
||||
struct route_node *node;
|
||||
node = XCALLOC(MTYPE_ISIS_EXT_ROUTE, sizeof(*node));
|
||||
return node;
|
||||
}
|
||||
|
||||
static void isis_redist_route_node_destroy(route_table_delegate_t *delegate,
|
||||
struct route_table *table,
|
||||
struct route_node *node)
|
||||
{
|
||||
if (node->info)
|
||||
XFREE(MTYPE_ISIS_EXT_INFO, node->info);
|
||||
XFREE(MTYPE_ISIS_EXT_ROUTE, node);
|
||||
}
|
||||
|
||||
static route_table_delegate_t isis_redist_rt_delegate = {
|
||||
.create_node = isis_redist_route_node_create,
|
||||
.destroy_node = isis_redist_route_node_destroy};
|
||||
|
||||
/* Install external reachability information into a
|
||||
* specific area for a specific level.
|
||||
* Schedule an lsp regenerate if necessary */
|
||||
static void isis_redist_install(struct isis_area *area, int level,
|
||||
struct prefix *p, struct isis_ext_info *info)
|
||||
const struct prefix *p,
|
||||
const struct prefix_ipv6 *src_p,
|
||||
struct isis_ext_info *info)
|
||||
{
|
||||
int family = p->family;
|
||||
struct route_table *er_table = get_ext_reach(area, family, level);
|
||||
@ -126,7 +107,7 @@ static void isis_redist_install(struct isis_area *area, int level,
|
||||
return;
|
||||
}
|
||||
|
||||
er_node = route_node_get(er_table, p);
|
||||
er_node = srcdest_rnode_get(er_table, p, src_p);
|
||||
if (er_node->info) {
|
||||
route_unlock_node(er_node);
|
||||
|
||||
@ -145,7 +126,8 @@ static void isis_redist_install(struct isis_area *area, int level,
|
||||
* specific area for a specific level.
|
||||
* Schedule an lsp regenerate if necessary. */
|
||||
static void isis_redist_uninstall(struct isis_area *area, int level,
|
||||
struct prefix *p)
|
||||
const struct prefix *p,
|
||||
const struct prefix_ipv6 *src_p)
|
||||
{
|
||||
int family = p->family;
|
||||
struct route_table *er_table = get_ext_reach(area, family, level);
|
||||
@ -159,7 +141,7 @@ static void isis_redist_uninstall(struct isis_area *area, int level,
|
||||
return;
|
||||
}
|
||||
|
||||
er_node = route_node_lookup(er_table, p);
|
||||
er_node = srcdest_rnode_lookup(er_table, p, src_p);
|
||||
if (!er_node)
|
||||
return;
|
||||
else
|
||||
@ -177,7 +159,8 @@ static void isis_redist_uninstall(struct isis_area *area, int level,
|
||||
* and prefix, using the given redistribution settings. */
|
||||
static void isis_redist_update_ext_reach(struct isis_area *area, int level,
|
||||
struct isis_redist *redist,
|
||||
struct prefix *p,
|
||||
const struct prefix *p,
|
||||
const struct prefix_ipv6 *src_p,
|
||||
struct isis_ext_info *info)
|
||||
{
|
||||
struct isis_ext_info area_info;
|
||||
@ -188,7 +171,8 @@ static void isis_redist_update_ext_reach(struct isis_area *area, int level,
|
||||
|
||||
if (redist->map_name) {
|
||||
map_ret =
|
||||
route_map_apply(redist->map, p, RMAP_ISIS, &area_info);
|
||||
route_map_apply(redist->map, (struct prefix *)p,
|
||||
RMAP_ISIS, &area_info);
|
||||
if (map_ret == RMAP_DENYMATCH)
|
||||
area_info.distance = 255;
|
||||
}
|
||||
@ -199,9 +183,9 @@ static void isis_redist_update_ext_reach(struct isis_area *area, int level,
|
||||
area_info.distance = 255;
|
||||
|
||||
if (area_info.distance < 255)
|
||||
isis_redist_install(area, level, p, &area_info);
|
||||
isis_redist_install(area, level, p, src_p, &area_info);
|
||||
else
|
||||
isis_redist_uninstall(area, level, p);
|
||||
isis_redist_uninstall(area, level, p, src_p);
|
||||
}
|
||||
|
||||
static void isis_redist_ensure_default(struct isis *isis, int family)
|
||||
@ -222,7 +206,7 @@ static void isis_redist_ensure_default(struct isis *isis, int family)
|
||||
} else
|
||||
assert(!"Unknown family!");
|
||||
|
||||
ei_node = route_node_get(ei_table, &p);
|
||||
ei_node = srcdest_rnode_get(ei_table, &p, NULL);
|
||||
if (ei_node->info) {
|
||||
route_unlock_node(ei_node);
|
||||
return;
|
||||
@ -238,8 +222,8 @@ static void isis_redist_ensure_default(struct isis *isis, int family)
|
||||
}
|
||||
|
||||
/* Handle notification about route being added */
|
||||
void isis_redist_add(int type, struct prefix *p, uint8_t distance,
|
||||
uint32_t metric)
|
||||
void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p,
|
||||
uint8_t distance, uint32_t metric)
|
||||
{
|
||||
int family = p->family;
|
||||
struct route_table *ei_table = get_ext_info(isis, family);
|
||||
@ -262,7 +246,7 @@ void isis_redist_add(int type, struct prefix *p, uint8_t distance,
|
||||
return;
|
||||
}
|
||||
|
||||
ei_node = route_node_get(ei_table, p);
|
||||
ei_node = srcdest_rnode_get(ei_table, p, src_p);
|
||||
if (ei_node->info)
|
||||
route_unlock_node(ei_node);
|
||||
else
|
||||
@ -274,8 +258,10 @@ void isis_redist_add(int type, struct prefix *p, uint8_t distance,
|
||||
info->distance = distance;
|
||||
info->metric = metric;
|
||||
|
||||
if (is_default_prefix(p))
|
||||
if (is_default_prefix(p)
|
||||
&& (!src_p || !src_p->prefixlen)) {
|
||||
type = DEFAULT_ROUTE;
|
||||
}
|
||||
|
||||
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
|
||||
for (level = 1; level <= ISIS_LEVELS; level++) {
|
||||
@ -284,11 +270,11 @@ void isis_redist_add(int type, struct prefix *p, uint8_t distance,
|
||||
continue;
|
||||
|
||||
isis_redist_update_ext_reach(area, level, redist, p,
|
||||
info);
|
||||
src_p, info);
|
||||
}
|
||||
}
|
||||
|
||||
void isis_redist_delete(int type, struct prefix *p)
|
||||
void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p)
|
||||
{
|
||||
int family = p->family;
|
||||
struct route_table *ei_table = get_ext_info(isis, family);
|
||||
@ -304,12 +290,14 @@ void isis_redist_delete(int type, struct prefix *p)
|
||||
zlog_debug("%s: Removing route %s from %s.", __func__, debug_buf,
|
||||
zebra_route_string(type));
|
||||
|
||||
if (is_default_prefix(p)) {
|
||||
if (is_default_prefix(p)
|
||||
&& (!src_p || !src_p->prefixlen)) {
|
||||
/* Don't remove default route but add synthetic route for use
|
||||
* by "default-information originate always". Areas without the
|
||||
* "always" setting will ignore routes with origin
|
||||
* DEFAULT_ROUTE. */
|
||||
isis_redist_add(DEFAULT_ROUTE, p, 254, MAX_WIDE_PATH_METRIC);
|
||||
isis_redist_add(DEFAULT_ROUTE, p, NULL,
|
||||
254, MAX_WIDE_PATH_METRIC);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -319,7 +307,7 @@ void isis_redist_delete(int type, struct prefix *p)
|
||||
return;
|
||||
}
|
||||
|
||||
ei_node = route_node_lookup(ei_table, p);
|
||||
ei_node = srcdest_rnode_lookup(ei_table, p, src_p);
|
||||
if (!ei_node || !ei_node->info) {
|
||||
char buf[BUFSIZ];
|
||||
prefix2str(p, buf, sizeof(buf));
|
||||
@ -339,7 +327,7 @@ void isis_redist_delete(int type, struct prefix *p)
|
||||
if (!redist->redist)
|
||||
continue;
|
||||
|
||||
isis_redist_uninstall(area, level, p);
|
||||
isis_redist_uninstall(area, level, p, src_p);
|
||||
}
|
||||
|
||||
XFREE(MTYPE_ISIS_EXT_INFO, ei_node->info);
|
||||
@ -418,17 +406,14 @@ static void isis_redist_set(struct isis_area *area, int level, int family,
|
||||
isis_redist_routemap_set(redist, routemap);
|
||||
|
||||
if (!area->ext_reach[protocol][level - 1]) {
|
||||
area->ext_reach[protocol][level - 1] =
|
||||
route_table_init_with_delegate(
|
||||
&isis_redist_rt_delegate);
|
||||
area->ext_reach[protocol][level - 1] = srcdest_table_init();
|
||||
}
|
||||
|
||||
for (i = 0; i < REDIST_PROTOCOL_COUNT; i++)
|
||||
for (i = 0; i < REDIST_PROTOCOL_COUNT; i++) {
|
||||
if (!area->isis->ext_info[i]) {
|
||||
area->isis->ext_info[i] =
|
||||
route_table_init_with_delegate(
|
||||
&isis_redist_rt_delegate);
|
||||
area->isis->ext_info[i] = srcdest_table_init();
|
||||
}
|
||||
}
|
||||
|
||||
isis_redist_update_zebra_subscriptions(area->isis);
|
||||
|
||||
@ -436,20 +421,27 @@ static void isis_redist_set(struct isis_area *area, int level, int family,
|
||||
isis_redist_ensure_default(area->isis, family);
|
||||
|
||||
ei_table = get_ext_info(area->isis, family);
|
||||
for (rn = route_top(ei_table); rn; rn = route_next(rn)) {
|
||||
for (rn = route_top(ei_table); rn; rn = srcdest_route_next(rn)) {
|
||||
if (!rn->info)
|
||||
continue;
|
||||
info = rn->info;
|
||||
|
||||
const struct prefix *p, *src_p;
|
||||
|
||||
srcdest_rnode_prefixes(rn, &p, &src_p);
|
||||
|
||||
if (type == DEFAULT_ROUTE) {
|
||||
if (!is_default_prefix(&rn->p))
|
||||
if (!is_default_prefix(p)
|
||||
|| (src_p && src_p->prefixlen)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (info->origin != type)
|
||||
continue;
|
||||
}
|
||||
|
||||
isis_redist_update_ext_reach(area, level, redist, &rn->p, info);
|
||||
isis_redist_update_ext_reach(area, level, redist, p,
|
||||
(struct prefix_ipv6 *)src_p, info);
|
||||
}
|
||||
}
|
||||
|
||||
@ -472,14 +464,19 @@ static void isis_redist_unset(struct isis_area *area, int level, int family,
|
||||
return;
|
||||
}
|
||||
|
||||
for (rn = route_top(er_table); rn; rn = route_next(rn)) {
|
||||
for (rn = route_top(er_table); rn; rn = srcdest_route_next(rn)) {
|
||||
if (!rn->info)
|
||||
continue;
|
||||
info = rn->info;
|
||||
|
||||
const struct prefix *p, *src_p;
|
||||
srcdest_rnode_prefixes(rn, &p, &src_p);
|
||||
|
||||
if (type == DEFAULT_ROUTE) {
|
||||
if (!is_default_prefix(&rn->p))
|
||||
if (!is_default_prefix(p)
|
||||
|| (src_p && src_p->prefixlen)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (info->origin != type)
|
||||
continue;
|
||||
|
@ -42,13 +42,14 @@ struct isis_redist {
|
||||
|
||||
struct isis_area;
|
||||
struct prefix;
|
||||
struct prefix_ipv6;
|
||||
struct vty;
|
||||
|
||||
struct route_table *get_ext_reach(struct isis_area *area, int family,
|
||||
int level);
|
||||
void isis_redist_add(int type, struct prefix *p, uint8_t distance,
|
||||
uint32_t metric);
|
||||
void isis_redist_delete(int type, struct prefix *p);
|
||||
void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p,
|
||||
uint8_t distance, uint32_t metric);
|
||||
void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p);
|
||||
int isis_redist_config_write(struct vty *vty, struct isis_area *area,
|
||||
int family);
|
||||
void isis_redist_init(void);
|
||||
|
@ -1912,6 +1912,11 @@ static void append_item(struct isis_item_list *dest, struct isis_item *item)
|
||||
dest->count++;
|
||||
}
|
||||
|
||||
static struct isis_item *last_item(struct isis_item_list *list)
|
||||
{
|
||||
return container_of(list->tail, struct isis_item, next);
|
||||
}
|
||||
|
||||
static int unpack_item(uint16_t mtid, enum isis_tlv_context context,
|
||||
uint8_t tlv_type, uint8_t len, struct stream *s,
|
||||
struct sbuf *log, void *dest, int indent)
|
||||
@ -3168,6 +3173,21 @@ void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
|
||||
append_item(l, (struct isis_item *)r);
|
||||
}
|
||||
|
||||
void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
|
||||
struct prefix_ipv6 *dest,
|
||||
struct prefix_ipv6 *src,
|
||||
uint32_t metric)
|
||||
{
|
||||
isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric);
|
||||
struct isis_item_list *l = isis_get_mt_items(&tlvs->mt_ipv6_reach,
|
||||
mtid);
|
||||
|
||||
struct isis_ipv6_reach *r = (struct isis_ipv6_reach*)last_item(l);
|
||||
r->subtlvs = isis_alloc_subtlvs();
|
||||
r->subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*src));
|
||||
memcpy(r->subtlvs->source_prefix, src, sizeof(*src));
|
||||
}
|
||||
|
||||
void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
|
||||
uint8_t metric)
|
||||
{
|
||||
|
@ -313,6 +313,10 @@ void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
|
||||
struct prefix_ipv4 *dest, uint32_t metric);
|
||||
void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
|
||||
struct prefix_ipv6 *dest, uint32_t metric);
|
||||
void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
|
||||
struct prefix_ipv6 *dest,
|
||||
struct prefix_ipv6 *src,
|
||||
uint32_t metric);
|
||||
void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
|
||||
uint8_t metric);
|
||||
void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
|
||||
|
@ -359,24 +359,23 @@ static int isis_zebra_read(int command, struct zclient *zclient,
|
||||
if (zapi_route_decode(zclient->ibuf, &api) < 0)
|
||||
return -1;
|
||||
|
||||
/* we completely ignore srcdest routes for now. */
|
||||
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Avoid advertising a false default reachability. (A default
|
||||
* route installed by IS-IS gets redistributed from zebra back
|
||||
* into IS-IS causing us to start advertising default reachabity
|
||||
* without this check)
|
||||
*/
|
||||
if (api.prefix.prefixlen == 0 && api.type == ZEBRA_ROUTE_ISIS)
|
||||
if (api.prefix.prefixlen == 0
|
||||
&& api.src_prefix.prefixlen == 0
|
||||
&& api.type == ZEBRA_ROUTE_ISIS) {
|
||||
command = ZEBRA_REDISTRIBUTE_ROUTE_DEL;
|
||||
}
|
||||
|
||||
if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
|
||||
isis_redist_add(api.type, &api.prefix, api.distance,
|
||||
api.metric);
|
||||
isis_redist_add(api.type, &api.prefix, &api.src_prefix,
|
||||
api.distance, api.metric);
|
||||
else
|
||||
isis_redist_delete(api.type, &api.prefix);
|
||||
isis_redist_delete(api.type, &api.prefix, &api.src_prefix);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
16
lib/zebra.h
16
lib/zebra.h
@ -365,6 +365,22 @@ struct in_pktinfo {
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#ifndef offsetof
|
||||
#ifdef __compiler_offsetof
|
||||
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
|
||||
#else
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const typeof(((type *)0)->member) *__mptr = (ptr); \
|
||||
(type *)((char *)__mptr - offsetof(type, member)); \
|
||||
})
|
||||
#endif
|
||||
|
||||
#define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0]))
|
||||
|
||||
/* For old definition. */
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user