diff options
Diffstat (limited to 'proto')
-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 | ||||
-rw-r--r-- | proto/ospf/config.Y | 30 | ||||
-rw-r--r-- | proto/ospf/hello.c | 37 | ||||
-rw-r--r-- | proto/ospf/ospf.c | 52 | ||||
-rw-r--r-- | proto/ospf/ospf.h | 14 | ||||
-rw-r--r-- | proto/ospf/packet.c | 3 | ||||
-rw-r--r-- | proto/ospf/rt.c | 4 | ||||
-rw-r--r-- | proto/ospf/topology.c | 230 | ||||
-rw-r--r-- | proto/pipe/config.Y | 11 | ||||
-rw-r--r-- | proto/pipe/pipe.c | 40 | ||||
-rw-r--r-- | proto/pipe/pipe.h | 5 | ||||
-rw-r--r-- | proto/rip/rip.c | 3 | ||||
-rw-r--r-- | proto/static/static.c | 4 |
17 files changed, 575 insertions, 324 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 diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 7f7d6a31..77ca26cf 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -18,6 +18,7 @@ CF_DEFINES static struct ospf_area_config *this_area; static struct nbma_node *this_nbma; static struct area_net_config *this_pref; +static struct ospf_stubnet_config *this_stubnet; static void finish_iface_config(struct ospf_iface_patt *ip) @@ -38,7 +39,7 @@ CF_KEYWORDS(NEIGHBORS, RFC1583COMPAT, STUB, TICK, COST, RETRANSMIT) CF_KEYWORDS(HELLO, TRANSMIT, PRIORITY, DEAD, NONBROADCAST, POINTOPOINT, TYPE) CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC) CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, LINK) -CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL) +CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY) %type <t> opttext @@ -75,6 +76,7 @@ ospf_area_start: AREA idval '{' { init_list(&this_area->patt_list); init_list(&this_area->vlink_list); init_list(&this_area->net_list); + init_list(&this_area->stubnet_list); } ; @@ -90,10 +92,36 @@ ospf_area_item: STUB COST expr { this_area->stub = $3 ; if($3<=0) cf_error("Stub cost must be greater than zero"); } | STUB bool {if($2) { if(!this_area->stub) this_area->stub=DEFAULT_STUB_COST;}else{ this_area->stub=0;}} | NETWORKS '{' pref_list '}' + | STUBNET ospf_stubnet | INTERFACE ospf_iface | ospf_vlink ; +ospf_stubnet: + ospf_stubnet_start '{' ospf_stubnet_opts '}' + | ospf_stubnet_start + ; + +ospf_stubnet_start: + prefix { + this_stubnet = cfg_allocz(sizeof(struct ospf_stubnet_config)); + add_tail(&this_area->stubnet_list, NODE this_stubnet); + this_stubnet->px = $1; + this_stubnet->cost = COST_D; + } + ; + +ospf_stubnet_opts: + /* empty */ + | ospf_stubnet_opts ospf_stubnet_item ';' + ; + +ospf_stubnet_item: + HIDDEN bool { this_stubnet->hidden = $2; } + | SUMMARY bool { this_stubnet->summary = $2; } + | COST expr { this_stubnet->cost = $2; } + ; + ospf_vlink: ospf_vlink_start '{' ospf_vlink_opts '}' { finish_iface_config(OSPF_PATT); } | ospf_vlink_start diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index c7d20273..45b6b613 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -24,16 +24,36 @@ ospf_hello_receive(struct ospf_hello_packet *ps, mask = ps->netmask; ipa_ntoh(mask); - if (((ifa->type != OSPF_IT_VLINK) && (ifa->type != OSPF_IT_PTP)) && - ((unsigned) ipa_mklen(mask) != ifa->iface->addr->pxlen)) - { - log(L_ERR "%s%I%sbad netmask %I.", beg, faddr, rec, mask); - return; - } + if (ifa->type != OSPF_IT_VLINK) + { + char *msg = L_WARN "Received HELLO packet %s (%I) is inconsistent " + "with the primary address of interface %s."; + + if ((ifa->type != OSPF_IT_PTP) && + !ipa_equal(mask, ipa_mkmask(ifa->iface->addr->pxlen))) + { + if (!n) log(msg, "netmask", mask, ifa->iface->name); + return; + } + + /* This check is not specified in RFC 2328, but it is needed + * to handle the case when there is more IP networks on one + * physical network (which is not handled in RFC 2328). + * We allow OSPF on primary IP address only and ignore HELLO packets + * with secondary addresses (which are sent for example by Quagga. + */ + if ((ifa->iface->addr->flags & IA_UNNUMBERED) ? + !ipa_equal(faddr, ifa->iface->addr->opposite) : + !ipa_equal(ipa_and(faddr,mask), ifa->iface->addr->prefix)) + { + if (!n) log(msg, "address", faddr, ifa->iface->name); + return; + } + } if (ntohs(ps->helloint) != ifa->helloint) { - log(L_WARN "%s%I%shello interval mismatch (%d).", beg, faddr, rec, + log(L_ERR "%s%I%shello interval mismatch (%d).", beg, faddr, rec, ntohs(ps->helloint)); return; } @@ -205,7 +225,8 @@ ospf_hello_send(timer * timer, int poll, struct ospf_neighbor *dirn) pkt->netmask = ipa_mkmask(ifa->iface->addr->pxlen); ipa_hton(pkt->netmask); - if (ifa->type == OSPF_IT_VLINK) pkt->netmask = IPA_NONE; + if ((ifa->type == OSPF_IT_VLINK) || (ifa->type == OSPF_IT_PTP)) + pkt->netmask = IPA_NONE; pkt->helloint = ntohs(ifa->helloint); pkt->options = ifa->oa->opt.byte; pkt->priority = ifa->priority; diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 0cab1d7b..c9b5f430 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -76,6 +76,9 @@ #include <stdlib.h> #include "ospf.h" + +static void ospf_rt_notify(struct proto *p, net * n, rte * new, rte * old UNUSED, ea_list * attrs); +static void ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a); static int ospf_rte_better(struct rte *new, struct rte *old); static int ospf_rte_same(struct rte *new, struct rte *old); static void ospf_disp(timer *timer); @@ -124,6 +127,9 @@ ospf_start(struct proto *p) po->disp_timer->hook = ospf_disp; po->disp_timer->recurrent = po->tick; tm_start(po->disp_timer, 1); + po->lsab_size = 256; + po->lsab_used = 0; + po->lsab = mb_alloc(p->pool, po->lsab_size); init_list(&(po->iface_list)); init_list(&(po->area_list)); fib_init(&po->rtf, p->pool, sizeof(ort), 16, ospf_rt_initort); @@ -142,6 +148,7 @@ ospf_start(struct proto *p) oa = mb_allocz(p->pool, sizeof(struct ospf_area)); add_tail(&po->area_list, NODE oa); po->areano++; + oa->ac = ac; oa->stub = ac->stub; oa->areaid = ac->areaid; oa->rt = NULL; @@ -224,8 +231,10 @@ ospf_init(struct proto_config *c) p->import_control = ospf_import_control; p->make_tmp_attrs = ospf_make_tmp_attrs; p->store_tmp_attrs = ospf_store_tmp_attrs; + p->accept_ra_types = RA_OPTIMAL; p->rt_notify = ospf_rt_notify; p->if_notify = ospf_iface_notify; + p->ifa_notify = ospf_ifa_notify; p->rte_better = ospf_rte_better; p->rte_same = ospf_rte_same; @@ -428,7 +437,7 @@ ospf_shutdown(struct proto *p) return PS_DOWN; } -void +static void ospf_rt_notify(struct proto *p, net * n, rte * new, rte * old UNUSED, ea_list * attrs) { @@ -473,6 +482,25 @@ ospf_rt_notify(struct proto *p, net * n, rte * new, rte * old UNUSED, } static void +ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) +{ + struct proto_ospf *po = (struct proto_ospf *) p; + struct ospf_iface *ifa; + + if ((a->flags & IA_SECONDARY) || (a->flags & IA_UNNUMBERED)) + return; + + WALK_LIST(ifa, po->iface_list) + { + if (ifa->iface == a->iface) + { + schedule_rt_lsa(ifa->oa); + return; + } + } +} + +static void ospf_get_status(struct proto *p, byte * buf) { struct proto_ospf *po = (struct proto_ospf *) p; @@ -602,9 +630,31 @@ ospf_reconfigure(struct proto *p, struct proto_config *c) if (!oa) return 0; + oa->ac = newac; oa->stub = newac->stub; if (newac->stub && (oa->areaid == 0)) oa->stub = 0; + /* Check stubnet_list */ + struct ospf_stubnet_config *oldsn = HEAD(oldac->stubnet_list); + struct ospf_stubnet_config *newsn = HEAD(newac->stubnet_list); + + while (((NODE(oldsn))->next != NULL) && ((NODE(newsn))->next != NULL)) + { + if (!ipa_equal(oldsn->px.addr, newsn->px.addr) || + (oldsn->px.len != newsn->px.len) || + (oldsn->hidden != newsn->hidden) || + (oldsn->summary != newsn->summary) || + (oldsn->cost != newsn->cost)) + break; + + oldsn = (struct ospf_stubnet_config *)(NODE(oldsn))->next; + newsn = (struct ospf_stubnet_config *)(NODE(newsn))->next; + } + + /* If there is no change, both pointers should be NULL */ + if (((NODE(oldsn))->next) != ((NODE(newsn))->next)) + schedule_rt_lsa(oa); + /* Change net_list */ FIB_WALK(&oa->net_fib, nf) /* First check if some networks are deleted */ { diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index fb78af4e..23f21b8e 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -99,6 +99,14 @@ struct area_net u32 metric; }; +struct ospf_stubnet_config +{ + node n; + struct prefix px; + int hidden, summary; + u32 cost; +}; + struct ospf_area_config { node n; @@ -107,6 +115,7 @@ struct ospf_area_config list patt_list; list vlink_list; list net_list; + list stubnet_list; }; struct obits @@ -523,6 +532,7 @@ struct ospf_area { node n; u32 areaid; + struct ospf_area_config *ac; /* Related area config */ int origrt; /* Rt lsa origination scheduled? */ struct top_hash_entry *rt; /* My own router LSA */ list cand; /* List of candidates for RT calc. */ @@ -550,6 +560,8 @@ struct proto_ospf int rfc1583; /* RFC1583 compatibility */ int ebit; /* Did I originate any ext lsa? */ struct ospf_area *backbone; /* If exists */ + void *lsab; /* LSA buffer used when originating router LSAs */ + int lsab_size, lsab_used; }; struct ospf_iface_patt @@ -585,8 +597,6 @@ int ospf_import_control(struct proto *p, rte **new, ea_list **attrs, struct linpool *pool); struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool); void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs); -void ospf_rt_notify(struct proto *p, net *n, rte *new, rte *old, - ea_list * attrs); void schedule_rt_lsa(struct ospf_area *oa); void schedule_rtcalc(struct proto_ospf *po); void schedule_net_lsa(struct ospf_iface *ifa); diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 23785fe8..783d28ed 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -323,6 +323,9 @@ ospf_rx_hook(sock * sk, int size) return 1; } + /* This is deviation from RFC 2328 - neighbours should be identified by + * IP address on broadcast and NBMA networks. + */ n = find_neigh(ifa, ntohl(((struct ospf_packet *) ps)->routerid)); if(!n && (ps->type != HELLO_P)) diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index f906de92..79b21e6a 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -1003,11 +1003,11 @@ again1: e->pref = p->preference; DBG("Mod rte type %d - %I/%d via %I on iface %s, met %d\n", a0.source, nf->fn.prefix, nf->fn.pxlen, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1); - rte_update(p->table, ne, p, e); + rte_update(p->table, ne, p, p, e); } else { - rte_update(p->table, ne, p, NULL); + rte_update(p->table, ne, p, p, NULL); FIB_ITERATE_PUT(&fit, nftmp); fib_delete(fib, nftmp); goto again1; diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index a15d2e35..371856f9 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -23,176 +23,198 @@ int ptp_unnumbered_stub_lsa = 0; static void * +lsab_alloc(struct proto_ospf *po, unsigned size) +{ + unsigned offset = po->lsab_used; + po->lsab_used += size; + if (po->lsab_used > po->lsab_size) + { + po->lsab_size = MAX(po->lsab_used, 2 * po->lsab_size); + po->lsab = mb_realloc(po->proto.pool, po->lsab, po->lsab_size); + } + return ((byte *) po->lsab) + offset; +} + +static inline void * +lsab_allocz(struct proto_ospf *po, unsigned size) +{ + void *r = lsab_alloc(po, size); + bzero(r, size); + return r; +} + +static inline void * +lsab_flush(struct proto_ospf *po) +{ + void *r = mb_alloc(po->proto.pool, po->lsab_size); + memcpy(r, po->lsab, po->lsab_used); + po->lsab_used = 0; + return r; +} + +static int +configured_stubnet(struct ospf_area *oa, struct ifa *a) +{ + struct ospf_stubnet_config *sn; + WALK_LIST(sn, oa->ac->stubnet_list) + { + if (sn->summary) + { + if (ipa_in_net(a->prefix, sn->px.addr, sn->px.len) && (a->pxlen >= sn->px.len)) + return 1; + } + else + { + if (ipa_equal(a->prefix, sn->px.addr) && (a->pxlen == sn->px.len)) + return 1; + } + } + return 0; +} + +static void * originate_rt_lsa_body(struct ospf_area *oa, u16 * length) { struct proto_ospf *po = oa->po; struct ospf_iface *ifa; - int j = 0, k = 0; - u16 i = 0; + int i = 0, j = 0, k = 0, bitv = 0; struct ospf_lsa_rt *rt; - struct ospf_lsa_rt_link *ln, *ln_after; + struct ospf_lsa_rt_link *ln; struct ospf_neighbor *neigh; DBG("%s: Originating RT_lsa body for area \"%I\".\n", po->proto.name, oa->areaid); - - WALK_LIST(ifa, po->iface_list) - { - if ((ifa->oa == oa) && (ifa->state != OSPF_IS_DOWN)) - { - i++; - if ((ifa->type == OSPF_IT_PTP) && (ifa->state == OSPF_IS_PTP) && - (ptp_unnumbered_stub_lsa || !(ifa->iface->addr->flags & IA_UNNUMBERED))) - i++; - } - } - rt = mb_allocz(po->proto.pool, sizeof(struct ospf_lsa_rt) + - i * sizeof(struct ospf_lsa_rt_link)); + + ASSERT(po->lsab_used == 0); + rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt)); if (po->areano > 1) rt->veb.bit.b = 1; if ((po->ebit) && (!oa->stub)) rt->veb.bit.e = 1; - ln = (struct ospf_lsa_rt_link *) (rt + 1); - ln_after = ln + i; + rt = NULL; /* buffer might be reallocated later */ WALK_LIST(ifa, po->iface_list) { - if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) && (!EMPTY_LIST(ifa->neigh_list))) + int master = 0; + + if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) && + (!EMPTY_LIST(ifa->neigh_list))) { neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list); if ((neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff)) - rt->veb.bit.v = 1; + bitv = 1; } if ((ifa->oa != oa) || (ifa->state == OSPF_IS_DOWN)) continue; - if (ln == ln_after) - die("LSA space overflow"); + /* BIRD does not support interface loops */ + ASSERT(ifa->state != OSPF_IS_LOOP); - if (ifa->state == OSPF_IS_LOOP) - { - ln->type = 3; - ln->id = ipa_to_u32(ifa->iface->addr->ip); - ln->data = 0xffffffff; - ln->metric = 0; - ln->notos = 0; - } - else - { - switch (ifa->type) + switch (ifa->type) { - case OSPF_IT_PTP: /* rfc2328 - pg126 */ + case OSPF_IT_PTP: /* RFC2328 - 12.4.1.1 */ neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list); if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL)) { + ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); ln->type = LSART_PTP; ln->id = neigh->rid; + ln->data = (ifa->iface->addr->flags & IA_UNNUMBERED) ? + ifa->iface->index : ipa_to_u32(ifa->iface->addr->ip); ln->metric = ifa->cost; ln->notos = 0; - if (ifa->iface->addr->flags & IA_UNNUMBERED) - { - ln->data = ifa->iface->index; - } - else - { - ln->data = ipa_to_u32(ifa->iface->addr->ip); - } - } - else - { - ln--; - i--; /* No link added */ - } - - if ((ifa->state == OSPF_IS_PTP) && - (ptp_unnumbered_stub_lsa || !(ifa->iface->addr->flags & IA_UNNUMBERED))) - { - ln++; - if (ln == ln_after) - die("LSA space overflow"); - - ln->type = LSART_STUB; - ln->metric = ifa->cost; - ln->notos = 0; - if (ifa->iface->addr->flags & IA_UNNUMBERED) - { - ln->id = ipa_to_u32(ifa->iface->addr->opposite); - ln->data = 0xffffffff; - } - else - { - ln->data = ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen)); - ln->id = ipa_to_u32(ifa->iface->addr->prefix) & ln->data; - } + i++; + master = 1; } break; - case OSPF_IT_BCAST: + + case OSPF_IT_BCAST: /* RFC2328 - 12.4.1.2 */ case OSPF_IT_NBMA: if (ifa->state == OSPF_IS_WAITING) - { - ln->type = LSART_STUB; - ln->data = ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen)); - ln->id = ipa_to_u32(ifa->iface->addr->prefix) & ln->data; - ln->metric = ifa->cost; - ln->notos = 0; - } - else - { - j = 0, k = 0; - WALK_LIST(neigh, ifa->neigh_list) + break; + + j = 0, k = 0; + WALK_LIST(neigh, ifa->neigh_list) { if ((neigh->rid == ifa->drid) && (neigh->state == NEIGHBOR_FULL)) k = 1; if (neigh->state == NEIGHBOR_FULL) j = 1; } - if (((ifa->state == OSPF_IS_DR) && (j == 1)) || (k == 1)) + + if (((ifa->state == OSPF_IS_DR) && (j == 1)) || (k == 1)) { + ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); ln->type = LSART_NET; ln->id = ipa_to_u32(ifa->drip); ln->data = ipa_to_u32(ifa->iface->addr->ip); ln->metric = ifa->cost; ln->notos = 0; + i++; + master = 1; } - else - { - ln->type = LSART_STUB; - ln->data = ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen)); - ln->id = ipa_to_u32(ifa->iface->addr->prefix) & ln->data; - ln->metric = ifa->cost; - ln->notos = 0; - } - } break; - case OSPF_IT_VLINK: + + case OSPF_IT_VLINK: /* RFC2328 - 12.4.1.3 */ neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list); if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff)) { + ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); ln->type = LSART_VLNK; ln->id = neigh->rid; + ln->data = ipa_to_u32(ifa->iface->addr->ip); ln->metric = ifa->cost; ln->notos = 0; - } - else - { - ln--; - i--; /* No link added */ + i++; + master = 1; } break; + default: - ln--; - i--; /* No link added */ log("Unknown interface type %s", ifa->iface->name); break; } - } - ln++; + + /* Now we will originate stub areas for interfaces addresses */ + struct ifa *a; + WALK_LIST(a, ifa->iface->addrs) + { + if (((a == ifa->iface->addr) && master) || + (a->flags & IA_SECONDARY) || + (a->flags & IA_UNNUMBERED) || + configured_stubnet(oa, a)) + continue; + + + ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); + ln->type = LSART_STUB; + ln->id = ipa_to_u32(a->prefix); + ln->data = ipa_to_u32(ipa_mkmask(a->pxlen)); + ln->metric = ifa->cost; + ln->notos = 0; + i++; + } } + + struct ospf_stubnet_config *sn; + WALK_LIST(sn, oa->ac->stubnet_list) + if (!sn->hidden) + { + ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link)); + ln->type = LSART_STUB; + ln->id = ipa_to_u32(sn->px.addr); + ln->data = ipa_to_u32(ipa_mkmask(sn->px.len)); + ln->metric = sn->cost; + ln->notos = 0; + i++; + } + + rt = po->lsab; rt->links = i; - *length = i * sizeof(struct ospf_lsa_rt_link) + sizeof(struct ospf_lsa_rt) + - sizeof(struct ospf_lsa_header); - return rt; + rt->veb.bit.v = bitv; + *length = po->lsab_used + sizeof(struct ospf_lsa_header); + return lsab_flush(po); } /** diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y index 52f70dce..4e6c80cd 100644 --- a/proto/pipe/config.Y +++ b/proto/pipe/config.Y @@ -10,9 +10,13 @@ CF_HDR #include "proto/pipe/pipe.h" +CF_DEFINES + +#define PIPE_CFG ((struct pipe_config *) this_proto) + CF_DECLS -CF_KEYWORDS(PIPE, PEER, TABLE) +CF_KEYWORDS(PIPE, PEER, TABLE, MODE, OPAQUE, TRANSPARENT) CF_GRAMMAR @@ -21,6 +25,7 @@ CF_ADDTO(proto, pipe_proto '}') pipe_proto_start: proto_start PIPE { this_proto = proto_config_new(&proto_pipe, sizeof(struct pipe_config)); this_proto->preference = DEF_PREF_PIPE; + PIPE_CFG->mode = PIPE_OPAQUE; } ; @@ -30,8 +35,10 @@ pipe_proto: | pipe_proto PEER TABLE SYM ';' { if ($4->class != SYM_TABLE) cf_error("Routing table name expected"); - ((struct pipe_config *) this_proto)->peer = $4->def; + PIPE_CFG->peer = $4->def; } + | pipe_proto MODE OPAQUE ';' { PIPE_CFG->mode = PIPE_OPAQUE; } + | pipe_proto MODE TRANSPARENT ';' { PIPE_CFG->mode = PIPE_TRANSPARENT; } ; CF_CODE diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index d1d6bba9..8ff430a9 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -31,12 +31,16 @@ #include "pipe.h" static void -pipe_send(struct pipe_proto *p, rtable *dest, net *n, rte *new, rte *old UNUSED, ea_list *attrs) +pipe_send(struct pipe_proto *p, rtable *dest, net *n, rte *new, rte *old, ea_list *attrs) { + struct proto *src; net *nn; rte *e; rta a; + if (!new && !old) + return; + if (dest->pipe_busy) { log(L_ERR "Pipe loop detected when sending %I/%d to table %s", @@ -47,17 +51,34 @@ pipe_send(struct pipe_proto *p, rtable *dest, net *n, rte *new, rte *old UNUSED, if (new) { memcpy(&a, new->attrs, sizeof(rta)); - a.proto = &p->p; - a.source = RTS_PIPE; + + if (p->mode == PIPE_OPAQUE) + { + a.proto = &p->p; + a.source = RTS_PIPE; + } + a.aflags = 0; a.eattrs = attrs; e = rte_get_temp(&a); e->net = nn; + + if (p->mode == PIPE_TRANSPARENT) + { + /* Copy protocol specific embedded attributes. */ + memcpy(&(e->u), &(new->u), sizeof(e->u)); + } + + src = new->attrs->proto; } else - e = NULL; + { + e = NULL; + src = old->attrs->proto; + } + dest->pipe_busy = 1; - rte_update(dest, nn, &p->p, e); + rte_update(dest, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e); dest->pipe_busy = 0; } @@ -82,7 +103,7 @@ pipe_rt_notify_sec(struct proto *P, net *net, rte *new, rte *old, ea_list *attrs static int pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED) { - struct proto *pp = (*ee)->attrs->proto; + struct proto *pp = (*ee)->sender; if (pp == P || pp == &((struct pipe_proto *) P)->phantom->p) return -1; /* Avoid local loops automatically */ @@ -106,6 +127,7 @@ pipe_start(struct proto *P) memcpy(ph, p, sizeof(struct pipe_proto)); p->phantom = ph; ph->phantom = p; + ph->p.accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY; ph->p.rt_notify = pipe_rt_notify_sec; ph->p.proto_state = PS_UP; ph->p.core_state = ph->p.core_goal = FS_HAPPY; @@ -141,6 +163,8 @@ pipe_init(struct proto_config *C) struct pipe_proto *p = (struct pipe_proto *) P; p->peer = c->peer->table; + p->mode = c->mode; + P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY; P->rt_notify = pipe_rt_notify_pri; P->import_control = pipe_import_control; return P; @@ -162,7 +186,7 @@ pipe_get_status(struct proto *P, byte *buf) { struct pipe_proto *p = (struct pipe_proto *) P; - bsprintf(buf, "-> %s", p->peer->name); + bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer->name); } static int @@ -171,7 +195,7 @@ pipe_reconfigure(struct proto *p, struct proto_config *new) struct pipe_config *o = (struct pipe_config *) p->cf; struct pipe_config *n = (struct pipe_config *) new; - return o->peer == n->peer; + return (o->peer == n->peer) && (o->mode == n->mode); } struct protocol proto_pipe = { diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h index 7e9cf8ae..368ba41b 100644 --- a/proto/pipe/pipe.h +++ b/proto/pipe/pipe.h @@ -9,14 +9,19 @@ #ifndef _BIRD_PIPE_H_ #define _BIRD_PIPE_H_ +#define PIPE_OPAQUE 0 +#define PIPE_TRANSPARENT 1 + struct pipe_config { struct proto_config c; struct rtable_config *peer; /* Table we're connected to */ + int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */ }; struct pipe_proto { struct proto p; struct rtable *peer; + int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */ struct pipe_proto *phantom; }; diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 12cc8783..c655cc36 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -268,7 +268,7 @@ rip_rte_update_if_better(rtable *tab, net *net, struct proto *p, rte *new) if (!old || p->rte_better(new, old) || (ipa_equal(old->attrs->from, new->attrs->from) && (old->u.rip.metric != new->u.rip.metric)) ) - rte_update(tab, net, p, new); + rte_update(tab, net, p, p, new); } /* @@ -946,6 +946,7 @@ rip_rte_remove(net *net UNUSED, rte *rte) void rip_init_instance(struct proto *p) { + p->accept_ra_types = RA_OPTIMAL; p->if_notify = rip_if_notify; p->rt_notify = rip_rt_notify; p->import_control = rip_import_control; diff --git a/proto/static/static.c b/proto/static/static.c index c5324796..c71d1da9 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -60,7 +60,7 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa) e = rte_get_temp(aa); e->net = n; e->pflags = 0; - rte_update(p->table, n, p, e); + rte_update(p->table, n, p, p, e); r->installed = 1; } @@ -75,7 +75,7 @@ static_remove(struct proto *p, struct static_route *r) DBG("Removing static route %I/%d\n", r->net, r->masklen); n = net_find(p->table, r->net, r->masklen); if (n) - rte_update(p->table, n, p, NULL); + rte_update(p->table, n, p, p, NULL); r->installed = 0; } |