Sending of update messages works!

This commit is contained in:
Martin Mares 2000-04-17 09:37:31 +00:00
parent c0668f3696
commit f421cfdd80
3 changed files with 275 additions and 91 deletions

View file

@ -31,13 +31,172 @@ struct attr_desc {
void (*format)(eattr *ea, byte *buf);
};
extern struct attr_desc bgp_attr_table[];
static int
bgp_check_origin(struct bgp_proto *p, byte *a, int len)
{
if (len > 2)
return 6;
return 0;
}
static void
bgp_format_origin(eattr *a, byte *buf)
{
static char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" };
bsprintf(buf, bgp_origin_names[a->u.data]);
}
static int
bgp_check_path(struct bgp_proto *p, byte *a, int len)
{
while (len)
{
DBG("Path segment %02x %02x\n", a[0], a[1]);
if (len < 2 ||
a[0] != BGP_PATH_AS_SET && a[0] != BGP_PATH_AS_SEQUENCE ||
2*a[1] + 2 > len)
return 11;
len -= 2*a[1] + 2;
a += 2*a[1] + 2;
}
return 0;
}
static int
bgp_check_next_hop(struct bgp_proto *p, byte *a, int len)
{
ip_addr addr;
memcpy(&addr, a, len);
ipa_ntoh(addr);
if (ipa_classify(addr) & IADDR_HOST)
return 0;
else
return 8;
}
static int
bgp_check_local_pref(struct bgp_proto *p, byte *a, int len)
{
if (!p->is_internal) /* Ignore local preference from EBGP connections */
return -1;
return 0;
}
static struct attr_desc bgp_attr_table[] = {
{ NULL, -1, 0, 0, /* Undefined */
NULL, NULL },
{ "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, /* BA_ORIGIN */
bgp_check_origin, bgp_format_origin },
{ "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, /* BA_AS_PATH */
bgp_check_path, NULL },
{ "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, /* BA_NEXT_HOP */
bgp_check_next_hop, NULL },
{ "MED", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_MULTI_EXIT_DISC */
NULL, NULL },
{ "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_LOCAL_PREF */
bgp_check_local_pref, NULL },
{ "atomic_aggr", 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_ATOMIC_AGGR */
NULL, NULL },
{ "aggregator", 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_AGGREGATOR */
NULL, NULL },
#if 0
/* FIXME: Handle community lists and remember to convert their endianity and normalize them */
{ 0, 0 }, /* BA_COMMUNITY */
{ 0, 0 }, /* BA_ORIGINATOR_ID */
{ 0, 0 }, /* BA_CLUSTER_LIST */
#endif
};
byte *
bgp_encode_attrs(byte *w, struct bgp_bucket *buck)
{
int remains = 1024;
unsigned int i, code, flags;
byte *start = w;
int len;
w += 2;
for(i=0; i<buck->eattrs->count; i++)
{
eattr *a = &buck->eattrs->attrs[i];
ASSERT(EA_PROTO(a->id) == EAP_BGP);
code = EA_ID(a->id);
if (code && code < ARRAY_SIZE(bgp_attr_table))
{
struct attr_desc *desc = &bgp_attr_table[code];
len = desc->expected_length;
if (len < 0)
{
ASSERT(!(a->type & EAF_EMBEDDED));
len = a->u.ptr->length;
}
}
else
{
ASSERT((a->type & EAF_TYPE_MASK) == EAF_TYPE_OPAQUE);
len = a->u.ptr->length;
}
DBG("\tAttribute %02x (type %02x, %d bytes)\n", code, a->type, len);
/* FIXME: Partial bit for locally added transitive attributes */
if (remains < len + 4)
{
log(L_ERR "BGP: attribute list too long, ignoring the remaining attributes");
break;
}
flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL);
if (len < 256)
{
*w++ = flags;
*w++ = code;
*w++ = len;
remains -= 3;
}
else
{
*w++ = flags | BAF_EXT_LEN;
*w++ = code;
put_u16(w, len);
w += 2;
remains -= 4;
}
switch (a->type & EAF_TYPE_MASK)
{
case EAF_TYPE_INT:
case EAF_TYPE_ROUTER_ID:
if (len == 4)
put_u32(w, a->u.data);
else
*w = a->u.data;
break;
case EAF_TYPE_IP_ADDRESS:
{
ip_addr ip = *(ip_addr *)a->u.ptr->data;
ipa_hton(ip);
memcpy(w, &ip, len);
break;
}
case EAF_TYPE_OPAQUE:
case EAF_TYPE_AS_PATH:
case EAF_TYPE_INT_SET:
memcpy(w, a->u.ptr->data, len);
break;
default:
bug("bgp_encode_attrs: unknown attribute type %02x", a->type);
}
remains -= len;
w += len;
}
put_u16(start, w-start-2);
return w;
}
static void
bgp_init_prefix(struct fib_node *N)
{
struct bgp_prefix *p = (struct bgp_prefix *) N;
/* FIXME */
p->bucket_node.next = NULL;
}
static void
@ -104,6 +263,8 @@ bgp_new_bucket(struct bgp_proto *p, ea_list *new, unsigned hash)
p->bucket_hash[index] = b;
b->hash_prev = NULL;
b->hash = hash;
add_tail(&p->bucket_queue, &b->send_node);
init_list(&b->prefixes);
memcpy(b->eattrs, new, ea_size);
dest = ((byte *)b->eattrs) + ea_size_aligned;
@ -169,7 +330,7 @@ bgp_get_bucket(struct bgp_proto *p, ea_list *old, ea_list *tmp)
*d = *a;
switch (d->type & EAF_TYPE_MASK)
{
case EAF_TYPE_INT_SET:
case EAF_TYPE_INT_SET: /* FIXME: Normalize the other attributes? */
{
struct adata *z = alloca(sizeof(struct adata) + d->u.ptr->length);
z->length = d->u.ptr->length;
@ -200,27 +361,60 @@ bgp_get_bucket(struct bgp_proto *p, ea_list *old, ea_list *tmp)
return NULL;
}
/* Check if next hop is valid */
a = ea_find(new, EA_CODE(EAP_BGP, BA_NEXT_HOP));
ASSERT(a);
if (ipa_equal(p->next_hop, *(ip_addr *)a->u.ptr))
return NULL;
/* Create new bucket */
DBG("Creating bucket.\n");
return bgp_new_bucket(p, new, hash);
}
void
bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck)
{
if (buck->hash_next)
buck->hash_next->hash_prev = buck->hash_prev;
if (buck->hash_prev)
buck->hash_prev->hash_next = buck->hash_next;
else
p->bucket_hash[buck->hash & (p->hash_size-1)] = buck->hash_next;
mb_free(buck);
}
void
bgp_rt_notify(struct proto *P, net *n, rte *new, rte *old, ea_list *tmpa)
{
struct bgp_proto *p = (struct bgp_proto *) P;
struct bgp_bucket *buck;
struct bgp_prefix *px;
DBG("BGP: Got route %I/%d\n", n->n.prefix, n->n.pxlen);
DBG("BGP: Got route %I/%d %s\n", n->n.prefix, n->n.pxlen, new ? "up" : "down");
if (new)
{
struct bgp_bucket *buck = bgp_get_bucket(p, new->attrs->eattrs, tmpa);
buck = bgp_get_bucket(p, new->attrs->eattrs, tmpa);
if (!buck) /* Inconsistent attribute list */
return;
}
/* FIXME: Normalize attributes */
/* FIXME: Check next hop */
else
{
if (!(buck = p->withdraw_bucket))
{
buck = p->withdraw_bucket = mb_alloc(P->pool, sizeof(struct bgp_bucket));
init_list(&buck->prefixes);
}
}
px = fib_get(&p->prefix_fib, &n->n.prefix, n->n.pxlen);
if (px->bucket_node.next)
{
DBG("\tRemoving old entry.\n");
rem_node(&px->bucket_node);
}
add_tail(&buck->prefixes, &px->bucket_node);
bgp_schedule_packet(p->conn, PKT_UPDATE);
}
static int
@ -404,83 +598,6 @@ bgp_path_loopy(struct bgp_proto *p, eattr *a)
return 0;
}
static int
bgp_check_origin(struct bgp_proto *p, byte *a, int len)
{
if (len > 2)
return 6;
return 0;
}
static void
bgp_format_origin(eattr *a, byte *buf)
{
static char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" };
bsprintf(buf, bgp_origin_names[a->u.data]);
}
static int
bgp_check_path(struct bgp_proto *p, byte *a, int len)
{
while (len)
{
DBG("Path segment %02x %02x\n", a[0], a[1]);
if (len < 2 ||
a[0] != BGP_PATH_AS_SET && a[0] != BGP_PATH_AS_SEQUENCE ||
2*a[1] + 2 > len)
return 11;
len -= 2*a[1] + 2;
a += 2*a[1] + 2;
}
return 0;
}
static int
bgp_check_next_hop(struct bgp_proto *p, byte *a, int len)
{
ip_addr addr;
memcpy(&addr, a, len);
if (ipa_classify(ipa_ntoh(addr)) & IADDR_HOST)
return 0;
else
return 8;
}
static int
bgp_check_local_pref(struct bgp_proto *p, byte *a, int len)
{
if (!p->is_internal) /* Ignore local preference from EBGP connections */
return -1;
return 0;
}
static struct attr_desc bgp_attr_table[] = {
{ NULL, -1, 0, 0, /* Undefined */
NULL, NULL },
{ "origin", 1, BAF_TRANSITIVE, EAF_TYPE_INT, /* BA_ORIGIN */
bgp_check_origin, bgp_format_origin },
{ "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, /* BA_AS_PATH */
bgp_check_path, NULL },
{ "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, /* BA_NEXT_HOP */
bgp_check_next_hop, NULL },
{ "MED", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_MULTI_EXIT_DISC */
NULL, NULL },
{ "local_pref", 4, BAF_OPTIONAL, EAF_TYPE_INT, /* BA_LOCAL_PREF */
bgp_check_local_pref, NULL },
{ "atomic_aggr", 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_ATOMIC_AGGR */
NULL, NULL },
{ "aggregator", 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, /* BA_AGGREGATOR */
NULL, NULL },
#if 0
/* FIXME: Handle community lists and remember to convert their endianity and normalize them */
{ 0, 0 }, /* BA_COMMUNITY */
{ 0, 0 }, /* BA_ORIGINATOR_ID */
{ 0, 0 }, /* BA_CLUSTER_LIST */
#endif
};
struct rta *
bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool)
{
@ -540,7 +657,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
DBG("Attr %02x %02x %d\n", code, flags, l);
if (seen[code/8] & (1 << (code%8)))
goto malformed;
if (code && code < sizeof(bgp_attr_table)/sizeof(bgp_attr_table[0]))
if (code && code < ARRAY_SIZE(bgp_attr_table))
{
struct attr_desc *desc = &bgp_attr_table[code];
if (desc->expected_length >= 0 && desc->expected_length != (int) l)
@ -591,7 +708,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
ea->attrs[0].u.data = get_u32(z);
break;
case EAF_TYPE_IP_ADDRESS:
*(ip_addr *)ad->data = ipa_ntoh(*(ip_addr *)ad->data);
ipa_ntoh(*(ip_addr *)ad->data);
break;
}
}

