From b1b19433602f2a2ff58cfe2c1858ff883eee7b20 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Tue, 16 Aug 2011 23:05:35 +0200 Subject: [PATCH] The generalized TTL security mechanism (RFC 5082) support. Thanks to Alexander V. Chernikov for the patch. --- doc/bird.sgml | 20 +++++++++++--- lib/socket.h | 3 ++- proto/bgp/bgp.c | 62 ++++++++++++++++++++++++++++++++++---------- proto/bgp/bgp.h | 1 + proto/bgp/config.Y | 5 ++-- sysdep/bsd/sysio.h | 31 ++++++++++++++++++++++ sysdep/linux/sysio.h | 48 ++++++++++++++++++++++++++++++++++ sysdep/unix/io.c | 24 ++++++++++++++++- sysdep/unix/main.c | 1 + 9 files changed, 174 insertions(+), 21 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 406adc69..2435d1cb 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1120,9 +1120,11 @@ for each neighbor using the following configuration parameters: subnets. Such IP address have to be reachable through system routing table. For multihop BGP it is recommended to explicitly configure source address Define local address we should use for next hop calculation and as a source address @@ -1169,6 +1171,18 @@ for each neighbor using the following configuration parameters: as an IGP routing table. Default: the same as the table BGP is connected to. + ttl security Use GTSM (RFC 5082 - the + generalized TTL security mechanism). GTSM protects against + spoofed packets by ignoring received packets with a smaller + than expected TTL. To work properly, GTSM have to be enabled + on both sides of a BGP session. If both password Use this password for MD5 authentication of BGP sessions. Default: no authentication. Password has to be set by external utility (e.g. setkey(8)) on BSD systems. diff --git a/lib/socket.h b/lib/socket.h index 348295f5..9b9452c6 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -54,7 +54,8 @@ int sk_send(sock *, unsigned len); /* Send data, <0=err, >0=ok, 0=sleep */ int sk_send_to(sock *, unsigned len, ip_addr to, unsigned port); /* sk_send to given destination */ void sk_reallocate(sock *); /* Free and allocate tbuf & rbuf */ void sk_dump_all(void); -int sk_set_ttl(sock *s, int ttl); /* Set 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 */ /* Add or remove security associations for given passive socket */ int sk_set_md5_auth(sock *s, ip_addr a, char *passwd); diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 70c9b2f9..d26b4817 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -92,6 +92,8 @@ static int bgp_open(struct bgp_proto *p) { struct config *cfg = p->cf->c.global; + int errcode; + bgp_counter++; if (!bgp_listen_sk) @@ -100,10 +102,8 @@ bgp_open(struct bgp_proto *p) if (!bgp_listen_sk) { bgp_counter--; - p->p.disabled = 1; - bgp_store_error(p, NULL, BE_MISC, BEM_NO_SOCKET); - proto_notify_state(&p->p, PS_DOWN); - return -1; + errcode = BEM_NO_SOCKET; + goto err; } if (!bgp_linpool) @@ -115,14 +115,18 @@ bgp_open(struct bgp_proto *p) if (rv < 0) { bgp_close(p, 0); - p->p.disabled = 1; - bgp_store_error(p, NULL, BE_MISC, BEM_INVALID_MD5); - proto_notify_state(&p->p, PS_DOWN); - return -1; + errcode = BEM_INVALID_MD5; + goto err; } } return 0; + +err: + p->p.disabled = 1; + bgp_store_error(p, NULL, BE_MISC, errcode); + proto_notify_state(&p->p, PS_DOWN); + return -1; } static void @@ -567,6 +571,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c { sock *s; struct bgp_conn *conn = &p->outgoing_conn; + int hops = p->cf->multihop ? : 1; DBG("BGP: Connecting\n"); s = sk_new(p->p.pool); @@ -574,7 +579,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c s->saddr = p->source_addr; s->daddr = p->cf->remote_ip; s->dport = BGP_PORT; - s->ttl = p->cf->multihop ? : 1; + s->ttl = p->cf->ttl_security ? 255 : hops; s->rbsize = BGP_RX_BUFFER_SIZE; s->tbsize = BGP_TX_BUFFER_SIZE; s->tos = IP_PREC_INTERNET_CONTROL; @@ -584,11 +589,25 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c bgp_setup_conn(p, conn); bgp_setup_sk(conn, s); bgp_conn_set_state(conn, BS_CONNECT); - if (sk_open(s)) + + if (sk_open(s) < 0) { bgp_sock_err(s, 0); return; } + + /* Set minimal receive TTL if needed */ + if (p->cf->ttl_security) + { + DBG("Setting minimum received TTL to %d", 256 - hops); + if (sk_set_min_ttl(s, 256 - hops) < 0) + { + log(L_ERR "TTL security configuration failed, closing session"); + bgp_sock_err(s, 0); + return; + } + } + DBG("BGP: Waiting for connect success\n"); bgp_start_timer(conn->connect_retry_timer, p->cf->connect_retry_time); } @@ -627,9 +646,22 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED) if (!acc) goto err; + int hops = p->cf->multihop ? : 1; + if (p->cf->ttl_security) + { + /* TTL security support */ + if ((sk_set_ttl(sk, 255) < 0) || + (sk_set_min_ttl(sk, 256 - hops) < 0)) + { + log(L_ERR "TTL security configuration failed, closing session"); + goto err; + } + } + else + sk_set_ttl(sk, hops); + bgp_setup_conn(p, &p->incoming_conn); bgp_setup_sk(&p->incoming_conn, sk); - sk_set_ttl(sk, p->cf->multihop ? : 1); bgp_send_open(&p->incoming_conn); return 0; } @@ -656,6 +688,7 @@ bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags) sock *s = sk_new(&root_pool); DBG("BGP: Creating listening socket\n"); s->type = SK_TCP_PASSIVE; + s->ttl = 255; s->saddr = addr; s->sport = port ? port : BGP_PORT; s->flags = flags ? 0 : SKF_V6ONLY; @@ -664,14 +697,15 @@ bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags) s->tbsize = BGP_TX_BUFFER_SIZE; s->rx_hook = bgp_incoming_connection; s->err_hook = bgp_listen_sock_err; - if (sk_open(s)) + + if (sk_open(s) < 0) { log(L_ERR "BGP: Unable to open listening socket"); rfree(s); return NULL; } - else - return s; + + return s; } static void diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 12478709..16e8ea89 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -20,6 +20,7 @@ struct bgp_config { u32 local_as, remote_as; ip_addr remote_ip; int multihop; /* Number of hops if multihop */ + int ttl_security; /* Enable TTL security [RFC5082] */ ip_addr source_addr; /* Source address to use */ int next_hop_self; /* Always set next hop to local IP address */ int missing_lladdr; /* What we will do when we don' know link-local addr, see MLL_* */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 93cc85f6..19d757a5 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -25,7 +25,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, CLUSTER, ID, AS4, ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH, INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP, - TABLE, GATEWAY, DIRECT, RECURSIVE, MED) + TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY) CF_GRAMMAR @@ -71,7 +71,7 @@ bgp_proto: | bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; } | bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; } | bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; } - | bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; } + | bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; if (($3<1) || ($3>255)) cf_error("Multihop must be in range 1-255"); } | bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; } | bgp_proto MISSING LLADDR SELF ';' { BGP_CFG->missing_lladdr = MLL_SELF; } | bgp_proto MISSING LLADDR DROP ';' { BGP_CFG->missing_lladdr = MLL_DROP; } @@ -98,6 +98,7 @@ bgp_proto: | bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; } | bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; } | bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; } + | bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; } ; CF_ADDTO(dynamic_attr, BGP_ORIGIN diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index 847ce261..95f7dcf4 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -237,3 +237,34 @@ sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd) return rv; } + + +#ifndef IPV6 + +static int +sk_set_min_ttl4(sock *s, int ttl) +{ + if (setsockopt(s->fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0) + { + if (errno == ENOPROTOOPT) + log(L_ERR "Kernel does not support IPv4 TTL security"); + else + log(L_ERR "sk_set_min_ttl4: setsockopt: %m"); + + return -1; + } + + return 0; +} + +#else + +static int +sk_set_min_ttl6(sock *s, int ttl) +{ + log(L_ERR "IPv6 TTL security not supported"); + return -1; +} + +#endif + diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index 9c10333a..bb522804 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -309,3 +309,51 @@ sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) */ #endif + + +#ifndef IP_MINTTL +#define IP_MINTTL 21 +#endif + +#ifndef IPV6_MINHOPCOUNT +#define IPV6_MINHOPCOUNT 73 +#endif + + +#ifndef IPV6 + +static int +sk_set_min_ttl4(sock *s, int ttl) +{ + if (setsockopt(s->fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0) + { + if (errno == ENOPROTOOPT) + log(L_ERR "Kernel does not support IPv4 TTL security"); + else + log(L_ERR "sk_set_min_ttl4: setsockopt: %m"); + + return -1; + } + + return 0; +} + +#else + +static int +sk_set_min_ttl6(sock *s, int ttl) +{ + if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_MINHOPCOUNT, &ttl, sizeof(ttl)) < 0) + { + if (errno == ENOPROTOOPT) + log(L_ERR "Kernel does not support IPv6 TTL security"); + else + log(L_ERR "sk_set_min_ttl4: setsockopt: %m"); + + return -1; + } + + return 0; +} + +#endif diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index b6c1b86c..815989f8 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -805,7 +805,7 @@ bad: } /** - * sk_set_ttl - set TTL for given socket. + * sk_set_ttl - set transmit TTL for given socket. * @s: socket * @ttl: TTL value * @@ -828,6 +828,28 @@ sk_set_ttl(sock *s, int ttl) return (err ? -1 : 0); } +/** + * sk_set_min_ttl - set minimal accepted TTL for given socket. + * @s: socket + * @ttl: TTL value + * + * Can be used in TTL security implementation + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_set_min_ttl(sock *s, int ttl) +{ + int err; +#ifdef IPV6 + err = sk_set_min_ttl6(s, ttl); +#else + err = sk_set_min_ttl4(s, ttl); +#endif + + return err; +} /** * sk_set_md5_auth - add / remove MD5 security association for given socket. diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 610d207d..55477913 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "nest/bird.h" #include "lib/lists.h"