]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Implementation of 4B ASN support for BGP
authorOndrej Zajicek <santiago@crfreenet.org>
Sun, 26 Oct 2008 21:36:08 +0000 (22:36 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Sun, 26 Oct 2008 21:36:08 +0000 (22:36 +0100)
filter/config.Y
filter/filter.c
nest/a-path.c
nest/attrs.h
proto/bgp/attrs.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/bgp/packets.c

index d4bf44cc628bfb4024d045b1874af31b6f5b100d..fdfb2e74ba39177bbede094d2c2768e74ab4b3b5 100644 (file)
@@ -39,7 +39,6 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
 %type <v> set_atom fprefix fprefix_s fipa
 %type <s> decls declsn one_decl function_params 
 %type <h> bgp_path
-%type <i> bgp_one
 
 CF_GRAMMAR
 
@@ -273,14 +272,12 @@ switch_body: /* EMPTY */ { $$ = NULL; }
 
 /* CONST '(' expr ')' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_INT; $$->a2.i = $3; } */
 
-bgp_one:
-   NUM { $$ = $1; }
- | '?' { $$ = PM_ANY; }
- ;
 
 bgp_path: 
-   bgp_one          { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = NULL; $$->val  = $1; }
- | bgp_one bgp_path { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2;   $$->val  = $1; }
+   NUM          { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = NULL; $$->val  = $1; $$->any = 0; }
+ | '?'          { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = NULL; $$->val  = 0;  $$->any = 1; }
+ | NUM bgp_path { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2;   $$->val  = $1; $$->any = 0; }
+ | '?' bgp_path { $$ = cfg_alloc(sizeof(struct f_path_mask)); $$->next = $2;   $$->val  = 0;  $$->any = 1; }
  ;
 
 constant:
index 9cde3d96ad6219b8176c28faeb24b430b6cb7ebc..7893d9ae27e88ddff208d900afd28f22e18fb81c 100644 (file)
@@ -69,6 +69,30 @@ pm_path_compare(struct f_path_mask *m1, struct f_path_mask *m2)
   }
 }
 
+static void
+pm_format(struct f_path_mask *p, byte *buf, unsigned int size)
+{
+  byte *end = buf + size - 16;
+
+  while (p)
+    {
+      if (buf > end)
+       {
+         strcpy(buf, " ...");
+         return;
+       }
+
+      if (p->any)
+       buf += bsprintf(buf, "? ");
+      else
+       buf += bsprintf(buf, "%u ", p->val);
+
+      p = p->next;
+    }
+
+  *buf = 0;
+}
+
 /**
  * val_compare - compare two values
  * @v1: first value
@@ -224,7 +248,7 @@ val_print(struct f_val v)
   case T_ENUM: PRINTF( "(enum %x)%d", v.type, v.val.i ); break;
   case T_PATH: as_path_format(v.val.ad, buf2, 1020); PRINTF( "(path %s)", buf2 ); break;
   case T_CLIST: int_set_format(v.val.ad, buf2, 1020); PRINTF( "(clist %s)", buf2 ); break;
-  case T_PATH_MASK: debug( "(pathmask " ); { struct f_path_mask *p = v.val.path_mask; while (p) { debug("%d ", p->val); p=p->next; } debug(")" ); } break;
+  case T_PATH_MASK: pm_format(v.val.path_mask, buf2, 1020); PRINTF( "(pathmask %s)", buf2 ); break;
   default: PRINTF( "[unknown type %x]", v.type );
 #undef PRINTF
   }
index 1b08f8099a4d372ba760988831a06ebadbff0ebf..f4666911f73e58a0e186dee48b720c46fb8563f1 100644 (file)
 #include "lib/unaligned.h"
 #include "lib/string.h"
 
+
+/* Global AS4 support, shared by all BGP instances.
+ * This specifies whether BA_AS_PATH attributes contain 2 or 4 B per ASN
+ */
+
+int bgp_as4_support = 1;
+
+static void
+put_as(byte *data, u32 as)
+{
+  if (bgp_as4_support)
+    put_u32(data, as);
+  else if (as <= 0xFFFF)
+    put_u16(data, as);
+  else
+    bug("put_as: Try to put 32bit AS to 16bit AS Path");
+}
+
+static inline u32
+get_as(byte *data)
+{
+  return bgp_as4_support ? get_u32(data) : get_u16(data);
+}
+
 struct adata *
-as_path_prepend(struct linpool *pool, struct adata *olda, int as)
+as_path_prepend(struct linpool *pool, struct adata *olda, u32 as)
 {
+  int bs = bgp_as4_support ? 4 : 2;
   struct adata *newa;
 
-  if (olda->length && olda->data[0] == AS_PATH_SEQUENCE &&
-      olda->data[1] < 255) /* Starting with sequence => just prepend the AS number */
+  if (olda->length && olda->data[0] == AS_PATH_SEQUENCE && olda->data[1] < 255)
+    /* Starting with sequence => just prepend the AS number */
     {
-      newa = lp_alloc(pool, sizeof(struct adata) + olda->length + 2);
-      newa->length = olda->length + 2;
-      newa->data[0] = 2;
+      int nl = olda->length + bs;
+      newa = lp_alloc(pool, sizeof(struct adata) + nl);
+      newa->length = nl;
+      newa->data[0] = AS_PATH_SEQUENCE;
       newa->data[1] = olda->data[1] + 1;
-      memcpy(newa->data+4, olda->data+2, olda->length-2);
+      memcpy(newa->data + bs + 2, olda->data + 2, olda->length - 2);
     }
-  else                                 /* Create new path segment */
+  else /* Create new path segment */
     {
-      newa = lp_alloc(pool, sizeof(struct adata) + olda->length + 4);
-      newa->length = olda->length + 4;
-      newa->data[0] = 2;
+      int nl = olda->length + bs + 2;
+      newa = lp_alloc(pool, sizeof(struct adata) + nl);
+      newa->length = nl;
+      newa->data[0] = AS_PATH_SEQUENCE;
       newa->data[1] = 1;
-      memcpy(newa->data+4, olda->data, olda->length);
+      memcpy(newa->data + bs + 2, olda->data, olda->length);
     }
-  put_u16(newa->data+2, as);
+  put_as(newa->data + 2, as);
   return newa;
 }
 
