bgp.c 31.7 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 *	BIRD -- The Border Gateway Protocol
 *
 *	(c) 2000 Martin Mares <mj@ucw.cz>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

Martin Mareš's avatar
Martin Mareš committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/**
 * DOC: Border Gateway Protocol
 *
 * The BGP protocol is implemented in three parts: |bgp.c| which takes care of the
 * connection and most of the interface with BIRD core, |packets.c| handling
 * both incoming and outgoing BGP packets and |attrs.c| containing functions for
 * manipulation with BGP attribute lists.
 *
 * As opposed to the other existing routing daemons, BIRD has a sophisticated core
 * architecture which is able to keep all the information needed by BGP in the
 * primary routing table, therefore no complex data structures like a central
 * BGP table are needed. This increases memory footprint of a BGP router with
 * many connections, but not too much and, which is more important, it makes
 * BGP much easier to implement.
 *
Martin Mareš's avatar
Martin Mareš committed
24
 * Each instance of BGP (corresponding to a single BGP peer) is described by a &bgp_proto
Martin Mareš's avatar
Martin Mareš committed
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 * structure to which are attached individual connections represented by &bgp_connection
 * (usually, there exists only one connection, but during BGP session setup, there
 * can be more of them). The connections are handled according to the BGP state machine
 * defined in the RFC with all the timers and all the parameters configurable.
 *
 * In incoming direction, we listen on the connection's socket and each time we receive
 * some input, we pass it to bgp_rx(). It decodes packet headers and the markers and
 * passes complete packets to bgp_rx_packet() which distributes the packet according
 * to its type.
 *
 * In outgoing direction, we gather all the routing updates and sort them to buckets
 * (&bgp_bucket) according to their attributes (we keep a hash table for fast comparison
 * of &rta's and a &fib which helps us to find if we already have another route for
 * the same destination queued for sending, so that we can replace it with the new one
 * immediately instead of sending both updates). There also exists a special bucket holding
 * all the route withdrawals which cannot be queued anywhere else as they don't have any
 * attributes. If we have any packet to send (due to either new routes or the connection
Martin Mareš's avatar
Martin Mareš committed
42
 * tracking code wanting to send a Open, Keepalive or Notification message), we call
Martin Mareš's avatar
Martin Mareš committed
43 44 45 46 47 48 49 50 51 52 53 54 55
 * bgp_schedule_packet() which sets the corresponding bit in a @packet_to_send
 * bit field in &bgp_conn and as soon as the transmit socket buffer becomes empty,
 * we call bgp_fire_tx(). It inspects state of all the packet type bits and calls
 * the corresponding bgp_create_xx() functions, eventually rescheduling the same packet
 * type if we have more data of the same type to send.
 *
 * The processing of attributes consists of two functions: bgp_decode_attrs() for checking
 * of the attribute blocks and translating them to the language of BIRD's extended attributes
 * and bgp_encode_attrs() which does the converse. Both functions are built around a
 * @bgp_attr_table array describing all important characteristics of all known attributes.
 * Unknown transitive attributes are attached to the route as %EAF_TYPE_OPAQUE byte streams.
 */

56
#undef LOCAL_DEBUG
57 58 59 60 61

#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
62
#include "nest/locks.h"
63
#include "nest/cli.h"
64
#include "conf/conf.h"
65
#include "lib/socket.h"
66
#include "lib/resource.h"
Martin Mareš's avatar
Martin Mareš committed
67
#include "lib/string.h"
68 69 70

#include "bgp.h"

71
struct linpool *bgp_linpool;		/* Global temporary pool */
72 73 74
static sock *bgp_listen_sk;		/* Global listening socket */
static int bgp_counter;			/* Number of protocol instances using the listening socket */

Ondřej Zajíček's avatar
Ondřej Zajíček committed
75
static void bgp_close(struct bgp_proto *p, int apply_md5);
76
static void bgp_connect(struct bgp_proto *p);
77
static void bgp_active(struct bgp_proto *p);
78
static sock *bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags);
79

80

Ondřej Zajíček's avatar
Ondřej Zajíček committed
81 82 83 84 85 86 87 88 89 90 91 92 93
/**
 * bgp_open - open a BGP instance
 * @p: BGP instance
 *
 * This function allocates and configures shared BGP resources.
 * Should be called as the last step during initialization
 * (when lock is acquired and neighbor is ready).
 * When error, state changed to PS_DOWN, -1 is returned and caller
 * should return immediately.
 */
static int
bgp_open(struct bgp_proto *p)
{
94
  struct config *cfg = p->cf->c.global;
95 96
  int errcode;

Ondřej Zajíček's avatar
Ondřej Zajíček committed
97 98 99
  bgp_counter++;

  if (!bgp_listen_sk)
100
    bgp_listen_sk = bgp_setup_listen_sk(cfg->listen_bgp_addr, cfg->listen_bgp_port, cfg->listen_bgp_flags);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
101

102 103 104
  if (!bgp_listen_sk)
    {
      bgp_counter--;
105 106
      errcode = BEM_NO_SOCKET;
      goto err;
107 108
    }

Ondřej Zajíček's avatar
Ondřej Zajíček committed
109 110 111 112 113
  if (!bgp_linpool)
    bgp_linpool = lp_new(&root_pool, 4080);

  if (p->cf->password)
    {
114
      int rv = sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, p->cf->password);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
115 116 117
      if (rv < 0)
	{
	  bgp_close(p, 0);
118 119
	  errcode = BEM_INVALID_MD5;
	  goto err;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
120 121 122 123
	}
    }

  return 0;
124 125 126 127 128 129

