static int
bgp_open(struct bgp_proto *p)
{
+ /* Interface-patterned listening sockets are created from the
+ * interface notifier. By default, listen to nothing. */
+ if (p->cf->ipatt)
+ return 0;
+
/* We assume that cf->iface is defined iff cf->local_ip is link-local */
struct bgp_listen_request *req = mb_allocz(p->p.pool, sizeof *req);
req->params = (struct bgp_socket_params) {
return bgp_listen_open(p, req);
}
+#define bgp_listen_debug(p, a, msg, args...) do { \
+ if ((p)->p.debug & D_IFACES) \
+ log(L_TRACE "%s: Listening socket at %I%J port %u (vrf %s) flags %u: " msg, \
+ (p)->p.name, (a)->addr, (a)->iface, (a)->port, \
+ (a)->vrf ? (a)->vrf->name : "default", (a)->flags, ## args); \
+} while (0)
+
static int
bgp_socket_match(const struct bgp_socket_params *a, const struct bgp_socket_params *b)
{
WALK_LIST(bs, bgp_sockets)
if (bgp_socket_match(&bs->params, &req->params))
{
+ bgp_listen_debug(p, &req->params, "exists: %p", bs);
add_tail(&p->listen, &req->pn);
add_tail(&bs->requests, &req->sn);
req->sock = bs;
add_tail(&p->listen, &req->pn);
add_tail(&bgp_sockets, &bs->n);
+ bgp_listen_debug(p, &req->params, "create: %p", bs);
+
return 0;
err:
rem_node(&req->pn);
rem_node(&req->sn);
if (!EMPTY_LIST(bs->requests))
+ {
+ bgp_listen_debug(p, &req->params, "unlink: %p", bs);
return;
+ }
+ bgp_listen_debug(p, &req->params, "free: %p", bs);
rfree(bs->sk);
rem_node(&bs->n);
mb_free(bs);
bgp_down(p);
}
-static struct bgp_proto *
-bgp_spawn(struct bgp_proto *pp, ip_addr remote_ip)
+static int
+bgp_spawn(struct bgp_proto *pp, sock *sk)
{
struct symbol *sym;
char fmt[SYM_MAX_LEN];
cfg_mem = NULL;
/* Just pass remote_ip to bgp_init() */
- ((struct bgp_config *) sym->proto)->remote_ip = remote_ip;
+ struct bgp_config *cf = SKIP_BACK(struct bgp_config, c, sym->proto);
+ cf->remote_ip = sk->daddr;
+ cf->iface = sk->iface;
+
+ struct bgp_proto *p = SKIP_BACK(struct bgp_proto, p, proto_spawn(sym->proto, 0));
+ p->postponed_sk = sk;
+ rmove(sk, p->p.pool);
- return (void *) proto_spawn(sym->proto, 0);
+ return 0;
}
void
/* For dynamic BGP, spawn new instance and postpone the socket */
if (bgp_is_dynamic(p))
- {
- p = bgp_spawn(p, sk->daddr);
- p->postponed_sk = sk;
- rmove(sk, p->p.pool);
- return 0;
- }
+ return bgp_spawn(p, sk);
rmove(sk, p->p.pool);
bgp_setup_conn(p, &p->incoming_conn);
bgp_initiate(p);
}
+static void
+bgp_iface_update(struct bgp_proto *p, uint flags, struct iface *i)
+{
+ int ps = p->p.proto_state;
+
+ ASSERT_DIE(p->cf->ipatt);
+ ASSERT_DIE(p->cf->strict_bind);
+
+ if ((ps == PS_DOWN) || (ps == PS_STOP))
+ return;
+
+ if (!iface_patt_match(p->cf->ipatt, i, NULL))
+ return;
+
+ struct bgp_socket_params params = {
+ .iface = i,
+ .vrf = p->p.vrf,
+ .addr = p->cf->local_ip,
+ .port = p->cf->local_port,
+ .flags = p->cf->free_bind ? SKF_FREEBIND : 0,
+ };
+
+ if (flags & IF_CHANGE_UP)
+ {
+ struct bgp_listen_request *req = mb_allocz(p->p.pool, sizeof *req);
+ req->params = params;
+ bgp_listen_open(p, req);
+ }
+
+ if (flags & IF_CHANGE_DOWN)
+ {
+ struct bgp_listen_request *req; node *nxt;
+ WALK_LIST2(req, nxt, p->listen, pn)
+ if (bgp_socket_match(&req->params, ¶ms))
+ {
+ bgp_listen_close(p, req);
+ mb_free(req);
+ break;
+ }
+ }
+}
+
+static void
+bgp_if_notify(struct proto *P, uint flags, struct iface *i)
+{
+ struct bgp_proto *p = (struct bgp_proto *) P;
+ ASSERT_DIE(ipa_zero(p->cf->local_ip));
+ bgp_iface_update(p, flags, i);
+}
+
+static void
+bgp_ifa_notify(struct proto *P, uint flags, struct ifa *i)
+{
+ struct bgp_proto *p = (struct bgp_proto *) P;
+ ASSERT_DIE(!ipa_zero(p->cf->local_ip));
+
+ if (ipa_equal(i->ip, p->cf->local_ip))
+ bgp_iface_update(p, flags, i->iface);
+}
+
static void
bgp_neigh_notify(neighbor *n)
{
p->remote_ip = cf->remote_ip;
p->remote_as = cf->remote_as;
+ P->if_notify = (cf->ipatt && ipa_zero(cf->local_ip)) ? bgp_if_notify : NULL;
+ P->ifa_notify = (cf->ipatt && !ipa_zero(cf->local_ip)) ? bgp_ifa_notify : NULL;
+
/* Hack: We use cf->remote_ip just to pass remote_ip from bgp_spawn() */
if (cf->c.parent)
cf->remote_ip = IPA_NONE;
if (ipa_zero(cf->remote_ip) && !cf->remote_range)
cf_error("Neighbor must be configured");
- if (ipa_zero(cf->local_ip) && cf->strict_bind)
- cf_error("Local address must be configured for strict bind");
+ if (ipa_zero(cf->local_ip) && !cf->ipatt && !cf->iface && cf->strict_bind)
+ cf_error("Local address or an interface must be configured for strict bind");
if (!cf->remote_as && !cf->peer_type)
cf_error("Remote AS number (or peer type) must be set");
ipa_is_link_local(cf->remote_ip)))
cf_error("Link-local addresses require defined interface");
+ if (cf->iface && cf->ipatt)
+ cf_error("Interface and interface range cannot be configured together");
+
+ if (cf->ipatt && !cf->strict_bind)
+ cf_error("Interface range needs strict bind");
+
if (!(cf->capabilities && cf->enable_as4) && (cf->remote_as > 0xFFFF))
cf_error("Neighbor AS number out of range (AS4 not available)");
ipa_is_link_local(cf->remote_ip)))
cf_error("Multihop BGP cannot be used with link-local addresses");
- if (cf->multihop && cf->iface)
+ if (cf->multihop && (cf->iface || cf->ipatt))
cf_error("Multihop BGP cannot be bound to interface");
if (cf->multihop && cf->check_link)
if (cf->multihop && cf->onlink)
cf_error("Multihop BGP cannot be configured onlink");
- if (cf->onlink && !cf->iface)
- cf_error("Onlink BGP must have interface configured");
+ if (cf->onlink && !cf->iface && !cf->ipatt &&
+ !cf->passive && !ipa_zero(cf->remote_ip))
+ cf_error("Active onlink BGP must have interface configured");
if (!cf->gr_mode && cf->llgr_mode)
cf_error("Long-lived graceful restart requires basic graceful restart");