diff --git a/TODO b/TODO index 5bca22e0..55f94305 100644 --- a/TODO +++ b/TODO @@ -9,6 +9,8 @@ Core - tagging of external routes? +- when an identical route is received, don't trigger updates + Commands ~~~~~~~~ - showing of routing table as seen by given protocol diff --git a/proto/bgp/Makefile b/proto/bgp/Makefile index 8358bc8a..0f0ba278 100644 --- a/proto/bgp/Makefile +++ b/proto/bgp/Makefile @@ -1,4 +1,4 @@ -source=bgp.c +source=bgp.c attrs.c root-rel=../../ dir-name=proto/bgp diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c new file mode 100644 index 00000000..bf747cbd --- /dev/null +++ b/proto/bgp/attrs.c @@ -0,0 +1,15 @@ +/* + * BIRD -- BGP Attributes + * + * (c) 2000 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "nest/bird.h" +#include "nest/iface.h" +#include "nest/protocol.h" +#include "nest/route.h" +#include "conf/conf.h" + +#include "bgp.h" diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 26e39061..40915a40 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -12,10 +12,20 @@ #include "nest/iface.h" #include "nest/protocol.h" #include "nest/route.h" +#include "nest/locks.h" #include "conf/conf.h" +#include "lib/socket.h" #include "bgp.h" +static sock *bgp_listen_sk; /* Global listening socket */ +static int bgp_counter; /* Number of protocol instances using the listening socket */ +static list bgp_list; /* List of active BGP instances */ + +static void bgp_close_conn(struct bgp_conn *conn); +static void bgp_connect(struct bgp_proto *p); +static void bgp_setup_sk(struct bgp_proto *p, struct bgp_conn *conn, sock *s); + static void bgp_rt_notify(struct proto *P, net *n, rte *new, rte *old, ea_list *tmpa) { @@ -29,18 +39,266 @@ bgp_init(struct proto_config *C) struct bgp_proto *p = (struct bgp_proto *) P; P->rt_notify = bgp_rt_notify; + p->cf = c; + p->local_as = c->local_as; + p->remote_as = c->remote_as; + p->is_internal = (c->local_as == c->remote_as); + p->conn.state = BS_IDLE; + p->incoming_conn.state = BS_IDLE; + p->local_id = C->global->router_id; return P; } +static void +bgp_close(struct bgp_proto *p) +{ + rem_node(&p->bgp_node); + ASSERT(bgp_counter); + bgp_counter--; + if (!bgp_counter) + { + rfree(bgp_listen_sk); + bgp_listen_sk = NULL; + } + /* FIXME: Automatic restart after errors? */ +} + +static void +bgp_reset(struct bgp_proto *p) +{ + bgp_close(p); + proto_notify_state(&p->p, PS_DOWN); +} + +static void +bgp_start_timer(timer *t, int value) +{ + /* FIXME: Randomize properly */ + /* FIXME: Check if anybody uses tm_start directly */ + tm_start(t, value); +} + +static int +bgp_rx(sock *sk, int size) +{ + DBG("BGP: Got %d bytes\n", size); + + return 1; /* Start from the beginning */ +} + +static void +bgp_send_open(struct bgp_conn *conn) +{ + DBG("BGP: Sending open\n"); + conn->sk->rx_hook = bgp_rx; + tm_stop(conn->connect_retry_timer); + /* FIXME */ + conn->state = BS_OPENSENT; +} + +static int +bgp_connected(sock *sk, int dummy) +{ + struct bgp_conn *conn = sk->data; + + DBG("BGP: Connected\n"); + bgp_send_open(conn); + return 0; +} + +static void +bgp_connect_timeout(timer *t) +{ + struct bgp_proto *p = t->data; + struct bgp_conn *conn = &p->conn; + + DBG("BGP: Connect timeout, retrying\n"); + bgp_close_conn(conn); + bgp_connect(p); +} + +static void +bgp_err(sock *sk, int err) +{ + struct bgp_conn *conn = sk->data; + + DBG("BGP: Socket error %d in state %d\n", err, conn->state); + sk->type = SK_DELETED; /* FIXME: Need to do this always! */ + switch (conn->state) + { + case BS_CONNECT: + case BS_OPENSENT: + conn->state = BS_ACTIVE; + bgp_start_timer(conn->connect_retry_timer, conn->bgp->cf->connect_retry_time); + break; + case BS_OPENCONFIRM: + case BS_ESTABLISHED: + /* FIXME: Should close the connection and go to Idle state */ + default: + bug("bgp_err called in invalid state %d", conn->state); + } +} + +static int +bgp_incoming_connection(sock *sk, int dummy) +{ + node *n; + + DBG("BGP: Incoming connection from %I port %d\n", sk->daddr, sk->dport); + WALK_LIST(n, bgp_list) + { + struct bgp_proto *p = SKIP_BACK(struct bgp_proto, bgp_node, n); + if (ipa_equal(p->cf->remote_ip, sk->daddr) && sk->dport == BGP_PORT) + { + DBG("BGP: Authorized\n"); + if (p->incoming_conn.sk) + { + DBG("BGP: But one incoming connection already exists, how is that possible?\n"); + break; + } + bgp_setup_sk(p, &p->incoming_conn, sk); + bgp_send_open(&p->incoming_conn); + return 0; + } + } + DBG("BGP: Unauthorized\n"); + rfree(sk); + return 0; +} + +static void +bgp_setup_sk(struct bgp_proto *p, struct bgp_conn *conn, sock *s) +{ + timer *t; + + s->data = conn; + s->ttl = p->cf->multihop ? : 1; + s->rbsize = BGP_RX_BUFFER_SIZE; +#if 0 + s->tx_hook = bgp_tx; +#endif + s->err_hook = bgp_err; + s->tos = IP_PREC_INTERNET_CONTROL; + + conn->bgp = p; + conn->sk = s; + + t = conn->connect_retry_timer = tm_new(p->p.pool); + t->hook = bgp_connect_timeout; + t->data = p; +#if 0 + t = p->hold_timer = tm_new(p->p.pool); + t->hook = bgp_hold_timeout; + t->data = p; + t = p->keepalive_timer = tm_new(p->p.pool); + t->hook = bgp_keepalive_timeout; + t->data = p; +#endif +} + +static void +bgp_close_conn(struct bgp_conn *conn) +{ + rfree(conn->connect_retry_timer); + conn->connect_retry_timer = NULL; + rfree(conn->keepalive_timer); + conn->keepalive_timer = NULL; + rfree(conn->hold_timer); + conn->hold_timer = NULL; + rfree(conn->sk); + conn->sk = NULL; + conn->state = BS_IDLE; +} + +static void +bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing connection */ +{ + sock *s; + struct bgp_conn *conn = &p->conn; + + DBG("BGP: Connecting\n"); + s = sk_new(p->p.pool); + s->type = SK_TCP_ACTIVE; + s->saddr = _MI(0x3ea80001); /* FIXME: Hack */ + s->daddr = p->cf->remote_ip; +#if 0 + s->sport = /* FIXME */ +#endif + s->dport = BGP_PORT; + s->rx_hook = bgp_connected; + bgp_setup_sk(p, conn, s); + conn->state = BS_CONNECT; + if (sk_open(s)) + { + bgp_err(s, 0); + return; + } + DBG("BGP: Waiting for connect success\n"); + bgp_start_timer(conn->connect_retry_timer, p->cf->connect_retry_time); +} + +static void +bgp_start_locked(struct object_lock *lock) +{ + struct bgp_proto *p = lock->data; + + DBG("BGP: Got lock\n"); + if (!bgp_counter++) + init_list(&bgp_list); + if (!bgp_listen_sk) + { + sock *s = sk_new(&root_pool); + DBG("BGP: Creating incoming socket\n"); + s->type = SK_TCP_PASSIVE; + s->sport = BGP_PORT; + s->tos = IP_PREC_INTERNET_CONTROL; + s->ttl = 1; + s->rbsize = BGP_RX_BUFFER_SIZE; + s->rx_hook = bgp_incoming_connection; + if (sk_open(s)) + { + log(L_ERR "Unable to open incoming BGP socket"); + rfree(s); + } + else + bgp_listen_sk = s; + } + add_tail(&bgp_list, &p->bgp_node); + bgp_connect(p); /* FIXME: Use neighbor cache for fast up/down transitions? */ +} + static int bgp_start(struct proto *P) { - return PS_UP; + struct bgp_proto *p = (struct bgp_proto *) P; + struct object_lock *lock; + + /* + * Before attempting to create the connection, we need to lock the + * port, so that are sure we're the only instance attempting to talk + * with that neighbor. + */ + + DBG("BGP: Startup. Acquiring lock.\n"); + lock = p->lock = olock_new(P->pool); + lock->addr = p->cf->remote_ip; + lock->type = OBJLOCK_TCP; + lock->port = BGP_PORT; + lock->iface = NULL; + lock->hook = bgp_start_locked; + lock->data = p; + olock_acquire(lock); + return PS_START; } static int bgp_shutdown(struct proto *P) { + struct bgp_proto *p = (struct bgp_proto *) P; + + DBG("BGP: Explicit shutdown\n"); + + bgp_close(p); return PS_DOWN; } diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 2e352be4..8e2e8dfe 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -14,18 +14,82 @@ struct bgp_config { unsigned int local_as, remote_as; ip_addr remote_ip; int multihop; /* Number of hops if multihop */ + int connect_retry_time; + int hold_time; + int keepalive_time; +}; + +struct bgp_conn { + struct bgp_proto *bgp; + struct birdsock *sk; + int state; /* State of connection state machine */ + struct timer *connect_retry_timer; + struct timer *hold_timer; + struct timer *keepalive_timer; }; struct bgp_proto { struct proto p; + struct bgp_config *cf; /* Shortcut to BGP configuration */ + node bgp_node; /* Node in global BGP protocol list */ + int local_as, remote_as; + int is_internal; /* Internal BGP connection (local_as == remote_as) */ + u32 local_id; /* BGP identifier of this router */ + u32 remote_id; /* BGP identifier of the neighbor */ + int hold_time; /* Hold time calculated from my and neighbor's requirements */ + struct bgp_conn conn; /* Our primary connection */ + struct bgp_conn incoming_conn; /* Incoming connection we have neither accepted nor rejected yet */ + struct object_lock *lock; /* Lock for neighbor connection */ }; -struct bgp_route { -}; - -struct bgp_attrs { -}; +#define BGP_PORT 179 +#define BGP_RX_BUFFER_SIZE 4096 void bgp_check(struct bgp_config *c); +/* attrs.c */ + +/* packets.c */ + +/* Packet types */ + +#define PKT_OPEN 0x01 +#define PKT_UPDATE 0x02 +#define PKT_NOTIFICATION 0x03 +#define PKT_KEEPALIVE 0x04 + +/* Attributes */ + +#define BAF_OPTIONAL 0x80 +#define BAF_TRANSITIVE 0x40 +#define BAF_PARTIAL 0x20 +#define BAF_EXT_LEN 0x10 + +#define BA_ORIGIN 0x01 /* [RFC1771] */ /* WM */ +#define BA_AS_PATH 0x02 /* WM */ +#define BA_NEXT_HOP 0x03 /* WM */ +#define BA_MULTI_EXIT_DISC 0x04 /* ON */ +#define BA_LOCAL_PREF 0x05 /* WM */ +#define BA_ATOMIC_AGGR 0x06 /* WD */ +#define BA_AGGREGATOR 0x07 /* OT */ +#define BA_COMMUNITY 0x08 /* [RFC1997] */ /* OT */ +#define BA_ORIGINATOR_ID 0x09 /* [RFC1966] */ /* ON */ +#define BA_CLUSTER_LIST 0x0a /* ON */ +/* We don't support these: */ +#define BA_DPA 0x0b /* ??? */ +#define BA_ADVERTISER 0x0c /* [RFC1863] */ +#define BA_RCID_PATH 0x0d +#define BA_MP_REACH_NLRI 0x0e /* [RFC2283] */ +#define BA_MP_UNREACH_NLRI 0x0f +#define BA_EXTENDED_COMM 0x10 /* draft-ramachandra-bgp-ext-communities */ + +/* BGP states */ + +#define BS_IDLE 0 +#define BS_CONNECT 1 /* Attempting to connect */ +#define BS_ACTIVE 2 /* Waiting for connection retry & listening */ +#define BS_OPENSENT 3 +#define BS_OPENCONFIRM 4 +#define BS_ESTABLISHED 5 + #endif diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index f66f3588..f50b0602 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -14,7 +14,8 @@ CF_HDR CF_DECLS -CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS) +CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, + MULTIHOP) CF_GRAMMAR @@ -23,6 +24,9 @@ CF_ADDTO(proto, bgp_proto '}' { bgp_check(BGP_CFG); } ) bgp_proto_start: proto_start BGP { this_proto = proto_config_new(&proto_bgp, sizeof(struct bgp_config)); this_proto->preference = DEF_PREF_BGP; + BGP_CFG->hold_time = 240; + BGP_CFG->connect_retry_time = 120; + BGP_CFG->keepalive_time = 30; } ; @@ -38,6 +42,10 @@ bgp_proto: BGP_CFG->remote_ip = $3; BGP_CFG->remote_as = $5; } + | bgp_proto HOLD TIME NUM ';' { BGP_CFG->hold_time = $4; } + | bgp_proto CONNECT RETRY TIME NUM ';' { BGP_CFG->connect_retry_time = $5; } + | bgp_proto KEEPALIVE TIME NUM ';' { BGP_CFG->connect_retry_time = $4; } + | bgp_proto MULTIHOP NUM ';' { BGP_CFG->multihop = $3; } ; CF_CODE diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c new file mode 100644 index 00000000..a5f52b88 --- /dev/null +++ b/proto/bgp/packets.c @@ -0,0 +1,15 @@ +/* + * BIRD -- BGP Packet Processing + * + * (c) 2000 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "nest/bird.h" +#include "nest/iface.h" +#include "nest/protocol.h" +#include "nest/route.h" +#include "conf/conf.h" + +#include "bgp.h"