From d3f36e5978e85a926c497e2aa2cbdf319776ebb5 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 13 Nov 2009 14:43:29 +0100 Subject: [PATCH 01/22] Fixes BIRD socket unlink. --- sysdep/unix/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 17845d20..75944c59 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -313,7 +313,7 @@ async_shutdown(void) void sysdep_shutdown_done(void) { - unlink(PATH_CONTROL_SOCKET); + unlink(path_control_socket); die("System shutdown completed"); } From 653b4015f137d9590147f8774ec686031696f81c Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 13 Nov 2009 14:54:43 +0100 Subject: [PATCH 02/22] After shutdown, BIRD should have exit code 0. --- sysdep/unix/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 75944c59..5e4aa97a 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -314,7 +314,8 @@ void sysdep_shutdown_done(void) { unlink(path_control_socket); - die("System shutdown completed"); + log_msg(L_FATAL "System shutdown completed"); + exit(0); } /* From 2eece54a04d95f534b935ccac4c3959b25516bd5 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sun, 15 Nov 2009 14:24:20 +0100 Subject: [PATCH 03/22] Fixes bug related to reconfiguration with bgp_path first/last operators. --- filter/filter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/filter/filter.c b/filter/filter.c index 07a25f4a..b88039fb 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -731,7 +731,7 @@ interpret(struct f_inst *what) case P('a','f'): /* Get first ASN from AS PATH */ ONEARG; if (v1.type != T_PATH) - runtime( "AS Path expected" ); + runtime( "AS path expected" ); as = 0; as_path_get_first(v1.val.ad, &as); @@ -942,6 +942,8 @@ i_same(struct f_inst *f1, struct f_inst *f2) case P('i','M'): TWOARGS; break; case P('A','p'): TWOARGS; break; case P('C','a'): TWOARGS; break; + case P('a','f'): + case P('a','l'): ONEARG; break; default: bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff); } From 3228c72cc030f409914134440a7e55bbcfc9ff6a Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 17 Nov 2009 11:41:29 +0100 Subject: [PATCH 04/22] Implements RFC 5004 - prefer older external routes. --- doc/bird.sgml | 5 +++++ proto/bgp/attrs.c | 10 ++++++++-- proto/bgp/bgp.h | 1 + proto/bgp/config.Y | 3 ++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 0c2b8fbe..40793ed3 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -981,6 +981,11 @@ for each neighbor using the following configuration parameters: path metric Enable comparison of path lengths when deciding which BGP route is the best one. Default: on. + prefer older Standard route selection algorithm + breaks ties by comparing router IDs. This changes the behavior + to prefer older routes (when both are external and from different + peer). For details, see RFC 5004. Default: off. + default bgp_med Value of the Multiple Exit Discriminator to be used during route selection when the MED attribute is missing. Default: 0. diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index d839ed09..9c7bc30a 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1044,7 +1044,6 @@ bgp_rte_better(rte *new, rte *old) return 0; /* RFC 4271 9.1.2.2. c) Compare MED's */ - if (bgp_get_neighbor(new) == bgp_get_neighbor(old)) { x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); @@ -1082,12 +1081,19 @@ bgp_rte_better(rte *new, rte *old) y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID)); n = x ? x->u.data : new_bgp->remote_id; o = y ? y->u.data : old_bgp->remote_id; + + /* RFC 5004 - prefer older routes */ + /* (if both are external and from different peer) */ + if ((new_bgp->cf->prefer_older || old_bgp->cf->prefer_older) && + !new_bgp->is_internal && n != o) + return 0; + + /* rest of RFC 4271 9.1.2.2. f) */ if (n < o) return 1; if (n > o) return 0; - /* RFC 4271 9.1.2.2. g) Compare peer IP adresses */ return (ipa_compare(new_bgp->cf->remote_ip, old_bgp->cf->remote_ip) < 0); } diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 0a82be2b..c487aafb 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -24,6 +24,7 @@ struct bgp_config { ip_addr source_addr; /* Source address to use */ int next_hop_self; /* Always set next hop to local IP address */ int compare_path_lengths; /* Use path lengths when selecting best route */ + int prefer_older; /* Prefer older routes according to RFC 5004 */ u32 default_local_pref; /* Default value for LOCAL_PREF attribute */ u32 default_med; /* Default value for MULTI_EXIT_DISC attribute */ int capabilities; /* Enable capability handshake [RFC3392] */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 7360820f..94011750 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -22,7 +22,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN, BGP_NEXT_HOP, BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY, SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4, - CAPABILITIES, LIMIT, PASSIVE) + CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER) CF_GRAMMAR @@ -65,6 +65,7 @@ bgp_proto: | bgp_proto MULTIHOP expr VIA ipa ';' { BGP_CFG->multihop = $3; BGP_CFG->multihop_via = $5; } | bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; } | bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; } + | bgp_proto PREFER OLDER bool ';' { BGP_CFG->prefer_older = $4; } | bgp_proto DEFAULT BGP_MED expr ';' { BGP_CFG->default_med = $4; } | bgp_proto DEFAULT BGP_LOCAL_PREF expr ';' { BGP_CFG->default_local_pref = $4; } | bgp_proto SOURCE ADDRESS ipa ';' { BGP_CFG->source_addr = $4; } From d0e2d6d1e05455cf1ec4e71135edaa659fe96dac Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 17 Nov 2009 14:17:23 +0100 Subject: [PATCH 05/22] Show both IPv6 next hop addresses in BGP. --- proto/bgp/attrs.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 9c7bc30a..7b49bdfa 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -165,6 +165,22 @@ bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a, int len) #endif } +static void +bgp_format_next_hop(eattr *a, byte *buf, int buflen UNUSED) +{ + ip_addr *ipp = (ip_addr *) a->u.ptr->data; +#ifdef IPV6 + /* in IPv6, we might have two addresses in NEXT HOP */ + if ((a->u.ptr->length == NEXT_HOP_LENGTH) && ipa_nonzero(ipp[1])) + { + bsprintf(buf, "%I %I", ipp[0], ipp[1]); + return; + } +#endif + + bsprintf(buf, "%I", ipp[0]); +} + static int bgp_check_aggregator(struct bgp_proto *p, byte *a UNUSED, int len) { @@ -234,7 +250,7 @@ static struct attr_desc bgp_attr_table[] = { { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */ NULL, NULL }, /* is checked by validate_as_path() as a special case */ { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */ - bgp_check_next_hop, NULL }, + bgp_check_next_hop, bgp_format_next_hop }, { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 1, /* BA_MULTI_EXIT_DISC */ NULL, NULL }, { "local_pref", 4, BAF_TRANSITIVE, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */ From e04555c04545278cfe3aeae85d707b1d78e5abeb Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 17 Nov 2009 15:45:05 +0100 Subject: [PATCH 06/22] Implement description field of protocol. --- nest/config.Y | 3 ++- nest/proto.c | 3 +++ nest/protocol.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nest/config.Y b/nest/config.Y index 4721112a..f2879430 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -44,7 +44,7 @@ CF_KEYWORDS(ROUTER, ID, PROTOCOL, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE) -CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT, PASSWORDS) +CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT, PASSWORDS, DESCRIPTION) CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE) @@ -141,6 +141,7 @@ proto_item: | EXPORT imexport { this_proto->out_filter = $2; } | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } + | DESCRIPTION dsc { this_proto->dsc = $2; } ; imexport: diff --git a/nest/proto.c b/nest/proto.c index 7bb1286d..c05db767 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -307,6 +307,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty if (sym && sym->class == SYM_PROTO && !new->shutdown) { /* Found match, let's check if we can smoothly switch to new configuration */ + /* No need to check description */ nc = sym->def; if (!force_reconfig && nc->protocol == oc->protocol @@ -702,6 +703,8 @@ proto_do_show(struct proto *p, int verbose) buf); if (verbose) { + if (p->cf->dsc) + cli_msg(-1006, " Description: %s", p->cf->dsc); cli_msg(-1006, " Preference: %d", p->preference); cli_msg(-1006, " Input filter: %s", filter_name(p->in_filter)); cli_msg(-1006, " Output filter: %s", filter_name(p->out_filter)); diff --git a/nest/protocol.h b/nest/protocol.h index 484df846..807b579f 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -81,6 +81,7 @@ struct proto_config { struct protocol *protocol; /* Protocol */ struct proto *proto; /* Instance we've created */ char *name; + char *dsc; unsigned debug, preference, disabled; /* Generic parameters */ u32 router_id; /* Protocol specific router ID */ struct rtable_config *table; /* Table we're attached to */ From 62aa96caa28180f76c8aff0c49dd21128e396153 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 17 Nov 2009 15:50:29 +0100 Subject: [PATCH 07/22] Adds some documentation to the description option. --- doc/bird.conf.example | 1 + doc/bird.sgml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/doc/bird.conf.example b/doc/bird.conf.example index 0a2aac7e..6ab03a23 100644 --- a/doc/bird.conf.example +++ b/doc/bird.conf.example @@ -162,6 +162,7 @@ protocol static { #protocol bgp { # disabled; +# description "My BGP uplink" # local as 65000; # neighbor 62.168.0.130 as 5588; # multihop 20 via 62.168.0.13; diff --git a/doc/bird.sgml b/doc/bird.sgml index 40793ed3..b475830e 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -307,6 +307,10 @@ to zero to disable it. An empty is equivalent to export This is similar to the import keyword, except that it works in the direction from the routing table to the protocol. Default: description " This is an optional + description of the protocol. It is displayed as a part of the + output of 'show route all' command. + table Connect this protocol to a non-default routing table. From 3f9b7bfe9ff050430a5886b22a5ab11b5f253048 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 18 Nov 2009 20:32:36 +0100 Subject: [PATCH 08/22] Implements option that controls IPv6 BGP next hops when lladdr is missing. --- doc/bird.sgml | 28 +++++++++++++++++++++------ nest/config.Y | 2 +- proto/bgp/bgp.c | 4 ++++ proto/bgp/bgp.h | 5 +++++ proto/bgp/config.Y | 6 +++++- proto/bgp/packets.c | 47 ++++++++++++++++++++++++++++++--------------- 6 files changed, 69 insertions(+), 23 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index b475830e..db9a7474 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -646,7 +646,7 @@ incompatible with each other (that is to prevent you from shooting in the foot). but 1.0.0.0/16 ˜ [ 1.0.0.0/8- ] is false. Cisco-style patterns like 10.0.0.0/8 ge 16 le 24 can be expressed - in Bird as 10.0.0.0/8{16,24}, 192.168.0.0/16 le 24 as + in BIRD as 10.0.0.0/8{16,24}, 192.168.0.0/16 le 24 as 192.168.0.0/16{16,24} and 192.168.0.0/16 ge 24 as 192.168.0.0/16{24,32}. @@ -887,12 +887,28 @@ for each neighbor using the following configuration parameters: we should route via our direct neighbor with address next hop self Avoid calculation of the Next Hop attribute - and always advertise our own source address (see below) as a next hop. - This needs to be used only - occasionally to circumvent misconfigurations of other routers. + next hop self Avoid calculation of the Next Hop + attribute and always advertise our own source address (see + below) as a next hop. This needs to be used only occasionally + to circumvent misconfigurations of other routers. Default: disabled. + missing lladdr self|drop|ignoreNext Hop attribute + in BGP-IPv6 sometimes contains just the global IPv6 address, + but sometimes it has to contain both global and link-local + IPv6 addresses. This option specifies what to do if BIRD have + to send both addresses but does not know link-local address. + This situation might happen when routes from other protocols + are exported to BGP, or when improper updates are received + from BGP peers. source address Define local address we should use for next hop calculation. Default: the address of the local end of the interface our neighbor is connected to. @@ -920,7 +936,7 @@ for each neighbor using the following configuration parameters: as a route server client. A route server is used as a replacement for full mesh EBGP routing in Internet exchange points in a similar way to route reflectors used in IBGP routing. - Bird does not implement obsoleted RFC 1863, but uses ad-hoc implementation, + BIRD does not implement obsoleted RFC 1863, but uses ad-hoc implementation, which behaves like plain EBGP but reduces modifications to advertised route attributes to be transparent (for example does not prepend its AS number to AS PATH attribute and keep MED attribute). Default: disabled. diff --git a/nest/config.Y b/nest/config.Y index f2879430..3d6f1548 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -141,7 +141,7 @@ proto_item: | EXPORT imexport { this_proto->out_filter = $2; } | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } - | DESCRIPTION dsc { this_proto->dsc = $2; } + | DESCRIPTION TEXT { this_proto->dsc = $2; } ; imexport: diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index b38c6b13..cde02768 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -886,6 +886,10 @@ bgp_check(struct bgp_config *c) if ((c->local_as == c->remote_as) && (c->rs_client)) cf_error("Only external neighbor can be RS client"); + + /* Different default based on rs_client */ + if (c->missing_lladdr == 0) + c->missing_lladdr = c->rs_client ? MLL_DROP : MLL_SELF; } static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" }; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index c487aafb..c4d03352 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -23,6 +23,7 @@ struct bgp_config { ip_addr multihop_via; /* Multihop: address to route to */ ip_addr source_addr; /* Source address to use */ int next_hop_self; /* Always set next hop to local IP address */ + int missing_lladdr; /* What we will do when we don' know link-local addr, see MLL_* */ int compare_path_lengths; /* Use path lengths when selecting best route */ int prefer_older; /* Prefer older routes according to RFC 5004 */ u32 default_local_pref; /* Default value for LOCAL_PREF attribute */ @@ -46,6 +47,10 @@ struct bgp_config { char *password; /* Password used for MD5 authentication */ }; +#define MLL_SELF 1 +#define MLL_DROP 2 +#define MLL_IGNORE 3 + struct bgp_conn { struct bgp_proto *bgp; struct birdsock *sk; diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 94011750..35769466 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -22,7 +22,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN, BGP_NEXT_HOP, BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY, SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4, - CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER) + CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER, MISSING, LLADDR, + DROP, IGNORE) CF_GRAMMAR @@ -64,6 +65,9 @@ bgp_proto: | bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; } | bgp_proto MULTIHOP expr VIA ipa ';' { BGP_CFG->multihop = $3; BGP_CFG->multihop_via = $5; } | bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; } + | bgp_proto MISSING LLADDR SELF ';' { BGP_CFG->missing_lladdr = MLL_SELF; } + | bgp_proto MISSING LLADDR DROP ';' { BGP_CFG->missing_lladdr = MLL_DROP; } + | bgp_proto MISSING LLADDR IGNORE ';' { BGP_CFG->missing_lladdr = MLL_IGNORE; } | bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; } | bgp_proto PREFER OLDER bool ';' { BGP_CFG->prefer_older = $4; } | bgp_proto DEFAULT BGP_MED expr ';' { BGP_CFG->default_med = $4; } diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index d126fe5f..4176c9fe 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -199,7 +199,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) if (a_size < 0) { - log(L_ERR "%s: Attribute list too long, skipping corresponding route group", p->p.name); + log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name); bgp_flush_prefixes(p, buck); rem_node(&buck->send_node); bgp_free_bucket(p, buck); @@ -234,9 +234,9 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) { struct bgp_proto *p = conn->bgp; struct bgp_bucket *buck; - int size, second; + int size, second, rem_stored; int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; - byte *w, *tmp, *tstart; + byte *w, *w_stored, *tmp, *tstart; ip_addr *ipp, ip, ip_ll; ea_list *ea; eattr *nh; @@ -272,28 +272,25 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) } DBG("Processing bucket %p\n", buck); - size = bgp_encode_attrs(p, w, buck->eattrs, 2048); + rem_stored = remains; + w_stored = w; + size = bgp_encode_attrs(p, w, buck->eattrs, 2048); if (size < 0) { - log(L_ERR "%s: Attribute list too long, ignoring corresponding route group", p->p.name); + log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name); bgp_flush_prefixes(p, buck); rem_node(&buck->send_node); bgp_free_bucket(p, buck); continue; } - w += size; remains -= size; - tstart = tmp = bgp_attach_attr_wa(&ea, bgp_linpool, BA_MP_REACH_NLRI, remains-8); - *tmp++ = 0; - *tmp++ = BGP_AF_IPV6; - *tmp++ = 1; + + /* We have two addresses here in NEXT_HOP eattr. Really. + Unless NEXT_HOP was modified by filter */ nh = ea_find(buck->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); ASSERT(nh); - - /* We have two addresses here in 'nh'. Really. - Unless NEXT_HOP was modified by filter */ second = (nh->u.ptr->length == NEXT_HOP_LENGTH); ipp = (ip_addr *) nh->u.ptr->data; ip = ipp[0]; @@ -322,12 +319,32 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) ip_ll = ipp[1]; else { - ip = p->source_addr; - ip_ll = p->local_link; + switch (p->cf->missing_lladdr) + { + case MLL_SELF: + ip = p->source_addr; + ip_ll = p->local_link; + break; + case MLL_DROP: + log(L_ERR "%s: Missing link-local next hop address, skipping corresponding routes", p->p.name); + w = w_stored; + remains = rem_stored; + bgp_flush_prefixes(p, buck); + rem_node(&buck->send_node); + bgp_free_bucket(p, buck); + continue; + case MLL_IGNORE: + break; + } } } } + tstart = tmp = bgp_attach_attr_wa(&ea, bgp_linpool, BA_MP_REACH_NLRI, remains-8); + *tmp++ = 0; + *tmp++ = BGP_AF_IPV6; + *tmp++ = 1; + if (ipa_nonzero(ip_ll)) { *tmp++ = 32; From a4644ed6ab32c098b755bdac03498634b2794409 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 19 Nov 2009 11:44:17 +0100 Subject: [PATCH 09/22] Implement option to exit after config file parsing. --- doc/bird.sgml | 5 ++++- sysdep/unix/main.c | 10 +++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index db9a7474..4b72ac61 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -129,7 +129,10 @@ options. The most important ones are: enable debug messages and run bird in foreground. -D - log debugging information to given file instead of stderr + log debugging information to given file instead of stderr. + + -p + just parse the config file and exit. -s use given filename for a socket for communications with the client, default is /var/run/bird.ctl. diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 5e4aa97a..533754ea 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -365,7 +365,7 @@ signal_init(void) * Parsing of command-line arguments */ -static char *opt_list = "c:dD:s:"; +static char *opt_list = "c:dD:ps:"; static void usage(void) @@ -374,6 +374,8 @@ usage(void) exit(1); } +int parse_and_exit; + static void parse_args(int argc, char **argv) { @@ -402,6 +404,9 @@ parse_args(int argc, char **argv) log_init_debug(optarg); debug_flag |= 2; break; + case 'p': + parse_and_exit = 1; + break; case 's': path_control_socket = optarg; break; @@ -444,6 +449,9 @@ main(int argc, char **argv) read_config(); + if (parse_and_exit) + exit(0); + if (!debug_flag) { pid_t pid = fork(); From 6a72a276f6a677a8adec9566867023d76ac7da7e Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 19 Nov 2009 12:53:55 +0100 Subject: [PATCH 10/22] New version. --- NEWS | 7 +++++++ TODO | 1 - misc/bird.spec | 2 +- sysdep/config.h | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 0b2e4ee9..81dc6190 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,10 @@ +Version 1.1.6 (2009-11-20) + o Implements RFC 5004 - prefer older external routes. + o There is a change how route servers handle missing link local + addresses in next hop atribute - see 'missing lladdr' option. + o Several minor features (description field, parse check option). + o Several minor bugfixes. + Version 1.1.5 (2009-10-29) o Better scalability of BGP. o New accessors for AS path - first and last. diff --git a/TODO b/TODO index f3140530..d5c10562 100644 --- a/TODO +++ b/TODO @@ -21,7 +21,6 @@ Globals Various ideas ~~~~~~~~~~~~~ -- client: paging of output - client: Ctrl-R eats one more enter - bgp: timing of updates? - netlink: realms diff --git a/misc/bird.spec b/misc/bird.spec index 021fba2f..8c8b04fc 100644 --- a/misc/bird.spec +++ b/misc/bird.spec @@ -1,6 +1,6 @@ Summary: BIRD Internet Routing Daemon Name: bird -Version: 1.1.5 +Version: 1.1.6 Release: 1 Copyright: GPL Group: Networking/Daemons diff --git a/sysdep/config.h b/sysdep/config.h index 03399bd0..11ade63e 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -7,7 +7,7 @@ #define _BIRD_CONFIG_H_ /* BIRD version */ -#define BIRD_VERSION "1.1.5" +#define BIRD_VERSION "1.1.6" /* Include parameters determined by configure script */ #include "sysdep/autoconf.h" From 1f8be1e46f666e79072cc5f800c2dba91788368b Mon Sep 17 00:00:00 2001 From: Ondrej Filip Date: Thu, 19 Nov 2009 23:15:58 +0100 Subject: [PATCH 11/22] Added word 'IPv6' to underline, that this problem was not IPv4 related. --- NEWS | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 81dc6190..e348c496 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,8 @@ -Version 1.1.6 (2009-11-20) +Version 1.1.6 (2009-11-19) o Implements RFC 5004 - prefer older external routes. - o There is a change how route servers handle missing link local - addresses in next hop atribute - see 'missing lladdr' option. + o There is a change how route servers handle missing IPv6 link + local addresses in next hop atribute - see 'missing lladdr' + option. o Several minor features (description field, parse check option). o Several minor bugfixes. From 717e4c4d8173a8dbae2956f1703ff6d4365a9d34 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 20 Nov 2009 09:29:29 +0100 Subject: [PATCH 12/22] Fixes in the documentation. --- doc/bird.sgml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 4b72ac61..9e74470f 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -903,13 +903,13 @@ for each neighbor using the following configuration parameters: to send both addresses but does not know link-local address. This situation might happen when routes from other protocols are exported to BGP, or when improper updates are received - from BGP peers. source address Define local address we should use From 069bfcb53cef012c063a27e5af93d620be2917bd Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 24 Nov 2009 17:15:20 +0100 Subject: [PATCH 13/22] Fixes serious bug in core related to route filtering. If protocol announces a route, route is accepted by import filter to routing table, and later it announces replacement of that route that is rejected by import filter, old route remains in routing table. --- nest/rt-table.c | 1 + 1 file changed, 1 insertion(+) diff --git a/nest/rt-table.c b/nest/rt-table.c index 87bf0dc7..dda0e56b 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -636,6 +636,7 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new drop: rte_free(new); + rte_recalculate(table, net, p, src, NULL, NULL); rte_update_unlock(); } From bf47fe4b2e40ccfcfe6af2d86548d06cdf9739c5 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 26 Nov 2009 20:47:59 +0100 Subject: [PATCH 14/22] Implements BGP route refresh. --- doc/bird.sgml | 16 +++++++++-- nest/config.Y | 7 +++++ nest/proto.c | 67 +++++++++++++++++++++++++++++++++++++++++---- nest/protocol.h | 9 ++++++ nest/rt-table.c | 14 ++++++++++ proto/bgp/bgp.c | 12 ++++++++ proto/bgp/bgp.h | 3 ++ proto/bgp/config.Y | 4 ++- proto/bgp/packets.c | 65 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 187 insertions(+), 10 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 9e74470f..d08b0c17 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -482,9 +482,6 @@ This argument can be omitted if there exists only a single instance. number of networks, number of routes before and after filtering). If you use enable|disable|restart - Enable, disable or restart a given protocol instance, instances matching the or configure [soft] [" Reload configuration from a given file. BIRD will smoothly switch itself to the new configuration, protocols are @@ -496,6 +493,9 @@ This argument can be omitted if there exists only a single instance. but new routes would be processed according to the new filters. + enable|disable|restart + Enable, disable or restart a given protocol instance, instances matching the or enable route refresh When BGP speaker + changes its import filter, it has to re-examine all routes + received from its neighbor against the new filter. As these + routes might not be available, there is a BGP protocol + extension Route Refresh (specified in RFC 2918) that allows + BGP speaker to request re-advertisment of all routes from its + neighbor. This option specifies whether BIRD advertises this + capability and accepts such requests. Even when disabled, BIRD + can send route refresh requests. Default: on. + enable as4 BGP protocol was designed to use 2B AS numbers and was extended later to allow 4B AS number. BIRD supports 4B AS extension, but by disabling this option it can be persuaded not to advertise it and diff --git a/nest/config.Y b/nest/config.Y index 3d6f1548..dde20c5a 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -45,6 +45,7 @@ CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILT CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE) CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT, PASSWORDS, DESCRIPTION) +CF_KEYWORDS(RELOAD, REFEED) CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE) @@ -442,6 +443,12 @@ CF_CLI(ENABLE, proto_patt, | \"\" | all, [[Enable protocol]] { proto_xxable($2, 1); } ; CF_CLI(RESTART, proto_patt, | \"\" | all, [[Restart protocol]]) { proto_xxable($2, 2); } ; +CF_CLI(RELOAD, proto_patt, | \"\" | all, [[Reload protocol]]) +{ proto_xxable($2, 3); } ; +CF_CLI(REFEED, proto_patt, | \"\" | all, [[Refeed protocol BROKEN]]) +{ proto_xxable($2, 4); } ; + + CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging]]) CF_CLI(DEBUG, proto_patt debug_mask, ( | | all) (all | off | { states | routes | filters | events | packets }), [[Control protocol debugging]]) diff --git a/nest/proto.c b/nest/proto.c index c05db767..c531d869 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -73,6 +73,10 @@ proto_relink(struct proto *p) rem_node(&p->n); switch (p->core_state) { + case FS_HUNGRY: + l = &inactive_proto_list; + break; + case FS_FEEDING: case FS_HAPPY: l = &active_proto_list; break; @@ -80,7 +84,7 @@ proto_relink(struct proto *p) l = &flush_proto_list; break; default: - l = &inactive_proto_list; + ASSERT(0); } proto_enqueue(l, p); } @@ -549,7 +553,7 @@ proto_feed_more(void *P) } static void -proto_feed(void *P) +proto_feed_initial(void *P) { struct proto *p = P; @@ -577,15 +581,44 @@ proto_schedule_flush(struct proto *p) } static void -proto_schedule_feed(struct proto *p) +proto_schedule_feed(struct proto *p, int initial) { DBG("%s: Scheduling meal\n", p->name); p->core_state = FS_FEEDING; proto_relink(p); - p->attn->hook = proto_feed; + p->attn->hook = initial ? proto_feed_initial : proto_feed_more; ev_schedule(p->attn); } +/** + * proto_request_feeding - request feeding routes to the protocol + * @p: given protocol + * + * Sometimes it is needed to send again all routes to the + * protocol. This is called feeding and can be requested by this + * function. This would cause protocol core state transition + * to FS_FEEDING (during feeding) and when completed, it will + * switch back to FS_HAPPY. This function can be called even + * when feeding is already running, in that case it is restarted. + */ +void +proto_request_feeding(struct proto *p) +{ + ASSERT(p->proto_state == PS_UP); + + /* If we are already feeding, we want to restart it */ + if (p->core_state == FS_FEEDING) + { + /* Unless feeding is in initial state */ + if (p->attn->hook == proto_feed_initial) + return; + + rt_feed_baby_abort(p); + } + + proto_schedule_feed(p, 0); +} + /** * proto_notify_state - notify core about protocol state change * @p: protocol the state of which has changed @@ -635,7 +668,7 @@ proto_notify_state(struct proto *p, unsigned ps) case PS_UP: ASSERT(ops == PS_DOWN || ops == PS_START); ASSERT(cs == FS_HUNGRY); - proto_schedule_feed(p); + proto_schedule_feed(p, 1); break; case PS_STOP: if ((cs = FS_FEEDING) || (cs == FS_HAPPY)) @@ -798,6 +831,7 @@ proto_xxable(char *pattern, int xx) { cli_msg(-9, "%s: disabled", p->name); p->disabled = 1; + proto_rethink_goal(p); } break; case 1: @@ -807,6 +841,7 @@ proto_xxable(char *pattern, int xx) { cli_msg(-11, "%s: enabled", p->name); p->disabled = 0; + proto_rethink_goal(p); } break; case 2: @@ -817,13 +852,33 @@ proto_xxable(char *pattern, int xx) p->disabled = 1; proto_rethink_goal(p); p->disabled = 0; + proto_rethink_goal(p); cli_msg(-12, "%s: restarted", p->name); } break; + case 3: + // FIXME change msg number + if (p->disabled) + cli_msg(-8, "%s: already disabled", p->name); + else if (p->reload_routes && p->reload_routes(p)) + cli_msg(-12, "%s: reloading", p->name); + else + cli_msg(-12, "%s: reload failed", p->name); + break; + case 4: + // FIXME change msg number + if (p->disabled) + cli_msg(-8, "%s: already disabled", p->name); + else + { + proto_request_feeding(p); + cli_msg(-12, "%s: reexport failed", p->name); + } + break; + default: ASSERT(0); } - proto_rethink_goal(p); } WALK_PROTO_LIST_END; if (!cnt) diff --git a/nest/protocol.h b/nest/protocol.h index 807b579f..66e6c43d 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -152,6 +152,9 @@ struct proto { * It can construct a new rte, add private attributes and * decide whether the route shall be imported: 1=yes, -1=no, * 0=process it through the import filter set by the user. + * reload_routes Request protocol to reload all its routes to the core + * (using rte_update()). Returns: 0=reload cannot be done, + * 1= reload is scheduled and will happen (asynchronously). */ void (*if_notify)(struct proto *, unsigned flags, struct iface *i); @@ -161,6 +164,7 @@ struct proto { struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool); void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs); int (*import_control)(struct proto *, struct rte **rt, struct ea_list **attrs, struct linpool *pool); + int (*reload_routes)(struct proto *); /* * Routing entry hooks (called only for rte's belonging to this protocol): @@ -190,6 +194,7 @@ struct proto { void *proto_new(struct proto_config *, unsigned size); void *proto_config_new(struct protocol *, unsigned size); +void proto_request_feeding(struct proto *p); void proto_show(struct symbol *, int); struct proto *proto_get_named(struct symbol *, struct protocol *); void proto_xxable(char *, int); @@ -271,6 +276,10 @@ void proto_notify_state(struct proto *p, unsigned state); * HUNGRY/DOWN --> HUNGRY/START --> HUNGRY/UP --> * FEEDING/UP --> HAPPY/UP --> FLUSHING/STOP|DOWN --> * HUNGRY/STOP|DOWN --> HUNGRY/DOWN + * + * Sometimes, protocol might switch from HAPPY/UP to FEEDING/UP + * if it wants to refeed the routes (for example BGP does so + * as a result of received ROUTE-REFRESH request). */ #define FS_HUNGRY 0 diff --git a/nest/rt-table.c b/nest/rt-table.c index dda0e56b..e0cd7971 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -199,6 +199,14 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, else p->stats.exp_withdraws_received++; + /* This is a tricky part - we don't know whether route 'old' was + exported to protocol 'p' or was filtered by the export filter. + We try tu run the export filter to know this to have a correct + value in 'old' argument of rt_update (and proper filter value) + + FIXME - this is broken because 'configure soft' may change + filters but keep routes */ + if (old) { if (p->out_filter == FILTER_REJECT) @@ -216,6 +224,7 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, } } + /* FIXME - This is broken because of incorrect 'old' value (see above) */ if (!new && !old) return; @@ -1122,6 +1131,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) ok = 0; else if (!ic && d->export_mode > 1) { + /* FIXME - this shows what should be exported according + to current filters, but not what was really exported. + 'configure soft' command may change the export filter + and do not update routes */ + if (p1->out_filter == FILTER_REJECT || p1->out_filter && f_run(p1->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT) ok = 0; diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index cde02768..24cd2023 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -676,6 +676,17 @@ bgp_neigh_notify(neighbor *n) } } +static int +bgp_reload_routes(struct proto *P) +{ + struct bgp_proto *p = (struct bgp_proto *) P; + if (!p->conn || !p->conn->peer_refresh_support) + return 0; + + bgp_schedule_packet(p->conn, PKT_ROUTE_REFRESH); + return 1; +} + static void bgp_start_locked(struct object_lock *lock) { @@ -792,6 +803,7 @@ bgp_init(struct proto_config *C) P->rte_better = bgp_rte_better; P->import_control = bgp_import_control; P->neigh_notify = bgp_neigh_notify; + P->reload_routes = bgp_reload_routes; p->cf = c; p->local_as = c->local_as; p->remote_as = c->remote_as; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index c4d03352..59ec9c16 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -29,6 +29,7 @@ struct bgp_config { u32 default_local_pref; /* Default value for LOCAL_PREF attribute */ u32 default_med; /* Default value for MULTI_EXIT_DISC attribute */ int capabilities; /* Enable capability handshake [RFC3392] */ + int enable_refresh; /* Enable local support for route refresh [RFC2918] */ int enable_as4; /* Enable local support for 4B AS numbers [RFC4893] */ u32 rr_cluster_id; /* Route reflector cluster ID, if different from local ID */ int rr_client; /* Whether neighbor is RR client of me */ @@ -66,6 +67,7 @@ struct bgp_conn { int start_state; /* protocol start_state snapshot when connection established */ int want_as4_support; /* Connection tries to establish AS4 session */ int peer_as4_support; /* Peer supports 4B AS numbers [RFC4893] */ + int peer_refresh_support; /* Peer supports route refresh [RFC2918] */ unsigned hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */ }; @@ -202,6 +204,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi #define PKT_UPDATE 0x02 #define PKT_NOTIFICATION 0x03 #define PKT_KEEPALIVE 0x04 +#define PKT_ROUTE_REFRESH 0x05 #define PKT_SCHEDULE_CLOSE 0x1f /* Used internally to schedule socket close */ /* Attributes */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 35769466..3c73d60e 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -23,7 +23,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY, SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER, MISSING, LLADDR, - DROP, IGNORE) + DROP, IGNORE, ROUTE, REFRESH) CF_GRAMMAR @@ -40,6 +40,7 @@ bgp_proto_start: proto_start BGP { BGP_CFG->error_amnesia_time = 300; BGP_CFG->error_delay_time_min = 60; BGP_CFG->error_delay_time_max = 300; + BGP_CFG->enable_refresh = 1; BGP_CFG->enable_as4 = bgp_as4_support; BGP_CFG->capabilities = 2; BGP_CFG->advertise_ipv4 = 1; @@ -77,6 +78,7 @@ bgp_proto: | bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; } | bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; } | bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; } + | bgp_proto ENABLE ROUTE REFRESH bool ';' { BGP_CFG->enable_refresh = $5; } | bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; } | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; } | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; } diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 4176c9fe..91b47927 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -63,6 +63,14 @@ bgp_put_cap_ipv4(struct bgp_conn *conn UNUSED, byte *buf) } #endif +static byte * +bgp_put_cap_rr(struct bgp_conn *conn UNUSED, byte *buf) +{ + *buf++ = 2; /* Capability 2: Support for route refresh */ + *buf++ = 0; /* Capability data length */ + return buf; +} + static byte * bgp_put_cap_as4(struct bgp_conn *conn, byte *buf) { @@ -105,6 +113,9 @@ bgp_create_open(struct bgp_conn *conn, byte *buf) cap = bgp_put_cap_ipv6(conn, cap); #endif + if (p->cf->enable_refresh) + cap = bgp_put_cap_rr(conn, cap); + if (conn->want_as4_support) cap = bgp_put_cap_as4(conn, cap); @@ -386,6 +397,24 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) #endif +static byte * +bgp_create_route_refresh(struct bgp_conn *conn, byte *buf) +{ + struct bgp_proto *p = conn->bgp; + BGP_TRACE(D_PACKETS, "Sending ROUTE-REFRESH"); + +#ifdef IPV6 + *buf++ = 0; /* AFI IPv6 */ + *buf++ = BGP_AF_IPV6; +#else + *buf++ = 0; /* AFI IPv4 */ + *buf++ = BGP_AF_IPV4; +#endif + *buf++ = 0; /* RFU */ + *buf++ = 1; /* and SAFI 1 */ + return buf; +} + static void bgp_create_header(byte *buf, unsigned int len, unsigned int type) { @@ -447,6 +476,12 @@ bgp_fire_tx(struct bgp_conn *conn) type = PKT_OPEN; end = bgp_create_open(conn, pkt); } + else if (s & (1 << PKT_ROUTE_REFRESH)) + { + s &= ~(1 << PKT_ROUTE_REFRESH); + type = PKT_ROUTE_REFRESH; + end = bgp_create_route_refresh(conn, pkt); + } else if (s & (1 << PKT_UPDATE)) { end = bgp_create_update(conn, pkt); @@ -517,6 +552,11 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len) switch (opt[0]) { + case 2: + if (cl != 0) + goto err; + conn->peer_refresh_support = 1; + break; case 65: if (cl != 4) goto err; @@ -1084,6 +1124,30 @@ bgp_rx_keepalive(struct bgp_conn *conn) } } +static void +bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, int len) +{ + struct bgp_proto *p = conn->bgp; + + BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH"); + + if (conn->state != BS_ESTABLISHED) + { bgp_error(conn, 5, 0, NULL, 0); return; } + + if (!p->cf->enable_refresh) + { bgp_error(conn, 1, 3, pkt+18, 1); return; } + + if (len != (BGP_HEADER_LENGTH + 4)) + { bgp_error(conn, 1, 2, pkt+16, 2); return; } + + /* FIXME - we ignore AFI/SAFI values, as we support + just one value and even an error code for an invalid + request is not defined */ + + proto_request_feeding(&p->p); +} + + /** * bgp_rx_packet - handle a received packet * @conn: BGP connection @@ -1103,6 +1167,7 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, unsigned len) case PKT_UPDATE: return bgp_rx_update(conn, pkt, len); case PKT_NOTIFICATION: return bgp_rx_notification(conn, pkt, len); case PKT_KEEPALIVE: return bgp_rx_keepalive(conn); + case PKT_ROUTE_REFRESH: return bgp_rx_route_refresh(conn, pkt, len); default: bgp_error(conn, 1, 3, pkt+18, 1); } } From a6250a7d1013442ad4feb0d67128a707f2c6880b Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 26 Nov 2009 23:23:29 +0100 Subject: [PATCH 15/22] Fix -p option. --- doc/bird.sgml | 3 ++- sysdep/unix/main.c | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index d08b0c17..1f494cce 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -132,7 +132,8 @@ options. The most important ones are: log debugging information to given file instead of stderr. -p - just parse the config file and exit. + just parse the config file and exit. Return value is zero if the config file is valid, + nonzero if there are some errors. -s use given filename for a socket for communications with the client, default is /var/run/bird.ctl. diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 533754ea..6107b226 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -370,7 +370,7 @@ static char *opt_list = "c:dD:ps:"; static void usage(void) { - fprintf(stderr, "Usage: bird [-c ] [-d] [-D ] [-s ]\n"); + fprintf(stderr, "Usage: bird [-c ] [-d] [-D ] [-p] [-s ]\n"); exit(1); } @@ -434,7 +434,8 @@ main(int argc, char **argv) log_init_debug(""); log_init(debug_flag, 1); - test_old_bird(path_control_socket); + if (!parse_and_exit) + test_old_bird(path_control_socket); DBG("Initializing.\n"); resource_init(); From 4b84bd4554b2a9331055bfd8d02a0bab0d10df92 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sun, 29 Nov 2009 10:29:33 +0100 Subject: [PATCH 16/22] Fixes some crashes in OSPFv2. --- proto/ospf/rt.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index a230d38f..2e1de440 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -776,6 +776,8 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, node *prev, *n; int added = 0; struct top_hash_entry *act; + ip_addr old_nh; + struct ospf_iface *old_nhi; if (en == NULL) return; @@ -798,13 +800,20 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par, DBG(" Adding candidate: rt: %R, id: %R, type: %u\n", en->lsa.rt, en->lsa.id, en->lsa.type); + old_nhi = en->nhi; + old_nh = en->nh; + en->nhi = NULL; en->nh = IPA_NONE; - calc_next_hop(en, par, oa); if (!en->nhi) - return; /* We cannot find next hop, ignore it */ + { + /* No next hop found, we undo changes and return */ + en->nhi = old_nhi; + en->nh = old_nh; + return; + } if (en->color == CANDIDATE) { /* We found a shorter path */ From e8b29bdc8dc34d4a0358458907a5d8ac29011d28 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 2 Dec 2009 14:33:34 +0100 Subject: [PATCH 17/22] Fixes one missing log message. --- nest/rt-table.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index e0cd7971..186a2dc6 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -504,11 +504,10 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte net->routes->next = new; rte_trace_in(D_ROUTES, p, new, "added"); } - else if (old && (p->debug & D_ROUTES)) - { - /* Not really a case - the list of routes is correct, we just - log the route removal */ + /* Log the route removal */ + if (!new && old && (p->debug & D_ROUTES)) + { if (old != old_best) rte_trace_in(D_ROUTES, p, old, "removed"); else if (net->routes) From 11787b8473ae1685d43dad809592fabc64eb8f46 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 2 Dec 2009 17:26:16 +0100 Subject: [PATCH 18/22] Fixes some problems in pipes. For transparent pipes, loop detection works correctly now. Pipes are now more symmetric - in both directions filtering is done in do_rte_announce(). --- nest/rt-table.c | 29 +++++++++++++++++++++++++---- proto/pipe/pipe.c | 25 +++++++++++++++++++------ 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index 186a2dc6..a8d0fee5 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -403,6 +403,26 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte { if (old->attrs->proto == src) { + /* If there is the same route in the routing table but from + * a different sender, then there are two paths from the + * source protocol to this routing table through transparent + * pipes, which is not allowed. + * + * We log that and ignore the route. If it is withdraw, we + * ignore it completely (there might be 'spurious withdraws', + * see FIXME in do_rte_announce()) + */ + if (old->sender != p) + { + if (new) + { + log(L_ERR "Pipe collision detected when sending %I/%d to table %s", + net->n.prefix, net->n.pxlen, table->name); + rte_free_quick(new); + } + return; + } + if (new && rte_same(old, new)) { /* No changes, ignore the new route */ @@ -597,11 +617,12 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new new->sender = p; struct filter *filter = p->in_filter; - /* Do not filter routes going to the secondary side of the pipe, - that should only go through export filter. - FIXME Make a better check whether p is really a pipe. */ - if (p->table != table) + /* Do not filter routes going through the pipe, + they are filtered in the export filter only. */ +#ifdef CONFIG_PIPE + if (p->proto == &proto_pipe) filter = FILTER_ACCEPT; +#endif p->stats.imp_updates_received++; if (!rte_validate(new)) diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index e57c9efd..c117f3b4 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -31,7 +31,7 @@ #include "pipe.h" static void -pipe_send(struct pipe_proto *p, rtable *dest, net *n, rte *new, rte *old, ea_list *attrs) +pipe_send(struct pipe_proto *p, rtable *src_table, rtable *dest, net *n, rte *new, rte *old, ea_list *attrs) { struct proto *src; net *nn; @@ -80,9 +80,9 @@ pipe_send(struct pipe_proto *p, rtable *dest, net *n, rte *new, rte *old, ea_lis src = old->attrs->proto; } - dest->pipe_busy = 1; + src_table->pipe_busy = 1; rte_update(dest, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e); - dest->pipe_busy = 0; + src_table->pipe_busy = 0; } static void @@ -91,7 +91,7 @@ pipe_rt_notify_pri(struct proto *P, net *net, rte *new, rte *old, ea_list *attrs struct pipe_proto *p = (struct pipe_proto *) P; DBG("PIPE %c> %I/%d\n", (new ? '+' : '-'), net->n.prefix, net->n.pxlen); - pipe_send(p, p->peer, net, new, old, attrs); + pipe_send(p, p->p.table, p->peer, net, new, old, attrs); } static void @@ -100,7 +100,7 @@ pipe_rt_notify_sec(struct proto *P, net *net, rte *new, rte *old, ea_list *attrs struct pipe_proto *p = ((struct pipe_proto *) P)->phantom; DBG("PIPE %c< %I/%d\n", (new ? '+' : '-'), net->n.prefix, net->n.pxlen); - pipe_send(p, p->p.table, net, new, old, attrs); + pipe_send(p, p->peer, p->p.table, net, new, old, attrs); } static int @@ -134,7 +134,20 @@ pipe_start(struct proto *P) ph->p.rt_notify = pipe_rt_notify_sec; ph->p.proto_state = PS_UP; ph->p.core_state = ph->p.core_goal = FS_HAPPY; - ph->p.in_filter = ph->p.out_filter = FILTER_ACCEPT; /* We do all filtering on the local end */ + + /* + * Routes should be filtered in the do_rte_announce() (export + * filter for protocols). Reverse direction is handled by putting + * specified import filter to out_filter field of the phantom + * protocol. + * + * in_filter fields are not important, there is an exception in + * rte_update() to ignore it for pipes. We cannot just set + * P->in_filter to FILTER_ACCEPT, because that would break other + * things (reconfiguration, show-protocols command). + */ + ph->p.in_filter = FILTER_ACCEPT; + ph->p.out_filter = P->in_filter; /* * Connect the phantom protocol to the peer routing table, but From 11361a101517c2c87e3d35d2c63cacb3ddb97724 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 2 Dec 2009 22:19:47 +0100 Subject: [PATCH 19/22] Implements route re-feed. This can be used to re-feed routes to protocol after soft change in export filters. --- nest/proto.c | 1 + nest/protocol.h | 1 + nest/rt-table.c | 34 +++++++++++++++++++++++----------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/nest/proto.c b/nest/proto.c index c531d869..bdac4bf6 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -585,6 +585,7 @@ proto_schedule_feed(struct proto *p, int initial) { DBG("%s: Scheduling meal\n", p->name); p->core_state = FS_FEEDING; + p->refeeding = !initial; proto_relink(p); p->attn->hook = initial ? proto_feed_initial : proto_feed_more; ev_schedule(p->attn); diff --git a/nest/protocol.h b/nest/protocol.h index 66e6c43d..e1adc525 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -134,6 +134,7 @@ struct proto { unsigned core_state; /* Core state machine (see below) */ unsigned core_goal; /* State we want to reach (see below) */ unsigned reconfiguring; /* We're shutting down due to reconfiguration */ + unsigned refeeding; /* We are refeeding (valid only if core_state == FS_FEEDING) */ u32 hash_key; /* Random key used for hashing of neighbors */ bird_clock_t last_state_change; /* Time of last state transition */ char *last_state_name_announced; /* Last state name we've announced to the user */ diff --git a/nest/rt-table.c b/nest/rt-table.c index a8d0fee5..8efc0a61 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -158,7 +158,7 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg) } static inline void -do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, ea_list *tmpa, int class) +do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, ea_list *tmpa, int class, int refeed) { struct proto *p = a->proto; rte *new0 = new; @@ -199,15 +199,27 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, else p->stats.exp_withdraws_received++; - /* This is a tricky part - we don't know whether route 'old' was - exported to protocol 'p' or was filtered by the export filter. - We try tu run the export filter to know this to have a correct - value in 'old' argument of rt_update (and proper filter value) + /* + * This is a tricky part - we don't know whether route 'old' was + * exported to protocol 'p' or was filtered by the export filter. + * We try tu run the export filter to know this to have a correct + * value in 'old' argument of rt_update (and proper filter value) + * + * FIXME - this is broken because 'configure soft' may change + * filters but keep routes. Refeed is expected to be called after + * change of the filters and with old == new, therefore we do not + * even try to run the filter on an old route, This may lead to + * 'spurious withdraws' but ensure that there are no 'missing + * withdraws'. + * + * This is not completely safe as there is a window between + * reconfiguration and the end of refeed - if a newly filtered + * route disappears during this period, proper withdraw is not + * sent (because old would be also filtered) and the route is + * not refeeded (because it disappeared before that). + */ - FIXME - this is broken because 'configure soft' may change - filters but keep routes */ - - if (old) + if (old && !refeed) { if (p->out_filter == FILTER_REJECT) old = NULL; @@ -313,7 +325,7 @@ rte_announce(rtable *tab, int type, net *net, rte *new, rte *old, ea_list *tmpa) { ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING); if (a->proto->accept_ra_types == type) - do_rte_announce(a, type, net, new, old, tmpa, class); + do_rte_announce(a, type, net, new, old, tmpa, class, 0); } } @@ -973,7 +985,7 @@ do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) rte_update_lock(); tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL; - do_rte_announce(h, type, n, e, NULL, tmpa, ipa_classify(n->n.prefix)); + do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, ipa_classify(n->n.prefix), p->refeeding); rte_update_unlock(); } From a421ec33cb9029899122d0ab63bab0fa268348d2 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Wed, 2 Dec 2009 22:22:40 +0100 Subject: [PATCH 20/22] Fixes silly bug. --- nest/proto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nest/proto.c b/nest/proto.c index bdac4bf6..b23011d7 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -649,7 +649,7 @@ proto_notify_state(struct proto *p, unsigned ps) switch (ps) { case PS_DOWN: - if ((cs = FS_FEEDING) || (cs == FS_HAPPY)) + if ((cs == FS_FEEDING) || (cs == FS_HAPPY)) proto_schedule_flush(p); neigh_prune(); // FIXME convert neighbors to resource? @@ -672,7 +672,7 @@ proto_notify_state(struct proto *p, unsigned ps) proto_schedule_feed(p, 1); break; case PS_STOP: - if ((cs = FS_FEEDING) || (cs == FS_HAPPY)) + if ((cs == FS_FEEDING) || (cs == FS_HAPPY)) proto_schedule_flush(p); break; default: From 28008482a97c0ac70e648759fe37bad0633ed9f7 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 12 Dec 2009 01:35:51 +0100 Subject: [PATCH 21/22] Minor fixes in OSPF. --- proto/ospf/lsreq.c | 2 +- proto/ospf/lsupd.c | 37 ++++++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/proto/ospf/lsreq.c b/proto/ospf/lsreq.c index 5eeb06f0..e7d7af24 100644 --- a/proto/ospf/lsreq.c +++ b/proto/ospf/lsreq.c @@ -18,7 +18,7 @@ static void ospf_dump_lsreq(struct proto *p, struct ospf_lsreq_packet *pkt) struct ospf_lsreq_header *plsr = (void *) (pkt + 1); int i, j; - j = (ntohs(op->length) - sizeof(struct ospf_dbdes_packet)) / + j = (ntohs(op->length) - sizeof(struct ospf_lsreq_packet)) / sizeof(struct ospf_lsreq_header); for (i = 0; i < j; i++) diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index ba09fec0..f8195dcd 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -456,27 +456,34 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, if (self) { - struct top_hash_entry *en; - if ((lsatmp.age == LSA_MAXAGE) && (lsatmp.sn == LSA_MAXSEQNO)) { ospf_lsack_enqueue(n, lsa, ACKL_DIRECT); continue; } - lsatmp.age = LSA_MAXAGE; - lsatmp.sn = LSA_MAXSEQNO; - lsa->age = htons(LSA_MAXAGE); - lsa->sn = htonl(LSA_MAXSEQNO); - OSPF_TRACE(D_EVENTS, "Premature aging self originated lsa."); - OSPF_TRACE(D_EVENTS, "Type: %d, Id: %R, Rt: %R", - lsatmp.type, lsatmp.id, lsatmp.rt); - lsasum_check(lsa, (lsa + 1)); /* It also calculates chsum! */ - lsatmp.checksum = ntohs(lsa->checksum); - ospf_lsupd_flood(NULL, lsa, &lsatmp, NULL, oa, 0); - if (en = ospf_hash_find_header(po->gr, oa->areaid, &lsatmp)) + OSPF_TRACE(D_EVENTS, "Received old self-originated LSA (Type: %04x, Id: %R, Rt: %R)", lsatmp.type, lsatmp.id, lsatmp.rt); + + if (lsadb) { - ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, oa, 1); + OSPF_TRACE(D_EVENTS, "Reflooding new self-originated LSA with newer SN"); + lsadb->lsa.sn = lsatmp.sn + 1; + lsadb->lsa.age = 0; + lsadb->inst_t = now; + lsadb->ini_age = 0; + lsasum_calculate(&lsadb->lsa, lsadb->lsa_body); + ospf_lsupd_flood(NULL, NULL, &lsadb->lsa, NULL, oa, 1); + } + else + { + OSPF_TRACE(D_EVENTS, "Premature aging it"); + lsatmp.age = LSA_MAXAGE; + lsatmp.sn = LSA_MAXSEQNO; + lsa->age = htons(LSA_MAXAGE); + lsa->sn = htonl(LSA_MAXSEQNO); + lsasum_check(lsa, (lsa + 1)); /* It also calculates chsum! */ + lsatmp.checksum = ntohs(lsa->checksum); + ospf_lsupd_flood(NULL, lsa, &lsatmp, NULL, oa, 0); } continue; } @@ -484,7 +491,7 @@ ospf_lsupd_receive(struct ospf_lsupd_packet *ps, /* pg 144 (5a) */ if (lsadb && ((now - lsadb->inst_t) <= MINLSARRIVAL)) /* FIXME: test for flooding? */ { - DBG("I got it in less that MINLSARRIVAL\n"); + OSPF_TRACE(D_EVENTS, "Skipping LSA received in less that MINLSARRIVAL"); sendreq = 0; continue; } From 8a7fb8858fa87bce6f2f15ee2bbb77704b5fff4e Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 14 Dec 2009 01:32:37 +0100 Subject: [PATCH 22/22] Finishes 'route reload' feature. --- doc/bird.sgml | 18 ++++++++++++++++++ doc/reply_codes | 3 +++ nest/config.Y | 18 +++++++++--------- nest/proto.c | 48 ++++++++++++++++++++++++++++++------------------ nest/protocol.h | 7 +++++++ nest/rt-table.c | 12 +++++++++++- 6 files changed, 78 insertions(+), 28 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 1f494cce..3905a29e 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -497,6 +497,24 @@ This argument can be omitted if there exists only a single instance. enable|disable|restart Enable, disable or restart a given protocol instance, instances matching the or reload [in|out] + + Reload a given protocol instance, that means re-import routes + from the protocol instance and re-export preferred routes to + the instance. If cannot dump +8006 Reload failed 9000 Command too long 9001 Parse error diff --git a/nest/config.Y b/nest/config.Y index dde20c5a..dbb10ada 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -45,7 +45,7 @@ CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILT CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE) CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT, PASSWORDS, DESCRIPTION) -CF_KEYWORDS(RELOAD, REFEED) +CF_KEYWORDS(RELOAD, IN, OUT) CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE) @@ -438,17 +438,17 @@ echo_size: ; CF_CLI(DISABLE, proto_patt, | \"\" | all, [[Disable protocol]]) -{ proto_xxable($2, 0); } ; +{ proto_xxable($2, XX_DISABLE); } ; CF_CLI(ENABLE, proto_patt, | \"\" | all, [[Enable protocol]]) -{ proto_xxable($2, 1); } ; +{ proto_xxable($2, XX_ENABLE); } ; CF_CLI(RESTART, proto_patt, | \"\" | all, [[Restart protocol]]) -{ proto_xxable($2, 2); } ; +{ proto_xxable($2, XX_RESTART); } ; CF_CLI(RELOAD, proto_patt, | \"\" | all, [[Reload protocol]]) -{ proto_xxable($2, 3); } ; -CF_CLI(REFEED, proto_patt, | \"\" | all, [[Refeed protocol BROKEN]]) -{ proto_xxable($2, 4); } ; - - +{ proto_xxable($2, XX_RELOAD); } ; +CF_CLI(RELOAD IN, proto_patt, | \"\" | all, [[Reload protocol (just imported routes)]]) +{ proto_xxable($3, XX_RELOAD_IN); } ; +CF_CLI(RELOAD OUT, proto_patt, | \"\" | all, [[Reload protocol (just exported routes)]]) +{ proto_xxable($3, XX_RELOAD_OUT); } ; CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging]]) CF_CLI(DEBUG, proto_patt debug_mask, ( | | all) (all | off | { states | routes | filters | events | packets }), [[Control protocol debugging]]) diff --git a/nest/proto.c b/nest/proto.c index b23011d7..4f352a6f 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -586,6 +586,11 @@ proto_schedule_feed(struct proto *p, int initial) DBG("%s: Scheduling meal\n", p->name); p->core_state = FS_FEEDING; p->refeeding = !initial; + + /* Hack: reset exp_routes during refeed, and do not decrease it later */ + if (!initial) + p->stats.exp_routes = 0; + proto_relink(p); p->attn->hook = initial ? proto_feed_initial : proto_feed_more; ev_schedule(p->attn); @@ -825,7 +830,7 @@ proto_xxable(char *pattern, int xx) cnt++; switch (xx) { - case 0: + case XX_DISABLE: if (p->disabled) cli_msg(-8, "%s: already disabled", p->name); else @@ -835,7 +840,8 @@ proto_xxable(char *pattern, int xx) proto_rethink_goal(p); } break; - case 1: + + case XX_ENABLE: if (!p->disabled) cli_msg(-10, "%s: already enabled", p->name); else @@ -845,7 +851,8 @@ proto_xxable(char *pattern, int xx) proto_rethink_goal(p); } break; - case 2: + + case XX_RESTART: if (p->disabled) cli_msg(-8, "%s: already disabled", p->name); else @@ -857,24 +864,29 @@ proto_xxable(char *pattern, int xx) cli_msg(-12, "%s: restarted", p->name); } break; - case 3: - // FIXME change msg number + + case XX_RELOAD: + case XX_RELOAD_IN: + case XX_RELOAD_OUT: if (p->disabled) - cli_msg(-8, "%s: already disabled", p->name); - else if (p->reload_routes && p->reload_routes(p)) - cli_msg(-12, "%s: reloading", p->name); - else - cli_msg(-12, "%s: reload failed", p->name); - break; - case 4: - // FIXME change msg number - if (p->disabled) - cli_msg(-8, "%s: already disabled", p->name); - else { - proto_request_feeding(p); - cli_msg(-12, "%s: reexport failed", p->name); + cli_msg(-8, "%s: already disabled", p->name); + break; } + + /* re-importing routes */ + if (xx != XX_RELOAD_OUT) + if (! (p->reload_routes && p->reload_routes(p))) + { + cli_msg(-8006, "%s: reload failed", p->name); + break; + } + + /* re-exporting routes */ + if (xx != XX_RELOAD_IN) + proto_request_feeding(p); + + cli_msg(-15, "%s: reloading", p->name); break; default: diff --git a/nest/protocol.h b/nest/protocol.h index e1adc525..21a1c1b4 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -201,6 +201,13 @@ struct proto *proto_get_named(struct symbol *, struct protocol *); void proto_xxable(char *, int); void proto_debug(char *, unsigned int); +#define XX_DISABLE 0 +#define XX_ENABLE 1 +#define XX_RESTART 2 +#define XX_RELOAD 3 +#define XX_RELOAD_IN 4 +#define XX_RELOAD_OUT 5 + static inline u32 proto_get_router_id(struct proto_config *pc) { diff --git a/nest/rt-table.c b/nest/rt-table.c index 8efc0a61..df2834a6 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -165,6 +165,8 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, rte *old0 = old; int ok; + int fast_exit_hack = 0; + if (new) { p->stats.exp_updates_received++; @@ -174,6 +176,7 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, { p->stats.exp_updates_rejected++; drop_reason = "out of scope"; + fast_exit_hack = 1; } else if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0) { @@ -199,6 +202,11 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, else p->stats.exp_withdraws_received++; + /* Hack: This is here to prevent 'spurious withdraws' + for loopback addresses during reload. */ + if (fast_exit_hack) + return; + /* * This is a tricky part - we don't know whether route 'old' was * exported to protocol 'p' or was filtered by the export filter. @@ -245,9 +253,11 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, else p->stats.exp_withdraws_accepted++; + /* Hack: We do not decrease exp_routes during refeed, we instead + reset exp_routes at the start of refeed. */ if (new) p->stats.exp_routes++; - if (old) + if (old && !refeed) p->stats.exp_routes--; if (p->debug & D_ROUTES)