Merge branch 'master' into HEAD

This commit is contained in:
Maria Matejka 2019-03-18 12:54:40 +01:00
commit 8d65add626
19 changed files with 320 additions and 210 deletions

View file

@ -65,7 +65,8 @@ endif
docgoals := docs userdocs progdocs docgoals := docs userdocs progdocs
testgoals := check test tests tests_run testgoals := check test tests tests_run
cleangoals := clean distclean testsclean cleangoals := clean distclean testsclean
.PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags cscope .PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags cscope prepare
all: daemon cli all: daemon cli
daemon: $(daemon) daemon: $(daemon)
@ -106,35 +107,50 @@ include $(addsuffix /Makefile,$(addprefix $(srcdir)/,$(dirs)))
# Generic rules # Generic rules
# Object file rules # Object file rules
$(objdir)/%.o: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h $(objdir)/%.o: $(srcdir)/%.c | prepare
$(E)echo CC -o $@ -c $< $(E)echo CC -o $@ -c $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $< $(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $<
$(objdir)/%.o: $(objdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h $(objdir)/%.o: $(objdir)/%.c | prepare
$(E)echo CC -o $@ -c $< $(E)echo CC -o $@ -c $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $< $(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $<
# Debug: Preprocessed source rules # Debug: Preprocessed source rules
$(objdir)/%.E: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h $(objdir)/%.E: $(srcdir)/%.c | prepare
$(E)echo CC -o $@ -E $< $(E)echo CC -o $@ -E $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -E $< $(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -E $<
$(objdir)/%.E: $(objdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h $(objdir)/%.E: $(objdir)/%.c | prepare
$(E)echo CC -o $@ -E $< $(E)echo CC -o $@ -E $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -E $< $(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -E $<
# Debug: Assembler object rules # Debug: Assembler object rules
$(objdir)/%.S: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h $(objdir)/%.S: $(srcdir)/%.c | prepare
$(E)echo CC -o $@ -S $< $(E)echo CC -o $@ -S $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $< $(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $<
$(objdir)/%.S: $(objdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h $(objdir)/%.S: $(objdir)/%.c | prepare
$(E)echo CC -o $@ -S $< $(E)echo CC -o $@ -S $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $< $(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $<
# Finally include the computed dependencies:
DEPS = $(shell find $(objdir) -name '*.d')
# ## if there is at least one non-clean goal
ifneq ($(filter-out $(cleangoals),$(MAKECMDGOALS)),)
-include $(DEPS)
endif
$(objdir)/.dir-stamp: # ## if the implicit goal is called
ifeq ($(MAKECMDGOALS),)
-include $(DEPS)
endif
# Rule for pre-generating all generated includables
# before compiling any C file
prepare: $(objdir)/sysdep/paths.h | $(objdir)/.dir-stamp
$(objdir)/.dir-stamp: Makefile
$(E)echo MKDIR -p $(addprefix $(objdir)/,$(dirs) doc) $(E)echo MKDIR -p $(addprefix $(objdir)/,$(dirs) doc)
$(Q)mkdir -p $(addprefix $(objdir)/,$(dirs) doc) $(Q)mkdir -p $(addprefix $(objdir)/,$(dirs) doc)
$(Q)touch $@ $(Q)touch $@
@ -153,7 +169,7 @@ $(objdir)/sysdep/paths.h: Makefile
tests_targets_ok = $(addsuffix .ok,$(tests_targets)) tests_targets_ok = $(addsuffix .ok,$(tests_targets))
$(tests_targets): %: %.o $(tests_objs) $(tests_targets): %: %.o $(tests_objs) | prepare
$(E)echo LD $(LDFLAGS) -o $@ $< "..." $(LIBS) $(E)echo LD $(LDFLAGS) -o $@ $< "..." $(LIBS)
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
@ -168,16 +184,6 @@ check: tests tests_run
tests: $(tests_targets) tests: $(tests_targets)
tests_run: $(tests_targets_ok) tests_run: $(tests_targets_ok)
# Finally include the computed dependencies
ifneq ($(filter-out $(cleangoals),$(MAKECMDGOALS)),)
-include $(shell find $(objdir) -name "*.d")
endif
ifeq ($(MAKECMDGOALS),)
-include $(shell find $(objdir) -name "*.d")
endif
tags: tags:
cd $(srcdir) ; etags -lc `find $(dirs) -name *.[chY]` cd $(srcdir) ; etags -lc `find $(dirs) -name *.[chY]`

8
NEWS
View file

@ -1,3 +1,11 @@
Version 2.0.4 (2019-02-27)
o OSPF: Opaque LSAs (RFC 5250)
o OSPF: DN-bit handling (RFC 4576)
o Preferred route counters are back
o Important BGP bugfix
o Several bugfixes related to route propagation
o some minor bugfixes
Version 2.0.3 (2019-01-05) Version 2.0.3 (2019-01-05)
o MRT table dumps (RFC 6396) o MRT table dumps (RFC 6396)
o BGP Long-lived graceful restart o BGP Long-lived graceful restart

View file

@ -4,8 +4,6 @@ obj := $(src-o-files)
$(all-client) $(all-client)
$(conf-y-targets): $(s)cmds.Y $(conf-y-targets): $(s)cmds.Y
$(o)commands.o: $(objdir)/conf/commands.h
$(exedir)/birdc: $(o)birdc.o $(exedir)/birdc: $(o)birdc.o
$(exedir)/birdc: LIBS += $(CLIENT_LIBS) $(exedir)/birdc: LIBS += $(CLIENT_LIBS)

View file

@ -25,9 +25,10 @@ $(o)cf-parse.tab.c: $(o)cf-parse.y
$(o)cf-lex.c: $(s)cf-lex.l $(o)cf-lex.c: $(s)cf-lex.l
$(FLEX) $(FLEX_DEBUG) -f -s -B -8 -Pcf_ -o$@ $< $(FLEX) $(FLEX_DEBUG) -f -s -B -8 -Pcf_ -o$@ $<
$(o)cf-lex.o: $(o)cf-parse.tab.h $(o)keywords.h
$(o)cf-lex.o: CFLAGS+=-Wno-sign-compare -Wno-unused-function $(o)cf-lex.o: CFLAGS+=-Wno-sign-compare -Wno-unused-function
prepare: $(o)keywords.h $(o)commands.h $(o)cf-parse.tab.h
$(addprefix $(o), cf-parse.y keywords.h commands.h cf-parse.tab.h cf-parse.tab.c cf-lex.c): $(objdir)/.dir-stamp $(addprefix $(o), cf-parse.y keywords.h commands.h cf-parse.tab.h cf-parse.tab.c cf-lex.c): $(objdir)/.dir-stamp
$(call clean,cf-parse.tab.h cf-parse.tab.c cf-parse.y keywords.h commands.h cf-lex.c cf-parse.output) $(call clean,cf-parse.tab.h cf-parse.tab.c cf-parse.y keywords.h commands.h cf-lex.c cf-parse.output)

View file

@ -3,8 +3,6 @@ obj := $(src-o-files)
$(all-daemon) $(all-daemon)
$(cf-local) $(cf-local)
$(conf-y-targets) $(conf-lex-targets) $(src-o-files): $(o)inst-gen.h
M4FLAGS_FILTERS=$(filter-out -s,$(M4FLAGS)) M4FLAGS_FILTERS=$(filter-out -s,$(M4FLAGS))
$(o)inst-gen.h: $(s)decl.m4 $(s)f-inst.c $(objdir)/.dir-stamp $(o)inst-gen.h: $(s)decl.m4 $(s)f-inst.c $(objdir)/.dir-stamp
@ -16,7 +14,7 @@ $(o)inst-gen.c: $(s)decl.m4 $(s)f-inst.c $(objdir)/.dir-stamp
$(o)inst-interpret.c: $(s)decl.m4 $(s)f-inst.c $(objdir)/.dir-stamp $(o)inst-interpret.c: $(s)decl.m4 $(s)f-inst.c $(objdir)/.dir-stamp
$(M4) $(M4FLAGS_FILTERS) -DTARGET=I -P $^ >$@ $(M4) $(M4FLAGS_FILTERS) -DTARGET=I -P $^ >$@
$(o)filter.o: $(o)inst-interpret.c $(o)inst-gen.h prepare: $(o)inst-interpret.c $(o)inst-gen.h
tests_src := tree_test.c filter_test.c trie_test.c tests_src := tree_test.c filter_test.c trie_test.c
tests_targets := $(tests_targets) $(tests-target-files) tests_targets := $(tests_targets) $(tests-target-files)

View file

@ -650,7 +650,7 @@
l->count = 1; l->count = 1;
l->attrs[0].id = da.ea_code; l->attrs[0].id = da.ea_code;
l->attrs[0].flags = 0; l->attrs[0].flags = 0;
l->attrs[0].type = EAF_TYPE_UNDEF | EAF_TEMP | EAF_ORIGINATED | EAF_FRESH; l->attrs[0].type = EAF_TYPE_UNDEF | EAF_ORIGINATED | EAF_FRESH;
l->attrs[0].u.data = 0; l->attrs[0].u.data = 0;
f_rta_cow(fs); f_rta_cow(fs);

View file

@ -1,6 +1,6 @@
Summary: BIRD Internet Routing Daemon Summary: BIRD Internet Routing Daemon
Name: bird Name: bird
Version: 2.0.3 Version: 2.0.4
Release: 1 Release: 1
Copyright: GPL Copyright: GPL
Group: Networking/Daemons Group: Networking/Daemons

View file

@ -194,8 +194,8 @@ struct proto {
* ifa_notify Notify protocol about interface address changes. * ifa_notify Notify protocol about interface address changes.
* rt_notify Notify protocol about routing table updates. * rt_notify Notify protocol about routing table updates.
* neigh_notify Notify protocol about neighbor cache events. * neigh_notify Notify protocol about neighbor cache events.
* make_tmp_attrs Construct ea_list from private attrs stored in rta. * make_tmp_attrs Add attributes to rta from from private attrs stored in rte. The route and rta MUST NOT be cached.
* store_tmp_attrs Store private attrs back to rta. The route MUST NOT be cached. * store_tmp_attrs Store private attrs back to rte and undef added attributes. The route and rta MUST NOT be cached.
* preexport Called as the first step of the route exporting process. * preexport Called as the first step of the route exporting process.
* It can construct a new rte, add private attributes and * It can construct a new rte, add private attributes and
* decide whether the route shall be exported: 1=yes, -1=no, * decide whether the route shall be exported: 1=yes, -1=no,
@ -211,8 +211,8 @@ struct proto {
void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a); void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old); void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
void (*neigh_notify)(struct neighbor *neigh); void (*neigh_notify)(struct neighbor *neigh);
struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool); void (*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
void (*store_tmp_attrs)(struct rte *rt); void (*store_tmp_attrs)(struct rte *rt, struct linpool *pool);
int (*preexport)(struct proto *, struct rte **rt, struct linpool *pool); int (*preexport)(struct proto *, struct rte **rt, struct linpool *pool);
void (*reload_routes)(struct channel *); void (*reload_routes)(struct channel *);
void (*feed_begin)(struct channel *, int initial); void (*feed_begin)(struct channel *, int initial);
@ -297,18 +297,6 @@ proto_get_router_id(struct proto_config *pc)
return pc->router_id ? pc->router_id : pc->global->router_id; return pc->router_id ? pc->router_id : pc->global->router_id;
} }
static inline void
rte_make_tmp_attrs(struct rte **rt, struct linpool *pool)
{
struct ea_list *(*mta)(struct rte *rt, struct linpool *pool);
mta = (*rt)->attrs->src->proto->make_tmp_attrs;
if (!mta) return;
*rt = rte_cow_rta(*rt, pool);
struct ea_list *ea = mta(*rt, pool);
ea->next = (*rt)->attrs->eattrs;
(*rt)->attrs->eattrs = ea;
}
extern pool *proto_pool; extern pool *proto_pool;
extern list proto_list; extern list proto_list;

View file

@ -308,6 +308,10 @@ void rte_free(rte *);
rte *rte_do_cow(rte *); rte *rte_do_cow(rte *);
static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; } static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; }
rte *rte_cow_rta(rte *r, linpool *lp); rte *rte_cow_rta(rte *r, linpool *lp);
void rte_init_tmp_attrs(struct rte *r, linpool *lp, uint max);
void rte_make_tmp_attr(struct rte *r, uint id, uint type, uintptr_t val);
void rte_make_tmp_attrs(struct rte **r, struct linpool *pool, struct rta **old_attrs);
uintptr_t rte_store_tmp_attr(struct rte *r, uint id);
void rt_dump(rtable *); void rt_dump(rtable *);
void rt_dump_all(void); void rt_dump_all(void);
int rt_feed_channel(struct channel *c); int rt_feed_channel(struct channel *c);
@ -481,6 +485,7 @@ typedef struct eattr {
#define EA_CODE(proto,id) (((proto) << 8) | (id)) #define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_ID(ea) ((ea) & 0xff) #define EA_ID(ea) ((ea) & 0xff)
#define EA_PROTO(ea) ((ea) >> 8) #define EA_PROTO(ea) ((ea) >> 8)
#define EA_ID_FLAG(ea) (1 << EA_ID(ea))
#define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT) #define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT)
#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT) #define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT)
#define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT) #define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT)
@ -510,7 +515,6 @@ const char *ea_custom_name(uint ea);
#define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */ #define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */
#define EAF_ORIGINATED 0x20 /* The attribute has originated locally */ #define EAF_ORIGINATED 0x20 /* The attribute has originated locally */
#define EAF_FRESH 0x40 /* An uncached attribute (e.g. modified in export filter) */ #define EAF_FRESH 0x40 /* An uncached attribute (e.g. modified in export filter) */
#define EAF_TEMP 0x80 /* A temporary attribute (the one stored in the tmp attr list) */
typedef struct adata { typedef struct adata {
uint length; /* Length of data */ uint length; /* Length of data */
@ -542,6 +546,7 @@ typedef struct ea_list {
#define EALF_SORTED 1 /* Attributes are sorted by code */ #define EALF_SORTED 1 /* Attributes are sorted by code */
#define EALF_BISECT 2 /* Use interval bisection for searching */ #define EALF_BISECT 2 /* Use interval bisection for searching */
#define EALF_CACHED 4 /* Attributes belonging to cached rta */ #define EALF_CACHED 4 /* Attributes belonging to cached rta */
#define EALF_TEMP 8 /* Temporary ea_list added by make_tmp_attrs hooks */
struct rte_src *rt_find_source(struct proto *p, u32 id); struct rte_src *rt_find_source(struct proto *p, u32 id);
struct rte_src *rt_get_source(struct proto *p, u32 id); struct rte_src *rt_get_source(struct proto *p, u32 id);
@ -574,6 +579,8 @@ void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **na
ea = t; \ ea = t; \
} \ } \
ea_sort(ea); \ ea_sort(ea); \
if (ea->count == 0) \
ea = NULL; \
} while(0) \ } while(0) \
static inline eattr * static inline eattr *

View file

@ -573,8 +573,8 @@ ea_do_sort(ea_list *e)
} }
/** /**
* In place discard duplicates, undefs and temporary attributes in sorted * In place discard duplicates and undefs in sorted ea_list. We use stable sort
* ea_list. We use stable sort for this reason. * for this reason.
**/ **/
static inline void static inline void
ea_do_prune(ea_list *e) ea_do_prune(ea_list *e)
@ -598,10 +598,6 @@ ea_do_prune(ea_list *e)
if ((s0->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF) if ((s0->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
continue; continue;
/* Drop temporary attributes */
if (s0->type & EAF_TEMP)
continue;
/* Copy the newest version to destination */ /* Copy the newest version to destination */
*d = *s0; *d = *s0;
@ -981,8 +977,6 @@ ea_dump(ea_list *e)
{ {
eattr *a = &e->attrs[i]; eattr *a = &e->attrs[i];
debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags); debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags);
if (a->type & EAF_TEMP)
debug("T");
debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]); debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]);
if (a->type & EAF_ORIGINATED) if (a->type & EAF_ORIGINATED)
debug("o"); debug("o");

View file

@ -45,10 +45,11 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d)
else else
from[0] = 0; from[0] = 0;
get_route_info = a->src->proto->proto->get_route_info;
/* Need to normalize the extended attributes */ /* Need to normalize the extended attributes */
if ((get_route_info || d->verbose) && !rta_is_cached(a)) if (d->verbose && !rta_is_cached(a) && a->eattrs)
ea_normalize(a->eattrs); ea_normalize(a->eattrs);
get_route_info = a->src->proto->proto->get_route_info;
if (get_route_info) if (get_route_info)
get_route_info(e, info); get_route_info(e, info);
else else
@ -114,7 +115,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
continue; continue;
ee = e; ee = e;
rte_make_tmp_attrs(&e, c->show_pool); rte_make_tmp_attrs(&e, c->show_pool, NULL);
/* Export channel is down, do not try to export routes to it */ /* Export channel is down, do not try to export routes to it */
if (ec && (ec->export_state == ES_DOWN)) if (ec && (ec->export_state == ES_DOWN))

View file

@ -326,6 +326,176 @@ rte_cow_rta(rte *r, linpool *lp)
return r; return r;
} }
/**
* rte_init_tmp_attrs - initialize temporary ea_list for route
* @r: route entry to be modified
* @lp: linpool from which to allocate attributes
* @max: maximum number of added temporary attribus
*
* This function is supposed to be called from make_tmp_attrs() and
* store_tmp_attrs() hooks before rte_make_tmp_attr() / rte_store_tmp_attr()
* functions. It allocates &ea_list with length for @max items for temporary
* attributes and puts it on top of eattrs stack.
*/
void
rte_init_tmp_attrs(rte *r, linpool *lp, uint max)
{
struct ea_list *e = lp_alloc(lp, sizeof(struct ea_list) + max * sizeof(eattr));
e->next = r->attrs->eattrs;
e->flags = EALF_SORTED | EALF_TEMP;
e->count = 0;
r->attrs->eattrs = e;
}
/**
* rte_make_tmp_attr - make temporary eattr from private route fields
* @r: route entry to be modified
* @id: attribute ID
* @type: attribute type
* @val: attribute value (u32 or adata ptr)
*
* This function is supposed to be called from make_tmp_attrs() hook for
* each temporary attribute, after temporary &ea_list was initialized by
* rte_init_tmp_attrs(). It checks whether temporary attribute is supposed to
* be defined (based on route pflags) and if so then it fills &eattr field in
* preallocated temporary &ea_list on top of route @r eattrs stack.
*
* Note that it may require free &eattr in temporary &ea_list, so it must not be
* called more times than @max argument of rte_init_tmp_attrs().
*/
void
rte_make_tmp_attr(rte *r, uint id, uint type, uintptr_t val)
{
if (r->pflags & EA_ID_FLAG(id))
{
ea_list *e = r->attrs->eattrs;
eattr *a = &e->attrs[e->count++];
a->id = id;
a->type = type;
a->flags = 0;
if (type & EAF_EMBEDDED)
a->u.data = (u32) val;
else
a->u.ptr = (struct adata *) val;
}
}
/**
* rte_store_tmp_attr - store temporary eattr to private route fields
* @r: route entry to be modified
* @id: attribute ID
*
* This function is supposed to be called from store_tmp_attrs() hook for
* each temporary attribute, after temporary &ea_list was initialized by
* rte_init_tmp_attrs(). It checks whether temporary attribute is defined in
* route @r eattrs stack, updates route pflags accordingly, undefines it by
* filling &eattr field in preallocated temporary &ea_list on top of the eattrs
* stack, and returns the value. Caller is supposed to store it in the
* appropriate private field.
*
* Note that it may require free &eattr in temporary &ea_list, so it must not be
* called more times than @max argument of rte_init_tmp_attrs()
*/
uintptr_t
rte_store_tmp_attr(rte *r, uint id)
{
ea_list *e = r->attrs->eattrs;
eattr *a = ea_find(e->next, id);
if (a)
{
e->attrs[e->count++] = (struct eattr) { .id = id, .type = EAF_TYPE_UNDEF };
r->pflags |= EA_ID_FLAG(id);
return (a->type & EAF_EMBEDDED) ? a->u.data : (uintptr_t) a->u.ptr;
}
else
{
r->pflags &= ~EA_ID_FLAG(id);
return 0;
}
}
/**
* rte_make_tmp_attrs - prepare route by adding all relevant temporary route attributes
* @r: route entry to be modified (may be replaced if COW)
* @lp: linpool from which to allocate attributes
* @old_attrs: temporary ref to old &rta (may be NULL)
*
* This function expands privately stored protocol-dependent route attributes
* to a uniform &eattr / &ea_list representation. It is essentially a wrapper
* around protocol make_tmp_attrs() hook, which does some additional work like
* ensuring that route @r is writable.
*
* The route @r may be read-only (with %REF_COW flag), in that case rw copy is
* obtained by rte_cow() and @r is replaced. If @rte is originally rw, it may be
* directly modified (and it is never copied).
*
* If the @old_attrs ptr is supplied, the function obtains another reference of
* old cached &rta, that is necessary in some cases (see rte_cow_rta() for
* details). It is freed by rte_store_tmp_attrs(), or manually by rta_free().
*
* Generally, if caller ensures that @r is read-only (e.g. in route export) then
* it may ignore @old_attrs (and set it to NULL), but must handle replacement of
* @r. If caller ensures that @r is writable (e.g. in route import) then it may
* ignore replacement of @r, but it must handle @old_attrs.
*/
void
rte_make_tmp_attrs(rte **r, linpool *lp, rta **old_attrs)
{
void (*make_tmp_attrs)(rte *r, linpool *lp);
make_tmp_attrs = (*r)->attrs->src->proto->make_tmp_attrs;
if (!make_tmp_attrs)
return;
/* We may need to keep ref to old attributes, will be freed in rte_store_tmp_attrs() */
if (old_attrs)
*old_attrs = rta_is_cached((*r)->attrs) ? rta_clone((*r)->attrs) : NULL;
*r = rte_cow_rta(*r, lp);
make_tmp_attrs(*r, lp);
}
/**
* rte_store_tmp_attrs - store temporary route attributes back to private route fields
* @r: route entry to be modified
* @lp: linpool from which to allocate attributes
* @old_attrs: temporary ref to old &rta
*
* This function stores temporary route attributes that were expanded by
* rte_make_tmp_attrs() back to private route fields and also undefines them.
* It is essentially a wrapper around protocol store_tmp_attrs() hook, which
* does some additional work like shortcut if there is no change and cleanup
* of @old_attrs reference obtained by rte_make_tmp_attrs().
*/
static void
rte_store_tmp_attrs(rte *r, linpool *lp, rta *old_attrs)
{
void (*store_tmp_attrs)(rte *rt, linpool *lp);
store_tmp_attrs = r->attrs->src->proto->store_tmp_attrs;
if (!store_tmp_attrs)
return;
ASSERT(!rta_is_cached(r->attrs));
/* If there is no new ea_list, we just skip the temporary ea_list */
ea_list *ea = r->attrs->eattrs;
if (ea && (ea->flags & EALF_TEMP))
r->attrs->eattrs = ea->next;
else
store_tmp_attrs(r, lp);
/* Free ref we got in rte_make_tmp_attrs(), have to do rta_lookup() first */
r->attrs = rta_lookup(r->attrs);
rta_free(old_attrs);
}
static int /* Actually better or at least as good as */ static int /* Actually better or at least as good as */
rte_better(rte *new, rte *old) rte_better(rte *new, rte *old)
{ {
@ -424,7 +594,7 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
goto accept; goto accept;
} }
rte_make_tmp_attrs(&rt, pool); rte_make_tmp_attrs(&rt, pool, NULL);
v = filter && ((filter == FILTER_REJECT) || v = filter && ((filter == FILTER_REJECT) ||
(f_run(filter, &rt, pool, (f_run(filter, &rt, pool,
@ -1008,12 +1178,13 @@ rte_free_quick(rte *e)
static int static int
rte_same(rte *x, rte *y) rte_same(rte *x, rte *y)
{ {
/* rte.flags are not checked, as they are mostly internal to rtable */
return return
x->attrs == y->attrs && x->attrs == y->attrs &&
x->flags == y->flags &&
x->pflags == y->pflags && x->pflags == y->pflags &&
x->pref == y->pref && x->pref == y->pref &&
(!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y)); (!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y)) &&
rte_is_filtered(x) == rte_is_filtered(y);
} }
static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); } static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
@ -1057,7 +1228,9 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
if (new && rte_same(old, new)) if (new && rte_same(old, new))
{ {
/* No changes, ignore the new route */ /* No changes, ignore the new route and refresh the old one */
old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
if (!rte_is_filtered(new)) if (!rte_is_filtered(new))
{ {
@ -1401,12 +1574,11 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
/* new is a private copy, i could modify it */ /* new is a private copy, i could modify it */
new->flags |= REF_FILTERED; new->flags |= REF_FILTERED;
} }
else else if (filter)
{ {
rte_make_tmp_attrs(&new, rte_update_pool); rta *old_attrs;
if (filter && (filter != FILTER_REJECT)) rte_make_tmp_attrs(&new, rte_update_pool, &old_attrs);
{
ea_list *oldea = new->attrs->eattrs;
int fr = f_run(filter, &new, rte_update_pool, 0); int fr = f_run(filter, &new, rte_update_pool, 0);
if (fr > F_ACCEPT) if (fr > F_ACCEPT)
{ {
@ -1414,13 +1586,15 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
rte_trace_in(D_FILTERS, p, new, "filtered out"); rte_trace_in(D_FILTERS, p, new, "filtered out");
if (! c->in_keep_filtered) if (! c->in_keep_filtered)
{
rta_free(old_attrs);
goto drop; goto drop;
}
new->flags |= REF_FILTERED; new->flags |= REF_FILTERED;
} }
if (new->attrs->eattrs != oldea && src->proto->store_tmp_attrs)
src->proto->store_tmp_attrs(new); rte_store_tmp_attrs(new, rte_update_pool, old_attrs);
}
} }
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */ if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
new->attrs = rta_lookup(new->attrs); new->attrs = rta_lookup(new->attrs);
@ -1517,7 +1691,7 @@ rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter)
int v = p->preexport ? p->preexport(p, &rt, rte_update_pool) : 0; int v = p->preexport ? p->preexport(p, &rt, rte_update_pool) : 0;
if (v == RIC_PROCESS) if (v == RIC_PROCESS)
{ {
rte_make_tmp_attrs(&rt, rte_update_pool); rte_make_tmp_attrs(&rt, rte_update_pool, NULL);
v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT); v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
} }
@ -2363,7 +2537,16 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
if (old->attrs->src == src) if (old->attrs->src == src)
{ {
if (new && rte_same(old, new)) if (new && rte_same(old, new))
{
/* Refresh the old rte, continue with update to main rtable */
if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
{
old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
return 1;
}
goto drop_update; goto drop_update;
}
/* Remove the old rte */ /* Remove the old rte */
*pos = old->next; *pos = old->next;

View file

@ -645,7 +645,7 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
rte->u.babel.seqno = r->seqno; rte->u.babel.seqno = r->seqno;
rte->u.babel.metric = r->metric; rte->u.babel.metric = r->metric;
rte->u.babel.router_id = r->router_id; rte->u.babel.router_id = r->router_id;
rte->pflags = 0; rte->pflags = EA_ID_FLAG(EA_BABEL_METRIC) | EA_ID_FLAG(EA_BABEL_ROUTER_ID);
e->unreachable = 0; e->unreachable = 0;
rte_update2(c, e->n.addr, rte, p->p.main_source); rte_update2(c, e->n.addr, rte, p->p.main_source);
@ -2077,32 +2077,6 @@ babel_kick_timer(struct babel_proto *p)
} }
static struct ea_list *
babel_prepare_attrs(struct linpool *pool, ea_list *next, uint metric, u64 router_id)
{
struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2*sizeof(eattr));
struct adata *rid = lp_alloc(pool, sizeof(struct adata) + sizeof(u64));
rid->length = sizeof(u64);
memcpy(&rid->data, &router_id, sizeof(u64));
l->next = next;
l->flags = EALF_SORTED;
l->count = 2;
l->attrs[0].id = EA_BABEL_METRIC;
l->attrs[0].flags = 0;
l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
l->attrs[0].u.data = metric;
l->attrs[1].id = EA_BABEL_ROUTER_ID;
l->attrs[1].flags = 0;
l->attrs[1].type = EAF_TYPE_OPAQUE | EAF_TEMP;
l->attrs[1].u.ptr = rid;
return l;
}
static int static int
babel_preexport(struct proto *P, struct rte **new, struct linpool *pool UNUSED) babel_preexport(struct proto *P, struct rte **new, struct linpool *pool UNUSED)
{ {
@ -2115,16 +2089,25 @@ babel_preexport(struct proto *P, struct rte **new, struct linpool *pool UNUSED)
return 0; return 0;
} }
static struct ea_list * static void
babel_make_tmp_attrs(struct rte *rt, struct linpool *pool) babel_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{ {
return babel_prepare_attrs(pool, NULL, rt->u.babel.metric, rt->u.babel.router_id); struct adata *id = lp_alloc_adata(pool, sizeof(u64));
memcpy(id->data, &rt->u.babel.router_id, sizeof(u64));
rte_init_tmp_attrs(rt, pool, 2);
rte_make_tmp_attr(rt, EA_BABEL_METRIC, EAF_TYPE_INT, rt->u.babel.metric);
rte_make_tmp_attr(rt, EA_BABEL_ROUTER_ID, EAF_TYPE_OPAQUE, (uintptr_t) id);
} }
static void static void
babel_store_tmp_attrs(struct rte *rt) babel_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{ {
rt->u.babel.metric = ea_get_int(rt->attrs->eattrs, EA_BABEL_METRIC, 0); rte_init_tmp_attrs(rt, pool, 2);
rt->u.babel.metric = rte_store_tmp_attr(rt, EA_BABEL_METRIC);
/* EA_BABEL_ROUTER_ID is read-only, we do not really save the value */
rte_store_tmp_attr(rt, EA_BABEL_ROUTER_ID);
} }
/* /*

View file

@ -496,7 +496,7 @@ mrt_rib_table_dump(struct mrt_table_dump_state *s, net *n, int add_path)
continue; continue;
} }
rte_make_tmp_attrs(&rt, s->linpool); rte_make_tmp_attrs(&rt, s->linpool, NULL);
if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT) if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT)
mrt_rib_table_entry(s, rt); mrt_rib_table_entry(s, rt);

View file

@ -106,8 +106,8 @@
#include "ospf.h" #include "ospf.h"
static int ospf_preexport(struct proto *P, rte **new, struct linpool *pool); static int ospf_preexport(struct proto *P, rte **new, struct linpool *pool);
static struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool); static void ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
static void ospf_store_tmp_attrs(struct rte *rt); static void ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool);
static void ospf_reload_routes(struct channel *C); static void ospf_reload_routes(struct channel *C);
static int ospf_rte_better(struct rte *new, struct rte *old); static int ospf_rte_better(struct rte *new, struct rte *old);
static int ospf_rte_same(struct rte *new, struct rte *old); static int ospf_rte_same(struct rte *new, struct rte *old);
@ -364,35 +364,6 @@ ospf_rte_same(struct rte *new, struct rte *old)
new->u.ospf.router_id == old->u.ospf.router_id; new->u.ospf.router_id == old->u.ospf.router_id;
} }
static ea_list *
ospf_build_attrs(ea_list * next, struct linpool *pool, u32 m1, u32 m2,
u32 tag, u32 rid)
{
struct ea_list *l =
lp_alloc(pool, sizeof(struct ea_list) + 4 * sizeof(eattr));
l->next = next;
l->flags = EALF_SORTED;
l->count = 4;
l->attrs[0].id = EA_OSPF_METRIC1;
l->attrs[0].flags = 0;
l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
l->attrs[0].u.data = m1;
l->attrs[1].id = EA_OSPF_METRIC2;
l->attrs[1].flags = 0;
l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
l->attrs[1].u.data = m2;
l->attrs[2].id = EA_OSPF_TAG;
l->attrs[2].flags = 0;
l->attrs[2].type = EAF_TYPE_INT | EAF_TEMP;
l->attrs[2].u.data = tag;
l->attrs[3].id = EA_OSPF_ROUTER_ID;
l->attrs[3].flags = 0;
l->attrs[3].type = EAF_TYPE_ROUTER_ID | EAF_TEMP;
l->attrs[3].u.data = rid;
return l;
}
void void
ospf_schedule_rtcalc(struct ospf_proto *p) ospf_schedule_rtcalc(struct ospf_proto *p)
@ -467,20 +438,24 @@ ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
return 0; return 0;
} }
static struct ea_list * static void
ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool) ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{ {
return ospf_build_attrs(NULL, pool, rt->u.ospf.metric1, rt->u.ospf.metric2, rte_init_tmp_attrs(rt, pool, 4);
rt->u.ospf.tag, rt->u.ospf.router_id); rte_make_tmp_attr(rt, EA_OSPF_METRIC1, EAF_TYPE_INT, rt->u.ospf.metric1);
rte_make_tmp_attr(rt, EA_OSPF_METRIC2, EAF_TYPE_INT, rt->u.ospf.metric2);
rte_make_tmp_attr(rt, EA_OSPF_TAG, EAF_TYPE_INT, rt->u.ospf.tag);
rte_make_tmp_attr(rt, EA_OSPF_ROUTER_ID, EAF_TYPE_ROUTER_ID, rt->u.ospf.router_id);
} }
static void static void
ospf_store_tmp_attrs(struct rte *rt) ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{ {
rt->u.ospf.metric1 = ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY); rte_init_tmp_attrs(rt, pool, 4);
rt->u.ospf.metric2 = ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC2, 10000); rt->u.ospf.metric1 = rte_store_tmp_attr(rt, EA_OSPF_METRIC1);
rt->u.ospf.tag = ea_get_int(rt->attrs->eattrs, EA_OSPF_TAG, 0); rt->u.ospf.metric2 = rte_store_tmp_attr(rt, EA_OSPF_METRIC2);
rt->u.ospf.router_id = ea_get_int(rt->attrs->eattrs, EA_OSPF_ROUTER_ID, 0); rt->u.ospf.tag = rte_store_tmp_attr(rt, EA_OSPF_TAG);
rt->u.ospf.router_id = rte_store_tmp_attr(rt, EA_OSPF_ROUTER_ID);
} }
/** /**

View file

@ -402,8 +402,6 @@ add_network(struct ospf_area *oa, net_addr *net, int metric, struct top_hash_ent
.type = RTS_OSPF, .type = RTS_OSPF,
.options = 0, .options = 0,
.metric1 = metric, .metric1 = metric,
.metric2 = LSINFINITY,
.tag = 0,
.rid = en->lsa.rt, .rid = en->lsa.rt,
.oa = oa, .oa = oa,
.nhs = en->nhs .nhs = en->nhs
@ -459,8 +457,6 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr
.type = RTS_OSPF, .type = RTS_OSPF,
.options = rt->options, .options = rt->options,
.metric1 = act->dist, .metric1 = act->dist,
.metric2 = LSINFINITY,
.tag = 0,
.rid = act->lsa.rt, .rid = act->lsa.rt,
.oa = oa, .oa = oa,
.nhs = act->nhs .nhs = act->nhs
@ -823,8 +819,6 @@ ospf_rt_sum(struct ospf_area *oa)
.type = RTS_OSPF_IA, .type = RTS_OSPF_IA,
.options = options, .options = options,
.metric1 = abr->n.metric1 + metric, .metric1 = abr->n.metric1 + metric,
.metric2 = LSINFINITY,
.tag = 0,
.rid = en->lsa.rt, /* ABR ID */ .rid = en->lsa.rt, /* ABR ID */
.oa = oa, .oa = oa,
.nhs = abr->n.nhs .nhs = abr->n.nhs
@ -1563,7 +1557,7 @@ ospf_ext_spf(struct ospf_proto *p)
{ {
nfa.type = RTS_OSPF_EXT1; nfa.type = RTS_OSPF_EXT1;
nfa.metric1 = br_metric + rt.metric; nfa.metric1 = br_metric + rt.metric;
nfa.metric2 = LSINFINITY; nfa.metric2 = 0;
} }
/* Mark the LSA as reachable */ /* Mark the LSA as reachable */
@ -2033,7 +2027,14 @@ again1:
e->u.ospf.metric2 = nf->old_metric2 = nf->n.metric2; e->u.ospf.metric2 = nf->old_metric2 = nf->n.metric2;
e->u.ospf.tag = nf->old_tag = nf->n.tag; e->u.ospf.tag = nf->old_tag = nf->n.tag;
e->u.ospf.router_id = nf->old_rid = nf->n.rid; e->u.ospf.router_id = nf->old_rid = nf->n.rid;
e->pflags = 0; e->pflags = EA_ID_FLAG(EA_OSPF_METRIC1) | EA_ID_FLAG(EA_OSPF_ROUTER_ID);
if (nf->n.type == RTS_OSPF_EXT2)
e->pflags |= EA_ID_FLAG(EA_OSPF_METRIC2);
/* Perhaps onfly if tag is non-zero? */
if ((nf->n.type == RTS_OSPF_EXT1) || (nf->n.type == RTS_OSPF_EXT2))
e->pflags |= EA_ID_FLAG(EA_OSPF_TAG);
DBG("Mod rte type %d - %N via %I on iface %s, met %d\n", DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1); a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);

