diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 8683ab80..c1c108a2 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -1586,7 +1586,7 @@ bgp_finish_attrs(struct bgp_parse_state *s, ea_list **to)
 HASH_DEFINE_REHASH_FN(RBH, struct bgp_bucket)
 
 static void
-bgp_init_bucket_table(struct bgp_pending_tx *c)
+bgp_init_bucket_table(struct bgp_channel *c)
 {
   HASH_INIT(c->bucket_hash, c->pool, 8);
 
@@ -1595,7 +1595,7 @@ bgp_init_bucket_table(struct bgp_pending_tx *c)
 }
 
 static struct bgp_bucket *
-bgp_get_bucket(struct bgp_pending_tx *c, ea_list *new)
+bgp_get_bucket(struct bgp_channel *c, ea_list *new)
 {
   /* Hash and lookup */
   u32 hash = ea_hash(new);
@@ -1624,7 +1624,7 @@ bgp_get_bucket(struct bgp_pending_tx *c, ea_list *new)
 }
 
 static struct bgp_bucket *
-bgp_get_withdraw_bucket(struct bgp_pending_tx *c)
+bgp_get_withdraw_bucket(struct bgp_channel *c)
 {
   if (!c->withdraw_bucket)
   {
@@ -1636,17 +1636,15 @@ bgp_get_withdraw_bucket(struct bgp_pending_tx *c)
 }
 
 static void
-bgp_free_bucket(struct bgp_pending_tx *c, struct bgp_bucket *b)
+bgp_free_bucket(struct bgp_channel *c, struct bgp_bucket *b)
 {
   HASH_REMOVE2(c->bucket_hash, RBH, c->pool, b);
   mb_free(b);
 }
 
 int
-bgp_done_bucket(struct bgp_channel *bc, struct bgp_bucket *b)
+bgp_done_bucket(struct bgp_channel *c, struct bgp_bucket *b)
 {
-  struct bgp_pending_tx *c = bc->ptx;
-
   /* Won't free the withdraw bucket */
   if (b == c->withdraw_bucket)
     return 0;
@@ -1662,21 +1660,19 @@ bgp_done_bucket(struct bgp_channel *bc, struct bgp_bucket *b)
 }
 
 void
-bgp_defer_bucket(struct bgp_channel *bc, struct bgp_bucket *b)
+bgp_defer_bucket(struct bgp_channel *c, struct bgp_bucket *b)
 {
-  struct bgp_pending_tx *c = bc->ptx;
   rem_node(&b->send_node);
   add_tail(&c->bucket_queue, &b->send_node);
 }
 
 void
-bgp_withdraw_bucket(struct bgp_channel *bc, struct bgp_bucket *b)
+bgp_withdraw_bucket(struct bgp_channel *c, struct bgp_bucket *b)
 {
   if (b->bmp)
     return;
 
-  struct bgp_proto *p = (void *) bc->c.proto;
-  struct bgp_pending_tx *c = bc->ptx;
+  SKIP_BACK_DECLARE(struct bgp_proto, p, p, c->c.proto);
   struct bgp_bucket *wb = bgp_get_withdraw_bucket(c);
 
   log(L_ERR "%s: Attribute list too long", p->p.name);
@@ -1685,8 +1681,8 @@ bgp_withdraw_bucket(struct bgp_channel *bc, struct bgp_bucket *b)
     struct bgp_prefix *px = HEAD(b->prefixes);
 
     log(L_ERR "%s: - withdrawing %N", p->p.name, &px->net);
-    rem_node(&px->buck_node_xx);
-    add_tail(&wb->prefixes, &px->buck_node_xx);
+    rem_node(&px->buck_node);
+    add_tail(&wb->prefixes, &px->buck_node);
   }
 }
 
@@ -1707,17 +1703,16 @@ bgp_withdraw_bucket(struct bgp_channel *bc, struct bgp_bucket *b)
 HASH_DEFINE_REHASH_FN(PXH, struct bgp_prefix)
 
 static void
-bgp_init_prefix_table(struct bgp_channel *bc)
+bgp_init_prefix_table(struct bgp_channel *c)
 {
-  struct bgp_pending_tx *c = bc->ptx;
   HASH_INIT(c->prefix_hash, c->pool, 8);
 
-  uint alen = net_addr_length[bc->c.net_type];
+  uint alen = net_addr_length[c->c.net_type];
   c->prefix_slab = alen ? sl_new(c->pool, sizeof(struct bgp_prefix) + alen) : NULL;
 }
 
 static struct bgp_prefix *