err:
  p->p.disabled = 1;
  bgp_store_error(p, NULL, BE_MISC, errcode);
  proto_notify_state(&p->p, PS_DOWN);
  return -1;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
130 131
}

132 133 134 135 136
static void
bgp_startup(struct bgp_proto *p)
{
  BGP_TRACE(D_EVENTS, "Started");
  p->start_state = p->cf->capabilities ? BSS_CONNECT : BSS_CONNECT_NOCAP;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
137 138 139

  if (!p->cf->passive)
    bgp_active(p);
140 141 142 143 144 145 146 147 148 149 150 151
}

static void
bgp_startup_timeout(timer *t)
{
  bgp_startup(t->data);
}


static void
bgp_initiate(struct bgp_proto *p)
{
152 153 154 155
  int rv = bgp_open(p);
  if (rv < 0)
    return;

156 157 158 159 160 161 162 163 164
  if (p->startup_delay)
    {
      BGP_TRACE(D_EVENTS, "Startup delayed by %d seconds", p->startup_delay);
      bgp_start_timer(p->startup_timer, p->startup_delay);
    }
  else
    bgp_startup(p);
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
165 166 167 168 169 170 171 172 173
/**
 * bgp_close - close a BGP instance
 * @p: BGP instance
 * @apply_md5: 0 to disable unsetting MD5 auth
 *
 * This function frees and deconfigures shared BGP resources.
 * @apply_md5 is set to 0 when bgp_close is called as a cleanup
 * from failed bgp_open().
 */
174
static void
Ondřej Zajíček's avatar
Ondřej Zajíček committed
175
bgp_close(struct bgp_proto *p, int apply_md5)
176 177 178
{
  ASSERT(bgp_counter);
  bgp_counter--;
179

Ondřej Zajíček's avatar
Ondřej Zajíček committed
180
  if (p->cf->password && apply_md5)
181
    sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, NULL);
182

183 184 185 186
  if (!bgp_counter)
    {
      rfree(bgp_listen_sk);
      bgp_listen_sk = NULL;
187 188
      rfree(bgp_linpool);
      bgp_linpool = NULL;
189 190 191
    }
}

Martin Mareš's avatar
Martin Mareš committed
192 193 194 195 196 197 198 199 200
/**
 * bgp_start_timer - start a BGP timer
 * @t: timer
 * @value: time to fire (0 to disable the timer)
 *
 * This functions calls tm_start() on @t with time @value and the
 * amount of randomization suggested by the BGP standard. Please use
 * it for all BGP timers.
 */
201
void
202 203
bgp_start_timer(timer *t, int value)
{
204
  if (value)
205 206 207 208 209
    {
      /* The randomization procedure is specified in RFC 1771: 9.2.3.3 */
      t->randomize = value / 4;
      tm_start(t, value - t->randomize);
    }
210 211 212 213
  else
    tm_stop(t);
}

Martin Mareš's avatar
Martin Mareš committed
214 215 216 217 218 219 220
/**
 * bgp_close_conn - close a BGP connection
 * @conn: connection to close
 *
 * This function takes a connection described by the &bgp_conn structure,
 * closes its socket and frees all resources associated with it.
 */
221 222 223
void
bgp_close_conn(struct bgp_conn *conn)
{
224
  // struct bgp_proto *p = conn->bgp;
225 226 227 228 229 230 231 232 233

  DBG("BGP: Closing connection\n");
  conn->packets_to_send = 0;
  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;
234
  rfree(conn->sk);
235
  conn->sk = NULL;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
  rfree(conn->tx_ev);
  conn->tx_ev = NULL;
}


/**
 * bgp_update_startup_delay - update a startup delay
 * @p: BGP instance
 *
 * This function updates a startup delay that is used to postpone next BGP connect.
 * It also handles disable_after_error and might stop BGP instance when error
 * happened and disable_after_error is on.
 *
 * It should be called when BGP protocol error happened.
 */