+int
+as_path_convert_to_old(struct adata *path, byte *dst, int *new_used)
+{
+  byte *src = path->data;
+  byte *src_end = src + path->length;
+  byte *dst_start = dst;
+  u32 as;
+  int i, n;
+  *new_used = 0;
+
+  while (src < src_end)
+    {
+      n = src[1];
+      *dst++ = *src++;
+      *dst++ = *src++;
+
+      for(i=0; i<n; i++)
+       {
+         as = get_u32(src);
+         if (as > 0xFFFF) 
+           {
+             as = AS_TRANS;
+             *new_used = 1;
+           }
+         put_u16(dst, as);
+         src += 4;
+         dst += 2;
+       }
+    }
+
+  return dst - dst_start;
+}
+
+int
+as_path_convert_to_new(struct adata *path, byte *dst, int req_as)
+{
+  byte *src = path->data;
+  byte *src_end = src + path->length;
+  byte *dst_start = dst;
+  u32 as;
+  int i, t, n;
+
+
+  while ((src < src_end) && (req_as > 0))
+    {
+      t = *src++;
+      n = *src++;
+
+      if (t == AS_PATH_SEQUENCE)
+       {
+         if (n > req_as)
+           n = req_as;
+
+         req_as -= n;
+       }
+      else // t == AS_PATH_SET
+       req_as--;
+
+      *dst++ = t;
+      *dst++ = n;
+
+      for(i=0; i<n; i++)
+       {
+         as = get_u16(src);
+         put_u32(dst, as);
+         src += 2;
+         dst += 4;
+       }
+    }
+
+  return dst - dst_start;
+}
+
 void
 as_path_format(struct adata *path, byte *buf, unsigned int size)
 {
+  int bs = bgp_as4_support ? 4 : 2;
   byte *p = path->data;
   byte *e = p + path->length;
-  byte *end = buf + size - 8;
+  byte *end = buf + size - 16;
   int sp = 1;
   int l, isset;
 
@@ -69,8 +170,8 @@ as_path_format(struct adata *path, byte *buf, unsigned int size)
        {
          if (!sp)
            *buf++ = ' ';
-         buf += bsprintf(buf, "%d", get_u16(p));
-         p += 2;
+         buf += bsprintf(buf, "%u", get_as(p));
+         p += bs;
          sp = 0;
        }
       if (isset)
@@ -86,6 +187,7 @@ as_path_format(struct adata *path, byte *buf, unsigned int size)
 int
 as_path_getlen(struct adata *path)
 {
+  int bs = bgp_as4_support ? 4 : 2;
   int res = 0;
   u8 *p = path->data;
   u8 *q = p+path->length;
@@ -95,8 +197,8 @@ as_path_getlen(struct adata *path)
     {
       switch (*p++)
        {
-       case AS_PATH_SET:      len = *p++; res++;    p += 2*len; break;
-       case AS_PATH_SEQUENCE: len = *p++; res+=len; p += 2*len; break;
+       case AS_PATH_SET:      len = *p++; res++;      p += bs * len; break;
+       case AS_PATH_SEQUENCE: len = *p++; res += len; p += bs * len; break;
        default: bug("as_path_getlen: Invalid path segment");
        }
     }
@@ -104,9 +206,11 @@ as_path_getlen(struct adata *path)
 }
 
 int
-as_path_get_first(struct adata *path)
+as_path_get_first(struct adata *path, u32 *orig_as)
 {
-  int res = -1;
+  int bs = bgp_as4_support ? 4 : 2;
+  int found = 0;
+  u32 res = 0;
   u8 *p = path->data;
   u8 *q = p+path->length;
   int len;
@@ -117,36 +221,70 @@ as_path_get_first(struct adata *path)
        {
        case AS_PATH_SET:
          if (len = *p++)
-           res = get_u16(p);
-         p += 2*len;
+           {
+             found = 1;
+             res = get_as(p);
+             p += bs * len;
+           }
          break;
        case AS_PATH_SEQUENCE:
          if (len = *p++)
-           res = get_u16(p+2*(len-1));
-         p += 2*len;
+           {
+             found = 1;
+             res = get_as(p + bs * (len - 1));
+             p += bs * len;
+           }
          break;
        default: bug("as_path_get_first: Invalid path segment");
        }
     }
-  return res;
+
+  *orig_as = res;
+  return found;
 }
 
+int
+as_path_is_member(struct adata *path, u32 as)
+{
+  int bs = bgp_as4_support ? 4 : 2;
+  u8 *p = path->data;
+  u8 *q = p+path->length;
+  int i, n;
+
+  while (p<q)
+    {
+      n = p[1];
+      p += 2;
+      for(i=0; i<n; i++)
+       {
+         if (get_as(p) == as)
+           return 1;
+         p += bs;
+       }
+    }
+  return 0;
+}
+
+
+
 #define MASK_PLUS do { mask = mask->next; if (!mask) return next == q; \
