mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
TCP-AO implementation based on RFC 5925, used Linux TCP-AO. Tested against JUNIPER.
This commit is contained in:
parent
adfff44878
commit
e8696ac484
@ -2912,6 +2912,38 @@ using the following configuration parameters:
|
|||||||
set manually by an external utility on NetBSD and OpenBSD. Default:
|
set manually by an external utility on NetBSD and OpenBSD. Default:
|
||||||
enabled (ignored on non-FreeBSD).
|
enabled (ignored on non-FreeBSD).
|
||||||
|
|
||||||
|
<tag><label id="bgp-tcp-ao">authentication keyed tcp AO</tag>
|
||||||
|
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.
|
||||||
|
If the other site knows 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 currently 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 trying other keys than required ones is not supported.
|
||||||
|
|
||||||
|
Editing existing keys (except of marking them "required" or "deprecated")
|
||||||
|
is not recommended and leads to restarting protocol.
|
||||||
|
|
||||||
<tag><label id="bgp-passive">passive <m/switch/</tag>
|
<tag><label id="bgp-passive">passive <m/switch/</tag>
|
||||||
Standard BGP behavior is both initiating outgoing connections and
|
Standard BGP behavior is both initiating outgoing connections and
|
||||||
accepting incoming connections. In passive mode, outgoing connections
|
accepting incoming connections. In passive mode, outgoing connections
|
||||||
@ -3657,6 +3689,20 @@ protocol bgp {
|
|||||||
local 198.51.100.14 as 65000; # Use a private AS number
|
local 198.51.100.14 as 65000; # Use a private AS number
|
||||||
neighbor 198.51.100.130 as 64496; # Our neighbor ...
|
neighbor 198.51.100.130 as 64496; # Our neighbor ...
|
||||||
multihop; # ... which is connected indirectly
|
multihop; # ... which is connected indirectly
|
||||||
|
authenticate manual {
|
||||||
|
key {
|
||||||
|
local id 2;
|
||||||
|
remote id 1;
|
||||||
|
cipher "cmac(aes128)";
|
||||||
|
master key "hello321";
|
||||||
|
required;
|
||||||
|
}key {
|
||||||
|
local id 3;
|
||||||
|
remote id 3;
|
||||||
|
cipher "cmac(aes128)";
|
||||||
|
master key "bye123";
|
||||||
|
}
|
||||||
|
}
|
||||||
ipv4 {
|
ipv4 {
|
||||||
export filter { # We use non-trivial export rules
|
export filter { # We use non-trivial export rules
|
||||||
if source = RTS_STATIC then { # Export only static routes
|
if source = RTS_STATIC then { # Export only static routes
|
||||||
|
38
lib/socket.h
38
lib/socket.h
@ -36,6 +36,29 @@ struct ssh_sock {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct ao_key
|
||||||
|
{
|
||||||
|
int local_id;
|
||||||
|
int remote_id;
|
||||||
|
const char *cipher;
|
||||||
|
const char *master_key;
|
||||||
|
int required;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {
|
typedef struct birdsock {
|
||||||
resource r;
|
resource r;
|
||||||
pool *pool; /* Pool where incoming connections should be allocated (for SK_xxx_PASSIVE) */
|
pool *pool; /* Pool where incoming connections should be allocated (for SK_xxx_PASSIVE) */
|
||||||
@ -77,6 +100,11 @@ typedef struct birdsock {
|
|||||||
node n;
|
node n;
|
||||||
void *rbuf_alloc, *tbuf_alloc;
|
void *rbuf_alloc, *tbuf_alloc;
|
||||||
const char *password; /* Password for MD5 authentication */
|
const char *password; /* Password for MD5 authentication */
|
||||||
|
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 */
|
const char *err; /* Error message */
|
||||||
struct ssh_sock *ssh; /* Used in SK_SSH */
|
struct ssh_sock *ssh; /* Used in SK_SSH */
|
||||||
} sock;
|
} sock;
|
||||||
@ -107,6 +135,16 @@ int sk_setup_broadcast(sock *s);
|
|||||||
int sk_set_ttl(sock *s, int ttl); /* Set transmit TTL for given socket */
|
int sk_set_ttl(sock *s, int ttl); /* Set transmit TTL for given socket */
|
||||||
int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given socket */
|
int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given socket */
|
||||||
int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey);
|
int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey);
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
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_ipv6_checksum(sock *s, int offset);
|
||||||
int sk_set_icmp6_filter(sock *s, int p1, int p2);
|
int sk_set_icmp6_filter(sock *s, int p1, int p2);
|
||||||
void sk_log_error(sock *s, const char *p);
|
void sk_log_error(sock *s, const char *p);
|
||||||
|
233
proto/bgp/bgp.c
233
proto/bgp/bgp.c
@ -233,7 +233,7 @@ bgp_close(struct bgp_proto *p)
|
|||||||
static inline int
|
static inline int
|
||||||
bgp_setup_auth(struct bgp_proto *p, int enable)
|
bgp_setup_auth(struct bgp_proto *p, int enable)
|
||||||
{
|
{
|
||||||
if (p->cf->password)
|
if (p->cf->password || p->cf->ao_key)
|
||||||
{
|
{
|
||||||
ip_addr prefix = p->cf->remote_ip;
|
ip_addr prefix = p->cf->remote_ip;
|
||||||
int pxlen = -1;
|
int pxlen = -1;
|
||||||
@ -243,10 +243,37 @@ bgp_setup_auth(struct bgp_proto *p, int enable)
|
|||||||
prefix = net_prefix(p->cf->remote_range);
|
prefix = net_prefix(p->cf->remote_range);
|
||||||
pxlen = net_pxlen(p->cf->remote_range);
|
pxlen = net_pxlen(p->cf->remote_range);
|
||||||
}
|
}
|
||||||
|
int rv = 0;
|
||||||
int rv = sk_set_md5_auth(p->sock->sk,
|
if (p->cf->ao_key)
|
||||||
|
{
|
||||||
|
if (enable)
|
||||||
|
{
|
||||||
|
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,
|
p->cf->local_ip, prefix, pxlen, p->cf->iface,
|
||||||
enable ? p->cf->password : NULL, p->cf->setkey);
|
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
|
||||||
|
{
|
||||||
|
struct bgp_ao_key *key = p->ao_key;
|
||||||
|
while (key)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (enable)
|
||||||
|
rv = sk_set_md5_auth(p->sock->sk,
|
||||||
|
p->cf->local_ip, prefix, pxlen, p->cf->iface,
|
||||||
|
p->cf->password, p->cf->setkey);
|
||||||
|
|
||||||
if (rv < 0)
|
if (rv < 0)
|
||||||
sk_log_error(p->sock->sk, p->p.name);
|
sk_log_error(p->sock->sk, p->p.name);
|
||||||
@ -1142,6 +1169,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->tbsize = p->cf->enable_extended_messages ? BGP_TX_BUFFER_EXT_SIZE : BGP_TX_BUFFER_SIZE;
|
||||||
s->tos = IP_PREC_INTERNET_CONTROL;
|
s->tos = IP_PREC_INTERNET_CONTROL;
|
||||||
s->password = p->cf->password;
|
s->password = p->cf->password;
|
||||||
|
s->ao_key_init = p->ao_key;
|
||||||
s->tx_hook = bgp_connected;
|
s->tx_hook = bgp_connected;
|
||||||
s->flags = p->cf->free_bind ? SKF_FREEBIND : 0;
|
s->flags = p->cf->free_bind ? SKF_FREEBIND : 0;
|
||||||
BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J",
|
BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J",
|
||||||
@ -1231,6 +1259,16 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
|
|||||||
rfree(sk);
|
rfree(sk);
|
||||||
return 0;
|
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
|
* BIRD should keep multiple incoming connections in OpenSent state (for
|
||||||
@ -1273,6 +1311,26 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
|
|||||||
if (sk_set_min_ttl(sk, 256 - hops) < 0)
|
if (sk_set_min_ttl(sk, 256 - hops) < 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
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.local_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)
|
if (p->cf->enable_extended_messages)
|
||||||
{
|
{
|
||||||
sk->rbsize = BGP_RX_BUFFER_EXT_SIZE;
|
sk->rbsize = BGP_RX_BUFFER_EXT_SIZE;
|
||||||
@ -1755,6 +1813,20 @@ bgp_init(struct proto_config *CF)
|
|||||||
p->remote_ip = cf->remote_ip;
|
p->remote_ip = cf->remote_ip;
|
||||||
p->remote_as = cf->remote_as;
|
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() */
|
/* Hack: We use cf->remote_ip just to pass remote_ip from bgp_spawn() */
|
||||||
if (cf->c.parent)
|
if (cf->c.parent)
|
||||||
cf->remote_ip = IPA_NONE;
|
cf->remote_ip = IPA_NONE;
|
||||||
@ -2180,6 +2252,146 @@ bgp_postconfig(struct proto_config *CF)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int compare_aos(struct ao_key *a, struct ao_key *b)
|
||||||
|
{
|
||||||
|
if (a->local_id != b->local_id)
|
||||||
|
return 1;
|
||||||
|
if (a->remote_id != b->remote_id)
|
||||||
|
return 1;
|
||||||
|
if (strcmp(a->cipher, b->cipher))
|
||||||
|
return 1;
|
||||||
|
return strcmp(a->master_key, b->master_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new)
|
||||||
|
{
|
||||||
|
log("reconfiguring proto");
|
||||||
|
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
|
||||||
|
|
||||||
|
if (!old_proto->conn)
|
||||||
|
{
|
||||||
|
log("tcp ao: reconfigure nonestablished connection");
|
||||||
|
return 0; // 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_loc = get_current_key_id(s_activ->fd);
|
||||||
|
|
||||||
|
if (key_in_use_loc == -1)
|
||||||
|
{
|
||||||
|
log(L_WARN "TCP AO: Unable to detect currently used key");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.local_id != key_in_use_loc)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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.local_id;
|
||||||
|
s_passiv->desired_ao_key = old_ao->key.local_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)
|
||||||
|
{
|
||||||
|
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.local_id;
|
||||||
|
s_passiv->desired_ao_key = found->key.local_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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
key_in_use_loc = 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.local_id == key_in_use_loc)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
bgp_reconfigure(struct proto *P, struct proto_config *CF)
|
bgp_reconfigure(struct proto *P, struct proto_config *CF)
|
||||||
{
|
{
|
||||||
@ -2190,11 +2402,13 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
|
|||||||
if (proto_get_router_id(CF) != p->local_id)
|
if (proto_get_router_id(CF) != p->local_id)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
log("bgp_reconfigure, will tcp ao?");
|
||||||
int same = !memcmp(((byte *) old) + sizeof(struct proto_config),
|
int same = !memcmp(((byte *) old) + sizeof(struct proto_config),
|
||||||
((byte *) new) + sizeof(struct proto_config),
|
((byte *) new) + sizeof(struct proto_config),
|
||||||
// password item is last and must be checked separately
|
// password item is last and must be checked separately
|
||||||
OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
|
OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
|
||||||
&& !bstrcmp(old->password, new->password)
|
&& !bstrcmp(old->password, new->password)
|
||||||
|
&& reconfigure_tcp_ao(p, *new)
|
||||||
&& ((!old->remote_range && !new->remote_range)
|
&& ((!old->remote_range && !new->remote_range)
|
||||||
|| (old->remote_range && new->remote_range && net_equal(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)
|
&& !bstrcmp(old->dynamic_name, new->dynamic_name)
|
||||||
@ -2654,6 +2868,17 @@ bgp_show_proto_info(struct proto *P)
|
|||||||
tm_remains(p->conn->keepalive_timer), p->conn->keepalive_time);
|
tm_remains(p->conn->keepalive_timer), p->conn->keepalive_time);
|
||||||
cli_msg(-1006, " Send hold timer: %t/%u",
|
cli_msg(-1006, " Send hold timer: %t/%u",
|
||||||
tm_remains(p->conn->send_hold_timer), p->conn->send_hold_time);
|
tm_remains(p->conn->send_hold_timer), p->conn->send_hold_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
|
#if 0
|
||||||
|
@ -136,6 +136,7 @@ struct bgp_config {
|
|||||||
u32 disable_after_cease; /* Disable it when cease is received, bitfield */
|
u32 disable_after_cease; /* Disable it when cease is received, bitfield */
|
||||||
|
|
||||||
const char *password; /* Password used for MD5 authentication */
|
const char *password; /* Password used for MD5 authentication */
|
||||||
|
struct ao_config *ao_key; /* Keys for tcp ao authentication */
|
||||||
net_addr *remote_range; /* Allowed neighbor range for dynamic BGP */
|
net_addr *remote_range; /* Allowed neighbor range for dynamic BGP */
|
||||||
const char *dynamic_name; /* Name pattern for dynamic BGP */
|
const char *dynamic_name; /* Name pattern for dynamic BGP */
|
||||||
int dynamic_name_digits; /* Minimum number of digits for dynamic names */
|
int dynamic_name_digits; /* Minimum number of digits for dynamic names */
|
||||||
@ -353,6 +354,7 @@ struct bgp_proto {
|
|||||||
struct object_lock *lock; /* Lock for neighbor connection */
|
struct object_lock *lock; /* Lock for neighbor connection */
|
||||||
struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */
|
struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */
|
||||||
struct bgp_socket *sock; /* Shared listening socket */
|
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 bfd_request *bfd_req; /* BFD request, if BFD is used */
|
||||||
struct birdsock *postponed_sk; /* Postponed incoming socket for dynamic BGP */
|
struct birdsock *postponed_sk; /* Postponed incoming socket for dynamic BGP */
|
||||||
struct bgp_stats stats; /* BGP statistics */
|
struct bgp_stats stats; /* BGP statistics */
|
||||||
|
@ -32,7 +32,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
|||||||
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
|
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
|
||||||
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
|
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
|
||||||
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
|
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
|
||||||
RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND)
|
RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND,
|
||||||
|
AUTHENTICATE, MANUAL, KEY, MASTER, DEPRECATED, REQUIRED, CIPHER, LOCAL, REMOTE)
|
||||||
|
|
||||||
%type <i> bgp_nh
|
%type <i> bgp_nh
|
||||||
%type <i32> bgp_afi
|
%type <i32> bgp_afi
|
||||||
@ -201,7 +202,8 @@ bgp_proto:
|
|||||||
| bgp_proto REQUIRE GRACEFUL RESTART bool ';' { BGP_CFG->require_gr = $5; }
|
| bgp_proto REQUIRE GRACEFUL RESTART bool ';' { BGP_CFG->require_gr = $5; }
|
||||||
| bgp_proto REQUIRE LONG LIVED GRACEFUL RESTART bool ';' { BGP_CFG->require_llgr = $7; }
|
| bgp_proto REQUIRE LONG LIVED GRACEFUL RESTART bool ';' { BGP_CFG->require_llgr = $7; }
|
||||||
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
|
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
|
||||||
| bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; }
|
| bgp_proto PASSWORD text ';' { log("%s", $3); BGP_CFG->password = $3; }
|
||||||
|
| bgp_proto AUTHENTICATE MANUAL '{' ao_keys '}' tcp_ao_end
|
||||||
| bgp_proto SETKEY bool ';' { BGP_CFG->setkey = $3; }
|
| bgp_proto SETKEY bool ';' { BGP_CFG->setkey = $3; }
|
||||||
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
|
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
|
||||||
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
|
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
|
||||||
@ -242,6 +244,95 @@ bgp_afi:
|
|||||||
| FLOW6 { $$ = BGP_AF_FLOW6; }
|
| FLOW6 { $$ = BGP_AF_FLOW6; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
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_first_item:
|
||||||
|
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->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:
|
||||||
|
{
|
||||||
|
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_config *key = BGP_CFG->ao_key;
|
||||||
|
while (key)
|
||||||
|
{
|
||||||
|
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'");
|
||||||
|
required_found = 1;
|
||||||
|
}
|
||||||
|
key = key->next_key;
|
||||||
|
}
|
||||||
|
if (required_found == 0)
|
||||||
|
cf_error("TCP AO: Missing 'REQUIRED'. Which key should be used?");
|
||||||
|
}
|
||||||
|
|
||||||
bgp_channel_start: bgp_afi
|
bgp_channel_start: bgp_afi
|
||||||
{
|
{
|
||||||
const struct bgp_af_desc *desc = bgp_get_af_desc($1);
|
const struct bgp_af_desc *desc = bgp_get_af_desc($1);
|
||||||
|
@ -3477,6 +3477,32 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, uint len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
delete_deprecated_keys(sock *sk, struct bgp_proto *p, int new_lnext)
|
||||||
|
{
|
||||||
|
struct bgp_ao_key *key = p->ao_key;
|
||||||
|
int ret = 1;
|
||||||
|
while (key)
|
||||||
|
{
|
||||||
|
if (key->key.required == -1)
|
||||||
|
{
|
||||||
|
if (new_lnext == key->key.local_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;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bgp_rx - handle received data
|
* bgp_rx - handle received data
|
||||||
* @sk: socket
|
* @sk: socket
|
||||||
@ -3491,6 +3517,25 @@ int
|
|||||||
bgp_rx(sock *sk, uint size)
|
bgp_rx(sock *sk, uint size)
|
||||||
{
|
{
|
||||||
struct bgp_conn *conn = sk->data;
|
struct bgp_conn *conn = sk->data;
|
||||||
|
if (sk->use_ao && sk->desired_ao_key != sk->last_used_ao_key)
|
||||||
|
{
|
||||||
|
int new_lnext = get_current_key_id(sk->fd);
|
||||||
|
if (new_lnext != 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 lnext %i, received %i", sk->desired_ao_key, new_lnext);
|
||||||
|
log_tcp_ao_info(sk->fd);
|
||||||
|
|
||||||
|
if (sk->proto_del_ao_key && sk->desired_ao_key == new_lnext)
|
||||||
|
{
|
||||||
|
if (delete_deprecated_keys(sk, sk->proto_del_ao_key, new_lnext))
|
||||||
|
sk->proto_del_ao_key = NULL;
|
||||||
|
}
|
||||||
|
sk->last_used_ao_key = new_lnext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
byte *pkt_start = sk->rbuf;
|
byte *pkt_start = sk->rbuf;
|
||||||
byte *end = pkt_start + size;
|
byte *end = pkt_start + size;
|
||||||
uint i, len;
|
uint i, len;
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "sysdep/linux/tcp-ao.h"
|
||||||
|
|
||||||
#ifndef IPV6_MINHOPCOUNT
|
#ifndef IPV6_MINHOPCOUNT
|
||||||
#define IPV6_MINHOPCOUNT 73
|
#define IPV6_MINHOPCOUNT 73
|
||||||
#endif
|
#endif
|
||||||
@ -22,6 +24,7 @@
|
|||||||
#define TCP_MD5SIG_FLAG_PREFIX 1
|
#define TCP_MD5SIG_FLAG_PREFIX 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* We redefine the tcp_md5sig structure with different name to avoid collision with older headers */
|
/* We redefine the tcp_md5sig structure with different name to avoid collision with older headers */
|
||||||
struct tcp_md5sig_ext {
|
struct tcp_md5sig_ext {
|
||||||
struct sockaddr_storage tcpm_addr; /* Address associated */
|
struct sockaddr_storage tcpm_addr; /* Address associated */
|
||||||
@ -209,6 +212,250 @@ sk_set_md5_auth(sock *s, ip_addr local UNUSED, ip_addr remote, int pxlen, struct
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log(L_INFO "TCP AO on socket %i:\ncurrent key id %i (loc), next key %i (rem),\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)
|
||||||
|
{
|
||||||
|
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: Getting current ao key for socket file descriptor %i failed with errno %i", sock_fd, errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return tmp.current_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_rnext_key_id(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);
|
||||||
|
|
||||||
|
if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_num_ao_keys(int sock_fd)
|
||||||
|
{
|
||||||
|
struct tcp_ao_getsockopt_ext tmp;
|
||||||
|
memset(&tmp, 0, sizeof(struct tcp_ao_getsockopt_ext));
|
||||||
|
socklen_t len = sizeof(tmp);
|
||||||
|
tmp.nkeys = 1;
|
||||||
|
tmp.get_all = 1;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
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_INFO "TCP AO on socket fd %i has %i keys", tm_all[0].nkeys);
|
||||||
|
for (int i = 0; i < nkeys; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
char key_val[TCP_AO_MAXKEYLEN_*2+1];
|
||||||
|
for (int ik = 0; ik<TCP_AO_MAXKEYLEN_; ik++)
|
||||||
|
sprintf(&key_val[ik*2], "%x", tm_all[i].key[ik]);
|
||||||
|
key_val[TCP_AO_MAXKEYLEN_*2] = 0;
|
||||||
|
log(L_INFO "sndid %i rcvid %i, %s %s, cipher %s key %x (%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, key_val, 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(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)
|
||||||
|
{
|
||||||
|
ao.set_rnext = 1;
|
||||||
|
ao.set_current = 1;
|
||||||
|
}
|
||||||
|
if (pxlen >= 0)
|
||||||
|
ao.prefix = pxlen;
|
||||||
|
else if(s->af == AF_INET)
|
||||||
|
ao.prefix = 32;
|
||||||
|
else
|
||||||
|
ao.prefix = 128;
|
||||||
|
ao.sndid = passwd_id_loc;
|
||||||
|
ao.rcvid = passwd_id_rem;
|
||||||
|
ao.maclen = 0;
|
||||||
|
ao.keyflags = 0;
|
||||||
|
ao.ifindex = 0;
|
||||||
|
|
||||||
|
strncpy(ao.alg_name, (cipher) ? cipher : DEFAULT_TEST_ALGO, 64);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
s->use_ao = 1;
|
||||||
|
if (set_current)
|
||||||
|
s->desired_ao_key = passwd_id_loc;
|
||||||
|
log_tcp_ao_get_key(s->fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
memset(&del, 0, sizeof(struct tcp_ao_del_ext));
|
||||||
|
sockaddr_fill((sockaddr *) &del.addr, s->af, remote, ifa, 0);
|
||||||
|
del.sndid = passwd_id_loc;
|
||||||
|
del.rcvid = passwd_id_rem;
|
||||||
|
if (pxlen >= 0)
|
||||||
|
del.prefix = pxlen;
|
||||||
|
else if(s->af == AF_INET)
|
||||||
|
del.prefix = 32;
|
||||||
|
else
|
||||||
|
del.prefix = 128;
|
||||||
|
|
||||||
|
if (setsockopt(s->fd, IPPROTO_TCP, TCP_AO_DEL_KEY, &del, sizeof(del)) < 0)
|
||||||
|
{
|
||||||
|
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(L_DEBUG "tcp ao: key %i %i deleted", passwd_id_loc, passwd_id_rem);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
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_rem;
|
||||||
|
|
||||||
|
if (setsockopt(s->fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, sizeof(tmp)))
|
||||||
|
{
|
||||||
|
log(L_WARN "TCP AO: change master key failed with err code %i", errno);
|
||||||
|
log_tcp_ao_get_key(s->fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
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_loc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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)
|
||||||
|
{
|
||||||
|
log(L_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
|
||||||
|
{
|
||||||
|
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.sndid] - 1 != sock_key.rcvid)
|
||||||
|
{
|
||||||
|
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.sndid, expected_keys[sock_key.sndid] - 1, sock_key.rcvid);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
expected_keys[sock_key.sndid] = 0;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
if (expected_keys[i] != 0)
|
||||||
|
{
|
||||||
|
log(L_WARN "TCP AO: key %i %i is not in socket", i, expected_keys - 1);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
sk_set_min_ttl4(sock *s, int ttl)
|
sk_set_min_ttl4(sock *s, int ttl)
|
||||||
{
|
{
|
||||||
|
109
sysdep/linux/tcp-ao.h
Normal file
109
sysdep/linux/tcp-ao.h
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
#define TCP_AO_MAXKEYLEN_ 80
|
||||||
|
|
||||||
|
#define DEFAULT_TEST_ALGO "cmac(aes128)"
|
||||||
|
|
||||||
|
struct tcp_ao_add_ext { /* setsockopt(TCP_AO_ADD_KEY) */
|
||||||
|
struct sockaddr_storage addr; /* peer's address for the key */
|
||||||
|
char alg_name[64]; /* crypto hash algorithm to use */
|
||||||
|
s32 ifindex; /* L3 dev index for VRF */
|
||||||
|
u32 set_current :1, /* set key as Current_key at once */
|
||||||
|
set_rnext :1, /* request it from peer with RNext_key */
|
||||||
|
reserved :30; /* must be 0 */
|
||||||
|
u16 reserved2; /* padding, must be 0 */
|
||||||
|
u8 prefix; /* peer's address prefix */
|
||||||
|
u8 sndid; /* SendID for outgoing segments */
|
||||||
|
u8 rcvid; /* RecvID to match for incoming seg */
|
||||||
|
u8 maclen; /* length of authentication code (hash) */
|
||||||
|
u8 keyflags; /* see TCP_AO_KEYF_ */
|
||||||
|
u8 keylen; /* length of ::key */
|
||||||
|
u8 key[TCP_AO_MAXKEYLEN_];
|
||||||
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
|
struct tcp_ao_del_ext { /* setsockopt(TCP_AO_DEL_KEY) */
|
||||||
|
struct sockaddr_storage addr; /* peer's address for the key */
|
||||||
|
s32 ifindex; /* L3 dev index for VRF */
|
||||||
|
u32 set_current :1, /* corresponding ::current_key */
|
||||||
|
set_rnext :1, /* corresponding ::rnext */
|
||||||
|
del_async :1, /* only valid for listen sockets */
|
||||||
|
reserved :29; /* must be 0 */
|
||||||
|
u16 reserved2; /* padding, must be 0 */
|
||||||
|
u8 prefix; /* peer's address prefix */
|
||||||
|
u8 sndid; /* SendID for outgoing segments */
|
||||||
|
u8 rcvid; /* RecvID to match for incoming seg */
|
||||||
|
u8 current_key; /* KeyID to set as Current_key */
|
||||||
|
u8 rnext; /* KeyID to set as Rnext_key */
|
||||||
|
u8 keyflags; /* see TCP_AO_KEYF_ */
|
||||||
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
|
struct tcp_ao_info_opt_ext { /* setsockopt(TCP_AO_INFO), getsockopt(TCP_AO_INFO) */
|
||||||
|
/* Here 'in' is for setsockopt(), 'out' is for getsockopt() */
|
||||||
|
u32 set_current :1, /* in/out: corresponding ::current_key */
|
||||||
|
set_rnext :1, /* in/out: corresponding ::rnext */
|
||||||
|
ao_required :1, /* in/out: don't accept non-AO connects */
|
||||||
|
set_counters :1, /* in: set/clear ::pkt_* counters */
|
||||||
|
accept_icmps :1, /* in/out: accept incoming ICMPs */
|
||||||
|
reserved :27; /* must be 0 */
|
||||||
|
u16 reserved2; /* padding, must be 0 */
|
||||||
|
u8 current_key; /* in/out: KeyID of Current_key */
|
||||||
|
u8 rnext; /* in/out: keyid of RNext_key */
|
||||||
|
u64 pkt_good; /* in/out: verified segments */
|
||||||
|
u64 pkt_bad; /* in/out: failed verification */
|
||||||
|
u64 pkt_key_not_found; /* in/out: could not find a key to verify */
|
||||||
|
u64 pkt_ao_required; /* in/out: segments missing TCP-AO sign */
|
||||||
|
u64 pkt_dropped_icmp; /* in/out: ICMPs that were ignored */
|
||||||
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
|
struct tcp_ao_getsockopt_ext { /* getsockopt(TCP_AO_GET_KEYS) */
|
||||||
|
struct sockaddr_storage addr; /* in/out: dump keys for peer
|
||||||
|
* with this address/prefix
|
||||||
|
*/
|
||||||
|
char alg_name[64]; /* out: crypto hash algorithm */
|
||||||
|
u8 key[TCP_AO_MAXKEYLEN_];
|
||||||
|
u32 nkeys; /* in: size of the userspace buffer
|
||||||
|
* @optval, measured in @optlen - the
|
||||||
|
* sizeof(struct tcp_ao_getsockopt)
|
||||||
|
* out: number of keys that matched
|
||||||
|
*/
|
||||||
|
u16 is_current :1, /* in: match and dump Current_key,
|
||||||
|
* out: the dumped key is Current_key
|
||||||
|
*/
|
||||||
|
|
||||||
|
is_rnext :1, /* in: match and dump RNext_key,
|
||||||
|
* out: the dumped key is RNext_key
|
||||||
|
*/
|
||||||
|
get_all :1, /* in: dump all keys */
|
||||||
|
reserved :13; /* padding, must be 0 */
|
||||||
|
u8 sndid; /* in/out: dump keys with SendID */
|
||||||
|
u8 rcvid; /* in/out: dump keys with RecvID */
|
||||||
|
u8 prefix; /* in/out: dump keys with address/prefix */
|
||||||
|
u8 maclen; /* out: key's length of authentication
|
||||||
|
* code (hash)
|
||||||
|
*/
|
||||||
|
u8 keyflags; /* in/out: see TCP_AO_KEYF_ */
|
||||||
|
u8 keylen; /* out: length of ::key */
|
||||||
|
s32 ifindex; /* in/out: L3 dev index for VRF */
|
||||||
|
u64 pkt_good; /* out: verified segments */
|
||||||
|
u64 pkt_bad; /* out: segments that failed verification */
|
||||||
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
|
struct tcp_ao_repair_ext { /* {s,g}etsockopt(TCP_AO_REPAIR) */
|
||||||
|
u32 snt_isn; //should be __be32 alias fdt32_t - 32-bit, big-endian, unsigned integer
|
||||||
|
u32 rcv_isn; //should be __be32 alias fdt32_t - 32-bit, big-endian, unsigned integer
|
||||||
|
u32 snd_sne;
|
||||||
|
u32 rcv_sne;
|
||||||
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
|
#endif /* TCP_AO_STRUCTS*/
|
@ -1480,9 +1480,24 @@ sk_open(sock *s)
|
|||||||
ERR2("bind");
|
ERR2("bind");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->password)
|
if (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->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)
|
||||||
|
{
|
||||||
if (sk_set_md5_auth(s, s->saddr, s->daddr, -1, s->iface, s->password, 0) < 0)
|
if (sk_set_md5_auth(s, s->saddr, s->daddr, -1, s->iface, s->password, 0) < 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
switch (s->type)
|
switch (s->type)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user