void
252
bgp_update_startup_delay(struct bgp_proto *p)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
253 254 255
{
  struct bgp_config *cf = p->cf;

256
  DBG("BGP: Updating startup delay\n");
Ondřej Zajíček's avatar
Ondřej Zajíček committed
257

258
  if (p->last_proto_error && ((now - p->last_proto_error) >= (int) cf->error_amnesia_time))
259 260
    p->startup_delay = 0;

Ondřej Zajíček's avatar
Ondřej Zajíček committed
261 262 263 264 265 266 267
  p->last_proto_error = now;

  if (cf->disable_after_error)
    {
      p->startup_delay = 0;
      p->p.disabled = 1;
      return;
268
    }
Ondřej Zajíček's avatar
Ondřej Zajíček committed
269 270 271 272

  if (!p->startup_delay)
    p->startup_delay = cf->error_delay_time_min;
  else
273
    p->startup_delay = MIN(2 * p->startup_delay, cf->error_delay_time_max);
274 275
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
276
static void
277
bgp_graceful_close_conn(struct bgp_conn *conn, unsigned subcode)
278
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
279
  switch (conn->state)
280 281
    {
    case BS_IDLE:
Ondřej Zajíček's avatar
Ondřej Zajíček committed
282 283
    case BS_CLOSE:
      return;
284 285
    case BS_CONNECT:
    case BS_ACTIVE:
Ondřej Zajíček's avatar
Ondřej Zajíček committed
286 287
      bgp_conn_enter_idle_state(conn);
      return;
288 289 290
    case BS_OPENSENT:
    case BS_OPENCONFIRM:
    case BS_ESTABLISHED:
291
      bgp_error(conn, 6, subcode, NULL, 0);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
292
      return;
293
    default:
Ondřej Zajíček's avatar
Ondřej Zajíček committed
294
      bug("bgp_graceful_close_conn: Unknown state %d", conn->state);
295 296 297
    }
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
298 299 300 301 302 303
static void
bgp_down(struct bgp_proto *p)
{
  if (p->start_state > BSS_PREPARE)
    bgp_close(p, 1);

304
  BGP_TRACE(D_EVENTS, "Down");
Ondřej Zajíček's avatar
Ondřej Zajíček committed
305 306 307 308 309 310 311 312 313 314
  proto_notify_state(&p->p, PS_DOWN);
}

static void
bgp_decision(void *vp)
{
  struct bgp_proto *p = vp;

  DBG("BGP: Decision start\n");
  if ((p->p.proto_state == PS_START)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
315 316
      && (p->outgoing_conn.state == BS_IDLE)
      && (!p->cf->passive))
317
    bgp_active(p);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
318 319 320 321 322 323 324

  if ((p->p.proto_state == PS_STOP)
      && (p->outgoing_conn.state == BS_IDLE)
      && (p->incoming_conn.state == BS_IDLE))
    bgp_down(p);
}

325 326
void
bgp_stop(struct bgp_proto *p, unsigned subcode)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
327 328
{
  proto_notify_state(&p->p, PS_STOP);
329 330
  bgp_graceful_close_conn(&p->outgoing_conn, subcode);
  bgp_graceful_close_conn(&p->incoming_conn, subcode);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
331 332 333
  ev_schedule(p->event);
}

334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
static inline void
bgp_conn_set_state(struct bgp_conn *conn, unsigned new_state)
{
  if (conn->bgp->p.mrtdump & MD_STATES)
    mrt_dump_bgp_state_change(conn, conn->state, new_state);

  conn->state = new_state;
}

void
bgp_conn_enter_openconfirm_state(struct bgp_conn *conn)
{
  /* Really, most of the work is done in bgp_rx_open(). */
  bgp_conn_set_state(conn, BS_OPENCONFIRM);
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
350 351 352 353 354 355 356 357
void
bgp_conn_enter_established_state(struct bgp_conn *conn)
{
  struct bgp_proto *p = conn->bgp;
 
  BGP_TRACE(D_EVENTS, "BGP session established");
  DBG("BGP: UP!!!\n");

358 359 360 361
  /* For multi-hop BGP sessions */
  if (ipa_zero(p->source_addr))
    p->source_addr = conn->sk->saddr; 

Ondřej Zajíček's avatar
Ondřej Zajíček committed
362 363 364 365
  p->conn = conn;
  p->last_error_class = 0;
  p->last_error_code = 0;
  bgp_attr_init(conn->bgp);
366
  bgp_conn_set_state(conn, BS_ESTABLISHED);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
367 368 369 370 371 372 373 374 375 376
  proto_notify_state(&p->p, PS_UP);
}

static void
bgp_conn_leave_established_state(struct bgp_proto *p)
{
  BGP_TRACE(D_EVENTS, "BGP session closed");
  p->conn = NULL;

  if (p->p.proto_state == PS_UP)
377
    bgp_stop(p, 0);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
378 379 380 381 382 383 384 385
}

void
bgp_conn_enter_close_state(struct bgp_conn *conn)
{
  struct bgp_proto *p = conn->bgp;
  int os = conn->state;

386
  bgp_conn_set_state(conn, BS_CLOSE);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
  tm_stop(conn->hold_timer);
  tm_stop(conn->keepalive_timer);
  conn->sk->rx_hook = NULL;

  if (os == BS_ESTABLISHED)
    bgp_conn_leave_established_state(p);
}

void
bgp_conn_enter_idle_state(struct bgp_conn *conn)
{
  struct bgp_proto *p = conn->bgp;
  int os = conn->state;

  bgp_close_conn(conn);
402
  bgp_conn_set_state(conn, BS_IDLE);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
403 404 405 406 407 408
  ev_schedule(p->event);

  if (os == BS_ESTABLISHED)
    bgp_conn_leave_established_state(p);
}

409 410 411
static void
bgp_send_open(struct bgp_conn *conn)
{
412 413 414
  conn->start_state = conn->bgp->start_state;
  conn->want_as4_support = conn->bgp->cf->enable_as4 && (conn->start_state != BSS_CONNECT_NOCAP);
  conn->peer_as4_support = 0;	// Default value, possibly changed by receiving capability.
415
  conn->advertised_as = 0;
416

417 418
  DBG("BGP: Sending open\n");
  conn->sk->rx_hook = bgp_rx;
419
  conn->sk->tx_hook = bgp_tx;
420
  tm_stop(conn->connect_retry_timer);
421
  bgp_schedule_packet(conn, PKT_OPEN);
422
  bgp_conn_set_state(conn, BS_OPENSENT);
423
  bgp_start_timer(conn->hold_timer, conn->bgp->cf->initial_hold_time);
424 425
}

426 427
static void
bgp_connected(sock *sk)
428 429
{
  struct bgp_conn *conn = sk->data;
Martin Mareš's avatar
Martin Mareš committed
430
  struct bgp_proto *p = conn->bgp;
431

Martin Mareš's avatar
Martin Mareš committed
432
  BGP_TRACE(D_EVENTS, "Connected");
433 434 435 436 437 438
  bgp_send_open(conn);
}

static void
bgp_connect_timeout(timer *t)
{
439
  struct bgp_conn *conn = t->data;
Martin Mareš's avatar
Martin Mareš committed
440
  struct bgp_proto *p = conn->bgp;
441

Martin Mareš's avatar
Martin Mareš committed
442
  DBG("BGP: connect_timeout\n");
Ondřej Zajíček's avatar
Ondřej Zajíček committed
443 444 445 446 447 448 449
  if (p->p.proto_state == PS_START)
    {
      bgp_close_conn(conn);
      bgp_connect(p);
    }
  else
    bgp_conn_enter_idle_state(conn);
450 451 452
}

static void
453
bgp_sock_err(sock *sk, int err)
454 455
{
  struct bgp_conn *conn = sk->data;
Martin Mareš's avatar
Martin Mareš committed
456
  struct bgp_proto *p = conn->bgp;
457

458 459 460 461 462 463 464 465 466
  /*
   * This error hook may be called either asynchronously from main
   * loop, or synchronously from sk_send().  But sk_send() is called
   * only from bgp_tx() and bgp_kick_tx(), which are both called
   * asynchronously from main loop. Moreover, they end if err hook is
   * called. Therefore, we could suppose that it is always called
   * asynchronously.
   */

Ondřej Zajíček's avatar
Ondřej Zajíček committed
467 468
  bgp_store_error(p, conn, BE_SOCKET, err);

469 470 471 472
  if (err)
    BGP_TRACE(D_EVENTS, "Connection lost (%M)", err);
  else
    BGP_TRACE(D_EVENTS, "Connection closed");
Ondřej Zajíček's avatar
Ondřej Zajíček committed
473 474

  bgp_conn_enter_idle_state(conn);
475 476
}

477 478 479 480 481
static void
bgp_hold_timeout(timer *t)
{
  struct bgp_conn *conn = t->data;

482 483 484 485 486 487 488 489 490
  DBG("BGP: Hold timeout\n");

  /* If there is something in input queue, we are probably congested
     and perhaps just not processed BGP packets in time. */

  if (sk_rx_ready(conn->sk) > 0)
    bgp_start_timer(conn->hold_timer, 10);
  else
    bgp_error(conn, 4, 0, NULL, 0);
491 492 493 494 495 496 497 498 499 500 501
}

static void
bgp_keepalive_timeout(timer *t)
{
  struct bgp_conn *conn = t->data;

  DBG("BGP: Keepalive timer\n");
  bgp_schedule_packet(conn, PKT_KEEPALIVE);
}

502
static void
503
bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn)
504 505 506
{
  timer *t;

507
  conn->sk = NULL;
508
  conn->bgp = p;
509
  conn->packets_to_send = 0;
510 511 512

  t = conn->connect_retry_timer = tm_new(p->p.pool);
  t->hook = bgp_connect_timeout;
513 514
  t->data = conn;
  t = conn->hold_timer = tm_new(p->p.pool);
515
  t->hook = bgp_hold_timeout;
516 517
  t->data = conn;
  t = conn->keepalive_timer = tm_new(p->p.pool);
518
  t->hook = bgp_keepalive_timeout;
519
  t->data = conn;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
520 521 522
  conn->tx_ev = ev_new(p->p.pool);
  conn->tx_ev->hook = bgp_kick_tx;
  conn->tx_ev->data = conn;
523 524
}

525
static void
526
bgp_setup_sk(struct bgp_conn *conn, sock *s)
527 528 529 530 531 532
{
  s->data = conn;
  s->err_hook = bgp_sock_err;
  conn->sk = s;
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
533
static void
534
bgp_active(struct bgp_proto *p)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
535
{
536
  int delay = MAX(1, p->cf->start_delay_time);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
537 538 539 540
  struct bgp_conn *conn = &p->outgoing_conn;

  BGP_TRACE(D_EVENTS, "Connect delayed by %d seconds", delay);
  bgp_setup_conn(p, conn);
541
  bgp_conn_set_state(conn, BS_ACTIVE);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
542 543 544
  bgp_start_timer(conn->connect_retry_timer, delay);
}

545 546 547 548 549 550 551
int
bgp_apply_limits(struct bgp_proto *p)
{
  if (p->cf->route_limit && (p->p.stats.imp_routes > p->cf->route_limit))
    {
      log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name);
      bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED);
552 553
      bgp_update_startup_delay(p);
      bgp_stop(p, 1); // Errcode 6, 1 - max number of prefixes reached
554 555 556 557 558 559 560
      return -1;
    }

  return 0;
}


