Some links are IPv4 CGNAT (notably Starlink at the moment).
What if I want IPv6.
Well, a simple approach is something like L2TP. The issue is that this is IPv6 over L2TP over UDP over IPv4 over CGNAT, and that means a much reduced MTU. This is not the end of the world if you know you have the reduced MTU, but a shame.
So would there be a way, even if only for outgoing sessions, to "tunnel" the IPv6 over IPv4 with zero overhead?
I think so. You need something in the Internet with IPv6 and something on the network that is stuck behind the IPv4 CGNAT end point.
The trick is that an IPv4 header is 20 bytes shorter. So if you reduced an IPv6 packet to IPv4 you get 20 bytes spare. You could tack something on to the end of the packet, and it should arrive at the tunnel endpoint via the CGNAT over IPv4.
So why not tack on the original external IPv6 address?
Yes, the source address would have to be tracked using NAT of the IPv6. And indeed, one could say you only need to tack on the original IPv6 at the start of the session as once you have created the NAT session you know both IPs involved.
Only catch is that TCP, for example, does not normally have extra data on SYN packets, so you have to check they arrive intact. One would hope so.
OK scrap that - I have a better idea :-)
You establish a link, maybe simply a TCP session, between your devices, outgoing via the CGNAT IPv4 to the far end. You use this to carry some control data. It could even be TLS with client cert, etc, for security.
Every outgoing session you create you send details over your TCP control link to the far end with details of that session, and it allocates an IPv4 UDP or TCP port for you and advises over the TCP control link. You can then send a new packet of the same type, e.g. TCP/UDP, to the far end. This then maps through to the IPv6 addresses you want to use, and the original ports. The CGNAT is then used to carry each new packet in or out over the session map each end.
What makes this even more clever is that you can do incoming new sessions as well. Obviously the IPv6 block being used has to be routed to the outside device. But simply carry the details of the new incoming connection over the TCP control link, and then have the NAT end start an outgoing session. Once established, that is used to carry the incoming packet and the outgoing reply packets. Yes, it could make for a slight odd TCP handshake, and that may need slight messing, e.g. send the SYN and SYN/ACK over TCP control link, but create an outgoing SYN and SYN/ACK exchange over the CGNAT outgoing, setting up the payload sequence numbers to match what the ongoing packets will be using.
This could allow full IPv6 both ways over an IPv4 only CGNAT, and not only would it have no overhead, allowing full MTU, it would actually use fewer 20 bytes per packet after the first control packets, and so be 1.3% more efficient on the wire. Technically you would lose per packet flow labels, but not a lot else. Of course things other than TCP and UDP would be a challenge, but could simply be mapped to look like UDP on the IPv4 if needed.
Of course the same could be done for IPv4, not saving 20 bytes per packet, but basically un-mapping the CGNAT at each end.
A zero overhead tunnel could save one of the classic IT/networking issues "MTU".