diff --git a/lib/socket.h b/lib/socket.h index 3772cb5c..98398ca5 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -87,7 +87,10 @@ typedef struct birdsock { node n; void *rbuf_alloc, *tbuf_alloc; const char *password; /* Password for MD5 authentication */ - struct ao_key *ao_key; /* Key for tcp ao authentication */ + struct ao_key *ao_key_init; /* Key for tcp ao authentication icialization. */ + char use_ao; + 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 */ struct ssh_sock *ssh; /* Used in SK_SSH */ } sock; @@ -125,7 +128,8 @@ int sk_set_ao_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct ifa void 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); -void ao_try_change_master(int sock_fd, int next_key_id); +int check_ao_keys_id(int sock_fd, struct 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); void sk_log_error(sock *s, const char *p); diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 02c1b8ef..dac9f7ec 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -192,7 +192,7 @@ bgp_open(struct bgp_proto *p) if (sk_open(sk) < 0) goto err; - log("____________________________________________________________________________%i %i", sk->fd, sk->ao_key); + log("____________________________________________________________________________%i %i", sk->fd, sk->ao_key_init); bs = mb_allocz(proto_pool, sizeof(struct bgp_socket)); bs->sk = sk; bs->uc = 1; @@ -223,14 +223,6 @@ bgp_close(struct bgp_proto *p) ASSERT(bs && bs->uc); - log("in bgp close"); - struct ao_key *key = bs->sk->ao_key; - while (key) - { - log("delete %i", key->local_id); - ao_delete_key(bs->sk, p->remote_ip, -1, bs->sk->iface, key->local_id, key->remote_id); - key = key->next_key; - } if (--bs->uc) return; @@ -255,16 +247,29 @@ bgp_setup_auth(struct bgp_proto *p, int enable) int rv = 0; if (p->cf->ao_key) { - log("set ao auth [%s]", p->cf->ao_key->master_key); - struct ao_key *key = p->cf->ao_key; - p->sock->sk->ao_key = key; - do { - rv = sk_set_ao_auth(p->sock->sk, + if (enable) + { + log("set ao auth [%s]", p->cf->ao_key->master_key); + struct ao_key *key = p->cf->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->next_key; + key->master_key, key->local_id, key->remote_id, key->cipher, 0); + key = key->next_key; - } while(key); + } while(key); + } + else + { + log("in bgp close"); + struct ao_key *key = p->cf->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); + key = key->next_key; + } + } } else if (enable) rv = sk_set_md5_auth(p->sock->sk, @@ -1092,7 +1097,6 @@ bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn) conn->channels_to_send = 0; conn->last_channel = 0; conn->last_channel_count = 0; - conn->last_used_ao_key = -1; conn->connect_timer = tm_new_init(p->p.pool, bgp_connect_timeout, conn, 0, 0); conn->hold_timer = tm_new_init(p->p.pool, bgp_hold_timeout, conn, 0, 0); @@ -1156,9 +1160,8 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c s->rbsize = p->cf->enable_extended_messages ? BGP_RX_BUFFER_EXT_SIZE : BGP_RX_BUFFER_SIZE; s->tbsize = p->cf->enable_extended_messages ? BGP_TX_BUFFER_EXT_SIZE : BGP_TX_BUFFER_SIZE; s->tos = IP_PREC_INTERNET_CONTROL; - // s->password = "abcd1234";/ s->password = p->cf->password; - s->ao_key = p->cf->ao_key; + s->ao_key_init = p->cf->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", @@ -1171,7 +1174,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c if (sk_open(s) < 0) goto err; - log("a..........................................................................................fd %i key %i %I", s->fd, s->ao_key, s->daddr); + log("a..........................................................................................fd %i %I", s->fd, s->daddr); /* Set minimal receive TTL if needed */ if (p->cf->ttl_security) @@ -1250,6 +1253,16 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED) rfree(sk); return 0; } + if (p->cf->ao_key) + { + if (get_current_key_id(sk->fd) == -1) + { + log(L_WARN "BGP: Connection from address %I%J (port %d) has no TCP AO key", + sk->daddr, ipa_is_link_local(sk->daddr) ? sk->iface : NULL, sk->dport); + rfree(sk); + return 0; + } + } /* * BIRD should keep multiple incoming connections in OpenSent state (for @@ -1292,6 +1305,11 @@ 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 (check_ao_keys_id(sk->fd, p->cf->ao_key) == 0) + sk->use_ao = 1; + } if (p->cf->enable_extended_messages) { sk->rbsize = BGP_RX_BUFFER_EXT_SIZE; @@ -2173,11 +2191,62 @@ 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) +{ + log("in reconf dead 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) + { + old_aos[ao_key->local_id] = ao_key; + old_aos_rem[ao_key->remote_id] = ao_key; + } + + 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; + int key_in_use_rem = get_current_key_id(s_activ->fd); if (key_in_use_rem == -1) @@ -2185,6 +2254,8 @@ int reconfigure_tcp_ao(struct bgp_proto old_proto, struct bgp_config new) log("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); @@ -2215,7 +2286,7 @@ int reconfigure_tcp_ao(struct bgp_proto old_proto, struct bgp_config new) struct ao_key *old_key = old_aos[ao_key->local_id]; ao_delete_key(s_activ, old_proto.remote_ip, -1, s_activ->iface, old_key->local_id, old_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_activ, old_proto.local_ip, old_proto.remote_ip, -1, s_activ->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, ao_key->required == 1); + sk_set_ao_auth(s_activ, old_proto.local_ip, old_proto.remote_ip, -1, s_activ->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, 0); 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); } old_aos[ao_key->local_id] = 0; @@ -2231,15 +2302,13 @@ int reconfigure_tcp_ao(struct bgp_proto old_proto, struct bgp_config new) } cf_warn("TCP AO reconfiguration: Reusing remote id %i with new local id %i. This might break your connection.", ao_key->remote_id, ao_key->local_id); } - sk_set_ao_auth(s_activ, old_proto.local_ip, old_proto.remote_ip, -1, s_activ->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, ao_key->required == 1); + sk_set_ao_auth(s_activ, old_proto.local_ip, old_proto.remote_ip, -1, s_activ->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, 0); 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); } - s_activ->ao_key = new.ao_key; - s_passiv->ao_key = new.ao_key; - if (ao_key->required == 1 && (ao_key->local_id != get_rnext_key_id(s_activ->fd))) + if (ao_key->required == 1 && (ao_key->local_id != rnext_id)) { - ao_try_change_master(s_activ->fd, ao_key->local_id); + 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 } @@ -2257,6 +2326,7 @@ int reconfigure_tcp_ao(struct bgp_proto old_proto, struct bgp_config new) 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"); return 1; } diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index d117364b..b5a730c5 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -307,7 +307,6 @@ struct bgp_conn { u8 last_channel_count; /* Number of times the last channel was used in succession */ int notify_code, notify_subcode, notify_size; byte *notify_data; - int last_used_ao_key; /* ID of last ao authentication key, which was used */ uint hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */ }; diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 229b099b..7bdadc63 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -3425,25 +3425,28 @@ bgp_rx(sock *sk, uint size) { struct bgp_conn *conn = sk->data; - if (sk->ao_key) + if (sk->use_ao && sk->desired_ao_key != sk->last_used_ao_key) { - if (get_current_key_id(sk->fd) != conn->last_used_ao_key) + 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. - conn->last_used_ao_key = get_current_key_id(sk->fd); + sk->last_used_ao_key = new_rnext; + log("Expected desired rnext %i, arrived %i", sk->desired_ao_key, new_rnext); log_ao(sk->fd); - log("%i", sk->ao_key); } 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); - log("%i %i", get_current_key_id(sk->fd), conn->last_used_ao_key); - log("fd %i sk %i key %i", sk->fd, sk, sk->ao_key); } } else - log("no ao"); + { + 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 4252623d..8aa2f352 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -234,7 +234,7 @@ void log_tcp_ao_info(int sock_fd) return; } else - log("current key id %i, next key %i,\n set current %i, ao required %i\n good packets %i, bad packets %i", + log("current key id %i (rem), next key %i (loc),\n set current %i, 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); } @@ -268,8 +268,7 @@ int get_rnext_key_id(int sock_fd) return tmp.rnext; } -void -log_tcp_ao_get_key(int sock_fd) +int get_num_ao_keys(int sock_fd) { struct tcp_ao_getsockopt_ext tmp; memset(&tmp, 0, sizeof(struct tcp_ao_getsockopt_ext)); @@ -279,12 +278,20 @@ log_tcp_ao_get_key(int sock_fd) if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_GET_KEYS, &tmp, &len)) { - log("log tcp ao get keys failed with err code %i", errno); - return; + log("tcp ao get keys failed with err code %i", errno); + return -1; } - - int nkeys = tmp.nkeys; + return tmp.nkeys; +} + +void +log_tcp_ao_get_key(int sock_fd) +{ + int nkeys = get_num_ao_keys(sock_fd); + if (nkeys < 0) + return; struct tcp_ao_getsockopt_ext tm_all[nkeys]; + socklen_t len = sizeof(struct tcp_ao_getsockopt_ext); memset(tm_all, 0, sizeof(struct tcp_ao_getsockopt_ext)*nkeys); tm_all[0].nkeys = nkeys; tm_all[0].get_all = 1; @@ -301,13 +308,11 @@ log_tcp_ao_get_key(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) +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)); - if (!s->ao_key) - bug("no ao key"); - log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>in sk set ao, pass %s fd %i sk %i %i", passwd, s->fd, s, s->ao_key); + 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; @@ -337,8 +342,16 @@ sk_set_ao_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface * 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) - bug("tcp ao err %i", errno); + { + 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; log_tcp_ao_get_key(s->fd); return 0; } @@ -359,46 +372,79 @@ ao_delete_key(sock *s, ip_addr remote, int pxlen, struct iface *ifa, int passwd_ del.prefix = 128; 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("tcp ao key %i %i deleted", passwd_id_loc, passwd_id_rem); } void -ao_try_change_master(int sock_fd, int next_master_id ) +ao_try_change_master(sock *s, int next_master_id_loc, int next_master_id_rem) { struct tcp_ao_info_opt_ext tmp; memset(&tmp, 0, sizeof(struct tcp_ao_info_opt_ext)); tmp.set_rnext = 1; - tmp.rnext = next_master_id; + tmp.rnext = next_master_id_loc; - if (setsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, sizeof(tmp))) + 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_tcp_ao_get_key(sock_fd); + log_tcp_ao_get_key(s->fd); return; } else log("tried to change master"); + s->desired_ao_key = next_master_id_rem; + } -void -save_to_repair(int sock_fd) +int check_ao_keys_id(int sock_fd, struct ao_key *keys) { - struct tcp_ao_repair_ext replace_me; //TODO not ignore replace_me - socklen_t len = sizeof(replace_me); - memset(&replace_me, 0, sizeof(struct tcp_ao_repair_ext)); - if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_REPAIR, &replace_me, &len) < 0) - bug("geting tcp ao img not succeed %i", errno); - log("got tcp ao img"); -} - - -void -repair_tcp_ao(int sock_fd, struct iface *ifa) -{ - //if (setsockopt(sock_fd, SOL_TCP, TCP_AO_REPAIR, &ifa->tcp_ao_img, sizeof(ifa->tcp_ao_img)) < 0) - // bug("tcp ao err %i", errno); - log("tcp ao repair skiped"); + 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 + int nkeys = get_num_ao_keys(sock_fd); + if(nkeys == -1) + { + cf_warn("TCP AO: unable to get num of keys"); + return 1; + } + struct tcp_ao_getsockopt_ext tm_all[nkeys]; + socklen_t len = sizeof(struct tcp_ao_getsockopt_ext); + memset(tm_all, 0, sizeof(struct tcp_ao_getsockopt_ext)*nkeys); + tm_all[0].nkeys = nkeys; + 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; + } + 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); + 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); + errors++; + } + 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); + errors++; + } + } + return errors; } static inline int diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 6057b6e1..992d4404 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -1097,8 +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->ao_key = s->ao_key; + t->tbsize = s->tbsize; if (type == SK_TCP) { @@ -1459,12 +1458,10 @@ sk_open(sock *s) if (bind(fd, &sa.sa, SA_LEN(sa)) < 0) ERR2("bind"); } - //s->password = "abcd1234"; - if (s->ao_key) + if (s->ao_key_init) { - log("set ao, %s", s->ao_key->cipher); - struct ao_key *key = s->ao_key; + struct 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) goto err;