]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: efficient NLRI packing for AFs != ipv4-unicast
authorPradosh Mohapatra <pmohapat@cumulusnetworks.com>
Wed, 15 Jan 2014 06:57:57 +0000 (06:57 +0000)
committerDavid Lamparter <equinox@opensourcerouting.org>
Tue, 3 Jun 2014 13:08:32 +0000 (15:08 +0200)
ISSUE:

  Currently, for non-ipv4-unicast address families where prefixes are
  encoded in MP_REACH/MP_UNREACH attributes, BGP ends up sending one
  prefix per UPDATE message. This is quite inefficient. The patch
  addresses the issue.

PATCH:

  We introduce a scratch buffer in the peer structure that stores the
  MP_REACH/MP_UNREACH attributes for non-ipv4-unicast families. This
  enables us to encode multiple prefixes. In the end, the two buffers
  are merged to create the UPDATE packet.

Signed-off-by: Pradosh Mohapatra <pmohapat@cumulusnetworks.com>
Reviewed-by: Daniel Walton <dwalton@cumulusnetworks.com>
[DL: removed no longer existing bgp_packet_withdraw prototype]
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_packet.c
bgpd/bgpd.c
bgpd/bgpd.h
lib/stream.c
lib/stream.h

index a0dfc65dfa651565f413e6c2784fec6304fbee1a..f284758ef531bd6f3203df7cfdf940d4e3e81c78 100644 (file)
@@ -2058,12 +2058,106 @@ bgp_attr_check (struct peer *peer, struct attr *attr)
 \f
 int stream_put_prefix (struct stream *, struct prefix *);
 
+size_t
+bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi,
+                        struct attr *attr)
+{
+  size_t sizep;
+
+  /* Set extended bit always to encode the attribute length as 2 bytes */
+  stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+  stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
+  sizep = stream_get_endp (s);
+  stream_putw (s, 0);  /* Marker: Attribute length. */
+  stream_putw (s, afi);        /* AFI */
+  stream_putc (s, safi);       /* SAFI */
+
+  /* Nexthop */
+  switch (afi)
+    {
+    case AFI_IP:
+      switch (safi)
+       {
+       case SAFI_UNICAST:
+       case SAFI_MULTICAST:
+         stream_putc (s, 4);
+         stream_put_ipv4 (s, attr->nexthop.s_addr);
+         break;
+       case SAFI_MPLS_VPN:
+         stream_putc (s, 12);
+         stream_putl (s, 0);
+         stream_putl (s, 0);
+         stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
+         break;
+       default:
+         break;
+       }
+      break;
+#ifdef HAVE_IPV6
+    case AFI_IP6:
+      switch (safi)
+      {
+      case SAFI_UNICAST:
+      case SAFI_MULTICAST:
+       {
+         unsigned long sizep;
+         struct attr_extra *attre = attr->extra;
+
+         assert (attr->extra);
+         stream_putc (s, attre->mp_nexthop_len);
+         stream_put (s, &attre->mp_nexthop_global, 16);
+         if (attre->mp_nexthop_len == 32)
+           stream_put (s, &attre->mp_nexthop_local, 16);
+       }
+      default:
+       break;
+      }
+      break;
+#endif /*HAVE_IPV6*/
+    default:
+      break;
+    }
+
+  /* SNPA */
+  stream_putc (s, 0);
+  return sizep;
+}
+
+void
+bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi,
+                         struct prefix *p, struct prefix_rd *prd,
+                         u_char *tag)
+{
+  switch (safi)
+    {
+    case SAFI_MPLS_VPN:
+      /* Tag, RD, Prefix write. */
+      stream_putc (s, p->prefixlen + 88);
+      stream_put (s, tag, 3);
+      stream_put (s, prd->val, 8);
+      stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
+      break;
+    default:
+      /* Prefix write. */
+      stream_put_prefix (s, p);
+      break;
+    }
+}
+
+void
+bgp_packet_mpattr_end (struct stream *s, size_t sizep)
+{
+  /* Set MP attribute length. Don't count the (2) bytes used to encode
+     the attr length */
+  stream_putw_at (s, sizep, (stream_get_endp (s) - sizep) - 2);
+}
+
 /* Make attribute packet. */
 bgp_size_t
 bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