-                      asterisk = (mask->val == PM_ANY); \
+                      asterisk = mask->any; \
                        if (asterisk) { mask = mask->next; if (!mask) { return 1; } } \
                       } while(0)
 
 int
 as_path_match(struct adata *path, struct f_path_mask *mask)
 {
+  int bs = bgp_as4_support ? 4 : 2;
   int i;
   int asterisk = 0;
   u8 *p = path->data;
   u8 *q = p+path->length;
   int len;
   u8 *next;
+  u32 as;
 
-  asterisk = (mask->val == PM_ANY);
+  asterisk = mask->any;
   if (asterisk)
     { mask = mask->next; if (!mask) return 1; }
 
@@ -156,20 +294,21 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
       len = *p++;
       {
        u8 *p_save = p;
-       next = p_save + 2*len;
+       next = p_save + bs * len;
       retry:
        p = p_save;
        for (i=0; i<len; i++) {
-         if (asterisk && (get_u16(p) == mask->val)) {
+         as = get_as(p);
+         if (asterisk && (as == mask->val)) {
            MASK_PLUS;
            goto retry;
          }
-         if (!asterisk && (get_u16(p) == mask->val)) {
+         if (!asterisk && (as == mask->val)) {
            p = next;
            MASK_PLUS;
            goto okay;
          }
-         p+=2;
+         p += bs;
        }
        if (!asterisk)
          return 0;
@@ -180,15 +319,15 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
     case AS_PATH_SEQUENCE:
       len = *p++;
       for (i=0; i<len; i++) {
-       next = p+2;
-       if (asterisk && (get_u16(p) == mask->val))
+       as = get_as(p);
+       if (asterisk && (as == mask->val))
          MASK_PLUS;
        else if (!asterisk) {
-         if (get_u16(p) != mask->val)
+         if (as != mask->val)
            return 0;
          MASK_PLUS;
        }
-       p+=2;
+       p += bs;
       }
       break;
 
index abd6b9e9d4d20572e3bdd92ada76899577d019d7..f63f2e452cb5976466e8a194dfda132ba7ebaf1a 100644 (file)
 #define AS_PATH_SET            1       /* Types of path segments */
 #define AS_PATH_SEQUENCE       2
 
-struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, int as);
+#define AS_PATH_MAXLEN         10000
+
+#define AS_TRANS               23456
+/* AS_TRANS is used when we need to store 32bit ASN larger than 0xFFFF
+ * to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details
+ */
+
+struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, u32 as);
+int as_path_convert_to_old(struct adata *path, byte *dst, int *new_used);
+int as_path_convert_to_new(struct adata *path, byte *dst, int req_as);
 void as_path_format(struct adata *path, byte *buf, unsigned int size);
 int as_path_getlen(struct adata *path);
-int as_path_get_first(struct adata *path);
+int as_path_get_first(struct adata *path, u32 *orig_as);
+int as_path_is_member(struct adata *path, u32 as);
+
 
 struct f_path_mask {
   struct f_path_mask *next;
-  int val;
+  u32 val;
+  int any;
 };
-#define PM_ANY -1
+
+// #define PM_ANY -1
 
 int as_path_match(struct adata *path, struct f_path_mask *mask);
 
index 30699f84dfe2dcde9f9ae9685c65cd35198b6e44..48cb9dd529f050230385a6ca0b6362fb5a230d79 100644 (file)
@@ -55,21 +55,37 @@ bgp_format_origin(eattr *a, byte *buf)
 }
 
 static int
-bgp_check_path(struct bgp_proto *p UNUSED, byte *a, int len)
+bgp_check_path(byte *a, int len, int bs, int errcode)
 {
   while (len)
     {
       DBG("Path segment %02x %02x\n", a[0], a[1]);
       if (len < 2 ||
-         a[0] != AS_PATH_SET && a[0] != AS_PATH_SEQUENCE ||
-         2*a[1] + 2 > len)
-       return 11;
-      len -= 2*a[1] + 2;
-      a += 2*a[1] + 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;
     }
   return 0;
 }
 
+static int
+bgp_check_as_path(struct bgp_proto *p, byte *a, int len)
+{
+  return bgp_check_path(a, len, (bgp_as4_support && p->as4_support) ? 4 : 2, 11);
+}
+
+static int
+bgp_check_as4_path(struct bgp_proto *p, byte *a, int len)
+{
+  if (bgp_as4_support && (! p->as4_support))
+    return bgp_check_path(a, len, 4, 9);
+  else 
+    return 0;
+}
+
+
 static int
 bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a, int len)
 {
@@ -87,6 +103,14 @@ bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a, int len)
 #endif
 }
 
+static int
+bgp_check_aggregator(struct bgp_proto *p UNUSED, UNUSED byte *a, int len)
+{
+  int exp_len = (bgp_as4_support && p->as4_support) ? 8 : 6;
+  
+  return (len == exp_len) ? 0 : 5;
+}
+
 static int
 bgp_check_reach_nlri(struct bgp_proto *p UNUSED, byte *a UNUSED, int len UNUSED)
 {
@@ -113,7 +137,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_path, NULL },
+    bgp_check_as_path, NULL },
   { "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, 0,                                   /* BA_MULTI_EXIT_DISC */
@@ -122,8 +146,8 @@ static struct attr_desc bgp_attr_table[] = {
     NULL, NULL },
   { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1,                      /* BA_ATOMIC_AGGR */
     NULL, NULL },
-  { "aggregator", 6, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1,                /* BA_AGGREGATOR */
-    NULL, NULL },
+  { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1,       /* BA_AGGREGATOR */
+    bgp_check_aggregator, NULL },
   { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET, 1,       /* BA_COMMUNITY */
     NULL, NULL },
   { NULL, },                                                                   /* BA_ORIGINATOR_ID */
@@ -135,8 +159,18 @@ static struct attr_desc bgp_attr_table[] = {
     bgp_check_reach_nlri, NULL },
   { "mp_unreach_nlri", -1, BAF_OPTIONAL, EAF_TYPE_OPAQUE, 1,                   /* BA_MP_UNREACH_NLRI */
     bgp_check_unreach_nlri, NULL },
+  { NULL, },                                                                   /* BA_EXTENDED_COMM */
+  { "as4_path", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1,         /* BA_AS4_PATH */
+    bgp_check_as4_path, NULL },
+  { "as4_aggregator", 8, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1,    /* BA_AS4_PATH */
+    NULL, NULL }
 };
 
