bgp.c 19.1 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.
 */

Martin Mareš's avatar
Martin Mareš committed
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 "conf/conf.h"
64
#include "lib/socket.h"
65
#include "lib/resource.h"
Martin Mareš's avatar
Martin Mareš committed
66
#include "lib/string.h"
67 68 69

#include "bgp.h"

70
struct linpool *bgp_linpool;		/* Global temporary pool */
71 72
static sock *bgp_listen_sk;		/* Global listening socket */
static int bgp_counter;			/* Number of protocol instances using the listening socket */
73
static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established" };
74 75

static void bgp_connect(struct bgp_proto *p);
76
static void bgp_initiate(struct bgp_proto *p);
77
static void bgp_setup_listen_sk(void);
78

79

80
static void
81
bgp_close(struct bgp_proto *p)
82 83 84
{
  ASSERT(bgp_counter);
  bgp_counter--;
85 86 87 88

  if (p->cf->password)
    sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, NULL);

89 90 91 92
  if (!bgp_counter)
    {
      rfree(bgp_listen_sk);
      bgp_listen_sk = NULL;
93 94
      rfree(bgp_linpool);
      bgp_linpool = NULL;
95 96 97
    }
}

Martin Mareš's avatar
Martin Mareš committed
98 99 100 101 102 103 104 105 106
/**
 * 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.
 */
107
void
108 109
bgp_start_timer(timer *t, int value)
{
110
  if (value)
111 112 113 114 115
    {
      /* The randomization procedure is specified in RFC 1771: 9.2.3.3 */
      t->randomize = value / 4;
      tm_start(t, value - t->randomize);
    }
116 117 118 119
  else
    tm_stop(t);
}

Martin Mareš's avatar
Martin Mareš committed
120 121 122 123 124 125 126 127 128 129 130 131 132
/**
 * 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.
 *
 * If the connection is being closed due to a protocol error, adjust
 * the connection restart timer as well according to the error recovery
 * policy set in the configuration.
 *
 * If the connection was marked as primary, it shuts down the protocol as well.
 */
133 134 135 136
void
bgp_close_conn(struct bgp_conn *conn)
{
  struct bgp_proto *p = conn->bgp;
137
  struct bgp_config *cf = p->cf;
138 139 140 141 142 143 144 145 146

  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;
147
  rfree(conn->sk);
148 149
  conn->sk = NULL;
  conn->state = BS_IDLE;
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
  if (conn->error_flag > 1)
    {
      if (cf->disable_after_error)
	p->p.disabled = 1;
      if (p->last_connect && (bird_clock_t)(p->last_connect + cf->error_amnesia_time) < now)
	p->startup_delay = 0;
      if (!p->startup_delay)
	p->startup_delay = cf->error_delay_time_min;
      else
	{
	  p->startup_delay *= 2;
	  if (p->startup_delay > cf->error_delay_time_max)
	    p->startup_delay = cf->error_delay_time_max;
	}
    }
165 166 167 168 169 170
  if (conn->primary)
    {
      bgp_close(p);
      p->conn = NULL;
      proto_notify_state(&p->p, PS_DOWN);
    }
171 172
  else if (conn->error_flag > 1)
    bgp_initiate(p);
173 174
}

175 176 177 178 179 180 181 182 183 184 185 186 187 188
static int
bgp_graceful_close_conn(struct bgp_conn *c)
{
  switch (c->state)
    {
    case BS_IDLE:
      return 0;
    case BS_CONNECT:
    case BS_ACTIVE:
      bgp_close_conn(c);
      return 1;
    case BS_OPENSENT:
    case BS_OPENCONFIRM:
    case BS_ESTABLISHED:
189
      bgp_error(c, 6, 0, NULL, 0);
190 191 192 193 194 195
      return 1;
    default:
      bug("bgp_graceful_close_conn: Unknown state %d", c->state);
    }
}

