summaryrefslogtreecommitdiff
path: root/proto/bgp/attrs.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bgp/attrs.c')
-rw-r--r--proto/bgp/attrs.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index bf747cbd..29d6acb8 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -6,10 +6,220 @@
* Can be freely distributed and used under the terms of the GNU GPL.
*/
+#define LOCAL_DEBUG
+
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "conf/conf.h"
+#include "lib/resource.h"
+#include "lib/string.h"
+#include "lib/unaligned.h"
#include "bgp.h"
+
+static int bgp_check_origin(byte *a, int len)
+{
+ if (len > 2)
+ return 6;
+ return 0;
+}
+
+static int bgp_check_path(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(byte *a, int len)
+{
+ ip_addr addr;
+
+ memcpy(&addr, a, len);
+ if (ipa_classify(ipa_ntoh(addr)) & IADDR_HOST)
+ return 0;
+ else
+ return 8;
+}
+
+struct attr_desc {
+ int expected_length;
+ int expected_flags;
+ int type;
+ int (*validate)(byte *attr, int len);
+};
+
+static struct attr_desc bgp_attr_table[] = {
+ { -1, 0, 0, NULL }, /* Undefined */
+ { 1, BAF_TRANSITIVE, EAF_TYPE_INT, bgp_check_origin }, /* BA_ORIGIN */
+ { -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, bgp_check_path }, /* BA_AS_PATH */
+ { 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, bgp_check_next_hop }, /* BA_NEXT_HOP */
+ { 4, BAF_OPTIONAL, EAF_TYPE_INT, NULL }, /* BA_MULTI_EXIT_DISC */
+ { 4, BAF_OPTIONAL, EAF_TYPE_INT, NULL }, /* BA_LOCAL_PREF */
+ { 0, BAF_OPTIONAL, EAF_TYPE_OPAQUE, NULL }, /* BA_ATOMIC_AGGR */
+ { 6, BAF_OPTIONAL, EAF_TYPE_OPAQUE, NULL }, /* BA_AGGREGATOR */
+#if 0
+ /* FIXME: Handle community lists */
+ { 0, 0 }, /* BA_COMMUNITY */
+ { 0, 0 }, /* BA_ORIGINATOR_ID */
+ { 0, 0 }, /* BA_CLUSTER_LIST */
+#endif
+};
+
+static int bgp_mandatory_attrs[] = { BA_ORIGIN, BA_AS_PATH, BA_NEXT_HOP };
+
+struct rta *
+bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct linpool *pool)
+{
+ struct bgp_proto *bgp = conn->bgp;
+ rta *a = lp_alloc(pool, sizeof(struct rta));
+ unsigned int flags, code, l, errcode, i, type;
+ byte *z, *attr_start;
+ byte seen[256/8];
+ eattr *e;
+ ea_list *ea;
+ struct adata *ad;
+ neighbor *neigh;
+ ip_addr nexthop;
+
+ a->proto = &bgp->p;
+ a->source = RTS_BGP;
+ a->scope = SCOPE_UNIVERSE;
+ a->cast = RTC_UNICAST;
+ a->dest = RTD_ROUTER;
+ a->flags = 0;
+ a->aflags = 0;
+ a->from = bgp->cf->remote_ip;
+ a->eattrs = NULL;
+
+ /* Parse the attributes */
+ bzero(seen, sizeof(seen));
+ DBG("BGP: Parsing attributes\n");
+ while (len)
+ {
+ if (len < 2)
+ goto malformed;
+ attr_start = attr;
+ flags = *attr++;
+ code = *attr++;
+ len -= 2;
+ if (flags & BAF_EXT_LEN)
+ {
+ if (len < 2)
+ goto malformed;
+ l = get_u16(attr);
+ attr += 2;
+ len -= 2;
+ }
+ else
+ {
+ if (len < 1)
+ goto malformed;
+ l = *attr++;
+ len--;
+ }
+ if (l > len)
+ goto malformed;
+ len -= l;
+ z = attr;
+ attr += l;
+ DBG("Attr %02x %02x %d\n", code, flags, l);
+ if (seen[code/8] & (1 << (code%8)))
+ goto malformed;
+ seen[code/8] |= (1 << (code%8));
+ if (code && code < sizeof(bgp_attr_table)/sizeof(bgp_attr_table[0]))
+ {
+ struct attr_desc *desc = &bgp_attr_table[code];
+ if (desc->expected_length >= 0 && desc->expected_length != (int) l)
+ { errcode = 5; goto err; }
+ if ((desc->expected_flags ^ flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
+ { errcode = 4; goto err; }
+ if (desc->validate && (errcode = desc->validate(z, l)))
+ goto err;
+ type = desc->type;
+ }
+ else /* Unknown attribute */
+ { /* FIXME: Send partial bit when forwarding */
+ if (!(flags & BAF_OPTIONAL))
+ { errcode = 2; goto err; }
+ type = EAF_TYPE_OPAQUE;
+ }
+ ea = lp_alloc(pool, sizeof(struct ea_list) + sizeof(struct eattr));
+ ea->next = a->eattrs;
+ a->eattrs = ea;
+ ea->flags = 0;
+ ea->count = 1;
+ ea->attrs[0].id = EA_CODE(EAP_BGP, code);
+ ea->attrs[0].flags = flags;
+ ea->attrs[0].type = type;
+ if (type & EAF_EMBEDDED)
+ ad = NULL;
+ else
+ {
+ ad = lp_alloc(pool, sizeof(struct adata) + l);
+ ea->attrs[0].u.ptr = ad;
+ ad->length = l;
+ memcpy(ad->data, z, l);
+ }
+ switch (type)
+ {
+ case EAF_TYPE_ROUTER_ID:
+ case EAF_TYPE_INT:
+ ea->attrs[0].u.data = get_u32(z);
+ break;
+ case EAF_TYPE_IP_ADDRESS:
+ *(ip_addr *)ad->data = ipa_ntoh(*(ip_addr *)ad->data);
+ break;
+ }
+ }
+
+ /* Check if all mandatory attributes are present */
+ for(i=0; i < sizeof(bgp_mandatory_attrs)/sizeof(bgp_mandatory_attrs[0]); i++)
+ {
+ code = bgp_mandatory_attrs[i];
+ if (!(seen[code/8] & (1 << (code%8))))
+ {
+ bgp_error(conn, 3, 3, code, 1);
+ return NULL;
+ }
+ }
+
+ /* Fill in the remaining rta fields */
+ e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
+ ASSERT(e);
+ nexthop = *(ip_addr *) e->u.ptr->data;
+ neigh = neigh_find(&bgp->p, &nexthop, 0);
+ if (!neigh)
+ {
+ if (bgp->cf->multihop)
+ neigh = neigh_find(&bgp->p, &bgp->cf->multihop_via, 0);
+ else
+ neigh = neigh_find(&bgp->p, &bgp->cf->remote_ip, 0);
+ }
+ if (!neigh || !neigh->iface)
+ {
+ DBG("BGP: No route to peer!\n"); /* FIXME */
+ return NULL;
+ }
+ a->gw = neigh->addr;
+ a->iface = neigh->iface;
+ return rta_lookup(a);
+
+malformed:
+ bgp_error(conn, 3, 1, len, 0);
+ return NULL;
+
+err:
+ bgp_error(conn, 3, errcode, code, 0); /* FIXME: Return attribute data! */
+ return NULL;
+}