From: Ondrej Zajicek (work) Date: Thu, 7 Dec 2017 20:54:47 +0000 (+0100) Subject: Merge commit '1e8721e2aeccfbc3f533e8b8abc07582cee77e9a' into int-new X-Git-Tag: v2.0.0~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=830ba75e6dd369c3e64d122f0537cc85211e56e6;p=thirdparty%2Fbird.git Merge commit '1e8721e2aeccfbc3f533e8b8abc07582cee77e9a' into int-new --- 830ba75e6dd369c3e64d122f0537cc85211e56e6 diff --cc filter/filter.h index 6c81b9bcd,72b374617..c277b67cb --- a/filter/filter.h +++ b/filter/filter.h @@@ -146,8 -168,7 +146,9 @@@ void val_format(struct f_val v, buffer #define T_ENUM_RTC 0x33 #define T_ENUM_RTD 0x34 #define T_ENUM_ROA 0x35 -#define T_ENUM_RA_PREFERENCE 0x36 +#define T_ENUM_NETTYPE 0x36 ++#define T_ENUM_RA_PREFERENCE 0x37 + /* new enums go here */ #define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */ diff --cc nest/config.Y index ad45a39dc,f51be6ea0..555c9e055 --- a/nest/config.Y +++ b/nest/config.Y @@@ -756,12 -696,12 +756,12 @@@ echo_size } ; - CF_CLI(DISABLE, proto_patt, | \"\" | all, [[Disable protocol]]) - { proto_apply_cmd($2, proto_cmd_disable, 1, 0); } ; - CF_CLI(ENABLE, proto_patt, | \"\" | all, [[Enable protocol]]) - { proto_apply_cmd($2, proto_cmd_enable, 1, 0); } ; - CF_CLI(RESTART, proto_patt, | \"\" | all, [[Restart protocol]]) - { proto_apply_cmd($2, proto_cmd_restart, 1, 0); } ; -CF_CLI(DISABLE, proto_patt text_or_none, ( | \"\" | all) [message], [[Disable protocol]]) ++CF_CLI(DISABLE, proto_patt opttext, ( | \"\" | all) [message], [[Disable protocol]]) + { proto_apply_cmd($2, proto_cmd_disable, 1, (uintptr_t) $3); } ; -CF_CLI(ENABLE, proto_patt text_or_none, ( | \"\" | all) [message], [[Enable protocol]]) ++CF_CLI(ENABLE, proto_patt opttext, ( | \"\" | all) [message], [[Enable protocol]]) + { proto_apply_cmd($2, proto_cmd_enable, 1, (uintptr_t) $3); } ; -CF_CLI(RESTART, proto_patt text_or_none, ( | \"\" | all) [message], [[Restart protocol]]) ++CF_CLI(RESTART, proto_patt opttext, ( | \"\" | all) [message], [[Restart protocol]]) + { proto_apply_cmd($2, proto_cmd_restart, 1, (uintptr_t) $3); } ; CF_CLI(RELOAD, proto_patt, | \"\" | all, [[Reload protocol]]) { proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ; CF_CLI(RELOAD IN, proto_patt, | \"\" | all, [[Reload protocol (just imported routes)]]) diff --cc nest/proto.c index ecc3b0fe3,552d53ae1..a2a2bc7e5 --- a/nest/proto.c +++ b/nest/proto.c @@@ -970,21 -602,20 +970,22 @@@ proto_rethink_goal(struct proto *p struct protocol *q; byte goal; - if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) - { - struct proto_config *nc = p->cf_new; - DBG("%s has shut down for reconfiguration\n", p->name); - p->cf->proto = NULL; - config_del_obstacle(p->cf->global); - rem_node(&p->n); - rem_node(&p->glob_node); - mb_free(p->message); - mb_free(p); - if (!nc) - return; - p = proto_init(nc); - } + if (p->reconfiguring && !p->active) + { + struct proto_config *nc = p->cf_new; + node *n = p->n.prev; + DBG("%s has shut down for reconfiguration\n", p->name); + p->cf->proto = NULL; + config_del_obstacle(p->cf->global); + proto_remove_channels(p); + rem_node(&p->n); + rfree(p->event); ++ mb_free(p->message); + mb_free(p); + if (!nc) + return; + p = proto_init(nc, n); + } /* Determine what state we want to reach */ if (p->disabled || p->reconfiguring) @@@ -1331,12 -1094,87 +1332,45 @@@ proto_schedule_down(struct proto *p, by p->down_sched = restart ? PDS_RESTART : PDS_DISABLE; p->down_code = code; - tm_start_max(proto_shutdown_timer, restart ? 2 : 0); + tm_start_max(proto_shutdown_timer, restart ? 250 MS : 0); } + /** + * proto_set_message - set administrative message to protocol + * @p: protocol + * @msg: message + * @len: message length (-1 for NULL-terminated string) + * + * The function sets administrative message (string) related to protocol state + * change. It is called by the nest code for manual enable/disable/restart + * commands all routes to the protocol, and by protocol-specific code when the + * protocol state change is initiated by the protocol. Using NULL message clears + * the last message. The message string may be either NULL-terminated or with an + * explicit length. + */ + void + proto_set_message(struct proto *p, char *msg, int len) + { + mb_free(p->message); + p->message = NULL; + + if (!msg || !len) + return; + + if (len < 0) + len = strlen(msg); + + if (!len) + return; + + p->message = mb_alloc(proto_pool, len + 1); + memcpy(p->message, msg, len); + p->message[len] = 0; + } + -/** - * 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 export state transition - * to ES_FEEDING (during feeding) and when completed, it will - * switch back to ES_READY. 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); - - /* Do nothing if we are still waiting for feeding */ - if (p->export_state == ES_DOWN) - return; - - /* If we are already feeding, we want to restart it */ - if (p->export_state == ES_FEEDING) - { - /* Unless feeding is in initial state */ - if (p->attn->hook == proto_feed_initial) - return; - - rt_feed_baby_abort(p); - } - - /* FIXME: This should be changed for better support of multitable protos */ - struct announce_hook *ah; - for (ah = p->ahooks; ah; ah = ah->next) - proto_reset_limit(ah->out_limit); - - /* Hack: reset exp_routes during refeed, and do not decrease it later */ - p->stats.exp_routes = 0; - - proto_schedule_feed(p, 0); - proto_log_state_change(p); -} - static const char * -proto_limit_name(struct proto_limit *l) +channel_limit_name(struct channel_limit *l) { const char *actions[] = { [PLA_WARN] = "warn", @@@ -1641,37 -1550,34 +1675,39 @@@ proto_cmd_show(struct proto *p, uintptr proto_state_name(p), tbuf, buf); + if (verbose) + { + if (p->cf->dsc) + cli_msg(-1006, " Description: %s", p->cf->dsc); ++ if (p->message) ++ cli_msg(-1006, " Message: %s", p->message); + if (p->cf->router_id) + cli_msg(-1006, " Router ID: %R", p->cf->router_id); + if (p->vrf) + cli_msg(-1006, " VRF: %s", p->vrf->name); + + if (p->proto->show_proto_info) + p->proto->show_proto_info(p); + else { - if (p->cf->dsc) - cli_msg(-1006, " Description: %s", p->cf->dsc); - - if (p->message) - cli_msg(-1006, " Message: %s", p->message); - - if (p->cf->router_id) - cli_msg(-1006, " Router ID: %R", p->cf->router_id); - - if (p->proto->show_proto_info) - p->proto->show_proto_info(p); - else - proto_show_basic_info(p); - - cli_msg(-1006, ""); + struct channel *c; + WALK_LIST(c, p->channels) + channel_show_info(c); } + + cli_msg(-1006, ""); + } } void - proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED) + proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED) { if (p->disabled) - { - cli_msg(-8, "%s: already disabled", p->name); - return; - } + { + cli_msg(-8, "%s: already disabled", p->name); + return; + } log(L_INFO "Disabling protocol %s", p->name); p->disabled = 1; @@@ -1681,13 -1588,13 +1718,13 @@@ } void - proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED) + proto_cmd_enable(struct proto *p, uintptr_t arg, int cnt UNUSED) { if (!p->disabled) - { - cli_msg(-10, "%s: already enabled", p->name); - return; - } + { + cli_msg(-10, "%s: already enabled", p->name); + return; + } log(L_INFO "Enabling protocol %s", p->name); p->disabled = 0; @@@ -1696,13 -1604,13 +1734,13 @@@ } void - proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED) + proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED) { if (p->disabled) - { - cli_msg(-8, "%s: already disabled", p->name); - return; - } + { + cli_msg(-8, "%s: already disabled", p->name); + return; + } log(L_INFO "Restarting protocol %s", p->name); p->disabled = 1; @@@ -1714,15 -1623,13 +1753,15 @@@ } void - proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED) + proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED) { + struct channel *c; + if (p->disabled) - { - cli_msg(-8, "%s: already disabled", p->name); - return; - } + { + cli_msg(-8, "%s: already disabled", p->name); + return; + } /* If the protocol in not UP, it has no routes */ if (p->proto_state != PS_UP) @@@ -1765,27 -1677,31 +1804,27 @@@ proto_cmd_mrtdump(struct proto *p, uint } static void - proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int), uint arg) + proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg) { if (s->class != SYM_PROTO) - { - cli_msg(9002, "%s is not a protocol", s->name); - return; - } + { + cli_msg(9002, "%s is not a protocol", s->name); + return; + } cmd(((struct proto_config *)s->def)->proto, arg, 0); cli_msg(0, ""); } static void - proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint arg) + proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg) { + struct proto *p; int cnt = 0; - node *nn; - WALK_LIST(nn, proto_list) - { - struct proto *p = SKIP_BACK(struct proto, glob_node, nn); - - if (!patt || patmatch(patt, p->name)) - cmd(p, arg, cnt++); - } + WALK_LIST(p, proto_list) + if (!patt || patmatch(patt, p->name)) + cmd(p, arg, cnt++); if (!cnt) cli_msg(8003, "No protocols match"); diff --cc nest/protocol.h index d7e84a441,5aca9a4ea..c8f373678 --- a/nest/protocol.h +++ b/nest/protocol.h @@@ -148,20 -146,26 +148,21 @@@ struct proto char *name; /* Name of this instance (== cf->name) */ u32 debug; /* Debugging flags */ u32 mrtdump; /* MRTDump flags */ - unsigned preference; /* Default route preference */ - byte accept_ra_types; /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */ + uint active_channels; /* Number of active channels */ + byte net_type; /* Protocol network type (NET_*), 0 for undefined */ byte disabled; /* Manually disabled */ byte proto_state; /* Protocol state machine (PS_*, see below) */ - byte core_state; /* Core state machine (FS_*, see below) */ - byte export_state; /* Route export state (ES_*, see below) */ + byte active; /* From PS_START to cleanup after PS_STOP */ + byte do_start; /* Start actions are scheduled */ + byte do_stop; /* Stop actions are scheduled */ byte reconfiguring; /* We're shutting down due to reconfiguration */ - byte refeeding; /* We are refeeding (valid only if export_state == ES_FEEDING) */ - byte flushing; /* Protocol is flushed in current flush loop round */ byte gr_recovery; /* Protocol should participate in graceful restart recovery */ - byte gr_lock; /* Graceful restart mechanism should wait for this proto */ - byte gr_wait; /* Route export to protocol is postponed until graceful restart */ byte down_sched; /* Shutdown is scheduled for later (PDS_*) */ byte down_code; /* Reason for shutdown (PDC_* codes) */ - byte merge_limit; /* Maximal number of nexthops for RA_MERGED */ u32 hash_key; /* Random key used for hashing of neighbors */ - bird_clock_t last_state_change; /* Time of last state transition */ + btime last_state_change; /* Time of last state transition */ char *last_state_name_announced; /* Last state name we've announced to the user */ + char *message; /* State-change message, allocated from proto_pool */ - struct proto_stats stats; /* Current protocol statistics */ /* * General protocol hooks: @@@ -235,9 -248,15 +236,10 @@@ struct proto_spec #define PDC_OUT_LIMIT_HIT 0x23 /* Route export limit reached */ -void *proto_new(struct proto_config *, unsigned size); +void *proto_new(struct proto_config *); void *proto_config_new(struct protocol *, int class); void proto_copy_config(struct proto_config *dest, struct proto_config *src); + void proto_set_message(struct proto *p, char *msg, int len); -void proto_request_feeding(struct proto *p); - -static inline void -proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned size) -{ memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); } void graceful_restart_recovery(void); void graceful_restart_init(void); @@@ -247,18 -266,18 +249,18 @@@ void channel_graceful_restart_unlock(st #define DEFAULT_GR_WAIT 240 -void proto_show_limit(struct proto_limit *l, const char *dsc); -void proto_show_basic_info(struct proto *p); +void channel_show_limit(struct channel_limit *l, const char *dsc); +void channel_show_info(struct channel *c); - void proto_cmd_show(struct proto *, uint, int); - void proto_cmd_disable(struct proto *, uint, int); - void proto_cmd_enable(struct proto *, uint, int); - void proto_cmd_restart(struct proto *, uint, int); - void proto_cmd_reload(struct proto *, uint, int); - void proto_cmd_debug(struct proto *, uint, int); - void proto_cmd_mrtdump(struct proto *, uint, int); + void proto_cmd_show(struct proto *, uintptr_t, int); + void proto_cmd_disable(struct proto *, uintptr_t, int); + void proto_cmd_enable(struct proto *, uintptr_t, int); + void proto_cmd_restart(struct proto *, uintptr_t, int); + void proto_cmd_reload(struct proto *, uintptr_t, int); + void proto_cmd_debug(struct proto *, uintptr_t, int); + void proto_cmd_mrtdump(struct proto *, uintptr_t, int); - void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int), int restricted, uint arg); + void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), int restricted, uintptr_t arg); struct proto *proto_get_named(struct symbol *, struct protocol *); #define CMD_RELOAD 0 diff --cc proto/babel/packets.c index 5b356fae7,768858d03..dd86222a4 --- a/proto/babel/packets.c +++ b/proto/babel/packets.c @@@ -588,36 -491,11 +597,36 @@@ babel_read_update(struct babel_tlv *hdr break; case BABEL_AE_IP4: - /* TODO */ - return PARSE_IGNORE; + if (tlv->plen > IP4_MAX_PREFIX_LENGTH) + return PARSE_ERROR; + + /* Cannot omit data if there is no saved prefix */ + if (tlv->omitted && !state->def_ip4_prefix_seen) + return PARSE_ERROR; + + /* Update must have next hop, unless it is retraction */ + if (ipa_zero(state->next_hop_ip4) && (msg->metric != BABEL_INFINITY)) + return PARSE_ERROR; + + /* Merge saved prefix and received prefix parts */ + memcpy(buf, state->def_ip4_prefix, tlv->omitted); + memcpy(buf + tlv->omitted, tlv->addr, len); + + ip4_addr prefix4 = get_ip4(buf); + net_fill_ip4(&msg->net, prefix4, tlv->plen); + - if (tlv->flags & BABEL_FLAG_DEF_PREFIX) ++ if (tlv->flags & BABEL_UF_DEF_PREFIX) + { + put_ip4(state->def_ip4_prefix, prefix4); + state->def_ip4_prefix_seen = 1; + } + + msg->next_hop = state->next_hop_ip4; + + break; case BABEL_AE_IP6: - if (tlv->plen > MAX_PREFIX_LENGTH) + if (tlv->plen > IP6_MAX_PREFIX_LENGTH) return PARSE_ERROR; /* Cannot omit data if there is no saved prefix */ @@@ -628,23 -506,20 +637,23 @@@ memcpy(buf, state->def_ip6_prefix, tlv->omitted); memcpy(buf + tlv->omitted, tlv->addr, len); - msg->plen = tlv->plen; - msg->prefix = ipa_from_ip6(get_ip6(buf)); + ip6_addr prefix6 = get_ip6(buf); + net_fill_ip6(&msg->net, prefix6, tlv->plen); - if (tlv->flags & BABEL_FLAG_DEF_PREFIX) + if (tlv->flags & BABEL_UF_DEF_PREFIX) { - put_ip6(state->def_ip6_prefix, msg->prefix); + put_ip6(state->def_ip6_prefix, prefix6); state->def_ip6_prefix_seen = 1; } - if (tlv->flags & BABEL_FLAG_ROUTER_ID) + if (tlv->flags & BABEL_UF_ROUTER_ID) { - state->router_id = ((u64) _I2(msg->prefix)) << 32 | _I3(msg->prefix); + state->router_id = ((u64) _I2(prefix6)) << 32 | _I3(prefix6); state->router_id_seen = 1; } + + msg->next_hop = state->next_hop_ip6; + break; case BABEL_AE_IP6_LL: @@@ -731,30 -582,8 +740,30 @@@ babel_write_update(struct babel_tlv *hd else { tlv->ae = BABEL_AE_IP6; - tlv->plen = msg->plen; - put_ip6_px(tlv->addr, msg->prefix, msg->plen); + tlv->plen = net6_pxlen(&msg->net); + + /* Address compression - omit initial matching bytes */ + u8 buf[16], omit; + put_ip6(buf, net6_prefix(&msg->net)); + omit = bytes_equal(buf, state->def_ip6_prefix, + MIN(tlv->plen, state->def_ip6_pxlen) / 8); + + if (omit > 0) + { + memcpy(tlv->addr, buf + omit, NET_SIZE(&msg->net) - omit); + + tlv->omitted = omit; + tlv->length -= omit; + len -= omit; + } + else + { + put_ip6_px(tlv->addr, &msg->net); - tlv->flags |= BABEL_FLAG_DEF_PREFIX; ++ tlv->flags |= BABEL_UF_DEF_PREFIX; + + put_ip6(state->def_ip6_prefix, net6_prefix(&msg->net)); + state->def_ip6_pxlen = tlv->plen; + } } put_time16(&tlv->interval, msg->interval); diff --cc proto/bgp/bgp.c index b0814791d,b99672f55..30fe75ba7 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@@ -403,28 -290,25 +403,28 @@@ bgp_update_startup_delay(struct bgp_pro } static void - bgp_graceful_close_conn(struct bgp_conn *conn, uint subcode) + bgp_graceful_close_conn(struct bgp_conn *conn, uint subcode, byte *data, uint len) { switch (conn->state) - { - case BS_IDLE: - case BS_CLOSE: - return; - case BS_CONNECT: - case BS_ACTIVE: - bgp_conn_enter_idle_state(conn); - return; - case BS_OPENSENT: - case BS_OPENCONFIRM: - case BS_ESTABLISHED: - bgp_error(conn, 6, subcode, data, len); - return; - default: - bug("bgp_graceful_close_conn: Unknown state %d", conn->state); - } + { + case BS_IDLE: + case BS_CLOSE: + return; + + case BS_CONNECT: + case BS_ACTIVE: + bgp_conn_enter_idle_state(conn); + return; + + case BS_OPENSENT: + case BS_OPENCONFIRM: + case BS_ESTABLISHED: - bgp_error(conn, 6, subcode, NULL, 0); ++ bgp_error(conn, 6, subcode, data, len); + return; + + default: + bug("bgp_graceful_close_conn: Unknown state %d", conn->state); + } } static void @@@ -601,12 -416,11 +601,12 @@@ bgp_conn_leave_established_state(struc BGP_TRACE(D_EVENTS, "BGP session closed"); p->conn = NULL; - bgp_free_prefix_table(p); - bgp_free_bucket_table(p); + // XXXX free these tables to avoid memory leak during graceful restart + // bgp_free_prefix_table(p); + // bgp_free_bucket_table(p); if (p->p.proto_state == PS_UP) - bgp_stop(p, 0); + bgp_stop(p, 0, NULL, 0); } void @@@ -1119,34 -967,34 +1119,34 @@@ bgp_neigh_notify(neighbor *n int prepare = (ps == PS_START) && (p->start_state == BSS_PREPARE); if (n->scope <= 0) + { + if (!prepare) { - if (!prepare) - { - BGP_TRACE(D_EVENTS, "Neighbor lost"); - bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST); - /* Perhaps also run bgp_update_startup_delay(p)? */ - bgp_stop(p, 0, NULL, 0); - } + BGP_TRACE(D_EVENTS, "Neighbor lost"); + bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST); + /* Perhaps also run bgp_update_startup_delay(p)? */ - bgp_stop(p, 0); ++ bgp_stop(p, 0, NULL, 0); } + } else if (p->cf->check_link && !(n->iface->flags & IF_LINK_UP)) + { + if (!prepare) { - if (!prepare) - { - BGP_TRACE(D_EVENTS, "Link down"); - bgp_store_error(p, NULL, BE_MISC, BEM_LINK_DOWN); - if (ps == PS_UP) - bgp_update_startup_delay(p); - bgp_stop(p, 0, NULL, 0); - } + BGP_TRACE(D_EVENTS, "Link down"); + bgp_store_error(p, NULL, BE_MISC, BEM_LINK_DOWN); + if (ps == PS_UP) + bgp_update_startup_delay(p); - bgp_stop(p, 0); ++ bgp_stop(p, 0, NULL, 0); } + } else + { + if (prepare) { - if (prepare) - { - BGP_TRACE(D_EVENTS, "Neighbor ready"); - bgp_start_neighbor(p); - } + BGP_TRACE(D_EVENTS, "Neighbor ready"); + bgp_start_neighbor(p); } + } } static void @@@ -1156,13 -1004,13 +1156,13 @@@ bgp_bfd_notify(struct bfd_request *req int ps = p->p.proto_state; if (req->down && ((ps == PS_START) || (ps == PS_UP))) - { - BGP_TRACE(D_EVENTS, "BFD session down"); - bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN); - if (ps == PS_UP) - bgp_update_startup_delay(p); - bgp_stop(p, 0, NULL, 0); - } + { + BGP_TRACE(D_EVENTS, "BFD session down"); + bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN); + if (ps == PS_UP) + bgp_update_startup_delay(p); - bgp_stop(p, 0); ++ bgp_stop(p, 0, NULL, 0); + } } static void @@@ -1352,43 -1205,45 +1356,45 @@@ bgp_shutdown(struct proto *P BGP_TRACE(D_EVENTS, "Shutdown requested"); switch (P->down_code) - { - case PDC_CF_REMOVE: - case PDC_CF_DISABLE: - subcode = 3; // Errcode 6, 3 - peer de-configured - break; - - case PDC_CF_RESTART: - subcode = 6; // Errcode 6, 6 - other configuration change - break; - - case PDC_CMD_DISABLE: - case PDC_CMD_SHUTDOWN: - subcode = 2; // Errcode 6, 2 - administrative shutdown - message = P->message; - break; - - case PDC_CMD_RESTART: - subcode = 4; // Errcode 6, 4 - administrative reset - message = P->message; - break; - - case PDC_RX_LIMIT_HIT: - case PDC_IN_LIMIT_HIT: - subcode = 1; // Errcode 6, 1 - max number of prefixes reached - /* log message for compatibility */ - log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name); - goto limit; - - case PDC_OUT_LIMIT_HIT: - subcode = proto_restart ? 4 : 2; // Administrative reset or shutdown - - limit: - bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED); - if (proto_restart) - bgp_update_startup_delay(p); - else - p->startup_delay = 0; - goto done; - } + { + case PDC_CF_REMOVE: + case PDC_CF_DISABLE: + subcode = 3; // Errcode 6, 3 - peer de-configured + break; + + case PDC_CF_RESTART: + subcode = 6; // Errcode 6, 6 - other configuration change + break; + + case PDC_CMD_DISABLE: + case PDC_CMD_SHUTDOWN: + subcode = 2; // Errcode 6, 2 - administrative shutdown ++ message = P->message; + break; + + case PDC_CMD_RESTART: + subcode = 4; // Errcode 6, 4 - administrative reset ++ message = P->message; + break; + + case PDC_RX_LIMIT_HIT: + case PDC_IN_LIMIT_HIT: + subcode = 1; // Errcode 6, 1 - max number of prefixes reached + /* log message for compatibility */ + log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name); + goto limit; + + case PDC_OUT_LIMIT_HIT: + subcode = proto_restart ? 4 : 2; // Administrative reset or shutdown + + limit: + bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED); + if (proto_restart) + bgp_update_startup_delay(p); + else + p->startup_delay = 0; + goto done; + } bgp_store_error(p, NULL, BE_MAN_DOWN, 0); p->startup_delay = 0; @@@ -1780,13 -1448,13 +1800,13 @@@ bgp_error(struct bgp_conn *c, uint code c->notify_subcode = subcode; c->notify_data = data; c->notify_size = (len > 0) ? len : 0; - bgp_schedule_packet(c, PKT_NOTIFICATION); + bgp_schedule_packet(c, NULL, PKT_NOTIFICATION); if (code != 6) - { - bgp_update_startup_delay(p); - bgp_stop(p, 0, NULL, 0); - } + { + bgp_update_startup_delay(p); - bgp_stop(p, 0); ++ bgp_stop(p, 0, NULL, 0); + } } /** diff --cc proto/bgp/bgp.h index 3d940c22a,22a150ab7..40c4b3f0f --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@@ -433,11 -208,11 +433,11 @@@ void bgp_conn_enter_established_state(s void bgp_conn_enter_close_state(struct bgp_conn *conn); void bgp_conn_enter_idle_state(struct bgp_conn *conn); void bgp_handle_graceful_restart(struct bgp_proto *p); -void bgp_graceful_restart_done(struct bgp_proto *p); -void bgp_refresh_begin(struct bgp_proto *p); -void bgp_refresh_end(struct bgp_proto *p); +void bgp_graceful_restart_done(struct bgp_channel *c); +void bgp_refresh_begin(struct bgp_channel *c); +void bgp_refresh_end(struct bgp_channel *c); void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code); - void bgp_stop(struct bgp_proto *p, unsigned subcode); + void bgp_stop(struct bgp_proto *p, uint subcode, byte *data, uint len); struct rte_source *bgp_find_source(struct bgp_proto *p, u32 path_id); struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id); diff --cc proto/bgp/packets.c index 0e9747462,af3b15b59..038e89f9b --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@@ -2678,12 -1494,37 +2678,37 @@@ bgp_error_dsc(uint code, uint subcode return buff; } + /* RFC 8203 - shutdown communication message */ + static int + bgp_handle_message(struct bgp_proto *p, byte *data, uint len, byte **bp) + { + byte *msg = data + 1; + uint msg_len = data[0]; + uint i; + + /* Handle zero length message */ + if (msg_len == 0) + return 1; + + /* Handle proper message */ + if ((msg_len > 128) && (msg_len + 1 > len)) + return 0; + + /* Some elementary cleanup */ + for (i = 0; i < msg_len; i++) + if (msg[i] < ' ') + msg[i] = ' '; + + proto_set_message(&p->p, msg, msg_len); + *bp += bsprintf(*bp, ": \"%s\"", p->p.message); + return 1; + } + void -bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len) +bgp_log_error(struct bgp_proto *p, u8 class, char *msg, uint code, uint subcode, byte *data, uint len) { - const byte *name; - byte *t, argbuf[36]; + byte argbuf[256], *t = argbuf; - unsigned i; + uint i; /* Don't report Cease messages generated by myself */ if (code == 6 && class == BE_BGP_TX) @@@ -2727,14 -1579,34 +2761,14 @@@ bgp_rx_notification(struct bgp_conn *co bgp_log_error(p, BE_BGP_RX, "Received", code, subcode, pkt+21, len-21); bgp_store_error(p, conn, BE_BGP_RX, (code << 16) | subcode); -#ifndef IPV6 - if ((code == 2) && ((subcode == 4) || (subcode == 7)) - /* Error related to capability: - * 4 - Peer does not support capabilities at all. - * 7 - Peer request some capability. Strange unless it is IPv6 only peer. - */ - && (p->cf->capabilities == 2) - /* Capabilities are not explicitly enabled or disabled, therefore heuristic is used */ - && (conn->start_state == BSS_CONNECT) - /* Failed connection attempt have used capabilities */ - && (p->cf->remote_as <= 0xFFFF)) - /* Not possible with disabled capabilities */ - { - /* We try connect without capabilities */ - log(L_WARN "%s: Capability related error received, retry with capabilities disabled", p->p.name); - p->start_state = BSS_CONNECT_NOCAP; - err = 0; - } -#endif - bgp_conn_enter_close_state(conn); - bgp_schedule_packet(conn, PKT_SCHEDULE_CLOSE); + bgp_schedule_packet(conn, NULL, PKT_SCHEDULE_CLOSE); - if (err) - { - bgp_update_startup_delay(p); - bgp_stop(p, 0, NULL, 0); - } + if (err) + { + bgp_update_startup_delay(p); - bgp_stop(p, 0); ++ bgp_stop(p, 0, NULL, 0); + } } static void diff --cc proto/radv/config.Y index 0e43c237c,84a2de0ef..37815f0d4 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@@ -55,7 -56,12 +58,8 @@@ radv_proto_item | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); } | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); } | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); } - | TRIGGER prefix { - RADV_CFG->trigger_prefix = $2.addr; - RADV_CFG->trigger_pxlen = $2.len; - RADV_CFG->trigger_valid = 1; - } + | TRIGGER net_ip6 { RADV_CFG->trigger = $2; } + | PROPAGATE ROUTES bool { RADV_CFG->propagate_routes = $3; } ; radv_proto_opts: @@@ -76,14 -82,18 +80,18 @@@ radv_iface_start init_list(&RADV_IFACE->rdnss_list); init_list(&RADV_IFACE->dnssl_list); -- RADV_IFACE->min_ra_int = -1; /* undefined */ ++ RADV_IFACE->min_ra_int = (u32) -1; /* undefined */ RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT; RADV_IFACE->min_delay = DEFAULT_MIN_DELAY; - RADV_IFACE->prefix_linger_time = -1; - RADV_IFACE->route_linger_time = -1; ++ RADV_IFACE->prefix_linger_time = (u32) -1; ++ RADV_IFACE->route_linger_time = (u32) -1; RADV_IFACE->current_hop_limit = DEFAULT_CURRENT_HOP_LIMIT; - RADV_IFACE->linger_time = DEFAULT_LINGER_TIME; -- RADV_IFACE->default_lifetime = -1; ++ RADV_IFACE->default_lifetime = (u32) -1; RADV_IFACE->default_lifetime_sensitive = 1; RADV_IFACE->default_preference = RA_PREF_MEDIUM; - RADV_IFACE->route_lifetime = -1; ++ RADV_IFACE->route_lifetime = (u32) -1; + RADV_IFACE->route_lifetime_sensitive = 0; + RADV_IFACE->route_preference = RA_PREF_MEDIUM; }; radv_iface_item: @@@ -92,17 -102,23 +100,23 @@@ | MIN DELAY expr { RADV_IFACE->min_delay = $3; if ($3 <= 0) cf_error("Min delay must be positive"); } | MANAGED bool { RADV_IFACE->managed = $2; } | OTHER CONFIG bool { RADV_IFACE->other_config = $3; } - | LINK MTU expr { RADV_IFACE->link_mtu = $3; if ($3 < 0) cf_error("Link MTU must be 0 or positive"); } - | REACHABLE TIME expr { RADV_IFACE->reachable_time = $3; if (($3 < 0) || ($3 > 3600000)) cf_error("Reachable time must be in range 0-3600000"); } - | RETRANS TIMER expr { RADV_IFACE->retrans_timer = $3; if ($3 < 0) cf_error("Retrans timer must be 0 or positive"); } - | CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if (($4 < 0) || ($4 > 255)) cf_error("Current hop limit must be in range 0-255"); } + | LINK MTU expr { RADV_IFACE->link_mtu = $3; } + | REACHABLE TIME expr { RADV_IFACE->reachable_time = $3; if ($3 > 3600000) cf_error("Reachable time must be in range 0-3600000"); } + | RETRANS TIMER expr { RADV_IFACE->retrans_timer = $3; } - | LINGER TIME expr { RADV_IFACE->linger_time = $3; if ($3 > 3600) cf_error("Linger time must be in range 0-3600"); } + | CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if ($4 > 255) cf_error("Current hop limit must be in range 0-255"); } | DEFAULT LIFETIME expr radv_sensitive { RADV_IFACE->default_lifetime = $3; - if (($3 < 0) || ($3 > 9000)) cf_error("Default lifetime must be in range 0-9000"); - if ($4 != -1) RADV_IFACE->default_lifetime_sensitive = $4; + if ($3 > 9000) cf_error("Default lifetime must be in range 0-9000"); + if ($4 != (uint) -1) RADV_IFACE->default_lifetime_sensitive = $4; } + | ROUTE LIFETIME expr radv_sensitive { + RADV_IFACE->route_lifetime = $3; - if ($4 != -1) RADV_IFACE->route_lifetime_sensitive = $4; ++ if ($4 != (uint) -1) RADV_IFACE->route_lifetime_sensitive = $4; + } | DEFAULT PREFERENCE radv_preference { RADV_IFACE->default_preference = $3; } + | ROUTE PREFERENCE radv_preference { RADV_IFACE->route_preference = $3; } + | PREFIX LINGER TIME expr { RADV_IFACE->prefix_linger_time = $4; } + | ROUTE LINGER TIME expr { RADV_IFACE->route_linger_time = $4; } | PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); } | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_IFACE->rdnss_list, &radv_dns_list); } | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_IFACE->dnssl_list, &radv_dns_list); } @@@ -125,9 -141,18 +139,18 @@@ radv_iface_finish if (ic->default_lifetime == (u32) -1) ic->default_lifetime = 3 * ic->max_ra_int; + if (ic->route_lifetime == (u32) -1) + ic->route_lifetime = 3 * ic->max_ra_int; + + if (ic->prefix_linger_time == (u32) -1) + ic->prefix_linger_time = 3 * ic->max_ra_int; + + if (ic->route_linger_time == (u32) -1) + ic->route_linger_time = 3 * ic->max_ra_int; + if ((ic->min_ra_int > 3) && (ic->min_ra_int > (ic->max_ra_int * 3 / 4))) - cf_error("Min RA interval must be at most 3/4 * Max RA interval %d %d", ic->min_ra_int, ic->max_ra_int); + cf_error("Min RA interval must be at most 3/4 * Max RA interval"); if ((ic->default_lifetime > 0) && (ic->default_lifetime < ic->max_ra_int)) cf_error("Default lifetime must be either 0 or at least Max RA interval"); @@@ -294,7 -331,7 +328,7 @@@ radv_mult ; radv_sensitive: -- /* empty */ { $$ = -1; } ++ /* empty */ { $$ = (uint) -1; } | SENSITIVE bool { $$ = $2; } ; diff --cc proto/radv/packets.c index 7c148b7dc,7d54a827e..b12d3a12f --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@@ -70,6 -80,44 +80,44 @@@ struct radv_opt_dnss char domain[]; }; + static int + radv_prepare_route(struct radv_iface *ifa, struct radv_route *rt, + char **buf, char *bufend) + { + struct radv_proto *p = ifa->ra; - u8 px_blocks = (rt->n.pxlen + 63) / 64; ++ u8 px_blocks = (net6_pxlen(rt->n.addr) + 63) / 64; + u8 opt_len = 8 * (1 + px_blocks); + + if (*buf + opt_len > bufend) + { + log(L_WARN, "%s: Too many RA options on interface %s", + p->p.name, ifa->iface->name); + return -1; + } + + uint preference = rt->preference_set ? rt->preference : ifa->cf->route_preference; + uint lifetime = rt->lifetime_set ? rt->lifetime : ifa->cf->route_lifetime; + uint valid = rt->valid && p->valid && (p->active || !ifa->cf->route_lifetime_sensitive); + + struct radv_opt_route *opt = (void *) *buf; + *buf += opt_len; + opt->type = OPT_ROUTE; + opt->length = 1 + px_blocks; - opt->pxlen = rt->n.pxlen; ++ opt->pxlen = net6_pxlen(rt->n.addr); + opt->flags = preference; + opt->lifetime = valid ? htonl(lifetime) : 0; + + /* Copy the relevant part of the prefix */ - ip6_addr px_addr = ip6_hton(rt->n.prefix); ++ ip6_addr px_addr = ip6_hton(net6_prefix(rt->n.addr)); + memcpy(opt->prefix, &px_addr, 8 * px_blocks); + + /* Keeping track of first linger timeout */ + if (!rt->valid) - ifa->valid_time = MIN(ifa->valid_time, rt->changed + ifa->cf->route_linger_time); ++ ifa->valid_time = MIN(ifa->valid_time, rt->changed + ifa->cf->route_linger_time S); + + return 0; + } + static int radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend) { @@@ -228,9 -277,14 +276,13 @@@ radv_prepare_prefix(struct radv_iface * op->preferred_lifetime = (ifa->ra->active || !pc->preferred_lifetime_sensitive) ? htonl(pc->preferred_lifetime) : 0; op->reserved = 0; - op->prefix = prefix->prefix; - ipa_hton(op->prefix); + op->prefix = ip6_hton(px->prefix.prefix); *buf += sizeof(*op); + /* Keeping track of first linger timeout */ - if (!prefix->valid) - ifa->valid_time = MIN(ifa->valid_time, prefix->changed + ifa->cf->prefix_linger_time); ++ if (!px->valid) ++ ifa->valid_time = MIN(ifa->valid_time, px->changed + ifa->cf->prefix_linger_time S); + return 0; } @@@ -240,6 -294,6 +292,7 @@@ radv_prepare_ra(struct radv_iface *ifa struct radv_proto *p = ifa->ra; struct radv_config *cf = (struct radv_config *) (p->p.cf); struct radv_iface_config *ic = ifa->cf; ++ btime now = current_time(); char *buf = ifa->sk->tbuf; char *bufstart = buf; @@@ -269,10 -323,17 +322,17 @@@ buf += sizeof (*om); } - struct radv_prefix *prefix; - WALK_LIST(prefix, ifa->prefixes) + /* Keeping track of first linger timeout */ + ifa->valid_time = TIME_INFINITY; + + struct radv_prefix *px; + WALK_LIST(px, ifa->prefixes) { - if (radv_prepare_prefix(ifa, prefix, &buf, bufend) < 0) + /* Skip invalid prefixes that are past linger timeout but still not pruned */ - if (!px->valid && (px->changed + ic->prefix_linger_time <= now)) ++ if (!px->valid && ((px->changed + ic->prefix_linger_time S) <= now)) + continue; + + if (radv_prepare_prefix(ifa, px, &buf, bufend) < 0) goto done; } @@@ -290,6 -351,22 +350,20 @@@ if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0) goto done; + if (p->fib_up) + { - FIB_WALK(&p->routes, n) ++ FIB_WALK(&p->routes, struct radv_route, rt) + { - struct radv_route *rt = (void *) n; - + /* Skip invalid routes that are past linger timeout but still not pruned */ - if (!rt->valid && (rt->changed + ic->route_linger_time <= now)) ++ if (!rt->valid && ((rt->changed + ic->route_linger_time S) <= now)) + continue; + + if (radv_prepare_route(ifa, rt, &buf, bufend) < 0) + goto done; + } + FIB_WALK_END; + } + done: ifa->plen = buf - bufstart; } diff --cc proto/radv/radv.c index e9140115a,d53b65e37..0a2a3e78a --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@@ -48,34 -52,32 +52,34 @@@ radv_timer(timer *tm { struct radv_iface *ifa = tm->data; struct radv_proto *p = ifa->ra; ++ btime now = current_time(); RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name); - /* - * If some dead prefixes expired, regenerate the prefix list and the packet. - * We do so by pretending there was a change on the interface. - * - * This sets the timer, but we replace it just at the end of this function - * (replacing a timer is fine). - */ - if (ifa->prefix_expires && (ifa->prefix_expires <= current_time())) - radv_iface_notify(ifa, RA_EV_GC); + if (ifa->valid_time <= now) + radv_invalidate(ifa); + + if (ifa->prune_time <= now) + radv_prune_prefixes(ifa); - radv_send_ra(ifa, 0); + if (p->prune_time <= now) + radv_prune_routes(p); + + radv_send_ra(ifa); /* Update timer */ - ifa->last = current_time(); + ifa->last = now; - unsigned after = ifa->cf->min_ra_int; - after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1); + btime t = ifa->cf->min_ra_int S; + btime r = (ifa->cf->max_ra_int - ifa->cf->min_ra_int) S; + t += random() % (r + 1); if (ifa->initial) + { + t = MIN(t, MAX_INITIAL_RTR_ADVERT_INTERVAL); ifa->initial--; + } - if (ifa->initial) - after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL); - - tm_start(ifa->timer, after); + tm_start(ifa->timer, t); } static struct radv_prefix_config default_prefix = { @@@ -115,8 -120,7 +119,8 @@@ static voi radv_prepare_prefixes(struct radv_iface *ifa) { struct radv_proto *p = ifa->ra; - struct radv_iface_config *cf = ifa->cf; - struct radv_prefix *pfx; + struct radv_prefix *pfx, *next; ++ btime now = current_time(); /* First mark all the prefixes as unused */ WALK_LIST(pfx, ifa->prefixes) @@@ -164,43 -165,44 +169,45 @@@ existing->cf = pc; } - /* - * Garbage-collect the prefixes. If something isn't used, it dies (but isn't - * dropped just yet). If something is dead and rots there for long enough, - * clean it up. - */ - btime now_ = current_time(); - btime expires = now_ + cf->linger_time S; - btime expires_min = 0; - struct radv_prefix *next; WALK_LIST_DELSAFE(pfx, next, ifa->prefixes) { - if (pfx->alive && !pfx->mark) + if (pfx->valid && !pfx->mark) { - RADV_TRACE(D_EVENTS, "Marking prefix %N on %s as dead", - RADV_TRACE(D_EVENTS, "Invalidating prefix %I/$d on %s", - pfx->prefix, pfx->len, ifa->iface->name); ++ RADV_TRACE(D_EVENTS, "Invalidating prefix %N on %s", + pfx->prefix, ifa->iface->name); - pfx->alive = 0; - pfx->expires = expires; + pfx->valid = 0; + pfx->changed = now; pfx->cf = &dead_prefix; } + } + } + + static void + radv_prune_prefixes(struct radv_iface *ifa) + { + struct radv_proto *p = ifa->ra; - bird_clock_t next = TIME_INFINITY; - bird_clock_t expires = 0; ++ btime now = current_time(); ++ btime next = TIME_INFINITY; ++ btime expires = 0; - if (!pfx->alive) + struct radv_prefix *px, *pxn; + WALK_LIST_DELSAFE(px, pxn, ifa->prefixes) + { + if (!px->valid) { - if (pfx->expires <= now_) - expires = px->changed + ifa->cf->prefix_linger_time; ++ expires = px->changed + ifa->cf->prefix_linger_time S; + + if (expires <= now) { - RADV_TRACE(D_EVENTS, "Removing prefix %I/%d on %s", - px->prefix, px->len, ifa->iface->name); + RADV_TRACE(D_EVENTS, "Removing prefix %N on %s", - pfx->prefix, ifa->iface->name); ++ px->prefix, ifa->iface->name); - rem_node(NODE pfx); - mb_free(pfx); + rem_node(NODE px); + mb_free(px); } else - { - /* Find minimum expiration time */ - if (!expires_min || (pfx->expires < expires_min)) - expires_min = pfx->expires; - } + next = MIN(next, expires); } } @@@ -232,11 -235,14 +240,9 @@@ radv_iface_notify(struct radv_iface *if break; } - radv_prepare_prefixes(ifa); - /* Update timer */ - unsigned delta = now - ifa->last; - unsigned after = 0; - - if (delta < ifa->cf->min_delay) - after = ifa->cf->min_delay - delta; - - tm_start(ifa->timer, after); + btime t = ifa->last + ifa->cf->min_delay S - current_time(); + tm_start(ifa->timer, t); } static void @@@ -289,8 -305,8 +294,9 @@@ radv_iface_new(struct radv_proto *p, st ifa->ra = p; ifa->cf = cf; ifa->iface = iface; + ifa->addr = iface->llv6; init_list(&ifa->prefixes); + ifa->prune_time = TIME_INFINITY; add_tail(&p->iface_list, NODE ifa); @@@ -401,7 -420,7 +410,7 @@@ radv_import_control(struct proto *P, rt } static void - radv_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED) -radv_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs) ++radv_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs) { struct radv_proto *p = (struct radv_proto *) P; struct radv_config *cf = (struct radv_config *) (P->cf); @@@ -420,7 -441,124 +431,123 @@@ RADV_TRACE(D_EVENTS, "Suppressed"); radv_iface_notify_all(p, RA_EV_CHANGE); + return; + } + + if (!cf->propagate_routes) + return; + + /* + * Some other route we want to send (or stop sending). Update the cache, + * with marking a removed one as dead or creating a new one as needed. + * + * And yes, we exclude the trigger route on purpose. + */ + + if (new) + { + /* Update */ + + ea = ea_find(attrs, EA_RA_PREFERENCE); + uint preference = ea ? ea->u.data : RA_PREF_MEDIUM; + uint preference_set = !!ea; + + ea = ea_find(attrs, EA_RA_LIFETIME); + uint lifetime = ea ? ea->u.data : 0; + uint lifetime_set = !!ea; + + if ((preference != RA_PREF_LOW) && + (preference != RA_PREF_MEDIUM) && + (preference != RA_PREF_HIGH)) + { - log(L_WARN "%s: Invalid ra_preference value %u on route %I/%d", - p->p.name, preference, n->n.prefix, n->n.pxlen); ++ log(L_WARN "%s: Invalid ra_preference value %u on route %N", ++ p->p.name, preference, n->n.addr); + preference = RA_PREF_MEDIUM; + preference_set = 1; + lifetime = 0; + lifetime_set = 1; + } + - rt = fib_get(&p->routes, &n->n.prefix, n->n.pxlen); ++ rt = fib_get(&p->routes, n->n.addr); + + /* Ignore update if nothing changed */ + if (rt->valid && + (rt->preference == preference) && + (rt->preference_set == preference_set) && + (rt->lifetime == lifetime) && + (rt->lifetime_set == lifetime_set)) + return; + + if (p->routes.entries == 18) + log(L_WARN "%s: More than 17 routes exported to RAdv", p->p.name); + + rt->valid = 1; - rt->changed = now; ++ rt->changed = current_time(); + rt->preference = preference; + rt->preference_set = preference_set; + rt->lifetime = lifetime; + rt->lifetime_set = lifetime_set; + } + else + { + /* Withdraw */ - rt = fib_find(&p->routes, &n->n.prefix, n->n.pxlen); ++ rt = fib_find(&p->routes, n->n.addr); + + if (!rt || !rt->valid) + return; + + /* Invalidate the route */ + rt->valid = 0; - rt->changed = now; ++ rt->changed = current_time(); + + /* Invalidated route will be pruned eventually */ - bird_clock_t expires = rt->changed + cf->max_linger_time; ++ btime expires = rt->changed + cf->max_linger_time S; + p->prune_time = MIN(p->prune_time, expires); } + + radv_iface_notify_all(p, RA_EV_CHANGE); + } + + /* + * Cleans up all the dead routes that expired and schedules itself to be run + * again if there are more routes waiting for expiration. + */ + static void + radv_prune_routes(struct radv_proto *p) + { + struct radv_config *cf = (struct radv_config *) (p->p.cf); - bird_clock_t next = TIME_INFINITY; - bird_clock_t expires = 0; ++ btime now = current_time(); ++ btime next = TIME_INFINITY; ++ btime expires = 0; + + /* Should not happen */ + if (!p->fib_up) + return; + + struct fib_iterator fit; + FIB_ITERATE_INIT(&fit, &p->routes); + + again: - FIB_ITERATE_START(&p->routes, &fit, node) ++ FIB_ITERATE_START(&p->routes, &fit, struct radv_route, rt) + { - struct radv_route *rt = (void *) node; - + if (!rt->valid) + { - expires = rt->changed + cf->max_linger_time; ++ expires = rt->changed + cf->max_linger_time S; + + /* Delete expired nodes */ + if (expires <= now) + { - FIB_ITERATE_PUT(&fit, node); - fib_delete(&p->routes, node); ++ FIB_ITERATE_PUT(&fit); ++ fib_delete(&p->routes, rt); + goto again; + } + else + next = MIN(next, expires); + } + } - FIB_ITERATE_END(node); ++ FIB_ITERATE_END; + + p->prune_time = next; } static int @@@ -460,6 -587,21 +587,22 @@@ radv_init(struct proto_config *CF return P; } + static void + radv_set_fib(struct radv_proto *p, int up) + { + if (up == p->fib_up) + return; + + if (up) - fib_init(&p->routes, p->p.pool, sizeof(struct radv_route), 4, NULL); ++ fib_init(&p->routes, p->p.pool, NET_IP6, sizeof(struct radv_route), ++ OFFSETOF(struct radv_route, n), 4, NULL); + else + fib_free(&p->routes); + + p->fib_up = up; + p->prune_time = TIME_INFINITY; + } + static int radv_start(struct proto *P) { @@@ -467,8 -609,13 +610,13 @@@ struct radv_config *cf = (struct radv_config *) (P->cf); init_list(&(p->iface_list)); + p->valid = 1; - p->active = !cf->trigger_valid; + p->active = !radv_trigger_valid(cf); + p->fib_up = 0; + radv_set_fib(p, cf->propagate_routes); + p->prune_time = TIME_INFINITY; + return PS_UP; } @@@ -492,26 -644,22 +645,25 @@@ radv_shutdown(struct proto *P } static int -radv_reconfigure(struct proto *P, struct proto_config *c) +radv_reconfigure(struct proto *P, struct proto_config *CF) { struct radv_proto *p = (struct radv_proto *) P; - // struct radv_config *old = (struct radv_config *) (p->cf); + struct radv_config *old = (struct radv_config *) (P->cf); - struct radv_config *new = (struct radv_config *) c; + struct radv_config *new = (struct radv_config *) CF; - /* - * The question is why there is a reconfigure function for RAdv if - * it has almost none internal state so restarting the protocol - * would probably suffice. One small reason is that restarting the - * protocol would lead to sending a RA with Router Lifetime 0 - * causing nodes to temporary remove their default routes. - */ - - P->cf = c; /* radv_check_active() requires proper P->cf */ + if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) + return 0; + + P->cf = CF; /* radv_check_active() requires proper P->cf */ p->active = radv_check_active(p); + /* Allocate or free FIB */ + radv_set_fib(p, new->propagate_routes); + + /* We started to accept routes so we need to refeed them */ + if (!old->propagate_routes && new->propagate_routes) - proto_request_feeding(&p->p); ++ channel_request_feeding(p->p.main_channel); + struct iface *iface; WALK_LIST(iface, iface_list) { @@@ -577,10 -747,8 +762,11 @@@ radv_get_attr(eattr *a, byte *buf, int struct protocol proto_radv = { .name = "RAdv", .template = "radv%d", + .attr_class = EAP_RADV, + .channel_mask = NB_IP6, + .proto_size = sizeof(struct radv_proto), .config_size = sizeof(struct radv_config), + .postconfig = radv_postconfig, .init = radv_init, .start = radv_start, .shutdown = radv_shutdown, diff --cc proto/radv/radv.h index 4672e3b2e,ab081397a..66f785a74 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@@ -51,7 -50,11 +50,9 @@@ struct radv_confi list rdnss_list; /* Global list of RDNSS configs (struct radv_rdnss_config) */ list dnssl_list; /* Global list of DNSSL configs (struct radv_dnssl_config) */ - ip_addr trigger_prefix; /* Prefix of a trigger route, if defined */ - u8 trigger_pxlen; /* Pxlen of a trigger route, if defined */ - u8 trigger_valid; /* Whether a trigger route is defined */ + net_addr trigger; /* Prefix of a trigger route, if defined */ + u8 propagate_routes; /* Do we propagate more specific routes (RFC 4191)? */ + u32 max_linger_time; /* Maximum of interface route_linger_time */ }; struct radv_iface_config @@@ -114,24 -121,44 +118,45 @@@ struct radv_dnssl_confi char *domain; /* Domain for DNS search list, in processed form */ }; + /* + * One more specific route as per RFC 4191. + * + * Note that it does *not* contain the next hop field. The next hop is always + * the router sending the advertisment and the more specific route only allows + * overriding the preference of the route. + */ + struct radv_route + { - struct fib_node n; + u32 lifetime; /* Lifetime from an attribute */ + u8 lifetime_set; /* Whether lifetime is defined */ + u8 preference; /* Preference of the route, RA_PREF_* */ + u8 preference_set; /* Whether preference is defined */ + u8 valid; /* Whethe route is valid or withdrawn */ - bird_clock_t changed; /* Last time when the route changed */ ++ btime changed; /* Last time when the route changed */ ++ ++ struct fib_node n; + }; struct radv_proto { struct proto p; list iface_list; /* List of active ifaces */ + u8 valid; /* Router is valid for forwarding, used for shutdown */ u8 active; /* Whether radv is active w.r.t. triggers */ + u8 fib_up; /* FIB table (routes) is initialized */ + struct fib routes; /* FIB table of specific routes (struct radv_route) */ - bird_clock_t prune_time; /* Next time of route table pruning */ ++ btime prune_time; /* Next time of route table pruning */ }; struct radv_prefix /* One prefix we advertise */ { node n; - ip_addr prefix; - u8 len; + net_addr_ip6 prefix; + - u8 alive; /* Is the prefix alive? If not, we advertise it + u8 valid; /* Is the prefix valid? If not, we advertise it with 0 lifetime, so clients stop using it */ u8 mark; /* A temporary mark for processing */ - btime expires; /* The time when we drop this prefix from - advertising. It is valid only if !alive. */ - bird_clock_t changed; /* Last time when the prefix changed */ ++ btime changed; /* Last time when the prefix changed */ struct radv_prefix_config *cf; /* The config tied to this prefix */ }; @@@ -144,7 -171,8 +169,8 @@@ struct radv_ifac struct ifa *addr; /* Link-local address of iface */ struct pool *pool; /* A pool for interface-specific things */ list prefixes; /* The prefixes we advertise (struct radv_prefix) */ - btime prefix_expires; /* When the soonest prefix expires (0 = none dead) */ - bird_clock_t prune_time; /* Next time of prefix list pruning */ - bird_clock_t valid_time; /* Cached packet is valid until first linger timeout */ ++ btime prune_time; /* Next time of prefix list pruning */ ++ btime valid_time; /* Cached packet is valid until first linger timeout */ timer *timer; struct object_lock *lock;