196 197 198 199 200
static void
bgp_send_open(struct bgp_conn *conn)
{
  DBG("BGP: Sending open\n");
  conn->sk->rx_hook = bgp_rx;
201
  conn->sk->tx_hook = bgp_tx;
202
  tm_stop(conn->connect_retry_timer);
203
  bgp_schedule_packet(conn, PKT_OPEN);
204
  conn->state = BS_OPENSENT;
205
  bgp_start_timer(conn->hold_timer, conn->bgp->cf->initial_hold_time);
206 207
}

208 209
static void
bgp_connected(sock *sk)
210 211
{
  struct bgp_conn *conn = sk->data;
Martin Mareš's avatar
Martin Mareš committed
212
  struct bgp_proto *p = conn->bgp;
213

Martin Mareš's avatar
Martin Mareš committed
214
  BGP_TRACE(D_EVENTS, "Connected");
215 216 217 218 219 220
  bgp_send_open(conn);
}

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

Martin Mareš's avatar
Martin Mareš committed
224
  DBG("BGP: connect_timeout\n");
225
  bgp_close_conn(conn);
Martin Mareš's avatar
Martin Mareš committed
226
  bgp_connect(p);
227 228 229
}

static void
230
bgp_sock_err(sock *sk, int err)
231 232
{
  struct bgp_conn *conn = sk->data;
Martin Mareš's avatar
Martin Mareš committed
233
  struct bgp_proto *p = conn->bgp;
234

235 236 237 238
  if (err)
    BGP_TRACE(D_EVENTS, "Connection lost (%M)", err);
  else
    BGP_TRACE(D_EVENTS, "Connection closed");
239 240 241 242
  switch (conn->state)
    {
    case BS_CONNECT:
    case BS_OPENSENT:
243
      rfree(conn->sk);
244
      conn->sk = NULL;
245
      conn->state = BS_ACTIVE;
Martin Mareš's avatar
Martin Mareš committed
246
      bgp_start_timer(conn->connect_retry_timer, p->cf->connect_retry_time);
247 248 249
      break;
    case BS_OPENCONFIRM:
    case BS_ESTABLISHED:
250
      bgp_close_conn(conn);
251
      break;
252
    default:
253
      bug("bgp_sock_err called in invalid state %d", conn->state);
254 255 256
    }
}

257 258 259 260 261
static void
bgp_hold_timeout(timer *t)
{
  struct bgp_conn *conn = t->data;

262
  DBG("BGP: Hold timeout, closing connection\n");
263
  bgp_error(conn, 4, 0, NULL, 0);
264 265 266 267 268 269 270 271 272 273 274
}

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

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

275
static void
276
bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn)
277 278 279
{
  timer *t;

280
  conn->sk = NULL;
281
  conn->bgp = p;
282
  conn->packets_to_send = 0;
283 284
  conn->error_flag = 0;
  conn->primary = 0;
285 286 287

  t = conn->connect_retry_timer = tm_new(p->p.pool);
  t->hook = bgp_connect_timeout;
288 289
  t->data = conn;
  t = conn->hold_timer = tm_new(p->p.pool);
290
  t->hook = bgp_hold_timeout;
291 292
  t->data = conn;
  t = conn->keepalive_timer = tm_new(p->p.pool);
293
  t->hook = bgp_keepalive_timeout;
294
  t->data = conn;
295 296
}

297 298 299 300 301 302 303 304
static void
bgp_setup_sk(struct bgp_proto *p, struct bgp_conn *conn, sock *s)
{
  s->data = conn;
  s->err_hook = bgp_sock_err;
  conn->sk = s;
}

Martin Mareš's avatar
Martin Mareš committed
305 306 307 308 309 310 311 312
/**
 * 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.
 */