Martin Mareš's avatar
Martin Mareš committed
561 562 563 564 565 566 567 568
/**
 * bgp_connect - initiate an outgoing connection
 * @p: BGP instance
 *
 * The bgp_connect() function creates a new &bgp_conn and initiates
 * a TCP connection to the peer. The rest of connection setup is governed
 * by the BGP state machine as described in the standard.
 */
569 570 571 572
static void
bgp_connect(struct bgp_proto *p)	/* Enter Connect state and start establishing connection */
{
  sock *s;
573
  struct bgp_conn *conn = &p->outgoing_conn;
574
  int hops = p->cf->multihop ? : 1;
575 576 577 578

  DBG("BGP: Connecting\n");
  s = sk_new(p->p.pool);
  s->type = SK_TCP_ACTIVE;
579
  s->saddr = p->source_addr;
580
  s->daddr = p->cf->remote_ip;
581
  s->iface = p->neigh ? p->neigh->iface : NULL;
582
  s->dport = BGP_PORT;
583
  s->ttl = p->cf->ttl_security ? 255 : hops;
584 585 586 587 588
  s->rbsize = BGP_RX_BUFFER_SIZE;
  s->tbsize = BGP_TX_BUFFER_SIZE;
  s->tos = IP_PREC_INTERNET_CONTROL;
  s->password = p->cf->password;
  s->tx_hook = bgp_connected;
589 590
  BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J", s->daddr, p->cf->iface,
	    s->saddr, ipa_has_link_scope(s->saddr) ? s->iface : NULL);
