From: Ondrej Zajicek (work) Date: Thu, 7 Dec 2017 17:35:46 +0000 (+0100) Subject: Merge commit '7b2c5f3d2826e3175bf31b1c36056c9efc587a2b' into int-new X-Git-Tag: v2.0.0~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=46434a3cad99260b5a659e5df874eab4615bcb36;p=thirdparty%2Fbird.git Merge commit '7b2c5f3d2826e3175bf31b1c36056c9efc587a2b' into int-new --- 46434a3cad99260b5a659e5df874eab4615bcb36 diff --cc nest/config.Y index ef29fb966,025e89695..ad45a39dc --- a/nest/config.Y +++ b/nest/config.Y @@@ -65,8 -55,7 +65,8 @@@ proto_postconfig(void CF_DECLS CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) - CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS) + CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, TABLE, STATES, ROUTES, FILTERS) +CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6) CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512) @@@ -223,57 -217,20 +223,58 @@@ proto_item | DISABLED bool { this_proto->disabled = $2; } | DEBUG debug_mask { this_proto->debug = $2; } | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; } - | IMPORT imexport { this_proto->in_filter = $2; } - | EXPORT imexport { this_proto->out_filter = $2; } - | RECEIVE LIMIT limit_spec { this_proto->rx_limit = $3; } - | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; } - | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; } - | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; } - | VRF TEXT { this_proto->vrf = if_get_by_name($2); } - | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } | DESCRIPTION text { this_proto->dsc = $2; } ++ | VRF text { this_proto->vrf = if_get_by_name($2); } + ; + + +channel_start: net_type +{ + $$ = this_channel = channel_config_new(NULL, $1, this_proto); +}; + +channel_item: + TABLE rtable { + if (this_channel->net_type && ($2->addr_type != this_channel->net_type)) + cf_error("Incompatible table type"); + this_channel->table = $2; + } + | IMPORT imexport { this_channel->in_filter = $2; } + | EXPORT imexport { this_channel->out_filter = $2; } + | RECEIVE LIMIT limit_spec { this_channel->rx_limit = $3; } + | IMPORT LIMIT limit_spec { this_channel->in_limit = $3; } + | EXPORT LIMIT limit_spec { this_channel->out_limit = $3; } + | PREFERENCE expr { this_channel->preference = $2; check_u16($2); } + | IMPORT KEEP FILTERED bool { this_channel->in_keep_filtered = $4; } + ; + +channel_opts: + /* empty */ + | channel_opts channel_item ';' + ; + +channel_opt_list: + /* empty */ + | '{' channel_opts '}' + ; + +channel_end: +{ + if (!this_channel->table) + cf_error("Routing table not specified"); + + this_channel = NULL; +}; + +proto_channel: channel_start channel_opt_list channel_end; + + +rtable: + SYM { + if ($1->class != SYM_TABLE) cf_error("Table expected"); + $$ = $1->def; + } ; imexport: diff --cc nest/iface.c index dbc4debe4,3dd450653..5e0a2450f --- a/nest/iface.c +++ b/nest/iface.c @@@ -142,11 -140,12 +144,13 @@@ if_copy(struct iface *to, struct iface static inline void ifa_send_notify(struct proto *p, unsigned c, struct ifa *a) { - if (p->ifa_notify && (p->proto_state != PS_DOWN)) - if (p->ifa_notify && (!p->vrf || p->vrf == a->iface->master)) ++ if (p->ifa_notify && ++ (p->proto_state != PS_DOWN) && ++ (!p->vrf || p->vrf == a->iface->master)) { if (p->debug & D_IFACES) - log(L_TRACE "%s <%s address %I/%d on interface %s %s", - p->name, (a->flags & IA_PRIMARY) ? " primary" : "", - a->prefix, a->pxlen, a->iface->name, + log(L_TRACE "%s < address %N on interface %s %s", + p->name, &a->prefix, a->iface->name, (c & IF_CHANGE_UP) ? "added" : "removed"); p->ifa_notify(p, c, a); } @@@ -178,7 -177,7 +182,9 @@@ ifa_notify_change(unsigned c, struct if static inline void if_send_notify(struct proto *p, unsigned c, struct iface *i) { - if (p->if_notify && (p->proto_state != PS_DOWN)) - if (p->if_notify && (!p->vrf || p->vrf == i->master)) ++ if (p->if_notify && ++ (p->proto_state != PS_DOWN) && ++ (!p->vrf || p->vrf == i->master)) { if (p->debug & D_IFACES) log(L_TRACE "%s < interface %s %s", p->name, i->name, @@@ -231,14 -235,16 +237,16 @@@ if_notify_change(unsigned c, struct ifa neigh_if_link(i); } -static unsigned -if_recalc_flags(struct iface *i, unsigned flags) +static uint +if_recalc_flags(struct iface *i UNUSED, uint flags) { - if ((flags & IF_ADMIN_UP) && !(flags & (IF_SHUTDOWN | IF_TMP_DOWN))) - if ((flags & (IF_SHUTDOWN | IF_TMP_DOWN)) || - !(flags & IF_ADMIN_UP) || - !i->addr || - (i->master_index && !i->master)) - flags &= ~IF_UP; - else ++ if ((flags & IF_ADMIN_UP) && ++ !(flags & (IF_SHUTDOWN | IF_TMP_DOWN)) && ++ !(i->master_index && !i->master)) flags |= IF_UP; + else + flags &= ~IF_UP; + return flags; } @@@ -835,7 -774,13 +843,13 @@@ if_show(void if (i->flags & IF_SHUTDOWN) continue; - cli_msg(-1001, "%s %s (index=%d)", i->name, (i->flags & IF_UP) ? "Up" : "Down", i->index); + char mbuf[16 + sizeof(i->name)] = {}; + if (i->master) + bsprintf(mbuf, " master=%s", i->master->name); + else if (i->master_index) + bsprintf(mbuf, " master=#%u", i->master_index); + - cli_msg(-1001, "%s %s (index=%d%s)", i->name, (i->flags & IF_UP) ? "up" : "DOWN", i->index, mbuf); ++ cli_msg(-1001, "%s %s (index=%d%s)", i->name, (i->flags & IF_UP) ? "Up" : "Down", i->index, mbuf); if (!(i->flags & IF_MULTIACCESS)) type = "PtP"; else diff --cc nest/iface.h index 59b1253c9,b8e698384..ab3f8f351 --- a/nest/iface.h +++ b/nest/iface.h @@@ -34,11 -34,10 +34,13 @@@ struct iface unsigned flags; unsigned mtu; unsigned index; /* OS-dependent interface index */ + unsigned master_index; /* Interface index of master iface */ - list addrs; /* Addresses assigned to this interface */ - struct ifa *addr; /* Primary address */ + struct iface *master; /* Master iface (e.g. for VRF) */ + list addrs; /* Addresses assigned to this interface */ + struct ifa *addr4; /* Primary address for IPv4 */ + struct ifa *addr6; /* Primary address for IPv6 */ + struct ifa *llv6; /* Primary link-local address for IPv6 */ + ip4_addr sysdep; /* Arbitrary IPv4 address for internal sysdep use */ list neighbors; /* All neighbors on this interface */ }; diff --cc nest/proto.c index b28ac5696,64422ee37..ecc3b0fe3 --- a/nest/proto.c +++ b/nest/proto.c @@@ -13,6 -13,6 +13,7 @@@ #include "lib/resource.h" #include "lib/lists.h" #include "lib/event.h" ++#include "lib/timer.h" #include "lib/string.h" #include "conf/conf.h" #include "nest/route.h" @@@ -433,265 -207,38 +434,266 @@@ channel_reloadable(struct channel *c } static void -proto_link_ahooks(struct proto *p) +channel_request_reload(struct channel *c) { - struct announce_hook *h; + ASSERT(c->channel_state == CS_UP); + // ASSERT(channel_reloadable(c)); + + c->proto->reload_routes(c); - if (p->rt_notify) - for(h=p->ahooks; h; h=h->next) - add_tail(&h->table->hooks, &h->n); + /* + * Should this be done before reload_routes() hook? + * Perhaps, but routes are updated asynchronously. + */ + channel_reset_limit(&c->rx_limit); + channel_reset_limit(&c->in_limit); } -static void -proto_unlink_ahooks(struct proto *p) +const struct channel_class channel_basic = { + .channel_size = sizeof(struct channel), + .config_size = sizeof(struct channel_config) +}; + +void * +channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto) +{ + struct channel_config *cf = NULL; + struct rtable_config *tab = NULL; + const char *name = NULL; + + if (net_type) + { + if (!net_val_match(net_type, proto->protocol->channel_mask)) + cf_error("Unsupported channel type"); + + if (proto->net_type && (net_type != proto->net_type)) + cf_error("Different channel type"); + + tab = new_config->def_tables[net_type]; + name = net_label[net_type]; + } + + if (!cc) + cc = &channel_basic; + + cf = cfg_allocz(cc->config_size); + cf->name = name; + cf->channel = cc; + cf->table = tab; + cf->out_filter = FILTER_REJECT; + + cf->net_type = net_type; + cf->ra_mode = RA_OPTIMAL; + cf->preference = proto->protocol->preference; + + add_tail(&proto->channels, &cf->n); + + return cf; +} + +struct channel_config * +channel_copy_config(struct channel_config *src, struct proto_config *proto) { - struct announce_hook *h; + struct channel_config *dst = cfg_alloc(src->channel->config_size); - if (p->rt_notify) - for(h=p->ahooks; h; h=h->next) - rem_node(&h->n); + memcpy(dst, src, src->channel->config_size); + add_tail(&proto->channels, &dst->n); + CALL(src->channel->copy_config, dst, src); + + return dst; } + +static int reconfigure_type; /* Hack to propagate type info to channel_reconfigure() */ + +int +channel_reconfigure(struct channel *c, struct channel_config *cf) +{ + /* FIXME: better handle these changes, also handle in_keep_filtered */ + if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode))) + return 0; + + int import_changed = !filter_same(c->in_filter, cf->in_filter); + int export_changed = !filter_same(c->out_filter, cf->out_filter); + + if (c->preference != cf->preference) + import_changed = 1; + + if (c->merge_limit != cf->merge_limit) + export_changed = 1; + + /* Reconfigure channel fields */ + c->in_filter = cf->in_filter; + c->out_filter = cf->out_filter; + c->rx_limit = cf->rx_limit; + c->in_limit = cf->in_limit; + c->out_limit = cf->out_limit; + + // c->ra_mode = cf->ra_mode; + c->merge_limit = cf->merge_limit; + c->preference = cf->preference; + c->in_keep_filtered = cf->in_keep_filtered; + + channel_verify_limits(c); + + /* Execute channel-specific reconfigure hook */ + if (c->channel->reconfigure && !c->channel->reconfigure(c, cf)) + return 0; + + /* If the channel is not open, it has no routes and we cannot reload it anyways */ + if (c->channel_state != CS_UP) + return 1; + + if (reconfigure_type == RECONFIG_SOFT) + { + if (import_changed) + log(L_INFO "Channel %s.%s changed import", c->proto->name, c->name); + + if (export_changed) + log(L_INFO "Channel %s.%s changed export", c->proto->name, c->name); + + return 1; + } + + /* Route reload may be not supported */ + if (import_changed && !channel_reloadable(c)) + return 0; + + if (import_changed || export_changed) + log(L_INFO "Reloading channel %s.%s", c->proto->name, c->name); + + if (import_changed) + channel_request_reload(c); + + if (export_changed) + channel_request_feeding(c); + + return 1; +} + + +int +proto_configure_channel(struct proto *p, struct channel **pc, struct channel_config *cf) +{ + struct channel *c = *pc; + + if (!c && cf) + { + *pc = proto_add_channel(p, cf); + } + else if (c && !cf) + { + if (c->channel_state != CS_DOWN) + { + log(L_INFO "Cannot remove channel %s.%s", c->proto->name, c->name); + return 0; + } + + proto_remove_channel(p, c); + *pc = NULL; + } + else if (c && cf) + { + if (!channel_reconfigure(c, cf)) + { + log(L_INFO "Cannot reconfigure channel %s.%s", c->proto->name, c->name); + return 0; + } + } + + return 1; +} + + static void -proto_free_ahooks(struct proto *p) +proto_event(void *ptr) { - struct announce_hook *h, *hn; + struct proto *p = ptr; - for(h = p->ahooks; h; h = hn) + if (p->do_start) { - hn = h->next; - mb_free(h); + if_feed_baby(p); + p->do_start = 0; } - p->ahooks = NULL; - p->main_ahook = NULL; + if (p->do_stop) + { + if (p->proto == &proto_unix_iface) + if_flush_ifaces(p); + p->do_stop = 0; + } + + if (proto_is_done(p)) + { + if (p->proto->cleanup) + p->proto->cleanup(p); + + p->active = 0; + proto_log_state_change(p); + proto_rethink_goal(p); + } +} + + +/** + * proto_new - create a new protocol instance + * @c: protocol configuration + * + * When a new configuration has been read in, the core code starts + * initializing all the protocol instances configured by calling their + * init() hooks with the corresponding instance configuration. The initialization + * code of the protocol is expected to create a new instance according to the + * configuration by calling this function and then modifying the default settings + * to values wanted by the protocol. + */ +void * +proto_new(struct proto_config *cf) +{ + struct proto *p = mb_allocz(proto_pool, cf->protocol->proto_size); + + p->cf = cf; + p->debug = cf->debug; + p->mrtdump = cf->mrtdump; + p->name = cf->name; + p->proto = cf->protocol; + p->net_type = cf->net_type; + p->disabled = cf->disabled; + p->hash_key = random_u32(); + cf->proto = p; + + init_list(&p->channels); + + return p; +} + +static struct proto * +proto_init(struct proto_config *c, node *n) +{ + struct protocol *pr = c->protocol; + struct proto *p = pr->init(c); + + p->proto_state = PS_DOWN; + p->last_state_change = current_time(); ++ p->vrf = c->vrf; + insert_node(&p->n, n); + + p->event = ev_new(proto_pool); + p->event->hook = proto_event; + p->event->data = p; + + PD(p, "Initializing%s", p->disabled ? " [disabled]" : ""); + + return p; +} + +static void +proto_start(struct proto *p) +{ + /* Here we cannot use p->cf->name since it won't survive reconfiguration */ + p->pool = rp_new(proto_pool, p->proto->name); + + if (graceful_restart_state == GRS_INIT) + p->gr_recovery = 1; } @@@ -818,17 -409,17 +820,18 @@@ proto_reconfigure(struct proto *p, stru /* If there is a too big change in core attributes, ... */ if ((nc->protocol != oc->protocol) || + (nc->net_type != oc->net_type) || - (nc->disabled != p->disabled)) + (nc->disabled != p->disabled) || - (nc->vrf != oc->vrf) || - (nc->table->table != oc->table->table)) ++ (nc->vrf != oc->vrf)) return 0; + p->name = nc->name; p->debug = nc->debug; p->mrtdump = nc->mrtdump; - proto_reconfig_type = type; + reconfigure_type = type; /* Execute protocol specific reconfigure hook */ - if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc))) + if (!p->proto->reconfigure || !p->proto->reconfigure(p, nc)) return 0; DBG("\t%s: same\n", oc->name); @@@ -1638,25 -1516,20 +1641,27 @@@ proto_cmd_show(struct proto *p, uint ve proto_state_name(p), tbuf, buf); + if (verbose) + { + if (p->cf->dsc) + cli_msg(-1006, " Description: %s", p->cf->dsc); + 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->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 diff --cc nest/protocol.h index e843b0b41,18dfbd6fb..d7e84a441 --- a/nest/protocol.h +++ b/nest/protocol.h @@@ -94,12 -91,17 +94,13 @@@ struct proto_config char *name; char *dsc; int class; /* SYM_PROTO or SYM_TEMPLATE */ + u8 net_type; /* Protocol network type (NET_*), 0 for undefined */ + u8 disabled; /* Protocol enabled/disabled by default */ u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */ - unsigned preference, disabled; /* Generic parameters */ - int in_keep_filtered; /* Routes rejected in import filter are kept */ u32 router_id; /* Protocol specific router ID */ + + list channels; /* List of channel configs (struct channel_config) */ + struct iface *vrf; /* Related VRF instance, NULL if global */ - struct rtable_config *table; /* Table we're attached to */ - struct filter *in_filter, *out_filter; /* Attached filters */ - struct proto_limit *rx_limit; /* Limit for receiving routes from protocol - (relevant when in_keep_filtered is active) */ - struct proto_limit *in_limit; /* Limit for importing routes from protocol */ - struct proto_limit *out_limit; /* Limit for exporting routes to protocol */ /* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */ @@@ -137,11 -141,7 +138,12 @@@ struct proto struct proto_config *cf; /* Configuration data */ struct proto_config *cf_new; /* Configuration we want to switch to after shutdown (NULL=delete) */ pool *pool; /* Pool containing local objects */ - struct event *attn; /* "Pay attention" event */ + event *event; /* Protocol event */ + + list channels; /* List of channels to rtables (struct channel) */ + struct channel *main_channel; /* Primary channel */ + struct rte_src *main_source; /* Primary route source */ ++ struct iface *vrf; /* Related VRF instance, NULL if global */ char *name; /* Name of this instance (== cf->name) */ u32 debug; /* Debugging flags */ diff --cc proto/babel/packets.c index 4abcf7e4c,904218365..5b356fae7 --- a/proto/babel/packets.c +++ b/proto/babel/packets.c @@@ -1331,7 -1080,7 +1331,8 @@@ babel_open_socket(struct babel_iface *i sk->sport = ifa->cf->port; sk->dport = ifa->cf->port; sk->iface = ifa->iface; + sk->saddr = ifa->addr; + sk->vrf = p->p.vrf; sk->rx_hook = babel_rx_hook; sk->tx_hook = babel_tx_hook; diff --cc proto/ospf/iface.c index 29d21a07c,1795ec220..e3d8d61bd --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@@ -202,8 -200,8 +203,9 @@@ ospf_open_vlink_sk(struct ospf_proto *p { sock *sk = sk_new(p->p.pool); sk->type = SK_IP; + sk->subtype = ospf_is_v2(p) ? SK_IPV4 : SK_IPV6; sk->dport = OSPF_PROTO; + sk->vrf = p->p.vrf; /* FIXME: configurable tos/priority ? */ sk->tos = IP_PREC_INTERNET_CONTROL; diff --cc proto/radv/packets.c index e07296e10,8a3018548..7c148b7dc --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@@ -384,9 -386,9 +384,10 @@@ radv_sk_open(struct radv_iface *ifa { sock *sk = sk_new(ifa->pool); sk->type = SK_IP; + sk->subtype = SK_IPV6; sk->dport = ICMPV6_PROTO; sk->saddr = ifa->addr->ip; + sk->vrf = ifa->ra->p.vrf; sk->ttl = 255; /* Mandatory for Neighbor Discovery packets */ sk->rx_hook = radv_rx_hook; diff --cc proto/radv/radv.c index c96d77248,c53a0a952..e9140115a --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@@ -294,10 -306,21 +294,9 @@@ radv_iface_new(struct radv_proto *p, st add_tail(&p->iface_list, NODE ifa); - ifa->addr = find_lladdr(iface); - if (!ifa->addr) - { - log(L_ERR "%s: Missing link-local address on interface %s", p->p.name, iface->name); - return; - } - - timer *tm = tm_new(pool); - tm->hook = radv_timer; - tm->data = ifa; - tm->randomize = 0; - tm->recurrent = 0; - ifa->timer = tm; + ifa->timer = tm_new_init(pool, radv_timer, ifa, 0, 0); struct object_lock *lock = olock_new(pool); - lock->addr = IPA_NONE; lock->type = OBJLOCK_IP; lock->port = ICMPV6_PROTO; lock->iface = iface; diff --cc proto/rip/packets.c index 4925ca36b,722a9012d..891f454f0 --- a/proto/rip/packets.c +++ b/proto/rip/packets.c @@@ -746,8 -739,16 +746,9 @@@ rip_open_socket(struct rip_iface *ifa sk->sport = ifa->cf->port; sk->dport = ifa->cf->port; sk->iface = ifa->iface; + sk->saddr = rip_is_v2(p) ? ifa->iface->addr4->ip : ifa->iface->llv6->ip; + sk->vrf = p->p.vrf; - /* - * For RIPv2, we explicitly choose a primary address, mainly to ensure that - * RIP and BFD uses the same one. For RIPng, we left it to kernel, which - * should choose some link-local address based on the same scope rule. - */ - if (rip_is_v2(p)) - sk->saddr = ifa->iface->addr->ip; - sk->rx_hook = rip_rx_hook; sk->tx_hook = rip_tx_hook; sk->err_hook = rip_err_hook; diff --cc sysdep/linux/netlink.c index 279f3c9c8,4802897b0..05c1fa8ca --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@@ -1091,14 -842,27 +1098,34 @@@ kif_do_scan(struct kif_proto *p UNUSED else log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type); + /* Re-resolve master interface for slaves */ + struct iface *i; + WALK_LIST(i, iface_list) + if (i->master_index) + { + struct iface f = { + .flags = i->flags, + .mtu = i->mtu, + .index = i->index, + .master_index = i->master_index, + .master = if_find_by_index(i->master_index) + }; + + if (f.master != i->master) + { + memcpy(f.name, i->name, sizeof(f.name)); + if_update(&f); + } + } + - nl_request_dump(BIRD_AF, RTM_GETADDR); + nl_request_dump(AF_INET, RTM_GETADDR); + while (h = nl_get_scan()) + if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) + nl_parse_addr(h, 1); + else + log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type); + + nl_request_dump(AF_INET6, RTM_GETADDR); while (h = nl_get_scan()) if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) nl_parse_addr(h, 1);