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)
| 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:
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);
}
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,
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;
}
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
unsigned flags;
unsigned mtu;
unsigned index; /* OS-dependent interface index */
- list addrs; /* Addresses assigned to this interface */
- struct ifa *addr; /* Primary address */
+ unsigned master_index; /* Interface index of master iface */
+ 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 */
};
#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"
}
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;
}
/* If there is a too big change in core attributes, ... */
if ((nc->protocol != oc->protocol) ||
- (nc->disabled != p->disabled))
+ (nc->net_type != oc->net_type) ||
- (nc->vrf != oc->vrf) ||
- (nc->table->table != oc->table->table))
+ (nc->disabled != p->disabled) ||
++ (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);
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
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 */
- 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 */
+
+ list channels; /* List of channel configs (struct channel_config) */
+ struct iface *vrf; /* Related VRF instance, NULL if global */
/* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
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 */
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;
{
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;
{
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;
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;
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;
else
log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type);
- nl_request_dump(BIRD_AF, RTM_GETADDR);
+ /* 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(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);