View file

@ -193,8 +193,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
e->u.rip.from = a0.nh.iface; e->u.rip.from = a0.nh.iface;
e->u.rip.metric = rt_metric; e->u.rip.metric = rt_metric;
e->u.rip.tag = rt_tag; e->u.rip.tag = rt_tag;
e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG);
e->pflags = 0;
rte_update(&p->p, en->n.addr, e); rte_update(&p->p, en->n.addr, e);
} }
@ -997,28 +996,6 @@ rip_trigger_update(struct rip_proto *p)
* RIP protocol glue * RIP protocol glue
*/ */
static struct ea_list *
rip_prepare_attrs(struct linpool *pool, ea_list *next, u8 metric, u16 tag)
{
struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
l->next = next;
l->flags = EALF_SORTED;
l->count = 2;
l->attrs[0].id = EA_RIP_METRIC;
l->attrs[0].flags = 0;
l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
l->attrs[0].u.data = metric;
l->attrs[1].id = EA_RIP_TAG;
l->attrs[1].flags = 0;
l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
l->attrs[1].u.data = tag;
return l;
}
static void static void
rip_reload_routes(struct channel *C) rip_reload_routes(struct channel *C)
{ {
@ -1032,17 +1009,20 @@ rip_reload_routes(struct channel *C)
rip_kick_timer(p); rip_kick_timer(p);
} }
static struct ea_list * static void
rip_make_tmp_attrs(struct rte *rt, struct linpool *pool) rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{ {
return rip_prepare_attrs(pool, NULL, rt->u.rip.metric, rt->u.rip.tag); rte_init_tmp_attrs(rt, pool, 2);
rte_make_tmp_attr(rt, EA_RIP_METRIC, EAF_TYPE_INT, rt->u.rip.metric);
rte_make_tmp_attr(rt, EA_RIP_TAG, EAF_TYPE_INT, rt->u.rip.tag);
} }
static void static void
rip_store_tmp_attrs(struct rte *rt) rip_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{ {
rt->u.rip.metric = ea_get_int(rt->attrs->eattrs, EA_RIP_METRIC, 1); rte_init_tmp_attrs(rt, pool, 2);
rt->u.rip.tag = ea_get_int(rt->attrs->eattrs, EA_RIP_TAG, 0); rt->u.rip.metric = rte_store_tmp_attr(rt, EA_RIP_METRIC);
rt->u.rip.tag = rte_store_tmp_attr(rt, EA_RIP_TAG);
} }
static int static int

View file

@ -13,7 +13,7 @@
#ifdef GIT_LABEL #ifdef GIT_LABEL
#define BIRD_VERSION XSTR1(GIT_LABEL) #define BIRD_VERSION XSTR1(GIT_LABEL)
#else #else
#define BIRD_VERSION "2.0.3" #define BIRD_VERSION "2.0.4"
#endif #endif
/* Include parameters determined by configure script */ /* Include parameters determined by configure script */

View file

@ -301,7 +301,7 @@ krt_learn_announce_update(struct krt_proto *p, rte *e)
net *n = e->net; net *n = e->net;
rta *aa = rta_clone(e->attrs); rta *aa = rta_clone(e->attrs);
rte *ee = rte_get_temp(aa); rte *ee = rte_get_temp(aa);
ee->pflags = 0; ee->pflags = EA_ID_FLAG(EA_KRT_SOURCE) | EA_ID_FLAG(EA_KRT_METRIC);
ee->u.krt = e->u.krt; ee->u.krt = e->u.krt;
rte_update(&p->p, n->n.addr, ee); rte_update(&p->p, n->n.addr, ee);
} }
@ -577,7 +577,7 @@ krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
if (filter == FILTER_REJECT) if (filter == FILTER_REJECT)
return NULL; return NULL;
rte_make_tmp_attrs(&rt, krt_filter_lp); rte_make_tmp_attrs(&rt, krt_filter_lp, NULL);
/* We could run krt_preexport() here, but it is already handled by KRF_INSTALLED */ /* We could run krt_preexport() here, but it is already handled by KRF_INSTALLED */
@ -910,33 +910,20 @@ krt_scan_timer_kick(struct krt_proto *p)
* Updates * Updates
*/ */
static struct ea_list * static void
krt_make_tmp_attrs(rte *rt, struct linpool *pool) krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{ {
struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr)); rte_init_tmp_attrs(rt, pool, 2);
rte_make_tmp_attr(rt, EA_KRT_SOURCE, EAF_TYPE_INT, rt->u.krt.proto);
l->next = NULL; rte_make_tmp_attr(rt, EA_KRT_METRIC, EAF_TYPE_INT, rt->u.krt.metric);
l->flags = EALF_SORTED;
l->count = 2;
l->attrs[0].id = EA_KRT_SOURCE;
l->attrs[0].flags = 0;
l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
l->attrs[0].u.data = rt->u.krt.proto;
l->attrs[1].id = EA_KRT_METRIC;
l->attrs[1].flags = 0;
l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
l->attrs[1].u.data = rt->u.krt.metric;
return l;
} }
static void static void
krt_store_tmp_attrs(rte *rt) krt_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{ {
/* EA_KRT_SOURCE is read-only */ rte_init_tmp_attrs(rt, pool, 2);
rt->u.krt.metric = ea_get_int(rt->attrs->eattrs, EA_KRT_METRIC, 0); rt->u.krt.proto = rte_store_tmp_attr(rt, EA_KRT_SOURCE);
rt->u.krt.metric = rte_store_tmp_attr(rt, EA_KRT_METRIC);
} }
static int static int