diff options
Diffstat (limited to 'proto/bgp')
-rw-r--r-- | proto/bgp/attrs.c | 191 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 202 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 19 | ||||
-rw-r--r-- | proto/bgp/config.Y | 3 | ||||
-rw-r--r-- | proto/bgp/packets.c | 51 |
5 files changed, 273 insertions, 193 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 8a849e73..68f21b97 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -39,9 +39,9 @@ struct attr_desc { }; static int -bgp_check_origin(struct bgp_proto *p UNUSED, byte *a UNUSED, int len) +bgp_check_origin(struct bgp_proto *p UNUSED, byte *a, int len UNUSED) { - if (len > 2) + if (*a > 2) return 6; return 0; } @@ -55,25 +55,97 @@ bgp_format_origin(eattr *a, byte *buf, int buflen) } static int -bgp_check_path(byte *a, int len, int bs, int errcode) +path_segment_contains(byte *p, int bs, u32 asn) { - while (len) + int i; + int len = p[1]; + p += 2; + + for(i=0; i<len; i++) { - DBG("Path segment %02x %02x\n", a[0], a[1]); - if (len < 2 || - (a[0] != AS_PATH_SET && a[0] != AS_PATH_SEQUENCE) || - bs * a[1] + 2 > len) - return errcode; - len -= bs * a[1] + 2; - a += bs * a[1] + 2; + u32 asn2 = (bs == 4) ? get_u32(p) : get_u16(p); + if (asn2 == asn) + return 1; + p += bs; } + return 0; } +/* Validates path attribute, removes AS_CONFED_* segments, and also returns path length */ static int -bgp_check_as_path(struct bgp_proto *p, byte *a, int len) +validate_path(struct bgp_proto *p, int as_path, int bs, byte *idata, unsigned int *ilength) +{ + int res = 0; + u8 *a, *dst; + int len, plen, copy; + + dst = a = idata; + len = *ilength; + + while (len) + { + if (len < 2) + return -1; + + plen = 2 + bs * a[1]; + if (len < plen) + return -1; + + switch (a[0]) + { + case AS_PATH_SET: + copy = 1; + res++; + break; + + case AS_PATH_SEQUENCE: + copy = 1; + res += a[1]; + break; + + case AS_PATH_CONFED_SEQUENCE: + case AS_PATH_CONFED_SET: + if (as_path && path_segment_contains(a, bs, p->remote_as)) + { + log(L_WARN "%s: AS_CONFED_* segment with peer ASN found, misconfigured confederation?", p->p.name); + return -1; + } + + log(L_WARN "%s: %s_PATH attribute contains AS_CONFED_* segment, skipping segment", + p->p.name, as_path ? "AS" : "AS4"); + copy = 0; + break; + + default: + return -1; + } + + if (copy) + { + if (dst != a) + memmove(dst, a, plen); + dst += plen; + } + + len -= plen; + a += plen; + } + + *ilength = dst - idata; + return res; +} + +static inline int +validate_as_path(struct bgp_proto *p, byte *a, int *len) { - return bgp_check_path(a, len, p->as4_session ? 4 : 2, 11); + return validate_path(p, 1, p->as4_session ? 4 : 2, a, len); +} + +static inline int +validate_as4_path(struct bgp_proto *p, struct adata *path) +{ + return validate_path(p, 0, 4, path->data, &path->length); } static int @@ -160,7 +232,7 @@ static struct attr_desc bgp_attr_table[] = { { "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, 1, /* BA_ORIGIN */ bgp_check_origin, bgp_format_origin }, { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */ - bgp_check_as_path, NULL }, + NULL, NULL }, /* is checked by validate_as_path() as a special case */ { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */ bgp_check_next_hop, NULL }, { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 1, /* BA_MULTI_EXIT_DISC */ @@ -1061,73 +1133,6 @@ as4_aggregator_valid(struct adata *aggr) return 0; } -static int -as4_path_sanitize_and_get_length(struct adata *path) -{ - int res = 0; - u8 *p, *dst; - int len, plen, copy; - - dst = p = path->data; - len = path->length; - - while (len) - { - if (len <= 2) /* We do not allow empty segments */ - goto inconsistent_path; - - switch (p[0]) - { - case AS_PATH_SET: - plen = 2 + 4 * p[1]; - copy = 1; - res++; - break; - - case AS_PATH_SEQUENCE: - plen = 2 + 4 * p[1]; - copy = 1; - res += p[1]; - break; - - case AS_PATH_CONFED_SEQUENCE: - case AS_PATH_CONFED_SET: - log(L_WARN "BGP: AS4_PATH attribute contains AS_CONFED_* segment, skipping segment"); - plen = 2 + 4 * p[1]; - copy = 0; - break; - - default: - goto unknown_segment; - } - - if (len < plen) - goto inconsistent_path; - - if (copy) - { - if (dst != p) - memmove(dst, p, plen); - dst += plen; - } - - len -= plen; - p += plen; - } - - path->length = dst - path->data; - return res; - - inconsistent_path: - log(L_WARN "BGP: AS4_PATH attribute is inconsistent, skipping attribute"); - return -1; - - unknown_segment: - log(L_WARN "BGP: AS4_PATH attribute contains unknown segment, skipping attribute"); - return -1; -} - - /* Reconstruct 4B AS_PATH and AGGREGATOR according to RFC 4893 4.2.3 */ static void @@ -1141,7 +1146,7 @@ bgp_reconstruct_4b_atts(struct bgp_proto *p, rta *a, struct linpool *pool) if (a4 && !as4_aggregator_valid(a4->u.ptr)) { - log(L_WARN "BGP: AS4_AGGREGATOR attribute is invalid, skipping attribute"); + log(L_WARN "%s: AS4_AGGREGATOR attribute is invalid, skipping attribute", p->p.name); a4 = NULL; a4_removed = 1; } @@ -1177,15 +1182,18 @@ bgp_reconstruct_4b_atts(struct bgp_proto *p, rta *a, struct linpool *pool) a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool); if ((a2_as == AS_TRANS) && !a4_removed) - log(L_WARN "BGP: AGGREGATOR attribute contain AS_TRANS, but AS4_AGGREGATOR is missing"); + log(L_WARN "%s: AGGREGATOR attribute contain AS_TRANS, but AS4_AGGREGATOR is missing", p->p.name); } } else if (a4) - log(L_WARN "BGP: AS4_AGGREGATOR attribute received, but AGGREGATOR attribute is missing"); + log(L_WARN "%s: AS4_AGGREGATOR attribute received, but AGGREGATOR attribute is missing", p->p.name); int p2_len = as_path_getlen(p2->u.ptr); - int p4_len = p4 ? as4_path_sanitize_and_get_length(p4->u.ptr) : -1; + int p4_len = p4 ? validate_as4_path(p, p4->u.ptr) : -1; + + if (p4 && (p4_len < 0)) + log(L_WARN "%s: AS4_PATH attribute is malformed, skipping attribute", p->p.name); if ((p4_len <= 0) || (p2_len < p4_len)) p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool); @@ -1200,7 +1208,7 @@ bgp_remove_as4_attrs(struct bgp_proto *p, rta *a) unsigned id2 = EA_CODE(EAP_BGP, BA_AS4_AGGREGATOR); ea_list **el = &(a->eattrs); - /* We know that ea_lists constructed in bgp_decode_attrs have one attribute per ea_list struct */ + /* We know that ea_lists constructed in bgp_decode attrs have one attribute per ea_list struct */ while (*el != NULL) { unsigned fid = (*el)->attrs[0].id; @@ -1302,6 +1310,12 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin if (errcode < 0) continue; } + else if (code == BA_AS_PATH) + { + /* Special case as it might also trim the attribute */ + if (validate_as_path(bgp, z, &l) < 0) + { errcode = 11; goto err; } + } type = desc->type; } else /* Unknown attribute */ @@ -1310,6 +1324,11 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin { errcode = 2; goto err; } type = EAF_TYPE_OPAQUE; } + + // Only OPTIONAL and TRANSITIVE attributes may have non-zero PARTIAL flag + // if (!((flags & BAF_OPTIONAL) && (flags & BAF_TRANSITIVE)) && (flags & BAF_PARTIAL)) + // { errcode = 4; goto err; } + seen[code/8] |= (1 << (code%8)); ea = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr)); ea->next = a->eattrs; diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index cbc699bb..a6b9d574 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -73,10 +73,8 @@ static int bgp_counter; /* Number of protocol instances using the listening so static void bgp_close(struct bgp_proto *p, int apply_md5); static void bgp_connect(struct bgp_proto *p); -static void bgp_active(struct bgp_proto *p, int delay); -static void bgp_initiate(struct bgp_proto *p); -static void bgp_stop(struct bgp_proto *p); -static sock *bgp_setup_listen_sk(void); +static void bgp_active(struct bgp_proto *p); +static sock *bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags); /** @@ -92,10 +90,11 @@ static sock *bgp_setup_listen_sk(void); static int bgp_open(struct bgp_proto *p) { + struct config *cfg = p->cf->c.global; bgp_counter++; if (!bgp_listen_sk) - bgp_listen_sk = bgp_setup_listen_sk(); + bgp_listen_sk = bgp_setup_listen_sk(cfg->listen_bgp_addr, cfg->listen_bgp_port, cfg->listen_bgp_flags); if (!bgp_linpool) bgp_linpool = lp_new(&root_pool, 4080); @@ -113,10 +112,36 @@ bgp_open(struct bgp_proto *p) } } - p->start_state = p->cf->capabilities ? BSS_CONNECT : BSS_CONNECT_NOCAP; return 0; } +static void +bgp_startup(struct bgp_proto *p) +{ + BGP_TRACE(D_EVENTS, "Started"); + p->start_state = p->cf->capabilities ? BSS_CONNECT : BSS_CONNECT_NOCAP; + bgp_active(p); +} + +static void +bgp_startup_timeout(timer *t) +{ + bgp_startup(t->data); +} + + +static void +bgp_initiate(struct bgp_proto *p) +{ + 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); +} + /** * bgp_close - close a BGP instance * @p: BGP instance @@ -196,9 +221,6 @@ bgp_close_conn(struct bgp_conn *conn) /** * bgp_update_startup_delay - update a startup delay * @p: BGP instance - * @conn: related BGP connection - * @code: BGP error code - * @subcode: BGP error subcode * * 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 @@ -207,26 +229,14 @@ bgp_close_conn(struct bgp_conn *conn) * It should be called when BGP protocol error happened. */ void -bgp_update_startup_delay(struct bgp_proto *p, struct bgp_conn *conn, unsigned code, unsigned subcode) +bgp_update_startup_delay(struct bgp_proto *p) { struct bgp_config *cf = p->cf; - /* Don't handle cease messages as errors */ - if (code == 6 && !subcode) - { - p->startup_delay = 0; - return; - } - - /* During start, we only consider errors on outgoing connection, because - otherwise delay timer for outgoing connection is already running and - we could increase delay time two times (or more) per one attempt to - connect. - */ - if ((p->p.proto_state == PS_START) && (conn != &p->outgoing_conn)) - return; + DBG("BGP: Updating startup delay\n"); - DBG("BGP: Updating startup delay %d %d\n", code, subcode); + if (p->last_proto_error && ((now - p->last_proto_error) >= cf->error_amnesia_time)) + p->startup_delay = 0; p->last_proto_error = now; @@ -234,27 +244,17 @@ bgp_update_startup_delay(struct bgp_proto *p, struct bgp_conn *conn, unsigned co { p->startup_delay = 0; p->p.disabled = 1; - if (p->p.proto_state == PS_START) - bgp_stop(p); - return; } - if (p->last_proto_error && ((now - p->last_proto_error) >= cf->error_amnesia_time)) - 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; - } + p->startup_delay = MIN(2 * p->startup_delay, cf->error_delay_time_max); } static void -bgp_graceful_close_conn(struct bgp_conn *conn) +bgp_graceful_close_conn(struct bgp_conn *conn, unsigned subcode) { switch (conn->state) { @@ -268,7 +268,7 @@ bgp_graceful_close_conn(struct bgp_conn *conn) case BS_OPENSENT: case BS_OPENCONFIRM: case BS_ESTABLISHED: - bgp_error(conn, 6, 0, NULL, 0); + bgp_error(conn, 6, subcode, NULL, 0); return; default: bug("bgp_graceful_close_conn: Unknown state %d", conn->state); @@ -281,7 +281,7 @@ bgp_down(struct bgp_proto *p) if (p->start_state > BSS_PREPARE) bgp_close(p, 1); - DBG("BGP: DOWN\n"); + BGP_TRACE(D_EVENTS, "Down"); proto_notify_state(&p->p, PS_DOWN); } @@ -293,7 +293,7 @@ bgp_decision(void *vp) DBG("BGP: Decision start\n"); if ((p->p.proto_state == PS_START) && (p->outgoing_conn.state == BS_IDLE)) - bgp_initiate(p); + bgp_active(p); if ((p->p.proto_state == PS_STOP) && (p->outgoing_conn.state == BS_IDLE) @@ -301,12 +301,12 @@ bgp_decision(void *vp) bgp_down(p); } -static void -bgp_stop(struct bgp_proto *p) +void +bgp_stop(struct bgp_proto *p, unsigned subcode) { proto_notify_state(&p->p, PS_STOP); - bgp_graceful_close_conn(&p->outgoing_conn); - bgp_graceful_close_conn(&p->incoming_conn); + bgp_graceful_close_conn(&p->outgoing_conn, subcode); + bgp_graceful_close_conn(&p->incoming_conn, subcode); ev_schedule(p->event); } @@ -333,7 +333,7 @@ bgp_conn_leave_established_state(struct bgp_proto *p) p->conn = NULL; if (p->p.proto_state == PS_UP) - bgp_stop(p); + bgp_stop(p, 0); } void @@ -473,8 +473,9 @@ bgp_setup_sk(struct bgp_proto *p, struct bgp_conn *conn, sock *s) } static void -bgp_active(struct bgp_proto *p, int delay) +bgp_active(struct bgp_proto *p) { + int delay = MAX(1, p->cf->start_delay_time); struct bgp_conn *conn = &p->outgoing_conn; BGP_TRACE(D_EVENTS, "Connect delayed by %d seconds", delay); @@ -483,6 +484,22 @@ bgp_active(struct bgp_proto *p, int delay) bgp_start_timer(conn->connect_retry_timer, delay); } +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); + bgp_update_startup_delay(p); + bgp_stop(p, 1); // Errcode 6, 1 - max number of prefixes reached + return -1; + } + + return 0; +} + + /** * bgp_connect - initiate an outgoing connection * @p: BGP instance @@ -522,17 +539,6 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c bgp_start_timer(conn->connect_retry_timer, p->cf->connect_retry_time); } -static void -bgp_initiate(struct bgp_proto *p) -{ - unsigned delay = MAX(p->startup_delay, p->cf->start_delay_time); - - if (delay) - bgp_active(p, delay); - else - bgp_connect(p); -} - /** * bgp_incoming_connection - handle an incoming connection * @sk: TCP socket @@ -549,7 +555,6 @@ static int bgp_incoming_connection(sock *sk, int dummy UNUSED) { struct proto_config *pc; - int match = 0; DBG("BGP: Incoming connection from %I port %d\n", sk->daddr, sk->dport); WALK_LIST(pc, config->protos) @@ -558,36 +563,39 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED) struct bgp_proto *p = (struct bgp_proto *) pc->proto; if (ipa_equal(p->cf->remote_ip, sk->daddr)) { - match = 1; - if ((p->p.proto_state == PS_START || p->p.proto_state == PS_UP) && (p->start_state > BSS_PREPARE)) - { - 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); - sk_set_ttl(sk, p->cf->multihop ? : 1); - bgp_send_open(&p->incoming_conn); - return 0; - } + /* 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); + + BGP_TRACE(D_EVENTS, "Incoming connection from %I (port %d) %s", + sk->daddr, sk->dport, acc ? "accepted" : "rejected"); + + if (!acc) + goto err; + + bgp_setup_conn(p, &p->incoming_conn); + bgp_setup_sk(p, &p->incoming_conn, sk); + sk_set_ttl(sk, p->cf->multihop ? : 1); + bgp_send_open(&p->incoming_conn); + return 0; } } - if (!match) - log(L_AUTH "BGP: Unauthorized connect from %I port %d", sk->daddr, sk->dport); + + log(L_WARN "BGP: Unexpected connect from unknown address %I (port %d)", sk->daddr, sk->dport); + err: rfree(sk); return 0; } static sock * -bgp_setup_listen_sk(void) +bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags) { sock *s = sk_new(&root_pool); DBG("BGP: Creating incoming socket\n"); s->type = SK_TCP_PASSIVE; - s->sport = BGP_PORT; + s->saddr = addr; + s->sport = port ? port : BGP_PORT; + s->flags = flags; s->tos = IP_PREC_INTERNET_CONTROL; s->rbsize = BGP_RX_BUFFER_SIZE; s->tbsize = BGP_TX_BUFFER_SIZE; @@ -653,7 +661,7 @@ bgp_neigh_notify(neighbor *n) { BGP_TRACE(D_EVENTS, "Neighbor lost"); bgp_store_error(p, NULL, BE_MISC, BEM_NEIGHBOR_LOST); - bgp_stop(p); + bgp_stop(p, 0); } } } @@ -715,6 +723,10 @@ bgp_start(struct proto *P) p->event->hook = bgp_decision; p->event->data = p; + p->startup_timer = tm_new(p->p.pool); + p->startup_timer->hook = bgp_startup_timeout; + p->startup_timer->data = p; + /* * Before attempting to create the connection, we need to lock the * port, so that are sure we're the only instance attempting to talk @@ -737,11 +749,23 @@ static int bgp_shutdown(struct proto *P) { struct bgp_proto *p = (struct bgp_proto *) P; + unsigned subcode; BGP_TRACE(D_EVENTS, "Shutdown requested"); bgp_store_error(p, NULL, BE_MAN_DOWN, 0); + + 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 + p->startup_delay = 0; - bgp_stop(p); + bgp_stop(p, subcode); return p->p.proto_state; } @@ -753,6 +777,7 @@ bgp_init(struct proto_config *C) struct proto *P = proto_new(C, sizeof(struct bgp_proto)); struct bgp_proto *p = (struct bgp_proto *) P; + P->accept_ra_types = RA_OPTIMAL; P->rt_notify = bgp_rt_notify; P->rte_better = bgp_rte_better; P->import_control = bgp_import_control; @@ -779,12 +804,13 @@ bgp_init(struct proto_config *C) void bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len) { + struct bgp_proto *p = c->bgp; + if (c->state == BS_CLOSE) return; - bgp_log_error(c->bgp, "Error", code, subcode, data, (len > 0) ? len : -len); - bgp_store_error(c->bgp, c, BE_BGP_TX, (code << 16) | subcode); - bgp_update_startup_delay(c->bgp, c, code, subcode); + 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); bgp_conn_enter_close_state(c); c->notify_code = code; @@ -792,6 +818,12 @@ bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int l c->notify_data = data; c->notify_size = (len > 0) ? len : 0; bgp_schedule_packet(c, PKT_NOTIFICATION); + + if (code != 6) + { + bgp_update_startup_delay(p); + bgp_stop(p, 0); + } } /** @@ -847,8 +879,9 @@ bgp_check(struct bgp_config *c) } static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" }; -static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown", ""}; +static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown: ", ""}; static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed" }; +static char *bgp_auto_errors[] = { "", "Route limit exceeded"}; static void @@ -872,6 +905,9 @@ bgp_get_status(struct proto *P, byte *buf) case BE_BGP_TX: err2 = bgp_error_dsc(errbuf, p->last_error_code >> 16, p->last_error_code & 0xFF); break; + case BE_AUTO_DOWN: + err2 = bgp_auto_errors[p->last_error_code]; + break; } if (P->proto_state == PS_DOWN) diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 8477f9e5..9bbdab8e 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -31,6 +31,7 @@ struct bgp_config { int rr_client; /* Whether neighbor is RR client of me */ int rs_client; /* Whether neighbor is RS client of me */ int advertise_ipv4; /* Whether we should add IPv4 capability advertisement to OPEN message */ + u32 route_limit; /* Number of routes that may be imported, 0 means disable limit */ unsigned connect_retry_time; unsigned hold_time, initial_hold_time; unsigned keepalive_time; @@ -81,6 +82,7 @@ struct bgp_proto { ip_addr local_addr; /* Address of the local end of the link to next_hop */ ip_addr source_addr; /* Address used as advertised next hop, usually local_addr */ struct event *event; /* Event for respawning and shutting process */ + struct timer *startup_timer; /* Timer used to delay protocol startup due to previous errors (startup_delay) */ struct bgp_bucket **bucket_hash; /* Hash table of attribute buckets */ unsigned int hash_size, hash_count, hash_limit; struct fib prefix_fib; /* Prefixes to be sent */ @@ -127,11 +129,14 @@ void bgp_start_timer(struct timer *t, int value); void bgp_check(struct bgp_config *c); void bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len); void bgp_close_conn(struct bgp_conn *c); -void bgp_update_startup_delay(struct bgp_proto *p, struct bgp_conn *conn, unsigned code, unsigned subcode); +void bgp_update_startup_delay(struct bgp_proto *p); void bgp_conn_enter_established_state(struct bgp_conn *conn); void bgp_conn_enter_close_state(struct bgp_conn *conn); void bgp_conn_enter_idle_state(struct bgp_conn *conn); void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code); +int bgp_apply_limits(struct bgp_proto *p); +void bgp_stop(struct bgp_proto *p, unsigned subcode); + #ifdef LOCAL_DEBUG @@ -181,7 +186,7 @@ void bgp_kick_tx(void *vconn); void bgp_tx(struct birdsock *sk); int bgp_rx(struct birdsock *sk, int size); const byte * bgp_error_dsc(byte *buff, unsigned code, unsigned subcode); -void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len); +void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len); /* Packet types */ @@ -239,8 +244,9 @@ void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subco */ #define BSS_PREPARE 0 /* Used before ordinary BGP started, i. e. waiting for lock */ -#define BSS_CONNECT 1 /* Ordinary BGP connecting */ -#define BSS_CONNECT_NOCAP 2 /* Legacy BGP connecting (without capabilities) */ +#define BSS_DELAY 1 /* Startup delay due to previous errors */ +#define BSS_CONNECT 2 /* Ordinary BGP connecting */ +#define BSS_CONNECT_NOCAP 3 /* Legacy BGP connecting (without capabilities) */ /* Error classes */ @@ -256,8 +262,11 @@ void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subco #define BEM_NEIGHBOR_LOST 1 #define BEM_INVALID_NEXT_HOP 2 -#define BEM_INVALID_MD5 3 /* MD5 authentication kernel request failed (possibly not supported */ +#define BEM_INVALID_MD5 3 /* MD5 authentication kernel request failed (possibly not supported) */ + +/* Automatic shutdown error codes */ +#define BEA_ROUTE_LIMIT_EXCEEDED 1 /* Well-known communities */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index c5ea87de..872fb271 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -22,7 +22,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN, BGP_NEXT_HOP, BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY, SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4, - CAPABILITIES) + CAPABILITIES, LIMIT) CF_GRAMMAR @@ -77,6 +77,7 @@ bgp_proto: | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; } | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; } | bgp_proto PASSWORD TEXT ';' { BGP_CFG->password = $3; } + | bgp_proto ROUTE LIMIT expr ';' { BGP_CFG->route_limit = $4; } ; CF_ADDTO(dynamic_attr, BGP_PATH diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 27adc166..ae4906ee 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -619,14 +619,14 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) { /* Should close the other connection */ BGP_TRACE(D_EVENTS, "Connection collision, giving up the other connection"); - bgp_error(other, 6, 0, NULL, 0); + bgp_error(other, 6, 7, NULL, 0); break; } /* Fall thru */ case BS_ESTABLISHED: /* Should close this connection */ BGP_TRACE(D_EVENTS, "Connection collision, giving up this connection"); - bgp_error(conn, 6, 0, NULL, 0); + bgp_error(conn, 6, 7, NULL, 0); return; default: bug("bgp_rx_open: Unknown state"); @@ -705,7 +705,7 @@ bgp_do_rx_update(struct bgp_conn *conn, DECODE_PREFIX(withdrawn, withdrawn_len); DBG("Withdraw %I/%d\n", prefix, pxlen); if (n = net_find(p->p.table, prefix, pxlen)) - rte_update(p->p.table, n, &p->p, NULL); + rte_update(p->p.table, n, &p->p, &p->p, NULL); } if (!attr_len && !nlri_len) /* shortcut */ @@ -724,14 +724,20 @@ bgp_do_rx_update(struct bgp_conn *conn, n = net_get(p->p.table, prefix, pxlen); e->net = n; e->pflags = 0; - rte_update(p->p.table, n, &p->p, e); + rte_update(p->p.table, n, &p->p, &p->p, e); + if (bgp_apply_limits(p) < 0) + goto bad2; } + rta_free(a); } -bad: + + return; + + bad: + bgp_error(conn, 3, err, NULL, 0); + bad2: if (a) rta_free(a); - if (err) - bgp_error(conn, 3, err, NULL, 0); return; } @@ -783,7 +789,7 @@ bgp_do_rx_update(struct bgp_conn *conn, DECODE_PREFIX(x, len); DBG("Withdraw %I/%d\n", prefix, pxlen); if (n = net_find(p->p.table, prefix, pxlen)) - rte_update(p->p.table, n, &p->p, NULL); + rte_update(p->p.table, n, &p->p, &p->p, NULL); } } @@ -824,7 +830,9 @@ bgp_do_rx_update(struct bgp_conn *conn, n = net_get(p->p.table, prefix, pxlen); e->net = n; e->pflags = 0; - rte_update(p->p.table, n, &p->p, e); + rte_update(p->p.table, n, &p->p, &p->p, e); + if (bgp_apply_limits(p) < 0) + goto bad2; } rta_free(a); } @@ -832,8 +840,9 @@ bgp_do_rx_update(struct bgp_conn *conn, return; -bad: + bad: bgp_error(conn, 3, 9, start, len0); + bad2: if (a) rta_free(a); return; @@ -948,14 +957,15 @@ bgp_error_dsc(byte *buff, unsigned code, unsigned subcode) } void -bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len) +bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len) { const byte *name; byte namebuf[32]; byte *t, argbuf[36]; unsigned i; - if (code == 6 && !subcode) /* Don't report Cease messages */ + /* Don't report Cease messages generated by myself */ + if (code == 6 && class == BE_BGP_TX) return; name = bgp_error_dsc(namebuf, code, subcode); @@ -985,10 +995,10 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, int len) unsigned code = pkt[19]; unsigned subcode = pkt[20]; - int delay = 1; + int err = (code != 6); - bgp_log_error(conn->bgp, "Received error notification", code, subcode, pkt+21, len-21); - bgp_store_error(conn->bgp, conn, BE_BGP_RX, (code << 16) | subcode); + bgp_log_error(p, BE_BGP_RX, "Received", code, subcode, pkt+21, len-21); + bgp_store_error(p, conn, BE_BGP_RX, (code << 16) | subcode); #ifndef IPV6 if ((code == 2) && ((subcode == 4) || (subcode == 7)) @@ -1005,14 +1015,19 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, int len) { /* We try connect without capabilities */ log(L_WARN "%s: Capability related error received, retry with capabilities disabled", p->p.name); - conn->bgp->start_state = BSS_CONNECT_NOCAP; - delay = 0; + p->start_state = BSS_CONNECT_NOCAP; + err = 0; } #endif - if (delay) bgp_update_startup_delay(conn->bgp, conn, code, subcode); bgp_conn_enter_close_state(conn); bgp_schedule_packet(conn, PKT_SCHEDULE_CLOSE); + + if (err) + { + bgp_update_startup_delay(p); + bgp_stop(p, 0); + } } static void |