591
  bgp_setup_conn(p, conn);
592
  bgp_setup_sk(conn, s);
593
  bgp_conn_set_state(conn, BS_CONNECT);
594 595 596 597 598 599 600 601 602 603 604 605

  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)
606
    {
607
      log(L_ERR "TTL security configuration failed, closing session");
608
      bgp_sock_err(s, 0);
609 610
      return;
    }
611 612
  }

613 614 615 616
  DBG("BGP: Waiting for connect success\n");
  bgp_start_timer(conn->connect_retry_timer, p->cf->connect_retry_time);
}

Martin Mareš's avatar
Martin Mareš committed
617 618 619 620 621 622 623 624 625 626 627 628
/**
 * bgp_incoming_connection - handle an incoming connection
 * @sk: TCP socket
 * @dummy: unused
 *
 * This function serves as a socket hook for accepting of new BGP
 * connections. It searches a BGP instance corresponding to the peer
 * which has connected and if such an instance exists, it creates a
 * &bgp_conn structure, attaches it to the instance and either sends
 * an Open message or (if there already is an active connection) it
 * closes the new connection by sending a Notification message.
 */
629
static int
Martin Mareš's avatar
Martin Mareš committed
630
bgp_incoming_connection(sock *sk, int dummy UNUSED)
631
{
632
  struct proto_config *pc;
633

634
  DBG("BGP: Incoming connection from %I port %d\n", sk->daddr, sk->dport);
635 636 637 638
  WALK_LIST(pc, config->protos)
    if (pc->protocol == &proto_bgp && pc->proto)
      {
	struct bgp_proto *p = (struct bgp_proto *) pc->proto;
639 640
	if (ipa_equal(p->cf->remote_ip, sk->daddr) &&
	    (!ipa_has_link_scope(sk->daddr) || (p->cf->iface == sk->iface)))
641
	  {
642 643 644 645
	    /* We are in proper state and there is no other incoming connection */
	    int acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) &&
	      (p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk);

646 647 648
	    BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s",
		      sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL,
		      sk->dport, acc ? "accepted" : "rejected");
649 650 651 652

	    if (!acc)
	      goto err;

653 654 655 656 657 658 659 660 661 662 663 664 665 666
	    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);

667
	    bgp_setup_conn(p, &p->incoming_conn);
668
	    bgp_setup_sk(&p->incoming_conn, sk);
669 670
	    bgp_send_open(&p->incoming_conn);
	    return 0;
671 672
	  }
      }
673

674 675
  log(L_WARN "BGP: Unexpected connect from unknown address %I%J (port %d)",
      sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL, sk->dport);
676
 err:
677 678 679 680
  rfree(sk);
  return 0;
}

681
static void
682
bgp_listen_sock_err(sock *sk UNUSED, int err)
683 684 685 686
{
  if (err == ECONNABORTED)
    log(L_WARN "BGP: Incoming connection aborted");
  else
687
    log(L_ERR "BGP: Error on listening socket: %M", err);
688 689
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
690
static sock *
691
bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags)
692
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
693
  sock *s = sk_new(&root_pool);
694
  DBG("BGP: Creating listening socket\n");
Ondřej Zajíček's avatar
Ondřej Zajíček committed
695
  s->type = SK_TCP_PASSIVE;
696
  s->ttl = 255;
697 698
  s->saddr = addr;
  s->sport = port ? port : BGP_PORT;
699
  s->flags = flags ? 0 : SKF_V6ONLY;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
700 701 702 703
  s->tos = IP_PREC_INTERNET_CONTROL;
  s->rbsize = BGP_RX_BUFFER_SIZE;
  s->tbsize = BGP_TX_BUFFER_SIZE;
  s->rx_hook = bgp_incoming_connection;
704
  s->err_hook = bgp_listen_sock_err;
705 706

  if (sk_open(s) < 0)
707
    {
708
      log(L_ERR "BGP: Unable to open listening socket");
Ondřej Zajíček's avatar
Ondřej Zajíček committed
709 710
      rfree(s);
      return NULL;
711
    }
712 713

  return s;
714 715 716 717 718
}

static void
bgp_start_neighbor(struct bgp_proto *p)
{
719 720
  /* Called only for single-hop BGP sessions */

721
  /* Remove this ? */
722 723
  if (ipa_zero(p->source_addr))
    p->source_addr = p->neigh->iface->addr->ip; 
724

725 726 727
#ifdef IPV6
  {
    struct ifa *a;
728
    p->local_link = IPA_NONE;
729 730 731 732 733 734
    WALK_LIST(a, p->neigh->iface->addrs)
      if (a->scope == SCOPE_LINK)
        {
	  p->local_link = a->ip;
	  break;
	}
735 736 737 738

    if (! ipa_nonzero(p->local_link))
      log(L_WARN "%s: Missing link local address on interface %s", p->p.name,  p->neigh->iface->name);

739 740 741
    DBG("BGP: Selected link-level address %I\n", p->local_link);
  }
#endif
Ondřej Zajíček's avatar
Ondřej Zajíček committed
742

743
  bgp_initiate(p);
744 745 746 747 748 749 750
}

