summaryrefslogtreecommitdiff
path: root/proto
diff options
context:
space:
mode:
Diffstat (limited to 'proto')
-rw-r--r--proto/bgp/attrs.c191
-rw-r--r--proto/bgp/bgp.c202
-rw-r--r--proto/bgp/bgp.h19
-rw-r--r--proto/bgp/config.Y3
-rw-r--r--proto/bgp/packets.c51
-rw-r--r--proto/ospf/config.Y30
-rw-r--r--proto/ospf/hello.c37
-rw-r--r--proto/ospf/ospf.c52
-rw-r--r--proto/ospf/ospf.h14
-rw-r--r--proto/ospf/packet.c3
-rw-r--r--proto/ospf/rt.c4
-rw-r--r--proto/ospf/topology.c230
-rw-r--r--proto/pipe/config.Y11
-rw-r--r--proto/pipe/pipe.c40
-rw-r--r--proto/pipe/pipe.h5
-rw-r--r--proto/rip/rip.c3
-rw-r--r--proto/static/static.c4
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;
}