313 314 315 316
static void
bgp_connect(struct bgp_proto *p)	/* Enter Connect state and start establishing connection */
{
  sock *s;
317
  struct bgp_conn *conn = &p->outgoing_conn;
318 319

  DBG("BGP: Connecting\n");
320
  p->last_connect = now;
321 322
  s = sk_new(p->p.pool);
  s->type = SK_TCP_ACTIVE;
323 324 325 326
  if (ipa_nonzero(p->cf->source_addr))
    s->saddr = p->cf->source_addr;
  else
    s->saddr = p->local_addr;
327 328
  s->daddr = p->cf->remote_ip;
  s->dport = BGP_PORT;
329 330 331 332 333 334
  s->ttl = p->cf->multihop ? : 1;
  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;
Martin Mareš's avatar
Martin Mareš committed
335
  BGP_TRACE(D_EVENTS, "Connecting to %I from local address %I", s->daddr, s->saddr);
336
  bgp_setup_conn(p, conn);
337 338 339 340
  bgp_setup_sk(p, conn, s);
  conn->state = BS_CONNECT;
  if (sk_open(s))
    {
341
      bgp_sock_err(s, 0);
342 343 344 345 346 347
      return;
    }
  DBG("BGP: Waiting for connect success\n");
  bgp_start_timer(conn->connect_retry_timer, p->cf->connect_retry_time);
}

348 349 350 351 352 353 354 355 356 357
static void
bgp_initiate(struct bgp_proto *p)
{
  unsigned delay;

  delay = p->cf->start_delay_time;
  if (p->startup_delay > delay)
    delay = p->startup_delay;
  if (delay)
    {
Martin Mareš's avatar
Martin Mareš committed
358
      BGP_TRACE(D_EVENTS, "Connect delayed by %d seconds", delay);
359 360 361 362 363 364 365
      bgp_setup_conn(p, &p->outgoing_conn);
      bgp_start_timer(p->outgoing_conn.connect_retry_timer, delay);
    }
  else
    bgp_connect(p);
}

Martin Mareš's avatar
Martin Mareš committed
366 367 368 369 370 371 372 373 374 375 376 377
/**
 * 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.
 */
378
static int
Martin Mareš's avatar
Martin Mareš committed
379
bgp_incoming_connection(sock *sk, int dummy UNUSED)
380
{
381 382
  struct proto_config *pc;
  int match = 0;
383

384
  DBG("BGP: Incoming connection from %I port %d\n", sk->daddr, sk->dport);
385 386 387 388 389 390 391
  WALK_LIST(pc, config->protos)
    if (pc->protocol == &proto_bgp && pc->proto)
      {
	struct bgp_proto *p = (struct bgp_proto *) pc->proto;
	if (ipa_equal(p->cf->remote_ip, sk->daddr))
	  {
	    match = 1;
392
	    if ((p->p.proto_state == PS_START || p->p.proto_state == PS_UP) && p->neigh && p->neigh->iface)
393 394 395 396 397 398 399 400 401
	      {
		BGP_TRACE(D_EVENTS, "Incoming connection from %I port %d", sk->daddr, sk->dport);
		if (p->incoming_conn.sk)
		  {
		    DBG("BGP: But one incoming connection already exists, how is that possible?\n");
		    break;
		  }
		bgp_setup_conn(p, &p->incoming_conn);
		bgp_setup_sk(p, &p->incoming_conn, sk);
402
		sk_set_ttl(sk, p->cf->multihop ? : 1);
403 404 405 406 407 408 409
		bgp_send_open(&p->incoming_conn);
		return 0;
	      }
	  }
      }
  if (!match)
    log(L_AUTH "BGP: Unauthorized connect from %I port %d", sk->daddr, sk->dport);
410 411 412 413 414
  rfree(sk);
  return 0;
}

static void
415
bgp_setup_listen_sk(void)
416
{
417 418 419 420 421 422 423 424
  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->rbsize = BGP_RX_BUFFER_SIZE;
Martin Mareš's avatar
Martin Mareš committed
425
      s->tbsize = BGP_TX_BUFFER_SIZE;
426 427 428 429 430 431 432 433 434
      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;
    }
435 436 437 438 439 440 441
}

