From: Maria Matejka Date: Tue, 1 Apr 2025 13:25:30 +0000 (+0200) Subject: Merge commit 'origin/master^' into thread-next X-Git-Tag: v3.1.0~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ca2fb2e4ad5f3ace76a081556cd9cf8ee2a36996;p=thirdparty%2Fbird.git Merge commit 'origin/master^' into thread-next The AO socket dumping is bonkers but we'll fix it later. --- ca2fb2e4ad5f3ace76a081556cd9cf8ee2a36996 diff --cc lib/socket.h index 013f8c3ca,91a4f12cf..f46324a4b --- a/lib/socket.h +++ b/lib/socket.h @@@ -77,10 -102,12 +103,13 @@@ typedef struct birdsock int rcv_ttl; /* TTL of last received datagram */ node n; void *rbuf_alloc, *tbuf_alloc; - const char *password; /* Password for MD5 authentication */ + const char *password; /* Password for MD5 authentication (for SK_TCP_ACTIVE) */ + const struct ao_key **ao_keys_init; /* Keys for TCP-AO authentication (for SK_TCP_ACTIVE) */ + int ao_keys_num; /* Number of keys in ao_keys_init */ + // int use_ao; /* This is the only reliable flag saying whether the socket uses TCP-AO */ const char *err; /* Error message */ struct ssh_sock *ssh; /* Used in SK_SSH */ + struct birdloop *loop; /* BIRDLoop owning this socket */ } sock; sock *sock_new(pool *); /* Allocate new socket */ diff --cc proto/bgp/bgp.c index adb6d3d1d,f5008f6cf..1a977d2b3 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@@ -148,24 -141,555 +149,469 @@@ static void bgp_setup_sk(struct bgp_con 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 + 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, - ((sk == p->sock->sk) ? "listening" : "session")); ++ ((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 */ - if (bgp_sk_add_ao_key(p, p->sock->sk, key) < 0) ++ 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, - ((sk == p->sock->sk) ? "listening" : "session")); ++ ((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 (bgp_sk_delete_ao_key(p, p->sock->sk, key, NULL, -1, -1) < 0) ++ 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 (p->start_state == BSS_PREPARE) ++ 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; @@@ -185,177 -709,10 +631,181 @@@ return rv; } - else - return 0; + + return 0; } ++/* ++ * 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_MD5); ++ 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) { @@@ -1357,7 -1610,14 +1806,14 @@@ bgp_connect(struct bgp_proto *p) /* Ent bgp_setup_sk(conn, s); bgp_conn_set_state(conn, BS_CONNECT); + 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) < 0) + if (sk_open(s, p->p.loop) < 0) goto err; /* Set minimal receive TTL if needed */ @@@ -1365,8 -1625,11 +1821,11 @@@ 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: @@@ -1449,8 -1702,20 +1909,22 @@@ bgp_incoming_connection(sock *sk, uint 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); - rfree(sk); - return 0; ++ 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 @@@ -1525,17 -1796,9 +2014,18 @@@ 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; } @@@ -1855,16 -2102,13 +2345,22 @@@ bgp_start(struct proto *P p->remote_id = 0; p->link_addr = IPA_NONE; + /* 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); + - proto_setup_mpls_map(P, RTS_BGP, 1); + 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) @@@ -2981,11 -3206,21 +3495,23 @@@ bgp_show_proto_info(struct proto *P 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; diff --cc proto/bgp/bgp.h index 083d75426,515ef2754..39a2fb55c --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@@ -288,10 -295,22 +295,22 @@@ struct bgp_caps for (ac = caps->af_data; ac < &caps->af_data[caps->af_count]; ac++) + struct bgp_ao_key { + node n; /* Node in bgp_ao_state.keys */ + struct ao_key key; + u32 active:1; + u32 failed:1; + }; + + struct bgp_ao_state { + list keys; /* List of TCP-AO keys (struct bgp_ao_key) */ + struct bgp_ao_key *best_key; + }; + struct bgp_socket { node n; /* Node in global bgp_sockets */ + list requests; /* Listen requests */ sock *sk; /* Real listening socket */ - u32 uc; /* Use count */ }; struct bgp_stats { @@@ -388,11 -383,10 +407,12 @@@ struct bgp_proto struct bgp_conn incoming_conn; /* Incoming connection we have neither accepted nor rejected yet */ struct object_lock *lock; /* Lock for neighbor connection */ struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */ - struct bgp_socket *sock; /* Shared listening socket */ - struct bfd_request *bfd_req; /* BFD request, if BFD is used */ + struct bgp_listen_request listen; /* Shared listening socket */ + struct bfd_request_ref *bfd_req; /* BFD request, if BFD is used */ + callback bfd_notify; /* BFD notification callback */ struct birdsock *postponed_sk; /* Postponed incoming socket for dynamic BGP */ + struct bgp_ao_state ao; + struct rt_uncork_callback uncork; /* Uncork hook */ struct bgp_stats stats; /* BGP statistics */ btime last_established; /* Last time of enter/leave of established state */ btime last_rx_update; /* Last time of RX update */ diff --cc proto/bgp/config.Y index 17906d75a,7cc02d52a..af3529b5a --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@@ -29,14 -33,14 +32,17 @@@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, H GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY, STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG, LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS, - DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE, + DYNAMIC, RANGE, NAME, DIGITS, AIGP, ORIGINATE, COST, ENFORCE, FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER, - RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND, TX, SIZE, WARNING, - MIN, MAX, FORMAT, NATIVE, SINGLE, DOUBLE) + RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND, RECV, MIN, MAX, ++ TX, SIZE, WARNING, + AUTHENTICATION, NONE, MD5, AO, FORMAT, NATIVE, SINGLE, DOUBLE) + + CF_KEYWORDS(KEY, KEYS, SECRET, DEPRECATED, PREFERRED, ALGORITHM, CMAC, AES128) +CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE) + - %type bgp_nh bgp_llnh + %type bgp_nh bgp_llnh bgp_auth_type bgp_role_name tcp_ao_algorithm %type bgp_afi CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER, diff --cc sysdep/unix/io.c index f9785c074,cc422ee1c..5b9c2e0e6 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@@ -40,9 -38,9 +40,10 @@@ #include "lib/resource.h" #include "lib/socket.h" #include "lib/event.h" +#include "lib/locking.h" #include "lib/timer.h" #include "lib/string.h" + #include "lib/mac.h" #include "nest/iface.h" #include "nest/cli.h" #include "conf/conf.h" @@@ -2395,6 -2242,6 +2407,10 @@@ sk_err(sock *s, int revents tmp_flush(); } ++ ++/* FIXME: these two functions should actually call bird_thread_sync_all() ++ * to get threads from all loops. Now they dump just mainloop. */ ++ void sk_dump_all(struct dump_request *dreq) { @@@ -2413,6 -2258,25 +2429,25 @@@ RDUMP("\n"); } + void + sk_dump_ao_all(struct dump_request *dreq) + { - RDUMP("TCP-AO sockets:\n"); - WALK_LIST_(node, n, sock_list) ++ RDUMP("TCP-AO listening sockets:\n"); ++ WALK_LIST_(node, n, main_birdloop.sock_list) + { + sock *s = SKIP_BACK(sock, n, n); + + /* Skip non TCP-AO sockets / not supported */ + if (sk_get_ao_info(s, &(struct ao_info){}) < 0) + continue; + + RDUMP("\n%p", s); + sk_dump(dreq, &s->r); + sk_dump_ao_info(s, dreq); + sk_dump_ao_keys(s, dreq); + } + } + /* * Internal event log and watchdog