diff --git a/conf/confbase.Y b/conf/confbase.Y
index 8a27c3d5e..9a83083c2 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -92,7 +92,7 @@ CF_DECLS
   struct proto_spec ps;
   struct channel_limit cl;
   struct timeformat *tf;
-  mpls_label_stack *mls;
+  struct adata *ad;
   struct bytestring *bs;
 }
 
@@ -113,7 +113,7 @@ CF_DECLS
 %type <a> ipa
 %type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
 %type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_
-%type <mls> label_stack_start label_stack
+%type <ad> label_stack_start label_stack
 
 %type <t> text opttext
 %type <s> symbol symbol_known toksym 
@@ -351,17 +351,19 @@ net_or_ipa:
 
 label_stack_start: NUM
 {
-  $$ = cfg_allocz(sizeof(mpls_label_stack));
-  $$->len = 1;
-  $$->stack[0] = $1;
+  $$ = cfg_allocz(ADATA_SIZE(MPLS_MAX_LABEL_STACK * sizeof(u32)));
+  $$->length = sizeof(u32);
+  *((u32 *)$$->data) = $1;
 };
 
 label_stack:
     label_stack_start
   | label_stack '/' NUM {
-    if ($1->len >= MPLS_MAX_LABEL_STACK)
+    if ($1->length >= MPLS_MAX_LABEL_STACK * sizeof(u32))
       cf_error("Too many labels in stack");
-    $1->stack[$1->len++] = $3;
+
+    *((u32 *)($$->data + $1->length)) = $3;
+    $1->length += sizeof(u32);
     $$ = $1;
   }
 ;
diff --git a/lib/ip.h b/lib/ip.h
index 9eef2e163..8f975abab 100644
--- a/lib/ip.h
+++ b/lib/ip.h
@@ -363,10 +363,6 @@ static inline ip6_addr ip6_ntoh(ip6_addr a)
 { return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); }
 
 #define MPLS_MAX_LABEL_STACK 8
-typedef struct mpls_label_stack {
-  uint len;
-  u32 stack[MPLS_MAX_LABEL_STACK];
-} mpls_label_stack;
 
 static inline int
 mpls_get(const char *buf, int buflen, u32 *stack)
diff --git a/lib/route.h b/lib/route.h
index cfb08bbe5..df648cf98 100644
--- a/lib/route.h
+++ b/lib/route.h
@@ -72,7 +72,6 @@ struct nexthop {
   struct nexthop *next;
   byte flags;
   byte weight;
-  byte labels_orig;			/* Number of labels before hostentry was applied */
   byte labels;				/* Number of all labels */
   u32 label[0];
 };
@@ -316,6 +315,10 @@ extern struct ea_class ea_gen_source;
 static inline u32 rt_get_source_attr(const rte *rt)
 { return ea_get_int(rt->attrs->eattrs, &ea_gen_source, 0); }
 
+/* MPLS labels: Use with a recursive nexthop specification
+ * to add additional labels to the resolved nexthop */
+extern struct ea_class ea_mpls_labels;
+
 /* Next hop structures */
 
 #define NEXTHOP_MAX_SIZE (sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
diff --git a/nest/rt-attr.c b/nest/rt-attr.c
index 39fd7db42..dc4fe7858 100644
--- a/nest/rt-attr.c
+++ b/nest/rt-attr.c
@@ -108,6 +108,12 @@ struct ea_class ea_gen_source = {
   .format = ea_gen_source_format,
 };
 
+struct ea_class ea_mpls_labels = {
+  .name = "mpls_labels",
+  .type = T_CLIST,
+  .readonly = 1,
+};
+
 const char * rta_dest_names[RTD_MAX] = {
   [RTD_NONE]		= "",
   [RTD_UNICAST]		= "unicast",
@@ -220,7 +226,7 @@ nexthop__same(struct nexthop *x, struct nexthop *y)
   {
     if (!ipa_equal(x->gw, y->gw) || (x->iface != y->iface) ||
 	(x->flags != y->flags) || (x->weight != y->weight) ||
-	(x->labels_orig != y->labels_orig) || (x->labels != y->labels))
+	(x->labels != y->labels))
       return 0;
 
     for (int i = 0; i < x->labels; i++)
@@ -402,7 +408,6 @@ nexthop_copy(struct nexthop *o)
       n->next = NULL;
       n->flags = o->flags;
       n->weight = o->weight;
-      n->labels_orig = o->labels_orig;
       n->labels = o->labels;
       for (int i=0; i<o->labels; i++)
 	n->label[i] = o->label[i];
@@ -1484,6 +1489,8 @@ rta_init(void)
   ea_register_init(&ea_gen_igp_metric);
   ea_register_init(&ea_gen_from);
   ea_register_init(&ea_gen_source);
+
+  ea_register_init(&ea_mpls_labels);
 }
 
 /*
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 3f3aee18f..ba4f51153 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -2504,7 +2504,7 @@ rt_preconfig(struct config *c)
  */
 
 void
-rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls)
+rta_apply_hostentry(rta *a, struct hostentry *he)
 {
   a->hostentry = he;
   a->dest = he->dest;
@@ -2516,15 +2516,12 @@ rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls)
     /* No nexthop */
 no_nexthop:
     a->nh = (struct nexthop) {};
