Is it possible to advertise a record across private network

I have Node A (has a public IP) it acts as a bootstrap peer. I also have Node B and C (Node B in my house with no public IP and Node C in my friends house also no public IP). Both of them can connect to Node A during the bootstrap phase. Node B now wants to Advertise a rendezvous point using discovery.RoutingDiscovery. Now Node C wants to discover this rendezvous point using discovery.FindPeers. However, I keep getting 0 peers found. My question is: Is what I am trying to do even possible?

I find that my scenario works if either Node B or C runs on the same machine as Node A (but with a non public address).

Here is the code for Node A (the bootstrap peer)
package main

import (
	context2 "context"
	"crypto/rand"
	"flag"
	"fmt"
	"github.com/libp2p/go-libp2p"
	relay "github.com/libp2p/go-libp2p-circuit"
	"github.com/libp2p/go-libp2p-core/crypto"
	"github.com/libp2p/go-libp2p-core/host"
	"github.com/libp2p/go-libp2p-core/routing"
	//dht "github.com/libp2p/go-libp2p-kad-dht"
	"github.com/libp2p/go-libp2p-kad-dht/dual"
	secio "github.com/libp2p/go-libp2p-secio"
)

func main() {
	listen := flag.String("listen", "/ip4/0.0.0.0/tcp/5000", "The listen address")
	flag.Parse()

	ctx, cancel := context2.WithCancel(context2.Background())
	defer cancel()

	routing := libp2p.Routing(func(host host.Host) (routing.PeerRouting, error) {
		return dual.New(ctx, host)
	})

	listenAddress := libp2p.ListenAddrStrings(*listen)

	enableRelay := libp2p.EnableRelay(relay.OptActive, relay.OptHop)

	priv, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, rand.Reader)
	if err != nil {
		panic(err)
	}

	identity := libp2p.Identity(priv)

	security := libp2p.Security(secio.ID, secio.New)

	host, err := libp2p.New(
		ctx,
		routing,
		listenAddress,
		enableRelay,
		identity,
		security,
	)

	if err != nil {
		panic(err)
	}

	for _, addr := range host.Addrs() {
		fmt.Printf("Addr: %s/p2p/%s\n", addr, host.ID().Pretty())
	}

	select {}
}

Here is the code for Node B and C

func main() {
	room := flag.String("room", "", "the room you want to create")
	joinRoom := flag.String("join", "", "the room you want to join")
	bootstrap := flag.String("bootstrap", "/ip4/134.209.171.195/tcp/5000/p2p/QmWpBxWhq8G9G9m2yxc314Hfmd39PiHuWC5EJv3xZz9KxZ", "the relay")
	flag.Parse()

	ctx := context.Background()
	//defer cancel()

	if *joinRoom == "" && *room == "" {
		panic("At least one flag must be provided")
	}

	var ddht *dual.DHT
	var routingDiscovery *discovery.RoutingDiscovery
	routing := libp2p.Routing(func(host host.Host) (routing.PeerRouting, error) {
		var err error
		ddht, err = dual.New(ctx, host)
		routingDiscovery = discovery.NewRoutingDiscovery(ddht)

		return ddht, err
	})



	listenAddress := libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0")

	//enableRelay := libp2p.EnableRelay()

	security := libp2p.Security(secio.ID, secio.New)

	host, err := libp2p.New(ctx, listenAddress, security, routing, libp2p.NATPortMap())
	if err != nil {
		panic(err)
	}

	fmt.Println("Host Created")
	for _, addr := range host.Addrs() {
		fmt.Printf("Address: %s\n", addr)
	}

	fmt.Printf("My ID %s\n", host.ID().Pretty())

	// connect to the bootstrap peers
	ma, err := multiaddr.NewMultiaddr(*bootstrap)
	if err != nil {
		panic(err)
	}

	peerInfo, err := peer.AddrInfoFromP2pAddr(ma)
	if err != nil {
		panic(err)
	}

	if err := ddht.Bootstrap(ctx); err != nil {
		panic(err)
	}

	if err := host.Connect(ctx, *peerInfo); err != nil {
		panic(err)
	}
	fmt.Println("we are connected to the bootstrap peer")

	fmt.Println("DHT in a bootstrapped state")

	time.Sleep(time.Second * 5)

	ddht.LAN.RoutingTable().Print()
	ddht.WAN.RoutingTable().Print()

	// now do chat specific stuff
	if *room != "" {
		host.SetStreamHandler(protocolKey(*room), handleStream)
		// create a room and wait for connections in it
		fmt.Printf("Advertising %s\n", protocolKey(*room))

		discovery.Advertise(ctx, routingDiscovery, string(protocolKey(*room)))

		fmt.Printf("Successfully advertised room: %s\n", *room)

		// wait forever
		select {}
	}

	if *joinRoom != "" {
		fmt.Printf("Joining room %s\n", protocolKey(*joinRoom))
		pctx, _ := context.WithTimeout(ctx, time.Second*10)
		peers, err := discovery.FindPeers(pctx, routingDiscovery, string(protocolKey(*joinRoom)))
		//peerChan, err := routingDiscovery.FindPeers(pctx, protocolKey(*joinRoom))
		if err != nil {
			panic(err)
		}

		if len(peers) == 0 {
			fmt.Println("No Peers Found")
			return
		}

		fmt.Printf("Found %d peers\n", len(peers))

		for _, p := range peers {
			fmt.Printf("Trying peer %s\n", p.ID.Pretty())
			fmt.Println("Addresses")
			for _, addr := range p.Addrs {
				ma, err := circuitRelay(addr.String(), p.ID.String())
				if err != nil {
					fmt.Printf("Error circuit Relay address %s\n", err)
					continue
				}
				info, err := peer.AddrInfoFromP2pAddr(ma)
				if err != nil {
					fmt.Printf("Error converting to addr info %s\n", err)
					continue
				}

				fmt.Printf("Going to try and connect via relay (%s)\n", ma)
				if err := host.Connect(ctx, *info); err == nil {
					fmt.Printf("We Have a connection try to create a stream now\n")
					stream, err := host.NewStream(ctx, p.ID, protocolKey(*joinRoom))
					if err != nil {
						fmt.Printf("Error dialing %s <%s>\n", p.ID.Pretty(), err)
					} else {
						go handleStream(stream)
						break
					}

				}

			}

		}

		select {}
	}

}

func protocolKey(key string) protocol.ID {
	return protocol.ID("/chat/" + key)
}

func handleStream(stream network.Stream) {
	fmt.Println("Handling Stream")
	defer stream.Close()
}

func circuitRelay(target string, id string) (multiaddr.Multiaddr, error) {
	return multiaddr.NewMultiaddr("/p2p-circuit" + target + "/p2p/" + id)
}

For anyone running into a similar issue.
I was using the dual.DHT and when setting the dht.Mode(dht.ModeServer) option of the bootstrap peer it worked. Node A (the bootstrap peer) has a public address and can therefore respond to dht requests.