summaryrefslogtreecommitdiff
path: root/proto/bgp/bgp.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bgp/bgp.c')
-rw-r--r--proto/bgp/bgp.c260
1 files changed, 259 insertions, 1 deletions
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;
}