RL8.9 nftables unexpected behavior with ipv6 masquerading and iproute2

I have Router with two ipv6 nics let it be eth0 (default route) and tun0, and Host with self assigned real ipv6 in the range of eth0. All traffic from Host goes thru eth0, no problem, but I want to pass some traffic via tun0 according ip set in nftables.
Because Host has it’s ipv6 and tun0 has different ip masquerading is required. Also packets marking according ip set. So the rules are

chain nat_POSTROUTING {type nat hook postrouting priority srcnat; policy accept;
meta nfproto ipv6 oifname tun0 masquerade
}

chain mangle_PREROUTING { type filter hook prerouting priority mangle; policy accept;
ip6 daddr @addr-tun0 counter meta mark set 4
}

iproute2 rule is

32762: from all fwmark 0x4 lookup tbltun0

In a result packets are correctly route via tun0 BUT no masquerading. Source address is Host’s real ipv6. If I delete mangle mark rule and add iproute2 rule

32760: from <Host’s ipv6> lookup tbltun0

All traffic(as expected) from Host goes via tun0 and source address nated to tun0 ip.
Seems I do not understand something.

Same idea with ipv4 traffic works perfectly.

Lets say the subnet on tun0 is a::0/x (I don’t use IPv6, so only pseudo-syntax)

  • tun0 has address a::1
  • the set addr-tun0 has address a::2
  • another machine in that subnet has address a::3

A packet arrives (from eth0) with destination a::y

  • if y is 1, then packet is routed to localhost (via input filter)
  • if y is 3, then packet is sent out from tun0 (after forward filter and nat postrouting)
  • if y is 2, then packet is routed according to routes in tbltun0
    you want that to send packet out from tun0 (after forward filter and nat postrouting)

Why the additional mangling as the default ought to “do the right thing”?

In the router eth0 is WAN interface with real address aaaa::1
tun0 has fd00:1::2 the other end of tunnel has fd00:1::1
Another Computer has real address aaaa::2 and wants connect bbbb::100
If do nothing packet will go out from eth0 aaaa::2→bbbb::100 and the answer come to eth0 from bbbb::100 to aaaa::2

I want this packet go via tun0. So I need create iproute2 rule that packets marked with 4 are going according route in tbltun0 (via tun0) and add bbbb::100 element to set @addr-tun0addr-tun0

Then packet from aaaa::2 goes via tun0 to bbbb::100, bbbb::100 send answer to aaaa::2 and this answer returns ANOTHER way to eth0 not tun0. That’s why masquerading is required, it changes source address from aaaa::2 to fd00:1::2. Answer returns same way then.

But masquerading somehow does not work if I add marking rule. Packet goes to tun0 wtih aaaa::2 source address instead fd00:1::2