+/* BA_AS4_PATH is type EAF_TYPE_OPAQUE and not type EAF_TYPE_AS_PATH because
+ * EAF_TYPE_AS_PATH is supposed to have different format (2 or 4 B for each ASN)
+ * depending on bgp_as4_support variable.
+ */
+
 #define ATTR_KNOWN(code) ((code) < ARRAY_SIZE(bgp_attr_table) && bgp_attr_table[code].name)
 
 static byte *
@@ -170,8 +204,90 @@ bgp_attach_attr(ea_list **to, struct linpool *pool, unsigned attr, unsigned val)
   return bgp_set_attr(a->attrs, pool, attr, val);
 }
 
+static int
+bgp_encode_attr_hdr(byte *dst, unsigned int flags, unsigned code, int len)
+{
+  int wlen;
+
+  DBG("\tAttribute %02x (%d bytes, flags %02x)\n", code, len, flags);
+
+  if (len < 256)
+    {
+      *dst++ = flags;
+      *dst++ = code;
+      *dst++ = len;
+      wlen = 3;
+    }
+  else
+    {
+      *dst++ = flags | BAF_EXT_LEN;
+      *dst++ = code;
+      put_u16(dst, len);
+      wlen = 4;
+    }
+
+  return wlen;
+}
+
+static void
+aggregator_convert_to_old(struct adata *aggr, byte *dst, int *new_used)
+{
+  byte *src = aggr->data;
+  *new_used = 0;
+
+  u32 as = get_u32(src);
+  if (as > 0xFFFF) 
+    {
+      as = AS_TRANS;
+      *new_used = 1;
+    }
+  put_u16(dst, as);
+
+  /* Copy IPv4 address */
+  memcpy(dst + 2, src + 4, 4);
+}
+
+static void
+aggregator_convert_to_new(struct adata *aggr, byte *dst)
+{
+  byte *src = aggr->data;
+
+  u32 as   = get_u16(src);
+  put_u32(dst, as);
+
+  /* Copy IPv4 address */
+  memcpy(dst + 4, src + 2, 4);
+}
+
+static int
+bgp_get_attr_len(eattr *a)
+{
+  int len;
+  if (ATTR_KNOWN(EA_ID(a->id)))
+    {
+      int code = EA_ID(a->id);
+      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;
+    }
+  
+  return len;
+}
+
+#define ADVANCE(w, r, l) do { r -= l; w += l; } while (0)
+
 /**
  * bgp_encode_attrs - encode BGP attributes
+ * @p: BGP instance
  * @w: buffer
  * @attrs: a list of extended attributes
  * @remains: remaining space in the buffer
@@ -182,11 +298,11 @@ bgp_attach_attr(ea_list **to, struct linpool *pool, unsigned attr, unsigned val)
  * Result: Length of the attribute block generated.
  */
 unsigned int
-bgp_encode_attrs(byte *w, ea_list *attrs, int remains)
+bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
 {
   unsigned int i, code, flags;
   byte *start = w;
-  int len;
+  int len, rv;
 
   for(i=0; i<attrs->count; i++)
     {
@@ -198,43 +314,90 @@ bgp_encode_attrs(byte *w, ea_list *attrs, int remains)
       if (code == BA_NEXT_HOP)
        continue;
 #endif
-      flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL);
-      if (ATTR_KNOWN(code))
-       {
-         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
+
+      /* When AS4-aware BGP speaker is talking to non-AS4-aware BGP speaker,
+       * we have to convert our 4B AS_PATH to 2B AS_PATH and send our AS_PATH 
+       * as optional AS4_PATH attribute.
+       */
+      if ((code == BA_AS_PATH) && bgp_as4_support && (! p->as4_support))
        {
-         ASSERT((a->type & EAF_TYPE_MASK) == EAF_TYPE_OPAQUE);
          len = a->u.ptr->length;
+
+         if (remains < (len + 4))
+           goto err_no_buffer;
+
+         /* Using temporary buffer because don't know a length of created attr
+          * and therefore a length of a header. Perhaps i should better always
+          * use BAF_EXT_LEN. */
+         
+         byte buf[len];
+         int new_used;
+         int nl = as_path_convert_to_old(a->u.ptr, buf, &new_used);
+
+         rv = bgp_encode_attr_hdr(w, BAF_TRANSITIVE, BA_AS_PATH, nl);
+         ADVANCE(w, remains, rv);
+         memcpy(w, buf, nl);
+         ADVANCE(w, remains, nl);
+
+         if (! new_used)
+           continue;
+
+         if (remains < (len + 4))
+           goto err_no_buffer;
+
+         /* We should discard AS_CONFED_SEQUENCE or AS_CONFED_SET path segments 
+          * here but we don't support confederations and such paths we already
+          * discarded in bgp_check_as_path().
+          */
+
+         rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AS4_PATH, len);
+         ADVANCE(w, remains, rv);
+         memcpy(w, a->u.ptr->data, len);
+         ADVANCE(w, remains, len);
+
+         continue;
        }
-      DBG("\tAttribute %02x (type %02x, %d bytes, flags %02x)\n", code, a->type, len, flags);
-      if (remains < len + 4)
-       {
-         log(L_ERR "BGP: attribute list too long, ignoring the remaining attributes");
-         break;
-       }
-      if (len < 256)
-       {
-         *w++ = flags;
-         *w++ = code;
-         *w++ = len;
-         remains -= 3;
-       }
-      else
+
+      /* The same issue with AGGREGATOR attribute */
+      if ((code == BA_AGGREGATOR) && bgp_as4_support && (! p->as4_support))
        {
-         *w++ = flags | BAF_EXT_LEN;
-         *w++ = code;
-         put_u16(w, len);
-         w += 2;
-         remains -= 4;
+         int new_used;
+
+         len = 6;
+         if (remains < (len + 3))
+           goto err_no_buffer;
+
+         rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AGGREGATOR, len);
+         ADVANCE(w, remains, rv);
+         aggregator_convert_to_old(a->u.ptr, w, &new_used);
+         ADVANCE(w, remains, len);
+
+         if (! new_used)
+           continue;
+
+         len = 8;
+         if (remains < (len + 3))
+           goto err_no_buffer;
+
+         rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AS4_AGGREGATOR, len);
+         ADVANCE(w, remains, rv);
+         memcpy(w, a->u.ptr->data, len);
+         ADVANCE(w, remains, len);
+
+         continue;
        }