View file

@ -68,8 +68,8 @@ struct bgp_prefix {
};
struct bgp_bucket {
struct bgp_bucket *hash_next, *hash_prev; /* Node in bucket hash table */
node send_node; /* Node in send queue */
struct bgp_bucket *hash_next, *hash_prev; /* Node in bucket hash table */
unsigned hash; /* Hash over extended attributes */
list prefixes; /* Prefixes in this buckets */
ea_list eattrs[0]; /* Per-bucket extended attributes */
@ -97,6 +97,8 @@ int bgp_rte_better(struct rte *, struct rte *);
void bgp_rt_notify(struct proto *, struct network *, struct rte *, struct rte *, struct ea_list *);
int bgp_import_control(struct proto *, struct rte **, struct ea_list **, struct linpool *);
void bgp_attr_init(struct bgp_proto *);
byte *bgp_encode_attrs(byte *w, struct bgp_bucket *buck);
void bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck);
/* packets.c */

View file

@ -46,12 +46,76 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
return buf+10;
}
static unsigned int
bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, unsigned int remains)
{
byte *start = w;
ip_addr a;
int bytes;
while (!EMPTY_LIST(buck->prefixes) && remains >= 5)
{
struct bgp_prefix *px = SKIP_BACK(struct bgp_prefix, bucket_node, HEAD(buck->prefixes));
DBG("\tDequeued route %I/%d\n", px->n.prefix, px->n.pxlen);
*w++ = px->n.pxlen;
bytes = (px->n.pxlen + 7) / 8;
a = px->n.prefix;
ipa_hton(a);
memcpy(w, &a, bytes);
w += bytes;
rem_node(&px->bucket_node);
fib_delete(&p->prefix_fib, px);
}
return w - start;
}
static byte *
bgp_create_update(struct bgp_conn *conn, byte *buf)
{
DBG("BGP: Sending update\n");
struct bgp_proto *bgp = conn->bgp;
struct bgp_bucket *buck;
int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4;
byte *w, *wold;
ip_addr ip;
int wd_size = 0;
int r_size = 0;
bug("Don't know how to create updates");
DBG("BGP: Sending update\n");
w = buf+2;
if ((buck = bgp->withdraw_bucket) && !EMPTY_LIST(buck->prefixes))
{
DBG("Withdrawn routes:\n");
wd_size = bgp_encode_prefixes(bgp, w, buck, remains);
w += wd_size;
remains -= wd_size;
}
put_u16(buf, wd_size);
if (remains >= 2048)
{
while ((buck = (struct bgp_bucket *) HEAD(bgp->bucket_queue))->send_node.next)
{
if (EMPTY_LIST(buck->prefixes))
{
DBG("Deleting empty bucket %p\n", buck);
rem_node(&buck->send_node);
bgp_free_bucket(bgp, buck);
continue;
}
DBG("Processing bucket %p\n", buck);
w = bgp_encode_attrs(w, buck);
remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - (w-buf);
r_size = bgp_encode_prefixes(bgp, w, buck, remains);
w += r_size;
break;
}
}
else
{
put_u16(w, 0);
w += 2;
}
return (wd_size || r_size) ? w : NULL;
}
static void
@ -225,7 +289,8 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
memcpy(&prefix, pp, q); \
pp += q; \
ll -= q; \
prefix = ipa_and(ipa_ntoh(prefix), ipa_mkmask(b)); \
ipa_ntoh(prefix); \
prefix = ipa_and(prefix, ipa_mkmask(b)); \
pxlen = b; \
} while (0)
/* FIXME: Check validity of prefixes */