static void
bgp_neigh_notify(neighbor *n)
{
  struct bgp_proto *p = (struct bgp_proto *) n->proto;

751
  if (n->scope > 0)
752
    {
Ondřej Zajíček's avatar
Ondřej Zajíček committed
753 754 755 756 757
      if ((p->p.proto_state == PS_START) && (p->start_state == BSS_PREPARE))
	{
	  BGP_TRACE(D_EVENTS, "Neighbor found");
	  bgp_start_neighbor(p);
	}
758 759 760
    }
  else
    {
Ondřej Zajíček's avatar
Ondřej Zajíček committed
761 762 763 764
      if ((p->p.proto_state == PS_START) || (p->p.proto_state == PS_UP))
	{
	  BGP_TRACE(D_EVENTS, "Neighbor lost");
	  bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST);
765
	  bgp_stop(p, 0);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
766
	}
767 768 769
    }
}

770 771 772 773 774 775 776 777 778 779 780
static int
bgp_reload_routes(struct proto *P)
{
  struct bgp_proto *p = (struct bgp_proto *) P;
  if (!p->conn || !p->conn->peer_refresh_support)
    return 0;

  bgp_schedule_packet(p->conn, PKT_ROUTE_REFRESH);
  return 1;
}

781 782 783 784 785 786
static void
bgp_start_locked(struct object_lock *lock)
{
  struct bgp_proto *p = lock->data;
  struct bgp_config *cf = p->cf;

Ondřej Zajíček's avatar
Ondřej Zajíček committed
787 788 789
  if (p->p.proto_state != PS_START)
    {
      DBG("BGP: Got lock in different state %d\n", p->p.proto_state);
790
      return;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
791 792
    }

793
  DBG("BGP: Got lock\n");
794

795
  if (cf->multihop)
796
    {
797 798 799
      /* Multi-hop sessions do not use neighbor entries */
      bgp_initiate(p);
      return;
800 801
    }

802
  p->neigh = neigh_find2(&p->p, &cf->remote_ip, cf->iface, NEF_STICKY);
803
  if (!p->neigh || (p->neigh->scope == SCOPE_HOST))
804
    {
805
      log(L_ERR "%s: Invalid remote address %I%J", p->p.name, cf->remote_ip, cf->iface);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
806
      /* As we do not start yet, we can just disable protocol */
807
      p->p.disabled = 1;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
808
      bgp_store_error(p, NULL, BE_MISC, BEM_INVALID_NEXT_HOP);
809
      proto_notify_state(&p->p, PS_DOWN);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
810
      return;
811
    }
Ondřej Zajíček's avatar
Ondřej Zajíček committed
812
  
813
  if (p->neigh->scope > 0)
814 815
    bgp_start_neighbor(p);
  else
816
    BGP_TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", cf->remote_ip, cf->iface);
817 818
}

819 820 821
static int
bgp_start(struct proto *P)
{
822 823 824
  struct bgp_proto *p = (struct bgp_proto *) P;
  struct object_lock *lock;

825
  DBG("BGP: Startup.\n");
Ondřej Zajíček's avatar
Ondřej Zajíček committed
826
  p->start_state = BSS_PREPARE;
827 828
  p->outgoing_conn.state = BS_IDLE;
  p->incoming_conn.state = BS_IDLE;
829
  p->neigh = NULL;
830

831 832
  rt_lock_table(p->igp_table);

Ondřej Zajíček's avatar
Ondřej Zajíček committed
833 834 835
  p->event = ev_new(p->p.pool);
  p->event->hook = bgp_decision;
  p->event->data = p;
836

837 838 839 840
  p->startup_timer = tm_new(p->p.pool);
  p->startup_timer->hook = bgp_startup_timeout;
  p->startup_timer->data = p;

841 842 843 844
  p->local_id = proto_get_router_id(P->cf);
  if (p->rr_client)
    p->rr_cluster_id = p->cf->rr_cluster_id ? p->cf->rr_cluster_id : p->local_id;

845 846 847
  p->remote_id = 0;
  p->source_addr = p->cf->source_addr;

848 849 850 851 852 853 854 855
  /*
   *  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.
   */

  lock = p->lock = olock_new(P->pool);
  lock->addr = p->cf->remote_ip;
856
  lock->iface = p->cf->iface;
857 858 859 860 861 862
  lock->type = OBJLOCK_TCP;
  lock->port = BGP_PORT;
  lock->iface = NULL;
  lock->hook = bgp_start_locked;
  lock->data = p;
  olock_acquire(lock);
863

864
  return PS_START;
865 866 867 868 869
}

static int
bgp_shutdown(struct proto *P)
{
870
  struct bgp_proto *p = (struct bgp_proto *) P;
871
  unsigned subcode;
872

Martin Mareš's avatar
Martin Mareš committed
873
  BGP_TRACE(D_EVENTS, "Shutdown requested");
Ondřej Zajíček's avatar
Ondřej Zajíček committed
874
  bgp_store_error(p, NULL, BE_MAN_DOWN, 0);
875 876 877 878 879 880 881 882 883 884 885

  if (P->reconfiguring)
    {
      if (P->cf_new)
	subcode = 6; // Errcode 6, 6 - other configuration change
      else
	subcode = 3; // Errcode 6, 3 - peer de-configured
    }
  else
    subcode = 2; // Errcode 6, 2 - administrative shutdown

Ondřej Zajíček's avatar
Ondřej Zajíček committed
886
  p->startup_delay = 0;
887
  bgp_stop(p, subcode);
888

Ondřej Zajíček's avatar
Ondřej Zajíček committed
889
  return p->p.proto_state;
890 891
}

892 893 894 895 896 897 898 899 900 901 902 903 904
static void
bgp_cleanup(struct proto *P)
{
  struct bgp_proto *p = (struct bgp_proto *) P;
  rt_unlock_table(p->igp_table);
}