-    if (mls)
-    { /* Store the label stack for later changes */
-      a->nh.labels_orig = a->nh.labels = mls->len;
-      memcpy(a->nh.label, mls->stack, mls->len * sizeof(u32));
-    }
     return;
   }
 
-  if (((!mls) || (!mls->len)) && he->nexthop_linkable)
+  eattr *mls_ea = ea_find(a->eattrs, &ea_mpls_labels);
+
+  if (!mls_ea && he->nexthop_linkable)
   { /* Just link the nexthop chain, no label append happens. */
     memcpy(&(a->nh), &(he->src->nh), nexthop_size(&(he->src->nh)));
     return;
@@ -2533,6 +2530,9 @@ no_nexthop:
   struct nexthop *nhp = NULL, *nhr = NULL;
   int skip_nexthop = 0;
 
+  const struct adata *mls = mls_ea ? mls_ea->u.ptr : NULL;
+  uint mls_cnt = mls ? mls->length / sizeof(u32) : 0;
+
   for (struct nexthop *nh = &(he->src->nh); nh; nh = nh->next)
   {
     if (skip_nexthop)
@@ -2549,17 +2549,16 @@ no_nexthop:
 
     if (mls)
     {
-      nhp->labels = nh->labels + mls->len;
-      nhp->labels_orig = mls->len;
+      nhp->labels = nh->labels + mls_cnt;
       if (nhp->labels <= MPLS_MAX_LABEL_STACK)
       {
 	memcpy(nhp->label, nh->label, nh->labels * sizeof(u32)); /* First the hostentry labels */
-	memcpy(&(nhp->label[nh->labels]), mls->stack, mls->len * sizeof(u32)); /* Then the bottom labels */
+	memcpy(&(nhp->label[nh->labels]), mls->data, mls->length); /* Then the bottom labels */
       }
       else
       {
 	log(L_WARN "Sum of label stack sizes %d + %d = %d exceedes allowed maximum (%d)",
-	    nh->labels, mls->len, nhp->labels, MPLS_MAX_LABEL_STACK);
+	    nh->labels, mls_cnt, nhp->labels, MPLS_MAX_LABEL_STACK);
 	skip_nexthop++;
 	continue;
       }
@@ -2567,7 +2566,6 @@ no_nexthop:
     else if (nh->labels)
     {
       nhp->labels = nh->labels;
-      nhp->labels_orig = 0;
       memcpy(nhp->label, nh->label, nh->labels * sizeof(u32));
     }
 
@@ -2620,10 +2618,7 @@ rt_next_hop_update_rte(rtable *tab, net *n, rte *old)
   rta *a = alloca(RTA_MAX_SIZE);
   memcpy(a, old->attrs, rta_size(old->attrs));
 
-  mpls_label_stack mls = { .len = a->nh.labels_orig };
-  memcpy(mls.stack, &a->nh.label[a->nh.labels - mls.len], mls.len * sizeof(u32));
-
-  rta_apply_hostentry(a, old->attrs->hostentry, &mls);
+  rta_apply_hostentry(a, old->attrs->hostentry);
   a->cached = 0;
 
   rte e0 = *old;
diff --git a/nest/rt.h b/nest/rt.h
index 6d7c28511..fc8e2d3c3 100644
--- a/nest/rt.h
+++ b/nest/rt.h
@@ -439,12 +439,12 @@ struct rt_show_data_rtable * rt_show_add_table(struct rt_show_data *d, rtable *t
 #define RSEM_EXPORTED	4		/* Routes marked in export map */
 
 struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
-void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);
+void rta_apply_hostentry(rta *a, struct hostentry *he);
 
 static inline void
-rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls)
+rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll)
 {
-  rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls);
+  rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep));
 }
 
 /*
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 44a2f80ae..a692c4dc9 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -980,15 +980,18 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
     s->hostentry = rt_get_hostentry(tab, gw, ll, c->c.table);
 
     if (!s->mpls)
-      rta_apply_hostentry(a, s->hostentry, NULL);
+      rta_apply_hostentry(a, s->hostentry);
 
     /* With MPLS, hostentry is applied later in bgp_apply_mpls_labels() */
   }
 }
 
 static void
-bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a, u32 *labels, uint lnum)
+bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a)
 {
+  u32 *labels = (u32 *) s->mpls_labels->data;
+  u32 lnum = s->mpls_labels->length / sizeof(u32);
+
   if (lnum > MPLS_MAX_LABEL_STACK)
   {
     REPORT("Too many MPLS labels ($u)", lnum);
@@ -1001,7 +1004,7 @@ bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a, u32 *labels, uint lnum)
 
   /* Handle implicit NULL as empty MPLS stack */
   if ((lnum == 1) && (labels[0] == BGP_MPLS_NULL))
-    lnum = 0;
+    lnum = s->mpls_labels->length = 0;
 
   if (s->channel->cf->gw_mode == GW_DIRECT)
   {
@@ -1009,13 +1012,7 @@ bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a, u32 *labels, uint lnum)
     memcpy(a->nh.label, labels, 4*lnum);
   }
   else /* GW_RECURSIVE */
