static int
bgp_open(struct bgp_proto *p)
{
- /* We assume that cf->iface is defined iff cf->local_ip is link-local */
+ /* Interface-patterned listening sockets are created from the
+ * interface notifier. By default, listen to nothing. */
+ if (p->cf->ipatt)
+ return 0;
+
+ /* Set parameters of the listening socket
+ *
+ * If strict_bind is set, we need local_ip and maybe also iface. Mandatory if
+ * local_ip is link-local. If strict_bind is not set, we bind to all addresses
+ * of that family and match the local IP later when accepting the connection. */
+
struct bgp_listen_request *req = mb_allocz(p->p.pool, sizeof *req);
- req->params = (struct bgp_socket_params) {
- .iface = p->cf->strict_bind ? p->cf->iface : NULL,
- .vrf = p->p.vrf,
- .addr = p->cf->strict_bind ? p->cf->local_ip :
- (p->ipv4 ? IPA_NONE4 : IPA_NONE6),
- .port = p->cf->local_port,
- .flags = p->cf->free_bind ? SKF_FREEBIND : 0,
- };
+ struct bgp_socket_params *par = &req->params;
+
+ par->iface = p->cf->strict_bind ? p->cf->iface : NULL;
+ par->vrf = p->p.vrf;
+ par->addr = p->cf->strict_bind ? p->cf->local_ip :
+ (p->ipv4 ? IPA_NONE4 : IPA_NONE6);
+ par->port = p->cf->local_port;
+ par->flags = p->cf->free_bind ? SKF_FREEBIND : 0;
+
+ /* Set parameters of the accepted socket */
+ req->local_ip = p->cf->local_ip;
+ req->iface = p->cf->iface;
+ req->remote_ip = p->remote_ip;
+ req->remote_range = p->cf->remote_range;
+
+ /* Initialize the incoming socket queue */
+ init_list(&req->incoming_sockets);
+
+ BGP_TRACE(D_EVENTS, "Requesting listen socket at %I%J port %u", req->params.addr, req->params.iface, req->params.port);
+
+ if (bgp_is_dynamic(p))
+ callback_init(&req->incoming_connection, bgp_incoming_connection_dynamic, &main_birdloop);
+ else
+ callback_init(&req->incoming_connection, bgp_incoming_connection_single, p->p.loop);
+ req->p = p;
return bgp_listen_open(p, req);
}
sk->data = bs;
req->sock = bs;
- init_list(&bs->requests);
- add_tail(&bs->requests, &req->sn);
+ init_list(&bsp->requests);
+ add_tail(&bsp->requests, &req->sn);
+ add_tail(&bl->sockets, &bs->n);
- add_tail(&p->listen, &req->pn);
- add_tail(&bgp_sockets, &bs->n);
+ bgp_listen_debug(p, &req->params, "create: %p", bs);
+ birdloop_leave(loop);
+ bgp_listen_debug(p, &req->params, "create: %p", bs);
+
return 0;
+}
-err:
- sk_log_error(sk, p->p.name);
- log(L_ERR "%s: Cannot open listening socket", p->p.name);
- rfree(sk);
- return -1;
+static void
+bgp_listen_done(void *_bs)
+{
+ /* Loop finishing must be done from the main loop */
+ ASSERT_DIE(birdloop_inside(&main_birdloop));
+ BGP_LISTEN_LOCK(bl);
+
+ /* The loop finisher must at least once enter the loop to ensure
+ * that the requestor is done */
+ BGP_SOCKET_LOCKED((bgp_socket *) _bs, bsp)
+ ;
+
+ /* Everything related is allocated from the loop's pool */
+ birdloop_free(((bgp_socket *) _bs)->loop);
}
static void
cfg_mem = NULL;
/* Just pass remote_ip to bgp_init() */
- ((struct bgp_config *) sym->proto)->remote_ip = sk->daddr;
+ 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));
+ /* Create the protocol disabled initially */
+ SKIP_BACK_DECLARE(struct bgp_proto, p, p, proto_spawn(sym->proto, 1));
+
+ /* Pass the socket */
p->postponed_sk = sk;
- rmove(sk, p->p.pool);
- return 0;
+ /* And enable the protocol */
+ proto_enable(&p->p);
}
void
bgp_initiate(p);
}
- if ((ps == PS_DOWN) || (ps == PS_STOP))
+ 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_XX) || (ps == PS_FLUSH) || (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;
+ p->postponed_sk = NULL;
+
++ P->iface_sub.if_notify = (cf->ipatt && ipa_zero(cf->local_ip)) ? bgp_if_notify : NULL;
++ P->iface_sub.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;