-                     struct stream *s, struct attr *attr, struct prefix *p,
-                     afi_t afi, safi_t safi, struct peer *from,
-                     struct prefix_rd *prd, u_char *tag)
+                     struct stream *s, struct attr *attr,
+                     struct prefix *p, afi_t afi, safi_t safi,
+                     struct peer *from, struct prefix_rd *prd, u_char *tag)
 {
   size_t cp;
   size_t aspath_sizep;
@@ -2071,6 +2165,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
   int send_as4_path = 0;
   int send_as4_aggregator = 0;
   int use32bit = (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0;
+  size_t mpattrlen_pos = 0;
 
   if (! bgp)
     bgp = bgp_get_default ();
@@ -2078,6 +2173,13 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
   /* Remember current pointer. */
   cp = stream_get_endp (s);
 
+  if (p && !(afi == AFI_IP && safi == SAFI_UNICAST))
+    {
+      mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi, attr);
+      bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag);
+      bgp_packet_mpattr_end(s, mpattrlen_pos);
+    }
+
   /* Origin attribute. */
   stream_putc (s, BGP_ATTR_FLAG_TRANS);
   stream_putc (s, BGP_ATTR_ORIGIN);
@@ -2286,96 +2388,6 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
        }
     }
 
-#ifdef HAVE_IPV6
-  /* If p is IPv6 address put it into attribute. */
-  if (p->family == AF_INET6)
-    {
-      unsigned long sizep;
-      struct attr_extra *attre = attr->extra;
-      
-      assert (attr->extra);
-      
-      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
-      stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
-      sizep = stream_get_endp (s);
-      stream_putc (s, 0);      /* Marker: Attribute length. */
-      stream_putw (s, AFI_IP6);        /* AFI */
-      stream_putc (s, safi);   /* SAFI */
-
-      stream_putc (s, attre->mp_nexthop_len);
-
-      if (attre->mp_nexthop_len == 16)
-       stream_put (s, &attre->mp_nexthop_global, 16);
-      else if (attre->mp_nexthop_len == 32)
-       {
-         stream_put (s, &attre->mp_nexthop_global, 16);
-         stream_put (s, &attre->mp_nexthop_local, 16);
-       }
-      
-      /* SNPA */
-      stream_putc (s, 0);
-
-      /* Prefix write. */
-      stream_put_prefix (s, p);
-
-      /* Set MP attribute length. */
-      stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1);
-    }
-#endif /* HAVE_IPV6 */
-
-  if (p->family == AF_INET && safi == SAFI_MULTICAST)
-    {
-      unsigned long sizep;
-
-      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
-      stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
-      sizep = stream_get_endp (s);
-      stream_putc (s, 0);      /* Marker: Attribute Length. */
-      stream_putw (s, AFI_IP); /* AFI */
-      stream_putc (s, SAFI_MULTICAST); /* SAFI */
-
-      stream_putc (s, 4);
-      stream_put_ipv4 (s, attr->nexthop.s_addr);
-
-      /* SNPA */
-      stream_putc (s, 0);
-
-      /* Prefix write. */
-      stream_put_prefix (s, p);
-
-      /* Set MP attribute length. */
-      stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1);
-    }
-
-  if (p->family == AF_INET && safi == SAFI_MPLS_VPN)
-    {
-      unsigned long sizep;
-
-      stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
-      stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
-      sizep = stream_get_endp (s);
-      stream_putc (s, 0);      /* Length of this attribute. */
-      stream_putw (s, AFI_IP); /* AFI */
-      stream_putc (s, SAFI_MPLS_LABELED_VPN);  /* SAFI */
-
-      stream_putc (s, 12);
-      stream_putl (s, 0);
-      stream_putl (s, 0);
-      stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
-
-      /* SNPA */
-      stream_putc (s, 0);
-
-      /* Tag, RD, Prefix write. */
-      stream_putc (s, p->prefixlen + 88);
-      stream_put (s, tag, 3);
-      stream_put (s, prd->val, 8);
-      stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
-
-      /* Set MP attribute length. */
-      stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1);
-    }
-
   /* Extended Communities attribute. */
   if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) 
       && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
@@ -2497,50 +2509,49 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
   return stream_get_endp (s) - cp;
 }
 
-bgp_size_t
-bgp_packet_withdraw (struct peer *peer, struct stream *s, struct prefix *p,
-                    afi_t afi, safi_t safi, struct prefix_rd *prd,
-                    u_char *tag)
+size_t
+bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi)
 {
-  unsigned long cp;
   unsigned long attrlen_pnt;
-  bgp_size_t size;
 
-  cp = stream_get_endp (s);
-
-  stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+  /* Set extended bit always to encode the attribute length as 2 bytes */
+  stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
   stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI);
 
   attrlen_pnt = stream_get_endp (s);
-  stream_putc (s, 0);          /* Length of this attribute. */
+  stream_putw (s, 0);          /* Length of this attribute. */
 
-  stream_putw (s, family2afi (p->family));
+  stream_putw (s, afi);
+  safi = (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi;
+  stream_putc (s, safi);
+  return attrlen_pnt;
+}
 
+void
+bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p,
+                            afi_t afi, safi_t safi, struct prefix_rd *prd,
+                            u_char *tag)
+{
   if (safi == SAFI_MPLS_VPN)
     {
-      /* SAFI */
-      stream_putc (s, SAFI_MPLS_LABELED_VPN);
-
-      /* prefix. */
       stream_putc (s, p->prefixlen + 88);
       stream_put (s, tag, 3);
       stream_put (s, prd->val, 8);
       stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
     }
   else
-    {
-      /* SAFI */
-      stream_putc (s, safi);
-
-      /* prefix */
-      stream_put_prefix (s, p);
-    }
+    stream_put_prefix (s, p);
+}
 
-  /* Set MP attribute length. */
-  size = stream_get_endp (s) - attrlen_pnt - 1;
-  stream_putc_at (s, attrlen_pnt, size);
+void
+bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt)
+{
+  bgp_size_t size;
 
-  return stream_get_endp (s) - cp;
+  /* Set MP attribute length. Don't count the (2) bytes used to encode
+     the attr length */
+  size = stream_get_endp (s) - attrlen_pnt - 2;
+  stream_putw_at (s, attrlen_pnt, size);
 }
 
 /* Initialization of attribute. */
index df87c863189b91ef6fdd3627c19972b0f13231c6..cdd54674bb372e9b8ee7012932e80581a6e72de6 100644 (file)
@@ -157,13 +157,11 @@ extern struct attr *bgp_attr_default_intern (u_char);
 extern struct attr *bgp_attr_aggregate_intern (struct bgp *, u_char,
                                         struct aspath *, 
                                         struct community *, int as_set);
-extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *, 
-                                 struct stream *, struct attr *, 
-                                 struct prefix *, afi_t, safi_t, 
-                                 struct peer *, struct prefix_rd *, u_char *);
-extern bgp_size_t bgp_packet_withdraw (struct peer *peer, struct stream *s, 
-                                struct prefix *p, afi_t, safi_t, 
-                                struct prefix_rd *, u_char *);
+extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *,
+                                       struct stream *, struct attr *,
+                                       struct prefix *, afi_t, safi_t,
+                                       struct peer *, struct prefix_rd *,
+                                       u_char *);
 extern void bgp_dump_routes_attr (struct stream *, struct attr *,
                                  struct prefix *);
 extern int attrhash_cmp (const void *, const void *);
@@ -194,4 +192,24 @@ extern int bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
 extern int bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
                                  struct bgp_nlri *);
 
+/**
+ * Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
+ * Typical call sequence is to call _start(), followed by multiple _prefix(),
+ * one for each NLRI that needs to be encoded into the UPDATE message, and
+ * finally the _end() function.
+ */
+extern size_t bgp_packet_mpattr_start(struct stream *s, afi_t afi, safi_t safi,
+                                     struct attr *attr);
+extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
+                                    struct prefix *p, struct prefix_rd *prd,
+                                    u_char *tag);
+extern void bgp_packet_mpattr_end(struct stream *s, size_t sizep);
+
+extern size_t bgp_packet_mpunreach_start (struct stream *s, afi_t afi,
+                                         safi_t safi);
+extern void bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p,
+                            afi_t afi, safi_t safi, struct prefix_rd *prd,
+                            u_char *tag);
+extern void bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt);
+
 #endif /* _QUAGGA_BGP_ATTR_H */
index d71df0823ffe3254563319e4a5d3405f220afc61..d5f24170c33dfa7669873a0f80577575653c9e01 100644 (file)
@@ -142,16 +142,21 @@ static struct stream *
 bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
 {
   struct stream *s;
+  struct stream *snlri;
   struct bgp_adj_out *adj;
   struct bgp_advertise *adv;
   struct stream *packet;
   struct bgp_node *rn = NULL;
   struct bgp_info *binfo = NULL;
   bgp_size_t total_attr_len = 0;
-  unsigned long pos;
+  unsigned long attrlen_pos = 0;
+  size_t mpattrlen_pos = 0;
+  size_t mpattr_pos = 0;
 
   s = peer->work;
   stream_reset (s);
+  snlri = peer->scratch;
+  stream_reset (snlri);
 
   adv = FIFO_HEAD (&peer->sync[afi][safi]->update);
 
@@ -164,39 +169,61 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
         binfo = adv->binfo;
 
       /* When remaining space can't include NLRI and it's length.  */
-      if (STREAM_REMAIN (s) <= BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen))
+      if (STREAM_CONCAT_REMAIN (s, snlri, STREAM_SIZE(s)) <=
+         (BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen)))
        break;
 
       /* If packet is empty, set attribute. */
       if (stream_empty (s))
        {
-         struct prefix_rd *prd = NULL;
-         u_char *tag = NULL;
          struct peer *from = NULL;
-         
-         if (rn->prn)
-           prd = (struct prefix_rd *) &rn->prn->p;
+
           if (binfo)
-            {
-              from = binfo->peer;
-              if (binfo->extra)
-                tag = binfo->extra->tag;
-            }
-          
+           from = binfo->peer;
+
+         /* 1: Write the BGP message header - 16 bytes marker, 2 bytes length,
+          * one byte message type.
+          */
          bgp_packet_set_marker (s, BGP_MSG_UPDATE);
-         stream_putw (s, 0);           
-         pos = stream_get_endp (s);
+
+         /* 2: withdrawn routes length */
+         stream_putw (s, 0);
+
+         /* 3: total attributes length - attrlen_pos stores the position */
+         attrlen_pos = stream_get_endp (s);
          stream_putw (s, 0);
-         total_attr_len = bgp_packet_attribute (NULL, peer, s, 
+
+         /* 4: if there is MP_REACH_NLRI attribute, that should be the first
+          * attribute, according to draft-ietf-idr-error-handling. Save the
+          * position.
+          */
+         mpattr_pos = stream_get_endp(s);
+
+         /* 5: Encode all the attributes, except MP_REACH_NLRI attr. */
+         total_attr_len = bgp_packet_attribute (NULL, peer, s,
                                                 adv->baa->attr,
-                                                &rn->p, afi, safi, 
-                                                from, prd, tag);
-         stream_putw_at (s, pos, total_attr_len);
+                                                NULL, afi, safi,
+                                                from, NULL, NULL);
        }
 
       if (afi == AFI_IP && safi == SAFI_UNICAST)
        stream_put_prefix (s, &rn->p);
-      
+      else
+       {
+         /* Encode the prefix in MP_REACH_NLRI attribute */
+         struct prefix_rd *prd = NULL;
+         u_char *tag = NULL;
+
+         if (rn->prn)
+           prd = (struct prefix_rd *) &rn->prn->p;
+         if (binfo && binfo->extra)
+           tag = binfo->extra->tag;
+
+         if (stream_empty(snlri))
+           mpattrlen_pos = bgp_packet_mpattr_start(snlri, afi, safi,
+                                                   adv->baa->attr);
+         bgp_packet_mpattr_prefix(snlri, afi, safi, &rn->p, prd, tag);
+       }
       if (BGP_DEBUG (update, UPDATE_OUT))
         {
           char buf[INET6_BUFSIZ];
@@ -216,18 +243,28 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi)
       adj->attr = bgp_attr_intern (adv->baa->attr);
 
       adv = bgp_advertise_clean (peer, adj, afi, safi);
-
-      if (! (afi == AFI_IP && safi == SAFI_UNICAST))
-       break;
     }
-        
+
   if (! stream_empty (s))
     {
-      bgp_packet_set_size (s);
-      packet = stream_dup (s);
+      if (!stream_empty(snlri))
+       {
+         bgp_packet_mpattr_end(snlri, mpattrlen_pos);
+         total_attr_len += stream_get_endp(snlri);
+       }
+
+      /* set the total attribute length correctly */
+      stream_putw_at (s, attrlen_pos, total_attr_len);
+
+      if (!stream_empty(snlri))
+       packet = stream_dupcat(s, snlri, mpattr_pos);
+      else
+       packet = stream_dup (s);
+      bgp_packet_set_size (packet);
       bgp_packet_add (peer, packet);
       BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
       stream_reset (s);
+      stream_reset (snlri);
       return packet;
     }
   return NULL;
@@ -277,6 +314,15 @@ bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi)
 }
 
 /* Make BGP withdraw packet.  */
+/* For ipv4 unicast:
+   16-octet marker | 2-octet length | 1-octet type |
+    2-octet withdrawn route length | withdrawn prefixes | 2-octet attrlen (=0)
+*/
+/* For other afi/safis:
+   16-octet marker | 2-octet length | 1-octet type |
+    2-octet withdrawn route length (=0) | 2-octet attrlen |
+     mp_unreach attr type | attr len | afi | safi | withdrawn prefixes
+*/
 static struct stream *
 bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi)
 {
@@ -288,6 +334,10 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi)
   unsigned long pos;
   bgp_size_t unfeasible_len;
   bgp_size_t total_attr_len;
+  size_t mp_start = 0;
+  size_t attrlen_pos = 0;
+  size_t mplen_pos = 0;
+  u_char first_time = 1;
 
   s = peer->work;
   stream_reset (s);
@@ -298,31 +348,38 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi)
       adj = adv->adj;
       rn = adv->rn;
 
-      if (STREAM_REMAIN (s) 
+      if (STREAM_REMAIN (s)
          < (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen)))
        break;
 
       if (stream_empty (s))
        {
          bgp_packet_set_marker (s, BGP_MSG_UPDATE);
-         stream_putw (s, 0);
+         stream_putw (s, 0); /* unfeasible routes length */
        }
+      else
+       first_time = 0;
 
       if (afi == AFI_IP && safi == SAFI_UNICAST)
        stream_put_prefix (s, &rn->p);
       else
        {
          struct prefix_rd *prd = NULL;
-         
+
          if (rn->prn)
            prd = (struct prefix_rd *) &rn->prn->p;
-         pos = stream_get_endp (s);
-         stream_putw (s, 0);
-         total_attr_len
-           = bgp_packet_withdraw (peer, s, &rn->p, afi, safi, prd, NULL);
-      
-         /* Set total path attribute length. */
-         stream_putw_at (s, pos, total_attr_len);
+
+         /* If first time, format the MP_UNREACH header */
+         if (first_time)
+           {
+             attrlen_pos = stream_get_endp (s);
+             /* total attr length = 0 for now. reevaluate later */
+             stream_putw (s, 0);
+             mp_start = stream_get_endp (s);
+             mplen_pos = bgp_packet_mpunreach_start(s, afi, safi);
+           }
+
+         bgp_packet_mpunreach_prefix(s, &rn->p, afi, safi, prd, NULL);
        }
 
       if (BGP_DEBUG (update, UPDATE_OUT))
@@ -339,20 +396,26 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi)
 
       bgp_adj_out_remove (rn, adj, peer, afi, safi);
       bgp_unlock_node (rn);
-
-      if (! (afi == AFI_IP && safi == SAFI_UNICAST))
-       break;
     }
 
   if (! stream_empty (s))
     {
       if (afi == AFI_IP && safi == SAFI_UNICAST)
        {
-         unfeasible_len 
+         unfeasible_len
            = stream_get_endp (s) - BGP_HEADER_SIZE - BGP_UNFEASIBLE_LEN;
          stream_putw_at (s, BGP_HEADER_SIZE, unfeasible_len);
          stream_putw (s, 0);
        }
+      else
+       {
+         /* Set the mp_unreach attr's length */
+         bgp_packet_mpunreach_end(s, mplen_pos);
+
+         /* Set total path attribute length. */
+         total_attr_len = stream_get_endp(s) - mp_start;
+         stream_putw_at (s, attrlen_pos, total_attr_len);
+       }
       bgp_packet_set_size (s);
       packet = stream_dup (s);
       bgp_packet_add (peer, packet);
@@ -439,10 +502,12 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
   struct stream *s;
   struct stream *packet;
   struct prefix p;
-  unsigned long pos;
+  unsigned long attrlen_pos = 0;
   unsigned long cp;
   bgp_size_t unfeasible_len;
   bgp_size_t total_attr_len;
+  size_t mp_start = 0;
+  size_t mplen_pos = 0;
 
   if (DISABLE_BGP_ANNOUNCE)
     return;
@@ -455,7 +520,6 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
 #endif /* HAVE_IPV6 */
 
   total_attr_len = 0;
-  pos = 0;
 
   if (BGP_DEBUG (update, UPDATE_OUT))
     {
@@ -490,12 +554,18 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
     }
   else
     {
-      pos = stream_get_endp (s);
+      attrlen_pos = stream_get_endp (s);
       stream_putw (s, 0);
-      total_attr_len = bgp_packet_withdraw (peer, s, &p, afi, safi, NULL, NULL);
+      mp_start = stream_get_endp (s);
+      mplen_pos = bgp_packet_mpunreach_start(s, afi, safi);
+      bgp_packet_mpunreach_prefix(s, &p, afi, safi, NULL, NULL);
+
+      /* Set the mp_unreach attr's length */
+      bgp_packet_mpunreach_end(s, mplen_pos);
 
       /* Set total path attribute length. */
-      stream_putw_at (s, pos, total_attr_len);
+      total_attr_len = stream_get_endp(s) - mp_start;
+      stream_putw_at (s, attrlen_pos, total_attr_len);
     }
 
   bgp_packet_set_size (s);
index 88d13ed8c1513d579f7cbbc8a2137ddba6a91247..6a21b11abc02525b751b36da7473645443872ede 100644 (file)
@@ -832,6 +832,7 @@ peer_new (struct bgp *bgp)
   peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE);
   peer->obuf = stream_fifo_new ();
   peer->work = stream_new (BGP_MAX_PACKET_SIZE);
+  peer->scratch = stream_new (BGP_MAX_PACKET_SIZE);
 
   bgp_sync_init (peer);
 
@@ -1272,8 +1273,10 @@ peer_delete (struct peer *peer)
     stream_fifo_free (peer->obuf);
   if (peer->work)
     stream_free (peer->work);
+  if (peer->scratch)
+    stream_free(peer->scratch);
   peer->obuf = NULL;
-  peer->work = peer->ibuf = NULL;
+  peer->work = peer->scratch = peer->ibuf = NULL;
 
   /* Local and remote addresses. */
   if (peer->su_local)
index 3d516d35a4f6b3541d53fb72e8524590190b52f0..688f459f263ab1880e33a892715cde14f5fc156d 100644 (file)
@@ -313,6 +313,12 @@ struct peer
   struct stream_fifo *obuf;
   struct stream *work;
 
+  /* We use a separate stream to encode MP_REACH_NLRI for efficient
+   * NLRI packing. peer->work stores all the other attributes. The
+   * actual packet is then constructed by concatenating the two.
+   */
+  struct stream *scratch;
+
   /* Status of the peer. */
   int status;
   int ostatus;
index ccd4623ff16f4c542fc44278dcb51393024c048a..9a6fcbcf272b6a88150a3dcf8eb3e89e18a85ffc 100644 (file)
@@ -154,6 +154,25 @@ stream_dup (struct stream *s)
   return (stream_copy (new, s));
 }
 
+struct stream *
+stream_dupcat (struct stream *s1, struct stream *s2, size_t offset)
+{
+  struct stream *new;
+
+  STREAM_VERIFY_SANE (s1);
+  STREAM_VERIFY_SANE (s2);
+
+  if ( (new = stream_new (s1->endp + s2->endp)) == NULL)
+    return NULL;
+
+  memcpy (new->data, s1->data, offset);
+  memcpy (new->data + offset, s2->data, s2->endp);
+  memcpy (new->data + offset + s2->endp, s1->data + offset,
+         (s1->endp - offset));
+  new->endp = s1->endp + s2->endp;
+  return new;
+}
+
 size_t
 stream_resize (struct stream *s, size_t newsize)
 {
index f10aa6d417125ea8ac77448b5e2ad2f836b6f402..f0c742c052218f7b60bde2f37b26e1caf908f34c 100644 (file)
@@ -122,6 +122,9 @@ struct stream_fifo
   /* number of bytes still to be read */
 #define STREAM_READABLE(S) ((S)->endp - (S)->getp)
 
+#define STREAM_CONCAT_REMAIN(S1, S2, size) \
+  ((size) - (S1)->endp - (S2)->endp)
+
 /* deprecated macros - do not use in new code */
 #define STREAM_PNT(S)   stream_pnt((S))
 #define STREAM_DATA(S)  ((S)->data)
@@ -145,6 +148,14 @@ extern size_t stream_get_endp (struct stream *);
 extern size_t stream_get_size (struct stream *);
 extern u_char *stream_get_data (struct stream *);
 
+/**
+ * Create a new stream structure; copy offset bytes from s1 to the new
+ * stream; copy s2 data to the new stream; copy rest of s1 data to the
+ * new stream.
+ */
+extern struct stream *stream_dupcat(struct stream *s1, struct stream *s2,
+                                   size_t offset);
+
 extern void stream_set_getp (struct stream *, size_t);
 extern void stream_set_endp (struct stream *, size_t);
 extern void stream_forward_getp (struct stream *, size_t);