static void bgp_send_open(struct bgp_conn *conn);
static void bgp_update_bfd(struct bgp_proto *p, const struct bfd_options *bfd);
+ static int bgp_disable_ao_keys(struct bgp_proto *p);
static int bgp_incoming_connection(sock *sk, uint dummy UNUSED);
static void bgp_listen_sock_err(sock *sk UNUSED, int err);
+static void bgp_initiate_disable(struct bgp_proto *p, int err_val);
-/**
- * bgp_open - open a BGP instance
- * @p: BGP instance
- *
- * This function allocates and configures shared BGP resources, mainly listening
- * sockets. Should be called as the last step during initialization (when lock
- * is acquired and neighbor is ready). When error, caller should change state to
- * PS_DOWN and return immediately.
- */
-static int
-bgp_open(struct bgp_proto *p)
-{
- struct bgp_socket *bs = NULL;
- struct iface *ifa = p->cf->strict_bind ? p->cf->iface : NULL;
- ip_addr addr = p->cf->strict_bind ? p->cf->local_ip :
- (p->ipv4 ? IPA_NONE4 : IPA_NONE6);
- uint port = p->cf->local_port;
- uint flags = p->cf->free_bind ? SKF_FREEBIND : 0;
- uint flag_mask = SKF_FREEBIND;
-
- /* We assume that cf->iface is defined iff cf->local_ip is link-local */
-
- WALK_LIST(bs, bgp_sockets)
- if (ipa_equal(bs->sk->saddr, addr) &&
- (bs->sk->sport == port) &&
- (bs->sk->iface == ifa) &&
- (bs->sk->vrf == p->p.vrf) &&
- ((bs->sk->flags & flag_mask) == flags))
- {
- bs->uc++;
- p->sock = bs;
- return 0;
- }
-
- sock *sk = sk_new(proto_pool);
- sk->type = SK_TCP_PASSIVE;
- sk->ttl = 255;
- sk->saddr = addr;
- sk->sport = port;
- sk->iface = ifa;
- sk->vrf = p->p.vrf;
- sk->flags = flags;
- sk->tos = IP_PREC_INTERNET_CONTROL;
- sk->rbsize = BGP_RX_BUFFER_SIZE;
- sk->tbsize = BGP_TX_BUFFER_SIZE;
- sk->rx_hook = bgp_incoming_connection;
- sk->err_hook = bgp_listen_sock_err;
-
- if (sk_open(sk) < 0)
- goto err;
-
- bs = mb_allocz(proto_pool, sizeof(struct bgp_socket));
- bs->sk = sk;
- bs->uc = 1;
- p->sock = bs;
- sk->data = bs;
-
- add_tail(&bgp_sockets, &bs->n);
-
- 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;
-}
-
-/**
- * bgp_close - close a BGP instance
- * @p: BGP instance
- *
- * This function frees and deconfigures shared BGP resources.
- */
-static void
-bgp_close(struct bgp_proto *p)
-{
- struct bgp_socket *bs = p->sock;
-
- ASSERT(bs && bs->uc);
-
- if (--bs->uc)
- return;
-
- rfree(bs->sk);
- rem_node(&bs->n);
- mb_free(bs);
-}
+static void bgp_graceful_restart_feed(struct bgp_channel *c);
+ /*
+ * TCP-AO keys
+ */
+
+ static struct bgp_ao_key *
+ bgp_new_ao_key(struct bgp_proto *p, struct ao_config *cf)
+ {
+ struct bgp_ao_key *key = mb_allocz(p->p.pool, sizeof(struct bgp_ao_key));
+
+ key->key = cf->key;
+ add_tail(&p->ao.keys, &key->n);
+
+ return key;
+ }
+
+ static struct bgp_ao_key *
+ bgp_find_ao_key_(list *l, int send_id, int recv_id)
+ {
+ WALK_LIST_(struct bgp_ao_key, key, *l)
+ if ((key->key.send_id == send_id) && (key->key.recv_id == recv_id))
+ return key;
+
+ return NULL;
+ }
+
+ static inline struct bgp_ao_key *
+ bgp_find_ao_key(struct bgp_proto *p, int send_id, int recv_id)
+ { return bgp_find_ao_key_(&p->ao.keys, send_id, recv_id); }
+
+ static int
+ bgp_same_ao_key(struct ao_key *a, struct ao_key *b)
+ {
+ return
+ (a->send_id == b->send_id) &&
+ (a->recv_id == b->recv_id) &&
+ (a->algorithm == b->algorithm) &&
+ (a->keylen == b->keylen) &&
+ !memcmp(a->key, b->key, a->keylen);
+ }
+
static inline int
- ((sk == p->sock->sk) ? "listening" : "session"));
+ bgp_sk_add_ao_key(struct bgp_proto *p, sock *sk, struct bgp_ao_key *key)
+ {
+ ip_addr prefix = p->cf->remote_ip;
+ int pxlen = -1;
+
+ int rv = sk_add_ao_key(sk, prefix, pxlen, p->cf->iface, &key->key, false, false);
+ if (rv < 0)
+ {
+ sk_log_error(sk, p->p.name);
+ log(L_ERR "%s: Cannot add TCP-AO key %d/%d to BGP %s socket",
+ p->p.name, key->key.send_id, key->key.recv_id,
- if (bgp_sk_add_ao_key(p, p->sock->sk, key) < 0)
++ ((sk == p->listen.sock->sk) ? "listening" : "session"));
+ }
+
+ return rv;
+ }
+
+ static int
+ bgp_enable_ao_key(struct bgp_proto *p, struct bgp_ao_key *key)
+ {
+ ASSERT(!key->active);
+
+ BGP_TRACE(D_EVENTS, "Adding TCP-AO key %d/%d", key->key.send_id, key->key.recv_id);
+
+ /* Handle listening socket */
- ((sk == p->sock->sk) ? "listening" : "session"));
++ if (bgp_sk_add_ao_key(p, p->listen.sock->sk, key) < 0)
+ {
+ key->failed = 1;
+ return -1;
+ }
+
+ key->active = 1;
+
+ /* Handle incoming socket */
+ if (p->incoming_conn.sk)
+ if (bgp_sk_add_ao_key(p, p->incoming_conn.sk, key) < 0)
+ return -1;
+
+ /* Handle outgoing socket */
+ if (p->outgoing_conn.sk)
+ if (bgp_sk_add_ao_key(p, p->outgoing_conn.sk, key) < 0)
+ return -1;
+
+ return 0;
+ }
+
+ struct bgp_active_keys {
+ int in_current, in_rnext;
+ int out_current, out_rnext;
+ struct bgp_ao_key *backup;
+ };
+
+ static int
+ bgp_sk_delete_ao_key(struct bgp_proto *p, sock *sk, struct bgp_ao_key *key,
+ struct bgp_ao_key *backup, int current_key_id, int rnext_key_id)
+ {
+ struct ao_key *set_current = NULL, *set_rnext = NULL;
+
+ if ((key->key.send_id == current_key_id) && backup)
+ {
+ log(L_WARN "%s: Deleting TCP-AO Current key %d/%d, setting Current key to %d/%d",
+ p->p.name, key->key.send_id, key->key.recv_id, backup->key.send_id, backup->key.recv_id);
+
+ set_current = &backup->key;
+ }
+
+ if ((key->key.recv_id == rnext_key_id) && backup)
+ {
+ log(L_WARN "%s: Deleting TCP-AO RNext key %d/%d, setting RNext key to %d/%d",
+ p->p.name, key->key.send_id, key->key.recv_id, backup->key.send_id, backup->key.recv_id);
+
+ set_rnext = &backup->key;
+ }
+
+ ip_addr prefix = p->cf->remote_ip;
+ int pxlen = -1;
+
+ int rv = sk_delete_ao_key(sk, prefix, pxlen, p->cf->iface, &key->key, set_current, set_rnext);
+ if (rv < 0)
+ {
+ sk_log_error(sk, p->p.name);
+ log(L_ERR "%s: Cannot delete TCP-AO key %d/%d from BGP %s socket",
+ p->p.name, key->key.send_id, key->key.recv_id,
- if (bgp_sk_delete_ao_key(p, p->sock->sk, key, NULL, -1, -1) < 0)
++ ((sk == p->listen.sock->sk) ? "listening" : "session"));
+ }
+
+ return rv;
+ }
+
+ static int
+ bgp_disable_ao_key(struct bgp_proto *p, struct bgp_ao_key *key, struct bgp_active_keys *info)
+ {
+ ASSERT(key->active);
+
+ BGP_TRACE(D_EVENTS, "Deleting TCP-AO key %d/%d", key->key.send_id, key->key.recv_id);
+
+ /* Handle listening socket */
- if (p->start_state == BSS_PREPARE)
++ if (bgp_sk_delete_ao_key(p, p->listen.sock->sk, key, NULL, -1, -1) < 0)
+ return -1;
+
+ key->active = 0;
+
+ /* Handle incoming socket */
+ if (p->incoming_conn.sk && info)
+ if (bgp_sk_delete_ao_key(p, p->incoming_conn.sk, key, info->backup, info->in_current, info->in_rnext) < 0)
+ return -1;
+
+ /* Handle outgoing socket */
+ if (p->outgoing_conn.sk && info)
+ if (bgp_sk_delete_ao_key(p, p->outgoing_conn.sk, key, info->backup, info->out_current, info->out_rnext) < 0)
+ return -1;
+
+ return 0;
+ }
+
+ static int
+ bgp_remove_ao_key(struct bgp_proto *p, struct bgp_ao_key *key, struct bgp_active_keys *info)
+ {
+ ASSERT(key != p->ao.best_key);
+
+ if (key->active)
+ if (bgp_disable_ao_key(p, key, info) < 0)
+ return -1;
+
+ rem_node(&key->n);
+ mb_free(key);
+ return 0;
+ }
+
+
+ static struct bgp_ao_key *
+ bgp_select_best_ao_key(struct bgp_proto *p)
+ {
+ struct bgp_ao_key *best = NULL;
+
+ WALK_LIST_(struct bgp_ao_key, key, p->ao.keys)
+ {
+ if (!key->active)
+ continue;
+
+ /* Never select deprecated keys */
+ if (key->key.preference < 0)
+ continue;
+
+ if (!best || (best->key.preference < key->key.preference))
+ best = key;
+ }
+
+ return best;
+ }
+
+ static int
+ bgp_sk_set_rnext_ao_key(struct bgp_proto *p, sock *sk, struct bgp_ao_key *key)
+ {
+ int rv = sk_set_rnext_ao_key(sk, &key->key);
+ if (rv < 0)
+ {
+ sk_log_error(sk, p->p.name);
+ log(L_ERR "%s: Cannot set TCP-AO key %d/%d as RNext key",
+ p->p.name, key->key.send_id, key->key.recv_id);
+ }
+
+ return rv;
+ }
+
+ static int
+ bgp_update_rnext_ao_key(struct bgp_proto *p)
+ {
+ struct bgp_ao_key *best = bgp_select_best_ao_key(p);
+
+ if (!best)
+ {
+ log(L_ERR "%s: No usable TCP-AO key", p->p.name);
+ return -1;
+ }
+
+ if (best == p->ao.best_key)
+ return 0;
+
+ BGP_TRACE(D_EVENTS, "Setting TCP-AO key %d/%d as RNext key", best->key.send_id, best->key.recv_id);
+
+ p->ao.best_key = best;
+
+ /* Handle incoming socket */
+ if (p->incoming_conn.sk)
+ if (bgp_sk_set_rnext_ao_key(p, p->incoming_conn.sk, best) < 0)
+ return -1;
+
+ /* Handle outgoing socket */
+ if (p->outgoing_conn.sk)
+ if (bgp_sk_set_rnext_ao_key(p, p->outgoing_conn.sk, best) < 0)
+ return -1;
+
+ /* Schedule Keepalive to trigger RNext ID exchange */
+ if (p->conn)
+ bgp_schedule_packet(p->conn, NULL, PKT_KEEPALIVE);
+
+ /* RFC 4271 4.4 says that Keepalive messages MUST NOT be sent more frequently
+ than one per second, but since key change is rare, this is harmless. */
+
+ return 0;
+ }
+
+
+ /**
+ * bgp_enable_ao_keys - Enable TCP-AO keys
+ * @p: BGP instance
+ *
+ * Enable all TCP-AO keys for the listening socket. We accept if some fail in
+ * non-fatal way (e.g. kernel does not support specific algorithm), but there
+ * must be at least one usable (non-deprecated) active key. In case of failure,
+ * we remove all keys, so there is no lasting effect on the listening socket.
+ * Returns: 0 for okay, -1 for failure.
+ */
+ static int
+ bgp_enable_ao_keys(struct bgp_proto *p)
+ {
+ ASSERT(!p->incoming_conn.sk && !p->outgoing_conn.sk);
+
+ WALK_LIST_(struct bgp_ao_key, key, p->ao.keys)
+ if (bgp_enable_ao_key(p, key) < 0)
+ goto fail;
+
+ p->ao.best_key = bgp_select_best_ao_key(p);
+
+ if (!p->ao.best_key)
+ {
+ log(L_ERR "%s: No usable TCP-AO key", p->p.name);
+ goto fail;
+ }
+
+ return 0;
+
+ fail:
+ bgp_disable_ao_keys(p);
+ return -1;
+ }
+
+ /**
+ * bgp_disable_ao_keys - Disable TCP-AO keys
+ * @p: BGP instance
+ *
+ * Disable all TCP-AO keys for the listening socket. We assume there are no
+ * active connection, so no issue with removal of the current key. Errors are
+ * ignored.
+ */
+ static int
+ bgp_disable_ao_keys(struct bgp_proto *p)
+ {
+ ASSERT(!p->incoming_conn.sk && !p->outgoing_conn.sk);
+
+ WALK_LIST_(struct bgp_ao_key, key, p->ao.keys)
+ if (key->active)
+ bgp_disable_ao_key(p, key, NULL);
+
+ return 0;
+ }
+
+ static int
+ bgp_reconfigure_ao_keys(struct bgp_proto *p, const struct bgp_config *cf)
+ {
+ /* TCP-AO not used */
+ if (EMPTY_LIST(p->ao.keys) && !cf->ao_keys)
+ return 1;
+
+ /* Cannot enable/disable TCP-AO */
+ if (EMPTY_LIST(p->ao.keys) || !cf->ao_keys)
+ return 0;
+
+ /* Too early, TCP-AO not yet enabled */
++ if (bgp_start_state(p) == BSS_PREPARE)
+ return 0;
+
+ /* Move existing keys to temporary list */
+ list old_keys;
+ init_list(&old_keys);
+ add_tail_list(&old_keys, &p->ao.keys);
+ init_list(&p->ao.keys);
+
+ /* Clean up the best key */
+ struct bgp_ao_key *old_best = p->ao.best_key;
+ p->ao.best_key = NULL;
+
+ /* Prepare new set of keys */
+ for (struct ao_config *key_cf = cf->ao_keys; key_cf; key_cf = key_cf->next)
+ {
+ struct bgp_ao_key *key = bgp_find_ao_key_(&old_keys, key_cf->key.send_id, key_cf->key.recv_id);
+
+ if (key && bgp_same_ao_key(&key->key, &key_cf->key))
+ {
+ /* Update key ptr and preference */
+ key->key = key_cf->key;
+
+ rem_node(&key->n);
+ add_tail(&p->ao.keys, &key->n);
+
+ if (key == old_best)
+ p->ao.best_key = key;
+
+ continue;
+ }
+
+ bgp_new_ao_key(p, key_cf);
+ }
+
+ /* Remove old keys */
+ if (!EMPTY_LIST(old_keys))
+ {
+ struct bgp_active_keys info = { -1, -1, -1, -1, NULL};
+
+ /* Find current/rnext keys on incoming connection */
+ if (p->incoming_conn.sk)
+ if (sk_get_active_ao_keys(p->incoming_conn.sk, &info.in_current, &info.in_rnext) < -1)
+ sk_log_error(p->incoming_conn.sk, p->p.name);
+
+ /* Find current/rnext keys on outgoing connection */
+ if (p->outgoing_conn.sk)
+ if (sk_get_active_ao_keys(p->outgoing_conn.sk, &info.out_current, &info.out_rnext) < -1)
+ sk_log_error(p->outgoing_conn.sk, p->p.name);
+
+ /*
+ * Select backup key in case of removal of current/rnext key.
+ *
+ * It is possible that we cannot select an intermediate best key (e.g. when
+ * the reconfiguration deprecates the old best key and adds the new one).
+ * That is not necessary bad, we may not even need the backup key anyways.
+ * In this case we use the old best key (ao.best_key) instead even if it may
+ * be deprecated (but not removed).
+ *
+ * If neither one is available, that means we are going to remove rnext key
+ * and we have no intermediate best key to switch to, therefore we fail
+ * later during bgp_remove_ao_key().
+ */
+ info.backup = bgp_select_best_ao_key(p) ?: p->ao.best_key;
+ if (!info.backup)
+ log(L_WARN "%s: No usable backup key", p->p.name);
+
+ struct bgp_ao_key *key, *key2;
+ WALK_LIST_DELSAFE(key, key2, old_keys)
+ bgp_remove_ao_key(p, key, &info);
+
+ /* If some key removals failed */
+ if (!EMPTY_LIST(old_keys))
+ return 0;
+ }
+
+ /* Enable new keys */
+ WALK_LIST_(struct bgp_ao_key, key, p->ao.keys)
+ if (!key->active && !key->failed)
+ if (bgp_enable_ao_key(p, key) < 0)
+ return 0;
+
+ /* Update RNext key */
+ if (bgp_update_rnext_ao_key(p) < 0)
+ return 0;
+
+ return 1;
+ }
+
+ /**
+ * bgp_list_ao_keys - List active TCP-AO keys
+ * @p: BGP instance
+ * @ao_keys: Returns array of keys
+ * @ao_keys_num: Returns number of keys
+ *
+ * Returns an array of pointers to active TCP-AO keys, for usage with socket
+ * functions. The best key is at the first position. The array is allocated from
+ * the temporary linpool. If there are no keys (or just no best key), the error
+ * is logged and the function fails. Returns: 0 for success, -1 for failure.
+ */
+ static int
+ bgp_list_ao_keys(struct bgp_proto *p, const struct ao_key ***ao_keys, int *ao_keys_num)
+ {
+ int num = 0;
+ WALK_LIST_(const struct bgp_ao_key, key, p->ao.keys)
+ if (key->active)
+ num++;
+
+ const struct bgp_ao_key *best = p->ao.best_key;
+
+ if (!num || !best)
+ {
+ log(L_ERR "%s: No usable TCP-AO key", p->p.name);
+ return -1;
+ }
+
+ const struct ao_key **keys = tmp_alloc(num * sizeof(const struct ao_key *));
+ int i = 0;
+
+ keys[i++] = &best->key;
+ WALK_LIST_(const struct bgp_ao_key, key, p->ao.keys)
+ if (key->active && (key != best) && (i < num))
+ keys[i++] = &key->key;
+
+ *ao_keys = keys;
+ *ao_keys_num = i;
+ return 0;
+ }
+
+
+
+
+ static int
bgp_setup_auth(struct bgp_proto *p, int enable)
{
- /* Beware. This is done from main_birdloop and protocol birdloop is NOT ENTERED.
- * Anyway, we are only accessing:
- * - protocol config which can be changed only from main_birdloop (reconfig)
- * - protocol listen socket which is always driven by main_birdloop
- * - protocol name which is set on reconfig
- */
+ if (p->cf->auth_type == BGP_AUTH_AO)
+ {
+ if (enable)
+ return bgp_enable_ao_keys(p);
+ else
+ return bgp_disable_ao_keys(p);
+ }
- if (p->cf->password && p->listen.sock)
+ if (p->cf->auth_type == BGP_AUTH_MD5)
{
ip_addr prefix = p->cf->remote_ip;
int pxlen = -1;
return rv;
}
- else
- return 0;
+
+ return 0;
}
- bgp_initiate_disable(p, BEM_INVALID_MD5);
++/*
++ * BGP Instance Management
++ */
++
+/**
+ * bgp_close - close a BGP instance
+ * @p: BGP instance
+ *
+ * This function frees and deconfigures shared BGP resources.
+ */
+static void
+bgp_close(struct bgp_proto *p)
+{
+ LOCK_DOMAIN(rtable, bgp_listen_domain);
+
+ struct bgp_listen_request *req = &p->listen;
+ struct bgp_socket *bs = req->sock;
+
+ if (enlisted(&req->n))
+ {
+ /* Remove listen request from listen socket or pending list */
+ rem_node(&req->n);
+
+ if (bs)
+ {
+ /* Already had a socket. */
+ req->sock = NULL;
+
+ /* Request listen socket cleanup */
+ if (bs && EMPTY_LIST(bs->requests))
+ ev_send(&global_event_list, &bgp_listen_event);
+ }
+ }
+
+ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
+}
+
+/**
+ * bgp_open - open a BGP instance
+ * @p: BGP instance
+ *
+ * This function allocates and configures shared BGP resources, mainly listening
+ * sockets. Should be called as the last step during initialization (when lock
+ * is acquired and neighbor is ready). When error, caller should change state to
+ * PS_DOWN and return immediately.
+ */
+static void
+bgp_open(struct bgp_proto *p)
+{
+ LOCK_DOMAIN(rtable, bgp_listen_domain);
+
+ struct bgp_listen_request *req = &p->listen;
+ /* We assume that cf->iface is defined iff cf->local_ip is link-local */
+ req->iface = p->cf->strict_bind ? p->cf->iface : NULL;
+ req->vrf = p->p.vrf;
+ req->addr = p->cf->strict_bind ? p->cf->local_ip :
+ (p->ipv4 ? IPA_NONE4 : IPA_NONE6);
+ req->port = p->cf->local_port;
+ req->flags = p->cf->free_bind ? SKF_FREEBIND : 0;
+
+ BGP_TRACE(D_EVENTS, "Requesting listen socket at %I%J port %u", req->addr, req->iface, req->port);
+
+ add_tail(&bgp_listen_pending, &req->n);
+ ev_send(&global_event_list, &bgp_listen_event);
+
+ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
+}
+
+static void
+bgp_listen_create(void *_ UNUSED)
+{
+ ASSERT_DIE(birdloop_inside(&main_birdloop));
+ uint flag_mask = SKF_FREEBIND;
+
+ while (1) {
+ LOCK_DOMAIN(rtable, bgp_listen_domain);
+
+ if (EMPTY_LIST(bgp_listen_pending))
+ {
+ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
+ break;
+ }
+
+ /* Get the first request to match */
+ struct bgp_listen_request *req = HEAD(bgp_listen_pending);
+ SKIP_BACK_DECLARE(struct bgp_proto, p, listen, req);
+ rem_node(&req->n);
+
+ /* First try to find existing socket */
+ struct bgp_socket *bs;
+ WALK_LIST(bs, bgp_sockets)
+ if (ipa_equal(bs->sk->saddr, req->addr) &&
+ (bs->sk->sport == req->port) &&
+ (bs->sk->iface == req->iface) &&
+ (bs->sk->vrf == req->vrf) &&
+ ((bs->sk->flags & flag_mask) == req->flags))
+ break;
+
+ /* Not found any */
+ if (NODE_VALID(bs))
+ BGP_TRACE(D_EVENTS, "Found a listening socket: %p", bs);
+ else
+ {
+ /* Allocating new socket from global protocol pool.
+ * We can do this in main_birdloop. */
+ sock *sk = sk_new(bgp_listen_pool);
+ sk->type = SK_TCP_PASSIVE;
+ sk->ttl = 255;
+ sk->saddr = req->addr;
+ sk->sport = req->port;
+ sk->iface = req->iface;
+ sk->vrf = req->vrf;
+ sk->flags = req->flags;
+ sk->tos = IP_PREC_INTERNET_CONTROL;
+ sk->rbsize = BGP_RX_BUFFER_SIZE;
+ sk->tbsize = BGP_TX_BUFFER_SIZE;
+ sk->rx_hook = bgp_incoming_connection;
+ sk->err_hook = bgp_listen_sock_err;
+
+ if (sk_open(sk, &main_birdloop) < 0)
+ {
+ sk_log_error(sk, p->p.name);
+ log(L_ERR "%s: Cannot open listening socket", p->p.name);
+ sk_close(sk);
+ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
+
+ bgp_initiate_disable(p, BEM_NO_SOCKET);
+ continue;
+ }
+
+ bs = mb_allocz(bgp_listen_pool, sizeof(struct bgp_socket));
+ bs->sk = sk;
+ sk->data = bs;
+
+ init_list(&bs->requests);
+ add_tail(&bgp_sockets, &bs->n);
+
+ BGP_TRACE(D_EVENTS, "Created new listening socket: %p", bs);
+ }
+
+ req->sock = bs;
+ add_tail(&bs->requests, &req->n);
+
+ if (bgp_setup_auth(p, 1) < 0)
+ {
+ rem_node(&req->n);
+ req->sock = NULL;
+
+ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
+
++ bgp_initiate_disable(p, BEM_INVALID_AUTH);
+ continue;
+ }
+
+ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
+ }
+
+ /* Cleanup leftover listening sockets */
+ LOCK_DOMAIN(rtable, bgp_listen_domain);
+ struct bgp_socket *bs;
+ node *nxt;
+ WALK_LIST_DELSAFE(bs, nxt, bgp_sockets)
+ if (EMPTY_LIST(bs->requests))
+ {
+ sk_close(bs->sk);
+ rem_node(&bs->n);
+ mb_free(bs);
+ }
+ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
+}
+
static inline struct bgp_channel *
bgp_find_channel(struct bgp_proto *p, u32 afi)
{
bgp_setup_sk(conn, s);
bgp_conn_set_state(conn, BS_CONNECT);
- if (sk_open(s) < 0)
+ if (p->cf->auth_type == BGP_AUTH_MD5)
+ s->password = p->cf->password;
+
+ if (p->cf->auth_type == BGP_AUTH_AO)
+ if (bgp_list_ao_keys(p, &s->ao_keys_init, &s->ao_keys_num) < 0)
+ goto err2;
+
+ if (sk_open(s, p->p.loop) < 0)
goto err;
/* Set minimal receive TTL if needed */
if (sk_set_min_ttl(s, 256 - hops) < 0)
goto err;
+ s->ao_keys_num = 0;
+ s->ao_keys_init = NULL;
+
DBG("BGP: Waiting for connect success\n");
- bgp_start_timer(conn->connect_timer, p->cf->connect_retry_time);
+ bgp_start_timer(p, conn->connect_timer, p->cf->connect_retry_time);
return;
err:
return 0;
}
- rfree(sk);
- return 0;
+ birdloop_enter(p->p.loop);
+
+ if (!EMPTY_LIST(p->ao.keys))
+ {
+ int current = -1, rnext = -1;
+ sk_get_active_ao_keys(sk, ¤t, &rnext);
+
+ if (current < 0)
+ {
+ log(L_WARN "%s: Connection from address %I%J (port %d) has no TCP-AO key",
+ p->p.name, sk->daddr, ipa_is_link_local(sk->daddr) ? sk->iface : NULL, sk->dport);
++ sk_close(sk);
++ goto leave;
+ }
+ }
+
/*
* BIRD should keep multiple incoming connections in OpenSent state (for
* details RFC 4271 8.2.1 par 3), but it keeps just one. Duplicate incoming
err:
sk_log_error(sk, p->p.name);
+ err2:
log(L_ERR "%s: Incoming connection aborted", p->p.name);
- rfree(sk);
+ sk_close(sk);
+
+leave:
+ UNLOCK_DOMAIN(rtable, bgp_listen_domain);
+
+ /* We need to announce possible state changes immediately before
+ * leaving the protocol's loop, otherwise we're gonna access the protocol
+ * without having it locked from proto_announce_state_later(). */
+ proto_announce_state(&p->p, p->p.ea_state);
+ birdloop_leave(p->p.loop);
return 0;
}
p->remote_id = 0;
p->link_addr = IPA_NONE;
- proto_setup_mpls_map(P, RTS_BGP, 1);
+ /* Initialize TCP-AO keys */
+ init_list(&p->ao.keys);
+ if (cf->auth_type == BGP_AUTH_AO)
+ for (struct ao_config *key_cf = cf->ao_keys; key_cf; key_cf = key_cf->next)
+ bgp_new_ao_key(p, key_cf);
+
+ ea_list *eal = p->p.ea_state;
+ ea_set_attr(&eal, EA_LITERAL_EMBEDDED(&ea_bgp_rem_id, 0, p->remote_id));
+ ea_set_attr(&eal, EA_LITERAL_EMBEDDED(&ea_bgp_loc_as, 0, p->local_as));
+ ea_set_attr(&eal, EA_LITERAL_EMBEDDED(&ea_bgp_rem_as, 0, p->remote_as));
+ ea_set_attr(&eal, EA_LITERAL_STORE_ADATA(&ea_bgp_rem_ip, 0, &p->remote_ip, sizeof(ip_addr)));
+
+ ea_set_attr(&eal, EA_LITERAL_EMBEDDED(&ea_bgp_out_conn_state, 0, BS_IDLE));
+ ea_set_attr(&eal, EA_LITERAL_EMBEDDED(&ea_bgp_in_conn_state, 0, BS_IDLE));
+
+ proto_announce_state(&p->p, eal);
/* Lock all channels when in GR recovery mode */
if (p->p.gr_recovery && p->cf->gr_mode)
tm_remains(p->conn->hold_timer), p->conn->hold_time);
cli_msg(-1006, " Keepalive timer: %t/%u",
tm_remains(p->conn->keepalive_timer), p->conn->keepalive_time);
+ cli_msg(-1006, " TX pending: %d bytes",
+ p->conn->sk->tpos - p->conn->sk->ttx);
cli_msg(-1006, " Send hold timer: %t/%u",
tm_remains(p->conn->send_hold_timer), p->conn->send_hold_time);
- }
+
+ if (!EMPTY_LIST(p->ao.keys))
+ {
+ struct ao_info info;
+ sk_get_ao_info(p->conn->sk, &info);
+
+ cli_msg(-1006, " TCP-AO:");
+ cli_msg(-1006, " Current key: %i", info.current_key);
+ cli_msg(-1006, " RNext key: %i", info.rnext_key);
+ cli_msg(-1006, " Good packets: %lu", info.pkt_good);
+ cli_msg(-1006, " Bad packets: %lu", info.pkt_bad);
+ }
+ }
#if 0
struct bgp_stats *s = &p->stats;