static void
bgp_start_neighbor(struct bgp_proto *p)
{
  p->local_addr = p->neigh->iface->addr->ip;
  DBG("BGP: local=%I remote=%I\n", p->local_addr, p->next_hop);
442 443 444 445 446 447 448 449 450 451 452 453 454
#ifdef IPV6
  {
    struct ifa *a;
    p->local_link = ipa_or(ipa_build(0xfe80,0,0,0), ipa_and(p->local_addr, ipa_build(0,0,~0,~0)));
    WALK_LIST(a, p->neigh->iface->addrs)
      if (a->scope == SCOPE_LINK)
        {
	  p->local_link = a->ip;
	  break;
	}
    DBG("BGP: Selected link-level address %I\n", p->local_link);
  }
#endif
455
  bgp_initiate(p);
456 457 458 459 460 461 462 463 464
}

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

  if (n->iface)
    {
Martin Mareš's avatar
Martin Mareš committed
465
      BGP_TRACE(D_EVENTS, "Neighbor found");
466 467 468 469
      bgp_start_neighbor(p);
    }
  else
    {
Martin Mareš's avatar
Martin Mareš committed
470
      BGP_TRACE(D_EVENTS, "Neighbor lost");
471 472 473 474 475 476 477 478 479 480 481 482 483 484
      /* Send cease packets, but don't wait for them to be delivered */
      bgp_graceful_close_conn(&p->outgoing_conn);
      bgp_graceful_close_conn(&p->incoming_conn);
      proto_notify_state(&p->p, PS_DOWN);
    }
}

static void
bgp_start_locked(struct object_lock *lock)
{
  struct bgp_proto *p = lock->data;
  struct bgp_config *cf = p->cf;

  DBG("BGP: Got lock\n");
485
  p->local_id = cf->c.global->router_id;
486 487
  p->next_hop = cf->multihop ? cf->multihop_via : cf->remote_ip;
  p->neigh = neigh_find(&p->p, &p->next_hop, NEF_STICKY);
488 489 490 491 492 493 494

  if (cf->rr_client)
    {
      p->rr_cluster_id = cf->rr_cluster_id ? cf->rr_cluster_id : p->local_id;
      p->rr_client = cf->rr_client;
    }

495 496
  p->rs_client = cf->rs_client;

497 498 499 500 501 502 503 504 505
  if (!p->neigh)
    {
      log(L_ERR "%s: Invalid next hop %I", p->p.name, p->next_hop);
      p->p.disabled = 1;
      proto_notify_state(&p->p, PS_DOWN);
    }
  else if (p->neigh->iface)
    bgp_start_neighbor(p);
  else
Martin Mareš's avatar
Martin Mareš committed
506
    BGP_TRACE(D_EVENTS, "Waiting for %I to become my neighbor", p->next_hop);
507 508
}

509 510 511
static int
bgp_start(struct proto *P)
{
512 513 514
  struct bgp_proto *p = (struct bgp_proto *) P;
  struct object_lock *lock;

515 516 517
  DBG("BGP: Startup.\n");
  p->outgoing_conn.state = BS_IDLE;
  p->incoming_conn.state = BS_IDLE;
518
  p->startup_delay = 0;
519
  p->neigh = NULL;
520

521
  bgp_counter++;
522
  bgp_setup_listen_sk();
523

524 525 526
  if (!bgp_linpool)
    bgp_linpool = lp_new(&root_pool, 4080);

527 528 529 530 531 532 533 534 535 536 537 538 539 540
  /*
   *  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;
  lock->type = OBJLOCK_TCP;
  lock->port = BGP_PORT;
  lock->iface = NULL;
  lock->hook = bgp_start_locked;
  lock->data = p;
  olock_acquire(lock);
541 542 543 544 545 546 547 548 549 550 551

  /* We should create security association after we get a lock not to 
   * break existing connections.
   */
  if (p->cf->password)
    {
      int rv = sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->password);
      if (rv < 0)
	return PS_STOP;
    }

552
  return PS_START;
553 554 555 556 557
}