-  {
-    mpls_label_stack ms;
-
-    ms.len = lnum;
-    memcpy(ms.stack, labels, 4*lnum);
-    rta_apply_hostentry(a, s->hostentry, &ms);
-  }
+    rta_apply_hostentry(a, s->hostentry);
 }
 
 static void
@@ -1419,7 +1416,13 @@ bgp_encode_mpls_labels(struct bgp_write_state *s UNUSED, const adata *mpls, byte
 static void
 bgp_decode_mpls_labels(struct bgp_parse_state *s, byte **pos, uint *len, uint *pxlen, rta *a)
 {
-  u32 labels[BGP_MPLS_MAX], label;
+  struct {
+    struct adata ad;
+    u32 labels[BGP_MPLS_MAX];
+  } labels_adata;
+
+  u32 *labels = labels_adata.labels;
+  u32 label;
   uint lnum = 0;
 
   do {
@@ -1441,19 +1444,19 @@ bgp_decode_mpls_labels(struct bgp_parse_state *s, byte **pos, uint *len, uint *p
   if (!a)
     return;
 
+  labels_adata.ad.length = lnum * sizeof(u32);
+
   /* Attach MPLS attribute unless we already have one */
   if (!s->mpls_labels)
-  {
-    s->mpls_labels = lp_alloc_adata(s->pool, 4*BGP_MPLS_MAX);
-    bgp_set_attr_ptr(&(a->eattrs), BA_MPLS_LABEL_STACK, 0, s->mpls_labels);
-  }
-
-  /* Overwrite data in the attribute */
-  s->mpls_labels->length = 4*lnum;
-  memcpy(s->mpls_labels->data, labels, 4*lnum);
+    ea_set_attr(&(a->eattrs),
+	EA_LITERAL_DIRECT_ADATA(&ea_mpls_labels, 0,
+	  (s->mpls_labels = tmp_store_adata(labels, BGP_MPLS_MAX * sizeof(u32)))));
+  else
+    /* Overwrite data in the attribute */
+    memcpy(s->mpls_labels, &labels_adata, sizeof labels_adata);
 
   /* Update next hop entry in rta */
-  bgp_apply_mpls_labels(s, a, labels, lnum);
+  bgp_apply_mpls_labels(s, a);
 
   /* Attributes were changed, invalidate cached entry */
   rta_free(s->cached_rta);
diff --git a/proto/static/static.c b/proto/static/static.c
index f7eddedb7..dffc4882c 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -76,8 +76,8 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
       nh->weight = r2->weight;
       if (r2->mls)
       {
-	nh->labels = r2->mls->len;
-	memcpy(nh->label, r2->mls->stack, r2->mls->len * sizeof(u32));
+	nh->labels = r2->mls->length / sizeof(u32);
+	memcpy(nh->label, r2->mls->data, r2->mls->length);
       }
 
       nexthop_insert(&nhs, nh);
@@ -92,7 +92,11 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
   if (r->dest == RTDX_RECURSIVE)
   {
     rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6;
-    rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls);
+    if (r->mls)
+      ea_set_attr(&a->eattrs,
+	  EA_LITERAL_DIRECT_ADATA(&ea_mpls_labels, 0, r->mls));
+
+    rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE);
   }
 
   /* Already announced */
@@ -303,31 +307,17 @@ static_same_dest(struct static_route *x, struct static_route *y)
 	  (x->weight != y->weight) ||
 	  (x->use_bfd != y->use_bfd) ||
 	  (!x->mls != !y->mls) ||
-	  ((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
+	  ((x->mls) && (y->mls) && adata_same(x->mls, y->mls)))
 	return 0;
-
-      if (!x->mls)
-	continue;
-
-      for (uint i = 0; i < x->mls->len; i++)
-	if (x->mls->stack[i] != y->mls->stack[i])
-	  return 0;
     }
     return !x && !y;
 
   case RTDX_RECURSIVE:
     if (!ipa_equal(x->via, y->via) ||
 	(!x->mls != !y->mls) ||
-	((x->mls) && (y->mls) && (x->mls->len != y->mls->len)))
+	((x->mls) && (y->mls) && adata_same(x->mls, y->mls)))
       return 0;
 
-    if (!x->mls)
-      return 1;
-
-    for (uint i = 0; i < x->mls->len; i++)
-      if (x->mls->stack[i] != y->mls->stack[i])
-	return 0;
-
     return 1;
 
   default:
diff --git a/proto/static/static.h b/proto/static/static.h
index d99f7ebd1..ea7ca33b8 100644
--- a/proto/static/static.h
+++ b/proto/static/static.h
@@ -49,7 +49,7 @@ struct static_route {
   byte weight;				/* Multipath next hop weight */
   byte use_bfd;				/* Configured to use BFD */
   struct bfd_request *bfd_req;		/* BFD request, if BFD is used */
-  mpls_label_stack *mls;		/* MPLS label stack; may be NULL */
+  struct adata *mls;			/* MPLS label stack; may be NULL */
 };
 
 /*