static rtable *
get_igp_table(struct bgp_config *cf)
{
  return cf->igp_table ? cf->igp_table->table : cf->c.table->table;
}

905 906 907 908 909 910 911
static struct proto *
bgp_init(struct proto_config *C)
{
  struct bgp_config *c = (struct bgp_config *) C;
  struct proto *P = proto_new(C, sizeof(struct bgp_proto));
  struct bgp_proto *p = (struct bgp_proto *) P;

912
  P->accept_ra_types = RA_OPTIMAL;
913 914 915 916
  P->rt_notify = bgp_rt_notify;
  P->rte_better = bgp_rte_better;
  P->import_control = bgp_import_control;
  P->neigh_notify = bgp_neigh_notify;
917
  P->reload_routes = bgp_reload_routes;
918 919 920 921

  if (c->deterministic_med)
    P->rte_recalculate = bgp_rte_recalculate;

922 923 924 925
  p->cf = c;
  p->local_as = c->local_as;
  p->remote_as = c->remote_as;
  p->is_internal = (c->local_as == c->remote_as);
926 927
  p->rs_client = c->rs_client;
  p->rr_client = c->rr_client;
928
  p->igp_table = get_igp_table(c);
929

930 931 932
  return P;
}

933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960

void
bgp_check_config(struct bgp_config *c)
{
  int internal = (c->local_as == c->remote_as);

  /* Do not check templates at all */
  if (c->c.class == SYM_TEMPLATE)
    return;

  if (!c->local_as)
    cf_error("Local AS number must be set");

  if (!c->remote_as)
    cf_error("Neighbor must be configured");

  if (!(c->capabilities && c->enable_as4) && (c->remote_as > 0xFFFF))
    cf_error("Neighbor AS number out of range (AS4 not available)");

  if (!internal && c->rr_client)
    cf_error("Only internal neighbor can be RR client");

  if (internal && c->rs_client)
    cf_error("Only external neighbor can be RS client");

  if (c->multihop && (c->gw_mode == GW_DIRECT))
    cf_error("Multihop BGP cannot use direct gateway mode");

961 962 963 964
  if (c->multihop && (ipa_has_link_scope(c->remote_ip) || 
		      ipa_has_link_scope(c->source_addr)))
    cf_error("Multihop BGP cannot be used with link-local addresses");

965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
  /* Different default based on rs_client */
  if (!c->missing_lladdr)
    c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;

  /* Different default for gw_mode */
  if (!c->gw_mode)
    c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT;
}

static int
bgp_reconfigure(struct proto *P, struct proto_config *C)
{
  struct bgp_config *new = (struct bgp_config *) C;
  struct bgp_proto *p = (struct bgp_proto *) P;
  struct bgp_config *old = p->cf;

  int same = !memcmp(((byte *) old) + sizeof(struct proto_config),
		     ((byte *) new) + sizeof(struct proto_config),
		     // password item is last and must be checked separately
		     OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
    && ((!old->password && !new->password)
	|| (old->password && new->password && !strcmp(old->password, new->password)))
    && (get_igp_table(old) == get_igp_table(new));

  /* We should update our copy of configuration ptr as old configuration will be freed */
  if (same)
    p->cf = new;

  return same;
}

static void
bgp_copy_config(struct proto_config *dest, struct proto_config *src)
{
  /* Just a shallow copy */
  proto_copy_rest(dest, src, sizeof(struct bgp_config));
}


Martin Mareš's avatar
Martin Mareš committed
1004 1005 1006 1007
/**
 * bgp_error - report a protocol error
 * @c: connection
 * @code: error code (according to the RFC)
1008
 * @subcode: error sub-code
Martin Mareš's avatar
Martin Mareš committed
1009 1010 1011 1012
 * @data: data to be passed in the Notification message
 * @len: length of the data
 *
 * bgp_error() sends a notification packet to tell the other side that a protocol
1013
 * error has occurred (including the data considered erroneous if possible) and
Martin Mareš's avatar
Martin Mareš committed
1014 1015
 * closes the connection.
 */
1016
void
1017
bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len)
1018
{
1019 1020
  struct bgp_proto *p = c->bgp;

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1021
  if (c->state == BS_CLOSE)
1022
    return;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1023

1024 1025
  bgp_log_error(p, BE_BGP_TX, "Error", code, subcode, data, (len > 0) ? len : -len);
  bgp_store_error(p, c, BE_BGP_TX, (code << 16) | subcode);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1026 1027
  bgp_conn_enter_close_state(c);

1028 1029
  c->notify_code = code;
  c->notify_subcode = subcode;
1030 1031
  c->notify_data = data;
  c->notify_size = (len > 0) ? len : 0;
1032
  bgp_schedule_packet(c, PKT_NOTIFICATION);
1033 1034 1035 1036 1037 1038

  if (code != 6)
    {
      bgp_update_startup_delay(p);
      bgp_stop(p, 0);
    }
1039 1040
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
/**
 * bgp_store_error - store last error for status report
 * @p: BGP instance
 * @c: connection
 * @class: error class (BE_xxx constants)
 * @code: error code (class specific)
 *
 * bgp_store_error() decides whether given error is interesting enough
 * and store that error to last_error variables of @p
 */
void
bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code)
{
  /* During PS_UP, we ignore errors on secondary connection */
  if ((p->p.proto_state == PS_UP) && c && (c != p->conn))
    return;

  /* During PS_STOP, we ignore any errors, as we want to report
   * the error that caused transition to PS_STOP
   */
  if (p->p.proto_state == PS_STOP)
    return;

  p->last_error_class = class;
  p->last_error_code = code;
}