static int
bgp_shutdown(struct proto *P)
{
558 559
  struct bgp_proto *p = (struct bgp_proto *) P;

Martin Mareš's avatar
Martin Mareš committed
560
  BGP_TRACE(D_EVENTS, "Shutdown requested");
561

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
  /*
   *  We want to send the Cease notification message to all connections
   *  we have open, but we don't want to wait for all of them to complete.
   *  We are willing to handle the primary connection carefully, but for
   *  the others we just try to send the packet and if there is no buffer
   *  space free, we'll gracefully finish.
   */

  proto_notify_state(&p->p, PS_STOP);
  if (!p->conn)
    {
      if (p->outgoing_conn.state != BS_IDLE)
	p->outgoing_conn.primary = 1;	/* Shuts protocol down after connection close */
      else if (p->incoming_conn.state != BS_IDLE)
	p->incoming_conn.primary = 1;
    }
578
  if (bgp_graceful_close_conn(&p->outgoing_conn) || bgp_graceful_close_conn(&p->incoming_conn))
579 580 581 582 583 584 585
    return p->p.proto_state;
  else
    {
      /* No connections open, shutdown automatically */
      bgp_close(p);
      return PS_DOWN;
    }
586 587
}

588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
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;

  P->rt_notify = bgp_rt_notify;
  P->rte_better = bgp_rte_better;
  P->import_control = bgp_import_control;
  P->neigh_notify = bgp_neigh_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);
  return P;
}

Martin Mareš's avatar
Martin Mareš committed
606 607 608 609
/**
 * bgp_error - report a protocol error
 * @c: connection
 * @code: error code (according to the RFC)
610
 * @subcode: error sub-code
Martin Mareš's avatar
Martin Mareš committed
611 612 613 614
 * @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
615
 * error has occurred (including the data considered erroneous if possible) and
Martin Mareš's avatar
Martin Mareš committed
616 617
 * closes the connection.
 */
618
void
619
bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len)
620 621 622
{
  if (c->error_flag)
    return;
623
  bgp_log_error(c->bgp, "Error", code, subcode, data, (len > 0) ? len : -len);
624
  c->error_flag = 1 + (code != 6);
625 626
  c->notify_code = code;
  c->notify_subcode = subcode;
627 628
  c->notify_data = data;
  c->notify_size = (len > 0) ? len : 0;
629 630
  if (c->primary)
    proto_notify_state(&c->bgp->p, PS_STOP);
631 632 633
  bgp_schedule_packet(c, PKT_NOTIFICATION);
}

634 635 636 637 638 639 640
void
bgp_check(struct bgp_config *c)
{
  if (!c->local_as)
    cf_error("Local AS number must be set");
  if (!c->remote_as)
    cf_error("Neighbor must be configured");
641 642 643
  if (!bgp_as4_support && c->enable_as4)
    cf_error("AS4 support disabled globbaly");
  if (!c->enable_as4 && (c->local_as > 0xFFFF))
644
    cf_error("Local AS number out of range");
645
  if (!c->enable_as4 && (c->remote_as > 0xFFFF))
646
    cf_error("Neighbor AS number out of range");
647 648
  if ((c->local_as != c->remote_as) && (c->rr_client))
    cf_error("Only internal neighbor can be RR client");
649 650
  if ((c->local_as == c->remote_as) && (c->rs_client))
    cf_error("Only external neighbor can be RS client");
651 652
}

653
static void
654 655 656 657
bgp_get_status(struct proto *P, byte *buf)
{
  struct bgp_proto *p = (struct bgp_proto *) P;

658 659 660 661
  if (P->proto_state == PS_DOWN)
    buf[0] = 0;
  else
    strcpy(buf, bgp_state_names[MAX(p->incoming_conn.state, p->outgoing_conn.state)]);
662 663
}

664 665 666 667 668 669 670 671 672 673 674 675
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;

  return !memcmp(((byte *) old) + sizeof(struct proto_config),
		 ((byte *) new) + sizeof(struct proto_config),
		 sizeof(struct bgp_config) - sizeof(struct proto_config));
}

676 677 678
struct protocol proto_bgp = {
  name:			"BGP",
  template:		"bgp%d",
679
  attr_class:		EAP_BGP,
680 681 682
  init:			bgp_init,
  start:		bgp_start,
  shutdown:		bgp_shutdown,
683
  get_status:		bgp_get_status,
684
  get_attr:		bgp_get_attr,
685
  reconfigure:		bgp_reconfigure,
686 687
  get_route_info:	bgp_get_route_info,
};