+
+      /* Standard path continues here ... */
+
+      flags = a->flags & (BAF_OPTIONAL | BAF_TRANSITIVE | BAF_PARTIAL);
+      len = bgp_get_attr_len(a);
+
+      if (remains < len + 4)
+       goto err_no_buffer;
+
+      rv = bgp_encode_attr_hdr(w, flags, code, len);
+      ADVANCE(w, remains, rv);
+
       switch (a->type & EAF_TYPE_MASK)
        {
        case EAF_TYPE_INT:
@@ -266,10 +429,13 @@ bgp_encode_attrs(byte *w, ea_list *attrs, int remains)
        default:
          bug("bgp_encode_attrs: unknown attribute type %02x", a->type);
        }
-      remains -= len;
-      w += len;
+      ADVANCE(w, remains, len);
     }
   return w - start;
+
+ err_no_buffer:
+  log(L_ERR "BGP: attribute list too long, ignoring the remaining attributes");
+  return w - start;
 }
 
 static void
@@ -566,10 +732,14 @@ bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p
     bgp_set_attr(ea->attrs+1, pool, BA_AS_PATH, 0);
   else
     {
-      z = bgp_set_attr(ea->attrs+1, pool, BA_AS_PATH, 4);
+      z = bgp_set_attr(ea->attrs+1, pool, BA_AS_PATH, bgp_as4_support ? 6 : 4);
       z[0] = AS_PATH_SEQUENCE;
       z[1] = 1;                                /* 1 AS */
-      put_u16(z+2, p->local_as);
+
+      if (bgp_as4_support)
+       put_u32(z+2, p->local_as);
+      else
+       put_u16(z+2, p->local_as);
     }
 
   z = bgp_set_attr(ea->attrs+2, pool, BA_NEXT_HOP, sizeof(ip_addr));
@@ -670,8 +840,8 @@ bgp_rte_better(rte *new, rte *old)
     {
       x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
       y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
-      n = x ? as_path_getlen(x->u.ptr) : 100000;
-      o = y ? as_path_getlen(y->u.ptr) : 100000;
+      n = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
+      o = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
       if (n < o)
        return 1;
       if (n > o)
@@ -712,23 +882,118 @@ bgp_rte_better(rte *new, rte *old)
 static int
 bgp_path_loopy(struct bgp_proto *p, eattr *a)
 {
-  byte *path = a->u.ptr->data;
-  int len = a->u.ptr->length;
-  int i, n;
+  return as_path_is_member(a->u.ptr, p->local_as);
+}
+
+
+static struct adata *
+bgp_aggregator_convert_to_new(struct adata *old, struct linpool *pool)
+{
+  struct adata *newa = lp_alloc(pool, sizeof(struct adata) + 8);
+  newa->length = 8;
+  aggregator_convert_to_new(old, newa->data);
+  return newa;
+}
+
+
+/* Take last req_as ASNs from path old2 (in 2B format), convert to 4B format
+ * and append path old4 (in 4B format).
+ */
+static struct adata *
+bgp_merge_as_paths(struct adata *old2, struct adata *old4, int req_as, struct linpool *pool)
+{
+  byte buf[old2->length * 2];
+
+  int ol = as_path_convert_to_new(old2, buf, req_as);
+  int nl = ol + (old4 ? old4->length : 0);
 
-  while (len > 0)
+  struct adata *newa = lp_alloc(pool, sizeof(struct adata) + nl);
+  newa->length = nl;
+  memcpy(newa->data, buf, ol);
+  if (old4) memcpy(newa->data + ol, old4->data, old4->length);
+
+  return newa;
+}
+
+
+/* Reconstruct 4B AS_PATH and AGGREGATOR according to RFC4893 4.2.3 */
+static void
+bgp_reconstruct_4b_atts(struct bgp_proto *p, rta *a, struct linpool *pool)
+{
+  eattr *p2 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
+  eattr *p4 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS4_PATH));
+  eattr *a2 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AGGREGATOR));
+  eattr *a4 =ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS4_AGGREGATOR));
+
+  if (a2)
     {
-      n = path[1];
-      len -= 2 + 2*n;
-      path += 2;
-      for(i=0; i<n; i++)
+      u32 a2_as = get_u16(a2->u.ptr->data);
+
+      if (a4)
        {
-         if (get_u16(path) == p->local_as)
-           return 1;
-         path += 2;
+         if (a2_as != AS_TRANS)
+           {
+             /* Routes were aggregated by old router and therefore AS4_PATH
+              * and AS4_AGGREGATOR is invalid
+              *
+              * Convert AS_PATH and AGGREGATOR to 4B format and finish.
+              */
+
+             a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool);
+             p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool);
+
+             return;
+           }
+         else
+           {
+             /* Common case, use AS4_AGGREGATOR attribute */
+             a2->u.ptr = a4->u.ptr;
+           }
+       }
+      else
+       {
+         /* Common case, use old AGGREGATOR attribute */
+         a2->u.ptr = bgp_aggregator_convert_to_new(a2->u.ptr, pool);
+
+         if (a2_as == AS_TRANS)
+           log(L_WARN "BGP: AGGREGATOR attribute contain AS_TRANS, but AS4_AGGREGATOR is missing");
        }
     }
