From 94a41cb6a04b0bc628e934071562b7a2036b60df Mon Sep 17 00:00:00 2001 From: Katerina Kubecova Date: Thu, 7 Mar 2024 12:25:58 +0100 Subject: [PATCH] bgp.c - tcp ao rewrited so ao_key is basic and bgp_ao_key and config_ao uses ao_key. bgp_ao_key keeps living info, config_ao just make it linked list --- lib/socket.h | 25 +++- proto/bgp/bgp.c | 258 ++++++++++++++++++++++++------------------ proto/bgp/bgp.h | 6 +- proto/bgp/config.Y | 109 +++++++++--------- proto/bgp/packets.c | 42 ++++--- sysdep/linux/sysio.h | 111 +++++++++--------- sysdep/linux/tcp-ao.h | 9 ++ sysdep/unix/io.c | 15 +-- 8 files changed, 331 insertions(+), 244 deletions(-) diff --git a/lib/socket.h b/lib/socket.h index 98398ca5..51d20f86 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -43,7 +43,20 @@ struct ao_key const char *cipher; const char *master_key; int required; - struct ao_key *next_key; +}; + +struct ao_config +{ + struct ao_key key; + struct ao_config *next_key; +}; + +struct bgp_ao_key { + struct ao_key key; + int activ_alive; /* this ao key is in activ socket */ + int passiv_alive; /* this ao key is in passiv socket */ + int to_delete; /* flag for reconfig */ + struct bgp_ao_key *next_key; }; typedef struct birdsock { @@ -87,8 +100,9 @@ typedef struct birdsock { node n; void *rbuf_alloc, *tbuf_alloc; const char *password; /* Password for MD5 authentication */ - struct ao_key *ao_key_init; /* Key for tcp ao authentication icialization. */ - char use_ao; + struct bgp_ao_key *ao_key_init; /* Key for tcp ao authentication icialization. */ + struct bgp_proto *proto_del_ao_key; /* For deletion of the currently used deprecated ao key */ + char use_ao; /* This is the only reliable flag saying if the socket use ao or not */ int last_used_ao_key; /* Last ID the other site requested */ int desired_ao_key; /* ID of requested ao key */ const char *err; /* Error message */ @@ -125,10 +139,11 @@ int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct if int get_current_key_id(int sock_fd); int get_rnext_key_id(int sock_fd); int sk_set_ao_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int passwd_id_loc, int passwd_id_rem, const char *cipher, int set_current); -void ao_delete_key(sock *s, ip_addr remote, int pxlen, struct iface *ifa, int passwd_id_rem, int passwd_id_loc); +int ao_delete_key(sock *s, ip_addr remote, int pxlen, struct iface *ifa, int passwd_id_rem, int passwd_id_loc); void log_tcp_ao_info(int sock_fd); void log_tcp_ao_get_key(int sock_fd); -int check_ao_keys_id(int sock_fd, struct ao_key *key); +void tcp_ao_get_info(int sock_fd, int key_info[4]); +int check_ao_keys_id(int sock_fd, struct bgp_ao_key *key); void ao_try_change_master(sock *s, int next_key_id_loc, int next_id_rem); int sk_set_ipv6_checksum(sock *s, int offset); int sk_set_icmp6_filter(sock *s, int p1, int p2); diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 05c2cdf9..2d1ea8d3 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -191,8 +191,7 @@ bgp_open(struct bgp_proto *p) if (sk_open(sk) < 0) goto err; - - log("____________________________________________________________________________%i %i", sk->fd, sk->ao_key_init); + bs = mb_allocz(proto_pool, sizeof(struct bgp_socket)); bs->sk = sk; bs->uc = 1; @@ -249,24 +248,24 @@ bgp_setup_auth(struct bgp_proto *p, int enable) { if (enable) { - log("set ao auth [%s]", p->cf->ao_key->master_key); - struct ao_key *key = p->cf->ao_key; + struct bgp_ao_key *key = p->ao_key; do { rv = sk_set_ao_auth(p->sock->sk, p->cf->local_ip, prefix, pxlen, p->cf->iface, - key->master_key, key->local_id, key->remote_id, key->cipher, 0); + key->key.master_key, key->key.local_id, key->key.remote_id, key->key.cipher, 0); + if (rv == 0) + key->passiv_alive = 1; key = key->next_key; } while(key); } else { - log("in bgp close"); - struct ao_key *key = p->cf->ao_key; + struct bgp_ao_key *key = p->ao_key; while (key) { - log("delete %i", key->local_id); - ao_delete_key(p->sock->sk, p->remote_ip, -1, p->sock->sk->iface, key->local_id, key->remote_id); + if (key->passiv_alive) + ao_delete_key(p->sock->sk, p->remote_ip, -1, p->sock->sk->iface, key->key.local_id, key->key.remote_id); key = key->next_key; } } @@ -1126,14 +1125,6 @@ bgp_active(struct bgp_proto *p) bgp_start_timer(conn->connect_timer, delay); } -void -log_ao(int fd) -{ - //log("the two ao logs"); - log_tcp_ao_info(fd); - log_tcp_ao_get_key(fd); -} - /** * bgp_connect - initiate an outgoing connection * @p: BGP instance @@ -1161,7 +1152,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c s->tbsize = p->cf->enable_extended_messages ? BGP_TX_BUFFER_EXT_SIZE : BGP_TX_BUFFER_SIZE; s->tos = IP_PREC_INTERNET_CONTROL; s->password = p->cf->password; - s->ao_key_init = p->cf->ao_key; + s->ao_key_init = p->ao_key; s->tx_hook = bgp_connected; s->flags = p->cf->free_bind ? SKF_FREEBIND : 0; BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J", @@ -1174,8 +1165,6 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c if (sk_open(s) < 0) goto err; - log("a..........................................................................................fd %i %I", s->fd, s->daddr); - /* Set minimal receive TTL if needed */ if (p->cf->ttl_security) if (sk_set_min_ttl(s, 256 - hops) < 0) @@ -1305,10 +1294,21 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED) if (sk_set_min_ttl(sk, 256 - hops) < 0) goto err; - if (p->cf->ao_key) + if (p->ao_key) { - if (check_ao_keys_id(sk->fd, p->cf->ao_key) == 0) + if (check_ao_keys_id(sk->fd, p->ao_key) == 0) + { sk->use_ao = 1; + for (struct bgp_ao_key *key = p->ao_key; key; key = key->next_key) + { + key->activ_alive = key->passiv_alive; + if (key->key.required == 1) + { + sk->desired_ao_key = key->key.remote_id; + ao_try_change_master(sk, key->key.local_id, key->key.remote_id); + } + } + } } if (p->cf->enable_extended_messages) { @@ -1779,6 +1779,20 @@ bgp_init(struct proto_config *CF) p->remote_ip = cf->remote_ip; p->remote_as = cf->remote_as; + if (cf->ao_key) + { + struct ao_config *cf_key = cf->ao_key; + do { + struct bgp_ao_key *key = mb_alloc(proto_pool, sizeof(struct bgp_ao_key)); + key->key = cf_key->key; + key->activ_alive = 0; + key->passiv_alive = 0; + key->next_key = p->ao_key; + p->ao_key = key; + cf_key = cf_key->next_key; + } while (cf_key); + } + /* Hack: We use cf->remote_ip just to pass remote_ip from bgp_spawn() */ if (cf->c.parent) cf->remote_ip = IPA_NONE; @@ -2191,85 +2205,37 @@ int compare_aos(struct ao_key *a, struct ao_key *b) return strcmp(a->master_key, b->master_key); } -int reconfigure_ao_without_conn(struct bgp_proto old_proto, struct bgp_config new) +int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new) { - log("in reconf dead ao"); + if (old_proto->cf->ao_key == NULL && new.ao_key == NULL) + return 1; // tcp ao not used + if (old_proto->cf->ao_key == NULL || new.ao_key == NULL) + return 0; // connection is changing from ao to no ao or no ao to ao - sock *s_passiv = old_proto.sock->sk; - - struct ao_key *old_aos[256]; - memset(&old_aos, 0, sizeof(struct ao_key*)*256); - struct ao_key *old_aos_rem[256]; - memset(&old_aos_rem, 0, sizeof(struct ao_key*)*256); - - for(struct ao_key *ao_key = old_proto.cf->ao_key; ao_key; ao_key = ao_key->next_key) + if (!old_proto->conn) { - old_aos[ao_key->local_id] = ao_key; - old_aos_rem[ao_key->remote_id] = ao_key; + log("tcp ao: reconfigure nonestablished connection"); + return 0; // Connection was not (re)established, so we can not change it. } - - for(struct ao_key *ao_key = new.ao_key; ao_key; ao_key = ao_key->next_key) - { - if(old_aos[ao_key->local_id]) - { - if(compare_aos(ao_key, old_aos[ao_key->local_id])) - { - struct ao_key *old_key = old_aos[ao_key->local_id]; - ao_delete_key(s_passiv, old_proto.remote_ip, -1, s_passiv->iface, old_key->local_id, old_key->remote_id); - sk_set_ao_auth(s_passiv, old_proto.local_ip, old_proto.remote_ip, -1, s_passiv->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, ao_key->required == 1); - } - old_aos[ao_key->local_id] = 0; - } - else if(old_aos_rem[ao_key->remote_id]) - { - struct ao_key *old_key = old_aos_rem[ao_key->remote_id]; - ao_delete_key(s_passiv, old_proto.remote_ip, -1, s_passiv->iface, old_key->local_id, old_key->remote_id); - sk_set_ao_auth(s_passiv, old_proto.local_ip, old_proto.remote_ip, -1, s_passiv->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, ao_key->required == 1); - old_aos[old_key->local_id] = 0; - } - else - sk_set_ao_auth(s_passiv, old_proto.local_ip, old_proto.remote_ip, -1, s_passiv->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, ao_key->required == 1); - } - for(int i = 0; i<256; i++) - { - if (old_aos[i]) - ao_delete_key(s_passiv, old_proto.remote_ip, -1, s_passiv->iface, old_aos[i]->local_id, old_aos[i]->remote_id); - } - - return 1; -} - -int reconfigure_tcp_ao(struct bgp_proto old_proto, struct bgp_config new) -{ - log("in reconf ao"); - if (!old_proto.conn) - return reconfigure_ao_without_conn(old_proto, new); // Connection was not (re)established, so we can not change it. - sock *s_passiv = old_proto.sock->sk; - sock *s_activ = old_proto.conn->sk; + sock *s_passiv = old_proto->sock->sk; + sock *s_activ = old_proto->conn->sk; int key_in_use_rem = get_current_key_id(s_activ->fd); if (key_in_use_rem == -1) - { - log("Unable to detect currently used key"); + { + log(L_WARN "TCP AO: Unable to detect currently used key"); return 0; - } - int rnext_id = get_rnext_key_id(s_activ->fd); - log("old rnext %i", rnext_id); - - struct ao_key *old_aos[256]; - memset(&old_aos, 0, sizeof(struct ao_key*)*256); - struct ao_key *old_rem_id[256]; - memset(&old_rem_id, 0, sizeof(struct ao_key*)*256); - - for(struct ao_key *ao_key = old_proto.cf->ao_key; ao_key; ao_key = ao_key->next_key) - { - old_aos[ao_key->local_id] = ao_key; - old_rem_id[ao_key->remote_id] = ao_key; } - int key_in_use = old_rem_id[key_in_use_rem]->local_id; - for(struct ao_key *ao_key = new.ao_key; ao_key; ao_key = ao_key->next_key) + + for (struct bgp_ao_key *ao_key = old_proto->ao_key; ao_key; ao_key = ao_key->next_key) + ao_key->to_delete = 1; + + struct bgp_ao_key *first = old_proto->ao_key; + for (struct ao_config *cf_ao = new.ao_key; cf_ao; cf_ao = cf_ao->next_key) { + if (cf_ao->key.required == -1 && cf_ao->key.remote_id != key_in_use_rem) + continue; if(old_aos[ao_key->local_id]) { if(compare_aos(ao_key, old_aos[ao_key->local_id])) @@ -2299,28 +2265,92 @@ int reconfigure_tcp_ao(struct bgp_proto old_proto, struct bgp_config new) sk_set_ao_auth(s_passiv, old_proto.local_ip, old_proto.remote_ip, -1, s_passiv->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, 0); } - if (ao_key->required == 1 && (ao_key->local_id != rnext_id)) + struct bgp_ao_key *found = NULL; + for (struct bgp_ao_key *old_ao = first; old_ao && !found; old_ao = old_ao->next_key) { + if (old_ao->key.local_id == cf_ao->key.local_id || old_ao->key.remote_id == cf_ao->key.remote_id) + { + if (compare_aos(&old_ao->key, &cf_ao->key)) + return 0; + if (old_ao->activ_alive == 0 && cf_ao->key.required >= 0) + { + if (sk_set_ao_auth(s_activ, old_proto->local_ip, old_proto->remote_ip, -1, s_activ->iface, old_ao->key.master_key, old_ao->key.local_id, old_ao->key.remote_id, old_ao->key.cipher, 0)) + return 0; + old_ao->activ_alive = 1; + } + if (old_ao->passiv_alive == 0 && cf_ao->key.required >= 0) + { + if (sk_set_ao_auth(s_passiv, old_proto->local_ip, old_proto->remote_ip, -1, s_passiv->iface, old_ao->key.master_key, old_ao->key.local_id, old_ao->key.remote_id, old_ao->key.cipher, 0)) + return 0; + old_ao->passiv_alive = 1; + } + + if (cf_ao->key.required == 1 && old_ao->key.required != 1) + { + s_activ->desired_ao_key = old_ao->key.remote_id; + s_passiv->desired_ao_key = old_ao->key.remote_id; + ao_try_change_master(s_activ, old_ao->key.local_id, old_ao->key.remote_id); + if (old_proto->conn->hold_timer->expires != 0) + bgp_schedule_packet(old_proto->conn, NULL, PKT_KEEPALIVE); // We might send this keepalive shortly after another. RFC says we should wait, but since reconfiguration is rare, this is harmless. + } + old_ao->key = cf_ao->key; + old_ao->to_delete = 0; + found = old_ao; + } + } + if (!found) { - ao_try_change_master(s_activ, ao_key->local_id, ao_key->remote_id); - if (old_proto.conn->hold_timer->expires != 0) - bgp_schedule_packet(old_proto.conn, NULL, PKT_KEEPALIVE); // According to RFC we should not send keepalive shortly after another, but since reconfiguration is rare, this is harmless + struct bgp_ao_key *key = mb_alloc(old_proto->p.pool, sizeof(struct bgp_ao_key)); + key->key = cf_ao->key; + key->activ_alive = 0; + key->passiv_alive = 0; + key->next_key = first; + old_proto->ao_key = key; + if (sk_set_ao_auth(s_passiv, old_proto->local_ip, old_proto->remote_ip, -1, s_passiv->iface, cf_ao->key.master_key, cf_ao->key.local_id, cf_ao->key.remote_id, cf_ao->key.cipher, 0)) + return 0; + key->passiv_alive = 1; + if (sk_set_ao_auth(s_activ, old_proto->local_ip, old_proto->remote_ip, -1, s_passiv->iface, cf_ao->key.master_key, cf_ao->key.local_id, cf_ao->key.remote_id, cf_ao->key.cipher, 0)) + return 0; + key->activ_alive = 1; + key->to_delete = 0; + found = key; + + if (found->key.required == 1) + { + s_activ->desired_ao_key = found->key.remote_id; + s_passiv->desired_ao_key = found->key.remote_id; + ao_try_change_master(s_activ, found->key.local_id, found->key.remote_id); + if (old_proto->conn->hold_timer->expires != 0) + bgp_schedule_packet(old_proto->conn, NULL, PKT_KEEPALIVE); // We might send this keepalive shortly after another. RFC says we should wait, but since reconfiguration is rare, this is harmless. + } } } - for(int i = 0; i<256; i++) - { - if (old_aos[i]) - { - if (i == key_in_use) - { - cf_warn("TCP AO reconfiguration: Currently used key (id %i) deletion. This is not allowed.", i); - return 0; - } - ao_delete_key(s_activ, old_proto.remote_ip, -1, s_activ->iface, old_aos[i]->local_id, old_aos[i]->remote_id); - ao_delete_key(s_passiv, old_proto.remote_ip, -1, s_passiv->iface, old_aos[i]->local_id, old_aos[i]->remote_id); - } - } - log("no big changes in ao"); + key_in_use_rem = get_current_key_id(s_activ->fd); + struct bgp_ao_key *previous = NULL; + for (struct bgp_ao_key *old_ao = old_proto->ao_key; old_ao; old_ao = old_ao->next_key) + { + if (old_ao->to_delete) + { + if (old_ao->key.remote_id == key_in_use_rem) + { + log(L_WARN "TCP AO: deleting currently used key"); + return 0; + } + if (ao_delete_key(s_activ, old_proto->remote_ip, -1, s_activ->iface, old_ao->key.local_id, old_ao->key.remote_id)) + return 0; + old_ao->activ_alive = 0; + + if (ao_delete_key(s_passiv, old_proto->remote_ip, -1, s_passiv->iface, old_ao->key.local_id, old_ao->key.remote_id)) + return 0; + old_ao->passiv_alive = 0; + if (previous) + previous->next_key = old_ao->next_key; + else + old_proto->ao_key = old_ao->next_key; + } + else + previous = old_ao; + } return 1; } @@ -2339,7 +2369,7 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF) // password item is last and must be checked separately OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config)) && !bstrcmp(old->password, new->password) - && reconfigure_tcp_ao(*p, *new) + && reconfigure_tcp_ao(p, *new) && ((!old->remote_range && !new->remote_range) || (old->remote_range && new->remote_range && net_equal(old->remote_range, new->remote_range))) && !bstrcmp(old->dynamic_name, new->dynamic_name) @@ -2780,6 +2810,16 @@ 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); + if (p->cf->ao_key) + { + int tmp[4]; + tcp_ao_get_info(p->conn->sk->fd, tmp); + cli_msg(-1006, " TCP AO:"); + cli_msg(-1006, " current key remote id %i", tmp[0]); + cli_msg(-1006, " rnext key local id %i", tmp[1]); + cli_msg(-1006, " good packets %i", tmp[2]); + cli_msg(-1006, " bad packets %i", tmp[3]); + } } #if 0 diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index b5a730c5..34360db7 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -134,8 +134,7 @@ struct bgp_config { u32 disable_after_cease; /* Disable it when cease is received, bitfield */ const char *password; /* Password used for MD5 authentication */ - struct ao_key *ao_key; /* Keys for tcp ao authentication TODO: copy to protocol? */ - struct linpool *ao_lp; /* Linpool for allocating ao keys */ + struct ao_config *ao_key; /* Keys for tcp ao authentication */ net_addr *remote_range; /* Allowed neighbor range for dynamic BGP */ const char *dynamic_name; /* Name pattern for dynamic BGP */ int dynamic_name_digits; /* Minimum number of digits for dynamic names */ @@ -343,6 +342,7 @@ struct bgp_proto { 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 bgp_ao_key *ao_key; /* Linked list for ao keys */ struct bfd_request *bfd_req; /* BFD request, if BFD is used */ struct birdsock *postponed_sk; /* Postponed incoming socket for dynamic BGP */ struct bgp_stats stats; /* BGP statistics */ @@ -541,8 +541,6 @@ bgp_parse_error(struct bgp_parse_state *s, uint subcode) longjmp(s->err_jmpbuf, 1); } -void log_ao(int fd); - void bgp_start_timer(timer *t, uint value); void bgp_check_config(struct bgp_config *c); diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index e0fc6aef..b2d924fe 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -234,76 +234,83 @@ bgp_afi: ; ao_keys: - KEY '{' ao_first_item ao_key '}' - | KEY '{' ao_first_item ao_key '}' ao_keys + KEY '{' ao_first_item ao_key '}' + | KEY '{' ao_first_item ao_key '}' ao_keys ; ao_key: - ao_item - | ao_item ao_key -; + ao_item + | ao_item ao_key + ; ao_first_item: - LOCAL ID expr ';' { - if($3 >= 256) - cf_error("Key ids ust be in range 0 - 255"); - if (!BGP_CFG->ao_lp) - BGP_CFG->ao_lp = lp_new(rp_new(&root_pool, "ao struct pool")); - struct ao_key *new_key = lp_alloc(BGP_CFG->ao_lp, sizeof(struct ao_key)); - new_key->next_key = BGP_CFG->ao_key; - BGP_CFG->ao_key = new_key; - BGP_CFG->ao_key->required = 0; - BGP_CFG->ao_key->local_id = $3; - BGP_CFG->ao_key->remote_id = -1; - } + LOCAL ID expr ';' { + if ($3 >= 256) + cf_error("Key ids ust be in range 0 - 255"); + struct ao_config *new_key = cfg_alloc(sizeof(struct ao_config)); + new_key->next_key = BGP_CFG->ao_key; + BGP_CFG->ao_key = new_key; + BGP_CFG->ao_key->key.required = 0; + BGP_CFG->ao_key->key.local_id = $3; + BGP_CFG->ao_key->key.remote_id = -1; + } ; ao_item: - REMOTE ID expr ';' { - if ($3 > 255) - cf_error("TCP AO: Key id must be in range 0 - 255"); - BGP_CFG->ao_key->remote_id = $3; } - | CIPHER text ';' { - if (strcmp($2, "cmac(aes128)") & strcmp($2, "hmac(sha1)") & strcmp($2, "hmac(sha224)") & strcmp($2, "hmac(sha256)") & strcmp($2, "hmac(sha384)") & strcmp($2, "hmac(sha512)")& strcmp($2, "hmac(md5)")) - cf_error("TCP AO: Here are ciphers 'cmac(aes128)', 'hmac(md5)', 'hmac(sha1)', 'hmac(sha224)', 'hmac(sha256)', 'hmac(sha384)' and 'hmac(sha512)' hardcoded. If there is another cipher available in kernel, please contact BIRD developers."); - char *c = lp_alloc(BGP_CFG->ao_lp, strlen($2)); - memcpy(c, $2, strlen($2)); - BGP_CFG->ao_key->cipher = c; - } - | MASTER KEY text ';' { - char *k = lp_alloc(BGP_CFG->ao_lp, strlen($3)); - memcpy(k, $3, strlen($3)); - BGP_CFG->ao_key->master_key = k; - } - | DEPRECATED ';' { BGP_CFG->ao_key->required = -1; } - | REQUIRED ';' { BGP_CFG->ao_key->required = 1; } + REMOTE ID expr ';' { + if ($3 > 255) + cf_error("TCP AO: Key id must be in range 0 - 255"); + BGP_CFG->ao_key->key.remote_id = $3; } + | CIPHER text ';' { + if (strcmp($2, "cmac(aes128)") & strcmp($2, "hmac(sha1)") & strcmp($2, "hmac(sha224)") & strcmp($2, "hmac(sha256)") & strcmp($2, "hmac(sha384)") & strcmp($2, "hmac(sha512)")& strcmp($2, "hmac(md5)")) + cf_error("TCP AO: Here are ciphers 'cmac(aes128)', 'hmac(md5)', 'hmac(sha1)', 'hmac(sha224)', 'hmac(sha256)', 'hmac(sha384)' and 'hmac(sha512)' hardcoded. If there is another cipher available in kernel, please contact BIRD developers."); + char *c = cfg_alloc(strlen($2)+1); + memcpy(c, $2, strlen($2)+1); + BGP_CFG->ao_key->key.cipher = c; + } + | MASTER KEY text ';' { + char *k = cfg_alloc(strlen($3)+1); + memcpy(k, $3, strlen($3)+1); + BGP_CFG->ao_key->key.master_key = k; + } + | DEPRECATED ';' { + if (BGP_CFG->ao_key->key.required == 0) + BGP_CFG->ao_key->key.required = -1; + else + cf_error("TCP AO: Key can be only once deprecated or once required, key id %i", BGP_CFG->ao_key->key.local_id); + } + | REQUIRED ';' { + if (BGP_CFG->ao_key->key.required == 0) + BGP_CFG->ao_key->key.required = 1; + else + cf_error("TCP AO: Key can be only once deprecated or once required, key id %i", BGP_CFG->ao_key->key.local_id); + } ; tcp_ao_end: -{ //TODO this is not all what we need to check - old current master same, cipher, key exist... +{ char used_aos_id_loc[256]; char used_aos_id_rem[256]; memset(used_aos_id_loc, 0, sizeof(char)*256); memset(used_aos_id_rem, 0, sizeof(char)*256); - int required_found = 0; - struct ao_key *key = BGP_CFG->ao_key; + struct ao_config *key = BGP_CFG->ao_key; while (key) { - if (used_aos_id_loc[key->local_id]) - cf_error("TCP AO: Reused local key id %i", key->local_id); - used_aos_id_loc[key->local_id] = 1; - if (key->remote_id == -1) - cf_error("TCP AO: No remote key id for local id %i", key->local_id); - if (used_aos_id_rem[key->remote_id]) - cf_error("TCP AO: Reused remote key id %i", key->remote_id); - used_aos_id_rem[key->remote_id] = 1; - if (!key->cipher) - cf_error("TCP AO: No cipher given for key id %i.", key->local_id); - if (!key->master_key) - cf_error("TCP AO: No master key given for key id %i.", key->local_id); - if (key->required == 1) + if (used_aos_id_loc[key->key.local_id]) + cf_error("TCP AO: Reused local key id %i", key->key.local_id); + used_aos_id_loc[key->key.local_id] = 1; + if (key->key.remote_id == -1) + cf_error("TCP AO: No remote key id for local id %i", key->key.local_id); + if (used_aos_id_rem[key->key.remote_id]) + cf_error("TCP AO: Reused remote key id %i", key->key.remote_id); + used_aos_id_rem[key->key.remote_id] = 1; + if (!key->key.cipher) + cf_error("TCP AO: No cipher given for key id %i.", key->key.local_id); + if (!key->key.master_key) + cf_error("TCP AO: No master key given for key id %i.", key->key.local_id); + if (key->key.required == 1) { if (required_found) cf_error("TCP AO: How do you want to use two keys at once? Check 'REQUIRED'"); diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 7bdadc63..360b6124 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -3410,6 +3410,24 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, uint len) } } +void +delete_deprecated_key(sock *sk, struct bgp_proto *p, int key_rem_id) +{ + struct bgp_ao_key *key = p->ao_key; + while (key->key.remote_id != key_rem_id) + { + key = key->next_key; + } + if (key->key.required != -1) + bug("TCP AO: unexpected key management error"); + if (ao_delete_key(sk, p->remote_ip, -1, sk->iface, key->key.local_id, key->key.remote_id)) + bug("TCP AO: Can not delete deprecated key %i %i on socket %i", key->key.local_id, key->key.remote_id, sk->fd); + key->activ_alive = 0; + if (ao_delete_key(p->sock->sk, p->remote_ip, -1, p->sock->sk->iface, key->key.local_id, key->key.remote_id)) + bug("TCP AO: Can not delete deprecated key %i %i on socket %i", key->key.local_id, key->key.remote_id, p->sock->sk->fd); + key->passiv_alive = 0; +} + /** * bgp_rx - handle received data * @sk: socket @@ -3424,29 +3442,25 @@ int bgp_rx(sock *sk, uint size) { struct bgp_conn *conn = sk->data; - if (sk->use_ao && sk->desired_ao_key != sk->last_used_ao_key) { - int new_rnext = get_current_key_id(sk->fd); + int new_rnext = get_current_key_id(sk->fd); if (new_rnext != sk->last_used_ao_key) { if (conn->hold_timer->expires != 0) bgp_schedule_packet(conn, NULL, PKT_KEEPALIVE); // We might send this keepalive shortly after another. RFC says we should wait, but since reconfiguration is rare, this is harmless. + log(L_INFO "TCP AO: Expected key rotation: desired rnext %i, received %i", sk->desired_ao_key, new_rnext); + log_tcp_ao_info(sk->fd); + + if (sk->proto_del_ao_key && sk->desired_ao_key == new_rnext) + { + delete_deprecated_key(sk, sk->proto_del_ao_key, sk->last_used_ao_key); + sk->proto_del_ao_key = NULL; + } sk->last_used_ao_key = new_rnext; - log("Expected desired rnext %i, arrived %i", sk->desired_ao_key, new_rnext); - log_ao(sk->fd); - } - else //todo delete after debug - { - log("Nothing happend %i %i", get_current_key_id(sk->fd), sk->last_used_ao_key); - log_ao(sk->fd); } } - else - { - log("No ao or not expecting changes %i %i", get_current_key_id(sk->fd), sk->last_used_ao_key); - log_ao(sk->fd); - } + byte *pkt_start = sk->rbuf; byte *end = pkt_start + size; uint i, len; diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index 8aa2f352..a6f0fe12 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -8,7 +8,6 @@ #include "sysdep/linux/tcp-ao.h" - #ifndef IPV6_MINHOPCOUNT #define IPV6_MINHOPCOUNT 73 #endif @@ -25,14 +24,6 @@ #define TCP_MD5SIG_FLAG_PREFIX 1 #endif -#ifndef TCP_AO_ADD_KEY -#define TCP_AO_ADD_KEY 38 /* Add/Set MKT */ -#define TCP_AO_DEL_KEY 39 /* Delete MKT */ -#define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */ -#define TCP_AO_GET_KEYS 41 /* List MKT(s) */ -#define TCP_AO_REPAIR 42 /* Get/Set SNEs and ISNs */ - -#endif /* We redefine the tcp_md5sig structure with different name to avoid collision with older headers */ struct tcp_md5sig_ext { @@ -176,11 +167,11 @@ sk_prepare_cmsgs4(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) /* * Miscellaneous Linux socket syscalls */ + int sk_set_md5_auth(sock *s, ip_addr local UNUSED, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey UNUSED) { struct tcp_md5sig_ext md5; - log("md5 password is %i, socket fd %i", passwd, s->fd); memset(&md5, 0, sizeof(md5)); sockaddr_fill((sockaddr *) &md5.tcpm_addr, s->af, remote, ifa, 0); @@ -198,7 +189,7 @@ sk_set_md5_auth(sock *s, ip_addr local UNUSED, ip_addr remote, int pxlen, struct if (pxlen < 0) { - if (setsockopt(s->fd, SOL_TCP, TCP_MD5SIG_EXT, &md5, sizeof(md5)) < 0) + if (setsockopt(s->fd, SOL_TCP, TCP_MD5SIG, &md5, sizeof(md5)) < 0) if (errno == ENOPROTOOPT) ERR_MSG("Kernel does not support TCP MD5 signatures"); else @@ -230,11 +221,11 @@ void log_tcp_ao_info(int sock_fd) if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len)) { - log("log tcp ao info failed with err code %i", errno); - return; + log(L_WARN "TCP AO: log tcp ao info failed with err code %i", errno); + return; } else - log("current key id %i (rem), next key %i (loc),\n set current %i, ao required %i\n good packets %i, bad packets %i", + log(L_INFO "TCP AO:\ncurrent key id %i (rem), next key %i (loc),\n set current %i, is ao required %i\n good packets %i, bad packets %i", tmp.current_key, tmp.rnext, tmp.set_current, tmp.ao_required, tmp.pkt_good, tmp.pkt_bad); } @@ -246,7 +237,7 @@ int get_current_key_id(int sock_fd) if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len)) { - log("get current ao key failed %i", errno); + log(L_WARN "TCP AO: Getting current ao key for socket file descriptor %i failed with errno %i", sock_fd, errno); return -1; } else @@ -261,8 +252,8 @@ int get_rnext_key_id(int sock_fd) if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len)) { - log("get rnext ao key failed %i", errno); - return -1; + log(L_WARN "TCP AO: Getting rnext ao key for socket file descriptor %i failed with errno %i", sock_fd, errno); + return -1; } else return tmp.rnext; @@ -278,7 +269,7 @@ int get_num_ao_keys(int sock_fd) if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_GET_KEYS, &tmp, &len)) { - log("tcp ao get keys failed with err code %i", errno); + log(L_WARN "TCP AO: get keys on socket fd %i failed with err code %i", sock_fd, errno); return -1; } return tmp.nkeys; @@ -286,7 +277,7 @@ int get_num_ao_keys(int sock_fd) void log_tcp_ao_get_key(int sock_fd) -{ +{ int nkeys = get_num_ao_keys(sock_fd); if (nkeys < 0) return; @@ -297,27 +288,41 @@ log_tcp_ao_get_key(int sock_fd) tm_all[0].get_all = 1; if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_GET_KEYS, tm_all, &len)) // len should be still size of one struct. Because kernel net/ipv4/tcp_ao.c line 2165 { - log("log tcp ao get keys failed with err code %i", errno); + log(L_WARN "TCP AO: getting keys on socket fd %i failed with err code %i", sock_fd, errno); return; } - log("keys %i %i", nkeys, tm_all[0].nkeys); + log(L_INFO "TCP AO on socket fd %i has %i keys", tm_all[0].nkeys); for (int i = 0; i < nkeys; i++) { - log("sndid %i rcvid %i, %s %s, cipher %s key %s (%i/%i)", tm_all[i].sndid, tm_all[i].rcvid, tm_all[i].is_current ? "current" : "", tm_all[i].is_rnext ? "rnext" : "", tm_all[i].alg_name, tm_all[i].key, i+1, tm_all[0].nkeys); + log(L_INFO "sndid %i rcvid %i, %s %s, cipher %s key %s (%i/%i)", tm_all[i].sndid, tm_all[i].rcvid, tm_all[i].is_current ? "current" : "", tm_all[i].is_rnext ? "rnext" : "", tm_all[i].alg_name, tm_all[i].key, i+1, tm_all[0].nkeys); } } +void +tcp_ao_get_info(int sock_fd, int key_info[4]) +{ + struct tcp_ao_info_opt_ext tmp; + memset(&tmp, 0, sizeof(struct tcp_ao_info_opt_ext)); + socklen_t len = sizeof(tmp); + + if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len)) + { + log(L_WARN "TCP AO: log tcp ao info failed with err code %i", errno); + return; + } + key_info[0] = tmp.current_key; + key_info[1] = tmp.rnext; + key_info[2] = tmp.pkt_good; + key_info[3] = tmp.pkt_bad; +} + int sk_set_ao_auth(sock *s, ip_addr local UNUSED, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int passwd_id_loc, int passwd_id_rem, const char* cipher, int set_current) { struct tcp_ao_add_ext ao; memset(&ao, 0, sizeof(struct tcp_ao_add_ext)); - log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>in sk set ao, pass %s fd %i sk %i %i", passwd, s->fd, s); - /* int af; - if (ipa_is_ip4(remote)) - af = AF_INET; - else - a = AF_INET6;*/ + log(L_DEBUG "tcp ao: socket sets ao, password %s socket fd %i", passwd, s->fd); + sockaddr_fill((sockaddr *) &ao.addr, s->af, remote, ifa, 0); if (set_current) { @@ -337,18 +342,16 @@ sk_set_ao_auth(sock *s, ip_addr local UNUSED, ip_addr remote, int pxlen, struct ao.ifindex = 0; strncpy(ao.alg_name, (cipher) ? cipher : DEFAULT_TEST_ALGO, 64); - - ao.keylen = strlen(passwd); + ao.keylen = strlen(passwd); memcpy(ao.key, passwd, (strlen(passwd) > TCP_AO_MAXKEYLEN_) ? TCP_AO_MAXKEYLEN_ : strlen(passwd)); if (setsockopt(s->fd, IPPROTO_TCP, TCP_AO_ADD_KEY, &ao, sizeof(ao)) < 0) { - if (errno == ENOPROTOOPT) - ERR_MSG("Kernel does not support extended TCP AO signatures"); - else - ERR("TCP_AOSIG_EXT"); + if (errno == ENOPROTOOPT) + ERR_MSG("Kernel does not support extended TCP AO signatures"); + else + ERR("TCP_AOSIG_EXT"); } - s->use_ao = 1; if (set_current) s->desired_ao_key = passwd_id_rem; @@ -356,7 +359,7 @@ sk_set_ao_auth(sock *s, ip_addr local UNUSED, ip_addr remote, int pxlen, struct return 0; } -void +int ao_delete_key(sock *s, ip_addr remote, int pxlen, struct iface *ifa, int passwd_id_loc, int passwd_id_rem) { struct tcp_ao_del_ext del; @@ -373,11 +376,11 @@ ao_delete_key(sock *s, ip_addr remote, int pxlen, struct iface *ifa, int passwd_ if (setsockopt(s->fd, IPPROTO_TCP, TCP_AO_DEL_KEY, &del, sizeof(del)) < 0) { - log("log keys for debug delete error key %i %i", passwd_id_loc, passwd_id_rem); - log_tcp_ao_get_key(s->fd); - bug("tcp ao deletion err %i", errno); + log(L_WARN "TCP AO: deletion of key %i %i on socket fd %i failed with err %i", passwd_id_loc, passwd_id_rem, s->fd, errno); + return errno; } - log("tcp ao key %i %i deleted", passwd_id_loc, passwd_id_rem); + log(L_DEBUG "tcp ao: key %i %i deleted", passwd_id_loc, passwd_id_rem); + return 0; } void @@ -390,27 +393,27 @@ ao_try_change_master(sock *s, int next_master_id_loc, int next_master_id_rem) if (setsockopt(s->fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, sizeof(tmp))) { - log(" tcp ao change master key failed with err code %i", errno); + log(L_WARN "TCP AO: change master key failed with err code %i", errno); log_tcp_ao_get_key(s->fd); return; } else - log("tried to change master"); + log(L_DEBUG "tcp ao: tried to change master to %i %i", next_master_id_loc, next_master_id_rem); s->desired_ao_key = next_master_id_rem; } -int check_ao_keys_id(int sock_fd, struct ao_key *keys) +int check_ao_keys_id(int sock_fd, struct bgp_ao_key *keys) { int errors = 0; int expected_keys[256]; //can not have char, because we must support 0 key id memset(expected_keys, 0, sizeof(int)*256); - for (struct ao_key *key = keys; key; key = key->next_key) - expected_keys[key->local_id] = key->remote_id + 1; // the + 1 because we do not want 0 id be 0 + for (struct bgp_ao_key *key = keys; key; key = key->next_key) + expected_keys[key->key.local_id] = key->key.remote_id + 1; // the + 1 because we do not want 0 id be 0 int nkeys = get_num_ao_keys(sock_fd); if(nkeys == -1) { - cf_warn("TCP AO: unable to get num of keys"); + log(L_WARN "TCP AO: unable to get num of keys"); return 1; } struct tcp_ao_getsockopt_ext tm_all[nkeys]; @@ -420,27 +423,27 @@ int check_ao_keys_id(int sock_fd, struct ao_key *keys) tm_all[0].get_all = 1; if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_GET_KEYS, tm_all, &len)) // len should be still size of one struct. Because kernel net/ipv4/tcp_ao.c line 2165 { - cf_warn("log tcp ao get keys failed with err code %i", errno); - return 1; + log(L_WARN "TCP AO: log tcp ao get keys failed with err code %i", errno); + return 1; } for (int i = 0; i< nkeys; i++) { struct tcp_ao_getsockopt_ext sock_key = tm_all[i]; if (expected_keys[sock_key.rcvid] - 1 != sock_key.sndid) { - if (expected_keys[sock_key.rcvid] == 0) - cf_warn("TCP AO: unexpected ao key %i %i", sock_key.rcvid, sock_key.sndid); + if (expected_keys[sock_key.rcvid] == 0) + log(L_WARN "TCP AO: unexpected ao key %i %i", sock_key.rcvid, sock_key.sndid); else - cf_warn("TCP AO: expected key local id %i has different remote id than expected (%i vs %i)", sock_key.rcvid, expected_keys[sock_key.rcvid] - 1, sock_key.sndid); + log(L_WARN "TCP AO: expected key local id %i has different remote id than expected (%i vs %i)", sock_key.rcvid, expected_keys[sock_key.rcvid] - 1, sock_key.sndid); errors++; - } - expected_keys[sock_key.rcvid] = 0; + } + expected_keys[sock_key.rcvid] = 0; } for (int i = 0; i < 256; i++) { if (expected_keys[i] != 0) { - cf_warn("TCP AO: key %i %i is not in socket", i, expected_keys - 1); + log(L_WARN "TCP AO: key %i %i is not in socket", i, expected_keys - 1); errors++; } } diff --git a/sysdep/linux/tcp-ao.h b/sysdep/linux/tcp-ao.h index ab4df93e..6cb92740 100644 --- a/sysdep/linux/tcp-ao.h +++ b/sysdep/linux/tcp-ao.h @@ -1,4 +1,13 @@ +#ifndef TCP_AO_ADD_KEY +#define TCP_AO_ADD_KEY 38 /* Add/Set MKT */ +#define TCP_AO_DEL_KEY 39 /* Delete MKT */ +#define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */ +#define TCP_AO_GET_KEYS 41 /* List MKT(s) */ +#define TCP_AO_REPAIR 42 /* Get/Set SNEs and ISNs */ +#endif + + #ifndef TCP_AO_STRUCTS #define TCP_AO_STRUCTS diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 992d4404..5e2c55e0 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1097,7 +1097,7 @@ sk_passive_connected(sock *s, int type) t->tos = s->tos; t->vrf = s->vrf; t->rbsize = s->rbsize; - t->tbsize = s->tbsize; + t->tbsize = s->tbsize; if (type == SK_TCP) { @@ -1341,7 +1341,6 @@ sk_open(sock *s) int bind_port = 0; ip_addr bind_addr = IPA_NONE; sockaddr sa; - log("opening sock"); if (s->type <= SK_IP) { @@ -1461,21 +1460,23 @@ sk_open(sock *s) if (s->ao_key_init) { - struct ao_key *key = s->ao_key_init; + struct bgp_ao_key *key = s->ao_key_init; do { - if (sk_set_ao_auth(s, s->saddr, s->daddr, -1, s->iface, key->master_key, key->local_id, key->remote_id, key->cipher, key->required == 1) < 0) + if (sk_set_ao_auth(s, s->saddr, s->daddr, -1, s->iface, key->key.master_key, key->key.local_id, key->key.remote_id, key->key.cipher, key->key.required == 1) < 0) goto err; + if (s->type == SK_TCP_ACTIVE) + key->activ_alive = 1; + else + key->passiv_alive = 1; key = key->next_key; } while (key); } else if (s->password) { - log("set md5"); if (sk_set_md5_auth(s, s->saddr, s->daddr, -1, s->iface, s->password, 0) < 0) goto err; } - else - log("no password given"); + switch (s->type) { case SK_TCP_ACTIVE: