From: David Lamparter <equi...@opensourcerouting.org>

Support for multiple label support handling in NLRI messages.
Ability to receive and sent BGP updates with NLRI containing multiple
labels. The commit follows label encoding rules, contained in RFC3107.

Signed-off-by: David Lamparter <equi...@opensourcerouting.org>
Signed-off-by: Christian Franke <ch...@opensourcerouting.org>
Signed-off-by: Philippe Guibert <philippe.guib...@6wind.com>
---
 bgpd/bgp_attr.c    |  27 +++++++---
 bgpd/bgp_attr.h    |   6 +--
 bgpd/bgp_encap.c   |   4 +-
 bgpd/bgp_mplsvpn.c | 123 +++++++++++++++++++++++++++++----------------
 bgpd/bgp_mplsvpn.h |   4 +-
 bgpd/bgp_packet.c  |  28 +++++++----
 bgpd/bgp_route.c   | 144 ++++++++++++++++++++++++++++++++++++-----------------
 bgpd/bgp_route.h   |  15 ++++--
 8 files changed, 230 insertions(+), 121 deletions(-)

diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index f34e649332e6..856d38d6793a 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -2474,13 +2474,23 @@ bgp_packet_mpattr_start (struct stream *s, afi_t afi, 
safi_t safi,
 void
 bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi,
                          struct prefix *p, struct prefix_rd *prd,
-                         u_char *tag)
+                         uint32_t *labels, size_t nlabels)
 {
   if (safi == SAFI_MPLS_VPN)
     {
-      /* Tag, RD, Prefix write. */
-      stream_putc (s, p->prefixlen + 88);
-      stream_put (s, tag, 3);
+      if (nlabels != 0)
+        {
+          /* Tag, RD, Prefix write. */
+          stream_putc (s, p->prefixlen + 8 * (8 + 3 * nlabels));
+          for (size_t i = 0; i < nlabels; i++)
+            stream_put3 (s, labels[i]);
+        }
+      else
+        {
+          /* Withdraw, put bottom of stack as only label */
+          stream_putc (s, p->prefixlen + 8 * (8 + 3));
+          stream_put3 (s, 0x1);
+        }
       stream_put (s, prd->val, 8);
       stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
     }
@@ -2596,7 +2606,8 @@ 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 peer *from, struct prefix_rd *prd,
+                     uint32_t *labels, size_t nlabels)
 {
   size_t cp;
   size_t aspath_sizep;
@@ -2615,7 +2626,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
     {
       size_t mpattrlen_pos = 0;
       mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi, attr);
-      bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag);
+      bgp_packet_mpattr_prefix(s, afi, safi, p, prd, labels, nlabels);
       bgp_packet_mpattr_end(s, mpattrlen_pos);
     }
 
@@ -2976,9 +2987,9 @@ bgp_packet_mpunreach_start (struct stream *s, afi_t afi, 
safi_t safi)
 void
 bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p,
                             afi_t afi, safi_t safi, struct prefix_rd *prd,
-                            u_char *tag)
+                            uint32_t *labels, size_t nlabels)
 {
-  bgp_packet_mpattr_prefix (s, afi, safi, p, prd, tag);
+  bgp_packet_mpattr_prefix (s, afi, safi, p, prd, labels, nlabels);
 }
 
 void
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index fe6c2a1a11c6..5cb67ec079ed 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -171,7 +171,7 @@ 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 *);
+                                       uint32_t *labels, size_t nlabels);
 extern void bgp_dump_routes_attr (struct stream *, struct attr *,
                                  struct prefix *);
 extern int attrhash_cmp (const void *, const void *);
@@ -218,7 +218,7 @@ 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);
+                                    uint32_t *labels, size_t nlabels);
 extern size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi,
                                             struct prefix *p);
 extern void bgp_packet_mpattr_end(struct stream *s, size_t sizep);
@@ -227,7 +227,7 @@ 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);
+                            uint32_t *labels, size_t nlabels);
 extern void bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt);
 
 #endif /* _QUAGGA_BGP_ATTR_H */
diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c
index 1a09ba6019d5..40b273a9e1ed 100644
--- a/bgpd/bgp_encap.c
+++ b/bgpd/bgp_encap.c
@@ -228,10 +228,10 @@ bgp_nlri_parse_encap(
 
       if (attr) {
        bgp_update (peer, &p, attr, afi, SAFI_ENCAP,
-                   ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0);
+                   ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0, 0);
       } else {
        bgp_withdraw (peer, &p, attr, afi, SAFI_ENCAP,
-                     ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL);
+                     ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0);
       }
     }
 
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 08a4272d6a44..020f1540a4f5 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -45,17 +45,6 @@ decode_rd_type (u_char *pnt)
   return v;
 }
 
-u_int32_t
-decode_label (u_char *pnt)
-{
-  u_int32_t l;
-
-  l = ((u_int32_t) *pnt++ << 12);
-  l |= (u_int32_t) *pnt++ << 4;
-  l |= (u_int32_t) ((*pnt & 0xf0) >> 4);
-  return l;
-}
-
 /* type == RD_TYPE_AS */
 static void
 decode_rd_as (u_char *pnt, struct rd_as *rd_as)
@@ -93,12 +82,30 @@ decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip)
   rd_ip->val |= (u_int16_t) *pnt;
 }
 
+char *
+labels2str (char *str, size_t size, uint32_t *labels, size_t nlabels)
+{
+  if (nlabels == 0)
+    {
+      snprintf (str, size, ":");
+      return str;
+    }
+  char *pos = str;
+  for (size_t i = 0; i < nlabels; i++)
+    {
+      snprintf (pos, str + size - pos, "%s%d", (i > 0) ? ":" : "",
+          labels[i] >> 4);
+      pos += strlen(pos);
+    }
+  return str;
+}
+
 int
 bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr, 
                     struct bgp_nlri *packet)
 {
   u_char *pnt;
-  u_char *lim;
+  u_char *lim, *lim2;
   struct prefix p;
   int psize = 0;
   int prefixlen;
@@ -106,7 +113,8 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
   struct rd_as rd_as;
   struct rd_ip rd_ip;
   struct prefix_rd prd;
-  u_char *tagpnt;
+  uint32_t labels[BGP_MAX_LABELS];
+  size_t nlabels;
 
   /* Check peer status. */
   if (peer->status != Established)
@@ -118,8 +126,8 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
 
   pnt = packet->nlri;
   lim = pnt + packet->length;
-
-#define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */
+#define VPN_LABEL_SIZE 3
+#define VPN_PREFIXLEN_MIN_BYTES (VPN_LABEL_SIZE + BGP_RD_SIZE) /* label + RD */
   for (; pnt < lim; pnt += psize)
     {
       /* Clear prefix structure. */
@@ -148,8 +156,33 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
                     prefixlen, (uint)(lim-pnt));
           return -1;
         }
-      
+      lim2 = pnt + psize;
+      nlabels = 0;
+      while (1)
+        {
+          u_char *pnt2 = pnt;
+          if (pnt2 + VPN_LABEL_SIZE > lim2)
+            {
+              zlog_err ("label stack running past prefix length");
+              return -1;
+            }
+          uint32_t label = (pnt2[0] << 16) + (pnt2[1] << 8) + pnt2[2];
+          pnt2 += VPN_LABEL_SIZE;
+          if (nlabels == BGP_MAX_LABELS)
+            {
+              zlog_err ("label stack too deep");
+              return -1;
+            }
+          labels[nlabels++] = label;
+          if (label == 0 || label == 0x800000 || label & 0x000001)
+            break;
+        }
       /* sanity check against storage for the IP address portion */
+      if (pnt + VPN_PREFIXLEN_MIN_BYTES + (nlabels - 1)*VPN_LABEL_SIZE  > lim)
+        {
+          zlog_err ("not enough bytes for RD left in NLRI?");
+          return -1;
+        }
       if ((psize - VPN_PREFIXLEN_MIN_BYTES) > (ssize_t) sizeof(p.u))
         {
           plog_err (peer->log,
@@ -172,27 +205,23 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
           return -1;
         }
       
-      /* Copyr label to prefix. */
-      tagpnt = pnt;
-
       /* Copy routing distinguisher to rd. */
-      memcpy (&prd.val, pnt + 3, 8);
-
+      memcpy (&prd.val, pnt + VPN_LABEL_SIZE*nlabels, BGP_RD_SIZE);
       /* Decode RD type. */
-      type = decode_rd_type (pnt + 3);
+      type = decode_rd_type (pnt + VPN_LABEL_SIZE*nlabels);
 
       switch (type)
         {
         case RD_TYPE_AS:
-          decode_rd_as (pnt + 5, &rd_as);
+          decode_rd_as (pnt + VPN_LABEL_SIZE*nlabels + 2, &rd_as);
           break;
 
         case RD_TYPE_AS4:
-          decode_rd_as4 (pnt + 5, &rd_as);
+          decode_rd_as4 (pnt + VPN_LABEL_SIZE*nlabels + 2, &rd_as);
           break;
 
         case RD_TYPE_IP:
-          decode_rd_ip (pnt + 5, &rd_ip);
+          decode_rd_ip (pnt + VPN_LABEL_SIZE*nlabels + 2, &rd_ip);
           break;
 
        default:
@@ -200,16 +229,16 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
           break;  /* just report */
       }
 
-      p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES*8;
-      memcpy (&p.u.prefix, pnt + VPN_PREFIXLEN_MIN_BYTES, 
-              psize - VPN_PREFIXLEN_MIN_BYTES);
+      p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES*8 - (nlabels - 
1)*VPN_LABEL_SIZE*8;
+      memcpy (&p.u.prefix, pnt + VPN_PREFIXLEN_MIN_BYTES + (nlabels - 
1)*VPN_LABEL_SIZE, 
+              psize - VPN_PREFIXLEN_MIN_BYTES - (nlabels - 1)*VPN_LABEL_SIZE);
 
       if (attr)
         bgp_update (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
-                    ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0);
+                    ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, labels, nlabels, 
0);
       else
         bgp_withdraw (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
-                      ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt);
+                      ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, labels, 
nlabels);
     }
   /* Packet length consistency check. */
   if (pnt != lim)
@@ -223,6 +252,7 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
   
   return 0;
 #undef VPN_PREFIXLEN_MIN_BYTES
+#undef VPN_LABEL_SIZE
 }
 
 int
@@ -286,28 +316,33 @@ out:
 }
 
 int
-str2tag (const char *str, u_char *tag)
+str2labels (const char *str, uint32_t*labels, size_t *nlabels)
 {
   unsigned long l;
   char *endptr;
-  u_int32_t t;
 
   if (*str == '-')
     return 0;
   
-  errno = 0;
-  l = strtoul (str, &endptr, 10);
-
-  if (*endptr != '\0' || errno || l > UINT32_MAX)
-    return 0;
+  if (str[0] == ':' && str[1] == '\0')
+    return 1;
 
-  t = (u_int32_t) l;
-  
-  tag[0] = (u_char)(t >> 12);
-  tag[1] = (u_char)(t >> 4);
-  tag[2] = (u_char)(t << 4);
-
-  return 1;
+  *nlabels = 0;
+  while (*nlabels < BGP_MAX_LABELS)
+    {
+      errno = 0;
+      l = strtoul (str, &endptr, 0);
+      if (endptr == str || (*endptr != '\0' && *endptr != ':') || l >= 
0x100000)
+        return 0;
+      labels[*nlabels] = l << 4;
+      (*nlabels)++;
+      if (*endptr == '\0')
+        {
+          labels[*nlabels - 1] |= 1;
+          break;
+        }
+    }
+  return *endptr == '\0';
 }
 
 char *
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index 3299b9cb9a04..bfa9caef616b 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -43,9 +43,9 @@ struct rd_ip
 
 extern void bgp_mplsvpn_init (void);
 extern int bgp_nlri_parse_vpn (struct peer *, struct attr *, struct bgp_nlri 
*);
-extern u_int32_t decode_label (u_char *);
 extern int str2prefix_rd (const char *, struct prefix_rd *);
-extern int str2tag (const char *, u_char *);
+extern int str2labels (const char *str, uint32_t *labels, size_t *nlabels);
+extern char *labels2str (char *str, size_t size, uint32_t *labels, size_t 
nlabels);
 extern char *prefix_rd2str (struct prefix_rd *, char *, size_t);
 
 #endif /* _QUAGGA_BGP_MPLSVPN_H */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 740b0f1ce603..ba4acee98427 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -180,7 +180,8 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t 
safi)
       if (stream_empty (s))
        {
          struct prefix_rd *prd = NULL;
-         u_char *tag = NULL;
+         uint32_t *labels = NULL;
+         size_t nlabels = 0;
          struct peer *from = NULL;
 
          if (rn->prn)
@@ -189,7 +190,10 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t 
safi)
             {
               from = binfo->peer;
               if (binfo->extra)
-                tag = binfo->extra->tag;
+                {
+                  labels = binfo->extra->labels;
+                  nlabels = binfo->extra->nlabels;
+                }
             }
 
          /* 1: Write the BGP message header - 16 bytes marker, 2 bytes length,
@@ -213,10 +217,10 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t 
safi)
          /* 5: Encode all the attributes, except MP_REACH_NLRI attr. */
          total_attr_len = bgp_packet_attribute (NULL, peer, s,
                                                 adv->baa->attr,
-                                                 ((afi == AFI_IP && safi == 
SAFI_UNICAST) ?
+                                                ((afi == AFI_IP && safi == 
SAFI_UNICAST) ?
                                                   &rn->p : NULL),
                                                  afi, safi,
-                                                from, prd, tag);
+                                                from, prd, labels, nlabels);
        }
 
       if (afi == AFI_IP && safi == SAFI_UNICAST)
@@ -225,17 +229,21 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t 
safi)
        {
          /* Encode the prefix in MP_REACH_NLRI attribute */
          struct prefix_rd *prd = NULL;
-         u_char *tag = NULL;
+         uint32_t *labels = NULL;
+         size_t nlabels = 0;
 
          if (rn->prn)
            prd = (struct prefix_rd *) &rn->prn->p;
          if (binfo && binfo->extra)
-           tag = binfo->extra->tag;
+            {
+              labels = binfo->extra->labels;
+              nlabels = binfo->extra->nlabels;
+            }
 
          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);
+         bgp_packet_mpattr_prefix(snlri, afi, safi, &rn->p, prd, labels, 
nlabels);
        }
       if (BGP_DEBUG (update, UPDATE_OUT))
         {
@@ -388,7 +396,7 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t 
safi)
              mplen_pos = bgp_packet_mpunreach_start(s, afi, safi);
            }
 
-         bgp_packet_mpunreach_prefix(s, &rn->p, afi, safi, prd, NULL);
+         bgp_packet_mpunreach_prefix(s, &rn->p, afi, safi, prd, NULL, 0);
        }
 
       if (BGP_DEBUG (update, UPDATE_OUT))
@@ -476,7 +484,7 @@ bgp_default_update_send (struct peer *peer, struct attr 
*attr,
   /* Make place for total attribute length.  */
   pos = stream_get_endp (s);
   stream_putw (s, 0);
-  total_attr_len = bgp_packet_attribute (NULL, peer, s, attr, &p, afi, safi, 
from, NULL, NULL);
+  total_attr_len = bgp_packet_attribute (NULL, peer, s, attr, &p, afi, safi, 
from, NULL, NULL, 0);
 
   /* Set Total Path Attribute Length. */
   stream_putw_at (s, pos, total_attr_len);
@@ -558,7 +566,7 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, 
safi_t safi)
       stream_putw (s, 0);
       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);
+      bgp_packet_mpunreach_prefix(s, &p, afi, safi, NULL, NULL, 0);
 
       /* Set the mp_unreach attr's length */
       bgp_packet_mpunreach_end(s, mplen_pos);
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index c364372f8bf7..90cc44f2e63b 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1882,10 +1882,33 @@ bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info 
*ri, struct peer *peer,
   bgp_rib_remove (rn, ri, peer, afi, safi);
 }
 
+static bool
+labels_equal(struct bgp_info *info, uint32_t *labels, size_t nlabels)
+{
+       uint32_t *info_labels;
+       size_t info_nlabels;
+
+       if (!info->extra) {
+               info_labels = NULL;
+               info_nlabels = 0;
+       } else {
+               info_labels = info->extra->labels;
+               info_nlabels = info->extra->nlabels;
+       }
+
+       if (info_nlabels != nlabels)
+               return false;
+
+       if (!nlabels)
+               return true;
+
+       return !memcmp(labels, info_labels, nlabels * sizeof(labels[0]));
+}
+
 static void
 bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi,
       struct attr *attr, struct peer *peer, struct prefix *p, int type,
-      int sub_type, struct prefix_rd *prd, u_char *tag)
+      int sub_type, struct prefix_rd *prd, uint32_t *labels, size_t nlabels)
 {
   struct bgp_node *rn;
   struct bgp *bgp;
@@ -1971,7 +1994,8 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, 
safi_t safi,
 
       /* Same attribute comes in. */
       if (!CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)
-          && attrhash_cmp (ri->attr, attr_new))
+          && attrhash_cmp (ri->attr, attr_new)
+          && labels_equal (ri, labels, nlabels))
         {
 
           bgp_info_unset_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
@@ -2007,9 +2031,14 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, 
safi_t safi,
       bgp_attr_unintern (&ri->attr);
       ri->attr = attr_new;
 
-      /* Update MPLS tag.  */
-      if (safi == SAFI_MPLS_VPN)
-        memcpy ((bgp_info_extra_get (ri))->tag, tag, 3);
+      /* Update MPLS tag. */
+      if (nlabels)
+        {
+          bgp_info_extra_get (ri)->nlabels = nlabels;
+          memcpy (ri->extra->labels, labels, sizeof(*labels) * nlabels);
+        }
+      else if (ri->extra)
+        ri->extra->nlabels = 0;
 
       bgp_info_set_flag (rn, ri, BGP_INFO_VALID);
 
@@ -2038,8 +2067,11 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, 
safi_t safi,
   new->uptime = bgp_clock ();
 
   /* Update MPLS tag. */
-  if (safi == SAFI_MPLS_VPN)
-    memcpy ((bgp_info_extra_get (new))->tag, tag, 3);
+  if (nlabels)
+    {
+      bgp_info_extra_get (ri)->nlabels = nlabels;
+      memcpy (ri->extra->labels, labels, sizeof(*labels) * nlabels);
+    }
 
   bgp_info_set_flag (rn, new, BGP_INFO_VALID);
 
@@ -2075,7 +2107,7 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, 
safi_t safi,
 static void
 bgp_withdraw_rsclient (struct peer *rsclient, afi_t afi, safi_t safi,
       struct peer *peer, struct prefix *p, int type, int sub_type,
-      struct prefix_rd *prd, u_char *tag)
+      struct prefix_rd *prd)
 {
   struct bgp_node *rn;
   struct bgp_info *ri;
@@ -2107,7 +2139,8 @@ bgp_withdraw_rsclient (struct peer *rsclient, afi_t afi, 
safi_t safi,
 static int
 bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
            afi_t afi, safi_t safi, int type, int sub_type,
-           struct prefix_rd *prd, u_char *tag, int soft_reconfig)
+           struct prefix_rd *prd, uint32_t *labels, size_t nlabels,
+           int soft_reconfig)
 {
   int ret;
   int aspath_loop_count = 0;
@@ -2232,7 +2265,8 @@ bgp_update_main (struct peer *peer, struct prefix *p, 
struct attr *attr,
 
       /* Same attribute comes in. */
       if (!CHECK_FLAG (ri->flags, BGP_INFO_REMOVED) 
-          && attrhash_cmp (ri->attr, attr_new))
+          && attrhash_cmp (ri->attr, attr_new)
+          && labels_equal (ri, labels, nlabels))
        {
          bgp_info_unset_flag (rn, ri, BGP_INFO_ATTR_CHANGED);
 
@@ -2320,9 +2354,14 @@ bgp_update_main (struct peer *peer, struct prefix *p, 
struct attr *attr,
       bgp_attr_unintern (&ri->attr);
       ri->attr = attr_new;
 
-      /* Update MPLS tag.  */
-      if (safi == SAFI_MPLS_VPN)
-        memcpy ((bgp_info_extra_get (ri))->tag, tag, 3);
+      /* Update MPLS tag. */
+      if (nlabels)
+        {
+          bgp_info_extra_get (ri)->nlabels = nlabels;
+          memcpy (ri->extra->labels, labels, sizeof(*labels) * nlabels);
+        }
+      else if (ri->extra)
+        ri->extra->nlabels = 0;
 
       bgp_attr_flush (&new_attr);
 
@@ -2384,8 +2423,11 @@ bgp_update_main (struct peer *peer, struct prefix *p, 
struct attr *attr,
   new->uptime = bgp_clock ();
 
   /* Update MPLS tag. */
-  if (safi == SAFI_MPLS_VPN)
-    memcpy ((bgp_info_extra_get (new))->tag, tag, 3);
+  if (nlabels)
+    {
+      bgp_info_extra_get (new)->nlabels = nlabels;
+      memcpy (new->extra->labels, labels, sizeof(*labels) * nlabels);
+    }
 
   /* Nexthop reachability check. */
   if ((afi == AFI_IP || afi == AFI_IP6)
@@ -2446,15 +2488,16 @@ bgp_update_main (struct peer *peer, struct prefix *p, 
struct attr *attr,
 int
 bgp_update (struct peer *peer, struct prefix *p, struct attr *attr,
             afi_t afi, safi_t safi, int type, int sub_type,
-            struct prefix_rd *prd, u_char *tag, int soft_reconfig)
+            struct prefix_rd *prd, uint32_t *labels, size_t nlabels,
+            int soft_reconfig)
 {
   struct peer *rsclient;
   struct listnode *node, *nnode;
   struct bgp *bgp;
   int ret;
 
-  ret = bgp_update_main (peer, p, attr, afi, safi, type, sub_type, prd, tag,
-          soft_reconfig);
+  ret = bgp_update_main (peer, p, attr, afi, safi, type, sub_type, prd,
+                         labels, nlabels, soft_reconfig);
 
   bgp = peer->bgp;
 
@@ -2463,7 +2506,7 @@ bgp_update (struct peer *peer, struct prefix *p, struct 
attr *attr,
     {
       if (CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
         bgp_update_rsclient (rsclient, afi, safi, attr, peer, p, type,
-                sub_type, prd, tag);
+                sub_type, prd, labels, nlabels);
     }
 
   return ret;
@@ -2472,7 +2515,7 @@ bgp_update (struct peer *peer, struct prefix *p, struct 
attr *attr,
 int
 bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr, 
             afi_t afi, safi_t safi, int type, int sub_type, 
-            struct prefix_rd *prd, u_char *tag)
+            struct prefix_rd *prd, uint32_t *labels, size_t nlabels)
 {
   struct bgp *bgp;
   char buf[SU_ADDRSTRLEN];
@@ -2508,7 +2551,7 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct 
attr *attr,
   for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, rsclient))
     {
       if (CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
-        bgp_withdraw_rsclient (rsclient, afi, safi, peer, p, type, sub_type, 
prd, tag);
+        bgp_withdraw_rsclient (rsclient, afi, safi, peer, p, type, sub_type, 
prd);
     }
 
   /* Logging. */
@@ -2728,10 +2771,11 @@ bgp_soft_reconfig_table_rsclient (struct peer 
*rsclient, afi_t afi,
     for (ain = rn->adj_in; ain; ain = ain->next)
       {
         struct bgp_info *ri = rn->info;
-        u_char *tag = (ri && ri->extra) ? ri->extra->tag : NULL;
+        uint32_t *labels = (ri && ri->extra) ? ri->extra->labels : NULL;
+        size_t nlabels = (ri && ri->extra) ? ri->extra->nlabels : 0;
 
         bgp_update_rsclient (rsclient, afi, safi, ain->attr, ain->peer,
-                &rn->p, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, tag);
+                &rn->p, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, labels, 
nlabels);
       }
 }
 
@@ -2775,11 +2819,12 @@ bgp_soft_reconfig_table (struct peer *peer, afi_t afi, 
safi_t safi,
        if (ain->peer == peer)
          {
            struct bgp_info *ri = rn->info;
-           u_char *tag = (ri && ri->extra) ? ri->extra->tag : NULL;
+           uint32_t *labels = (ri && ri->extra) ? ri->extra->labels : NULL;
+           size_t nlabels = (ri && ri->extra) ? ri->extra->nlabels : 0;
 
            ret = bgp_update (peer, &rn->p, ain->attr, afi, safi,
                              ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
-                             prd, tag, 1);
+                             prd, labels, nlabels, 1);
 
            if (ret < 0)
              {
@@ -3341,10 +3386,10 @@ bgp_nlri_parse_ip (struct peer *peer, struct attr *attr,
       /* Normal process. */
       if (attr)
        ret = bgp_update (peer, &p, attr, packet->afi, packet->safi, 
-                         ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0);
+                         ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0, 0);
       else
        ret = bgp_withdraw (peer, &p, attr, packet->afi, packet->safi, 
-                           ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL);
+                           ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0);
 
       /* Address family configuration mismatch or maximum-prefix count
          overflow. */
@@ -3753,7 +3798,8 @@ bgp_check_local_routes_rsclient (struct peer *rsclient, 
afi_t afi, safi_t safi)
  */
 static void
 bgp_static_withdraw_safi (struct bgp *bgp, struct prefix *p, afi_t afi,
-                          safi_t safi, struct prefix_rd *prd, u_char *tag)
+                          safi_t safi, struct prefix_rd *prd,
+                          uint32_t *labels, size_t nlabels)
 {
   struct bgp_node *rn;
   struct bgp_info *ri;
@@ -3824,7 +3870,7 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
           aspath_unintern (&attr.aspath);
           bgp_attr_extra_free (&attr);
           bgp_static_withdraw_safi (bgp, p, afi, safi, &bgp_static->prd,
-                                    bgp_static->tag);
+                                    bgp_static->labels, bgp_static->nlabels);
           return;
         }
 
@@ -3885,7 +3931,9 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
   SET_FLAG (new->flags, BGP_INFO_VALID);
   new->uptime = bgp_clock ();
   new->extra = bgp_info_extra_new();
-  memcpy (new->extra->tag, bgp_static->tag, 3);
+  new->extra->nlabels = bgp_static->nlabels;
+  memcpy (new->extra->labels, bgp_static->labels,
+                  sizeof(*bgp_static->labels) * bgp_static->nlabels);
 
   /* Aggregate address increment. */
   bgp_aggregate_increment (bgp, p, new, afi, safi);
@@ -4073,7 +4121,8 @@ bgp_static_delete (struct bgp *bgp)
                    bgp_static_withdraw_safi (bgp, &rm->p,
                                               AFI_IP, safi,
                                               (struct prefix_rd *)&rn->p,
-                                              bgp_static->tag);
+                                              bgp_static->labels,
+                                               bgp_static->nlabels);
                    bgp_static_free (bgp_static);
                    rn->info = NULL;
                    bgp_unlock_node (rn);
@@ -4108,7 +4157,8 @@ bgp_static_set_safi (safi_t safi, struct vty *vty, const 
char *ip_str,
   struct bgp_node *rn;
   struct bgp_table *table;
   struct bgp_static *bgp_static;
-  u_char tag[3];
+  uint32_t labels[BGP_MAX_LABELS];
+  size_t nlabels;
 
   bgp = vty->index;
 
@@ -4127,8 +4177,7 @@ bgp_static_set_safi (safi_t safi, struct vty *vty, const 
char *ip_str,
       return CMD_WARNING;
     }
 
-  ret = str2tag (tag_str, tag);
-  if (! ret)
+  if (! str2labels (tag_str, labels, &nlabels))
     {
       vty_out (vty, "%% Malformed tag%s", VTY_NEWLINE);
       return CMD_WARNING;
@@ -4157,7 +4206,8 @@ bgp_static_set_safi (safi_t safi, struct vty *vty, const 
char *ip_str,
       bgp_static->valid = 0;
       bgp_static->igpmetric = 0;
       bgp_static->igpnexthop.s_addr = 0;
-      memcpy(bgp_static->tag, tag, 3);
+      memcpy(bgp_static->labels, labels, sizeof(labels[0]) * nlabels);
+      bgp_static->nlabels = nlabels;
       bgp_static->prd = prd;
 
       if (rmap_str)
@@ -4189,7 +4239,8 @@ bgp_static_unset_safi(safi_t safi, struct vty *vty, const 
char *ip_str,
   struct bgp_node *rn;
   struct bgp_table *table;
   struct bgp_static *bgp_static;
-  u_char tag[3];
+  uint32_t labels[BGP_MAX_LABELS];
+  size_t nlabels;
 
   bgp = vty->index;
 
@@ -4209,8 +4260,7 @@ bgp_static_unset_safi(safi_t safi, struct vty *vty, const 
char *ip_str,
       return CMD_WARNING;
     }
 
-  ret = str2tag (tag_str, tag);
-  if (! ret)
+  if (! str2labels (tag_str, labels, &nlabels))
     {
       vty_out (vty, "%% Malformed tag%s", VTY_NEWLINE);
       return CMD_WARNING;
@@ -4228,7 +4278,7 @@ bgp_static_unset_safi(safi_t safi, struct vty *vty, const 
char *ip_str,
 
   if (rn)
     {
-      bgp_static_withdraw_safi (bgp, &p, AFI_IP, safi, &prd, tag);
+      bgp_static_withdraw_safi (bgp, &p, AFI_IP, safi, &prd, labels, nlabels);
 
       bgp_static = rn->info;
       bgp_static_free (bgp_static);
@@ -6047,7 +6097,6 @@ route_vty_out_tag (struct vty *vty, struct prefix *p,
                   struct bgp_info *binfo, int display, safi_t safi)
 {
   struct attr *attr;
-  u_int32_t label = 0;
   
   if (!binfo->extra)
     return;
@@ -6092,9 +6141,9 @@ route_vty_out_tag (struct vty *vty, struct prefix *p,
        }
     }
 
-  label = decode_label (binfo->extra->tag);
-
-  vty_out (vty, "notag/%d", label);
+  char buf[BUFSIZ];
+  vty_out (vty, ":/%s", labels2str (buf, sizeof(buf),
+                          binfo->extra->labels, binfo->extra->nlabels));
 
   vty_out (vty, "%s", VTY_NEWLINE);
 }  
@@ -15531,9 +15580,9 @@ bgp_config_write_network_vpnv4 (struct vty *vty, struct 
bgp *bgp,
   struct prefix *p;
   struct prefix_rd *prd;
   struct bgp_static *bgp_static;
-  u_int32_t label;
   char buf[SU_ADDRSTRLEN];
   char rdbuf[RD_ADDRSTRLEN];
+  char lblbuf[BUFSIZ];
   
   /* Network configuration. */
   for (prn = bgp_table_top (bgp->route[afi][safi]); prn; prn = bgp_route_next 
(prn))
@@ -15549,12 +15598,13 @@ bgp_config_write_network_vpnv4 (struct vty *vty, 
struct bgp *bgp,
 
            /* "network" configuration display.  */
            prefix_rd2str (prd, rdbuf, RD_ADDRSTRLEN);
-           label = decode_label (bgp_static->tag);
+            labels2str (lblbuf, sizeof(lblbuf),
+                          bgp_static->labels, bgp_static->nlabels);
 
-           vty_out (vty, " network %s/%d rd %s tag %d",
+           vty_out (vty, " network %s/%d rd %s tag %s",
                     inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), 
                     p->prefixlen,
-                    rdbuf, label);
+                    rdbuf, lblbuf);
            vty_out (vty, "%s", VTY_NEWLINE);
          }
   return 0;
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index c803759256d7..42ceef878939 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -23,6 +23,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, 
Boston, MA
 
 #include "bgp_table.h"
 
+#define BGP_MAX_LABELS 6
+
 /* Ancillary information to struct bgp_info, 
  * used for uncommonly used data (aggregation, MPLS, etc.)
  * and lazily allocated to save memory.
@@ -39,7 +41,8 @@ struct bgp_info_extra
   u_int32_t igpmetric;
 
   /* MPLS label.  */
-  u_char tag[3];  
+  uint32_t labels[BGP_MAX_LABELS];
+  size_t nlabels;
 };
 
 struct bgp_info
@@ -123,8 +126,9 @@ struct bgp_static
   /* Route Distinguisher */
   struct prefix_rd     prd;
 
-  /* MPLS label.  */
-  u_char tag[3];
+  /* MPLS label. */
+  uint32_t labels[BGP_MAX_LABELS];
+  size_t nlabels;
 };
 
 /* Flags which indicate a route is unuseable in some form */
@@ -220,9 +224,10 @@ extern int bgp_static_unset_safi (safi_t safi, struct vty 
*, const char *,
 /* this is primarily for MPLS-VPN */
 extern int bgp_update (struct peer *, struct prefix *, struct attr *,
                       afi_t, safi_t, int, int, struct prefix_rd *, 
-                      u_char *, int);
+                      uint32_t *labels, size_t nlabels, int);
 extern int bgp_withdraw (struct peer *, struct prefix *, struct attr *,
-                        afi_t, safi_t, int, int, struct prefix_rd *, u_char *);
+                        afi_t, safi_t, int, int, struct prefix_rd *,
+                        uint32_t *labels, size_t nlabels);
 
 /* for bgp_nexthop and bgp_damp */
 extern void bgp_process (struct bgp *, struct bgp_node *, afi_t, safi_t);
-- 
2.1.4


_______________________________________________
Quagga-dev mailing list
Quagga-dev@lists.quagga.net
https://lists.quagga.net/mailman/listinfo/quagga-dev

Reply via email to