-  return 0;
+  else
+    if (a4)
+      log(L_WARN "BGP: AS4_AGGREGATOR attribute received, but AGGREGATOR attribute is missing");
+
+  int p2_len = as_path_getlen(p2->u.ptr);
+  int p4_len = p4 ? as_path_getlen(p4->u.ptr) : AS_PATH_MAXLEN;
+
+  if (p2_len < p4_len)
+    p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, NULL, AS_PATH_MAXLEN, pool);
+  else
+    p2->u.ptr = bgp_merge_as_paths(p2->u.ptr, p4->u.ptr, p2_len - p4_len, pool);
+
+}
+
+static void
+bgp_remove_as4_attrs(struct bgp_proto *p, rta *a)
+{
+  unsigned id1 = EA_CODE(EAP_BGP, BA_AS4_PATH);
+  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 */
+  while (*el != NULL)
+    {
+      unsigned fid = (*el)->attrs[0].id;
+
+      if ((fid == id1) || (fid == id2))
+       {
+         *el = (*el)->next;
+         if (p->as4_support)
+           log(L_WARN "BGP: Unexpected AS4_* attributes received");
+       }
+      else
+       el = &((*el)->next);
+    }
 }
 
 /**
@@ -883,6 +1148,15 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
            }
        }
     }
+  
+  /* When receiving attributes from non-AS4-aware BGP speaker,
+   * we have to reconstruct 4B AS_PATH and AGGREGATOR attributes
+   */
+  if (bgp_as4_support && (! bgp->as4_support))
+    bgp_reconstruct_4b_atts(bgp, a, pool);
+
+  if (bgp_as4_support)
+    bgp_remove_as4_attrs(bgp, a);
 
   /* If the AS path attribute contains our AS, reject the routes */
   e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
@@ -945,11 +1219,11 @@ bgp_get_route_info(rte *e, byte *buf, ea_list *attrs)
 {
   eattr *p = ea_find(attrs, EA_CODE(EAP_BGP, BA_AS_PATH));
   eattr *o = ea_find(attrs, EA_CODE(EAP_BGP, BA_ORIGIN));
-  int origas;
+  u32 origas;
 
   buf += bsprintf(buf, " (%d) [", e->pref);
-  if (p && (origas = as_path_get_first(p->u.ptr)) >= 0)
-    buf += bsprintf(buf, "AS%d", origas);
+  if (p && as_path_get_first(p->u.ptr, &origas))
+    buf += bsprintf(buf, "AS%u", origas);
   if (o)
     buf += bsprintf(buf, "%c", "ie?"[o->u.data]);
   strcpy(buf, "]");
index cedd223b75b7e3fb8125ac26ce482fdd097c90b4..ed2524c8edbfbdeb4337ab08ccf658366f77382a 100644 (file)
@@ -76,6 +76,7 @@ static void bgp_connect(struct bgp_proto *p);
 static void bgp_initiate(struct bgp_proto *p);
 static void bgp_setup_listen_sk(void);
 
+
 static void
 bgp_close(struct bgp_proto *p UNUSED)
 {
@@ -611,6 +612,10 @@ bgp_check(struct bgp_config *c)
     cf_error("Local AS number must be set");
   if (!c->remote_as)
     cf_error("Neighbor must be configured");
+  if (!bgp_as4_support && (c->local_as > 0xFFFF))
+    cf_error("Local AS number out of range");
+  if (!bgp_as4_support && (c->remote_as > 0xFFFF))
+    cf_error("Neighbor AS number out of range");
 }
 
 static void
index 6519db856177ce98a849c057f86ad731187265aa..aa1bd10795ed67ef98008863f232832e9a2ae317 100644 (file)
@@ -16,7 +16,7 @@ struct eattr;
 
 struct bgp_config {
   struct proto_config c;
-  unsigned int local_as, remote_as;
+  u32 local_as, remote_as;
   ip_addr remote_ip;
   int multihop;                                /* Number of hops if multihop */
   ip_addr multihop_via;                        /* Multihop: address to route to */
@@ -47,14 +47,16 @@ struct bgp_conn {
   byte *notify_data;
   int error_flag;                      /* Error state, ignore all input */
   int primary;                         /* This connection is primary */
+  u32 advertised_as;                   /* Temporary value for AS number received */
   unsigned hold_time, keepalive_time;  /* Times calculated from my and neighbor's requirements */
 };
 
 struct bgp_proto {
   struct proto p;
   struct bgp_config *cf;               /* Shortcut to BGP configuration */
-  unsigned local_as, remote_as;
+  u32 local_as, remote_as;
   int is_internal;                     /* Internal BGP connection (local_as == remote_as) */
+  int as4_support;                     /* Peer supports 4B AS numbers [RFC4893] */
   u32 local_id;                                /* BGP identifier of this router */
   u32 remote_id;                       /* BGP identifier of the neighbor */
   struct bgp_conn *conn;               /* Connection we have established */
@@ -100,6 +102,9 @@ struct bgp_bucket {
 
 extern struct linpool *bgp_linpool;
 
+extern int bgp_as4_support;
+
+
 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);
@@ -122,7 +127,7 @@ 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 *);
-unsigned int bgp_encode_attrs(byte *w, struct ea_list *attrs, int remains);
+unsigned int bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains);
 void bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck);
 void bgp_get_route_info(struct rte *, byte *buf, struct ea_list *attrs);
 