static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" };
1069
static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown: ", ""};
1070
static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed", "No listening socket" };
1071
static char *bgp_auto_errors[] = { "", "Route limit exceeded"};
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1072

1073 1074
static const char *
bgp_last_errmsg(struct bgp_proto *p)
1075
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1076 1077 1078
  switch (p->last_error_class)
    {
    case BE_MISC:
1079
      return bgp_misc_errors[p->last_error_code];
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1080
    case BE_SOCKET:
1081
      return (p->last_error_code == 0) ? "Connection closed" : strerror(p->last_error_code);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1082 1083
    case BE_BGP_RX:
    case BE_BGP_TX:
1084
      return bgp_error_dsc(p->last_error_code >> 16, p->last_error_code & 0xFF);
1085
    case BE_AUTO_DOWN:
1086 1087 1088
      return bgp_auto_errors[p->last_error_code];
    default:
      return "";
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1089
    }
1090 1091 1092 1093 1094
}

static const char *
bgp_state_dsc(struct bgp_proto *p)
{
1095 1096
  if (p->p.proto_state == PS_DOWN)
    return "Down";
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111

  int state = MAX(p->incoming_conn.state, p->outgoing_conn.state);
  if ((state == BS_IDLE) && (p->start_state >= BSS_CONNECT) && p->cf->passive)
    return "Passive";

  return bgp_state_names[state];
}

static void
bgp_get_status(struct proto *P, byte *buf)
{
  struct bgp_proto *p = (struct bgp_proto *) P;

  const char *err1 = bgp_err_classes[p->last_error_class];
  const char *err2 = bgp_last_errmsg(p);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1112

1113
  if (P->proto_state == PS_DOWN)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1114
    bsprintf(buf, "%s%s", err1, err2);
1115
  else
1116 1117 1118
    bsprintf(buf, "%-14s%s%s", bgp_state_dsc(p), err1, err2);
}

1119 1120 1121
static inline bird_clock_t tm_remains(timer *t)
{ return t->expires ? t->expires - now : 0; }

1122 1123 1124 1125 1126 1127
static void
bgp_show_proto_info(struct proto *P)
{
  struct bgp_proto *p = (struct bgp_proto *) P;
  struct bgp_conn *c = p->conn;

1128 1129
  proto_show_basic_info(P);

1130
  cli_msg(-1006, "  BGP state:          %s", bgp_state_dsc(p));
1131
  cli_msg(-1006, "    Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
1132
  cli_msg(-1006, "    Neighbor AS:      %u", p->remote_as);
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149

  if (P->proto_state == PS_START)
    {
      struct bgp_conn *oc = &p->outgoing_conn;

      if ((p->start_state < BSS_CONNECT) &&
	  (p->startup_timer->expires))
	cli_msg(-1006, "    Error wait:       %d/%d", 
		p->startup_timer->expires - now, p->startup_delay);

      if ((oc->state == BS_ACTIVE) &&
	  (oc->connect_retry_timer->expires))
	cli_msg(-1006, "    Start delay:      %d/%d", 
		oc->connect_retry_timer->expires - now, p->cf->start_delay_time);
    }
  else if (P->proto_state == PS_UP)
    {
1150 1151 1152 1153 1154
      cli_msg(-1006, "    Neighbor ID:      %R", p->remote_id);
      cli_msg(-1006, "    Neighbor caps:   %s%s",
	      c->peer_refresh_support ? " refresh" : "",
	      c->peer_as4_support ? " AS4" : "");
      cli_msg(-1006, "    Session:          %s%s%s%s%s",
1155
	      p->is_internal ? "internal" : "external",
1156
	      p->cf->multihop ? " multihop" : "",
1157 1158 1159 1160 1161 1162 1163
	      p->rr_client ? " route-reflector" : "",
	      p->rs_client ? " route-server" : "",
	      p->as4_session ? " AS4" : "");
      cli_msg(-1006, "    Source address:   %I", p->source_addr);
      if (p->cf->route_limit)
	cli_msg(-1006, "    Route limit:      %d/%d",
		p->p.stats.imp_routes, p->cf->route_limit);
1164 1165 1166 1167
      cli_msg(-1006, "    Hold timer:       %d/%d",
	      tm_remains(c->hold_timer), c->hold_time);
      cli_msg(-1006, "    Keepalive timer:  %d/%d",
	      tm_remains(c->keepalive_timer), c->keepalive_time);
1168 1169 1170 1171 1172 1173 1174 1175 1176
    }

  if ((p->last_error_class != BE_NONE) && 
      (p->last_error_class != BE_MAN_DOWN))
    {
      const char *err1 = bgp_err_classes[p->last_error_class];
      const char *err2 = bgp_last_errmsg(p);
      cli_msg(-1006, "    Last error:       %s%s", err1, err2);
    }
1177 1178
}

1179 1180 1181
struct protocol proto_bgp = {
  name:			"BGP",
  template:		"bgp%d",
1182
  attr_class:		EAP_BGP,
1183
  preference:		DEF_PREF_BGP,
1184 1185 1186
  init:			bgp_init,
  start:		bgp_start,
  shutdown:		bgp_shutdown,
1187
  cleanup:		bgp_cleanup,
1188
  reconfigure:		bgp_reconfigure,
1189
  copy_config:		bgp_copy_config,
1190
  get_status:		bgp_get_status,
1191
  get_attr:		bgp_get_attr,
1192
  get_route_info:	bgp_get_route_info,
1193
  show_proto_info:	bgp_show_proto_info
1194
};