-bgp_get_prefix(struct bgp_pending_tx *c, const net_addr *net, struct rte_src *src, int add_path_tx)
+bgp_get_prefix(struct bgp_channel *c, const net_addr *net, struct rte_src *src, int add_path_tx)
 {
   u32 path_id = src->global_id;
   u32 path_id_hash = add_path_tx ? path_id : 0;
@@ -1753,12 +1748,12 @@ bgp_get_prefix(struct bgp_pending_tx *c, const net_addr *net, struct rte_src *sr
   return px;
 }
 
-static void bgp_free_prefix(struct bgp_pending_tx *c, struct bgp_prefix *px);
+static void bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px);
 
 static inline int
 bgp_update_prefix(struct bgp_channel *c, struct bgp_prefix *px, struct bgp_bucket *b)
 {
-#define IS_WITHDRAW_BUCKET(b)	((b) == c->ptx->withdraw_bucket)
+#define IS_WITHDRAW_BUCKET(b)	((b) == c->withdraw_bucket)
 #define BPX_TRACE(what)	do { \
   if (c->c.debug & D_ROUTES) log(L_TRACE "%s.%s < %s %N %uG %s", \
       c->c.proto->name, c->c.name, what, \
@@ -1775,7 +1770,7 @@ bgp_update_prefix(struct bgp_channel *c, struct bgp_prefix *px, struct bgp_bucke
   /* Unqueue from the old bucket */
   if (px->cur)
   {
-    rem_node(&px->buck_node_xx);
+    rem_node(&px->buck_node);
     bgp_done_bucket(c, px->cur);
   }
 
@@ -1789,7 +1784,7 @@ bgp_update_prefix(struct bgp_channel *c, struct bgp_prefix *px, struct bgp_bucke
 
     /* Well, we haven't sent anything yet */
     if (!px->last)
-      bgp_free_prefix(c->ptx, px);
+      bgp_free_prefix(c, px);
 
     px->cur = NULL;
     return 0;
@@ -1797,10 +1792,10 @@ bgp_update_prefix(struct bgp_channel *c, struct bgp_prefix *px, struct bgp_bucke
 
   /* Enqueue the bucket if it has been empty */
   if (!IS_WITHDRAW_BUCKET(b) && EMPTY_LIST(b->prefixes))
-    add_tail(&c->ptx->bucket_queue, &b->send_node);
+    add_tail(&c->bucket_queue, &b->send_node);
 
   /* Enqueue to the new bucket and indicate the change */
-  add_tail(&b->prefixes, &px->buck_node_xx);
+  add_tail(&b->prefixes, &px->buck_node);
   px->cur = b;
 
   BPX_TRACE("queued");
@@ -1810,7 +1805,7 @@ bgp_update_prefix(struct bgp_channel *c, struct bgp_prefix *px, struct bgp_bucke
 }
 
 static void
-bgp_free_prefix(struct bgp_pending_tx *c, struct bgp_prefix *px)
+bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
 {
   HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px);
 
@@ -1831,7 +1826,7 @@ bgp_done_prefix(struct bgp_channel *c, struct bgp_prefix *px, struct bgp_bucket
 
   /* Cleanup: We're called from bucket senders. */
   ASSERT_DIE(px->cur == buck);
-  rem_node(&px->buck_node_xx);
+  rem_node(&px->buck_node);
 
   /* We may want to store the updates */
   if (c->c.out_table)
@@ -1854,49 +1849,46 @@ bgp_done_prefix(struct bgp_channel *c, struct bgp_prefix *px, struct bgp_bucket
     /* Prefixes belonging to the withdraw bucket are freed always */
   }
 
-  bgp_free_prefix(c->ptx, px);
+  bgp_free_prefix(c, px);
 }
 
-static void
-bgp_pending_tx_rfree(resource *r)
-{
-  SKIP_BACK_DECLARE(struct bgp_pending_tx, ptx, r, r);
-
-  HASH_WALK(ptx->prefix_hash, next, n)
-    rt_unlock_source(rt_find_source_global(n->path_id));
-  HASH_WALK_END;
-}
-
-static void bgp_pending_tx_dump(resource *r UNUSED, unsigned indent UNUSED) { debug("\n"); }
-
-static struct resclass bgp_pending_tx_class = {
-  .name = "BGP Pending TX",
-  .size = sizeof(struct bgp_pending_tx),
-  .free = bgp_pending_tx_rfree,
-  .dump = bgp_pending_tx_dump,
-};
-
 void
 bgp_init_pending_tx(struct bgp_channel *c)
 {
-  ASSERT_DIE(!c->ptx);
-
-  pool *p = rp_new(c->pool, proto_domain(c->c.proto), "BGP Pending TX");
-  c->ptx = ralloc(p, &bgp_pending_tx_class);
-  c->ptx->pool = p;
-
-  bgp_init_bucket_table(c->ptx);
+  bgp_init_bucket_table(c);
   bgp_init_prefix_table(c);
 }
 
 void
 bgp_free_pending_tx(struct bgp_channel *c)
 {
-  ASSERT_DIE(c->ptx);
-  ASSERT_DIE(c->ptx->pool);
+  if (!c->bucket_hash.data)
+    return;
 
-  rp_free(c->ptx->pool);
-  c->ptx = NULL;
+  struct bgp_prefix *px;
+  if (c->withdraw_bucket)
+    WALK_LIST_FIRST(px, c->withdraw_bucket->prefixes)
+      bgp_done_prefix(c, px, c->withdraw_bucket);
+
+  struct bgp_bucket *b;
+  WALK_LIST_FIRST(b, c->bucket_queue)
+  {
+    WALK_LIST_FIRST(px, b->prefixes)
+      bgp_done_prefix(c, px, b);
+    bgp_done_bucket(c, b);
+  }
+
+  HASH_WALK(c->prefix_hash, next, n)
+    bug("Stray prefix after cleanup");
+  HASH_WALK_END;
+
+  HASH_FREE(c->prefix_hash);
+
+  HASH_WALK(c->bucket_hash, next, n)
+    bug("Stray bucket after cleanup");
+  HASH_WALK_END;
+
+  HASH_FREE(c->bucket_hash);
 }
 
 #if 0
@@ -1913,8 +1905,7 @@ static void
 bgp_out_table_feed(void *data)
 {
   struct bgp_out_export_hook *hook = data;
-  SKIP_BACK_DECLARE(struct bgp_channel, bc, prefix_exporter, hook->h.table);
-  struct bgp_pending_tx *c = bc->ptx;
+  SKIP_BACK_DECLARE(struct bgp_channel, c, prefix_exporter, hook->h.table);
 
   int max = 512;
 
@@ -2059,7 +2050,7 @@ bgp_setup_out_table(struct bgp_channel *c)
 }
 #else
 void
-bgp_setup_out_table(struct bgp_channel *c)
+bgp_setup_out_table(struct bgp_channel *c UNUSED)
 {}
 #endif
 
@@ -2299,16 +2290,16 @@ bgp_rt_notify(struct proto *P, struct channel *C, const net_addr *n, rte *new, c
       log(L_ERR "%s: Invalid route %N withdrawn", p->p.name, n);
 
     /* If attributes are invalid, we fail back to withdraw */
-    buck = attrs ? bgp_get_bucket(c->ptx, attrs) : bgp_get_withdraw_bucket(c->ptx);
+    buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
     path = new->src;
   }
   else
   {
-    buck = bgp_get_withdraw_bucket(c->ptx);
+    buck = bgp_get_withdraw_bucket(c);
     path = old->src;
   }
 
-  if (bgp_update_prefix(c, bgp_get_prefix(c->ptx, n, path, c->add_path_tx), buck))
+  if (bgp_update_prefix(c, bgp_get_prefix(c, n, path, c->add_path_tx), buck))
     bgp_schedule_packet(p->conn, c, PKT_UPDATE);
 }
 
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index b2e5da9c..e93765a1 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -630,8 +630,7 @@ bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len)
 
   struct bgp_channel *c;
   BGP_WALK_CHANNELS(p, c)
-    if (c->ptx)
-      bgp_free_pending_tx(c);
+    bgp_free_pending_tx(c);
 
   proto_send_event(&p->p, p->event);
 }
@@ -2839,14 +2838,13 @@ bgp_show_proto_info(struct proto *P)
       uint prefix_cnt = 0;
       struct bgp_bucket *buck;
       struct bgp_prefix *px;
-      if (c->ptx)
-	WALK_LIST(buck, c->ptx->bucket_queue)
-	{
-	  bucket_cnt++;
-	  WALK_LIST(px, buck->prefixes)
-	    if (px->cur)
-	      prefix_cnt++;
-	}
+      WALK_LIST(buck, c->bucket_queue)
+      {
+	bucket_cnt++;
+	WALK_LIST(px, buck->prefixes)
+	  if (px->cur)
+	    prefix_cnt++;
+      }
 
       cli_msg(-1006, "    Pending %u attribute sets with total %u prefixes to send",
 	 bucket_cnt, prefix_cnt);
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 862d18dc..5ed29fc3 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -394,7 +394,13 @@ struct bgp_channel {
 
   /* Rest are zeroed when down */
   pool *pool;
-  struct bgp_pending_tx	*ptx;		/* Routes waiting to be sent */
+
+  HASH(struct bgp_bucket) bucket_hash;	/* Hash table of route buckets */
+  struct bgp_bucket *withdraw_bucket;	/* Withdrawn routes */
+  list bucket_queue;			/* Queue of buckets to send (struct bgp_bucket) */
+
+  HASH(struct bgp_prefix) prefix_hash;	/* Prefixes to be sent */
+  slab *prefix_slab;			/* Slab holding prefix nodes */
 //  struct rt_exporter prefix_exporter;	/* Table-like exporter for ptx */
 
   ip_addr next_hop_addr;		/* Local address for NEXT_HOP attribute */
@@ -422,7 +428,7 @@ struct bgp_channel {
 };
 
 struct bgp_prefix {
-  node buck_node_xx;			/* Node in per-bucket list */
+  node buck_node;			/* Node in per-bucket list */
   struct bgp_prefix *next;		/* Node in prefix hash table */
   struct bgp_bucket *last;		/* Last bucket sent with this prefix */
   struct bgp_bucket *cur;		/* Current bucket (cur == last) if no update is required */
@@ -442,18 +448,6 @@ struct bgp_bucket {
   ea_list eattrs[0];			/* Per-bucket extended attributes */
 };
 
-struct bgp_pending_tx {
-  resource r;
-  pool *pool;
-
-  HASH(struct bgp_bucket) bucket_hash;	/* Hash table of route buckets */
-  struct bgp_bucket *withdraw_bucket;	/* Withdrawn routes */
-  list bucket_queue;			/* Queue of buckets to send (struct bgp_bucket) */
-
-  HASH(struct bgp_prefix) prefix_hash;	/* Prefixes to be sent */
-  slab *prefix_slab;			/* Slab holding prefix nodes */
-};
-
 struct bgp_export_state {
   struct bgp_proto *proto;
   struct bgp_channel *channel;
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 498f67e4..4bc6340a 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -2306,7 +2306,7 @@ bgp_create_ip_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu
    *	var	IPv4 Network Layer Reachability Information
    */
 
-  ASSERT_DIE(s->channel->ptx->withdraw_bucket != buck);
+  ASSERT_DIE(s->channel->withdraw_bucket != buck);
 
   int lr, la;
 
@@ -2329,7 +2329,7 @@ bgp_create_ip_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu
 static byte *
 bgp_create_mp_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, byte *end)
 {
-  ASSERT_DIE(s->channel->ptx->withdraw_bucket != buck);
+  ASSERT_DIE(s->channel->withdraw_bucket != buck);
 
   /*
    *	2 B	IPv4 Withdrawn Routes Length (zero)
@@ -2562,7 +2562,7 @@ again:
   };
 
   /* Try unreachable bucket */
-  if ((buck = c->ptx->withdraw_bucket) && !EMPTY_LIST(buck->prefixes))
+  if ((buck = c->withdraw_bucket) && !EMPTY_LIST(buck->prefixes))
   {
     res = (c->afi == BGP_AF_IPV4) && !c->ext_next_hop ?
       bgp_create_ip_unreach(&s, buck, buf, end):
@@ -2572,9 +2572,9 @@ again:
   }
 
   /* Try reachable buckets */
-  if (!EMPTY_LIST(c->ptx->bucket_queue))
+  if (!EMPTY_LIST(c->bucket_queue))
   {
-    buck = HEAD(c->ptx->bucket_queue);
+    buck = HEAD(c->bucket_queue);
 
     /* Cleanup empty buckets */
     if (bgp_done_bucket(c, buck))