@@ -165,6 +170,8 @@ void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subco
 #define BA_MP_REACH_NLRI       0x0e    /* [RFC2283] */
 #define BA_MP_UNREACH_NLRI     0x0f
 #define BA_EXTENDED_COMM       0x10    /* draft-ramachandra-bgp-ext-communities */
+#define BA_AS4_PATH             0x11    /* [RFC4893] */
+#define BA_AS4_AGGREGATOR       0x12
 
 /* BGP states */
 
index 52ad731eda1ee0424616f516c41cf6fc4f3e575e..63dfb61525d0dfa4115870293941ee8cc6bea62d 100644 (file)
@@ -44,12 +44,10 @@ bgp_proto_start: proto_start BGP {
 bgp_proto:
    bgp_proto_start proto_name '{'
  | bgp_proto proto_item ';'
- | bgp_proto LOCAL AS expr ';' {
-     if ($4 < 0 || $4 > 65535) cf_error("AS number out of range");
-     BGP_CFG->local_as = $4;
-   }
+ | bgp_proto LOCAL AS expr ';' { BGP_CFG->local_as = $4; }
  | bgp_proto NEIGHBOR ipa AS expr ';' {
-     if ($5 < 0 || $5 > 65535) cf_error("AS number out of range");
+     if (ipa_nonzero(BGP_CFG->remote_ip)) cf_error("Only one neighbor per BGP instance is allowed");
+
      BGP_CFG->remote_ip = $3;
      BGP_CFG->remote_as = $5;
    }
index 2e6f0b6062f2b00869e666d4771f57cd4f53c0b6..0dd920e4d2322a85cd50dd74f7152acdb9703acf 100644 (file)
@@ -12,6 +12,7 @@
 #include "nest/iface.h"
 #include "nest/protocol.h"
 #include "nest/route.h"
+#include "nest/attrs.h"
 #include "conf/conf.h"
 #include "lib/unaligned.h"
 #include "lib/socket.h"
@@ -30,33 +31,64 @@ bgp_create_notification(struct bgp_conn *conn, byte *buf)
   return buf + 2 + conn->notify_size;
 }
 
+#ifdef IPV6
+static byte *
+bgp_put_cap_ipv6(struct bgp_conn *conn UNUSED, byte *buf)
+{
+  *buf++ = 1;          /* Capability 1: Multiprotocol extensions */
+  *buf++ = 4;          /* Capability data length */
+  *buf++ = 0;          /* We support AF IPv6 */
+  *buf++ = BGP_AF_IPV6;
+  *buf++ = 0;          /* RFU */
+  *buf++ = 1;          /* and SAFI 1 */
+  return buf;
+}
+#endif
+
+static byte *
+bgp_put_cap_as4(struct bgp_conn *conn, byte *buf)
+{
+  *buf++ = 65;         /* Capability 65: Support for 4-octet AS number */
+  *buf++ = 4;          /* Capability data length */
+  put_u32(buf, conn->bgp->local_as);
+  return buf + 4;
+}
+
 static byte *
 bgp_create_open(struct bgp_conn *conn, byte *buf)
 {
   struct bgp_proto *p = conn->bgp;
+  byte *cap;
+  int cap_len;
 
   BGP_TRACE(D_PACKETS, "Sending OPEN(ver=%d,as=%d,hold=%d,id=%08x)",
            BGP_VERSION, p->local_as, p->cf->hold_time, p->local_id);
   buf[0] = BGP_VERSION;
-  put_u16(buf+1, p->local_as);
+  put_u16(buf+1, (p->local_as < 0xFFFF) ? p->local_as : AS_TRANS);
   put_u16(buf+3, p->cf->hold_time);
   put_u32(buf+5, p->local_id);
-#ifndef IPV6
-  buf[9] = 0;                          /* No optional parameters */
-  return buf+10;
-#else
-  buf += 9;
-  *buf++ = 8;          /* Optional params len */
-  *buf++ = 2;          /* Option: Capability list */
-  *buf++ = 6;          /* Option length */
-  *buf++ = 1;          /* Capability 1: Multiprotocol extensions */
-  *buf++ = 4;          /* Capability data length */
-  *buf++ = 0;          /* We support AF IPv6 */
-  *buf++ = BGP_AF_IPV6;
-  *buf++ = 0;          /* RFU */
-  *buf++ = 1;          /* and SAFI 1 */
-  return buf;
+  /* Skipped 3 B for length field and Capabilities parameter header */
+  cap = buf + 12;
+
+#ifdef IPV6
+  cap = bgp_put_cap_ipv6(conn, cap);
 #endif
+  if (bgp_as4_support)
+    cap = bgp_put_cap_as4(conn, cap);
+
+  cap_len = cap - buf - 12;
+  if (cap_len > 0)
+    {
+      buf[9]  = cap_len + 2;   /* Optional params len */
+      buf[10] = 2;             /* Option: Capability list */
+      buf[11] = cap_len;       /* Option length */
+      return cap;
+    }
+  else
+    {
+      buf[9] = 0;              /* No optional parameters */
+      return buf + 10;
+    }
 }
 
 static unsigned int
@@ -118,7 +150,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
              continue;
            }
          DBG("Processing bucket %p\n", buck);
-         a_size = bgp_encode_attrs(w+2, buck->eattrs, 1024);
+         a_size = bgp_encode_attrs(p, w+2, buck->eattrs, 1024);
          put_u16(w, a_size);
          w += a_size + 2;
          r_size = bgp_encode_prefixes(p, w, buck, remains - a_size);
@@ -166,7 +198,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
       *tmp++ = BGP_AF_IPV6;
       *tmp++ = 1;
       ea->attrs[0].u.ptr->length = bgp_encode_prefixes(p, tmp, buck, remains-11);
