diff --git a/doc/bird.sgml b/doc/bird.sgml
index 09da89df..a48500c3 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -2433,6 +2433,38 @@ protocol bfd [<name>] {
offers better resistance to replay attacks but may require more
computation.
+ authentication keyed tcp AO
+ This authentication is similar to md5, but enables changing keys on living connection.
+ Key change is done via reconfiguring.
+
+ Key configuration of one key consists of two ids - one for local and one for remote machine.
+ The ids may, but does not have to be the same and must be in range 0 - 255. Among keys
+ on one protocol the local ids must be unique and the remote ids must be unique.
+
+ Used cryphtographic algorithm must be specified for each key.
+ Possible ciphers are "cmac(aes128)", "hmac(md5)" "hmac(sha1)", "hmac(sha224)",
+ "hmac(sha256)", "hmac(sha384)" and "hmac(sha512)". And, of course, there must
+ be specified a string password.
+
+ One key must be marked as "required". This key will be send as rnext key.
+ That means, if the other site know the required key, it uses the key for next packet.
+
+ In order to delete a currently used key (key which is required by the other site),
+ it is possible to mark the key as "deprecated". This key will be deleted first time
+ the other site requires another key.
+
+ Deleting a curently used key in config causes restart of the protocol.
+ The problem of directly deleting current key is, that we could treat such deleted key as deprecated,
+ but only until the protocol restarts. If it restarts, key is lost.
+ For example, connection is established. Then one side decides to remove current key and requires a newly added key.
+ The other side does not know the new key yet. For now, this is not problem, they still use the old key.
+ But, at this moment, some unexpected error occures at the first site.
+ It restarts, but it does not have the old key in config. One site does not have old key,
+ the other new key and triing other keys than required ones is not supported.
+
+ Editing existing keys (except of marking them "required" or "deprecated")
+ is not reccomended and leads to restarting protocol.
+
password "text"
Specifies a password used for authentication. See [ common option for detailed description. Note that
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 2d1ea8d3..147ffd2f 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1296,19 +1296,23 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
if (p->ao_key)
{
- 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 (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);
+ }
+ else if (key->key.required == -1)
+ {
+ sk->proto_del_ao_key = p;
+ }
+ }
+ }
}
if (p->cf->enable_extended_messages)
{
@@ -2272,14 +2276,16 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new)
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))
+ {
+ 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))
+ 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;
}
@@ -2290,11 +2296,12 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new)
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.
+ 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;
+ old_ao->to_delete = 0;
+ found = old_ao;
}
}
if (!found)
@@ -2305,10 +2312,12 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new)
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))
+ 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))
+ 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;
@@ -2320,7 +2329,8 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new)
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.
+ 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.
}
}
}
@@ -2334,7 +2344,7 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new)
if (old_ao->key.remote_id == key_in_use_rem)
{
log(L_WARN "TCP AO: deleting currently used key");
- return 0;
+ 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;
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 360b6124..25542af9 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -3410,22 +3410,30 @@ 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)
+int
+delete_deprecated_keys(sock *sk, struct bgp_proto *p, int new_rnext)
{
struct bgp_ao_key *key = p->ao_key;
- while (key->key.remote_id != key_rem_id)
+ int ret = 1;
+ while (key)
{
+ if (key->key.required == -1)
+ {
+ if (new_rnext == key->key.remote_id)
+ ret = 0;
+ else
+ {
+ 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;
+ }
+ }
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;
+ return ret;
}
/**
@@ -3454,8 +3462,8 @@ bgp_rx(sock *sk, uint size)
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;
+ if (delete_deprecated_keys(sk, sk->proto_del_ao_key, new_rnext))
+ sk->proto_del_ao_key = NULL;
}
sk->last_used_ao_key = new_rnext;
}
diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h
index a6f0fe12..53c82018 100644
--- a/sysdep/linux/sysio.h
+++ b/sysdep/linux/sysio.h
@@ -217,7 +217,6 @@ void log_tcp_ao_info(int sock_fd)
struct tcp_ao_info_opt_ext tmp;
memset(&tmp, 0, sizeof(struct tcp_ao_info_opt_ext));
socklen_t len = sizeof(tmp);
- log("socket: fd %i", sock_fd);
if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len))
{
@@ -225,8 +224,8 @@ void log_tcp_ao_info(int sock_fd)
return;
}
else
- 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);
+ log(L_INFO "TCP AO on socket %i:\ncurrent key id %i (rem), next key %i (loc),\n set current %i, is ao required %i\n good packets %i, bad packets %i",
+ sock_fd, tmp.current_key, tmp.rnext, tmp.set_current, tmp.ao_required, tmp.pkt_good, tmp.pkt_bad);
}
int get_current_key_id(int sock_fd)
@@ -237,8 +236,8 @@ int get_current_key_id(int sock_fd)
if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len))
{
- log(L_WARN "TCP AO: Getting current ao key for socket file descriptor %i failed with errno %i", sock_fd, errno);
- return -1;
+ log(L_WARN "TCP AO: Getting current ao key for socket file descriptor %i failed with errno %i", sock_fd, errno);
+ return -1;
}
else
return tmp.current_key;
@@ -269,8 +268,8 @@ int get_num_ao_keys(int sock_fd)
if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_GET_KEYS, &tmp, &len))
{
- log(L_WARN "TCP AO: get keys on socket fd %i failed with err code %i", sock_fd, errno);
- return -1;
+ log(L_WARN "TCP AO: get keys on socket fd %i failed with err code %i", sock_fd, errno);
+ return -1;
}
return tmp.nkeys;
}
@@ -288,13 +287,15 @@ 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(L_WARN "TCP AO: getting keys on socket fd %i failed with err code %i", sock_fd, errno);
- return;
+ log(L_WARN "TCP AO: getting keys on socket fd %i failed with err code %i", sock_fd, errno);
+ return;
}
log(L_INFO "TCP AO on socket fd %i has %i keys", tm_all[0].nkeys);
for (int i = 0; i < nkeys; i++)
{
- 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);
+ 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);
}
}
@@ -411,7 +412,7 @@ int check_ao_keys_id(int sock_fd, struct bgp_ao_key *keys)
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)
+ if (nkeys == -1)
{
log(L_WARN "TCP AO: unable to get num of keys");
return 1;
@@ -434,7 +435,7 @@ int check_ao_keys_id(int sock_fd, struct bgp_ao_key *keys)
if (expected_keys[sock_key.rcvid] == 0)
log(L_WARN "TCP AO: unexpected ao key %i %i", sock_key.rcvid, sock_key.sndid);
else
- 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);
+ 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;
]