-      size = bgp_encode_attrs(w, ea, remains);
+      size = bgp_encode_attrs(p, w, ea, remains);
       w += size;
       remains -= size;
     }
@@ -183,7 +215,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
              continue;
            }
          DBG("Processing bucket %p\n", buck);
-         size = bgp_encode_attrs(w, buck->eattrs, 1024);
+         size = bgp_encode_attrs(p, w, buck->eattrs, 1024);
          w += size;
          remains -= size;
          tstart = tmp = bgp_attach_attr(&ea, bgp_linpool, BA_MP_REACH_NLRI, remains-8);
@@ -230,7 +262,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
          *tmp++ = 0;                   /* No SNPA information */
          tmp += bgp_encode_prefixes(p, tmp, buck, remains - (8+3+32+1));
          ea->attrs[0].u.ptr->length = tmp - tstart;
-         w += bgp_encode_attrs(w, ea, remains);
+         w += bgp_encode_attrs(p, w, ea, remains);
          break;
        }
     }
@@ -353,9 +385,49 @@ bgp_tx(sock *sk)
     ;
 }
 
+/* Capatibility negotiation as per RFC 2842 */
+
+void
+bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
+{
+  struct bgp_proto *p = conn->bgp;
+  int cl;
+  u32 as;
+
+  while (len > 0)
+    {
+      if (len < 2 || len < 2 + opt[1])
+       goto err;
+      
+      cl = opt[1];
+
+      switch (opt[0])
+       {
+       case 65:
+         if (cl != 4)
+           goto err;
+         p->as4_support = 1;
+         if (bgp_as4_support)
+           conn->advertised_as = get_u32(opt + 2);
+         break;
+
+         /* We can safely ignore all other capabilities */
+       }
+      len -= 2 + cl;
+      opt += 2 + cl;
+    }
+  return;
+
+    err:
+  bgp_error(conn, 2, 0, NULL, 0);
+  return;
+}
+
 static int
 bgp_parse_options(struct bgp_conn *conn, byte *opt, int len)
 {
+  int ol;
+
   while (len > 0)
     {
       if (len < 2 || len < 2 + opt[1])
@@ -369,12 +441,14 @@ bgp_parse_options(struct bgp_conn *conn, byte *opt, int len)
        DBG("\n");
       }
 #endif
+
+      ol = opt[1];
       switch (opt[0])
        {
        case 2:
-         /* Capatibility negotiation as per RFC 2842 */
-         /* We can safely ignore all capabilities announced */
+         bgp_parse_capabilities(conn, opt + 2, ol);
          break;
+
        default:
          /*
           *  BGP specs don't tell us to send which option
@@ -382,11 +456,11 @@ bgp_parse_options(struct bgp_conn *conn, byte *opt, int len)
           *  to do so. Also, capability negotiation with
           *  Cisco routers doesn't work without that.
           */
-         bgp_error(conn, 2, 4, opt, opt[1]);
+         bgp_error(conn, 2, 4, opt, ol);
          return 0;
        }
-      len -= 2 + opt[1];
-      opt += 2 + opt[1];
+      len -= 2 + ol;
+      opt += 2 + ol;
     }
   return 0;
 }
@@ -397,7 +471,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
   struct bgp_conn *other;
   struct bgp_proto *p = conn->bgp;
   struct bgp_config *cf = p->cf;
-  unsigned as, hold;
+  unsigned hold;
   u32 id;
 
   /* Check state */
@@ -409,20 +483,27 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
     { bgp_error(conn, 1, 2, pkt+16, 2); return; }
   if (pkt[19] != BGP_VERSION)
     { bgp_error(conn, 2, 1, pkt+19, 1); return; } /* RFC 1771 says 16 bits, draft-09 tells to use 8 */
-  as = get_u16(pkt+20);
+  conn->advertised_as = get_u16(pkt+20);
   hold = get_u16(pkt+22);
   id = get_u32(pkt+24);
-  BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%08x)", as, hold, id);
-  if (cf->remote_as && as != p->remote_as)
-    { bgp_error(conn, 2, 2, pkt+20, -2); return; }
-  if (hold > 0 && hold < 3)
-    { bgp_error(conn, 2, 6, pkt+22, 2); return; }
-  p->remote_id = id;
+  BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%08x)", conn->advertised_as, hold, id);
+
+  p->remote_id = id; // ???
   if (bgp_parse_options(conn, pkt+29, pkt[28]))
     return;
+
+  if (hold > 0 && hold < 3)
+    { bgp_error(conn, 2, 6, pkt+22, 2); return; }
+
   if (!id || id == 0xffffffff || id == p->local_id)
     { bgp_error(conn, 2, 3, pkt+24, -4); return; }
 
+
+  if (conn->advertised_as != p->remote_as)
+    {
+      bgp_error(conn, 2, 2, (byte *) &(conn->advertised_as), -4); return;
+    }
+
   /* Check the other connection */
   other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
   switch (other->state)
@@ -463,7 +544,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
   else
     conn->hold_time = p->cf->hold_time;
   conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
-  p->remote_as = as;
+  // p->remote_as = conn->advertised_as;
   p->remote_id = id;
   DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x\n", conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id);
 
@@ -720,7 +801,7 @@ static struct {
   { 2, 4, "Unsupported optional parameter" },
   { 2, 5, "Authentication failure" },
   { 2, 6, "Unacceptable hold time" },
-  { 2, 7, "Required capability missing" }, /* capability negotiation draft */
+  { 2, 7, "Required capability missing" }, /* [RFC3392] */
   { 3, 0, "Invalid UPDATE message" },
   { 3, 1, "Malformed attribute list" },
   { 3, 2, "Unrecognized well-known attribute" },